# Changelog

## 2026.3.488

### Highlights

- Working status talk is plain trajectory for anyone. While koi works you hear short human lines about where it is in YOUR task — not canned fillers like "still on it", and not tool names or micro tech. A fast-lane model writes each line from the moment; quiet stretches get a fresh reassurance; progress pings speak too.

### Changes

- status: new `src/koi/fast-lane-status.ts` asks the configured fast-lane model for one short first-person line (tool start, heartbeat, wake, instant act); failures stay quiet instead of recycling phrase tables.
- tool-narration: removed hard-coded filler lists; coarser beat spacing; skips plumbing tools; scrubs accidental tech from spoken lines; prefers the model's own plain `_description`.
- reflex / wake / stop: instant-actor and performances feed context into fast-lane status instead of fixed say strings; bare wake and stop acks are model lines too.
- progress: tool schema asks for plain 4–12 word trajectory lines; `chat.progress` also emits narration so voice hears the same stream.
- run_code: `_description` schema text requires plain spoken trajectory (no tool names/paths).

## 2026.3.487

### Highlights

- Gateway status stops contradicting itself. A running gateway with no autostart unit used to print "Runtime: stopped (Scheduled Task not installed)" on the same screen as "RPC probe: ok". Liveness is now read from the running process (its lockfile / listening port / RPC), reported on a separate line from whether an autostart unit is registered — two true statements instead of two contradictory ones. Sign-in also tells you when the gateway came up without autostart (it would otherwise stop on logout) and points at `skykoi gateway install`.
- Fresh installs are calm on disk and CPU. Config writes no longer trigger a full gateway restart every time (a stray timestamp used to force one, restarting the tunnel and capture and churning backup files); the graph upload queue drains with a tiny append instead of rewriting the whole file every 30s (it was writing on the order of 100 GB/day on an idle machine); the first-run file scan paces itself to the uplink instead of flooding the queue to its cap; and old logs, capture frames, and session transcripts are actually reaped on a schedule now.
- You can see and clear what capture is doing. Gateway status now reports screen frames on disk, the pointer-trail depth, and the upload backlog, and there is a wipe for captured frames and the pointer trail alongside the existing audio wipe. On Windows, `koi capture status` stops reporting macOS-only "input daemon" facts that were never true there.
- The tunnel binary is verified before it runs. cloudflared is checked against a SHA-256 and, on Windows, a valid Cloudflare code signature before it is allowed to execute — it used to be downloaded from an unpinned URL and run with no integrity check. Local HTTP surfaces also now reject cross-site browser requests, so a web page you visit can no longer reach the loopback control endpoints.

### Changes

- gateway status: process liveness resolved lockfile → port → RPC and reported separately from autostart configuration; deduplicated the runtime hints and the doubled "File logs:" line.
- login: reports when the gateway has no autostart unit ("stops on logout/reboot") instead of unqualified success.
- gateway: the shutdown watchdog restarts the run loop on a restart instead of `exit(0)`-ing an unsupervised gateway; a latch prevents a slow close from clobbering the freshly-started server.
- config: a `meta` stamp change is a no-op in the reload planner (no restart), and identical writes are suppressed entirely (no backup churn).
- graph outbox: acknowledgements are tombstoned in a sidecar and compacted rarely rather than rewriting the whole 50 MB file per drain; the event and preview outboxes both read once per flush.
- capture: the first-run file inventory backpressures on outbox depth and shares one file budget across roots (default 1,000, was 10,000 per root); inventory progress is surfaced in `capture.status`.
- retention: an independent reaper (gateway-startup + daily) bounds capture frames, logs, and session transcripts, honoring `retentionDays`; it never touches unsent outbox data or a file written in the last hour.
- capture: `capture.status` reports frames/pointer/backlog; a `capture.wipe` RPC clears frames and the pointer trail; the `capture status`/`stop` CLI is platform-gated.
- security: cloudflared is verified (SHA-256 + win32 Authenticode) before it is promoted to an executable path; the version is pinnable and the install is recorded.
- security: local token-less HTTP surfaces (`/connect/api/*`, the canvas plane) reject cross-site requests via a Sec-Fetch-Site / same-origin guard; `/files/download` echoes a validated Origin instead of `*`.
- mesh: the advertised peer address is reconciled with the actual bind — a loopback-only gateway no longer advertises an unreachable LAN address.
- outbox files: owner-restriction (real ACLs on Windows) is applied on create and after compaction instead of a no-op chmod on every write.

## 2026.3.486

### Highlights

- Signing in no longer races a 10-minute clock. `koi login` (and the installer's sign-in step) now waits up to 45 minutes for you to click Approve, and the platform keeps the request alive the whole time the CLI is waiting — so an approval tab you get to a little late just works instead of dying with "request expired."

### Changes

- login: browser-approval wait 10 → 45 min (pairs with the platform's sliding request TTL); clearer message on the rare true expiry.

## 2026.3.485

### Highlights

- Voice now starts on its own on fresh Windows installs. The installer sets up ffmpeg in the background, and the gateway's "rechecking every minute" promise was broken — a first probe that ran before ffmpeg landed (or while Defender scanned the fresh download) was cached as a permanent miss, leaving hearing dead until a manual gateway restart. The recheck now really rechecks.

### Changes

- capture: `resolveFfmpeg` caches success only (a miss stays re-probed each minute) and the probe timeout is 15s so the first-ever run of a freshly-downloaded ffmpeg.exe survives its Defender scan.

## 2026.3.484

### Highlights

- First launch always lands on the zen stage — even when the model can't run (no key, provider outage, out of funds). A failed first onboarding used to strand you in the classic console forever, making a fresh install look old or broken; the koi now seeds a starter profile, unlocks the stage, and hands you the terminal in-session, then deepens its dossier as it gets to know you.
- `koi uninstall` is truly no-trace now. It stops the supervised gateway's relaunch loop before deleting data — so `~/.skykoi` is actually removed instead of silently surviving — and clears the private Node runtime, leftover `koi` shims, and skykoi PATH entries. ffmpeg removal is opt-in (`--remove-ffmpeg`). A matching one-liner lives at skykoi.com/uninstall.ps1 for wedged installs.

### Changes

- onboarding: workspace bootstrap seeds a minimal real profile.json (never clobbering one the koi wrote); every onboarding conclusion — success, error, stall cap, kickoff failure — seeds + marks onboarded, and when the launch held chat for onboarding the TUI flips to the zen stage in-session instead of waiting for a relaunch.
- uninstall: kill the cmd/wscript supervisor and gateway node before data removal (excluding the uninstall's own process); remove installer traces outside ~/.skykoi (private ~/.skykoi-node runtime, skykoi-referencing shims only, skykoi-only User PATH entries); new `--remove-ffmpeg` flag; trace removal is skipped with `--keep-cli` so a kept CLI still works.

## 2026.3.483

### Highlights

- Fixes a turn that could freeze on "getting started…" when the model service was down and the backup model couldn't be reached either. The turn now always resolves to one honest error instead of hanging silently — on the web and in the terminal.
- No more error "flash": the terminal no longer shows a red error mid-failover for a turn it's still trying to recover from. The error appears once, after the whole turn has actually resolved.

### Changes

- run: model fallback shares one run id across attempts, so a failed non-final attempt was marking the run terminated — which suppressed the real error terminal at the exhaustion point (every model spent). Each attempt now resets the per-run terminal guard, so exactly one honest terminal fires once failover is exhausted; the web/terminal no longer hangs on "getting started".
- tui: a terminal "error" is no longer painted from a raw intermediate lifecycle error (which could be a fallback attempt the turn recovers from). Terminal status is owned by the grace-window-aware chat final/error, matching the web; a successful end still reflects immediately.

## 2026.3.482

### Highlights

- Chat never hangs on a failure anymore. When a turn can't finish — the model backend is down, or every model failed — the koi now shows a real, honest error and the "getting started…" spinner clears, instead of sitting silent forever. This holds on the web, in voice/call mode, and in the terminal, for single-koi and multi-koi turns alike.
- Honest errors when the model service is down: a backend outage on our side now reads as "the model service is temporarily unavailable — a service-account issue, not your balance," and the koi fails over to its backup model — never "you're being rate-limited, try again in a moment" (false) or "top up your credits" (not your account).
- Ask the koi what model it's running and it answers you directly and truthfully — "running on Claude Fable 5" — instead of pointing you at a status screen.
- The gateway keeps itself up to date without needing root. When a new build is already installed on disk, the gateway restarts into it directly instead of trying to reinstall — which fixes devices getting silently stranded on an old version (a stale `/model` list was the tell). If an update genuinely can't install because the global npm dir needs root, it now says so with the one-line fix instead of failing quietly.

### Changes

- run: a failed turn emits one honest terminal lifecycle event (phase "error" with a stable errorClass + user message) instead of a bare "end" that rendered as an empty reply; single owner of the run terminal.
- broadcast: a multi-koi turn emits a completion terminal under the parent run id so the working indicator clears once every companion has answered.
- errors: `model_backend_unfunded` (503) is a first-class `service_unavailable` class/reason — triggers failover, never conflated with rate-limit/overloaded/billing, and never cools down a healthy auth profile; the "all models failed" aggregate now reports the primary root cause, not the last fallback to fail.
- fast lane: model-identity questions are answered directly from the session model; the digest labels it cleanly.
- update: the gateway restarts into a newer on-disk build with no npm/root (the shared on-disk build reader now backs both the gateway loop and the TUI build-watch); permission-blocked installs log the exact fix.

## 2026.3.481

### Highlights

- The `/billing` panel works end-to-end again: balance, Stripe top-ups, card/receipts, and monthly caps all talk to the restored platform billing plane (the old endpoints were lost in the platform→live merge and every call 404'd). Capability usage (hearing/voice/fast lane) is metered and billed again instead of silently dropped.
- Honest errors when the model backend is down: a platform-side outage no longer reads as "your credit balance is too low" — the koi reports a service outage, fails over to the backup model, and the alert clears as soon as the provider recovers instead of lingering for 30 minutes.
- `/model` shows the SkyKoi catalog again (a keyless provider entry was silently wiping every custom model from the picker), and on claimed devices the picker now lists ONLY the SkyKoi models — refreshed from the cloud catalog endpoint, so new models appear without reinstalling.
- The fast voice tells the truth about itself: asked which model it is, it answers from `/status` instead of claiming the smart model's name, and every instant reply carries a `⚡ … · fast lane` attribution line so you can always tell which lane answered.

### Changes

- models: keyless custom providers are omitted from models.json (one invalid provider used to make the registry drop ALL custom models); registry load errors are logged; the gateway model catalog re-resolves when config changes instead of caching for the process lifetime.
- models: new cloud-vended catalog client (`/api/device/models`, 12h cache, falls back to the baked-in catalog when absent); claimed devices' `models.list` returns the SkyKoi catalog only; `fable`/`opus`/`sonnet` aliases now land on the `skykoi/` provider spelling claimed devices actually use.
- billing: the friendly billing message is no longer shadowed by the generic invalid-request branch; the platform/web dispatch path emits a stable `errorClass` + user-safe sentence; billing alerts carry the real failing provider and clear on the next successful turn; "all profiles unavailable" reports as a billing hold when that is the cause.
- billing: `/billing` pay/portal links open the browser on Windows and Linux, not just macOS.
- errors: the platform proxy's `model_backend_unfunded` signal (shared model account exhausted) classifies as an outage — fallback fires and the user is never told to top up someone else's account.
- fast lane: the context digest labels the session model as `smart_model` ("the deep model behind you — not you"); the router prompt pins its identity and routes model questions to `/status`.
- capture: exited processes are tombstoned, the preview leak is fixed, and the hot path is unblocked; graph no longer loses data to false deletes and outbox fan-out.
- dossier: secrets are redacted on export; cross-account context is hardened.
- onboarding: the deep onboarding pass is reliable and non-trapping.

## 2026.3.480

### Highlights

- macOS hearing now works permanently, not just for an hour. The capture daemon is rebuilt to run on macOS 13+ (universal Intel + Apple Silicon; the previous binary required macOS 26 and never ran elsewhere), and it now re-resolves the rotating Soniox key on every reconnect instead of caching one at startup — so live transcription no longer dies when the key rotates.
- The proactive assistant no longer goes silent when the primary model is unavailable: it now falls back to the configured backup model (e.g. grok-4.5) like the main chat lane already did.

### Changes

- audio-daemon: build universal (arm64 + x86_64) targeting macOS 13 (`-target …-macos13.0`); the previous build inherited the build machine's OS (macOS 26) as its minimum and dyld-aborted on every older Mac.
- audio-daemon: re-resolve the Soniox key on each WebSocket connect from env/.env, else the gateway's `provisioned-keys.json`; send `language_hints` (default `en`) for parity with the Windows listener.
- gateway (capture): while audio capture is active, re-mint the provisioned key every 40 min so the file the daemon reads never goes stale.
- proactive: the smart actor falls back to the configured fallback model(s) (grok-4.5, then moonshot) in OpenAI chat format when the Anthropic proxy can't answer — previously it had no failover and spammed HTTP 400s.

## 2026.3.479

### Highlights

- Fixed voice/hearing being completely dead on macOS installs: the mic-capture audio daemon is now bundled in the published package. It was never shipped before, so on any npm-installed macOS koi the daemon couldn't be found or built and the mic never turned on — Windows worked (its listener is plain bundled JS) but macOS didn't. Now both platforms hear.

### Changes

- packaging: ship `src/capture/audio-daemon/` (the macOS capture daemon app + source) in the npm package so `daemonBinary()` resolves on real installs; previously only present in dev checkouts.
- capture (macOS daemon): emit live `mic-partial.json` interim transcription (throttled ~100ms) so the visible-hearing ticker streams the wearer's words as they speak — matching the Windows listener. (Takes effect once the bundled daemon binary is rebuilt from this source on a machine with a working Swift toolchain.)

## 2026.3.478

### Highlights

- Fixed koi mishearing English as another language: the mic transcriber now pins recognition to English, so "Koi, …" stops being transcribed as Mandarin and the wake/stop words match reliably.
- Full-width punctuation from a drifted transcript ("Hey koi。") is normalized so wake, stop, and the hearing ticker still match.

### Changes

- capture: send `language_hints` to the speech-to-text (default `en`, override with `SONIOX_LANGUAGE_HINTS`) — stops per-utterance language drift that swallowed the wake word.
- capture: normalize full-width CJK punctuation to ASCII at the daemon, the trigger pipeline, and inside the stop matchers.
- capture: koi's own live work-narration no longer re-triggers it — narration lines register in the self-echo tracker at emit time, so their mic echoes can't spawn duplicate runs.
- capture: the replayed recent-audio context now attributes koi's own spoken replies as koi (not the user), so "answer what I just said" resolves correctly instead of replying to itself.
- tui: the hearing ticker handles wide characters (CJK/emoji) without breaking column alignment.

## 2026.3.477

### Highlights

- Voice you can trust: fixed every clip being cut off at ~60% of its length (an ffplay buffering flag made `-autoexit` fire early), so spoken replies and acks now play to the end.
- A real emergency stop: saying "koi stop" (or "koi abort") halts the mission, batches, and voice instantly — hardcoded, no model, no gating. A bare spoken "stop" does the same with a wearer-voice check so a TV or stranger can't kill your run.
- Visible hearing: a live ticker above the composer streams everything koi hears — trigger words color-coded by what they fired, with a tiny annotation (⏹STOP, ●wake, ⚡action, →smart, ∅reason, ·echo) anchored above the exact word, scrolling in sync. Debugging what koi heard and why it acted is now on-screen instead of in logs.
- Live captions synced to koi's voice: synthesis uses ElevenLabs character timestamps, so the caption lights up word-by-word exactly as the voice speaks it.

### Changes

- voice: barge-in now only triggers on stop-class utterances — repeating yourself while koi is replying no longer silences the reply.
- voice: wake acks and confirmations are short phrases instead of single words (isolated words synthesized as phonetic mush).
- triggers: bare wake ("koi." / "hey koi") is answered instantly from the trigger tail instead of burning a smart-lane run.
- triggers: short conversation-mode fragments without a koi mention require a positive wearer voice match — kills echo storms where koi's own voice or an open video spawned duplicate runs and tabs.
- triggers: every heard utterance now emits a verdict (including device-audio echo and self-echo drops), so the hearing ticker accounts for everything.
- instant lane: a fast-lane model maps direct spoken commands (play/open/close/volume/scroll…) to immediate visible actions, with learned-skill caching; self-complete control actions are swallowed so the slow lane can't undo them.
- stop: run-level abort — stop halts the whole mission, not just the current batch.

### Highlights

- Fixed the zen stage on Windows: the koi-pond backdrop now renders in full 24-bit color and covers the whole terminal on any window shape.
- Stopped console windows flashing every few seconds on Windows while the gateway advertises on the local network.

### Changes

- zen: primed TERM/COLORTERM on Windows so terminal-kit resolves a truecolor profile instead of a colorless generic one (previously every RGB attribute of the pond was dropped).
- zen: pond backdrop scales to cover the stage (aspect preserved, center-cropped) instead of letterboxing on wide or short windows.
- zen: boot size and resize events are logged under ZEN_DEBUG for diagnosing sizing issues.
- sessions: event logs now persist on Windows for session ids containing colons (previously every append failed with ENOENT and history replay was silently empty).
- windows: patched @homebridge/ciao to pass windowsHide to its periodic `arp -a` interface poll, eliminating the recurring terminal-window flash.

## 2026.3.425

### Highlights

- Added consent-scoped device capture, signed durable-graph synchronization, and preview delivery across macOS, Windows, and Linux.
- Added skippable natural-language onboarding with local dossier review before cross-device synchronization.

### Changes

- Added resumable event and preview outboxes with reconnect-safe cursor handling.
- Added file, process, audio, and input capture controls with bounded retention and explicit approved roots.
- Added cross-platform preview generation for images, video, documents, code, text, and other file types.
- Added gateway graph projection, capture-policy reload, and platform synchronization health reporting.

### Safety

- Capture remains off until the user approves a purpose and specific source categories.
- Passwords, tokens, secure fields, unapproved filesystem roots, and command-line contents are excluded.
- Dossier and preview synchronization require a separate approval and remain revocable.

## 2026.3.424

### Highlights

- Added Universal Access Orchestration across API, browser, enterprise, vault, device, and human-assisted routes.
- Rebuilt onboarding as six resumable stages that exhaustively map accounts, relationships, work, personalization, portraits, and realtime automation.

### Changes

- Added provider-neutral account, route, capability, health, trust, subscription, and policy models.
- Added lowest-friction route selection with short-lived opaque access leases.
- Added browser, messaging, email, enterprise directory, and vault discovery adapters.
- Added enterprise connection support for Google Workspace, Microsoft 365, Okta, Slack, GitHub, Nylas, and 1Password Connect.
- Added realtime event normalization, relevance policy, deduplication, Koi wakeups, and safe notification routing.
- Added per-user onboarding checkpoints, source coverage, provenance requirements, gap tracking, and continuous refresh instructions.

### Safety

- Credentials remain behind local references and are excluded from dossiers and platform snapshots.
- Human approval remains required for unavoidable consent, unlock, passkey, two-factor authentication, CAPTCHA, and security challenges.
