# Changelog All notable changes to Aivyx are recorded here. This project adheres to [Semantic Versioning](https://semver.org). Dates are ISO-8601. ## [Unreleased] ### Added - **`[agent] max_reply_tokens`**: the most tokens one reply may use. Default 4096 (it was a fixed 1024). The model server's own limit still applies. The larger value also raises the context size routing asks a model for and the budget gate's worst-case cost estimate, so in routed mode a model with a small context is passed over sooner. - **`[agent] role`**: the role to start in when neither `--role` nor `AIVYX_PA_ROLE` picks one. `aivyx-pa init` writes it for templates that have no `default` role (coder, researcher, personal, kitchen), so a config made from them starts without `--role`. A schedule, webhook or file watch with no `role` now runs as this role. For a config made earlier, `aivyx-pa doctor` names the line to add. - **Notify webhooks carry a version.** The JSON a `kind = "webhook"` notify target receives has `"version": 1`. Receivers that ignore fields they don't know need no change. - **Versions on what pa stores, before 1.0.** Each instance gets a `layout-version` file in its data directory, the encrypted store records each table's record version, the audit chain gets a header with its format version (beside the chain, so existing chains verify as before), and each `aivyx-pa audit export` line carries `"version": 1`. Data from earlier releases is version 1 and is recorded as such the first time this release opens it; nothing to do. A newer release's data is refused with a message instead of misread. ### Changed - **`aivyx-pa audit export --event-type` takes snake_case** (`tool_call`, `scope_denied`, ...), like every other CLI value. The CamelCase names (`ToolCall`) still work. - **A reply the model server cut off says so.** It ends with "⚠ The reply was cut off by the model server's output limit…" instead of passing for a finished reply. - **A trigger with no `role` follows `--role`.** With `--role X` or `AIVYX_PA_ROLE=X`, a trigger with no `role` is now checked (notify scopes) and listed as role X instead of `default`, so an old config whose role X lacks a trigger's notify scope now fails to load, with a message. - **`--headless` prints a turn's notes on stdout**, after the reply (the cut-off note, for example), and prints a reply that wasn't streamed. - **Image generation reports the backend's notes.** `vision.generate_image` output gains `warnings`, for example when mold adjusted the requested size. ### Security - **Local traffic no longer goes through a proxy.** With `HTTP_PROXY`, `HTTPS_PROXY` or `ALL_PROXY` set, requests to local services (Ollama, llama.cpp, Lemonade, the broker, mold, a self-hosted n8n or MCP server, a local webhook) went through the proxy, prompts and keys included. Requests to a local address now always go direct. Cloud providers and internet services still use the configured proxy. - **Generated images are owner-only.** A new `vision` output directory is created `0700` and each image `0600`. ### Fixed - **Studio text is readable in both themes.** Links and brass text in the light theme were 3.09:1 and are now 4.98:1. Error, success, info and warning text and their chips, notices and the "forget skill" button now pass WCAG AA (4.5:1). Rust-coloured text uses a lighter shade in the dark theme. - **Image generation works with mold 0.37.** mold now requires the model, size and step count on every request, so every `vision.generate_image` call failed. They're now filled from the model's own defaults, a size off the model's grid is rounded (with a note), and the vision tool's `[mold]` section takes an optional `model` (unset: the image model downloaded on mold). Tested against a real mold 0.37 on an RTX 4090. A mold in Docker or on another machine must run with `--allow-direct-api`; the error now says so. - **Image generation errors say what went wrong.** A busy GPU is reported only when the broker says so (a proxy's or another service's 503 no longer reads as "GPU busy"), an unknown mold model and a rejected mold API key get their own messages, and an unreadable reference image fails before the GPU is reserved. ## [0.20.1] — 2026-11-08 v0.20.0's binaries were never published: GitHub had retired the macOS 14 build machine its release used. This release ships everything in v0.20.0 (below) plus: ### Security - **Matrix encryption library updated** (vodozemac 0.11.2, RUSTSEC-2026-0445 and RUSTSEC-2026-0451: encryption key material was not always wiped from memory). ### Fixed - **Release builds** for Apple Silicon and the macOS desktop app use the macOS 15 build machine. ## [0.20.0] — 2026-11-05 ### Changed - **The Studio sidebar is grouped.** Mission Control sits under Missions, Wiki and Graph under Memory, and Gallery, Voice, Sessions, MCP and Tools under a collapsible "More"; Guide and Settings stay pinned at the bottom. Which groups are open is remembered per browser. - **Readable Studio text.** Help text, empty screens, routine prompts and the Audit notes are set in normal sentence case at readable contrast; file and config names keep their real case. Screens say "Missions" and "Default team", and the status bar shows the model's short name (full name on hover). - **Empty screens say how to start.** Memory, Wiki, Graph, Models, Loop, Gallery and Notifications explain what the feature does and offer a button or the exact steps with a link to the guide, instead of raw config. - **Routines wait for the model at start.** A routine due right after the daemon starts no longer fails while the model is still loading, so a new install no longer opens on "The routine … failed". - **Sessions and Tools are plainer.** Sessions shows the channel and client and drops closed Studio tabs; Tools shows a short description per tool with the full text under "Details". - **The terminal chat has `/help`, `/quit` and `/exit`.** An unknown `/word` gets "unknown command" instead of going to the model. The approval prompt reads `[y] allow [n] deny (default) [a] show all`, the same words as aivyx-coder. - **A timed-out turn says why and what to do:** the limit, that tool results so far are kept, and the `turn_timeout_secs` setting under `[agent]`. Chat apps get the same wording. - **`init` no longer invents an identity from empty answers**, shows the drafting time and lets Ctrl-C skip the draft, and says what the background service installs (its name, what it runs, its log and how to remove it) before asking. The top-level help lists developer commands separately. ### Fixed - **The Studio link is only printed when the Studio is running, at its real port.** If its port is taken (for example by another pa instance), the REPL, `aivyx-pa studio` and `doctor` say so and name the setting to change, instead of printing a link to the other instance. - **A daemon that fails to start shows the reason** from its log in the message, and names the log once. The Studio's bind error says "port". - The daemon names `aivyx-pa studio` for the sign-in link. ### Security - **Spreadsheet reading:** calamine 0.38 (RUSTSEC-2026-0436), so a malformed `.xls` file can no longer crash or exhaust memory while being read. - Dependency updates: async-executor 1.14.1 (1.14.0 was yanked); the zip extract advisory RUSTSEC-2026-0429 is accepted with its reason (the affected function is never called). ## [0.19.4] — 2026-11-01 ### Changed - **A `./aivyx-pa.toml` in the current directory is no longer read.** The config comes from `AIVYX_PA_CONFIG_PATH` or the instance's own file (`~/.config/aivyx-pa/aivyx-pa.toml`). To keep using a config elsewhere, set `AIVYX_PA_CONFIG_PATH=/path/to/aivyx-pa.toml`. `aivyx-pa daemon install` run from a folder that holds a config now writes that path into the unit; `aivyx-pa doctor` (and a one-line notice on start) reports a config left in the current directory or the service's working directory, and says how to point at it. Re-run `aivyx-pa daemon install` for a service that relied on its working directory. - **Mission-gate `/approve` and `/reject` on Telegram, Discord and Slack now need your id in `team_command_allowed_senders`**, the list `/team` commands already use (empty means nobody). Add your id if you resolve gates from chat. Matrix, Signal and Proton admit only you already. - **A bare `daemon run --web-ui` now serves the Studio on the instance's `[daemon] web_ui_port`** (it used the default port and ignored it, so a named instance failed to bind). `init` for a named instance writes its own port into the config and names it in "Next steps". - **A config pack can no longer ask for shell, delete, send, post or write scopes** in its `[[role]]` entries or team members; `pack check`, `pack inspect` and `pack install` list every scope a pack asks for. ### Security - **Approval prompts show everything they cover.** Recipients (`to`, `cc`, `bcc`, arrays too) are named in the summary, every argument is listed with short ones first, and any cut says how many characters are hidden: type `a` at the terminal prompt to see it all; the Studio card has a "Show everything" section. Control, bidirectional and zero-width characters are shown as escapes. (audit P4) - **Chat sessions that run in-process (the fallback when the daemon is down) now honour `manual` autonomy areas** like the daemon does. (audit P7) - **A parked-step preview and the Documents browser refuse a named pipe** in the agent's folder instead of hanging a worker. (audit P5) - **Telegram photo-download errors no longer log the bot token; recalled memory topics cannot forge a prompt section; a tool process's stderr is drained; the federation signature names its recipient; secret-holding structs print `` under `{:?}`; CI tokens are read-only; a failed tool call shows its reason; an empty reply after tool calls says what ran rather than "try again".** (audit P8-P14, P19, P20, P22) - **The daemon no longer loads a config from its working directory.** The installed service runs in `$HOME`, where the agent's shell could create an `aivyx-pa.toml` that the next start would obey (tool processes, MCP servers, access level). Configs are now loaded only from the explicit path or the instance's standard path. - **Login and session environment files are protected from the file tools** (`~/.config/environment.d`, `.pam_environment`, `.xsessionrc`, `.xinitrc`, `plasma-workspace/env`, fish `conf.d`, macOS `environment.plist` and `LaunchAgents`), so the agent cannot set `AIVYX_PA_CONFIG_PATH` or `XDG_CONFIG_HOME` for the next login. `shell.exec` also asks for awk/sed programs that run commands or write files, git aliases that run a shell, and the destructive archiver forms. - **At access `home`/`full` the file tools refuse every dot entry of `$HOME`.** Login and session files cannot be listed by name (`uwsm/env`, `hypr/hyprland.conf`, `niri`, `sway`, fish `functions`, `.kshrc`, `~/.local/share/applications/*.desktop`, ...), and each sets `AIVYX_PA_CONFIG_PATH` or runs code at the next login. `fs.write`, `fs.delete` and the data writers now apply the same dot-entry rule `shell.exec`'s confiner enforces: nothing under `~/.*` or `~/bin` is writable, except the personal workspace, the generated-image folder and `[access] allow_sensitive_paths` (those exceptions are the file tools' own; the confiner has its own). **Behaviour change:** the agent can no longer edit dotfiles under `$HOME` unless you allow the path. The named list still applies to roots that do not cover `$HOME`. `shell.exec`'s confirmation now skips options per wrapper (`time -p`, `sudo -E`, `timeout -s KILL 5`) and asks for more wrappers, interpreters, git settings that store a command, `git apply`, `find -fprint` and similar. - **An approval applies to exactly one call.** The "operator replied" ledger was keyed by tool name, so one refusal or one declined prompt let a later call to the same tool run unasked. It is now keyed by the call itself (tool, arguments, resolved target), counts only in the operator's next turn, expires after ten minutes, is used once, and honours only a short yes (`yes`, `y`, `ok`, `approve`, `go ahead`, ...): a "no" or any other message drops the pending approval. - **The agent can no longer write pa's own config, packs or tool-process files.** The file tools' guard denies the instance config, data, `~/.aivyx-pa` and runtime folders, any file named `aivyx-pa.toml` or `team.toml`, and the resolved config paths, at every access level, for new files as well as overwrites. A refused `fs.write` leaves no directories behind. - **The loop's gate command runs confined.** It executes what the loop agent wrote, so it now runs under the `shell.exec` confinement (only the working folder is writable), with a cleared environment, in its own process group. **Behaviour change:** `[loop] working_dir` is required when `gate_command` is set (reported when the config loads), and the gate inherits only `PATH`, `HOME`, `LANG`, `TERM` plus the new `[loop] gate_env` list (secret-looking names are refused). Captured command output is capped at its last 1 MiB. - **`shell.exec` asks before a destructive command** when `confirm_destructive` is on, as the docs and the init wizard promise: removals, overwrites, forced or history-rewriting git, `dd`, `mkfs`, `chmod -R`, and what the check cannot read (scripts, interpreters, `sh -c "$VAR"`). `git` commands follow the git area's posture, the rest the file area's. The question is an escalation, so it also applies in loop, trigger and headless runs: there it is refused or parked like any other confirm-first call instead of running. ## [0.19.3] — 2026-10-29 ### Security - **A repository's own git settings can no longer steer the checkpoint git.** The checkpoint git runs outside the sandbox on a repository that the sandboxed agent can write. Settings planted in that repository could make it run a program, snapshot files from outside the working folder, or pick a planted checkpoint when restoring. The checkpoint git now works only on its own folder with fixed settings, accepts a repository's config only from an allow-list, refuses symlinks in its own git folders, and restores only checkpoints it took itself (aivyx-checkpoint `a41da40`). - **The prompt-injection check now ignores invisible Unicode tag characters and variation selectors** that could hide a known injection phrase from it (aivyx-injection-guard `a506c9c`). - Dependency updates for RustSec advisories: rustls 0.23.46, rustls-webpki 0.103.16, the root-certificate bundles, quinn-proto 0.11.19, tokio 1.54, ctutils; zip 9 for the spreadsheet readers and the Kokoro voice loader. ### Changed - **When a repository's checkpoints are refused, pa says so.** A git repository whose settings fall outside the allow-list has no checkpoints, so changes there can't be undone or rolled back. pa now prints one warning per folder naming the setting, and `aivyx-pa doctor` lists such folders under "Checkpoints". - CI checks dependency advisories with cargo-deny and runs weekly. ## [0.19.2] — 2026-10-25 ### Fixed - **A checkpoint is no longer skipped after a failed one.** When git couldn't save a checkpoint, the next checkpoint of the same files was taken for a duplicate of it and reported success without saving anything, so there was nothing to go back to. Now a checkpoint only counts as a duplicate of one that was actually saved (aivyx-checkpoint `b8072e7`). ### Security - `slack-morphism` bumped to 2.28.0 (Dependabot alert, GHSA-26w4-8w32-4x3v: when verifying a Slack request failed, the error written to the logs could include a valid request signature). ## [0.19.1] — 2026-10-21 ### Fixed - **A killed aivyx-pa no longer leaves its commands running.** A `shell.exec` command runs in a process group of its own, which never sees the terminal's signals and was only killed when the tool call finished. So when aivyx-pa ended on `SIGTERM`, on `SIGHUP` (its terminal closed) or on a second Ctrl-C, a running command was left behind and ran on indefinitely; `daemon run` did the same on `SIGHUP`. Running commands are now killed first in every case, and the daemon kills them before its graceful shutdown instead of waiting for them (up to a command's ten-minute timeout). `SIGKILL` still leaves them running; a systemd service is covered either way. - **Ctrl-C stops a running command.** Cancelling a turn used to wait for a running `shell.exec` command to finish or time out (30 seconds by default). It is now killed at once, and the tool call returns what it had printed, marked `"cancelled": true`. The same applies when a turn hits its deadline. ## [0.19.0] — 2026-10-18 ### Added Three private chat channels, each a **preview**: tested end to end against local stand-ins, not yet with real accounts. See [Chat apps and accounts](docs/guide/13-chat-apps-and-accounts.md). - **Matrix:** talk to your assistant in an end-to-end-encrypted room on a homeserver you choose. It answers only people you list whose device you've verified (`aivyx-pa connect matrix`, `aivyx-pa matrix verify`). - **Proton Mail:** email your assistant through Proton Mail Bridge and get the answer in the same thread, and let it search, read, draft and send in your mailbox (`proton.*` tools; sending asks you every time). It answers only mail from your own Proton addresses, or from outside senders you list whose mail passed DMARC. - **Signal:** message your assistant on its own Signal number through a signal-cli you run, and let it send a Signal message to a contact you list (`signal.send`, asking you every time). It answers only contacts whose safety number you've verified (`aivyx-pa signal verify`). ### Security - **`shell.exec` at access `home` or `full` can't plant code or read your secrets in your home folder** (aivyx-confine `6e46575`, audit SH1). A confined command could read `~/.ssh` keys, GitHub tokens and keyrings, and add code that runs unconfined at your next login (`~/.bashrc`, `~/.config/autostart`, a missing `~/.bash_profile`). Now nothing can be created, deleted or renamed directly in `~`; dot folders and files, `~/bin` and linked dotfiles are read-only; secret stores and Aivyx PA's own state (every instance's) are unreadable. Other folders work as before. The assistant's `~/.aivyx-pa/workspace` can't be used from `shell.exec` at these levels. ## [0.18.2] — 2026-10-11 ### Security - **Tool packs can't write outside the packs folder.** A pack's version became part of its install path unchecked, so a signed pack could unpack its files anywhere in your home — for example a program onto your PATH. A pack's version must now be `x.y.z`. - **Config packs can't change what's yours to decide.** A pack's starter config may only set who the assistant is and what it does (its model, profile, persona, roles, autonomy within `supervised`, routines, team, memory and skills). A pack that sets how far it reaches, whether its Studio is open to the network, which programs it runs, where its requests go, whom it trusts, or the prompt-injection check now fails `pack check`, `pack inspect` and `pack install`. - **`web.fetch` blocks more private addresses:** Tailscale and other carrier-grade NAT addresses (`100.64.0.0/10`), `0.0.0.0/8`, broadcast, multicast, and IPv6 forms that wrap a private IPv4 address. - **`pack inspect` shows a pack's text escaped**, so a pack can't hide part of what you read before installing it. - Dependency updates clear every `cargo audit` vulnerability, including the XML parser behind the xlsx reader (a crafted spreadsheet could stall it). ### Fixed - **The terminal says why a turn stopped.** It showed only `[turn escalated]` or `[turn failed]`; it now gives the reason or the error. With the model server down, a turn showed `[turn completed]` and nothing else — it now fails and says the server couldn't be reached (`--headless` exits 1). Notes the assistant adds after its reply are shown too. - Reading a named pipe with `fs.read` or the data readers is refused instead of hanging the turn. - An MCP server can't make the daemon hold a single message over 64 MiB. - `team run` and `team roster` use the installed team (a pack's, or `team.toml` beside your config) without `--config`, like the daemon. - `doctor` and the end of `init` check llama.cpp and Jan as local servers instead of asking for an API key. - `connect toolkit` (and notion, obsidian, n8n, vision) explains that the integration runs as a tool process instead of saying "unknown service". - `pack inspect` shows a pack's aivyx-pa checks under its aivyx-pa part. ## [0.18.1] — 2026-10-10 ### Security - `rustls` bumped to 0.23.45 (Dependabot alert: TLS 1.3 handshake messages were accepted across encryption-level boundaries), with the newer `aws-lc-rs` it needs. ## [0.18.0] — 2026-10-10 ### Added - **Config packs.** A pack can now be configuration only — a starter config, a team, skills and routines over the tools Aivyx PA already has. `aivyx-pa pack install` makes one a **new assistant** (a named instance, named after the pack or `--instance `): it runs the usual setup pre-filled from the pack, then says which accounts to connect. A pack can't set autonomy above `supervised`, and an existing instance is never changed. `aivyx-pa pack inspect` shows a config pack's routines, autonomy and whether it passes its checks; `aivyx-pa pack check ` runs those checks for pack authors; `aivyx-pa instances list` shows which pack each assistant came from. ### Changed - **The pack format moved to its own shared crate, [`aivyx-pack`](https://github.com/Aivyx-Agent/aivyx-pack)**, so aivyx-pa and aivyx-coder verify packs with the same code. Packs install exactly as before; a newer config-only pack (format 2) is refused with a clear message until this release learns to install it. ### Fixed - **`aivyx-pa pack inspect` works before any assistant is set up.** On a fresh machine it refused to read a pack at all; it now checks the signature against the built-in publisher keys, as `pack install` does. - **`aivyx-pa pack check` checks routine schedules.** A routine whose schedule isn't a valid 6- or 7-field pattern (seconds first) is reported by the check instead of failing later in the scheduler. - **A pack's assistant gets only the pack's routines.** Installing a config pack no longer also adds the five starter routines or asks about them; on a cloud model, setup says the pack's routines are on and each run costs tokens. ## [0.17.0] — 2026-10-07 ### Added - **Supervised batching.** At the `supervised` autonomy level — globally or for one area with `[[autonomy.override]]` — an unattended run (a routine, webhook, file watch, the autonomous loop or a team mission) parks a step that needs your approval instead of refusing it, and carries on without it. Review parked steps under *Needs you* in the Command Center, which shows what each one touches as it is now, or with `aivyx-pa review`; an approved step runs once, exactly as parked. Your default notification target hears when something parks, and steps left unreviewed lapse after `[autonomy] review_expiry_days` (default 7); at most 100 wait at once. With no area at `supervised`, nothing changes. ## [0.16.0] — 2026-10-06 ### Added - **Different autonomy levels for different areas.** `[[autonomy.override]]` now does what it says: `domain = "email"`, `level = "manual"` makes every email change ask first, and `domain = "fs"`, `level = "unleashed"` stops file deletes and overwrites asking, while the rest follow the global level. An area is the first word of a capability (`fs`, `shell`, `git`, `email`…); an unknown or repeated area is a config error, an override looser than the global level is called out at start-up, and `aivyx-pa autonomy show` and the Studio list them. ### Fixed - **Switching to the sandbox stopped deletes and overwrites asking.** `aivyx-pa access set` (and the Studio's *Apply access level*) wrote `confirm_destructive` into `[access]` — `false` for `sandbox` — and an explicit setting beats the autonomy level, so after moving to the sandbox nothing asked, at any autonomy level. Changing the access level now removes the key instead, so the autonomy level decides again; run `aivyx-pa access set ` once to clear an old one. - **`unleashed` didn't switch confirmation off above the sandbox.** The access level's own default was combined with the autonomy level, so at `workspace`, `home` or `full` deletes still asked. Now, unless you set `confirm_destructive` yourself, the autonomy level alone decides: on at every level except `unleashed`. `aivyx-pa init` writes the key only when you decline confirmation, and `access show` and the start-up summary show the setting that's actually in effect. - **Emails and other integration writes keep asking at `unleashed`.** Sending mail and writing to Drive, Calendar, Contacts, Notion, Obsidian or n8n asks first at every autonomy level; only an explicit `[access] confirm_destructive = false` turns that off. The Studio's Settings now also shows the confirmation setting actually in effect. ## [0.15.0] — 2026-10-06 ### Added - **Named instances** — run several fully separate agents as one OS user, each with its own roles, Profile, Persona, memory, audit log, passphrase, background service and Studio (amendment A17): - `--instance ` on every command, or `AIVYX_PA_INSTANCE`. - `aivyx-pa instances list | create | remove `; `create` picks a free Studio port from 7844 and runs setup for the new instance; `remove` refuses the default instance, a running one, or one with an installed service, and asks for the name to be typed back. - Each Studio shows its instance name in the tab title; `daemon status` prints it. - The default instance keeps every existing path, so current installs are unchanged. The desktop app and chat channels still use the default instance. ## [0.14.8] — 2026-10-05 ### Security - `xxhash-rust` bumped to 0.8.19 (Dependabot alert: its safe xxh3 custom-secret API accepted a too-short secret in release builds). ## [0.14.7] — 2026-10-05 ### Changed - Model routing decides local or cloud by address, not just by kind (`aivyx-route` bumped to `d387a52`): - A `[routing.endpoints.*]` entry of a non-cloud kind (`openai_compat`, `ollama`, `llama_router`, `lemonade`) at a hosted address — e.g. `https://api.groq.com/openai/v1` — or an `openai_compat` one with no `base_url` now counts as cloud: it is rejected under `[routing.escalation] mode = "never"` (the error says why), turns escalation on otherwise, and is reachable only through the escalation gate, never as an ordinary candidate. Previously it counted as local and bypassed `mode = "never"` and the sensitivity taint. Mark a box on your own network with a public-looking name `locality = "local"`. - The `[agent]` backend is judged by its real address (the configured `base_url`, `[broker] base_url` for the broker, else the provider's default): an Ollama/llama.cpp/Jan/Lemonade/broker backend on a public host counts as cloud, so cloud-tagged roster entries on it may be chosen, and `routing` warns at startup. `provider = "openai"` with any local `base_url` (not just loopback) counts as a local OpenAI-compatible server; mistral.rs is always local. - `aivyx-pa doctor`, `aivyx-pa init` and residency polling still read your own `[agent]` backend whatever its address. Discovery now runs concurrently under a 15 s deadline, and never probes or polls a cloud endpoint. - `aivyx-pa federation yubikey-init` (behind the optional `yubikey` feature) is safer against hardware mistakes (`aivyx-yubi` bumped to `fd93451`): - Lists every attached YubiKey (serial + Signature-slot occupancy) before touching any of them; with more than one attached, a new `--card ` option is now required to pick which one. - Refuses to overwrite a Signature slot that already holds a key (e.g. a real GPG signing key) unless a new `--overwrite-existing-key` flag is passed, and even then requires typing the card's serial back at an interactive prompt to confirm — previously this command silently destroyed an existing key on every run. - Surfaces a reduced PIN retry counter (found before this command touched the card at all) with guidance to check `gpg --card-status` first, instead of the (wrong, in that case) factory-default-PIN instructions. - The closing post-provisioning verification pass now prompts for the real User PIN (hidden) instead of constructing a throwaway signer with an empty one, which the bumped `aivyx-yubi` now rejects outright at construction. - `aivyx-vision` bumped to `531aa7d`: generated SVG loses all CSS and every reference that doesn't point inside the document, input over 256 KiB or nested deeper than 64 elements is rejected, and the output is no longer indented. ### Security - `shell.exec` and `git.*` run under a stricter sandbox (`aivyx-confine` bumped to `671e0bd`, which fixes the 2026-10-04 subsystem-audit findings). What a confined command notices: - **No local daemons.** Unix sockets can't be opened and the session IPC variables are removed, so ssh-agent (`git push` over ssh), `git commit -S`, `docker`, `psql`/`mysql` over their default sockets, git credential-cache/libsecret helpers and `systemctl --user` fail. Previously the D-Bus session bus (`systemd-run --user`) let a confined command run code outside the sandbox. - **Private `TMPDIR`.** Each tool gets its own temp directory, exported as `TMPDIR`; the shared `/tmp` is no longer writable, and tools that hard-code `/tmp` fail. - **No leaving the process group.** `setsid`/`setpgid` fail, which breaks the `setsid` tool, interactive job control, Python `start_new_session=True` and test runners that use per-test process groups. Git's background auto-maintenance prints `fatal: setsid failed` and skips that run; the git command itself still succeeds. - **Carved directories.** Under the `home`/`full` access levels, when `~/.cargo/credentials(.toml)`, `~/.config/git/credentials` or `~/.git-credentials` (git's default credential store) exists (now always unreadable), `rm`/`mv` of entries directly in `$HOME` (and the other directories on the path to that file) fail, and a file created there can't be written until the next call. Deeper subdirectories are unaffected. `aivyx-pa doctor` and `aivyx-pa access show` say when this applies. - Namespaces (`unshare`, `bwrap`, rootless containers) and signals to processes outside the call are blocked. - Everything a `shell.exec` or `git.*` call starts is killed when the command exits, errors, times out or the turn is cancelled. A background job (`cmd &`, a git hook's detached child) no longer outlives the call, and one still holding the output pipe no longer stalls the call until its timeout. On Linux nothing can leave the group; on other platforms (no sandbox backend) a process that calls `setsid` itself escapes the kill. - `git.*` commands (and so git hooks) no longer inherit the daemon's environment: like `shell.exec`, they get only `PATH`, `HOME`, `USER`, `LANG` and `TERM`. A hook planted in a configured repo could read `AIVYX_PA_PASSPHRASE` and provider API keys. The operator's `GIT_DIR`/`GIT_CONFIG_*` variables no longer apply to `git.*` either. - New `[confine]` opt-outs, all default `false`: `allow_unix_sockets`, `allow_leaving_process_group` and `share_system_tmp` (see `examples/aivyx-pa.toml`). With `allow_unix_sockets` on, the sandbox no longer contains code execution. - `git.status`, `git.diff` and `git.commit` refuse a repo whose `.git` is a file (a linked worktree or submodule) instead of running git unconfined for it. A confined `shell.exec` in the repo could write such a file pointing at a gitdir it planted, so the next `git.*` call ran that gitdir's hooks outside the sandbox. The daemon warns about such entries at startup. ## [0.14.6] — 2026-10-05 ### Fixed - The prompt-injection check (`aivyx-injection-guard` `a0b0e7a`) now catches phrases broken up by line breaks, extra spaces, JSON escapes or invisible characters, and scans the whole of a fetched page or file instead of only its first 64 KB. - Ordinary text such as "you are now ready" or licence wording no longer trips it. ## [0.14.5] — 2026-10-04 ### Fixed - Checkpointing no longer switches itself off for good after a cancelled or timed-out snapshot: the stale lock git leaves on the checkpointer's private index is cleared on the next run (`aivyx-checkpoint` `0711690`). - Files matched by a bare deny pattern (`.env`, `.env.*`, `*.pem`, …) are now excluded from checkpoint snapshots and left alone by a restore. - A git configuration check that fails for a real reason now stops the git command instead of letting it run without its usual protections. - Skill files (`SKILL.md`) are read only when they are regular files, up to a size cap, so an unusual file can no longer stall or exhaust memory (`aivyx-skills` `4ee706e`). ## [0.14.4] — 2026-10-03 ### Security - Checkpoint snapshots and `/undo`-style restores no longer run any program a project's own git configuration names. The vendored `aivyx-checkpoint` dependency (bumped to `b3aef65`) now runs its git commands with repository-configured `clean`/`smudge`/`process` filters switched off, hooks disabled (including `reference-transaction`, which `update-ref` fires), fsmonitor off and commit signing off. Filters configured in your own global git config (such as git-lfs) still apply. ## [0.14.3] — 2026-10-02 ### Security - No tool can write (or delete) inside a `.git` directory any more — `fs.write`, `fs.delete`, `workspace.write`, `workspace.delete`, `workspace.note`, `data.xlsx.write`, and `data.pdf.write` all refuse a `.git`-component path unconditionally, before any confirm-first logic. This closes a path to redefining what `git` itself runs on a later command (`core.fsmonitor`, a `filter.*.clean`/`.smudge` entry, a planted hook) via the checkpointer's own unconfined `git add -A`. The `git.*` tools additionally run every git invocation with `-c core.fsmonitor=false` (plus `--no-ext-diff --no-textconv` on `git.diff`), and the vendored `aivyx-checkpoint` dependency is bumped to pick up the same hardening in its own `git add`. ## [0.14.2] — 2026-10-02 ### Fixed - Builds cleanly with Rust 1.99 (`async-trait` 0.1.92). The v0.14.1 tag's release build failed on Rust 1.99's new `double_must_use` lint, so no v0.14.1 CLI release was published; v0.14.2 carries all of v0.14.1's changes. ## [0.14.1] — 2026-10-02 ### Changed - Command Center briefing polls are cheaper: the daemon caches the audit walk across repeated `GetBriefing` polls, and the operator-activity clock persists to disk only when it meaningfully changes, not on every action. ### Fixed - A digest routine that fell back to a plain mission (no digest builder configured) no longer counts twice in the Command Center log. - The 24 h spend instrument now says when it's a lower bound: if any model used has no known price, Studio shows `$0.42+ · 24 h` instead of a total that looks exact but isn't. ## [0.14.0] — 2026-10-01 ### Changed - **Studio Command Center is now a logbook.** One readable column instead of stat cards: a ruled instrument strip (daemon, model, 24 h spend against your day budget, memory, audit chain), what needs you (mission and team approvals, pending proposals, failed routines and notifications, reminders coming up in the next 2 hours — with Approve/Deny/Done/Snooze right there), a first-person log of what your assistant did since you were last here, and what's coming up. Written from the record with fixed wording — no model involved. ### Added - **The `manual` autonomy level does what it says.** Every tool call that can change something — a note, a file, a command, a message — asks you first (Studio card, terminal prompt; reply "yes" in chat apps); looking things up doesn't. Scheduled routines aren't affected. It used to behave like `assisted`. - **IPC:** `GetBriefing`, `CompleteReminder`, `SnoozeReminder` queries (see `docs/DAEMON_IPC.md`); the daemon remembers your last activity across restarts. ## [0.13.0] — 2026-10-01 ### Added - **Approve or deny, and the reply carries on.** When the assistant wants to do something that can't be undone — delete or overwrite a file, commit, send an email, place an order — the terminal chat asks `Approve? [y/N]` and the Studio shows an *Approval needed* card. Approve and it does exactly what it showed you, then continues; deny (or no answer in 10 minutes) and it tells you it didn't. Chat apps keep "reply yes to approve". The assistant can never approve for you: the confirmation flag is set by Aivyx PA, not the model. ### Fixed - **Ctrl-C didn't cancel a reply through the daemon.** The daemon didn't read the connection while a turn ran, and the terminal's channel ignored the cancel anyway; both fixed, so Ctrl-C stops a reply at once. ## [0.12.2] — 2026-10-01 ### Fixed - **Integrations couldn't act from chat once confirm-first was on** (the default since 0.12.1): every email send, calendar or Drive write escalated, and plain chat has no approve-and-continue step. Your reply is now the approval — the first call asks, and in your next message that tool may run once. Unattended runs never get it. - **The in-process chat ignored `[budget]` and `[rate_limit]` caps**, as did the in-process Telegram, Discord and Slack fallbacks; they apply everywhere now. - **The spreadsheet and PDF writers replaced files unasked** on the model's own `overwrite: true`; with confirm-first on, replacing asks you first. - **The schedule tool told the model crons run in UTC** — they run in your local time — and gave no weekday guidance (numbered days count from Sunday = 1). It says both now. - **Hugging Face models pulled through Ollama** (`hf.co/Qwen/…`) lost their prompt strategy; family detection reads them now. - **A failed macOS service install left the passphrase plist behind**; it's removed, like the Linux path. - **The autonomy options overstated what they do.** The Studio's labels and a new *Autonomy* section in the guide describe what each level changes today. ### Changed - **`git.commit` and out-of-process confirm-first tools (like `kitchen.order.send`) follow the same rule as file deletes:** the model's `confirmed: true` only counts in a turn after the tool refused that same commit or call, so the operator's reply is what approves it. For a bridged tool the check happens on the daemon side, which knows the real conversation. ## [0.12.1] — 2026-09-30 ### Changed - **Deleting or overwriting a file always asks you first — and it has to be you.** Confirm-first (`[access] confirm_destructive`) was off at the default `sandbox` level, where the files you ask for now live, and even where it was on the model could pass `confirmed: true` on its first try. It now defaults on at every access level (the autonomy level decides; only `unleashed` turns it off, and an explicit setting still wins), and for `fs.delete` and overwrites `confirmed: true` only counts in a turn after the tool refused that same file — so the assistant has to ask and wait for your reply. ## [0.12.0] — 2026-09-30 ### Changed - **`aivyx-pa` finds its config from any directory.** `init` writes `~/.config/aivyx-pa/aivyx-pa.toml`, and every command looks there. A `./aivyx-pa.toml` in the current directory still takes precedence, so an existing setup keeps working unchanged; `AIVYX_PA_CONFIG_PATH` points anywhere else. Before, a config written in one folder was "not set up" from every other, including for the desktop app. - **`init` sets up the passphrase.** It asks for the passphrase that encrypts your store (twice) and keeps it in the OS keyring, so the daemon and the Studio start on their own from the first launch. Without a keyring it offers (default yes) to save it in `~/.config/aivyx-pa/daemon.env`, readable only by you — the file the background service already used — and `aivyx-pa`, the daemon it starts and `daemon install` all read it after the keyring. A file other users can read is refused. Decline, and aivyx-pa asks each time it starts. - **`init` offers the background service only where it can work** (a systemd user session, or macOS), and ends by checking that your model answers — after the last question, not before the passphrase. - **Every model is told where files live.** The note on the `fs.*` root (your files) versus the assistant's own notebook (`workspace.*`) only reached Ollama models; Lemonade, llama.cpp, Jan, broker and cloud models never got it, so a file you asked for could land in the notebook where `fs.*` couldn't find it, and the assistant couldn't say where it was. - **The Studio welcomes a newcomer.** The Command Center shows a start-here card until anything has run; an empty Chat greets you by the assistant's name with example first messages; routines read "every day at 07:00" instead of cron; the audit feed says "Model usage", not "LlmCost". - **`aivyx-pa connect` names the Google steps that gate sign-in** (the consent screen, adding yourself as a test user) and no longer asks for a redirect URI a Desktop-app client doesn't have. A blank Client ID cancels. - **The installer ends with the next step** (`aivyx-pa init`), and the README's quick start is installer → `init` → chat. A new guide page, *Chat apps & accounts*, covers Telegram, Discord, Slack and Google. - **`init` asks before turning on background routines**, listing them in plain words (default yes). Declined routines are still written, disabled. - **Quieter first launch.** An interactive chat no longer prints the config-provenance block (the daemon's log and `aivyx-pa doctor` still have it), and the keyring and persona-seed notices read plainly. ### Fixed - **The desktop app couldn't start on its own.** Its `.deb` (and `.app`) contained only the window, not the `aivyx-pa` daemon it runs, and when the daemon failed the window showed a bare connection error. The packages now include the daemon (the app prefers the one beside it, so the versions match), and a failed start shows a page with what the daemon said, how to fix it (usually `aivyx-pa init`), and a **Try again** button. The daemon's output goes to `~/.local/state/aivyx-pa/desktop-daemon.log`. - **Before any setup, `aivyx-pa` said `anthropic_api_key` was missing**, as if Anthropic were required. It now says aivyx-pa isn't set up yet and where it looked (`aivyx-pa: not set up yet — there's no config at …`). - **`init`'s service install defaulted to yes, then failed** on an empty passphrase in the middle of the "Next steps" list. It reuses the passphrase you just chose, and the next steps come last, in one list. - **A reply ran into the next prompt** in daemon-mode chat (`84.> `). - **`init` wrote the config readable by others for a moment** before tightening it to `0600`; it's created `0600` from the start. - **Chat without the daemon forgot the conversation.** In-process chat (a first run without a keyring, or `--no-daemon`) started every message from scratch; it now replays recent messages like the daemon does (`[agent] conversation_history_turns`). - **A failed service install left files behind** — a unit file and a copy of the passphrase. `daemon install` now checks for a systemd user session before writing anything, and removes what it wrote if a later step fails. - **The weekly digest ran on Sundays.** `init` promised Mondays but wrote cron day `1`, which the scheduler counts as Sunday; it's `Mon` now. Existing configs keep `1`, and the Studio labels it honestly. - **Before `init`, `doctor` reported a made-up Anthropic setup** and `studio` said to start the daemon; both now say aivyx-pa isn't set up. - **Smaller first-run rough edges:** the identity preview read "Never never …"; drafted names could be built from tasks ("EmailEva"); a launch line cited `[persona_seed]` when you'd declined seeding; diagnostic lines (skill injection, tool counts) printed into the chat. ## [0.11.0] — 2026-09-30 ### Changed - **Cloud-consent requests read as a request, not an error.** Instead of "LLM error: model routing: this needs a cloud model: `m@e` (tier)…", the assistant says which model, why, and roughly how many tokens would be sent, worded for where you are: send `/allow-cloud` in the terminal, press **Allow cloud for this conversation** on the Studio's new consent card, or — in Telegram, Discord and Slack, which can't grant it — allow it from the terminal or the Studio. Those chat apps no longer show "(no reply)" in front of it. - **The Studio is on by default, behind a sign-in token.** The daemon now serves the Studio at `http://127.0.0.1:7843` without any `[daemon]` setting. Loopback is reachable by every account on the machine, so unless you set `web_ui_auth_token` (or `web_ui_insecure_no_auth`), the daemon creates a random token in `studio-token` next to the store (0600, reused; delete it to rotate), and the agent can't read it (Ward). Sign in once with the link from `aivyx-pa studio`; `aivyx-pa daemon run` shows it too when started in a terminal (never in the journal or `docker logs`), and so does `aivyx-pa doctor`. **To opt out: `[daemon] web_ui = false`**, which now also wins over an explicit `web_ui_port`. Side effects of the default: the in-memory "studio" notify target now exists on every install that has no default target, and a config that already set `web_ui_host` beyond loopback with `web_ui_insecure_no_auth = true` but never enabled the Studio now serves it (both keys were signed; check that's intended). - **`aivyx-pa` starts the daemon for you.** An interactive `aivyx-pa` with no daemon running now starts one in the background (log next to the socket) and connects, when the passphrase is available without a prompt (the OS keyring, `AIVYX_PA_PASSPHRASE`, or the TOML). Otherwise it chats in-process and says why — and what's unavailable. Piped input and `--no-daemon` behave as before; `--provider`, `--mcp-server` and `--mcp-sse` keep a session in-process, since they apply to it alone. Daemons started this way (and by `aivyx-pa tui`) now run in their own process group, so ctrl-C or closing the terminal no longer kills them. - **`aivyx-pa init` detects every local runtime and never defaults to a cloud provider.** It probes Ollama, Lemonade, llama.cpp, Jan and the broker, lists the ones running first, and offers their models; with nothing found it asks you to choose (no pre-selected answer, unless `--template` names a local provider). Every provider is listed, and the separate "Also serve the Studio web UI?" question is gone. ### Added - **Model routing is visible everywhere, not just in the CLI.** - The Studio's status bar shows the model that answered the last message (hover for why), and a new **Models** screen lists the candidates, what's loaded, VRAM, and this conversation's model with a pin control. - `/models`, `/models why`, `/models refresh`, `/model ` and `/model auto` work in chat — the REPL, terminal UI, Studio and chat apps — with the same wording as aivyx-coder. (Chat without the daemon isn't routed, so there they say so.) - The terminal UI shows the routed model in its status bar; the REPL prints `routing → ()` when it changes. - New IPC: `ModelRouted` and `CloudConsentRequested` stream events, `GetRoutingStatus` and `SetRoutingPin` queries (see `docs/DAEMON_IPC.md`). Adapters that skip unknown event kinds are unaffected. - A new guide page, "Models & routing". - **Real help.** `aivyx-pa --help` (or `-h`, `help`) lists every command; `aivyx-pa --help` shows that command's usage; and an unrecognized argument points at the right help instead of a stale list. - **`aivyx-pa studio [--token]`** prints the Studio sign-in link (or just the token). The desktop app uses it to sign in by itself. - **`aivyx-pa doctor` checks local broker and mistral.rs setups.** With `provider = "broker"` it checks that `aivyx-broker` answers and that the llama-server behind it is up (a running broker in front of a stopped llama-server is reported as such). With `provider = "mistralrs"` it checks that the binary was built with mistral.rs support and that the configured GGUF exists, without loading it. Both previously fell into the cloud check and printed "an API key is configured". ### Fixed - **`aivyx-pa --headless` treated a cloud-consent stop as success (exit 0)** — nothing had run, but a cron or batch caller saw a completed turn. It now exits 3, the "refused" code, and a one-shot run explains how to give consent (in chat or the Studio, or by piping `/allow-cloud` and the task into one `--headless` conversation). - **The Studio's access-level menu always said the sandbox is `~/aivyx-pa-sandbox`**, even when `[fs] root` or `AIVYX_PA_FS_ROOT` moved it. It now shows the real folder while sandbox is the current level, and plain words otherwise. - **An interactive `aivyx-pa` could never use a running daemon** — it opened the store first and hit the daemon's lock. The daemon decision now comes before the store is opened. - **`/allow-cloud` in a session without the daemon was sent to the model** as an ordinary message. It now gets a local reply explaining that cloud escalation needs the daemon, and such sessions warn at startup when routing has a cloud endpoint. - **`provider = "jan"` sent every request to `/v1/v1/chat/completions`**: its default address (and the documented override) ended in `/v1`, which the OpenAI provider adds itself. The default is now `http://localhost:1337`, and a trailing `/v1` is dropped. - **The desktop app's approval watcher printed long runs of spaces** in its two "401" messages. - **The release pipeline can no longer publish a release without its CLI binaries.** cargo-dist's generated `host` job treated skipped build jobs as fine and didn't wait for the quality gate, so a dropped plan output (what hit 0.10.0) or a failing quality gate still published a green, binary-less release. `host` now depends on the quality gate and fails, before uploading anything, unless the gate and every build succeeded. `release.yml` is hand-edited for this, so `dist-workspace.toml` sets `allow-dirty = ["ci"]` (regenerate and re-apply the guard when upgrading cargo-dist). ## [0.10.1] — 2026-09-28 Republishes 0.10.0 with its CLI binaries and shell installer; no code changes. The 0.10.0 release published only the desktop, WSL and Docker builds: a literal bearer-token header example in its release notes looked like a credential to GitHub Actions, which silently dropped the release plan, so the CLI build jobs were skipped. See 0.10.0 below for everything in this release. ## [0.10.0] — 2026-09-28 **The product is now Aivyx PA** — a breaking rename of the binary, config file and data paths (see Changed for the manual migration). Also in this release: opt-in per-call **model routing** over the shared `aivyx-route` crate (discovery, residency, consent-gated cloud escalation, a tier classifier), new local providers (**Lemonade Server**, **aivyx-broker**), a **2026-09-16 security audit**'s fixes (three rated HIGH — upgrade promptly), a shared default skill library, local generation tools (`vision.*`), and YubiKey-backed federation identity. ### Changed - **The product is now Aivyx PA. BREAKING: binary, config file, and data paths all renamed, with no automatic migration.** The CLI binary is `aivyx-pa` (previously `aivyx`); the config file the CLI looks for by default is `aivyx-pa.toml` (previously `aivyx.toml`), and its passphrase section is now `[aivyx_pa]` (previously `[aivyx]`); the config/data directories moved from `~/.config/aivyx/` and `~/.local/share/aivyx/` to `~/.config/aivyx-pa/` and `~/.local/share/aivyx-pa/`; the `AIVYX_*` environment-variable prefix is now `AIVYX_PA_*`; and the OS-keyring service name used to store the passphrase moved from `aivyx` to `aivyx-pa`. Nothing above is copied, renamed, or migrated automatically — an existing install that wants to keep its data has to move it by hand before running the new binary: ```sh mv ~/.config/aivyx ~/.config/aivyx-pa mv ~/.local/share/aivyx ~/.local/share/aivyx-pa mv aivyx.toml aivyx-pa.toml # if using a CWD-relative config ``` then edit the moved config's `[aivyx]` section header to `[aivyx_pa]`, rename any `AIVYX_*` environment variables it relies on to `AIVYX_PA_*`, re-enter the passphrase once if it was stored in the OS keyring (a fresh `aivyx-pa` lookup under the new service name won't find an entry saved under the old one), and update the binary name in any systemd/launchd unit, shell alias, or script that invokes it. The passphrase itself and the encrypted store's contents are unaffected — this is a path/name rename only, not a re-encryption. - **The Studio web UI moves to the Wick & Compass identity** — new palette, Fraunces + IBM Plex Sans/Mono type, and the finished brand marks; glass/blur surfaces retired. - **The shared crates `aivyx-pa` depends on** (`aivyx-confine`, `aivyx-checkpoint`, `aivyx-kvcache`, `aivyx-recall`, `aivyx-injection-guard`, …) **are now BUSL-1.1**, matching `aivyx-pa`'s own license; pins bumped to their relicense commits. ### Added - **Lemonade Server as a provider and routing endpoint.** `[agent] provider = "lemonade"` targets a local Lemonade Server at `http://127.0.0.1:13305/api` by default (override with `[openai] base_url`, keeping the `/api` suffix), keyless unless `[openai] api_key` is set, with streamed token usage. `[routing.endpoints.*] kind = "lemonade"` adds one as a routing endpoint (aivyx-route af1b748): discovery lists its downloaded models from `/v1/models`, mapping its labels onto capabilities, and residency reads `/v1/health` to see which model is loaded. Lemonade holds one LLM at a time, so the endpoint is never assumed resident, whether it is the default endpoint or an extra one. - **Lemonade hardening (aivyx-route 5cdc111).** A model Lemonade labels `reasoning` is now known to think, so the routing classifier avoids it without a roster entry (an unlabelled model may still think: declare `capabilities = ["thinking"]` for those). A Lemonade base ending in `/v1` is tolerated by discovery and residency. `aivyx-pa doctor` with `provider = "lemonade"` now checks the server is reachable and the model is downloaded, and says which model is loaded, instead of the cloud "an API key is configured" note. - **The routing classifier avoids thinking models.** A reasoning model spends the classifier's few tokens thinking and never answers, so it always fell back to medium. Classification now prefers a model not known to think (aivyx-route 4ff5b7f), still using one when nothing else qualifies. Declare `capabilities = ["thinking"]` in `[[routing.models]]` for a thinking model the backend doesn't report. - **Local OpenAI-compatible backends now report token usage.** Keyless providers (`llamacpp`, `jan`, `broker`, and routed OpenAI-compatible or llama-server endpoints) never asked for streamed usage, so every local turn was billed as zero tokens: the cost ledger, `[budget]` caps and the routing classifier's billing all saw nothing. They now request it (`stream_options.include_usage`), which llama.cpp-family servers answer in a final chunk. Found in a live run against Lemonade Server. - **Model routing — the routing classifier is wired in.** New `[routing.classifier]` (`enabled = false`, `timeout_ms = 2000`). When on, a chat conversation with no pinned or sticky model yet is classified at most once per conversation, and only when a local model can serve it: a side call (`TaskKind::Classify`) asks whether it needs a small/medium/large model, bounded by `timeout_ms`; the answer becomes its soft tier, remembered for the conversation's later unstuck turns, and any failure, timeout, or unparseable answer falls back to medium. The side call runs on the model `[routing.tasks] classify` picks among the configured candidates: a small local model when there is one, but with a cloud `[agent]` provider it can be that provider's model. No new destination either way. `aivyx-pa routing status` and the `routing.status` tool now show whether it's on. Off by default: nothing changes without it. The classifier's own call is now billed too — its tokens are added to the turn's cost under its own model, alongside the main call's, instead of being silently thrown away. See `examples/aivyx-pa.toml`. - **Model routing (Part 3b) — `on_failure` escalation (Amendment A16).** New `[routing.escalation] on_failure` (default `false`). When on, a local turn that got stuck — `Looping`, or both tool-call repair rounds spent — arms that conversation's **next turn only** for cloud escalation, under the same gate as every other escalation: never a replay, never a mid-turn model switch, and a tainted conversation still never escalates. In `ask` mode the stuck turn ends with "The local model got stuck; send /allow-cloud and resend to retry on ``." An ignored offer lapses: a turn that stops for consent leaves a pending offer that `/allow-cloud` arms for the resend, and the next turn sent without it runs locally. Only the operator's own turns arm; trigger, cron, webhook and file-watch fires never do. The autonomous loop arms only with `mode = "auto"`: a Verdict FAIL (the completion judge rejecting a left-pending story) arms the next iteration, and a run the stall breaker would end gets exactly one armed rescue iteration first (also after a stall on a Foreman-delegated story: the rescue always runs as a solo turn, since a delegation can't carry the arm, and a delegation drops any pending Verdict FAIL arm); a second stall ends it, with "(after one on_failure rescue)" on the stop reason. New `EscalationArmed` audit entry per new mark; the escalation itself is a `CloudEscalation` with trigger `on_failure`. Marks live in memory only. With `on_failure = false`, nothing changes. See `examples/aivyx-pa.toml` and `docs/THREAT_MODEL.md` §4.12. - **Model routing (Part 4) — routing prefers loaded models.** A background poll, every 5 s, reads which models are already resident: Ollama's `/api/ps`, llama-server's `/models` (router mode), or aivyx-broker; the `default` endpoint's own model counts as loaded for free on a single-model server (llama-server, mistral.rs, or a broker fronting one). New `[routing] vram_bytes` adds host GPU memory when nothing else reports it. Residency is a soft signal — worth at most one tier-step, never a hard filter — so a model that needs a load can still win on capability/tier alone. `routing.status` and `aivyx-pa routing status` now show it: total/available VRAM, which endpoints are fully resident, and each candidate's load note (`loaded` / `needs_load` / `wont_fit` / unknown). No config required to keep today's behavior: without a source, residency has no opinion and ranking is unchanged. See `examples/aivyx-pa.toml`. - **Model routing (Part 3b) — consent-gated cloud escalation (Amendment A15).** Off unless a `[routing.endpoints.*]` entry has `kind = "anthropic"` or `"openai"`. Then a conversation whose call no local model can serve (`no_local_candidate`), or whose task kind is in `[routing.escalation] tiers`, may go to that cloud model under `mode = "ask"` (the turn stops and names the model; send `/allow-cloud` from a Trusted channel or run `aivyx-pa routing allow-cloud `, then resend — the grant lasts until the daemon restarts), `"auto"`, or `"never"`. A conversation that has touched sensitive data — a `[routing.sensitive] tool_prefixes` tool's output or error (by default every tool returning the operator's own data: mail, files, repos, shell, screen, history, finances, health, tasks, memory), memory recall, a sensitive channel — is tainted permanently (persisted) and never escalates, in any mode. Calls outside a conversation never escalate. Every decision is a `CloudEscalation` audit entry carrying a payload hash, never content; also `CloudConsentGranted` and `ConversationTainted`. `routing.status` and `aivyx-pa routing status|explain` show it. New `KeyDomain::RoutingTaint`. See `examples/aivyx-pa.toml` and `docs/THREAT_MODEL.md` §4.12. - **Model routing (Part 3a) — an optional `[routing]` section picks a model per call.** Off by default: without `[routing]` (or with `enabled = false`) nothing changes. With `enabled = true`, a `RoutedProvider` (over the shared `aivyx-route` router, the same `[routing]` shape `aivyx-coder` reads) chooses among the `[agent]` model (the reserved `default` endpoint), the `[[routing.models]]` roster, and models discovered on local `[routing.endpoints.*]` (Ollama, llama-server router mode, OpenAI-compatible). Routed calls: daemon conversation turns (`chat`, sticky per conversation), the completion judge and — when `judge_model` is unset — the auto-proposer judge (`judge`), and team mission planning (`plan`); every other call uses the `[agent]` model unchanged. No new cloud destinations: cloud endpoint kinds are rejected at startup, and cloud candidates are allowed only on a cloud `[agent]` provider's own endpoint. Connection errors, unknown models and HTTP 404/408/5xx cool a model down for 60 s and fall back to the next candidate. New read-only, Trusted-tier tools `routing.status` / `routing.explain` (registered only when routing is on; in the default role's backcompat floor), new offline `aivyx-pa routing status` and `aivyx-pa routing explain [--limit N]` commands, a `ModelRouted` audit entry per successfully routed call, and `LlmCost` recorded per model actually used. A request carrying a PDF is never routed (it goes to the `[agent]` model), and models on routed Ollama endpoints are assumed to have the window aivyx-pa serves them with — `num_ctx = min(trained window, 16384)`; a roster `context_window` can only lower that. Declare your `[agent]` model's `capabilities` and `context_window` in `[[routing.models]]`, or it ranks below discovered tool-capable models (startup warns). See `examples/aivyx-pa.toml` for the full reference. - **A shared default skill library (Aivyx-Skills Part 3) — two new read-only tools plus a `## Default skills` system-prompt section.** `skill_defaults.list` / `skill_defaults.read` expose the 5 skills bundled in the new `aivyx-skills` crate (e.g. `systematic-debugging`, `writing-plans`) — the same small, shared `SKILL.md`-format library crate adopted by `aivyx-coder`, distinct from the agent's own persona-chain `LearnedSkill`/`skills.*` system. Both tools are Trusted-tier and included in the zero-config default role's capability floor (read-only over a server-side-fixed set, no model-supplied path). A new, optional `[skill_defaults]` config section lets an operator layer `project_dir`/`user_dir` overlay directories on top of the bundled defaults (project overrides user overrides bundled, per same-named skill) — see `examples/aivyx-pa.toml` for the required `//SKILL.md` directory shape. - **`aivyx federation yubikey-init` — hardware-backed federation identity provisioning (Chapter Passport, Task 8).** Discovers a connected YubiKey's OpenPGP card, refuses to proceed on a still-factory-default PIN, generates an on-card Ed25519 keypair in the Signature slot, sets its touch-policy to fixed (physical touch required on every future signature), and writes the resulting `{instance_id, card_serial, public_key_base64}` binding record to an operator-chosen path. Off by default (`cargo build -p aivyx-cli --features yubikey`) — the underlying `aivyx-yubi`/`aivyx-federation` dependencies transitively need `libpcsclite`/`pcscd` at build and run time; without the feature the subcommand still parses but refuses with a clear rebuild message. See `docs/INSTALL.md`'s "Hardware-backed federation identity (YubiKey)" section and `docs/FEDERATION.md`. - **`provider = "broker"` — run through `aivyx-broker`**, a loopback daemon (default `http://127.0.0.1:8899`, `[broker] base_url`) that coordinates llama-server KV-cache slots across several local processes (e.g. `aivyx-pa` and `aivyx-coder`) sharing one server. Also accepted by `--provider`, and team specialists pass their slot hints through it. - **Local generation tools (Aivyx-Vision), in the `aivyx-vision` tool process.** `vision.generate_svg` (LLM-generated, sanitized SVG), `vision.generate_image` (local image generation via `mold serve`, with a `[mold]` section in the tool process's own config), and `vision.generate_3d` (registered but not implemented yet: it always fails with a clear error). One new scope, `vision.generate`, SemiTrusted-reachable. See `crates/aivyx-vision/README.md` and `docs/TOOLS.md`. - **`[kvcache] store_path`** (env `AIVYX_PA_KVCACHE_STORE_PATH`) makes the KV-cache store's location configurable, and Ward now denies agent reads of it (previously unprotected: its file names match none of Ward's patterns). - **`[agent] injection_scan_enabled` / `injection_scan_exempt`** tune Picket's active prompt-injection scan (on by default; exempt named tools). Bulwark's fencing is never affected by either. - **CI now fails fast, by name, if a workspace git dependency ever silently reverts to private.** `scripts/check-git-deps-public.sh` (Phase 192) is wired into `quality-gate.yml` as an early step — closes Chapter N (Release & Distribution Integrity). See `docs/archive/phases/PHASE_197.md`. ### Security Fixes from a full ecosystem security audit (2026-09-16). **HIGH** first: - **Rampart (the SSRF guard) had two bypasses.** IPv4-mapped IPv6 addresses (`::ffff:169.254.169.254`, `::ffff:127.0.0.1`, …) were never unwrapped, and Rampart's hand-rolled URL parser could see a different host than the one reqwest connects to (a backslash in the authority, a non-dotted IPv4 literal). Host parsing now uses the same WHATWG parser reqwest does, and mapped addresses are classified as IPv4. - **Connected integrations no longer auto-grant their write/send/delete scopes.** A default `aivyx-pa connect gmail` granted unqualified `email.send` with no confirmation. The zero-config floor now withholds `email.write`, `email.send`, `drive.write`, `notion.write`, `obsidian.write`, `n8n.write`, `contacts.write` and `calendar.write` unless granted explicitly, and a tool process asking for escalation now actually escalates (it used to surface as a plain tool failure). **Grant those scopes in your role if a workflow relies on them.** - **`[access] confirm_destructive` now takes effect.** The gate existed but no production agent was built with it; every agent now honors it (on by default at any access level above `sandbox`), so irreversible operations ask for confirmation. - **Webhook-triggered turns now require authentication and run at `Untrusted`, not `Trusted`.** Part of the 2026-09-16 security-audit fixes: previously any local process (or any webpage the operator merely visited, via a CORS "simple request") could fire an agent turn through the webhook listener with no shared secret, and that turn ran at the same trust tier as a local CLI session. Every webhook now carries a `secret` (generated once at creation, shown only then) that callers must present as a bearer token in the `Authorization` header, and the triggered turn runs at `Untrusted`. **If an existing webhook's prompt relies on tools gated above `Untrusted`** (e.g. `fs.write`, `shell.exec`), those tool calls will now fail their capability check after upgrading — grant the narrower scope the webhook's task actually needs via that role's `capability_scopes`, or rework the prompt, rather than restoring blanket trust. Existing webhook records created before this change have no `secret` on disk and, by design, an unset secret never authorizes — re-create any such webhook to get a fresh secret and keep it callable. The secret is also no longer shown in LLM-visible tool output. **MEDIUM:** - **Telegram, Discord and Slack now authorize each sender.** All three gave every sender SemiTrusted; an allowlisted sender now gets SemiTrusted and anyone else Untrusted, as `docs/THREAT_MODEL.md` specifies (Slack's `team_id` is now actually checked, daemon path included). - **`shell.exec` and `git.*` output is now fenced (Bulwark) and scanned (Picket)** like other untrusted tool output. - **Tool processes can no longer claim scopes the operator didn't expect.** A new `[[tool_process]] expected_scopes` map rejects a registered scope exceeding the configured one and, when non-empty, refuses tool names it doesn't list. Tool processes also now start with a scrubbed environment (proxy variables kept). - **Local secrets and IPC are written safely.** The daemon's Unix socket is bound at 0600 atomically (its directory at 0700); `config.toml` is tightened to 0600 on load and written atomically; `daemon.env` is written atomically at 0600, only after checking the OS keyring; and the TOML passphrase is no longer copied in plaintext during load. - **The audit chain now detects tail truncation** through a persisted chain anchor (last sequence number + MAC); a corrupt anchor fails closed instead of being ignored. - **`toolkit.health_check` URLs are checked against Rampart.** - **Pack bundles are bounded before signature verification:** entries capped at 512 MiB, decompression at 20× the input, and manifest `bin`/`team_config` paths validated against traversal. - **The federation replay guard is hardened:** per-entry expiry instead of a global flush (up to ~59 s of replayability), a 5 s future-skew bound, and a 10,000-entry cap. ### Fixed - **Telegram/Discord/Slack's standalone (`--no-daemon`) session paths now honor the operator's `[agent]` config.** Previously these three in-process-fallback paths constructed every agent via `TurnSafety::default()`, silently ignoring `turn_timeout_secs`, `cycle_detection`, `injection_scan_enabled`, and `injection_scan_exempt` — including a configured `injection_scan_enabled = false`, which had no effect on these paths. They now route through `TurnSafety::interactive(...)` like every other agent construction site. Daemon-mode dispatch was never affected (it never constructs agents client-side). - **First-launch safety: running `aivyx` before `aivyx init` on a genuinely fresh machine no longer silently creates a permanent, unconfirmed encrypted store.** Previously a bare `aivyx` invocation with no config anywhere would create the sandbox/storage directories, prompt once (unconfirmed) for a brand-new passphrase, and open a real encrypted store — all before config validation ever ran — so a later, real `aivyx init` run could write a config pointing at that same orphaned store under a different passphrase, failing to decrypt it with no indication why. `run()` now validates first when no store exists yet, offering to run the setup wizard inline (interactive) or failing immediately with a clear `aivyx init` pointer (non-interactive) — zero side effects either way. `aivyx init` also now warns and requires confirmation before writing a config that points at a storage path where a store already exists, and the first-time interactive passphrase prompt for a brand-new store now requires confirm-reentry (matching `aivyx keyring set`'s existing shape) instead of accepting an unconfirmed, unrecoverable-if-mistyped entry. ## [0.9.4] — 2026-09-05 **Milestone: the first real, working GitHub Release since `v0.8.3`.** Ships Chapter N's root-cause and release-cutting work (Phases 192-193, opened this same day after discovering `v0.9.0` never actually published — see `docs/archive/phases/PHASE_192.md` and `PHASE_193.md`; the chapter's closing phase, 197, is the `[Unreleased]` entry above) and closes Chapter Picket (the prompt-injection tripwire, `PHASE_194.md`-`PHASE_196.md`) in one release. ### Added - **Active prompt-injection tripwire (Chapter Picket).** `aivyx-injection-guard`, a new shared crate extracted from `aivyx-coder`'s existing production tripwire, scans untrusted tool output for known injection phrasings and escalates the turn via the existing `TurnOutcome::Escalated` → `ApprovalGate`/`HeadlessRefusal` path — a side-channel signal checked after the tool's real outcome is recorded, so a mutating tool's audit trail always reflects what actually executed. Complements, doesn't replace, Bulwark's existing passive `fence_untrusted_output` labeling. See `docs/THREAT_MODEL.md` and `PHASE_196.md`. ### Fixed - **`aivyx-confine`'s `require_enforcement` check incorrectly required `RulesetStatus::FullyEnforced`.** GitHub's own hosted-runner kernel only ever achieves `PartiallyEnforced` at the crate's hardcoded Landlock ABI version, which made every release build fail its own quality gate before reaching the real cross-compile work. Now only `NotEnforced` fails closed. - **`aivyx-confine`'s syscall blocklist referenced `libc::SYS_kexec_file_load`, absent from libc's musl bindings for aarch64/riscv64,** breaking the `aarch64-unknown-linux-musl` release target's build. - **The WSL release workflow's retry budget for pulling the appliance base image (10 minutes) was too short for real-world timing;** raised to 30 minutes to match the sibling wait-for-release retry loop in the same workflow. - **The three private git dependencies (`aivyx-confine`, `aivyx-checkpoint`, `aivyx-kvcache`) that silently broke every `v0.9.0` release workflow — and any outside contributor's build-from-source path — are now public** (the CI regression guard that enforces this going forward ships in `[Unreleased]` above, Phase 197). ## [0.9.0] — 2026-09-03 **Milestone: the Interface Polish phase is complete — v0.9.0 cuts as the capstone.** `docs/V09_PLAN.md`'s own stated rule ("v0.9.0 cuts as the capstone when the polish backlog is empty") is now satisfied: all 8 rows of that plan are done. This release folds in the two months of continuous work since `v0.8.3` that shipped without its own version cut — **Gatehouse** (Studio remote auth: the refuse-to-bind interlock, the `web_ui_insecure_no_auth` escape hatch, first-boot token generation; `docs/GATEHOUSE.md`), **Freight** (signed pack bundles — format core, the `aivyx pack` CLI, the Kitchen worked example; `docs/FREIGHT.md`), the full **Vitrine** operator walkthrough across all 15 Studio screens + the TUI + the desktop shell (`docs/VITRINE.md`), and the resulting 8-sub-project polish backlog (`docs/POLISH_WAVES.md`): a small backlog sweep, `/classic` retirement, Repertoire completions, agent turn-quality fixes, Missions polish, a UI modernization pass, config-write surface-area completions (memory profile / embedding / proactive settings, reflection-schedule CRUD), and tool/server call-stat observability. Also ships as part of this release: **Chapter Mission Control** (live LEAD/specialist mission graph, click-to-drill-in, gate approve/reject, abort, pause/resume — see Added below) and **Phase 185's Terminal TUI foundation** (`ratatui`/`crossterm` replacing the line-based REPL, plus Missions / Dashboard / Audit / Tools panels; `docs/ROADMAP.md`'s Chapter I). Full phase-by-phase detail lives in those docs, not reconstructed here. ### Added - **KV-cache persistence for `llama-server` users (`provider = "llama_cpp"`).** A fresh daemon process's first turn on a system prompt + tool-def combination it has seen before can now skip re-prefilling that stable prefix, restoring it from disk instead — requires `llama-server` started with `--slot-save-path` pointed at `/aivyx/kvcache/slots`; see `docs/INSTALL.md`'s "KV-cache persistence" section. No effect on Ollama or Jan. - **Pause and resume a running team mission (Chapter Mission Control).** `aivyx team pause ` requests a graceful pause at the mission's next wave boundary — unlike abort, this is resumable. `aivyx team resume ` continues a paused mission from its preserved checkpoint. - **A Mission Control screen (`aivyx-web`, Chapter Mission Control).** A new "Mission Control" nav destination shows one active mission's LEAD/specialist graph live — click a specialist to drill in (current step, declared capability scopes, the NT-02 "inert" hint) — plus the first UI controls for gate approve/reject, abort, and the new pause/resume. ### Changed - **`shell.exec` and the `git.rs` tools (`git.status`/`git.diff`/ `git.commit`) now confine every spawned command with Landlock + seccomp-bpf, on by default.** Previously, at `[access] level = "sandbox"`, `shell.exec` could read and execute anywhere on disk the operator's OS permissions allowed, subject only to the string-level `[access] guard_sensitive_paths` check — a command that got past that guard had the full authority of the daemon's own OS user. Every spawned child now additionally runs under kernel-level confinement (`aivyx-confine`): filesystem writes are scoped to the tool's own working-directory root (`shell.exec`'s `cwd_root` / the git repo being operated on) plus a fixed system/toolchain read list, and a seccomp-bpf denylist blocks a set of syscalls with no legitimate use in a coding agent's spawned commands (`ptrace`, `mount`, `bpf`, `perf_event_open`, and others). New `[confine] require_enforcement` config key (default `true`) controls what happens if Landlock itself fails to establish a ruleset on a given machine: fail-closed (refuse to run the command, the default) or fail-open (run unconfined, for operators on a kernel without Landlock support who still want `shell.exec` usable). It does not provide a way to turn confinement off when Landlock is working normally. Linux only — non-Linux builds are unaffected (no confinement, same as before). See `docs/THREAT_MODEL.md` §5.6/§6 and `docs/INSTALL.md`'s `[confine]` section. ## [0.8.3] — 2026-07-08 ### Fixed - **Studio footer showed the wrong version number.** The WASM bundle released in 0.8.2 was built before that version bump, so `env!("CARGO_PKG_VERSION")` baked "v0.8.1" into the Studio's status bar even though the daemon binary itself correctly reported 0.8.2. No functional change — the bundle is rebuilt from the same 0.8.2 source, now after the version bump. ## [0.8.2] — 2026-07-07 ### Added - **Studio Gallery — view images your assistant generates.** A new screen renders images produced through a `comfyui`-named `[[mcp_server]]` (e.g. the community `comfyui-mcp-server` bridge in front of a local ComfyUI instance). The daemon reads ComfyUI's own `/history` API directly and serves bytes to the browser through a new authenticated `/studio-asset` proxy route — no MCP-call plumbing involved, and the browser never needs to reach ComfyUI's own (loopback-only) port directly. Each image shows the prompt that produced it (traced through the submitted workflow graph, not guessed by node order) and when it was made, newest first, with a click-to-enlarge view. The screen is a no-op empty state when no `comfyui` server is configured. ## [0.8.1] — 2026-07-05 **The Vitrine harvest: one day of live operator walkthrough, fifteen-plus bugs found, nearly all fixed same-day and re-proven on the rig.** The first five walkthrough sections (Gatehouse baptism, Command Center, Chat, Missions, Memory, Skills) drove every fix in this release; the standout theme is honesty under pressure — silent failures now speak, stochastic judges get deterministic backstops, and skills finally get used. ### Added - **Conversation-history replay (Chapter Thread).** Interactive-session turns now replay the session's recent user/assistant messages as real conversation history, so follow-ups like "did you find the correct code?" resolve against what was actually said. Default-on (`[agent] conversation_history_turns = 8`; `0` restores fresh-context turns exactly); trigger-fired turns are structurally unaffected, and durable memory remains the persistence layer. - **Skill trigger injection — skills are finally used.** Each turn, the best trigger-matching approved skill's procedure is injected into context alongside memory recall (embedding cosine with live-calibrated 0.50 floor; token-overlap fallback for embedding-free installs). Local models never took the `skills.list`/`skills.invoke` indirection, so a fresh agent's skills were dead weight. Verified end-to-end: teach → use on the next turn with no restart; update → use; `[skills] trigger_injection = false` opts out. - **Deterministic identifier backstop in the completion judge.** When a goal names uppercase identifiers (ICAO codes, tickers) and the majority appear nowhere in the deliverable or evidence, the verdict rejects before any LLM opinion — after a live mission's judge hallucinated a PASS over a brief about entirely different airports. - **Team specialists can use the operator's MCP servers.** The researcher/analyst/verifier roles now receive qualified per-server `mcp.call` grants and see bridged MCP tools; previously the whole team was structurally MCP-blind and approximated one weather-tool call with sixteen raw page fetches. ### Fixed - **Web search reports backend refusals instead of silently returning nothing.** DuckDuckGo answers bot-flagged traffic with HTTP 202 + a challenge page; the zero-config backend parsed that to an empty result set, and the model either went mute or invented answers. - **Reflection proposals were scope-dead on a clean install** — the default capability floor granted neither `reflection.propose` nor `persona.propose`, so every organic proposal died with a denial. - **Reflection no longer fires on every daemon restart** (the last-fired anchor was epoch, not boot). - **Clean systemd stops no longer report "unclean shutdown"** — the daemon now handles SIGTERM like Ctrl-C. - **The planner's context budget honors an explicit `[ollama] num_ctx`** instead of an 8k class default, and **giant tool results are capped** (~half the context window, with an explicit truncation marker) — one raw page fetch used to become an un-prunable 30k-token message that pushed the request past the real window and silently truncated the system prompt server-side. - **Pruning can no longer discard the turn's own question.** The task message is pinned through context pruning; a fat tool turn used to lose its task and reset to a greeting mid-mission. - **Mission retries are bounded again.** The Reprise attempt counter is persisted on the mission record; an approval gate used to reset a driver-local counter on every "Approve", producing an infinite reject-retry-gate loop. - **The trigger-injection embedding cache keys on trigger text**, not `name@version` — a Tutor `skills update` preserves the version and would have served the stale trigger embedding forever. - **`capture-note`'s starter trigger is written in instance nouns** (dates, expiries, favourites) so real volunteered facts actually match it; with injection live, a stated fact is now saved to memory and confirmed in one line. ## [0.8.0] — 2026-07-04 **Milestone: the self-learning arc is real, live-proven, and honest.** v0.8.0 marks core refinement converged: across the v0.7.38–v0.7.41 run and this release, the complete self-learning loop was exercised live on real hardware — skills invoked, effectiveness measured with honest signals, underperformers refined, new skills authored from the agent's own consolidated knowledge, every change gated by operator governance on the signed persona chain, and the restraint properties (no churn, no re-nagging, no twin proposals) verified under soak. This begins the run-up to the v0.9 interface-polish phase. ### Fixed - **Cron schedules now fire on the operator's local wall clock.** The init template always said "local time," but the engine evaluated cron fields in UTC — on any non-UTC host every routine fired hours off intent (a "nightly" reflection at 10 a.m. local). Both schedulers now share one local-time evaluation. One-time upgrade effect: a schedule whose local-time tick already passed today fires a single catch-up. - **Skill authoring no longer proposes twin skills from near-duplicate topic names** ("triathlon" and "triathlon-basic"): candidacy skips a topic whose normalized name is contained by — or contains — any covered skill or prior authoring proposal. Synonym-named twins remain the operator's call in the proposal inbox, by design. - **The memory conflict detector no longer flags change-over-time sequences as contradictions** ("file X present" then "file X removed" is the world changing, not a conflict) — the recurring false-positive class produced by daily environment observations. - **The agent can now see its own routines**: `schedule.list` joins the default-role capability floor (read-only, like `skills.list`). The schedule *write* tools deliberately remain outside the floor pending an autonomy-gating decision. ## [0.7.41] — 2026-07-04 **Chapter Wire** — headless multi-turn sessions. Pipe newline-delimited turns into `aivyx --headless` and they run as consecutive turns of one daemon session, making session-scoped behavior (session-partitioned memory, recall's conversation window, consecutive-turn signals like the correction detector) reachable from a terminal, a cron line, or a test harness for the first time. ### Added - **`printf "first\nsecond\n" | aivyx --headless`** — bare `--headless` with piped stdin reads each non-empty line as one turn of a single session. Fail-fast for batch callers: the stream stops at the first non-completed turn and exits with the existing headless code (`3` gate-refusal, `1` other failure); all turns completed exits `0`. The one-shot `aivyx --headless ""` form is unchanged, and bare `--headless` on an interactive terminal errors with a hint to pipe. Every turn keeps the unattended posture (gates refuse, never park). ### Notes - A session carries session-partitioned memory, the conversation-window relevance feed, and turn adjacency — turns are deliberately fresh-context, with continuity flowing through memory and recall rather than transcript replay. `docs/WIRE.md` records the distinction. ## [0.7.40] — 2026-07-04 **Chapter Strop** — skill effectiveness now measures how well a skill *serves*, not how often it runs, so the self-learning refinement loop fires organically at its default thresholds. Live-verified on the test rig: one hollow skill turn (result claimed, tool never called) was enough for the agent to propose a sharper version of that skill — with no healthy skill swept up. Refinement remains propose-only and operator-governed. ### Changed - **A completed turn is no longer an automatic +1 in the per-skill effectiveness ledger.** The fold now reads Candor's verdict: a turn that completed but carries an unfulfilled-claim annotation (the skill's result was claimed while the fulfilling tool was never called — the dominant local-model failure) folds negative. Failed/looping turns stay negative; clean completions stay positive. Ledger format and thresholds unchanged. - **Corrected skill turns count against the skill.** On the reflection cadence, a completed skill turn the operator immediately reworked (the correction ledger's definition) retro-folds negative — the correction cross-reference the original Whetstone design specified. Each correction folds exactly once across the repeating lookback windows via a per-schedule watermark keyed on the follow-up turn. ## [0.7.39] — 2026-07-04 The self-learning loops now actually run for a fresh install. A live dogfood of the skill self-learning arc (Whetstone refinement + Praxis authoring) found five gaps that together kept it structurally dark; all are fixed and each was verified live on the test rig — including the first-ever agent-refined skill and first-ever knowledge-authored skill landing on a real persona chain under operator governance. ### Fixed - **The default role can now use its own skills.** `skills.list` and `skills.invoke` were never granted in the default-role capability floor, so every fresh install's agent was denied on calling them — no skill invocation could be audited and the skill-effectiveness ledger could never accumulate. Both scopes (read-only over the agent's own operator-approved skill set) are now in the floor; `skills.propose` deliberately stays out. - **Fresh installs get a real reflection schedule.** `aivyx init` never wrote a `[[reflection_schedule]]`, so the whole reflection-pass family (persona proposals, consolidation, skill refinement, skill authoring) never fired — the starter routine *named* "nightly-reflection" is a plain prompt turn, not the reflection scheduler. init now plants a daily, skip-when-idle reflection schedule (enabled on local providers, written-disabled on cloud). - **Skill authoring can actually find candidates.** Praxis gated candidacy on the wiki topic name appearing verbatim as a knowledge-graph subject — but topics are slugs while graph subjects are LLM-extracted phrases, so the lookup never matched and the pass was silently starved. Candidacy now joins on normalized word overlap, verified against the exact shapes observed live. - **The loop's bookkeeping no longer pollutes the knowledge base.** The `loop:progress` progress log had leaked a knowledge-wiki page; `is_internal_topic` now covers the loop's reserved prefix and the wiki sweep purges any already-leaked internal pages. - **Skill authoring skips the agent's own journal.** The authoring pass could synthesize a "skill" from a scheduled routine's own memory writes (the agent talking to itself). Configured routine and reflection schedule names are now excluded authoring topics. ## [0.7.38] — 2026-07-04 Dogfood-surfaced reliability fixes from the 2026-07-04 single-agent loop run, each live-verified on the test rig before release. ### Fixed - **A planner-named unknown specialist no longer kills the story.** When the LLM planner reaches for a generic role word that isn't in the roster ("Operations", "QA-Engineer"), `SpecialistPool::resolve` now maps the word to a capability and routes to the best-fit, least-privilege roster member instead of hard-failing the whole delegation. Truly unmatchable names still error, and naming the lead is still rejected; Keystone's artifact gate keeps a bad best-effort attempt honest. - **`aivyx loop add` no longer trips over the 200-character title cap.** A second positional argument is now accepted as the story body (`aivyx loop add [body]`), and an over-long title auto-splits at the last word boundary that fits — the overflow moves into the body with a printed note instead of a bare error. The cap error itself now says where the detail belongs. ### Changed - **`memory.write` supersedes near-duplicate rewrites.** A refinement of an already-stored fact (same content with a citation appended, a punctuation or case tweak) now replaces the older entry instead of piling up beside it as recall noise. Thresholds are deliberately conservative — distinct facts that merely share words ("…Tuesday…" vs "…Thursday…") stay side by side — and the new entry always lands before any older one is removed, so a mid-way failure leaves noise, never loss. Superseded sequence numbers are reported in the tool output. ### Security - Bumped the transitive `cmov` dependency 0.5.3 → 0.5.4 (Dependabot alert: wrong Cmov/CmovEq results on aarch64 when register high bits are set; sits under the audit chain's HMAC stack). ## [0.7.37] — 2026-07-04 ### Added - **A WSL distribution now ships with each release (`Aivyx.wsl`).** Windows users get a one-command-import WSL2 distro with the daemon and all tool binaries pre-installed — no in-distro install step. It's the cheapest real "Aivyx on Windows" path: it sidesteps the deferred native Windows port because the daemon's Unix-domain-socket IPC and `0600` secret-at-rest posture both work unchanged inside WSL2's real Linux kernel. Built by reusing the Harbor appliance image (no duplicate compile): `Dockerfile.wsl` layers WSL config (default user, `/etc/wsl.conf`, `/etc/wsl-distribution.conf`, a first-launch OOBE script) and `.github/workflows/wsl-release.yml` exports the root filesystem to the release. Install with `wsl --install --from-file Aivyx.wsl` (WSL 2.4.4+) or `wsl --import`; see docs/INSTALL.md "Windows". ## [0.7.36] — 2026-07-04 ### Added - **Specialist tool calls are now traced in the mission journal (Chapter Spyglass).** A Nonagon specialist's sub-turns previously emitted nothing, so you could not see whether (say) the writer actually called `workspace.write` — the exact blind spot that made the recent team capability bugs hard to diagnose. Each specialist channel now logs its tool activity, labelled by role: `aivyx team: [writer] → workspace.write` / `← workspace.write — completed`. Pure observability (logging only; no behavioural change). ### Changed - **The analyst can now fetch data itself.** The Analyst role gained `web.fetch` (+ the `net.fetch` capability) alongside `web.search`, so it can pull datasets, APIs, and reference pages directly rather than relying on search snippets. - **The `ops` role is now a verify-by-execution `verifier` (Verifier/QA).** The old Operations role (`shell.exec` + `fs.read`) was a strict capability subset of the coder. It is recast as a Verifier that proves a deliverable works by *executing* it — running the tests, reproducing the claim, checking data against its source — and reports PASS/FAIL with evidence without modifying the work (same `shell.exec` + `fs.read`, plus `net.fetch` to reproduce against remote sources). This fills the gap left by the read-only reviewer (which cannot run anything) while keeping the team a nine-role Nonagon. ## [0.7.35] — 2026-07-04 ### Fixed - **A team mission retries once when it produced nothing (Chapter Reprise).** A specialist sometimes claims to have written the deliverable but doesn't (model-ceiling variance — a fresh attempt often succeeds where the first missed). When the artifact gate (Keystone) finds no deliverable, the mission now re-drives once from a cleared checkpoint before giving up, converting many such misses into real completions instead of honest-but-empty rejections. Bounded (one retry) so a genuinely impossible mission can't loop; only when verification is on. - **A reviewer gate no longer kills a whole team mission (Chapter Ombudsman).** An in-DAG *auto* gate would abort the entire mission on any reviewer `FAIL` — so a research→review→write plan died at the review step, before the writer ever produced the deliverable. An auto gate is now **advisory**: its verdict is recorded and fed to downstream steps as context (a "FAIL: add X" review makes the next step better), and the mission continues; final quality is enforced end-to-end by the artifact gate (Keystone), and a **human** gate still blocks for operator approval. Live-verified: a multi-step mission now runs its write step instead of aborting at the review. - **Team specialists can actually use their tools (Chapter Ensemble).** The root cause of "team missions do nothing": a specialist's caps are `declared ∩ what the lead grants`, but the coordinator lead held only `[memory, team.delegate]` and the roster declares *bare* scopes (`fs.write`, `net.fetch`) that can't match the daemon's *qualified* floor (`fs.write:<root>/**`, `net.fetch:<url>`) — so every specialist was attenuated to memory-only and couldn't write files, fetch, or run commands. The mission lead now holds the daemon's real authority and each specialist inherits the lead's floor scopes for the bases its role declares. Stays least-privilege (a reviewer remains read-only; a writer gets no network/shell) and `⊆ daemon floor`. Live-verified: a writer now writes its file to the workspace and the mission completes `Done`. (Supersedes the workspace-only Anchorage fix with one general mechanism.) - **Team missions left `Executing` on a daemon restart no longer become permanent zombies (Chapter Reckon).** There is no resume machinery for team missions, so a mission interrupted mid-flight would sit `Executing` forever in `team list`. On reload, an interrupted `Executing` mission is now reconciled to `Halted` with a truthful reason ("interrupted by a daemon restart") and persisted; `AwaitingApproval` (a real operator pause) is left untouched. - **The planner routes save/create-file steps to a specialist that can actually write (Chapter Handoff).** The decomposition prompt hid specialist capabilities, so the coordinator could assign a `save_file` step to a read-only role (e.g. `Operations`, which has `shell.exec, fs.read` but no `fs.write`) — a step that then cannot produce the deliverable. The planning roster now tags each specialist `[writes files]` / `[writes memory]` from its scopes, with a rule that persist steps must go to a write-capable specialist. ### Changed - **A team mission is `Done` only if its deliverable exists (Chapter Keystone).** Live Nonagon dogfooding found the team subsystem's sharpest bug: a mission ran every step to "done" — including a `create_file` step — and reported `phase: done`, but the file existed nowhere. Steps completed when the specialist *sub-turn returned*, with no grounding that the deliverable was actually produced. Now, when a mission runs to completion, it is graded against its goal — grounded on the workspace/memory artifacts it produced (the same `CompletionJudge`) — and a mission that claims done but produced nothing is flipped to `Rejected` with the verdict on the audit chain. Opt-in via `TeamRunDeps.verify_missions` (the daemon enables it; default off ⇒ pre-Keystone behavior); best-effort and fails-open (no grounding / LLM error ⇒ keeps `Done`). Covers both direct `team start` and loop/Foreman missions. ## [0.7.34] — 2026-07-03 ### Changed - **Accord detector precision: fewer borderline false positives.** The Soul- contradiction judge's prompt was teaching itself to over-flag — it used "warm" vs "be candid" as a *positive* example, so it intermittently flagged complementary traits on a perfectly coherent Soul. Rewrote the prompt around a sharp mutual-exclusivity test ("is there NO situation in which both can be honored at once?") with explicit NOT-a-contradiction cases (complementary traits, default+exception, broad tone next to a specific rule). Live-verified on the rig: the real coherent Soul went from intermittent 1–2 false positives to 8/8 clean, while a genuine contradiction (one-line vs multi-paragraph) is still caught — recall preserved. ## [0.7.33] — 2026-07-03 ### Added - **Accord skill-layer coherence: contradiction detection across learned skills.** The contradiction pass now also considers the agent's learned *skills* (shown to the judge as `skill "X": when <trigger> → <procedure>`), so two skills that give opposite instructions for the same situation — or a skill whose procedure contradicts a facet or operator constraint — are flagged by `aivyx persona conflicts` alongside facet conflicts. Resolution removes the losing skill BY NAME (reusing the Repertoire operator-forget primitive), reversible via `aivyx persona revert`; dismiss ("keep both") works too. Live-verified: seeded two opposite reply-style skills → detected → resolved. - **Accord prevent-at-write: the Soul can't accrete a contradiction (coherence gate at approval).** Beyond finding contradictions after the fact, approving a persona proposal now runs a targeted coherence check first: if the proposed facet would contradict an existing facet (or an operator Profile constraint), the approval is refused with the specific conflict named — so the operator rejects it, resolves the existing facet, or accepts the tension. The override reuses Accord's dismiss ("keep both"): `aivyx persona dismiss <id>` then re-approve, so a fuzzy-detector false positive is never an unescapable lockout. Best-effort (no LLM ⇒ no gate); only gates operator approvals of new list facets. Reuses the live-verified detector; the dismiss set is the escape hatch. - **Accord "keep both": dismiss a Soul contradiction (false positive).** The contradiction detector is a fuzzy LLM pass, so it can flag a *legitimate* nuance ("concise by default" + "detailed when asked") — which `persona conflicts` would otherwise re-surface every run. `aivyx persona dismiss <id>` now records the pair's stable id (in the existing encrypted dismissals domain, namespaced `soul:` so it can't collide with a memory dismissal) and future detection passes suppress it; nothing is removed from the Soul. Mirrors Chapter Concord's `memory dismiss`. New `DismissSoulConflict` IPC verb. ## [0.7.32] — 2026-07-03 ### Added - **Soul-coherence: contradiction detection over the Persona (Chapter Accord).** The identity-stack sibling of Chapter Concord (which does this for memory). The Soul accretes reflection-learned facets; the lifecycle layer merges near-*duplicates* and decays the *unreinforced*, but nothing flagged two approved facets that flatly *contradict* — a seeded "communicate concisely" living next to a later "give thorough, detailed explanations", both injected every turn — nor a learned facet drifting against an operator Profile constraint ("be candid, never flatter me" vs a learned "warm and effusive"). New `aivyx persona conflicts` runs an on-demand LLM detection pass (zero background cost, no config) and `aivyx persona resolve <id> --remove <a|b>` removes the losing facet via a `RemoveList` persona delta (operator-authored, reversible via `aivyx persona revert`). Operator Profile constraints are immutable — flagged, never removed. New wasm-clean `SoulConflict` IPC type + `GetSoulConflicts` / `ResolveSoulConflict` verbs (Studio can consume later). ## [0.7.31] — 2026-07-02 ### Changed - **The autonomous loop now verifies-and-closes a finished story the agent forgot to complete (Chapter Capstone).** A recurring dogfood finding: small local models often do the work but never call `loop.complete` (or report it in chat), so a genuinely-finished story stays pending until the stall breaker ends the run. When `[loop] verify_completion` is on, at each iteration's end the driver judges the still-pending story it handed out against its own acceptance criteria — grounded on the memory/workspace artifacts the turn produced — and marks it done iff the judge passes. Genuinely-incomplete work is left pending (the grounded judge rejects it, e.g. "only 1 of 2 requested items"). Reuses the existing completion judge; no new config. ## [0.7.30] — 2026-07-02 ### Security - **The web UI can now require an auth token (Chapter Postern).** The Studio's `/ws` WebSocket is its control plane — agent turns, config writes, and memory reads all flow over it — and exposing it off-host (`web_ui_host = "0.0.0.0"`, the Docker appliance) previously left it unauthenticated. Set `[daemon] web_ui_auth_token = "<opaque>"` to require a shared secret: browsers are prompted via HTTP Basic (the token is the password) and a cookie is planted that gates the `/ws` upgrade; non-browser clients send `Authorization: Bearer`. Default-off (localhost posture byte-identical), with a loud daemon warning when bound off-host without a token. Not a TLS substitute — terminate TLS at a proxy for remote access. ## [0.7.29] — 2026-07-02 ### Security - **`shell.exec` now honors the sensitive-path guards (Ward/Portcullis).** Previously the sensitive-read and persistence-write guards protected only the `fs.*` tools, so a shell command routed around them — `cat ~/.ssh/id_rsa`, `echo … >> ~/.bashrc`. `shell.exec` now scans the command text and refuses a command that references a protected location (secret dirs/files, cloud creds, or persistence targets like `.bashrc` / `authorized_keys` / `crontab`) before `sh` runs. Uses the same `[access] allow_sensitive_paths` opt-in. Best-effort by nature (obfuscated paths can still slip through — the docs continue to point to OS-level isolation for hard guarantees); disabled-by-default so behavior is byte-identical until the guard is configured on. ## [0.7.28] — 2026-07-02 ### Changed - **Loop completion verdicts now ground on FILE artifacts, not just memory.** The acceptance judge that gates `loop.complete` (and delegated team missions) reads a bounded snapshot of the most recent workspace files alongside recent memory, and treats both as ground truth. A file-producing task is graded on the file that actually exists rather than on how tersely the agent phrased its summary — closing the documented #17b/#17d residual (previously memory-only). ## [0.7.27] — 2026-07-02 ### Security Final hardening pass of the security thread — extending the existing guards to the last uncovered surfaces: - **Prompt-injection fencing now covers `fs.read` and every tool-process integration (Bulwark extension).** A file's contents (attacker-supplied, downloaded, or in a broad-access location) and the output of Gmail / Calendar / Contacts / Drive / Obsidian / the web-search toolkit are fenced as untrusted data — never instructions. Verified: the agent describes an injection in a file rather than obeying it. - **`net.dns` honors the egress policy (closes a DNS-exfil channel).** A DNS lookup is itself an exfiltration channel (`<secret>.attacker.com` leaks to the attacker's nameserver); `net.dns` now applies the same `[access] allow_egress_hosts` allow-list + private-host block as the web tools, so an allow-listed deployment can't be DNS-tunneled. ## [0.7.26] — 2026-07-02 ### Security - **Master passphrase in the OS keyring (Chapter Keyring).** For interactive / desktop use, store the master passphrase in the OS credential store (Secret Service / Keychain / Credential Manager) instead of the `AIVYX_PASSPHRASE` env var or `[aivyx] passphrase` TOML (both plaintext). `aivyx keyring set` / `clear` / `status`; the daemon reads it automatically (env/TOML still take precedence; the keyring is tried before the interactive prompt; an unavailable/locked keyring falls through). The headless systemd service keeps using the `0600 daemon.env` file (no session Secret Service there). - **Fixed a PDF denial-of-service (RUSTSEC-2026-0187).** The `data.pdf` reader pulled `lopdf 0.34`, where a crafted ~21 KB PDF with deeply nested arrays triggers unbounded-recursion stack overflow (SIGABRT) — crashing the daemon on any turn that reads an untrusted PDF. Upgraded `pdf-extract` → 0.12 and `lopdf` → 0.42 (the patched line); reader behavior unchanged. ## [0.7.25] — 2026-07-02 ### Security Continuing the privacy-first hardening pass — two more layers, both live-verified on the dogfood rig: - **Sensitive-path write guard (Portcullis).** The symmetric completion of Ward: `fs.write` now hard-refuses writes to secret *and* persistence locations — `~/.ssh/authorized_keys`, shell rc files (`.bashrc`/`.zshrc`/`.profile`), `~/.config/autostart` & `systemd`, `cron.*`, git hooks — even inside the sandbox and even at `full`. This closes the backdoor/persistence vector `confirm_destructive` only *soft*-gated (it's self-confirmable; this is a hard refusal). Same `[access] allow_sensitive_paths` opt-in. Reads of these files are still allowed; only writes are blocked. - **DNS-rebinding defense (Rampart).** The network egress guard now filters private/loopback/link-local addresses at DNS-*resolution* time via a custom resolver, so a public hostname that resolves to `127.0.0.1` / `169.254.169.254` / an RFC-1918 address is never connected to (TOCTOU-safe). Failed requests now also surface the full error cause chain, so a blocked request reports why. ## [0.7.24] — 2026-07-02 ### Security A privacy-first hardening pass — three composing, default-on layers, each live-verified on the dogfood rig: - **Sensitive-path read guard (Ward).** The agent can no longer read known secret locations — `~/.ssh`, `~/.aws`, `~/.gnupg`, cloud/k8s/docker creds, `.env` files, private keys, **and Aivyx's own encrypted store + passphrase** — at any access level, on the canonical (symlink-resolved) path, across `fs.read` and the data readers. Opt specific paths back in with `[access] allow_sensitive_paths`; disable with `guard_sensitive_paths = false`. - **Network egress guard (Rampart).** The web tools refuse loopback / link-local / private / unique-local targets by default — on the initial URL and every redirect hop — blocking SSRF and cloud-metadata theft (`169.254.169.254`) and local-service pivots. `[access] allow_private_egress` re-enables localhost/LAN; `allow_egress_hosts` hard-restricts to named hosts. - **Prompt-injection resistance (Bulwark).** Content the agent ingests (web pages, extracted articles, parsed files, third-party MCP outputs) is fenced as untrusted DATA — never instructions — via a demarcation envelope plus a standing charter rule. Defense-in-depth, composing with the layers above. ### Fixed - **Auto-delegated stories are held to the real artifact.** Completion verification for a loop-delegated team mission is now grounded on the memory the team actually wrote (matching the solo `loop.complete` path), so a terse mission result over genuine work is no longer false-rejected. ## [0.7.23] — 2026-07-01 ### Fixed Autonomous-loop hardening, all surfaced by a live dogfood run (gpt-oss:20b): - **Auto-delegated stories no longer skip on a specialist name/role mismatch.** The planner refers to a team specialist by its role label ("Operations"), but resolution matched only the roster id ("ops") → the mission errored `no specialist "Operations"` → retried → skipped a doable story. Resolution now matches the name **or** the role, case-insensitively (extends the earlier case-only fix, which never covered genuine name↔role differences). - **Completion verification now judges the real artifact, not just the summary.** With `[loop] verify_completion`, the acceptance judge was rejecting genuinely-complete research/memory stories because the agent's summary was terse — even though the note was in memory. The judge is now shown a snapshot of the recent memory the agent wrote and passes when that evidence satisfies the criteria (solo `loop.complete` path). - **A malformed tool call from a local model no longer hard-fails the turn.** Ollama returns a bare HTTP 500 when a model emits unparseable tool-call JSON; the provider now retries once (resampling almost always parses), while genuine 500s still surface immediately. ## [0.7.22] — 2026-07-01 ### Added - **`aivyx memory conflicts` / `resolve` / `dismiss` — contradiction detection for stored memory (Chapter Concord).** The agent stored whatever it was told, so contradictory facts coexisted silently ("home airport is YPPH/Perth" *and* "...Sydney/YSSY"). An on-demand LLM pass now flags pairs of entries that assert incompatible facts about the same subject — within one topic **or across two topics** — and the operator resolves each by keeping one (`resolve <topic> --archive <seq>` deletes the other) or dismissing a false positive (`dismiss <id>`, durably suppressed). No background cost: detection only runs when asked. New `Memory::delete_entry` substrate primitive + a 26th encrypted storage domain for dismissals. - **`aivyx memory wiki` / `graph` — CLI knowledge-base inspection.** The agent's synthesized wiki pages and typed knowledge graph, previously Studio-only, are now viewable from the terminal. ### Fixed - **Internal `context:pruned:*` bookkeeping no longer leaks into the knowledge base or the agent's context.** The earlier internal-topic filter covered only `memory list`; the same machine-state archives still surfaced through six more paths — the wiki + graph sweeps, both graph queries, memory search, the agent's RAG auto-recall, and its wildcard `memory.read`. All are now filtered from one shared definition in the memory substrate. - **The typed knowledge graph no longer fills with conversation-mechanics noise** ("conversation history → messages", "assistant → tool_call → web_search"): the extractor drops triples about the session itself and keeps domain knowledge. - **The first daemon query after a restart no longer fails** with a spurious `RecoveryNotice` protocol error (the long-standing "memory list transient flakiness"). ## [0.7.21] — 2026-07-01 ### Changed - **Acceptance verification now covers auto-delegated work too.** When `[loop] verify_completion` is on, a story the loop auto-delegates to the agent team is judged against its acceptance criteria — the team mission's result must pass the same LLM acceptance check a solo `loop.complete` gets — before it's marked done; otherwise it's retried (then skipped). Previously a delegated mission was accepted on completion alone, skipping the check solo work received. ## [0.7.20] — 2026-07-01 ### Fixed - **Auto-delegated team missions no longer fail and retry (and can no longer skip a doable story).** The autonomous loop's `delegate_above` path could hand a story to the team, have the mission error out, retry, and eventually skip the story — wasting work. The cause was a case-sensitive specialist lookup: the planner names a specialist "Researcher" but the roster is "researcher", so the mission errored with "no specialist". Specialist matching is now case-insensitive. (Unattended decompositions also no longer emit approval gates that can't be satisfied without an operator.) ## [0.7.19] — 2026-06-30 ### Changed - **The release pipeline retries transient crates.io network failures.** A `.cargo/config.toml` with `[net] retry = 10` plus a step-level retry around `cargo install` in the desktop build stop the recurring "curl failed / HTTP2 framing layer" flakes that needed a manual rerun each release. - **The loop's completion judge logs its verdict.** When `[loop] verify_completion` is on, the daemon now logs each ACCEPT/REJECT decision (and the reason) instead of gating silently — so an operator can see why a story was held back or accepted. ## [0.7.18] — 2026-06-30 ### Added - **The agent owns up when it claims an action it didn't take (Chapter Candor).** After a turn, Aivyx compares what its reply claims against the tools it actually called, and appends an honest note if it said it did something (e.g. "saved to memory", "scheduled a routine", "sent a notification") without the matching tool call. Non-blocking — it annotates, never breaks the turn — and upholds the agent's honesty contract instead of quietly papering over a dropped step. ## [0.7.17] — 2026-06-30 ### Fixed - **`aivyx memory list` (and the Studio Memory browser) no longer shows internal topics.** The per-session `context:pruned:*` context-pruning archives — machine bookkeeping, not operator knowledge — are hidden from topic listings (they were swamping the real topics). They remain reachable by exact `aivyx memory show <topic>`; only the cluttered enumeration is filtered. ### Changed - **The autonomous loop skips a story after repeated failed auto-delegations.** When `[loop] delegate_above` is set, a story whose delegated mission keeps ending non-Done is marked skipped after a couple of attempts instead of re-running a full team mission every iteration. ## [0.7.16] — 2026-06-30 ### Added - **Deterministic auto-delegation for the autonomous loop (Chapter Foreman).** Opt-in `[loop] delegate_above = N`: before each solo turn the loop scores the next backlog story with a structural complexity heuristic and, if it scores `>= N`, hands it to the agent team (headless) instead of attempting it solo — so delegation no longer depends on a small local model choosing to call `team.run`. Default off. ### Fixed - **`aivyx team status` shows the real halt reason.** A mission stopped with `aivyx team abort` (or a budget cap) now reports *why* it halted (e.g. "aborted by operator") instead of always labelling it "(budget)". The reason is persisted on the mission record and rendered in the detail view. ## [0.7.15] — 2026-06-29 Three multi-agent / autonomy refinements. ### Added - **Heterogeneous teams (Chapter Ensemble).** Each `[[team.member]]` may now declare its own `model` and/or `base_url` (same provider kind). A coordinator can run on a big model while grunt specialists run on a small fast one (role-fit + cost), and pointing roles at different endpoints (e.g. a second Ollama / GPU) gives true parallel execution instead of serializing on one server. Both optional — omit them and a role uses the team's shared default. - **Per-story acceptance verification (Chapter Verdict).** Opt-in `[loop] verify_completion`: when the autonomous loop calls `loop.complete`, an LLM judge checks the agent's summary against the story's acceptance criteria and blocks the completion on a FAIL (the story stays pending) instead of trusting the self-report. Fails open; stack with `gate_command` for artifact-grounded checks. - **Abort a running team mission (Chapter Belay).** `aivyx team abort <id>` stops a running mission — it halts gracefully at its next step boundary (in-flight work finishes, completed outputs preserved). Completes the operator control surface alongside budget caps and human-gate approve/reject. ## [0.7.14] — 2026-06-29 ### Fixed - **`aivyx mcp status` no longer falsely reports "no MCP servers".** The status snapshot is daemon state, but it was being overwritten by transient CLI invocations (a `--headless` turn, the REPL), which clobbered the running daemon's snapshot with empty entries — so the command showed no servers while MCP was actually live. Only the daemon writes the snapshot now. (Backlog #9.) - **The `trend-scan` routine never broadcasts fabricated findings.** A new delivery gate (`notify_when = "on_completed_grounded"`, now the default for `trend-scan`) only pushes a result when the turn actually did work (≥1 tool call). A trend-scan that produced "findings" with no real web search is suppressed instead of being sent as if real. (Backlog #6 follow-on.) ## [0.7.13] — 2026-06-29 ### Fixed - **The weekly digest no longer fabricates.** A check-in caught the scheduled `weekly-digest` inventing profile-themed "accomplishments" that never happened (a "Flutter revenue dashboard", etc.) — a local model fills a sparse summary with plausible fiction no matter how the prompt is worded. The digest is now **assembled deterministically by the daemon** from your actual memory (entries written since the last digest) plus the live pending-proposal count, with real dates; if nothing was recorded it says so plainly. There is no LLM in the content path, so it cannot confabulate. (Backlog #6, Chapter Ledger.) ### Added - **Chapter Deckhand — opt-in "use your open applications" (experimental).** With `[applications] enabled = true`, the agent can use the GUI apps open on your own machine: list windows, focus one, type / press keys / click, and screenshot. Default **off**; Trusted-tier only; input injection is **confirm-first**; every action is an audited tool call. Linux X11 / Xwayland in this release (needs `xdotool` + a screenshot tool); native-Wayland windows are a known limitation. See `docs/APPLICATIONS.md`. ## [0.7.12] — 2026-06-29 ### Added - **Chapter Helm — autonomous loop runs survive a daemon restart.** A new opt-in `[loop] resume_on_boot` resumes an interrupted autonomous-loop run when the daemon restarts — so a "runs for days" agent (e.g. under systemd `Restart=on-failure`) keeps working through its backlog instead of silently stopping after a crash. It resumes only when a run was genuinely active when the daemon stopped (a crash or restart) and stories remain pending; an explicit `aivyx loop stop` is remembered across the restart and is **not** resumed. Default off — auto-resuming a code-committing loop is a deliberate operator choice. ## [0.7.11] — 2026-06-29 ### Fixed - **Release pipeline gate.** A test helper added in 0.7.10 (Chapter Ballast) triggered a `clone_on_copy` clippy lint, which the cargo-dist release gate (`clippy --all-targets -D warnings`) treats as an error — so 0.7.10's CLI installer binaries failed to publish (the desktop app and Docker image, built by separate workflows, shipped fine). This release republishes the CLI binaries with the lint fixed. No runtime behavior change from 0.7.10. ## [0.7.10] — 2026-06-29 ### Added - **Chapter Ballast — a per-mission budget for autonomous team missions.** When the autonomous loop delegates a story to a background team mission, that mission previously had no aggregate spending limit (only a per-call token cap). Two new opt-in `[budget]` caps — `per_mission_tokens` and `per_mission_usd` — bound a single mission's total spend across all its specialist sub-turns; a tripped cap **halts the mission gracefully** at the next step boundary (completed work preserved, the reason recorded). Tokens bound local/free runs where the dollar cap (priced at $0) never trips. Both default to unbounded, so behavior is unchanged unless you opt in. ### Fixed - **`aivyx init --template <name>` now matches a plain `aivyx init`.** The template path silently skipped semantic-memory setup (`[embedding]` + `[memory] profile`), the onboarding persona seed, and the web-search server — so a templated agent diverged from a default one. It now applies each of those (any a template already declares is respected, never duplicated). ## [0.7.9] — 2026-06-28 ### Added - **Chapter Ember — embedding-free "lite" recall.** `[memory] profile = lite` now delivers real recall with **zero setup** — no embedding model, no vectors, no paid generation. It fuses BM25 lexical search with a co-occurrence graph walk (seeded from the lexical hits) over the memory the agent already has. Previously `lite` produced *no* recall because the whole recall stack was gated on an embedding provider. `aivyx doctor` reports it as "lite recall active". Set `profile = smart` with an `[embedding]` section for semantic recall; a `smart` config with no embeddings stays off (and now warns). (Backlog opportunity C.) ### Fixed - **Chapter Etch — the agent now reliably persists explicit "remember this" requests.** Previously a local model treated "remember X" as conversation and often never called `memory.write`, so a later turn couldn't recall it (and a soft charter instruction didn't move it — verified). Now the per-turn memory hook **deterministically** detects a leading "remember / note / save / don't forget X" request in the operator's message and persists the fact itself — independent of the model's tool-calling — embedding it so it's immediately recallable. Conservative detection: it ignores questions, reminiscing ("remember when…"), reminders ("remember to…"), and first-person mentions. Live-verified: "Remember my home airport is YSSY" → a fresh-context "is it good flying at my home airport?" now recalls YSSY and answers, instead of asking which airport. (Backlog #8.) - **A `smart` memory profile with no embeddings is no longer silently inert.** `[memory] profile = smart` arms the semantic recall stack, but with no `[embedding]` provider the semantic source does nothing — the trap that can leave "smart" memory dark. The config loader now warns at startup and `aivyx doctor` flags the mismatch (use `profile = lite` for embedding-free recall, or add an `[embedding]` section). (Backlog #1.) - **Clearer error when a non-interactive `aivyx` collides with a running daemon.** A piped/non-TTY turn (`printf … | aivyx`) opened the store directly and failed with redb's opaque "Cannot acquire lock" while the daemon held it. It now explains the situation and points to `aivyx --headless "…"` (which routes over the daemon) or `aivyx daemon stop`. (Backlog #2.) - **The daemon no longer logs clean client disconnects as errors.** A finishing CLI query dropping its socket produced a `connection handler error: Broken pipe` line on every call, spamming `journalctl` and masking real errors. Clean hang-ups are now silent; genuine faults still log. (Backlog #4.) ## [0.7.8] — 2026-06-28 ### Fixed - **`aivyx init` no longer shreds list items that contain commas.** The wizard's comma-separated Profile fields (use cases, behavioral preferences/constraints, character traits) are now split **paren-aware** — a comma inside `(...)`/`[...]` no longer splits, so an item like "Research my passions (flying, food, coffee)" stays a single entry instead of fragmenting. ## [0.7.7] — 2026-06-28 ### Fixed - **Chapter Plumb — default report routines no longer confabulate.** A fresh-agent check-in caught the `weekly-digest` routine *fabricating* a week of accomplishments (zero tool calls) — and `trend-scan` silently doing nothing — because those default `[[schedule]]` prompts were passive "summarize / search" instructions the local model could satisfy by inventing prose instead of reading (empty) memory/journal. The reporting prompts now **name an explicit first read** (recall memory + read the journal; *actually* web-search for trend-scan), report **only what's genuinely found**, and say "nothing notable to report yet" when the record is empty — never invent, infer, or pad. Re-verified live: the digest now grounds in real memory instead of fabricating. (A daemon-level "an aggregation routine that made no tool calls is a no-op" backstop is noted as a follow-up.) ## [0.7.6] — 2026-06-28 ### Added - **Chapter Anchor — `aivyx daemon install` (run for days).** The bare daemon — the default install — now installs as a first-class persistent **user** service so a local-first agent meant to run for days actually stays running (its scheduled routines firing, its loop available) across logout and reboot, without hand-rolled `systemd`/`launchd` files. - **`aivyx daemon install [--web-ui] [--no-start]`** + **`aivyx daemon uninstall`**. Linux = a systemd user unit + `loginctl enable-linger` (no root); macOS = a launchd `LaunchAgent`. Idempotent re-install. - The unattended store passphrase is captured at install (`AIVYX_PASSPHRASE` or a one-time hidden prompt) and kept owner-only at rest — a `0600` `EnvironmentFile` the unit references on Linux (never in the unit itself), the `0600` plist's `EnvironmentVariables` on macOS. - `aivyx doctor` gains a **Service** section (installed / running), and `aivyx init` points new users at `daemon install`. Documented in `docs/INSTALL.md`. Live-verified end to end on the dogfood rig. - **Chapter Passport — the identity & cross-boundary trust keystone (FED.1–5).** The open-core trust **substrate** for agent-to-agent interaction across an operator boundary — the one primitive `VISION.md` flags as impossible to retrofit (*"a network of agents is a Nonagon team with the trust boundary moved"*). New `crates/aivyx-federation`: - **Identity** — an operator-owned Ed25519 keypair + signed, replay-guarded request envelope; key-at-rest sealed with a subkey derived from the storage `MasterKey`; key material never logged. - **Trust** — a per-peer `TrustPolicy` (deny-by-default; allowed scopes in the `Scope` vocabulary; a confirm-first autonomy ceiling) and the cross-operator attenuation (`effective = asked ∩ policy ∩ host-ceiling`, autonomy floored) — NT-02 generalized so a peer can never exceed what *both* operators allow, and revocation narrows reach immediately. - **Relay shape** — the `chat`/`task`/`search` verbs as wasm-clean `aivyx-ipc::federation` types ("as if the peer is a stranger"), plus the forensic `Crossing` shape for the audit chain. - **Untrusted peer content** — provenance-tagged, payload-validated, runnable only under its attenuated authority (never the host's broader caps). - **Operator consent** — peer-initiated effect (a `task`, or any irreversible scope) routes through the existing confirm-first gate; no cross-boundary self-escalation. Substrate only — no transport, peer discovery, reputation, or the Nexus product (built last, on an installed base). Proven end-to-end by a two-identity in-process integration test. ## [0.7.5] — 2026-06-28 ### Added - **Chapter Circuit — agentic-loop hardening (CI.0–CI.6).** A deliberate audit + hardening pass over the autonomous loop, prompted by the v0.7.4 scope-floor bug. - **CI.0** — the loop's `team.run` delegation tool is now granted to the default role when the loop is armed (it was registered and offered in the iteration prompt but, like `loop.*` before v0.7.4, never floor-granted, so the "delegate to a team" branch was dead). `git.write` stays operator-opt-in by design. Adds a prompt↔floor drift guard so the two can't silently diverge again. - **CI.1** — a **cross-iteration stall breaker**. The loop driver now stops a run after `[loop] max_idle_iterations` consecutive iterations make no progress — neither completing/delegating a story nor recording a fresh progress note. This catches a loop spinning on an unrecoverable error (the v0.7.4 bug burned all 25 iterations / 631k tokens re-failing identically) instead of letting it exhaust the iteration/token caps. Default 3; set `0` to disable. Distinct from Bridle's within-turn repeat breaker — this is across fresh-context iterations. - **CI.2** — the iteration prompt is now **task-agnostic**. It previously hardcoded a software-dev flow (run the build/tests, commit with `git`), which stranded everyday-PA stories — the model had to improvise past instructions for work that has no project and no granted `git.write`. The verification step now offers a fitting check per task type (gates for code, re-read for research/writing, read-back for a file), and the persistence step routes the result to its proper home (a commit for code *only if committing is available*, memory for notes, the requested path for a file). - **CI.3** — audit of gate verification and `team.run` delegation governance (documentation, no behavior change). Clarified that the loop gate is a tree-level regression guard, not a per-story completion verifier; and decided to keep `team.run` available whenever the loop is armed (rather than posture-gate it) while documenting that loop-delegated team missions are not yet bounded by an aggregate budget — both captured as tracked follow-ups. - **CI.4** — durability & re-entrancy audit. Fixed a wedge: if the loop driver task panicked mid-run it left the run flagged `active` with no driver behind it, so every later `loop start` silently no-op'd ("already running") until a daemon restart — a new run-scoped guard now clears the flag on an abnormal exit. Documented that loop run-state is in-memory only (a daemon restart mid-run does not auto-resume; stories persist, the operator re-issues `loop start`); opt-in auto-resume is a tracked follow-up. - **CI.5** — observability. `aivyx loop status` now shows the stall-breaker configuration (`stall breaker: stop after N idle iteration(s)` / `off`) alongside the other run caps, and a live warning while a run is spinning (`no progress for N of M iteration(s) before the stall breaker stops the run`) — so a stalling run is legible without reading `journalctl`. The stall threshold + live idle count ride the `LoopStatus` IPC / run-state. - **CI.6** — live-verified the chapter end-to-end on the dogfood rig: research stories complete through the task-agnostic path with no git noise (CI.2); an unrecoverable story trips the stall breaker after 3 idle iterations instead of burning the 25-iteration cap, and stays pending rather than being false-completed (CI.1); `loop status` shows the stall-breaker config + stop reason (CI.5). (The `team.run` grant is present and never scope-denied; delegation dispatch wasn't observed because the small local model consistently self-implements even when told to delegate.) ### Fixed - **Flaky e2e store-path collision.** Four `aivyx-channel` integration suites (`fs_tool_e2e`, `cli_e2e`, `audit_persistence_e2e`, `memory_tool_e2e`) built their scratch `store.redb` path from `pid + nanos`, which is not unique across the crate's parallel test threads — under CI load two tests could land on the same clock tick, collide on the path, and fail the second `RedbStorage::open` on the redb lock (this failed the v0.7.4 release's quality gate). Each path now carries a uuid, matching the existing `storage_persistence_e2e` fix. ## [0.7.4] — 2026-06-28 ### Fixed - **The autonomous loop's tools were never granted to the default role.** Arming `[loop]` (or an `[autonomy]` level that arms it) spawned the loop driver, but the default role's backcompat scope floor granted `memory.*` / `fs.*` / `net.*` / `shell.exec` / `workspace.*` / `ollama.*` / `mcp.call:*` and **never `loop.*`** — so every iteration was denied at step one (`loop.next ... not currently granted`) and the driver burned its full iteration cap re-failing at the same wall. The autonomous-loop backlog mechanism was effectively dead on arrival for the zero-config default role. The floor now grants `loop.next` / `loop.complete` / `loop.note` when the loop is armed, mirroring the existing ollama/MCP grants; self-escalation scopes are still withheld. Found and verified on a live multi-day dogfood run. ## [0.7.3] — 2026-06-27 ### Added - **Default starter skills (Chapter Outfit).** A brand-new agent now ships with a curated starter repertoire — `summarize-document`, `research-and-summarize`, `draft-reply`, `daily-briefing`, `capture-note` — so it can do real work on turn one instead of arriving with none. Each is a lightweight `{name, trigger, procedure}` recipe whose steps compose the agent's own tools and pillars (the structured-data readers, web search/extract, memory, workspace, persona). Compiled-in and default-on, planted onto a fresh agent's persona chain at first boot; an already-running agent is never retro-injected. Opt out with `[skills] starter = false`. - **Operator-initiated skill teaching (Chapter Tutor).** New `aivyx skills teach | update | forget` commands let the operator author a skill directly onto a **grown** agent's persona chain — the channel that was missing (genesis seeding is empty-chain-only, and the agent's own `skills.teach` tool is gated behind a scope an autonomous agent shouldn't hold). Operator authoring routes over the local daemon socket (same authority as `aivyx persona revert`) and is cleanly separated from agent self-teaching, so it needs no agent scope. Signed, audited on the persona chain, and reversible via `aivyx persona revert`. ### Changed - **Semantic memory works out of the box (Chapter Engram).** `aivyx init` now configures an embedding provider — pulling `nomic-embed-text` on the local Ollama path, or using OpenAI embeddings on the cloud path — and turns on the full memory profile (`[memory] profile = "smart"`). Previously the whole graph-augmented memory stack was dormant on a fresh install: the daemon only builds the auto-recall pipeline when `[embedding]` is configured, and `init` never wrote one, so a new agent did no semantic recall at all regardless of the memory profile. (Existing installs are unchanged on upgrade — the new defaults are written only into new configs, so the token-spending extraction sweeps are never a surprise.) `aivyx doctor` gained a Memory section reporting the embedding provider, the active profile, and — on the local path — whether the embedding model is actually downloaded. ## [0.7.2] — 2026-06-27 ### Changed - **The default system prompt is now an operating charter (Chapter Keel).** The out-of-box base layer was a single line ("a terse and thoughtful assistant running in a local terminal"). It is now a compact (~270-token) operating charter that states, in prose the model actually reads, what the dynamic Profile/Persona/Tools/Skills layers don't: how the agent works (terse and honest, tool-first, uses its memory and workspace, doesn't loop), its safety posture (confirm-first on irreversible/outbound actions, never widens its own authority/reach/autonomy, everything is recorded — mirroring `docs/SECURITY_POSTURE.md`, previously stated only in code), and turn discipline. It remains a compiled-in default, fully overridable via `[agent] system_prompt`, a per-`[[role]]` `system_prompt`, or `AIVYX_SYSTEM_PROMPT` — existing installs inherit it live and pick up future refinements on upgrade. No new config is planted into `aivyx.toml`. - **The Studio's Command Center reflects a live, working agent.** The home dashboard now surfaces an agent-vitals rail (model · provider · context · autonomy · access, with a pulsing "live" dot when the daemon is online) and a Routines panel showing each scheduled background routine's cadence, enabled state, and last/next fire — the clearest "the agent is working on its own" signal. Backed by a new read-only `GetSchedules` IPC; no new capability. ### Fixed - **Eliminated a parallel-test flake in `aivyx-config`.** Two config-load tests read the ambient environment without holding the test suite's env-guard, occasionally racing parallel env-mutating tests (intermittent failures under default test threads; green at `--test-threads=1`). Both now hold the guard, and the invariant is enforced via a thread-local check so any future unguarded config-load fails deterministically with an actionable message instead of flaking. ## [0.7.1] — 2026-06-27 ### Security - **Bumped `quinn-proto` 0.11.14 → 0.11.15** (RUSTSEC-2026-0185): a remote memory-exhaustion via unbounded out-of-order QUIC stream reassembly. A transitive dependency (via `reqwest`'s HTTP/3 path); a lockfile-only patch bump, no API change. `cargo audit` no longer reports any vulnerability. - **Documented the `aivyx-desktop` GTK3 advisory cluster in `deny.toml`.** The native desktop app's webview (tao/wry/webkit2gtk → the frozen gtk-rs GTK3 bindings, pinned at glib 0.18) pulls 11 `unmaintained` advisories (RUSTSEC-2024-0370 + 0411–0420) plus the glib `VariantStrIter` unsoundness (RUSTSEC-2024-0429 = GitHub Dependabot alert #6). All are transitive, desktop-build-only (the daemon/core/CLI never link them), unfixable until the webview ecosystem moves off GTK3, and not exploitable in the thin shell. The unmaintained ones are now ignored with documented rationale (restoring `cargo deny` to green); the glib unsoundness is recorded as the accepted disposition for the Dependabot alert (dismiss as "tolerable risk"). ### Changed - **`aivyx init` ends on real next steps.** The completion message now surfaces the three things a new operator actually needs — `aivyx` (terminal chat), `aivyx daemon run --web-ui` to open the **Studio** at `http://127.0.0.1:7843` (the web GUI a first-run user wouldn't otherwise discover), and `aivyx doctor` to re-check — plus, on the local path, a pointer to `docs/LOCAL_HOSTING.md` for sizing the model/context to a capable GPU. The Studio port comes from the canonical `DEFAULT_WEB_UI_PORT` so it can't drift. - **One `TurnSafety` choke point for the per-turn knobs.** The deadline + cycle breaker were applied ad-hoc at ~7 `ConcreteAgent::new` sites — which is exactly why three of them drifted and shipped unprotected. Now every agent-construction path ends with `TurnSafety::<posture>(…).apply(agent)`: `interactive` inherits the operator's `[agent]` config (REPL, voice, daemon, role-switch child), `autonomous` forces the breaker floor (team lead + specialists), and the standalone remote-channel builders route through `default()`. `SessionConfig` / `AgentStackSpec` now carry a single `turn_safety` field instead of two. No behaviour change — the wiring just can't drift across sites again. ### Added - **`aivyx autonomy` — one dial for how autonomous your agent is (Chapter Reins).** A new `[autonomy] level` (`manual` → `assisted` *(default)* → `supervised` → `autonomous` → `unleashed`) composes the scattered autonomy knobs (access, confirm-first, gate policy, loop arming, self-improvement adoption) into named tiers, with per-domain `[[autonomy.override]]` exceptions ("autonomous at shell, manual on email") and an `[autonomy.auto_approve]` reversible-scope allowlist. `aivyx autonomy show` renders the resolved level and the posture it expands to; `aivyx autonomy set <level>` rewrites the section (`autonomous`/`unleashed` confirm first). The default `assisted` expands to today's behavior byte-for-byte, so an absent `[autonomy]` section changes nothing. Its **first runtime effect**: a `supervised`/`autonomous`/`unleashed` level **arms the autonomous loop** even without an explicit `[loop] enabled` (additive — it never disarms an explicitly-enabled loop). Arming only makes the loop *available* (a run still needs `aivyx loop start`) and takes effect only when a `[loop]` section exists (which carries the iteration/budget caps). The remaining dimensions (gate policy, growth) are wired incrementally — see `docs/AUTONOMY.md`. No new capability base; a composition front end to primitives already enforced. Settable from the **Studio Settings** screen too (an "Autonomy" section with a level picker over a new `SetAutonomyLevel` IPC, server-side confirm-first on the autonomy-granting levels). - **`aivyx --headless "<task>"` — one-shot unattended runs from the CLI.** The headless execution mode (Chapter H) was reachable over IPC and from the operator-absent drivers, but never from the command line. This wires the missing entry: it connects to a **running daemon**, submits one turn that *refuses* (records the reason on the audit chain) at any approval gate rather than parking for an operator, streams the output, and maps the turn's outcome onto a **process exit code** for cron / batch / autonomous callers — `0` completed, `3` refused-at-a-gate, `1` any other non-completion. No daemon running yields a clear "start `aivyx daemon run` first" error (there is no in-process fallback — headless relies on the daemon's gate interception). No new tool/base/dep; the interactive paths are byte-identical when the flag is absent. - **Small-cycle breaker (`[agent] cycle_detection`).** A loop-safety companion to the consecutive-identical breaker (Chapter Bridle): it catches a repeating *cycle* of tool calls (`A,B,A,B,…`) that the consecutive counter resets on — the one runaway shape that previously ran until the 32-step cap or the 120s deadline. A bounded ring of recent call signatures trips when the tail is `min_repeats` back-to-back copies of a `2..=max_period` block, reusing the existing `Looping` outcome (no new audit surface). **Default-off** (the turn loop stays byte-identical); operators arm it with `[agent] cycle_detection = true` — or toggle it from the **Studio Settings screen** (a new "Agent" section with a Cycle-breaker toggle, over a `SetCycleDetection` IPC that rewrites the `[agent]` section; `GetSettings` now reports the current state). - **Nonagon team agents get the cycle breaker as a built-in safety floor.** The lead and every specialist run autonomously inside a mission — no human watches each turn to `/cancel` a runaway — so they always get the small-cycle breaker (like `MAX_STEPS_PER_TURN` is always on), independent of the interactive `[agent] cycle_detection` knob. - **Default scheduled "starter routines" at first-run.** `aivyx init` now plants a small set of read-only, self-contained `[[schedule]]` routines so a fresh agent orients itself and stays useful unattended: a daily **environment-review** (self-bootstrapping — first run builds a baseline of the accessible environment, later runs diff it and journal changes), a nightly **reflection** (consolidate the day's memory into the knowledge base), a 6-hourly **health-check** (silent unless something breaks), a Monday **weekly-digest** (learnings + pending proposals), and an opt-in daily **trend-scan** (web research across the operator's interests). Each prompt is read-only and explicitly forbids destructive actions — they run unattended. Gated by the wizard's existing answers: the four core routines are **enabled on a local (Ollama) provider** and written **present-but-disabled on a cloud provider** (discoverable, a one-line flip to enable, and cost-aware since cloud schedules spend tokens); `trend-scan` additionally requires web search. The wizard prints what it set up, so it is never surprise behavior. Verified live on real hardware (all five fire and execute) before shipping. See `docs/ROUTINES.md`. ### Fixed - **Daemon no longer panics when a runaway turn ends in `MaxStepsExceeded` / `Looping` with failure-learning on.** A `match` building the failure summary for the skill auto-proposer handled `Failed`/`Cancelled`/`TimedOut`/`Escalated` but fell through to `_ => unreachable!()` for the two outcomes a *local model* is most likely to produce when it runs away (the step cap or the cycle breaker) — so with `[skills.auto_propose] from_failed_turns = true`, exactly that scenario killed the daemon mid-mission. The match is now **exhaustive** (no `_` arm), so the compiler forces every present and future `TurnOutcome` to be handled here — a single turn can never panic a 24/7 daemon. Surfaced by the Pass A panic-resistance audit, which otherwise found the untrusted-input parse boundaries (LLM responses, tool-call JSON, tool inputs, IPC frames, config, channel ingestion) already defensive. - **`[agent]` per-turn knobs now reach the daemon agent and the role-switch child** (the Studio + persistent chat path, and role sub-sessions). `turn_timeout_secs` and the new `cycle_detection` were applied only on the REPL/voice paths (`build_agent_stack`); the daemon agent and the `role.switch` child factory built raw `ConcreteAgent`s that skipped them, so they always ran the 120s default and no cycle breaker — a latent gap on the primary path, now closed (the child inherits the operator's config, like its parent). - The `channel-voice-full` feature build: the voice `AgentStackSpec` literal had drifted from the struct (it predated `turn_timeout`), so it failed to compile under that feature. Restored, with the new cycle-detection knob wired in. - **`access_level = full` granted nothing — the most permissive level was the only broken one.** `full` resolves the sandbox root to `/`, and every root-anchored scope grant was built as `format!("{root}/**")`, which at root `/` produced `fs.read://**` (double slash) — a glob whose leading `//` matches no single-rooted path. So a `full`-access agent could not read or write a single file (it would narrate a permission refusal). Fixed with a `rooted_glob` helper that collapses the trailing slash (`/` → `/**`), routed through all five root-anchored grant sites (`fs.read`/`fs.write`/`fs.metadata` + `shell.exec:cwd` + `fs.delete`). Found live on a real-hardware dogfood; the needed scope was always correct, so it hid behind a model that politely refused. Regression test added. - **Local thinking-model output was verbose and off-style — the `think` flag was inverted.** For a model advertising the `thinking` capability the Ollama provider sent `think: false`, assuming that forces the answer into `content`. On current Ollama (0.30) that is wrong for hybrid-reasoning models: e.g. `qwen3:30b-a3b` ignores `think: false` and dumps its whole chain-of-thought into `content`. `think: true` instead cleanly routes reasoning into the separate `thinking` field (which the agent already discards) and leaves the answer — or tool_calls — in `content`. Now sends `think: true` for thinking-capable models (verified for qwen3:8b and qwen3:30b-a3b, with and without tools). Test updated. - **Configured MCP servers' tools were registered but un-callable by the default role.** Each MCP tool call needs the scope `mcp.call:<server>:<tool>`, but the default-role capability floor granted no `mcp.call` scope at all — so the bundled web-search server the wizard's "Enable web search?" adds was dead on arrival (the agent saw the tool, lacked the scope, and reported it as "not authorized"). The floor now grants `mcp.call:<server>:*` per configured MCP bridge (mirroring the existing `ollama.*` grant) — least-privilege per server. Roles that declare their own `capability_scopes` still name `mcp.call:<server>` grants explicitly. Scope-matching regression test added. ## [0.7.0] — 2026-06-24 The desktop & experience release. A native desktop app, a top-to-bottom Studio overhaul, end-user documentation, and the first ecosystem seam for vertical packs — all over the unchanged security core (no new P10 amendment, no new capability surface). ### Added - **Native desktop app (`aivyx-desktop`).** A thin `tao` + `wry` shell that hosts the Studio in a system webview (reusing the exact WASM UI, no port), with daemon lifecycle (attach or spawn), a **system tray** (Open Studio · Restart daemon · Start at login · Quit) and hide-to-tray, **native approval-gate notifications** (a background WS client watches for missions awaiting approval), a **global hotkey** (`Ctrl+Shift+A`) to summon the window, and launch-on-login. Packaged with `cargo-bundle` (a `.deb` on Linux, a `.app`/`.dmg` on macOS) via a dedicated release workflow. Linux runtime deps: `webkit2gtk-4.1`, `libayatana-appindicator`, `xdotool`/`libxdo`. - **`aivyx-vertical-sdk`** — a thin, semver-stable facade that re-exports only the pack-facing slice of the engine, so vertical packs survive core refactors. The Kitchen pack is re-pointed at it as the open reference example; `crates/verticals-private/` (git-ignored, auto-joined via a workspace member glob) is the home for commercial packs. - **End-user guide** — a task-oriented `docs/guide/` (welcome, getting started, the Genesis wizard, per-feature pages, the desktop app, troubleshooting), surfaced as a **Guide screen** in the Studio (markdown rendered in-app via `pulldown-cmark`) with working cross-page links. - **Studio command palette** — `Ctrl/Cmd-K` to fuzzy-jump to any screen. - **Deep-linking** — the active screen is mirrored in the URL hash, so screens are bookmarkable/shareable and survive a reload, and back/forward navigate. - **Contextual help** — a topbar "?" that opens the Guide to the page for the current screen. ### Changed - **Responsive Studio shell** — the sidebar collapses to a hamburger drawer below tablet width, the nav is grouped into labeled sections, and the screen grids stack on narrow viewports. - **Loading skeletons** across every data-driven screen (Command Center, Memory, Skills, MCP, Teams, Documents) instead of flashing empty/zero states. - **UI polish** — a distinct icon per nav item, a single daemon-status indicator (was duplicated), and a corrected version footer. - **Windows** is documented as supported via WSL2 or the Docker appliance (no native binary yet — the daemon's IPC is Unix-socket-only). ### Accessibility - Visible `:focus-visible` keyboard focus rings (there were none), a skip-to-content link, `main`/`nav` landmarks, and `aria-label`s on previously-unlabeled inputs and icon-only buttons. ### Fixed - The CI quality gate is green again: install the desktop crate's GTK/WebKit system deps for the `--workspace` build, and clear a `clippy::type_complexity` lint in `aivyx-web`. ## [0.6.0] — 2026-06-21 The toolbox release. Three chapters **widen what the agent can do** without touching the security model: a pack of exact pure-compute utilities, readers that turn operator files into legible structured content, and the wiring that makes the whole MCP server ecosystem an operator-config story. None adds a P10 substrate amendment or a new capability surface beyond what each tier already allows. ### Added - **Utilities pack (Chapter Abacus).** Five exact, deterministic helpers a language model is structurally bad at doing in its head, in the existing `aivyx-toolkit` process: **`calc.eval`** (a hand-rolled, zero-dependency arithmetic evaluator — `+ - * / % ^`, parens, `sqrt/abs/round/floor/ceil/ min/max`), **`convert.units`** + **`convert.time`** (length/mass/temperature/ volume/digital via a curated table, and IANA-timezone conversion via `chrono-tz`), and **`date.diff`** + **`date.add`** (calendar-correct date arithmetic). Three group bases (`calc.eval`, `convert.units`, `date.compute`) — the **first toolkit surface reachable below the Trusted tier** (SemiTrusted), because pure compute touches no network, filesystem, or operator data. - **Structured-data readers (Chapter Sheaf).** Three tools in the new `aivyx-dataread` crate that turn a file the agent can already reach into legible structured content — the file analogue of `web.extract`: **`data.csv`** (delimited text → rows), **`data.xlsx`** (a spreadsheet's binary zip+XML, which `fs.read` cannot expose, via `calamine`), and **`data.pdf`** (a PDF's text layer via `pdf-extract`, no OCR). Each **reuses the existing `fs.read` capability and sandbox** — it only parses bytes the agent could already read, so it adds no new capability base and no new I/O reach (infrastructure tier, no P10 amendment). - **MCP servers that actually work, especially keyed ones (Chapter Conduit).** The MCP client could connect to servers but not *authenticate* to them, and a misconfigured server failed silently. Conduit adds **`[[mcp_server]] env`** (secrets to a stdio child — the GitHub MCP server's `GITHUB_PERSONAL_ACCESS_TOKEN` was previously unconfigurable) and **`[[mcp_server]] headers`** (e.g. `Authorization: Bearer` to a remote server, never overriding protocol-reserved headers), both with **`${VAR}` interpolation** resolved from the daemon environment so secrets stay out of `aivyx.toml`. Stdio **stderr is now captured** (last 50 lines) instead of discarded, and **`aivyx mcp status`** reports each configured server as connected (with its tool count) or failed (with the reason and captured stderr). No new capability base, P10 amendment, or dependency — the GitHub/weather/Google-Tasks integrations are now operator config, not in-tree builds. ## [0.5.0] — 2026-06-21 The local tool-calling release. A small local model (an in-process GGUF on the mistral.rs engine) can now reliably **drive the agent loop to completion** — emitting a valid, real-named tool call by construction, then finishing its turn instead of looping. Both chapters are **opt-in and byte-identical by default**; no new tool, capability base, P10 amendment, or dependency. ### Added - **Reliable local tool-calling (Chapter Stencil).** Small local models are fragile on the agent loop — they hallucinate tool names and emit malformed arguments, and four prompt-substrate phases proved it can't be fixed from the prompt. Stencil adds the lever the prompt can't reach: **grammar-constrained decoding** on the in-process mistral.rs engine. With **`[mistralrs] constrain_tool_calls = true`** (default off), the decoder is constrained to a JSON-Schema grammar built from the registered tools, so a small GGUF emits a valid, *real-named* tool call — or a `respond` text escape — **by construction**, not by hoping. Live-proven on Qwen3-4B: it called real `fs.read` + `memory.write` with schema-valid arguments where prior phases never got it to invoke a write tool at all. No new tool, capability base, P10 amendment, or dependency; byte-identical when off. - **Hardened local tool-calling (Chapter Bridle).** The harness that makes Stencil's primitive usable in the wild — a grammar that forces a valid call is necessary but not sufficient if the model then can't stop calling it (Stencil's live run watched a constrained model loop on one tool call until the deadline). Three guarded, default-safe fixes: **(A)** a turn-loop **repeated-call breaker** that stops a turn after *N* consecutive identical tool calls (default 3) with a distinct `[turn stopped: repeated tool call]` outcome — a generalizable safety net for any local model, on by default because it only fires on identical repeats; **(B)** a constrained-mode **`respond` preamble** that teaches the model how to reply in plain text and finish (the root-cause fix for the loop); and **(C)** an operator-configurable **`[agent] turn_timeout_secs`** so a legitimately slow local backend can complete a turn (unset → the 120s default, byte-identical). Live re-verified on Qwen3-4B: the looping scenario now ends cleanly — one tool call, a plain-text answer, a completed turn. No new tool, capability base, P10 amendment, or dependency. ## [0.4.0] — 2026-06-20 The memory + skills release. Everything below is **opt-in and byte-identical by default** — nothing changes for an existing config until you enable it. The agent's memory now compounds (graph-augmented recall + a knowledge-wiki + a typed knowledge graph, all behind one `[memory] profile` switch), and its skills now learn (they sharpen when they underperform, the agent authors new specialized ones from its own knowledge, and a Studio Skills screen makes the whole repertoire legible). Plus a pre-release cleanup sweep that cleared the chapter deferrals and stabilized the test suite. ### Added - **The Skills library (Chapter Repertoire).** A new Studio **Skills** screen (the thirteenth) gives the agent's skills a home: every skill — operator- taught, agent-authored (Praxis), agent-refined (Whetstone) — with its effectiveness (the WH.2 decayed EWMA as a bar + bucket label), provenance badge, `domain`, version, and lineage, and the full procedure on demand. A banner links pending skill proposals to the Agents screen (where their approval already lives). Read-only over a new `GetSkills` IPC; no new capability base, tool, or storage domain. - **Skills authored from knowledge (Chapter Praxis).** The agent now writes its own **specialized skills** from what it has learned: on the reflection cadence it finds a topic with rich, connected knowledge (a substantial wiki page + a typed-graph neighbourhood) but no skill, and proposes a grounded specialized skill synthesized from that page + those relations (tagged with the topic's `domain`, agent provenance). Like Whetstone's refinements, it's a governed, propose-only persona proposal in the existing Agents UI. Opt-in via `[skill_authoring]`, off by default; reuses the wiki/graph stores — no new agent tool, capability base, or storage domain. - **Skills that sharpen (Chapter Whetstone).** Skills are no longer a static list. Each turn folds its skill outcomes into a decayed per-skill effectiveness ledger, and on the reflection cadence the agent **proposes a refined version** of an underperforming skill — the operator's *or* its own — as a governed supersession pair (provenance + lineage) that surfaces in the existing Agents approve / edit / reject UI. Opt-in via `[skill_refinement]`, off by default, propose-only; no new agent tool or capability base. - **One-switch smart memory (Chapter Synapse).** The whole memory stack above (graph-augmented recall + the knowledge-wiki and typed-graph layers + their extraction sweeps) was opt-in and spread across ~14 knobs. **`[memory] profile = "smart"`** now arms the coherent bundle with one line (explicitly-set knobs still win; the default stays `off` ⇒ byte-identical). Plus the end-to-end integration proof the layered arc lacked — the real memory + wiki synthesizer + graph extractor + `graph.query` + recall fusion, verified to compose into a single turn — and an operator live-verify runbook. - **Typed knowledge graph (Chapter Lattice).** A real, **directed, typed** graph the agent extracts from memory: nodes are entities, edges are directed `(subject)-[predicate]->(object)` relations (`deploy` —*depends-on*→ `ci`), stored in a new encrypted `KnowledgeGraph` domain. The agent can **query** it with a new **`graph.query`** tool (a multi-hop typed traversal — "what depends on X?", "what did Y cause?"; gated by a new `graph.read` *infrastructure* capability base, no P10 amendment); a new **Studio "Graph" screen** browses the directed graph; and `recall_graph_typed_weight > 0` lets the typed relations steer recall along *meaningful* edges (vs. mere co-occurrence). Opt-in via `[graph].enabled` (a periodic extraction sweep); always derived from memory; zero new dependencies. **Chapter Lexicon** then gives the graph a **controlled relation vocabulary** — a curated set of canonical relation types (`depends-on`, `causes`, `part-of`, …) that synonymous predicates fold into, so `depends on` / `requires` / `needs` become one edge instead of three (applied at extraction + query, with a sweep that merges existing synonyms); unknown relations are kept as-is. - **Knowledge-wiki layer (Chapter Codex).** A derived, browsable layer over memory: the agent consolidates each topic's entries into a **`WikiPage`** — an LLM-written summary plus co-occurrence **backlinks** — persisted in a new encrypted `KnowledgeWiki` storage domain. A new **Studio "Wiki" screen** browses the pages (index → summary + clickable backlinks + source-entry count) over read-only IPC. Opt-in: `[wiki].enabled` arms a periodic stale-page sweep on the maintenance cadence, and `recall_wiki_weight > 0` lets a page summary compete in recall as a single high-signal unit. Pages are always *derived* — memory stays the source of truth. Zero new dependencies. - **`web.extract` + `git.commit` (Chapter Forge).** Two new substrate tools: `web.extract` returns a page's readable article text (readability over the existing `net.fetch` capability), and `git.commit` stages + commits in an operator-allowed repo (a new `git.write` capability base, Trusted-tier only, confirm-first). The substrate count moves 13 → 15 (**Amendment A13b**). - **`tools.list` runtime tool introspection (Chapter Atlas).** A refinement pass over the ~92-tool surface: a runtime `tools.list` tool, a drift-guarded [`docs/TOOLS.md`](docs/TOOLS.md) catalog, and a tool-metadata quality guard. - **Permissive voice (Chapter Timbre).** Swapped the GPL Piper TTS for **Kokoro-82M (Apache-2.0)** + an espeak-free MIT G2P, closing the last GPL door (`cargo deny check licenses` clean with no copyleft exception). ### Changed - **Graph-augmented recall (Chapter Loom).** Auto-recall now fuses three signals on one ranking via weighted Reciprocal Rank Fusion: **semantic** (vectors), **lexical** (a real BM25 scorer, replacing the old substring match), and a **multi-hop co-occurrence graph-walk** (the agent's topic-affinity graph, promoted from a passive view to an active retrieval signal). Tunable per source under `[embedding]` and proven by a `recall@k` eval harness; default off ⇒ identical to prior recall. ## 0.3.0 — source-available (BUSL-1.1) (2026-06-19) **The headline is the license.** Aivyx moves from **MIT** to the **Business Source License 1.1 (BUSL-1.1)**: the whole public workspace is now **source-available** — **free for personal, individual, and non-commercial use**, with a **paid commercial license for business or production use** — and **every released version auto-reverts to MIT four years after it ships.** BUSL-1.1 is source-available, *not* OSI "open source," and we no longer call it that. **v0.2.0 and every prior release remain MIT in perpetuity** — a license can't be revoked; the relicense applies from this tag forward. See [`LICENSE`](LICENSE), [`COMMERCIAL.md`](COMMERCIAL.md), and [`docs/LICENSING.md`](docs/LICENSING.md) (model + FAQ). This release also lands three breadth chapters since the Studio: Contacts, Genesis, and the Docker appliance (Harbor). ### Changed - **Relicensed MIT → BUSL-1.1 (Chapter Charter).** `LICENSE` is the full canonical BUSL-1.1 (Additional Use Grant = personal/non-commercial; Change Date = 4 years per release; Change License = **MIT**, preserved at [`LICENSES/MIT.txt`](LICENSES/MIT.txt)). A dependency-license audit (`cargo deny check licenses`, all-features) confirmed no copyleft poisons the combined work. Every "open source"/"MIT" claim about Aivyx's own code is now "source-available under BUSL-1.1" (README, TRADEMARK, and DESIGN.md's open-core deliverable via **Amendment A14**). - **Contributing now requires a CLA** ([`CONTRIBUTING.md`](CONTRIBUTING.md) + [`CLA.md`](CLA.md)), accepted via a `git commit -s` sign-off — necessary so the free + commercial dual model can lawfully cover contributed code. ### Added - **Google Contacts (Chapter Contacts).** A new `aivyx-contacts` tool process (People API) with six tools over `contacts.read`/`contacts.write` (`contacts.search`/`list`/`get`/`create`/`update`/`delete`); connect with `aivyx connect contacts`. The fifth Google integration on the substrate pattern. See [`docs/CONTACTS.md`](docs/CONTACTS.md). - **Unified agent creation (Chapter Genesis).** One onboarding flow across CLI and web: the Profile drafter is lifted into the daemon (`DraftProfile` IPC) and a "Create your agent" flow (Profile → Persona → access) lands in the Studio. See [`docs/ONBOARDING.md`](docs/ONBOARDING.md). - **Docker server appliance (Chapter Harbor).** A `docker-compose` deployment of the daemon + Studio in a container (distinct from the desktop local-first install), with two opt-in web-exposure knobs (`web_ui_host`, `web_ui_allowed_origins`); CI publishes the image to GHCR. See [`docs/DOCKER.md`](docs/DOCKER.md). - **Tool-call rate limits & quotas (Chapter Throttle).** A third dispatch gate — after the capability + role gates, before execute — bounds *how often* tools run, the sibling of Chapter K's dollar budgets for call counts. Opt-in `[rate_limit]` config sets per-turn-per-tool, per-turn-total, and per-tool sliding-window caps with an `alert` (warn + proceed) or `deny` (block) action; a throttled call yields a forensically-distinct `ToolOutcome::RateLimited` and a dedicated `RateLimited` audit record (separate from capability/role denials). Bounds a runaway turn — autonomous loop, Nonagon mission, or interactive — from hammering `web.fetch` / `shell.exec`. Uncapped by default, so existing configs are unchanged. See [`docs/RATE_LIMITS.md`](docs/RATE_LIMITS.md); closes backend audit finding **F2**. ### Fixed - **Storage-domain count corrected (21, was documented as 20)** — the Phase-183 `Reminders` `KeyDomain` was never propagated to the docs. Added a `key_domain_count_matches_docs` drift-guard test (audit **F7**). ### Security - **The Studio `/ws` bridge now enforces an `Origin` check** (closes a Cross-Site WebSocket Hijacking / DNS-rebinding vector). A loopback bind is not a boundary against the browser — WebSockets are exempt from the same-origin policy — so a malicious page the operator visited could otherwise open `ws://127.0.0.1:7843/ws` and drive the already-unlocked daemon, which since the Studio's write screens can change config and the filesystem. The `/ws` upgrade is now accepted only when `Origin` is absent (a non-browser client, already inside the trust boundary) or exactly matches a loopback origin on the bound port; cross-site, rebinding, wrong-port, and `null` origins are rejected with `403`. See `THREAT_MODEL.md` §4.11. ## 0.2.0 — the Studio (2026-06-16) The headline is the **Studio** — the daemon's web GUI grew from two tabs into a **complete** local-first mission-control surface, one screen per chapter (R–Z plus Voice), every one live-verified in a real browser with its WASM bundle committed. The entire screen inventory is now live: Command, Missions, Chat, Memory, Settings, Agents, Teams, Documents, Voice. ### Added - **The Studio web GUI is complete.** Every screen is live, offline, and Stitch-styled, served from the daemon's embedded bundle on `:7843`: - **Command Center** (S) — the default dashboard: stat cards, active missions, a live audit-trail feed, agent status. - **Memory** (T) — topic rail + entry cards + keyword/semantic search over the self-learning memory, **plus a knowledge-graph view** (MG): a real weighted graph (nodes = topics, edges = the co-occurrence ledger's pair scores) laid out in-WASM with a deterministic force-directed simulation; click a node to filter its entries. - **Settings** (U) — the **first config-write surface**: edit the access level (confirm-first, enforced server-side) and budgets; section-scoped `toml_edit` rewrites with a `ConfigChanged` audit and an honest "restart to apply". - **Agents** (V) — a direct **Profile** editor plus the self-learned **Persona** governance loop (approve / edit / reject proposals, revert deltas), live. - **Teams** (Y) — a read-only view of the active **Nonagon** roster. - **Documents** (Z) — a **file browser and editor** over the agent workspace and the access-scoped `fs_root`: read files, and (DW) edit + save, create files/folders, rename, and delete. - **Voice** — a `[voice]` config editor with a daemon-side readiness check (ASR/TTS model + espeak data: present / missing / unset) and the launch command. Voice itself is a host-local CLI loop (`aivyx --channel voice`), so the screen configures it rather than doing browser audio. - **Onboarding Persona/Skills seed (Chapters W–X).** The end user can give the agent a starting Persona + Skills — by hand or by **describing it in words** (the model drafts it) — at first launch (`aivyx init` → `[persona_seed]`, planted on the signed chain at boot iff empty, adopted turn-one) or live from the Studio. See [`docs/PERSONA_SEED.md`](docs/PERSONA_SEED.md). ### Security - **Documents browsing never escapes its root.** `ListDir`/`ReadFile` reuse the fs tools' lexical-resolve → canonicalize → `starts_with(root)` guard, so `..` and symlink escapes are rejected over IPC; reads are size-capped + binary-aware; the `fs` root is exactly the operator-granted access level. - **Editable Documents is the most safety-sensitive surface, and gated to match (DW).** Writes canonicalize the *parent* dir (a new file can't be canonicalized) and re-check `starts_with(root)`; `WriteFile` carries an explicit `overwrite` flag (no accidental clobber); rename/mkdir refuse existing targets; `DeleteFile` is **empty-only, never recursive, and always requires `confirm: true`** (the web shows a confirm modal). Writes are atomic (temp-file + rename), and **every mutation is recorded as a signed `DocumentMutated` audit event.** ### Fixed - The `web_asset_lookup` unit test asserted the bundle was *absent*, which has been false since the bundle became committed in Chapter R; rewritten for the shipped reality. The full workspace test suite is green (4,666 passing). ## 0.1.0 — first public pre-release (2026-06-13) **This is an early pre-release.** Aivyx is a capable, actively-developed personal agent, but `0.1.0` is its first published binary and has not yet had external users. Expect rough edges, breaking changes between releases, and gaps in the docs. Try it, file issues — but don't depend on it for anything critical yet. ### What Aivyx is A local-first, capability-secured personal AI agent that runs as a daemon on **your** machine. Its headline path is **"runs on your hardware, no API key"** via [Ollama](https://ollama.com) — cloud providers (Anthropic, OpenAI) are optional. The agent has a persistent, self-learning Profile/Persona/Soul, an HMAC-chained audit log of everything it does, and a capability/trust-tier model that bounds exactly what it can reach. ### Highlights in this release - **Local-first on-ramp that just works.** Pick Ollama in `aivyx init` and you get a working, tool-using first conversation with **zero manual config**: context length is auto-detected from the model (no more starved `num_ctx` one-token replies), the wizard recommends and offers to download a vetted tool-capable model, and `aivyx doctor` verifies the whole path end to end. - **Operator-chosen access levels.** *You* decide how far the agent reaches — from a sandbox, to your home directory, to full-machine access — via `aivyx access` and the init wizard. Irreversible filesystem operations are confirm-first; remote channels are automatically attenuated. - **The agent's own workspace.** A private, always-available directory (`~/.aivyx/workspace`) the agent uses for its own thoughts, ideas, plans, and projects, with optional proactive journaling. - **Multi-agent teams in the daemon.** Goal → plan → delegated execution with a live TUI feed and human approval gates. - **A browser Mission-Control GUI.** A Rust→WASM (Dioxus) web client that speaks the daemon's wire protocol directly — Missions and Chat in the browser. - **Productivity integrations.** Gmail, Google Calendar, Google Drive, Notion, Obsidian, n8n, and a web/task/health toolkit, each as an isolated tool process with operator-provided OAuth. ### Install ```sh curl --proto '=https' --tlsv1.2 -LsSf \ https://github.com/Aivyx-Agent/aivyx/releases/download/v0.1.0/aivyx-cli-installer.sh | sh ``` Then run `aivyx init` to set up your agent. See `docs/INSTALL.md` for building from source and `docs/LOCAL_FIRST_RUN.md` for the local-model path. ### Platforms Prebuilt binaries for Linux (x86_64, aarch64; musl-static) and macOS (x86_64, aarch64). Windows is not yet supported (the daemon currently uses Unix domain sockets). All platforms can build from source.