VAF v0.1.0a20
Pre-release
Pre-release
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
public interface, and it is the exact same one the agent itself uses: the same permission
rules, the same confirmation question, the same identity handling, the same time limits.
Documented in the embedding guide.
Changed
- An embedded agent created with the machine owner's account key now reads the
owner's profile instead of an empty per-key workspace. Keys belonging to anyone
else are unaffected and still never resolve to the owner. - The repository root is quieter. The contributor, conduct and security documents
moved into.github/, where GitHub still finds them, and the commercial terms and
third-party inventory moved todocs/legal/. The separateruff.tomlis gone; its one
setting lives inpyproject.toml. Nothing a user runs changed: the installers, the
start scripts and the licence files stayed exactly where they were. - The default look is monochrome. The terminal theme is a black-to-white ramp
now, so nothing in the frame competes with what the agent is actually saying, and
the agent's white dot is the brightest thing on screen. Success, warning and error
keep a colour, muted enough to belong but distinct enough to still be read at a
glance. Both terminal interfaces share the theme, so both change together, and
whichever one you pick is the one you get next time. The light theme is gone:
it sat at the end of the cycle, so pressing the theme key often enough landed
on it and stuck, and on a white background the agent's white mark and every
white accent simply disappeared. Anyone who had it selected comes up on the
default instead. - The terminal app's bottom row no longer cuts itself in half. The key hints and
the context usage share one line and do not fit together below roughly 120
columns, and the overflow used to be resolved by clipping - mid-label, so
/exitappeared without itsQuitand on a busy context bar evenHelpwas
gone. Now the context bar gives up its token counts first and hint pairs drop
whole, so whatever is on the line is readable. - The terminal transcript follows a streaming answer again. It scrolled when a
message was mounted but not while one grew, so a long reply ran on below the
fold while the view sat on its first lines. It now follows the text - and
stops following the moment you scroll up to read something, instead of
yanking you back down. - Long command descriptions in the terminal help wrap under themselves instead
of continuing beneath the key column, where they read like another command.
The session list, the start block and the mark itself also survive a narrow
window now rather than losing their last characters. - The mark in the terminal's start block is now the Veyllo logo converted to
terminal art rather than drawn in solid blocks. - The README opens on the mark rather than on an ASCII wordmark, and shows the
terminal and the web UI as one image above the installation steps.
The desktop and terminal modes each show what they look like. - Workflow steps now pass the same security checks as chat. A step used to run its
tool directly, skipping every rule a chat turn answers: admin-only tools, channel
policy, the per-user tool selection, and the veto an application embedding VAF can
attach. Steps now run through the shared dispatch pipeline, so all of those hold
inside workflows too. Three deliberate differences remain: the confirmation question
stays off for workflows (they run unattended - taking that away is its own decision),
the heavy sub-agent steps of a temporary workflow still run as child processes outside
the step pipeline (their internal tools remain constrained separately), and a step
whose tool crashes now fails that step and lets the workflow branch, instead of
aborting the whole run. Malformed step arguments are now refused cleanly before the
tool runs. The existing rollback switch restores the entire previous behaviour if
needed. - A workflow now runs as the person who started it, by default. Until this release a
saved workflow always acted as the machine owner, whoever ran it: its files went to the
owner's folders, its GitHub calls used the owner's account, and anything it created was
filed under the owner. That was switchable before and is now the default. What changes in
practice is that 47 further tools finally learn who is running the workflow - files,
GitHub, automations, skills and reading messages among them - and none of them lose an
identity they already had. If a workflow of yours relied on reaching the owner's files by
an absolute path or a folder name likeDesktop/..., that step will now be refused for
anyone who is not an administrator; the settingworkflow_identity_injectionset to
legacyrestores the previous behaviour. - Workflows carrying an identity now pass it to far more tools. The setting introduced
in 0.1.0a19 only reached a fixed list of tools; set todeclaredit now asks each tool
what it needs, the same way a normal chat message already did. In practice that means
files, GitHub, the browser, skills and automations finally know who is running the
workflow, and mail learns the person's role rather than only their name. Nothing loses
access - every tool that had an identity before keeps exactly the one it had. Still off
by default (legacy).
Removed
- Three launcher scripts that nothing called.
stop_vaf.sh,launch_vaf.scptand
Start VAF.commandwere not referenced anywhere: not by the installers, not by the
Linux desktop entry or the systemd service (which stops VAF by signalling the process),
not by any documentation. On macOS the installers already build a properVAF.appthat
launches from wherever the project actually lives, whilelaunch_vaf.scptstill assumed
it sat in~/VAF. Stopping a persisted server is documented separately and unchanged.
Fixed
vaf run --webnow says that it changes the interface. The flag has always
routed to the previous prompt interface, because that is where the web server and
its watchers are wired, but it did so without a word: you typed a flag you had used
for months and got a different-looking VAF with no reason given. It now says so at
startup, and only when the interface actually changes.- The terminal app asks for an API key instead of storing a provider that cannot
work. Switching to a provider you had no key for wrote it to the config anyway
and told you to restart, which was the one thing that could not help: the next
start came up on a provider with no way to reach it. Now a provider with no key
opens a masked key field,kon a provider row opens it for a key you want to
replace, and the key is checked with one real request before anything moves. A
key that does not verify is kept but the provider stays where it was, because
that request can fail on the network as easily as on the key. The key itself is
never shown, never written into the conversation, and the check runs in the
background so the app keeps responding while the provider answers. - Choosing a provider for sub-agents in the terminal app now actually moves them.
The row wrote the provider's name but not the switch that turns the choice on, so
it reported success, moved its marker onto your choice, and every sub-agent went
on using the main agent's provider. Six places worked out that pair by hand and
now ask one function, so the two halves cannot drift apart again. Two more things
come with it: the oldervaf settingsmenu announced the chosen provider in its
panel even when the switch was off, and it does not any more; and picking a
provider you have no API key for is now refused in the terminal app too, instead
of being stored and failing inside every sub-agent afterwards. - VAF no longer names one theme while showing another. If your saved theme was
not the default, the terminal app's Theme list put its marker on the default row,
and thevaf settingsmenu labelled the default too, while the screen was painted
in the theme you had actually chosen. The name came from a value that only two of
the four terminal lanes ever loaded from your config; every surface now reads the
saved choice, and a--themeflag reaches them all. Worth knowing, because it made
the above look like the theme was not applied at all:twrites your choice
immediately, so cycling through the themes to look at them leaves you on the last
one you stopped at, and that is what the next start uses. - A timer set in the terminal app now actually arrives. Asking for a reminder
invaf runset the timer, said so, and then nothing happened when it elapsed -
the terminal app never looked at the queue the timer fires into, so it expired
in silence. It now shows an amber card with what woke the agent, followed by the
agent's answer. A timer belonging to a different conversation is dropped with a
note saying so, rather than swapping the conversation you are reading. - A note the agent saves for itself now belongs to the person it was talking to.
On an installation with more than one account, a note written while a background
pass happened to be running was filed under that pass's account instead - and notes
are read back into that account's next background pass as instructions to follow. - Background thinking no longer changes what a waiting person's turn is allowed to
do. While it ran, anyone chatting at the same time silently got a fraction of the
tool budget, a cap after three lookups, no retry when the model returned nothing -
which is what leaves the web page stuck on a loading block - and a refusal from
their own "update what I'm working on" action. Each of those limits is meant for
the background pass alone. - The program keeps asking you for confirmation after an automation has run.
The first automation switched the whole program into a mode that never prompts and
never switched it back, so from then on confirmations a person should have seen were
answered without them. - A scheduled automation can no longer put its output in someone else's place.
On an installation with more than one account, a background task had no session
of its own and quietly adopted whichever conversation was last active in the
program. Its document was then created inside that person's workspace folder,
announced in that person's browser, and their conversation was permanently
re-pointed at the folder. In the same run their Stop button would cancel the
background task, while the task's real owner could not stop it at all. A run now
states which conversation it belongs to - a scheduled task says "none" - and
nothing falls back to a program-wide value. Single-user installations were never
exposed to the cross-account part of this. - An update can no longer make sub-agent results stop arriving. If a newer VAF
wrote one entry into the shared sub-agent file and an older one read it, the older
one gave up on the entire file rather than on that one entry - so every finished
sub-agent result was silently dropped, with nothing in any log. This only needed
one mixed-version moment to trigger and did not repair itself. - A coding run without an open Web UI no longer does the work of drawing one.
Every loop iteration and every written file gathered a full project snapshot -
severalgitcalls and a walk of the project tree - before checking whether
anyone was watching. On the terminal, where nobody is, that work was thrown away.
The librarian did the same with a filesystem scan that also wrote a cache file. - A skill you shared with one person is visible to that person again. On the
lanes that stored the account key as a parsed object rather than as text, the
visibility check compared two different kinds of value and never matched, so
use_skillandread_skillanswered "not available to you" to the very person
the skill had been shared with. - The machine owner is no longer locked out of their own files. If the owner
key was ever stored in a different spelling than the one VAF generates, the
terminal app rewrote it on startup and then compared the rewritten value against
the stored one. It never matched, so the owner was treated as a guest: jailed out
of their own project folder and refused on their own files. - A timer or automation can no longer leave a conversation without an owner.
Stamping the owner onto a fresh conversation could fail silently on the
background lanes, and a conversation with no owner loses the channel it came
from, so a later sub-agent result had nowhere to go back to. - A background "thinking" run no longer borrows the machine owner's profile.
After loading a person's chat history, the run took its identity from that
conversation; if the conversation had no name stored, three fallbacks quietly
substituted the owner's, and the owner's personal profile went into a different
person's prompt and into the message sent to them. - An elevated permission no longer survives from one queued message into the
next. The role was only ever cleared on two of the paths that start a turn. - A damaged owner key in a restored or hand-edited automation file no longer
resolves to the machine owner. It gets its own isolated bucket instead. - The classic CLI no longer loses its prompt for good after a second message
from the web UI. Once two messages had been queued for the same conversation,
the terminal dropped to a plainMessage:line with no completion, no history
and no voice, and stayed there until you restarted it. Behind it, a finished
task was never marked finished, so the conversation looked permanently busy and
every later message for it was held back with nothing to release it. Both the
hold and the crash it caused are gone, and any lane that consumes queued work -
including ones written later - is covered by the same fix rather than having to
remember the release itself. - A long answer no longer streams past the bottom of the terminal app. The
transcript stopped following after the first few lines and the reply ran on
below the fold, so you had to scroll down yourself to see the agent finish.
Whether it happened at all depended on your terminal size, which is why it
looked intermittent. Scrolling up still leaves you where you are reading, and
coming back down picks the answer up again. - On Windows, attaching a file with
@works. Typing@C:\Users\me\notes.txt
attached nothing and left the path sitting in your message: the reference stopped at
the drive letter's colon, so VAF only ever saw@C. Relative paths and every other
platform were unaffected, which is why it went unnoticed. - The licence note now points somewhere you can actually go.
vaf infoand the
licence panel in settings told you to readLICENSE,LICENSING.mdand
COMMERCIAL.md, which is fine in a checkout and useless afterpip install vaf:
the licence texts live inside the package metadata there, and the commercial terms
were never part of the distribution at all. Both now show a link instead. - VAF understands German written without umlauts. Plenty of people type "taeglich"
or "ueberweisung" because their keyboard has no umlaut keys, and language models have
lately taken to writing German that way too. Everything VAF matches on - the words that
start a workflow, the risk check on an outgoing mail, the phishing score, the guard that
catches the assistant claiming success it never earned - looked only for the spelling
with umlauts and quietly did nothing for the other one. Both spellings now reach the
same place. The guard against unearned success claims is the one that mattered most: it
had been blind to exactly the models it exists to catch. - Sending a message no longer stalls while VAF saves what it learned. Every
submitted line rewrote the entire learned-phrases file before anything else could
happen - on a well-used install that is a 2.5 MB write and about a seventh of a
second, paid in the terminal and in the web app alike. The learning is kept in
memory immediately and written out a few seconds later, in the background, and
the file is replaced atomically so an interrupted save cannot corrupt it. - A sub-agent no longer draws over the terminal app. When the coding agent, the
librarian or the research agent ran without their own window, each painted a live
progress panel straight onto the terminal - which shredded the display of the new
full-screenvaf run. All three now ask one shared question first, and while an
application owns the screen they report their progress into the transcript instead
of over it. - The terminal app no longer loses its model, its settings or its crash reports.
Five things went wrong quietly in the new full-screenvaf run: the desktop tray
could unload the local model in the middle of a session, because the app never
told it that a session was alive; a failing turn left no traceback anywhere; a
theme picked in Settings only changed half the colors; the model name in the top
bar always read "local"; the speech-input row could show "off" while the
microphone was actually live; and switching server persistence did not reach the
running agent.vaf runnow also checks for Git before it starts, instead of
letting tools fail deep inside an answer, and falls back to the previous
interface if the full-screen one cannot start at all. - Files sent through the messengers are confined to your own data on shared
machines. Attaching a file to an outgoing Telegram, WhatsApp or Discord message
(or to the main-messenger delivery) accepted any path the general safety checks
allow - including another user's project tree. Only email attachments were
confined. All senders now enter the same per-user boundary as the file tools, so
a non-admin can only send files from their own tree. - A symbolic link can no longer smuggle a protected file into a file operation or
an outgoing attachment. The shared path rule checked the link's own location and
the file was then opened at the link's target - so a link inside an allowed folder
could reach VAF's credential store or system files. The rule now resolves links
and re-checks the real target, for every caller. - Generated text no longer carries stream debris or hidden reasoning. Several
features collected the model's streamed answer with their own hand-written loops,
and the loops disagreed: AI git commit messages could end in a raw
{"finish_reason": "stop"}control frame, tool utility completions could return a
provider ERROR message as if it were the answer, and on local reasoning models the
coder's template detection and other short calls received empty text because the
whole token budget went into hidden reasoning. All of these now share one collector
that filters control frames, treats backend errors as "no answer", strips reasoning
blocks, and disables reasoning for local utility calls. - Streamed memory answers work again. Asking the memory panel a question with
streaming on crashed on the very first token and returned an error string instead of
the answer - every time, on every backend. The stream now delivers the text. - Attachment summaries respect their time budget. The per-section summarizer
declared a timeout that was never wired up, so a slow backend could stall document
ingestion far beyond the budget the caller computed. The timeout now binds. - Settings no longer claims "API key missing" for a key that is safely stored. Since
keys moved into the encrypted store, the browser's copy of the config deliberately
carries no key values - but six places in Settings still judged "is a key configured"
from that copy: the warning banner in the provider section, the model-refresh buttons
next to the provider, vision and voice-agent model pickers, and the three voice hints.
A stored key therefore showed as missing, and refresh buttons stayed disabled although
the server could resolve the key fine. All of them now ask the same stored-state the
key fields already use, count a freshly typed key as present, and stay silent while the
state is still loading or unreadable - "nothing configured" and "cannot tell" are
different answers and no longer render as the same warning. - Editing an offline user no longer deactivates their account. The user table showed
"Active/Inactive" for presence - is the user connected right now - but the edit dialog
read that same field as the account state and wrote it back on save. Editing anyone who
happened to be offline and clicking Save silently blocked their sign-in, and a
reactivation appeared not to stick because the list refresh dropped the mapped fields.
Presence and account state are now two separate things everywhere, and the list refresh
is one shared loader instead of four drifting copies. The user list also says
"Online/Offline" for presence now, so it no longer borrows the words the account
setting uses, and a deactivated account is marked as such right in the list. - An admin can no longer lock themselves - or everyone - out. You cannot deactivate,
demote or delete your own account (another admin can still do all three to you), and
nobody can deactivate, demote or delete the last remaining admin who is able to sign
in. Deleting was the gap: it only checked that some other admin account existed, even
a deactivated one that could never log in to repair anything. The server refuses with a
clear message, and the dialog does not offer the self-lockout controls in the first
place. - A generated password is shown once instead of being thrown away. Creating a user
with the password field left empty is the advertised way to let VAF generate one - and
the generated password was then discarded without ever being displayed, so the new
account could not be used until an admin ran a separate password reset. It now appears
in a confirmation dialog with a copy button, once, because it exists nowhere else. - Opening the user editor from the detail view no longer carries the previous user's
state. The access preset picked for the last user stayed selected and immediately
rewrote the newly opened user's tool selection, and a temporary password from the
previous user could appear in the new user's dialog. - The account status toggle says what it does. It flips whether the user may sign
in, but showed only a nameless switch. It now shows the state in words - Active or
Deactivated - plus what each means, including the honest limit: deactivating blocks
sign-in and token renewal immediately, while a session that is already signed in ends
when its token expires. - The per-user access picker is searchable. With 117 tools in the grid, finding the
one to grant or revoke meant scrolling; both the tool and the workflow list now have a
search field. Filtering is visual only - presets and "Select All" keep operating on
the full list, so a filter can never silently shrink what they apply to. - The per-user tool permission is now actually enforced. Since user management
existed, an admin could choose which tools each user may use - and the choice was
saved, displayed, and checked by nothing (the interface said so honestly). It is now
enforced on every tool call: a user can only run the tools selected for their account,
in chat and inside the coding agent, and a revocation takes effect within seconds, not
at the next login. The coding agent's internal tools - the shell above all - now appear
in the admin's picker as well, so "this user gets the coder but not the shell" is a
choice the admin can express. Admins themselves are never restricted and an empty
selection means unrestricted. - The per-user workflow permission is enforced as well. The other half of the same
admin choice - which saved workflows a user may run - was also stored and checked by
nothing. A saved workflow now passes that list when it starts, wherever it is started
from: chat, the workflow tool, an automation, the command line, or the resume of a
paused run - so revoking a workflow while it is paused means it does not come back.
The same rules apply as for tools: admins are never restricted, an empty selection
means unrestricted. One-off workflows the agent designs on the fly have no saved
identity to check; what governs them is the tool permission of the feature that
builds them. - The coding agent now acts as the person who asked. It runs as a separate process,
and no identity crossed that boundary - so its file tools ran with the machine owner's
rights for every caller. The caller's identity now travels with the task, and the
coder's file tools are confined to that person's own workspace. The owner's own runs are
unchanged, and the shell deliberately keeps full strength - controlling who may use the
coder at all remains the admin's lever. - Each user's cloud accounts are their own. Connected cloud storage - Google Drive,
OneDrive, Dropbox, Nextcloud, iCloud - was addressed by user NAME, and any part of VAF
that had no name to give fell back to the machine owner's. On a shared installation that
meant another person's request could list, read and download from the owner's cloud
accounts using the owner's own login. Credentials are now addressed by the account they
belong to, end to end, including when a login token is refreshed. Existing connections
keep working and move across by themselves the first time they are used; the owner's own
accounts are unchanged. Cloud downloads follow the same rule: they used to land in the
owner's Downloads folder no matter who asked, and now arrive in the requester's own
space. - The OAuth client secrets get the same treatment as the API keys - and stop travelling
to the browser too. The Google, Microsoft, Dropbox and GitHub client secrets in
Settings showed their stored value to any admin session and echoed it back on every
save - the same mechanism that corrupted a stored API key. They are now write-only like
the keys: the field shows a partial glimpse and locks until you choose to change it,
deleting is its own confirmed action, and what never leaves the server cannot be echoed
back into it. One consequence to know: clearing such a field and saving no longer
removes the secret - the delete action does, deliberately. - A stored key field shows you which key it holds. "Key stored" answers whether, not
which - so each field with a stored key now shows a recognisable glimpse in grey: the
first characters, a run of dots, the tail (likevaf_live_q0``...``Ab4d). It is a display
built on the server and deliberately partial; the key itself still never travels to the
browser, which is what keeps the save round-trip incapable of corrupting it. - Your API keys no longer travel to the browser at all - and a Settings save can no
longer corrupt a stored key. The Settings page used to receive the key values still
sitting in the configuration file and showed them as dots; saving any setting then sent
them back, and VAF stored that echo as if it were the key. For most keys the echo
happened to be identical and nothing broke; for one whose on-disk form was encoded, the
stored key was silently replaced by its encoded shell and every request with it would
have been refused - found because one key field looked different from the others. Key
fields now always arrive empty (the stored value is confirmed, never displayed), the
save response no longer carries secrets to anyone, and the round trip is pinned by a
test that replays it against the real save route. - "Fetch models" works again for a stored key. It used to read the key from the form
field, which is empty by design now; the server resolves the stored key itself, and the
form only needs a value while you are typing a new one. - A new or changed API key is checked against the provider when you save it. Until now
a wrong key looked exactly like a right one until the next message failed, and the answer
arrived as a chat error with nothing connecting it to the screen that caused it. Saving a
key you entered or changed now asks the provider whether it works, and reports back next to
that key. A refused key says so with the provider's own error number and is left in place
for you to correct; VAF does not delete it, because a check is not a revocation. A provider
that cannot be reached is reported as exactly that and never as a bad key - an outage or a
rate limit says nothing about what you typed, and treating it as a verdict would send you
chasing a key that was never the problem. - Settings no longer closes when you save. Saving applies your changes; closing the
window is your decision, with Escape or a click outside. They used to be the same action,
which also meant a key check had nowhere to report to. - You can now actually delete an API key, and Settings shows you which keys are stored.
Clearing the field and saving did nothing: a blank value has always meant "the form did not
re-send this", which is what stops a half-filled page from wiping a key, so there was no way
at all to remove one. That matters more than it sounds, because the usual reason to delete a
key is that it leaked - and the interface reported success while the key stayed live. Each
key now has its own delete action, and it removes the key from both places it can be stored,
in the order that matters: get it wrong and the next use quietly restores it from the older
location. If any part of the removal fails, you are told the key is still live and should be
rotated at the provider instead of being told it worked. - Settings tells you again which API keys are stored. When keys moved out of the plain
configuration file into the encrypted store, the key fields in Settings went blank and
stayed blank - a configured, working key looked exactly like no key at all, on the one page
where you go to check. Each field now says whether a key is stored, without ever showing the
key itself, and says so honestly when the stored keys cannot be read rather than reporting
"none". - Changing your API key now takes effect without restarting VAF. Saving a new key
appeared to work while the old one kept answering, and only a full restart applied the
change - which meant a key you had just deleted or replaced could still be in use, with
nothing on screen saying so. VAF runs several chat workers side by side, and the change
was being applied to one of them; the rest carried on with the key they started with. It
now reaches every one of them. Agents belonging to an application that embeds VAF are
deliberately left alone, so a key passed in by that application is never replaced by the
one on the machine. - The protection shield no longer reports "no anomalies" on a day something was
blocked. The shield summarised the state of each protection module but ignored what had
actually happened that day, so a stopped high-risk skill installation and an admin
overriding a security refusal appeared only as two small numbers in the Skills panel,
next to a large green "no anomalies". Today's blocked attempts now appear at the shield
itself as a clickable badge that opens the full list, and an overridden refusal or a
worsened re-scan raises the shield to amber. A block on its own stays green with a count:
a blocked attempt is the protection working, not a problem. - The security event list now names every kind of event. Half of what VAF records -
every skill event and both mail events - had no label anywhere in the interface and would
have been shown as a raw internal identifier instead of a description. - A broken stored API key now says so instead of looking unconfigured. Until now any
problem reading a key produced an empty value, and an empty value means "not set up" to
everything that asks - so a damaged key silently dropped you to the local model, or to no
provider at all, with nothing to indicate why. It now reports the problem for the provider
you are actually using, at the moment it is used: a damaged entry for a provider you never
touch cannot take the installation down. If you see it, re-enter that key in Settings. - Your API keys move out of the plain configuration file. They were stored in
config.json, scrambled but not encrypted - readable to anyone who opened the file, and
carried along in any backup or screenshot of it. They now live in the same encrypted store
as your mail, GitHub and cloud logins. Existing keys keep working and move across by
themselves the first time they are used; nothing is deleted, so going back to an older
version still finds them. Worth being precise about what this buys: unless you set a master
passphrase, the protection is still only your file permissions - the win is that the secret
is no longer sitting in the file everything else reads. - Cloud storage no longer treats everyone as the machine owner. The assistant's cloud
tool worked out who was calling from a setting that is never actually set, so it always
concluded it was the owner - whoever was really asking. On a shared installation that
meant anyone could list, read, download and upload through the owner's connected Google
Drive, OneDrive, Dropbox, Nextcloud and iCloud accounts, using the owner's stored logins.
It now uses the identity of the actual caller. In a normal chat that means you see your
own connected accounts and nobody else's. When the assistant reaches the tool through its
file assistant, your name is not passed along yet, so you see nothing rather than someone
else's accounts - safe, but not yet complete; the remaining half is in progress.
Single-user installations are unaffected.
In the same place, two file paths are no longer taken at face value: asking the tool to
upload a local file, or to fetch one back out of the sync folder, is refused when the path
points outside your own area instead of copying the file somewhere the assistant can serve
it back. - A tool run without a username no longer guesses whose data it is. Some runs carry no
username - after switching into a chat that has none stored, from the command line, from a
workflow started without one - and the assistant used to fill in the literal name "admin".
On any installation where you registered under a different name, that named nobody: things
stored under a name (your cloud accounts and their sync folder, your GitHub account,
credentials in the lanes that have no scope) are filed under the name you registered with,
so the same person could be recognised in the web interface and be a stranger with an empty
account list one lane over. The missing name is now resolved from the user the run belongs
to: your own runs get your account, and a run belonging to somebody else gets their own
separate space instead of yours. Installations with a single user called "admin" behave
exactly as before. Some traces of the old behaviour remain visible in an old credential key
shape the store keeps reading, and in stored chat history filed under the literal name. - The macOS launcher works on any machine.
launch_vaf.scptopened a hardcoded home
directory belonging to one account, so it failed for everybody else. It now uses$HOME. - Examples in the assistant's own instructions no longer name one particular person. The
shipped prompt text used a real first name in its examples ("... said", "Tell ... to call me
back"), which read oddly for anybody else and put a private name in a public repository.
Examples, default display names and documentation now use neutral placeholders. A guard
checks committed content for real home directories by shape, so the next one is caught
before it is published rather than after. - Code search and the linter stayed inside your own files. Both took a path and worked on
it without asking whether it was yours. Code search looked contained - it clamps a path back
into the project it was given - but that only applies to the copy the coding assistant uses;
the one in normal chat is created without a project, so the clamp did nothing and an absolute
path was searched and its matching lines returned. Both now confine to the calling user's
own files, on every path they can be called from. - On a shared machine, one person could reach into another's chat session. Three of the
commands the browser sends act on a session named in the message itself, and they never
checked that the session belonged to whoever sent it. The consequences differed: the
attachment panel of another user's chat could be emptied or filled with someone else's
documents, which they would then find there and could unknowingly teach to VAF; a voice call
could place a task into another person's chat queue; and a Stop press could interrupt
another person's running answer. All three now refuse to act on a session that is not
yours, the same way the other commands already did. - Asking VAF to write a document can no longer put it outside your documents folder.
The tool takes a file NAME, but never checked that it got one: a name that was actually a
path replaced the target folder entirely, so an absolute one wrote wherever it pointed. The
file-type restriction did not help - it limits which formats may be written, not where. A
name containing a path is now refused with a pointer to the tool that does take one, and
the document lands in the calling user's own folder on a shared machine rather than
wherever the name led. - Asking VAF to learn a document can no longer pull in files it protects. This tool
carried its own idea of which files were readable, and it disagreed with the one every
other file tool uses in both directions: it allowed anything in your home folder, including
SSH keys,.envfiles and VAF's own settings and credential store, while refusing ordinary
files kept outside it. Learning is also the worst place for that to go
wrong, because it keeps what it reads: the contents are summarised into long-term memory
and stay searchable long after the conversation ends. The tool's private rule is gone and
the shared one decides, which on a multi-user machine also applies the per-user boundary it
previously skipped. Together with the document panel fix below, a protected file is now
refused whether you ask to view it or to learn it. - Opening a protected file in the document panel no longer showed its contents. The
viewer read every file twice: once through the checked reader, and once raw to send the
original bytes to the browser. Only the first read was checked, and its refusal was not
acted on, so a file VAF protects for everyone - SSH keys,.envfiles, VAF's own settings
and credential store - was refused in the text pane while the panel received the real file,
and the tool reported it as opened. It is now one decision, taken before anything is read,
and it covers both the protected locations and each user's own boundary.
Its sibling, the document editor, now also reports honestly: it says it asked the
interface to open a file, because whether the file is actually served is decided
elsewhere and can still be refused. Claiming success for something that cannot
happen is the same problem one level up. - The file assistant's own tools now follow the same rules as everything else - this time
on both of its paths. When you ask VAF to work with your files, it hands the job to an
assistant that runs its own small agent, and that agent called its thirteen tools directly,
bypassing the checks every other tool call goes through. An earlier entry said this was
fixed; it was fixed for one of the assistant's two paths. The other one answers simple
requests straight from the wording, without ever starting the inner agent - "how big is this
folder", "rename this to that", "show me the structure" - and it kept calling five tools
directly, including the one that moves files.
Both paths go through the shared checks now, which also means that for the first time these
actions appear in the activity log: previously the assistant could rename or write a file
and leave no record of it anywhere. Your files were never less protected on that path - the
per-user boundary applied throughout - but nothing recorded what happened.
One thing reads differently as a result: when one of those actions fails, the message is now
the same short one used everywhere else instead of a hand-written sentence, so the assistant
says what it is about to do beforehand ("Renaming X to Y...") to keep the context. - A failed web search could answer with someone else's memories. When every web provider
is unavailable, VAF falls back to your own long-term memory - but on a server running
several conversations at once, it picked whose memory to read from a process-wide value
that any of them could have overwritten a moment earlier. The answer could therefore come
from another person's memories. The tool is now told who is asking, the same way every
other per-user tool already was. Single-user installations were never affected, and a
request with no user attached was already refused rather than answered broadly.