@staix/agent-hub 0.34.0 → 0.35.0

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 (161) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +35 -6
  3. package/docs/adding-an-ai-tool.md +2 -2
  4. package/docs/addons/claude-mem.md +4 -4
  5. package/docs/addons/open-code-review.md +1 -1
  6. package/docs/agent-notes/README.md +6 -0
  7. package/docs/agent-notes/adapters.md +9 -5
  8. package/docs/agent-notes/budget.md +1 -1
  9. package/docs/agent-notes/bus.md +1 -1
  10. package/docs/agent-notes/daemon.md +4 -3
  11. package/docs/agent-notes/local-worker.md +1 -1
  12. package/docs/agent-notes/models.md +2 -0
  13. package/docs/agent-notes/tasks.md +1 -1
  14. package/docs/agent-notes/tests.md +5 -0
  15. package/docs/design/adapters.md +110 -2
  16. package/docs/design/agent-hub-architecture.md +63 -1
  17. package/docs/design/agent-hub.md +6 -0
  18. package/docs/design/bus.md +18 -0
  19. package/docs/design/execution-reference.md +146 -0
  20. package/docs/design/task-board.md +10 -0
  21. package/docs/dogfood.md +33 -12
  22. package/docs/events.md +38 -20
  23. package/docs/memory.md +21 -7
  24. package/docs/operations.md +539 -24
  25. package/docs/quickstart.md +18 -4
  26. package/docs/research.md +71 -13
  27. package/docs/roles.md +1 -1
  28. package/docs/sdlc.md +9 -7
  29. package/docs/security.md +87 -11
  30. package/docs/smoke.md +14 -0
  31. package/docs/specs/2026-09-19-agent-hub-design.md +8 -3
  32. package/docs/verification/2026-11-07-0.34.1.md +54 -0
  33. package/docs/verification/2026-11-07-compat-python-startup-1248.md +51 -0
  34. package/docs/verification/2026-11-07-compat-receipt-harness-1248.md +105 -0
  35. package/docs/verification/2026-11-07-compat-receipt-producers-1248.md +96 -0
  36. package/docs/verification/2026-11-07-compat-workflow-1248.md +94 -0
  37. package/docs/verification/2026-11-07-daemon-startup-1232.md +169 -0
  38. package/docs/verification/2026-11-07-jcode-receipt-reconstruction-1248.md +118 -0
  39. package/docs/verification/2026-11-07-kimi-delayed-usage.md +34 -0
  40. package/docs/verification/2026-11-07-kimi-review-watch-1284.md +115 -0
  41. package/docs/verification/2026-11-08-agy-1.5.3.md +96 -0
  42. package/docs/verification/2026-11-08-agy-acp-t0.md +56 -0
  43. package/docs/verification/2026-11-08-hub-visual-1058.md +161 -0
  44. package/docs/verification/2026-11-08-jcode-0991-model-setter-1317.md +143 -0
  45. package/docs/verification/2026-11-08-kimi-252-model-setter-1317.md +137 -0
  46. package/docs/verification/2026-11-08-kimi-tui-completion-1282.md +45 -0
  47. package/docs/verification/2026-11-08-kimi-wire-yield-1283.md +31 -0
  48. package/docs/verification/2026-11-08-live-model-override-1317.md +250 -0
  49. package/docs/verification/2026-11-08-qwen-0269-acp.md +138 -0
  50. package/docs/verification/2026-11-08-qwen-0269-model-setter-1317.md +91 -0
  51. package/docs/verification/2026-11-09-0.35.0.md +129 -0
  52. package/docs/verification/2026-11-09-headless-records-1320.md +157 -0
  53. package/docs/verification/claude-permission-transport-native.py +10 -3
  54. package/docs/verification/claude-shell-wrapper-native.py +8 -8
  55. package/docs/verified.json +245 -15
  56. package/package.json +3 -3
  57. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  58. package/plugins/agent-hub/kimi-hook.js +20397 -0
  59. package/plugins/agent-hub/kimi-review-policy-worker.js +217 -0
  60. package/plugins/agent-hub/server.js +2701 -664
  61. package/scripts/check-docs.mjs +12 -1
  62. package/src/adapters/acp-model-policy.ts +56 -0
  63. package/src/adapters/acp.ts +175 -5
  64. package/src/adapters/agy/entry.ts +8 -5
  65. package/src/adapters/agy/hooks.ts +65 -15
  66. package/src/adapters/channel-peer.ts +33 -1
  67. package/src/adapters/claude-implementer.ts +16 -3
  68. package/src/adapters/codex-appserver.ts +118 -8
  69. package/src/adapters/codex-review-isolation.ts +3 -1
  70. package/src/adapters/compat.ts +11 -6
  71. package/src/adapters/definitions/builtin-metadata.ts +8 -1
  72. package/src/adapters/definitions/native-cli-entry-kimi.ts +76 -7
  73. package/src/adapters/definitions/native-cli-preview-common.ts +2 -2
  74. package/src/adapters/definitions/native-cli-preview-kimi.ts +9 -2
  75. package/src/adapters/definitions/reviewer-variants.ts +3 -1
  76. package/src/adapters/kimi-review-policy-worker.js +2 -0
  77. package/src/adapters/kimi-review-policy-worker.ts +85 -0
  78. package/src/adapters/kimi-review-policy.ts +68 -0
  79. package/src/adapters/kimi-review.ts +41 -41
  80. package/src/adapters/kimi-tui-facts.ts +153 -0
  81. package/src/adapters/kimi-wire-usage.ts +33 -8
  82. package/src/adapters/local-worker.ts +59 -20
  83. package/src/adapters/model-token.ts +3 -0
  84. package/src/adapters/pi.ts +27 -5
  85. package/src/adapters/registry-types.ts +34 -3
  86. package/src/adapters/registry.ts +1 -0
  87. package/src/adapters/reviewer-policy-watch.ts +86 -0
  88. package/src/adapters/reviewer-print.ts +80 -18
  89. package/src/cli/agents.ts +1 -1
  90. package/src/cli/facts-hook.ts +4 -2
  91. package/src/cli/help.ts +3 -2
  92. package/src/cli/identity.ts +3 -0
  93. package/src/cli/kimi-facts-hook.ts +11 -0
  94. package/src/cli/kimi-plugin.ts +157 -0
  95. package/src/cli/main.ts +48 -9
  96. package/src/cli/model.ts +52 -0
  97. package/src/cli/output.ts +23 -1
  98. package/src/cli/recovery-package.ts +15 -7
  99. package/src/cli/reset.ts +1 -1
  100. package/src/cli/status-lines.ts +8 -4
  101. package/src/hub/budget.ts +1 -1
  102. package/src/hub/bus.ts +40 -2
  103. package/src/hub/code-facts.ts +10 -3
  104. package/src/hub/completions.ts +3 -1
  105. package/src/hub/config-apply.ts +2 -1
  106. package/src/hub/control-client.ts +1 -1
  107. package/src/hub/daemon.ts +1050 -106
  108. package/src/hub/deliverable-files.ts +38 -5
  109. package/src/hub/dogfood-profile.ts +1 -1
  110. package/src/hub/envelope.ts +1 -1
  111. package/src/hub/events.ts +28 -18
  112. package/src/hub/execution-lineage.ts +120 -0
  113. package/src/hub/execution-reference.ts +36 -0
  114. package/src/hub/hub-tools.ts +21 -2
  115. package/src/hub/instances.ts +4 -3
  116. package/src/hub/kimi-events.ts +46 -0
  117. package/src/hub/kimi-usage.ts +132 -29
  118. package/src/hub/lifecycle.ts +31 -15
  119. package/src/hub/limits.ts +13 -4
  120. package/src/hub/manager.ts +15 -2
  121. package/src/hub/memory-policy.ts +2 -1
  122. package/src/hub/memory-sync-attestation.ts +54 -0
  123. package/src/hub/memory-sync-state.ts +20 -5
  124. package/src/hub/model-overrides.ts +115 -0
  125. package/src/hub/native-hook-launch.ts +12 -3
  126. package/src/hub/operational-view.ts +2 -2
  127. package/src/hub/peers.ts +27 -0
  128. package/src/hub/permission-mode.ts +1 -1
  129. package/src/hub/person-figures.ts +88 -0
  130. package/src/hub/report.ts +38 -4
  131. package/src/hub/research-dataset.ts +158 -0
  132. package/src/hub/research-memory.ts +68 -0
  133. package/src/hub/research-quality.ts +118 -0
  134. package/src/hub/research-record.ts +279 -0
  135. package/src/hub/research.ts +126 -27
  136. package/src/hub/restart.ts +14 -0
  137. package/src/hub/settings.ts +3 -0
  138. package/src/hub/startup-event-seeds.ts +51 -0
  139. package/src/hub/startup-readiness.ts +65 -0
  140. package/src/hub/startup-timing.ts +38 -0
  141. package/src/hub/tasks.ts +29 -9
  142. package/src/hub/ui.ts +9 -5
  143. package/src/hub/visual-assets.ts +50 -0
  144. package/src/hub/visual.ts +257 -0
  145. package/src/memory/brief.ts +169 -14
  146. package/src/memory/capture.ts +2 -1
  147. package/src/memory/recall.ts +46 -5
  148. package/src/memory/record-provenance.ts +82 -0
  149. package/src/memory/records.ts +116 -0
  150. package/src/models/route/adec.ts +190 -0
  151. package/src/models/route/config.ts +37 -3
  152. package/src/models/route/decision-evidence.ts +83 -0
  153. package/src/models/route/decision.ts +143 -0
  154. package/src/models/route/runtime.ts +13 -6
  155. package/src/ui/index.html +134 -6
  156. package/src/ui/plugin.ts +65 -4
  157. package/templates/AGENTS.block.md +1 -1
  158. package/templates/config.json +1 -1
  159. package/templates/profiles/general.json +1 -1
  160. package/templates/profiles/research.json +1 -1
  161. package/templates/profiles/writing.json +1 -1
@@ -79,6 +79,18 @@ Older daemons retain their existing close-and-retry standby behavior. An ordinar
79
79
  owner departure may release an eligible standby through normal claim admission;
80
80
  a standby never evicts a live owner without the person's takeover operation.
81
81
 
82
+ The detailed status the console reads also names the current owner (#1310), so it is never
83
+ inferred from standby rows, process start times or attach order. `ownerAttachment` holds the
84
+ current socket's bounded `ownerPid` when it reported one, `attachedAt` (when the asynchronous
85
+ attach completed, not the earlier hello), and `nativeSessionId` only after the host bound a native
86
+ session through its verified native-facts path and only when it is a short plain token. It is
87
+ absent while a newer hello is claiming, while offline, and after a detach, roster removal or stop,
88
+ and a new attach replaces every field in one step, so a previous owner's pid, time or native
89
+ session never sits beside the new socket. It is display metadata, never authentication, delivery,
90
+ takeover or permission evidence. It carries no environment, argv, cwd, credential, prompt, title
91
+ or text, adds no event, log or memory payload, and is withheld from `status.json` and every
92
+ non-console reader. The field is additive and optional; no control protocol version changes.
93
+
82
94
  ## Accepted channel delivery settlement (#1172)
83
95
 
84
96
  Only an attached push channel opts into explicit disposition of its own accepted delivery. An `accepted` receipt acknowledges that the notification was accepted by the channel transport; it is not a native handling or task-completion signal. The host reads `bus.accepted_settle_ms` from accepted configuration on its existing maintenance sweep, defaulting to ten minutes. A row that remains unconfirmed becomes `needs_review` with no replay. `hub_inbox` returns bounded id/revision metadata for the channel's accepted and held rows, including when an accepted push prevents the pull itself. The plugin renders that metadata and avoids both an empty-inbox claim and polling advice while there is an actionable row. Important, task and review timeouts produce one durable status-kind, status-priority conductor message containing ids only when the existing `conductor.feed` policy is on; `feed: off` remains an opt-out. Pull-only sessions do not gain the explicit accepted-disposition port.
@@ -89,16 +101,104 @@ Permission modes control whether native actions prompt; they do not widen a sand
89
101
 
90
102
  When the hub starts a child, the host constructs its environment, binds its launch and generation, and owns its process-group lifecycle. Stop and recovery require current evidence for the process actually started; a PID, adapter label, successful start call, or stale session record is not enough. This keeps teardown from signaling a person's unrelated process and prevents a late native callback from acquiring a replaced session's authority ([#115](https://github.com/STAIxBWLB/agent-hub/issues/115), [#625](https://github.com/STAIxBWLB/agent-hub/issues/625)).
91
103
 
104
+ Fresh Kimi reviews keep the complete synchronous isolation fence before spawn and
105
+ at verdict/task-MCP authority boundaries. Periodic full checks use one review-owned
106
+ worker with expected paths and fingerprints, no serialized task text or inherited
107
+ environment, and boolean replies. A serialized 100 ms absolute cadence coalesces
108
+ missed ticks without overlapping checks or adding a fixed delay after a slow check.
109
+ Worker results cannot grant or restore authority. Revocation fences the exact
110
+ child/review sequence; worker termination and join precede private-profile removal
111
+ and confirmed input cleanup, including failed native teardown. An unconfirmed join
112
+ retains ownership and the profile for an explicit stop retry ([#1284](https://github.com/STAIxBWLB/agent-hub/issues/1284)).
113
+
92
114
  Permission decisions are bound to the authenticated caller and current session. A hub CLI call inside an agent session acts as that peer, not as the person; role and task checks still apply at each tool boundary. Permission modes are person-controlled, and a conductor role does not grant them. This avoids turning a received message into user consent ([#193](https://github.com/STAIxBWLB/agent-hub/issues/193), [#242](https://github.com/STAIxBWLB/agent-hub/issues/242)).
93
115
 
94
116
  ## Model, account, and memory boundaries
95
117
 
96
- Subscription-based native CLIs keep their own account authentication; those credentials are not pooled through the hub. Hub-owned inference, including internal summaries and local-worker model calls, uses the in-process model route and OmniRoute provider boundary. Prompt text may inform configured routing, but it cannot grant credential access or replace host routing policy. This separates native subscription identity from hub service credentials ([#553](https://github.com/STAIxBWLB/agent-hub/issues/553)).
118
+ Subscription-based native CLIs keep their own account authentication; those credentials are not pooled through the hub. Hub-owned inference, including internal summaries and local-worker model calls, uses the in-process model route and OmniRoute provider boundary. Prompt text may inform configured routing, but it cannot grant credential access or replace host routing policy. The optional local-worker adec consumer (#554) releases only bounded host-screened evidence, after campus/privacy and observer admission; native account, TUI, reviewer and task-assignment authority are unchanged. Shadow preserves the gateway model/effort and conversation; enforcement is refused at configuration load and no pair is applied. An explicit live model override bypasses the decision service. Current board/turn privacy and observer authority fence the optional request across waits and its proposal before telemetry; an authority change refuses the stale turn, and the refusal is not converted into a fixed-model fallback. There is no new gateway or native turn/spawn effort path. This separates native subscription identity from hub service credentials ([#553](https://github.com/STAIxBWLB/agent-hub/issues/553)).
97
119
 
98
120
  The PII screen is distinct from ordinary optional inference. It may use an on-device model; a gateway screen requires positive on-campus admission and `onCampusOnly`. If no allowed path returns a closed `clear` or PII verdict before the deadline, the result is unknown and handled as private. This keeps network and model uncertainty from becoming a disclosure ([#198](https://github.com/STAIxBWLB/agent-hub/issues/198)).
99
121
 
100
122
  Runtime memory capture and recall use the in-process `@staix/agent-mem` package, screened and invoked by host integration. claude-mem is off by default and import-only; there is no worker HTTP or model-summary path in the runtime. This avoids a parallel capture route with different privacy checks ([#752](https://github.com/STAIxBWLB/agent-hub/issues/752), [agent-mem#13](https://github.com/STAIxBWLB/agent-mem/issues/13)).
101
123
 
124
+ ## Person-owned live model selection (#1317)
125
+
126
+ A person may store a per-peer model override in `hub.db` with `ahub model set`,
127
+ clear it with `ahub model clear`, or inspect it with `ahub model list`. The CLI
128
+ uses the settings command's identity boundary, but all model subcommands are
129
+ person-only, including list. Dashboard changes use the existing person settings
130
+ session and preview/confirm flow. No hub tool, conductor role or agent shell
131
+ receives this authority. Provider, account, native home and executable changes
132
+ remain on their existing machine-local configuration paths.
133
+
134
+ The host stores desired state independently of an active turn. A model selection
135
+ is captured once at the next supported turn/review boundary and stays unchanged
136
+ through steering and local tool rounds. Captured application callbacks bind the
137
+ exact adapter owner and turn; they cannot mark a newer desired revision applied.
138
+ Two changes before a turn leave the last desired value. Status distinguishes the
139
+ effective selection, its source, a pending next-turn value and unsupported live
140
+ selection; a desired row alone is never evidence that a native model accepted it.
141
+ The precedence is override, instance model, launch model, then native default,
142
+ where the surface already supports that source. An unknown native default stays
143
+ unknown rather than becoming an invented model id.
144
+
145
+ Claude headless implementers select `--model` per spawned turn; codex-review
146
+ selects `-m` per isolated review spawn. Supported Codex app-server `turn/start`
147
+ requests carry `model` only for an override, alongside the existing permission
148
+ policy. Both hub-delivered and client-forwarded app-server `turn/start` paths
149
+ support it, but an explicit client model (for example the person's native
150
+ `/model` choice) wins and is never rewritten. The app-server retains a
151
+ `turn/start` model as its thread default. Clearing the hub override returns
152
+ selection to that current native default; it does not reconstruct an earlier
153
+ thread model. Native client acceptance does not certify a conflicting hub
154
+ override as applied. Raw or pull-only `WsPeer` TUI attachments remain unsupported.
155
+ When clear retains an acknowledged former override as the thread default,
156
+ status labels its source `native thread default (was override)`. A rejected
157
+ start retires its unused capture so a later start can sample a corrected choice;
158
+ an older rejection cannot retire a newer accepted or pending start.
159
+ `turn/steer` never acquires a model field. Local uses the existing direct
160
+ OmniRoute model path for an explicit override while retaining budget, campus,
161
+ observer and generation guards. Pi reuses its model-selection path at the next
162
+ turn and retains a separate baseline so clear can restore normal selection.
163
+ Existing no-override behavior and native permission/isolation proofs stay intact.
164
+
165
+ Live ACP selection requires two independent facts: the existing native admission
166
+ and an exact-version model-free setter receipt. Qwen and Jcode compatibility
167
+ admission includes the person's existing exact-version trust; Kimi headless uses
168
+ its existing native-identity admission, independently of Kimi reviewer isolation
169
+ compatibility. Only Kimi Code CLI 2.5.2 headless is currently enabled, through
170
+ `session/set_config_option` with `configId: model` and the
171
+ [model-free setter receipt](../verification/2026-11-08-kimi-252-model-setter-1317.md).
172
+ Qwen remains unavailable because its native clear fixture observed A/B/B after
173
+ ACKs for B then A. Jcode headless A/B/A passed, but its shared-TUI turn fence
174
+ across failover remains unqualified; its production setter stays unavailable. A native TUI or external adapter cannot acquire a setter from an
175
+ advertisement or a registry label.
176
+
177
+ The host captures one selection before awaiting a setter, validates bounded
178
+ current-session model choices and owned process identity, then sends the prompt.
179
+ Setter acknowledgement and model/config notifications are not prompt admission:
180
+ only current prompt activity or successful completion acknowledges application.
181
+ An unanswered setter leaves native selection unknown and takes the peer offline
182
+ through its owned stop path. Trust revocation is rechecked before the prompt.
183
+ Clear restores the validated pre-override session baseline, with instance and
184
+ launch precedence retained; status does not expose a pending setter target as
185
+ that baseline. Unsupported no-override delivery retains its ordinary behavior;
186
+ a retained override instead refuses before a prompt until the person clears it
187
+ or starts a supported owner.
188
+
189
+ Fresh reviewer variants explicitly strip inherited model capability. Only an
190
+ explicit per-spawn capability, such as codex-review, receives a model getter;
191
+ a long-lived ACP policy never enables the reviewer print path. Unsupported
192
+ surfaces refuse a live set with a cause and `ahub restart <peer>` as the next
193
+ step, without storing a newly refused override or restarting anything.
194
+
195
+ Overrides survive daemon restart and are cleared by runtime reset. Retained
196
+ state must remain protected by recovery feature/integrity admission. Model ids
197
+ use the existing #844 bounded token rule, with no spaces or leading dash.
198
+ `model_change` records only peer, previous/new ids, person surface and a next-turn
199
+ boundary, never prompts or credentials. A future native turn id is not invented;
200
+ `appliesFrom: next_turn` and an existing `afterTurn` identify the boundary.
201
+
102
202
  ## Compatibility and external modules
103
203
 
104
204
  Native CLI compatibility is centralized in `compat.ts`. Each gated operation asks that policy for a verified, in-contract, person-trusted, or refused result; person trust for one exact version does not skip a separate isolation or runtime proof. This lets upstream versions change without turning an untested protocol assumption into support ([#691](https://github.com/STAIxBWLB/agent-hub/issues/691)).
@@ -107,7 +207,9 @@ External adapter modules are an explicit, person-owned, machine-local opt-in. Th
107
207
 
108
208
  `questionWaitMs` (#1053) is the time a `hub_ask` call waits for the person before it answers `pending`. It is a fixed-builtin field like `title`: `assertNativeCapabilityScope` refuses it on a configured or external definition, and the person overrides it per kind with `questions.wait_ms.<kind>`, never the caller. MCP peers carry 45 s, Pi and the local worker 300 s, provisional until the live timeout measurements of #1053 T1.
109
209
 
110
- `settle` (#1054) is the code-verified signal that ends a peer's logical run, so the hub can create a completion for a run a person started. It is a fixed-builtin field like `questionWaitMs`: `assertNativeCapabilityScope` refuses it on a configured or external definition. **Owner decision 2026-11-04 (issue #1054):** completions exist only for adapters whose signal the T1 audit verified in code. Claude's TUI (`native-stop`: the transcript-verified Stop, never a lost channel or watchdog) and headless implementer, Codex (`turn-completed`), Pi (`agent-settled`, its own event: the `agent_end` plus retry/compaction fallback is not implemented), the local worker (`route-outcome`), ACP Kimi and Jcode headless (`prompt-response`) and the Qwen TUI (`message-stop`) declare one. **Absent means no completions:** Kimi in its own TUI (pull-only, no signal), agy (its declared `native-hook` signal is not verified end to end) and phase-2 person-typed Codex turns declare none, and the daemon starts no run for a peer attached pull-only. agy joins after a live receipt verifies its Stop readback and completion row; Codex person-typed turns after an origin receipt. The settle rule, outcomes and restart reconstruction live in `src/hub/completions.ts` and `src/hub/completions-store.ts`; a run settles only when the adapter signal fired AND no queued continuation, open permission or question, owned child, running check or held delivery remains.
210
+ `settle` (#1054) is the code-verified signal that ends a peer's logical run, so the hub can create a completion for a run a person started. It is a fixed-builtin field like `questionWaitMs`: `assertNativeCapabilityScope` refuses it on a configured or external definition. **Owner decision 2026-11-04 (issue #1054):** completions exist only for adapters whose signal the T1 audit verified in code. Claude's TUI (`native-stop`: the transcript-verified Stop, never a lost channel or watchdog) and headless implementer, Codex (`turn-completed`), Pi (`agent-settled`, its own event: the `agent_end` plus retry/compaction fallback is not implemented), the local worker (`route-outcome`), ACP Kimi and Jcode headless (`prompt-response`) and the Qwen TUI (`message-stop`) declare one. **Absent means no completions:** agy (its declared `native-hook` signal is not verified end to end) and phase-2 person-typed Codex turns declare none, and an unattached pull-only peer starts no run. agy joins after a live receipt verifies its Stop readback and completion row; Codex person-typed turns after an origin receipt. The settle rule, outcomes and restart reconstruction live in `src/hub/completions.ts` and `src/hub/completions-store.ts`; a run settles only when the adapter signal fired AND no queued continuation, open permission or question, owned child, running check or held delivery remains.
211
+
212
+ Kimi 2.5.2's own TUI also earns completions, but only through the `ahub kimi` launcher's ephemeral `native_hook_launch` and local plugin (#1282). The plugin record binds the launch to its owner pid and process signature. A different launch may replace it only when that owner is proven gone; an active or unknown owner, including one from another project sharing the native home, is left unchanged. Replacement and cleanup share one fail-closed lock per native home, with no automatic stale-lock deletion. If begin or setup fails, the launcher reports the cause and starts the plain TUI without completion receipts. Quiet background/final-turn re-reads back off from 250 ms to 1 s, and the 2 s Stop deadline also arms them. A hook is a candidate observation, never proof. The daemon admits only the exact `kimi-tui` compat version and binds the observation to daemon, launch, native home, native session, project and current socket generation. The transcript's `turn.prompt` supplies the origin and turn id; its final `turn.ended` supplies the outcome. `prompt.completed` and `prompt.aborted`, when present, must agree with that turn; a notification may omit both id forms, so the native notification id or turn id is the fallback identity. A blocked Stop has no final row, so it cannot settle. StopFailure needs `failed`, Interrupt stays quiet with no result, and launcher loss or daemon reconstruction creates at most one `interrupted`. Every native-person result is private because the hook path cannot prove it is public. Headless ACP keeps `prompt-response`; an unverified TUI version or a launch without these readbacks keeps the old no-completion behavior. Native background task files supply the count or an explicit unknown; work or an unreadable inventory never settles. A 250 ms re-read covers missing TaskStarted/Notification hooks without turning elapsed time into evidence. No prompt, answer, error, task command or title reaches completion rows, events, logs or memory. The exact-version receipt is `docs/verification/2026-11-08-kimi-tui-completion-1282.md`.
111
213
 
112
214
  ## Session title slots
113
215
 
@@ -182,3 +284,9 @@ When a quote-bearing review reaches a peer without a stable native session ID, t
182
284
  ## Local review turns (#1056)
183
285
 
184
286
  The local worker binds review observer exclusion to its actual input envelopes before awaiting model or tool work. An anchored `hub_review` submission latches that exclusion for the rest of the batch/turn, including sibling and later tool calls. While latched, `hub_remember` and `hub_task_propose` are refused before reaching the host task tools; the caller can retry them in a later ordinary turn. Such turns do not call capture, commit review messages into later model history, or put provider error text and partial model text in failure logs. A later ordinary local turn resumes ordinary history and capture without inheriting the review payload. This is distinct from the PII policy: a public review package retains ordinary task authority and does not acquire an on-campus-only model route. Vendor-native histories remain owned by their adapters; the hub's current-turn and named-parent observer provenance is described in [the bus contract](bus.md#review-observer-provenance-1056).
287
+
288
+ ## Headless Record boundaries (#1320)
289
+
290
+ Only a host-owned getter supplies the imported agent-mem instruction at an admitted headless turn boundary, after owned input and busy state exist and before the native prompt/model request. Multi-call local turns keep one fixed prompt. Native-client prompts, steer, Claude hooks and isolated reviewers keep their separate surfaces. A getter failure omits optional Record guidance and does not change ordinary routing. The production getter reads the public `recordsEnabled(activeHome, executionCwd)` accessor from the exact-pinned amem package; either machine or repository false disables the optional Record path. Its boolean revision is not a privacy-policy fingerprint: current package and hub screens are rechecked separately before effects.
291
+
292
+ Model final output uses a host-only final-answer callback distinct from explicit send tools and diagnostic messages. Only normal completion can append Record rows; partial output is stripped for sharing without certifying a settled record. Capture binds immutable owner, turn, session/generation, execution cwd, profile and policy identity. Replacement, exclusion or unsafe steering revokes the frame before any later append. This grants no task, permission, native consumption or model acceptance authority.
@@ -90,14 +90,76 @@ returns an ids-only receipt and excludes its arguments/results from capture.
90
90
  Its T1 authorization, revision and recovery boundaries are defined in
91
91
  [the task-board contract](task-board.md#claim-evidence-records-931-t1).
92
92
 
93
+ ## Bounded visual attachments (#1058)
94
+
95
+ Bounded visual attachments (#1058) are local project records in `hub.db`, separate
96
+ from task transitions and delivery receipts. The hub validates and privacy-screens
97
+ the exact acon `VisualPayload` shape; peers receive only a sanitized stub with
98
+ `refs.visual`, and explicitly request the source through a guarded read. acon owns
99
+ rendering and its sandbox; the standalone dashboard uses text/source fallback.
100
+ The hub imports contract types only, with no renderer or new runtime dependency.
101
+ Image bytes use the existing guarded deliverable reader and current hash/policy
102
+ checks; console asset identifiers bind their originating project and instance.
103
+ Raw visual tool arguments/results never enter observer capture, even on refusal.
104
+ The unchanged acon shape has additional hub admission ceilings: 1 MiB serialized
105
+ payload; UTF-8 bytes Mermaid source 20 KiB, title 300, caption 2000, alt 1000,
106
+ text side 20 KiB, option
107
+ summary 4000, each pro/con 2000, table cell 4000, key 128, path 4096 and asset
108
+ reference 256. These are hub limits, not a claim that acon imposes each limit;
109
+ validation refuses rather than truncates. acon counts Mermaid UTF-16 code units
110
+ and leaves captions unbounded, while the hub counts UTF-8 bytes. All 42 published
111
+ acon 0.1.7 visual cases are consumed directly; six named valid-acon cases are
112
+ rejected only by these stricter source/caption/asset-ID ceilings. Historical
113
+ hub-owned cases remain extra coverage, not a substitute for the shared fixture.
114
+
115
+ Periodic dashboard snapshots and task lists project bounded, screened metadata
116
+ only; they do not deserialize or screen complete visual payloads. Explicit show
117
+ and asset requests perform the full current-policy checks. A missing image does
118
+ not erase its visual's public explanation; asset existence and hash validation
119
+ belong to the guarded asset read. Non-sensitive refusals identify recovery,
120
+ authority, concurrent change or unavailable screening and their repair action;
121
+ sensitive privacy refusals keep a uniform message.
122
+
123
+ Publication does a non-consuming rate check before model screening or image
124
+ reads and still consumes admission once immediately before publication. Visuals
125
+ are explanatory output, not task progress for no-write detection.
126
+ Held controlled recovery compares retained visual integrity before startup
127
+ retention pruning; startup, periodic and tool pruning all wait until both the
128
+ recovery transaction and bus recovery hold are clear.
129
+
93
130
  ## Models and memory
94
131
 
95
- Peer selection, hub-owned model routing, and provider access are distinct layers. Task routing chooses a peer; the in-process route selects a model for hub-owned inference; OmniRoute owns provider keys, provider fallback, and usage. Native CLIs retain their own account and provider configuration; subscription OAuth credentials are not pooled through the gateway. The former Switchyard sidecar is retired; routing now runs in process. This keeps provider credentials and fallback policy in one gateway while avoiding a second routing service ([#553](https://github.com/STAIxBWLB/agent-hub/issues/553)).
132
+
133
+ Peer selection, hub-owned model routing, and provider access are distinct layers. Task routing chooses a peer; the in-process route selects a model for hub-owned inference; OmniRoute owns provider keys, provider fallback, and usage. Native CLIs retain their own account and provider configuration; subscription OAuth credentials are not pooled through the gateway. The former Switchyard sidecar is retired; routing now runs in process. An opt-in `decision` route may consult the loopback agent-decide policy service; execution and provider access remain in the hub and OmniRoute, with the existing heuristic as the consumer fallback. This keeps worker-provider credentials and provider failover in one gateway; the optional policy consult does not proxy the worker's inference traffic ([#553](https://github.com/STAIxBWLB/agent-hub/issues/553)).
134
+
135
+ Person-owned live model overrides (#1317) are runtime state in `hub.db`, not
136
+ configuration edits. The host owns person admission, desired-versus-applied
137
+ selection, next-turn fencing, status and audit; adapters acknowledge only their
138
+ own captured spawn/transport acceptance. The override does not grant a model,
139
+ provider or account authority to a peer. Unsupported surfaces remain unsupported
140
+ without automatic restarts. Tier 3 configuration, recovery and native isolation
141
+ boundaries stay in place; the [adapter contract](adapters.md#person-owned-live-model-selection-1317)
142
+ defines supported turn paths and evidence limits.
96
143
 
97
144
  The hub's portable memory storage, sync, indexing, and project helpers come from the pinned first-party `@staix/agent-mem` package. `src/memory/` and `Tasks` provide hub-specific capture, recall, privacy screening, task briefs, and shared notes. Runtime memory is in-process: there is no claude-mem worker HTTP client or model summarizer. claude-mem is an off-by-default, import-only source. This removes a second capture path while keeping caller authority and task privacy in the hub ([#752](https://github.com/STAIxBWLB/agent-hub/issues/752), [agent-mem#13](https://github.com/STAIxBWLB/agent-mem/issues/13)).
98
145
 
99
146
  Long-term hub writes and core promotion callbacks use the configured standalone sync policy plus hub narrowing. Profile readiness is independent of the current daemon's last automatic sync result. Public status exposes safe cause/count/action metadata; person-only declined inspection exposes IDs and rule categories, with explicit incomplete state when the portable core cannot supply a bounded physical view. Portable row placement and physical sync inspection remain agent-mem responsibilities.
100
147
 
148
+
149
+ The adec backend (#554) is default-off and shadow-first. A shadow pick changes
150
+ neither the heuristic model/effort nor the forwarded conversation. The service
151
+ owns question assembly, hard rules, thresholds, pins and policy fallback; the hub
152
+ owns allowed-pair validation, its default-off shadow switch, current turn and
153
+ budget/campus admission, and original gateway execution. `enforce` is refused at configuration load and
154
+ no pick is applied in this delivery. The bounded evidence
155
+ builder runs only after admission and uses the host public-view screen; it never
156
+ sends tool arguments, system instructions or full history. Sensitive turns are
157
+ campus-gated before evidence and carry dimensions only plus privacy flags.
158
+ Review-observer and remote inputs do not reach this optional observer. Route
159
+ records retain closed metadata, not evidence or service error prose. Direct
160
+ System One integration, the optional stage classifier and the manual triage
161
+ comparison remain outside this consumer slice.
162
+
101
163
  ## Privacy and recovery
102
164
 
103
165
  Session titles for the two hub-owned title slots (#1055) are conditional writes
@@ -25,6 +25,12 @@ The core loop is to propose and assign work, exchange addressed messages while p
25
25
 
26
26
  The hub also owns a local worker and optional model calls for its own inference. It does not replace every native agent with that loop, pool subscription credentials, or make native behavior identical across vendors. Native adapters preserve those differences behind explicit contracts ([#625](https://github.com/STAIxBWLB/agent-hub/issues/625), [#553](https://github.com/STAIxBWLB/agent-hub/issues/553)).
27
27
 
28
+ Bounded local visual attachments let ordinary peers explain public work with a
29
+ diagram, comparison, table or before/after view. The hub stores and screens the
30
+ record while acon renders it; recipients receive a text stub, and the standalone
31
+ dashboard retains a readable source fallback. This adds no renderer, native-agent
32
+ authority or cross-project board to the hub ([#1058](https://github.com/STAIxBWLB/agent-hub/issues/1058)).
33
+
28
34
  ## Product decisions
29
35
 
30
36
  | Current rule | Reason and deciding issue |
@@ -31,6 +31,18 @@ The bus applies limits to a sender, sender-recipient pair, important messages, a
31
31
 
32
32
  ## Queues and delivery outcomes
33
33
 
34
+ `hub_visual` (#1058) publishes only a bounded chat stub naming a local visual
35
+ record through `refs.visual`. Its default audience is the console at FYI priority;
36
+ explicit recipients use existing addressed-message rules. Neither Mermaid source,
37
+ table/comparison bodies nor image bytes are embedded in the envelope or delivery
38
+ journal. A source read is explicit, local to the project and privacy-guarded.
39
+ Visual publication grants no task, native-session, invocation or broker authority.
40
+ Before expensive visual screening or image reads, `Limiter.peek` uses the same
41
+ admission decision as `admit` without changing buckets or repeat history. The
42
+ final `admit` still consumes once against current state before record/publication
43
+ effects; a successful preflight never reserves or grants future delivery.
44
+
45
+
34
46
  The in-memory bus schedules messages; `DeliveryJournal` durably records an individual transport attempt and the original envelopes behind any rendered output. A peer receipt such as `accepted` or `completed` describes transport, not the corresponding task's acceptance, completion, or approval. That separation lets a timeout or restart remain explicitly uncertain without inventing a task outcome ([#37](https://github.com/STAIxBWLB/agent-hub/issues/37)).
35
47
 
36
48
  A closed bus returns an empty publication outcome before provenance, privacy,
@@ -297,3 +309,9 @@ step. Support is rechecked across awaited admission and at actual publication.
297
309
  Live signed observations are scoped to the current connection and roster; saved,
298
310
  stale or replayed presence never grants codec support. Existing native, exposure,
299
311
  privacy and invocation authorization checks still decide whether a send is allowed.
312
+
313
+ ## Record final-answer projection (#1320)
314
+
315
+ The final-answer boundary strips agent-authored `amem:` lines before invocation answer handling, ordinary publication, journaled/shared bodies, digest rendering or console envelope taps. Explicit send tools keep their existing path. Original priority and audience/hop/receipt rules are retained. Stripping runs even when optional capture is disabled or fails. Final dispatch traverses the current `onMessage` observer chain exactly once. A peer/body/options-scoped host marker identifies the final-only raw capture path, is consumed before nested messages and is restored or cleared on exit; no final marker is accepted from a wire envelope. Stale replaced owners cannot route or capture a final answer.
316
+
317
+ The raw-final capture observer is host-only and limited to a complete, currently owned public turn. Private inputs or outputs, review/observer holds, remote inputs/audiences, invocation answers and unknown parents cannot reach it. Count-only observers cannot change routing. The memory owner applies current policy before storage or selection; captured Record rows remain uncurated reference data. A bounded, ephemeral saved-ID/raw-anchor registry can prove display substitution for generated notes, with no persistence or inference from titles or prose. Restart and eviction preserve unproved old content.
@@ -0,0 +1,146 @@
1
+ ---
2
+ id: execution-reference
3
+ type: module
4
+ title: Execution Reference and Bounded Lineage
5
+ status: draft
6
+ parent: agent-hub-architecture
7
+ depends-on: [task-board, bus]
8
+ references: [task-board, bus]
9
+ implements: []
10
+ covers: [execution-reference]
11
+ tags: [agent-hub]
12
+ paths:
13
+ - src/hub/execution-reference.ts
14
+ - src/hub/execution-lineage.ts
15
+ - test/execution-reference.test.ts
16
+ - test/execution-lineage.test.ts
17
+ - test/fixtures/execution-reference-v1.json
18
+ ---
19
+
20
+ # ExecutionReferenceV1
21
+
22
+ Source-owner contract for [#1220](https://github.com/STAIxBWLB/agent-hub/issues/1220).
23
+ T1 supplies an address validator, neutral vectors and a pure reader. T2 owns
24
+ Tasks admission and CLI/MCP wiring; T3 owns the console and measured performance.
25
+ There is no new persistence, automatic resolver or runtime polling in T1.
26
+
27
+ ## Address contract
28
+
29
+ An address is `{v: 1, tool, projectKey, kind, id, revision?, sha256?}`.
30
+ It names evidence; it does not attest to it or grant access to it.
31
+
32
+ | Field | Rule |
33
+ | --- | --- |
34
+ | `v` | Exactly the integer 1 |
35
+ | `tool` | Known producer id: `ahub`, `ares`, `amem`, `vmem` |
36
+ | `projectKey` | Opaque producer project identity, nonempty, at most 256 UTF-8 bytes |
37
+ | `kind` | Producer-namespaced by `(tool, kind)`, lowercase token matching `[a-z][a-z0-9._-]*`, at most 64 bytes |
38
+ | `id` | Opaque source identity, nonempty, at most 256 UTF-8 bytes |
39
+ | `revision` | Optional nonempty opaque source revision, at most 128 UTF-8 bytes |
40
+ | `sha256` | Optional lowercase 64-hex content hash |
41
+
42
+ Reject unknown fields, unsupported producers/versions, null optional fields,
43
+ control characters, unpaired surrogates, accessors, symbol keys and inherited
44
+ objects. Do not trim, case-fold or Unicode-normalize identity. Absent optional
45
+ fields stay absent. The producer vocabulary identifies owners, not enabled
46
+ resolvers: an unknown kind can be syntactically valid yet unsupported by its owner.
47
+ Consumers validate this boundary locally without importing hub internals or a new
48
+ shared runtime dependency. Neutral valid/invalid JSON vectors live in
49
+ `test/fixtures/execution-reference-v1.json`; UTF-8 limits include multibyte cases.
50
+
51
+ A path-like or URL-like id is still opaque data. Never open, execute, fetch,
52
+ route to another node or interpret credentials from any address field. A resolver
53
+ must separately select a known source owner and its authorized project; no
54
+ implicit node or project fallback is allowed.
55
+
56
+ ## Pure source boundary
57
+
58
+ `readExecutionLineage(source, query)` receives a host-owned snapshot of an explicit
59
+ root neighborhood and returns values only. It does not construct a Board,
60
+ DeliveryJournal or CompletionStore (their constructors can write), read files,
61
+ call a model, spawn a process, probe, replay, fetch or write memory. T2 must reuse
62
+ already-open owner readers and indexed relationships to assemble the snapshot;
63
+ it must not scan the entire board on every inbox poll.
64
+
65
+ The host pins `id`, `revision`, `projectKey` and caller `scope`, source `health`,
66
+ source-read `authorized` authority and diagnostic authority. These are never
67
+ accepted from a producer or query. A snapshot contains metadata nodes and explicit
68
+ edges, already privacy-projected by the source owner. Each node carries its own
69
+ source reference, state token and evidence strength; no title, note, path,
70
+ session-native identifier or review body enters this projection. States keep
71
+ their owner's meaning: a completed dispatch is not a successful attempt; an
72
+ approved review does not change a failed attempt.
73
+
74
+ Evidence strengths are `declared`, `host-observed`, `runner-attested` and
75
+ `unknown`. Relations are `dispatched`, `attempted`, `produced`, `reviewed` and
76
+ `declared-external`. An edge is emitted only when both endpoints are visible.
77
+ An external declaration remains declared; a reference/hash never upgrades it.
78
+ The reader never infers edges from names, titles, timestamps, shared paths,
79
+ trace ids, current roles or matching hashes alone.
80
+
81
+ ### Existing owner mappings and missing observations
82
+
83
+ | Existing relationship | Valid projection | Ceiling |
84
+ | --- | --- | --- |
85
+ | Task `offer.envelopeId` and matching journal `originals[].id` / `refs.task` | Task to dispatch | Dispatch/handout observation, not model consumption |
86
+ | `ExperimentAttemptRecord.attempt.taskId` | Task to research attempt | Does not identify the dispatch that caused it |
87
+ | Attempt id on output event and host observations | Attempt to output | Event is declared unless its stored owner observation proves more |
88
+ | Verified runner receipt `attemptId`, `protocolHash`, output name/hash | Attempt/output evidence | Preserve verified runner provenance; a runner output name is not a hub deliverable id |
89
+ | Review history `deliverables[].id/sha256`, `reviewedHead` | Output to review | Historical exact-hash/head binding, not current approval of edited bytes |
90
+ | Journal `previousId` | Exact predecessor metadata | `attempt` is a retry ordinal, not a research attempt id |
91
+
92
+ There is currently no persisted dispatch-to-research-attempt relationship.
93
+ `JournalDelivery.out` is rendered input handed to a peer, not produced output.
94
+ The fixture modeling current record relationships therefore leaves this gap. A separate deterministic
95
+ full-chain fixture supplies an explicit `declared-external` edge and keeps that
96
+ edge declared. This proves traversal, not native causal coverage. T1 does not
97
+ introduce a new relationship writer to make the gap disappear.
98
+
99
+ ## Bounds, privacy and pages
100
+
101
+ Pages contain at most 100 nodes, 200 edges and 262144 UTF-8 bytes of serialized
102
+ JSON, including metadata and cursor. A query can lower the node/edge ceilings;
103
+ its byte budget is between 4096 and 262144 bytes. Nodes and edges have independent
104
+ continuations: a page's edge may refer to a node in another page. Consumers combine
105
+ pages by the complete source reference and never label a truncated page complete.
106
+ Traversal follows only explicit edges in either direction from the named root.
107
+
108
+ After owner privacy projection, snapshot admission bounds query-local work to 2048 nodes, 4096 edges and 4 MiB of
109
+ projected metadata. Oversized sources are unavailable, not an empty or silently
110
+ partial successful graph. The owner must supply a complete indexed neighborhood
111
+ within this ceiling; larger graphs require a future owner paging contract.
112
+ This is a bounded projection ceiling, not a performance or full-board coverage claim.
113
+
114
+ The reader checks source authority before reading health, records or the root.
115
+ For callers without source-read authority, absent, denied and unavailable all
116
+ return exactly `{state: "unavailable"}`. Ordinary authorized readers get the
117
+ same shape for hidden or missing roots. Only an already authorized diagnostic
118
+ operator can see the closed reason `missing`, `corrupt`, `unsupported`, `withheld`,
119
+ `cursor_invalid` or `invalid`; never a hidden id, count or source prose.
120
+ Host privacy applies in the source owner's indexed read before assembling this
121
+ snapshot, so hidden rows never enter even its bound calculations or cursor.
122
+ Only the host supplies these snapshots; accepting a producer-supplied graph or
123
+ policy flags would violate this boundary. A hidden requested root is a source
124
+ `withheld` result. Hidden records neither consume public page slots nor create
125
+ dangling edges; a dangling edge in an admitted public snapshot is corrupt.
126
+
127
+ An available page has `{state: "available", nodes, edges, truncated, cursor?}`.
128
+ The cursor is an opaque continuation bound to source id/revision, caller scope,
129
+ root, limits and the visible graph. Changed source snapshots, privacy views,
130
+ roots or limits invalidate it. Cursor contents grant no authority. A cursor
131
+ is authenticated with a host-owned ephemeral HMAC key (32 to 128 UTF-8 bytes),
132
+ never a key supplied by a query. This reader creates or persists no key; key
133
+ rotation/restart invalidates prior cursors. Edited offsets are refused.
134
+ A cursor
135
+ must be used only with the same admitted source; on invalidation refresh from
136
+ the root. Source health failure is never a complete empty graph. No cursor is
137
+ returned on a complete page.
138
+
139
+ ## Verification boundary
140
+
141
+ `test/execution-reference.test.ts` consumes the neutral vectors and pins closed
142
+ parsing. `test/execution-lineage.test.ts` pins explicit traversal, missing edges,
143
+ independent states, evidence ceilings, privacy, unavailable equivalence, bounded
144
+ pages and source/scope/root invalidation. Fixtures run without a daemon, native
145
+ agent or account. CLI/MCP equivalence, admission, review currency and native
146
+ coverage remain T2/T3 work; these fixtures do not claim those acceptance legs.
@@ -61,6 +61,16 @@ The task board is the shared record of work and its authority, not a transcript
61
61
 
62
62
  ## Authority and state
63
63
 
64
+ Visual attachments (#1058) annotate public work without changing a task's state,
65
+ outcome or evidence authority. Publication checks current role, turn, task and
66
+ privacy admission before record/bus effects and after awaited file reads. A PII
67
+ task or private turn refuses publication; a task becoming private also withholds
68
+ its retained visual from public reads and assets. A visual id is not a verdict or
69
+ deliverable receipt, and visual-only calls do not count as task progress for
70
+ no-write detection. Ordinary shared tools and the local worker can publish where
71
+ their role permits; isolated fresh-reviewer MCP keeps its exact two-tool contract.
72
+
73
+
64
74
  `Tasks` mediates task proposals, planning, owner/reviewer changes, acceptance, completion and review. `Board` persists the rows, history and typed evidence in SQLite; transport, CLI and UI layers do not independently apply task transitions. The issue remains the change spec and GitHub remains authority for PR/check/tag facts. Separating these authorities prevents a chat claim or adapter callback from bypassing task policy ([#481](https://github.com/STAIxBWLB/agent-hub/issues/481)).
65
75
 
66
76
  A command is authorized against the current actor, role, task state and revision. An asynchronous operation can lose authority while preparing evidence, so write boundaries recheck current scope. Console actions bind the selected task ID and its observed flow revision, rather than an unstable list position. The reason is to keep an approved action attached to the row and state the person actually saw ([#424](https://github.com/STAIxBWLB/agent-hub/issues/424), [#525](https://github.com/STAIxBWLB/agent-hub/issues/525)).