@aragon-agent/cli 0.5.8 → 0.5.9

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 (123) hide show
  1. package/CHANGELOG.md +279 -0
  2. package/README.md +1429 -1163
  3. package/dist/agent/coalesce.d.ts +13 -0
  4. package/dist/agent/coalesce.js +50 -11
  5. package/dist/agent/controller.d.ts +141 -2
  6. package/dist/agent/controller.js +396 -4
  7. package/dist/agent/headless.d.ts +8 -0
  8. package/dist/agent/headless.js +40 -0
  9. package/dist/agent/reducer.d.ts +245 -3
  10. package/dist/agent/reducer.js +490 -17
  11. package/dist/agent/retry-view.d.ts +68 -0
  12. package/dist/agent/retry-view.js +95 -0
  13. package/dist/agent/system-prompt.d.ts +11 -0
  14. package/dist/agent/system-prompt.js +2 -0
  15. package/dist/cli.js +86 -2
  16. package/dist/commands/builtins.d.ts +1 -1
  17. package/dist/commands/builtins.js +308 -2
  18. package/dist/config/cli-commands.d.ts +53 -2
  19. package/dist/config/cli-commands.js +136 -3
  20. package/dist/config/env.d.ts +6 -3
  21. package/dist/config/env.js +103 -3
  22. package/dist/config/load.d.ts +49 -0
  23. package/dist/config/load.js +83 -1
  24. package/dist/config/schema.d.ts +212 -2
  25. package/dist/config/schema.js +207 -1
  26. package/dist/config/store.js +20 -1
  27. package/dist/fast/critique.d.ts +43 -0
  28. package/dist/fast/critique.js +62 -0
  29. package/dist/fast/digest.d.ts +42 -0
  30. package/dist/fast/digest.js +85 -0
  31. package/dist/fast/frames.d.ts +99 -0
  32. package/dist/fast/frames.js +180 -0
  33. package/dist/fast/limits.d.ts +98 -0
  34. package/dist/fast/limits.js +98 -0
  35. package/dist/fast/prompt.d.ts +72 -0
  36. package/dist/fast/prompt.js +94 -0
  37. package/dist/fast/resolve.d.ts +43 -0
  38. package/dist/fast/resolve.js +105 -0
  39. package/dist/fast/review-call.d.ts +99 -0
  40. package/dist/fast/review-call.js +130 -0
  41. package/dist/fast/reviewer.d.ts +248 -0
  42. package/dist/fast/reviewer.js +664 -0
  43. package/dist/fast/types.d.ts +128 -0
  44. package/dist/fast/types.js +14 -0
  45. package/dist/fast/wiring.d.ts +170 -0
  46. package/dist/fast/wiring.js +316 -0
  47. package/dist/logging/install.d.ts +15 -1
  48. package/dist/logging/install.js +37 -4
  49. package/dist/logging/logger.d.ts +1 -1
  50. package/dist/session/persist.js +57 -1
  51. package/dist/team/normalize.d.ts +26 -1
  52. package/dist/team/normalize.js +23 -3
  53. package/dist/team/prompt.d.ts +11 -0
  54. package/dist/team/prompt.js +10 -0
  55. package/dist/team/report.d.ts +18 -0
  56. package/dist/team/report.js +27 -1
  57. package/dist/team/runtime.d.ts +13 -2
  58. package/dist/team/runtime.js +33 -6
  59. package/dist/team/subagent.d.ts +19 -1
  60. package/dist/team/subagent.js +72 -10
  61. package/dist/team/task-tool.d.ts +26 -0
  62. package/dist/team/task-tool.js +48 -4
  63. package/dist/team/types.d.ts +80 -1
  64. package/dist/tools/bash-tool.js +19 -3
  65. package/dist/tools/diff.d.ts +25 -5
  66. package/dist/tools/diff.js +72 -41
  67. package/dist/tools/file-change-store.d.ts +34 -0
  68. package/dist/tools/file-change-store.js +57 -0
  69. package/dist/tools/fs-tools.d.ts +26 -0
  70. package/dist/tools/fs-tools.js +62 -5
  71. package/dist/tools/index.d.ts +31 -0
  72. package/dist/tools/index.js +7 -1
  73. package/dist/tools/patch.d.ts +115 -0
  74. package/dist/tools/patch.js +453 -0
  75. package/dist/tools/terminal-output.d.ts +107 -0
  76. package/dist/tools/terminal-output.js +198 -0
  77. package/dist/tools/tool-output-store.d.ts +68 -0
  78. package/dist/tools/tool-output-store.js +102 -0
  79. package/dist/ui/ActivityLine.d.ts +48 -0
  80. package/dist/ui/ActivityLine.js +22 -0
  81. package/dist/ui/App.d.ts +1 -4
  82. package/dist/ui/App.js +273 -37
  83. package/dist/ui/BottomStatusRow.d.ts +43 -0
  84. package/dist/ui/BottomStatusRow.js +14 -0
  85. package/dist/ui/Composer.d.ts +1 -6
  86. package/dist/ui/Composer.js +2 -2
  87. package/dist/ui/PromptInput.d.ts +1 -15
  88. package/dist/ui/PromptInput.js +1 -38
  89. package/dist/ui/StatusBar.d.ts +37 -0
  90. package/dist/ui/StatusBar.js +5 -3
  91. package/dist/ui/TeamPanel.d.ts +5 -4
  92. package/dist/ui/TeamPanel.js +1 -1
  93. package/dist/ui/TodoPanel.d.ts +5 -4
  94. package/dist/ui/Transcript.d.ts +26 -3
  95. package/dist/ui/Transcript.js +76 -10
  96. package/dist/ui/activity-phrases.d.ts +33 -0
  97. package/dist/ui/activity-phrases.js +50 -0
  98. package/dist/ui/entries/AssistantEntry.d.ts +20 -0
  99. package/dist/ui/entries/AssistantEntry.js +3 -2
  100. package/dist/ui/entries/DiffView.d.ts +67 -0
  101. package/dist/ui/entries/DiffView.js +103 -0
  102. package/dist/ui/entries/FastCard.d.ts +53 -0
  103. package/dist/ui/entries/FastCard.js +87 -0
  104. package/dist/ui/entries/RetryCard.d.ts +56 -0
  105. package/dist/ui/entries/RetryCard.js +60 -0
  106. package/dist/ui/entries/TeamCard.js +6 -1
  107. package/dist/ui/entries/ToolCard.d.ts +38 -1
  108. package/dist/ui/entries/ToolCard.js +68 -5
  109. package/dist/ui/glyphs.d.ts +9 -0
  110. package/dist/ui/glyphs.js +2 -0
  111. package/dist/ui/layout/AppShell.d.ts +13 -22
  112. package/dist/ui/layout/AppShell.js +3 -38
  113. package/dist/ui/layout/virtual-window.d.ts +1 -1
  114. package/dist/ui/layout/virtual-window.js +109 -5
  115. package/dist/ui/overlays/HelpOverlay.js +16 -2
  116. package/dist/ui/overlays/SettingsScreen.d.ts +82 -1
  117. package/dist/ui/overlays/SettingsScreen.js +147 -1
  118. package/dist/ui/transcript-text.js +27 -0
  119. package/dist/ui/use-wheel-routing.d.ts +16 -32
  120. package/dist/ui/use-wheel-routing.js +21 -38
  121. package/package.json +2 -2
  122. package/dist/ui/layout/regions.d.ts +0 -55
  123. package/dist/ui/layout/regions.js +0 -52
@@ -13,7 +13,10 @@
13
13
  import type { AgentEvent, ModelCost, ToolResult, TokenUsage } from '@aragon-agent/core';
14
14
  import type { AgentMode } from './agent-mode.js';
15
15
  import type { DispatchOutcome, SubagentRun, SubagentSpec, TeamSnapshot } from '../team/types.js';
16
+ import type { FastReview, FastSnapshot } from '../fast/types.js';
16
17
  import type { TodoItem, TodoSnapshot } from '../todo/types.js';
18
+ import type { RetryPhase } from './retry-view.js';
19
+ import type { FilePatch } from '../tools/patch.js';
17
20
  export type NoticeLevel = 'info' | 'warn' | 'error';
18
21
  export type ToastLevel = NoticeLevel | 'success';
19
22
  export type Overlay = null | 'settings' | 'model' | 'help' | 'confirm' | 'question' | 'plan';
@@ -40,6 +43,21 @@ export type Entry = {
40
43
  streaming: boolean;
41
44
  aborted?: boolean;
42
45
  usage?: TokenUsage;
46
+ /**
47
+ * Epoch ms the turn started reasoning (agent-activity-presentation §3.1.2).
48
+ * Set by `thinkingStart`, cleared by `streamRestart`.
49
+ */
50
+ thinkingStartedAt?: number;
51
+ /**
52
+ * Wall-clock reasoning time, SEALED ONCE — at the first text delta, or at
53
+ * turn/run end for a turn that thought and then called a tool without
54
+ * emitting any text.
55
+ *
56
+ * With thinking hidden this is the only thing the collapsed marker can say
57
+ * that the user could not otherwise know, which is the whole argument for
58
+ * having a marker at all (§1.2).
59
+ */
60
+ thinkingMs?: number;
43
61
  } | {
44
62
  id: string;
45
63
  kind: 'tool';
@@ -52,6 +70,44 @@ export type Entry = {
52
70
  preview?: string;
53
71
  durationMs?: number;
54
72
  isError?: boolean;
73
+ /**
74
+ * The structured file diff (agent-activity-presentation §3.3), or absent.
75
+ *
76
+ * IMMUTABLE AND WRITTEN ONCE, by `toolExecEnd`. `ToolCard` is `React.memo`'d
77
+ * with the DEFAULT comparator, so this object is only free while its
78
+ * reference is stable — which it is, because the reducer never rebuilds it
79
+ * and `mapEntry` preserves identity for untouched entries. A future action
80
+ * that recomputed or normalized a patch would turn that memo boundary into
81
+ * a no-op WITH NOTHING FAILING (P2-9).
82
+ *
83
+ * Needs no clause in `normalizeLoadedEntries` (D-9): the four that exist
84
+ * are all about a LIVE flag that would never settle, and a patch is settled
85
+ * by construction.
86
+ */
87
+ patch?: FilePatch;
88
+ /**
89
+ * The sanitised live tail while the tool runs
90
+ * (agent-activity-presentation-live §3.3.1). Cleared at settle (D-23), and
91
+ * cleared again by EVERY path that reaches `status: 'idle'` (D-35).
92
+ *
93
+ * REDUCER STATE RATHER THAN A STORE READ DURING RENDER, and that is the
94
+ * load-bearing decision of the round (D-22): `entryRevision` is what tells
95
+ * the height cache an entry's rendered output changed, and an entry mutated
96
+ * outside `ViewState` keeps its revision and freezes at a stale height AND
97
+ * a stale subtree with nothing anywhere reporting it (I-L3-1).
98
+ */
99
+ live?: readonly string[];
100
+ /**
101
+ * Monotonic counter -- the revision term for a NON-APPEND mutation (D-22).
102
+ *
103
+ * A COUNTER AND NOT `live.length`: a fixed-size tail evicting its oldest
104
+ * row while appending a new one of the same width leaves the joined length
105
+ * unchanged, which is exactly the lossy case `virtual-window.ts` says the
106
+ * reducer cannot produce today and that this round introduces.
107
+ */
108
+ liveSeq?: number;
109
+ /** Epoch ms of the last output row, for the stall row (§3.3.4). */
110
+ lastOutputAt?: number;
55
111
  } | {
56
112
  id: string;
57
113
  kind: 'notice';
@@ -107,12 +163,85 @@ export type Entry = {
107
163
  * card this session produced.
108
164
  */
109
165
  interrupted?: boolean;
166
+ }
167
+ /**
168
+ * The turn's retry story (llm-api-retry-backoff §6.4). ONE per turn, REWRITTEN
169
+ * IN PLACE — ten retries rendered as ten notices would bury the transcript in
170
+ * the one moment the user most needs it legible.
171
+ *
172
+ * JSON-SERIALIZABLE by construction, so `/save` needs no format change.
173
+ *
174
+ * `phase: 'waiting' | 'retrying'` is normalized away ON LOAD, in
175
+ * `session/persist.ts`, exactly as the team entry's `active` and the todo
176
+ * card's `live` are (R-10): `Transcript`'s settled boundary is MONOTONIC, so a
177
+ * card that never settles never reaches `<Static>` and is re-rendered on every
178
+ * frame for the rest of the session.
179
+ */
180
+ | {
181
+ id: string;
182
+ kind: 'retry';
183
+ /** 1-based retry index of the LATEST attempt this card has seen. */
184
+ attempt: number;
185
+ maxRetries: number;
186
+ errorType: string;
187
+ message: string;
188
+ delayMs: number;
189
+ /** Epoch ms the next attempt fires; the UI ticks against this. */
190
+ resumeAt?: number;
191
+ phase: RetryPhase;
192
+ startedAt: number;
193
+ /** Filled on settle. */
194
+ totalRetries?: number;
195
+ elapsedMs?: number;
196
+ }
197
+ /**
198
+ * One fast-model review (fast-model-tier §5.4). ONE CARD PER REVIEW, never
199
+ * rewritten across reviews — a critique is a single event, unlike the todo
200
+ * card's per-turn checklist.
201
+ *
202
+ * JSON-SERIALIZABLE by construction, so `/save` needs no format change.
203
+ *
204
+ * `live: true` is normalized away ON LOAD, in `session/persist.ts`, next to
205
+ * `team.active`, `todo.live` and the retry card's phase, and for the identical
206
+ * reason (C-5): `Transcript`'s settled boundary is MONOTONIC, so an entry that
207
+ * never settles never reaches `<Static>` and is re-rendered on every frame for
208
+ * the rest of the session. The reducer never legitimately sees a stale-live
209
+ * entry — only the file does.
210
+ */
211
+ | {
212
+ id: string;
213
+ kind: 'fast';
214
+ /** 1-based within the session, so the card can name itself. */
215
+ reviewIndex: number;
216
+ model: string;
217
+ status: 'running' | 'ok' | 'advice' | 'empty' | 'failed' | 'dropped';
218
+ /** Present only for `advice`. */
219
+ text?: string;
220
+ /** Failure / drop reason, shown as one muted line. */
221
+ detail?: string;
222
+ turn: number;
223
+ durationMs?: number;
224
+ live: boolean;
110
225
  };
111
226
  export interface UsageTotal {
112
227
  inputTokens: number;
113
228
  outputTokens: number;
114
229
  costUsd: number;
115
230
  }
231
+ /**
232
+ * The ephemeral retry projection held in `ViewState.retry` (§9).
233
+ *
234
+ * Only the two IN-FLIGHT phases exist here: once a card settles the projection is
235
+ * `null`, because the settled card IS the history and a chip that reported a
236
+ * finished retry would never go away.
237
+ */
238
+ export interface RetrySnapshot {
239
+ attempt: number;
240
+ maxRetries: number;
241
+ resumeAt?: number;
242
+ phase: 'waiting' | 'retrying';
243
+ errorType: string;
244
+ }
116
245
  export interface ViewState {
117
246
  entries: Entry[];
118
247
  status: 'idle' | 'running';
@@ -150,11 +279,32 @@ export interface ViewState {
150
279
  * build (AC-24).
151
280
  */
152
281
  todos: TodoSnapshot | null;
282
+ /**
283
+ * The live retry projection, or `null` when nothing is retrying.
284
+ *
285
+ * NEVER PERSISTED — `entries` is the history and the settled card is the
286
+ * record. It drives the status-bar chip and the 1 Hz tick, and it is nulled on
287
+ * every settle path. Same contract as `team`.
288
+ */
289
+ retry: RetrySnapshot | null;
290
+ /**
291
+ * The live fast-tier projection, or `null` when the tier is off for this
292
+ * session (fast-model-tier §5.2).
293
+ *
294
+ * NEVER PERSISTED — `entries` is the history and the settled cards are the
295
+ * record. It drives the status chip and `/fast status`'s totals. `null` is
296
+ * what leaves an ordinary session's status bar byte-identical (AC-4).
297
+ */
298
+ fast: FastSnapshot | null;
153
299
  seq: number;
154
300
  /** The `kind: 'team'` entry the live dispatch is writing into. */
155
301
  teamEntryId?: string;
156
302
  /** The `kind: 'todo'` entry this turn is writing into. */
157
303
  todoEntryId?: string;
304
+ /** The `kind: 'retry'` entry this turn is writing into. */
305
+ retryEntryId?: string;
306
+ /** The `kind: 'fast'` entry the in-flight review is writing into. */
307
+ fastEntryId?: string;
158
308
  streamingId?: string;
159
309
  turnProduced: boolean;
160
310
  errorNoticed: boolean;
@@ -169,7 +319,24 @@ export interface ViewState {
169
319
  */
170
320
  droppedEntries: number;
171
321
  }
172
- export declare function initialViewState(): ViewState;
322
+ export interface ViewStateSeed {
323
+ /** Start with thinking blocks expanded. Defaults to FALSE, the product default. */
324
+ thinkingVisible?: boolean;
325
+ }
326
+ /**
327
+ * The initial view state, optionally seeded from config.
328
+ *
329
+ * THE DEFAULT IS `false`, NOT `true`, AND THAT IS THE WHOLE SAFETY ARGUMENT
330
+ * (D-2). There is exactly one production call site — `App`'s `useReducer`, which
331
+ * passes `cfg.showThinking`. Every other caller is a test. A future call site
332
+ * that forgets the seed fails CLOSED, toward the product default, rather than
333
+ * silently restoring the reasoning firehose for one code path with nothing
334
+ * reporting it.
335
+ *
336
+ * The seed is a PARAMETER rather than a post-mount dispatch so that `/resume`
337
+ * cannot flash a restored thinking block on frame 1.
338
+ */
339
+ export declare function initialViewState(seed?: ViewStateSeed): ViewState;
173
340
  export type ViewAction = {
174
341
  type: 'submit';
175
342
  text: string;
@@ -207,6 +374,22 @@ export type ViewAction = {
207
374
  isError: boolean;
208
375
  duration: number;
209
376
  preview: string;
377
+ /** A fully-built, immutable diff, so the reducer stays pure (§3.3.7). */
378
+ patch?: FilePatch;
379
+ }
380
+ /**
381
+ * A running tool's live tail (agent-activity-presentation-live §3.3.1).
382
+ *
383
+ * `rows` IS THE WHOLE TAIL, not an increment: the store replaces it on every
384
+ * chunk, which is what lets `mergeDeltas` keep only the last action per
385
+ * `toolCallId` (D-30). `at` is read in `App`'s listener rather than here, so
386
+ * this reducer stays pure.
387
+ */
388
+ | {
389
+ type: 'toolOutputDelta';
390
+ toolCallId: string;
391
+ rows: readonly string[];
392
+ at: number;
210
393
  } | {
211
394
  type: 'turnEnd';
212
395
  usage: TokenUsage;
@@ -266,6 +449,54 @@ export type ViewAction = {
266
449
  snapshot: TodoSnapshot;
267
450
  } | {
268
451
  type: 'todoCleared';
452
+ } | {
453
+ type: 'retryScheduled';
454
+ attempt: number;
455
+ maxRetries: number;
456
+ delayMs: number;
457
+ resumeAt: number;
458
+ errorType: string;
459
+ message: string;
460
+ } | {
461
+ type: 'retryAttempt';
462
+ attempt: number;
463
+ maxRetries: number;
464
+ } | {
465
+ type: 'streamRestart';
466
+ discardedToolCallIds: string[];
467
+ }
468
+ /**
469
+ * The 1 Hz countdown tick. NO STATE OF ITS OWN — it bumps `seq` so the frame
470
+ * re-renders and the card recomputes `resumeAt - Date.now()`. `App` runs the
471
+ * interval ONLY while `state.retry?.phase === 'waiting'`, so an idle session has
472
+ * no timer at all.
473
+ */
474
+ | {
475
+ type: 'retryTick';
476
+ } | {
477
+ type: 'fastStart';
478
+ index: number;
479
+ turn: number;
480
+ model: string;
481
+ } | {
482
+ type: 'fastEnd';
483
+ review: FastReview;
484
+ }
485
+ /**
486
+ * Review spend.
487
+ *
488
+ * `costDelta` IS PRECOMPUTED BY THE CALLER, exactly as `teamUsage`'s is: the
489
+ * reducer stays pure and cost-table-free, which is the property that makes it
490
+ * testable — and the fast tier has a DIFFERENT cost table from the lead, so a
491
+ * reducer that computed its own would have to know about both (§3.6 / R-7).
492
+ */
493
+ | {
494
+ type: 'fastUsage';
495
+ usage: TokenUsage;
496
+ costDelta: number;
497
+ } | {
498
+ type: 'fastTier';
499
+ snapshot: FastSnapshot;
269
500
  };
270
501
  export declare const STORED_PREVIEW_CHARS = 8000;
271
502
  export declare const STORED_PREVIEW_LINES = 200;
@@ -284,12 +515,23 @@ export declare const PREVIEW_TRUNCATION_MARK = "...";
284
515
  * over-promises how much more is expandable.
285
516
  */
286
517
  export declare function buildToolPreview(result: ToolResult): string;
518
+ /**
519
+ * Where a `FilePatch` is collected from on `tool_execution_end`.
520
+ *
521
+ * Supplying one makes `reduceEvent` CONSUME-ONCE rather than pure, which is safe
522
+ * because `App.tsx` calls it exactly once per event and `mergeDeltas` only ever
523
+ * merges `textDelta` / `thinkingDelta`, never `toolExecEnd`.
524
+ */
525
+ export interface PatchSource {
526
+ take(toolCallId: string): FilePatch | undefined;
527
+ }
287
528
  /**
288
529
  * Map one core `AgentEvent` to view actions. `cost` (the active model's cost
289
530
  * table) lets `turn_end` accumulate USD cost; omit it in tests that only assert
290
- * token totals.
531
+ * token totals. `patches` is the CLI-local diff side channel (§3.3.7); omitting
532
+ * it — every existing test, and headless mode — produces byte-identical actions.
291
533
  */
292
- export declare function reduceEvent(event: AgentEvent, cost?: ModelCost): ViewAction[];
534
+ export declare function reduceEvent(event: AgentEvent, cost?: ModelCost, patches?: PatchSource): ViewAction[];
293
535
  /** Turn a provider error into a friendly banner, mapping LLMError.errorType. */
294
536
  export declare function formatStreamError(error: Error): string;
295
537
  export declare function viewReducer(state: ViewState, action: ViewAction): ViewState;