@aragon-agent/cli 0.6.22 → 0.6.23
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.
- package/dist/agent/controller.js +9 -1
- package/dist/agent/reducer.js +8 -6
- package/dist/config/load.js +42 -3
- package/dist/config/schema.d.ts +25 -0
- package/dist/config/schema.js +38 -0
- package/dist/team/overseer.d.ts +5 -3
- package/dist/team/overseer.js +15 -4
- package/dist/team/runtime.js +20 -3
- package/dist/team/subagent.d.ts +10 -0
- package/dist/team/subagent.js +20 -6
- package/dist/team/types.d.ts +4 -3
- package/package.json +2 -2
package/dist/agent/controller.js
CHANGED
|
@@ -634,7 +634,15 @@ export class AgentController {
|
|
|
634
634
|
// `dispatchTimeoutMs: 0` (the default) reaches the executor as an
|
|
635
635
|
// override of `0` = NO TIMER AT ALL: the dispatch lasts as long as its
|
|
636
636
|
// slowest child, and only an Esc ends it early (main-agent parity).
|
|
637
|
-
|
|
637
|
+
// D-4, THE SECOND ARMING SITE: the runtime arms its own dispatch
|
|
638
|
+
// timer under the same gate, because the executor timeout being
|
|
639
|
+
// cooperative means this override must never be the ONLY ceiling.
|
|
640
|
+
// With the supervisor gate open the ceiling disarms HERE too, so
|
|
641
|
+
// team.overseer can never leave two kill paths alive behind one
|
|
642
|
+
// config. Read once at construction, exactly as before.
|
|
643
|
+
...(this.teamRegistered
|
|
644
|
+
? { task: config.team.overseer ? 0 : config.team.dispatchTimeoutMs }
|
|
645
|
+
: {}),
|
|
638
646
|
},
|
|
639
647
|
},
|
|
640
648
|
});
|
package/dist/agent/reducer.js
CHANGED
|
@@ -183,18 +183,20 @@ export function reduceEvent(event, cost, patches) {
|
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
185
|
* Tools that render their own transcript entry, so the generic tool card would
|
|
186
|
-
* be a duplicate.
|
|
187
|
-
*
|
|
186
|
+
* be a duplicate. `task` is the second member (F-8 A'3): every dispatch opens
|
|
187
|
+
* a TeamCard immediately after, so the pair read as TWO dispatches - the first
|
|
188
|
+
* failed, the second succeeded - when there was only ever one. `todo_write`'s
|
|
189
|
+
* reasons (todo-plan-execution §3.8 / D-10) carry over unchanged.
|
|
188
190
|
*
|
|
189
191
|
* SUPPRESSING ONLY `tool_call_start` IS SUFFICIENT, AND THAT IS THE WHOLE REASON
|
|
190
192
|
* THIS IS SAFE (D-10). Every later handler — `toolCallDelta` / `toolCallEnd` /
|
|
191
193
|
* `toolExecStart` / `toolExecEnd` — resolves its target through
|
|
192
194
|
* `findToolEntryId`, which returns `undefined` when no entry was created, and
|
|
193
|
-
* every one of them already early-returns on that. There is no fifth handler.
|
|
194
|
-
*
|
|
195
|
-
*
|
|
195
|
+
* every one of them already early-returns on that. There is no fifth handler.
|
|
196
|
+
* Blast radius verified: headless and `exec` writers key on EVENTS, not tool
|
|
197
|
+
* names, and `transcript-text` renders `kind:'team'` on its own.
|
|
196
198
|
*/
|
|
197
|
-
const SELF_RENDERING_TOOLS = new Set(['todo_write']);
|
|
199
|
+
const SELF_RENDERING_TOOLS = new Set(['todo_write', 'task']);
|
|
198
200
|
function reduceStreamEvent(se) {
|
|
199
201
|
switch (se.type) {
|
|
200
202
|
case 'thinking_start':
|
package/dist/config/load.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* legitimately long single tool run can never be watchdog-killed.
|
|
7
7
|
*/
|
|
8
8
|
import process from 'node:process';
|
|
9
|
-
import { DEFAULT_CONFIG, IDLE_TIMEOUT_MARGIN_MS, clampAskRounds, clampDensity, clampHumanTimeout, clampCompactionConfig, clampLogConfig, clampFastConfig, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampBashConfig, clampUpdateConfig, clampTheme, clampThinkingLevel, clampContextWindow, clampMaxTokens, clampMaxRenderInterval, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAutoToken, isSkillsToolPolicyMode, parseThresholdInput, } from './schema.js';
|
|
9
|
+
import { DEFAULT_CONFIG, IDLE_TIMEOUT_MARGIN_MS, clampAskRounds, clampDensity, clampHumanTimeout, clampCompactionConfig, clampLogConfig, clampFastConfig, clampRetryConfig, clampSkillsConfig, clampTeamConfig, clampTodoConfig, clampBashConfig, clampUpdateConfig, clampTheme, clampThinkingLevel, clampContextWindow, clampMaxTokens, clampMaxRenderInterval, clampScrollResumeMs, clampTranscriptRetain, clampTranscriptWindow, coercePositiveInt, isAutoToken, isSkillsToolPolicyMode, legacyTeamCeilingPatch, parseThresholdInput, } from './schema.js';
|
|
10
10
|
import { loadDotenv, readEnvConfig, readEnvDisabledSkills } from './env.js';
|
|
11
|
-
import { readConfigFile } from './store.js';
|
|
12
|
-
import { projectModelProfiles, resolveModelProfileState, resolveModelRoleKey } from './model-profile-resolution.js';
|
|
11
|
+
import { readConfigFile, updatePersistedConfig } from './store.js';
|
|
12
|
+
import { projectModelProfiles, resolveModelProfileState, resolveModelRoleKey, } from './model-profile-resolution.js';
|
|
13
13
|
import { getConfigPath } from './app-paths.js';
|
|
14
14
|
import { setHistoryEnabled } from './prompt-history.js';
|
|
15
15
|
import { setEntryRetain } from '../agent/entry-limits.js';
|
|
@@ -439,6 +439,44 @@ function resolveContextWindow(env, file) {
|
|
|
439
439
|
}
|
|
440
440
|
return DEFAULT_CONFIG.contextWindow;
|
|
441
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* Rewrite the cli<=0.6.16 persisted team ceiling defaults to 0 and persist the
|
|
444
|
+
* change (F-8 A'4). ONE-SHOT and IDEMPOTENT: after the first rewrite the file
|
|
445
|
+
* carries 0, which matches nothing, so nothing is written again.
|
|
446
|
+
*
|
|
447
|
+
* THE FILE LAYER ONLY, deliberately: env and flags are this-launch intent and
|
|
448
|
+
* survive untouched, and with the D-4 semantics a positive surviving value is
|
|
449
|
+
* a supervisor cadence hint, never a kill. The raw file object is patched IN
|
|
450
|
+
* PLACE (the same precedent `readConfigFile` sets with `fullscreen`) so the
|
|
451
|
+
* `file` layer this function's caller resolves from already reads 0; the
|
|
452
|
+
* write goes through `updatePersistedConfig`, the one atomic write path every
|
|
453
|
+
* other settings change uses.
|
|
454
|
+
*
|
|
455
|
+
* A FAILED WRITE MUST NOT BLOCK STARTUP: the values are already neutralized
|
|
456
|
+
* in memory, and the next launch retries — which is why the catch is empty
|
|
457
|
+
* and the migration is logged instead of surfaced.
|
|
458
|
+
*/
|
|
459
|
+
function migrateLegacyTeamCeilings(raw) {
|
|
460
|
+
const patch = legacyTeamCeilingPatch(raw?.team);
|
|
461
|
+
if (patch === null)
|
|
462
|
+
return;
|
|
463
|
+
// THE FULL SECTION, not the bare patch: the write port types `team` as a
|
|
464
|
+
// whole `TeamConfig` (its merge then deep-merges over what is on disk), and
|
|
465
|
+
// the section the file already carries plus the zeroed keys IS the section
|
|
466
|
+
// we want on disk.
|
|
467
|
+
const migratedTeam = { ...raw.team, ...patch };
|
|
468
|
+
Object.assign(raw.team, patch);
|
|
469
|
+
try {
|
|
470
|
+
updatePersistedConfig({ team: migratedTeam });
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
// Unwritable config dir: in-memory values are already migrated; retry
|
|
474
|
+
// next launch (idempotent).
|
|
475
|
+
}
|
|
476
|
+
getLogger().warn('config', 'team_legacy_ceilings_migrated', {
|
|
477
|
+
keys: Object.keys(patch).join(','),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
442
480
|
/**
|
|
443
481
|
* Resolve the effective config by merging all layers, then derive the runtime
|
|
444
482
|
* (non-persisted) fields and enforce the timeout invariant.
|
|
@@ -448,6 +486,7 @@ export function loadConfig(flags = {}, options = {}) {
|
|
|
448
486
|
// `.env` must be loaded before reading env overrides.
|
|
449
487
|
loadDotenv(cwd);
|
|
450
488
|
const read = readConfigFile();
|
|
489
|
+
migrateLegacyTeamCeilings(read.config);
|
|
451
490
|
const projection = projectModelProfiles(read.config ?? {});
|
|
452
491
|
const file = projection.file;
|
|
453
492
|
const env = readEnvConfig();
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -380,6 +380,31 @@ export declare const DEFAULT_TEAM_CONFIG: TeamConfig;
|
|
|
380
380
|
* the two disagree would make the panel's `n/m` readout confusing for no gain.
|
|
381
381
|
*/
|
|
382
382
|
export declare function clampTeamConfig(raw: unknown): TeamConfig;
|
|
383
|
+
/**
|
|
384
|
+
* The team ceilings cli <= 0.6.16 (7a6bf5630) persisted as its OWN defaults:
|
|
385
|
+
* every user who touched team mode back then still carries this exact triple
|
|
386
|
+
* in config.json, because `resolveTeamConfig` merges file values verbatim and
|
|
387
|
+
* no migration ever rewrote them. They are the kill ceilings behind F-8's
|
|
388
|
+
* "first block all failed" dispatches, so loading rewrites them to 0 (= no
|
|
389
|
+
* ceiling, the shipped default since 2ff15a9c3).
|
|
390
|
+
*
|
|
391
|
+
* EXACT-EQUALITY ONLY: a value that differs was a deliberate user choice and
|
|
392
|
+
* survives untouched. The price of that test is that a user who hand-types
|
|
393
|
+
* 300000 today is migrated to 0 too - the accepted trade, and the safe
|
|
394
|
+
* direction (no ceiling, not a cliff).
|
|
395
|
+
*/
|
|
396
|
+
export declare const LEGACY_TEAM_CEILING_DEFAULTS: Readonly<{
|
|
397
|
+
subagentTimeoutMs: number;
|
|
398
|
+
dispatchTimeoutMs: number;
|
|
399
|
+
maxTurnsPerSubagent: number;
|
|
400
|
+
}>;
|
|
401
|
+
/**
|
|
402
|
+
* The `{ key: 0 }` patch for legacy-default-equal ceilings, or `null` when
|
|
403
|
+
* nothing matches. Pure on purpose: `loadConfig` decides WHERE the patch is
|
|
404
|
+
* applied and persisted; this function only answers WHICH keys match, so the
|
|
405
|
+
* equality test lives beside the constants it reads.
|
|
406
|
+
*/
|
|
407
|
+
export declare function legacyTeamCeilingPatch(raw: unknown): Partial<TeamConfig> | null;
|
|
383
408
|
export interface TodoConfig {
|
|
384
409
|
/** Register `todo_write` at all. Default TRUE. */
|
|
385
410
|
enabled: boolean;
|
package/dist/config/schema.js
CHANGED
|
@@ -492,6 +492,44 @@ export function clampTeamConfig(raw) {
|
|
|
492
492
|
overseerIntervalMs: clampIntAllowingZero(src.overseerIntervalMs, DEFAULT_TEAM_CONFIG.overseerIntervalMs, OVERSEER_INTERVAL_RANGE),
|
|
493
493
|
};
|
|
494
494
|
}
|
|
495
|
+
/**
|
|
496
|
+
* The team ceilings cli <= 0.6.16 (7a6bf5630) persisted as its OWN defaults:
|
|
497
|
+
* every user who touched team mode back then still carries this exact triple
|
|
498
|
+
* in config.json, because `resolveTeamConfig` merges file values verbatim and
|
|
499
|
+
* no migration ever rewrote them. They are the kill ceilings behind F-8's
|
|
500
|
+
* "first block all failed" dispatches, so loading rewrites them to 0 (= no
|
|
501
|
+
* ceiling, the shipped default since 2ff15a9c3).
|
|
502
|
+
*
|
|
503
|
+
* EXACT-EQUALITY ONLY: a value that differs was a deliberate user choice and
|
|
504
|
+
* survives untouched. The price of that test is that a user who hand-types
|
|
505
|
+
* 300000 today is migrated to 0 too - the accepted trade, and the safe
|
|
506
|
+
* direction (no ceiling, not a cliff).
|
|
507
|
+
*/
|
|
508
|
+
export const LEGACY_TEAM_CEILING_DEFAULTS = {
|
|
509
|
+
subagentTimeoutMs: 300_000,
|
|
510
|
+
dispatchTimeoutMs: 900_000,
|
|
511
|
+
maxTurnsPerSubagent: 24,
|
|
512
|
+
};
|
|
513
|
+
/**
|
|
514
|
+
* The `{ key: 0 }` patch for legacy-default-equal ceilings, or `null` when
|
|
515
|
+
* nothing matches. Pure on purpose: `loadConfig` decides WHERE the patch is
|
|
516
|
+
* applied and persisted; this function only answers WHICH keys match, so the
|
|
517
|
+
* equality test lives beside the constants it reads.
|
|
518
|
+
*/
|
|
519
|
+
export function legacyTeamCeilingPatch(raw) {
|
|
520
|
+
if (!raw || typeof raw !== 'object')
|
|
521
|
+
return null;
|
|
522
|
+
const src = raw;
|
|
523
|
+
const patch = {};
|
|
524
|
+
let matched = false;
|
|
525
|
+
for (const key of Object.keys(LEGACY_TEAM_CEILING_DEFAULTS)) {
|
|
526
|
+
if (src[key] === LEGACY_TEAM_CEILING_DEFAULTS[key]) {
|
|
527
|
+
patch[key] = 0;
|
|
528
|
+
matched = true;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return matched ? patch : null;
|
|
532
|
+
}
|
|
495
533
|
export const DEFAULT_TODO_CONFIG = {
|
|
496
534
|
enabled: true,
|
|
497
535
|
panel: true,
|
package/dist/team/overseer.d.ts
CHANGED
|
@@ -49,8 +49,10 @@ import type { OverseerAction, OverseerDecision, SubagentRun, SubagentSpec } from
|
|
|
49
49
|
* Bumped whenever either supervisor protocol prompt changes (D-9), so a
|
|
50
50
|
* behaviour report can be tied to a prompt revision with one grep.
|
|
51
51
|
* `TEAM_BLOCK_VERSION` covers only the LEAD's `<team>` block, not these.
|
|
52
|
+
*
|
|
53
|
+
* v3 adds the turn-cap trigger to the single-inspection prompt (F-8 A'2).
|
|
52
54
|
*/
|
|
53
|
-
export declare const OVERSEER_PROMPT_VERSION = "
|
|
55
|
+
export declare const OVERSEER_PROMPT_VERSION = "v3-2026-10";
|
|
54
56
|
/** One remembered decision, so the second look knows the first happened. */
|
|
55
57
|
export interface OverseerMemoryEntry {
|
|
56
58
|
/** Seconds into the dispatch. */
|
|
@@ -63,7 +65,7 @@ export interface ChildDigestInput {
|
|
|
63
65
|
run: SubagentRun;
|
|
64
66
|
/** False only in the post-mortem inspection, after the child settled. */
|
|
65
67
|
childAlive: boolean;
|
|
66
|
-
trigger: 'silence' | 'clock' | 'postmortem';
|
|
68
|
+
trigger: 'silence' | 'clock' | 'turncap' | 'postmortem';
|
|
67
69
|
/** The child's LIVE history (`SubagentAgentLike.state.messages`). */
|
|
68
70
|
messages: readonly Message[];
|
|
69
71
|
memory: readonly OverseerMemoryEntry[];
|
|
@@ -203,7 +205,7 @@ export declare function createFastOverseerProvider(deps: FastOverseerDeps): Over
|
|
|
203
205
|
export interface OverseerInspectRequest {
|
|
204
206
|
run: SubagentRun;
|
|
205
207
|
messages: readonly Message[];
|
|
206
|
-
trigger: 'silence' | 'clock' | 'postmortem';
|
|
208
|
+
trigger: 'silence' | 'clock' | 'turncap' | 'postmortem';
|
|
207
209
|
childAlive: boolean;
|
|
208
210
|
}
|
|
209
211
|
export interface TeamOverseerOptions {
|
package/dist/team/overseer.js
CHANGED
|
@@ -50,8 +50,10 @@ import { TEAM_LIMITS } from './limits.js';
|
|
|
50
50
|
* Bumped whenever either supervisor protocol prompt changes (D-9), so a
|
|
51
51
|
* behaviour report can be tied to a prompt revision with one grep.
|
|
52
52
|
* `TEAM_BLOCK_VERSION` covers only the LEAD's `<team>` block, not these.
|
|
53
|
+
*
|
|
54
|
+
* v3 adds the turn-cap trigger to the single-inspection prompt (F-8 A'2).
|
|
53
55
|
*/
|
|
54
|
-
export const OVERSEER_PROMPT_VERSION = '
|
|
56
|
+
export const OVERSEER_PROMPT_VERSION = 'v3-2026-10';
|
|
55
57
|
/** How many trailing history messages the digest carries. */
|
|
56
58
|
const DIGEST_TAIL_MESSAGES = 8;
|
|
57
59
|
/** Per-message text clamp inside the digest. */
|
|
@@ -78,6 +80,13 @@ function messageText(message) {
|
|
|
78
80
|
function clampChars(text, max) {
|
|
79
81
|
return text.length <= max ? text : `${text.slice(0, Math.max(0, max - 3))}...`;
|
|
80
82
|
}
|
|
83
|
+
/** One line per trigger, as the digest names it (a union VALUE, never prose). */
|
|
84
|
+
const TRIGGER_LABELS = {
|
|
85
|
+
silence: 'event silence (suspected stall)',
|
|
86
|
+
clock: 'wall-clock check',
|
|
87
|
+
turncap: 'turn cap reached (inspection trigger, not a stop)',
|
|
88
|
+
postmortem: 'post-mortem (child already ended)',
|
|
89
|
+
};
|
|
81
90
|
/**
|
|
82
91
|
* Build the bounded text one inspection reads.
|
|
83
92
|
*
|
|
@@ -91,7 +100,7 @@ export function buildChildDigest(input) {
|
|
|
91
100
|
const { run, childAlive, trigger, messages, memory, elapsedSec } = input;
|
|
92
101
|
const lines = [];
|
|
93
102
|
lines.push(`child: ${run.label} "${run.description}"`);
|
|
94
|
-
lines.push(`trigger: ${trigger
|
|
103
|
+
lines.push(`trigger: ${TRIGGER_LABELS[trigger]}`);
|
|
95
104
|
lines.push(`elapsed: ${Math.round(elapsedSec)}s, alive: ${childAlive ? 'yes' : 'no'}`);
|
|
96
105
|
lines.push(`phase: ${run.phase}, turns: ${run.turns}, toolCalls: ${run.toolCalls}` +
|
|
97
106
|
(run.lastTool ? `, lastTool: ${run.lastTool}` : ''));
|
|
@@ -270,8 +279,10 @@ export function parseOverseerBatchText(text) {
|
|
|
270
279
|
}
|
|
271
280
|
const OVERSEER_SYSTEM_PROMPT = [
|
|
272
281
|
'You are the supervisor of a team of subagents. You are shown ONE subagent',
|
|
273
|
-
'that crossed a
|
|
274
|
-
'decide what to do with it.
|
|
282
|
+
'that crossed a supervision trigger (event silence, a wall-clock check, or',
|
|
283
|
+
'the configured turn cap) and must decide what to do with it. Crossing a',
|
|
284
|
+
'ceiling does NOT stop the child - your decision does. Answer with ONE JSON',
|
|
285
|
+
'object and nothing else:',
|
|
275
286
|
'{"action": "wait" | "nudge" | "replace" | "abandon", "reason": "<= 200 chars",',
|
|
276
287
|
' "guidance": "<= 1200 chars, nudge only", "nextCheckMs": <ms, wait only>}.',
|
|
277
288
|
'wait: the child looks healthy or is legitimately blocked (API backoff,',
|
package/dist/team/runtime.js
CHANGED
|
@@ -488,6 +488,18 @@ export class TeamRuntime {
|
|
|
488
488
|
// ONE KICK PER REAL AGENT EVENT, unthrottled (I-OV3): the silence
|
|
489
489
|
// window resets on evidence, not on a coalesced UI frame.
|
|
490
490
|
onActivity: () => this.supervision?.kick(spec.label),
|
|
491
|
+
// A'2: under supervision the turn cap is ONE inspection, fired from the
|
|
492
|
+
// child's own `turn_end` at the crossing turn. Async-and-forgotten
|
|
493
|
+
// exactly like the silence timer's `onFire`: nothing in the child's
|
|
494
|
+
// event path may block or throw. ABSENT in the legacy regime, which
|
|
495
|
+
// keeps the hard stop - the hook's presence IS the gate.
|
|
496
|
+
...(this.overseer !== null
|
|
497
|
+
? {
|
|
498
|
+
onTurnCapReached: (label) => {
|
|
499
|
+
void this.inspectAndApply(dispatchId, label, 'turncap');
|
|
500
|
+
},
|
|
501
|
+
}
|
|
502
|
+
: {}),
|
|
491
503
|
});
|
|
492
504
|
// ASSIGNMENT, not a declaration: a `const` here would shadow the `let` above
|
|
493
505
|
// the bus, `canSend` would see the empty array forever, and every peer would
|
|
@@ -520,10 +532,15 @@ export class TeamRuntime {
|
|
|
520
532
|
// its slowest child and only an Esc ends it early. The lead's idle
|
|
521
533
|
// watchdog is paused for the whole dispatch either way, exactly as it is
|
|
522
534
|
// for the lead's own long turns.
|
|
535
|
+
//
|
|
536
|
+
// D-4, NOW FOR THE WHOLE DISPATCH TOO: with the supervisor gate open the
|
|
537
|
+
// ceiling NEVER arms a hard abort - the cadence ladder is what looks at a
|
|
538
|
+
// long dispatch, exactly as it already looks at a long child. The legacy
|
|
539
|
+
// hard `abortAll()` exists ONLY under `team.overseer: false`, the same
|
|
540
|
+
// regime line `runCtx.subagentTimeoutMs` below draws.
|
|
541
|
+
const dispatchCeilingMs = team.overseer ? 0 : team.dispatchTimeoutMs;
|
|
523
542
|
this.dispatchTimer =
|
|
524
|
-
|
|
525
|
-
? setTimeout(() => this.abortAll(), team.dispatchTimeoutMs)
|
|
526
|
-
: null;
|
|
543
|
+
dispatchCeilingMs > 0 ? setTimeout(() => this.abortAll(), dispatchCeilingMs) : null;
|
|
527
544
|
const runCtx = {
|
|
528
545
|
dispatchId,
|
|
529
546
|
handles,
|
package/dist/team/subagent.d.ts
CHANGED
|
@@ -135,6 +135,16 @@ export interface SubagentHooks {
|
|
|
135
135
|
* so the phase read at fire time is current.
|
|
136
136
|
*/
|
|
137
137
|
onActivity?: () => void;
|
|
138
|
+
/**
|
|
139
|
+
* THE TURN CAP AS AN INSPECTION TRIGGER (F-8 A'2). Called ONCE, at the turn
|
|
140
|
+
* that crossed `maxTurnsPerSubagent`, ONLY when the dispatch supervisor is
|
|
141
|
+
* live - the runtime wires this hook under the same gate as
|
|
142
|
+
* `overseerActive`, so its presence IS the supervised/legacy branch. The
|
|
143
|
+
* child is NOT stopped: the supervisor decides wait / nudge / replace /
|
|
144
|
+
* abandon through the same `inspectAndApply` path every other trigger
|
|
145
|
+
* uses, and a child it lets run carries no `truncated` flag.
|
|
146
|
+
*/
|
|
147
|
+
onTurnCapReached?: (label: string) => void;
|
|
138
148
|
}
|
|
139
149
|
export interface SubagentHandle {
|
|
140
150
|
spec: SubagentSpec;
|
package/dist/team/subagent.js
CHANGED
|
@@ -359,12 +359,26 @@ export function createSubagent(spec, deps, hooks) {
|
|
|
359
359
|
// not runaway - it is working.
|
|
360
360
|
const turnCap = deps.config.team.maxTurnsPerSubagent;
|
|
361
361
|
if (turnCap > 0 && run.turns >= turnCap) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
362
|
+
if (hooks.onTurnCapReached !== undefined) {
|
|
363
|
+
// A'2, D-4 for the turn cap: SUPERVISED means the ceiling is
|
|
364
|
+
// an inspection trigger, not a death sentence. ONE look at the
|
|
365
|
+
// crossing turn (`===`), never a per-turn storm; the child
|
|
366
|
+
// keeps working unless the supervisor decides replace or
|
|
367
|
+
// abandon, and a child that keeps running carries NO
|
|
368
|
+
// `truncated` flag - the report must not call it partial.
|
|
369
|
+
if (run.turns === turnCap)
|
|
370
|
+
hooks.onTurnCapReached(spec.label);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
// LEGACY regime (no supervisor for this dispatch): the hard
|
|
374
|
+
// stop, byte for byte what it always was.
|
|
375
|
+
run.truncated = true;
|
|
376
|
+
// THE FLAG FIRST, exactly like `handle.abort()`: the child's
|
|
377
|
+
// reviewer reads `abortRequested` as its guard-1 fact, and a
|
|
378
|
+
// turn-cap stop must be visible to it in the same tick.
|
|
379
|
+
abortRequested = true;
|
|
380
|
+
agent.abort();
|
|
381
|
+
}
|
|
368
382
|
}
|
|
369
383
|
break;
|
|
370
384
|
}
|
package/dist/team/types.d.ts
CHANGED
|
@@ -201,13 +201,14 @@ export interface OverseerIntervention {
|
|
|
201
201
|
action: OverseerAction;
|
|
202
202
|
reason: string;
|
|
203
203
|
/**
|
|
204
|
-
* What the trigger was: event stall, wall-clock check,
|
|
205
|
-
* the supervisor
|
|
204
|
+
* What the trigger was: event stall, wall-clock check, the child's turn
|
|
205
|
+
* cap (F-8 A'2 - an inspection the supervisor then ruled on), post-mortem,
|
|
206
|
+
* or the supervisor going QUIET (subagent-overseer-v2 D-10) - the look
|
|
206
207
|
* budget ran out and supervision stopped waking this child. A VALUE OF
|
|
207
208
|
* THE UNION, never a reason prefix: a human-readable sentence is not a
|
|
208
209
|
* machine contract (the `run.error` rule).
|
|
209
210
|
*/
|
|
210
|
-
trigger: 'silence' | 'clock' | 'postmortem' | 'quiet';
|
|
211
|
+
trigger: 'silence' | 'clock' | 'turncap' | 'postmortem' | 'quiet';
|
|
211
212
|
}
|
|
212
213
|
/**
|
|
213
214
|
* A terminal supervisor decision handed to the WORKER LOOP, which is the only
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aragon-agent/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.23",
|
|
4
4
|
"description": "AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AragonAgent contributors",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"prepublishOnly": "npm run build"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@aragon-agent/core": "^0.2.
|
|
59
|
+
"@aragon-agent/core": "^0.2.35",
|
|
60
60
|
"chalk": "^5.6.2",
|
|
61
61
|
"cli-highlight": "^2.0.0",
|
|
62
62
|
"commander": "^12.1.0",
|