@wrongstack/core 0.5.5 → 0.5.7
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-bridge-B07AYFBk.d.ts → agent-bridge-BKNiE1VH.d.ts} +1 -1
- package/dist/{compactor-BWhJXxsW.d.ts → compactor-RIPuTtWK.d.ts} +1 -1
- package/dist/{config-BgM0BIpz.d.ts → config-BGGuP_Ar.d.ts} +1 -1
- package/dist/{context-CLZXPPYk.d.ts → context-CDRyrkKQ.d.ts} +7 -0
- package/dist/coordination/index.d.ts +10 -9
- package/dist/coordination/index.js +558 -61
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +19 -18
- package/dist/defaults/index.js +521 -64
- package/dist/defaults/index.js.map +1 -1
- package/dist/director-state-BmYi3DGA.d.ts +108 -0
- package/dist/{events-qnDZbrtb.d.ts → events-DPQKFX7W.d.ts} +1 -1
- package/dist/execution/index.d.ts +18 -11
- package/dist/execution/index.js +19 -3
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{index-De4R4rQ7.d.ts → index-Bf9Bpkdc.d.ts} +452 -9
- package/dist/{index-DPLJw_ZI.d.ts → index-j2WyAyML.d.ts} +20 -5
- package/dist/index.d.ts +108 -27
- package/dist/index.js +819 -90
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +9 -9
- package/dist/{mcp-servers-CSMfaBuL.d.ts → mcp-servers-DBdh3cee.d.ts} +3 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/{multi-agent-Cv8wk47i.d.ts → multi-agent-Cpp7FXUl.d.ts} +11 -3
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-DiCUvEg6.d.ts → path-resolver--g_hKJ7V.d.ts} +2 -2
- package/dist/{director-state-BUxlqkOa.d.ts → plan-templates-Bveo2W8n.d.ts} +37 -76
- package/dist/{provider-runner-3SHqk9zB.d.ts → provider-runner-hl4Il3xS.d.ts} +3 -3
- package/dist/{retry-policy-LLUxJmYY.d.ts → retry-policy-LKS8MHsB.d.ts} +1 -1
- package/dist/sdd/index.d.ts +3 -3
- package/dist/{secret-scrubber-Z_VXuXQT.d.ts → secret-scrubber-BzQR5BiL.d.ts} +1 -1
- package/dist/{secret-scrubber-BhJTNr9v.d.ts → secret-scrubber-CfMdAJ_l.d.ts} +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/{selector-DB2-byKH.d.ts → selector-C7HqnZJU.d.ts} +1 -1
- package/dist/{session-reader-4jxsYLZ8.d.ts → session-reader-CzfRA6Vk.d.ts} +1 -1
- package/dist/storage/index.d.ts +6 -5
- package/dist/storage/index.js +206 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/{system-prompt-DI4Dtc1I.d.ts → system-prompt-Dl2QY1_B.d.ts} +1 -1
- package/dist/{tool-executor-DSvmOBe6.d.ts → tool-executor-DbAFkHdP.d.ts} +4 -4
- package/dist/types/index.d.ts +15 -15
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12,6 +12,13 @@ import { Readable } from 'stream';
|
|
|
12
12
|
import { pipeline } from 'stream/promises';
|
|
13
13
|
import { spawn } from 'child_process';
|
|
14
14
|
|
|
15
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
16
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
17
|
+
}) : x)(function(x) {
|
|
18
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
19
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
20
|
+
});
|
|
21
|
+
|
|
15
22
|
// src/types/errors.ts
|
|
16
23
|
var WrongStackError = class extends Error {
|
|
17
24
|
code;
|
|
@@ -3748,11 +3755,11 @@ function validateAgainstSchema(value, schema) {
|
|
|
3748
3755
|
walk2(value, schema, "", errors);
|
|
3749
3756
|
return { ok: errors.length === 0, errors };
|
|
3750
3757
|
}
|
|
3751
|
-
function walk2(value, schema,
|
|
3758
|
+
function walk2(value, schema, path25, errors) {
|
|
3752
3759
|
if (schema.enum !== void 0) {
|
|
3753
3760
|
if (!schema.enum.some((e) => deepEqual(e, value))) {
|
|
3754
3761
|
errors.push({
|
|
3755
|
-
path:
|
|
3762
|
+
path: path25 || "<root>",
|
|
3756
3763
|
message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
|
|
3757
3764
|
});
|
|
3758
3765
|
return;
|
|
@@ -3761,7 +3768,7 @@ function walk2(value, schema, path24, errors) {
|
|
|
3761
3768
|
if (typeof schema.type === "string") {
|
|
3762
3769
|
if (!checkType(value, schema.type)) {
|
|
3763
3770
|
errors.push({
|
|
3764
|
-
path:
|
|
3771
|
+
path: path25 || "<root>",
|
|
3765
3772
|
message: `expected ${schema.type}, got ${describeType(value)}`
|
|
3766
3773
|
});
|
|
3767
3774
|
return;
|
|
@@ -3771,19 +3778,19 @@ function walk2(value, schema, path24, errors) {
|
|
|
3771
3778
|
const obj = value;
|
|
3772
3779
|
for (const req of schema.required ?? []) {
|
|
3773
3780
|
if (!(req in obj)) {
|
|
3774
|
-
errors.push({ path: joinPath(
|
|
3781
|
+
errors.push({ path: joinPath(path25, req), message: "required property missing" });
|
|
3775
3782
|
}
|
|
3776
3783
|
}
|
|
3777
3784
|
if (schema.properties) {
|
|
3778
3785
|
for (const [key, subSchema] of Object.entries(schema.properties)) {
|
|
3779
3786
|
if (key in obj) {
|
|
3780
|
-
walk2(obj[key], subSchema, joinPath(
|
|
3787
|
+
walk2(obj[key], subSchema, joinPath(path25, key), errors);
|
|
3781
3788
|
}
|
|
3782
3789
|
}
|
|
3783
3790
|
}
|
|
3784
3791
|
}
|
|
3785
3792
|
if (schema.type === "array" && Array.isArray(value) && schema.items) {
|
|
3786
|
-
value.forEach((item, i) => walk2(item, schema.items, `${
|
|
3793
|
+
value.forEach((item, i) => walk2(item, schema.items, `${path25}[${i}]`, errors));
|
|
3787
3794
|
}
|
|
3788
3795
|
}
|
|
3789
3796
|
function checkType(value, type) {
|
|
@@ -5341,9 +5348,149 @@ function matchIndex(plan, idOrIndex) {
|
|
|
5341
5348
|
const lower = idOrIndex.toLowerCase();
|
|
5342
5349
|
return plan.items.findIndex((it) => it.title.toLowerCase().includes(lower));
|
|
5343
5350
|
}
|
|
5351
|
+
function deriveTodosFromPlanItem(plan, idOrIndex, subtasks) {
|
|
5352
|
+
const idx = matchIndex(plan, idOrIndex);
|
|
5353
|
+
if (idx === -1) return null;
|
|
5354
|
+
const item = plan.items[idx];
|
|
5355
|
+
if (!item) return null;
|
|
5356
|
+
let updatedPlan = plan;
|
|
5357
|
+
if (item.status !== "done") {
|
|
5358
|
+
updatedPlan = setPlanItemStatus(plan, idOrIndex, "in_progress");
|
|
5359
|
+
}
|
|
5360
|
+
const todos = [];
|
|
5361
|
+
todos.push({
|
|
5362
|
+
id: `todo_${Date.now()}_plan`,
|
|
5363
|
+
content: item.title,
|
|
5364
|
+
status: "in_progress",
|
|
5365
|
+
activeForm: item.title
|
|
5366
|
+
});
|
|
5367
|
+
if (subtasks && subtasks.length > 0) {
|
|
5368
|
+
for (const st of subtasks) {
|
|
5369
|
+
todos.push({
|
|
5370
|
+
id: `todo_${Date.now()}_${randomUUID().slice(0, 6)}`,
|
|
5371
|
+
content: st,
|
|
5372
|
+
status: "pending"
|
|
5373
|
+
});
|
|
5374
|
+
}
|
|
5375
|
+
}
|
|
5376
|
+
return { plan: updatedPlan, todos };
|
|
5377
|
+
}
|
|
5344
5378
|
function attachPlanCheckpoint(_state, _filePath, _sessionId) {
|
|
5345
5379
|
return () => void 0;
|
|
5346
5380
|
}
|
|
5381
|
+
|
|
5382
|
+
// src/storage/plan-templates.ts
|
|
5383
|
+
var templates = {
|
|
5384
|
+
"new-feature": {
|
|
5385
|
+
name: "new-feature",
|
|
5386
|
+
description: "Standard workflow for adding a new feature",
|
|
5387
|
+
category: "development",
|
|
5388
|
+
items: [
|
|
5389
|
+
{ title: "Write specification / design doc", details: "Define scope, acceptance criteria, edge cases" },
|
|
5390
|
+
{ title: "Set up feature branch", details: "git checkout -b feature/..." },
|
|
5391
|
+
{ title: "Implement core logic", details: "TDD preferred \u2014 write tests first" },
|
|
5392
|
+
{ title: "Add unit tests", details: ">= 80% coverage for new code" },
|
|
5393
|
+
{ title: "Add integration tests", details: "End-to-end happy path + error paths" },
|
|
5394
|
+
{ title: "Update documentation", details: "README, API docs, changelog" },
|
|
5395
|
+
{ title: "Code review", details: "Self-review before requesting review" },
|
|
5396
|
+
{ title: "Merge and deploy", details: "CI green, tag release" }
|
|
5397
|
+
]
|
|
5398
|
+
},
|
|
5399
|
+
"bug-fix": {
|
|
5400
|
+
name: "bug-fix",
|
|
5401
|
+
description: "Systematic approach to fixing bugs",
|
|
5402
|
+
category: "maintenance",
|
|
5403
|
+
items: [
|
|
5404
|
+
{ title: "Reproduce the bug", details: "Minimal reproduction case" },
|
|
5405
|
+
{ title: "Root cause analysis", details: "Trace through logs, debugger" },
|
|
5406
|
+
{ title: "Write failing test", details: "Test must fail before fix" },
|
|
5407
|
+
{ title: "Implement fix", details: "Smallest possible change" },
|
|
5408
|
+
{ title: "Verify fix", details: "Test passes, reproduction no longer fails" },
|
|
5409
|
+
{ title: "Regression test", details: "Ensure no related tests broken" },
|
|
5410
|
+
{ title: "Document in changelog", details: "Brief description + issue link" }
|
|
5411
|
+
]
|
|
5412
|
+
},
|
|
5413
|
+
"refactor": {
|
|
5414
|
+
name: "refactor",
|
|
5415
|
+
description: "Safe refactoring workflow",
|
|
5416
|
+
category: "maintenance",
|
|
5417
|
+
items: [
|
|
5418
|
+
{ title: "Identify refactoring target", details: "Code smell, performance bottleneck, or tech debt" },
|
|
5419
|
+
{ title: "Ensure test coverage", details: "Existing tests must pass before and after" },
|
|
5420
|
+
{ title: "Write characterization tests", details: "Capture current behavior if tests weak" },
|
|
5421
|
+
{ title: "Apply refactoring", details: "Small steps, frequent commits" },
|
|
5422
|
+
{ title: "Run full test suite", details: "All tests must pass" },
|
|
5423
|
+
{ title: "Performance check", details: "Ensure no regression" },
|
|
5424
|
+
{ title: "Code review", details: "Explain the why, not just the what" }
|
|
5425
|
+
]
|
|
5426
|
+
},
|
|
5427
|
+
"release": {
|
|
5428
|
+
name: "release",
|
|
5429
|
+
description: "Preparing a new release",
|
|
5430
|
+
category: "release",
|
|
5431
|
+
items: [
|
|
5432
|
+
{ title: "Version bump", details: "package.json, lockfiles, tags" },
|
|
5433
|
+
{ title: "Update changelog", details: "All changes since last release" },
|
|
5434
|
+
{ title: "Run full test suite", details: "Unit + integration + e2e" },
|
|
5435
|
+
{ title: "Build artifacts", details: "Docker images, bundles, binaries" },
|
|
5436
|
+
{ title: "Staging smoke tests", details: "Deploy to staging, verify" },
|
|
5437
|
+
{ title: "Production deploy", details: "Blue-green or canary" },
|
|
5438
|
+
{ title: "Post-deploy verification", details: "Health checks, error rates" },
|
|
5439
|
+
{ title: "Announce release", details: "Slack, email, GitHub release notes" }
|
|
5440
|
+
]
|
|
5441
|
+
},
|
|
5442
|
+
"security-audit": {
|
|
5443
|
+
name: "security-audit",
|
|
5444
|
+
description: "Security review and hardening",
|
|
5445
|
+
category: "infrastructure",
|
|
5446
|
+
items: [
|
|
5447
|
+
{ title: "Dependency audit", details: "npm audit, Snyk, Dependabot alerts" },
|
|
5448
|
+
{ title: "Secret scan", details: "git-secrets, truffleHog, manual review" },
|
|
5449
|
+
{ title: "Access control review", details: "IAM, roles, least privilege" },
|
|
5450
|
+
{ title: "Input validation audit", details: "SQL injection, XSS, path traversal" },
|
|
5451
|
+
{ title: "Authentication review", details: "Session management, MFA, password policy" },
|
|
5452
|
+
{ title: "Logging and monitoring", details: "PII in logs, audit trails" },
|
|
5453
|
+
{ title: "Incident response plan", details: "Runbooks, contacts, escalation" }
|
|
5454
|
+
]
|
|
5455
|
+
},
|
|
5456
|
+
"onboarding": {
|
|
5457
|
+
name: "onboarding",
|
|
5458
|
+
description: "New developer onboarding checklist",
|
|
5459
|
+
category: "infrastructure",
|
|
5460
|
+
items: [
|
|
5461
|
+
{ title: "Repository access", details: "GitHub/GitLab permissions" },
|
|
5462
|
+
{ title: "Local environment setup", details: "Docker, dependencies, env files" },
|
|
5463
|
+
{ title: "Run tests locally", details: "Verify green suite" },
|
|
5464
|
+
{ title: "Read architecture docs", details: "ADR, README, onboarding guide" },
|
|
5465
|
+
{ title: "First commit", details: "Docs fix or small improvement" },
|
|
5466
|
+
{ title: "Pair programming session", details: "Walk through codebase with buddy" },
|
|
5467
|
+
{ title: "Deploy to staging", details: "Verify CI/CD access" }
|
|
5468
|
+
]
|
|
5469
|
+
}
|
|
5470
|
+
};
|
|
5471
|
+
function listPlanTemplates() {
|
|
5472
|
+
return Object.values(templates);
|
|
5473
|
+
}
|
|
5474
|
+
function getPlanTemplate(name) {
|
|
5475
|
+
return templates[name];
|
|
5476
|
+
}
|
|
5477
|
+
function formatPlanTemplates() {
|
|
5478
|
+
const cats = /* @__PURE__ */ new Map();
|
|
5479
|
+
for (const t2 of Object.values(templates)) {
|
|
5480
|
+
const arr = cats.get(t2.category) ?? [];
|
|
5481
|
+
arr.push(t2);
|
|
5482
|
+
cats.set(t2.category, arr);
|
|
5483
|
+
}
|
|
5484
|
+
const lines = ["Available plan templates:"];
|
|
5485
|
+
for (const [cat, items] of cats) {
|
|
5486
|
+
lines.push(`
|
|
5487
|
+
${cat}:`);
|
|
5488
|
+
for (const t2 of items) {
|
|
5489
|
+
lines.push(` ${t2.name.padEnd(18)} \u2014 ${t2.description}`);
|
|
5490
|
+
}
|
|
5491
|
+
}
|
|
5492
|
+
return lines.join("\n");
|
|
5493
|
+
}
|
|
5347
5494
|
async function loadDirectorState(filePath) {
|
|
5348
5495
|
let raw;
|
|
5349
5496
|
try {
|
|
@@ -5359,15 +5506,48 @@ async function loadDirectorState(filePath) {
|
|
|
5359
5506
|
return null;
|
|
5360
5507
|
}
|
|
5361
5508
|
}
|
|
5509
|
+
async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
5510
|
+
let existing;
|
|
5511
|
+
try {
|
|
5512
|
+
existing = await fsp2.readFile(lockPath, "utf8");
|
|
5513
|
+
} catch {
|
|
5514
|
+
}
|
|
5515
|
+
if (existing) {
|
|
5516
|
+
try {
|
|
5517
|
+
const lock2 = JSON.parse(existing);
|
|
5518
|
+
try {
|
|
5519
|
+
process.kill(lock2.pid, 0);
|
|
5520
|
+
return false;
|
|
5521
|
+
} catch {
|
|
5522
|
+
}
|
|
5523
|
+
} catch {
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
const lock = {
|
|
5527
|
+
pid: processId,
|
|
5528
|
+
hostname: __require("os").hostname(),
|
|
5529
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5530
|
+
};
|
|
5531
|
+
await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
|
|
5532
|
+
return true;
|
|
5533
|
+
}
|
|
5534
|
+
async function releaseDirectorStateLock(lockPath) {
|
|
5535
|
+
try {
|
|
5536
|
+
await fsp2.unlink(lockPath);
|
|
5537
|
+
} catch {
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5362
5540
|
var DirectorStateCheckpoint = class {
|
|
5363
5541
|
snapshot;
|
|
5364
5542
|
filePath;
|
|
5543
|
+
lockPath;
|
|
5365
5544
|
timer = null;
|
|
5366
5545
|
debounceMs;
|
|
5367
5546
|
writing = false;
|
|
5368
5547
|
rewriteRequested = false;
|
|
5369
5548
|
constructor(filePath, init, debounceMs = 250) {
|
|
5370
5549
|
this.filePath = filePath;
|
|
5550
|
+
this.lockPath = `${filePath}.lock`;
|
|
5371
5551
|
this.debounceMs = debounceMs;
|
|
5372
5552
|
this.snapshot = {
|
|
5373
5553
|
version: 1,
|
|
@@ -5377,10 +5557,36 @@ var DirectorStateCheckpoint = class {
|
|
|
5377
5557
|
maxSpawns: init.maxSpawns,
|
|
5378
5558
|
spawnDepth: init.spawnDepth,
|
|
5379
5559
|
maxSpawnDepth: init.maxSpawnDepth,
|
|
5560
|
+
directorBudget: init.directorBudget,
|
|
5380
5561
|
subagents: [],
|
|
5381
5562
|
tasks: []
|
|
5382
5563
|
};
|
|
5383
5564
|
}
|
|
5565
|
+
/**
|
|
5566
|
+
* Attempt to acquire the lock for this checkpoint. Call this before
|
|
5567
|
+
* resuming a crashed director run. If it returns false, another
|
|
5568
|
+
* director process is still running this fleet — do not resume.
|
|
5569
|
+
*/
|
|
5570
|
+
async acquireLock() {
|
|
5571
|
+
return acquireDirectorStateLock(this.lockPath);
|
|
5572
|
+
}
|
|
5573
|
+
/**
|
|
5574
|
+
* Release the lock on graceful shutdown. Call `flush()` first to ensure
|
|
5575
|
+
* the final checkpoint state is on disk before removing the lock.
|
|
5576
|
+
* Without this, the next resume will see a stale-lock and refuse.
|
|
5577
|
+
*/
|
|
5578
|
+
async releaseLock() {
|
|
5579
|
+
return releaseDirectorStateLock(this.lockPath);
|
|
5580
|
+
}
|
|
5581
|
+
/**
|
|
5582
|
+
* Resume from a snapshot previously loaded via `loadDirectorState()`.
|
|
5583
|
+
* Use this when `--resume <runId>` is triggered — the snapshot has
|
|
5584
|
+
* the full fleet state (subagents, tasks) from before the crash; the
|
|
5585
|
+
* checkpoint continues from there.
|
|
5586
|
+
*/
|
|
5587
|
+
resume(snapshot) {
|
|
5588
|
+
this.snapshot = snapshot;
|
|
5589
|
+
}
|
|
5384
5590
|
current() {
|
|
5385
5591
|
return this.snapshot;
|
|
5386
5592
|
}
|
|
@@ -6864,6 +7070,14 @@ var DoneConditionChecker = class {
|
|
|
6864
7070
|
};
|
|
6865
7071
|
}
|
|
6866
7072
|
break;
|
|
7073
|
+
case "directive":
|
|
7074
|
+
if (this.condition.maxIterations && state.iterations >= this.condition.maxIterations) {
|
|
7075
|
+
return { done: true, reason: `max iterations (${this.condition.maxIterations}) reached`, ...state };
|
|
7076
|
+
}
|
|
7077
|
+
if (this.condition.maxToolCalls && state.toolCalls >= this.condition.maxToolCalls) {
|
|
7078
|
+
return { done: true, reason: `max tool calls (${this.condition.maxToolCalls}) reached`, ...state };
|
|
7079
|
+
}
|
|
7080
|
+
break;
|
|
6867
7081
|
}
|
|
6868
7082
|
return { done: false, iterations: state.iterations, toolCalls: state.toolCalls };
|
|
6869
7083
|
}
|
|
@@ -6883,10 +7097,16 @@ var AutonomousRunner = class {
|
|
|
6883
7097
|
const offToolExecuted = this.opts.agent.events?.on?.("tool.executed", () => {
|
|
6884
7098
|
this.toolCalls++;
|
|
6885
7099
|
});
|
|
7100
|
+
const offIterationCompleted = this.opts.agent.events?.on?.("iteration.completed", () => {
|
|
7101
|
+
if (this.opts.enableAutonomousContinue && this.opts.doneCondition.type === "directive") {
|
|
7102
|
+
this.iterations++;
|
|
7103
|
+
}
|
|
7104
|
+
});
|
|
6886
7105
|
try {
|
|
6887
7106
|
return await this.runLoop();
|
|
6888
7107
|
} finally {
|
|
6889
7108
|
offToolExecuted?.();
|
|
7109
|
+
offIterationCompleted?.();
|
|
6890
7110
|
}
|
|
6891
7111
|
}
|
|
6892
7112
|
async runLoop() {
|
|
@@ -6910,12 +7130,14 @@ var AutonomousRunner = class {
|
|
|
6910
7130
|
const ctrl = new AbortController();
|
|
6911
7131
|
const timeout = setTimeout(() => ctrl.abort(), this.opts.iterationTimeoutMs ?? 3e4);
|
|
6912
7132
|
try {
|
|
7133
|
+
const isDirectiveMode = this.opts.doneCondition.type === "directive" && this.opts.enableAutonomousContinue;
|
|
6913
7134
|
const result = await this.opts.agent.run("", {
|
|
6914
7135
|
signal: ctrl.signal,
|
|
6915
|
-
maxIterations: 1,
|
|
6916
|
-
executionStrategy: "sequential"
|
|
7136
|
+
maxIterations: isDirectiveMode ? this.opts.doneCondition.maxIterations ?? 100 : 1,
|
|
7137
|
+
executionStrategy: "sequential",
|
|
7138
|
+
autonomousContinue: isDirectiveMode ? true : void 0
|
|
6917
7139
|
});
|
|
6918
|
-
this.iterations++;
|
|
7140
|
+
if (!isDirectiveMode) this.iterations++;
|
|
6919
7141
|
if (result.status === "done") {
|
|
6920
7142
|
this.lastOutput = result.finalText;
|
|
6921
7143
|
}
|
|
@@ -8112,7 +8334,10 @@ function makeSpawnTool(director, roster) {
|
|
|
8112
8334
|
const subagentId = await director.spawn(cfg);
|
|
8113
8335
|
return { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name };
|
|
8114
8336
|
} catch (err) {
|
|
8115
|
-
if (err instanceof
|
|
8337
|
+
if (err instanceof FleetSpawnBudgetError) {
|
|
8338
|
+
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
8339
|
+
}
|
|
8340
|
+
if (err instanceof FleetCostCapError) {
|
|
8116
8341
|
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
8117
8342
|
}
|
|
8118
8343
|
return { error: err instanceof Error ? err.message : String(err) };
|
|
@@ -8252,9 +8477,68 @@ function makeFleetUsageTool(director) {
|
|
|
8252
8477
|
}
|
|
8253
8478
|
};
|
|
8254
8479
|
}
|
|
8480
|
+
function makeFleetSessionTool(director) {
|
|
8481
|
+
return {
|
|
8482
|
+
name: "fleet_session",
|
|
8483
|
+
description: "Read a subagent's JSONL transcript and extract its last assistant text, stop reason, and tool-use count. Use this to see what a running or timed-out subagent actually produced.",
|
|
8484
|
+
permission: "auto",
|
|
8485
|
+
mutating: false,
|
|
8486
|
+
inputSchema: {
|
|
8487
|
+
type: "object",
|
|
8488
|
+
properties: {
|
|
8489
|
+
subagentId: { type: "string", description: "Subagent id to read the transcript of." },
|
|
8490
|
+
/** Number of trailing lines to return (last N JSONL lines). Default: all. */
|
|
8491
|
+
tail: { type: "number", description: "Number of trailing JSONL lines to return. Omit for the full transcript." }
|
|
8492
|
+
},
|
|
8493
|
+
required: ["subagentId"]
|
|
8494
|
+
},
|
|
8495
|
+
async execute(input) {
|
|
8496
|
+
const i = input;
|
|
8497
|
+
const result = await director.readSession(i.subagentId, i.tail);
|
|
8498
|
+
if (!result) {
|
|
8499
|
+
return {
|
|
8500
|
+
error: `fleet_session: transcript unavailable for "${i.subagentId}". Is sessionsRoot configured?`
|
|
8501
|
+
};
|
|
8502
|
+
}
|
|
8503
|
+
return result;
|
|
8504
|
+
}
|
|
8505
|
+
};
|
|
8506
|
+
}
|
|
8507
|
+
function makeFleetHealthTool(director) {
|
|
8508
|
+
return {
|
|
8509
|
+
name: "fleet_health",
|
|
8510
|
+
description: "Per-subagent health report: budget pressure (pct of limits consumed), last activity timestamp, and current status. Use to decide whether to assign more work to a subagent or spawn a fresh one.",
|
|
8511
|
+
permission: "auto",
|
|
8512
|
+
mutating: false,
|
|
8513
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
8514
|
+
async execute() {
|
|
8515
|
+
const status = director.status();
|
|
8516
|
+
const snapshot = director.snapshot();
|
|
8517
|
+
const subagents = status.subagents ?? [];
|
|
8518
|
+
const perSubagent = snapshot.perSubagent ?? {};
|
|
8519
|
+
return {
|
|
8520
|
+
subagents: subagents.map((s) => {
|
|
8521
|
+
const usage = perSubagent[s.id];
|
|
8522
|
+
return {
|
|
8523
|
+
id: s.id,
|
|
8524
|
+
status: s.status,
|
|
8525
|
+
lastEventAt: usage?.lastEventAt,
|
|
8526
|
+
// Budget pressure: fraction of each limit consumed if we have it.
|
|
8527
|
+
// BudgetWarning events carry used/limit ratios; surface them here.
|
|
8528
|
+
budgetPressure: {
|
|
8529
|
+
iterations: usage?.iterations,
|
|
8530
|
+
toolCalls: usage?.toolCalls,
|
|
8531
|
+
costUsd: usage?.cost
|
|
8532
|
+
}
|
|
8533
|
+
};
|
|
8534
|
+
})
|
|
8535
|
+
};
|
|
8536
|
+
}
|
|
8537
|
+
};
|
|
8538
|
+
}
|
|
8255
8539
|
|
|
8256
8540
|
// src/coordination/director.ts
|
|
8257
|
-
var
|
|
8541
|
+
var FleetSpawnBudgetError = class extends Error {
|
|
8258
8542
|
kind;
|
|
8259
8543
|
limit;
|
|
8260
8544
|
observed;
|
|
@@ -8262,16 +8546,48 @@ var DirectorBudgetError = class extends Error {
|
|
|
8262
8546
|
super(
|
|
8263
8547
|
kind === "max_spawns" ? `Director spawn budget exceeded: tried to spawn #${observed} but maxSpawns is ${limit}` : `Director spawn depth budget exceeded: this director is at depth ${observed} and maxSpawnDepth is ${limit}`
|
|
8264
8548
|
);
|
|
8265
|
-
this.name = "
|
|
8549
|
+
this.name = "FleetSpawnBudgetError";
|
|
8266
8550
|
this.kind = kind;
|
|
8267
8551
|
this.limit = limit;
|
|
8268
8552
|
this.observed = observed;
|
|
8269
8553
|
}
|
|
8270
8554
|
};
|
|
8555
|
+
var FleetCostCapError = class extends Error {
|
|
8556
|
+
kind;
|
|
8557
|
+
limit;
|
|
8558
|
+
observed;
|
|
8559
|
+
constructor(limit, observed) {
|
|
8560
|
+
super(
|
|
8561
|
+
`Director cost cap exceeded: total fleet spend ${observed.toFixed(4)} exceeds maxCostUsd ${limit.toFixed(4)}`
|
|
8562
|
+
);
|
|
8563
|
+
this.name = "FleetCostCapError";
|
|
8564
|
+
this.kind = "max_cost_usd";
|
|
8565
|
+
this.limit = limit;
|
|
8566
|
+
this.observed = observed;
|
|
8567
|
+
}
|
|
8568
|
+
};
|
|
8271
8569
|
var Director = class {
|
|
8570
|
+
/** Alias for the ICoordinator contract. `id` is retained for backward compatibility. */
|
|
8571
|
+
get coordinatorId() {
|
|
8572
|
+
return this.id;
|
|
8573
|
+
}
|
|
8272
8574
|
id;
|
|
8575
|
+
/**
|
|
8576
|
+
* The fleet event bus. Backed by `fleetManager?.fleet` when a FleetManager
|
|
8577
|
+
* is injected; otherwise own FleetBus instance (preserves existing behavior).
|
|
8578
|
+
*/
|
|
8273
8579
|
fleet;
|
|
8580
|
+
/**
|
|
8581
|
+
* Usage rollup. Backed by `fleetManager?.usage` when a FleetManager is
|
|
8582
|
+
* injected; otherwise own FleetUsageAggregator.
|
|
8583
|
+
*/
|
|
8274
8584
|
usage;
|
|
8585
|
+
/**
|
|
8586
|
+
* Optional fleet-level policy container. When provided the Director
|
|
8587
|
+
* delegates spawn budgeting, manifest entries, and checkpointing to it
|
|
8588
|
+
* instead of managing those internally. All other behavior is unchanged.
|
|
8589
|
+
*/
|
|
8590
|
+
fleetManager;
|
|
8275
8591
|
/**
|
|
8276
8592
|
* Director-side bridge endpoint. Subagents are wired to the same
|
|
8277
8593
|
* in-memory transport so the director can `ask()` them synchronously
|
|
@@ -8322,6 +8638,14 @@ var Director = class {
|
|
|
8322
8638
|
/** Debounce timer for periodic manifest writes. */
|
|
8323
8639
|
manifestTimer = null;
|
|
8324
8640
|
manifestDebounceMs;
|
|
8641
|
+
/** Fleet-wide cost cap (entire fleet total, distinct from SubagentBudget limits). Infinity means no cap. */
|
|
8642
|
+
maxFleetCostUsd;
|
|
8643
|
+
/** Max auto-extensions per subagent per budget kind before denying. */
|
|
8644
|
+
maxBudgetExtensions;
|
|
8645
|
+
/** Sessions root for direct subagent JSONL reads (fleet_session tool). */
|
|
8646
|
+
sessionsRoot;
|
|
8647
|
+
/** Director run id for JSONL path resolution. */
|
|
8648
|
+
directorRunId;
|
|
8325
8649
|
/** Resolves task descriptions back from `assign()` so completion events
|
|
8326
8650
|
* can also carry a human-readable title. */
|
|
8327
8651
|
taskDescriptions = /* @__PURE__ */ new Map();
|
|
@@ -8348,12 +8672,18 @@ var Director = class {
|
|
|
8348
8672
|
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
8349
8673
|
this.sessionWriter = opts.sessionWriter ?? null;
|
|
8350
8674
|
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
8675
|
+
this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
|
|
8676
|
+
this.maxBudgetExtensions = opts.maxBudgetExtensions ?? 2;
|
|
8677
|
+
this.sessionsRoot = opts.sessionsRoot;
|
|
8678
|
+
this.directorRunId = opts.directorRunId ?? this.id;
|
|
8351
8679
|
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(opts.stateCheckpointPath, {
|
|
8352
8680
|
directorRunId: this.id,
|
|
8353
8681
|
maxSpawns: opts.maxSpawns,
|
|
8354
8682
|
spawnDepth: this.spawnDepth,
|
|
8355
|
-
maxSpawnDepth: this.maxSpawnDepth
|
|
8356
|
-
|
|
8683
|
+
maxSpawnDepth: this.maxSpawnDepth,
|
|
8684
|
+
directorBudget: opts.directorBudget
|
|
8685
|
+
}, opts.checkpointDebounceMs ?? 250) : null;
|
|
8686
|
+
this.fleetManager = opts.fleetManager;
|
|
8357
8687
|
if (this.sharedScratchpadPath) {
|
|
8358
8688
|
void fsp2.mkdir(this.sharedScratchpadPath, { recursive: true }).catch(() => void 0);
|
|
8359
8689
|
}
|
|
@@ -8362,12 +8692,17 @@ var Director = class {
|
|
|
8362
8692
|
{ agentId: this.id, coordinatorId: this.id },
|
|
8363
8693
|
this.transport
|
|
8364
8694
|
);
|
|
8365
|
-
this.
|
|
8366
|
-
|
|
8367
|
-
this.
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8695
|
+
if (this.fleetManager) {
|
|
8696
|
+
this.fleet = this.fleetManager.fleet;
|
|
8697
|
+
this.usage = this.fleetManager.usage;
|
|
8698
|
+
} else {
|
|
8699
|
+
this.fleet = new FleetBus();
|
|
8700
|
+
this.usage = new FleetUsageAggregator(
|
|
8701
|
+
this.fleet,
|
|
8702
|
+
(id) => this.priceLookups.get(id),
|
|
8703
|
+
(id) => this.subagentMeta.get(id)
|
|
8704
|
+
);
|
|
8705
|
+
}
|
|
8371
8706
|
this.coordinator = new DefaultMultiAgentCoordinator(
|
|
8372
8707
|
{ ...opts.config, coordinatorId: this.id },
|
|
8373
8708
|
{ runner: opts.runner }
|
|
@@ -8414,7 +8749,7 @@ var Director = class {
|
|
|
8414
8749
|
const payload = e.payload;
|
|
8415
8750
|
const guardKey = `${e.subagentId}:${payload.kind}`;
|
|
8416
8751
|
const prior = extendCounts.get(guardKey) ?? 0;
|
|
8417
|
-
if (prior >=
|
|
8752
|
+
if (prior >= this.maxBudgetExtensions) {
|
|
8418
8753
|
payload.deny();
|
|
8419
8754
|
extendCounts.delete(guardKey);
|
|
8420
8755
|
return;
|
|
@@ -8469,52 +8804,73 @@ var Director = class {
|
|
|
8469
8804
|
* it the `cost` column in `usage.snapshot()` stays at 0.
|
|
8470
8805
|
*/
|
|
8471
8806
|
async spawn(config, priceLookup) {
|
|
8472
|
-
if (this.
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8807
|
+
if (this.fleetManager) {
|
|
8808
|
+
const rejection = this.fleetManager.canSpawn(config);
|
|
8809
|
+
if (rejection) {
|
|
8810
|
+
if (rejection.kind === "max_spawn_depth") throw new FleetSpawnBudgetError("max_spawn_depth", rejection.limit, rejection.observed);
|
|
8811
|
+
if (rejection.kind === "max_spawns") throw new FleetSpawnBudgetError("max_spawns", rejection.limit, rejection.observed);
|
|
8812
|
+
if (rejection.kind === "max_cost_usd") throw new FleetCostCapError(rejection.limit, rejection.observed);
|
|
8813
|
+
}
|
|
8814
|
+
} else {
|
|
8815
|
+
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
8816
|
+
throw new FleetSpawnBudgetError("max_spawn_depth", this.maxSpawnDepth, this.spawnDepth);
|
|
8817
|
+
}
|
|
8818
|
+
if (this.spawnCount >= this.maxSpawns) {
|
|
8819
|
+
throw new FleetSpawnBudgetError("max_spawns", this.maxSpawns, this.spawnCount + 1);
|
|
8820
|
+
}
|
|
8821
|
+
if (this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
8822
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
8823
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
8824
|
+
throw new FleetCostCapError(this.maxFleetCostUsd, totalCost);
|
|
8825
|
+
}
|
|
8826
|
+
}
|
|
8477
8827
|
}
|
|
8478
8828
|
const result = await this.coordinator.spawn(config);
|
|
8479
|
-
this.
|
|
8480
|
-
|
|
8481
|
-
|
|
8482
|
-
|
|
8483
|
-
|
|
8484
|
-
|
|
8829
|
+
if (this.fleetManager) {
|
|
8830
|
+
this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
8831
|
+
} else {
|
|
8832
|
+
this.spawnCount += 1;
|
|
8833
|
+
this.subagentMeta.set(result.subagentId, {
|
|
8834
|
+
provider: config.provider,
|
|
8835
|
+
model: config.model
|
|
8836
|
+
});
|
|
8837
|
+
if (priceLookup) this.priceLookups.set(result.subagentId, priceLookup);
|
|
8838
|
+
}
|
|
8485
8839
|
const subagentBridge = new InMemoryAgentBridge(
|
|
8486
8840
|
{ agentId: result.subagentId, coordinatorId: this.id },
|
|
8487
8841
|
this.transport
|
|
8488
8842
|
);
|
|
8489
8843
|
this.coordinator.setSubagentBridge(result.subagentId, subagentBridge);
|
|
8490
8844
|
this.subagentBridges.set(result.subagentId, subagentBridge);
|
|
8491
|
-
this.
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
role: config.role,
|
|
8495
|
-
provider: config.provider,
|
|
8496
|
-
model: config.model,
|
|
8497
|
-
taskIds: []
|
|
8498
|
-
});
|
|
8499
|
-
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8500
|
-
this.stateCheckpoint?.recordSpawn(
|
|
8501
|
-
{
|
|
8502
|
-
id: result.subagentId,
|
|
8845
|
+
if (!this.fleetManager) {
|
|
8846
|
+
this.manifestEntries.set(result.subagentId, {
|
|
8847
|
+
subagentId: result.subagentId,
|
|
8503
8848
|
name: config.name,
|
|
8504
8849
|
role: config.role,
|
|
8505
8850
|
provider: config.provider,
|
|
8506
8851
|
model: config.model,
|
|
8507
|
-
|
|
8508
|
-
}
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8852
|
+
taskIds: []
|
|
8853
|
+
});
|
|
8854
|
+
const spawnedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8855
|
+
this.stateCheckpoint?.recordSpawn(
|
|
8856
|
+
{
|
|
8857
|
+
id: result.subagentId,
|
|
8858
|
+
name: config.name,
|
|
8859
|
+
role: config.role,
|
|
8860
|
+
provider: config.provider,
|
|
8861
|
+
model: config.model,
|
|
8862
|
+
spawnedAt
|
|
8863
|
+
},
|
|
8864
|
+
this.spawnCount
|
|
8865
|
+
);
|
|
8866
|
+
void this.appendSessionEvent({
|
|
8867
|
+
type: "agent_spawned",
|
|
8868
|
+
ts: spawnedAt,
|
|
8869
|
+
agentId: result.subagentId,
|
|
8870
|
+
role: config.role ?? config.name
|
|
8871
|
+
});
|
|
8872
|
+
this.scheduleManifest();
|
|
8873
|
+
}
|
|
8518
8874
|
return result.subagentId;
|
|
8519
8875
|
}
|
|
8520
8876
|
/**
|
|
@@ -8654,6 +9010,7 @@ var Director = class {
|
|
|
8654
9010
|
if (this.stateCheckpoint) {
|
|
8655
9011
|
this.stateCheckpoint.setUsage(this.usage.snapshot());
|
|
8656
9012
|
await this.stateCheckpoint.flush().catch((err) => this.logShutdownError("state_checkpoint_flush", err));
|
|
9013
|
+
await this.stateCheckpoint.releaseLock().catch((err) => this.logShutdownError("state_checkpoint_lock_release", err));
|
|
8657
9014
|
}
|
|
8658
9015
|
}
|
|
8659
9016
|
/**
|
|
@@ -8680,8 +9037,12 @@ var Director = class {
|
|
|
8680
9037
|
async assign(task) {
|
|
8681
9038
|
const taskWithId = task.id ? task : { ...task, id: randomUUID() };
|
|
8682
9039
|
if (task.subagentId) {
|
|
8683
|
-
|
|
8684
|
-
|
|
9040
|
+
if (this.fleetManager) {
|
|
9041
|
+
this.fleetManager.addTaskToSubagent(task.subagentId, taskWithId.id);
|
|
9042
|
+
} else {
|
|
9043
|
+
const entry = this.manifestEntries.get(task.subagentId);
|
|
9044
|
+
if (entry) entry.taskIds.push(taskWithId.id);
|
|
9045
|
+
}
|
|
8685
9046
|
}
|
|
8686
9047
|
await this.coordinator.assign(taskWithId);
|
|
8687
9048
|
this.taskDescriptions.set(taskWithId.id, taskWithId.description);
|
|
@@ -8757,6 +9118,58 @@ var Director = class {
|
|
|
8757
9118
|
completedResults() {
|
|
8758
9119
|
return Array.from(this.completed.values());
|
|
8759
9120
|
}
|
|
9121
|
+
/**
|
|
9122
|
+
* Inject a previously-saved checkpoint snapshot. Call this right after
|
|
9123
|
+
* constructing a Director during a `--resume` run so the in-memory state
|
|
9124
|
+
* (subagents, tasks, waiters) reflects the pre-crash reality instead of
|
|
9125
|
+
* starting from a blank slate. The director then resumes from there —
|
|
9126
|
+
* completing any in-flight tasks and ignoring tasks that already reached
|
|
9127
|
+
* a terminal state in the prior run.
|
|
9128
|
+
*/
|
|
9129
|
+
setCheckpointState(snapshot) {
|
|
9130
|
+
this.stateCheckpoint?.resume(snapshot);
|
|
9131
|
+
}
|
|
9132
|
+
/**
|
|
9133
|
+
* Read a subagent's JSONL transcript directly from disk (no bridge
|
|
9134
|
+
* round-trip needed). Returns the last assistant text, stop reason,
|
|
9135
|
+
* tool-use count, and line count — or null if the file is unavailable.
|
|
9136
|
+
* Requires `sessionsRoot` to be set on construction.
|
|
9137
|
+
*/
|
|
9138
|
+
async readSession(subagentId, tail) {
|
|
9139
|
+
if (!this.sessionsRoot) return null;
|
|
9140
|
+
const filePath = path6.join(this.sessionsRoot, this.directorRunId, `${subagentId}.jsonl`);
|
|
9141
|
+
let raw;
|
|
9142
|
+
try {
|
|
9143
|
+
raw = await fsp2.readFile(filePath, "utf8");
|
|
9144
|
+
} catch {
|
|
9145
|
+
return null;
|
|
9146
|
+
}
|
|
9147
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
9148
|
+
const targetLines = tail ? lines.slice(-tail) : lines;
|
|
9149
|
+
let lastAssistantText;
|
|
9150
|
+
let lastStopReason;
|
|
9151
|
+
let toolUses = 0;
|
|
9152
|
+
for (const line of targetLines) {
|
|
9153
|
+
try {
|
|
9154
|
+
const ev = JSON.parse(line);
|
|
9155
|
+
if (ev.type === "assistant" && typeof ev.text === "string") {
|
|
9156
|
+
lastAssistantText = ev.text;
|
|
9157
|
+
} else if (ev.type === "stop" && ev.stopReason) {
|
|
9158
|
+
lastStopReason = ev.stopReason;
|
|
9159
|
+
} else if (ev.type === "tool_use") {
|
|
9160
|
+
toolUses++;
|
|
9161
|
+
}
|
|
9162
|
+
} catch {
|
|
9163
|
+
}
|
|
9164
|
+
}
|
|
9165
|
+
return {
|
|
9166
|
+
lastAssistantText,
|
|
9167
|
+
lastStopReason,
|
|
9168
|
+
toolUsesObserved: toolUses,
|
|
9169
|
+
events: targetLines.length,
|
|
9170
|
+
path: filePath
|
|
9171
|
+
};
|
|
9172
|
+
}
|
|
8760
9173
|
snapshot() {
|
|
8761
9174
|
return this.usage.snapshot();
|
|
8762
9175
|
}
|
|
@@ -8827,18 +9240,37 @@ var Director = class {
|
|
|
8827
9240
|
* still permission-checked normally.
|
|
8828
9241
|
*/
|
|
8829
9242
|
tools(roster) {
|
|
9243
|
+
const effectiveRoster = roster ?? this.roster;
|
|
8830
9244
|
const t2 = [
|
|
8831
|
-
makeSpawnTool(this,
|
|
9245
|
+
makeSpawnTool(this, effectiveRoster),
|
|
8832
9246
|
makeAssignTool(this),
|
|
8833
9247
|
makeAwaitTasksTool(this),
|
|
8834
9248
|
makeAskTool(this),
|
|
8835
9249
|
makeRollUpTool(this),
|
|
8836
9250
|
makeTerminateTool(this),
|
|
8837
9251
|
makeFleetStatusTool(this),
|
|
8838
|
-
makeFleetUsageTool(this)
|
|
9252
|
+
makeFleetUsageTool(this),
|
|
9253
|
+
makeFleetSessionTool(this),
|
|
9254
|
+
makeFleetHealthTool(this)
|
|
8839
9255
|
];
|
|
8840
9256
|
return t2;
|
|
8841
9257
|
}
|
|
9258
|
+
/**
|
|
9259
|
+
* Attempt to acquire the checkpoint lock. Must be called before
|
|
9260
|
+
* resuming — if another director process is alive, this returns
|
|
9261
|
+
* false and the caller should not proceed with the resume.
|
|
9262
|
+
*/
|
|
9263
|
+
async acquireCheckpointLock() {
|
|
9264
|
+
return this.stateCheckpoint ? this.stateCheckpoint.acquireLock() : true;
|
|
9265
|
+
}
|
|
9266
|
+
/**
|
|
9267
|
+
* Resume from a prior checkpoint snapshot (loaded via
|
|
9268
|
+
* `loadDirectorState()`). Re-attach to the fleet mid-flight so
|
|
9269
|
+
* subsequent spawn/assign calls update the checkpoint normally.
|
|
9270
|
+
*/
|
|
9271
|
+
resumeFromCheckpoint(snapshot) {
|
|
9272
|
+
this.stateCheckpoint?.resume(snapshot);
|
|
9273
|
+
}
|
|
8842
9274
|
};
|
|
8843
9275
|
function createDelegateTool(opts) {
|
|
8844
9276
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 4 * 60 * 60 * 1e3;
|
|
@@ -8944,7 +9376,7 @@ function createDelegateTool(opts) {
|
|
|
8944
9376
|
if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
|
|
8945
9377
|
cfg.maxToolCalls = i.maxToolCalls;
|
|
8946
9378
|
}
|
|
8947
|
-
const SUBAGENT_TIMEOUT_BUFFER_MS = 3e4;
|
|
9379
|
+
const SUBAGENT_TIMEOUT_BUFFER_MS = opts.subagentTimeoutBufferMs ?? 3e4;
|
|
8948
9380
|
const desiredSubTimeout = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
8949
9381
|
if (!cfg.timeoutMs || cfg.timeoutMs > desiredSubTimeout) {
|
|
8950
9382
|
cfg.timeoutMs = desiredSubTimeout;
|
|
@@ -8996,7 +9428,7 @@ function createDelegateTool(opts) {
|
|
|
8996
9428
|
toolCalls: result.toolCalls,
|
|
8997
9429
|
durationMs: result.durationMs,
|
|
8998
9430
|
...partial ? { partial } : {},
|
|
8999
|
-
...hintForKind(errorKind, retryable, backoffMs) ? { hint: hintForKind(errorKind, retryable, backoffMs) } : {}
|
|
9431
|
+
...hintForKind(errorKind, retryable, backoffMs, partial) ? { hint: hintForKind(errorKind, retryable, backoffMs, partial) } : {}
|
|
9000
9432
|
};
|
|
9001
9433
|
} catch (err) {
|
|
9002
9434
|
return {
|
|
@@ -9016,7 +9448,7 @@ function instantiateRosterConfig2(role, base) {
|
|
|
9016
9448
|
id: `${role}-${randomUUID().slice(0, 8)}`
|
|
9017
9449
|
};
|
|
9018
9450
|
}
|
|
9019
|
-
function hintForKind(kind, retryable, backoffMs) {
|
|
9451
|
+
function hintForKind(kind, retryable, backoffMs, partial) {
|
|
9020
9452
|
if (!kind) return void 0;
|
|
9021
9453
|
switch (kind) {
|
|
9022
9454
|
case "provider_rate_limit":
|
|
@@ -9032,16 +9464,40 @@ function hintForKind(kind, retryable, backoffMs) {
|
|
|
9032
9464
|
case "budget_iterations":
|
|
9033
9465
|
case "budget_tool_calls":
|
|
9034
9466
|
case "budget_tokens":
|
|
9035
|
-
case "budget_cost":
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9467
|
+
case "budget_cost": {
|
|
9468
|
+
const base = "Subagent exhausted its budget. The coordinator may auto-extend; otherwise raise the matching `max*` field (e.g. maxToolCalls: 600) on the next delegate, or split the task.";
|
|
9469
|
+
if (partial?.lastAssistantText) {
|
|
9470
|
+
return `${base}
|
|
9471
|
+
|
|
9472
|
+
Partial output produced before budget hit:
|
|
9473
|
+
${partial.lastAssistantText}`;
|
|
9474
|
+
}
|
|
9475
|
+
return base;
|
|
9476
|
+
}
|
|
9477
|
+
case "budget_timeout": {
|
|
9478
|
+
const base = "Subagent hit its wall-clock budget. Raise `timeoutMs` on the next delegate or split the task.";
|
|
9479
|
+
if (partial?.lastAssistantText) {
|
|
9480
|
+
return `${base}
|
|
9481
|
+
|
|
9482
|
+
Partial output produced before timeout:
|
|
9483
|
+
${partial.lastAssistantText}`;
|
|
9484
|
+
}
|
|
9485
|
+
return base;
|
|
9486
|
+
}
|
|
9039
9487
|
case "aborted_by_parent":
|
|
9040
9488
|
return "Subagent was aborted (user Ctrl+C, parent unwound, or sibling failure cascade). Not retryable until the abort condition is resolved.";
|
|
9041
9489
|
case "empty_response":
|
|
9042
9490
|
return "Subagent ended its turn with no text and no tool calls. Almost always a prompt / config issue \u2014 clarify the task or check the model.";
|
|
9043
|
-
case "tool_failed":
|
|
9044
|
-
|
|
9491
|
+
case "tool_failed": {
|
|
9492
|
+
const base = "A tool inside the subagent returned ok:false. Retry with corrected inputs.";
|
|
9493
|
+
if (partial?.lastAssistantText) {
|
|
9494
|
+
return `${base}
|
|
9495
|
+
|
|
9496
|
+
Agent reasoning before failure:
|
|
9497
|
+
${partial.lastAssistantText}`;
|
|
9498
|
+
}
|
|
9499
|
+
return base;
|
|
9500
|
+
}
|
|
9045
9501
|
case "bridge_failed":
|
|
9046
9502
|
return "Parent-child bridge transport failed. This is rare \u2014 restart the session and retry.";
|
|
9047
9503
|
default:
|
|
@@ -9252,6 +9708,9 @@ function makeDirectorSessionFactory(opts) {
|
|
|
9252
9708
|
}
|
|
9253
9709
|
};
|
|
9254
9710
|
}
|
|
9711
|
+
|
|
9712
|
+
// src/coordination/null-fleet-bus.ts
|
|
9713
|
+
var NULL_FLEET_BUS = new FleetBus();
|
|
9255
9714
|
var DefaultModeStore = class {
|
|
9256
9715
|
activeModeId = null;
|
|
9257
9716
|
modes;
|
|
@@ -10577,10 +11036,10 @@ var AISpecBuilder = class {
|
|
|
10577
11036
|
async saveSession() {
|
|
10578
11037
|
if (!this.sessionPath) return;
|
|
10579
11038
|
try {
|
|
10580
|
-
const
|
|
10581
|
-
const
|
|
10582
|
-
await
|
|
10583
|
-
await
|
|
11039
|
+
const fsp15 = await import('fs/promises');
|
|
11040
|
+
const path25 = await import('path');
|
|
11041
|
+
await fsp15.mkdir(path25.dirname(this.sessionPath), { recursive: true });
|
|
11042
|
+
await fsp15.writeFile(this.sessionPath, JSON.stringify(this.session, null, 2), "utf8");
|
|
10584
11043
|
} catch {
|
|
10585
11044
|
}
|
|
10586
11045
|
}
|
|
@@ -10588,8 +11047,8 @@ var AISpecBuilder = class {
|
|
|
10588
11047
|
async loadSession() {
|
|
10589
11048
|
if (!this.sessionPath) return false;
|
|
10590
11049
|
try {
|
|
10591
|
-
const
|
|
10592
|
-
const raw = await
|
|
11050
|
+
const fsp15 = await import('fs/promises');
|
|
11051
|
+
const raw = await fsp15.readFile(this.sessionPath, "utf8");
|
|
10593
11052
|
const loaded = JSON.parse(raw);
|
|
10594
11053
|
if (loaded?.id && loaded?.phase && loaded?.title) {
|
|
10595
11054
|
this.session = loaded;
|
|
@@ -10603,8 +11062,8 @@ var AISpecBuilder = class {
|
|
|
10603
11062
|
async deleteSession() {
|
|
10604
11063
|
if (!this.sessionPath) return;
|
|
10605
11064
|
try {
|
|
10606
|
-
const
|
|
10607
|
-
await
|
|
11065
|
+
const fsp15 = await import('fs/promises');
|
|
11066
|
+
await fsp15.unlink(this.sessionPath);
|
|
10608
11067
|
} catch {
|
|
10609
11068
|
}
|
|
10610
11069
|
}
|
|
@@ -11282,15 +11741,15 @@ function computeCriticalPath(graph, topoOrder, blockedByMap) {
|
|
|
11282
11741
|
maxId = id;
|
|
11283
11742
|
}
|
|
11284
11743
|
}
|
|
11285
|
-
const
|
|
11744
|
+
const path25 = [];
|
|
11286
11745
|
let current = maxId;
|
|
11287
11746
|
const visited = /* @__PURE__ */ new Set();
|
|
11288
11747
|
while (current && !visited.has(current)) {
|
|
11289
11748
|
visited.add(current);
|
|
11290
|
-
|
|
11749
|
+
path25.unshift(current);
|
|
11291
11750
|
current = prev.get(current) ?? null;
|
|
11292
11751
|
}
|
|
11293
|
-
return
|
|
11752
|
+
return path25;
|
|
11294
11753
|
}
|
|
11295
11754
|
function computeParallelGroups(graph, blockedByMap) {
|
|
11296
11755
|
const groups = [];
|
|
@@ -12000,7 +12459,7 @@ async function startMetricsServer(opts) {
|
|
|
12000
12459
|
const tls = opts.tls;
|
|
12001
12460
|
const useHttps = !!(tls?.cert && tls?.key);
|
|
12002
12461
|
const host = opts.host ?? "127.0.0.1";
|
|
12003
|
-
const
|
|
12462
|
+
const path25 = opts.path ?? "/metrics";
|
|
12004
12463
|
const healthPath = opts.healthPath ?? "/healthz";
|
|
12005
12464
|
const healthRegistry = opts.healthRegistry;
|
|
12006
12465
|
const listener = (req, res) => {
|
|
@@ -12010,7 +12469,7 @@ async function startMetricsServer(opts) {
|
|
|
12010
12469
|
return;
|
|
12011
12470
|
}
|
|
12012
12471
|
const url = req.url.split("?")[0];
|
|
12013
|
-
if (url ===
|
|
12472
|
+
if (url === path25) {
|
|
12014
12473
|
let body;
|
|
12015
12474
|
try {
|
|
12016
12475
|
body = renderPrometheus(opts.sink.snapshot());
|
|
@@ -12074,7 +12533,7 @@ async function startMetricsServer(opts) {
|
|
|
12074
12533
|
const protocol = useHttps ? "https" : "http";
|
|
12075
12534
|
return {
|
|
12076
12535
|
port: boundPort,
|
|
12077
|
-
url: `${protocol}://${host}:${boundPort}${
|
|
12536
|
+
url: `${protocol}://${host}:${boundPort}${path25}`,
|
|
12078
12537
|
close: () => new Promise((resolve5, reject) => {
|
|
12079
12538
|
server.close((err) => err ? reject(err) : resolve5());
|
|
12080
12539
|
})
|
|
@@ -14182,8 +14641,8 @@ var ReportGenerator = class {
|
|
|
14182
14641
|
try {
|
|
14183
14642
|
await stat(this.options.outputDir);
|
|
14184
14643
|
} catch {
|
|
14185
|
-
const { mkdir:
|
|
14186
|
-
await
|
|
14644
|
+
const { mkdir: mkdir11 } = await import('fs/promises');
|
|
14645
|
+
await mkdir11(this.options.outputDir, { recursive: true });
|
|
14187
14646
|
}
|
|
14188
14647
|
}
|
|
14189
14648
|
generateMarkdown(result) {
|
|
@@ -15084,16 +15543,16 @@ Use \`/security report <number>\` to view a specific report.` };
|
|
|
15084
15543
|
}
|
|
15085
15544
|
const index = parseInt(reportId, 10) - 1;
|
|
15086
15545
|
if (!isNaN(index) && reports[index]) {
|
|
15087
|
-
const { readFile:
|
|
15088
|
-
const content = await
|
|
15546
|
+
const { readFile: readFile28 } = await import('fs/promises');
|
|
15547
|
+
const content = await readFile28(join(reportsDir, reports[index]), "utf-8");
|
|
15089
15548
|
return { message: `# Security Report
|
|
15090
15549
|
|
|
15091
15550
|
${content}` };
|
|
15092
15551
|
}
|
|
15093
15552
|
const match = reports.find((r) => r.includes(reportId));
|
|
15094
15553
|
if (match) {
|
|
15095
|
-
const { readFile:
|
|
15096
|
-
const content = await
|
|
15554
|
+
const { readFile: readFile28 } = await import('fs/promises');
|
|
15555
|
+
const content = await readFile28(join(reportsDir, match), "utf-8");
|
|
15097
15556
|
return { message: `# Security Report
|
|
15098
15557
|
|
|
15099
15558
|
${content}` };
|
|
@@ -15143,6 +15602,202 @@ function getHelpMessage() {
|
|
|
15143
15602
|
`;
|
|
15144
15603
|
}
|
|
15145
15604
|
var securitySlashCommand = createSecuritySlashCommand();
|
|
15605
|
+
var FleetManager = class {
|
|
15606
|
+
/** The fleet-wide event bus. */
|
|
15607
|
+
fleet;
|
|
15608
|
+
/** Usage rollup across all subagents. */
|
|
15609
|
+
usage;
|
|
15610
|
+
manifestPath;
|
|
15611
|
+
sessionsRoot;
|
|
15612
|
+
directorRunId;
|
|
15613
|
+
/** Spawn cap (lifetime total). Infinity means unlimited. */
|
|
15614
|
+
maxSpawns;
|
|
15615
|
+
/** Nesting cap. */
|
|
15616
|
+
maxSpawnDepth;
|
|
15617
|
+
/** This director's depth in a director chain. Root = 0. */
|
|
15618
|
+
spawnDepth;
|
|
15619
|
+
/** Live spawn counter. */
|
|
15620
|
+
spawnCount = 0;
|
|
15621
|
+
stateCheckpoint;
|
|
15622
|
+
sessionWriter;
|
|
15623
|
+
manifestTimer = null;
|
|
15624
|
+
manifestDebounceMs;
|
|
15625
|
+
/** Fleet-wide cost cap. Infinity = no cap. Distinct from SubagentBudget limits,
|
|
15626
|
+
* which track per-subagent spend — this field caps the entire fleet total. */
|
|
15627
|
+
maxFleetCostUsd;
|
|
15628
|
+
manifestEntries = /* @__PURE__ */ new Map();
|
|
15629
|
+
/** Pending tasks with their descriptions — populated by `addPendingTask`
|
|
15630
|
+
* and cleared by `removePendingTask`. Replaces the host-side `pending`
|
|
15631
|
+
* Map so task descriptions live in one place (FleetManager). */
|
|
15632
|
+
pendingTasks = /* @__PURE__ */ new Map();
|
|
15633
|
+
subagentMeta = /* @__PURE__ */ new Map();
|
|
15634
|
+
priceLookups = /* @__PURE__ */ new Map();
|
|
15635
|
+
constructor(opts = {}) {
|
|
15636
|
+
this.manifestPath = opts.manifestPath;
|
|
15637
|
+
this.sessionsRoot = opts.sessionsRoot;
|
|
15638
|
+
this.directorRunId = opts.directorRunId ?? randomUUID();
|
|
15639
|
+
this.maxSpawns = opts.maxSpawns ?? Number.POSITIVE_INFINITY;
|
|
15640
|
+
this.maxSpawnDepth = opts.maxSpawnDepth ?? 2;
|
|
15641
|
+
this.spawnDepth = opts.spawnDepth ?? 0;
|
|
15642
|
+
this.sessionWriter = opts.sessionWriter ?? null;
|
|
15643
|
+
this.manifestDebounceMs = opts.manifestDebounceMs ?? 2e3;
|
|
15644
|
+
this.maxFleetCostUsd = opts.directorBudget?.maxCostUsd ?? Number.POSITIVE_INFINITY;
|
|
15645
|
+
this.stateCheckpoint = opts.stateCheckpointPath ? new DirectorStateCheckpoint(
|
|
15646
|
+
opts.stateCheckpointPath,
|
|
15647
|
+
{
|
|
15648
|
+
directorRunId: this.directorRunId,
|
|
15649
|
+
maxSpawns: opts.maxSpawns,
|
|
15650
|
+
spawnDepth: this.spawnDepth,
|
|
15651
|
+
maxSpawnDepth: this.maxSpawnDepth,
|
|
15652
|
+
directorBudget: opts.directorBudget
|
|
15653
|
+
},
|
|
15654
|
+
opts.checkpointDebounceMs ?? 250
|
|
15655
|
+
) : null;
|
|
15656
|
+
this.fleet = new FleetBus();
|
|
15657
|
+
this.usage = new FleetUsageAggregator(
|
|
15658
|
+
this.fleet,
|
|
15659
|
+
(id) => this.priceLookups.get(id),
|
|
15660
|
+
(id) => this.subagentMeta.get(id)
|
|
15661
|
+
);
|
|
15662
|
+
}
|
|
15663
|
+
// -----------------------------------------------------------------------
|
|
15664
|
+
// IFleetManager surface
|
|
15665
|
+
// -----------------------------------------------------------------------
|
|
15666
|
+
get fleetBus() {
|
|
15667
|
+
return this.fleet;
|
|
15668
|
+
}
|
|
15669
|
+
snapshot() {
|
|
15670
|
+
return this.usage.snapshot();
|
|
15671
|
+
}
|
|
15672
|
+
getSubagentMeta(id) {
|
|
15673
|
+
return this.subagentMeta.get(id);
|
|
15674
|
+
}
|
|
15675
|
+
/**
|
|
15676
|
+
* Returns null if the spawn is allowed, or an object describing
|
|
15677
|
+
* which cap was exceeded. Does NOT throw — the caller decides
|
|
15678
|
+
* how to surface the rejection.
|
|
15679
|
+
*/
|
|
15680
|
+
canSpawn(config) {
|
|
15681
|
+
if (this.spawnDepth >= this.maxSpawnDepth) {
|
|
15682
|
+
return { kind: "max_spawn_depth", limit: this.maxSpawnDepth, observed: this.spawnDepth };
|
|
15683
|
+
}
|
|
15684
|
+
if (this.spawnCount >= this.maxSpawns) {
|
|
15685
|
+
return { kind: "max_spawns", limit: this.maxSpawns, observed: this.spawnCount + 1 };
|
|
15686
|
+
}
|
|
15687
|
+
if (this.maxFleetCostUsd < Number.POSITIVE_INFINITY) {
|
|
15688
|
+
const totalCost = this.usage.snapshot().total?.cost ?? 0;
|
|
15689
|
+
if (totalCost >= this.maxFleetCostUsd) {
|
|
15690
|
+
return { kind: "max_cost_usd", limit: this.maxFleetCostUsd, observed: totalCost };
|
|
15691
|
+
}
|
|
15692
|
+
}
|
|
15693
|
+
return null;
|
|
15694
|
+
}
|
|
15695
|
+
/**
|
|
15696
|
+
* Records a spawn: increments counter, stores metadata, updates state checkpoint,
|
|
15697
|
+
* and schedules a debounced manifest write. Call AFTER the coordinator
|
|
15698
|
+
* has successfully spawned the subagent.
|
|
15699
|
+
*
|
|
15700
|
+
* @param subagentId The subagent's id (from coordinator.spawn result)
|
|
15701
|
+
* @param config The SubagentConfig that was used
|
|
15702
|
+
* @param priceLookup Optional per-subagent pricing data
|
|
15703
|
+
*/
|
|
15704
|
+
recordSpawn(subagentId, config, priceLookup) {
|
|
15705
|
+
this.spawnCount += 1;
|
|
15706
|
+
this.subagentMeta.set(subagentId, {
|
|
15707
|
+
provider: config.provider,
|
|
15708
|
+
model: config.model
|
|
15709
|
+
});
|
|
15710
|
+
if (priceLookup) this.priceLookups.set(subagentId, priceLookup);
|
|
15711
|
+
this.manifestEntries.set(subagentId, {
|
|
15712
|
+
subagentId,
|
|
15713
|
+
name: config.name,
|
|
15714
|
+
role: config.role,
|
|
15715
|
+
provider: config.provider,
|
|
15716
|
+
model: config.model,
|
|
15717
|
+
taskIds: []
|
|
15718
|
+
});
|
|
15719
|
+
this.stateCheckpoint?.recordSpawn({
|
|
15720
|
+
id: subagentId,
|
|
15721
|
+
name: config.name,
|
|
15722
|
+
role: config.role,
|
|
15723
|
+
provider: config.provider,
|
|
15724
|
+
model: config.model,
|
|
15725
|
+
spawnedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15726
|
+
}, this.spawnCount);
|
|
15727
|
+
void this.appendSessionEvent({
|
|
15728
|
+
type: "agent_spawned",
|
|
15729
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15730
|
+
agentId: subagentId,
|
|
15731
|
+
role: config.role ?? config.name
|
|
15732
|
+
});
|
|
15733
|
+
this.scheduleManifest();
|
|
15734
|
+
}
|
|
15735
|
+
async writeManifest() {
|
|
15736
|
+
if (!this.manifestPath) return null;
|
|
15737
|
+
const manifest = {
|
|
15738
|
+
version: 1,
|
|
15739
|
+
directorRunId: this.directorRunId,
|
|
15740
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15741
|
+
children: Array.from(this.manifestEntries.values()).map((entry) => ({
|
|
15742
|
+
id: entry.subagentId,
|
|
15743
|
+
name: entry.name,
|
|
15744
|
+
role: entry.role,
|
|
15745
|
+
provider: entry.provider,
|
|
15746
|
+
model: entry.model,
|
|
15747
|
+
taskIds: entry.taskIds
|
|
15748
|
+
})),
|
|
15749
|
+
usage: this.usage.snapshot()
|
|
15750
|
+
};
|
|
15751
|
+
await fsp2.mkdir(path6.dirname(this.manifestPath), { recursive: true });
|
|
15752
|
+
await fsp2.writeFile(this.manifestPath, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
15753
|
+
return this.manifestPath;
|
|
15754
|
+
}
|
|
15755
|
+
/**
|
|
15756
|
+
* Attach task ids to an already-spawned subagent. Called by
|
|
15757
|
+
* `Director.assign()` after the coordinator assigns a task.
|
|
15758
|
+
*/
|
|
15759
|
+
addTaskToSubagent(subagentId, taskId) {
|
|
15760
|
+
const entry = this.manifestEntries.get(subagentId);
|
|
15761
|
+
if (entry) entry.taskIds.push(taskId);
|
|
15762
|
+
}
|
|
15763
|
+
/**
|
|
15764
|
+
* Debounced manifest write. Call after any state mutation
|
|
15765
|
+
* (spawn, assign, complete) so a burst collapses into one write.
|
|
15766
|
+
*/
|
|
15767
|
+
scheduleManifest() {
|
|
15768
|
+
if (!this.manifestPath || this.manifestDebounceMs <= 0) return;
|
|
15769
|
+
if (this.manifestTimer) return;
|
|
15770
|
+
this.manifestTimer = setTimeout(() => {
|
|
15771
|
+
this.manifestTimer = null;
|
|
15772
|
+
void this.writeManifest().catch(() => void 0);
|
|
15773
|
+
}, this.manifestDebounceMs);
|
|
15774
|
+
}
|
|
15775
|
+
/** Best-effort session event writer. Swallows failures. */
|
|
15776
|
+
async appendSessionEvent(event) {
|
|
15777
|
+
if (!this.sessionWriter) return;
|
|
15778
|
+
try {
|
|
15779
|
+
await this.sessionWriter.append(event);
|
|
15780
|
+
} catch {
|
|
15781
|
+
}
|
|
15782
|
+
}
|
|
15783
|
+
// -----------------------------------------------------------------------
|
|
15784
|
+
// Pending task management — eliminates host-side state duplication
|
|
15785
|
+
// -----------------------------------------------------------------------
|
|
15786
|
+
addPendingTask(taskId, subagentId, description) {
|
|
15787
|
+
this.pendingTasks.set(taskId, { subagentId, description });
|
|
15788
|
+
}
|
|
15789
|
+
removePendingTask(taskId) {
|
|
15790
|
+
this.pendingTasks.delete(taskId);
|
|
15791
|
+
}
|
|
15792
|
+
getFleetStatus() {
|
|
15793
|
+
const pending = Array.from(this.pendingTasks.entries()).map(([taskId, v]) => ({
|
|
15794
|
+
taskId,
|
|
15795
|
+
description: v.description,
|
|
15796
|
+
subagentId: v.subagentId
|
|
15797
|
+
}));
|
|
15798
|
+
return { pending, live: [] };
|
|
15799
|
+
}
|
|
15800
|
+
};
|
|
15146
15801
|
|
|
15147
15802
|
// src/extension/registry.ts
|
|
15148
15803
|
var ExtensionRegistry = class {
|
|
@@ -15352,6 +16007,50 @@ var ExtensionRegistry = class {
|
|
|
15352
16007
|
}
|
|
15353
16008
|
};
|
|
15354
16009
|
|
|
16010
|
+
// src/core/continue-to-next-iteration.ts
|
|
16011
|
+
function parseContinueDirective(text) {
|
|
16012
|
+
const LINE_MARKERS = /^\s*\[(continue|next step|proceed|done)\]\s*$/gim;
|
|
16013
|
+
let match;
|
|
16014
|
+
let lastDirective = "none";
|
|
16015
|
+
while ((match = LINE_MARKERS.exec(text)) !== null) {
|
|
16016
|
+
const value = (match[1] ?? "").toLowerCase();
|
|
16017
|
+
if (value === "continue" || value === "next step" || value === "proceed") {
|
|
16018
|
+
lastDirective = "continue";
|
|
16019
|
+
} else if (value === "done") {
|
|
16020
|
+
lastDirective = "stop";
|
|
16021
|
+
}
|
|
16022
|
+
}
|
|
16023
|
+
return lastDirective;
|
|
16024
|
+
}
|
|
16025
|
+
var META_KEY = "_autonomousContinue";
|
|
16026
|
+
function setAutonomousContinue(ctx) {
|
|
16027
|
+
ctx.meta[META_KEY] = true;
|
|
16028
|
+
}
|
|
16029
|
+
function consumeAutonomousContinue(ctx) {
|
|
16030
|
+
const val = ctx.meta[META_KEY] === true;
|
|
16031
|
+
delete ctx.meta[META_KEY];
|
|
16032
|
+
return val;
|
|
16033
|
+
}
|
|
16034
|
+
function makeContinueToNextIterationTool() {
|
|
16035
|
+
const inputSchema = {
|
|
16036
|
+
type: "object",
|
|
16037
|
+
properties: {},
|
|
16038
|
+
required: [],
|
|
16039
|
+
description: "Signal that the agent should continue to the next iteration immediately, without waiting for user input. Use this when you have completed a step and want to proceed automatically to the next step in your plan."
|
|
16040
|
+
};
|
|
16041
|
+
return {
|
|
16042
|
+
name: "continue_to_next_iteration",
|
|
16043
|
+
description: "Continue to the next iteration without returning to the user. Call this when you have finished a step and want to keep working autonomously.",
|
|
16044
|
+
permission: "auto",
|
|
16045
|
+
mutating: false,
|
|
16046
|
+
inputSchema,
|
|
16047
|
+
async execute(_input, ctx) {
|
|
16048
|
+
setAutonomousContinue(ctx);
|
|
16049
|
+
return { continue: true };
|
|
16050
|
+
}
|
|
16051
|
+
};
|
|
16052
|
+
}
|
|
16053
|
+
|
|
15355
16054
|
// src/core/iteration-limit.ts
|
|
15356
16055
|
function requestLimitExtension(opts) {
|
|
15357
16056
|
const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
|
|
@@ -15430,6 +16129,8 @@ var Agent = class {
|
|
|
15430
16129
|
plugins = [];
|
|
15431
16130
|
toolExecutor;
|
|
15432
16131
|
autoExtendLimit;
|
|
16132
|
+
/** Enables autonomous continue: model can signal `[continue]` or call continue_to_next_iteration() to re-run. */
|
|
16133
|
+
autonomousContinue;
|
|
15433
16134
|
tracer;
|
|
15434
16135
|
extensions;
|
|
15435
16136
|
constructor(init) {
|
|
@@ -15444,6 +16145,7 @@ var Agent = class {
|
|
|
15444
16145
|
this.executionStrategy = init.executionStrategy ?? "smart";
|
|
15445
16146
|
this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
|
|
15446
16147
|
this.autoExtendLimit = init.autoExtendLimit ?? true;
|
|
16148
|
+
this.autonomousContinue = init.autonomousContinue ?? false;
|
|
15447
16149
|
this.tracer = init.tracer;
|
|
15448
16150
|
this.extensions = init.extensions ?? new ExtensionRegistry();
|
|
15449
16151
|
this.extensions.setLogger(this.container.resolve(TOKENS.Logger));
|
|
@@ -15592,6 +16294,9 @@ var Agent = class {
|
|
|
15592
16294
|
if (controller.signal.aborted) {
|
|
15593
16295
|
return { status: "aborted", iterations };
|
|
15594
16296
|
}
|
|
16297
|
+
if (this.autonomousContinue) {
|
|
16298
|
+
consumeAutonomousContinue(this.ctx);
|
|
16299
|
+
}
|
|
15595
16300
|
const limitCheck = await this.checkIterationLimit(
|
|
15596
16301
|
i,
|
|
15597
16302
|
effectiveLimit,
|
|
@@ -15668,12 +16373,32 @@ var Agent = class {
|
|
|
15668
16373
|
const toolUses = res.content.filter(isToolUseBlock);
|
|
15669
16374
|
if (toolUses.length === 0) {
|
|
15670
16375
|
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
16376
|
+
if (this.autonomousContinue && responseResult.directive === "continue") {
|
|
16377
|
+
await this.compactContextIfNeeded();
|
|
16378
|
+
await this.extensions.runAfterIteration(this.ctx, i);
|
|
16379
|
+
continue;
|
|
16380
|
+
}
|
|
16381
|
+
if (this.autonomousContinue && responseResult.directive === "stop") {
|
|
16382
|
+
return { status: "done", iterations, finalText };
|
|
16383
|
+
}
|
|
15671
16384
|
return { status: "done", iterations, finalText };
|
|
15672
16385
|
}
|
|
15673
16386
|
await this.executeTools(toolUses);
|
|
16387
|
+
if (this.autonomousContinue && consumeAutonomousContinue(this.ctx)) {
|
|
16388
|
+
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
16389
|
+
await this.compactContextIfNeeded();
|
|
16390
|
+
await this.extensions.runAfterIteration(this.ctx, i);
|
|
16391
|
+
continue;
|
|
16392
|
+
}
|
|
15674
16393
|
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
15675
16394
|
await this.compactContextIfNeeded();
|
|
15676
16395
|
await this.extensions.runAfterIteration(this.ctx, i);
|
|
16396
|
+
if (this.autonomousContinue && responseResult.directive === "continue") {
|
|
16397
|
+
continue;
|
|
16398
|
+
}
|
|
16399
|
+
if (this.autonomousContinue && responseResult.directive === "stop") {
|
|
16400
|
+
return { status: "done", iterations, finalText };
|
|
16401
|
+
}
|
|
15677
16402
|
}
|
|
15678
16403
|
}
|
|
15679
16404
|
/**
|
|
@@ -15760,7 +16485,11 @@ var Agent = class {
|
|
|
15760
16485
|
if (!streamed) this.renderer?.write(rendered);
|
|
15761
16486
|
}
|
|
15762
16487
|
}
|
|
15763
|
-
|
|
16488
|
+
let directive = "none";
|
|
16489
|
+
if (this.autonomousContinue && finalText) {
|
|
16490
|
+
directive = parseContinueDirective(finalText);
|
|
16491
|
+
}
|
|
16492
|
+
return { finalText, aborted: false, done: false, directive };
|
|
15764
16493
|
}
|
|
15765
16494
|
/**
|
|
15766
16495
|
* Execute tools and append tool results to context.
|
|
@@ -17353,6 +18082,6 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
|
|
|
17353
18082
|
});
|
|
17354
18083
|
}
|
|
17355
18084
|
|
|
17356
|
-
export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director,
|
|
18085
|
+
export { AISpecBuilder, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FleetBus, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GitignoreUpdater, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, ScopedEventBus, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, applyRosterBudget, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createAutoExecutor, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMessage, createSecuritySlashCommand, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, downloadGitHubTarball, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatPlan, formatPlanTemplates, formatTodosList, getContextWindowMode, getPlanTemplate, getTemplate, githubServer, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeContinueToNextIterationTool, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, parseContinueDirective, parseSkillRef, projectHash, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, savePlan, saveTodosCheckpoint, securitySlashCommand, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
|
|
17357
18086
|
//# sourceMappingURL=index.js.map
|
|
17358
18087
|
//# sourceMappingURL=index.js.map
|