@staix/agent-hub 0.12.38 → 0.12.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the previous repository, archived on 2026-09-30 when this repository's history was rewritten; the one exception is the open smoke-check issue, formerly #12, which moved here as #1. Numbers in newer entries refer to this repository.
4
4
 
5
+ ## 0.12.39
6
+
7
+ `ahub jcode` now opens jcode's own TUI on the hub-driven session (#464), and an ACP peer that is running a long tool call is no longer stopped as silent (#542).
8
+
9
+ - `ahub jcode` with no flag opens jcode's native TUI in your terminal, attached to the session the hub drives over ACP; ACP stays the hub's only control channel. If jcode has not saved that session yet, the launcher prints `jcode's TUI opens after its first hub turn; send it work, or Ctrl-C` and opens the TUI as soon as the session is saved; Ctrl-C ends only the wait. A console or conductor start opens it through the terminal provider, never without a terminal. `ahub jcode --mode headless`, or `peers.jcode.start_mode: headless`, keeps the previous behaviour. TUI input during a hub turn joins that turn (no single-writer lock), and jcode's own tool policy applies (#464, #557).
10
+ - An ACP tool call that was announced and has not completed or failed counts as running work: it keeps the turn alive up to a 30 min tool ceiling (never below `watchdog_ms`) before the inactivity cancel runs, with a log line naming the open tool (#542, #547).
11
+ - Tests: the MLX fixtures take a free loopback port instead of fixed ports that collided on hosted Linux (#548, #552).
12
+
5
13
  ## 0.12.38
6
14
 
7
15
  Antigravity (`agy`) joins as an optional peer started from your own terminal (#485), Kimi can review in a fresh session like Claude and Codex (#533), and a reply past the hop cap now says so instead of claiming no peer is attached (#542).
package/README.md CHANGED
@@ -5,7 +5,7 @@ Codex, Kimi Code, Pi and a hub-owned local worker (Qwen Code, Jcode and Antigrav
5
5
  optional) share a message bus and a task board per project and one memory on the
6
6
  machine, each attached through its own native interface.
7
7
 
8
- Status: 0.12.38, control protocol 16. Bun + TypeScript, one loopback daemon per
8
+ Status: 0.12.39, control protocol 16. Bun + TypeScript, one loopback daemon per
9
9
  project. Site: [agent-hub.staix.net](https://agent-hub.staix.net).
10
10
 
11
11
  ## What it does today
@@ -81,17 +81,21 @@ starts of Jcode. The entry's format is unchanged in 0.12.37; a daemon start that
81
81
  reports the entry missing or stale is fixed by the same command, which since 0.12.36
82
82
  writes the running hub's own plugin bundle (#502). Since 0.12.37 the hub accepts jcode
83
83
  0.98.0 or later, below 1.0.0, and no longer 0.94.0 (#529).
84
+ Since 0.12.39 `ahub jcode` opens Jcode's native TUI on the hub-owned ACP session by
85
+ default (#464); `--mode headless` or `ahub settings set peers.jcode.start_mode headless`
86
+ keeps headless startup.
84
87
  Cancellation needs a daemon advertising `task_cancel`; controlled downgrades
85
88
  refuse while cancelled rows exist. For local/Pi implementation, opt in to an
86
89
  available `checks.runner = "container"`; issue-backed owners also need authenticated
87
90
  spec access. PII work and designated reviewers retain their separate exemptions.
88
- See [release verification](docs/verification/2026-10-20-0.12.38.md) for the live legs
91
+ See [release verification](docs/verification/2026-10-20-0.12.39.md) for the live legs
89
92
  still open, including #418 AC8, #438 AC7, the native console settings leg (#439 AC5),
90
93
  real upgrades with reattach (#463) and with a managed terminal and cancelled tasks
91
94
  (#478), a live headless Claude review (#223), a Claude session over 32 MiB (#517), a
92
95
  live fresh Codex review (#519), a live split of a real memory home (#536), the live
93
- agy TUI/account leg (#485, hub task #251) and Kimi's interactive-TUI coexistence and
94
- usage checks (#533).
96
+ agy TUI/account leg (#485, hub task #251), Kimi's interactive-TUI coexistence and
97
+ usage checks (#533), Jcode's TUI with a personal account, a real provider and hub-tool
98
+ calls (#464) and a live Jcode tool call held past the inactivity watchdog (#542).
95
99
 
96
100
  Before migrating a memory home with `amem`, upgrade every hub using that home to
97
101
  0.12.30 or later, check `ahub memory profile-check`, then quiesce and close all memory
@@ -28,6 +28,7 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
28
28
  - A peer must set `busy` synchronously inside `deliver()`, otherwise the bus drains the next envelope into a running turn (Kimi answers `turn.agent_busy`). Outside a turn the adapter accepts an occupancy diagnostic (a `context-used` `usage_update` naming its current session) and nothing else: no token total, no chunk, nothing unnamed or for an offline peer (#285).
29
29
  - A watchdog-cancelled turn still reports later. Anything a turn does on completion must check it is still the current turn (`turn` generation in `acp.ts`, `activeTurns` in `codex-appserver.ts`). A rejected prompt other than a recognised quota refusal, or an abnormal prompt end, reports through the adapter's `onTurnFailure` (same shape in `acp.ts` and `pi.ts`), even when ACP streamed partial text; a stale cancelled turn never reports it; a rejected watchdog-cancelled ACP prompt during its journal-less wait is discarded with one log line, not requeued as on the earlier adapter.
30
30
  - Native owner teardown must handle a lost shutdown acknowledgement using verified process identity. Pending launches must be revocable, and active tools/streaming output must count as watchdog activity. A reloaded `watchdog_ms` reaches a peer only through `configureWatchdog` (`src/hub/peers.ts`, #439) and applies from its next busy turn, never to the running one.
31
+ - ACP tool ceiling (#542): a tool call announced (`tool_call`) and not yet `completed` or `failed` in the current turn keeps a silent busy turn alive past the watchdog, up to `AcpOptions.toolCeilingMs` (never below the watchdog) counted from the last protocol message. Re-arm with `touch(<remaining ceiling, at most the watchdog>)`, never a full extra watchdog, and clear the open calls at each `deliver()`. Past the ceiling, cancel as an ordinary watchdog; its log line names the open tools only on a turn that is not private (latch `isPrivateTurn`, fail closed when it throws), each title as one sanitized, quoted, cut line.
31
32
  - Keep Pi owner identity on the shared `processSignature`/`processLiveness` contract exported by the exact-pinned package's Node-safe `@staix/agent-mem/platform` entry, re-exported unchanged by `src/pi/process-signature.ts` for the startup claim, extension claim, TUI owner monitor, `recoveryReady` and verified teardown. Memory's `sync.lock` uses the same implementation. Preserve the Node-safe process-entry regression in `test/memory-parity.test.ts`, the existing Pi process behavior tests, the LC_ALL=C/TZ=UTC `ps` read and strict pid + start + command identity; never accept PID-only or a fuzzy command match.
32
33
  - Return the shared `toolResultFailed` verdict as `/tool`'s `failed`; map only `failed === true` to Pi's `isError`. Preserve text and the older bridge's absent-flag behavior. The one exception is a person's `!command` whose process exit was observed: its `failed` is `exitCode !== 0` and the reply carries `exitCode`, from `onProcessExit` through `packPiShellResult`, never parsed from the output (#253).
33
34
  - Pi's bridge executes `/tool` only for the verified owner (`verifiedOwner()`: the claimed pid with its `processSignature`; a headless owner is pinned from the spawned process once `get_state` proved its session) with the current `sessionId` and turn `generation`, and admits `/budget` only for that owner and generation, checked again after the awaited admission. Refuse anything else before execution. `tool_abort` aborts that pending call, or fences its id when it arrives before the call (64 ids a turn, then every further call of that turn). A person's `!command` (`purpose: "idle_user_bash"`) is taken only in TUI mode from an idle owner on its one-use reservation, and it alone skips `piPermit`: never widen that to a model's bash call (#253).
@@ -55,7 +56,8 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
55
56
  - Spawn with `peerChildEnv(<instance id>)`, not the kind name. An ACP `kind` comes only from the host declaration, never from the id suffix or agent text.
56
57
 
57
58
  - TUI launchers (Kimi, Jcode, agy) write the plugin bundle the hub reports in `status` into their MCP entry, and the CLI's own copy only when the hub reports none (#502). Only the Jcode start path checks the entry against the daemon's own `SERVER_JS` (`jcodeMcpReady`); Kimi TUI admission does not compare it.
58
- - Jcode (#529): accept the native version only through `jcodeVersionSupported` (from `JCODE_MIN_VERSION` up to, not including, `JCODE_BREAKING_VERSION`), in both the version probe and ACP `expectedAgent`; never pin the minor, and lower the breaking version when a release breaks the contract. Every owned jcode process (probe, serve, ACP) gets `JCODE_OWNED_ENV`, and the private socket name ends in `.sock`.
59
+ - Jcode (#529): accept the native version only through `jcodeVersionSupported` (from `JCODE_MIN_VERSION` up to, not including, `JCODE_BREAKING_VERSION`), in both the version probe and ACP `expectedAgent`; never pin the minor, and lower the breaking version when a release breaks the contract. Every owned jcode process (probe, serve, ACP and TUI) gets `JCODE_OWNED_ENV`, and the private socket name ends in `.sock`.
60
+ - Jcode TUI (#464): use only the current owned launch's top-level `--socket`/`--resume` tuple after native saved-session proof. Prove the save only through `savedSession` (no-follow, single-link regular file, size-bounded, unchanged across the read, its `id` and `messages` matching) and refuse a requested model, provider profile or session that differs from the running launch. Hand the tuple only to a person's launcher through `native_tui_launch` (admission: `daemon.md`); previews (`--dry-run`, or `--print-command` from anyone but a person) start nothing and never resolve it. Never synthesize a save or prompt to induce one. Ctrl-C during the save wait and frontend close retain ACP; preserve session/load identity refusal and headless owned recovery metadata.
59
61
  - For fresh Kimi reviewers and task-bound MCP (#533), retain the common per-child task/head/review-revision/generation verdict fences, privacy screening and cleanup-before-settlement. Expose only a host in-process two-tool MCP capability for `hub_review` and `hub_task_show`; never give the native child the full hub control token. Supply source evidence only through the screened review brief/read result and host facts; exclude native Read/file/shell tools. Admit covered Kimi 2.2.0 only when current runtime preparation passes; otherwise keep it unavailable with the refusal reason. Keep actual interactive-TUI/usage ceilings separate from completed live/fake receipts; do not reuse historical Seatbelt/dummy-provider probes as revised-contract passes.
60
62
  - Start fresh Kimi 2.2.0 without Seatbelt, with private `KIMI_CODE_HOME`, managed-provider-only configuration, clean temporary cwd, explicit empty skills directory and exactly `mcp__agent-hub__hub_review` plus `mcp__agent-hub__hub_task_show` in its agent tools. Use directory symlinks for the person's native `credentials/` and `oauth/`, and link `device_id` to the native identity; do not copy token files, select static-API-key mode or set `KIMI_DISABLE_OAUTH_LOCK`. Let native OAuth share refresh/atomic rename/`proper-lockfile` coordination and the existing Kimi account budget. Revoke verdict authority before shutdown; allow bounded TERM grace while the OAuth lock is held, then use verified owned-group teardown. Report a forced-kill/re-login requirement without attempting host auth repair.
61
63
  - Keep the default reviewer kind order Claude, Codex, Kimi (selection rules: `tasks.md`).
@@ -8,7 +8,7 @@ Scope: the CooperBench runners, their fixtures, teardown and recovery. Read befo
8
8
  - A latched active-window terminal failure carries the #175 bounded diagnosis: the terminal class comes only from the pinned Qwen build's exact loop-protection message contract (anything else stays `unknown`), the active-window tool trace is count-only and phase/generation-fenced like the latch (setup probes and teardown never count), tool categories come from the protocol kinds and the #138 canonical binding (`canonicalMcpToolName` in `src/adapters/acp.ts`), and the native guard's threshold, predicate and no-op signals are declared unavailable, never derived from event totals. The diagnosis explains the latched failure and never changes it: no retry, no continuation, no guard relaxation.
9
9
  - Classify Pi's ceiling only from the adapter's validated producer signal bound to the latched peer/active generation. Keep shared budgets and Qwen's guard distinct; diagnostics never change the frozen end cause.
10
10
  - Read terminal coverage only through bounded `terminal_coverage` metadata reads, rebinding the audit, ledger and manifest to the seal index. Require complete sealed state, all audit checks, plan/cell/count reconciliation and zero recorded live owners. Report recorded verification separately from fresh audit; missing or inconsistent evidence is unavailable.
11
- - Publish Pi benchmark descriptors from `benchPiToolProblem`; keep production schemas unchanged and omit solo peer messaging. Attest every bootstrap tool publication against declared native/schema/source pins before scored generation. Fail/defer missing, mixed, malformed, truncated or changed publications; retain only bounded names/counts/hashes. Declare a new study condition before any changed collection; never rewrite historical artifacts or select retries by score. The driver's Qwen profile and Pi tool sandbox are `profile()` from `src/local/sandbox.ts`: a change to that profile (#270's refusal of the `.git` name is one) is such a changed condition.
11
+ - Publish Pi benchmark descriptors from `benchPiToolProblem`; keep production schemas unchanged and omit solo peer messaging. Attest every bootstrap tool publication against declared native/schema/source pins before scored generation. Fail/defer missing, mixed, malformed, truncated or changed publications; retain only bounded names/counts/hashes. Declare a new study condition before any changed collection; never rewrite historical artifacts or select retries by score. The driver's Qwen profile and Pi tool sandbox are `profile()` from `src/local/sandbox.ts`: a change to that profile (#270's refusal of the `.git` name is one) is such a changed condition. So is a change to when its Qwen `AcpPeer` cancels a turn: it passes no `toolCeilingMs`, so a silent open tool call runs to the default ACP tool ceiling (#542), not `watchdogMs`.
12
12
  - The benchmark runner's Claude trust entry: a write still `pending` in the runner's own process never landed, so it is `not_written` on every path (contained or not, its temp file removed or left), and neither the runner nor the recovery touches an entry then. Only a dead runner's `pending` leaves that question to the recovery, which takes back only an entry exactly as the runner would have written it, and none while the runner's temp file is still there (the rename never happened). `changed_concurrently` is the user's entry and is never taken back. The runner's settlement is `settleTrust` in `scripts/benchmarks/teardown.ts`, tested as a table: change it there, never inline.
13
13
 
14
14
  - Treat an earlier journal-less ACP attempt as possibly affected by cancellation overlap (#304) when its recorded peer `state` goes idle without an answer near `watchdogMs` after the last recorded activity, followed by rapid `failure` entries and `qwen_log` prompt-failed lines, then `active_end` as `peer-failure` with that reason. The driver's tap does not record `undeliverable`, main logged no watchdog cancel, and Qwen's busy-refusal wording is still unmeasured: these are candidate signs, not proof, and absence does not clear an attempt. Keep historical results unchanged and the native measurement owed in `docs/smoke.md`.
@@ -12,7 +12,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
12
12
  - Settings (#269): the dashboard's `setting`, `setting_undo`, `setting_preview` and `setting_reload` actions and the console-role `settings_get|set|undo|preview|reload` requests go through `settingsSet`, `settingsUndo`, `settingsPreview` (`settingsUndoPreview` for `undo: true`) and `reloadConfig` (`src/hub/daemon.ts`) and the registry in `src/hub/settings.ts`. A new editable setting is a registry entry, never a new write path; a key that chooses what the hub runs or where secrets or task text go joins `NEVER_EDITABLE` instead. Writes go only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml` through `store()`: the loader's own validation first, temp file and rename, never a file git tracks. Authority comes from the dashboard session (`DashboardSession`, `src/hub/ui.ts`) or the console role, never from the request body; `ui_action` from the manager is an ordinary session. Never return a setting's value in an event or notice except through `recordSetting`.
13
13
  - Live reload (#439): `reloadConfig` is the only publication path. Settings writes and undo, `ahub settings reload`, the dashboard reload and the debounced watcher of the four `CONFIG_SOURCE_FILES` (`src/hub/config-reload.ts`) all reach it; `prepareConfiguration` validates the complete candidate before anything is published, and recovery or shutdown holds it. A write or undo passes its own key as `scope` and publishes only that path, and `acknowledgeConfiguration` records only the accepted paths, so other edits stay pending. Every changed key, inside the registry or not, is decided by `authorizeConfigLeaf` (`src/hub/config-apply.ts`): a refused key keeps its accepted value and is reported by name while the others apply, `roster.*` applies only through its own scoped roster write, and a change of the settled planner or conductor holders (`roleHolders` over `resolveSettledRoles`) is guarded even when a role list shrinks. Outside a named write, only the console's `settings_reload` (authority `terminal`) adopts guarded file edits, by sending back the `confirmation` token bound to the candidate `fingerprint` and `configRevision`; changed bytes or accepted state refuse it, and a watcher, dashboard or settings session never confirms. An undo that names `key` and `undoDigest` writes only while `pendingUndoIdentity` still matches both. A `restart` key (`configApply`) keeps its running value and is listed in `pendingRestart`; reload notices and events carry key names and reasons, never values. The console Settings panel arms a write only from a preview reply that matches its open edit's token, key and value, and pasted text never confirms.
14
14
  - Retained configuration (#544): a key the reload refused, or a file edit outside a scoped write that still differs from the accepted value, is listed in `retainedConfig` (key and sanitized reason only, never a value) in status, `status.json`, the settings view and the console snapshot until a reload accepts or reverts it (`retainedConfigAfterReload`). A config leaf that selects a peer's native launch belongs in that definition's `cli.launchConfigKeys`: the `start` and `peer_start` replies then carry `retainedLaunchConfig` (the running values), the launcher starts with them and prints `retainedLaunchWarning` before its start request.
15
- - Start modes (#269): every start the hub makes itself goes through `hubStart` (`src/hub/daemon.ts`): the dashboard's `start_peer`, the console-role `peer_start`, the conductor's `start` hook and `pi.auto_start`. It takes the mode from `startMode(peer)` and nothing else; never add a caller-chosen mode, flag or command to it, and never fall back to headless when a terminal cannot be opened (refuse with the manual command). The words a terminal runs are assembled there from `process.execPath`, the CLI entry, the project root and `startWords(peer)`. The only continuity exception is a validated Pi bookmark on `pi.auto_start`: append its recorded session file (or its session id when no file was recorded), backend and model; never pass both session selectors or accept caller-written replacements. Recovery and `startPeer` callers that pass a recorded mode keep it. A launcher with no `--mode` takes the daemon's accepted start mode from `status.json` `startModes` (`readControl`) before the file's value. The real terminal providers are wired only in `src/hub/lifecycle.ts` (`DaemonOptions.terminal`): a daemon built without them, as in every test, opens nothing; a test injects a recording provider and never the real one.
15
+ - Start modes (#269): every start the hub makes itself goes through `hubStart` (`src/hub/daemon.ts`): the dashboard's `start_peer`, the console-role `peer_start`, the conductor's `start` hook and `pi.auto_start`. It takes the mode from `startMode(peer)` and nothing else; never add a caller-chosen mode, flag or command to it, and never fall back to headless when a terminal cannot be opened (refuse with the manual command). The words a terminal runs are assembled there from `process.execPath`, the CLI entry, the project root and `startWords(peer)`. The only continuity exception is a validated Pi bookmark on `pi.auto_start`: append its recorded session file (or its session id when no file was recorded), backend and model; never pass both session selectors or accept caller-written replacements. Recovery and `startPeer` callers that pass a recorded mode keep it. A launcher with no `--mode` takes the daemon's accepted start mode from `status.json` `startModes` (`readControl`) before the file's value. A TUI start of an `owned-acp` peer (Jcode) opens its terminal even while the ACP owner is online: that terminal is a frontend on the owner, so never answer `already` for it. The real terminal providers are wired only in `src/hub/lifecycle.ts` (`DaemonOptions.terminal`): a daemon built without them, as in every test, opens nothing; a test injects a recording provider and never the real one.
16
16
  - `ahub setup` reads Claude Code's state from `--json` listings and takes one step at a time, re-reading after each; never match paths or names by substring.
17
17
  - A copy of the git index keeps the original's mtime (`snapshot()`): git's racy-entry check compares entries with the index file's time in whole seconds, and a fresh copy makes a same-size edit look clean.
18
18
  - State files that clients read (`status.json`, `control-token`) are written after the port is bound, and `status.json` via temp file + rename.
@@ -54,6 +54,7 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
54
54
 
55
55
  - A Kimi one-launch TUI admission ends on its owning detach, roster removal, matched launcher cleanup or the unclaimed 30 s deadline. An old socket or launcher never clears a newer admission. Pin --mode tui in the hub-opened terminal argv.
56
56
  - `native_hook_launch` (#543) is console-only and needs a definition with `hooks.factsHook`; one launch per peer. Refuse `begin` during recovery or shutdown, off roster or while the peer is attached. `end` needs the matching `launchId` and is the only request besides `status` and `kill` a stopping hub admits; closing the launcher's socket ends its launch. Accept `native_facts` only from the tools role for the current `instanceId` and `launchId`, the latched native session and socket generation, and workspace paths whose `realPath` is the project. Close a `ws-pull` peer's hello without `channel: false` with 4403.
57
+ - `native_tui_launch` (#464) is console-only and resolves only a running owned `JcodePeer` under its own kind id (`tuiLaunch`). Refuse it while stopping, during recovery, off roster, during that peer's roster change or requested stop, or when `jcodeMcpReady` fails against the daemon's `SERVER_JS`; a `binding` must name the current `launchId` and `sessionId`. Failures answer fixed errors with details withheld, and the launch tuple (socket, env) goes only into that console reply, never into `status`, `status.json` or a log. A launcher sends it only when the hub's `features` list `native_tui_launch`.
57
58
 
58
59
  - When preserving a recovery source, copy only the exact-pinned `@staix/agent-mem` package through `preserveRuntimeDependencies` in `src/cli/recovery-package.ts`; include its identity and bytes in `packageDigest`. Never copy the whole node_modules tree or link a preserved source to a mutable installation. Verify preserved sources with `ownDependencies: true` so a missing or symlinked memory copy cannot resolve an ancestor installation. Verify staged releases with `dependencyRoot` bound to their retained install directory; never resolve beyond it. Keep no-dependency release digests compatible.
59
60
 
@@ -8,7 +8,7 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
8
8
  - Bind agent identity markers explicitly in native/CLI fixture children. The full gate starts the test runner without inherited agent markers (including `AGENTHUB_DELIVERY_ONLY`) and without an `ahub claude` session's hub variables (`AGENTHUB_PROJECT_DIR`, `AGENTHUB_STATE_DIR`, `AGENTHUB_INSTANCE_ID`, `AGENTHUB_LAUNCH_ID`); a new variable a native session exports and a fixture child reads joins that `env -u` list in `scripts/check.sh`. Never weaken production marker detection to make them pass.
9
9
  - `bunfig.toml` preloads `test/hub-home.ts`, which first clears inherited `AMEM_HOME` and gives every test run (the gate and `bun test <file>` alike) a temp `AGENTHUB_HOME` unless one is set; scoped tests may set their own `AMEM_HOME` after preload. Hub memory (#258) is on with any project config, and no test may write the person's `~/.agenthub`. Do not remove it; a test that needs its own home sets and restores the variable.
10
10
  - `test/registry-guard.test.ts` statically scans `src/` against the committed `test/registry-guard.baseline.json` (#451); a test never writes, regenerates or repairs that baseline, and a failing guard is fixed in the source by moving the policy into a definition (rule: `docs/agent-notes/adapters.md`); the baseline stays `[]`. Scanner behavior is pinned on inline sources in `test/registry-guard-fixtures.test.ts`, never on edits to real files.
11
- - A free-port probe binds `hostname: "127.0.0.1"`, matching the loopback server that takes the port (#232).
11
+ - A fixture that needs a port takes it from a free-port probe, never a fixed number, which collides with whatever holds it on a shared runner (#548). The probe binds `127.0.0.1`, matching the loopback server that takes the port (#232).
12
12
  - `ahub upgrade` without `--to` asks the registry (`npm view`) and hands a target newer than the CLI to `bun x` (#272): a test that reaches either puts a fake `npm` first on `PATH` and a recording stand-in for `process.execPath`, as `test/upgrade-cli.test.ts` does. The interactive screens are tested through a scripted `ScreenIO`; a CLI test that needs them marks `process.stdin` and `process.stdout` as terminals in a wrapper script.
13
13
  - Cross-process fixtures wait for the consuming client to process its handshake (the channel connected diagnostic), not merely the daemon's attached/idle state. Deadline-routing fixtures hold and release responses at the fallback decision; do not place a fixed sleep between deadline fractions and assume scheduler headroom (#287). Receipt-order fixtures use the normal watchdog for ordinary I/O, trigger expiry after observed native acceptance, and await a consumed transport barrier before asserting on late results.
14
14
  - A bounded polling wait must assert its condition after the last attempt, or document why giving up is intentional. Append-only event histories wait for a correlated closing event, not removal of the opening event. Before changing a slow wait, identify the unreached state and repair its cause (#296).
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.12.37 and control protocol 16. Live verification
3
+ This guide describes ahub 0.12.39 and control protocol 16. Live verification
4
4
  results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
5
5
 
6
6
  ## Agent-operated live verification
@@ -405,7 +405,7 @@ Launch that peer with `ahub claude` for channel pushes, or select `codex` in
405
405
  observes `hub_status`, moves stalled work and obtains review before reporting
406
406
  results and open decisions. It does not implement the tasks it handed out.
407
407
  `hub_peer_start` starts a peer in the start mode a person set for it (see
408
- "Start modes"): local and headless Kimi, Pi or Codex start at once; for a
408
+ "Start modes"): local and headless Kimi, Pi, Codex or Jcode start at once; for a
409
409
  TUI peer the hub opens a terminal running the fixed `ahub <peer>` command,
410
410
  after validating it through the shared launcher planner, and when it cannot
411
411
  open one the call fails with that command for the person to run. The
@@ -1477,7 +1477,7 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
1477
1477
  | --- | --- | --- | --- |
1478
1478
  | Permissions | `permission.<peer>` (the mode now) | the running hub only | at once |
1479
1479
  | Permissions | `permission_modes.<peer>` (the default mode) | `config.local.json` | next peer start |
1480
- | Start | `peers.pi.start_mode`, `peers.codex.start_mode`, `peers.kimi.start_mode` (`tui` or `headless`) | `config.local.json` | that peer's next start |
1480
+ | Start | `peers.pi.start_mode`, `peers.codex.start_mode`, `peers.kimi.start_mode`, `peers.jcode.start_mode` (`tui` or `headless`) | `config.local.json` | that peer's next start |
1481
1481
  | Start | `pi.auto_start` | `config.local.json` | next startup/recovery auto-start decision; never starts a peer merely on reload |
1482
1482
  | Routing | `routing.stay_switch`, `routing.classes.<class>.peers`, `.escalate_to`, `.route`, `.pi_backend` | `routing.local.toml` | at once |
1483
1483
  | Switches | `research.enabled`, `approvals.notify`, `snapshots.enabled` | `config.local.json` | live, without disturbing an active turn |
@@ -1558,6 +1558,7 @@ A peer that has a TUI the hub attaches to starts in it by default (issue #269).
1558
1558
  | codex | `tui` (`ahub codex`) | `peers.codex.start_mode: headless`: a hub-made start runs the app-server only |
1559
1559
  | claude | `tui` (`ahub claude`) | none |
1560
1560
  | kimi | `tui`, pull-only (`ahub kimi`) | `ahub kimi --mode headless`, or `peers.kimi.start_mode: headless` |
1561
+ | jcode (optional) | `tui` on the hub-owned ACP session (`ahub jcode`) | `ahub jcode --mode headless`, or `peers.jcode.start_mode: headless` |
1561
1562
  | local | headless (hub-native) | not applicable |
1562
1563
 
1563
1564
  - **You type the command:** `ahub pi` opens Pi's TUI in that terminal.
@@ -1755,6 +1756,12 @@ the system marks it `needs_review`. Establish evidence before choosing
1755
1756
  disconnects, cancellation, and partial effects are uncertain; do not replay
1756
1757
  them automatically.
1757
1758
 
1759
+ For an ACP peer (headless Kimi, Qwen, Jcode) a tool call that was announced and
1760
+ has not completed or failed counts as running work, not silence: the inactivity
1761
+ watchdog (`watchdog_ms`) keeps the turn alive up to a 30 min tool ceiling (never
1762
+ below `watchdog_ms`) since the peer's last output, then cancels the turn as
1763
+ before and logs which open tool call was silent (#542).
1764
+
1758
1765
  After the inactivity watchdog cancels a Codex turn, a late acceptance, answer or
1759
1766
  completion from that turn is dropped and never reaches a later audience; its
1760
1767
  delivery is held as `needs_review`. When the cancelled turn has no verified
@@ -1982,21 +1989,21 @@ section is what they run.
1982
1989
 
1983
1990
  Upgrade running projects with the target release's own coordinator. It accepts
1984
1991
  a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
1985
- 11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.12.37), and only
1992
+ 11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.12.39), and only
1986
1993
  a target on its own protocol, so the target's coordinator fits every supported
1987
1994
  source and carries every recovery fix released up to it. Protocol 8 and older
1988
1995
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
1989
1996
  project directory, without replacing the global CLI first:
1990
1997
 
1991
1998
  ```bash
1992
- bunx --package @staix/agent-hub@0.12.37 ahub upgrade --to 0.12.37 --dry-run
1993
- bunx --package @staix/agent-hub@0.12.37 ahub upgrade --to 0.12.37 --yes
1999
+ bunx --package @staix/agent-hub@0.12.39 ahub upgrade --to 0.12.39 --dry-run
2000
+ bunx --package @staix/agent-hub@0.12.39 ahub upgrade --to 0.12.39 --yes
1994
2001
  ```
1995
2002
 
1996
2003
  | Running now | Coordinator to use |
1997
2004
  | --- | --- |
1998
2005
  | 0.6.x (protocol 9) | the target's, through `bunx` as above |
1999
- | 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.12.37 (protocol 16) | the target's, through `bunx` as above |
2006
+ | 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.12.39 (protocol 16) | the target's, through `bunx` as above |
2000
2007
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
2001
2008
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
2002
2009
 
@@ -2028,14 +2035,14 @@ projects first:
2028
2035
 
2029
2036
  ```bash
2030
2037
  ahub restart --dry-run
2031
- ahub upgrade --to 0.12.37 --dry-run
2038
+ ahub upgrade --to 0.12.39 --dry-run
2032
2039
  ```
2033
2040
 
2034
2041
  Apply only after reviewing the plan:
2035
2042
 
2036
2043
  ```bash
2037
2044
  ahub restart --yes
2038
- ahub upgrade --to 0.12.37 --yes
2045
+ ahub upgrade --to 0.12.39 --yes
2039
2046
  ahub recovery status [<operation-id>]
2040
2047
  ahub recovery resume [<operation-id>]
2041
2048
  ahub recovery abort [<operation-id>]
@@ -3095,12 +3102,18 @@ Qwen controlled recovery and automatic crash resume load the recorded conversati
3095
3102
 
3096
3103
  ## Optional Jcode peer
3097
3104
 
3098
- Use ahub roster add jcode, then ahub jcode --mode headless [--model <alias>] [--provider-profile <name>]. The launcher reads roster membership from the hub's status reply; a hub older than 0.12.35 does not send it, so the launcher stops with an upgrade-or-restart remedy and starts nothing (#484). The person launcher configures only agent-hub-jcode in .jcode/mcp.json; run ahub init afterwards to ignore the untracked machine-path file. It writes the plugin bundle the running hub reports in `status`, so a CLI from another install no longer leaves a stale entry; `ahub kimi` does the same for Kimi's TUI entry (#502). Conductor/dashboard starts read that entry without editing it. Preview starts nothing, and ahub stop jcode stops only the owned private daemon/ACP groups. jcode_cmd, jcode_home and jcode_provider_profile are machine-local and never editable from settings.
3105
+ Use `ahub roster add jcode`, then `ahub jcode [--model <alias>] [--provider-profile <name>]`. It opens the native TUI attached to the hub-owned ACP session by default. Use `--mode headless` or `ahub settings set peers.jcode.start_mode headless` to keep headless startup. The launcher reads roster membership from the hub's status reply; a hub older than 0.12.35 does not send it, so the launcher stops with an upgrade-or-restart remedy and starts nothing (#484). The person launcher configures only agent-hub-jcode in .jcode/mcp.json; run ahub init afterwards to ignore the untracked machine-path file. It writes the plugin bundle the running hub reports in `status`, so a CLI from another install no longer leaves a stale entry; `ahub kimi` does the same for Kimi's TUI entry (#502). Conductor/dashboard starts read that entry without editing it. Preview starts nothing, and ahub stop jcode stops only the owned private daemon/ACP groups. jcode_cmd, jcode_home and jcode_provider_profile are machine-local and never editable from settings.
3106
+
3107
+ The native TUI opens only after Jcode has saved the selected session. On 0.98.1, `session/new` does not save it; the first real hub turn does. The launcher prints `jcode's TUI opens after its first hub turn; send it work, or Ctrl-C` once and waits for the same owned launch's saved session. Send Jcode work from the hub, or press Ctrl-C to end only the wait. No artificial prompt is sent. A console/conductor start uses a terminal provider to run the same launcher. Without a provider, run its displayed command in your own terminal. Options with no terminal need that manual path.
3108
+
3109
+ `ahub jcode --dry-run` stays pure and redacted. A person's `--print-command` can show the actual top-level native argv and closed native environment for an already-saved running session; it starts nothing. The private socket/session tuple is not returned to hub tools or status. Native saved-session evidence is read-only and capped at 32 MiB; unsafe, mismatched or larger evidence refuses the TUI launch.
3110
+
3111
+ TUI input may interleave with an ACP turn; there is no single-writer guard. Closing the TUI leaves the owned ACP session running, and native policy remains unmanaged. The isolated 0.98.1 transport receipt is [recorded separately](verification/2026-10-20-jcode-tui-0.98.1.md); it is not personal account or real provider verification.
3099
3112
 
3100
3113
  After upgrading to the recipient-retry release, a Jcode project MCP entry written before `AGENTHUB_DELIVERY_ONLY` was added is intentionally stale. Once per project, the person runs `ahub jcode --mode headless` before using conductor/dashboard starts. The launcher rewrites only the owned entry with the delivery-only marker, preserving foreign entries and refusing collisions; recovery relaunches already use this CLI path. Readiness checks never rewrite the file, and an explicit false marker or any other drift remains refused. Until that rewrite, daemon starts keep their actionable stale-entry refusal rather than advertising a retry tool Jcode cannot use.
3101
3114
 
3102
3115
 
3103
- Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown. Supported versions run from 0.98.0 up to, not including, 1.0.0 (#529). Every owned jcode process (version probe, daemon, ACP) runs with `JCODE_NO_TELEMETRY=1`. Native session/load (verified on 0.94.0 and 0.98.1) cannot prove identity, so attached Jcode blocks controlled recovery before effects and crash recovery asks for deliberate native reopening. A requested load without an echoed identity refuses; no fresh session is presented as restored. Provider/tool/native teardown receipts remain unverified in the pinned contract report.
3116
+ Native policy is unmanaged and has NO hub sandbox; no permission_modes.jcode or runtime mode mapping is invented. Quota and consumption remain unknown. Supported versions run from 0.98.0 up to, not including, 1.0.0 (#529). Every owned jcode process (version probe, daemon, ACP and TUI) runs with `JCODE_NO_TELEMETRY=1`. Native session/load (verified on 0.94.0 and 0.98.1) cannot prove identity, so attached Jcode blocks controlled recovery before effects and crash recovery asks for deliberate native reopening. A requested load without an echoed identity refuses; no fresh session is presented as restored. Provider/tool/native teardown receipts remain unverified in the pinned contract report.
3104
3117
 
3105
3118
  ## Optional Antigravity (agy) peer
3106
3119
 
@@ -17,7 +17,7 @@ ahub setup # installs the Claude Code channel plu
17
17
  Or use the matching GitHub release:
18
18
 
19
19
  ```bash
20
- bun add -g github:STAIxBWLB/agent-hub#v0.12.38
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.12.39
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,9 +30,15 @@ and upgrade or crash recovery, see the [operations guide](operations.md). To
30
30
  move running projects to a newer release later, run `ahub upgrade` in a
31
31
  terminal: it reviews the plan with you, applies it and follows it.
32
32
 
33
- ## Upgrading to 0.12.38
33
+ ## Upgrading to 0.12.39
34
34
 
35
- Restart Claude sessions after upgrading: each keeps the plugin server it started with, and the 0.12.38 plugin reports a reply stopped by the hop cap as not delivered instead of as sent (#546).
35
+ Restart Claude sessions after upgrading: each keeps the plugin server it started with until restarted, so a restart loads the 0.12.39 plugin.
36
+
37
+ `ahub jcode` now opens Jcode's native TUI on the hub-owned ACP session by default (#464). The TUI opens after Jcode's first hub turn saves the session; until then the launcher prints one waiting line, and Ctrl-C ends only the wait. Hub-made starts (the dashboard's Start control, the conductor's `hub_peer_start`) follow the start mode too, so they open a terminal for Jcode through a terminal provider. To keep the old headless default, run `ahub settings set peers.jcode.start_mode headless`, or pass `--mode headless`. See [Jcode operations](operations.md#optional-jcode-peer).
38
+
39
+ Upgrading from 0.12.37 or earlier, the 0.12.38 notes below also apply.
40
+
41
+ The 0.12.38 plugin reports a reply stopped by the hop cap as not delivered instead of as sent (#546); the restart above loads it.
36
42
 
37
43
  Antigravity (`agy`) can join as an optional pull-only implementer (#485). It is off roster by default: install the Antigravity CLI 1.4.2, run `ahub roster add agy`, then run `ahub agy` in a terminal. Its native permissions are unmanaged and it has no hub sandbox. See [Antigravity operations](operations.md#optional-antigravity-agy-peer).
38
44
 
package/docs/security.md CHANGED
@@ -171,7 +171,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
171
171
  - A settings session is as strong as the console for these settings and no stronger elsewhere. A mode change is the console's own request with the same refusals; tool approvals stay deny-only for Pi and local; there is still no shell, file, task-detail or launch-command API. `ahub ui --settings` and `ahub settings` pass the same identity gate as `ahub permission`, which is an operating policy, not authentication. The hostile-agent boundary above applies unchanged: an agent with an unrestricted shell as the same user can impersonate the console, and so can open a settings session.
172
172
  - Every change writes a `settings` event (the key, the value before and after, the source `dashboard` or `terminal`) and a line in `hub.log` and open consoles; opening a settings session is logged too. The unified dashboard (`ahub ui --all`) forwards settings actions as an ordinary session and opens no settings session.
173
173
  - Configuration reload (issue #439). The daemon watches `config.json`, `config.local.json`, `routing.toml` and `routing.local.toml` and re-reads the complete candidate after a change, as the dashboard's Reload configuration and `ahub settings reload` do; an invalid candidate applies nothing. Each changed key is then authorized on its own against its file's actual git provenance. A privacy tightening (local-only PII, the model screen on, added PII patterns, narrower roles or capability lists, unless it moves the planner or conductor) and a safe registered setting apply from any file. Every other change, machine-local fields included, keeps its running value when its file is tracked or git cannot answer. From an untracked file such a guarded change is adopted only by `ahub settings reload` in an interactive terminal, after it lists each guarded key with its full before and after values and the person types `y` within 30 seconds; the confirmation is bound to the file contents and accepted state it was shown for. File watches and dashboard reloads, a settings session's included, never confirm, and no reload changes the roster: membership moves only through the roster setting. Keys whose consumers are bound at start (memory, the gateway and MLX, checks, the privacy and sandbox policy of existing sessions, the terminal provider and others) stay pending until a restart.
174
- - Start modes. A start the hub makes itself (the dashboard's Start control, `peer_start` from a console child, the conductor's `hub_peer_start`, `pi.auto_start`) takes the peer's start mode. Through settings only a settings session or the terminal can set it to headless; a config file can also set `peers.<id>.start_mode`, the shared one included (it is not a machine-local field: headless was the only form for Pi, Kimi and the local worker before this change, a hub-made headless Codex start is the app-server the hub already ran for it, and a start mode grants nothing a permission mode or a capability controls). A declared extra instance accepts only `headless` from any surface; a Claude channel instance accepts no start mode, and a headless Claude instance must have roles exactly `["reviewer"]` and no worktree. For a TUI the hub opens a terminal running `<runtime> <its own entry point> --project <this project> <peer>` (and `--mode tui` for Pi): every word is the hub's own, the command line is those words each quoted for a POSIX shell, and no request field, tool argument or agent message is part of it. In a `terminal.open` template `{command}` is that quoted line and `{title}` and `{cwd}` are raw text for argv elements of their own; each placeholder is substituted once, so a project path that itself contains a placeholder, a quote or `$` stays one word. A provider's own error text goes to `hub.log`, not to the caller. The terminal comes from `terminal.open`, a machine-local command template that config trust refuses from a committed file and that no settings surface can write, or from Orca. The opened terminal's environment carries no agent marker, so `ahub` there has the console's identity. Without a provider the start is refused with the command to run by hand; it is never downgraded to headless. A conductor can start a peer but cannot choose its mode.
174
+ - Start modes. A start the hub makes itself (the dashboard's Start control, `peer_start` from a console child, the conductor's `hub_peer_start`, `pi.auto_start`) takes the peer's start mode. Through settings only a settings session or the terminal can set it to headless; a config file can also set `peers.<id>.start_mode`, the shared one included (it is not a machine-local field: headless was the only form for Pi, Kimi and the local worker before this change, and for Jcode before 0.12.39, a hub-made headless Codex start is the app-server the hub already ran for it, and a start mode grants nothing a permission mode or a capability controls). A declared extra instance accepts only `headless` from any surface; a Claude channel instance accepts no start mode, and a headless Claude instance must have roles exactly `["reviewer"]` and no worktree. For a TUI the hub opens a terminal running `<runtime> <its own entry point> --project <this project> <peer>` (with `--mode tui` for Pi, Kimi and Jcode): every word is the hub's own, the command line is those words each quoted for a POSIX shell, and no request field, tool argument or agent message is part of it. In a `terminal.open` template `{command}` is that quoted line and `{title}` and `{cwd}` are raw text for argv elements of their own; each placeholder is substituted once, so a project path that itself contains a placeholder, a quote or `$` stays one word. A provider's own error text goes to `hub.log`, not to the caller. The terminal comes from `terminal.open`, a machine-local command template that config trust refuses from a committed file and that no settings surface can write, or from Orca. The opened terminal's environment carries no agent marker, so `ahub` there has the console's identity. Without a provider the start is refused with the command to run by hand; it is never downgraded to headless. A conductor can start a peer but cannot choose its mode.
175
175
 
176
176
  ## Multi-project manager (protocol 7)
177
177
 
@@ -240,7 +240,7 @@ never-ask still requires the person's confirmation. qwen_cmd is machine-local an
240
240
  never editable through settings. Legacy benchmark adapters remain unmanaged by
241
241
  this production table. Startup diagnostics withhold native host/payload details.
242
242
 
243
- Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts only a plain jcode release from 0.98.0 up to, not including, 1.0.0 (#529), and runs every owned jcode process (version probe, daemon, ACP) with `JCODE_NO_TELEMETRY=1`, because jcode's telemetry can report the provider account's email. Native diagnostics withhold payloads/hosts. Unproved conversation loads refuse; no fresh replacement is called a restore.
243
+ Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts only a plain jcode release from 0.98.0 up to, not including, 1.0.0 (#529), and runs every owned jcode process (probe, serve, ACP and TUI) with `JCODE_NO_TELEMETRY=1`, because jcode's telemetry can report the provider account's email. Native diagnostics withhold payloads/hosts. Unproved conversation loads refuse; no fresh replacement is called a restore. Native TUI launch exposes its private socket/session tuple only to the authenticated console, bound to the same live owned socket and session after read-only native persistence proof. Tools/status do not receive it. The TUI has no single-writer guard: person input may interleave with ACP. Native policy remains unmanaged and closing the frontend does not stop ACP.
244
244
 
245
245
  Antigravity (`agy`, #485) is optional, cloud and off roster by default. It runs native tools under its own unmanaged permissions, without a hub sandbox or hub permission-mode mapping; headless, resume, unattended and native permission flags are refused. Its roles are capped at implementer and verifier, so it never plans, reviews or receives PII work. Only the person's `ahub agy` launcher writes its three plugin files in `.agents/plugins/agent-hub/` (`plugin.json`, `mcp_config.json`, `hooks.json`): linked or foreign entries refuse before any write, cleanup removes only that launch's matching files, and it never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Conductor starts are refused. Hook authority (`native_hook_launch`) comes only from that launcher, belongs to the console socket that began it and ends when that socket closes; hook input over 1 MiB is refused, and observations are bound to the daemon, launch, native session and project.
246
246
 
@@ -253,7 +253,10 @@ durable delivery goes offline with `needs_review`; on a bus without a journal, t
253
253
  stays busy until the cancelled prompt reports, then drains on idle. After the 30 s bound
254
254
  without a report it goes offline with queued work retained, never idle. Cancelled results
255
255
  and errors are discarded; an error during that wait leaves one log line. Other adapters
256
- keep their cancellation rules (`turn/interrupt` for Codex). Queues drain in order only
256
+ keep their cancellation rules (`turn/interrupt` for Codex). An ACP tool call announced
257
+ and not yet `completed` or `failed` is running work, not silence: it keeps the turn alive
258
+ up to a 30 min tool ceiling (never below `watchdog_ms`), past which the cancel above runs
259
+ with a log line naming the open tool (#542 D4). Queues drain in order only
257
260
  when the adapter is idle. Delivery is at-least-once with
258
261
  idempotency by `id`: a failed delivery returns to the queue head and is retried after 1 s, and
259
262
  after 3 failures it is reported as undeliverable on the console so one envelope cannot block a
@@ -2325,13 +2328,13 @@ acts on the stopped state directory.
2325
2328
 
2326
2329
  ## Amendment: TUI by default, headless as opt-in (issue #269, part 2)
2327
2330
 
2328
- - `peers.<id>.start_mode` (`tui` or `headless`, for pi, codex and kimi; `tui` when unset) is the mode a start takes when nothing more specific was asked. Claude has only a TUI; Kimi defaults to its own pull-only TUI, with ACP selectable; local stays headless. The loader refuses a malformed `peers` block.
2331
+ - `peers.<id>.start_mode` (`tui` or `headless`, for pi, codex, kimi and jcode; `tui` when unset) is the mode a start takes when nothing more specific was asked. Claude has only a TUI; Kimi defaults to its own pull-only TUI, with ACP selectable; local stays headless. The loader refuses a malformed `peers` block.
2329
2332
  - A person typing `ahub pi` gets that mode: the TUI in their terminal by default. `--mode headless` or `--headless` asks for headless. Without a terminal (the console's command line, a script) and with the default asking for the TUI, the CLI sends the console-role `peer_start` request and the hub opens a terminal, or refuses with the command; an option given then is refused, because the fixed command carries none. An explicit `--mode tui` is taken at its word. `ahub codex` and `ahub claude` typed by a person are unchanged.
2330
2333
  - A start the hub makes itself is `hubStart(peer)`: the dashboard's `start_peer` action, `peer_start`, the conductor's `hub_peer_start` and `pi.auto_start` at hub start. With `headless` it starts the peer as before (for Codex: the app-server only). With `tui` it opens a terminal running `<runtime> <CLI entry> --project <root> <peer>` (`--mode tui` for Pi), assembled from the hub's own words and quoted word by word; no caller text is part of it. The peer is reserved before the provider is asked, so starts that arrive together open one terminal, and a second start within 30 seconds of an opened terminal is refused instead of opening another (a failed open frees it at once). A start mode of headless never takes over a Pi that runs in a terminal.
2331
2334
  - Terminal providers, in order: `terminal.open`, a machine-local argv template (`{command}` in exactly one element, plus `{title}` and `{cwd}`; a `MACHINE_LOCAL` field under config trust and on the never-editable list), then Orca (`orca terminal create --worktree id:<id> --command ...`) when the hub runs in an Orca worktree or a recorded launch of the project names one. Each template placeholder is substituted once; only `{command}` is shell-quoted. The opened terminal's environment carries no agent marker and no Orca terminal identity of the hub's own. A provider's error text is logged, not returned. With no provider the start is refused with the manual command; it never falls back to headless. Only the running product wires the real providers; a daemon constructed without them opens nothing.
2332
2335
  - The conductor's `hub_peer_start` no longer takes a mode: it takes the peer's start mode, and a `mode` argument that differs is refused. It may now start claude and codex through the provider, and codex headless when a person set that.
2333
2336
  - Recovery restores each peer in the mode its recorded session had. After an unplanned stop, `pi.auto_start` no longer starts a fresh headless Pi in place of one that ran in a terminal (with or without a recorded session file), unless headless is Pi's start mode. `peers.<id>.start_mode` may be set in the shared config file too: it is not a machine-local field.
2334
- - Settings: `peers.pi.start_mode`, `peers.codex.start_mode` and `peers.kimi.start_mode` join the registry (group Start, stored in `config.local.json`, raising except to `tui`). They are the one stored setting a running hub takes up: after a settings write the hub re-reads the block, so the mode applies at that peer's next start. The dashboard gains a Start control per peer that is not attached, saying the mode and the provider (or why there is none); it offers no button when the start would be refused. An ordinary session may press it: it runs only the fixed start. The page blocks repeat starts while the request is in flight and unlocks after a confirmed answer, leaving the hub's terminal reopen guard authoritative. The manager waits 35 seconds (the terminal provider cap plus its margin); a slower headless start can end unconfirmed. After an unconfirmed manager result the page holds that project/instance/peer control for `PEER_START_RETRY_MS` (the same 30-second bound as the hub's reopen guard), or until the `starts` snapshot confirms attachment, and names the retry bound. A confirmed project-local dashboard start behaves as before; no manager or settings authority changes.
2337
+ - Settings: `peers.pi.start_mode`, `peers.codex.start_mode` and `peers.kimi.start_mode` and `peers.jcode.start_mode` join the registry (group Start, stored in `config.local.json`, raising except to `tui`). They are the one stored setting a running hub takes up: after a settings write the hub re-reads the block, so the mode applies at that peer's next start. The dashboard gains a Start control per peer that is not attached, saying the mode and the provider (or why there is none); it offers no button when the start would be refused. An ordinary session may press it: it runs only the fixed start. The page blocks repeat starts while the request is in flight and unlocks after a confirmed answer, leaving the hub's terminal reopen guard authoritative. The manager waits 35 seconds (the terminal provider cap plus its margin); a slower headless start can end unconfirmed. After an unconfirmed manager result the page holds that project/instance/peer control for `PEER_START_RETRY_MS` (the same 30-second bound as the hub's reopen guard), or until the `starts` snapshot confirms attachment, and names the retry bound. A confirmed project-local dashboard start behaves as before; no manager or settings authority changes.
2335
2338
  - Control protocol stays 16: `peer_start` is a new console-role request an older hub answers as unknown, and `ui_snapshot` gains the optional `starts` list.
2336
2339
  - Migration: Pi's default changes from headless to its TUI. `ahub settings set peers.pi.start_mode headless` (or the same key in `config.local.json`) keeps a headless auto-started Pi; scripts add `--headless`.
2337
2340
 
@@ -2458,6 +2461,10 @@ the queue.
2458
2461
  - A private foreground daemon/socket and ACP client belong to this peer. The global daemon is never adopted or stopped. No automatic update, self-dev or credential copy. The native MCP store must already exist to avoid implicit Codex import.
2459
2462
  - ACP session MCP arrays are ignored natively, so the person launcher manages only agent-hub-jcode, shared:false, in project .jcode/mcp.json. Later override layers, foreign names and links refuse. Daemon starts verify read-only. .jcode is outside scoped grants.
2460
2463
  - Native policy is unmanaged and has no hub sandbox. Hub permission mappings refuse; native quota/consumption are not inferred. Every task mutation stays in Tasks and PII remains at existing redaction/routing boundaries.
2464
+ - Jcode's default start mode is `tui`; `--mode headless` and `peers.jcode.start_mode: headless` retain the owned headless driver. Mode precedence is the explicit flag, the running hub's accepted mode, then the file/default. A console/conductor start opens the same waiting launcher through the terminal provider, including when ACP is already running; no provider means an actionable terminal command, never a headless downgrade.
2465
+ - J1.4 (#464): the 0.98.1 native receipt disproved persistence after `session/new`. A person launcher opens the top-level `jcode --socket S --resume ID` only after bounded native saved-session evidence for the same launch, socket and session. Until then it prints once: `jcode's TUI opens after its first hub turn; send it work, or Ctrl-C`. Ctrl-C ends that wait without stopping or cancelling ACP. No artificial production prompt or hub-created native session JSON is allowed. The native prerequisite and account limits are recorded in `docs/verification/2026-10-20-jcode-tui-0.98.1.md`.
2466
+ - The actual private tuple is available only through the authenticated console-only, feature-negotiated `native_tui_launch` request, never status, tools replies or generic start replies. Pure dry previews remain redacted and start nothing; a person's print-command may resolve an already-saved running session. TUI argv preserves the owned command prefix, model and provider profile; its environment selects the same native home/socket and telemetry opt-out. No `jcode connect` path or external/global daemon is adopted.
2467
+ - J3's lack of a single-writer guard is accepted: a person's TUI input may interleave with a hub ACP turn. Closing the TUI keeps the owned ACP runtime alive. Owned ACP recovery metadata remains headless, and the terminal is recorded separately; native session/load identity refusal is unchanged.
2461
2468
  - The private socket name ends in `.sock`: jcode names its debug socket by replacing that suffix. Every owned jcode process runs with `JCODE_NO_TELEMETRY=1` (#529).
2462
2469
  - Native ACP load (verified on 0.94.0 and 0.98.1) lacks identity proof. Controlled recovery refuses before effects, crash recovery is manual and a load without an exact echoed id refuses before a prompt or restored claim. Real provider/tool/teardown evidence and comparative measurement remain unverified.
2463
2470
 
@@ -3329,7 +3336,7 @@ A2 preserves literal `KNOWN_PEERS` and `OPTIONAL_PEERS` exports in
3329
3336
  `peer-catalog.ts` for released coordinators that inspect their source. The guard
3330
3337
  records these exact file/symbol exceptions separately with the reason
3331
3338
  "cross-release source probe", and tests their values against the typed registry
3332
- catalog. The same exception preserves `start-mode.ts`'s literal `START_MODE_PEERS`, which released coordinators inspect before restoring Kimi headlessly. The three probes are counted separately (5 + 2 + 3 sites). The native vendor baseline is otherwise empty. A retained coordinator's
3339
+ catalog. The same exception preserves `start-mode.ts`'s literal `START_MODE_PEERS`, which released coordinators inspect before restoring Kimi headlessly. The three probes are counted separately (5 + 3 + 4 sites). The native vendor baseline is otherwise empty. A retained coordinator's
3333
3340
  existing source probe must accept the A2 target; no unsupported-target ceiling is
3334
3341
  introduced. ponytail: retire these compatibility literals only after a separately
3335
3342
  approved minimum supported upgrade source no longer reads them.
@@ -0,0 +1,55 @@
1
+ # 0.12.39 release verification
2
+
3
+ This record covers release preparation and source review. It does not claim publication, installation, a running-hub restart or native/live acceptance.
4
+
5
+ ## Candidate
6
+
7
+ Version 0.12.39, control protocol 16 (unchanged; the daemon adds the `native_tui_launch` feature), from merged main `f9df83d6`. The person directed the release on 2026-10-20 ("finish the open PRs and release the next version").
8
+
9
+ - #464 (PR #557, hub task #246): `ahub jcode` opens jcode's native TUI by default, attached to the hub-driven ACP session.
10
+ - The console-only `native_tui_launch` hands the launch tuple only to the person's launcher, after a bounded read-only proof that the session is saved.
11
+ - The launcher waits with one fixed line and opens the TUI once the session is saved; Ctrl-C ends only the wait.
12
+ - Start mode resolves from the flag, then the accepted daemon mode, then file/default; `--mode headless` is unchanged.
13
+ - J1.4 is amended: jcode 0.98.1 saves a session only after its first real prompt.
14
+ - #542 D4 (PR #547, hub task #245): an announced ACP tool call that has not completed or failed keeps the turn alive up to a 30 min tool ceiling (never below `watchdog_ms`). The watchdog re-arms only for the remaining ceiling.
15
+ - #548 (PR #552, hub task #247): the MLX test fixtures take a free loopback port.
16
+ - Still open (unchanged from 0.12.38): live native clients, installed managed-Pi #418 AC8 (#142), #438 AC7, the native console settings leg (#439 AC5), real upgrades with reattach (#463) and with a managed terminal and cancelled rows (#478), a live fresh Codex review with a real account, a live split of a real memory home (#536), the agy live TUI/account leg (#251), and Kimi's interactive-TUI coexistence and usage checks (#533).
17
+ - New open legs:
18
+ - Jcode's TUI with a personal account, a real provider and hub-tool calls (#464). The receipt covers only an isolated HOME with a loopback fixture provider (docs/verification/2026-10-20-jcode-tui-0.98.1.md).
19
+ - A live Jcode tool call held past the inactivity watchdog (#542); only fake-ACP tests cover it.
20
+ - Follow-ups: #559 (review nits of #557), #558 (macOS `runCheck` hang), #555 (agy native reads into Facts).
21
+
22
+ ## Reviews
23
+
24
+ | PR | Final head | Review | CI |
25
+ | --- | --- | --- | --- |
26
+ | #552 (#548) | 28e37cf2 | codex approved hub task #247 | exact head, success |
27
+ | #557 (#464) | 7c0475c3 | planner: full-diff read-only review of 26 paths, no Important findings; affected tests run by the planner (43 pass; the one failure only reproduces under macOS's symlinked default TMPDIR, follow-up #559 N6); nits in #559 | exact head, success on Ubuntu and macOS with all four seeded shards |
28
+ | #547 (#542 D4) | 26ad11cb | round 1 changes requested by codex, fixed at 36015a76; codex re-review approved with no findings (109 ACP and daemon tests pass) | exact head, success; macOS passed on a rerun of the failed job after the unrelated #558 hang |
29
+
30
+ ## Document review
31
+
32
+ Eleven documents were stale against the 0.12.38 stamps (`adf2d5fd`). Two fresh-context reviewers read them against the three PRs above, one the person-facing documents and the other the agent notes, and applied their own corrections. The planner spot-checked the claims against the code.
33
+
34
+ | Document | Result | Checked claims and correction |
35
+ | --- | --- | --- |
36
+ | `README.md` | corrected | Status 0.12.39 (protocol 16 unchanged); the Jcode TUI default and how to keep headless (#464); the verification link; open legs add Jcode's live TUI with a real account and a live tool call held past the watchdog. |
37
+ | `docs/quickstart.md` | corrected | Install pin `v0.12.39`; "Upgrading to 0.12.39": restart Claude sessions; `ahub jcode` opens the TUI after the first hub turn saves the session; hub-made starts open a terminal; how to keep headless. The 0.12.38 notes are kept for upgrades from 0.12.37 or earlier. |
38
+ | `docs/operations.md` | corrected | Version line and upgrade commands 0.12.39 (protocol-16 range through 0.12.39); `hub_peer_start` includes Jcode; `peers.jcode.start_mode` in the settings and start-mode tables; the ACP tool-ceiling paragraph (#542). |
39
+ | `docs/security.md` | corrected | `JCODE_NO_TELEMETRY` covers probe, serve, ACP and TUI (#559 N5); start words with `--mode tui` for Pi, Kimi and Jcode; Jcode was headless-only before 0.12.39. |
40
+ | `docs/agent-notes/adapters.md` | corrected | New ACP tool-ceiling rule (re-arm for the remaining ceiling, reset per delivery, private-turn log latch); the Jcode TUI rule: saved-session proof only through `savedSession`, option drift refused, the tuple only to a person's launcher, previews resolve nothing, no synthesized save or prompt. |
41
+ | `docs/agent-notes/daemon.md` | corrected | New `native_tui_launch` rule (console-only, refusals, binding, the tuple only in the console reply, feature-gated); a TUI start of an owned-ACP peer opens a terminal even while ACP is online. |
42
+ | `docs/agent-notes/benchmarks.md` | corrected | The driver's Qwen `AcpPeer` now runs a silent open tool call to the ACP tool ceiling, which counts as a changed study condition. |
43
+ | `docs/agent-notes/tests.md` | corrected | Fixtures that need a port take it from a free-port probe, never a fixed number (#548). |
44
+ | `docs/agent-notes/budget.md` | no change | The changed paths touch no quota, pause or context rule. |
45
+ | `docs/agent-notes/bus.md` | no change | Delivery, digest and steer code is unchanged; the Jcode TUI is not a pull-only peer. |
46
+ | `docs/agent-notes/tasks.md` | no change | The changes are launch and preview only, with no task, board, routing or reviewer logic. |
47
+ | `docs/agent-notes/README.md` | no change | Stale only because `daemon.md`, a tracked path, changed; its content is unaffected. |
48
+
49
+ Found during the review, not changed here: `test/local-worker.test.ts` still derives Sidecar ports from the PID (removed with the sidecar under #553); older tests call `realpathSync` on temp dirs (`test/liveness.test.ts`, `test/peer-stop.test.ts`, `test/conflicts.test.ts`); `ahub upgrade` announces no Jcode default change, as it does for Kimi.
50
+
51
+ ## Checks
52
+
53
+ - Each merged PR's exact-head CI passed (table above). The release branch is main `f9df83d6` plus the version, release notes and document corrections. The bundle is rebuilt with the pinned Bun 1.4.2 under host admission and is fresh; `tsc` is clean.
54
+ - The stale stamps are restamped to the release commit `09d85684` (reviewed against main `f9df83d6`; the release commit adds only the version, notes and documents); `node scripts/check-docs.mjs --require-fresh` reports 0 stale documents and runs again on the merged main before the tag.
55
+ - Exact-head CI on the release PR.
@@ -0,0 +1,39 @@
1
+ # Jcode 0.98.1 TUI native prerequisite (#464, task #246)
2
+
3
+ The parent-sanitized native receipt `task246-native-evidence.json` records the prerequisite transport proof below. Installed Jcode was 0.98.1; the public native source revision was `1c7426eb1`. This record does not verify the pending hub implementation, its CI, or personal OAuth/account behavior. It supplements [the 0.98.1 contract record](2026-10-19-jcode-0.98.1.md).
4
+
5
+ ## Probe scope
6
+
7
+ - Isolated `HOME`, `JCODE_HOME` and `TMPDIR`, with an owned private socket directory of mode `0700`; no real-home access.
8
+ - The same fixture prefix, `-p anthropic-api -m claude-sonnet-4-6`, on `serve`, ACP and the top-level native TUI. The TUI did not use `connect`.
9
+ - Only dummy per-run auth for a loopback provider fixture. No personal credentials or remote provider account proof; earlier failed OAuth fixture probes are not passes.
10
+ - Actual native PTY TUI observations, with provider replies supplied by the loopback fixture. This proves native transport behavior under that fixture, not model quality, production provider behavior or hub-tool execution.
11
+
12
+ ## Recorded native results
13
+
14
+ | Case | Evidence | Result and boundary |
15
+ | --- | --- | --- |
16
+ | First save | `session/new` left no saved native session JSON. The first real loopback-provider `session/prompt` ended normally, made one loopback request and produced a verified native save. | A new ACP session ID alone is insufficient evidence for opening the selected TUI conversation. |
17
+ | J0-1 | The actual native PTY TUI displayed a new reply marker supplied only by the loopback provider after an ACP prompt. The native turn ended normally. | Passed the new ACP reply visibility prerequisite; the marker was not merely old transcript text. |
18
+ | J0-4 | The TUI closed during an in-flight prompt. The owned server and ACP remained alive at detach; the original prompt completed. | Passed the detach independence prerequisite for that owned native runtime. |
19
+ | J0-5 | The owned server restarted with the same home; `session/load` selected the same session ID. The top-level TUI displayed both the old transcript marker and a new ACP-turn marker. | Passed the native selected-session continuity prerequisite. The load response omitted the ID echo, so this is not automatic adapter recovery success. |
20
+ | Cleanup | Seven owned process groups had cleanup confirmations. The final process table was available and showed zero survivors. | Cleanup confirmed for the probe's owned groups. |
21
+
22
+ Native `session/load` still does not echo its selected session ID. The adapter's existing refusal of unproven automatic recovery remains unchanged; the observed native continuity does not satisfy that adapter identity contract.
23
+
24
+ ## Launcher contract (J1.4)
25
+
26
+ The planner-approved correction follows the observed first-save boundary. While the same launch, socket and session lack saved native evidence, the launcher prints exactly:
27
+
28
+ ```text
29
+ jcode's TUI opens after its first hub turn; send it work, or Ctrl-C
30
+ ```
31
+
32
+ It waits for the native save and then opens the TUI. Ctrl-C ends only the launcher wait and retains ACP; a provider-opened terminal runs the same launcher. The implementation must not send artificial production prompts or create native session JSON on the hub's behalf. These are launcher requirements, not validation results of this native probe.
33
+
34
+ ## Accepted limits and remaining evidence
35
+
36
+ - No single-writer guard: TUI input may join an ACP turn.
37
+ - Native policy remains unmanaged, without hub approval or a hub sandbox.
38
+ - Source implementation, focused regressions, the full local gate and exact-head CI require separate receipts. This document's author performed read-only/text checks only and did not rerun the native probe, tests or builds.
39
+ - Personal account/OAuth behavior, real provider serving, project MCP discovery and real hub-tool calls remain unverified by this receipt.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "documents": {
4
4
  "README.md": {
5
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
5
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
6
6
  "paths": [
7
7
  "package.json",
8
8
  "src/",
@@ -11,14 +11,14 @@
11
11
  ]
12
12
  },
13
13
  "docs/security.md": {
14
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
14
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
15
15
  "paths": [
16
16
  "src/",
17
17
  "templates/"
18
18
  ]
19
19
  },
20
20
  "docs/operations.md": {
21
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
21
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
22
22
  "paths": [
23
23
  "package.json",
24
24
  "src/",
@@ -35,7 +35,7 @@
35
35
  ]
36
36
  },
37
37
  "docs/quickstart.md": {
38
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
38
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
39
39
  "paths": [
40
40
  "package.json",
41
41
  "src/",
@@ -43,7 +43,7 @@
43
43
  ]
44
44
  },
45
45
  "docs/agent-notes/adapters.md": {
46
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
46
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
47
47
  "paths": [
48
48
  "src/adapters/",
49
49
  "src/pi/",
@@ -69,7 +69,7 @@
69
69
  ]
70
70
  },
71
71
  "docs/agent-notes/benchmarks.md": {
72
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
72
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
73
73
  "paths": [
74
74
  "scripts/benchmarks/",
75
75
  "test/benchmarks/",
@@ -81,7 +81,7 @@
81
81
  ]
82
82
  },
83
83
  "docs/agent-notes/budget.md": {
84
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
84
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
85
85
  "paths": [
86
86
  "src/hub/budget.ts",
87
87
  "src/cli/statusline-tee.ts",
@@ -98,7 +98,7 @@
98
98
  ]
99
99
  },
100
100
  "docs/agent-notes/bus.md": {
101
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
101
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
102
102
  "paths": [
103
103
  "src/hub/bus.ts",
104
104
  "src/hub/envelope.ts",
@@ -112,7 +112,7 @@
112
112
  ]
113
113
  },
114
114
  "docs/agent-notes/daemon.md": {
115
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
115
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
116
116
  "paths": [
117
117
  "src/hub/daemon.ts",
118
118
  "src/hub/control-client.ts",
@@ -188,7 +188,7 @@
188
188
  ]
189
189
  },
190
190
  "docs/agent-notes/tasks.md": {
191
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
191
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
192
192
  "paths": [
193
193
  "src/hub/tasks.ts",
194
194
  "src/hub/board.ts",
@@ -212,7 +212,7 @@
212
212
  ]
213
213
  },
214
214
  "docs/agent-notes/tests.md": {
215
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
215
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
216
216
  "paths": [
217
217
  "test/",
218
218
  "scripts/check.sh",
@@ -231,7 +231,7 @@
231
231
  ]
232
232
  },
233
233
  "docs/agent-notes/README.md": {
234
- "verifiedAgainst": "adf2d5fd2fc2e9f5ce5b5e9daf594269de227238",
234
+ "verifiedAgainst": "09d85684a53322b816f47f37c61faeb8a359419c",
235
235
  "paths": [
236
236
  "src/cli/console-state.ts",
237
237
  "src/cli/attention.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staix/agent-hub",
3
- "version": "0.12.38",
3
+ "version": "0.12.39",
4
4
  "description": "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
5
5
  "license": "MIT",
6
6
  "type": "module",