@aragon-agent/cli 0.6.16 → 0.6.18

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 (79) hide show
  1. package/CHANGELOG.md +107 -51
  2. package/README.md +91 -48
  3. package/dist/agent/controller.js +52 -47
  4. package/dist/agent/headless.js +7 -0
  5. package/dist/agent/reducer.js +0 -4
  6. package/dist/cli.js +0 -13
  7. package/dist/commands/builtins.js +21 -20
  8. package/dist/commands/index-build.d.ts +39 -0
  9. package/dist/commands/index-build.js +61 -0
  10. package/dist/compaction/child.d.ts +10 -8
  11. package/dist/compaction/child.js +23 -25
  12. package/dist/compaction/limits.d.ts +0 -2
  13. package/dist/compaction/limits.js +0 -2
  14. package/dist/config/cli-commands.d.ts +2 -2
  15. package/dist/config/cli-commands.js +19 -15
  16. package/dist/config/load.d.ts +0 -17
  17. package/dist/config/load.js +0 -6
  18. package/dist/config/schema.d.ts +41 -9
  19. package/dist/config/schema.js +18 -11
  20. package/dist/exec/events.d.ts +16 -9
  21. package/dist/exec/events.js +2 -2
  22. package/dist/exec/runner.js +17 -1
  23. package/dist/fast/limits.d.ts +2 -2
  24. package/dist/fast/limits.js +2 -2
  25. package/dist/fast/prompt.d.ts +10 -11
  26. package/dist/fast/prompt.js +19 -20
  27. package/dist/fast/reviewer.d.ts +1 -1
  28. package/dist/fast/reviewer.js +1 -1
  29. package/dist/fast/types.d.ts +0 -2
  30. package/dist/fast/wiring.d.ts +18 -34
  31. package/dist/fast/wiring.js +20 -51
  32. package/dist/input/csiu-keys.js +13 -2
  33. package/dist/input/limits.d.ts +15 -0
  34. package/dist/input/limits.js +15 -0
  35. package/dist/input/win32-input-mode.js +7 -1
  36. package/dist/logging/install.d.ts +9 -0
  37. package/dist/logging/install.js +11 -0
  38. package/dist/session/validate-session.js +3 -1
  39. package/dist/team/child-reviewer.d.ts +105 -0
  40. package/dist/team/child-reviewer.js +117 -0
  41. package/dist/team/human-queue.d.ts +11 -0
  42. package/dist/team/human-queue.js +18 -1
  43. package/dist/team/limits.d.ts +40 -0
  44. package/dist/team/limits.js +41 -0
  45. package/dist/team/normalize.d.ts +7 -26
  46. package/dist/team/normalize.js +9 -22
  47. package/dist/team/overseer.d.ts +157 -0
  48. package/dist/team/overseer.js +348 -0
  49. package/dist/team/prompt.d.ts +0 -11
  50. package/dist/team/prompt.js +3 -12
  51. package/dist/team/report.d.ts +4 -19
  52. package/dist/team/report.js +27 -28
  53. package/dist/team/runtime.d.ts +76 -16
  54. package/dist/team/runtime.js +410 -57
  55. package/dist/team/subagent.d.ts +48 -19
  56. package/dist/team/subagent.js +88 -31
  57. package/dist/team/task-tool.d.ts +6 -26
  58. package/dist/team/task-tool.js +17 -48
  59. package/dist/team/types.d.ts +65 -41
  60. package/dist/ui/App.d.ts +1 -1
  61. package/dist/ui/App.js +64 -4
  62. package/dist/ui/BottomStatusRow.js +2 -1
  63. package/dist/ui/Header.js +13 -2
  64. package/dist/ui/PromptInput.js +11 -1
  65. package/dist/ui/StatusBar.js +2 -1
  66. package/dist/ui/TeamPanel.js +1 -1
  67. package/dist/ui/entries/TeamCard.js +1 -6
  68. package/dist/ui/layout/status-detail-layout.js +12 -2
  69. package/dist/ui/layout/status-layout.d.ts +4 -2
  70. package/dist/ui/layout/status-layout.js +24 -1
  71. package/dist/ui/overlays/HelpOverlay.js +2 -2
  72. package/package.json +2 -2
  73. package/skills/project-indexer/SKILL.md +163 -0
  74. package/skills/project-indexer/templates/cleancode-overrides.md +46 -0
  75. package/skills/project-indexer/templates/cleancode-template.md +94 -0
  76. package/skills/project-indexer/templates/config-template.md +69 -0
  77. package/skills/project-indexer/templates/index-template.md +129 -0
  78. package/dist/ui/ModeChip.d.ts +0 -18
  79. package/dist/ui/ModeChip.js +0 -24
@@ -14,7 +14,7 @@ import { getConfigBackupPath, getConfigPath, getHomeRoot, getLogsDir, getPromptH
14
14
  import { getHomeMigrationBreadcrumbPath } from './migrate-home.js';
15
15
  import { loadPersistedConfig, updatePersistedConfig } from './store.js';
16
16
  import { normalizeModelProfiles, validateModelProfiles } from './model-profiles.js';
17
- import { clampFastConfig, clampRetryConfig, clampCompactionConfig, parseThresholdInput, clampUpdateConfig, coercePositiveInt, maskSecret, DEFAULT_FAST_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_TEAM_CONFIG, DEFAULT_COMPACTION_CONFIG, DEFAULT_UPDATE_CONFIG, } from './schema.js';
17
+ import { clampFastConfig, clampRetryConfig, clampCompactionConfig, clampTeamConfig, parseThresholdInput, clampUpdateConfig, coercePositiveInt, maskSecret, DEFAULT_FAST_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_TEAM_CONFIG, DEFAULT_COMPACTION_CONFIG, DEFAULT_UPDATE_CONFIG, } from './schema.js';
18
18
  import { clampLogLevel } from '../logging/levels.js';
19
19
  /** Config keys under `log.` that `aragon config set` accepts. */
20
20
  export const LOG_CONFIG_SET_KEYS = [
@@ -43,6 +43,7 @@ export const TEAM_CONFIG_SET_KEYS = [
43
43
  'team.subagentTimeoutMs',
44
44
  'team.dispatchTimeoutMs',
45
45
  'team.maxTurnsPerSubagent',
46
+ 'team.overseer',
46
47
  ];
47
48
  /**
48
49
  * Config keys under `retry.` that `aragon config set` accepts
@@ -81,7 +82,6 @@ export const FAST_CONFIG_SET_KEYS = [
81
82
  'fast.model',
82
83
  'fast.baseUrl',
83
84
  'fast.thinkingLevel',
84
- 'fast.delegate',
85
85
  'fast.review',
86
86
  'fast.reviewEveryTurns',
87
87
  'fast.reviewContextTurns',
@@ -177,25 +177,31 @@ export function applyLogConfigSet(key, value) {
177
177
  */
178
178
  export function applyTeamConfigSet(key, value) {
179
179
  const patch = (team) => ({ team });
180
+ // EVERY NUMERIC KEY ROUTES THROUGH `clampTeamConfig`, not through
181
+ // `coercePositiveInt`, for the reason `applyRetryConfigSet` records one block
182
+ // below: `coercePositiveInt` returns its fallback for `n <= 0`, so
183
+ // `config set team.subagentTimeoutMs 0` would write 300000 (the old default)
184
+ // instead of the documented NO-LIMIT value, silently. The caller echoes the
185
+ // STORED value, which is what makes an out-of-range input visible.
186
+ const clampOne = (field) => {
187
+ const merged = clampTeamConfig({ ...DEFAULT_TEAM_CONFIG, [field]: value });
188
+ return { [field]: merged[field] };
189
+ };
180
190
  switch (key) {
181
191
  case 'team.enabled':
182
192
  return patch({ enabled: isTrue(value) });
183
193
  case 'team.maxSubagents':
184
- return patch({ maxSubagents: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxSubagents) });
194
+ return patch(clampOne('maxSubagents'));
185
195
  case 'team.maxConcurrent':
186
- return patch({ maxConcurrent: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxConcurrent) });
196
+ return patch(clampOne('maxConcurrent'));
187
197
  case 'team.subagentTimeoutMs':
188
- return patch({
189
- subagentTimeoutMs: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.subagentTimeoutMs),
190
- });
198
+ return patch(clampOne('subagentTimeoutMs'));
191
199
  case 'team.dispatchTimeoutMs':
192
- return patch({
193
- dispatchTimeoutMs: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.dispatchTimeoutMs),
194
- });
200
+ return patch(clampOne('dispatchTimeoutMs'));
195
201
  case 'team.maxTurnsPerSubagent':
196
- return patch({
197
- maxTurnsPerSubagent: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxTurnsPerSubagent),
198
- });
202
+ return patch(clampOne('maxTurnsPerSubagent'));
203
+ case 'team.overseer':
204
+ return patch({ overseer: isTrue(value) });
199
205
  default:
200
206
  return null;
201
207
  }
@@ -273,8 +279,6 @@ export function applyFastConfigSet(key, value) {
273
279
  return patch(clampOne('baseUrl'));
274
280
  case 'fast.thinkingLevel':
275
281
  return patch(clampOne('thinkingLevel'));
276
- case 'fast.delegate':
277
- return patch({ delegate: isTrue(value) });
278
282
  case 'fast.review':
279
283
  return patch({ review: isTrue(value) });
280
284
  case 'fast.reviewEveryTurns':
@@ -193,23 +193,6 @@ export interface CliFlags {
193
193
  * to keep in step with `toFlags`.
194
194
  */
195
195
  fastReview?: string;
196
- /**
197
- * `--fast-delegate` ⇒ true, `--no-fast-delegate` ⇒ false, absent ⇒ undefined
198
- * (web-use-tier-cooperation-and-control-closure §4.1.5).
199
- *
200
- * DECLARED AS A PAIR — MANDATORY, for the reason `--team`, `--todo-panel`,
201
- * `--render-governor`, `--retry` and `--fast` each record in turn, and here
202
- * with the SAME polarity as `--update`: `fast.delegate` is PERSISTED and
203
- * defaults to `true`, so a lone `--no-fast-delegate` would make commander
204
- * default `opts.fastDelegate` to `true` and silently overwrite a stored
205
- * `false` on every run that passed no flag at all.
206
- *
207
- * It exists because it is the ONLY `fast.*` sub-switch with no channel a
208
- * wrapper can reach: `--fast-review <n|off>` already covers `review` and
209
- * `reviewEveryTurns`, and `enabled` / `model` / `provider` have both a flag
210
- * and an environment variable.
211
- */
212
- fastDelegate?: boolean;
213
196
  /**
214
197
  * `--update` ⇒ true, `--no-update` ⇒ false, absent ⇒ undefined
215
198
  * (cli-auto-update §4.2).
@@ -243,12 +243,6 @@ function resolveFastConfig(flags, env, file) {
243
243
  ...(flags.fastProvider !== undefined && flags.fastProvider !== ''
244
244
  ? { provider: flags.fastProvider }
245
245
  : {}),
246
- // `!== undefined`, for the reason stated on `flags.fast` above: commander
247
- // materialises a lone `--no-fast-delegate` as `opts.fastDelegate = true`
248
- // when the flag is absent, and `delegate` defaults to TRUE — so a truthiness
249
- // check would force it on for every run that passed no flag at all,
250
- // overwriting a stored `false` with nothing to show for it.
251
- ...(flags.fastDelegate !== undefined ? { delegate: flags.fastDelegate } : {}),
252
246
  ...reviewPatch,
253
247
  });
254
248
  }
@@ -303,12 +303,43 @@ export interface TeamConfig {
303
303
  * failed subagent.
304
304
  */
305
305
  maxConcurrent: number;
306
- /** One child's wall clock before its own abort. */
306
+ /**
307
+ * One child's wall clock before its own abort. `0` (the default) means NO
308
+ * ceiling: a subagent runs until it finishes or is aborted, exactly like
309
+ * the lead, whose only in-run ceiling is the event-silence idle watchdog
310
+ * a child already keeps. A wedged child still dies on its OWN watchdog.
311
+ *
312
+ * WITH `team.overseer` ON (the default) this is the FIRST wall-clock
313
+ * INSPECTION point, not a hard abort: the supervisor looks and decides
314
+ * (wait / nudge / replace / abandon), so a slow-but-healthy child is no
315
+ * longer killed at an arbitrary wall clock.
316
+ */
307
317
  subagentTimeoutMs: number;
308
- /** The whole dispatch's wall clock. Also the `task` tool-timeout override. */
318
+ /**
319
+ * The whole dispatch's wall clock, and the `task` tool-timeout override.
320
+ * `0` (the default) means NO ceiling: the executor override of `0` arms no
321
+ * timer (see Core `ToolExecutor`), so the fan-out lasts as long as its
322
+ * slowest child and only an Esc ends it early.
323
+ */
309
324
  dispatchTimeoutMs: number;
310
- /** Runaway-loop cap: turns one child may take before it is stopped. */
325
+ /**
326
+ * Runaway-loop cap: turns one child may take before it is stopped.
327
+ * `0` (the default) means NO cap - the same exposure the lead has, and
328
+ * the deliberate trade of "subagents as capable as the main agent".
329
+ */
311
330
  maxTurnsPerSubagent: number;
331
+ /**
332
+ * The dispatch supervisor (team-overseer): time ceilings on children
333
+ * become inspection triggers for a fast-tier supervisor that decides
334
+ * wait / nudge / replace / abandon, instead of aborts. Default TRUE.
335
+ *
336
+ * READ PER DISPATCH. With the supervisor off (or no fast tier), children
337
+ * run exactly the pre-feature regime: `subagentTimeoutMs` is a hard abort
338
+ * and silence is the child's own watchdog. With it ON,
339
+ * `subagentTimeoutMs > 0` is the FIRST wall-clock inspection point, not
340
+ * a death sentence.
341
+ */
342
+ overseer: boolean;
312
343
  }
313
344
  /**
314
345
  * The requirement's ceiling on the fan-out (R-g).
@@ -458,8 +489,6 @@ export interface FastConfig {
458
489
  /** Applied to fast-tier children and to the review call. Defaults to `'off'`:
459
490
  * a "fast" model asked to think for 32 768 tokens is not fast (D-13). */
460
491
  thinkingLevel: ThinkingLevel;
461
- /** Allow `model: "fast"` on `task`. */
462
- delegate: boolean;
463
492
  /** Run the periodic asynchronous review. */
464
493
  review: boolean;
465
494
  /** Completed turns between reviews. */
@@ -584,10 +613,13 @@ export interface CompactionConfig {
584
613
  * (context-auto-compaction-hardening §3.4 / W3). Default TRUE.
585
614
  *
586
615
  * IT REVERSES D-15, WHOSE PREMISE IS MEASURABLY FALSE at the configured bounds:
587
- * `team.maxTurnsPerSubagent` is 24 and `team.dispatchTimeoutMs` is 900 000, and
588
- * 24 turns over fifteen minutes is the same profile as the lead run this
589
- * feature exists for. When a child overflows today its history is discarded and
590
- * the lead receives one partial sentence for the whole dispatch.
616
+ * D-15 measured `team.maxTurnsPerSubagent` 24 and `team.dispatchTimeoutMs`
617
+ * 900 000, where 24 turns over fifteen minutes is already the same profile
618
+ * as the lead run this feature exists for — and the parity defaults of
619
+ * 2ff15a9c3 later removed both ceilings (0 = no turn cap, no wall clock),
620
+ * which makes the premise falser still, not truer. When a child overflows
621
+ * today its history is discarded and the lead receives one partial sentence
622
+ * for the whole dispatch.
591
623
  *
592
624
  * `false` REPRODUCES ROUND 1 EXACTLY: `subagent.ts` spreads no `contextManager`
593
625
  * key at all, so the child's loop gate tests an absent field.
@@ -447,15 +447,23 @@ export const DEFAULT_TEAM_CONFIG = {
447
447
  enabled: true,
448
448
  maxSubagents: 5,
449
449
  maxConcurrent: 3,
450
- subagentTimeoutMs: 300_000,
451
- dispatchTimeoutMs: 900_000,
452
- maxTurnsPerSubagent: 24,
450
+ // 0 = NO LIMIT. Subagents run under the same regime as the lead: no
451
+ // wall clock, no turn cap; only the idle watchdog and the user's Esc.
452
+ subagentTimeoutMs: 0,
453
+ dispatchTimeoutMs: 0,
454
+ maxTurnsPerSubagent: 0,
455
+ // The dispatch supervisor, default ON (team-overseer): ceilings become
456
+ // inspections; the fast-tier supervisor decides what to do.
457
+ overseer: true,
453
458
  };
454
459
  const SUBAGENTS_RANGE = { min: 1, max: HARD_MAX_SUBAGENTS };
455
460
  const CONCURRENT_RANGE = { min: 1, max: HARD_MAX_SUBAGENTS };
456
- const SUBAGENT_TIMEOUT_RANGE = { min: 30_000, max: 1_800_000 };
457
- const DISPATCH_TIMEOUT_RANGE = { min: 60_000, max: 3_600_000 };
458
- const TURNS_RANGE = { min: 4, max: 100 };
461
+ // `min: 0` is the documented OFF SWITCH for each bound, so these MUST use
462
+ // `clampIntAllowingZero`: plain `clampInt` folds `0` back to the fallback
463
+ // ("my setting won't stick"), the exact trap `scrollResumeMs` records above.
464
+ const SUBAGENT_TIMEOUT_RANGE = { min: 0, max: 1_800_000 };
465
+ const DISPATCH_TIMEOUT_RANGE = { min: 0, max: 3_600_000 };
466
+ const TURNS_RANGE = { min: 0, max: 100 };
459
467
  /**
460
468
  * THE single gate for every team-config read AND write, mirroring
461
469
  * `clampSkillsConfig` / `clampLogConfig` for the reason those two record:
@@ -474,9 +482,10 @@ export function clampTeamConfig(raw) {
474
482
  enabled: bool(src.enabled, DEFAULT_TEAM_CONFIG.enabled),
475
483
  maxSubagents,
476
484
  maxConcurrent: Math.min(maxSubagents, clampInt(src.maxConcurrent, DEFAULT_TEAM_CONFIG.maxConcurrent, CONCURRENT_RANGE)),
477
- subagentTimeoutMs: clampInt(src.subagentTimeoutMs, DEFAULT_TEAM_CONFIG.subagentTimeoutMs, SUBAGENT_TIMEOUT_RANGE),
478
- dispatchTimeoutMs: clampInt(src.dispatchTimeoutMs, DEFAULT_TEAM_CONFIG.dispatchTimeoutMs, DISPATCH_TIMEOUT_RANGE),
479
- maxTurnsPerSubagent: clampInt(src.maxTurnsPerSubagent, DEFAULT_TEAM_CONFIG.maxTurnsPerSubagent, TURNS_RANGE),
485
+ subagentTimeoutMs: clampIntAllowingZero(src.subagentTimeoutMs, DEFAULT_TEAM_CONFIG.subagentTimeoutMs, SUBAGENT_TIMEOUT_RANGE),
486
+ dispatchTimeoutMs: clampIntAllowingZero(src.dispatchTimeoutMs, DEFAULT_TEAM_CONFIG.dispatchTimeoutMs, DISPATCH_TIMEOUT_RANGE),
487
+ maxTurnsPerSubagent: clampIntAllowingZero(src.maxTurnsPerSubagent, DEFAULT_TEAM_CONFIG.maxTurnsPerSubagent, TURNS_RANGE),
488
+ overseer: bool(src.overseer, DEFAULT_TEAM_CONFIG.overseer),
480
489
  };
481
490
  }
482
491
  export const DEFAULT_TODO_CONFIG = {
@@ -615,7 +624,6 @@ export const DEFAULT_FAST_CONFIG = {
615
624
  model: '',
616
625
  baseUrl: '',
617
626
  thinkingLevel: 'off',
618
- delegate: true,
619
627
  review: true,
620
628
  reviewEveryTurns: 5,
621
629
  reviewContextTurns: 3,
@@ -655,7 +663,6 @@ export function clampFastConfig(raw) {
655
663
  model: text(src.model, DEFAULT_FAST_CONFIG.model),
656
664
  baseUrl: text(src.baseUrl, DEFAULT_FAST_CONFIG.baseUrl),
657
665
  thinkingLevel: clampThinkingLevel(src.thinkingLevel, DEFAULT_FAST_CONFIG.thinkingLevel),
658
- delegate: bool(src.delegate, DEFAULT_FAST_CONFIG.delegate),
659
666
  review: bool(src.review, DEFAULT_FAST_CONFIG.review),
660
667
  reviewEveryTurns: clampInt(src.reviewEveryTurns, DEFAULT_FAST_CONFIG.reviewEveryTurns, REVIEW_EVERY_RANGE),
661
668
  reviewContextTurns: clampInt(src.reviewContextTurns, DEFAULT_FAST_CONFIG.reviewContextTurns, REVIEW_CONTEXT_RANGE),
@@ -193,7 +193,10 @@ export interface ExecTodoEvent {
193
193
  export interface ExecTeamEvent {
194
194
  type: 'team';
195
195
  sessionId: string;
196
- subtype: 'dispatch_start' | 'agent_update' | 'dispatch_end';
196
+ subtype: 'dispatch_start' | 'agent_update' | 'dispatch_end' | 'overseer';
197
+ action?: string;
198
+ trigger?: string;
199
+ reason?: string;
197
200
  label?: string;
198
201
  phase?: string;
199
202
  description?: string;
@@ -219,11 +222,11 @@ export interface ExecFastReviewEvent {
219
222
  * not know is unaffected.
220
223
  *
221
224
  * THE PROBLEM IT SOLVES. `fast_review` only fires when a review produced TEXT,
222
- * so every other outcome — a review that failed, a tier that switched itself off
223
- * after three failures, a budget that ran out, a delegation that did or did not
224
- * happen — was invisible to a wrapper. The single most likely and least
225
- * self-diagnosable failure ("it runs but not one review card ever arrives") had
226
- * no signal at all on this stream.
225
+ * so every other outcome — a review that failed, a tier that switched itself
226
+ * off after three failures, or a budget that ran out — was invisible to a
227
+ * wrapper. The single most likely and least self-diagnosable failure ("it
228
+ * runs but not one review card ever arrives") had no signal at all on this
229
+ * stream.
227
230
  *
228
231
  * IT IS A STATE EVENT, NOT A LOG LINE. It fires on `tier_changed` and on every
229
232
  * `review_end`, which in a session with reviews is a handful per hundred turns.
@@ -249,7 +252,11 @@ export interface ExecFastTierEvent {
249
252
  reviews: number;
250
253
  reviewBudget: number;
251
254
  budgetReached: boolean;
252
- /** Fast-tier children dispatched — the only evidence delegation is working. */
255
+ /**
256
+ * DEPRECATED, ALWAYS `0`. Fast-tier children no longer exist (every `task`
257
+ * child runs the lead's model); the field is kept so schema-v1 consumers
258
+ * keep parsing. Do not build new readers on it.
259
+ */
253
260
  delegated: number;
254
261
  inFlight: boolean;
255
262
  /** Present only on the `review_end` branch. */
@@ -399,8 +406,8 @@ export interface ExecInitParams {
399
406
  *
400
407
  * · `'interrupt'` — `{"type":"interrupt"}` on stdin settles the turn
401
408
  * without killing the process.
402
- * · `'fast-policy'` — this build understands `--fast-delegate` and
403
- * `--fast-review`. FOR AFTER-THE-FACT DISPLAY ONLY:
409
+ * · `'fast-policy'` — this build understands `--fast-review`.
410
+ * FOR AFTER-THE-FACT DISPLAY ONLY:
404
411
  * argv is fixed before `system/init` arrives, so it
405
412
  * cannot gate whether the flags are passed.
406
413
  * · `'fast-tier-events'` — this build emits `fast_tier`. Its ABSENCE is what
@@ -34,8 +34,8 @@ export const EXEC_SCHEMA_VERSION = 1;
34
34
  *
35
35
  * · `'interrupt'` — `{"type":"interrupt"}` on stdin settles the turn
36
36
  * without killing the process.
37
- * · `'fast-policy'` — this build understands `--fast-delegate` and
38
- * `--fast-review`. FOR AFTER-THE-FACT DISPLAY ONLY:
37
+ * · `'fast-policy'` — this build understands `--fast-review`.
38
+ * FOR AFTER-THE-FACT DISPLAY ONLY:
39
39
  * argv is fixed before `system/init` arrives, so it
40
40
  * cannot gate whether the flags are passed.
41
41
  * · `'fast-tier-events'` — this build emits `fast_tier`. Its ABSENCE is what
@@ -153,7 +153,10 @@ export class ExecRunner {
153
153
  reviews: snapshot.reviews,
154
154
  reviewBudget: snapshot.reviewBudget,
155
155
  budgetReached: snapshot.budgetReached,
156
- delegated: snapshot.delegated,
156
+ // Deprecated in schema v1: always 0 since fast-tier children were
157
+ // removed (every task child runs the lead's model). Emitted so
158
+ // schema-v1 consumers keep parsing.
159
+ delegated: 0,
157
160
  inFlight: snapshot.inFlight,
158
161
  ...(event.type === 'review_end'
159
162
  ? {
@@ -704,6 +707,19 @@ export class ExecRunner {
704
707
  total: runs.length,
705
708
  aborted: event.outcome.aborted,
706
709
  });
710
+ return;
711
+ }
712
+ if (event.type === 'overseer') {
713
+ this.emit({
714
+ type: 'team',
715
+ sessionId: this.sessionId,
716
+ subtype: 'overseer',
717
+ label: event.label,
718
+ action: event.decision.action,
719
+ trigger: event.trigger,
720
+ reason: event.decision.reason,
721
+ });
722
+ return;
707
723
  }
708
724
  }
709
725
  }
@@ -76,7 +76,7 @@ export declare const FAST_LIMITS: {
76
76
  * A critique waiting for its injection window goes stale (RV-12).
77
77
  *
78
78
  * The window is not guaranteed to be prompt: one `task` dispatch is a single
79
- * tool call that can run for `team.dispatchTimeoutMs`, so a critique triggered
79
+ * tool call that can run for as long as its children need, so a critique triggered
80
80
  * just before one arrives at the far side describing a state five children
81
81
  * have since rewritten. Advice that is merely late is worse than no advice,
82
82
  * because the lead cannot tell that it is late.
@@ -95,4 +95,4 @@ export declare const FAST_LIMITS: {
95
95
  * changes, so a behaviour report can be tied to a block revision with one grep -
96
96
  * exactly what `TEAM_BLOCK_VERSION` and `TODO_BLOCK_VERSION` are for.
97
97
  */
98
- export declare const FAST_BLOCK_VERSION = "v1-2026-07";
98
+ export declare const FAST_BLOCK_VERSION = "v2-2026-10";
@@ -76,7 +76,7 @@ export const FAST_LIMITS = {
76
76
  * A critique waiting for its injection window goes stale (RV-12).
77
77
  *
78
78
  * The window is not guaranteed to be prompt: one `task` dispatch is a single
79
- * tool call that can run for `team.dispatchTimeoutMs`, so a critique triggered
79
+ * tool call that can run for as long as its children need, so a critique triggered
80
80
  * just before one arrives at the far side describing a state five children
81
81
  * have since rewritten. Advice that is merely late is worse than no advice,
82
82
  * because the lead cannot tell that it is late.
@@ -95,4 +95,4 @@ export const FAST_LIMITS = {
95
95
  * changes, so a behaviour report can be tied to a block revision with one grep -
96
96
  * exactly what `TEAM_BLOCK_VERSION` and `TODO_BLOCK_VERSION` are for.
97
97
  */
98
- export const FAST_BLOCK_VERSION = 'v1-2026-07';
98
+ export const FAST_BLOCK_VERSION = 'v2-2026-10';
@@ -19,23 +19,22 @@ export interface FastBlockParams {
19
19
  /** The resolved fast model id, interpolated so the prompt never names a model
20
20
  * the tier will not actually use (§3.9 / RV-3). */
21
21
  model: string;
22
- /** `fast.delegate` — whether `model: "fast"` is honoured on `task`. */
23
- delegate: boolean;
24
22
  /** `fast.review` — whether `<fast_review>` blocks can appear at all. */
25
23
  review: boolean;
26
24
  }
27
25
  /**
28
- * The lead's `<fast_tier>` block. Under 1100 characters on purpose: it is paid
29
- * for on EVERY turn of every fast-enabled session, so anything that does not
30
- * change a delegation decision does not belong in it (R-11).
26
+ * The `<fast_tier>` block, spliced for the lead AND for task children running
27
+ * under a per-child reviewer. Under 1100 characters on purpose: it is paid for
28
+ * on EVERY turn of every fast-enabled session, so anything that does not change
29
+ * a decision does not belong in it (R-11).
31
30
  *
32
- * EACH PARAGRAPH IS PRESENT ONLY WHEN ITS CAPABILITY IS. `fast.delegate: false,
33
- * fast.review: true` advertises only the review, because telling a model about a
34
- * `model: "fast"` field that the normalizer will downgrade every time is how you
35
- * get a model that keeps asking for something it can never have.
31
+ * REVIEW-ONLY SIN MAIN-AGENT PARITY: every `task` child runs the lead's own
32
+ * model, so the block no longer advertises a per-child `model:"fast"`. Telling
33
+ * a model about a field that no longer exists is how you get a model that keeps
34
+ * asking for something it can never have.
36
35
  *
37
- * Returns `''` when neither capability is live, which the splice treats as "no
38
- * block" and which keeps the byte-identity branch intact.
36
+ * Returns `''` when the review is off, which the splice treats as "no block"
37
+ * and which keeps the byte-identity branch intact.
39
38
  */
40
39
  export declare function buildFastBlock(params: FastBlockParams): string;
41
40
  export interface ReviewSystemPromptParams {
@@ -16,33 +16,32 @@
16
16
  import { FAST_BLOCK_VERSION } from './limits.js';
17
17
  export { FAST_BLOCK_VERSION };
18
18
  /**
19
- * The lead's `<fast_tier>` block. Under 1100 characters on purpose: it is paid
20
- * for on EVERY turn of every fast-enabled session, so anything that does not
21
- * change a delegation decision does not belong in it (R-11).
19
+ * The `<fast_tier>` block, spliced for the lead AND for task children running
20
+ * under a per-child reviewer. Under 1100 characters on purpose: it is paid for
21
+ * on EVERY turn of every fast-enabled session, so anything that does not change
22
+ * a decision does not belong in it (R-11).
22
23
  *
23
- * EACH PARAGRAPH IS PRESENT ONLY WHEN ITS CAPABILITY IS. `fast.delegate: false,
24
- * fast.review: true` advertises only the review, because telling a model about a
25
- * `model: "fast"` field that the normalizer will downgrade every time is how you
26
- * get a model that keeps asking for something it can never have.
24
+ * REVIEW-ONLY SIN MAIN-AGENT PARITY: every `task` child runs the lead's own
25
+ * model, so the block no longer advertises a per-child `model:"fast"`. Telling
26
+ * a model about a field that no longer exists is how you get a model that keeps
27
+ * asking for something it can never have.
27
28
  *
28
- * Returns `''` when neither capability is live, which the splice treats as "no
29
- * block" and which keeps the byte-identity branch intact.
29
+ * Returns `''` when the review is off, which the splice treats as "no block"
30
+ * and which keeps the byte-identity branch intact.
30
31
  */
31
32
  export function buildFastBlock(params) {
32
- if (!params.delegate && !params.review)
33
+ if (!params.review)
33
34
  return '';
34
- const lines = [
35
+ return [
35
36
  '<fast_tier>',
36
37
  `A second, cheaper model is available in this session (${params.model}).`,
37
- ];
38
- if (params.delegate) {
39
- lines.push('', 'Delegation: pass model:"fast" to a task subagent whose work is mechanical and', 'high-volume - reading or searching many files, summarizing long command output,', 'applying the same small edit in several places. Keep model:"main" (the default)', 'for design decisions, tricky debugging, and anything that has to be right the', 'first time. A fast subagent has the same tools and the same permissions as any', 'other; only the model differs.');
40
- }
41
- if (params.review) {
42
- lines.push('', 'Reviews: every few turns a <fast_review> block may appear in the conversation.', 'It is an automated second opinion from the fast model - NOT a message from the', 'user. Treat it as advice: act on it when it is right, say so briefly and carry', 'on when it is not, and never ask the user to confirm it.');
43
- }
44
- lines.push('</fast_tier>');
45
- return lines.join('\n');
38
+ '',
39
+ 'Reviews: every few turns a <fast_review> block may appear in the conversation.',
40
+ 'It is an automated second opinion from the fast model - NOT a message from the',
41
+ 'user. Treat it as advice: act on it when it is right, say so briefly and carry',
42
+ 'on when it is not, and never ask the user to confirm it.',
43
+ '</fast_tier>',
44
+ ].join('\n');
46
45
  }
47
46
  /**
48
47
  * The reviewer's own system prompt.
@@ -97,7 +97,7 @@ export interface FastReviewerDeps {
97
97
  * `FastWiring.snapshot()`, so the self-disable can announce itself.
98
98
  *
99
99
  * The reviewer cannot build a `FastSnapshot` on its own — `live`,
100
- * `sameAsMain`, `delegated` and `pricingUnknown` all belong to the wiring —
100
+ * `sameAsMain` and `pricingUnknown` all belong to the wiring —
101
101
  * and the two existing `tier_changed` emitters read it the same way.
102
102
  */
103
103
  snapshot(): FastSnapshot;
@@ -375,7 +375,7 @@ export class FastReviewer {
375
375
  return;
376
376
  this.budgetAnnouncedAt = limit;
377
377
  this.deps.notify('info', `Fast reviews: session budget reached (${limit}). ` +
378
- `Delegation is unaffected. Raise it with /fast budget <n>.`);
378
+ `Raise it with /fast budget <n>.`);
379
379
  this.log.info('fast_review_budget_reached', { limit, reviews: this.reviewIndex });
380
380
  }
381
381
  // -----------------------------------------------------------------------
@@ -107,8 +107,6 @@ export interface FastSnapshot {
107
107
  * report a budget reached for a session that never had one (RV-H10).
108
108
  */
109
109
  budgetReached: boolean;
110
- /** Fast-tier children dispatched this session. */
111
- delegated: number;
112
110
  usage: TokenUsage;
113
111
  /**
114
112
  * No price table for `model` (C-11 / RV-4).
@@ -7,8 +7,8 @@
7
7
  * THIS CLASS EXISTS FOR A BUDGET REASON, NOT AN AESTHETIC ONE (C-12 / RV-5).
8
8
  * `CLAUDE.md` caps a source file at 1000 lines and `controller.ts` was at 960
9
9
  * before this feature, which handed it nine new responsibilities. `FastWiring`
10
- * owns the flag pair, the tier cache, the reviewer's lifetime and the
11
- * `resolveTier` closure; `AgentController` keeps one field and four thin
10
+ * owns the flag pair, the tier cache, the reviewer's lifetime and the shared
11
+ * fail-fast review transport; `AgentController` keeps one field and four thin
12
12
  * forwarders. It is still CLI-local and still holds no `ui/` import.
13
13
  *
14
14
  * TWO FLAGS AND ONE LIVE PREDICATE (§3.3), which is the shape every optional
@@ -31,10 +31,10 @@
31
31
  * caused it (`agent-loop.ts:140-145`).
32
32
  */
33
33
  import type { ModelRole } from '../config/model-profiles.js';
34
- import { type AgentEvent, type AssistantMessage, type LLMRequest, type ModelRef, type ThinkingLevel } from '@aragon-agent/core';
34
+ import { type AgentEvent, type AssistantMessage, type LLMRequest, type ModelRef } from '@aragon-agent/core';
35
35
  import type { CliConfig } from '../config/schema.js';
36
36
  import type { NoticeLevel } from '../agent/reducer.js';
37
- import type { FastEventListener, FastSnapshot, FastTier, FastTierName } from './types.js';
37
+ import type { FastEventListener, FastSnapshot, FastTier } from './types.js';
38
38
  export interface FastWiringDeps {
39
39
  /** Read LIVE on every call - the controller replaces the object on mutation. */
40
40
  getConfig: () => CliConfig;
@@ -88,7 +88,6 @@ export declare class FastWiring {
88
88
  private tier;
89
89
  /** Warned once per TRANSITION, so a broken tier does not shout every turn. */
90
90
  private lastReportedReason;
91
- private delegated;
92
91
  constructor(deps: FastWiringDeps);
93
92
  /**
94
93
  * The registry the REVIEW calls through — separate from the lead's, and
@@ -120,22 +119,13 @@ export declare class FastWiring {
120
119
  * §3.3's live predicate. THE THIRD TERM IS THE POINT - see the header.
121
120
  *
122
121
  * `FastReviewer.isSelfDisabled()` is DELIBERATELY NOT a fourth term. Folding
123
- * it in would look tidier and would break something else: `delegationAvailable()`
124
- * is `available() && config.fast.delegate === true`, so three consecutive
125
- * REVIEW failures would silently switch DELEGATION off as well - turning a
126
- * diagnostic signal into a behaviour change for every user. The two facts are
127
- * reported separately on `FastSnapshot` instead (`live` / `selfDisabled`).
122
+ * it in would look tidier and would break something else: per-child reviewers
123
+ * gate on `available()` too, so three consecutive REVIEW failures would
124
+ * silently stop every child's reviews as well - turning a diagnostic signal
125
+ * into a behaviour change for every dispatch. The two facts are reported
126
+ * separately on `FastSnapshot` instead (`live` / `selfDisabled`).
128
127
  */
129
128
  available(): boolean;
130
- /**
131
- * Whether `model: "fast"` on `task` is honoured right now.
132
- *
133
- * Evaluated AT DISPATCH TIME, never snapshot (§3.4 / RV-3). `fast.delegate:
134
- * false` and "the tier stopped resolving twenty minutes ago" are the same
135
- * observable to the model - a downgrade, counted and reported - which is the
136
- * honest reading of both.
137
- */
138
- delegationAvailable(): boolean;
139
129
  setEnabled(enabled: boolean): void;
140
130
  /**
141
131
  * Re-resolve the tier after ANY config mutation (§3.2 rule 8 / §3.9 / RV-3).
@@ -152,24 +142,18 @@ export declare class FastWiring {
152
142
  * (§3.3). Raised once, at construction, through the controller's `notify`.
153
143
  */
154
144
  startupNotice(): string | null;
145
+ /** The resolved fast `ModelRef`, or `null` - for cost lookups (§3.6). */
146
+ fastRef(): ModelRef | null;
155
147
  /**
156
- * The `ModelRef` and thinking level one subagent tier runs on.
148
+ * The fail-fast review transport, exposed so PER-CHILD reviewers share ONE
149
+ * registry instead of building one each - the same adapter-state reuse
150
+ * argument `TeamRuntime` makes about the lead's `ProviderRegistry`.
157
151
  *
158
- * `'fast'` FALLS BACK TO MAIN when delegation is not available, so the two
159
- * guards agree BY CONSTRUCTION: the normalizer can never produce a
160
- * `tier: 'fast'` spec this factory would refuse, and this factory can never
161
- * build a child against a `ModelRef` the tier no longer resolves (RV-3).
152
+ * Lazy: a session that never reviews allocates nothing (I-2). The retry
153
+ * policy is `FAST_RETRY_POLICY`, deliberately not the user's `retry` config
154
+ * - see `getFastRegistry()` for the reason.
162
155
  */
163
- resolveTier(tier: FastTierName): {
164
- ref: ModelRef;
165
- thinkingLevel: ThinkingLevel;
166
- role?: ModelRole;
167
- };
168
- /** The resolved fast `ModelRef`, or `null` - for per-tier cost lookup (§3.6). */
169
- fastRef(): ModelRef | null;
170
- /** Count fast-tier children so `/fast status` can answer "is this saving me
171
- * anything?" — the aggregate on the status bar cannot. */
172
- noteDelegated(count: number): void;
156
+ completeViaFastRegistry(providerId: string, request: LLMRequest): Promise<AssistantMessage>;
173
157
  setGoal(text: string): void;
174
158
  abort(): void;
175
159
  dispose(): void;