@aragon-agent/cli 0.6.44 → 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 (236) 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/session/validate-session.js +32 -1
  170. package/dist/team/runtime.d.ts +8 -0
  171. package/dist/team/runtime.js +9 -0
  172. package/dist/team/subagent.d.ts +23 -0
  173. package/dist/team/subagent.js +14 -3
  174. package/dist/team/types.d.ts +6 -0
  175. package/dist/tools/index.d.ts +30 -7
  176. package/dist/tools/index.js +14 -0
  177. package/dist/tools/plan-tools.d.ts +14 -2
  178. package/dist/tools/plan-tools.js +5 -2
  179. package/dist/ui/App.js +145 -25
  180. package/dist/ui/BottomStatusRow.js +1 -1
  181. package/dist/ui/Composer.d.ts +7 -1
  182. package/dist/ui/Composer.js +9 -6
  183. package/dist/ui/GovernancePanel.d.ts +50 -0
  184. package/dist/ui/GovernancePanel.js +77 -0
  185. package/dist/ui/GovernanceRun.d.ts +18 -0
  186. package/dist/ui/GovernanceRun.js +11 -0
  187. package/dist/ui/StatusBar.d.ts +15 -2
  188. package/dist/ui/StatusBar.js +10 -4
  189. package/dist/ui/Transcript.js +3 -0
  190. package/dist/ui/entries/GovernanceCard.d.ts +30 -0
  191. package/dist/ui/entries/GovernanceCard.js +32 -0
  192. package/dist/ui/governance-copy.d.ts +69 -0
  193. package/dist/ui/governance-copy.js +104 -0
  194. package/dist/ui/governance-status.d.ts +145 -0
  195. package/dist/ui/governance-status.js +326 -0
  196. package/dist/ui/interaction-copy.d.ts +4 -0
  197. package/dist/ui/interaction-copy.js +5 -0
  198. package/dist/ui/interaction-hints.d.ts +17 -0
  199. package/dist/ui/interaction-hints.js +14 -2
  200. package/dist/ui/layout/governance-board.d.ts +121 -0
  201. package/dist/ui/layout/governance-board.js +382 -0
  202. package/dist/ui/layout/governance-card.d.ts +53 -0
  203. package/dist/ui/layout/governance-card.js +134 -0
  204. package/dist/ui/layout/governance-chip.d.ts +38 -0
  205. package/dist/ui/layout/governance-chip.js +64 -0
  206. package/dist/ui/layout/governance-panel.d.ts +85 -0
  207. package/dist/ui/layout/governance-panel.js +358 -0
  208. package/dist/ui/layout/governance-roster.d.ts +65 -0
  209. package/dist/ui/layout/governance-roster.js +275 -0
  210. package/dist/ui/layout/status-detail-layout.js +3 -3
  211. package/dist/ui/layout/status-layout.d.ts +5 -2
  212. package/dist/ui/layout/status-layout.js +10 -6
  213. package/dist/ui/layout/virtual-window.js +11 -0
  214. package/dist/ui/overlays/GovernanceBoardOverlay.d.ts +41 -0
  215. package/dist/ui/overlays/GovernanceBoardOverlay.js +76 -0
  216. package/dist/ui/overlays/GovernanceSettingsPage.d.ts +48 -0
  217. package/dist/ui/overlays/GovernanceSettingsPage.js +93 -0
  218. package/dist/ui/overlays/HelpOverlay.js +4 -3
  219. package/dist/ui/overlays/QuestionOverlay.d.ts +9 -1
  220. package/dist/ui/overlays/QuestionOverlay.js +4 -2
  221. package/dist/ui/overlays/SettingsScreen.d.ts +7 -0
  222. package/dist/ui/overlays/SettingsScreen.js +21 -1
  223. package/dist/ui/overlays/governance-settings.d.ts +79 -0
  224. package/dist/ui/overlays/governance-settings.js +140 -0
  225. package/dist/ui/overlays/model-profile-state.d.ts +6 -1
  226. package/dist/ui/overlays/model-profile-state.js +10 -0
  227. package/dist/ui/palettes.d.ts +17 -0
  228. package/dist/ui/palettes.js +9 -0
  229. package/dist/ui/scrollback/live-preview.js +2 -0
  230. package/dist/ui/scrollback/settled-boundary.js +3 -0
  231. package/dist/ui/shortcuts.js +2 -2
  232. package/dist/ui/theme.d.ts +7 -0
  233. package/dist/ui/theme.js +1 -0
  234. package/dist/ui/transcript-text.js +9 -0
  235. package/dist/ui/use-wheel-routing.js +3 -1
  236. package/package.json +2 -2
@@ -0,0 +1,134 @@
1
+ /**
2
+ * What follows a ledger change (governance-mode 5.8): elimination is already decided by the
3
+ * pure ledger (`confirmed <= 0` removes the agent inside `apply`), but a removed agent is
4
+ * only half the story - the roster needs a replacement and the work needs an owner.
5
+ *
6
+ * ASCII ONLY (`governance/` is glyph-scanned).
7
+ *
8
+ * RESPAWN IS A SEPARATE LOGGED OP, NOT A SIDE EFFECT OF `apply`. The ledger is rebuilt by
9
+ * replaying `ledger` events; a respawn that happened implicitly in the live path would have
10
+ * no event to replay and the rebuilt ledger would silently lack the successor (I-G2). So
11
+ * the engine issues `{op:'respawn'}` through `core.ledgerOp` like any other mutation.
12
+ *
13
+ * Neither the orchestrator nor any model can trigger a respawn: it is a roster property,
14
+ * not an orchestration action (5.8), and this is the only caller.
15
+ */
16
+ import { evaluateTail } from '../rules/elimination.js';
17
+ import { successorId } from '../rules/ledger.js';
18
+ const REASON_WORD = {
19
+ depleted: 'confirmed GT reached zero',
20
+ tail: 'lowest in role for two consecutive phases',
21
+ clawback: 'escrow clawed back after an integration failure',
22
+ };
23
+ function waitingForReplacement(core) {
24
+ return Object.values(core.ledger.agents)
25
+ .filter((a) => a.status === 'eliminated' && a.replacedBy === null)
26
+ .sort((a, b) => a.id.localeCompare(b.id));
27
+ }
28
+ /** Hand the predecessor's unfinished work to the successor (sticky REV/TEST included). */
29
+ function reassign(core, from, to) {
30
+ const run = core.run;
31
+ if (run === null)
32
+ return [];
33
+ const moved = [];
34
+ for (const id of run.wpOrder) {
35
+ const wp = run.workPackages[id];
36
+ if (wp === undefined || wp.status === 'integrated' || wp.status === 'dropped')
37
+ continue;
38
+ let touched = false;
39
+ if (wp.assignee === from) {
40
+ wp.assignee = to;
41
+ touched = true;
42
+ }
43
+ if (wp.reviewer === from) {
44
+ wp.reviewer = to;
45
+ touched = true;
46
+ }
47
+ if (wp.tester === from) {
48
+ wp.tester = to;
49
+ touched = true;
50
+ }
51
+ if (touched)
52
+ moved.push(id);
53
+ }
54
+ return moved;
55
+ }
56
+ function announceElimination(core, agent) {
57
+ const reason = agent.eliminatedReason ?? 'depleted';
58
+ core.log('eliminated', 'engine', { agentId: agent.id, role: agent.role, reason });
59
+ core.setAgentState(agent.id, 'eliminated');
60
+ core.emit((snapshot) => ({ type: 'eliminated', snapshot, agentId: agent.id, reason }));
61
+ }
62
+ /**
63
+ * Replace every eliminated-but-unreplaced agent. Idempotent: an agent with `replacedBy`
64
+ * set is skipped, so calling this after every ledger change is safe and cheap.
65
+ */
66
+ export function afterLedgerChange(core, options) {
67
+ const result = { eliminated: [], respawned: [] };
68
+ for (const agent of waitingForReplacement(core)) {
69
+ result.eliminated.push(agent.id);
70
+ announceElimination(core, agent);
71
+ const id = successorId(agent);
72
+ const op = {
73
+ op: 'respawn',
74
+ request: { agentId: agent.id, phase: options.phase, at: core.now() },
75
+ };
76
+ const { booked } = core.ledgerOp(op);
77
+ const successor = core.ledger.agents[id];
78
+ if (successor === undefined)
79
+ continue;
80
+ const moved = reassign(core, agent.id, id);
81
+ core.setAgentState(id, 'idle');
82
+ core.log('respawned', 'engine', { predecessor: agent.id, successor: id, wps: moved });
83
+ const reason = REASON_WORD[agent.eliminatedReason ?? 'depleted'];
84
+ const takeover = moved.length > 0 ? `; takes over ${moved.join(', ')}` : '';
85
+ core.stageWarnings.push(`${agent.id} eliminated (${reason}); replaced by ${id} at ${successor.confirmed} ` +
86
+ `GT${takeover}.`);
87
+ core.noteBooked(booked);
88
+ result.respawned.push({ predecessor: agent.id, successor: id });
89
+ core.emit((snapshot) => ({
90
+ type: 'respawned',
91
+ snapshot,
92
+ agentId: id,
93
+ predecessor: agent.id,
94
+ }));
95
+ }
96
+ if (result.eliminated.length > 0)
97
+ core.checkpoint();
98
+ return result;
99
+ }
100
+ /**
101
+ * Apply a list of engine / inspector events one op each (each gets its own seq so the log
102
+ * reads as the sequence of decisions it was), then settle the roster. Returns every entry
103
+ * booked, in order.
104
+ */
105
+ export function applyEvents(core, events, options) {
106
+ const booked = [];
107
+ for (const event of events) {
108
+ const op = { op: 'event', event };
109
+ booked.push(...core.ledgerOp(op, options.actor ?? 'engine').booked);
110
+ }
111
+ core.noteBooked(booked);
112
+ if (booked.length > 0)
113
+ emitLedger(core);
114
+ afterLedgerChange(core, { phase: options.phase });
115
+ return booked;
116
+ }
117
+ export function emitLedger(core) {
118
+ core.emit((snapshot) => ({ type: 'ledger', snapshot }));
119
+ }
120
+ /**
121
+ * Phase-end tail check (5.8): warn on the first strike, eliminate on the second, skip ties.
122
+ * The preview is computed on the current ledger first because `core.ledgerOp` returns only
123
+ * booked entries, and the warnings / tie notes are exactly what the digest should print.
124
+ */
125
+ export function runTailCheck(core, phase) {
126
+ const at = core.now();
127
+ const preview = evaluateTail(core.ledger, { phase, at });
128
+ const { booked } = core.ledgerOp({ op: 'tail_check', phase, at });
129
+ core.noteBooked(booked);
130
+ for (const note of preview.notes)
131
+ core.stageWarnings.push(note);
132
+ afterLedgerChange(core, { phase });
133
+ return preview.notes;
134
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Accept-then-persist for submissions and inspector reports.
3
+ *
4
+ * ASCII ONLY (`governance/` is glyph-scanned).
5
+ *
6
+ * The cache entry is created synchronously (the next tool call must see it) and the file
7
+ * write is queued behind everything else on the engine's single write chain. The artifact
8
+ * id is computed HERE from the cache rather than discovered from disk, because a tool call
9
+ * cannot wait for a directory listing; `ArtifactStore.write` derives the same `n` from the
10
+ * files that earlier queued writes have already produced, so the two agree as long as
11
+ * writes succeed.
12
+ *
13
+ * A refused write (the 20 MB run budget, spec 12.1) cannot be reported to the call that
14
+ * caused it - that call has already returned. Instead the run flips to `storageExhausted`,
15
+ * escalates, and every LATER submission is refused with `NOT ACCEPTED: run storage budget
16
+ * exhausted`, which is the visible, non-silent form of the same rule.
17
+ */
18
+ import type { InspectorReport, StageKind, Submission } from '../types.js';
19
+ import type { EngineCore } from './engine-core.js';
20
+ export declare const STORAGE_EXHAUSTED_TEXT = "NOT ACCEPTED: run storage budget exhausted";
21
+ export interface PersistTarget {
22
+ /** 0-based phase (the artifact id shows it 1-based). */
23
+ phase: number;
24
+ wpId: string;
25
+ stage: StageKind;
26
+ identity: string;
27
+ }
28
+ /** Next 1-based attempt number for this (phase, wp, stage, identity). */
29
+ export declare function nextAttempt(core: EngineCore, target: PersistTarget): number;
30
+ export declare function artifactIdFor(core: EngineCore, target: PersistTarget): string;
31
+ /**
32
+ * Cache `item` under `id` and queue its artifact write. `markdown` is the human rendering.
33
+ * The submission's own `id` field must already equal `id`.
34
+ */
35
+ export declare function persistArtifact(core: EngineCore, target: PersistTarget, id: string, item: Submission | InspectorReport, markdown: string): void;
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Accept-then-persist for submissions and inspector reports.
3
+ *
4
+ * ASCII ONLY (`governance/` is glyph-scanned).
5
+ *
6
+ * The cache entry is created synchronously (the next tool call must see it) and the file
7
+ * write is queued behind everything else on the engine's single write chain. The artifact
8
+ * id is computed HERE from the cache rather than discovered from disk, because a tool call
9
+ * cannot wait for a directory listing; `ArtifactStore.write` derives the same `n` from the
10
+ * files that earlier queued writes have already produced, so the two agree as long as
11
+ * writes succeed.
12
+ *
13
+ * A refused write (the 20 MB run budget, spec 12.1) cannot be reported to the call that
14
+ * caused it - that call has already returned. Instead the run flips to `storageExhausted`,
15
+ * escalates, and every LATER submission is refused with `NOT ACCEPTED: run storage budget
16
+ * exhausted`, which is the visible, non-silent form of the same rule.
17
+ */
18
+ import { getLogger } from '../../logging/logger.js';
19
+ import { buildArtifactId } from '../store/artifacts.js';
20
+ import { openEscalation, nextEscalationId } from './state-machine.js';
21
+ export const STORAGE_EXHAUSTED_TEXT = 'NOT ACCEPTED: run storage budget exhausted';
22
+ /** Next 1-based attempt number for this (phase, wp, stage, identity). */
23
+ export function nextAttempt(core, target) {
24
+ let n = 0;
25
+ for (const id of core.submissionCache.keys()) {
26
+ const prefix = `p${target.phase + 1}/${target.wpId}/${target.stage}-${target.identity}-`;
27
+ if (id.startsWith(prefix))
28
+ n += 1;
29
+ }
30
+ return n + 1;
31
+ }
32
+ export function artifactIdFor(core, target) {
33
+ return buildArtifactId({
34
+ phase: target.phase + 1,
35
+ wpId: target.wpId,
36
+ stage: target.stage,
37
+ identity: target.identity,
38
+ n: nextAttempt(core, target),
39
+ });
40
+ }
41
+ /** Flip the run into the storage-exhausted state, once. */
42
+ function onExhausted(core, message) {
43
+ if (core.storageExhausted)
44
+ return;
45
+ core.storageExhausted = true;
46
+ const run = core.run;
47
+ if (run === null)
48
+ return;
49
+ openEscalation(run, {
50
+ id: nextEscalationId(run),
51
+ level: 'L3',
52
+ kind: 'dispute',
53
+ topic: `Run storage budget exhausted (${message}). Close or abandon the run.`,
54
+ positions: [],
55
+ status: 'open',
56
+ });
57
+ core.log('escalation', 'engine', { reason: 'storage_exhausted', message });
58
+ core.checkpoint();
59
+ }
60
+ /**
61
+ * Cache `item` under `id` and queue its artifact write. `markdown` is the human rendering.
62
+ * The submission's own `id` field must already equal `id`.
63
+ */
64
+ export function persistArtifact(core, target, id, item, markdown) {
65
+ core.submissionCache.set(id, item);
66
+ const runId = core.run?.runId;
67
+ if (runId === undefined)
68
+ return;
69
+ core.enqueueTask(async () => {
70
+ const result = await core.deps.artifacts.write(runId, {
71
+ phase: target.phase + 1,
72
+ wpId: target.wpId,
73
+ stage: target.stage,
74
+ identity: target.identity,
75
+ json: item,
76
+ markdown,
77
+ });
78
+ if (!result.ok) {
79
+ getLogger().warn('agent', 'governance.artifact_write_refused', {
80
+ id,
81
+ message: result.message,
82
+ });
83
+ onExhausted(core, result.message);
84
+ }
85
+ });
86
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Team-token budget accounting (governance-mode 8.5, RV-11).
3
+ *
4
+ * ASCII ONLY (`governance/` is glyph-scanned). Pure.
5
+ *
6
+ * WHAT IS COUNTED. `run.usage` and `governance.budgetTokens` cover the governed TEAM's
7
+ * spend only: every dispatch's `outcome.usage` plus `outcome.overseerUsage` (the
8
+ * supervisor's fast-tier spend). The orchestrator's own turns are already in the
9
+ * session total on the status bar, and labelling this figure "team tokens" everywhere
10
+ * keeps a user from reading it as the session total.
11
+ *
12
+ * COST IS NEVER FABRICATED. `costUsd` stays `null` until a price table is supplied; once
13
+ * it is `null` it is only replaced by a number when a price arrives, and a run that had
14
+ * an unpriced dispatch afterwards reports `null` again rather than a misleading partial
15
+ * sum (a partial sum would read as complete). Same rule as the status bar (C-11 / RV-4):
16
+ * unknown pricing is shown as unknown, never as `$0.00`.
17
+ *
18
+ * STOP RULE. At >= 80% of `budgetTokens` a warning is attached to the next digest; at
19
+ * >= 100% NEW stages are refused. A stage already running is never killed (the caller
20
+ * checks `shouldStopNewStages` only before starting one).
21
+ */
22
+ import type { ModelCost, TokenUsage } from '@aragon-agent/core';
23
+ import type { RunUsage } from '../types.js';
24
+ export interface DispatchUsage {
25
+ usage?: TokenUsage;
26
+ overseerUsage?: TokenUsage;
27
+ }
28
+ export declare const EMPTY_RUN_USAGE: RunUsage;
29
+ /** A fresh counter: cost starts at 0 and turns `null` only if something is unpriced. */
30
+ export declare function newRunUsage(): RunUsage;
31
+ /**
32
+ * Fold one dispatch into the run usage. Returns a NEW object. If any non-empty part of
33
+ * the dispatch cannot be priced (`price` absent) the running cost becomes `null`.
34
+ */
35
+ export declare function accumulateUsage(previous: RunUsage, outcome: DispatchUsage, price?: ModelCost): RunUsage;
36
+ /** Total team tokens (input + output) - the budget's unit. */
37
+ export declare function totalTokens(usage: RunUsage): number;
38
+ export type BudgetLevel = 'ok' | 'warning' | 'exhausted' | 'unlimited';
39
+ /** `budgetTokens <= 0` means unlimited. */
40
+ export declare function budgetLevel(usage: RunUsage, budgetTokens: number): BudgetLevel;
41
+ /** Share of the budget used (0..); null when unlimited. */
42
+ export declare function budgetFraction(usage: RunUsage, budgetTokens: number): number | null;
43
+ /** True when no NEW stage may start. In-flight stages are never affected. */
44
+ export declare function shouldStopNewStages(usage: RunUsage, budgetTokens: number): boolean;
45
+ /** `1,234` style grouping without locale dependence (ASCII, deterministic). */
46
+ export declare function formatTokens(count: number): string;
47
+ /** "$0.42" or "pricing unknown". */
48
+ export declare function formatCost(costUsd: number | null): string;
49
+ /** One line for digests and `gov_run status`. Always says "team tokens". */
50
+ export declare function budgetLine(usage: RunUsage, budgetTokens: number): string;
51
+ /** The `BUDGET WARNING` / refusal text, or null when nothing needs saying. */
52
+ export declare function budgetNotice(usage: RunUsage, budgetTokens: number): string | null;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Team-token budget accounting (governance-mode 8.5, RV-11).
3
+ *
4
+ * ASCII ONLY (`governance/` is glyph-scanned). Pure.
5
+ *
6
+ * WHAT IS COUNTED. `run.usage` and `governance.budgetTokens` cover the governed TEAM's
7
+ * spend only: every dispatch's `outcome.usage` plus `outcome.overseerUsage` (the
8
+ * supervisor's fast-tier spend). The orchestrator's own turns are already in the
9
+ * session total on the status bar, and labelling this figure "team tokens" everywhere
10
+ * keeps a user from reading it as the session total.
11
+ *
12
+ * COST IS NEVER FABRICATED. `costUsd` stays `null` until a price table is supplied; once
13
+ * it is `null` it is only replaced by a number when a price arrives, and a run that had
14
+ * an unpriced dispatch afterwards reports `null` again rather than a misleading partial
15
+ * sum (a partial sum would read as complete). Same rule as the status bar (C-11 / RV-4):
16
+ * unknown pricing is shown as unknown, never as `$0.00`.
17
+ *
18
+ * STOP RULE. At >= 80% of `budgetTokens` a warning is attached to the next digest; at
19
+ * >= 100% NEW stages are refused. A stage already running is never killed (the caller
20
+ * checks `shouldStopNewStages` only before starting one).
21
+ */
22
+ import { GOV_LIMITS } from '../limits.js';
23
+ export const EMPTY_RUN_USAGE = { inputTokens: 0, outputTokens: 0, costUsd: 0 };
24
+ /** A fresh counter: cost starts at 0 and turns `null` only if something is unpriced. */
25
+ export function newRunUsage() {
26
+ return { ...EMPTY_RUN_USAGE };
27
+ }
28
+ function costOf(usage, price) {
29
+ const perMillion = (tokens, rate) => tokens && rate ? (tokens / 1_000_000) * rate : 0;
30
+ return (perMillion(usage.inputTokens, price.input) +
31
+ perMillion(usage.outputTokens, price.output) +
32
+ perMillion(usage.cacheReadTokens, price.cacheRead) +
33
+ perMillion(usage.cacheWriteTokens, price.cacheWrite));
34
+ }
35
+ function parts(outcome) {
36
+ return [outcome.usage, outcome.overseerUsage].filter((u) => u !== undefined);
37
+ }
38
+ /**
39
+ * Fold one dispatch into the run usage. Returns a NEW object. If any non-empty part of
40
+ * the dispatch cannot be priced (`price` absent) the running cost becomes `null`.
41
+ */
42
+ export function accumulateUsage(previous, outcome, price) {
43
+ let inputTokens = previous.inputTokens;
44
+ let outputTokens = previous.outputTokens;
45
+ let costUsd = previous.costUsd;
46
+ for (const usage of parts(outcome)) {
47
+ inputTokens += usage.inputTokens;
48
+ outputTokens += usage.outputTokens;
49
+ if (costUsd === null)
50
+ continue;
51
+ if (price)
52
+ costUsd += usage.cost ?? costOf(usage, price);
53
+ else if (usage.cost !== undefined)
54
+ costUsd += usage.cost;
55
+ else if (usage.inputTokens + usage.outputTokens > 0)
56
+ costUsd = null;
57
+ }
58
+ return { inputTokens, outputTokens, costUsd };
59
+ }
60
+ /** Total team tokens (input + output) - the budget's unit. */
61
+ export function totalTokens(usage) {
62
+ return usage.inputTokens + usage.outputTokens;
63
+ }
64
+ /** `budgetTokens <= 0` means unlimited. */
65
+ export function budgetLevel(usage, budgetTokens) {
66
+ if (!(budgetTokens > 0))
67
+ return 'unlimited';
68
+ const fraction = totalTokens(usage) / budgetTokens;
69
+ if (fraction >= 1)
70
+ return 'exhausted';
71
+ return fraction >= GOV_LIMITS.budgetWarnFraction ? 'warning' : 'ok';
72
+ }
73
+ /** Share of the budget used (0..); null when unlimited. */
74
+ export function budgetFraction(usage, budgetTokens) {
75
+ return budgetTokens > 0 ? totalTokens(usage) / budgetTokens : null;
76
+ }
77
+ /** True when no NEW stage may start. In-flight stages are never affected. */
78
+ export function shouldStopNewStages(usage, budgetTokens) {
79
+ return budgetLevel(usage, budgetTokens) === 'exhausted';
80
+ }
81
+ /** `1,234` style grouping without locale dependence (ASCII, deterministic). */
82
+ export function formatTokens(count) {
83
+ return Math.round(count).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
84
+ }
85
+ /** "$0.42" or "pricing unknown". */
86
+ export function formatCost(costUsd) {
87
+ return costUsd === null ? 'pricing unknown' : `$${costUsd.toFixed(2)}`;
88
+ }
89
+ /** One line for digests and `gov_run status`. Always says "team tokens". */
90
+ export function budgetLine(usage, budgetTokens) {
91
+ const used = `team tokens: ${formatTokens(totalTokens(usage))} ` +
92
+ `(in ${formatTokens(usage.inputTokens)} / out ${formatTokens(usage.outputTokens)}), ` +
93
+ `cost ${formatCost(usage.costUsd)}`;
94
+ if (!(budgetTokens > 0))
95
+ return `${used}; no team-token budget set`;
96
+ const pct = Math.round((totalTokens(usage) / budgetTokens) * 100);
97
+ return `${used}; ${pct}% of the ${formatTokens(budgetTokens)} team-token budget ` +
98
+ '(the budget covers team spend only, not the orchestrator)';
99
+ }
100
+ /** The `BUDGET WARNING` / refusal text, or null when nothing needs saying. */
101
+ export function budgetNotice(usage, budgetTokens) {
102
+ const level = budgetLevel(usage, budgetTokens);
103
+ if (level === 'warning') {
104
+ const pct = Math.round((totalTokens(usage) / budgetTokens) * 100);
105
+ return `BUDGET WARNING: ${pct}% of the team-token budget is used; new stages stop at 100%.`;
106
+ }
107
+ if (level === 'exhausted') {
108
+ return ('NOT ACCEPTED: the team-token budget is exhausted; no new stage can start. ' +
109
+ 'Ask the user to raise the budget (/gov budget <n> or settings) or abandon the run.');
110
+ }
111
+ return null;
112
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Transcript card payloads (governance-mode 10.4): charter, phase settlement, escalation and
3
+ * final. Pure over `EngineCore` state; the result is plain data that is persisted with the
4
+ * transcript, so it holds no functions, no Dates and no class instances.
5
+ *
6
+ * ASCII ONLY (`governance/` is glyph-scanned). Cards only exist at those four moments - the
7
+ * per-stage chatter belongs to the panel, not the transcript.
8
+ */
9
+ import type { Escalation, GovernanceCardData } from '../types.js';
10
+ import type { EngineCore } from './engine-core.js';
11
+ /** The run charter: goal, work packages, contracts, limits. Shown once at `start`. */
12
+ export declare function charterCard(core: EngineCore): GovernanceCardData;
13
+ /** A phase settlement: who gained or lost what, rework and eliminations, team health. */
14
+ export declare function phaseCard(core: EngineCore, phaseIndex: number, headline: string, tone: GovernanceCardData['tone']): GovernanceCardData;
15
+ /** An escalation to the user: what is disputed and which options exist. */
16
+ export declare function escalationCard(core: EngineCore, escalation: Escalation): GovernanceCardData;
17
+ /** The closing card: delivered / accepted with issues / dropped, standings, spend. */
18
+ export declare function finalCard(core: EngineCore): GovernanceCardData;
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Transcript card payloads (governance-mode 10.4): charter, phase settlement, escalation and
3
+ * final. Pure over `EngineCore` state; the result is plain data that is persisted with the
4
+ * transcript, so it holds no functions, no Dates and no class instances.
5
+ *
6
+ * ASCII ONLY (`governance/` is glyph-scanned). Cards only exist at those four moments - the
7
+ * per-stage chatter belongs to the panel, not the transcript.
8
+ */
9
+ import { formatDispatchEstimate } from '../copy.js';
10
+ import { formatCost } from './budget.js';
11
+ import { estimateForRun } from './estimate.js';
12
+ import { teamHealthStatus } from '../rules/elimination.js';
13
+ const MAX_ROWS = 24;
14
+ const MAX_NOTES = 12;
15
+ function signed(delta) {
16
+ return delta > 0 ? `+${delta}` : String(delta);
17
+ }
18
+ /** `+15P D1`, `+25P->C`, `-20 D5`: one short ASCII token pair per ledger entry. */
19
+ function entryText(entry) {
20
+ switch (entry.code) {
21
+ case 'ESCROW_RELEASE':
22
+ return `+${entry.delta}P->C`;
23
+ case 'CLAWBACK':
24
+ return `${signed(entry.delta)} clawback`;
25
+ case 'RESPAWN':
26
+ return `start ${entry.delta}`;
27
+ default: {
28
+ const pool = entry.settlement === 'provisional' ? 'P' : '';
29
+ return `${signed(entry.delta)}${pool} ${entry.code}`;
30
+ }
31
+ }
32
+ }
33
+ function deltaTextFor(core, agentId, phase) {
34
+ const agent = core.ledger.agents[agentId];
35
+ if (agent === undefined)
36
+ return '';
37
+ const entries = agent.history.filter((e) => e.delta !== 0 && (phase === null || e.phase === phase) && e.code !== 'RESPAWN');
38
+ const text = entries.slice(-8).map(entryText).join(' ');
39
+ return entries.length > 8 ? `... ${text}` : text;
40
+ }
41
+ function rowsFor(core, phase) {
42
+ const rows = [];
43
+ for (const agent of Object.values(core.ledger.agents)) {
44
+ const deltaText = deltaTextFor(core, agent.id, phase);
45
+ if (agent.status === 'eliminated' && deltaText === '')
46
+ continue;
47
+ rows.push({
48
+ id: agent.id,
49
+ role: agent.role,
50
+ confirmed: agent.confirmed,
51
+ deltaText: agent.status === 'eliminated'
52
+ ? `ELIMINATED${agent.replacedBy ? ` -> ${agent.replacedBy}` : ''} ${deltaText}`.trim()
53
+ : deltaText,
54
+ });
55
+ }
56
+ return rows.slice(0, MAX_ROWS);
57
+ }
58
+ function wpsOf(core) {
59
+ const run = core.run;
60
+ if (run === null)
61
+ return [];
62
+ return run.wpOrder
63
+ .map((id) => run.workPackages[id])
64
+ .filter((w) => w !== undefined);
65
+ }
66
+ /** The run charter: goal, work packages, contracts, limits. Shown once at `start`. */
67
+ export function charterCard(core) {
68
+ const run = core.requireRun();
69
+ const notes = [];
70
+ for (const wp of wpsOf(core)) {
71
+ notes.push(`${wp.id} (phase ${wp.phase + 1}): ${wp.title}`);
72
+ }
73
+ if (run.contracts.length > 0) {
74
+ notes.push(`contracts: ${run.contracts.map((c) => c.name).join(', ')}`);
75
+ }
76
+ notes.push(`limits: rework ${run.limits.maxRework}, fix cycles ${run.limits.maxFixCycles}` +
77
+ (run.limits.budgetTokens > 0 ? `, budget ${run.limits.budgetTokens} tokens` : ''));
78
+ const plan = formatDispatchEstimate(estimateForRun(run, core.deps.getTeamCeiling()));
79
+ if (plan !== '')
80
+ notes.push(plan);
81
+ if (run.integrationCommands.length > 0) {
82
+ notes.push(`integration: ${run.integrationCommands.join(' | ')}`);
83
+ }
84
+ return {
85
+ variant: 'charter',
86
+ runId: run.runId,
87
+ title: `Governance - run ${run.runId} started`,
88
+ headline: `${run.goal} (${Object.keys(run.workPackages).length} WPs, ${run.phases.length} ` +
89
+ `phase(s))`,
90
+ tone: 'info',
91
+ rows: rowsFor(core, null).map((r) => ({ ...r, deltaText: '' })),
92
+ notes: notes.slice(0, MAX_NOTES),
93
+ };
94
+ }
95
+ /** A phase settlement: who gained or lost what, rework and eliminations, team health. */
96
+ export function phaseCard(core, phaseIndex, headline, tone) {
97
+ const run = core.requireRun();
98
+ const phase = run.phases[phaseIndex];
99
+ const notes = [];
100
+ const reworked = wpsOf(core).filter((w) => w.phase === phaseIndex && w.reworkCount > 0);
101
+ for (const wp of reworked)
102
+ notes.push(`${wp.id} reworked ${wp.reworkCount}x`);
103
+ const gone = Object.values(core.ledger.agents).filter((a) => a.status === 'eliminated');
104
+ for (const agent of gone) {
105
+ notes.push(`${agent.id} eliminated${agent.replacedBy ? `, replaced by ${agent.replacedBy}` : ''}`);
106
+ }
107
+ if (phase !== undefined && phase.fixCycles > 0)
108
+ notes.push(`fix cycles: ${phase.fixCycles}`);
109
+ const health = teamHealthStatus(core.ledger);
110
+ if (health.health !== null)
111
+ notes.push(`health ${Math.round(health.health)}`);
112
+ if (health.advisory !== null)
113
+ notes.push(health.advisory);
114
+ return {
115
+ variant: 'phase',
116
+ runId: run.runId,
117
+ title: `Governance - phase ${phaseIndex + 1} ${tone === 'error' ? 'failed' : 'integrated'}`,
118
+ headline,
119
+ tone,
120
+ rows: rowsFor(core, phaseIndex),
121
+ notes: notes.slice(0, MAX_NOTES),
122
+ };
123
+ }
124
+ /** An escalation to the user: what is disputed and which options exist. */
125
+ export function escalationCard(core, escalation) {
126
+ const run = core.requireRun();
127
+ const notes = [escalation.topic];
128
+ for (const position of escalation.positions.slice(0, 4)) {
129
+ notes.push(`${position.by}: ${position.text}`);
130
+ }
131
+ notes.push(options(escalation));
132
+ const onWp = escalation.wpId ? ` on ${escalation.wpId}` : '';
133
+ return {
134
+ variant: 'escalation',
135
+ runId: run.runId,
136
+ title: `Governance - escalation ${escalation.id}`,
137
+ headline: `${escalation.kind.replace(/_/g, ' ')}${onWp}`,
138
+ tone: 'warn',
139
+ rows: [],
140
+ notes: notes.slice(0, MAX_NOTES),
141
+ };
142
+ }
143
+ function options(escalation) {
144
+ switch (escalation.kind) {
145
+ case 'wp_rework_cap':
146
+ return 'ruling needed: retry, accept as is, or drop the work package';
147
+ case 'integration_exhausted':
148
+ return 'ruling needed: retry once more or accept the failing integration';
149
+ case 'dispute':
150
+ return 'ruling needed: the user decides';
151
+ }
152
+ }
153
+ /** The closing card: delivered / accepted with issues / dropped, standings, spend. */
154
+ export function finalCard(core) {
155
+ const run = core.requireRun();
156
+ const wps = wpsOf(core);
157
+ const delivered = wps.filter((w) => w.status === 'integrated' && !w.acceptedWithIssues);
158
+ const issues = wps.filter((w) => w.acceptedWithIssues);
159
+ const dropped = wps.filter((w) => w.status === 'dropped');
160
+ const open = wps.filter((w) => w.status !== 'integrated' && w.status !== 'dropped');
161
+ const notes = [];
162
+ if (issues.length > 0)
163
+ notes.push(`accepted with issues: ${issues.map((w) => w.id).join(', ')}`);
164
+ if (dropped.length > 0)
165
+ notes.push(`not delivered: ${dropped.map((w) => w.id).join(', ')}`);
166
+ if (open.length > 0)
167
+ notes.push(`unfinished: ${open.map((w) => w.id).join(', ')}`);
168
+ for (const phase of run.phases) {
169
+ if (phase.integrated && !phase.integrationAccepted) {
170
+ notes.push(`phase ${phase.index + 1}: integration FAILED, accepted by the user`);
171
+ }
172
+ }
173
+ const usage = run.usage;
174
+ notes.push(`team tokens in ${usage.inputTokens} out ${usage.outputTokens}, cost ` +
175
+ `${formatCost(usage.costUsd)}`);
176
+ const clean = issues.length === 0 && dropped.length === 0 && open.length === 0;
177
+ return {
178
+ variant: 'final',
179
+ runId: run.runId,
180
+ title: `Governance - run ${run.runId} ${run.status === 'abandoned' ? 'abandoned' : 'closed'}`,
181
+ headline: `${delivered.length}/${wps.length} work packages delivered`,
182
+ tone: run.status === 'abandoned' ? 'warn' : clean ? 'ok' : 'warn',
183
+ rows: rowsFor(core, null).map((r) => ({ ...r, deltaText: '' })),
184
+ notes: notes.slice(0, MAX_NOTES),
185
+ };
186
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Digest assembly shared by the lifecycle and the stage runner (governance-mode D-16).
3
+ *
4
+ * ASCII ONLY (`governance/` is glyph-scanned).
5
+ *
6
+ * Every `gov_run` / `gov_dispatch` answer is built here from the same four parts: a
7
+ * headline that starts with `OK:` / `NOT ACCEPTED:` / `ESCALATION:` / `PAUSED:`, the result
8
+ * lines, a footer with the limits and the TEAM-token line, and `NEXT:`. One builder means
9
+ * one place that guarantees the prefix, the byte budget and the footer wording.
10
+ */
11
+ import type { EngineCore } from './engine-core.js';
12
+ export interface DigestParts {
13
+ headline: string;
14
+ results?: string[];
15
+ ledger?: string[];
16
+ warnings?: string[];
17
+ /** Include the standings table (stage ends and status; not every refusal). */
18
+ standings?: boolean;
19
+ }
20
+ /** The footer every digest carries: limits, then team tokens (never the session total). */
21
+ export declare function footerLines(core: EngineCore): string[];
22
+ /**
23
+ * What a digest says to do next while the USER has paused the run (`/gov pause`). The usual
24
+ * `paused` answer is `gov_run resume`, which would invite the Orchestrator to resume a run
25
+ * its human just stopped; only the human resumes it (their `/gov resume` is what sends the
26
+ * Orchestrator to call `gov_run resume`).
27
+ */
28
+ export declare const USER_PAUSED_NEXT = "wait for the user: only they resume this run (/gov resume). Do not dispatch.";
29
+ /** Compose a complete tool answer for the current run state. */
30
+ export declare function composeDigest(core: EngineCore, parts: DigestParts): string;
31
+ /** A refusal with the legal next moves attached (no run needed). */
32
+ export declare function refusal(core: EngineCore, reason: string): string;