@staix/agent-hub 0.12.26 → 0.12.28

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,19 @@
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.28
6
+
7
+ - Recover an offline Pi owner with person-selected preserved history via `ahub pi --replace-session --session-file`, retaining prior identity metadata and queued work while refusing live owners and unsettled effects (#403).
8
+
9
+ - Tell a pull-only session (a Kimi TUI, a Claude session without pushes) that important messages are waiting: the hub sends an advisory count over its control link and every hub tool result but `hub_inbox` ends with it until a read drains them; `hub_inbox` replies also carry the important count. Make `scripts/check.sh` take a heavy-job slot shared by every worktree of the repo, so a second gate waits (bounded by `AHUB_CHECK_LOCK_WAIT_S`) and names the holder instead of overlapping (#398).
10
+
11
+ ## 0.12.27
12
+
13
+ - Refine the dashboard Office view with an intrinsic 12 px/unit scale, 1 px non-scaling hairlines and padded room cards in flat and isometric themes, and un-nest the desk More control into a separately reconciled sibling button so screen readers can reach it (#394).
14
+ - Shrink the floating pet window to a compact 150x150 panel with the original pixel art in deeper state colors and a configurable base palette (`pet.palette`, or 1/2/3/4 while the window is key: blue, teal, violet, ochre) with a matching rim line so it reads on light backgrounds, reveal the status pill and message composer only while the pointer is over the character, and drop the pill shadow (#391 follow-up).
15
+ - Split the local dashboard into cookie-backed views, compact task cards with shared dialog/column details, and exact-id attention badges; move character presentation to the standalone pet window (#391 Task A).
16
+ - Add an opt-in macOS floating pet window with built-in pixels, counts-only local state, a console message pencil, saved position and signed process ownership; keep terminal pet reads available to agent shells (#391 Task B).
17
+
5
18
  ## 0.12.26
6
19
 
7
20
  - Import claude-mem observations under a checkout-independent identity, so the same observation is one entry on every machine after sync however the checkouts differ; scan the source database project-aware with a persisted machine-local cursor (`--restart` re-scans), so unrelated projects cannot starve the requested one; reconcile legacy duplicate imports with `ahub memory dedupe` through append-only supersede operations (#380).
package/README.md CHANGED
@@ -4,7 +4,7 @@ Native multi-agent hub for one developer's machine: Claude Code, Codex, Kimi Cod
4
4
  hub-owned local-LLM worker, with optional Qwen Code and Jcode support, collaborate as peers in independent project directories, with
5
5
  task-aware model routing (Switchyard) in front of a self-hosted gateway (OmniRoute).
6
6
 
7
- Status: 0.12.26, control protocol 16. Durable delivery records distinguish queued
7
+ Status: 0.12.28, control protocol 16. Durable delivery records distinguish queued
8
8
  work from uncertain execution. The [smoke checklist](docs/smoke.md) records
9
9
  verified paths and remaining prerequisites.
10
10
 
@@ -33,7 +33,7 @@ cd <your project> && ahub init && ahub up
33
33
  Or install the same version from GitHub:
34
34
 
35
35
  ```bash
36
- bun add -g github:STAIxBWLB/agent-hub#v0.12.26 && ahub setup
36
+ bun add -g github:STAIxBWLB/agent-hub#v0.12.28 && ahub setup
37
37
  ```
38
38
 
39
39
  The installed commands remain `ahub` and `agent-hub`.
@@ -81,7 +81,7 @@ an N-peer message bus and attaches each agent through its native control surface
81
81
  | --- | --- | --- | --- |
82
82
  | Claude Code | channel plugin push (`notifications/claude/channel`) under `ahub claude`; otherwise tools-only, read with `hub_inbox` | MCP tools | queued by Claude Code, grouped on next turn; tools-only: queued at the hub |
83
83
  | Codex | app-server `turn/start` (idle) / `turn/steer` (busy) | proxy intercepts `item/agentMessage` | steer or queue |
84
- | Kimi Code | ACP `session/prompt` | ACP `session/update` stream | queue (`turn.agent_busy`) |
84
+ | Kimi Code | TUI: pull with `hub_inbox`; headless: ACP `session/prompt` | TUI: `hub_send`; headless: ACP `session/update` stream | TUI: hub queue with important-count hints; headless: queue (`turn.agent_busy`) |
85
85
  | Pi | RPC or managed native TUI extension | hub-approved tools | queued until `agent_settled` |
86
86
  | Local worker | hub-native agent loop | hub-native | hub-owned |
87
87
 
@@ -14,6 +14,7 @@ Scope: delivery, digests and condensation, reply addressing, priority and limits
14
14
  - A reply is addressed, never broadcast. An adapter that answers a delivery passes `to: replyAudience(envs)`; anything else with an `inReplyTo` inherits that envelope's sender. Leaving `to` empty fans the message out to every peer.
15
15
  - `digest` is not a peer: addressing a reply at the envelopes the peer was handed sends it nowhere once a delivery was condensed. The bus resolves `digest` back through `lastDelivery.originals`; anything else that reads a reply's `to` has to do the same.
16
16
  - A pull-only peer (a Claude session without channel pushes or Kimi in its own TUI) is never drained; `Bus.pull` hands it what one push would take. Drain and pull share `held()` (recovery, an uncertain delivery, any pause, the lifecycle `deliveryHeld` fence) and `remember()` (so `reply_to` resolves), and a drain rechecks `deliverable()` after condensing because such a session can take the peer meanwhile. A pull is journaled `completed` in the same transaction as the bus snapshot, never `accepted`; a failed one puts back only that peer's queue and preface and never reloads the snapshot.
17
+ - The `onImportantQueued` nudge for a pull-only peer (#398) is advisory: it never dequeues, steers, journals or settles, and a throw from it never stops an enqueue.
17
18
  - `resolveDelivery` mutates queues in place; a refused or failed resolution puts back only that peer's queue and preface and keeps every pause. Never reload a snapshot on a live bus.
18
19
  - `restore` takes queued work from the journal, never from the committed snapshot once the journal has state (an empty journal, revision 0, is seeded from the commit): from the commit only an empty queue key is carried over, for an offline peer whose key no delivery ever persisted (#234). Never resurrect a non-empty queue from it.
19
20
  - The journal's owner check (`rejectLiveOwner` in `delivery-journal.ts`) judges the manifest pid with `processLiveness` and its `pidSignature`, never a bare probe (rule: `daemon.md`).
@@ -14,5 +14,6 @@ Scope: tests, fakes and the test gate. Read before adding or changing a test or
14
14
  - A request that a test expects a stop or shutdown to cut gets its rejection handler where the request is created, never after the awaited teardown: a late `catch` races the reset and the run fails on an unhandled rejection.
15
15
  - Run `bun scripts/seeded-check.ts` before reporting the seeded-guard gate complete. Keep the `scripts/seeds.json` cases sequential; require the same named test green before its seeded assertion fails. Seed rot, survivors, setup/compiler failures, timeout/watchdog termination and process leaks fail the gate. Never run the full seed runner from a unit test or mutate the source checkout; inspect preserved failed fixtures before removing them. CI runs this in the required Linux `seeded guards` job after the ordinary check, separately from `scripts/check.sh`.
16
16
 
17
+ - `scripts/check.sh` holds the repo's heavy-job slot (`<git common dir>/ahub-heavy.lock`, #398) for the whole run, so a test never runs the real gate; `test/check-lock.test.ts` runs a copy in a scratch repo.
17
18
  - The whole-suite watchdog is `AHUB_CHECK_HANG_S` (default in `scripts/check.sh`, hosted macOS value in `.github/workflows/check.yml`; #378, macOS CI hit it while tests still passed): raise it only when the suite was still progressing at the cutoff, never for a hung test, and keep the per-test 20 s timeout.
18
19
  - A seed's named test must run on Linux, where the seeded job runs: a darwin-only test reports no result there and fails the job (#382). Cover a macOS-only protection with its regression test instead.
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.12.26 and control protocol 16. Live verification
3
+ This guide describes ahub 0.12.28 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
  ## Command output
@@ -156,8 +156,8 @@ status read it says loading; later failures preserve the prior count with a noti
156
156
  Panels retain their existing full-board refresh and loading/error handling.
157
157
 
158
158
  The dashboard Task board shows the same shared public-board progress model,
159
- including state counts, waiting, stacked segments and a labeled four-stage track
160
- per row. Filtering rows does not filter overall progress. The existing signature guard
159
+ including overall state counts, waiting and stacked segments; each task detail
160
+ retains its labeled four-stage track. Filtering rows does not filter overall progress. The existing signature guard
161
161
  updates the live region only when progress changes. The stacked SVG fills the
162
162
  section width; its text legend carries every state count. Theme offers System,
163
163
  Light and Dark; it is applied before first paint and stored in a host-scoped loopback cookie
@@ -170,8 +170,8 @@ is tracked in `docs/smoke.md` and remains unverified until performed.
170
170
  The dashboard Office section (#264, phase 1 of 4) draws the same snapshot as a
171
171
  static flat office: one desk per peer in a stable slot (claude, codex, kimi, pi,
172
172
  local, then others by id; a peer going offline dims its desk and never moves
173
- another), rooms for the board columns in a fixed order (To Do, Work floor,
174
- Review, Done shelf as a count only), at most 8 cards per room with "+N more"
173
+ another), rooms for the board columns in a fixed order (To do, Work floor,
174
+ Review, Done as a count only), at most 8 cards per room with "+N more"
175
175
  filtering the task list to that state, and at most 12 desks with "+N peers".
176
176
  Work-floor cards sit at the owner's desk, marked for rework; a busy peer's task
177
177
  bubble names its task only when it has exactly one in progress. Every peer
@@ -1289,7 +1289,9 @@ reporting, and shares results explicitly with `hub_send`. Status, the console an
1289
1289
  the dashboard show `pull-only`, the waiting count and the last inbox read. Messages
1290
1290
  and important markers do not push or interrupt it. There is no hub busy state,
1291
1291
  watchdog, console approval, hub permission mode, context reading, automatic quota
1292
- pause/handoff, shared final answer or session resume. Read quota and approve tools
1292
+ pause/handoff, shared final answer or session resume. When important messages are
1293
+ waiting, every other hub tool result ends with a line saying how many, so the model
1294
+ reads `hub_inbox` before starting long local work (#398). Read quota and approve tools
1293
1295
  in Kimi's own TUI; a person can pause the hub queue by hand. End the native terminal
1294
1296
  session there, not with `ahub stop kimi`.
1295
1297
 
@@ -1619,21 +1621,21 @@ section is what they run.
1619
1621
 
1620
1622
  Upgrade running projects with the target release's own coordinator. It accepts
1621
1623
  a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
1622
- 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.26), and only
1624
+ 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.28), and only
1623
1625
  a target on its own protocol, so the target's coordinator fits every supported
1624
1626
  source and carries every recovery fix released up to it. Protocol 8 and older
1625
1627
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
1626
1628
  project directory, without replacing the global CLI first:
1627
1629
 
1628
1630
  ```bash
1629
- bunx --package @staix/agent-hub@0.12.26 ahub upgrade --to 0.12.26 --dry-run
1630
- bunx --package @staix/agent-hub@0.12.26 ahub upgrade --to 0.12.26 --yes
1631
+ bunx --package @staix/agent-hub@0.12.28 ahub upgrade --to 0.12.28 --dry-run
1632
+ bunx --package @staix/agent-hub@0.12.28 ahub upgrade --to 0.12.28 --yes
1631
1633
  ```
1632
1634
 
1633
1635
  | Running now | Coordinator to use |
1634
1636
  | --- | --- |
1635
1637
  | 0.6.x (protocol 9) | the target's, through `bunx` as above |
1636
- | 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.26 (protocol 16) | the target's, through `bunx` as above |
1638
+ | 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.28 (protocol 16) | the target's, through `bunx` as above |
1637
1639
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
1638
1640
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
1639
1641
 
@@ -1665,14 +1667,14 @@ projects first:
1665
1667
 
1666
1668
  ```bash
1667
1669
  ahub restart --dry-run
1668
- ahub upgrade --to 0.12.26 --dry-run
1670
+ ahub upgrade --to 0.12.28 --dry-run
1669
1671
  ```
1670
1672
 
1671
1673
  Apply only after reviewing the plan:
1672
1674
 
1673
1675
  ```bash
1674
1676
  ahub restart --yes
1675
- ahub upgrade --to 0.12.26 --yes
1677
+ ahub upgrade --to 0.12.28 --yes
1676
1678
  ahub recovery status [<operation-id>]
1677
1679
  ahub recovery resume [<operation-id>]
1678
1680
  ahub recovery abort [<operation-id>]
@@ -2354,6 +2356,13 @@ test files twenty times on macOS, in its own concurrency group; it does not repl
2354
2356
  `scripts/check.sh` stops a suite still running after `AHUB_CHECK_HANG_S` seconds
2355
2357
  (600 by default); hosted macOS CI sets 780 inside the unchanged 15-minute job
2356
2358
  limit (#378).
2359
+ The gate holds `<git common dir>/ahub-heavy.lock` across every worktree of a
2360
+ repository. A second gate waits up to `AHUB_CHECK_LOCK_WAIT_S` (1800 seconds by
2361
+ default; 0 fails immediately) and names the holder. A holder whose PID is gone
2362
+ is reaped. This filesystem slot works without a running hub; it covers this
2363
+ gate, not arbitrary build commands. PID reuse can retain a stale slot, and two
2364
+ waiters reaping the same dead owner can race; broader scheduling remains an
2365
+ operator responsibility (#398).
2357
2366
 
2358
2367
  ## Preview initialization and native launch
2359
2368
 
@@ -2453,6 +2462,31 @@ terminal exits, a requested stop (`ahub stop pi`), requested shutdown and
2453
2462
  recovery operations suppress this restart. Crash recovery's #66
2454
2463
  recorded-session/fresh-start choices are unchanged.
2455
2464
 
2465
+ ### Explicit Pi history replacement
2466
+
2467
+ If an offline Pi owner has no verified persisted resume state, inspect the old
2468
+ session and any partial work, then select a preserved file explicitly:
2469
+
2470
+ ```sh
2471
+ ahub pi --replace-session --session-file <managed-file>
2472
+ ```
2473
+
2474
+ The file must be inside this project's `.agenthub/state/pi-sessions/` and its
2475
+ session header must identify the same project. The selected session keeps its
2476
+ existing identity. This is a person-only recovery action; default restart never
2477
+ selects another session or infers that missing history was empty. A session id
2478
+ alone is insufficient. Add `--headless` to run the selected history in background.
2479
+ `--print-command` validates flags without checking the live owner or changing it.
2480
+
2481
+ Replacement requires an existing offline owner whose native process is proven
2482
+ gone, with settled tools, receipts and approvals, outside a recovery operation.
2483
+ An unclaimed pending TUI launch uses the ordinary restart path instead. Before
2484
+ revoking the old owner, the hub durably saves private previous/selected session
2485
+ metadata in `pi-session-replacements/` beside `pi-sessions/`. Failed validation or
2486
+ record storage leaves the old owner intact. All session files, queued work and
2487
+ journal records remain. Uncertain effects are never retried by this selection;
2488
+ inspect and reconcile them before continuing.
2489
+
2456
2490
  ### Pi/local unanswered approvals
2457
2491
 
2458
2492
  An expired Pi/local request says "approval expired: no person answered". The
@@ -2522,8 +2556,10 @@ An idle Pi with no message/file yet records its live empty-session proof before
2522
2556
  stopping, so its next start resumes that identity. A Pi stopped after its first
2523
2557
  prompt was accepted but before history was persisted is not treated as empty:
2524
2558
  its delivery is held, and another start refuses until resume history is verified.
2525
- Inspect and settle the held delivery before deliberately replacing that unpersisted
2526
- session by restarting the hub; the stop never silently chooses a fresh session.
2559
+ Inspect and settle the held delivery before selecting preserved history through
2560
+ [explicit Pi history replacement](#explicit-pi-history-replacement). The stop
2561
+ never silently chooses a fresh session; replacement requires an offline, settled
2562
+ owner and a validated managed session file.
2527
2563
 
2528
2564
  A pull-only Kimi TUI has no controlled restore or resume. While it is attached,
2529
2565
  upgrade/restart planning stops before effects and asks the person to end Kimi in
@@ -2879,20 +2915,36 @@ nor drawing a character changes authority, config files, task state or polling.
2879
2915
  Explicit `ahub pet` and text status use the configured line face; prompt tokens
2880
2916
  remain character-independent. Native glyph width and tmux are unverified.
2881
2917
 
2882
- ## Dashboard attention character (#280)
2918
+ ## Dashboard views and attention (#391, replaces the #280 in-page character)
2919
+
2920
+ Top buttons select Board, Office, Stream, Agents, Message, Memory or Settings
2921
+ within the same page. The selected view and board column focus use the shared
2922
+ host-scoped cookie helper; the URL fragment remains reserved for the session
2923
+ ticket. Sections are hidden rather than fetched separately. Projects and
2924
+ Benchmarks live under Agents alongside peers, approvals and usage budgets.
2925
+ Office mode stays an explicit Off/Flat/Isometric preference inside Office.
2926
+
2927
+ Board cards show id/title, priority, owner dot and overdue/check markers.
2928
+ Click or Enter opens a native detail dialog with the full stage/dependency,
2929
+ release/date, SDLC, detail and GitHub content plus existing Pull/Return/Assign
2930
+ controls. Snapshot changes refresh the dialog; project reset or a task leaving
2931
+ the snapshot closes it. Expand on a column shows one wide list of full details;
2932
+ Back returns to compact cards. State/agent/release filters still apply.
2933
+
2934
+ The shared pure needs model now drives the nav attention badge and existing
2935
+ action-count tab title. Agents has a live pending-approval count even when
2936
+ attention metadata is unavailable. Activation switches to the matching view
2937
+ and focuses only the exact visible approval/task id; missing or filtered rows
2938
+ receive fixed structural guidance, with no peer or first-row fallback. No
2939
+ workflow action is issued and no filter is changed by attention navigation.
2940
+ Invalid/legacy attention hides its badge and restores the base title.
2941
+
2942
+ The in-page pet, its SVG and character/pixel payload are removed. The standalone
2943
+ macOS window owns character presentation. Existing snapshot poll, session,
2944
+ request types and CSP hashes remain; no route, request or timer is added.
2945
+ Browser screenshots/CSP-console verification are separate manual evidence on
2946
+ the implementation PR; VM/HTTP fixtures do not establish human visual results.
2883
2947
 
2884
- The dashboard header draws the configured built-in character independently of
2885
- Office. It uses the same structural needs model as status and the console. The
2886
- accessible button names the character, state and action/watch counts; the tab
2887
- title shows the action count. Activating the button scrolls and focuses the exact
2888
- visible approval/task/peer row, or its related section with fixed id-only guidance
2889
- when the row is filtered or unavailable. It performs no action and changes no
2890
- filters. Legacy snapshots without attention keep the widget hidden.
2891
-
2892
- Only a live approval alternates the two waiting poses once per second through
2893
- CSS. `pet.blink:false` and reduced motion retain a static pose. No new timer,
2894
- request, image protocol or character-file load is added. Project reset clears
2895
- the widget and title. Native/browser visual validation remains unverified.
2896
2948
  With mini/full characters, active console notices remain visible away from the
2897
2949
  character: panel body rows show the bounded wrapped notice, and stream mode
2898
2950
  prints each fresh notice once through the existing local stream. Footer prose
@@ -2943,3 +2995,43 @@ A later issue lookup or profile-preparation failure also returns the IDs confirm
2943
2995
  Release cut audits return exact counts with at most 32 example IDs per category; the dashboard states how many IDs are shown when truncated. Scope grouping and indexed approval dates avoid scanning each task history for every release. Full audit pagination remains a future extension.
2944
2996
 
2945
2997
  Release cards include task states in their refresh key, so completion of an outside prerequisite updates blocked counts even when the release and audit preview are unchanged.
2998
+
2999
+ ## Standalone macOS pet window (#391)
3000
+
3001
+ From a person's terminal, `ahub pet --window` starts one floating, transparent
3002
+ JXA panel per project; `ahub pet --window --close` closes it. Other platforms
3003
+ print unsupported and retain `ahub pet` / `ahub pet --short`. An agent-marked
3004
+ shell may read the terminal pet but cannot start or close the window.
3005
+
3006
+ Set `pet.window: true` in `.agenthub/config.json` or `config.local.json` to start
3007
+ it with `ahub up`. A window-only local opt-in preserves inherited `pet:false`
3008
+ for terminal/status/dashboard presentation; explicit local character options
3009
+ retain their existing presentation override behavior. The
3010
+ window uses the configured built-in character, full pixel frame and blink
3011
+ choice. Its body opens `ahub ui`; the pencil opens an inline console message
3012
+ box (Enter sends through `ahub say`, Esc cancels). Drag the body to move it;
3013
+ `pet-window-pos.json` remembers the position, with an on-screen fallback if a
3014
+ saved display has disappeared. The panel joins all Spaces without a Dock icon.
3015
+ The 150x150 panel keeps the status pill and message composer hidden until the
3016
+ pointer is over the character. Deep state colors and a dark rim shadow keep the
3017
+ pixel silhouette readable on light backgrounds. `pet.palette` selects the base
3018
+ color: teal (default, b), blue (a), violet (c) or ochre (d), each with its own
3019
+ rim line; the waiting/blocked/review/offline colors stay fixed. While the
3020
+ window is key, 1/2/3/4 switch blue/teal/violet/ochre and save the choice.
3021
+ From a terminal, `ahub pet --palette <blue|teal|violet|ochre>` writes the
3022
+ choice to `pet.palette` in `.agenthub/config.local.json`.
3023
+ While working, the pixel art bobs in a 0.7-second stepped CSS loop; reduced-motion preferences keep it still.
3024
+
3025
+ A detached Bun host refreshes `pet-view.json` once per second using `readPet`
3026
+ and `loadPetConfig`. The file contains only state/counts/character/blink and
3027
+ built-in pixels. It uses no dashboard session or HTTP listener. An unavailable
3028
+ hub displays Offline for two seconds, then the panel and host exit. Closing
3029
+ JXA exits the host too. `pet-window.pid` holds the host's process signature;
3030
+ an uncertain owner is retained rather than signalled. A stale owner requires
3031
+ an explicit `--close` before restarting; a malformed pidfile requires operator
3032
+ inspection. A send failure is unconfirmed: check the stream before retrying.
3033
+
3034
+ Native transparency/floating and the bridge were verified in the macOS spike.
3035
+ Real keystrokes, dragging and persistence, Spaces, message delivery and body
3036
+ click need the manual PR checklist before merge; automated fixtures do not
3037
+ establish those native results.
@@ -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.26
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.12.28
21
21
  ahub setup
22
22
  ```
23
23
 
package/docs/security.md CHANGED
@@ -109,7 +109,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
109
109
 
110
110
  - `ahub ui` uses the authenticated console control connection to start an ephemeral HTTP listener on `127.0.0.1`. No UI listener exists before that command. The control and Codex proxy ports continue to refuse every `Origin` header.
111
111
  - The daemon issues a cryptographically random, single-use bootstrap ticket valid for 60 seconds. The CLI opens the dashboard with that ticket in the URL fragment, never a query string or the control token. The static page removes the fragment immediately and exchanges it with a same-origin POST. Tickets are consumed once; expired tickets cannot create sessions.
112
- - The exchange creates a separate random session, valid for one hour without renewal, in an `HttpOnly; SameSite=Strict; Path=/` cookie named for this listener's port. Sessions and tickets live only in memory and die with the daemon. The page itself keeps one more cookie, `agent-hub-theme` (issue #246): script-set, `SameSite=Strict`, kept for a year for the whole `127.0.0.1` host, and read back only as one of `system`, `light` or `dark`. A second script-set cookie, `agent-hub-office` (issue #264), keeps the Office view choice under the same attributes and is read back only as `off`, `flat` or `isometric`. HTTP is loopback-only; the cookie is not a substitute for the origin checks.
112
+ - The exchange creates a separate random session, valid for one hour without renewal, in an `HttpOnly; SameSite=Strict; Path=/` cookie named for this listener's port. Sessions and tickets live only in memory and die with the daemon. The page itself keeps one more cookie, `agent-hub-theme` (issue #246): script-set, `SameSite=Strict`, kept for a year for the whole `127.0.0.1` host, and read back only as one of `system`, `light` or `dark`. A second script-set cookie, `agent-hub-office` (issue #264), keeps the Office view choice under the same attributes and is read back only as `off`, `flat` or `isometric`. The #391 view split adds two more script-set cookies under the same attributes: `agent-hub-view`, read back only as one of the seven view names, and `agent-hub-column-focus`, read back only as one of the five board columns or empty. HTTP is loopback-only; the cookie is not a substitute for the origin checks.
113
113
  - Every request must have the exact listener Host. Every data or action request, including the ticket exchange and snapshot polling, must be POST with the exact listener Origin and JSON content type. Foreign and missing origins, missing or expired sessions, oversized bodies and unknown actions are rejected. There is no CORS support. The only unauthenticated GET is the fixed, data-free HTML shell. No files or paths are served dynamically.
114
114
  - Responses are non-cacheable, cannot be framed, suppress referrers, and use a content security policy restricting scripts/styles to the shipped inline content and connections to this origin. Browser text is rendered with textContent, never interpreted as HTML.
115
115
  - The browser receives a bounded redacted event stream, peer states and queue counts, public task views, budget windows, pending approvals, the settings rows (closed values and ids, the file each comes from) and each startable peer's start plan (issue #269), benchmark run summaries (issue #251): the newest runs in `~/.agenthub/bench/`, whichever project on this machine ran them, by suite name, arm label, state, the task id in progress, counts, tokens and times, never suite text, and the office view's optional fields (issue #264): each peer's open turn (start time, attributed task id, attribution kind), a `checking` flag on tasks whose check is running, and warning `links` of overlapping, conflicting or cohort tasks (task and peer ids only, never paths, symbols or reasons; a PII task never joins a link, and a private or PII-attributed turn exposes no task id). Private envelope bodies and PII task text never cross this endpoint. Local-worker and Pi approval titles can contain PII, so their details remain terminal-only; the dashboard identifies the request and directs the operator to `ahub tail` before allowing it. Budget checkpoint summaries are also omitted.
@@ -226,11 +226,14 @@ owner/reviewer ids, approval ids and numeric times, peer delivery states and
226
226
  budget pause times. It is assembled from the same allowlisted input as status;
227
227
  task text, plans, approval prose, error text and paths are excluded. This input
228
228
  is supplied only inside console-authorized dashboard snapshots, never ordinary
229
- status replies or status.json. The pet creates trusted inline SVG pixel nodes
230
- from built-in literal frames with existing theme colours, without HTML insertion,
231
- inline handlers, style attributes or external assets. Existing CSP hashing,
232
- headers, origin and session authorization remain in force. Clicking scrolls and
233
- focuses only; it cannot grant permissions or invoke a mutation.
229
+ status replies or status.json. The #391 dashboard projects this input into nav
230
+ and tab-title counts; the in-page pet and its character/pixel injection are gone.
231
+ Attention navigation switches a local view and focuses an exact visible row id,
232
+ with structural guidance when missing. It cannot grant permissions or invoke a
233
+ mutation. Compact-card detail/dialog controls retain the existing authenticated
234
+ workflow actions and project/instance fencing. Existing CSP hashing, headers,
235
+ origin and session authorization remain in force; data uses text/DOM nodes,
236
+ without HTML insertion, inline handlers or external assets.
234
237
 
235
238
 
236
239
  ## Hub-owned structured memory (#258)
@@ -251,3 +254,28 @@ refused; unknown visibility proceeds with a warning for the configured remote.
251
254
  Short-term segments, machine identity and the derived index are excluded from
252
255
  sync. Suggested graph edges are off by default and are not evidence until a
253
256
  person confirms them. Operational details and limits: [hub memory](memory.md).
257
+
258
+ ## Standalone pet window (#391)
259
+
260
+ The macOS window reads local files only. Its detached host reuses `readPet`,
261
+ including signed-daemon liveness and recovery-lock handling, and publishes an
262
+ atomic counts-only `pet-view.json` (state, act/watch, known character, blink,
263
+ palette from the closed list, built-in pixel frame). It excludes item ids,
264
+ task titles/text, refs, paths,
265
+ notices and approval prose. Missing or unconfirmed status produces only
266
+ `{offline:true}`. Process and position files are separate from this view.
267
+
268
+ The local WKWebView has a deny-by-default CSP, with only its shipped inline
269
+ script/style allowed and network connections forbidden. Text never becomes
270
+ HTML or executable source. The retained JXA bridge accepts fixed drag/send/
271
+ palette/open/close operations; `NSTask` invokes the host-supplied Bun/CLI/project argv
272
+ and appends `say`, `--` and a single message argument, or `pet`, `--palette`
273
+ and one closed palette name. It does not evaluate a
274
+ shell string. Message text is never logged by the window/host; command stdout
275
+ and stderr go to the null device. `ahub say` keeps its existing console-message
276
+ authority and recording behavior, and `ahub ui` opens its existing session.
277
+ The CLI restricts `pet --window` (including close) to console identity, while
278
+ plain terminal pet reads and `pet --palette` remain available to agent shells;
279
+ the palette write merges only the closed name into `.agenthub/config.local.json`,
280
+ validated and replaced by temp file and rename. Signed pidfiles
281
+ fence instance ownership and close; unknown owners are never signalled.
@@ -710,7 +710,11 @@ fragment ticket for an HttpOnly, SameSite=Strict browser session. The control li
710
710
  retains its blanket Origin refusal. The browser receives no control token.
711
711
 
712
712
  One static HTML file, without a build step or runtime dependency, polls public
713
- snapshots once per second. The latest 200 redacted bus events are retained only
713
+ snapshots once per second. #391 Task A keeps this `/` surface and poll while
714
+ splitting its sections into Board/Office/Stream/Agents/Message/Memory/Settings
715
+ views with a cookie-backed selection. Compact task cards open a native dialog;
716
+ one `taskDetail` function renders both the dialog and expanded-column cards.
717
+ Filters, exact-id attention navigation and existing workflow controls remain. The latest 200 redacted bus events are retained only
714
718
  once the listener starts. The API exposes only permission answers, peer
715
719
  pause/resume, console messages, task proposal and assignment. All task changes
716
720
  use `Tasks`; budget pauses keep the terminal's override requirement.
@@ -731,7 +735,9 @@ project registry and adds explicit project selection, authenticated instance
731
735
  ownership and a separate optional dashboard manager. Runtime data and routing
732
736
  remain per project. Native memory aliases remain compatible and shared.
733
737
  Control protocol 7 adds project/instance identity and console-only dashboard
734
- snapshot/action forwarding. Existing project-local UI security rules still apply.
738
+ snapshot/action forwarding. Existing project-local UI security rules still apply. #391 Task A changes only client view selection;
739
+ project/instance fencing still invalidates open task details and stale controls.
740
+ No additional project route, request or timer is introduced.
735
741
 
736
742
 
737
743
  ## Amendment: externally managed Ollama MLX (issue #51)
@@ -2678,21 +2684,21 @@ C/dumb/linux/unknown locale surfaces use ASCII even when blocks were requested.
2678
2684
  Body foreground is uncoloured; only the Gyul leaf uses an existing accent tone.
2679
2685
  Native terminal/font/tmux proof remains a separate verification leg.
2680
2686
 
2681
- ## Shared dashboard attention (#280, phase 4)
2687
+ ## Shared dashboard attention (#280, replaced by #391 Task A)
2682
2688
 
2683
- The existing authenticated UI snapshot receives an optional allowlisted attention
2684
- input and enabled/character/blink presentation fields. Status wire/file callers
2685
- retain their phase3 summary/items projection and never receive this input. The
2686
- page invokes the same injected needs function on this body-free input, renders
2687
- the selected built-in full block frame as SVG pixels and exposes state/counts
2688
- through an accessible button. Its click only reaches an exact visible list row
2689
- or the relevant heading with fixed structural guidance; no action request occurs.
2689
+ The authenticated snapshot retains optional body-free needs input. The same
2690
+ injected pure needs function drives global nav action/watch counts and the
2691
+ existing tab-title action count; status/file readers retain their summary/items
2692
+ projection. The Agents button separately shows live pending approvals. The
2693
+ in-page character, SVG, character frames and pixel injection are removed; the
2694
+ standalone #391 Task B window owns that presentation.
2690
2695
 
2691
- DOM writes and title counts follow the derived signature, not every poll. A
2692
- waiting pulse uses CSS transform/opacity only, with a static reduced-motion and
2693
- blink-off escape. There is no extra timer, request, route or authority; legacy
2694
- or invalid snapshots make attention unavailable. Project changes clear stale
2695
- attention and restore the title. The existing CSP/header policy is unchanged.
2696
+ Attention activation switches views and selects only the exact visible
2697
+ approval/task id. Default/delivery/budget items without a visible exact target
2698
+ show their related heading and fixed id-only guidance, never a peer or first-row
2699
+ fallback. Filters and workflow state are unchanged. Signature guards suppress
2700
+ unchanged DOM writes, invalid legacy input stays unavailable, and project
2701
+ changes reset attention and close stale dialogs. CSP/header policy remains.
2696
2702
 
2697
2703
  ## Amendment: hub memory, tiers and graph (#258, phase 1)
2698
2704
 
@@ -2710,20 +2716,12 @@ and rebuilds; the dashboard's Memory section is read-only inside the existing CS
2710
2716
  long-term tasks with leases (decision 9's amendment), claude-mem import and model-suggested edges follow in later phases.
2711
2717
 
2712
2718
 
2713
- ## Dashboard attention projection (#280, phase 4 in progress)
2719
+ ## Dashboard attention projection (#280, superseded by #391)
2714
2720
 
2715
- The existing dashboard snapshot supplies optional allowlisted needs input and
2716
- known presentation choices; normal status/file payloads do not gain that input.
2717
- The header independently projects the shared pure needs function into literal
2718
- built-in SVG pixels and an accessible state/count label. Unchanged state and
2719
- choices do not rewrite pet DOM. Clicks navigate only to exact visible rows or
2720
- related headings, never issue actions or alter filters. Missing/invalid legacy
2721
- input hides the widget. Project resets clear retained navigation and title.
2721
+ #391 Task A replaces the in-page pixel widget with nav/title counts as defined
2722
+ in Shared dashboard attention above. Native character presentation is the
2723
+ standalone Task B window; browser attention uses no character data or frames.
2722
2724
 
2723
- The two approved waiting frames alternate in CSS once per second only while
2724
- a live approval exists. Reduced motion and blink-off remain static. Every other
2725
- state stays still. No timer/request or new asset dependency is introduced;
2726
- existing header policy and computed CSP hashes apply.
2727
2725
  ## Amendment: hub memory between machines and long-term tasks (#258, phase 2)
2728
2726
 
2729
2727
  Decision 9 is amended for memory only: the memory directory is a git repository that tracks `log/*.jsonl` and syncs
@@ -2830,3 +2828,89 @@ session instead of assuming that an older extension provides the fence.
2830
2828
  Prompt lineage lasts until logical `agent_settled`: native retry, compaction or
2831
2829
  continuation starts inside that run retain the same command id. A later personal
2832
2830
  run has no hub command lineage.
2831
+
2832
+ ## Amendment: standalone attention window (#391 Task B, supersedes #280 dashboard pet)
2833
+
2834
+ The #280 phase-4 dashboard pet is replaced by an optional standalone macOS JXA
2835
+ window. Task B adds the window first; #391 Task A removes the existing in-page
2836
+ character and retains shared attention counts/navigation. Terminal characters
2837
+ and read-only `ahub pet` behavior remain.
2838
+
2839
+ `ahub pet --window` / `--window --close` are console-identity commands;
2840
+ `pet.window:true` opts into startup from `ahub up`. Non-macOS reports unsupported.
2841
+ One detached Bun host per project holds a signed pidfile in stateDir, reads
2842
+ `readPet` and `loadPetConfig` every second and atomically writes state, act/watch,
2843
+ character, blink and the full built-in `petPixels(CHARACTERS[...])` frame. No
2844
+ item ids/text/titles or external image assets are included. Unavailable status
2845
+ writes exactly `{offline:true}`; JXA displays Offline for two seconds, exits,
2846
+ and the host follows. A closed/crashed JXA also ends the host. A stale owner
2847
+ requires explicit close; an unconfirmed owner is retained.
2848
+
2849
+ `osascript -l JavaScript` creates an accessory, borderless nonactivating
2850
+ NSPanel subclass that can become key, uses NSFloatingWindowLevel and
2851
+ canJoinAllSpaces | stationary, and embeds a transparent local WKWebView. Its
2852
+ one-second NSTimer loads the atomic view and calls `update(json)` with an
2853
+ explicit no-op completion function (a JS null completion block crashes on the
2854
+ verified macOS). The fixed bridge moves/saves position, makes the pencil's
2855
+ inline text box key, sends a console message through NSTask argv to `ahub say`,
2856
+ opens the dashboard through `ahub ui`, or closes. Enter sends; Esc cancels.
2857
+ No shell evaluation, new dependency, compile step, HTTP listener or network
2858
+ request is added. Message contents are not logged by this surface.
2859
+ Working animates only the pixel SVG through a 0.7-second stepped CSS bob, disabled by prefers-reduced-motion; no frame, timer or view-file change is added.
2860
+
2861
+ The repaired Mac spike verified a transparent floating key panel and both
2862
+ bridge directions. Real input/drag/Spaces and end-to-end stream delivery remain
2863
+ manual acceptance evidence on the implementation PR, not inferred from the
2864
+ spike's process metrics or automated fixtures.
2865
+
2866
+ ## Amendment: dashboard views and task detail (#391 Task A)
2867
+
2868
+ The existing page has one nav button per view, with `aria-current`; sections
2869
+ carry `data-view` and selection toggles only `hidden`. A shared cookie helper
2870
+ stores theme, Office mode, selected view and optional board column focus.
2871
+ The one-time ticket keeps exclusive use of the URL fragment. Projects and
2872
+ Benchmarks join peers, approvals and budgets under Agents.
2873
+
2874
+ Compact task cards retain drag/drop, id/title, priority, owner dot and
2875
+ check/overdue markers. Click/Enter opens a native `dialog` via `showModal()`.
2876
+ `taskDetail` supplies full current public content and existing controls to that
2877
+ dialog and to a focused column's one-wide-list cards. Each snapshot updates the
2878
+ open detail; disappearance/project reset closes it. Back restores compact mode,
2879
+ and state/peer/release filters remain active in focus. Attention targets exact
2880
+ visible row ids with heading guidance when absent. No new route, API request,
2881
+ timer, source text/ref/history projection or external asset is introduced.
2882
+
2883
+
2884
+ ## Explicit offline Pi history selection (#403)
2885
+
2886
+ A person may run `ahub pi --replace-session --session-file <managed-file>` only
2887
+ for an existing offline, settled Pi owner with a proven-gone native process.
2888
+ Pending tools, receipts, approvals and recovery hold replacement. The console
2889
+ control path alone authorizes this operation; peer and conductor tools cannot
2890
+ supply that authority. Validate the selected managed path and project/session
2891
+ header before any prior owner teardown, then durably write private previous
2892
+ and selected metadata. Retain all session files, queues and journal records.
2893
+ Start the selected persisted identity through the existing permission policy
2894
+ and verified teardown. There is no automatic fallback, implicit empty-session
2895
+ proof or effect retry; ordinary restart and captureResume remain strict.
2896
+
2897
+ ## Amendment: pull-only latency (#398)
2898
+
2899
+ A pull-only peer learns of important work only when its model calls a hub tool,
2900
+ and repo-heavy work was coordinated over chat it read too late.
2901
+
2902
+ - When an `important` envelope is queued for a pull-only peer, the bus calls the
2903
+ advisory `onImportantQueued` hook and the daemon sends that peer's control
2904
+ socket `{t:"queued", queued, important}`, as it does when a pull-only session
2905
+ attaches with important work already queued. It is not a delivery: nothing is
2906
+ dequeued, steered, journaled or settled, and the queue still waits for
2907
+ `hub_inbox`. The `inbox` reply adds `waitingImportant`.
2908
+ - The plugin caches the counts and appends one line naming the important count
2909
+ to every hub tool result except `hub_inbox` until a read reports none. An older
2910
+ plugin ignores the unknown push; an older hub never sends it.
2911
+ - `scripts/check.sh` takes a heavy-job slot, a directory in the git common dir
2912
+ shared by every worktree, before any step. A second gate waits up to
2913
+ `AHUB_CHECK_LOCK_WAIT_S` (1800 s; 0 fails at once) and names the holder; a
2914
+ holder whose pid is gone is reaped. The slot works with no hub running. It is
2915
+ not bus-visible; moving it into the hub is the upgrade if pid reuse or reap
2916
+ races bite.