From dc5ee6bec9753a0c9922575b874072b86541f676 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 17 Aug 2026 03:39:19 +0000 Subject: [PATCH 1/2] =?UTF-8?q?chore(release):=20all=20four=20lanes=20?= =?UTF-8?q?=E2=80=94=20mobile/hub/host=202026.817.322-alpha,=20desktop=202?= =?UTF-8?q?026.817.322?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four tags go on this commit once merged: mobile-v2026.817.322-alpha APK + IPA, non-prerelease, owns releases/latest hub-v2026.817.322-alpha hub-server tarballs, prerelease host-v2026.817.322-alpha host-runner tarballs, prerelease electron-v2026.817.322-alpha desktop unsigned alpha, prerelease Stamp minted with `date -u` (2026-08-17 03:22 UTC). 46 commits since 2026.805.1022 — the largest gap between cuts so far. **Hub/host, 18 commits.** Pane-state detection lands as a lane: a declarative evaluator over vendored screen manifests (P1), wired to the poll tick (P2), raising and retracting attention on a blocked pane (P3), and explaining its own verdict rule by rule through `pane_explain` (P4). Plus native-resume recipes as shared data (N1), generic pane-input hardening (Q1), and four engine-vocabulary corrections found by measuring real binaries: claude's discarded sub-agent provenance (R5), codex's swallowed command output (E3), the attachment shapes codex rejects (L4c), and a runtime mode/model switch that resolved the engine from the wrong column so it had never worked for a steward (#570) — plus the field mask that makes the three impossible switches say so (R6). Security: an engine-supplied session id could inject shell into the next respawn. **Desktop, 32 commits.** The Companion runs a claude (L3a) or codex (L4c) session locally with no hub, surviving app restart with transcript and memory restored separately (L3b); the transcript grew streamed output and inline media (R4), a sub-agent panel (R5), and model/permission pills (R6). Read became a discovery workspace — six literature sources plus Scholar, cadence monitors, social subscriptions, ratings, citation details. Plus rich Inspect previews, a bundled terminal font, an SFTP transfer queue, and a monochrome theme pass. ⚠ Operators must redeploy the hub and host-runner. Both the 2026.730.1231 and 2026.805.1022 sections said the same about their own 51 and 17 commits; if neither happened this cut compounds both, and every pane-state feature above is host-runner code by construction. **Changelog gaps closed in the same pass.** Neither changelog's Unreleased section covered the ~18 Fleet desktop PRs or four hub-side changes (the Read library's attachments + rating columns, and the hub halves of R5 and R6), and the desktop section had seven feature entries misfiled under `### Fixed` plus a duplicate heading in each file. Written up and restructured rather than shipped as a partial record. Verified: hub `go build ./...` + full `go test ./...` (exit 0, no FAIL), desktop tsc + vite build (4m43s), 900 frontend tests, 726 electron tests (720 pass / 6 skipped / 0 fail), 12 lints, NUL/CJK scans clean. Flutter is absent on this host, so the mobile analyze/test/APK legs are CI-only. Co-Authored-By: Claude Opus 5 --- desktop/package-lock.json | 4 +- desktop/package.json | 2 +- docs/changelog-desktop.md | 334 ++++++++++++++++++---------- docs/changelog.md | 134 +++++++++-- hub/internal/buildinfo/buildinfo.go | 2 +- pubspec.yaml | 2 +- 6 files changed, 338 insertions(+), 140 deletions(-) diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 691b41a1..f6f11e38 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "termipod-desktop", - "version": "2026.805.1022", + "version": "2026.817.322", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "termipod-desktop", - "version": "2026.805.1022", + "version": "2026.817.322", "dependencies": { "@codemirror/commands": "^6.10.4", "@codemirror/lang-markdown": "^6.5.0", diff --git a/desktop/package.json b/desktop/package.json index 82886d84..25fd66a3 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,7 +1,7 @@ { "name": "termipod-desktop", "private": true, - "version": "2026.805.1022", + "version": "2026.817.322", "type": "module", "description": "TermiPod desktop control plane — the unified web-tech client shell (WS2 of docs/plans/desktop-control-plane.md). Browser-target build; the Electron shell (desktop/electron) packages it (ADR-055).", "scripts": { diff --git a/docs/changelog-desktop.md b/docs/changelog-desktop.md index 13369497..3f85748c 100644 --- a/docs/changelog-desktop.md +++ b/docs/changelog-desktop.md @@ -1,9 +1,9 @@ # Desktop Changelog > **Type:** reference -> **Status:** Current (2026-08-05) +> **Status:** Current (2026-08-17) > **Audience:** contributors, operators -> **Last verified vs code:** desktop 2026.805.1022 / electron-v2026.805.1022-alpha +> **Last verified vs code:** desktop 2026.817.322 / electron-v2026.817.322-alpha **TL;DR.** Append-only record of what shipped in each **desktop workbench** release. One section per version, newest first. Format follows @@ -39,86 +39,27 @@ This complements: --- -## Unreleased +## 2026.817.322 — 2026-08-17 + +**The Companion becomes somewhere you can actually run an agent, and Read +becomes somewhere you can actually find one.** A claude or codex session now +runs on this machine with no hub in the path, survives quitting the app, and +comes back with both halves restored — the transcript from our own append-only +log, the memory from the vendor's native resume. The transcript around it grew +what a session needs: streamed command output, inline images, a sub-agent +panel, and model/permission pills that appear only where the switch is real. +In parallel the Read lane went from a library to a discovery workspace — six +literature sources plus Google Scholar, saved monitors on a cadence, social +subscriptions, ratings and citation details — and the workbench itself took a +visual pass: a monochrome product theme, integrated window chrome on macOS and +Linux, and a bundled terminal font. `electron-v2026.817.322-alpha`, 32 desktop +commits since `2026.805.1022`. + +> This is an unsigned alpha prerelease. It does **not** become the go-live +> build until a `promote=2026.817.322` dispatch points the `electron-latest` +> feed at it — a switch still owed from the previous two cuts. ### Added -- **You can now see what a sub-agent actually did.** The Sub-agents chip above - the composer lists the delegated agents; clicking one opens what it was asked - to do and everything it has done — its tool calls, its notes, and what it is - running right now. (vision-parity R5) - - For an engine that does not say which events belong to a sub-agent, the panel - still shows the request and says plainly that the rest cannot be separated — - which is a different sentence from "this sub-agent has not done anything - yet", and the panel picks the right one. - -- **The transcript now shows which model is about to answer — and lets you - change it where that is actually possible.** Two pills sit beside the - context ring, in the composer's own row, because these are questions asked - while typing rather than facts looked up in a details panel. An agent that - advertises its choices (the ACP engines) gets a menu; claude, which - advertises none, gets a text entry taking an alias or a full model name. - Switching a model on claude or codex **restarts the agent** — the hub spawns - a replacement on the same session, so the transcript continues — and the - pill says so and asks first, rather than doing it and letting you find out. - (vision-parity R6) - - **A pill that cannot change anything says so instead of disappearing.** - Mobile's picker hides itself whenever the agent advertises no options, which - on a claude session means no model indicator at all. Here the value still - shows, greyed, with the reason on hover: "cannot be changed while running" - and "unknown" are different facts and should not look the same. - -### Fixed -- **A sub-agent's work used to be reported as the main agent's.** claude's - stream carries a `parent_tool_use_id` on every frame — null for the main - agent, the delegating call's id for a sub-agent's — and the translator threw - it away. So a delegated tool call appeared in the transcript as if the agent - you were talking to had run it, and the guard that keeps a sub-agent's token - usage out of the session's turn counts (added for kimi) never fired for - claude, quietly inflating them. Both translators now stamp it, along with the - sub-agent's type, and a real recording of a delegated run is pinned in the - parity corpus so the two implementations cannot drift apart on it. -- **Three of the four runtime switches could never have worked, and nothing - said so.** The registry recorded *how* a mode/model switch travels but not - *whether* a given field can travel at all, and a switch that rewrites a - launch flag can only work if the flag is in the spawn command to begin with. - Measured against the real binaries: claude's `--permission-mode` is a real - flag that no template ships, `codex app-server` takes no `--model`, and - `--approval-policy` is not a codex flag at all (0.147.0 answers *"unexpected - argument"*; it is `--ask-for-approval`). All three answered a 422 advising - the director to pick a template that exposes the flag — a template that - cannot exist. Families now declare `runtime_switch_fields` per field, the - hub checks it before routing, and the UI hides a control it knows would be - refused. Only claude's model switch was ever real; it still is. - -- **The Companion can now run a *codex* session on this machine too.** Pick - `codex` in the local picker and the dock drives it through the vendor's own - `app-server` — a JSON-RPC thread that streams as it writes, folds tool calls - the same way a claude session does, and comes back after an app restart with - its memory intact (`thread/resume`) and its transcript re-read from disk. It - reuses the hub's own codex frame profile, so a local codex transcript and a - hub-driven one are the same rows. Where a shared codex daemon is available - the session attaches to it and survives the app; otherwise it runs a - per-session app server, and the transcript **says which one it got** — those - are different promises. (vision-parity L4c) - - **What the agent may do is measured, not named.** A codex session lowers the - same three postures to codex's own sandbox: `read_local` (the default) opens - the thread with `sandbox: read-only` and `approvalPolicy: never`, which was - probed by asking codex to create a file — it tried, failed, said *"the - environment is read-only"*, and nothing appeared on disk. `converse` cannot - be kept exactly (codex has no way to turn its tools off), so the transcript - carries a line saying the agent can still read files, rather than letting the - name imply otherwise. - - **Approvals and questions arrive as cards in the feed**, answered inline — - a local session has no attention table, so this is where codex's gates live. - Four kinds are declined immediately instead, each with a line saying why: a - form-fill an inline card cannot type into, a "open this URL" request, a - permission-profile grant, and — deliberately — any question codex marks - **secret**, because an answer sent through the Companion is written to the - session transcript on disk, which is not where a secret belongs. - **The Companion can run a claude session on this machine, with no hub.** A local agent service in Electron main owns the engine child and keeps the @@ -165,6 +106,99 @@ This complements: generated artifact and pinned to the hub's conformance corpus, so the two languages cannot drift on how an engine reattaches. (vision-parity L3b) +- **The Companion can now run a *codex* session on this machine too.** Pick + `codex` in the local picker and the dock drives it through the vendor's own + `app-server` — a JSON-RPC thread that streams as it writes, folds tool calls + the same way a claude session does, and comes back after an app restart with + its memory intact (`thread/resume`) and its transcript re-read from disk. It + reuses the hub's own codex frame profile, so a local codex transcript and a + hub-driven one are the same rows. Where a shared codex daemon is available + the session attaches to it and survives the app; otherwise it runs a + per-session app server, and the transcript **says which one it got** — those + are different promises. (vision-parity L4c) + + **What the agent may do is measured, not named.** A codex session lowers the + same three postures to codex's own sandbox: `read_local` (the default) opens + the thread with `sandbox: read-only` and `approvalPolicy: never`, which was + probed by asking codex to create a file — it tried, failed, said *"the + environment is read-only"*, and nothing appeared on disk. `converse` cannot + be kept exactly (codex has no way to turn its tools off), so the transcript + carries a line saying the agent can still read files, rather than letting the + name imply otherwise. + + **Approvals and questions arrive as cards in the feed**, answered inline — + a local session has no attention table, so this is where codex's gates live. + Four kinds are declined immediately instead, each with a line saying why: a + form-fill an inline card cannot type into, a "open this URL" request, a + permission-profile grant, and — deliberately — any question codex marks + **secret**, because an answer sent through the Companion is written to the + session transcript on disk, which is not where a secret belongs. + +- **Groundwork for driving codex locally: the transport to its app-server.** + A codex installed by the official installer script runs a shared background + app-server, and a session on it outlives this app and is visible in codex's + own TUI. The desktop can now open that channel, or spawn a private + per-session app-server when the shared one is unavailable — falling back + with a sentence saying which it got, because "shared with your TUI" and + "dies with this window" are different promises. Not yet wired to any + surface: the driver that speaks the protocol is the next wedge, so nothing + visible changes yet. (vision-parity L4b) + + **codex installed by the official script is now found even when it is not on + PATH.** The installer writes `~/.local/bin/codex` and puts its PATH line in + `.bashrc`, which an app launched from a Dock or Start-menu icon never reads + — so the officially-installed codex was *less* discoverable than a + distro-packaged one. The well-known install dirs are searched directly, with + `TERMIPOD_CODEX_BIN` as an override. + +- **You can now see what a sub-agent actually did.** The Sub-agents chip above + the composer lists the delegated agents; clicking one opens what it was asked + to do and everything it has done — its tool calls, its notes, and what it is + running right now. (vision-parity R5) + + For an engine that does not say which events belong to a sub-agent, the panel + still shows the request and says plainly that the rest cannot be separated — + which is a different sentence from "this sub-agent has not done anything + yet", and the panel picks the right one. + +- **The transcript now shows which model is about to answer — and lets you + change it where that is actually possible.** Two pills sit beside the + context ring, in the composer's own row, because these are questions asked + while typing rather than facts looked up in a details panel. An agent that + advertises its choices (the ACP engines) gets a menu; claude, which + advertises none, gets a text entry taking an alias or a full model name. + Switching a model on claude or codex **restarts the agent** — the hub spawns + a replacement on the same session, so the transcript continues — and the + pill says so and asks first, rather than doing it and letting you find out. + (vision-parity R6) + + **A pill that cannot change anything says so instead of disappearing.** + Mobile's picker hides itself whenever the agent advertises no options, which + on a claude session means no model indicator at all. Here the value still + shows, greyed, with the reason on hover: "cannot be changed while running" + and "unknown" are different facts and should not look the same. + +- **A running command's output now appears while it runs, and pictures render + as pictures.** Two halves of the same gap — the transcript could describe + what a tool did but never show it. A long build or test run used to sit as a + silent spinner until it exited; its output now streams into the tool row, + scroll-capped and pinned to the newest line, and stands down when the + finished result arrives carrying the same bytes. And an image a tool returns + — claude reading a PNG, or any bridge tool behind the hub relay — now paints + inline instead of printing a screen of base64 at you. Both dialects our + engines actually emit are read (claude's `source:{type:"base64"}` and + MCP/ACP's `mimeType`+`data`), and a `url` source is deliberately not painted: + it would make the renderer fetch a host the agent chose. (vision-parity R4, + consuming E3 + E4) + + **Large images stopped disappearing.** The hub replaces any payload field + over 64 KiB with a content-addressed reference on ingest, which is every real + screenshot — and the transcript skipped those references rather than + resolving them, so an image vanished at exactly the size where it mattered. + They are now fetched by hash. The MIME comes from the event, not the blob + record, because the hub stores externalized bytes as + `application/octet-stream` and no browser will paint that as an image. + - **`author_guide` — the agent looks up a format before it writes one.** `author_apply` refuses a malformed body rather than repairing it, which is only a fair trade if the rules are readable somewhere cheaper than a failed @@ -190,51 +224,106 @@ This complements: that writes `mcp_servers` in a form the splicer can't edit safely is left alone and reported, never guessed at. (vision-parity F4) -- **A running command's output now appears while it runs, and pictures render - as pictures.** Two halves of the same gap — the transcript could describe - what a tool did but never show it. A long build or test run used to sit as a - silent spinner until it exited; its output now streams into the tool row, - scroll-capped and pinned to the newest line, and stands down when the - finished result arrives carrying the same bytes. And an image a tool returns - — claude reading a PNG, or any bridge tool behind the hub relay — now paints - inline instead of printing a screen of base64 at you. Both dialects our - engines actually emit are read (claude's `source:{type:"base64"}` and - MCP/ACP's `mimeType`+`data`), and a `url` source is deliberately not painted: - it would make the renderer fetch a host the agent chose. (vision-parity R4, - consuming E3 + E4) +- **Read becomes a discovery workspace, not just a library.** A dedicated + Discovery surface searches six literature sources (arXiv, Crossref, OpenAlex, + PubMed, Semantic Scholar, CORE) plus **Google Scholar** through SerpApi, and + what you find can be kept: a query becomes a **monitor** that re-runs on a + cadence and shows only what is new since the last run, with its history + retained so "new" means something. Monitors extend past papers to RSS feeds + and **social subscriptions** — Bluesky authors, feeds and queries, Mastodon + authors and tags, YouTube channels, X authors and queries — so a research + watch covers where the work is announced as well as where it is published. + Provider API keys are held in the vault rather than in settings, and + Unpaywall enrichment fills in open-access links. (#575, #572, #543) + +- **Ratings and provider-aware citation details in the library.** A reference + can be scored 1–5 to prioritize a reading list (backed by the hub's new + `rating` column), and the citation inspector reports counts alongside which + provider supplied them — OpenAlex and Google Scholar disagree often enough + that an unattributed number is not a fact. Library columns rebalance and + scroll horizontally rather than crushing, and the inspector metadata is + compacted. (#572, #543) + +- **Rich Markdown and CSV previews in Inspect.** A `.md` file renders as + formatted prose instead of source, and a `.csv`/`.tsv` opens as a sortable + table with soft wrap. Deliberately read-only and deliberately separate from + Author's table model: Inspect shows the bytes as they are and never + manufactures editable document state from them, so opening a data file + cannot start an edit you did not ask for. (#552) + +- **PDF reading grew the controls a long document needs.** Hierarchical + outlines fold, the toolbar reflows instead of overflowing at narrow widths, + fit-width pins to the toolbar, and a text selection offers actions in place — + with the native context menu preserved rather than replaced. Annotation + actions are compacted and their swatches stay circular. (#562, #561, #543) + +- **A bundled terminal font, and a correct terminal on macOS.** Maple Mono NF + CN ships with the app (SIL Open Font License, see the bundled `LICENSE.txt`) + so a Nerd-Font prompt and CJK text render without asking the user to install + anything. macOS switches to the DOM renderer, where the WebGL path + mis-rendered, and the right-edge gutter and final glyph are no longer + clipped. (#557, #558) + +- **SFTP became a usable file browser.** Transfers queue in the background and + can be cancelled, column headers sort, the split is adjustable and centred on + open, panes scroll independently, and refresh is on the file menus. An SSH + connection is saved when you connect rather than only when you remember to + press Save, and saved hosts get a quick-connect. (#554, #553, #551, #561) + +- **Appearance controls.** Theme and density are settable in Settings, with a + light-mode Linux icon and a title-bar logo routed through the theme tokens so + it inverts with everything else. (#548, #546) + +- **Projects gained archiving and typed documents.** A project or workspace can + be archived behind a confirm, template groups collapse by default instead of + presenting everything at once, and a project document with a section schema + renders as its sections — each with an `empty` / `draft` / `ratified` status — + rather than as raw body text. (#558) - **Large images stopped disappearing.** The hub replaces any payload field - over 64 KiB with a content-addressed reference on ingest, which is every real - screenshot — and the transcript skipped those references rather than - resolving them, so an image vanished at exactly the size where it mattered. - They are now fetched by hash. The MIME comes from the event, not the blob - record, because the hub stores externalized bytes as - `application/octet-stream` and no browser will paint that as an image. +### Changed -- **Groundwork for driving codex locally: the transport to its app-server.** - A codex installed by the official installer script runs a shared background - app-server, and a session on it outlives this app and is visible in codex's - own TUI. The desktop can now open that channel, or spawn a private - per-session app-server when the shared one is unavailable — falling back - with a sentence saying which it got, because "shared with your TUI" and - "dies with this window" are different promises. Not yet wired to any - surface: the driver that speaks the protocol is the next wedge, so nothing - visible changes yet. (vision-parity L4b) +- **A monochrome product theme, and window chrome that belongs to the app.** + The workbench moved to a monochrome palette with a stronger pane tonal + hierarchy and soft glass chrome; pane boundaries were simplified and the + dividers and popovers that broke along the way were repaired. macOS and Linux + window chrome are integrated into the shell rather than sitting above it, and + a shared `PopoverMenu` replaces the one-off menus. The app icon is + re-cut — the midnight logo is now canonical in the brand docs. (#540, #562, + #561, #546) - **codex installed by the official script is now found even when it is not on - PATH.** The installer writes `~/.local/bin/codex` and puts its PATH line in - `.bashrc`, which an app launched from a Dock or Start-menu icon never reads - — so the officially-installed codex was *less* discoverable than a - distro-packaged one. The well-known install dirs are searched directly, with - `TERMIPOD_CODEX_BIN` as an override. +- **Workbench headers and pane grids align.** Pane toggles pin to their surface + headers, every pane-aware header uses the same alignment, the activity rail + and Inspect tree search controls are compacted, and Settings content centres + at wide widths. (#560, #556, #543) -### Changed - **The UI-sharing consent text now lists all eight gated tools.** It had said "four things" since the first Author lane and had never been updated for `author_render` or `desktop_open` — so the sentence understated what the toggle grants. Both dicts. ### Fixed + +- **A sub-agent's work used to be reported as the main agent's.** claude's + stream carries a `parent_tool_use_id` on every frame — null for the main + agent, the delegating call's id for a sub-agent's — and the translator threw + it away. So a delegated tool call appeared in the transcript as if the agent + you were talking to had run it, and the guard that keeps a sub-agent's token + usage out of the session's turn counts (added for kimi) never fired for + claude, quietly inflating them. Both translators now stamp it, along with the + sub-agent's type, and a real recording of a delegated run is pinned in the + parity corpus so the two implementations cannot drift apart on it. +- **Three of the four runtime switches could never have worked, and nothing + said so.** The registry recorded *how* a mode/model switch travels but not + *whether* a given field can travel at all, and a switch that rewrites a + launch flag can only work if the flag is in the spawn command to begin with. + Measured against the real binaries: claude's `--permission-mode` is a real + flag that no template ships, `codex app-server` takes no `--model`, and + `--approval-policy` is not a codex flag at all (0.147.0 answers *"unexpected + argument"*; it is `--ask-for-approval`). All three answered a 422 advising + the director to pick a template that exposes the flag — a template that + cannot exist. Families now declare `runtime_switch_fields` per field, the + hub checks it before routing, and the UI hides a control it knows would be + refused. Only claude's model switch was ever real; it still is. - **A local session's transcript was missing your own messages.** Every row in it is translated from an engine frame, and the engine never echoes the prompt back — so the Companion showed the agent's replies and none of the questions. @@ -252,6 +341,21 @@ This complements: standalone result was labelled just "Result". The full transcript surface was always correct; only the dock was wired backwards. Found while threading the streamed-output prop through the same call sites. +- **A local file edited outside the app kept showing its old contents.** Author + and Inspect held what they had read, so an external write — an agent's, a + shell's — was invisible until the tab was reopened. Files are re-read when + they change on disk. Vault details also fill the width available instead of + being cramped into a fixed column. (#555) +- **Author tab bookkeeping.** A saved tab can be closed directly, file actions + stay in sync with the tree, and the document tabs no longer drift out of + step with what is open. (#543, #540) +- **Inspect's compare view scrolls and resizes again**, and an imported + discovery result can be inspected without ending the discovery session it + came from. (#562, #543) +- **Attachment sync no longer loses portable coordinates.** Zotero and managed + attachments travel as `{source, key, file}` descriptors matching the hub's + new `attachments_json`, so a library opened on a second device resolves the + same files instead of chasing an absolute path from the first. (#546, #550) --- diff --git a/docs/changelog.md b/docs/changelog.md index 843a56f8..f61c5473 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,9 +1,9 @@ # Changelog > **Type:** reference -> **Status:** Current (2026-08-05) +> **Status:** Current (2026-08-17) > **Audience:** contributors, operators -> **Last verified vs code:** 2026.805.1022-alpha +> **Last verified vs code:** 2026.817.322-alpha **TL;DR.** Append-only record of what shipped in each tagged release. One section per version, newest first. Format follows @@ -39,7 +39,38 @@ binding). Seed entries prior to that are in --- -## Unreleased +## 2026.817.322-alpha — 2026-08-17 + +**The fleet learns to read a pane, and the hub stops mis-attributing what an +agent said.** host-runner can classify what is on an agent's screen — working, +blocked, idle — from vendored declarative manifests instead of one regex and a +90-second stall; a blocked pane raises attention and withdraws it again; and +`pane_explain` answers *why* it reached that verdict, rule by rule. Alongside +that, four engine-vocabulary corrections, each found by measuring a real +binary rather than reading a spec: claude's stream carries sub-agent +provenance we were discarding, codex's streaming command output was being +swallowed, codex rejects the attachment shapes we were sending it, and the +runtime mode/model switch resolved the engine from the wrong column — so it +had never worked for a steward at all. + +**All three lanes are cut at this version** — `mobile-v2026.817.322-alpha`, +`hub-v2026.817.322-alpha`, `host-v2026.817.322-alpha` — carrying 18 hub and +host-runner commits since `2026.805.1022-alpha`. The desktop workbench cuts +separately at `2026.817.322` +([`changelog-desktop.md`](changelog-desktop.md)), where the bulk of this +cycle's 32 commits landed. + +> ⚠ **Operators must redeploy the hub and host-runner binaries.** Both the +> `2026.730.1231` and `2026.805.1022` sections said the same thing about their +> own 51 and 17 commits. If neither redeploy has happened, this cut compounds +> both and nothing server-side in any of the three sections is live — including +> every pane-state feature above, which is host-runner code by construction. + +> **Mobile is a near-formality again.** Two changes reached `lib/` this cycle, +> both riding hub wedges: `pane_state` joins the transcript's verbose tier, and +> the replay de-duplication key now folds streamed text (below). Mobile shares +> one version with hub and host by design (`make bump` writes both files), so +> the lane is cut to keep the three in lockstep. ### Added @@ -171,6 +202,26 @@ binding). Seed entries prior to that are in because the argv the driver uses is threaded per turn, not spliced at spawn. Pane-state-manifests plan, N1. +- **A reference carries its attachments as portable coordinates, so a Read + library syncs across devices.** `reference_items` gains `attachments_json` + (migration 0075): a list of `{source, key, file, content_type, …}` + descriptors naming an attachment inside its backend — Zotero storage or the + managed store — rather than where one machine happens to keep it. Absolute + device paths are deliberately absent from the schema, because a path from + another laptop is not a fact the second device can use. The existing + `zotero_storage_json` column stays for clients that know only one Zotero + attachment; new clients read and write both. `reference_create` / + `reference_update` accept the array, with `file`, `source` and `key` + required. Bytes never move — the hub stores metadata, the backend keeps the + file. (#550) + +- **References can be rated 1–5.** `reference_items.rating` (migration 0076), + exposed on the REST payload and both reference MCP tools, so the director can + prioritize a reading list and an agent can read that priority back. `NULL` + means unrated, which is a different fact from "rated low" — the column is + nullable and constrained rather than defaulted to a number nobody chose. + (#572) + ### Changed - **A screenshot taken through the hub arrives as an image.** A remote @@ -249,6 +300,51 @@ binding). Seed entries prior to that are in model switch would have started respawning with no `--resume` — trading a loud 422 for a silent cold start mid-session. Both lookups moved together. +- **Three of the four runtime switches could never have worked, and the hub + had no way to say so.** The registry recorded only *how* a mode/model switch + travels (`runtime_mode_switch`, keyed on driving mode) — but that route + answers for two independent capabilities at once, and a switch that rewrites + a launch flag can only work if the flag is in the spawn command to begin + with. Measured against the real binaries: claude's `--permission-mode` is a + genuine flag that no shipped template carries, `codex app-server` (the argv + our M2 driver uses) accepts no `--model`, and `--approval-policy` is not a + codex flag at all — 0.147.0 answers *"unexpected argument … a similar + argument exists: `--approve-for-me`"*, the real one being + `-a, --ask-for-approval`. All three reached the respawn executor and came + back 422 advising the director to pick a template that exposes the flag: a + template that cannot exist. Families now declare `runtime_switch_fields` + per field (`{mode, model}`), the hub checks that mask **before** routing, + and `GET /agent-families` publishes it so a client can hide a control rather + than learn from the error. Only claude's model switch was ever real; it + still is, and flipping a bit later turns another one on with no client + change. (#573, vision-parity R6) + +- **The hub threw away claude's sub-agent provenance, so a delegated agent's + work was reported as the main agent's — and its tokens were counted against + the session.** claude's stream-json carries `parent_tool_use_id` on every + assistant and user frame (`null` for the main agent, the delegating call's + `tool_use_id` for a sub-agent's), plus `subagent_type` and + `task_description`, and four `system` subtypes narrating a delegated run. + None of it survived translation. The frame profile and the legacy stdio + translator now stamp `parent_tool_use_id` / `subagent` / `subagent_type` on + text, thinking, tool calls, tool results **and usage** — usage being the one + kind the existing sub-agent guard reads, so this is what makes that guard + (written for kimi, the only engine that had ever marked a sub-agent) finally + fire for claude and stop inflating turn counts. `subagent` is derived once + at the producer rather than re-derived per consumer, so both clients answer + one vocabulary. A real 15-frame recording of a delegated run is pinned in + the parity corpus — its first sidechain coverage — so the Go and TypeScript + interpreters cannot drift apart on it. (#574, vision-parity R5) + +- **A replayed codex command collapsed to its oldest output.** The mobile + transcript's de-duplication key for a `tool_call_update` was + `kind:id:status`, which is enough to separate ACP's discrete updates but not + a chain of streaming partials — and E3's partials deliberately carry no + status, so every partial for one command keyed identically and a replay kept + only the first. The card then showed the opening slice of a command that had + long since finished. The key now folds the streamed text in, the way the + text and thought cases already did. + - **A host-runner-raised attention row was attributed to the host, never to the agent that asked.** `POST /attention` honoured a body-supplied `actor_handle` only when the authenticated caller had no handle of its @@ -265,23 +361,6 @@ binding). Seed entries prior to that are in pane-state row against the real handler instead of a stub; neither client renders these two columns yet, which is why it went unnoticed. -### Security - -- **A session id from the engine could inject shell into the next - respawn.** `engine_session_id` is captured verbatim from the agent's own - `session.init` payload with no validation, and the hub splices it into - the spawn spec's `backend.cmd` — which tmux runs through a shell. It was - joined in unquoted, so an id like `x; rm -rf /` would have executed with - the agent's privileges at the next resume, respawn or teleport. Values - now pass a validation envelope (non-empty, no control characters, ≤512 - bytes) and are single-quoted when they contain anything outside a - conservative safe set; an id that quoting cannot rescue is refused and - the respawn cold-starts, which is what every other failure on that path - already does. Ordinary ids — UUIDs, hyphenated slugs — are byte-identical - to before, so no live spawn spec changed. - -### Fixed - - **The hub's two resume-dispatch switches had already diverged.** `handlers_sessions.go` (the resume + teleport path) and `respawn_with_spec_mutation.go` (the mode/model picker path) each @@ -341,6 +420,21 @@ binding). Seed entries prior to that are in backward-compatible command argument (an older hub sends nothing and the host falls back to its own environment). +### Security + +- **A session id from the engine could inject shell into the next + respawn.** `engine_session_id` is captured verbatim from the agent's own + `session.init` payload with no validation, and the hub splices it into + the spawn spec's `backend.cmd` — which tmux runs through a shell. It was + joined in unquoted, so an id like `x; rm -rf /` would have executed with + the agent's privileges at the next resume, respawn or teleport. Values + now pass a validation envelope (non-empty, no control characters, ≤512 + bytes) and are single-quoted when they contain anything outside a + conservative safe set; an id that quoting cannot rescue is refused and + the respawn cold-starts, which is what every other failure on that path + already does. Ordinary ids — UUIDs, hyphenated slugs — are byte-identical + to before, so no live spawn spec changed. + --- ## 2026.805.1022-alpha — 2026-08-05 diff --git a/hub/internal/buildinfo/buildinfo.go b/hub/internal/buildinfo/buildinfo.go index 7c18acd5..34d70ec9 100644 --- a/hub/internal/buildinfo/buildinfo.go +++ b/hub/internal/buildinfo/buildinfo.go @@ -13,7 +13,7 @@ import ( // mobile and hub use the same x.y.z-alpha numbering. Use // `make bump VERSION=...` from the repo root to update both files // atomically. -const Version = "2026.805.1022-alpha" +const Version = "2026.817.322-alpha" var ( Commit string diff --git a/pubspec.yaml b/pubspec.yaml index e5a391a0..ae61af72 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # Date-based CalVer YYYY.MMDD.HHMM (UTC build time); the +N build number is # "minutes since 2020-01-01 UTC" (monotonic int32 versionCode — CI recomputes it # from the tag). See docs/changelog.md header for the scheme. -version: 2026.805.1022-alpha+3468142 +version: 2026.817.322-alpha+3485002 environment: sdk: ^3.10.7 From 265424421fdc0fe744a200596b05c9c95dcd58c9 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 17 Aug 2026 03:42:42 +0000 Subject: [PATCH 2/2] docs(roadmap): reconcile with 2026.805.1022 + 2026.817.322; pane-state plan is in flight, not proposed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The roadmap was two releases behind: its Done-this-quarter tables stopped at 2026.730.1231 / 2026.730.1242, and its Now table had no row for either lane that dominated the 2026-08 cycle. - Status block re-stamped to the current cut. - Both Done tables gain their two missing releases, mobile/hub/host and desktop, described from the changelog sections rather than commit subjects. - Now gains two rows: the **hub + host-runner redeploy**, which three consecutive cuts have now deferred (51 + 17 + 18 server-side commits are not live), and the **vision-parity + pane-state** lanes with what actually remains in each — L3c, P5, S1/S2, all deferrable or unstarted. - `plans/pane-state-manifests.md` said "Proposed — for review" while P1–P4, N1 and Q1 had all shipped. Status corrected; `Last verified vs code` left alone, because re-stamping it would claim an anchor-by-anchor re-verification this commit did not do. Release commits do not touch the roadmap in this repo (neither 1f8fc773 nor e60c83fd did) — it is maintained by dedicated docs: commits, which is why this is separate from the version bump it accompanies. Co-Authored-By: Claude Opus 5 --- docs/plans/pane-state-manifests.md | 3 ++- docs/roadmap.md | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/plans/pane-state-manifests.md b/docs/plans/pane-state-manifests.md index 24af094e..486c631c 100644 --- a/docs/plans/pane-state-manifests.md +++ b/docs/plans/pane-state-manifests.md @@ -1,7 +1,8 @@ # Pane-state manifests — declarative screen detection for every engine > **Type:** plan -> **Status:** Proposed (2026-08-08) — for review. Derived from +> **Status:** In flight (2026-08-17) — **P1–P4 + N1 + Q1 shipped**, all +> cut in `2026.817.322-alpha`; **P5** and **S1/S2** remain. Derived from > [`discussions/herdr-runtime-borrows.md`](../discussions/herdr-runtime-borrows.md) > (B1 flagship + B2/B3/B4/B5 independents). The vendor-vs-fork call is > made: the director chose **vendor-plus-overlay** (2026-08-08); D-1 diff --git a/docs/roadmap.md b/docs/roadmap.md index bf1cb18d..c6cee222 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,9 +1,9 @@ # Roadmap > **Type:** vision -> **Status:** Current (2026-07-30) +> **Status:** Current (2026-08-17) > **Audience:** principal, contributors, reviewers -> **Last verified vs code:** mobile/hub/host `2026.730.1231-alpha` · desktop `2026.730.1242` +> **Last verified vs code:** mobile/hub/host `2026.817.322-alpha` · desktop `2026.817.322` **TL;DR.** The MVP target is unchanged: the research demo from `blueprint.md` §9 Phase 4 — a user writes a directive → steward @@ -143,6 +143,8 @@ working list — what's actually moving this week or next. | Item | Why | Where | |---|---|---| +| ⚠ **Redeploy the hub + host-runner binaries** | Three consecutive cuts — `2026.730.1231`, `2026.805.1022`, `2026.817.322` — carry 51 + 17 + 18 server-side commits that are **not live** until the binaries are redeployed. Every pane-state feature is host-runner code by construction; the streamed-output relay (E3), the bridge result passthrough (E4), the sub-agent provenance stamp (R5) and the `runtime_switch_fields` gate (R6) are hub code. This is the longest-outstanding item in the release record, and it compounds each cut | Director action. `changelog.md` §2026.817.322-alpha | +| **Desktop Companion vision-parity + pane-state detection** | The two lanes that dominated the 2026-08 cycle, both now past their bulk. Vision-parity **W1–W4 complete**: the Companion drives a claude or codex session locally with no hub, survives an app restart, and shows streamed output, inline media, sub-agent attribution and model/mode pills. Pane-state **P1–P4** give host-runner a declarative state authority for every engine without a structured M4 adapter, replacing a single regex and a 90-second stall | Remaining: vision-parity **L3c** (session catalog + loopback WebSocket, deferrable) and pane-state **P5** (hub-distributed manifest updates, deferrable) + **S1/S2** (settle semantics, not started). `plans/desktop-companion-vision-parity.md`, `plans/pane-state-manifests.md` | | **Embodied research program — environments + replay (ADR-058…061)** | J8 replay shipped end-to-end on the desktop (dataset library rail, episode player with channel plots + multi-camera video, URDF forward kinematics + 3D pose, Rerun companion + export, remote roots over SFTP) and the host-job surface (ADR-058 executor + export kind) landed. The **Environment entity** shipped E0 (`env_ref` on runs + episodes) and E2a (hub CRUD + `/environments/resolve`). All four ADRs 058–061 Accepted 2026-07-30 with a laned build order. | In flight: E2b/E2c resolution surfaces; device-verify of remote video + Rerun export on hardware. `plans/replay-datasets-episodes.md`, `plans/environments-and-embodiments.md`, `plans/desktop-workbench-jobs.md` | | **Agent ↔ UI interplay (ADR-059, ADR-062)** | The desktop is becoming agent-addressable: browser bridge W1–W3 shipped (MCP webtabs read → act behind per-spawn opt-in + audit → remote hub-relayed driving behind approval cards, ADR-059); ui-context D1–D2.1 shipped (`ui_get_focus` off-by-default, annotation overlay pointing, global annotate trigger). ADR-062 (UI as an agent-addressable entity) is Proposed. | In flight: ui-context wedges D3–D6. `plans/desktop-agent-browser-bridge.md`, `plans/desktop-ui-context-and-pointing.md` | | **Desktop ↔ mobile parity backlog** | The desktop reached feature parity on SSH (jump hosts, SOCKS5, `ssh_config` import), spawn env-sealing, and — with PR #485 (merged 2026-07-30) — steward spawning from the Navigator rail (it previously spawned only workers). | Ongoing. `plans/desktop-mobile-parity.md` | @@ -219,6 +221,8 @@ CalVer with per-component tags): | Version | What | |---|---| +| 2026.817.322-alpha | **Pane-state detection becomes a lane, and four engine vocabularies get corrected.** host-runner classifies what is on an agent's pane from vendored declarative manifests instead of one regex plus a 90-second stall — a pure evaluator over herdr's schema + 19 TOMLs (P1), wired to the poll tick (P2), raising and *retracting* attention on a blocked pane (P3, the first self-retracting host-runner row), and explaining its own verdict rule by rule via `pane_explain` (P4). Independents: native-resume recipes as shared YAML pinned across Go and TS (N1), generic pane-input hardening so multi-line input arrives as one block (Q1). Engine corrections, each measured against a real binary: claude's discarded sub-agent provenance (R5), codex's swallowed streaming command output (E3), the attachment shapes codex rejects (L4c), and the runtime mode/model switch resolving the engine from `agents.kind` rather than `backend_json.kind` — dead for every steward (#570) — plus the per-field `runtime_switch_fields` mask that makes three impossible switches say so instead of advising an impossible template (R6). Security: an engine-supplied `engine_session_id` could inject shell into the next respawn. Read gains `attachments_json` + `rating` columns. Mobile: `pane_state` in the verbose tier, replay dedupe folds streamed text | +| 2026.805.1022-alpha | **A server-side cut: environments, a datasets catalog, and MCP 2026-07-28 compatibility.** The Environment entity E2a (hub CRUD + `/environments/resolve`); the `datasets_*` MCP read/write tools that were Replay's catalog gap; ADR-063 MCP compatibility (`MCP-Protocol-Version` headers, `resultType`, cacheable lists, `server/discover`, `_meta` tolerance, dual-published annotations, `structuredContent`); decision records as a durable entity; kimi 0.31 wire events; corrected agent-event vocabulary for claude M2 and codex M2. Mobile unchanged apart from two strings — cut for lockstep | | 2026.730.1231-alpha | **Env profiles + sealed secrets + session teleport reach the phone.** Spawn-sheet env-profile picker + management screen; secret refs sealed client-side to the target host's key (ADR-056 — the hub stores only ciphertext; Go/Rust/Dart KAT-locked); paused sessions teleport to another host with the handoff bundle re-sealed (ADR-057, claude-code + kimi-code-ts). Hub side also carries the environments E0+E2a entity work (`env_ref` on runs/episodes, migration 0073 CRUD + `/environments/resolve`), the datasets/episodes entity (ADR-060, migrations 0068–0069), blob lifetime (ADR-061, 0071), host-command progress (0070), and the transcript-digest issue classes + streaming-markdown fixes. All three lanes cut together; first triple-lane tag | | 2026.727.206-alpha | **Fleet updates from mobile, with live progress.** `host.update` verbs fixed + admin update-progress rendering | | 2026.724.335-alpha | **Release lanes split per component** — `mobile-v*` / `hub-v*` / `host-v*` tag namespaces (desktop already separate on `electron-v*`) | @@ -285,6 +289,8 @@ CalVer with per-component tags): | Version | What | |---|---| +| 2026.817.322 | **The Companion becomes a place you can run an agent, and Read becomes a discovery workspace.** A claude (L3a) or codex (L4c) session runs on this machine with no hub in the path and survives quitting the app (L3b) — transcript restored from our own append-only log, memory from the vendor's native resume, because those two fail separately. The transcript around it gained streamed command output and inline media (R4), a sub-agent panel with real provenance (R5), and model/permission pills that appear only where the switch is real (R6). `author_guide` gives the agent the format rules before it writes (coworking C2+C3), and UI-context sharing reseeds claude and codex, not just kimi (F4). Read went from library to workspace: six literature sources plus Google Scholar via SerpApi, cadence monitors with history, social subscriptions, ratings, provider-attributed citation counts. Plus rich Markdown/CSV Inspect previews, a bundled Maple Mono NF CN terminal font with the macOS DOM renderer, a background SFTP transfer queue, project archiving and typed project documents, appearance controls, and a monochrome theme with integrated window chrome. **vision-parity W4 complete** | +| 2026.805.1022 | **The agent becomes a co-worker in the local tabs.** `author_read` / `author_apply` / `author_render` behind a per-(agent, target, session) consent lease rather than bearer scope (ADR-064); live-apply adapters so an external write reaches the mounted editor (diagram, canvas, excalidraw, tables, figures); ID-addressed diagram ops with all-or-nothing batches; `desktop_open` as the navigate class. Alongside it the Companion telemetry a session needs — context ring, engine-reported cost, turn footers, inline approval cards — plus the comparison wall with decision records, Inspect media previews, and the TypeScript frame-profile interpreter pinned to the hub's Go one | | 2026.730.1242 | **The largest desktop cut so far — five plans reached their shipping wedges at once.** J8 Replay end-to-end (dataset rail + episodes table, episode player with channel plots + multi-camera video, URDF forward kinematics + 3D pose, Rerun companion + export incl. remote-over-SSH); architecture graph goes hybrid-aware with honest KV-cache math, zoom/annotate/SVG-PNG export, and config-vs-config diff; **agent browser bridge W1–W3** (read → opt-in actions + audit → hub-relayed remote driving with approval cards, ADR-059); **split pane S1–S3**; **ui-context D1–D2** (`ui_get_focus` off-by-default + annotation-overlay pointing) and D2.1 (global annotate trigger); env profiles + sealed secrets + teleport on desktop; SSH jump-host/SOCKS5 parity with `ssh_config` import; unified assistant dock (kimi web + Companion tabs) | | 2026.727.206–938 | **The Inspect arc.** Project trees over local/SFTP/hub/forge roots; config-only model view with VRAM + FLOPS estimators; LeRobot policy/VLA configs; roots context menus; Windows kimi-web fix | | 2026.724.305/405 | **Projects task board W1** — master-detail kanban with rich cards (W2 inline edits + W3 agent-aware drag-and-drop and filters followed direct-to-main) |