@staix/agent-hub 0.12.22 → 0.12.23

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +16 -8
  3. package/docs/agent-notes/adapters.md +10 -5
  4. package/docs/agent-notes/benchmarks.md +3 -0
  5. package/docs/agent-notes/budget.md +4 -0
  6. package/docs/agent-notes/daemon.md +8 -1
  7. package/docs/agent-notes/local-worker.md +3 -2
  8. package/docs/agent-notes/tasks.md +2 -1
  9. package/docs/agent-notes/tests.md +2 -0
  10. package/docs/events.md +7 -3
  11. package/docs/operations.md +232 -26
  12. package/docs/quickstart.md +9 -4
  13. package/docs/security.md +15 -6
  14. package/docs/smoke.md +72 -5
  15. package/docs/specs/2026-09-19-agent-hub-design.md +77 -10
  16. package/docs/verification/2026-10-10-cli-output-phase1.md +309 -0
  17. package/docs/verification/2026-10-10-cli-output-phase2.md +672 -0
  18. package/docs/verification/2026-10-11-0.12.23.md +90 -0
  19. package/docs/verified.json +25 -16
  20. package/package.json +1 -1
  21. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  22. package/plugins/agent-hub/server.js +14 -3
  23. package/src/adapters/acp-quota.ts +13 -0
  24. package/src/adapters/acp.ts +95 -13
  25. package/src/adapters/codex-appserver.ts +43 -13
  26. package/src/cli/bench.ts +2 -1
  27. package/src/cli/console-state.ts +28 -22
  28. package/src/cli/console.ts +20 -10
  29. package/src/cli/help.ts +19 -14
  30. package/src/cli/identity.ts +2 -2
  31. package/src/cli/init.ts +1 -1
  32. package/src/cli/main.ts +272 -159
  33. package/src/cli/output.ts +363 -0
  34. package/src/cli/preview.ts +2 -1
  35. package/src/cli/reset.ts +3 -0
  36. package/src/cli/status-lines.ts +0 -28
  37. package/src/cli/terminal-recovery.ts +27 -1
  38. package/src/cli/upgrade-runtime.ts +24 -7
  39. package/src/cli/upgrade.ts +23 -5
  40. package/src/hub/board.ts +2 -0
  41. package/src/hub/budget.ts +39 -7
  42. package/src/hub/bus.ts +10 -11
  43. package/src/hub/child-process.ts +11 -0
  44. package/src/hub/conductor.ts +23 -11
  45. package/src/hub/config-trust.ts +16 -5
  46. package/src/hub/conflicts.ts +2 -0
  47. package/src/hub/context-window.ts +8 -1
  48. package/src/hub/crash.ts +2 -1
  49. package/src/hub/daemon.ts +338 -23
  50. package/src/hub/db-timeout.ts +3 -0
  51. package/src/hub/delivery-journal.ts +2 -1
  52. package/src/hub/envelope.ts +1 -1
  53. package/src/hub/events.ts +2 -0
  54. package/src/hub/execution-budget.ts +2 -0
  55. package/src/hub/facts.ts +5 -4
  56. package/src/hub/git.ts +32 -0
  57. package/src/hub/hub-tools.ts +1 -1
  58. package/src/hub/lifecycle.ts +10 -4
  59. package/src/hub/manager.ts +18 -5
  60. package/src/hub/peers.ts +5 -3
  61. package/src/hub/permission-mode.ts +14 -0
  62. package/src/hub/project.ts +2 -1
  63. package/src/hub/recovery-error.ts +31 -0
  64. package/src/hub/recovery-store.ts +6 -5
  65. package/src/hub/registry.ts +2 -1
  66. package/src/hub/routing.ts +114 -13
  67. package/src/hub/settings.ts +392 -0
  68. package/src/hub/snapshots.ts +4 -2
  69. package/src/hub/start-mode.ts +48 -0
  70. package/src/hub/tasks.ts +129 -31
  71. package/src/hub/terminal-open.ts +64 -0
  72. package/src/hub/ui.ts +25 -13
  73. package/src/local/deny.ts +29 -7
  74. package/src/local/sandbox.ts +44 -6
  75. package/src/memory/recall.ts +2 -2
  76. package/src/models/mlx.ts +2 -1
  77. package/src/pi/tool-receipts.ts +2 -1
  78. package/src/ui/index.html +157 -10
package/docs/smoke.md CHANGED
@@ -2,6 +2,47 @@
2
2
 
3
3
  `scripts/check.sh` covers everything against fakes. The legs below need real accounts and an interactive terminal, so they are run by hand and recorded here.
4
4
 
5
+ ## Settings from the dashboard and the terminal (#269, part 1)
6
+
7
+ Not run live yet (unverified). `test/settings.test.ts` drives a real daemon, its
8
+ dashboard listener over HTTP and the real CLI; no browser was opened and no
9
+ native agent was started. `ahub ui --settings` and `ahub settings` are a
10
+ person's commands, so the live leg is run by hand, on a scratch project:
11
+
12
+ 1. `ahub up`, then `ahub ui`. The Settings section lists Permissions, Start,
13
+ Routing and Switches; each row shows its source file and when it applies.
14
+ Record the look in a light and a dark theme and at phone width.
15
+ 2. In that ordinary session, set `research.enabled` (saved), then try to raise
16
+ `permission.kimi` to ask-when-needed (refused, naming `ahub ui --settings`).
17
+ 3. `ahub ui --settings`. With Kimi attached, set `permission.kimi` to
18
+ ask-when-needed, then never-ask with the peer id typed; `ahub permission`
19
+ and `ahub status` show each mode, and `ahub tail` shows each change with
20
+ `(dashboard)`.
21
+ 4. Preview a new order for `routing.classes.implement.peers` with a task open,
22
+ save it, run `ahub route explain --class implement x` (it names
23
+ `routing.local.toml`), then "Undo last write".
24
+ 5. `git status` shows no change to `.agenthub/config.json` or
25
+ `.agenthub/routing.toml`; `ahub settings` in the terminal shows the same
26
+ rows as the page.
27
+
28
+ Record the date, the hub version, the browser and each step's result.
29
+
30
+ Part 2, start modes (not run live either; `test/start-modes.test.ts` uses a
31
+ recording terminal provider and a scripted hub, and no terminal was opened):
32
+
33
+ 6. In an Orca terminal of the project, `ahub up` with Pi enabled. `ahub pi`
34
+ opens Pi's TUI in that terminal; in another, `ahub pi --headless` moves it
35
+ to the background.
36
+ 7. Stop Pi. In the dashboard's Peers panel the Start control for pi says
37
+ "Start mode tui: opens ahub pi --mode tui in a terminal through orca".
38
+ Press it: a new Orca terminal runs the TUI and Pi attaches.
39
+ 8. In a settings session set `peers.pi.start_mode` to headless, stop Pi, press
40
+ Start again: Pi comes up headless and no terminal opens.
41
+ 9. Outside Orca with no `terminal.open`: the Start control shows no button and
42
+ the command to run; typing `pi` in `ahub console` prints the same refusal.
43
+ 10. With `terminal.open` set to a tmux or wezterm template in
44
+ `config.local.json`, Start opens Pi's TUI there.
45
+
5
46
  ## Codex resumed approval policy (#270)
6
47
 
7
48
  2026-10-10: installed version read back as `codex-cli 0.162.1`. The live
@@ -461,14 +502,14 @@ In the project directory, one terminal each:
461
502
  3. `ahub codex`. The TUI opens; after its first thread starts, `ahub status` shows `codex idle`.
462
503
  4. `ahub claude`. `ahub status` shows `claude idle`.
463
504
  5. `ahub say "Each of you: reply with your name and nothing else."` Expect three replies in `ahub tail`, each within its turn time, and each agent seeing the others' replies framed as untrusted (`<channel source="agent-hub">` in Claude, `[agent-hub message from ...]` in Codex and Kimi).
464
- 6. While Codex is mid-turn on a long prompt typed in its TUI, run `ahub say @codex "status?"`. `ahub status` shows `queued 1` for codex; the message is injected after the turn completes.
505
+ 6. While Codex is mid-turn on a long prompt typed in its TUI, run `ahub say @codex "status?"`. `ahub status` shows `1` in the codex row's `Q` column; the message is injected after the turn completes.
465
506
  7. `ahub say @kimi one`, `ahub say @kimi two` back to back: both answered in order, none lost.
466
507
  8. Ask Kimi for something that needs a tool (`ahub say @kimi "create /tmp/agenthub-smoke.txt"`): `ahub tail` prints the permission request; `ahub permit <id> <option>` answers it; no answer within 120 s cancels it.
467
508
  9. `ahub kill`: no `kimi acp` or `codex app-server` process survives (`pgrep -fl "kimi acp|codex app-server"`).
468
509
 
469
510
  ## M2: tiers, digests, steer, recall
470
511
 
471
- 1. `ahub pause kimi`, then `ahub say @kimi "[STATUS] one"`, `ahub say @kimi "[STATUS] two"`, `ahub say @kimi "how many agent-hub messages are in this prompt?"`. `ahub status` shows `kimi paused queued 3`. `ahub resume kimi`: one Kimi turn answers for all of them, and on the first delivery of the hub run it also reports the `hub` memory item (`grep recall .agenthub/state/hub.log` shows its size).
512
+ 1. `ahub pause kimi`, then `ahub say @kimi "[STATUS] one"`, `ahub say @kimi "[STATUS] two"`, `ahub say @kimi "how many agent-hub messages are in this prompt?"`. `ahub status` shows `3` in Kimi's `Q` column and a `paused` detail. `ahub resume kimi`: one Kimi turn answers for all of them, and on the first delivery of the hub run it also reports the `hub` memory item (`grep recall .agenthub/state/hub.log` shows its size).
472
513
  2. `ahub say "[FYI] note"` appears on `ahub tail` as `[fyi: record only]` and no peer goes busy.
473
514
  3. With Codex mid-turn on a long prompt typed in its TUI: `ahub say @codex "[IMPORTANT] stop and summarize"`. The running turn changes course (steer) instead of a new turn starting afterwards; `ahub status` never shows it queued. `ahub say @codex "[STATUS] later"` during the same turn stays queued until the turn ends.
474
515
  4. Two agents chatting without markers: their replies reach the third agent as digests, not one turn per message.
@@ -478,8 +519,8 @@ In the project directory, one terminal each:
478
519
  Needs a model gateway in `omniroute.urls` (for the owner: the campus gateway over VPN, or the Access-protected public URL with its two header files) and a key: `OMNIROUTE_API_KEY`, or `omniroute.api_key_file` in `.agenthub/config.json`.
479
520
 
480
521
  1. `ahub doctor`: `omniroute` healthy, `omniroute key` present, `switchyard` installed or not.
481
- 2. In a scratch git repo: `ahub up`, `ahub local`, `ahub tail`, then `ahub say @local "fix the typos in <file>, run git diff --stat and report"`. `ahub tail` shows a permission request for the edit; `ahub permit <id> allow`. The file changes, the answer is a short conclusion, `ahub status` shows `last call: omniroute <model> (provider vllm)`.
482
- 3. Same with `AGENTHUB_SWITCHYARD_BIN` (or `switchyard-server` on PATH) set before `ahub up`: `ahub status` shows `last call: switchyard sy/coding -> <model>` and `switchyard: 127.0.0.1:<port>`; `lsof -nP -iTCP -sTCP:LISTEN | grep switchy` shows loopback only; `.agenthub/state/switchyard.toml` is mode 600 and holds no key; after `ahub kill` the file and the process are gone.
522
+ 2. In a scratch git repo: `ahub up`, `ahub local`, `ahub tail`, then `ahub say @local "fix the typos in <file>, run git diff --stat and report"`. `ahub tail` shows a permission request for the edit; `ahub permit <id> allow`. The file changes, the answer is a short conclusion, `ahub status` shows the served model in the local row's `MODEL` column and `vllm` in the backend table's `PROVIDER` column.
523
+ 3. Same with `AGENTHUB_SWITCHYARD_BIN` (or `switchyard-server` on PATH) set before `ahub up`: `ahub status` shows the served model in `MODEL`, `switchyard` in the backend's `PROVIDER` column, and a `switchyard` detail with `127.0.0.1:<port>`; `lsof -nP -iTCP -sTCP:LISTEN | grep switchy` shows loopback only; `.agenthub/state/switchyard.toml` is mode 600 and holds no key; after `ahub kill` the file and the process are gone.
483
524
  4. claude-mem: `sqlite3 -readonly ~/.claude-mem/claude-mem.db "select agent_id, agent_type, project, title from observations order by id desc limit 3"` shows `local | local-worker` rows a minute or two later (claude-mem's observer runs asynchronously).
484
525
 
485
526
  ## M4: task board
@@ -493,7 +534,7 @@ Needs a model gateway in `omniroute.urls` (for the owner: the campus gateway ove
493
534
  ## M5: budget relay
494
535
 
495
536
  1. `ahub up`, `ahub kimi`, `ahub local`, `ahub tail`. Give Kimi a task, then `ahub budget set kimi 0.95 --resets-in 2m`.
496
- 2. `ahub tail`: Kimi is asked for a checkpoint, writes `.agenthub/checkpoint.md`, calls `hub_checkpoint`; then `budget: kimi paused ...; checkpoint received` and `budget: moved from kimi: #<id> owner -> local`. `ahub status` shows `kimi paused (budget: ...)`, `ahub board` shows the task with `local`.
537
+ 2. `ahub tail`: Kimi is asked for a checkpoint, writes `.agenthub/checkpoint.md`, calls `hub_checkpoint`; then `budget: kimi paused ...; checkpoint received` and `budget: moved from kimi: #<id> owner -> local`. `ahub status` shows a `paused` detail for Kimi naming the budget reason, `ahub board` shows the task with `local`.
497
538
  3. `ahub budget set kimi 0.97` again changes nothing. `ahub resume kimi` is refused while the record is open; `ahub budget resume kimi` overrides it, and further readings over the gate do not pause Kimi again until that window has reset.
498
539
  4. `ahub budget set kimi 0.1` (the mocked reset), or wait for the reset time: `kimi resumed`, and Kimi gets one envelope listing what moved.
499
540
  5. Codex: with a TUI attached through `ahub codex`, `ahub budget` shows its windows from `account/rateLimits/read`; on a limited account Codex is paused until `resetsAt` without a checkpoint.
@@ -548,6 +589,7 @@ Needs a model gateway in `omniroute.urls` (for the owner: the campus gateway ove
548
589
  | 2026-09-20 | #40-#43 after the review fixes, live hub from the PR branch (Kimi 2.0.1, Pi 0.86.0 MLX) | pass: say/remember refuse an absorbed flag and send it after `--`; paused peer printed `queued 3 (2 important)`; Pi mlx->auto handover emitted one `state pi -> idle` and no offline; unprimed Kimi answered a no-action note as `fyi` (`NOT DELIVERED(fyi)`) once the shared instruction named the markers |
549
590
  | 2026-09-20 | status.json queue depth, live hub | found: the file was one message behind (`queued 3` vs live 4) and kept `queued 4` on an empty queue, because `publish` emits before it enqueues and a delivery emits nothing. Fixed: `bus.onQueues` -> `writeStatus`; re-measured 1 -> 2 -> 3 -> 0 exact, field absent at 0 |
550
591
  | 2026-09-20 | 0.6.3 gate + isolated install | pass: `bun run check` 301 tests / 1600 expect / `check: OK`; npm pack tgz (281 KB) → isolated BUN_INSTALL prefix, `ahub --version` 0.6.3, `ahub init` wrote all five files; direct `bun add -g .` of the workspace root fails (nameless dependency, DependencyLoop) |
592
+ | 2026-10-10 | #285 live item 1: Kimi 2.1.1 sends `usage_update` in a hub ACP session, and when | unverified: the running hub records nothing about the notification (the gap phase 1 closes), so observing it means starting a Kimi turn with a stream capture, which this phase did not do. The evidence so far: the 2.0.1 capture above (2026-09-19 row, before the adapter's busy gate existed) and the 2.1.1 source text (the notification's own comment names `used` the context token count and `size` the bound model's maximum, emitted after the turn settles). The adapter now accepts the update whenever it names the current session, idle included, so the unverified order cannot drop it |
551
593
 
552
594
  ## Issue #7 verification ledger (2026-09-19)
553
595
 
@@ -1481,3 +1523,28 @@ with the dashboard. In the dashboard select Light, Dark and System, reload each
1481
1523
  choice, change OS theme on System, and check text, bars, stage labels and controls.
1482
1524
  Repeat with cookies blocked and across two dashboard ports; verify unchanged
1483
1525
  progress snapshots do not repeatedly announce the live region. Record the exact head and verdict here.
1526
+
1527
+ ## One-shot command output (#284 phase 1)
1528
+
1529
+ Human visual inspection: **not run**. Automated geometry and color checks are separate evidence.
1530
+ At the PR head, run `ahub status`, `ahub board`, `ahub budget` and `ahub doctor` in a light and a dark terminal,
1531
+ at 80 and 120 columns. Inspect Korean and long task titles, settlement and hold details, modes and quota windows.
1532
+ Confirm readable state/level words with `--color=never`, matching visible text with `--color=always`, and no cut fields.
1533
+ Compare status's shortened informational ids with `status --full`; suggested command ids must always be whole. In `ahub console`,
1534
+ run `status`, `board` and `budget` and confirm their rows stay within the four-column indent and output remains plain. Run `ahub doctor` in the terminal outside the console.
1535
+ Record the exact head and visual verdict here after the owner performs this leg.
1536
+
1537
+ ## ACP cancellation settlement (#304)
1538
+
1539
+ Not run: real Qwen and Kimi cancellation timing remains owed and does not gate the adapter fix, per issue #304. In an operator-approved ACP session for each provider, record the native version, observe prompt activity, send `session/cancel`, and measure until the correlated prompt result. Record what a new prompt receives before that result. Distinguish those measurements from the fake's ordered barrier and cancellation acknowledgement; do not spend a native turn solely to complete this implementation.
1540
+ ## Remaining command output (#284 phases 2 and 3)
1541
+
1542
+ Human visual inspection: **not run**. At the PR head, use light and dark terminals at 80 and 120 columns.
1543
+ Run `ahub projects`, `ahub status --all`, `ahub queue list`, `ahub turns`, `ahub doctor --orphans`,
1544
+ `ahub queue show <id>`, `ahub models status`, `ahub budget execution status`, `ahub report` and `ahub report --by task`.
1545
+ Inspect long Korean paths, all changed filenames, public private-delivery stubs, nested labelled fields, relative times and report coverage sentences.
1546
+ Compare `--color=never` with `--color=always`. Confirm turn, queue, project and execution budget ids are whole without `--full` and can be copied into their commands.
1547
+ In the console run `status --all`, `queue list`, `queue show <id>`, `turns`, `budget execution status`, `report` and `report --by task`;
1548
+ run `ahub projects`, `ahub doctor --orphans` and `ahub models status` in the terminal outside the console. Status's informational ids can be compared with `status --full`.
1549
+ Confirm JSON output stays plain under `--color=always`. Record the exact head and visual verdict here after the owner performs this leg.
1550
+
@@ -151,7 +151,9 @@ hub CLI / console ── control WS 127.0.0.1:<ctl> ─────────
151
151
  when it has one, otherwise what the `tool_call` update announced for the same `toolCallId`.
152
152
  A payload that cannot be resolved either way is titled as unresolved and its `allow_always`
153
153
  option is withheld, so a blind click cannot grant every later call of the session. The
154
- watchdog sends `session/cancel` before forcing idle. `--model` maps to `kimi --model <alias> acp`
154
+ watchdog sends `session/cancel`. A durable delivery goes offline with `needs_review`;
155
+ without a journal, the peer stays busy until its cancelled prompt reports, bounded by
156
+ 30 s, then goes offline with queued work retained if no report arrives. `--model` maps to `kimi --model <alias> acp`
155
157
  (verified flag). Reusable for `opencode acp`.
156
158
  - Local worker (amended in M3): a hub-native tool-calling loop over non-streaming
157
159
  `POST /chat/completions` with plain `fetch`, no SDK (the repo keeps zero runtime
@@ -243,9 +245,13 @@ interface Envelope {
243
245
  Every queue is bounded (`queue_cap`, default 200): on overflow the oldest non-`important`
244
246
  envelope is dropped and reported on the console.
245
247
 
246
- Inactivity watchdog per busy turn (default 300 s) forces `idle` after cancelling the silent
247
- turn (`session/cancel` for ACP, `turn/interrupt` for Codex); a late result of the cancelled
248
- turn is discarded. Queues are drained in order on `idle`. Delivery is at-least-once with
248
+ The inactivity watchdog per busy turn defaults to 300 s. ACP sends `session/cancel`: a
249
+ durable delivery goes offline with `needs_review`; on a bus without a journal, the peer
250
+ stays busy until the cancelled prompt reports, then drains on idle. After the 30 s bound
251
+ without a report it goes offline with queued work retained, never idle. Cancelled results
252
+ and errors are discarded; an error during that wait leaves one log line. Other adapters
253
+ keep their cancellation rules (`turn/interrupt` for Codex). Queues drain in order only
254
+ when the adapter is idle. Delivery is at-least-once with
249
255
  idempotency by `id`: a failed delivery returns to the queue head and is retried after 1 s, and
250
256
  after 3 failures it is reported as undeliverable on the console so one envelope cannot block a
251
257
  peer. Peer ids claimed over the control WS must not be `user` or a hub-managed adapter id.
@@ -382,12 +388,23 @@ inside a peer.
382
388
  and runs the original command unchanged. `~/.claude/settings.json` is never edited; a
383
389
  user-supplied `--settings` wins and turns the tee off. The `--settings` injection is
384
390
  verified live (2026-09-19: `claude-usage.json` appears within seconds, the wrapped HUD
385
- renders unchanged, `ahub budget` shows both windows). Kimi: `usage_update` tokens over a
386
- rolling 5 h against `budget.kimi_tokens_5h` (off by default). Verified live on kimi
391
+ renders unchanged, `ahub budget` shows both windows). Kimi has an optional soft
392
+ rolling 5 h ceiling for validated consumed-token readings, `budget.kimi_tokens_5h`
393
+ (off by default), and the observed Kimi Code CLI 2.1.1 five-hour provider refusal
394
+ is a hard source (#306). Only the pinned rejected-prompt text is recognised,
395
+ bound to verified native identity and the current turn. It reports `5h`, used 1,
396
+ source `kimi ACP usage-limit refusal`, and a conservative 300-minute reset
397
+ estimate; weekly/monthly limits and other ACP providers remain unverified. Verified live on kimi
387
398
  2.0.1: one update per turn, payload `{"sessionUpdate":"usage_update","used":<tokens>,
388
399
  "size":<context window>}`; `used` is the session's context occupancy against `size`
389
400
  (1M), not billed quota. ACP records this shape as context occupancy diagnostics,
390
- never as cumulative consumed tokens. Only validated cumulative totals or input/output
401
+ never as cumulative consumed tokens. Since #285 phase 1 it is also the peer's
402
+ native context reading (source `acp_usage_update`), shown in `ahub status`, the
403
+ console and the dashboard like Claude's and Codex's; the adapter accepts the
404
+ update whenever it names the current session, since 2.1.1's source text emits it
405
+ after the prompt resolves (an order read from the source, not yet observed live
406
+ through the hub; docs/smoke.md). A gate crossing records the
407
+ event and notice for an ACP peer but sends it no checkpoint request yet. Only validated cumulative totals or input/output
391
408
  pairs feed native token readings; unsupported shapes remain unknown. `ahub budget set`
392
409
  feeds a reading by hand. `local` has no quota and is never paused.
393
410
  - Gate at `budget.gate` (default 0.9) on any fresh window; readings older than
@@ -405,7 +422,17 @@ inside a peer.
405
422
  released, so it leads the first delivery. Moved tasks stay with their new owners. A manual
406
423
  `ahub pause` is never lifted by the coordinator; `ahub resume` does not override a budget
407
424
  pause, `ahub budget resume <peer>` does, and the coordinator then leaves that peer alone
408
- until the window that paused it has reset. A handoff waits until another peer is attached
425
+ until the window that paused it has reset, unless a provider refuses another turn:
426
+ that hard refusal ends the override and supersedes a pending soft checkpoint. An
427
+ untouched ACP refusal is requeued only after the hub confirms its budget pause;
428
+ otherwise it stays held for review. After activity, the delivery remains held
429
+ even though open tasks are relayed, and the queue stays held after the budget
430
+ reset until a person inspects and resolves it. Keep one pending resume notice carrying
431
+ every public move since the previous notice was delivered; retain an older unstructured
432
+ notice as public text. Within one hub run, retain a still-future reset bound for the same
433
+ window across an override, while an explicit provider reset remains authoritative. A
434
+ restored pause without per-window reading identity starts a fresh estimate at the next refusal.
435
+ A handoff waits until another peer is attached
409
436
  (right after a restart nobody is), readings keep their own timestamp, and a window whose
410
437
  reset time has passed no longer counts. A peer whose window reset while the hub was down is
411
438
  not paused again, and still gets the resume envelope once it attaches. With no status line
@@ -667,7 +694,7 @@ M6 internal inference, packaging
667
694
  | Anthropic-to-chat translation drops `tool_use` | Claude never runs on local models in this design |
668
695
  | Local worker executes untrusted room text | Untrusted framing, cwd scope, denylist, no `--unattended` by default |
669
696
  | Steering a Codex turn changes its plan mid-flight | Only `important` steers; default tier is `status` |
670
- | Kimi ACP has no quota API | Token-based soft limit only; documented |
697
+ | Kimi ACP has no quota API | Optional token soft ceiling plus the observed five-hour provider refusal as a hard source; conservative reset, other limits unverified |
671
698
  | claude-mem worker API is internal and unversioned for third-party clients | Client isolated in `src/memory/`, pinned to a plugin version range, fail-open everywhere, contract test against a fake worker plus one live smoke |
672
699
 
673
700
 
@@ -1402,12 +1429,19 @@ Codex reports `tokenUsage.last.totalTokens / tokenUsage.modelContextWindow` in
1402
1429
  and [native TUI](https://github.com/openai/codex/blob/main/codex-rs/tui/src/token_usage.rs)
1403
1430
  distinguish the last active context from the accumulated `total`; the displayed
1404
1431
  raw occupancy does not apply the TUI's baseline-adjusted remaining percentage.
1432
+ The Kimi peer reports `used / size` in `usage_update` (source
1433
+ `acp_usage_update`, #285 phase 1; only Kimi is wired): the session's context
1434
+ occupancy, accepted whenever it names the adapter's current session, including
1435
+ outside a turn; 2.1.1's source text emits it after the prompt resolves (read
1436
+ from the source, not observed live), when the peer may already be idle.
1405
1437
  Pi's RPC state exposes no measured native counter. Its extension context API
1406
1438
  returns an estimate, so Pi and unsupported peers remain unknown here.
1407
1439
 
1408
1440
  Each reading records source and measurement time. Claude's status-line file is
1409
1441
  bound to the daemon instance, managed launcher and native session; Codex's
1410
- notification is fenced by its owning link and native thread. Stale, invalid,
1442
+ notification is fenced by its owning link and native thread; an ACP reading is
1443
+ bound to the adapter's own session id and dropped when that is not the daemon's
1444
+ current session. Stale, invalid,
1411
1445
  disconnected or replaced-session readings expose unknown occupancy, never zero.
1412
1446
  `context.gate` defaults to 0 (off); `context.stale_min` defaults to 30.
1413
1447
  A valid above-gate reading emits one metadata-only `context_pressure` event
@@ -1446,6 +1480,15 @@ approval. Deny is direct. Pending requests include expiry and are withdrawn by
1446
1480
  `permission_closed` on any answer or cancellation. Answer audits have no title.
1447
1481
  Panels expose Peers, Approvals, Tasks, Queue and Events with bounded polling and
1448
1482
  Unicode cell widths, fall back below 80x24, and restore terminal state on exit.
1483
+ Tabs change with the digits 1 to 5 or with Left and Right, wrapping at both
1484
+ ends, with the digit's guards and resets (an arrow under a confirmation dismisses
1485
+ it like any key but y, without moving a tab); a read holding nothing but arrow
1486
+ sequences is that many key presses, never a paste. An Events row's header is
1487
+ tokenized by the stream's own tokenizer on the same tab-flattened text, with the
1488
+ event's tone and kind; body lines stay plain, and selection changes only the
1489
+ `>` marker. The tail prints a peer's context reading once and repeats it only
1490
+ when the printed line changes apart from its time (the printed percentage, the
1491
+ freshness, the source, or known against unknown).
1449
1492
 
1450
1493
  Whole-board progress (#246) uses one pure `taskProgress` function shared by the
1451
1494
  console and hashed dashboard script. It reads only public id/state/deps fields;
@@ -2137,3 +2180,27 @@ acts on the stopped state directory.
2137
2180
  - `ahub stop <peer>` is human-only in `classifyPeerCommand`, prints the returned state, and reports an older hub's unknown request as requiring an upgrade, with a nonzero exit. The upgrade plan's headless `endPlannedPeer` uses the same console request; the daemon does not close a native terminal.
2138
2181
 
2139
2182
  - Requested stop admission also holds per-peer drain, pull and steering before any teardown await, without changing operator/budget pauses. Arriving work remains queued for explicit restart. Idle Pi captures live empty-session proof before stopping; a first accepted prompt with unpersisted history is not silently recreated as empty.
2183
+
2184
+ ## Amendment: settings from the dashboard and the terminal (issue #269, part 1)
2185
+
2186
+ - The dashboard stops being read-mostly for one closed list: a settings registry in `src/hub/settings.ts`, one entry per editable setting (key, type and allowed values, the store, when it applies, and whether it can raise authority). The dashboard's Settings section and `ahub settings list|get|set|undo` render from it and write through it; a key outside it cannot be written from either. Keys that choose what the hub runs or where secrets and task text go (`kimi_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx`, `omniroute`, `memory.worker_url`, `local.read_allow`, `local.bash_network`, `local.network_allow`, `bench.enabled`, `terminal`) are refused by name from every surface.
2187
+ - First list: `permission.<peer>` (the running mode, the console's `permission` request), `permission_modes.<peer>` (the default at hub start), `pi.auto_start`, `routing.stay_switch`, per class `routing.classes.<class>.peers`, `.escalate_to`, `.route` and `.pi_backend`, and the switches `research.enabled`, `approvals.notify`, `snapshots.enabled` and `coordination`. Start modes (`peers.<id>.start_mode`, the terminal provider, Pi's TUI default) are part 2 of the issue.
2188
+ - Stores: config values go to `.agenthub/config.local.json`; routing values go to a new machine-local overlay, `.agenthub/routing.local.toml`, which may hold only those routing keys and is merged over `routing.toml` key by key by the one loader (`parseRouting`). `assign()` stays pure; its trace, and so `ahub route explain`, names the keys the overlay set. The tracked `config.json` and the hand-written `routing.toml` are never written, a machine-local file that git tracks is refused, and `ahub init` adds the overlay to `.gitignore`. A write is validated by the real loader first (a scratch copy for the config, the parser for the overlay), replaces the file through a temp file and rename, and keeps one previous version: undo restores it, refuses when the file changed since, and validates what it restores. The undo record is written after the file, so a write that failed leaves the earlier undo in place. A write that would change nothing (the same value again, or removing a value that is not set) writes no file, keeps the undo record and records no event. A config setting is refused in a project that has no config file at all (neither `config.json` nor `config.local.json`): its first config file would switch the project defaults on (snapshots, limits, the budget relay), and that is `ahub init`'s to do. In a repository where git cannot say whether the file is tracked, nothing is written; a project outside git has nothing tracked. Removing a machine-local value (`inherit`) is a write like any other. When config trust (#17) drops the written field at load (`permission_modes` in a file git cannot vouch for), the reply and the setting's row say that the hub does not read the value.
2189
+ - Authority. An ordinary dashboard session reads settings, lowers a mode to `ask`, turns `pi.auto_start` off and changes the safe switches. A settings session, opened by `ahub ui --settings` (a console command behind the identity gate, a console-role `ui` request with `settings: true`), may also raise: a non-ask mode or default, routing, `pi.auto_start`, removing a machine-local value, and undo. Its ticket marks the session for 15 minutes; afterwards the session continues as an ordinary one until its hour ends. `never-ask`, running or stored, needs the peer id as the confirmation (typed in the dashboard, `--yes` in the terminal); a stored never-ask default still waits for the console's `y` at each hub start (#242). No other path puts never-ask on disk: an undo that would restore it is refused (set it again with its confirmation), and `inherit` that would uncover a never-ask from the shared file needs the same confirmation. Tool approvals stay deny-only for Pi and local in every session. The unified dashboard forwards settings actions with ordinary authority.
2190
+ - A mode set from the dashboard or `ahub settings` is `changePermission` inside `permissionChange`, the console's path, with its refusals (not attached, unverified Claude hook, unattended Codex or Claude, a stop in progress) and its `permission_mode` event.
2191
+ - Preview before save: a routing change returns `route explain` for up to 20 open tasks under the policy in force and under the candidate (PII tasks are counted, not shown); a mode change returns the peer's bounds and what the mode grants.
2192
+ - Every change is announced in `hub.log` and open consoles with its source and recorded as a `settings` event: key, value before and after, source (`dashboard` or `terminal`), `undo`. Values are closed words, booleans, peer ids or route ids, never free text.
2193
+ - Control protocol: `settings_get`, `settings_set`, `settings_undo` and `settings_preview` are new console-role requests, and `ui` takes an optional `settings` flag and echoes it; an older hub answers the requests as unknown, and the CLI refuses a `ui` reply without the echo, so `PROTOCOL` stays 16. The `ui_snapshot` reply gains an optional `settings` object. Events schema stays 1.
2194
+ - `ahub settings` talks to the running hub (ponytail: a file-only path if setting a start-time value on a stopped hub is ever wanted).
2195
+
2196
+ ## Amendment: TUI by default, headless as opt-in (issue #269, part 2)
2197
+
2198
+ - `peers.<id>.start_mode` (`tui` or `headless`, for pi and codex; `tui` when unset) is the mode a start takes when nothing more specific was asked. Claude has only a TUI; Kimi (ACP) and the local worker have no hub-attached TUI and stay headless. The loader refuses a malformed `peers` block.
2199
+ - 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.
2200
+ - 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.
2201
+ - 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.
2202
+ - 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.
2203
+ - 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.
2204
+ - Settings: `peers.pi.start_mode` and `peers.codex.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.
2205
+ - 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.
2206
+ - 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`.
@@ -0,0 +1,309 @@
1
+ # One-shot output fixture (#284 phase 1)
2
+
3
+ Deterministic synthetic fixture at fixed `now=1800000000000`. Before uses the previous command formatting; after uses the pure renderers. This is automated output evidence, not a human terminal inspection.
4
+
5
+ ## status, 80 columns
6
+
7
+ Before:
8
+
9
+ ```text
10
+ hub pid 25893, control 127.0.0.1:12345, /project/agent-hub
11
+ claude idle queued 0 context 80% (fresh, claude_statusline, measured 2027-01-15T07:59:46.000Z)
12
+ codex busy queued 2 awaiting settlement 8d03b496-1111-2222-3333-444444444444, 3325f7a0-1111-2222-3333-444444444444 (adapter completion; Claude: reply or hub_delivery_done; task state is independent) held by needs_review abcdef12-1111-2222-3333-444444444444; ahub queue resolve abcdef12-1111-2222-3333-444444444444 --action completed|retry|discard --reason <text> (manual) last call: dgx/qwen-coder-large context 34% (fresh, codex_token_usage, measured 2027-01-15T07:59:00.000Z)
13
+ kimi idle queued 0 permission: ask-when-needed context 25% (fresh, acp_usage_update, measured 2027-01-15T07:58:00.000Z)
14
+ pi idle queued 0 tools-only: messages wait for hub_inbox; for pushes restart Claude with ahub claude context unknown (unknown)
15
+ tasks: 6 approved, 1 in_progress (ahub board)
16
+ ```
17
+
18
+ After:
19
+
20
+ ```text
21
+ agent-hub 0.12.22 pid 25893 control 127.0.0.1:12345
22
+ /project/agent-hub
23
+
24
+ PEER STATE MODE LINK Q PAUSE QUOTA MODEL CONTEXT
25
+ claude idle - attached - - 5h - 80% 14s ago
26
+ 23% claude_statu
27
+ in sline
28
+ 10m,
29
+ week
30
+ 49%
31
+ in
32
+ 5d09h
33
+ codex busy - attached 2 user - dgx/q 34% 1m ago
34
+ wen-c codex_token_
35
+ oder- usage
36
+ large
37
+ settling 8d03b496, 3325f7a0
38
+ held by needs_review abcdef12; ahub queue resolve
39
+ abcdef12-1111-2222-3333-444444444444 --action completed|retry|discard
40
+ --reason <text>
41
+ paused manual
42
+ kimi idle ask-when-needed attached - - - - 25% 2m ago
43
+ acp_usage_up
44
+ date
45
+ pi idle - tools-only - - - - unknown
46
+ tools-only messages wait for hub_inbox; for pushes restart Claude with ahub
47
+ claude
48
+
49
+ settling: the adapter has not confirmed these deliveries yet (Claude: reply or
50
+ hub_delivery_done). Task state is independent.
51
+
52
+ TASKS 6 approved, 1 in_progress (ahub board)
53
+ ```
54
+
55
+ ## board, 80 columns
56
+
57
+ Before:
58
+
59
+ ```text
60
+ #1 approved implement codex review:claude 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증
61
+ #2 in_progress implement kimi review:claude AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (after #1)
62
+ #3 proposed implement local review:claude [pii] (after #2) (ahub task show 3)
63
+ ```
64
+
65
+ After:
66
+
67
+ ```text
68
+ ID STATE OWNER REVIEWER CLASS AGE TITLE STAGE
69
+ #1 approved codex claude implement 1m 제주한라대학교 AI [####]
70
+ 도구를 활용한
71
+ 구현과 검증
72
+ 제주한라대학교 AI
73
+ 도구를 활용한
74
+ 구현과 검증
75
+ 제주한라대학교 AI
76
+ 도구를 활용한
77
+ 구현과 검증
78
+ 제주한라대학교 AI
79
+ 도구를 활용한
80
+ 구현과 검증
81
+ 제주한라대학교 AI
82
+ 도구를 활용한
83
+ 구현과 검증
84
+ 제주한라대학교 AI
85
+ 도구를 활용한
86
+ 구현과 검증
87
+ 제주한라대학교 AI
88
+ 도구를 활용한
89
+ 구현과 검증
90
+ 제주한라대학교 AI
91
+ 도구를 활용한
92
+ 구현과 검증
93
+ 제주한라대학교 AI
94
+ 도구를 활용한
95
+ 구현과 검증
96
+ 제주한라대학교 AI
97
+ 도구를 활용한
98
+ 구현과 검증
99
+ 제주한라대학교 AI
100
+ 도구를 활용한
101
+ 구현과 검증
102
+ 제주한라대학교 AI
103
+ 도구를 활용한
104
+ 구현과 검증
105
+ #2 in_progress kimi claude implement 2m AAAAAAAAAAAAAAAAA [##--]
106
+ AAAAAAAAAAAAAAAAA
107
+ AAAAAAAAAAAAAAAAA
108
+ AAAAAAAAAAAAAAAAA
109
+ AAAAAAAAAAAAAAAAA
110
+ AAAAAAAAAAAAAAAAA
111
+ AAAAAAAAAAAAAAAAA
112
+ AAAAAAAAAAAAAAAAA
113
+ AAAAAAAAAAAAAAAAA
114
+ AAAAAAAAAAAAAAAAA
115
+ AAAAAAAAAAAAAAAAA
116
+ AAAAAAAAAAAAAAAAA
117
+ AAAAAAAAAAAAAAAAA
118
+ AAAAAAAAAAAAAAAAA
119
+ AAAAAAAAAAAAAAAAA
120
+ AAAAAAAAAAAAAAAAA
121
+ AAAAAAAAAAAAAAAAA
122
+ AAAAAAAAAAA
123
+ after #1
124
+ #3 proposed waiting local claude implement 30s [pii] after #2 [#---]
125
+ (ahub task show
126
+ 3)
127
+
128
+ 3 tasks: 1 approved, 1 in_progress, 1 proposed
129
+ ```
130
+
131
+ ## budget, 80 columns
132
+
133
+ Before:
134
+
135
+ ```text
136
+ claude PAUSED: quota threshold, resumes in 10m00s
137
+ week 49% resets in 129h00m [claude_statusline, 14s ago]
138
+ codex
139
+ week 8% [codex_rate_limits, 60s ago, STALE]
140
+ ```
141
+
142
+ After:
143
+
144
+ ```text
145
+ PEER WINDOW USED RESETS SOURCE MEASURED STATE
146
+ claude week 49% in 5d09h claude_statusline 14s ago fresh
147
+ codex week 8% - codex_rate_limits 1m ago stale
148
+ paused claude quota threshold; resumes in 10m
149
+ ```
150
+
151
+ ## doctor, 80 columns
152
+
153
+ Before:
154
+
155
+ ```text
156
+ ok bun 1.4.2
157
+ -- local fixed_model vllm/large-model is not served by the gateway
158
+ -- pi mlx error: Unable to connect. Is the computer able to access the URL?
159
+ ? capture: codex unknown, dot ai memory status is unavailable
160
+ ```
161
+
162
+ After:
163
+
164
+ ```text
165
+ Tools
166
+ LEVEL CHECK FINDING
167
+ ok bun 1.4.2
168
+
169
+ Models
170
+ LEVEL CHECK FINDING
171
+ fail local fixed_model vllm/large-model is not served by the gateway
172
+ warn pi mlx error: Unable to connect. Is the computer able to
173
+ access the URL?
174
+
175
+ Memory
176
+ LEVEL CHECK FINDING
177
+ unknown capture: codex unknown, dot ai memory status is unavailable
178
+
179
+ 1 failure, 1 warning, 1 ok, 1 unknown
180
+ ```
181
+
182
+ ## status, 120 columns
183
+
184
+ Before:
185
+
186
+ ```text
187
+ hub pid 25893, control 127.0.0.1:12345, /project/agent-hub
188
+ claude idle queued 0 context 80% (fresh, claude_statusline, measured 2027-01-15T07:59:46.000Z)
189
+ codex busy queued 2 awaiting settlement 8d03b496-1111-2222-3333-444444444444, 3325f7a0-1111-2222-3333-444444444444 (adapter completion; Claude: reply or hub_delivery_done; task state is independent) held by needs_review abcdef12-1111-2222-3333-444444444444; ahub queue resolve abcdef12-1111-2222-3333-444444444444 --action completed|retry|discard --reason <text> (manual) last call: dgx/qwen-coder-large context 34% (fresh, codex_token_usage, measured 2027-01-15T07:59:00.000Z)
190
+ kimi idle queued 0 permission: ask-when-needed context 25% (fresh, acp_usage_update, measured 2027-01-15T07:58:00.000Z)
191
+ pi idle queued 0 tools-only: messages wait for hub_inbox; for pushes restart Claude with ahub claude context unknown (unknown)
192
+ tasks: 6 approved, 1 in_progress (ahub board)
193
+ ```
194
+
195
+ After:
196
+
197
+ ```text
198
+ agent-hub 0.12.22 pid 25893 control 127.0.0.1:12345
199
+ /project/agent-hub
200
+
201
+ PEER STATE MODE LINK Q PAUSE QUOTA MODEL CONTEXT
202
+ claude idle - attached - - 5h 23% in 10m, week 49% in - 80% 14s ago
203
+ 5d09h claude_statu
204
+ sline
205
+ codex busy - attached 2 user - dgx/qwen-coder-large 34% 1m ago
206
+ codex_token_
207
+ usage
208
+ settling 8d03b496, 3325f7a0
209
+ held by needs_review abcdef12; ahub queue resolve abcdef12-1111-2222-3333-444444444444 --action
210
+ completed|retry|discard --reason <text>
211
+ paused manual
212
+ kimi idle ask-when-needed attached - - - - 25% 2m ago
213
+ acp_usage_up
214
+ date
215
+ pi idle - tools-only - - - - unknown
216
+ tools-only messages wait for hub_inbox; for pushes restart Claude with ahub claude
217
+
218
+ settling: the adapter has not confirmed these deliveries yet (Claude: reply or hub_delivery_done). Task state is
219
+ independent.
220
+
221
+ TASKS 6 approved, 1 in_progress (ahub board)
222
+ ```
223
+
224
+ ## board, 120 columns
225
+
226
+ Before:
227
+
228
+ ```text
229
+ #1 approved implement codex review:claude 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증 제주한라대학교 AI 도구를 활용한 구현과 검증
230
+ #2 in_progress implement kimi review:claude AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA (after #1)
231
+ #3 proposed implement local review:claude [pii] (after #2) (ahub task show 3)
232
+ ```
233
+
234
+ After:
235
+
236
+ ```text
237
+ ID STATE OWNER REVIEWER CLASS AGE TITLE STAGE
238
+ #1 approved codex claude implement 1m 제주한라대학교 AI 도구를 활용한 구현과 검증 [####]
239
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
240
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
241
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
242
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
243
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
244
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
245
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
246
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
247
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
248
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
249
+ 제주한라대학교 AI 도구를 활용한 구현과 검증
250
+ #2 in_progress kimi claude implement 2m AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA [##--]
251
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
252
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
253
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
254
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
255
+ AAAAAAAAAAAAAAA after #1
256
+ #3 proposed waiting local claude implement 30s [pii] after #2 (ahub task show 3) [#---]
257
+
258
+ 3 tasks: 1 approved, 1 in_progress, 1 proposed
259
+ ```
260
+
261
+ ## budget, 120 columns
262
+
263
+ Before:
264
+
265
+ ```text
266
+ claude PAUSED: quota threshold, resumes in 10m00s
267
+ week 49% resets in 129h00m [claude_statusline, 14s ago]
268
+ codex
269
+ week 8% [codex_rate_limits, 60s ago, STALE]
270
+ ```
271
+
272
+ After:
273
+
274
+ ```text
275
+ PEER WINDOW USED RESETS SOURCE MEASURED STATE
276
+ claude week 49% in 5d09h claude_statusline 14s ago fresh
277
+ codex week 8% - codex_rate_limits 1m ago stale
278
+ paused claude quota threshold; resumes in 10m
279
+ ```
280
+
281
+ ## doctor, 120 columns
282
+
283
+ Before:
284
+
285
+ ```text
286
+ ok bun 1.4.2
287
+ -- local fixed_model vllm/large-model is not served by the gateway
288
+ -- pi mlx error: Unable to connect. Is the computer able to access the URL?
289
+ ? capture: codex unknown, dot ai memory status is unavailable
290
+ ```
291
+
292
+ After:
293
+
294
+ ```text
295
+ Tools
296
+ LEVEL CHECK FINDING
297
+ ok bun 1.4.2
298
+
299
+ Models
300
+ LEVEL CHECK FINDING
301
+ fail local fixed_model vllm/large-model is not served by the gateway
302
+ warn pi mlx error: Unable to connect. Is the computer able to access the URL?
303
+
304
+ Memory
305
+ LEVEL CHECK FINDING
306
+ unknown capture: codex unknown, dot ai memory status is unavailable
307
+
308
+ 1 failure, 1 warning, 1 ok, 1 unknown
309
+ ```