@nexrall/code-core 1.4.33 → 1.4.35
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/agentRegistry.d.ts +27 -2
- package/dist/agent/agentRegistry.d.ts.map +1 -1
- package/dist/agent/agentRegistry.js +24 -3
- package/dist/agent/agentTypes.d.ts +1 -1
- package/dist/agent/agentTypes.d.ts.map +1 -1
- package/dist/agent/agentTypes.js +72 -14
- package/dist/agent/loop.d.ts +80 -8
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +502 -141
- package/dist/agent/skills.d.ts +1 -1
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/commands/loader.d.ts +1 -1
- package/dist/commands/loader.d.ts.map +1 -1
- package/dist/tools/executor.d.ts.map +1 -1
- package/dist/tools/executor.js +10 -9
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/util/safeSlice.d.ts +20 -0
- package/dist/util/safeSlice.d.ts.map +1 -0
- package/dist/util/safeSlice.js +40 -0
- package/package.json +8 -7
|
@@ -9,6 +9,22 @@ export interface StoredAgent {
|
|
|
9
9
|
* after the agent first ran.
|
|
10
10
|
*/
|
|
11
11
|
agentName: string | null;
|
|
12
|
+
/**
|
|
13
|
+
* The agent scope that SPAWNED this run — who is allowed to resume it.
|
|
14
|
+
*
|
|
15
|
+
* Ids are handed to exactly one frame (the parent that spawned the sub-agent), but the
|
|
16
|
+
* registry is one flat process-global Map and ids are sequential, so without an owner an
|
|
17
|
+
* id is an unauthenticated, guessable capability. That was contained while sub-agents
|
|
18
|
+
* were leaves: only the main agent could spawn, so only it ever held ids.
|
|
19
|
+
*
|
|
20
|
+
* With nesting it is not. Any descendant granted `task` could pass
|
|
21
|
+
* `resume_agent_id: "agent_1"` and (a) read another agent's entire unredacted transcript
|
|
22
|
+
* — file contents, command output — and (b) OVERWRITE it via updateAgent, poisoning what
|
|
23
|
+
* a more privileged frame later ingests as trusted prior context.
|
|
24
|
+
*
|
|
25
|
+
* `null` means the main agent owns it (scope 'root').
|
|
26
|
+
*/
|
|
27
|
+
ownerScope: string;
|
|
12
28
|
/** Full conversation: the sub-agent's own user/assistant/tool messages. */
|
|
13
29
|
messages: Message[];
|
|
14
30
|
/** The description shown in the UI, for `/agents --resumable` style listings. */
|
|
@@ -30,9 +46,18 @@ export interface StoredAgent {
|
|
|
30
46
|
bytes: number;
|
|
31
47
|
}
|
|
32
48
|
/** Store a finished sub-agent's transcript. Returns its resumable id. */
|
|
33
|
-
export declare function rememberAgent(agentName: string | null, description: string, messages: Message[]): string;
|
|
49
|
+
export declare function rememberAgent(agentName: string | null, description: string, messages: Message[], ownerScope?: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* May `scope` resume/modify the agent stored under `id`?
|
|
52
|
+
*
|
|
53
|
+
* Only the frame that spawned it, or the main agent. Deliberately NOT "any ancestor":
|
|
54
|
+
* scopes are opaque ids (`sub_7`), not a path, so ancestry cannot be derived from them —
|
|
55
|
+
* and inventing a prefix convention to fake it would be a second guessable capability.
|
|
56
|
+
* The main agent is included because it owns the session and already sees every result.
|
|
57
|
+
*/
|
|
58
|
+
export declare function canResume(stored: StoredAgent, scope: string): boolean;
|
|
34
59
|
/** Append a resumed run's new messages to an existing agent. */
|
|
35
|
-
export declare function updateAgent(id: string, messages: Message[]): void;
|
|
60
|
+
export declare function updateAgent(id: string, messages: Message[], scope?: string): void;
|
|
36
61
|
export declare function getAgent(id: string): StoredAgent | undefined;
|
|
37
62
|
/** Newest first — the order a "which agents can I resume?" list wants. */
|
|
38
63
|
export declare function listAgents(): StoredAgent[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentRegistry.d.ts","sourceRoot":"","sources":["../../src/agent/agentRegistry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agentRegistry.d.ts","sourceRoot":"","sources":["../../src/agent/agentRegistry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AA8CxC,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX;;;;;;OAMG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;;;;;;;;;;;OAcG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,iFAAiF;IACjF,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;;OASG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;CACf;AA4CD,yEAAyE;AACzE,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,EAAE,EACnB,UAAU,SAAS,GAClB,MAAM,CAkBR;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAErE;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,SAAS,GAAG,IAAI,CAgBjF;AAED,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAE5D;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,IAAI,WAAW,EAAE,CAE1C;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C;AAED,eAAO,MAAM,OAAO;;;CAAkC,CAAC"}
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports._limits = void 0;
|
|
4
4
|
exports.rememberAgent = rememberAgent;
|
|
5
|
+
exports.canResume = canResume;
|
|
5
6
|
exports.updateAgent = updateAgent;
|
|
6
7
|
exports.getAgent = getAgent;
|
|
7
8
|
exports.listAgents = listAgents;
|
|
8
9
|
exports._resetAgentRegistry = _resetAgentRegistry;
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
11
|
// ─── Resumable sub-agents ────────────────────────────────────────────────────
|
|
10
12
|
//
|
|
11
13
|
// A sub-agent normally starts with a blank slate: fresh context, no memory of a
|
|
@@ -90,12 +92,15 @@ function sizeOf(messages) {
|
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
/** Store a finished sub-agent's transcript. Returns its resumable id. */
|
|
93
|
-
function rememberAgent(agentName, description, messages) {
|
|
94
|
-
|
|
95
|
+
function rememberAgent(agentName, description, messages, ownerScope = 'root') {
|
|
96
|
+
// Unguessable, so an id cannot be ENUMERATED even before the ownership check runs.
|
|
97
|
+
// Defence in depth only — randomness is not authorisation, `canResume` is.
|
|
98
|
+
const id = `agent_${++_counter}_${(0, node_crypto_1.randomUUID)().slice(0, 8)}`;
|
|
95
99
|
const now = Date.now();
|
|
96
100
|
_agents.set(id, {
|
|
97
101
|
id,
|
|
98
102
|
agentName,
|
|
103
|
+
ownerScope,
|
|
99
104
|
description,
|
|
100
105
|
messages,
|
|
101
106
|
createdAt: now,
|
|
@@ -106,11 +111,27 @@ function rememberAgent(agentName, description, messages) {
|
|
|
106
111
|
evictUntilWithinLimits();
|
|
107
112
|
return id;
|
|
108
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* May `scope` resume/modify the agent stored under `id`?
|
|
116
|
+
*
|
|
117
|
+
* Only the frame that spawned it, or the main agent. Deliberately NOT "any ancestor":
|
|
118
|
+
* scopes are opaque ids (`sub_7`), not a path, so ancestry cannot be derived from them —
|
|
119
|
+
* and inventing a prefix convention to fake it would be a second guessable capability.
|
|
120
|
+
* The main agent is included because it owns the session and already sees every result.
|
|
121
|
+
*/
|
|
122
|
+
function canResume(stored, scope) {
|
|
123
|
+
return stored.ownerScope === scope || scope === 'root';
|
|
124
|
+
}
|
|
109
125
|
/** Append a resumed run's new messages to an existing agent. */
|
|
110
|
-
function updateAgent(id, messages) {
|
|
126
|
+
function updateAgent(id, messages, scope = 'root') {
|
|
111
127
|
const existing = _agents.get(id);
|
|
112
128
|
if (!existing)
|
|
113
129
|
return;
|
|
130
|
+
// Same authorisation as reading. Without it a low-privilege descendant could overwrite a
|
|
131
|
+
// privileged agent's transcript while its `agentName` stayed unchanged — indirect prompt
|
|
132
|
+
// injection into whatever frame resumes it next.
|
|
133
|
+
if (!canResume(existing, scope))
|
|
134
|
+
return;
|
|
114
135
|
existing.messages = messages;
|
|
115
136
|
existing.updatedAt = Date.now();
|
|
116
137
|
existing.seq = ++_seq;
|
|
@@ -4,7 +4,7 @@ export interface AgentType {
|
|
|
4
4
|
/** Optional allowlist — when set, the sub-agent may only use these tools. */
|
|
5
5
|
tools?: string[];
|
|
6
6
|
/** Optional model override for the sub-agent. */
|
|
7
|
-
model?:
|
|
7
|
+
model?: string;
|
|
8
8
|
/** System instructions (the markdown body below the frontmatter). */
|
|
9
9
|
prompt: string;
|
|
10
10
|
source: 'project' | 'global' | 'builtin' | 'plugin';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentTypes.d.ts","sourceRoot":"","sources":["../../src/agent/agentTypes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agentTypes.d.ts","sourceRoot":"","sources":["../../src/agent/agentTypes.ts"],"names":[],"mappings":"AAkCA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACpD;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qDAAqD;AACrD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAiW5D;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAuND;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,YAAY,EAAE,CAAA;CAAE,CAqB5G;AAED,yFAAyF;AACzF,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,IAAI,SAAS,EAAE,CAE3C;AACD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,CAY1D;AAQD,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAIjG"}
|
package/dist/agent/agentTypes.js
CHANGED
|
@@ -130,9 +130,17 @@ const BUILTIN_AGENTS = [
|
|
|
130
130
|
//
|
|
131
131
|
// `tools` is deliberately UNDEFINED, which means "no allowlist" — full access,
|
|
132
132
|
// inheriting whatever the session permits. That is the same power an unnamed
|
|
133
|
-
// sub-task always had; naming it changes only who can see and deny it.
|
|
134
|
-
//
|
|
135
|
-
//
|
|
133
|
+
// sub-task always had; naming it changes only who can see and deny it.
|
|
134
|
+
//
|
|
135
|
+
// It also makes this the ONLY builtin that can nest, since every specialist omits
|
|
136
|
+
// `task` on purpose. So the tree this enables is up to `maxSubagentDepth` generations
|
|
137
|
+
// of unrestricted agents — accepted deliberately, because the alternative (a catch-all
|
|
138
|
+
// that cannot delegate) removes the one agent suited to orchestration.
|
|
139
|
+
//
|
|
140
|
+
// The safety properties that bound it: plan mode is inherited, the user's permission
|
|
141
|
+
// gate runs on every call, a child's allowlist is INTERSECTED with its parent's so a
|
|
142
|
+
// restricted ancestor cannot be escaped through this agent, `test_files_only` is
|
|
143
|
+
// likewise sticky, and both the depth ceiling and the session budget still apply.
|
|
136
144
|
name: 'general-purpose',
|
|
137
145
|
description: 'General-purpose worker for a multi-step task that needs BOTH exploration and changes ' +
|
|
138
146
|
'(edit files, run commands) and that no specialist above fits. Inherits the session model ' +
|
|
@@ -355,7 +363,20 @@ const KNOWN_META_KEYS = new Set([
|
|
|
355
363
|
'name', 'description', 'tools', 'model', 'test_files_only', 'testfilesonly',
|
|
356
364
|
'memory',
|
|
357
365
|
]);
|
|
358
|
-
|
|
366
|
+
// Models this build knows how to NAME. Used only for the frontmatter warning
|
|
367
|
+
// below, never to reject a value — see parseModel for why an allowlist must not
|
|
368
|
+
// gate what is sent.
|
|
369
|
+
//
|
|
370
|
+
// The warning exists for a specific real bug: an old VS Code scaffold wrote
|
|
371
|
+
// `model: auto`, which parsed to undefined and was silently ignored, so the
|
|
372
|
+
// agent ran on the session model while its file claimed otherwise. What made
|
|
373
|
+
// that costly was the SILENCE, not the value.
|
|
374
|
+
const VALID_MODELS = [
|
|
375
|
+
'claude-sonnet-5', 'claude-opus-5', 'claude-fable-5',
|
|
376
|
+
'gpt-5.4', 'gpt-5.4-mini', 'gpt-4.1',
|
|
377
|
+
// Legacy tier aliases — still accepted, still resolved by the backend.
|
|
378
|
+
'turbo', 'pro', 'ultra', 'fast',
|
|
379
|
+
];
|
|
359
380
|
function parseFrontmatter(raw) {
|
|
360
381
|
const m = /^\s*---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
|
|
361
382
|
if (!m)
|
|
@@ -368,9 +389,30 @@ function parseFrontmatter(raw) {
|
|
|
368
389
|
}
|
|
369
390
|
return { meta, body: (m[2] ?? '').trim(), ok: true };
|
|
370
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Parse the `model:` frontmatter of an agent definition.
|
|
394
|
+
*
|
|
395
|
+
* Accepts a tier alias (turbo/pro/ultra) OR a real model id now that the picker
|
|
396
|
+
* exposes those. Validation is deliberately NOT an allowlist of ids: this file
|
|
397
|
+
* ships in a client that users do not update in lockstep with the backend, so
|
|
398
|
+
* an allowlist here would reject a model that is perfectly valid server-side
|
|
399
|
+
* and make new models unusable until everyone upgrades. The backend already
|
|
400
|
+
* refuses anything it does not recognise (resolveExplicitModel), which is the
|
|
401
|
+
* correct place for that decision — it is the side that knows.
|
|
402
|
+
*
|
|
403
|
+
* Only aliases are lowercased. Real model ids are case- and punctuation-
|
|
404
|
+
* sensitive ('gpt-5.4', 'claude-haiku-4-5-20251001'), so lowercasing them
|
|
405
|
+
* blindly — as this function used to do to everything — would corrupt ids that
|
|
406
|
+
* happen to contain uppercase.
|
|
407
|
+
*/
|
|
371
408
|
function parseModel(v) {
|
|
372
|
-
const
|
|
373
|
-
|
|
409
|
+
const raw = (v ?? '').trim();
|
|
410
|
+
if (!raw)
|
|
411
|
+
return undefined;
|
|
412
|
+
const lower = raw.toLowerCase();
|
|
413
|
+
if (lower === 'turbo' || lower === 'pro' || lower === 'ultra' || lower === 'fast')
|
|
414
|
+
return lower;
|
|
415
|
+
return raw;
|
|
374
416
|
}
|
|
375
417
|
/** Parse a frontmatter boolean, accepting the usual truthy spellings. */
|
|
376
418
|
function parseBool(v) {
|
|
@@ -459,11 +501,22 @@ function loadDir(dir, source, into, warnings) {
|
|
|
459
501
|
message: `has memory: "${meta.memory}", which is not a valid scope — use one of ${VALID_MEMORY_SCOPES.join(', ')}, or omit the line to give this agent no persistent notes.`,
|
|
460
502
|
});
|
|
461
503
|
}
|
|
462
|
-
|
|
504
|
+
// Warn on a model this build does not RECOGNISE, while still passing it
|
|
505
|
+
// through (parseModel accepts it). The two are deliberately different
|
|
506
|
+
// decisions: recognising is a local, always-stale question — this client
|
|
507
|
+
// ships independently of the backend, so a model added server-side is
|
|
508
|
+
// valid before this list knows it. Rejecting on that basis would make new
|
|
509
|
+
// models unusable until everyone upgrades; staying silent would recreate
|
|
510
|
+
// the `model: auto` bug where a file claimed one thing and the run did
|
|
511
|
+
// another. So: pass it on, but say something.
|
|
512
|
+
const declaredModel = meta.model === undefined ? undefined : parseModel(meta.model);
|
|
513
|
+
if (meta.model !== undefined
|
|
514
|
+
&& (declaredModel === undefined
|
|
515
|
+
|| !VALID_MODELS.includes(declaredModel))) {
|
|
463
516
|
warnings.push({
|
|
464
517
|
file: full,
|
|
465
518
|
agent: name,
|
|
466
|
-
message: `has model: "${meta.model}", which
|
|
519
|
+
message: `has model: "${meta.model}", which this version does not recognise — expected one of ${VALID_MODELS.join(', ')}, or omit the line to inherit the current session's model. It will still be sent; the server decides whether it is valid.`,
|
|
467
520
|
});
|
|
468
521
|
}
|
|
469
522
|
// The highest-value warning of the lot. An allowlist only ever GRANTS, so a
|
|
@@ -478,15 +531,20 @@ function loadDir(dir, source, into, warnings) {
|
|
|
478
531
|
message: `lists unknown tool name(s): ${unknown.join(', ')}. An allowlist only grants, so these silently do nothing and the agent cannot use them. Tool names are lower_snake_case (read_file, search_files, glob, bash).`,
|
|
479
532
|
});
|
|
480
533
|
}
|
|
481
|
-
// `task`
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
//
|
|
485
|
-
|
|
534
|
+
// `task` in a `tools:` line is MEANINGFUL, and deliberately so.
|
|
535
|
+
//
|
|
536
|
+
// Nesting is now configurable (maxSubagentDepth, default 3), so an allowlist that
|
|
537
|
+
// omits `task` is how you make one specific agent a leaf while others may delegate —
|
|
538
|
+
// per-agent nesting control, enforced at the permission gate.
|
|
539
|
+
//
|
|
540
|
+
// No warning for listing it, therefore. It is only worth flagging when it cannot
|
|
541
|
+
// possibly work: an agent whose ONLY tool is `task` can delegate but not read, write
|
|
542
|
+
// or run anything, which is a definition mistake rather than a policy.
|
|
543
|
+
if ((tools ?? []).length === 1 && tools?.[0] === 'task') {
|
|
486
544
|
warnings.push({
|
|
487
545
|
file: full,
|
|
488
546
|
agent: name,
|
|
489
|
-
message: 'lists `task
|
|
547
|
+
message: 'lists `task` as its ONLY tool, so it can delegate but cannot read, write or run anything itself — it has no way to do or verify work. Add the tools it needs, or drop the `tools:` line to inherit its parent\'s.',
|
|
490
548
|
});
|
|
491
549
|
}
|
|
492
550
|
const strayKeys = Object.keys(meta).filter((k) => !KNOWN_META_KEYS.has(k));
|
package/dist/agent/loop.d.ts
CHANGED
|
@@ -108,6 +108,8 @@ export declare function lockPathsFor(name: string, input: Record<string, unknown
|
|
|
108
108
|
* degrade to "a lot" rather than fork-bomb the machine.
|
|
109
109
|
*/
|
|
110
110
|
export declare function resolveMaxConcurrentSubtasks(settingsRaw?: Record<string, unknown>): number;
|
|
111
|
+
/** Resolve the session-total sub-agent ceiling: env → settings.json → default. */
|
|
112
|
+
export declare function resolveMaxSubagentsPerSession(settingsRaw?: Record<string, unknown>): number;
|
|
111
113
|
/**
|
|
112
114
|
* Minimal concurrency gate. Hand-rolled rather than pulling in `p-limit` because
|
|
113
115
|
* the CLI ships as a single esbuild bundle with no node_modules, and this is a
|
|
@@ -116,7 +118,46 @@ export declare function resolveMaxConcurrentSubtasks(settingsRaw?: Record<string
|
|
|
116
118
|
export declare function createLimiter(max: number): <T>(fn: () => Promise<T>) => Promise<T>;
|
|
117
119
|
/** Test-only: forget the memoised limiter so a new limit can take effect. */
|
|
118
120
|
export declare function _resetSubTaskLimiter(): void;
|
|
119
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Start a fresh sub-agent budget. Call when a NEW conversation begins.
|
|
123
|
+
*
|
|
124
|
+
* Exported for clients that reuse one process across conversations (the VS Code extension
|
|
125
|
+
* host). A client that never calls it gets process-lifetime semantics, which is correct
|
|
126
|
+
* for a one-shot CLI invocation.
|
|
127
|
+
*/
|
|
128
|
+
export declare function resetSessionSubAgentBudget(): void;
|
|
129
|
+
/** Test-only: observe the session counter without exporting the mutable binding. */
|
|
130
|
+
export declare function _sessionSubAgentCount(): number;
|
|
131
|
+
/**
|
|
132
|
+
* Deepest nesting level allowed to spawn: env → settings.json → default 3.
|
|
133
|
+
*
|
|
134
|
+
* Was a hard-coded 1, i.e. "sub-agents are leaves". That was the right default while the
|
|
135
|
+
* three capability decisions disagreed with each other (see canSpawnSubAgents), but it is
|
|
136
|
+
* no longer where the ecosystem is: Claude Code lifted the no-nesting rule and, after a
|
|
137
|
+
* brief period with it disabled entirely, settled on a configurable default of 3.
|
|
138
|
+
*
|
|
139
|
+
* 3, not 5, deliberately. Depth is a budget you SPEND, not headroom you fill: every level
|
|
140
|
+
* is a context window that receives only a dispatch prompt on the way down and returns
|
|
141
|
+
* only a summary on the way up, so the deeper frames pay full freight to carry less
|
|
142
|
+
* information. 3 covers orchestrator → worker → helper, which is where the observed value
|
|
143
|
+
* is; beyond that latency and token cost tend to exceed the benefit.
|
|
144
|
+
*
|
|
145
|
+
* Depth 1 remains available (`maxSubagentDepth: 1`) for anyone who wants leaves-only.
|
|
146
|
+
*/
|
|
147
|
+
export declare function resolveMaxSubagentDepth(settingsRaw?: Record<string, unknown>): number;
|
|
148
|
+
/**
|
|
149
|
+
* The ONE explanation for "this run may not spawn a sub-agent", shared by every place
|
|
150
|
+
* that can refuse it, so the same impossibility never gets two different stories.
|
|
151
|
+
*
|
|
152
|
+
* Parameterised on the REASON because the reasons are no longer interchangeable. It used
|
|
153
|
+
* to be a flat constant reading "Sub-agents cannot spawn further sub-agents — this is a
|
|
154
|
+
* structural limit"; with nesting configurable that sentence is now false for most runs,
|
|
155
|
+
* and telling a depth-1 agent its limit is structural when the user could raise it by one
|
|
156
|
+
* line of settings is the same class of misdirection as the "needs a different agent" text
|
|
157
|
+
* this replaced. A refusal has to be accurate about whether it can be lifted, or the model
|
|
158
|
+
* either gives up when it shouldn't or hunts for an escape that doesn't exist.
|
|
159
|
+
*/
|
|
160
|
+
export declare function noSpawnReason(kind: 'depth' | 'denied', limit?: number): string;
|
|
120
161
|
/**
|
|
121
162
|
* Whether a run at `depth` may spawn sub-agents.
|
|
122
163
|
*
|
|
@@ -126,11 +167,34 @@ export declare const NO_NESTED_SUBAGENTS_MSG: string;
|
|
|
126
167
|
* independently, and the result was a sub-agent that got the tool plus instructions to use
|
|
127
168
|
* it but no catalogue — then a permission-gate refusal telling it not to ask for approval.
|
|
128
169
|
*
|
|
129
|
-
* `depth <
|
|
130
|
-
*
|
|
131
|
-
*
|
|
170
|
+
* `depth < limit` because the children this run would create land at `depth + 1`; the
|
|
171
|
+
* guard inside runSubTask mirrors it as `depth >= limit`.
|
|
172
|
+
*
|
|
173
|
+
* `limit` is injected rather than read from module state so this stays pure and testable.
|
|
174
|
+
* Callers pass the resolved per-workspace value; it defaults to the built-in for the
|
|
175
|
+
* handful of call sites that have no settings in hand.
|
|
176
|
+
*/
|
|
177
|
+
/**
|
|
178
|
+
* A child sub-agent's effective tool allowlist: its own, narrowed by its parent's.
|
|
179
|
+
*
|
|
180
|
+
* Exported and pure because it is a SECURITY boundary and was previously verified only by
|
|
181
|
+
* grepping the source for the intersection expression — which matched happily while the
|
|
182
|
+
* code threw a TypeError on one of its own four cases. A boundary needs behavioural tests.
|
|
183
|
+
*
|
|
184
|
+
* `null` means "no allowlist" (unrestricted), and it is returned only when BOTH sides say
|
|
185
|
+
* so. The four cases:
|
|
186
|
+
* own + parent → intersection (a child can narrow, never widen)
|
|
187
|
+
* own only → own (the main agent, which has no allowlist, spawning a specialist)
|
|
188
|
+
* parent only → a COPY of parent (an unnamed/general-purpose child inherits the
|
|
189
|
+
* restriction instead of resetting to full access — this is the
|
|
190
|
+
* escalation path, since `general-purpose` declares no tools at all)
|
|
191
|
+
* neither → null
|
|
192
|
+
*
|
|
193
|
+
* The parent-only case must COPY: the caller adds AGENT_MEMORY_TOOL to the returned set,
|
|
194
|
+
* which would otherwise mutate the parent's live allowlist.
|
|
132
195
|
*/
|
|
133
|
-
export declare function
|
|
196
|
+
export declare function intersectAllowlists(own: Set<string> | null, parent: Set<string> | undefined): Set<string> | null;
|
|
197
|
+
export declare function canSpawnSubAgents(depth: number, allowedTools?: Set<string>, limit?: number): boolean;
|
|
134
198
|
/**
|
|
135
199
|
* How long a sub-agent may make NO progress before it is stopped.
|
|
136
200
|
*
|
|
@@ -197,8 +261,16 @@ export declare function summariseSubTaskProgress(messages: Message[]): string;
|
|
|
197
261
|
* error), and a head slice is the half that identifies what was found.
|
|
198
262
|
*/
|
|
199
263
|
export declare function lastToolResults(messages: Message[], count: number, maxChars: number): string;
|
|
200
|
-
/**
|
|
201
|
-
|
|
264
|
+
/**
|
|
265
|
+
* Context window (tokens) for a model alias or real model id.
|
|
266
|
+
*
|
|
267
|
+
* The fallback is deliberately the SMALLEST window in the table rather than the
|
|
268
|
+
* largest. An unknown model is most likely a newly added one this client build
|
|
269
|
+
* predates, and guessing high is the failure that cannot be recovered from: the
|
|
270
|
+
* turn hits a provider 400 with no chance to compact. Guessing low only costs
|
|
271
|
+
* an earlier, lossy compaction — annoying, not broken.
|
|
272
|
+
*/
|
|
273
|
+
export declare function contextWindowFor(model?: string): number;
|
|
202
274
|
/** Auto-prune / auto-compact thresholds as fractions of the context window — for UI display (e.g. `/context`). */
|
|
203
275
|
export declare function compactionThresholds(): {
|
|
204
276
|
prune: number;
|
|
@@ -341,7 +413,7 @@ export declare function estimateTokensRough(messages: Message[]): number;
|
|
|
341
413
|
* the in-loop path shows, so the behaviour is visually consistent.
|
|
342
414
|
*/
|
|
343
415
|
export declare function compactMessagesForResume(messages: Message[], opts: {
|
|
344
|
-
model?:
|
|
416
|
+
model?: string;
|
|
345
417
|
workDir: string;
|
|
346
418
|
clientType?: string;
|
|
347
419
|
env?: EnvContext;
|
package/dist/agent/loop.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAKP,UAAU,EACV,gBAAgB,EAChB,UAAU,EACX,MAAM,UAAU,CAAC;AAIlB,OAAO,EAAkE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMrH,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"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"}
|