Releases: Veyllo-Labs/VAF
Release list
VAF v0.1.0a21
Added
-
The README says plainly what VAF sends and where it goes. A new
"What VAF sends, and where" section: VAF collects no usage data and reports
nothing to Veyllo, there is no analytics SDK and no crash reporter, and the
only outbound request VAF makes on its own is the startup version check
against GitHub's public releases API - which carries no data about you and
whichupdate_check_on_start: falseturns off. The section is equally plain
about the other direction: if you point VAF at a cloud provider, your prompts
go to that provider. "No telemetry" means Veyllo receives nothing; it does not
mean a cloud-backed agent works without talking to the cloud. The README badge
row now also shows the release, last commit, tool and test counts, the number
of supported LLM providers, and the supported platforms. -
The license now states, in one place, what a fork owes its users. VAF
carries a short legal notice under Section 7(b) of the AGPL, in English and
German, naming its origin and the obligations that come with it: pass it on
under the same license, make the corresponding source available, and if you run
a modified version as a network service, offer that service's users your
complete source. It also names the commercial license as the way out of those
obligations. A fork must keep the notice in its source. It does not have to
be displayed anywhere, and it restricts nothing else - renaming, rebranding and
commercial operation stay permitted, exactly as the AGPL allows. The notice is
static text: nothing about it is transmitted, and it involves no telemetry of
any kind. -
Tools can write their own log lines:
self.log(...). Every tool - including
one written by a third party against the publicBaseTool- can now report what
it is doing without reaching into VAF's internals. Lines land intools_*.log
with the tool name and session filled in, follow the sameVAF_LOG_DIRand
Debug Logssettings as everything else, and are cleaned up by the same garbage
collector. It never raises, so a logging mistake cannot fail a tool call. -
Sessions can be created, renamed and deleted inside the terminal app.
The sessions panel (Ctrl+S) gainednfor a fresh session,rto rename
the highlighted one anddto delete it after a confirmation - deleting
the session you are in is refused until you switch away./session new
and/session rename <name>do the same from the prompt. The panel now
shows the same list the web sidebar shows: messenger chats and internal
thinking runs stay in their dashboards instead of flooding the list. -
A session keeps ONE name everywhere, including across a web rename.
Renaming is now a single engine operation that changes the session file
and nothing else - before, renaming from a list could silently drag the
renamed session's saved state into the running one. The terminal app
adopts the on-disk name before saving on exit, so renaming a chat in the
web while the terminal had it open no longer loses the new name when the
terminal closes; the background worker had silently ignored web renames
altogether and now honors them. -
Switching sessions in the terminal app now shows the conversation.
Loading another session swapped the agent's memory but left the previous
conversation on screen, and a resumed session started with an empty
transcript as if nothing had ever been said. The transcript now follows: a
switch repaints the loaded conversation (the newest forty messages, with an
honest note when older ones were trimmed -/exportwrites the full
record), and starting VAF with an existing session shows its conversation
under the start banner. Replayed messages carry the time they were actually
sent. -
Automations and the tool catalog live in the terminal app's settings.
The Automations submenu shows what the classic menu showed - name, schedule,
next run, enabled state - and selecting an automation switches it on or off;
the storage folder opens from its own row. "Show All Tools" now opens the
tool catalog instead of pointing at a restart. -
The local model can be switched inside the terminal app, without losing
the conversation. "Select Active Model" now lists the models on disk with
the active one marked, and picking one swaps the running agent live: the
llama server is restarted with the chosen weights (it verifies which model
it serves instead of blindly reusing whatever runs), the model-specific
behavior follows the new weights, and the chat you were in stays. The swap
waits until the new model has loaded and says so; while a reply is being
generated it refuses instead of pulling the model out from under it. With a
cloud provider selected, picking a file stores the choice for the next time
the local provider serves - as the classic menu did. Only the model download
still points atvaf settings. -
The context limit, custom numbers, the microphone and About live in the
terminal app now. Four more settings rows stop pointing atvaf settings:
the context limit offers the classic presets plus a free value and says
honestly that it applies at the next start; the sub-agent timeout and the
auto-open tab cap take any number in their classic ranges; the microphone
submenu lists your real input devices and switches the live microphone at
once; and About shows version, licence and links without leaving the app. -
/export <file>writes the conversation from inside the terminal app -
markdown, or JSON when the filename ends in.json. The sessions panel now
shows each session's id and a line of its summary, andsession current
prints the full id - the one thingvaf run --session <id>needs and the
panel can only truncate.session listopens the panel instead of failing as
an unknown id.
Changed
- Files VAF writes and reads back now say which format they are in. Three
stores kept no format identity of their own, so a reader had to guess from the
keys it happened to find. The filesystem index cache carries a schema tag and a
cache without the current tag is rebuilt instead of being read as if it were
current - previously any JSON file at that path with a matchingoskey was
accepted, including one written by an older build or another tool. Handoff
bundles are written with a format tag, and bundles stored before the tag keep
loading, so an open handover is not lost across an update. The audit timeline's
hash chain starts from a versioned seed; timeline files written earlier start
from the old seed and still verify as intact. One visible effect: after the
update the filesystem index is rebuilt once on first use. - The tool-use log now covers every lane, not only chat.
tool_use_*.log
records which session and which user scope were behind a tool call - the first
place to look when isolation looks wrong. It was written from the chat loop
only, so workflow steps, librarian sub-tools, training samples and tools added
by an embedded application never appeared in it. The shared dispatcher writes
it now, so all of them do. Two details worth knowing: a call that a permission
check refused is logged too (a blocked attempt is exactly what this file is
opened for), and the argument preview is sanitized the same way the live event
stream already was, so a large field such as a file's contents is summarised by
length and digest instead of pasted in whole. The coder's own tool loop does not
use the shared dispatcher and is still absent. - A pip-installed VAF no longer writes logs into its own install directory.
The log directory search included the folder above the package, which in a
checkout is the repository and in a pip install is site-packages. Installed
copies now fall through to the normal application data directory; a checkout is
unaffected. SetVAF_LOG_DIRto choose explicitly. - Tool-loading diagnostics no longer print underneath the terminal app. The
per-turn tool hot-reload, the custom-tool reload and a failed provider switch
wrote their warnings straight to the raw terminal - under the full-screen app
that corrupted the display mid-conversation. These messages now travel the
same event lane as the rest of VAF's status output: the terminal app shows
them as notes, the classic terminal shows the usual styled event line instead
of a bare[WARN] ..., and the web log receives them too. vaf run --webno longer starts the background service behind your back.
The README has always described this command as the dashboard WITHOUT the tray,
and the lane hosts the dashboard itself - but if the background service was not
running, the command quietly launched it as a detached process that outlived
your session. Now the dashboard lives and dies with your session, as promised.
If you relied onvaf run --webto bootstrap the persistent service, start it
the intended way:vaf tray. A service that is already running keeps serving
unchanged.
Fixed
- The sub-agent windows are dark in dark mode. The librarian's file browser
came up with bright, near-white panels: its window body, the file area behind
the listing and the toolbar above it were painted with fixed colour values,
and fixed values do not follow the theme. All sub-agent windows shared those
values, so the coder, research, document and browser views were bright in the
same places. They are now defined once and follow the theme. Light mode is
unchanged. - Asking about files in one folder no longer answers about another one. The
librarian answers simple folder questions from a cached index in about a
second. That index decided which folder to report from the file type in your
question, not from the folder you named - so "how many PDFs are in Downloads"
came back with the count from Documents, fluently and fast enoug...
VAF v0.1.0a20
Added
- The terminal now shows how far a sub-agent has got. The task line above the
prompt showed only that something was running and for how long. It now also shows
the count, like2/5, for the two sub-agents that plan their work upfront: the
coder and the document agent. It is a count and never a percentage, because a
coding run legitimately finishes below its total when a task fails, and a bar that
has to reach 100% would have to lie about that. The three sub-agents that do not
plan upfront show nothing rather than a made-up number. - A session now opens by telling you where you are. Instead of a bare "new
session" line,vaf rungreets you with the Veyllo mark and the facts beside it,
centred on screen: version, your agent's name, the session name and id, the
active model, the local date and time - and the one line that was missing
entirely, how to get back into an earlier conversation. - Most settings are editable inside the terminal app now. Rows that used to say
"usevaf settings" work where they can: the AI provider and its model take effect
on the running agent without a restart, and the speech engine, input language,
sub-agent provider, sub-agent timeout, auto-open tab limit and local-server
auto-start are simply set. The three that genuinely need a fresh start - the local
model, the context limit and downloading a model - still point atvaf settings,
and now say why rather than just deferring. - Sessions you never wrote in do not pile up any more. Opening
vaf runand
closing it again left an empty session behind every time, and the list filled up
with rows that held nothing. An untouched session is now dropped when you leave
it, whether you quit or load a different one; anything you actually wrote in is
kept as before. - You can switch sessions from inside the terminal app. The sessions panel was
a list you could look at but not use; the only way back into an earlier
conversation was quitting and passing its id on the command line. Ctrl+S now
opens it, the arrow keys walk it and enter loads the one you picked. And when
you leave, VAF prints the session id and both commands that bring you back,
instead of letting it disappear. - The prompt remembers, suggests and completes again. The full-screen
vaf run
had a plain text box: no history on the arrow keys, no inline suggestion, no
completion for commands or file paths. All three are back, and they share the
older interface's history file, so your past messages are the same list whichever
way you start VAF. Typing/or@opens a menu you can walk with the arrow
keys; while it is open Enter picks an entry, and the next Enter sends. - The terminal commands are back, and all of them work in both interfaces. Typing
clear,tools,undo,restore,context,haltorrestartin the new
full-screenvaf runused to send the word to the model as a chat message. They
are commands again, alongside the ones that already worked, and a mistyped
/commandnow says so (with the closest match) instead of costing a turn.
Arguments work too:theme darkpicks that theme instead of cycling, and
session <id>loads that session. The ones that cannot be undone - clearing the
conversation, rolling back files, restarting - ask first. Behind this, the command
list lived in six places that had already drifted apart: the older interface
offered completions for words it could not run, and ran a word it did not offer.
There is one list now, and both interfaces read it. - Answers in the terminal are formatted again. The full-screen
vaf runshowed
the model's markdown verbatim: literal asterisks around bold text, raw list
markers, and code fences as three backticks and unhighlighted text. Headings,
lists and emphasis now render, and code blocks are syntax-highlighted and follow
the active theme. Long answers stream just as smoothly as before, because the
text is redrawn on a fixed rhythm instead of on every word. - Tool cards in the terminal now show what the tool actually returned. The
observation events an application can subscribe to reported that a tool finished,
how long it took and whether it failed, but never what came back - so anyone
building on VAF had to correlate results out of debug log files, and three places
inside VAF carried their own private copy of the result for the same reason, each
cut to a different length. Thetool_endevent now carries aresultfield
(capped at 800 characters, always a string, safe to serialize). Invaf runthe
tool card is no longer an empty fold: it opens onto the result. vaf runopens a full-screen terminal app. The terminal chat is no longer a
scrolling prompt: it is a full-screen app with a live transcript (strictly
chronological, streamed answers with the model's reasoning as a separate muted
think block), tool cards, event narration, the sub-agent status line, the
context-usage bar, the agent's animated avatar beside the newest reply, and
keyboard-complete overlays for settings, model, history, sessions and help. The
classic run-loop letters (s,c,t,h,l,?) still work typed into the
prompt, as do slash commands and@fileattachments. Tool confirmations finally
work in the terminal: previously they silently waited on the web dashboard and
timed out after five minutes if no browser was open; the app shows the question
and answers it in place. The previous interfaces stay available -vaf run --classicfor the plain prompt, and the newtui_modeconfig key to pick the
default lane (vaf run --webkeeps the previous lane, which owns the web-server
startup). Voice capture and provider switching inside the app land next.- You can now ask for one completion without a conversation. Building something on
VAF that just needs one answer - a classification, a summary, a commit message -
meant either running a full chat turn (history, tools, memory, routers) or
hand-rolling the backend call, and about twenty places inside VAF had done exactly
that, each slightly differently wrong.agent.complete(prompt)is one call with the
agent's configured backend: it never enters the conversation, runs no tools, writes
no memory, strips model reasoning from the result, and returns text or None - never
an exception and never an error message dressed as an answer. The same primitive now
powers the tools' own utility completions and the CLI features below. - An application built on VAF now decides which tools each account may use - with one
registered resolver instead of VAF's own user database. The per-account tool allowlist
used to be wired straight to the product's auth DB inside the dispatch pipeline, so an
application embedding VAF got a check it could neither feed nor replace.
set_account_allowlist_resolveris now part of the public interface: register one
function that answers "which tools for this account" from your own storage, and it is
enforced everywhere the pipeline runs - before the per-call authorizer, so anallow()
cannot lift an account-level ban, and inside the coding agent, where the answer crosses
into the child process as data. VAF's own product registers its resolver through the
same primitive, and the pipeline no longer imports the product's auth layer at all (a
test now keeps it that way). A registered resolver that crashes refuses rather than
quietly enforcing nothing; registering nothing means unrestricted, as before. - A tool can now say that it touches files, and the per-user boundary is installed for
it. Building something on VAF that reads or writes a user's files meant declaring who is
calling and then writing the confinement yourself, inside every tool, exactly right, every
time. Eleven built-in tools did precisely that, across five files, and five of the
twenty-two that needed it had simply forgotten. A tool now declaresfile_access = "read"
or"write"next to its identity, and the boundary is applied around it on every path -
including when something calls the tool directly, with no dispatcher involved. Declaring a
mode without the matching identity is refused when the class is defined, rather than
quietly doing nothing later: a tool that receives no identity would otherwise run
completely unconfined while looking confined. A boundary inside another can only narrow
what it inherited, never widen it. - A new example runs without needing a model at all.
examples/07_tool_caller_and_authorizer.pyshows the two pieces below in one runnable
script: running a tool with VAF's rules but no conversation, and deciding about each call.
It needs no API key, no provider and no network, which makes it the quickest way to check
a fresh install actually works. - An application built on VAF can now decide about each tool call itself. Until now the
only way to keep a tool away from someone was to leave it out entirely - a choice made once
at startup, for everybody.set_tool_authorizeris asked before every call and can refuse
it, insist on a confirmation question, or let one through without the question. It sees who
is calling and what the call would do, so decisions like "this customer's plan has no shell
access" or "not that file, it belongs to someone else" are finally expressible. Refusing is
the safe direction throughout: an authorizer that answers nothing changes nothing, and one
that crashes refuses rather than waving the call through. - You can now run a single tool without starting a conversation. Building something on
VAF that just needs a tool done - a scheduled job, a queue worker, your own agent loop -
meant either wrapping a whole chat agent around it or rebuilding the safety checks by
hand, and rebuilt checks drift apart from the real ones.ToolCalleris now part of the
pu...
VAF v0.1.0a19
Added
- Workflows can now run as the person who started them. Until now a saved workflow always
acted as the machine owner, no matter who ran it - so anything it did with memory, mail,
messages, the calendar or contacts was filed under the owner's account. The new setting
workflow_identity_injectionswitches this over: leave it atlegacyfor the previous
behaviour, set it todeclaredto have workflows carry the identity of whoever started
them. Off by default, because it changes where a running workflow's data goes. - You can now choose who a skill is for. The skill editor has a visibility setting -
everyone, only you, or named people - the same one custom tools already had. It decides who
sees the skill and who can use it, and now also whose agent may open the files bundled with
it. Existing skills keep the setting they have; a new one starts as visible to everyone. - Tools you write yourself can now be told who is calling them. VAF only handed the
current user's identity to its own built-in tools, which meant a tool added through the
framework could not tell one person from another - and the documentation could only warn
about it. A tool now states what it needs and receives exactly that, whether it ships with
VAF or you wrote it. Nothing changes for existing tools. - A completely new built-in mail client, and it is now the only one
(design docdocs/integrations/EMAIL_CLIENT.md). VAF has a real mail engine:
a per-user local mail store (SQLite with full-text search over
subject/sender/recipients/BODY, threaded conversations, encrypted-at-rest
cached bodies, configurable retention with headers kept forever) and an
RFC 4549 incremental IMAP sync engine (UID-based, UIDVALIDITY-safe, batched
fetches, IMAP IDLE push on the inbox plus periodic sweeps, native Gmail
thread/label handling). The mail window is a three-pane client: folder sidebar
with unread counts and collapsible labels, conversation view, HTML mail
rendered sanitized in a sandboxed frame with remote images blocked by default
for tracking protection, attachment download, and search over message bodies.
Mail opens offline from the local store. New permissive-licensed dependencies
(in themailextra): IMAPClient (BSD-3-Clause), nh3 (MIT),
zstandard (BSD-3-Clause), listed inTHIRD_PARTY.mdand the About tab's
third-party license list. - Mail can be acted on, not just read: read/unread, star, archive and trash in
the mail window (trash-only delete semantics, nothing is ever expunged),
reply/reply-all/forward with proper quoting and threading, compose with a
15-second undo window (the mail is held locally and can be withdrawn before it
leaves the machine), and automatic filing of sent mail into the Sent folder.
Changes apply locally first and replay to the mail server through a durable
operation queue oncemail_engine_write_enabledis on. The agent gains
reply_mail,forward_mail,archive_mailanddelete_mailtools (all
excluded from the front-office contact lane by design). - Outgoing mail is sent natively over SMTP (password or OAuth XOAUTH2), so it no
longer depends on the provider REST APIs; an ambiguous failure after the message
is handed to the server is parked, never re-sent, so a mail is never delivered
twice. IMAP/SMTP server presets added for GMX, web.de, T-Online and outlook.de
addresses. - Blocked remote images in mail can now be loaded on explicit opt-in through
a privacy proxy: the sender's server never sees the reader's address, SVG
and non-image responses are refused, and refused hosts are logged to the
security event log. - The mail client warns about suspicious (possible phishing) messages: the
conversation list shows a warning badge and the reader a warning banner on mails
the agent's phishing filter would hide, so nothing dangerous is silently
surfaced only to the human. - The mail client shows which mail has already been answered: a reply marker with
the date in the reader and a marker on answered conversations in the list, so a
reply is not accidentally sent twice. - Gmail-style categories: a category chip on Promotions/Social conversations and a
relabel picker in the reader. Relabeling also teaches the category - VAF
remembers a rule for that sender and labels every other mail from them, past and
future. All of it is local; nothing is changed on the mail server. - Mail Composer: the compose window can now write the reply for you, or rewrite
what you typed. The window is wider now, with the message on the left and the
Composer beside it on the right: a chat where you say what the reply should say,
ask for changes ("shorter", "now more formal") and it refines what it just wrote,
with Stop while it writes and an Undo that restores your text exactly. Draft and
Rewrite sit in the footer next to Send. It puts a suggestion in the text field and stops there, so nothing is
ever sent without you reading it and pressing Send, and it tells you how much of
the conversation it actually read. It refuses to draft from a message flagged as
possible phishing, and it can use what VAF remembers about you when you say what
the reply should be about. New: a light-mode button in compose, so you can read
the draft the way the recipient will. Admins can turn all of it off or change how
much of a thread it reads (mail_composer_*settings). If the local model is not
running yet, it is started for you instead of asking you to do it, with a note
while it loads. Admins can additionally allow the Composer to quote older mail
from other conversations (mail_composer_mailbox_search_enabled, off by default).
The Composer knows what VAF remembers about you the same way the chat does, every
time rather than only when you phrase a request a certain way, and it now writes a
complete message (greeting, the point, a closing) in the language of the mail it is
answering instead of a single bare sentence. It also tells apart which messages in
a conversation you wrote and which the other person wrote, and writes in YOUR tone
by following how you replied earlier in the same thread; if there is nothing of
yours to go by it says so and stays neutral instead of inventing a style. - The mail window's gear opens a built-in account panel: see your mail accounts,
connect a new Gmail/Microsoft or IMAP account (with a Test button), reconnect,
verify a connection, rename an account, toggle auto-sync, and remove an account.
Removing an account that also powers your Calendar keeps it connected for
Calendar. - Library embedders can now set the agent's persona directly:
Agent(system_prompt="...")replaces the on-disk "Soul" in the system prompt
for that instance only, while the engine's technical instructions are kept.
Previously the persona was a global on-disk file with no public API.
Documented in EMBEDDING.md with a runnable example (examples/06_custom_persona.py). - EMBEDDING.md now has a "Sub-agents as a library" section explaining that
sub-agents run inline in a bare library process, while their windowed/async
modes and the coder's sandbox need the full product's services. - The skill scanner module gained a dependency-free content-hashing facility
(SHA-2 and SHA-3):hash_bytes/hash_textand a deterministic,
tamper-evidenthash_skill_folderfingerprint, on a strong-only algorithm
allow-list. Available for later integrity checks; not yet wired into the
scan result.
Changed
- The wording around mail image loading was corrected in the documentation. It
protects the reader's browser identity (no cookies, referrer or browser
fingerprint reach the sender) but it does NOT hide the reader's IP address, and
it does not stop open-tracking: a tracking pixel's address is unique per
recipient, so loading it still tells the sender the message was opened and when.
Blocking images by default is the protection.docs/integrations/EMAIL_CLIENT.md
now states both halves for privacy reviews. - Server-side mailbox changes (read/unread, archive and delete replayed to the
mail server) stay behindmail_engine_write_enabled, still off by default. Note
that SENDING is deliberately NOT gated by it: a queued mail must be able to
leave, so the agent'sreply_mailandforward_mailverbs are live regardless
(each still passes the high-risk send gate). - Removed the dead Apple OAuth lane (provider entry, config keys
email_oauth_apple_client_id/_secret, admin settings inputs and their
locale strings, a dead sign-in URL branch in the setup wizard): Apple
offers no OAuth mail API. iCloud Mail continues to connect via IMAP with
an app-specific password, unchanged.
Removed
- The
batchtool is gone. It was listed as a Coder tool but could never be called: it was
not registered for the main agent and not part of the Coder's tool set either. What it
offered - running several tools at once - is what the agent already does in a single turn. - The old mail dashboard and the separate email setup wizard are gone. Everything
they did is in the mail window: reading mail, and the account panel behind its
gear for connecting, reconnecting, testing, renaming, auto-sync and removing
accounts (including the IMAP and SMTP server overrides the wizard offered, and
the same hiding of sign-in buttons for providers an admin has not configured).
The Overview security page reads its mail data from the new engine now. - The old mail REST endpoints under
/api/emailare gone (message list, search,
body, categories, category change, sender-rule backfill and the per-account
sync). Mail is served from/api/mail. Sign-in and account management under
/api/emailare unchanged, because Calendar and the Connections page use them. - The 30-minute background mail sync was removed. The mail engine's own sync
(continuous, with push updates) is now the only one, so...
VAF v0.1.0a18
Security
- The ephemeral sandbox fallback (used when the persistent vaf-sandbox
container is unavailable) now carries the same hardening as the persistent
one: all Linux capabilities dropped, no-new-privileges, and its own isolated
bridge network instead of Docker's default bridge - previously two
concurrent ephemeral sandboxes (for example of two different users) could
reach each other over the shared default bridge. - Sandbox pip installs are now temporary and per-run: packages requested via
the tool's packages parameter (and even in-code pip installs, redirected via
PIP_TARGET) land in the run's private directory and are deleted with it, so
nothing accumulates in the shared sandbox container across runs or users;
package specs are validated before reaching the shell and the shared pip
cache no longer grows.
Fixed
-
The desktop window no longer navigation-loops between the dashboard and the
login page. With an expired auth cookie but a still-valid token in browser
storage, the server-side route gate and the login page's "already logged in"
check disagreed forever: the gate redirected to /login, the login page bounced
back, and the window reloaded in a tight loop until the browser engine
throttled navigation and the UI froze. The login page's bounce decision now
uses the same authority as the server gate (the cookie only), stale
browser-storage tokens are cleaned up, and a circuit breaker stops any future
redirect ping-pong after two bounces instead of trapping the user. The root
desync is also removed on the backend: the auth cookie's lifetime is now
always derived from the token's own expiry instead of hardcoded 30-day
values, so a cookie can never outlive the session it carries. -
The Linux desktop window no longer stays dead after a GPU-driver crash. Two
incidents aborted the whole tray process from inside the host-side GPU
compositing path (once inside the NVIDIA GL library while resizing the window),
which the existing renderer-only crash recovery cannot catch. VAF now disables
the NVIDIA driver's threaded optimizations for the tray process on NVIDIA hosts
(GPU acceleration and vsync unaffected; export__GL_THREADED_OPTIMIZATIONS
yourself to opt out), and the Linux shell launchers run the windowed tray under
a bounded restart supervisor: an abnormal exit restarts the app within seconds
(at most 3 times per 10 minutes), while a normal quit or Ctrl+C never does. -
Chat no longer shows phantom date separators around invisible rows. Live
system-step notes are spliced into the message list with the current time;
when one landed inside an older conversation, the day-separator logic compared
against the invisible row and painted misordered "day ended / continued"
pairs (e.g. July 21 / July 22 / July 22 / July 21) between two messages from
the same day. Separators now only compare messages that actually render.
Added
- Installed skills are re-scanned periodically (post-install tamper
detection). The security scanner already checks every skill at create,
update, upload and editor-save time (verified end-to-end against synthetic
malicious skills); what it could not catch was a skill whose files change on
disk AFTER installation. A background worker now re-scans all installed
skills everyskills_rescan_interval_hours(default 5,0disables),
updates their persisted scan results, and raises a security event when a
skill's risk level worsened. Blocked installs and admin overrides are
recorded as security events too, so the Overview's skills panel shows real
numbers: skills by risk level as a live donut, threats blocked today, admin
overrides, re-scan alerts, the riskiest skills, and the time of the last
full scan - an installed high-risk skill turns the protection banner red,
a medium-risk one amber. A skill that re-scans as high-risk after install is
automatically QUARANTINED: it disappears from every agent path (skill list,
the read/list tools, the system prompt) until an admin resolves it on the
dashboard - either delete it, or, if it is a false positive, restore it,
which requires re-entering the admin's 2FA code so a stolen session alone
cannot re-expose it to the agent. The protection banner now names the actual
worst cause (e.g. "High-risk skill installed", "Channel in permissive mode")
instead of always attributing a red state to the audit chain. Clicking a skill
in the panel opens a detail view that re-scans it live and shows exactly WHY
it was flagged (the matched rules with category, message and snippet) next to
the resolution actions. A medium-risk skill can be acknowledged (2FA): it
stays visible and still shown as medium, but the banner returns to green -
the admin has reviewed and accepted it. When security events were recorded
today (blocked access, rejected senders, skill blocks), the Log Files section
shows a pulsing count that nudges the admin to open the security log, and the
Logs button in the main sidebar shows an unread notification dot for new
security events, so a blocked login or a quarantined skill is noticed without
the window being open. Both the sidebar dot and the in-window log badge are
unread-based against a shared marker: they clear once the admin opens the
security log and re-light only on a newer event, rather than sticking as a
permanent count. - Logs Overview: the last three panels are now live. "Background agent" shows
the proactive agent per user across all scopes (active run, the question it
is waiting on with channel and nudge state, time since the last run with the
tools it used, and the recent asked/replied/done/declined question history) -
an admin oversight view served by a new admin-gatedGET /api/thinking/status.
"Recent supervised activity" lists the newest hash-chained tool actions of
the selected day with per-user attribution, derived from already-loaded audit
data. "Active supervised units" shows the live sub-agent watchdog (agent
type, owner, runtime, heartbeat) with a stuck marker for stale units. - Security: the supervisor watchdog endpoints are now caller-scoped. Previously
any authenticated user could list ALL running sub-agent units (including
other users' task text) and kill them by task id; now non-admins only see and
cancel units of their own sessions, while the admin keeps the full attributed
watchdog view. - Fixed a test-isolation gap where a test suite wrote synthetic scope
directories into the real thinking-requests store; the debris surfaced as
phantom users in the new Background-agent panel and has been cleaned up. - Security event log: blocked access attempts are now recorded and visible.
Rejected connection attempts (non-LAN IPs, requests without or with invalid
authentication, rejected WebSocket handshakes), failed login/2FA attempts,
and unauthorized messenger senders (Telegram/WhatsApp/Discord messages
dropped by the pairing gate, recorded with channel, sender id and time)
are written to a newsecuritylog (visible in the Logs window's file rail)
and to a structured store behind an admin-only API. Never logs passwords,
codes, or tokens; a flood throttle keeps hammering attackers from growing the
log unboundedly. In the Overview dashboard every protection module is now
clickable: a detail popup shows the module's live data - for the firewall
module the deflected attempts of the day (with counts for blocked requests
and failed logins), the live-inspected Docker network isolation - every VAF
container with its network and published ports, shown independent of LAN
mode; a port bound beyond loopback (LAN-exposed) flips the module and the
banner to the warning state - plus a button that jumps straight into the
security log's history; for the code sandbox the live-inspected container hardening; for the
audit chain the verification facts; for user isolation the enforcement mode
(fail-closed in server mode), memory-DB reachability, and admin-level
operational metrics: a live-measured RAG search latency (real pgvector
distance query), the isolated per-user memory stores by username with their
entry counts, the total memory-DB size, and the per-user folder totals (how
many isolated folders each user owns and their summed disk usage, with
legacy folders in an explicit unassigned bucket) - an unreachable memory
database now shows as an amber warning on the dashboard instead of failing
silently. The channel-perimeter module answers "is someone unauthorized
talking to my bot?": per messenger it shows enabled state, ingress mode,
paired-sender count, last activity and today's rejected count, with the
rejected senders listed in the detail popup - and an enabled channel running
in permissive mode turns the module and the banner amber. The audit-chain
popup additionally breaks the day's secured events down per user, making
visible that all users share one tamper-evident chain the admin verifies.
The phishing-shield module shows how many synced messages are flagged and,
per flagged mail, WHY: the matched heuristics (provider spam category,
punycode sender domain, urgency/social-engineering language, executive
impersonation via free-mail, phishing wording patterns) as readable chips
with the score, plus a note that flagged mail is hidden from the agent's
tools but never deleted. The guardrails module shows what actually governs
the agent: the gate switches (plan gate, confirmation gate, incident gates,
channel tool restrictions - with a visible warning when loosened), the live
tool inventory grouped by permission level (read/write/dangerous/system,
admin-only, channel-blocked), and - previously invisible anywhere - the
standing permissions from the trust store: which tools carry a permanent
"always allow" and which directories are trusted. With that, every
protection module o...
VAF v0.1.0a17
Added
- VAF is published to PyPI with every release. Library users can install the
framework withpip install --pre vaf(--preis the recommended spelling while
VAF is in alpha) and update it withpip install -U --pre vaf; the desktop/server installers
keep working exactly as before. Publishing is tokenless (PyPI Trusted Publishing),
and releases can be rehearsed against TestPyPI via a manual workflow first. The
embedding guide gained a "Choosing a backend: local vs API" section, and the README
now opens with a library quickstart.
Changed
vaf updaterefuses to run outside a VAF source checkout. On a pip-installed
VAF (or a folder that is not a VAF source tree) the git self-updater now points to
pip install -U --pre vafinstead of offering a git conversion that could have
damaged the Python environment.- VAF's packaging moved to the modern Python standard (
pyproject.toml). The
install commands do not change (pip install -e ., extras likevaf[server]and
vaf[all]stay exactly the same), the license is now declared in the standard
machine-readable form, and theLICENSING.mdterms file ships inside the package. - Installing VAF with pip no longer runs platform setup scripts. A plain
pip install -e .used to silently trigger macOS/Windows provisioning scripts in
some legacy flows; a pip install is now strictly a Python-package install. The
desktop installers (install.sh/install.ps1) are unaffected and keep doing the
full provisioning. - The
vafcommand now works on a minimal library install. With only the base
dependencies installed,vaf --version,vaf promptand other light commands run
normally, and commands that need optional components (for examplevaf run --web
or the Discord bridge) explain which extra to install (such as
pip install "vaf[server]") instead of crashing with an import error.
Fixed
- The document editor's page stays white in dark mode. The DOCX editor's sheet is a
rendering of real paper, and Print and PDF show exactly what is on screen, so it keeps its
light appearance while the app around it goes dark. - A tool the local model tried to run no longer silently vanishes. With the built-in
local model, some tool calls (for example adding a calendar event) came out in a format the
app recognised only for one specific model family. For every other local model the call was
left as plain text in the chat and simply never ran, so nothing happened and no error was
shown. That format is now recognised for any local model. - The app no longer runs its window on native Wayland, which could crash it on Linux.
VAF has always meant to run its window through XWayland (native Wayland conflicts with the
browser engine and, with the GPU shared in-process, could freeze and then kill the app).
That safeguard silently never applied on KDE and GNOME Wayland desktops, because those
sessions set the display server themselves and VAF only filled in a value when none was
set. It now sets it deliberately. If your system has no XWayland, VAF leaves your session
alone instead of starting with no display at all, andVAF_ALLOW_WAYLAND=1keeps native
Wayland if it works well for you. The choice is written to the startup log. - A slow or unreachable speech provider can no longer freeze the whole app. The
ElevenLabs voice/model catalog was fetched in a way that blocked the server's event loop,
so opening Voice settings with a slow, unreachable or exhausted account could stall every
request and the live connection for everyone until it timed out. It is now fetched without
blocking, a failure is remembered briefly instead of being retried on every redraw, and two
simultaneous requests share one lookup. The same blocking pattern was fixed in the email
account verification and the Telegram dashboard. - The local model is no longer unloaded while it is still working. When a longer task was
running and you had not typed for a while, the app counted you as away and freed the model
mid-task. The work then stalled behind failing retries and looked like a freeze, and two
competing attempts to load the model again could collide. The app now keeps the model
loaded whenever something is actually running, whether that is your message, a background
helper or a live call. Loading is also serialized, so two parts of the app can no longer
fight over it, a model that is merely still loading is waited for instead of killed, and
stopped model processes are cleaned up instead of lingering. - A finished workflow is no longer labelled FAILED when it succeeded. Workflows that run
in their own process reported their outcome to the app, but the outcome itself was dropped
on the way to the browser, so the panel fell back to showing a failure. Every step showed a
green tick, the document was written, the chat said it worked, and the panel still said
FAILED. The result now arrives intact, and so does the live output those runs stream. - The workflow panel no longer gets stuck showing a run that has long finished. If the
connection to the browser dropped while a workflow was running, everything that happened
afterwards was lost, and the panel kept showing the last thing it had seen, with no way to
close it. It now asks the app what the real state is when the connection returns, when you
come back to the tab, and after a reload. If the run is over, the panel says so plainly and
closes instead of inventing a result. It also has a close button now, which matters on
phones where the panel covers the whole screen. - Terminal windows opened for background work now close again on Linux. A window opened
for a sub-agent or a workflow announced that it was closing and then stayed on a shell
prompt forever, because a shell was started on top of the finished task. Windows now close
when the work succeeded and stay open when it did not, so an error can still be read, and
--no-auto-closekeeps working as documented. On macOS this also depends on your Terminal
profile setting for what happens when the shell exits. - No stray terminal window when the browser connection drops. Work started from the app
ran without a visible window only while a browser was connected. If the connection dropped
during a long run, the next helper opened a terminal window on the desktop, and the app's
Stop button could not reach it. The decision no longer depends on a browser being attached
at that moment. - A busy workflow no longer floods and kills its own connection to the browser. While a
research step was running, its progress animation was forwarded to the browser frame by
frame, hundreds of times per second. That was enough to drop the live connection in the
middle of a run, after which the Workflow Runtime panel never advanced again and sat at
the last state it had received. Progress output is now filtered and rate-limited before it
is sent, in every place that forwards it, and the animation that produced it is no longer
started when nobody is watching a real terminal. The separate workflow terminal keeps its
colours and live display. Long, quiet steps are also no longer mistaken for stuck ones:
the "no output" watchdog now allows a heavy step the time it is actually given. - A workflow that hands work to a background helper is no longer reported as crashed.
When a workflow step passes its work to a helper (writing a document, researching a
topic), the workflow pauses and waits instead of finishing. Three of the places that read
the result knew only "done" and "broken", so a perfectly healthy run was announced as
failed while the work was still being produced, and the assistant apologized for a crash
that never happened. A paused run is now recognized everywhere as still running, in chat,
in the separate workflow terminal and in automation reports. Paused runs also remember
which conversation they belong to, so switching conversations no longer discards them, and
a run whose helper was the final step is now completed automatically in the app instead of
only in the terminal client. - Telegram stays responsive while a voice message or file is transferred. Sending or
receiving audio, voice notes and documents held up the Telegram connection for as long as
the transfer took, so other messages in the same chat had to wait behind it. Transfers now
run out of the way of the message loop.
VAF v0.1.0a16
Added
- The agent recovers when a call misreads your voice, instead of ignoring you. In a
room with other people, the voice check can briefly lose track of who is the owner (a
guest speaking resets the in-call "this is the owner" bridge), and your own short command
could be treated as a stranger's and silently dropped. Now, when a voice it cannot place
is clearly talking TO the agent, it asks "did you mean me?" in the language being spoken;
if your reply confirms your voice, it picks straight back up (and carries out what you
asked). It never just goes quiet on your answer: if you say yes but it still cannot place
your voice, it says so and asks you to confirm on screen or via your messenger so it can
learn your voice and misread you less often. It never acts on an unconfirmed voice,
and your voice profile is only ever updated from a confirmation you make yourself. The
agent also no longer goes silent on a turn that was clearly addressed to it, and you can
arm "talk to the other person" with a plain spoken command even when the local model is
being unreliable. - In a voice call you can now ask the agent to talk to the other person with you. By
default the agent overhears the people around you and stays quiet, which is right when
you are just talking to someone else. When you actually want it to take part - "answer
her", "talk to my mother, she is asking you something" - it now does: it starts replying
to the guest directly IN THEIR OWN LANGUAGE (even if your call started in another), and
greets them. While it is talking with the guest it now follows the actual back-and-forth
in the room - it is given the shared, spoken-aloud conversation (everyone's turns, in
order, in whatever languages are spoken) so it can hold a real multi-person conversation
instead of replying to each line in isolation; the guest still never sees your private
information, only what was said out loud after you brought them in. It stays in that mode
until you tell it you are done ("thanks, that's all"), the conversation goes quiet for a
while, or the call ends. - The voice agent no longer refuses to speak a language it actually knows. On a call it
now replies in the language it is being spoken to, or the language you ask it to use,
instead of being pinned to one language by its instructions - so asking it to talk to
someone in another language works, rather than getting "I'm not fluent in that." Throughout, the guest can only be spoken to - the agent never shares your
private information and never runs an action on a guest's request. On a call with more
than one person the agent is also given the situation (who is present and the language
being spoken) so it responds more naturally. - In a voice call, the agent now understands your answer to its own question. When it
asks you something, your next reply is treated as the answer even if it is brief ("yes",
"at three"), instead of a stray remark; if you ask it to repeat, it re-asks the same
question in your language. It is aware of the room: one-on-one it takes your reply
directly; with other people around, a brief reply still counts, but a longer one is only
treated as your answer when it is on-topic, so it does not mistake side-talk for an
answer. Someone else can get a brief spoken reply when they say something clearly
on-topic, but their words are never taken as your answer and can never trigger an action.
Changed
- Choosing the live-call voice model is now a dropdown, not a text field. For a
dedicated local model you pick from your already-downloaded models (the recommended
Gemma 4 E4B is always offered and is fetched automatically when you select it); for an
API provider you pick from that provider's models, with a refresh button to pull the
live list, instead of typing a name by hand. Downloading local models stays in the AI &
Model settings, and a value saved by an earlier version keeps working. - The agent stops asking "was that you?" on every unrecognized voice. The speaker
confirmation now fires in two more targeted cases: promptly when a voice it does not
recognize CLAIMS to be you ("I'm NAME") - a spoofing check that asks you to confirm -
and, far more rarely, on a borderline recording of your OWN voice (for the adaptive
re-recognition). A stranger simply talking near the mic no longer triggers the
question. The claim detection is multilingual.
Fixed
- The voice agent no longer speaks a stray fragment of its own reasoning. Two cases are
now covered: a model's thinking wrapped in a tag is always removed before anything is
spoken - not only<think>but the variants other models use (<thinking>,
<reasoning>,<scratchpad>, and more), whether the tag is closed or the stream was cut
off mid-thought; and when a weak model leaks its thinking as PLAIN text (no tag) starting
with a connective like "But we need to check: the user might be...", the filter now looks
past a leading "but/so/and/well/okay" and drops the fragment to a short "say that again"
instead of reading it aloud. - The voice agent recognizes you from the first words of a call. The voice-recognition
model is now warmed up the moment a call opens, instead of loading lazily on the first
thing you say. During that cold load you were briefly treated as an unknown speaker -
formal replies, a needless "did you mean me?", and it staying quiet on side-talk - until
the model finished loading; now you are placed correctly from the start. - The agent no longer reads its own thinking out loud in a voice call. When the local
model announcing a delegated result leaked its internal reasoning into the text (for
example while it was stuck), the voice could read that<think>reasoning aloud. It is
now stripped before anything is spoken or stored, so you only ever hear the actual answer. - A voice-call reply in another language is now spoken by that language's voice. When the
agent answers in a different language than you spoke (e.g. Turkish in a German conversation),
it uses a matching voice for that language instead of speaking it with your usual voice -
whenever a voice for that language is available (a downloaded local voice, or any cloud voice
provider). Otherwise it stays on your call voice. - Your spoken language is no longer mis-detected at the start of a voice call. The call
now tells the speech-to-text service the language from your profile up front, so a short
first sentence in German is not transcribed as French (or another language) - which had
the agent constantly asking you to repeat. It still notices if you genuinely switch
languages mid-call. - The agent stops mistaking you for a stranger mid-call on short replies. In a voice
call, once it has clearly recognized your voice, a brief or noisy follow-up ("ja", "at
three") no longer flips you to an unknown speaker and locks you out of acting - your
recognition stays "sticky" for a while after it verifies you. A clearly different voice
still switches immediately, so it does not weaken the guard against someone else acting
as you. - The local voice model now starts on Apple Silicon Macs. The recommended local
German voice model (Gemma) could fail to start on macOS/Metal with only "Server failed
to start" and an empty log. A fallback for the quantized cache existed but never fired:
the non-debug log level wrote an empty log, hiding the very error the retry looked for.
The fallback now runs whenever the first start attempt dies and an alternative exists,
non-debug logging captures fatal errors again, and the retry scans the full log. So an
empty or low-verbosity log can no longer silently disable it. - The one-time "what's new" alpha notice no longer reappears on every Settings close.
Closing Settings refreshed your time format and name, but also reset the seen-version
gate, which re-showed the one-time notice each time you left Settings. It now refreshes
those without touching the gate. (A deeper per-user workspace persistence quirk can
still surface it once per app start; that root cause is tracked separately.)
VAF v0.1.0a15
Added
-
Voice call, the reflex features speak your language: the agent's "did you mean me?"
clarification and the cue words behind it now ship in about 35 languages (generated
from English, so unlisted languages fall back to English), and it detects being spoken
to across the major languages, not just German and English. What it hears (speech
recognition) and what it says (the spoken reply) were already multilingual; this widens
the small fixed phrases in between. The proactive chime-in was also tuned so it actually
speaks up on genuinely on-topic overheard talk instead of staying silent. -
Voice call, you can interrupt the agent: while the agent is speaking you can now
just start talking and it stops and listens instead of making you wait for it to
finish. The microphone is opened with echo cancellation so it
does not hear its own voice, and it only yields to a real, sustained interruption (a
brief noise will not cut it off). For now this covers interrupting while it is
speaking, on a web call; interrupting while it is still thinking, and having it resume
where it left off, are planned next. -
Voice call, the agent chimes in on interesting talk: during a live call the agent
now keeps a short rolling transcript of what it hears, and when someone else in the
room says something that matches your configured interest topics, it can briefly and
naturally chime in with a grounded remark instead of staying silent. It never invents
a reason to speak (a chime-in must be grounded in your topics, and the agent may still stay
silent), never chimes in while it is busy with a task, and does not repeat itself. One
simple dial,voice_awareness_activity(quiet..active), sets how readily it joins in
(at the lowest setting it only listens); it behaves calmly on its own when you are in a
conversation with someone else and more readily one-to-one, without you managing any
modes. It also asks "did you mean me?" when an unclear speaker says something like "can
you hear me?" and it cannot tell whether it was addressed. Tool actions stay yours
alone (a guest still cannot make it do anything), and your private context is never
used to chime in for a guest. -
Per-speaker language hint for cloud STT: the shared speech client now caches
the language the cloud provider already returns and passes it as a hint on the
next transcription (a more precise, cheaper call), instead of running a local
model to pre-detect it. The cache is keyed per speaker (the web mic uses the
user's scope, so it stays user-isolated), and to catch a mid-conversation
language switch it re-detects hint-free every few turns and always refreshes from
the actually-detected language. No new dependency and no pre-call overhead. The
hint is language-agnostic (normalized to ISO-639-1, ISO-639-3 mapped, locales
reduced to base) so it works across every supported language, and the Veyllo lane
auto-detects withmulti(automatic code-switching) when no language is pinned. -
Veyllo speech-to-text: Veyllo is now selectable as a cloud STT provider
(speech_stt_provider = veyllo, modelveyllo-transcribe), using the same
API key and base URL as the Veyllo chat/vision provider. The first time a Veyllo
key is added (at onboarding or later in Settings) while no STT provider was
chosen, Veyllo becomes the default STT lane, and it always falls back to the
local engine on any failure (no internet, empty credits, timeout); an explicit
later choice (local, OpenAI, ElevenLabs) overrides it. Theveyllo-transcribe
audio model is filtered out of the chat-model dropdowns. -
Data Explorer legibility: deleting a chat now saves the chat's title
into the surviving workspace folder, so folders left behind by deleted
chats keep their human name instead of showing a raw internal folder name
(an explicit rename always wins; folders orphaned before this change keep
their old names). The badge on such folders now reads "chat deleted"
instead of the jargon "orphan", the list sorts live chats first and
deleted-chat folders to the end, and a new info button in the header
explains the folder colors, the badge, and the current-chat dot. The
window now keeps a fixed height with a refined scrollbar instead of
growing and shrinking with the workspace count, and each tile counts
its folders too (a workspace holding only a subfolder no longer reads
"0 files" as if it were empty). -
Data Explorer search: a search field in the "My Workspaces" header
filters workspaces by name instantly and also searches file names and
text-file contents inside them (server-side, scoped to the user's own
workspaces, bounded per workspace); matching files are shown under each
workspace tile with a content snippet in the tooltip. -
Three new developer docs make the existing machinery usable from
outside, each verified against the code: an observability guide
(structured tool/gate events via the engine's event sink, plus the
machine-readable NDJSON mode of the scripting CLI for integrating VAF as a
subprocess from any language), a debugging guide (the complete map of log
files, how to redirect them, what the debug switch does and does not
silence, how to read a session file), and an engine reference (the
constructor, lifecycle, turn and tool-dispatch contracts, and concurrency
rules of the advanced embedding class). The embedding guide gains a
security-posture section (what needs Docker, what runs on the host, what
ports are and are not opened), pointers to the other extension points
(workflows, skills, MCP servers, the update-surviving custom-tools folder),
and an honest note on custom OpenAI-compatible endpoints. Key engine
methods now carry docstrings. -
Persistent conversations, richer events, async and marker constants on
the library facade. Four additions for developers embedding VAF:
agent.save_session()plusAgent(session=<id>)persist and resume a
conversation across process restarts (idempotent updates, loud failure on
unknown ids, tenant-ownership check underuser_scope; runnable chatbot
example included);vaf.markersexports the special return-value strings
(SYSTEM_LOG_ONLY,GENERATION_STOPPED, ...) as constants with a CI
guard pinning them against the engine source; the structured event sink
growsduration_msand a dispatch-levelokflag ontool_endplus new
llm_start/llm_endevents with token usage on API providers (attached
across backend swaps; facade shortcutagent.on_event(cb)); and
await agent.run_async(...)runs a turn in a worker thread for
event-loop applications - documented honestly as a thread-executor
wrapper, not native async. -
Multi-tenant embedding:
Agent(user_scope=...). An application
embedding VAF can now assert which end user a conversation belongs to
with one parameter. The value is validated as a UUID at construction
(a bad scope fails loudly instead of silently operating on the machine
owner's data), the account username is resolved together with the scope
(never the admin fallback), the identity is bound before the system
prompt is built and re-asserted on every turn, and memory/reminders/
per-user files then key on that scope with the product's fail-closed
filters. The embedding guide gains a "Multi-tenant embedding" section
spelling out the trust model and the hard limits (one tenant per
process, the machine-global trust store, shared on-disk config, no
reliance on database-level isolation yet), and now states honestly that
a bare unscoped agent acts as the machine owner in local mode. Also
fixed: the sandbox tool now receives the user scope from the dispatcher
(spoof-proof direct assignment), so its per-user container work
directories key on the calling user (previously every main-agent run
shared one prefix). -
One provider registry instead of eleven copies. The LLM provider set
and its endpoints now live in a single source of truth
(vaf/core/provider_registry.py); the backend factory, the coder's
endpoint map, live model discovery (both copies), the vision-capability
check (previously three manually-synced copies that had drifted apart) and
the CLI settings menus all read from it, guarded by new CI sync tests plus
a factory-pinning test that locks today's behavior byte for byte. Four
real drift bugs died in the process: the CLI provider menus did not offer
Veyllo at all, the provider-coverage test itself skipped Veyllo, and two
of the three vision checks did not recognize Veyllo models as
vision-capable. Provider-specific behavior (DeepSeek reasoning fields,
OpenAI reasoning-parameter gating, Veyllo tool-call-id handling) stays in
gated code paths, unchanged. -
Per-instance tool registration on the library facade. An embedded
Agentcan now be handed extra tools directly:agent.add_tool(MyTool())
before the first run registers aBaseToolfor that instance only - no
pip package, no file drop-in. Late or invalid registrations raise clear
errors, and the facade CI guard pins the behavior. -
A developer FAQ. Short verified answers to the questions developers
actually hit: Docker requirements, the confirmation-gate error string,
thread-safety, the engine's return contract, log redirection, custom
endpoints, the tool lanes, and what the alpha stability promise covers. -
A runnable examples/ directory. Five self-contained artifacts for the
embedding surface: the five-line quickstart, streaming plus the structured
event sink, driving VAF as a subprocess via the NDJSON output format (the
pattern for non-Python integrations), per-instance tool registration, and
a complete pip-installable custom-tool package using the entry-point
mechanism. A CI test keeps every
example compiling and the example tool loadable, and the license-header
check now covers the ex...
VAF v0.1.0a14
Fixed
- The a12 updater self-heal never actually fired - fixed for real. The
live verification on a Mac caught it: the updater's git wrapper strips
its output, the dirty-line parser read paths at a fixed offset, and the
lockfile churn was misread as a real user edit - so updates kept
aborting despite the a12 fix. The parser is position-independent now,
the restore uses exact paths, and the test suite gained an end-to-end
test through a real git repository (the fixture shape that would have
caught this). Stuck installs still need the one-time
git checkout -- web/package-lock.jsonbefore their old updater can
reach this version.
VAF v0.1.0a13
No functional changes: a verification release. Updating a12 to a13
exercises the fixed updater end to end on a real install - the npm
lockfile self-churn restore, the forced tag fetch and the renormalized
line endings must carry an update through without any manual step.
VAF v0.1.0a12
Fixed
vaf updatecan no longer deadlock itself. Three causes found on a
Mac that sat on a7 while four newer releases existed: (1) the updater's
own npm step (and the first-run frontend install) rewrote
web/package-lock.json, and the dirty-tree pre-check then refused every
future update - npm runscinow (never modifies the lockfile), and the
pre-check restores updater-managed files instead of aborting on them;
real user edits still abort. (2) Release tags that were ever recreated on
the remote madegit fetch --tagsfail mid-update with a rollback - tags
are fetched with--forcenow. (3) Two shell scripts were stored with
Windows line endings despite theireol=lfattribute, so a fresh checkout
started dirty - the repository is renormalized. If your install is
currently stuck on an old version, rungit checkout -- web/package-lock.json
in the VAF folder once, thenvaf update.