@aragon-agent/cli 0.6.16 → 0.6.17

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 (55) hide show
  1. package/README.md +44 -47
  2. package/dist/agent/controller.js +39 -47
  3. package/dist/agent/reducer.js +0 -4
  4. package/dist/cli.js +0 -13
  5. package/dist/commands/builtins.js +3 -17
  6. package/dist/compaction/child.d.ts +10 -8
  7. package/dist/compaction/child.js +23 -25
  8. package/dist/compaction/limits.d.ts +0 -2
  9. package/dist/compaction/limits.js +0 -2
  10. package/dist/config/cli-commands.d.ts +1 -1
  11. package/dist/config/cli-commands.js +16 -15
  12. package/dist/config/load.d.ts +0 -17
  13. package/dist/config/load.js +0 -6
  14. package/dist/config/schema.d.ts +17 -5
  15. package/dist/config/schema.js +14 -11
  16. package/dist/exec/events.d.ts +12 -8
  17. package/dist/exec/events.js +2 -2
  18. package/dist/exec/runner.js +4 -1
  19. package/dist/fast/limits.d.ts +2 -2
  20. package/dist/fast/limits.js +2 -2
  21. package/dist/fast/prompt.d.ts +10 -11
  22. package/dist/fast/prompt.js +19 -20
  23. package/dist/fast/reviewer.d.ts +1 -1
  24. package/dist/fast/reviewer.js +1 -1
  25. package/dist/fast/types.d.ts +0 -2
  26. package/dist/fast/wiring.d.ts +18 -34
  27. package/dist/fast/wiring.js +20 -51
  28. package/dist/session/validate-session.js +3 -1
  29. package/dist/team/child-reviewer.d.ts +105 -0
  30. package/dist/team/child-reviewer.js +117 -0
  31. package/dist/team/normalize.d.ts +7 -26
  32. package/dist/team/normalize.js +9 -22
  33. package/dist/team/prompt.d.ts +0 -11
  34. package/dist/team/prompt.js +3 -12
  35. package/dist/team/report.d.ts +4 -19
  36. package/dist/team/report.js +1 -27
  37. package/dist/team/runtime.d.ts +5 -10
  38. package/dist/team/runtime.js +35 -39
  39. package/dist/team/subagent.d.ts +18 -18
  40. package/dist/team/subagent.js +80 -30
  41. package/dist/team/task-tool.d.ts +6 -26
  42. package/dist/team/task-tool.js +13 -47
  43. package/dist/team/types.d.ts +3 -42
  44. package/dist/ui/App.js +3 -4
  45. package/dist/ui/BottomStatusRow.js +2 -1
  46. package/dist/ui/StatusBar.js +2 -1
  47. package/dist/ui/TeamPanel.js +1 -1
  48. package/dist/ui/entries/TeamCard.js +1 -6
  49. package/dist/ui/layout/status-detail-layout.js +12 -2
  50. package/dist/ui/layout/status-layout.d.ts +4 -2
  51. package/dist/ui/layout/status-layout.js +24 -1
  52. package/dist/ui/overlays/HelpOverlay.js +1 -2
  53. package/package.json +2 -2
  54. package/dist/ui/ModeChip.d.ts +0 -18
  55. 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 = [
@@ -81,7 +81,6 @@ export const FAST_CONFIG_SET_KEYS = [
81
81
  'fast.model',
82
82
  'fast.baseUrl',
83
83
  'fast.thinkingLevel',
84
- 'fast.delegate',
85
84
  'fast.review',
86
85
  'fast.reviewEveryTurns',
87
86
  'fast.reviewContextTurns',
@@ -177,25 +176,29 @@ export function applyLogConfigSet(key, value) {
177
176
  */
178
177
  export function applyTeamConfigSet(key, value) {
179
178
  const patch = (team) => ({ team });
179
+ // EVERY NUMERIC KEY ROUTES THROUGH `clampTeamConfig`, not through
180
+ // `coercePositiveInt`, for the reason `applyRetryConfigSet` records one block
181
+ // below: `coercePositiveInt` returns its fallback for `n <= 0`, so
182
+ // `config set team.subagentTimeoutMs 0` would write 300000 (the old default)
183
+ // instead of the documented NO-LIMIT value, silently. The caller echoes the
184
+ // STORED value, which is what makes an out-of-range input visible.
185
+ const clampOne = (field) => {
186
+ const merged = clampTeamConfig({ ...DEFAULT_TEAM_CONFIG, [field]: value });
187
+ return { [field]: merged[field] };
188
+ };
180
189
  switch (key) {
181
190
  case 'team.enabled':
182
191
  return patch({ enabled: isTrue(value) });
183
192
  case 'team.maxSubagents':
184
- return patch({ maxSubagents: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxSubagents) });
193
+ return patch(clampOne('maxSubagents'));
185
194
  case 'team.maxConcurrent':
186
- return patch({ maxConcurrent: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxConcurrent) });
195
+ return patch(clampOne('maxConcurrent'));
187
196
  case 'team.subagentTimeoutMs':
188
- return patch({
189
- subagentTimeoutMs: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.subagentTimeoutMs),
190
- });
197
+ return patch(clampOne('subagentTimeoutMs'));
191
198
  case 'team.dispatchTimeoutMs':
192
- return patch({
193
- dispatchTimeoutMs: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.dispatchTimeoutMs),
194
- });
199
+ return patch(clampOne('dispatchTimeoutMs'));
195
200
  case 'team.maxTurnsPerSubagent':
196
- return patch({
197
- maxTurnsPerSubagent: coercePositiveInt(value, DEFAULT_TEAM_CONFIG.maxTurnsPerSubagent),
198
- });
201
+ return patch(clampOne('maxTurnsPerSubagent'));
199
202
  default:
200
203
  return null;
201
204
  }
@@ -273,8 +276,6 @@ export function applyFastConfigSet(key, value) {
273
276
  return patch(clampOne('baseUrl'));
274
277
  case 'fast.thinkingLevel':
275
278
  return patch(clampOne('thinkingLevel'));
276
- case 'fast.delegate':
277
- return patch({ delegate: isTrue(value) });
278
279
  case 'fast.review':
279
280
  return patch({ review: isTrue(value) });
280
281
  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,11 +303,25 @@ 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
+ */
307
312
  subagentTimeoutMs: number;
308
- /** The whole dispatch's wall clock. Also the `task` tool-timeout override. */
313
+ /**
314
+ * The whole dispatch's wall clock, and the `task` tool-timeout override.
315
+ * `0` (the default) means NO ceiling: the executor override of `0` arms no
316
+ * timer (see Core `ToolExecutor`), so the fan-out lasts as long as its
317
+ * slowest child and only an Esc ends it early.
318
+ */
309
319
  dispatchTimeoutMs: number;
310
- /** Runaway-loop cap: turns one child may take before it is stopped. */
320
+ /**
321
+ * Runaway-loop cap: turns one child may take before it is stopped.
322
+ * `0` (the default) means NO cap - the same exposure the lead has, and
323
+ * the deliberate trade of "subagents as capable as the main agent".
324
+ */
311
325
  maxTurnsPerSubagent: number;
312
326
  }
313
327
  /**
@@ -458,8 +472,6 @@ export interface FastConfig {
458
472
  /** Applied to fast-tier children and to the review call. Defaults to `'off'`:
459
473
  * a "fast" model asked to think for 32 768 tokens is not fast (D-13). */
460
474
  thinkingLevel: ThinkingLevel;
461
- /** Allow `model: "fast"` on `task`. */
462
- delegate: boolean;
463
475
  /** Run the periodic asynchronous review. */
464
476
  review: boolean;
465
477
  /** Completed turns between reviews. */
@@ -447,15 +447,20 @@ 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,
453
455
  };
454
456
  const SUBAGENTS_RANGE = { min: 1, max: HARD_MAX_SUBAGENTS };
455
457
  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 };
458
+ // `min: 0` is the documented OFF SWITCH for each bound, so these MUST use
459
+ // `clampIntAllowingZero`: plain `clampInt` folds `0` back to the fallback
460
+ // ("my setting won't stick"), the exact trap `scrollResumeMs` records above.
461
+ const SUBAGENT_TIMEOUT_RANGE = { min: 0, max: 1_800_000 };
462
+ const DISPATCH_TIMEOUT_RANGE = { min: 0, max: 3_600_000 };
463
+ const TURNS_RANGE = { min: 0, max: 100 };
459
464
  /**
460
465
  * THE single gate for every team-config read AND write, mirroring
461
466
  * `clampSkillsConfig` / `clampLogConfig` for the reason those two record:
@@ -474,9 +479,9 @@ export function clampTeamConfig(raw) {
474
479
  enabled: bool(src.enabled, DEFAULT_TEAM_CONFIG.enabled),
475
480
  maxSubagents,
476
481
  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),
482
+ subagentTimeoutMs: clampIntAllowingZero(src.subagentTimeoutMs, DEFAULT_TEAM_CONFIG.subagentTimeoutMs, SUBAGENT_TIMEOUT_RANGE),
483
+ dispatchTimeoutMs: clampIntAllowingZero(src.dispatchTimeoutMs, DEFAULT_TEAM_CONFIG.dispatchTimeoutMs, DISPATCH_TIMEOUT_RANGE),
484
+ maxTurnsPerSubagent: clampIntAllowingZero(src.maxTurnsPerSubagent, DEFAULT_TEAM_CONFIG.maxTurnsPerSubagent, TURNS_RANGE),
480
485
  };
481
486
  }
482
487
  export const DEFAULT_TODO_CONFIG = {
@@ -615,7 +620,6 @@ export const DEFAULT_FAST_CONFIG = {
615
620
  model: '',
616
621
  baseUrl: '',
617
622
  thinkingLevel: 'off',
618
- delegate: true,
619
623
  review: true,
620
624
  reviewEveryTurns: 5,
621
625
  reviewContextTurns: 3,
@@ -655,7 +659,6 @@ export function clampFastConfig(raw) {
655
659
  model: text(src.model, DEFAULT_FAST_CONFIG.model),
656
660
  baseUrl: text(src.baseUrl, DEFAULT_FAST_CONFIG.baseUrl),
657
661
  thinkingLevel: clampThinkingLevel(src.thinkingLevel, DEFAULT_FAST_CONFIG.thinkingLevel),
658
- delegate: bool(src.delegate, DEFAULT_FAST_CONFIG.delegate),
659
662
  review: bool(src.review, DEFAULT_FAST_CONFIG.review),
660
663
  reviewEveryTurns: clampInt(src.reviewEveryTurns, DEFAULT_FAST_CONFIG.reviewEveryTurns, REVIEW_EVERY_RANGE),
661
664
  reviewContextTurns: clampInt(src.reviewContextTurns, DEFAULT_FAST_CONFIG.reviewContextTurns, REVIEW_CONTEXT_RANGE),
@@ -219,11 +219,11 @@ export interface ExecFastReviewEvent {
219
219
  * not know is unaffected.
220
220
  *
221
221
  * 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.
222
+ * so every other outcome — a review that failed, a tier that switched itself
223
+ * off after three failures, or a budget that ran out — was invisible to a
224
+ * wrapper. The single most likely and least self-diagnosable failure ("it
225
+ * runs but not one review card ever arrives") had no signal at all on this
226
+ * stream.
227
227
  *
228
228
  * IT IS A STATE EVENT, NOT A LOG LINE. It fires on `tier_changed` and on every
229
229
  * `review_end`, which in a session with reviews is a handful per hundred turns.
@@ -249,7 +249,11 @@ export interface ExecFastTierEvent {
249
249
  reviews: number;
250
250
  reviewBudget: number;
251
251
  budgetReached: boolean;
252
- /** Fast-tier children dispatched — the only evidence delegation is working. */
252
+ /**
253
+ * DEPRECATED, ALWAYS `0`. Fast-tier children no longer exist (every `task`
254
+ * child runs the lead's model); the field is kept so schema-v1 consumers
255
+ * keep parsing. Do not build new readers on it.
256
+ */
253
257
  delegated: number;
254
258
  inFlight: boolean;
255
259
  /** Present only on the `review_end` branch. */
@@ -399,8 +403,8 @@ export interface ExecInitParams {
399
403
  *
400
404
  * · `'interrupt'` — `{"type":"interrupt"}` on stdin settles the turn
401
405
  * without killing the process.
402
- * · `'fast-policy'` — this build understands `--fast-delegate` and
403
- * `--fast-review`. FOR AFTER-THE-FACT DISPLAY ONLY:
406
+ * · `'fast-policy'` — this build understands `--fast-review`.
407
+ * FOR AFTER-THE-FACT DISPLAY ONLY:
404
408
  * argv is fixed before `system/init` arrives, so it
405
409
  * cannot gate whether the flags are passed.
406
410
  * · `'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
  ? {
@@ -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;