Skip to content

Releases: Veyllo-Labs/VAF

VAF v0.1.0a21

VAF v0.1.0a21 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 08 Aug 17:29

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
    which update_check_on_start: false turns 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 public BaseTool - can now report what
    it is doing without reaching into VAF's internals. Lines land in tools_*.log
    with the tool name and session filled in, follow the same VAF_LOG_DIR and
    Debug Logs settings 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) gained n for a fresh session, r to rename
    the highlighted one and d to 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 - /export writes 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 at vaf settings.

  • The context limit, custom numbers, the microphone and About live in the
    terminal app now.
    Four more settings rows stop pointing at vaf 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, and session current
    prints the full id - the one thing vaf run --session <id> needs and the
    panel can only truncate. session list opens 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 matching os key 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. Set VAF_LOG_DIR to 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 --web no 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 on vaf run --web to 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...
Read more

VAF v0.1.0a20

VAF v0.1.0a20 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 05 Aug 19:20

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, like 2/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 run greets 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
    "use vaf 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 at vaf settings,
    and now say why rather than just deferring.
  • Sessions you never wrote in do not pile up any more. Opening vaf run and
    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, halt or restart in the new
    full-screen vaf run used to send the word to the model as a chat message. They
    are commands again, alongside the ones that already worked, and a mistyped
    /command now says so (with the closest match) instead of costing a turn.
    Arguments work too: theme dark picks 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 run showed
    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. The tool_end event now carries a result field
    (capped at 800 characters, always a string, safe to serialize). In vaf run the
    tool card is no longer an empty fold: it opens onto the result.
  • vaf run opens 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 @file attachments. 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 --classic for the plain prompt, and the new tui_mode config key to pick the
    default lane (vaf run --web keeps 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_resolver is 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 an allow()
    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 declares file_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.py shows 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_authorizer is 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. ToolCaller is now part of the
    pu...
Read more

VAF v0.1.0a19

VAF v0.1.0a19 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 27 Jul 05:42

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_injection switches this over: leave it at legacy for the previous
    behaviour, set it to declared to 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 doc docs/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 the mail extra): IMAPClient (BSD-3-Clause), nh3 (MIT),
    zstandard (BSD-3-Clause), listed in THIRD_PARTY.md and 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 once mail_engine_write_enabled is on. The agent gains
    reply_mail, forward_mail, archive_mail and delete_mail tools (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_text and a deterministic,
    tamper-evident hash_skill_folder fingerprint, 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 behind mail_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's reply_mail and forward_mail verbs 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 batch tool 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/email are 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/email are 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...
Read more

VAF v0.1.0a18

VAF v0.1.0a18 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 23 Jul 00:29

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 every skills_rescan_interval_hours (default 5, 0 disables),
    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-gated GET /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 new security log (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...
Read more

VAF v0.1.0a17

VAF v0.1.0a17 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 22 Jul 17:00

Added

  • VAF is published to PyPI with every release. Library users can install the
    framework with pip install --pre vaf (--pre is the recommended spelling while
    VAF is in alpha) and update it with pip 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 update refuses 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 vaf instead 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 like vaf[server] and
    vaf[all] stay exactly the same), the license is now declared in the standard
    machine-readable form, and the LICENSING.md terms 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 vaf command now works on a minimal library install. With only the base
    dependencies installed, vaf --version, vaf prompt and other light commands run
    normally, and commands that need optional components (for example vaf 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, and VAF_ALLOW_WAYLAND=1 keeps 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-close keeps 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

VAF v0.1.0a16 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 19 Jul 18:04

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

VAF v0.1.0a15 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 18 Jul 02:19

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 with multi (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, model veyllo-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. The veyllo-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() plus Agent(session=<id>) persist and resume a
    conversation across process restarts (idempotent updates, loud failure on
    unknown ids, tenant-ownership check under user_scope; runnable chatbot
    example included); vaf.markers exports 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
    grows duration_ms and a dispatch-level ok flag on tool_end plus new
    llm_start/llm_end events with token usage on API providers (attached
    across backend swaps; facade shortcut agent.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
    Agent can now be handed extra tools directly: agent.add_tool(MyTool())
    before the first run registers a BaseTool for 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...

Read more

VAF v0.1.0a14

VAF v0.1.0a14 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Jul 09:10

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.json before their old updater can
    reach this version.

VAF v0.1.0a13

VAF v0.1.0a13 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Jul 08:34

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

VAF v0.1.0a12 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 16 Jul 08:27

Fixed

  • vaf update can 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 runs ci now (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 made git fetch --tags fail mid-update with a rollback - tags
    are fetched with --force now. (3) Two shell scripts were stored with
    Windows line endings despite their eol=lf attribute, so a fresh checkout
    started dirty - the repository is renormalized. If your install is
    currently stuck on an old version, run git checkout -- web/package-lock.json
    in the VAF folder once, then vaf update.