@aragon-agent/cli 0.6.15 → 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.
- package/README.md +108 -71
- package/dist/agent/controller.d.ts +10 -0
- package/dist/agent/controller.js +52 -47
- package/dist/agent/reducer.js +0 -4
- package/dist/cli.js +49 -15
- package/dist/commands/builtins.js +27 -43
- package/dist/compaction/child.d.ts +10 -8
- package/dist/compaction/child.js +23 -25
- package/dist/compaction/context-command.js +14 -3
- package/dist/compaction/limits.d.ts +0 -2
- package/dist/compaction/limits.js +0 -2
- package/dist/compaction/types.d.ts +6 -1
- package/dist/config/app-paths.d.ts +10 -0
- package/dist/config/app-paths.js +12 -0
- package/dist/config/cli-commands.d.ts +1 -1
- package/dist/config/cli-commands.js +16 -15
- package/dist/config/env.d.ts +2 -1
- package/dist/config/env.js +13 -1
- package/dist/config/load.d.ts +9 -17
- package/dist/config/load.js +14 -6
- package/dist/config/model-windows.d.ts +91 -0
- package/dist/config/model-windows.js +181 -0
- package/dist/config/schema.d.ts +34 -5
- package/dist/config/schema.js +15 -11
- package/dist/exec/events.d.ts +12 -8
- package/dist/exec/events.js +2 -2
- package/dist/exec/runner.js +4 -1
- package/dist/fast/limits.d.ts +2 -2
- package/dist/fast/limits.js +2 -2
- package/dist/fast/prompt.d.ts +10 -11
- package/dist/fast/prompt.js +19 -20
- package/dist/fast/reviewer.d.ts +1 -1
- package/dist/fast/reviewer.js +1 -1
- package/dist/fast/types.d.ts +0 -2
- package/dist/fast/wiring.d.ts +18 -34
- package/dist/fast/wiring.js +20 -51
- package/dist/input/csiu-keys.d.ts +66 -0
- package/dist/input/csiu-keys.js +170 -0
- package/dist/input/keyboard-enhancement.d.ts +42 -0
- package/dist/input/keyboard-enhancement.js +65 -0
- package/dist/input/stdin-filter.d.ts +9 -0
- package/dist/input/stdin-filter.js +45 -9
- package/dist/input/win32-input-mode.d.ts +61 -0
- package/dist/input/win32-input-mode.js +251 -0
- package/dist/session/validate-session.js +3 -1
- package/dist/team/child-reviewer.d.ts +105 -0
- package/dist/team/child-reviewer.js +117 -0
- package/dist/team/normalize.d.ts +7 -26
- package/dist/team/normalize.js +9 -22
- package/dist/team/prompt.d.ts +0 -11
- package/dist/team/prompt.js +3 -12
- package/dist/team/report.d.ts +4 -19
- package/dist/team/report.js +1 -27
- package/dist/team/runtime.d.ts +5 -10
- package/dist/team/runtime.js +35 -39
- package/dist/team/subagent.d.ts +18 -18
- package/dist/team/subagent.js +80 -30
- package/dist/team/task-tool.d.ts +6 -26
- package/dist/team/task-tool.js +13 -47
- package/dist/team/types.d.ts +3 -42
- package/dist/ui/App.js +3 -4
- package/dist/ui/BottomStatusRow.js +2 -1
- package/dist/ui/StatusBar.js +2 -1
- package/dist/ui/TeamPanel.js +1 -1
- package/dist/ui/entries/TeamCard.js +1 -6
- package/dist/ui/layout/status-detail-layout.js +12 -2
- package/dist/ui/layout/status-layout.d.ts +4 -2
- package/dist/ui/layout/status-layout.js +24 -1
- package/dist/ui/overlays/HelpOverlay.js +1 -2
- package/package.json +2 -2
- package/dist/ui/ModeChip.d.ts +0 -18
- package/dist/ui/ModeChip.js +0 -24
package/dist/team/normalize.d.ts
CHANGED
|
@@ -17,32 +17,18 @@
|
|
|
17
17
|
* whether an optional install step succeeded.
|
|
18
18
|
*
|
|
19
19
|
* NEVER THROWS. Zero survivors is the caller's single hard failure.
|
|
20
|
+
*
|
|
21
|
+
* EVERY CHILD RUNS THE LEAD'S MODEL (main-agent parity): the former
|
|
22
|
+
* `model: "fast"` per-child tier was removed, so a spec that still carries a
|
|
23
|
+
* `model` key simply ignores it - there is no tier to downgrade TO, and the
|
|
24
|
+
* honest reading of a stale key is silence, not a report line about a
|
|
25
|
+
* capability that no longer exists.
|
|
20
26
|
*/
|
|
21
27
|
import type { SubagentSpec } from './types.js';
|
|
22
28
|
export interface NormalizedSpecs {
|
|
23
29
|
specs: SubagentSpec[];
|
|
24
30
|
/** How many entries the model asked for, before dropping and capping. */
|
|
25
31
|
requested: number;
|
|
26
|
-
/**
|
|
27
|
-
* Specs that asked for `model:"fast"` and were downgraded to `main`
|
|
28
|
-
* (fast-model-tier §3.4 / R-6).
|
|
29
|
-
*
|
|
30
|
-
* ADDITIVE: `NormalizedSpecs` GAINS a field, it does not change one, so every
|
|
31
|
-
* existing caller and test compiles unchanged.
|
|
32
|
-
*/
|
|
33
|
-
downgraded: number;
|
|
34
|
-
}
|
|
35
|
-
/** Options for the fast tier. Optional for the reason `normalizeSubagentSpecs`
|
|
36
|
-
* documents below (RV-14). */
|
|
37
|
-
export interface NormalizeOptions {
|
|
38
|
-
/**
|
|
39
|
-
* §3.3's LIVE predicate, evaluated at DISPATCH time (RV-3).
|
|
40
|
-
*
|
|
41
|
-
* `fast.delegate: false` and "the tier stopped resolving twenty minutes ago"
|
|
42
|
-
* are the same observable to the model — a downgrade, counted and reported —
|
|
43
|
-
* which is the honest reading of both.
|
|
44
|
-
*/
|
|
45
|
-
fastAvailable?: boolean;
|
|
46
32
|
}
|
|
47
33
|
/**
|
|
48
34
|
* Repair and bound a raw `subagents` array.
|
|
@@ -55,10 +41,5 @@ export interface NormalizeOptions {
|
|
|
55
41
|
* @param max The resolved `team.maxSubagents`; further clamped to
|
|
56
42
|
* `TEAM_LIMITS.hardMaxSubagents` here so a hand-edited config file
|
|
57
43
|
* cannot raise the requirement's ceiling (R-g).
|
|
58
|
-
* @param opts Fast-tier availability. OPTIONAL, DEFAULTING TO `false` (RV-14):
|
|
59
|
-
* this function and `NormalizedSpecs` are both exported and both
|
|
60
|
-
* have existing callers and tests, so a required third parameter
|
|
61
|
-
* would be a breaking edit that buys nothing — and an omitted one
|
|
62
|
-
* now means exactly what a pre-feature build meant.
|
|
63
44
|
*/
|
|
64
|
-
export declare function normalizeSubagentSpecs(raw: unknown, max: number
|
|
45
|
+
export declare function normalizeSubagentSpecs(raw: unknown, max: number): NormalizedSpecs;
|
package/dist/team/normalize.js
CHANGED
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
* whether an optional install step succeeded.
|
|
18
18
|
*
|
|
19
19
|
* NEVER THROWS. Zero survivors is the caller's single hard failure.
|
|
20
|
+
*
|
|
21
|
+
* EVERY CHILD RUNS THE LEAD'S MODEL (main-agent parity): the former
|
|
22
|
+
* `model: "fast"` per-child tier was removed, so a spec that still carries a
|
|
23
|
+
* `model` key simply ignores it - there is no tier to downgrade TO, and the
|
|
24
|
+
* honest reading of a stale key is silence, not a report line about a
|
|
25
|
+
* capability that no longer exists.
|
|
20
26
|
*/
|
|
21
27
|
import { TEAM_LIMITS } from './limits.js';
|
|
22
28
|
function asString(value) {
|
|
@@ -76,21 +82,13 @@ function dedupeLabel(label, taken) {
|
|
|
76
82
|
* @param max The resolved `team.maxSubagents`; further clamped to
|
|
77
83
|
* `TEAM_LIMITS.hardMaxSubagents` here so a hand-edited config file
|
|
78
84
|
* cannot raise the requirement's ceiling (R-g).
|
|
79
|
-
* @param opts Fast-tier availability. OPTIONAL, DEFAULTING TO `false` (RV-14):
|
|
80
|
-
* this function and `NormalizedSpecs` are both exported and both
|
|
81
|
-
* have existing callers and tests, so a required third parameter
|
|
82
|
-
* would be a breaking edit that buys nothing — and an omitted one
|
|
83
|
-
* now means exactly what a pre-feature build meant.
|
|
84
85
|
*/
|
|
85
|
-
export function normalizeSubagentSpecs(raw, max
|
|
86
|
+
export function normalizeSubagentSpecs(raw, max) {
|
|
86
87
|
if (!Array.isArray(raw))
|
|
87
|
-
return { specs: [], requested: 0
|
|
88
|
+
return { specs: [], requested: 0 };
|
|
88
89
|
const requested = raw.length;
|
|
89
|
-
const fastAvailable = opts.fastAvailable === true;
|
|
90
90
|
const kept = [];
|
|
91
91
|
const taken = new Set();
|
|
92
|
-
/** Parallel to `kept`: did entry `i` ask for the fast tier? */
|
|
93
|
-
const wantedFast = [];
|
|
94
92
|
for (let i = 0; i < raw.length; i += 1) {
|
|
95
93
|
const item = raw[i];
|
|
96
94
|
if (!item || typeof item !== 'object')
|
|
@@ -102,25 +100,14 @@ export function normalizeSubagentSpecs(raw, max, opts = {}) {
|
|
|
102
100
|
const prompt = clamp(asString(src.prompt), TEAM_LIMITS.promptChars);
|
|
103
101
|
if (prompt.length === 0)
|
|
104
102
|
continue;
|
|
105
|
-
// REPAIR, NEVER REJECT — this file's own contract. `'fast'` without
|
|
106
|
-
// availability becomes `'main'` AND IS COUNTED; anything else (a typo, a
|
|
107
|
-
// model id, `undefined`) is simply `'main'` and is NOT counted, because the
|
|
108
|
-
// model did not ask for the fast tier and has nothing to be told about.
|
|
109
|
-
const wantsFast = src.model === 'fast';
|
|
110
|
-
wantedFast.push(wantsFast);
|
|
111
103
|
kept.push({
|
|
112
104
|
label: dedupeLabel(slugLabel(src.label, kept.length), taken),
|
|
113
105
|
description,
|
|
114
106
|
prompt,
|
|
115
107
|
readOnly: src.readOnly === true,
|
|
116
|
-
tier: wantsFast && fastAvailable ? 'fast' : 'main',
|
|
117
108
|
});
|
|
118
109
|
}
|
|
119
110
|
const ceiling = Math.max(1, Math.min(Math.floor(max) || 1, TEAM_LIMITS.hardMaxSubagents));
|
|
120
111
|
const specs = kept.slice(0, ceiling);
|
|
121
|
-
|
|
122
|
-
// cap dropped never ran at all, so reporting it as "ran on the main model"
|
|
123
|
-
// would be a second, different lie in a line that exists to stop the first.
|
|
124
|
-
const downgraded = specs.filter((spec, i) => spec.tier === 'main' && wantedFast[i] === true).length;
|
|
125
|
-
return { specs, requested, downgraded };
|
|
112
|
+
return { specs, requested };
|
|
126
113
|
}
|
package/dist/team/prompt.d.ts
CHANGED
|
@@ -18,17 +18,6 @@ export { TEAM_BLOCK_VERSION };
|
|
|
18
18
|
export interface TeamBlockParams {
|
|
19
19
|
maxSubagents: number;
|
|
20
20
|
maxConcurrent: number;
|
|
21
|
-
/**
|
|
22
|
-
* Whether `model:"fast"` is honoured on `task` right now (fast-model-tier
|
|
23
|
-
* §3.8).
|
|
24
|
-
*
|
|
25
|
-
* ONE SENTENCE, AND ONLY WHEN IT IS TRUE. `<fast_tier>` carries the full
|
|
26
|
-
* guidance; this is the cross-reference that stops a model reading
|
|
27
|
-
* `<team_mode>` in isolation and concluding every child costs the same. With
|
|
28
|
-
* it absent the block is BYTE-IDENTICAL to the pre-feature output, which is
|
|
29
|
-
* what `--no-fast` and every existing snapshot depend on.
|
|
30
|
-
*/
|
|
31
|
-
fastDelegation?: boolean;
|
|
32
21
|
}
|
|
33
22
|
/**
|
|
34
23
|
* The lead's `<team_mode>` block. Under 1200 characters on purpose: it is paid
|
package/dist/team/prompt.js
CHANGED
|
@@ -44,19 +44,10 @@ export function buildTeamBlock(params) {
|
|
|
44
44
|
'',
|
|
45
45
|
'Each subagent starts with no memory of this conversation, so its prompt must',
|
|
46
46
|
'carry everything it needs: the goal, the files or areas it owns, and what to',
|
|
47
|
-
'report back. Give each one a disjoint set of files to write.
|
|
48
|
-
'
|
|
47
|
+
'report back. Give each one a disjoint set of files to write. Every subagent',
|
|
48
|
+
'runs on your own model. Subagents cannot dispatch further subagents and',
|
|
49
|
+
'cannot ask the user anything.',
|
|
49
50
|
'',
|
|
50
|
-
// THE TRAILING BLANK IS INSIDE THE CONDITIONAL, not outside it. An
|
|
51
|
-
// unconditional `''` here would add a line to EVERY team-enabled prompt and
|
|
52
|
-
// silently break the byte-identity `--no-fast` depends on (I-2).
|
|
53
|
-
...(params.fastDelegation
|
|
54
|
-
? [
|
|
55
|
-
'A subagent doing mechanical, high-volume work can run on the cheaper fast',
|
|
56
|
-
'model - see <fast_tier>.',
|
|
57
|
-
'',
|
|
58
|
-
]
|
|
59
|
-
: []),
|
|
60
51
|
'You get one combined report when they all finish. Read it before deciding what',
|
|
61
52
|
'to do next, and tell the user what the team found in your own words.',
|
|
62
53
|
'</team_mode>',
|
package/dist/team/report.d.ts
CHANGED
|
@@ -23,26 +23,11 @@
|
|
|
23
23
|
import type { ModelCost } from '@aragon-agent/core';
|
|
24
24
|
import type { DispatchOutcome, SubagentRun } from './types.js';
|
|
25
25
|
export interface ReportOptions {
|
|
26
|
-
/** The lead's model cost table, so the header can state real spend (R-5).
|
|
27
|
-
cost?: ModelCost;
|
|
28
|
-
/**
|
|
29
|
-
* The FAST model's cost table (fast-model-tier §3.4 / R-7).
|
|
30
|
-
*
|
|
31
|
-
* A SECOND TABLE, not a second number derived from the first. Summing
|
|
32
|
-
* fast-tier tokens at the lead's price over-reports a Haiku child under a
|
|
33
|
-
* Sonnet lead by roughly an order of magnitude, and the whole justification
|
|
34
|
-
* for delegating to a cheaper model is the figure on this line.
|
|
35
|
-
*/
|
|
36
|
-
fastCost?: ModelCost;
|
|
37
|
-
/**
|
|
38
|
-
* The static price table has never heard of the fast model (C-11 / RV-4).
|
|
26
|
+
/** The lead's model cost table, so the header can state real spend (R-5).
|
|
39
27
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
|
|
43
|
-
* would make the feature look free while it is spending money.
|
|
44
|
-
*/
|
|
45
|
-
fastPricingUnknown?: boolean;
|
|
28
|
+
* THE ONE TABLE, because every child runs the lead's own model
|
|
29
|
+
* (main-agent parity): one tier, one price, one honest total. */
|
|
30
|
+
cost?: ModelCost;
|
|
46
31
|
}
|
|
47
32
|
/**
|
|
48
33
|
* Cut `text` to at most `maxBytes` WITHOUT splitting a UTF-8 sequence.
|
package/dist/team/report.js
CHANGED
|
@@ -106,28 +106,6 @@ function buildHeader(outcome, options) {
|
|
|
106
106
|
`(${counts}) in ${formatDuration(durationMs)}.`);
|
|
107
107
|
lines.push(`Tokens: in ${formatTokens(outcome.usage.inputTokens)}, out ` +
|
|
108
108
|
`${formatTokens(outcome.usage.outputTokens)}. Cost: ${formatCost(computeCost(outcome.usage, options.cost))}.`);
|
|
109
|
-
// A SECOND LINE, NOT A COMBINED TOTAL (fast-model-tier §3.4). Two tiers with
|
|
110
|
-
// two price tables have two answers, and folding them into one figure is the
|
|
111
|
-
// misattribution R-7 is about. Present only when a fast child actually ran, so
|
|
112
|
-
// an ordinary dispatch's header is byte-identical.
|
|
113
|
-
const fastUsage = outcome.fastUsage;
|
|
114
|
-
if (fastUsage) {
|
|
115
|
-
// `unknown` rather than a currency amount when the model has no price table
|
|
116
|
-
// (RV-4): a lower bound rendered as a total is the same class of lie as
|
|
117
|
-
// pricing a Haiku child at Sonnet rates.
|
|
118
|
-
const fastCostLabel = options.fastPricingUnknown
|
|
119
|
-
? 'unknown (no price table)'
|
|
120
|
-
: formatCost(computeCost(fastUsage, options.fastCost));
|
|
121
|
-
lines.push(`Fast tier: in ${formatTokens(fastUsage.inputTokens)}, out ` +
|
|
122
|
-
`${formatTokens(fastUsage.outputTokens)}. Cost: ${fastCostLabel}.`);
|
|
123
|
-
}
|
|
124
|
-
// THE LINE THAT KEEPS A DOWNGRADE FROM BEING SILENT (R-6). Without it the
|
|
125
|
-
// model asked for a cheap child, got an expensive one, and has no way to learn
|
|
126
|
-
// that its cost model is wrong.
|
|
127
|
-
if (outcome.downgraded && outcome.downgraded > 0) {
|
|
128
|
-
const n = outcome.downgraded;
|
|
129
|
-
lines.push(`${n} ${n === 1 ? 'subagent' : 'subagents'} ran on the main model (fast tier off).`);
|
|
130
|
-
}
|
|
131
109
|
for (const conflict of findFileConflicts(outcome.runs)) {
|
|
132
110
|
const who = conflict.labels.map((l) => `"${l}"`).join(' and ');
|
|
133
111
|
lines.push(`WARNING: agents ${who} both wrote ${conflict.path} - review before trusting either.`);
|
|
@@ -139,10 +117,6 @@ function buildSection(run) {
|
|
|
139
117
|
// the only place the lead learns that a child's first attempt died before it
|
|
140
118
|
// did anything (F-4).
|
|
141
119
|
const retried = run.retries ? `, retried ${run.retries}x` : '';
|
|
142
|
-
// `[fast]` after the LABEL, on the head line, which is never trimmed. Six
|
|
143
|
-
// bytes, and it is the only place the lead learns which of its children were
|
|
144
|
-
// cheap — the fact the whole second cost line is about.
|
|
145
|
-
const tier = run.tier === 'fast' ? ' [fast]' : '';
|
|
146
120
|
// `compacted N` in the same position `retried Nx` uses, and for the same
|
|
147
121
|
// reason (context-auto-compaction-hardening §3.4.4 / W3): this is the ONLY
|
|
148
122
|
// place the lead learns that a child's own history was summarized mid-task, so
|
|
@@ -150,7 +124,7 @@ function buildSection(run) {
|
|
|
150
124
|
// rather than an unexplained one. Absent when it never happened.
|
|
151
125
|
const compacted = run.compactions ? `, compacted ${run.compactions}` : '';
|
|
152
126
|
const head = [
|
|
153
|
-
`### ${run.label}
|
|
127
|
+
`### ${run.label} "${run.description}" ${statusBadge(run)} ${elapsed(run)}, ` +
|
|
154
128
|
`${run.turns} turns, ${run.toolCalls} tools${retried}${compacted}`,
|
|
155
129
|
];
|
|
156
130
|
if (run.filesTouched.length > 0)
|
package/dist/team/runtime.d.ts
CHANGED
|
@@ -56,11 +56,11 @@ export interface TeamRuntimeDeps {
|
|
|
56
56
|
/** Test seam (§8.1): a stub factory keeps the whole scheduler off the network. */
|
|
57
57
|
agentFactory?: SubagentAgentFactory;
|
|
58
58
|
/**
|
|
59
|
-
* Per-
|
|
60
|
-
* `SubagentDeps
|
|
61
|
-
*
|
|
59
|
+
* Per-child fast review (main-agent parity), passed straight through to
|
|
60
|
+
* `SubagentDeps`. ABSENT means the session has no fast wiring and children
|
|
61
|
+
* are built exactly as they were before the reviewers existed.
|
|
62
62
|
*/
|
|
63
|
-
|
|
63
|
+
review?: SubagentDeps['review'];
|
|
64
64
|
/**
|
|
65
65
|
* Per-child context compaction (context-auto-compaction-hardening §3.4 / W3).
|
|
66
66
|
*
|
|
@@ -104,12 +104,7 @@ export declare class TeamRuntime {
|
|
|
104
104
|
isBusy(): boolean;
|
|
105
105
|
/** The live roster, for `/team` status. `null` when nothing is dispatching. */
|
|
106
106
|
snapshot(): TeamSnapshot | null;
|
|
107
|
-
dispatch(specs: SubagentSpec[], requested: number, signal?: AbortSignal
|
|
108
|
-
/** Carried through from the normalizer so the report can state it (§3.4 /
|
|
109
|
-
* R-6). Optional, so every existing caller compiles unchanged. */
|
|
110
|
-
extra?: {
|
|
111
|
-
downgraded?: number;
|
|
112
|
-
}): Promise<DispatchOutcome>;
|
|
107
|
+
dispatch(specs: SubagentSpec[], requested: number, signal?: AbortSignal): Promise<DispatchOutcome>;
|
|
113
108
|
/** One slot of the pool: take the next index until the list is exhausted. */
|
|
114
109
|
private worker;
|
|
115
110
|
/**
|
package/dist/team/runtime.js
CHANGED
|
@@ -65,10 +65,7 @@ export class TeamRuntime {
|
|
|
65
65
|
// -------------------------------------------------------------------------
|
|
66
66
|
// Dispatch
|
|
67
67
|
// -------------------------------------------------------------------------
|
|
68
|
-
async dispatch(specs, requested, signal
|
|
69
|
-
/** Carried through from the normalizer so the report can state it (§3.4 /
|
|
70
|
-
* R-6). Optional, so every existing caller compiles unchanged. */
|
|
71
|
-
extra = {}) {
|
|
68
|
+
async dispatch(specs, requested, signal) {
|
|
72
69
|
this.counter += 1;
|
|
73
70
|
const dispatchId = `d${this.counter}`;
|
|
74
71
|
const config = this.deps.getConfig();
|
|
@@ -131,7 +128,7 @@ export class TeamRuntime {
|
|
|
131
128
|
confirmTools: config.confirmTools,
|
|
132
129
|
...(this.deps.humanQueue ? { humanQueue: this.deps.humanQueue } : {}),
|
|
133
130
|
agentFactory: this.deps.agentFactory ?? defaultSubagentFactory,
|
|
134
|
-
...(this.deps.
|
|
131
|
+
...(this.deps.review ? { review: this.deps.review } : {}),
|
|
135
132
|
// Site two of two, and the omission is silent in the same way the policy
|
|
136
133
|
// one above is: declaring it on `TeamRuntimeDeps` and forgetting this line
|
|
137
134
|
// type-checks and forwards nothing, so every child would quietly run with
|
|
@@ -147,11 +144,7 @@ export class TeamRuntime {
|
|
|
147
144
|
// show the child its brief twice.
|
|
148
145
|
const makeHandle = (spec) => createSubagent(spec, subDeps, {
|
|
149
146
|
onUpdate: (run) => this.publish(dispatchId, run),
|
|
150
|
-
|
|
151
|
-
// event rather than always using the lead's (§3.6 / R-7). Taken from the
|
|
152
|
-
// SPEC, which the normalizer already downgraded when the tier was
|
|
153
|
-
// unavailable, so it names the model that actually billed.
|
|
154
|
-
onUsage: (label, usage) => this.emit({ type: 'usage', dispatchId, label, usage, tier: spec.tier }),
|
|
147
|
+
onUsage: (label, usage) => this.emit({ type: 'usage', dispatchId, label, usage }),
|
|
155
148
|
});
|
|
156
149
|
// ASSIGNMENT, not a declaration: a `const` here would shadow the `let` above
|
|
157
150
|
// the bus, `canSend` would see the empty array forever, and every peer would
|
|
@@ -179,7 +172,15 @@ export class TeamRuntime {
|
|
|
179
172
|
// so the override only bites because `task` listens to `ctx.signal` — and a
|
|
180
173
|
// second, independent timer means a signal that never arrives still ends the
|
|
181
174
|
// dispatch.
|
|
182
|
-
|
|
175
|
+
//
|
|
176
|
+
// `0` MEANS NO CEILING (main-agent parity): the dispatch lasts as long as
|
|
177
|
+
// its slowest child and only an Esc ends it early. The lead's idle
|
|
178
|
+
// watchdog is paused for the whole dispatch either way, exactly as it is
|
|
179
|
+
// for the lead's own long turns.
|
|
180
|
+
this.dispatchTimer =
|
|
181
|
+
team.dispatchTimeoutMs > 0
|
|
182
|
+
? setTimeout(() => this.abortAll(), team.dispatchTimeoutMs)
|
|
183
|
+
: null;
|
|
183
184
|
const runCtx = {
|
|
184
185
|
dispatchId,
|
|
185
186
|
handles,
|
|
@@ -195,24 +196,13 @@ export class TeamRuntime {
|
|
|
195
196
|
signal?.removeEventListener('abort', onAbort);
|
|
196
197
|
for (const handle of handles)
|
|
197
198
|
handle.unsubscribe();
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
// `main` IS THE COMPLEMENT OF `fast`, not an equality test. `SubagentRun.tier`
|
|
203
|
-
// is populated by `createSubagent` from the spec, but a hand-built run in a
|
|
204
|
-
// test — or a run restored from a session file written before this field
|
|
205
|
-
// existed — has it `undefined`, and `=== 'main'` would silently drop that
|
|
206
|
-
// child's spend from the total. Under-reporting is the one failure this
|
|
207
|
-
// split exists to prevent, so the default direction has to be "counted".
|
|
208
|
-
const sumTier = (tier) => handles
|
|
209
|
-
.filter((h) => (tier === 'fast' ? h.run.tier === 'fast' : h.run.tier !== 'fast'))
|
|
210
|
-
.reduce((acc, h) => ({
|
|
199
|
+
// ONE TOTAL, AT THE LEAD'S TABLE. Every child runs the lead's own model
|
|
200
|
+
// (main-agent parity), so there is exactly one tier to sum and one price to
|
|
201
|
+
// sum it at; a split would be two answers to one question.
|
|
202
|
+
const usage = handles.reduce((acc, h) => ({
|
|
211
203
|
inputTokens: acc.inputTokens + h.run.usage.inputTokens,
|
|
212
204
|
outputTokens: acc.outputTokens + h.run.usage.outputTokens,
|
|
213
205
|
}), { inputTokens: 0, outputTokens: 0 });
|
|
214
|
-
const fastUsage = sumTier('fast');
|
|
215
|
-
const ranFast = handles.some((h) => h.run.tier === 'fast');
|
|
216
206
|
const outcome = {
|
|
217
207
|
dispatchId,
|
|
218
208
|
runs: handles.map((h) => ({ ...h.run, filesTouched: [...h.run.filesTouched] })),
|
|
@@ -221,11 +211,7 @@ export class TeamRuntime {
|
|
|
221
211
|
endedAt: Date.now(),
|
|
222
212
|
aborted: this.aborted,
|
|
223
213
|
leadMail: bus.leadMail(),
|
|
224
|
-
usage
|
|
225
|
-
// ABSENT when no fast child ran, which is what keeps a default session's
|
|
226
|
-
// outcome shape byte-identical to the pre-feature one.
|
|
227
|
-
...(ranFast ? { fastUsage } : {}),
|
|
228
|
-
...(extra.downgraded ? { downgraded: extra.downgraded } : {}),
|
|
214
|
+
usage,
|
|
229
215
|
};
|
|
230
216
|
this.live = [];
|
|
231
217
|
this.bus = null;
|
|
@@ -262,14 +248,23 @@ export class TeamRuntime {
|
|
|
262
248
|
for (let attempt = 0;; attempt += 1) {
|
|
263
249
|
const handle = handles[index];
|
|
264
250
|
const { run, agent } = handle;
|
|
251
|
+
// ABORTS GO THROUGH THE HANDLE (never `agent.abort()` directly): the
|
|
252
|
+
// handle sets the synchronous `abortRequested` flag the child's fast
|
|
253
|
+
// reviewer reads as its never-steer-into-a-requested-abort fact.
|
|
265
254
|
run.phase = 'starting';
|
|
266
255
|
run.startedAt = Date.now();
|
|
267
256
|
this.publish(dispatchId, run);
|
|
257
|
+
// `subagentTimeoutMs: 0` MEANS NO CEILING (main-agent parity): no timer
|
|
258
|
+
// is armed and `timedOut` can never become true. A wedged child is still
|
|
259
|
+
// ended by its OWN idle watchdog, which is the exact ceiling the lead
|
|
260
|
+
// runs under.
|
|
268
261
|
let timedOut = false;
|
|
269
|
-
const timer =
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
262
|
+
const timer = subagentTimeoutMs > 0
|
|
263
|
+
? setTimeout(() => {
|
|
264
|
+
timedOut = true;
|
|
265
|
+
handle.abort();
|
|
266
|
+
}, subagentTimeoutMs)
|
|
267
|
+
: null;
|
|
273
268
|
try {
|
|
274
269
|
await agent.prompt(handle.spec.prompt);
|
|
275
270
|
}
|
|
@@ -281,7 +276,8 @@ export class TeamRuntime {
|
|
|
281
276
|
run.error = err instanceof Error ? err.message : String(err);
|
|
282
277
|
}
|
|
283
278
|
finally {
|
|
284
|
-
|
|
279
|
+
if (timer)
|
|
280
|
+
clearTimeout(timer);
|
|
285
281
|
}
|
|
286
282
|
// THE TWO ABORT EXCLUSIONS THAT MAKE THE PREDICATE SAFE. Every abort in
|
|
287
283
|
// this system reaches the provider as an aborted fetch, and `core` marks
|
|
@@ -319,7 +315,7 @@ export class TeamRuntime {
|
|
|
319
315
|
const retries = (handle.run.retries ?? 0) + 1;
|
|
320
316
|
handle.unsubscribe();
|
|
321
317
|
try {
|
|
322
|
-
handle.
|
|
318
|
+
handle.abort();
|
|
323
319
|
}
|
|
324
320
|
catch {
|
|
325
321
|
// Already gone; this must not stop the retry.
|
|
@@ -346,7 +342,7 @@ export class TeamRuntime {
|
|
|
346
342
|
if (this.aborted) {
|
|
347
343
|
run.phase = 'aborted';
|
|
348
344
|
}
|
|
349
|
-
else if (timedOut) {
|
|
345
|
+
else if (timedOut && subagentTimeoutMs > 0) {
|
|
350
346
|
run.phase = 'failed';
|
|
351
347
|
run.error = run.error ?? `subagent exceeded ${Math.round(subagentTimeoutMs / 1000)}s`;
|
|
352
348
|
}
|
|
@@ -398,7 +394,7 @@ export class TeamRuntime {
|
|
|
398
394
|
this.aborted = true;
|
|
399
395
|
for (const handle of this.live) {
|
|
400
396
|
try {
|
|
401
|
-
handle.
|
|
397
|
+
handle.abort();
|
|
402
398
|
}
|
|
403
399
|
catch {
|
|
404
400
|
// A child that is already gone must not stop the others being aborted.
|
package/dist/team/subagent.d.ts
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
* omission has a reason that is a correctness boundary rather than caution.
|
|
12
12
|
*/
|
|
13
13
|
import type { ModelRole } from '../config/model-profiles.js';
|
|
14
|
-
import { type AgentConfig, type AgentEvent, type AgentTool, type Message, type
|
|
14
|
+
import { type AgentConfig, type AgentEvent, type AgentTool, type Message, type ProviderRegistry, type SkillRegistry, type TokenUsage, type ToolPolicyDecision, type ToolPolicyVerdict } from '@aragon-agent/core';
|
|
15
15
|
import type { AgentMode } from '../agent/agent-mode.js';
|
|
16
16
|
import type { ChildContextManagerProvider } from '../compaction/child.js';
|
|
17
17
|
import type { CliConfig } from '../config/schema.js';
|
|
18
18
|
import type { ToolPermission } from '../exec/permission.js';
|
|
19
|
-
import type {
|
|
19
|
+
import type { ChildReviewProvider } from './child-reviewer.js';
|
|
20
20
|
import { TeamBus } from './bus.js';
|
|
21
21
|
import type { TeamHumanQueue, WatchdogPausable } from './human-queue.js';
|
|
22
22
|
import type { SubagentRun, SubagentSpec } from './types.js';
|
|
@@ -31,6 +31,10 @@ export interface SubagentAgentLike extends WatchdogPausable {
|
|
|
31
31
|
prompt(text: string): Promise<void>;
|
|
32
32
|
abort(): void;
|
|
33
33
|
subscribe(listener: (event: AgentEvent) => void): () => void;
|
|
34
|
+
/** Steering surface for the child's own fast reviewer (core `Agent.steer`). */
|
|
35
|
+
steer(text: string): void;
|
|
36
|
+
/** Queued-block hygiene on abort, again mirroring the lead (D-21). */
|
|
37
|
+
clearAllQueues(): void;
|
|
34
38
|
/**
|
|
35
39
|
* The child's live history, for its own context manager
|
|
36
40
|
* (context-auto-compaction-hardening §3.4.3 / W3).
|
|
@@ -95,23 +99,11 @@ export interface SubagentDeps {
|
|
|
95
99
|
humanQueue?: TeamHumanQueue;
|
|
96
100
|
agentFactory: SubagentAgentFactory;
|
|
97
101
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* and test compiles unchanged and a `--no-fast` session builds children
|
|
102
|
-
* exactly as it always did.
|
|
103
|
-
*
|
|
104
|
-
* `resolveTier('fast')` RETURNS THE MAIN TIER when the fast tier is not
|
|
105
|
-
* available, which is what makes the two guards agree BY CONSTRUCTION: the
|
|
106
|
-
* normalizer can never produce a `tier: 'fast'` spec this factory would
|
|
107
|
-
* refuse, and this factory can never build a child against a `ModelRef` the
|
|
108
|
-
* tier stopped resolving twenty minutes ago (RV-3 / R-14).
|
|
102
|
+
* Per-child fast review (main-agent parity): the controller's shared
|
|
103
|
+
* provider, built on the lead's `FastWiring`. ABSENT means the session
|
|
104
|
+
* has no fast wiring, and the child is then exactly what it always was.
|
|
109
105
|
*/
|
|
110
|
-
|
|
111
|
-
ref: ModelRef;
|
|
112
|
-
thinkingLevel: ThinkingLevel;
|
|
113
|
-
role?: ModelRole;
|
|
114
|
-
};
|
|
106
|
+
review?: ChildReviewProvider;
|
|
115
107
|
/**
|
|
116
108
|
* Build this child its own context manager
|
|
117
109
|
* (context-auto-compaction-hardening §3.4.3 / W3).
|
|
@@ -134,6 +126,14 @@ export interface SubagentHandle {
|
|
|
134
126
|
run: SubagentRun;
|
|
135
127
|
agent: SubagentAgentLike;
|
|
136
128
|
tools: AgentTool[];
|
|
129
|
+
/**
|
|
130
|
+
* THE HANDLE-LEVEL ABORT, and the only one `TeamRuntime` may call. It sets a
|
|
131
|
+
* synchronous `abortRequested` flag BEFORE `agent.abort()`, which is what the
|
|
132
|
+
* child's fast reviewer reads as its guard-1 fact: an Esc or a dispatch stop
|
|
133
|
+
* must be visible to the reviewer in the same tick it happens, or the
|
|
134
|
+
* reviewer steers a `<fast_review>` block into a run that is already dying.
|
|
135
|
+
*/
|
|
136
|
+
abort: () => void;
|
|
137
137
|
unsubscribe: () => void;
|
|
138
138
|
}
|
|
139
139
|
/**
|