@aragon-agent/cli 0.6.43 → 0.6.45

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 (251) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +69 -2
  3. package/dist/agent/controller.d.ts +69 -0
  4. package/dist/agent/controller.js +224 -11
  5. package/dist/agent/governance-view.d.ts +40 -0
  6. package/dist/agent/governance-view.js +40 -0
  7. package/dist/agent/governance-wiring.d.ts +162 -0
  8. package/dist/agent/governance-wiring.js +470 -0
  9. package/dist/agent/plan-prompt.d.ts +7 -0
  10. package/dist/agent/plan-prompt.js +2 -1
  11. package/dist/agent/reducer.d.ts +33 -1
  12. package/dist/agent/reducer.js +45 -9
  13. package/dist/agent/system-prompt.d.ts +19 -0
  14. package/dist/agent/system-prompt.js +9 -1
  15. package/dist/cli.js +45 -1
  16. package/dist/commands/builtins.js +10 -4
  17. package/dist/commands/governance-command.d.ts +35 -0
  18. package/dist/commands/governance-command.js +292 -0
  19. package/dist/commands/governance-runs-table.d.ts +40 -0
  20. package/dist/commands/governance-runs-table.js +130 -0
  21. package/dist/commands/registry.d.ts +12 -0
  22. package/dist/config/app-paths.d.ts +10 -0
  23. package/dist/config/app-paths.js +12 -0
  24. package/dist/config/cli-commands.d.ts +18 -0
  25. package/dist/config/cli-commands.js +47 -1
  26. package/dist/config/env.d.ts +2 -1
  27. package/dist/config/env.js +10 -1
  28. package/dist/config/governance-config.d.ts +125 -0
  29. package/dist/config/governance-config.js +164 -0
  30. package/dist/config/governance-launch.d.ts +71 -0
  31. package/dist/config/governance-launch.js +58 -0
  32. package/dist/config/load.d.ts +15 -0
  33. package/dist/config/load.js +26 -1
  34. package/dist/config/model-profile-store.d.ts +4 -2
  35. package/dist/config/schema.d.ts +22 -0
  36. package/dist/config/schema.js +6 -0
  37. package/dist/config/store.js +14 -1
  38. package/dist/config/ui-state.d.ts +11 -0
  39. package/dist/config/ui-state.js +11 -0
  40. package/dist/diagnostics/doctor.js +20 -0
  41. package/dist/governance/card-normalize.d.ts +24 -0
  42. package/dist/governance/card-normalize.js +76 -0
  43. package/dist/governance/copy.d.ts +109 -0
  44. package/dist/governance/copy.js +223 -0
  45. package/dist/governance/engine/aftermath.d.ts +49 -0
  46. package/dist/governance/engine/aftermath.js +134 -0
  47. package/dist/governance/engine/artifact-persist.d.ts +35 -0
  48. package/dist/governance/engine/artifact-persist.js +86 -0
  49. package/dist/governance/engine/budget.d.ts +52 -0
  50. package/dist/governance/engine/budget.js +112 -0
  51. package/dist/governance/engine/cards.d.ts +18 -0
  52. package/dist/governance/engine/cards.js +186 -0
  53. package/dist/governance/engine/digest.d.ts +32 -0
  54. package/dist/governance/engine/digest.js +63 -0
  55. package/dist/governance/engine/engine-core.d.ts +219 -0
  56. package/dist/governance/engine/engine-core.js +562 -0
  57. package/dist/governance/engine/engine.d.ts +68 -0
  58. package/dist/governance/engine/engine.js +314 -0
  59. package/dist/governance/engine/estimate.d.ts +38 -0
  60. package/dist/governance/engine/estimate.js +46 -0
  61. package/dist/governance/engine/inspector.d.ts +16 -0
  62. package/dist/governance/engine/inspector.js +396 -0
  63. package/dist/governance/engine/integration.d.ts +35 -0
  64. package/dist/governance/engine/integration.js +220 -0
  65. package/dist/governance/engine/lifecycle.d.ts +29 -0
  66. package/dist/governance/engine/lifecycle.js +656 -0
  67. package/dist/governance/engine/pause.d.ts +56 -0
  68. package/dist/governance/engine/pause.js +107 -0
  69. package/dist/governance/engine/phase-members.d.ts +15 -0
  70. package/dist/governance/engine/phase-members.js +34 -0
  71. package/dist/governance/engine/phase-planner.d.ts +53 -0
  72. package/dist/governance/engine/phase-planner.js +282 -0
  73. package/dist/governance/engine/rebuild.d.ts +27 -0
  74. package/dist/governance/engine/rebuild.js +57 -0
  75. package/dist/governance/engine/report.d.ts +66 -0
  76. package/dist/governance/engine/report.js +220 -0
  77. package/dist/governance/engine/run-digest.d.ts +20 -0
  78. package/dist/governance/engine/run-digest.js +42 -0
  79. package/dist/governance/engine/spec-builder.d.ts +117 -0
  80. package/dist/governance/engine/spec-builder.js +321 -0
  81. package/dist/governance/engine/stage-runner.d.ts +22 -0
  82. package/dist/governance/engine/stage-runner.js +664 -0
  83. package/dist/governance/engine/start-input.d.ts +19 -0
  84. package/dist/governance/engine/start-input.js +111 -0
  85. package/dist/governance/engine/state-machine.d.ts +101 -0
  86. package/dist/governance/engine/state-machine.js +586 -0
  87. package/dist/governance/engine/submission-queries.d.ts +42 -0
  88. package/dist/governance/engine/submission-queries.js +97 -0
  89. package/dist/governance/engine/submission-render.d.ts +12 -0
  90. package/dist/governance/engine/submission-render.js +92 -0
  91. package/dist/governance/engine/submissions.d.ts +27 -0
  92. package/dist/governance/engine/submissions.js +455 -0
  93. package/dist/governance/engine/visibility.d.ts +104 -0
  94. package/dist/governance/engine/visibility.js +223 -0
  95. package/dist/governance/governed-dispatch.d.ts +19 -0
  96. package/dist/governance/governed-dispatch.js +17 -0
  97. package/dist/governance/limits.d.ts +107 -0
  98. package/dist/governance/limits.js +131 -0
  99. package/dist/governance/posture-transition.d.ts +38 -0
  100. package/dist/governance/posture-transition.js +44 -0
  101. package/dist/governance/posture.d.ts +35 -0
  102. package/dist/governance/posture.js +59 -0
  103. package/dist/governance/profiles.d.ts +48 -0
  104. package/dist/governance/profiles.js +140 -0
  105. package/dist/governance/prompts/orchestrator-block.d.ts +40 -0
  106. package/dist/governance/prompts/orchestrator-block.js +128 -0
  107. package/dist/governance/prompts/plan-addendum.d.ts +22 -0
  108. package/dist/governance/prompts/plan-addendum.js +60 -0
  109. package/dist/governance/prompts/refusals.d.ts +48 -0
  110. package/dist/governance/prompts/refusals.js +82 -0
  111. package/dist/governance/prompts/role-blocks.d.ts +40 -0
  112. package/dist/governance/prompts/role-blocks.js +255 -0
  113. package/dist/governance/report-reader.d.ts +31 -0
  114. package/dist/governance/report-reader.js +57 -0
  115. package/dist/governance/roles.d.ts +83 -0
  116. package/dist/governance/roles.js +142 -0
  117. package/dist/governance/rules/claims.d.ts +83 -0
  118. package/dist/governance/rules/claims.js +250 -0
  119. package/dist/governance/rules/elimination.d.ts +66 -0
  120. package/dist/governance/rules/elimination.js +105 -0
  121. package/dist/governance/rules/events-table.d.ts +46 -0
  122. package/dist/governance/rules/events-table.js +94 -0
  123. package/dist/governance/rules/failure-analysis.d.ts +28 -0
  124. package/dist/governance/rules/failure-analysis.js +93 -0
  125. package/dist/governance/rules/ledger-replay.d.ts +53 -0
  126. package/dist/governance/rules/ledger-replay.js +53 -0
  127. package/dist/governance/rules/ledger-state.d.ts +85 -0
  128. package/dist/governance/rules/ledger-state.js +175 -0
  129. package/dist/governance/rules/ledger.d.ts +228 -0
  130. package/dist/governance/rules/ledger.js +560 -0
  131. package/dist/governance/rules/rca.d.ts +68 -0
  132. package/dist/governance/rules/rca.js +220 -0
  133. package/dist/governance/rules/settlement.d.ts +129 -0
  134. package/dist/governance/rules/settlement.js +363 -0
  135. package/dist/governance/rules/suspicion.d.ts +66 -0
  136. package/dist/governance/rules/suspicion.js +169 -0
  137. package/dist/governance/store/artifacts.d.ts +81 -0
  138. package/dist/governance/store/artifacts.js +173 -0
  139. package/dist/governance/store/atomic.d.ts +79 -0
  140. package/dist/governance/store/atomic.js +119 -0
  141. package/dist/governance/store/events-log.d.ts +35 -0
  142. package/dist/governance/store/events-log.js +86 -0
  143. package/dist/governance/store/run-lock.d.ts +51 -0
  144. package/dist/governance/store/run-lock.js +122 -0
  145. package/dist/governance/store/run-store.d.ts +229 -0
  146. package/dist/governance/store/run-store.js +644 -0
  147. package/dist/governance/store/scoreboard-export.d.ts +53 -0
  148. package/dist/governance/store/scoreboard-export.js +65 -0
  149. package/dist/governance/tools/child-tools.d.ts +31 -0
  150. package/dist/governance/tools/child-tools.js +65 -0
  151. package/dist/governance/tools/guard.d.ts +71 -0
  152. package/dist/governance/tools/guard.js +194 -0
  153. package/dist/governance/tools/inspector-tools.d.ts +63 -0
  154. package/dist/governance/tools/inspector-tools.js +424 -0
  155. package/dist/governance/tools/lead-tools.d.ts +42 -0
  156. package/dist/governance/tools/lead-tools.js +226 -0
  157. package/dist/governance/tools/normalize-util.d.ts +101 -0
  158. package/dist/governance/tools/normalize-util.js +230 -0
  159. package/dist/governance/tools/scan-incomplete.d.ts +79 -0
  160. package/dist/governance/tools/scan-incomplete.js +486 -0
  161. package/dist/governance/tools/shared-tools.d.ts +44 -0
  162. package/dist/governance/tools/shared-tools.js +125 -0
  163. package/dist/governance/tools/submit-normalizers.d.ts +28 -0
  164. package/dist/governance/tools/submit-normalizers.js +474 -0
  165. package/dist/governance/tools/submit-tools.d.ts +26 -0
  166. package/dist/governance/tools/submit-tools.js +171 -0
  167. package/dist/governance/types.d.ts +661 -0
  168. package/dist/governance/types.js +27 -0
  169. package/dist/proc/kill-tree.d.ts +19 -1
  170. package/dist/proc/kill-tree.js +32 -5
  171. package/dist/proc/limits.d.ts +19 -0
  172. package/dist/proc/limits.js +19 -0
  173. package/dist/proc/supervisor.js +8 -1
  174. package/dist/session/validate-session.js +32 -1
  175. package/dist/team/runtime.d.ts +8 -0
  176. package/dist/team/runtime.js +9 -0
  177. package/dist/team/subagent.d.ts +23 -0
  178. package/dist/team/subagent.js +14 -3
  179. package/dist/team/types.d.ts +6 -0
  180. package/dist/tools/bounded-spawn.d.ts +56 -0
  181. package/dist/tools/bounded-spawn.js +76 -0
  182. package/dist/tools/index.d.ts +30 -7
  183. package/dist/tools/index.js +14 -0
  184. package/dist/tools/plan-tools.d.ts +14 -2
  185. package/dist/tools/plan-tools.js +5 -2
  186. package/dist/tools/search-tools.js +52 -44
  187. package/dist/ui/App.js +145 -25
  188. package/dist/ui/BottomStatusRow.js +1 -1
  189. package/dist/ui/Composer.d.ts +7 -1
  190. package/dist/ui/Composer.js +9 -6
  191. package/dist/ui/GovernancePanel.d.ts +50 -0
  192. package/dist/ui/GovernancePanel.js +77 -0
  193. package/dist/ui/GovernanceRun.d.ts +18 -0
  194. package/dist/ui/GovernanceRun.js +11 -0
  195. package/dist/ui/ScrollbackShell.d.ts +35 -7
  196. package/dist/ui/ScrollbackShell.js +1 -1
  197. package/dist/ui/StatusBar.d.ts +15 -2
  198. package/dist/ui/StatusBar.js +10 -4
  199. package/dist/ui/Transcript.js +3 -0
  200. package/dist/ui/entries/GovernanceCard.d.ts +30 -0
  201. package/dist/ui/entries/GovernanceCard.js +32 -0
  202. package/dist/ui/governance-copy.d.ts +69 -0
  203. package/dist/ui/governance-copy.js +104 -0
  204. package/dist/ui/governance-status.d.ts +145 -0
  205. package/dist/ui/governance-status.js +326 -0
  206. package/dist/ui/interaction-copy.d.ts +4 -0
  207. package/dist/ui/interaction-copy.js +5 -0
  208. package/dist/ui/interaction-hints.d.ts +17 -0
  209. package/dist/ui/interaction-hints.js +14 -2
  210. package/dist/ui/layout/governance-board.d.ts +121 -0
  211. package/dist/ui/layout/governance-board.js +382 -0
  212. package/dist/ui/layout/governance-card.d.ts +53 -0
  213. package/dist/ui/layout/governance-card.js +134 -0
  214. package/dist/ui/layout/governance-chip.d.ts +38 -0
  215. package/dist/ui/layout/governance-chip.js +64 -0
  216. package/dist/ui/layout/governance-panel.d.ts +85 -0
  217. package/dist/ui/layout/governance-panel.js +358 -0
  218. package/dist/ui/layout/governance-roster.d.ts +65 -0
  219. package/dist/ui/layout/governance-roster.js +275 -0
  220. package/dist/ui/layout/status-detail-layout.js +3 -3
  221. package/dist/ui/layout/status-layout.d.ts +5 -2
  222. package/dist/ui/layout/status-layout.js +10 -6
  223. package/dist/ui/layout/virtual-window.js +11 -0
  224. package/dist/ui/overlays/GovernanceBoardOverlay.d.ts +41 -0
  225. package/dist/ui/overlays/GovernanceBoardOverlay.js +76 -0
  226. package/dist/ui/overlays/GovernanceSettingsPage.d.ts +48 -0
  227. package/dist/ui/overlays/GovernanceSettingsPage.js +93 -0
  228. package/dist/ui/overlays/HelpOverlay.js +4 -3
  229. package/dist/ui/overlays/QuestionOverlay.d.ts +9 -1
  230. package/dist/ui/overlays/QuestionOverlay.js +4 -2
  231. package/dist/ui/overlays/SettingsScreen.d.ts +7 -0
  232. package/dist/ui/overlays/SettingsScreen.js +21 -1
  233. package/dist/ui/overlays/governance-settings.d.ts +79 -0
  234. package/dist/ui/overlays/governance-settings.js +140 -0
  235. package/dist/ui/overlays/model-profile-state.d.ts +6 -1
  236. package/dist/ui/overlays/model-profile-state.js +10 -0
  237. package/dist/ui/palettes.d.ts +17 -0
  238. package/dist/ui/palettes.js +9 -0
  239. package/dist/ui/scrollback/LiveTail.d.ts +3 -1
  240. package/dist/ui/scrollback/budget.d.ts +12 -6
  241. package/dist/ui/scrollback/budget.js +11 -4
  242. package/dist/ui/scrollback/live-preview.js +2 -0
  243. package/dist/ui/scrollback/settled-boundary.js +3 -0
  244. package/dist/ui/scrollback/tail-anchor.d.ts +5 -2
  245. package/dist/ui/scrollback/tail-anchor.js +3 -1
  246. package/dist/ui/shortcuts.js +2 -2
  247. package/dist/ui/theme.d.ts +7 -0
  248. package/dist/ui/theme.js +1 -0
  249. package/dist/ui/transcript-text.js +9 -0
  250. package/dist/ui/use-wheel-routing.js +3 -1
  251. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -8,6 +8,44 @@ were written.
8
8
  ## Unreleased
9
9
 
10
10
  ### Added
11
+ - **Governance mode - a multi-agent "legion" collaboration posture.** `Shift+Tab` (and `Ctrl+N`)
12
+ now cycle `BUILD -> PLAN -> GOVERN -> PLAN+GOVERN -> BUILD`; `/gov` goes to any of them
13
+ directly. In `GOVERN` the main agent becomes an **Orchestrator** that cannot write files or run
14
+ shell commands (enforced at the tool layer) and drives a team under a separation of powers
15
+ (development, review, testing, plus an unscored inspector) and a GT token economy: automatic
16
+ scoring from structured verdicts, escrow released only when integration passes, clawback on
17
+ failure, elimination and respawn. `PLAN+GOVERN` interviews you for at least
18
+ `governance.minAskRounds` answered rounds before the plan is accepted, then runs the approved
19
+ plan as the specification. New: the `gov_run` / `gov_dispatch` tools (never part of
20
+ `aragon exec`), the `/gov` command (`status|on|plan|off|board|runs|resume|abort|budget|help`), a
21
+ Legion panel, settlement cards in the transcript, a `/gov board` overview, the `governAccent`
22
+ theme token, `--govern` / `--plan-govern` / `--no-governance`, the `governance.*` config keys
23
+ and `ARAGON_GOVERNANCE=0`. Runs are persisted under `<home>/governance/` and survive `Esc`,
24
+ crashes and restarts. With governance disabled the system prompt, the tool list and the tool
25
+ timeout table are byte-identical to before. Cost note: the two orchestrator tool definitions
26
+ add a small per-request overhead to every session in which governance is available.
27
+ - **Governance mode, round two: a gentle stop, a settings page, visible cost, a report you can
28
+ re-read.** `/gov pause` pauses a governed run at the end of the stage that is running - nothing is
29
+ killed, no submission is lost, no GT moves (Esc is still the immediate stop) - and pauses at once
30
+ when no stage is running; `/gov pause cancel` withdraws a request that has not fired. While it
31
+ waits, the panel header reads `pausing after REVIEW`, the status chip `GOV pausing`, and the hint
32
+ row `pause pending: /gov pause cancel`. A run that is `escalated` (waiting on your ruling) is never
33
+ overwritten by a pause. The Orchestrator is told to stop and wait, and not to resume a run you
34
+ paused. **Settings > Governance** is now a page (a row under Team): the eleven `governance.*`
35
+ keys adjust with the arrow keys and save with the rest of Settings; turning `Governance` on or
36
+ off says that it applies next launch. `/gov runs` is a table with phase, health, team tokens,
37
+ cost (`n/a` when the model is unpriced, never `$0.00`) and the last update, and marks unfinished
38
+ runs with `*`; the columns drop with the terminal width. `/gov report [runId]` re-reads the final
39
+ report of a run (or its newest phase report, or the board) in the transcript, capped at 120
40
+ lines with the full path. The charter card, `/gov status` and the Orchestrator's `gov_run start`
41
+ result now carry a **dispatch plan** (`Dispatch plan: 10 for a clean run; a rework round adds up to
42
+ 3, a fix cycle up to 2; the run is capped at 120. Counts only`). It states counts of
43
+ `gov_dispatch` calls only - never tokens or money - and it is deliberately not an upper bound.
44
+ A model-free screen gallery (`GOV_GALLERY=1 npx vitest run governance-gallery`) renders every
45
+ governance surface at 60/80/100/160 columns in colour, no-colour and ASCII variants. No config
46
+ key changed. On disk, `index.jsonl` rows gain an optional `digest` and a run now appends at most
47
+ one extra index row per change of that digest (about 250 rows for a run of 120 dispatches);
48
+ older rows read back as `-`.
11
49
  - **Automatic skill triage, and no round limit on questions.** Two changes that
12
50
  share one idea: replace a hard limit with guidance the agent can act on.
13
51
  `ask_user` is no longer capped at four rounds per turn — the cap, its prompt
@@ -168,6 +206,12 @@ were written.
168
206
  the same bytes it wrote before.
169
207
 
170
208
  ### Fixed
209
+ - `/gov runs` listed the **oldest** ten governed runs instead of the newest ten once there
210
+ were more than ten (the store already returned newest first and the listing then took the last
211
+ ten and reversed them). It now shows the newest ten, newest first.
212
+ - A settings save now composes the Orchestrator prompt from the config being saved, not from the
213
+ one it replaces, so the limits the prompt quotes (`maxRework`, `maxWorkPackages`, ...) are the
214
+ new ones the moment Settings closes.
171
215
  - Changing only the fan-out width no longer drops the subagent model a session kit
172
216
  installed. The settings save read "the `team` section was written" as "the user
173
217
  re-decided the subagent slot", so adjusting `Max subagents` silently released a slot the
package/README.md CHANGED
@@ -950,7 +950,8 @@ ignores them rather than rejecting the library):
950
950
  | `/save [file]` | Export the session to JSON - a file you keep, unchanged by this feature |
951
951
  | `/resume [file]` | Load a saved session. This session moves to a NEW id, and the spend counter restarts |
952
952
  | `/session [copy\|pin]` | Report this session's id, file and branch parent; `copy` puts `aragon --resume <id>` on the clipboard; `pin` makes `sessions prune` skip the file forever |
953
- | `/plan [on\|off\|status]` | Toggle plan mode - the keyboard-free equivalent of `Shift+Tab` |
953
+ | `/plan [on\|off\|status]` | Toggle plan mode (and leave governance) - the keyboard-free equivalent of `Shift+Tab` |
954
+ | `/gov [status\|on\|plan\|off\|board\|runs\|report [id]\|resume [id]\|pause [cancel]\|abort\|budget <n\|off>\|help]` | **Governance mode**: a governed engineering team (dev / review / test / inspector) with a GT scoring economy. `on` = GOVERN, `plan` = PLAN+GOVERN (interview first), `board` opens the full-screen overview, `runs` lists past runs with phase / health / tokens / cost, `report` re-reads a run's report, `pause` stops at the end of the running stage (nothing is killed), `abort` stops the running stage at once (the run is resumable). See [Governance mode](#governance-mode) |
954
955
  | `/team [on\|off\|status]` / `/team [-n <n>] [--parts a,b,c] [--read-only] <brief>` | **Force team mode and start now.** `/team <brief>` arms forced team mode and submits that work as a normal turn: the lead analyses and plans it, then calls `task` — the directive, the `<forced_team_mode>` prompt block and a mechanical end-of-turn gate all say so. `/team on` arms without submitting, `/team off` leaves, `/team` reports. The configuration it used to carry (team switch, width, concurrency, supervisor, subagent model) lives in `Settings > Team` |
955
956
  | `/todo [on\|off\|panel on\|off\|follow <mode>\|acceptance on\|off\|reset\|clear\|continue]` | Todo planning: status, the on/off switch, the rail, follow-through, acceptance criteria, and picking an unfinished plan back up |
956
957
  | `/retry [show\|on\|off\|max <n>]` | API retry: the effective ladder, the on/off switch, the retry count. Takes effect **in the running session**, children included |
@@ -984,7 +985,7 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
984
985
  | `Ctrl+G` | **快捷键抽屉**:自上而下列出全部快捷键、各自的作用关键词,选中行下方再给一句话说明。`Up`/`Down`(或 `k`/`j`)移动、`PgUp`/`PgDn` 翻页、`d` 切换状态详情行、`r` 重新生成任务摘要、`Esc` 关闭。再按一次 `Ctrl+G` 关闭;其它参考类浮层打开时按它会直接接管,等待决策的 `confirm` / `question` / `plan` 除外 |
985
986
  | `Enter` | 空闲时发送,运行时加入队列;收到 Core 已纳入会话的接收凭据后才移除 Queue 提示 |
986
987
  | `Ctrl+J` / `Alt+Enter` / `Shift+Enter` | 插入换行。应用默认请求终端区分修饰键(Windows 用 win32-input-mode,macOS/Linux 用 kitty/modifyOtherKeys),Shift+Enter 在支持的终端开箱即用;不支持的终端用 Ctrl+J。参见 [终端设置](#terminal-setup-for-shiftenter) |
987
- | `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan`. On Windows this needs a console flag Node < 22.17 does not set; aragon sets it at startup — see [Requirements](#requirements) |
988
+ | `Shift+Tab` | Cycle the posture `BUILD` -> `PLAN` -> `GOVERN` -> `PLAN+GOVERN` -> `BUILD` (two states when governance is unavailable or `governance.shiftTab` is off). Equivalent: `/plan`, `/gov`. On Windows this needs a console flag Node < 22.17 does not set; aragon sets it at startup — see [Requirements](#requirements) |
988
989
  | `Ctrl+N` | The same toggle, over a channel no Windows console can swallow. Use it if `Shift+Tab` does nothing. Written `Ctrl+N` everywhere, never `^N` - `^N` is the header's line-offset chip |
989
990
  | `Ctrl+P` | **Quick prompt library**: the manager - create, edit, delete, favourite and reorder your own prompts. See [Quick prompt library](#quick-prompt-library) |
990
991
  | `Ctrl+B` | **Agents board**: the details of a running dispatch. Left column lists every child (not just the five the roster fits), the right-hand sections show the selected child's state, turns, tools, tokens, files, mail, supervisor actions, retries, compaction, its current activity, its final answer or error, and the last 24 events. `Up` / `Down` change the child, `PgUp` / `PgDn` and the wheel page, `Esc` closes. The board survives the end of the dispatch - it freezes the last live snapshot and marks it `dispatch ended` - and opens after the fact from the session's own team card, where everything except the live-only sections (current activity, mail count, timeline) is still readable. Pressing `Ctrl+B` again, or `/agents`, opens and closes it |
@@ -1539,6 +1540,72 @@ rung. `--no-render-governor` (or `renderGovernor: false`, or
1539
1540
  `ARAGON_RENDER_GOVERNOR=0`) turns the adaptation off entirely; the output is
1540
1541
  identical, just heavier under load.
1541
1542
 
1543
+ ## Governance mode
1544
+
1545
+ `GOVERN` and `PLAN+GOVERN` are two more postures on the same `Shift+Tab` cycle. In either,
1546
+ your session is led by an **Orchestrator** that cannot touch your files: it reads, asks you what
1547
+ it must, splits the work into work packages (WPs) and drives a governed team through the
1548
+ `gov_run` / `gov_dispatch` tools. Every implementation step is done by a dispatched agent, and
1549
+ the writer of a change is never the one who judges it.
1550
+
1551
+ | Power | Role | What it does |
1552
+ | --- | --- | --- |
1553
+ | Development | `DEV` | Implements one WP completely and reports which interface contracts it verified |
1554
+ | Review | `REV` | Reviews independently, may reject or fix small issues directly |
1555
+ | Testing | `TEST` | Verifies behaviour and traces every acceptance criterion to evidence |
1556
+ | Inspector | `INSP` | The fourth power: rules on claims, checks contracts, runs the registered integration commands, arbitrates. Not scored |
1557
+
1558
+ **GT (governance tokens)** is a score for the run, unrelated to LLM context tokens. Every
1559
+ DEV / REV / TEST agent starts with 100 GT. Rule-engine events are booked automatically from
1560
+ structured verdicts (a passed review is +15, a rejection -20, ...); positive scores are held in
1561
+ **escrow** until the phase's integration check passes, and are clawed back from whoever caused a
1562
+ failure. An agent whose confirmed score reaches zero is replaced by a successor that is told why.
1563
+ The inspector books only judgement events, and each one needs written evidence.
1564
+
1565
+ Each phase runs `dev -> review -> test -> settle -> integrate` (plus `fix` and a second
1566
+ `integrate` if integration fails). The engine refuses illegal orders and every tool result ends
1567
+ with `NEXT:` - the legal next actions - so a compacted or `/clear`ed session can resume.
1568
+ Disputes go to the inspector first and, if fundamental, to you (`ask_user`), and your ruling
1569
+ closes the loop with `gov_run resolve`.
1570
+
1571
+ `PLAN+GOVERN` first runs the normal plan mode with at least `governance.minAskRounds` (default 2)
1572
+ **answered** question rounds before `submit_plan` is accepted; approving the plan switches to
1573
+ `GOVERN` and the plan becomes the specification.
1574
+
1575
+ While a stage runs, a compact panel shows the pipeline, the roster, the GT bars and the latest
1576
+ ledger movements; the transcript gets a card only at charter, phase settlement, escalation and
1577
+ close. `/gov board` opens the full overview. `Esc` stops the running stage; the run is saved
1578
+ (`<home>/governance/<runId>/`) and `/gov resume` picks it up, re-dispatching only the missing
1579
+ work. A stage in flight defers any posture change until it ends.
1580
+
1581
+ **Stopping gently.** `/gov pause` lets the stage that is running finish - every submission lands,
1582
+ nothing is killed or rolled back, no GT moves - and then pauses the run; with no stage running it
1583
+ pauses at once. `/gov pause cancel` withdraws a request that has not fired, and `/gov resume`
1584
+ continues a paused run. Unlike `Esc`, it never throws away work in progress. A run that is waiting
1585
+ on your ruling is left as it is.
1586
+
1587
+ **Cost before you start.** The charter, `/gov status` and the `gov_run start` result carry a
1588
+ dispatch plan: how many `gov_dispatch` calls a clean run needs (stages wider than
1589
+ `team.maxSubagents` are split, which adds calls), how many one more rework round or fix cycle adds,
1590
+ and the hard cap of 120 per run. These are counts of calls, not a forecast of tokens or money, and
1591
+ not an upper bound on the total.
1592
+
1593
+ **Looking back.** `/gov runs` shows each run's phase, health, team tokens, cost (`n/a` for an
1594
+ unpriced model) and last update, newest first, with `*` on runs you can still resume. `/gov report
1595
+ [runId]` prints the run's final report (else its newest phase report, else the board) in the
1596
+ transcript, up to 120 lines and the path of the full file. Nothing is regenerated; it only reads.
1597
+
1598
+ Limits and switches (`aragon config set governance.<key> <value>`, or **Settings > Governance**): `enabled`, `shiftTab`,
1599
+ `panel`, `profile` (`general` / `ml` / `product` / `api-security`), `maxWorkPackages` (<= 8),
1600
+ `maxPhases` (<= 6), `maxRework` (<= 5), `maxFixCycles` (<= 5), `minAskRounds` (0-5),
1601
+ `budgetTokens` (team tokens only; 0 = unlimited), `confirmCharter`. `ARAGON_GOVERNANCE=0` and
1602
+ `--no-governance` disable it; `--govern` / `--plan-govern` start in a governed posture. It is not
1603
+ available in `aragon exec` / `-p`. Governed agents are team agents: each one spends tokens, so a
1604
+ run costs several times a single-agent turn - watch the status-bar chip and set `budgetTokens`.
1605
+
1606
+ The information boundary is a tripwire, not a sandbox: roles are shown only what they may see and
1607
+ cannot read the governance store through the tools, but a determined `bash` command could.
1608
+
1542
1609
  ## Plan mode
1543
1610
 
1544
1611
  `aragon` has two postures, and `Shift+Tab` switches between them. `/plan` does
@@ -35,6 +35,12 @@ import type { TaskSummaryOutcome } from '../fast/task-summary-types.js';
35
35
  import { ContextMeter, type ContextUsageListener } from '../compaction/meter.js';
36
36
  import type { CompactionEventListener, CompactionSnapshot, ContextUsageSnapshot } from '../compaction/types.js';
37
37
  import type { AcceptanceItem, TodoEventListener, TodoSnapshot } from '../todo/types.js';
38
+ import { GovernanceWiring } from './governance-wiring.js';
39
+ import { type GovernanceConfig } from '../config/governance-config.js';
40
+ import { type Posture } from '../governance/posture.js';
41
+ import type { GovernanceAvailability } from '../governance/copy.js';
42
+ import type { PauseOutcome } from '../governance/engine/pause.js';
43
+ import type { GovernanceEventListener, GovernanceSnapshot } from '../governance/types.js';
38
44
  import { type HumanInputGate } from '../tools/human-input.js';
39
45
  import { SkillService, type ApprovalGate } from '../skills/service.js';
40
46
  import type { SkillRoutingReport } from '../skills/triage.js';
@@ -198,6 +204,12 @@ export interface AgentModeState {
198
204
  /** Non-null only while a `plan -> build` switch is waiting for `agent_end`. */
199
205
  pending: AgentMode | null;
200
206
  }
207
+ /** What `setPosture` adopted (governance-mode 4.2): the same contract as `AgentModeState`. */
208
+ export interface PostureState {
209
+ effective: Posture;
210
+ /** A change waiting for `agent_end` (plan-to-build, or any change while a stage runs). */
211
+ pending: Posture | null;
212
+ }
201
213
  export interface PreflightResult {
202
214
  ok: boolean;
203
215
  message?: string;
@@ -297,6 +309,11 @@ export declare class AgentController {
297
309
  private readonly planInteractive;
298
310
  private readonly teamRegistered;
299
311
  private readonly teamRuntime;
312
+ /**
313
+ * Governance mode (governance-mode 14.2). Every decision lives in the wiring class; this
314
+ * file only forwards. `registered` inside it is fixed at construction (D-22).
315
+ */
316
+ private readonly governance;
300
317
  private forcedTeam;
301
318
  private readonly todoRegistered;
302
319
  private todoEnabled;
@@ -635,6 +652,58 @@ export declare class AgentController {
635
652
  applyPendingMode(): AgentModeState | null;
636
653
  /** Named read surface for `/plan status`, so no command reaches into internals. */
637
654
  getPlanStatus(): AgentModeState;
655
+ getPosture(): Posture;
656
+ getPostureState(): PostureState;
657
+ /** The Shift+Tab cycle: four states when governance is usable, else the classic two. */
658
+ getPostureCycle(): readonly Posture[];
659
+ isGovernanceAvailable(): GovernanceAvailability;
660
+ /** The precise sentence for why governance cannot be used now, or null. */
661
+ getGovernanceUnavailableText(): string | null;
662
+ /**
663
+ * Request a posture and return what was ADOPTED (the `setAgentMode` contract: the caller
664
+ * dispatches the returned value, never the requested one). Tighten at once, loosen when
665
+ * idle, and while a governed stage runs defer EVERYTHING to `agent_end` (D-23).
666
+ */
667
+ setPosture(next: Posture, opts?: {
668
+ force?: boolean;
669
+ }): PostureState;
670
+ /** Adopt a deferred posture. Call from `agent_end`. Null when nothing was pending. */
671
+ applyPendingPosture(): PostureState | null;
672
+ /** Esc / `/gov abort`: stop the in-flight governed stage (the run becomes `paused`). */
673
+ abortGovernanceStage(): void;
674
+ isGovernanceStageRunning(): boolean;
675
+ getGovernanceSnapshot(): GovernanceSnapshot | null;
676
+ /** Run + ledger behind the `/gov board` tabs that the snapshot alone cannot fill. */
677
+ getGovernanceBoardInput(): ReturnType<GovernanceWiring['boardInput']>;
678
+ subscribeGovernance(listener: GovernanceEventListener): () => void;
679
+ /** Recent governed runs (`/gov runs`). */
680
+ listGovernanceRuns(): ReturnType<GovernanceWiring['listRuns']>;
681
+ /** `/gov pause` and `/gov pause cancel`: stop at the next clean stage boundary. */
682
+ requestGovernancePause(): Promise<PauseOutcome>;
683
+ cancelGovernancePause(): boolean;
684
+ /** Whether the governance tools were registered at launch (Settings > Governance). */
685
+ isGovernanceRegistered(): boolean;
686
+ /** `/gov report [runId]`: a stored report, read-only. */
687
+ readGovernanceReport(runId?: string): ReturnType<GovernanceWiring['readReport']>;
688
+ /** The dispatch-plan line for the live run, for `/gov status`. */
689
+ getGovernanceDispatchPlan(): string | null;
690
+ /**
691
+ * The PLAN+GOVERN interview progress for the question overlay's chip, or null outside that
692
+ * posture or when no minimum is enforced (governance-mode 9.3).
693
+ */
694
+ getGovernanceInterview(): {
695
+ answered: number;
696
+ required: number;
697
+ } | null;
698
+ /** `/gov resume`: the run id to hand to the orchestrator, or null when none exists. */
699
+ findResumableGovernanceRun(runId?: string): Promise<string | null>;
700
+ getGovernanceConfig(): GovernanceConfig;
701
+ /** Adopt a governance config patch for this session and re-render (the block quotes limits). */
702
+ setGovernanceConfig(patch: Partial<GovernanceConfig>): GovernanceConfig;
703
+ /** `submit_plan` approval: PLAN+GOVERN hands over to the team, plain PLAN goes to BUILD. */
704
+ private onPlanApproved;
705
+ /** What a team child costs per token, or undefined for an unpriced model (never $0.00). */
706
+ private teamModelCost;
638
707
  /**
639
708
  * READ-ONLY guard probe, called by `ask_user` BEFORE it opens the overlay.
640
709
  *
@@ -48,6 +48,12 @@ import { TodoStore } from '../todo/store.js';
48
48
  import { createTodoTool } from '../todo/todo-tool.js';
49
49
  import { buildTodoBlock } from '../todo/prompt.js';
50
50
  import { createPlanTools } from '../tools/plan-tools.js';
51
+ import { GovernanceWiring } from './governance-wiring.js';
52
+ import { clampGovernanceConfig } from '../config/governance-config.js';
53
+ import { decidePostureTransition } from '../governance/posture-transition.js';
54
+ import { composePosture, splitPosture } from '../governance/posture.js';
55
+ import { APPROVED_GOVERN_RESULT } from '../governance/prompts/refusals.js';
56
+ import { GOV_LEAD_TOOL_NAMES } from '../governance/limits.js';
51
57
  import { ASK_GUARD_LIMITS, DENY_ALL_HUMAN_INPUT, questionFingerprint, } from '../tools/human-input.js';
52
58
  import { getLogger } from '../logging/logger.js';
53
59
  import { createNodeSkillHost } from '../skills/node-host.js';
@@ -190,6 +196,11 @@ export class AgentController {
190
196
  // -----------------------------------------------------------------------
191
197
  teamRegistered;
192
198
  teamRuntime;
199
+ /**
200
+ * Governance mode (governance-mode 14.2). Every decision lives in the wiring class; this
201
+ * file only forwards. `registered` inside it is fixed at construction (D-22).
202
+ */
203
+ governance;
193
204
  // -----------------------------------------------------------------------
194
205
  // Forced team mode (team-command-forced-dispatch §3.5 / D-4).
195
206
  //
@@ -447,6 +458,29 @@ export class AgentController {
447
458
  // snapshot of "is `task` in this.tools at all". The live switch is
448
459
  // `config.team.enabled`, read through `isTeamEnabled()` — no second copy.
449
460
  this.teamRegistered = config.team.enabled;
461
+ // BEFORE the runtime: its child hooks are read off this object. `getRuntime` is a
462
+ // closure because the engine (created lazily on first use) needs the runtime, and the
463
+ // runtime needs these hooks.
464
+ this.governance = new GovernanceWiring({
465
+ teamRegistered: this.teamRegistered,
466
+ config: () => this.config,
467
+ gate: humanInput,
468
+ getCwd: () => this.cwd,
469
+ getGateMode: () => this.effectiveMode,
470
+ withPausedWatchdog: (fn) => this.withPausedWatchdog(fn),
471
+ withHumanWait: (fn) => this.withHumanWait(fn),
472
+ hasApiKey: () => this.hasApiKey(),
473
+ modelCost: () => this.teamModelCost(),
474
+ getRuntime: () => this.teamRuntime,
475
+ });
476
+ // THE LAUNCH POSTURE (`--govern` / `--plan-govern`, governance-mode 11.2). Only when the
477
+ // tools were actually registered: `loadConfig` already degraded an unusable request to the
478
+ // classic posture, and this guards the remaining construction-time case.
479
+ const initialPosture = config.initialPosture;
480
+ if (this.governance.isRegistered() && (initialPosture === 'govern' || initialPosture === 'plan_govern')) {
481
+ this.governance.setGovernOn(true);
482
+ this.effectiveMode = initialPosture === 'plan_govern' ? 'plan' : 'build';
483
+ }
450
484
  this.teamRuntime = this.teamRegistered
451
485
  ? new TeamRuntime({
452
486
  getConfig: () => this.config,
@@ -530,6 +564,16 @@ export class AgentController {
530
564
  // and nothing anywhere reports it. Spread conditionally so a session
531
565
  // with no policy builds `SubagentDeps` exactly as it does today.
532
566
  ...(deps.permission ? { permission: deps.permission } : {}),
567
+ // GOVERNANCE CHILD HOOKS (governance-mode 13.2): present only when governance was
568
+ // registered, so every other session builds children exactly as before.
569
+ ...(this.governance.isRegistered()
570
+ ? {
571
+ extraToolsFor: (spec) => this.governance.childToolsFor(spec),
572
+ narrowToolsFor: (spec, tools) => this.governance.narrowToolsFor(spec, tools),
573
+ governanceRoleBlockFor: (spec) => this.governance.roleBlockFor(spec),
574
+ extraPolicyExempt: this.governance.childPolicyExempt(),
575
+ }
576
+ : {}),
533
577
  })
534
578
  : null;
535
579
  // ABOVE `createBuiltinTools`, and the ordering is load-bearing for the
@@ -654,7 +698,8 @@ export class AgentController {
654
698
  hasAnsweredQuestions: (questions) => this.hasAnswered(questions),
655
699
  noteAnsweredQuestions: (questions) => this.noteAnswered(questions),
656
700
  withHumanWait: (fn) => this.withHumanWait(fn),
657
- onPlanApproved: () => this.setAgentMode('build', { force: true }),
701
+ onPlanApproved: (plan) => this.onPlanApproved(plan),
702
+ beforeSubmit: () => this.governance.beforeSubmitPlan(),
658
703
  }),
659
704
  // THROUGH THE FACTORY for the reason `teamTools` records below (C-2), and
660
705
  // empty when todo planning was off at construction — which leaves the
@@ -742,14 +787,7 @@ export class AgentController {
742
787
  // table (I-2 byte parity); a fast or profile model the static
743
788
  // tables do not price yields undefined, so the report says
744
789
  // `pricing unknown` rather than fabricating $0.00 (C-11 / RV-4).
745
- modelCost: () => {
746
- const resolution = this.subagentModelResolution();
747
- if (resolution.effective === 'main')
748
- return this.getModelInfo().cost;
749
- return this.isPricedModel(resolution.ref)
750
- ? this.getModelInfoFor(resolution.ref).cost
751
- : undefined;
752
- },
790
+ modelCost: () => this.teamModelCost(),
753
791
  // The same resolution, re-read per dispatch, feeds the report's
754
792
  // disclosure line - one read per consumer, one authority both.
755
793
  subagentModel: () => this.subagentModelResolution(),
@@ -770,6 +808,20 @@ export class AgentController {
770
808
  }),
771
809
  ]
772
810
  : [],
811
+ // GOVERNANCE (governance-mode 14.3): the Orchestrator's two tools, the ceiling
812
+ // exemption for exactly those two names (D-22b: NOT HOST_TOOL_NAMES), and the live gate
813
+ // that refuses the lead's own writes. All three are spread ONLY when registered, so a
814
+ // session without governance builds the identical options bag.
815
+ ...(this.governance.isRegistered()
816
+ ? {
817
+ governTools: this.governance.leadTools(),
818
+ policyExempt: GOV_LEAD_TOOL_NAMES,
819
+ governGate: () => ({
820
+ governOn: this.governance.isGovernOn(),
821
+ mode: this.effectiveMode,
822
+ }),
823
+ }
824
+ : {}),
773
825
  // Read at CALL time, never cached: the mode flips mid-run when the user
774
826
  // approves a plan, and a cached 'plan' would refuse the work they just
775
827
  // authorized.
@@ -909,6 +961,14 @@ export class AgentController {
909
961
  // outlived a mid-session `Settings > Team` switch to the supervisor
910
962
  // and killed the dispatch the supervisor was meant to look after.
911
963
  ...(this.teamRegistered ? { task: 0 } : {}),
964
+ // P0 (governance-mode D-21 / RV-0): `gov_dispatch` runs a whole governed stage,
965
+ // routinely longer than `toolTimeoutMs`, and it listens to `ctx.signal` (it hands
966
+ // it to `TeamRuntime.dispatch`), which is what makes `0` safe. Registered in the SAME
967
+ // change as the tool itself: a registered tool with no entry here is killed by the
968
+ // executor at the default timeout on the first long stage, and no unit test with an
969
+ // instant stub dispatch would notice. Absent when unregistered, so the table is
970
+ // unchanged for every session without governance.
971
+ ...(this.governance.isRegistered() ? { gov_dispatch: 0 } : {}),
912
972
  },
913
973
  },
914
974
  });
@@ -1066,6 +1126,13 @@ export class AgentController {
1066
1126
  const triageBlock = triageOn && skillsCatalog.length > 0
1067
1127
  ? buildSkillTriageBlock({ candidates: this.turnRouting?.candidates ?? [] })
1068
1128
  : '';
1129
+ // GOVERNANCE (governance-mode 6.1). '' for every non-governed posture, which splices
1130
+ // nothing. In GOVERN with gate mode `build` the team blocks are dropped below: `task` is
1131
+ // refused there, and advertising it would describe a refused capability.
1132
+ const posture = this.getPosture();
1133
+ const governBlock = this.governance.orchestratorBlock(posture, config);
1134
+ const governAddendum = this.governance.planAddendum(posture, config);
1135
+ const dropTeamBlocks = governBlock !== '' && this.effectiveMode === 'build';
1069
1136
  return buildSystemPrompt({
1070
1137
  cwd: this.cwd,
1071
1138
  tools: this.tools,
@@ -1075,13 +1142,15 @@ export class AgentController {
1075
1142
  skillTriageBlock: triageBlock,
1076
1143
  agentMode: this.effectiveMode,
1077
1144
  planInteractive: this.planInteractive,
1145
+ ...(governBlock ? { governBlock } : {}),
1146
+ ...(governAddendum ? { governAddendum } : {}),
1078
1147
  // BOTH halves, and both are required (§4.5). `teamRegistered` false means
1079
1148
  // there is no `task` tool to advertise; `config.team.enabled` false means
1080
1149
  // the user turned the capability off (in settings — the only writer).
1081
1150
  // Advertising in either case would describe a capability the model does not
1082
1151
  // have. Read through the field rather than `this.isTeamEnabled()` so the
1083
1152
  // sentence and the code agree, as the two flags always have here (P1-4).
1084
- teamBlock: this.teamRegistered && this.config.team.enabled
1153
+ teamBlock: this.teamRegistered && this.config.team.enabled && !dropTeamBlocks
1085
1154
  ? buildTeamBlock({
1086
1155
  maxSubagents: config.team.maxSubagents,
1087
1156
  maxConcurrent: config.team.maxConcurrent,
@@ -1094,7 +1163,7 @@ export class AgentController {
1094
1163
  // session can never be told to use a tool the prompt above it does not
1095
1164
  // mention. Empty everywhere else, which is what keeps I-2 (byte-identical
1096
1165
  // prompt) true for every session that never types `/team`.
1097
- forcedTeamBlock: this.forcedTeam && this.teamRegistered && this.config.team.enabled
1166
+ forcedTeamBlock: this.forcedTeam && this.teamRegistered && this.config.team.enabled && !dropTeamBlocks
1098
1167
  ? buildForcedTeamBlock({
1099
1168
  count: this.forcedTeam.count,
1100
1169
  planMode: this.effectiveMode === 'plan',
@@ -1635,6 +1704,148 @@ export class AgentController {
1635
1704
  pending: this.pendingMode,
1636
1705
  };
1637
1706
  }
1707
+ // -----------------------------------------------------------------------
1708
+ // Posture (governance-mode 4.2). The gate mode above stays the only thing the tools
1709
+ // read; governance is a second, orthogonal flag (D-1), so none of the 25 `=== 'plan'`
1710
+ // sites changed. These methods compose the two and are the only writers of both.
1711
+ // -----------------------------------------------------------------------
1712
+ getPosture() {
1713
+ return composePosture(this.effectiveMode, this.governance.isGovernOn());
1714
+ }
1715
+ getPostureState() {
1716
+ return { effective: this.getPosture(), pending: this.governance.getPendingPosture() };
1717
+ }
1718
+ /** The Shift+Tab cycle: four states when governance is usable, else the classic two. */
1719
+ getPostureCycle() {
1720
+ return this.governance.postureCycle();
1721
+ }
1722
+ isGovernanceAvailable() {
1723
+ return this.governance.isAvailable();
1724
+ }
1725
+ /** The precise sentence for why governance cannot be used now, or null. */
1726
+ getGovernanceUnavailableText() {
1727
+ return this.governance.unavailableText();
1728
+ }
1729
+ /**
1730
+ * Request a posture and return what was ADOPTED (the `setAgentMode` contract: the caller
1731
+ * dispatches the returned value, never the requested one). Tighten at once, loosen when
1732
+ * idle, and while a governed stage runs defer EVERYTHING to `agent_end` (D-23).
1733
+ */
1734
+ setPosture(next, opts = {}) {
1735
+ const before = this.getPosture();
1736
+ // An unregistered session has no governance to be "on": a stray governed request
1737
+ // degrades to its classic half, so the badge can never claim a gate that is not there.
1738
+ const target = this.governance.isRegistered() ? next : splitPosture(next).mode;
1739
+ const result = decidePostureTransition({
1740
+ current: { mode: this.effectiveMode, governOn: this.governance.isGovernOn() },
1741
+ target,
1742
+ force: opts.force === true,
1743
+ running: this.isRunning(),
1744
+ stageRunning: this.governance.isStageRunning(),
1745
+ });
1746
+ this.effectiveMode = result.mode;
1747
+ this.governance.setGovernOn(result.governOn);
1748
+ this.governance.setPendingPosture(result.pending);
1749
+ // A posture request supersedes a stale plan-to-build deferral from `setAgentMode`.
1750
+ this.pendingMode = null;
1751
+ this.governance.onPostureAdopted(this.getPosture(), before);
1752
+ this.rebuildSystemPrompt();
1753
+ return this.getPostureState();
1754
+ }
1755
+ /** Adopt a deferred posture. Call from `agent_end`. Null when nothing was pending. */
1756
+ applyPendingPosture() {
1757
+ const pending = this.governance.getPendingPosture();
1758
+ if (pending === null)
1759
+ return this.applyPendingMode() === null ? null : this.getPostureState();
1760
+ if (this.governance.isStageRunning())
1761
+ return null;
1762
+ this.governance.setPendingPosture(null);
1763
+ return this.setPosture(pending, { force: true });
1764
+ }
1765
+ /** Esc / `/gov abort`: stop the in-flight governed stage (the run becomes `paused`). */
1766
+ abortGovernanceStage() {
1767
+ this.governance.abortStage();
1768
+ }
1769
+ isGovernanceStageRunning() {
1770
+ return this.governance.isStageRunning();
1771
+ }
1772
+ getGovernanceSnapshot() {
1773
+ return this.governance.snapshot();
1774
+ }
1775
+ /** Run + ledger behind the `/gov board` tabs that the snapshot alone cannot fill. */
1776
+ getGovernanceBoardInput() {
1777
+ return this.governance.boardInput();
1778
+ }
1779
+ subscribeGovernance(listener) {
1780
+ return this.governance.subscribe(listener);
1781
+ }
1782
+ /** Recent governed runs (`/gov runs`). */
1783
+ listGovernanceRuns() {
1784
+ return this.governance.listRuns();
1785
+ }
1786
+ /** `/gov pause` and `/gov pause cancel`: stop at the next clean stage boundary. */
1787
+ requestGovernancePause() {
1788
+ return this.governance.requestPause();
1789
+ }
1790
+ cancelGovernancePause() {
1791
+ return this.governance.cancelPause();
1792
+ }
1793
+ /** Whether the governance tools were registered at launch (Settings > Governance). */
1794
+ isGovernanceRegistered() {
1795
+ return this.governance.isRegistered();
1796
+ }
1797
+ /** `/gov report [runId]`: a stored report, read-only. */
1798
+ readGovernanceReport(runId) {
1799
+ return this.governance.readReport(runId);
1800
+ }
1801
+ /** The dispatch-plan line for the live run, for `/gov status`. */
1802
+ getGovernanceDispatchPlan() {
1803
+ return this.governance.dispatchPlanText();
1804
+ }
1805
+ /**
1806
+ * The PLAN+GOVERN interview progress for the question overlay's chip, or null outside that
1807
+ * posture or when no minimum is enforced (governance-mode 9.3).
1808
+ */
1809
+ getGovernanceInterview() {
1810
+ if (this.getPosture() !== 'plan_govern')
1811
+ return null;
1812
+ const required = this.getGovernanceConfig().minAskRounds;
1813
+ if (required <= 0)
1814
+ return null;
1815
+ return { answered: this.governance.answeredRoundCount(), required };
1816
+ }
1817
+ /** `/gov resume`: the run id to hand to the orchestrator, or null when none exists. */
1818
+ findResumableGovernanceRun(runId) {
1819
+ return this.governance.findResumableRun(runId);
1820
+ }
1821
+ getGovernanceConfig() {
1822
+ return clampGovernanceConfig(this.config.governance);
1823
+ }
1824
+ /** Adopt a governance config patch for this session and re-render (the block quotes limits). */
1825
+ setGovernanceConfig(patch) {
1826
+ const governance = clampGovernanceConfig({ ...this.getGovernanceConfig(), ...patch });
1827
+ this.advanceSettingsRevision();
1828
+ this.config = { ...this.config, governance };
1829
+ this.rebuildSystemPrompt();
1830
+ return governance;
1831
+ }
1832
+ /** `submit_plan` approval: PLAN+GOVERN hands over to the team, plain PLAN goes to BUILD. */
1833
+ onPlanApproved(plan) {
1834
+ if (this.governance.handlePlanApproved(plan) === 'govern') {
1835
+ this.setPosture('govern', { force: true });
1836
+ return APPROVED_GOVERN_RESULT;
1837
+ }
1838
+ this.setAgentMode('build', { force: true });
1839
+ }
1840
+ /** What a team child costs per token, or undefined for an unpriced model (never $0.00). */
1841
+ teamModelCost() {
1842
+ const resolution = this.subagentModelResolution();
1843
+ if (resolution.effective === 'main')
1844
+ return this.getModelInfo().cost;
1845
+ return this.isPricedModel(resolution.ref)
1846
+ ? this.getModelInfoFor(resolution.ref).cost
1847
+ : undefined;
1848
+ }
1638
1849
  /**
1639
1850
  * READ-ONLY guard probe, called by `ask_user` BEFORE it opens the overlay.
1640
1851
  *
@@ -1653,6 +1864,7 @@ export class AgentController {
1653
1864
  * deleted round cap wearing a bigger number.
1654
1865
  */
1655
1866
  noteAnswered(questions) {
1867
+ this.governance.noteAnsweredRound(questions);
1656
1868
  this.askedFingerprints.add(questionFingerprint(questions));
1657
1869
  while (this.askedFingerprints.size > ASK_GUARD_LIMITS.maxRemembered) {
1658
1870
  const oldest = this.askedFingerprints.values().next();
@@ -1872,6 +2084,7 @@ export class AgentController {
1872
2084
  this.modelMetadataGeneration += 1;
1873
2085
  this.modelMetadataAbort.abort();
1874
2086
  this.teamRuntime?.dispose();
2087
+ this.governance.dispose();
1875
2088
  this.fast?.dispose();
1876
2089
  this.compaction?.dispose();
1877
2090
  this.contextMeter.dispose();
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The governance slice of the view model: the small pure folds `reducer.ts` delegates to
3
+ * (governance-mode section 14.4).
4
+ *
5
+ * Pure: no React, no I/O. ASCII ONLY (`agent/` is glyph-scanned).
6
+ *
7
+ * WHY A FILE OF ITS OWN. `reducer.ts` is already past the file-size ceiling and the repo
8
+ * rule is "wiring-style growth only": each governance `case` in the reducer is a line or
9
+ * two that calls a function here, so the logic is unit-testable without a `ViewState`.
10
+ */
11
+ import type { AgentMode } from './agent-mode.js';
12
+ import type { GovernanceSnapshot } from '../governance/types.js';
13
+ import { type Posture } from '../governance/posture.js';
14
+ /** The posture half of a `setAgentMode` action. */
15
+ export interface AdoptedPostureInput {
16
+ mode: AgentMode;
17
+ pending: AgentMode | null;
18
+ /** Written from the controller's adopted state; absent for callers that predate postures. */
19
+ posture?: Posture;
20
+ pendingPosture?: Posture | null;
21
+ }
22
+ /**
23
+ * The pair of posture fields one `setAgentMode` dispatch writes.
24
+ *
25
+ * "NEVER DISPATCH THE REQUESTED VALUE, ONLY THE ADOPTED ONE" (spec 4.3): the controller
26
+ * reports all four values together and the reducer mirrors them, so the badge and the gate
27
+ * cannot disagree. A dispatch that carries only the gate mode (every pre-governance call
28
+ * site) keeps the governance dimension the view already has and swaps the mode half, which
29
+ * is exactly what such a caller meant.
30
+ */
31
+ export declare function adoptPosture(current: Posture, action: AdoptedPostureInput): {
32
+ posture: Posture;
33
+ pendingPosture: Posture | null;
34
+ };
35
+ /**
36
+ * Fold a pushed snapshot into the view: a snapshot OLDER than the one held, for the same
37
+ * run, is dropped (the wiring throttles, and a late flush must not roll the panel back).
38
+ * Returns the held object unchanged when nothing should change.
39
+ */
40
+ export declare function foldGovernanceSnapshot(held: GovernanceSnapshot | null, incoming: GovernanceSnapshot): GovernanceSnapshot;