@wrongstack/core 0.298.3 → 0.300.0
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/chronicle/index.js +4 -1
- package/dist/coordination/agents/index.js +4 -1
- package/dist/coordination/director.d.ts +8 -0
- package/dist/coordination/fleet-manager.d.ts +48 -3
- package/dist/coordination/ifleet-manager.d.ts +2 -0
- package/dist/coordination/index.js +127 -24
- package/dist/coordination/multi-agent-coordinator.d.ts +1 -0
- package/dist/core/fallback-model.d.ts +48 -0
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +288 -34
- package/dist/core/instruction-template.d.ts +80 -0
- package/dist/core/system-prompt-blocks.d.ts +10 -1
- package/dist/core/system-prompt-builder.d.ts +35 -1
- package/dist/defaults/index.js +358 -117
- package/dist/design/index.js +4 -1
- package/dist/execution/autonomy-brain.d.ts +7 -0
- package/dist/execution/council-brain.d.ts +17 -2
- package/dist/execution/council-orchestrator.d.ts +23 -4
- package/dist/execution/council-personas.d.ts +10 -0
- package/dist/execution/council-prompts.d.ts +12 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +412 -145
- package/dist/fleet-notifier.d.ts +9 -2
- package/dist/goal/index.js +4 -1
- package/dist/hooks/index.js +140 -10
- package/dist/hq/exposure.d.ts +0 -11
- package/dist/hq/index.js +34 -8
- package/dist/hq/protocol/client.d.ts +14 -1
- package/dist/hq/protocol/fleet.d.ts +22 -0
- package/dist/hq/protocol.js +12 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1718 -753
- package/dist/infrastructure/index.js +50 -2
- package/dist/infrastructure/mcp-servers.d.ts +35 -0
- package/dist/kernel/events/brain-events.d.ts +9 -0
- package/dist/kernel/events/provider-events.d.ts +49 -2
- package/dist/kernel/events/sdd-events.d.ts +2 -0
- package/dist/models/index.js +1 -1
- package/dist/plugin/api.d.ts +6 -0
- package/dist/plugin/config.d.ts +55 -0
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.js +138 -22
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +157 -42
- package/dist/security/permission-helpers.d.ts +23 -6
- package/dist/security/permission-policy.d.ts +16 -0
- package/dist/security/totp.d.ts +14 -0
- package/dist/storage/director-state.d.ts +7 -0
- package/dist/storage/index.js +46 -9
- package/dist/tools/council-tool.d.ts +1 -1
- package/dist/tools/fallback-system-config-view-tool.d.ts +1 -1
- package/dist/tools/index.js +449 -112
- package/dist/types/config/skills-fleet-brain.d.ts +4 -2
- package/dist/types/config/tools.d.ts +99 -0
- package/dist/types/council.d.ts +11 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.js +3 -3
- package/dist/types/multi-agent.d.ts +10 -0
- package/dist/types/one-shot-llm.d.ts +31 -3
- package/dist/types/plugin.d.ts +28 -0
- package/dist/types/session.d.ts +5 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/wstack-paths.d.ts +2 -0
- package/dist/worktree/index.js +47 -25
- package/dist/worktree/worktree-manager.d.ts +16 -10
- package/instructions/coordination/subagent-baseline.md +8 -0
- package/instructions/system-lite.md +83 -3
- package/instructions/system-pro.md +286 -97
- package/instructions/system.md +236 -85
- package/package.json +3 -3
package/dist/chronicle/index.js
CHANGED
|
@@ -582,7 +582,9 @@ function safeProfileName(name) {
|
|
|
582
582
|
function activeProfileName(globalRoot) {
|
|
583
583
|
try {
|
|
584
584
|
const parsed = JSON.parse(fs2.readFileSync(path4.join(globalRoot, "config.json"), "utf8"));
|
|
585
|
-
return safeProfileName(
|
|
585
|
+
return safeProfileName(
|
|
586
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
587
|
+
);
|
|
586
588
|
} catch {
|
|
587
589
|
return "default";
|
|
588
590
|
}
|
|
@@ -662,6 +664,7 @@ function resolveWstackPaths(opts) {
|
|
|
662
664
|
projectPlan: path4.join(projectDir, "plan.json"),
|
|
663
665
|
projectAutophase: path4.join(projectDir, "autophase"),
|
|
664
666
|
projectSddBoards: path4.join(projectDir, "sdd-boards"),
|
|
667
|
+
projectRequirementIntakes: path4.join(projectDir, "requirement-intakes"),
|
|
665
668
|
syncConfig: path4.join(profileDir, "sync.json"),
|
|
666
669
|
configHistoryDir: path4.join(globalRoot, "config-history"),
|
|
667
670
|
projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -98,7 +98,9 @@ function safeProfileName(name) {
|
|
|
98
98
|
function activeProfileName(globalRoot) {
|
|
99
99
|
try {
|
|
100
100
|
const parsed = JSON.parse(fs.readFileSync(path.join(globalRoot, "config.json"), "utf8"));
|
|
101
|
-
return safeProfileName(
|
|
101
|
+
return safeProfileName(
|
|
102
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
103
|
+
);
|
|
102
104
|
} catch {
|
|
103
105
|
return "default";
|
|
104
106
|
}
|
|
@@ -178,6 +180,7 @@ function resolveWstackPaths(opts) {
|
|
|
178
180
|
projectPlan: path.join(projectDir, "plan.json"),
|
|
179
181
|
projectAutophase: path.join(projectDir, "autophase"),
|
|
180
182
|
projectSddBoards: path.join(projectDir, "sdd-boards"),
|
|
183
|
+
projectRequirementIntakes: path.join(projectDir, "requirement-intakes"),
|
|
181
184
|
syncConfig: path.join(profileDir, "sync.json"),
|
|
182
185
|
configHistoryDir: path.join(globalRoot, "config-history"),
|
|
183
186
|
projectStatus: (projectHash2) => path.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -149,5 +149,13 @@ export declare class Director implements DirectorFleetHost, ICoordinator {
|
|
|
149
149
|
acquireCheckpointLock(): Promise<boolean>;
|
|
150
150
|
spawnCollab(options: CollabSessionOptions): Promise<CollabDebugReport>;
|
|
151
151
|
resumeFromCheckpoint(snapshot: DirectorStateSnapshot): void;
|
|
152
|
+
/**
|
|
153
|
+
* After re-attaching checkpoint metadata, pin the live maxSpawns ceiling
|
|
154
|
+
* (profile/flag/env wins over historical checkpoint metadata). The
|
|
155
|
+
* historical cumulative spawn counter is deliberately NOT restored — the
|
|
156
|
+
* lifetime budget is scoped to this director run, so a restarted session
|
|
157
|
+
* resumes with a fresh budget rather than a possibly-exhausted counter.
|
|
158
|
+
*/
|
|
159
|
+
private applyResumeBudget;
|
|
152
160
|
}
|
|
153
161
|
//# sourceMappingURL=director.d.ts.map
|
|
@@ -72,6 +72,14 @@ export declare class FleetManager implements IFleetManager {
|
|
|
72
72
|
readonly spawnDepth: number;
|
|
73
73
|
/** Live spawn counter. */
|
|
74
74
|
private spawnCount;
|
|
75
|
+
/**
|
|
76
|
+
* Historical `maxSpawns` from the last restored checkpoint (set for any
|
|
77
|
+
* finite checkpoint ceiling, matching or not). Surfaced in
|
|
78
|
+
* `budgetSnapshot()` so operators can see resume reconciliation without
|
|
79
|
+
* reading the JSON file; `ceilingMismatch` carries the actual mismatch signal.
|
|
80
|
+
*/
|
|
81
|
+
private checkpointMaxSpawnsAtResume;
|
|
82
|
+
private ceilingMismatchAtResume;
|
|
75
83
|
private readonly stateCheckpoint;
|
|
76
84
|
private readonly sessionWriter;
|
|
77
85
|
private manifestTimer;
|
|
@@ -129,7 +137,7 @@ export declare class FleetManager implements IFleetManager {
|
|
|
129
137
|
* which cap was exceeded. Does NOT throw — the caller decides
|
|
130
138
|
* how to surface the rejection.
|
|
131
139
|
*/
|
|
132
|
-
canSpawn(
|
|
140
|
+
canSpawn(config: SubagentConfig): {
|
|
133
141
|
kind: 'max_spawns' | 'max_spawn_depth' | 'max_cost_usd' | 'max_tokens' | 'max_context_load';
|
|
134
142
|
limit: number;
|
|
135
143
|
observed: number;
|
|
@@ -144,6 +152,41 @@ export declare class FleetManager implements IFleetManager {
|
|
|
144
152
|
maxCostUsd: number;
|
|
145
153
|
usedCostUsd: number;
|
|
146
154
|
remainingCostUsd: number;
|
|
155
|
+
/** Historical ceiling from the last restored checkpoint, when it differs. */
|
|
156
|
+
checkpointMaxSpawns?: number | undefined;
|
|
157
|
+
/** True when checkpoint metadata and the live ceiling disagree. */
|
|
158
|
+
ceilingMismatch?: boolean | undefined;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Re-attach the on-disk checkpoint after a resume while keeping the live
|
|
162
|
+
* `maxSpawns` ceiling from construction (profile / flag / env). The
|
|
163
|
+
* historical `spawnCount` is deliberately NOT restored: the lifetime
|
|
164
|
+
* budget is scoped to this director run, so a restarted session gets a
|
|
165
|
+
* fresh budget instead of inheriting a (possibly exhausted) counter from
|
|
166
|
+
* before the restart. The checkpoint ceiling is rewritten to the live
|
|
167
|
+
* value so subsequent writes do not reintroduce a stale limit.
|
|
168
|
+
*/
|
|
169
|
+
restoreFromCheckpoint(snapshot: {
|
|
170
|
+
spawnCount: number;
|
|
171
|
+
maxSpawns?: number | undefined;
|
|
172
|
+
version?: 1 | undefined;
|
|
173
|
+
directorRunId?: string | undefined;
|
|
174
|
+
updatedAt?: string | undefined;
|
|
175
|
+
spawnDepth?: number | undefined;
|
|
176
|
+
maxSpawnDepth?: number | undefined;
|
|
177
|
+
directorBudget?: {
|
|
178
|
+
maxCostUsd?: number | undefined;
|
|
179
|
+
maxTokens?: number | undefined;
|
|
180
|
+
} | undefined;
|
|
181
|
+
subagents?: unknown[] | undefined;
|
|
182
|
+
tasks?: unknown[] | undefined;
|
|
183
|
+
usage?: unknown | undefined;
|
|
184
|
+
}): {
|
|
185
|
+
usedSpawns: number;
|
|
186
|
+
maxSpawns: number;
|
|
187
|
+
remainingSpawns: number;
|
|
188
|
+
checkpointMaxSpawns?: number | undefined;
|
|
189
|
+
ceilingMismatch: boolean;
|
|
147
190
|
};
|
|
148
191
|
setLeaderContextPressure(tokens: number): void;
|
|
149
192
|
/** Test-only accessor: number of entries in the private subagent-meta map. */
|
|
@@ -175,8 +218,10 @@ export declare class FleetManager implements IFleetManager {
|
|
|
175
218
|
*/
|
|
176
219
|
get usedNicknames(): ReadonlySet<string>;
|
|
177
220
|
/**
|
|
178
|
-
* Records a spawn:
|
|
179
|
-
*
|
|
221
|
+
* Records a spawn: stores metadata, updates state checkpoint, and schedules
|
|
222
|
+
* a debounced manifest write. Increments the lifetime spawn counter UNLESS
|
|
223
|
+
* the config is `spawnBudgetExempt` (ephemeral Chimera reviewer/cascade
|
|
224
|
+
* spawns do not consume the leader's budget). Call AFTER the coordinator
|
|
180
225
|
* has successfully spawned the subagent.
|
|
181
226
|
*
|
|
182
227
|
* @param subagentId The subagent's id (from coordinator.spawn result)
|
|
@@ -52,6 +52,8 @@ export interface IFleetManager {
|
|
|
52
52
|
maxCostUsd: number;
|
|
53
53
|
usedCostUsd: number;
|
|
54
54
|
remainingCostUsd: number;
|
|
55
|
+
checkpointMaxSpawns?: number | undefined;
|
|
56
|
+
ceilingMismatch?: boolean | undefined;
|
|
55
57
|
}) | undefined;
|
|
56
58
|
/**
|
|
57
59
|
* Update the leader agent's current context pressure (tokens used in the
|
|
@@ -1361,7 +1361,9 @@ function safeProfileName(name) {
|
|
|
1361
1361
|
function activeProfileName(globalRoot) {
|
|
1362
1362
|
try {
|
|
1363
1363
|
const parsed = JSON.parse(fs.readFileSync(path.join(globalRoot, "config.json"), "utf8"));
|
|
1364
|
-
return safeProfileName(
|
|
1364
|
+
return safeProfileName(
|
|
1365
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
1366
|
+
);
|
|
1365
1367
|
} catch {
|
|
1366
1368
|
return "default";
|
|
1367
1369
|
}
|
|
@@ -1441,6 +1443,7 @@ function resolveWstackPaths(opts) {
|
|
|
1441
1443
|
projectPlan: path.join(projectDir, "plan.json"),
|
|
1442
1444
|
projectAutophase: path.join(projectDir, "autophase"),
|
|
1443
1445
|
projectSddBoards: path.join(projectDir, "sdd-boards"),
|
|
1446
|
+
projectRequirementIntakes: path.join(projectDir, "requirement-intakes"),
|
|
1444
1447
|
syncConfig: path.join(profileDir, "sync.json"),
|
|
1445
1448
|
configHistoryDir: path.join(globalRoot, "config-history"),
|
|
1446
1449
|
projectStatus: (projectHash2) => path.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -6755,16 +6758,21 @@ var BrainMonitor = class {
|
|
|
6755
6758
|
this.activeRuns += 1;
|
|
6756
6759
|
this.lastProgressAt = Date.now();
|
|
6757
6760
|
}),
|
|
6761
|
+
// `agent.run.completed` is the only terminator. `Agent.run` emits it on
|
|
6762
|
+
// every exit path — the success path and, unconditionally, the catch
|
|
6763
|
+
// path — and emits `agent.run.error` *in addition* when a run fails
|
|
6764
|
+
// (`core/agent.ts:302` then `:310`). Decrementing on both meant one
|
|
6765
|
+
// failed run subtracted two, and the `Math.max(0, …)` clamp hid it
|
|
6766
|
+
// instead of letting the counter go visibly negative.
|
|
6767
|
+
//
|
|
6768
|
+
// The effect was that the watchdog below stopped watching: with two
|
|
6769
|
+
// concurrent runs where one fails, `activeRuns` reaches 0 while a run
|
|
6770
|
+
// is still live, and the stall check returns early on every tick.
|
|
6758
6771
|
this.opts.events.on("agent.run.completed", (e) => {
|
|
6759
6772
|
const lsid = this.resolveLeaderSessionId();
|
|
6760
6773
|
if (lsid && e.sessionId && e.sessionId !== lsid) return;
|
|
6761
6774
|
this.activeRuns = Math.max(0, this.activeRuns - 1);
|
|
6762
6775
|
}),
|
|
6763
|
-
this.opts.events.on("agent.run.error", (e) => {
|
|
6764
|
-
const lsid = this.resolveLeaderSessionId();
|
|
6765
|
-
if (lsid && e.sessionId && e.sessionId !== lsid) return;
|
|
6766
|
-
this.activeRuns = Math.max(0, this.activeRuns - 1);
|
|
6767
|
-
}),
|
|
6768
6776
|
this.opts.events.on("iteration.started", (e) => {
|
|
6769
6777
|
const lsid = this.resolveLeaderSessionId();
|
|
6770
6778
|
if (lsid && e.sessionId && e.sessionId !== lsid) return;
|
|
@@ -9780,6 +9788,21 @@ var DirectorStateCheckpoint = class {
|
|
|
9780
9788
|
resume(snapshot) {
|
|
9781
9789
|
this.snapshot = snapshot;
|
|
9782
9790
|
}
|
|
9791
|
+
/**
|
|
9792
|
+
* After resume, pin the live spawn ceiling from the current profile/flag
|
|
9793
|
+
* while preserving `spawnCount` (cumulative used budget). Checkpoint
|
|
9794
|
+
* metadata previously stored a historical `maxSpawns` that can diverge
|
|
9795
|
+
* from the live runtime ceiling — operators need the live value to win.
|
|
9796
|
+
*/
|
|
9797
|
+
applyLiveMaxSpawns(maxSpawns) {
|
|
9798
|
+
if (this.snapshot.maxSpawns === maxSpawns) return;
|
|
9799
|
+
this.snapshot = {
|
|
9800
|
+
...this.snapshot,
|
|
9801
|
+
maxSpawns
|
|
9802
|
+
};
|
|
9803
|
+
this.bumpUpdatedAt();
|
|
9804
|
+
this.schedule();
|
|
9805
|
+
}
|
|
9783
9806
|
current() {
|
|
9784
9807
|
return this.snapshot;
|
|
9785
9808
|
}
|
|
@@ -12609,7 +12632,7 @@ var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not eno
|
|
|
12609
12632
|
var ROUTE_SCOPED_QUOTA_RE = /\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b|\b(?:route|model)(?:[-_\s]+[\w.-]+)?[-_\s]*(?:quota|limit)\b|\b(?:quota|limit).{0,24}\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\b/i;
|
|
12610
12633
|
|
|
12611
12634
|
// src/types/provider.ts
|
|
12612
|
-
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|prompt
|
|
12635
|
+
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|(?:prompt|request|input|messages?).{0,12}too (?:large|long)|exceeds the context|\btokens\b.*exceed|too many tokens|reduce the length|resulted in \d+ tokens|context_length_exceeded/i;
|
|
12613
12636
|
var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
|
|
12614
12637
|
var RATE_LIMIT_EXCEEDED_RE = /rate[-_\s]*limit[-_\s]*exceeded/i;
|
|
12615
12638
|
function classifyProviderError(status, body, message) {
|
|
@@ -12619,7 +12642,7 @@ function classifyProviderError(status, body, message) {
|
|
|
12619
12642
|
if (status === 408) return "timeout";
|
|
12620
12643
|
if (status === 599) return "stream_hang";
|
|
12621
12644
|
if (status === 402 || QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
12622
|
-
if (status === 429 && body?.message && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
12645
|
+
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
12623
12646
|
return "quota_exhausted";
|
|
12624
12647
|
}
|
|
12625
12648
|
if (type === "rate_limit_error" || status === 429) return "rate_limit";
|
|
@@ -12721,7 +12744,7 @@ var ProviderError = class extends WrongStackError {
|
|
|
12721
12744
|
const e = err;
|
|
12722
12745
|
const name = e.name;
|
|
12723
12746
|
if (typeof name !== "string" || !name.endsWith("Error")) return false;
|
|
12724
|
-
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string";
|
|
12747
|
+
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string" && typeof e.describe === "function";
|
|
12725
12748
|
}
|
|
12726
12749
|
constructor(message, status, retryable, providerId, opts = {}) {
|
|
12727
12750
|
const kind = opts.kind ?? classifyProviderError(status, opts.body, message);
|
|
@@ -13523,7 +13546,7 @@ async function spawn2(host, config, priceLookup) {
|
|
|
13523
13546
|
if (host.spawnDepth >= maxSpawnDepth) {
|
|
13524
13547
|
throw new FleetSpawnBudgetError("max_spawn_depth", maxSpawnDepth, host.spawnDepth);
|
|
13525
13548
|
}
|
|
13526
|
-
if (host.spawnCount >= host.maxSpawns) {
|
|
13549
|
+
if (host.spawnCount >= host.maxSpawns && !config.spawnBudgetExempt) {
|
|
13527
13550
|
throw new FleetSpawnBudgetError("max_spawns", host.maxSpawns, host.spawnCount + 1);
|
|
13528
13551
|
}
|
|
13529
13552
|
if (host.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
@@ -13574,7 +13597,9 @@ async function spawn2(host, config, priceLookup) {
|
|
|
13574
13597
|
...Number.isFinite(budget?.remainingSpawns ?? host.maxSpawns - host.spawnCount) ? {
|
|
13575
13598
|
remainingSpawns: Math.max(
|
|
13576
13599
|
0,
|
|
13577
|
-
|
|
13600
|
+
// Exempt spawns don't consume leader budget, so the reported
|
|
13601
|
+
// headroom is not decremented for them.
|
|
13602
|
+
(budget?.remainingSpawns ?? host.maxSpawns - host.spawnCount) - (config.spawnBudgetExempt ? 0 : 1)
|
|
13578
13603
|
)
|
|
13579
13604
|
} : {},
|
|
13580
13605
|
...Number.isFinite(maxFleetTokens) ? { maxTokens: maxFleetTokens } : {},
|
|
@@ -13587,7 +13612,9 @@ async function spawn2(host, config, priceLookup) {
|
|
|
13587
13612
|
if (host.fleetManager) {
|
|
13588
13613
|
host.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
13589
13614
|
} else {
|
|
13590
|
-
|
|
13615
|
+
if (!config.spawnBudgetExempt) {
|
|
13616
|
+
host.spawnCount += 1;
|
|
13617
|
+
}
|
|
13591
13618
|
host.subagentMeta.set(result.subagentId, {
|
|
13592
13619
|
provider: config.provider,
|
|
13593
13620
|
model: config.model
|
|
@@ -14512,8 +14539,17 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
14512
14539
|
durationMs: 0
|
|
14513
14540
|
};
|
|
14514
14541
|
this.completedResults.push(synthetic);
|
|
14542
|
+
this.trimCompletedResults();
|
|
14515
14543
|
this.emit("task.completed", { task, result: synthetic });
|
|
14516
14544
|
}
|
|
14545
|
+
trimCompletedResults() {
|
|
14546
|
+
if (this.completedResults.length > _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS) {
|
|
14547
|
+
this.completedResults.splice(
|
|
14548
|
+
0,
|
|
14549
|
+
this.completedResults.length - _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS
|
|
14550
|
+
);
|
|
14551
|
+
}
|
|
14552
|
+
}
|
|
14517
14553
|
async runDispatched(subagentId, task) {
|
|
14518
14554
|
const subagent = this.subagents.get(subagentId);
|
|
14519
14555
|
if (!subagent) return;
|
|
@@ -14647,12 +14683,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
14647
14683
|
}
|
|
14648
14684
|
recordCompletion(result) {
|
|
14649
14685
|
this.completedResults.push(result);
|
|
14650
|
-
|
|
14651
|
-
this.completedResults.splice(
|
|
14652
|
-
0,
|
|
14653
|
-
this.completedResults.length - _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS
|
|
14654
|
-
);
|
|
14655
|
-
}
|
|
14686
|
+
this.trimCompletedResults();
|
|
14656
14687
|
this.totalIterations += result.iterations;
|
|
14657
14688
|
if (this.inFlight > 0) {
|
|
14658
14689
|
this.inFlight--;
|
|
@@ -19294,6 +19325,7 @@ var Director = class _Director {
|
|
|
19294
19325
|
}
|
|
19295
19326
|
setCheckpointState(snapshot) {
|
|
19296
19327
|
setCheckpointState(this.checkpointHost(), snapshot);
|
|
19328
|
+
this.applyResumeBudget(snapshot);
|
|
19297
19329
|
}
|
|
19298
19330
|
async readSession(subagentId, tail) {
|
|
19299
19331
|
return readDirectorSubagentSession({
|
|
@@ -19345,6 +19377,22 @@ var Director = class _Director {
|
|
|
19345
19377
|
}
|
|
19346
19378
|
resumeFromCheckpoint(snapshot) {
|
|
19347
19379
|
resumeFromCheckpoint(this.checkpointHost(), snapshot);
|
|
19380
|
+
this.applyResumeBudget(snapshot);
|
|
19381
|
+
}
|
|
19382
|
+
/**
|
|
19383
|
+
* After re-attaching checkpoint metadata, pin the live maxSpawns ceiling
|
|
19384
|
+
* (profile/flag/env wins over historical checkpoint metadata). The
|
|
19385
|
+
* historical cumulative spawn counter is deliberately NOT restored — the
|
|
19386
|
+
* lifetime budget is scoped to this director run, so a restarted session
|
|
19387
|
+
* resumes with a fresh budget rather than a possibly-exhausted counter.
|
|
19388
|
+
*/
|
|
19389
|
+
applyResumeBudget(snapshot) {
|
|
19390
|
+
if (this.fleetManager) {
|
|
19391
|
+
this.fleetManager.restoreFromCheckpoint(snapshot);
|
|
19392
|
+
}
|
|
19393
|
+
this.stateCheckpoint?.applyLiveMaxSpawns(
|
|
19394
|
+
Number.isFinite(this.maxSpawns) ? this.maxSpawns : void 0
|
|
19395
|
+
);
|
|
19348
19396
|
}
|
|
19349
19397
|
};
|
|
19350
19398
|
|
|
@@ -19367,6 +19415,14 @@ var FleetManager = class {
|
|
|
19367
19415
|
spawnDepth;
|
|
19368
19416
|
/** Live spawn counter. */
|
|
19369
19417
|
spawnCount = 0;
|
|
19418
|
+
/**
|
|
19419
|
+
* Historical `maxSpawns` from the last restored checkpoint (set for any
|
|
19420
|
+
* finite checkpoint ceiling, matching or not). Surfaced in
|
|
19421
|
+
* `budgetSnapshot()` so operators can see resume reconciliation without
|
|
19422
|
+
* reading the JSON file; `ceilingMismatch` carries the actual mismatch signal.
|
|
19423
|
+
*/
|
|
19424
|
+
checkpointMaxSpawnsAtResume;
|
|
19425
|
+
ceilingMismatchAtResume = false;
|
|
19370
19426
|
stateCheckpoint;
|
|
19371
19427
|
sessionWriter;
|
|
19372
19428
|
manifestTimer = null;
|
|
@@ -19470,11 +19526,11 @@ var FleetManager = class {
|
|
|
19470
19526
|
* which cap was exceeded. Does NOT throw — the caller decides
|
|
19471
19527
|
* how to surface the rejection.
|
|
19472
19528
|
*/
|
|
19473
|
-
canSpawn(
|
|
19529
|
+
canSpawn(config) {
|
|
19474
19530
|
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
19475
19531
|
return { kind: "max_spawn_depth", limit: this.maxSpawnDepth, observed: this.spawnDepth };
|
|
19476
19532
|
}
|
|
19477
|
-
if (this.spawnCount >= this.maxSpawns) {
|
|
19533
|
+
if (!config.spawnBudgetExempt && this.spawnCount >= this.maxSpawns) {
|
|
19478
19534
|
return { kind: "max_spawns", limit: this.maxSpawns, observed: this.spawnCount + 1 };
|
|
19479
19535
|
}
|
|
19480
19536
|
if (this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
@@ -19516,7 +19572,39 @@ var FleetManager = class {
|
|
|
19516
19572
|
remainingTokens: Math.max(0, this.maxFleetTokens - usedTokens),
|
|
19517
19573
|
maxCostUsd: this.maxFleetCostUsd,
|
|
19518
19574
|
usedCostUsd,
|
|
19519
|
-
remainingCostUsd: Math.max(0, this.maxFleetCostUsd - usedCostUsd)
|
|
19575
|
+
remainingCostUsd: Math.max(0, this.maxFleetCostUsd - usedCostUsd),
|
|
19576
|
+
...this.checkpointMaxSpawnsAtResume !== void 0 ? { checkpointMaxSpawns: this.checkpointMaxSpawnsAtResume } : {},
|
|
19577
|
+
...this.ceilingMismatchAtResume ? { ceilingMismatch: true } : {}
|
|
19578
|
+
};
|
|
19579
|
+
}
|
|
19580
|
+
/**
|
|
19581
|
+
* Re-attach the on-disk checkpoint after a resume while keeping the live
|
|
19582
|
+
* `maxSpawns` ceiling from construction (profile / flag / env). The
|
|
19583
|
+
* historical `spawnCount` is deliberately NOT restored: the lifetime
|
|
19584
|
+
* budget is scoped to this director run, so a restarted session gets a
|
|
19585
|
+
* fresh budget instead of inheriting a (possibly exhausted) counter from
|
|
19586
|
+
* before the restart. The checkpoint ceiling is rewritten to the live
|
|
19587
|
+
* value so subsequent writes do not reintroduce a stale limit.
|
|
19588
|
+
*/
|
|
19589
|
+
restoreFromCheckpoint(snapshot) {
|
|
19590
|
+
const checkpointMax = typeof snapshot.maxSpawns === "number" && Number.isFinite(snapshot.maxSpawns) ? snapshot.maxSpawns : void 0;
|
|
19591
|
+
const ceilingMismatch = checkpointMax !== void 0 && checkpointMax !== this.maxSpawns;
|
|
19592
|
+
this.checkpointMaxSpawnsAtResume = checkpointMax;
|
|
19593
|
+
this.ceilingMismatchAtResume = ceilingMismatch;
|
|
19594
|
+
if (this.stateCheckpoint) {
|
|
19595
|
+
if (snapshot.version === 1 && snapshot.directorRunId && snapshot.updatedAt) {
|
|
19596
|
+
this.stateCheckpoint.resume(snapshot);
|
|
19597
|
+
}
|
|
19598
|
+
this.stateCheckpoint.applyLiveMaxSpawns(
|
|
19599
|
+
Number.isFinite(this.maxSpawns) ? this.maxSpawns : void 0
|
|
19600
|
+
);
|
|
19601
|
+
}
|
|
19602
|
+
return {
|
|
19603
|
+
usedSpawns: this.spawnCount,
|
|
19604
|
+
maxSpawns: this.maxSpawns,
|
|
19605
|
+
remainingSpawns: Math.max(0, this.maxSpawns - this.spawnCount),
|
|
19606
|
+
...checkpointMax !== void 0 ? { checkpointMaxSpawns: checkpointMax } : {},
|
|
19607
|
+
ceilingMismatch
|
|
19520
19608
|
};
|
|
19521
19609
|
}
|
|
19522
19610
|
setLeaderContextPressure(tokens) {
|
|
@@ -19570,8 +19658,10 @@ var FleetManager = class {
|
|
|
19570
19658
|
return this._usedNicknames;
|
|
19571
19659
|
}
|
|
19572
19660
|
/**
|
|
19573
|
-
* Records a spawn:
|
|
19574
|
-
*
|
|
19661
|
+
* Records a spawn: stores metadata, updates state checkpoint, and schedules
|
|
19662
|
+
* a debounced manifest write. Increments the lifetime spawn counter UNLESS
|
|
19663
|
+
* the config is `spawnBudgetExempt` (ephemeral Chimera reviewer/cascade
|
|
19664
|
+
* spawns do not consume the leader's budget). Call AFTER the coordinator
|
|
19575
19665
|
* has successfully spawned the subagent.
|
|
19576
19666
|
*
|
|
19577
19667
|
* @param subagentId The subagent's id (from coordinator.spawn result)
|
|
@@ -19580,7 +19670,9 @@ var FleetManager = class {
|
|
|
19580
19670
|
*/
|
|
19581
19671
|
recordSpawn(subagentId, config, priceLookup) {
|
|
19582
19672
|
this.closing = false;
|
|
19583
|
-
|
|
19673
|
+
if (!config.spawnBudgetExempt) {
|
|
19674
|
+
this.spawnCount += 1;
|
|
19675
|
+
}
|
|
19584
19676
|
this.subagentMeta.set(subagentId, {
|
|
19585
19677
|
provider: config.provider,
|
|
19586
19678
|
model: config.model
|
|
@@ -26464,6 +26556,9 @@ var KnowledgeGraph = class _KnowledgeGraph {
|
|
|
26464
26556
|
};
|
|
26465
26557
|
|
|
26466
26558
|
// src/coordination/task-dag.ts
|
|
26559
|
+
function isTerminalDagStatus(status) {
|
|
26560
|
+
return status === "done" || status === "failed" || status === "skipped";
|
|
26561
|
+
}
|
|
26467
26562
|
var TaskDAG = class {
|
|
26468
26563
|
nodes = /* @__PURE__ */ new Map();
|
|
26469
26564
|
handlers = /* @__PURE__ */ new Set();
|
|
@@ -26546,6 +26641,7 @@ var TaskDAG = class {
|
|
|
26546
26641
|
complete(id, result) {
|
|
26547
26642
|
const node = this.nodes.get(id);
|
|
26548
26643
|
if (!node) return;
|
|
26644
|
+
if (isTerminalDagStatus(node.status)) return;
|
|
26549
26645
|
node.status = "done";
|
|
26550
26646
|
node.result = result;
|
|
26551
26647
|
node.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -26571,6 +26667,7 @@ var TaskDAG = class {
|
|
|
26571
26667
|
fail(id, error) {
|
|
26572
26668
|
const node = this.nodes.get(id);
|
|
26573
26669
|
if (!node) return;
|
|
26670
|
+
if (isTerminalDagStatus(node.status)) return;
|
|
26574
26671
|
node.status = "failed";
|
|
26575
26672
|
node.error = error;
|
|
26576
26673
|
node.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -26599,6 +26696,7 @@ var TaskDAG = class {
|
|
|
26599
26696
|
skip(id, reason) {
|
|
26600
26697
|
const node = this.nodes.get(id);
|
|
26601
26698
|
if (!node) return;
|
|
26699
|
+
if (isTerminalDagStatus(node.status)) return;
|
|
26602
26700
|
node.status = "skipped";
|
|
26603
26701
|
node.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26604
26702
|
this.invalidateCache();
|
|
@@ -26755,6 +26853,9 @@ var TaskDAG = class {
|
|
|
26755
26853
|
|
|
26756
26854
|
// src/coordination/task-auctioneer.ts
|
|
26757
26855
|
import { randomUUID as randomUUID18 } from "node:crypto";
|
|
26856
|
+
function isTerminalGoalStatus(status) {
|
|
26857
|
+
return status === "done" || status === "failed";
|
|
26858
|
+
}
|
|
26758
26859
|
var TaskAuctioneer = class {
|
|
26759
26860
|
graph;
|
|
26760
26861
|
fleet;
|
|
@@ -26950,6 +27051,7 @@ Priority: ${goal.priority}`,
|
|
|
26950
27051
|
async complete(taskId, _result) {
|
|
26951
27052
|
const goal = this.graph.get(taskId);
|
|
26952
27053
|
if (!goal) return;
|
|
27054
|
+
if (isTerminalGoalStatus(goal.status)) return;
|
|
26953
27055
|
const agentId = goal.assignee ?? "unknown";
|
|
26954
27056
|
this.agentTaskCount(agentId, -1);
|
|
26955
27057
|
await this.graph.update(taskId, {
|
|
@@ -26990,6 +27092,7 @@ ${_result ?? "No result provided."}`
|
|
|
26990
27092
|
async fail(taskId, error) {
|
|
26991
27093
|
const goal = this.graph.get(taskId);
|
|
26992
27094
|
if (!goal) return;
|
|
27095
|
+
if (isTerminalGoalStatus(goal.status)) return;
|
|
26993
27096
|
const agentId = goal.assignee ?? "unknown";
|
|
26994
27097
|
this.agentTaskCount(agentId, -1);
|
|
26995
27098
|
await this.graph.update(taskId, {
|
|
@@ -186,6 +186,7 @@ export declare class DefaultMultiAgentCoordinator extends EventEmitter implement
|
|
|
186
186
|
* `awaitTasks()` caller. Pushes the result and fires the event directly.
|
|
187
187
|
*/
|
|
188
188
|
private emitPendingAborted;
|
|
189
|
+
private trimCompletedResults;
|
|
189
190
|
private runDispatched;
|
|
190
191
|
private executeWithTimeout;
|
|
191
192
|
private recordCompletion;
|
|
@@ -90,7 +90,55 @@ export interface FallbackModelDeps {
|
|
|
90
90
|
* the fallback chain.
|
|
91
91
|
*/
|
|
92
92
|
statusTracker?: ProviderModelStatusTracker | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* When set, the fallback chain pauses BEFORE attempting any fallback entry
|
|
95
|
+
* and emits `provider.fallback_pending`. The gate waits for a choice
|
|
96
|
+
* (manual pick or auto-countdown) before proceeding. This lets the UI show
|
|
97
|
+
* a modal with a countdown and manual model selection on every fallback hop.
|
|
98
|
+
*
|
|
99
|
+
* Returns the chosen model reference, or `null` to auto-switch to the next
|
|
100
|
+
* candidate (countdown expired or user accepted the default).
|
|
101
|
+
*/
|
|
102
|
+
fallbackGate?: FallbackGateFn | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Seconds the UI counts down before auto-switching. Default: 7.
|
|
105
|
+
*/
|
|
106
|
+
fallbackGateSeconds?: number | undefined;
|
|
93
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Gate function invoked when the fallback chain is about to engage. It should
|
|
110
|
+
* emit `provider.fallback_pending` on the supplied `events` bus — carrying a
|
|
111
|
+
* unique `requestId` and a `timestamp` in the payload — wait for the UI's
|
|
112
|
+
* `provider.fallback_choice` emission echoing that `requestId` (or the
|
|
113
|
+
* countdown), and resolve with the selected model or `null` to accept the
|
|
114
|
+
* default next. See `createFallbackGate` in the CLI wiring for a reference
|
|
115
|
+
* implementation.
|
|
116
|
+
*/
|
|
117
|
+
export type FallbackGateFn = (params: {
|
|
118
|
+
events: EventBus;
|
|
119
|
+
sessionId: string | undefined;
|
|
120
|
+
from: {
|
|
121
|
+
providerId: string;
|
|
122
|
+
model: string;
|
|
123
|
+
};
|
|
124
|
+
status: number;
|
|
125
|
+
candidates: Array<{
|
|
126
|
+
providerId: string;
|
|
127
|
+
model: string;
|
|
128
|
+
}>;
|
|
129
|
+
autoSwitchSeconds: number;
|
|
130
|
+
/**
|
|
131
|
+
* Correlation id owned by the caller (the fallback-model extension). The
|
|
132
|
+
* gate MUST echo it in `provider.fallback_pending` so the UI's choice
|
|
133
|
+
* reply and the eventual `provider.fallback` completion event share the
|
|
134
|
+
* same id — clients use it to match the modal to the right gate when
|
|
135
|
+
* parallel requests fail on the same primary.
|
|
136
|
+
*/
|
|
137
|
+
requestId: string;
|
|
138
|
+
}) => Promise<{
|
|
139
|
+
providerId: string;
|
|
140
|
+
model: string;
|
|
141
|
+
} | null>;
|
|
94
142
|
export declare function fallbackProfileChain(config: Config, profileName: string | undefined): string[];
|
|
95
143
|
export declare function smartDefaultFallbackChain(config: Config): string[];
|
|
96
144
|
/**
|
package/dist/core/index.d.ts
CHANGED
|
@@ -5,11 +5,12 @@ export { ConversationState, type ReadonlyConversationState, type StateChange, ty
|
|
|
5
5
|
export { InputBuilder, type InputBuilderEvent, type InputBuilderOptions, } from './input-builder.js';
|
|
6
6
|
export { buildBtwBlock, consumeBtwNotes, pendingBtwCount, setBtwNote, } from './btw.js';
|
|
7
7
|
export { type ContinuationInput, type ContinuationSource, detectContinueIntent, type ResolvedContinuation, resolveContinuation, } from './continue-intent.js';
|
|
8
|
-
export { createFallbackModelExtension, effectiveFallbackChain, fallbackProfileChain, smartDefaultFallbackChain, } from './fallback-model.js';
|
|
8
|
+
export { createFallbackModelExtension, effectiveFallbackChain, type FallbackGateFn, type FallbackModelDeps, fallbackProfileChain, smartDefaultFallbackChain, } from './fallback-model.js';
|
|
9
9
|
export { FallbackProfileManager } from './fallback-profile-manager.js';
|
|
10
10
|
export { formatModelRef, normalizeModelRef, parseModelRef, type ModelRef, } from './model-ref.js';
|
|
11
11
|
export { DefaultSystemPromptBuilder, type DefaultSystemPromptBuilderOptions, type SystemBlockSource, } from './system-prompt-builder.js';
|
|
12
|
-
export type
|
|
12
|
+
export { type InstructionBundle, type InstructionBundlePaths, loadInstructionBundle, type SystemInstructionVariant, } from './instruction-bundle.js';
|
|
13
|
+
export { type InstructionTemplateContext, renderInstructionLayer, } from './instruction-template.js';
|
|
13
14
|
export { setQueuedMessagesSnapshot } from './queued-messages.js';
|
|
14
15
|
export { runProviderWithRetry } from './provider-runner.js';
|
|
15
16
|
//# sourceMappingURL=index.d.ts.map
|