@wrongstack/core 0.281.1 → 0.282.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/{agent-subagent-runner-DyOSK2Wy.d.ts → agent-subagent-runner-Dksf0QVU.d.ts} +15 -4
- package/dist/{config-CN-vdDEI.d.ts → config-BCAEJicb.d.ts} +33 -1
- package/dist/coordination/index.d.ts +147 -48
- package/dist/coordination/index.js +1244 -274
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +18 -18
- package/dist/defaults/index.js +991 -173
- package/dist/defaults/index.js.map +1 -1
- package/dist/{director-state-BfeCUbmk.d.ts → director-state-B_GPa78Q.d.ts} +14 -0
- package/dist/{events-C5hLU0AT.d.ts → events-CovDylaH.d.ts} +2 -1
- package/dist/execution/index.d.ts +10 -10
- package/dist/execution/index.js +69 -3
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +4 -4
- package/dist/{global-mailbox-CHM1QkCi.d.ts → global-mailbox-CNfgvRw3.d.ts} +1 -1
- package/dist/{goal-store-BLGZq16t.d.ts → goal-store-CBd749M4.d.ts} +1 -1
- package/dist/hq/index.d.ts +4 -4
- package/dist/{index-C1E-_71T.d.ts → index-D5HB7WkV.d.ts} +4 -3
- package/dist/index.d.ts +3005 -2704
- package/dist/index.js +51869 -49930
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +3 -3
- package/dist/kernel/index.d.ts +7 -7
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-EKRAcJDi.d.ts → mcp-servers-DJYwZXyH.d.ts} +1 -1
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.js +111 -21
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-coordinator-IpHCuHUa.d.ts → multi-agent-coordinator-4DC31QWu.d.ts} +1 -1
- package/dist/{null-fleet-bus-ZBkKGR9j.d.ts → null-fleet-bus-DuvA8PIR.d.ts} +74 -6
- package/dist/observability/index.d.ts +1 -1
- package/dist/{parallel-eternal-engine-Cc2CNgmv.d.ts → parallel-eternal-engine-BwcA0bAt.d.ts} +4 -4
- package/dist/{pipeline-E7_jqGyV.d.ts → pipeline-DM-CPTUs.d.ts} +1 -1
- package/dist/{provider-model-resolve-KgIdiEQ6.d.ts → provider-model-resolve-hNUbewMd.d.ts} +1 -1
- package/dist/{provider-runner-DxWXNHL1.d.ts → provider-runner-Bz4GyUqo.d.ts} +1 -1
- package/dist/sdd/index.d.ts +6 -6
- package/dist/sdd/index.js +76 -2
- package/dist/sdd/index.js.map +1 -1
- package/dist/{session-event-bridge-vA2fLV58.d.ts → session-event-bridge-Bkp7ORfc.d.ts} +2 -2
- package/dist/skills/index.d.ts +84 -84
- package/dist/storage/index.d.ts +168 -168
- package/dist/storage/index.js +19 -7
- package/dist/storage/index.js.map +1 -1
- package/dist/{todos-checkpoint-B1QVkoa8.d.ts → todos-checkpoint-addCdQ9f.d.ts} +2 -2
- package/dist/{tool-executor-BWtYwuPI.d.ts → tool-executor-B3spV_Q6.d.ts} +7 -7
- package/dist/tools/index.d.ts +1 -1
- package/dist/types/index.d.ts +33 -33
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +113 -113
- package/dist/{worktree-manager-C6CIilTT.d.ts → worktree-manager-BBXdJq-Y.d.ts} +1 -1
- package/instructions/agents/reviewer.md +25 -0
- package/instructions/agents/verifier.md +24 -0
- package/instructions/coordination/director-preamble.md +24 -2
- package/instructions/coordination/subagent-baseline.md +8 -1
- package/package.json +1 -1
package/dist/defaults/index.js
CHANGED
|
@@ -2052,20 +2052,20 @@ function compactDiff(diff) {
|
|
|
2052
2052
|
merged.push(current);
|
|
2053
2053
|
}
|
|
2054
2054
|
}
|
|
2055
|
-
const
|
|
2055
|
+
const excerpt2 = [];
|
|
2056
2056
|
let prevLine = -1;
|
|
2057
2057
|
for (const [start, end] of merged) {
|
|
2058
2058
|
if (start > prevLine + 1) {
|
|
2059
2059
|
const omitted = prevLine === -1 ? start : start - prevLine - 1;
|
|
2060
|
-
|
|
2060
|
+
excerpt2.push(`[serializer omitted ${omitted} diff line(s)]`);
|
|
2061
2061
|
}
|
|
2062
2062
|
for (let j = start; j <= end; j++) {
|
|
2063
|
-
|
|
2063
|
+
excerpt2.push(lines[j] ?? "");
|
|
2064
2064
|
}
|
|
2065
2065
|
prevLine = end;
|
|
2066
2066
|
}
|
|
2067
2067
|
const trailing = lines.length - prevLine - 1;
|
|
2068
|
-
if (trailing > 0)
|
|
2068
|
+
if (trailing > 0) excerpt2.push(`[serializer omitted ${trailing} trailing diff line(s)]`);
|
|
2069
2069
|
return joinSections([
|
|
2070
2070
|
renderHeader("diff_summary", {
|
|
2071
2071
|
files: fileCount,
|
|
@@ -2075,7 +2075,7 @@ function compactDiff(diff) {
|
|
|
2075
2075
|
removed,
|
|
2076
2076
|
lines: lines.length
|
|
2077
2077
|
}),
|
|
2078
|
-
|
|
2078
|
+
excerpt2.join("\n")
|
|
2079
2079
|
]);
|
|
2080
2080
|
}
|
|
2081
2081
|
function compactFailureOutput(output) {
|
|
@@ -3923,6 +3923,34 @@ var BUILD_AGENTS = [
|
|
|
3923
3923
|
|
|
3924
3924
|
// src/coordination/agents/phase4-verify.ts
|
|
3925
3925
|
var VERIFY_AGENTS = [
|
|
3926
|
+
{
|
|
3927
|
+
config: {
|
|
3928
|
+
id: "verifier",
|
|
3929
|
+
name: "Verifier",
|
|
3930
|
+
role: "verifier",
|
|
3931
|
+
tools: [...TOOLS.inspect, "bash", "exec", "lint", "typecheck", "test", "git"],
|
|
3932
|
+
prompt: agentPrompt("verifier")
|
|
3933
|
+
},
|
|
3934
|
+
budget: HEAVY_BUDGET,
|
|
3935
|
+
capability: {
|
|
3936
|
+
phase: "verify",
|
|
3937
|
+
summary: "Independent verification gate: runs the relevant checks until they pass or returns exact failures that need another implementation pass.",
|
|
3938
|
+
keywords: [
|
|
3939
|
+
"verify",
|
|
3940
|
+
"verification",
|
|
3941
|
+
"quality gate",
|
|
3942
|
+
"prove it works",
|
|
3943
|
+
"run checks",
|
|
3944
|
+
"run tests until pass",
|
|
3945
|
+
"test pass",
|
|
3946
|
+
"green build",
|
|
3947
|
+
"typecheck",
|
|
3948
|
+
"lint",
|
|
3949
|
+
"regression check",
|
|
3950
|
+
"acceptance gate"
|
|
3951
|
+
]
|
|
3952
|
+
}
|
|
3953
|
+
},
|
|
3926
3954
|
{
|
|
3927
3955
|
config: {
|
|
3928
3956
|
id: "test",
|
|
@@ -4189,6 +4217,34 @@ var VERIFY_AGENTS = [
|
|
|
4189
4217
|
|
|
4190
4218
|
// src/coordination/agents/phase5-review.ts
|
|
4191
4219
|
var REVIEW_AGENTS = [
|
|
4220
|
+
{
|
|
4221
|
+
config: {
|
|
4222
|
+
id: "reviewer",
|
|
4223
|
+
name: "Reviewer",
|
|
4224
|
+
role: "reviewer",
|
|
4225
|
+
tools: [...TOOLS.inspect, "git"],
|
|
4226
|
+
prompt: agentPrompt("reviewer")
|
|
4227
|
+
},
|
|
4228
|
+
budget: MEDIUM_BUDGET,
|
|
4229
|
+
capability: {
|
|
4230
|
+
phase: "review",
|
|
4231
|
+
summary: "Independent AI reviewer: audits another agent's output, flags uncertainty, correlated-failure risk, and must-fix defects.",
|
|
4232
|
+
keywords: [
|
|
4233
|
+
"reviewer",
|
|
4234
|
+
"independent review",
|
|
4235
|
+
"ai review",
|
|
4236
|
+
"review agent",
|
|
4237
|
+
"quality review",
|
|
4238
|
+
"second opinion",
|
|
4239
|
+
"check another agent",
|
|
4240
|
+
"cross check",
|
|
4241
|
+
"uncertainty",
|
|
4242
|
+
"correlated error",
|
|
4243
|
+
"verify output",
|
|
4244
|
+
"quality control"
|
|
4245
|
+
]
|
|
4246
|
+
}
|
|
4247
|
+
},
|
|
4192
4248
|
{
|
|
4193
4249
|
config: {
|
|
4194
4250
|
id: "code-reviewer",
|
|
@@ -5174,6 +5230,77 @@ FLEET_ROSTER_BUDGETS["goose"] = { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations:
|
|
|
5174
5230
|
// src/coordination/delegate-tool.ts
|
|
5175
5231
|
init_safe_json();
|
|
5176
5232
|
init_error();
|
|
5233
|
+
|
|
5234
|
+
// src/security/capabilities.ts
|
|
5235
|
+
var ToolCapabilities = {
|
|
5236
|
+
/** Can execute arbitrary commands in the user's shell (the `bash` tool). */
|
|
5237
|
+
SHELL_ARBITRARY: "shell.arbitrary",
|
|
5238
|
+
/** Can execute a restricted set of commands (the `exec` tool). */
|
|
5239
|
+
SHELL_RESTRICTED: "shell.restricted",
|
|
5240
|
+
/** Can run a restricted project formatter/linter-style command. */
|
|
5241
|
+
SHELL_EXEC: "shell.exec",
|
|
5242
|
+
/** Can read files inside the project (and possibly outside via symlinks if not guarded). */
|
|
5243
|
+
FS_READ: "fs.read",
|
|
5244
|
+
/** Can write / modify / delete files inside the project. */
|
|
5245
|
+
FS_WRITE: "fs.write",
|
|
5246
|
+
/** Can write files outside the current project root (very high risk). */
|
|
5247
|
+
FS_WRITE_OUTSIDE_PROJECT: "fs.write.outside-project",
|
|
5248
|
+
/** Can perform outbound network requests. */
|
|
5249
|
+
NET_OUTBOUND: "net.outbound",
|
|
5250
|
+
/** Can invoke arbitrary registered tools through a meta-tool. */
|
|
5251
|
+
TOOL_MUTATE_ANY: "tool.mutate.any",
|
|
5252
|
+
/** Can write persistent memory. */
|
|
5253
|
+
MEMORY_WRITE: "memory.write",
|
|
5254
|
+
/** Can delete persistent memory. */
|
|
5255
|
+
MEMORY_DELETE: "memory.delete",
|
|
5256
|
+
/** Proxies tools from external MCP servers (unknown capability). */
|
|
5257
|
+
MCP_PROXY: "mcp.proxy",
|
|
5258
|
+
/** Can spawn or manage subagents / multi-agent tasks. */
|
|
5259
|
+
SUBAGENT_SPAWN: "subagent.spawn",
|
|
5260
|
+
/** Can inspect fleet/subagent coordination state without mutating it. */
|
|
5261
|
+
COORDINATION_FLEET_READ: "coordination.fleet.read",
|
|
5262
|
+
/** Can mutate global or session configuration / trust state. */
|
|
5263
|
+
CONFIG_MUTATE: "config.mutate",
|
|
5264
|
+
/** Can install packages or run package managers with side effects. */
|
|
5265
|
+
PACKAGE_INSTALL: "package.install"
|
|
5266
|
+
};
|
|
5267
|
+
var DANGEROUS_FOR_SUBAGENTS = [
|
|
5268
|
+
ToolCapabilities.SHELL_ARBITRARY,
|
|
5269
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
5270
|
+
ToolCapabilities.SHELL_EXEC,
|
|
5271
|
+
ToolCapabilities.FS_WRITE,
|
|
5272
|
+
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
5273
|
+
ToolCapabilities.TOOL_MUTATE_ANY,
|
|
5274
|
+
ToolCapabilities.MEMORY_WRITE,
|
|
5275
|
+
ToolCapabilities.MEMORY_DELETE,
|
|
5276
|
+
ToolCapabilities.MCP_PROXY,
|
|
5277
|
+
ToolCapabilities.SUBAGENT_SPAWN,
|
|
5278
|
+
ToolCapabilities.CONFIG_MUTATE,
|
|
5279
|
+
ToolCapabilities.PACKAGE_INSTALL
|
|
5280
|
+
];
|
|
5281
|
+
function hasDangerousCapabilityForSubagents(toolOrCaps) {
|
|
5282
|
+
if (!toolOrCaps) return false;
|
|
5283
|
+
const input = toolOrCaps;
|
|
5284
|
+
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
5285
|
+
return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
|
|
5286
|
+
}
|
|
5287
|
+
function hasCapability(toolOrCaps, capability) {
|
|
5288
|
+
if (!toolOrCaps) return false;
|
|
5289
|
+
const input = toolOrCaps;
|
|
5290
|
+
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
5291
|
+
const toCheck = Array.isArray(capability) ? capability : [capability];
|
|
5292
|
+
return toCheck.some((c) => caps.includes(c));
|
|
5293
|
+
}
|
|
5294
|
+
function getDangerousCapabilities(toolOrCaps) {
|
|
5295
|
+
if (!toolOrCaps) return [];
|
|
5296
|
+
const input = toolOrCaps;
|
|
5297
|
+
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
5298
|
+
return caps.filter(
|
|
5299
|
+
(c) => DANGEROUS_FOR_SUBAGENTS.includes(c)
|
|
5300
|
+
);
|
|
5301
|
+
}
|
|
5302
|
+
|
|
5303
|
+
// src/coordination/delegate-tool.ts
|
|
5177
5304
|
function createDelegateTool(opts) {
|
|
5178
5305
|
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 30 * 60 * 1e3;
|
|
5179
5306
|
const rosterIds = opts.roster ? Object.keys(opts.roster) : [];
|
|
@@ -5244,6 +5371,7 @@ function createDelegateTool(opts) {
|
|
|
5244
5371
|
usageHint: "Set `task` to a complete instruction. Either pick `role` from the roster (audit-log, bug-hunter, refactor-planner, security-scanner) or pass `name` to run a free-form coding agent. For non-trivial work, also pass `timeoutMs`, `maxIterations`, and `maxToolCalls`. Returns the subagent's `TaskResult` \u2014 including the textual `result`, iteration count, tool count, and duration. Auto-promotes the host into director mode on first call.",
|
|
5245
5372
|
permission: "auto",
|
|
5246
5373
|
mutating: false,
|
|
5374
|
+
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
5247
5375
|
inputSchema,
|
|
5248
5376
|
async execute(input) {
|
|
5249
5377
|
const sessionId = opts.directorRunId;
|
|
@@ -5714,9 +5842,24 @@ var DirectorStateCheckpoint = class {
|
|
|
5714
5842
|
recordTaskStatus(taskId, patch) {
|
|
5715
5843
|
this.snapshot = {
|
|
5716
5844
|
...this.snapshot,
|
|
5717
|
-
tasks: this.snapshot.tasks.map(
|
|
5718
|
-
|
|
5719
|
-
|
|
5845
|
+
tasks: this.snapshot.tasks.map((t) => t.taskId === taskId ? { ...t, ...patch } : t)
|
|
5846
|
+
};
|
|
5847
|
+
this.bumpUpdatedAt();
|
|
5848
|
+
this.schedule();
|
|
5849
|
+
}
|
|
5850
|
+
recordTaskWorktree(taskId, worktree) {
|
|
5851
|
+
const exists = this.snapshot.tasks.some((t) => t.taskId === taskId);
|
|
5852
|
+
this.snapshot = {
|
|
5853
|
+
...this.snapshot,
|
|
5854
|
+
tasks: exists ? this.snapshot.tasks.map((t) => t.taskId === taskId ? { ...t, worktree } : t) : [
|
|
5855
|
+
...this.snapshot.tasks,
|
|
5856
|
+
{
|
|
5857
|
+
taskId,
|
|
5858
|
+
subagentId: worktree.subagentId,
|
|
5859
|
+
status: "running",
|
|
5860
|
+
worktree
|
|
5861
|
+
}
|
|
5862
|
+
]
|
|
5720
5863
|
};
|
|
5721
5864
|
this.bumpUpdatedAt();
|
|
5722
5865
|
this.schedule();
|
|
@@ -5759,10 +5902,7 @@ var DirectorStateCheckpoint = class {
|
|
|
5759
5902
|
mode: 384
|
|
5760
5903
|
});
|
|
5761
5904
|
} catch (err) {
|
|
5762
|
-
console.warn(
|
|
5763
|
-
"[director-state] checkpoint write failed:",
|
|
5764
|
-
toErrorMessage(err)
|
|
5765
|
-
);
|
|
5905
|
+
console.warn("[director-state] checkpoint write failed:", toErrorMessage(err));
|
|
5766
5906
|
} finally {
|
|
5767
5907
|
this.writing = false;
|
|
5768
5908
|
if (this.rewriteRequested) {
|
|
@@ -6368,6 +6508,49 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
|
|
|
6368
6508
|
this.snapshot.files.length = 0;
|
|
6369
6509
|
}
|
|
6370
6510
|
};
|
|
6511
|
+
|
|
6512
|
+
// src/coordination/director/director-errors.ts
|
|
6513
|
+
var FleetSpawnBudgetError = class extends Error {
|
|
6514
|
+
kind;
|
|
6515
|
+
limit;
|
|
6516
|
+
observed;
|
|
6517
|
+
constructor(kind, limit, observed, message) {
|
|
6518
|
+
const defaultMsg = 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}`;
|
|
6519
|
+
super(message ?? defaultMsg);
|
|
6520
|
+
this.name = "FleetSpawnBudgetError";
|
|
6521
|
+
this.kind = kind;
|
|
6522
|
+
this.limit = limit;
|
|
6523
|
+
this.observed = observed;
|
|
6524
|
+
}
|
|
6525
|
+
};
|
|
6526
|
+
var FleetCostCapError = class extends Error {
|
|
6527
|
+
kind;
|
|
6528
|
+
limit;
|
|
6529
|
+
observed;
|
|
6530
|
+
constructor(limit, observed) {
|
|
6531
|
+
super(
|
|
6532
|
+
`Director cost cap exceeded: total fleet spend ${observed.toFixed(4)} exceeds maxCostUsd ${limit.toFixed(4)}`
|
|
6533
|
+
);
|
|
6534
|
+
this.name = "FleetCostCapError";
|
|
6535
|
+
this.kind = "max_cost_usd";
|
|
6536
|
+
this.limit = limit;
|
|
6537
|
+
this.observed = observed;
|
|
6538
|
+
}
|
|
6539
|
+
};
|
|
6540
|
+
var FleetContextOverflowError = class extends Error {
|
|
6541
|
+
kind;
|
|
6542
|
+
limit;
|
|
6543
|
+
observed;
|
|
6544
|
+
constructor(limit, observed) {
|
|
6545
|
+
super(
|
|
6546
|
+
`Leader context overflow: leader has ${observed} tokens in flight (limit: ${limit}). Compact the leader context before spawning more subagents.`
|
|
6547
|
+
);
|
|
6548
|
+
this.name = "FleetContextOverflowError";
|
|
6549
|
+
this.kind = "max_context_load";
|
|
6550
|
+
this.limit = limit;
|
|
6551
|
+
this.observed = observed;
|
|
6552
|
+
}
|
|
6553
|
+
};
|
|
6371
6554
|
var textCache = /* @__PURE__ */ new Map();
|
|
6372
6555
|
var rootCandidates;
|
|
6373
6556
|
function readBundledInstructionText(relativePath) {
|
|
@@ -6469,6 +6652,7 @@ function rosterSummaryFromConfigs(roster) {
|
|
|
6469
6652
|
}
|
|
6470
6653
|
return lines.join("\n");
|
|
6471
6654
|
}
|
|
6655
|
+
init_error();
|
|
6472
6656
|
|
|
6473
6657
|
// src/coordination/dispatcher.ts
|
|
6474
6658
|
init_safe_json();
|
|
@@ -6593,95 +6777,57 @@ function makeLLMClassifier(complete) {
|
|
|
6593
6777
|
};
|
|
6594
6778
|
}
|
|
6595
6779
|
|
|
6596
|
-
// src/coordination/director-tools.ts
|
|
6597
|
-
init_error();
|
|
6598
|
-
|
|
6599
|
-
// src/security/capabilities.ts
|
|
6600
|
-
var ToolCapabilities = {
|
|
6601
|
-
/** Can execute arbitrary commands in the user's shell (the `bash` tool). */
|
|
6602
|
-
SHELL_ARBITRARY: "shell.arbitrary",
|
|
6603
|
-
/** Can execute a restricted set of commands (the `exec` tool). */
|
|
6604
|
-
SHELL_RESTRICTED: "shell.restricted",
|
|
6605
|
-
/** Can run a restricted project formatter/linter-style command. */
|
|
6606
|
-
SHELL_EXEC: "shell.exec",
|
|
6607
|
-
/** Can read files inside the project (and possibly outside via symlinks if not guarded). */
|
|
6608
|
-
FS_READ: "fs.read",
|
|
6609
|
-
/** Can write / modify / delete files inside the project. */
|
|
6610
|
-
FS_WRITE: "fs.write",
|
|
6611
|
-
/** Can write files outside the current project root (very high risk). */
|
|
6612
|
-
FS_WRITE_OUTSIDE_PROJECT: "fs.write.outside-project",
|
|
6613
|
-
/** Can perform outbound network requests. */
|
|
6614
|
-
NET_OUTBOUND: "net.outbound",
|
|
6615
|
-
/** Can invoke arbitrary registered tools through a meta-tool. */
|
|
6616
|
-
TOOL_MUTATE_ANY: "tool.mutate.any",
|
|
6617
|
-
/** Can write persistent memory. */
|
|
6618
|
-
MEMORY_WRITE: "memory.write",
|
|
6619
|
-
/** Can delete persistent memory. */
|
|
6620
|
-
MEMORY_DELETE: "memory.delete",
|
|
6621
|
-
/** Proxies tools from external MCP servers (unknown capability). */
|
|
6622
|
-
MCP_PROXY: "mcp.proxy",
|
|
6623
|
-
/** Can spawn or manage subagents / multi-agent tasks. */
|
|
6624
|
-
SUBAGENT_SPAWN: "subagent.spawn",
|
|
6625
|
-
/** Can inspect fleet/subagent coordination state without mutating it. */
|
|
6626
|
-
COORDINATION_FLEET_READ: "coordination.fleet.read",
|
|
6627
|
-
/** Can mutate global or session configuration / trust state. */
|
|
6628
|
-
CONFIG_MUTATE: "config.mutate",
|
|
6629
|
-
/** Can install packages or run package managers with side effects. */
|
|
6630
|
-
PACKAGE_INSTALL: "package.install"
|
|
6631
|
-
};
|
|
6632
|
-
var DANGEROUS_FOR_SUBAGENTS = [
|
|
6633
|
-
ToolCapabilities.SHELL_ARBITRARY,
|
|
6634
|
-
ToolCapabilities.SHELL_RESTRICTED,
|
|
6635
|
-
ToolCapabilities.SHELL_EXEC,
|
|
6636
|
-
ToolCapabilities.FS_WRITE,
|
|
6637
|
-
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
6638
|
-
ToolCapabilities.TOOL_MUTATE_ANY,
|
|
6639
|
-
ToolCapabilities.MEMORY_WRITE,
|
|
6640
|
-
ToolCapabilities.MEMORY_DELETE,
|
|
6641
|
-
ToolCapabilities.MCP_PROXY,
|
|
6642
|
-
ToolCapabilities.SUBAGENT_SPAWN,
|
|
6643
|
-
ToolCapabilities.CONFIG_MUTATE,
|
|
6644
|
-
ToolCapabilities.PACKAGE_INSTALL
|
|
6645
|
-
];
|
|
6646
|
-
function hasDangerousCapabilityForSubagents(toolOrCaps) {
|
|
6647
|
-
if (!toolOrCaps) return false;
|
|
6648
|
-
const input = toolOrCaps;
|
|
6649
|
-
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
6650
|
-
return caps.some((c) => DANGEROUS_FOR_SUBAGENTS.includes(c));
|
|
6651
|
-
}
|
|
6652
|
-
function hasCapability(toolOrCaps, capability) {
|
|
6653
|
-
if (!toolOrCaps) return false;
|
|
6654
|
-
const input = toolOrCaps;
|
|
6655
|
-
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
6656
|
-
const toCheck = Array.isArray(capability) ? capability : [capability];
|
|
6657
|
-
return toCheck.some((c) => caps.includes(c));
|
|
6658
|
-
}
|
|
6659
|
-
function getDangerousCapabilities(toolOrCaps) {
|
|
6660
|
-
if (!toolOrCaps) return [];
|
|
6661
|
-
const input = toolOrCaps;
|
|
6662
|
-
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
6663
|
-
return caps.filter(
|
|
6664
|
-
(c) => DANGEROUS_FOR_SUBAGENTS.includes(c)
|
|
6665
|
-
);
|
|
6666
|
-
}
|
|
6667
|
-
|
|
6668
6780
|
// src/coordination/director-tools.ts
|
|
6669
6781
|
function makeSpawnTool(director, roster) {
|
|
6670
6782
|
const inputSchema = {
|
|
6671
6783
|
type: "object",
|
|
6672
6784
|
properties: {
|
|
6673
|
-
role: {
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6785
|
+
role: {
|
|
6786
|
+
type: "string",
|
|
6787
|
+
description: "Roster role id. When set, the spawn uses the matching config from the roster and ignores other fields."
|
|
6788
|
+
},
|
|
6789
|
+
description: {
|
|
6790
|
+
type: "string",
|
|
6791
|
+
description: "Free-form task description. When `role` is not set, the director uses the smart dispatcher to route this to the best-matching catalog agent. Use this when you don't know the exact role name."
|
|
6792
|
+
},
|
|
6793
|
+
name: {
|
|
6794
|
+
type: "string",
|
|
6795
|
+
description: "Display name for the subagent. Used as a fallback when description-based dispatch does not resolve a role."
|
|
6796
|
+
},
|
|
6797
|
+
provider: {
|
|
6798
|
+
type: "string",
|
|
6799
|
+
description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.'
|
|
6800
|
+
},
|
|
6801
|
+
model: {
|
|
6802
|
+
type: "string",
|
|
6803
|
+
description: "Model id within the provider. Defaults to the leader model when omitted."
|
|
6804
|
+
},
|
|
6805
|
+
systemPromptOverride: {
|
|
6806
|
+
type: "string",
|
|
6807
|
+
description: "Extra prompt text appended after the role-base prompt."
|
|
6808
|
+
},
|
|
6679
6809
|
maxIterations: { type: "number", minimum: 1 },
|
|
6680
6810
|
maxToolCalls: { type: "number", minimum: 1 },
|
|
6681
6811
|
maxCostUsd: { type: "number", minimum: 0 },
|
|
6682
|
-
timeoutMs: {
|
|
6683
|
-
|
|
6684
|
-
|
|
6812
|
+
timeoutMs: {
|
|
6813
|
+
type: "number",
|
|
6814
|
+
minimum: 1,
|
|
6815
|
+
description: "Hard wall-clock cap in milliseconds. Defaults to none (idle timeout is the default reaper)."
|
|
6816
|
+
},
|
|
6817
|
+
idleTimeoutMs: {
|
|
6818
|
+
type: "number",
|
|
6819
|
+
minimum: 1,
|
|
6820
|
+
description: "Idle timeout in ms: reap the subagent after this long with no activity. Resets on every iteration/tool call. Default is role/coordinator-specific."
|
|
6821
|
+
},
|
|
6822
|
+
maxTokens: {
|
|
6823
|
+
type: "number",
|
|
6824
|
+
minimum: 1,
|
|
6825
|
+
description: "Maximum total tokens (input + output) the subagent may use."
|
|
6826
|
+
},
|
|
6827
|
+
worktree: {
|
|
6828
|
+
anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }],
|
|
6829
|
+
description: 'Git-worktree isolation override. true/"required" requires an isolated worktree; false/"off" disables it; "auto" follows fleet policy.'
|
|
6830
|
+
}
|
|
6685
6831
|
},
|
|
6686
6832
|
required: []
|
|
6687
6833
|
};
|
|
@@ -6700,7 +6846,8 @@ function makeSpawnTool(director, roster) {
|
|
|
6700
6846
|
let cfg;
|
|
6701
6847
|
if (role && roster) {
|
|
6702
6848
|
const base = roster[role];
|
|
6703
|
-
if (!base)
|
|
6849
|
+
if (!base)
|
|
6850
|
+
return { error: `unknown role "${role}". roster has: ${Object.keys(roster).join(", ")}` };
|
|
6704
6851
|
cfg = instantiateRosterConfig2(role, base);
|
|
6705
6852
|
} else if (description && !role) {
|
|
6706
6853
|
const dispatchResult = await dispatchAgent(description, {
|
|
@@ -6724,16 +6871,26 @@ function makeSpawnTool(director, roster) {
|
|
|
6724
6871
|
if (typeof i.name === "string") cfg.name = i.name;
|
|
6725
6872
|
if (typeof i.provider === "string") cfg.provider = i.provider;
|
|
6726
6873
|
if (typeof i.model === "string") cfg.model = i.model;
|
|
6727
|
-
if (typeof i.systemPromptOverride === "string")
|
|
6874
|
+
if (typeof i.systemPromptOverride === "string")
|
|
6875
|
+
cfg.systemPromptOverride = i.systemPromptOverride;
|
|
6728
6876
|
if (typeof i.maxIterations === "number") cfg.maxIterations = i.maxIterations;
|
|
6729
6877
|
if (typeof i.maxToolCalls === "number") cfg.maxToolCalls = i.maxToolCalls;
|
|
6730
6878
|
if (typeof i.maxCostUsd === "number") cfg.maxCostUsd = i.maxCostUsd;
|
|
6731
6879
|
if (typeof i.timeoutMs === "number") cfg.timeoutMs = i.timeoutMs;
|
|
6732
6880
|
if (typeof i.idleTimeoutMs === "number") cfg.idleTimeoutMs = i.idleTimeoutMs;
|
|
6733
6881
|
if (typeof i.maxTokens === "number") cfg.maxTokens = i.maxTokens;
|
|
6882
|
+
if (typeof i.worktree === "boolean" || i.worktree === "auto" || i.worktree === "required" || i.worktree === "off") {
|
|
6883
|
+
cfg.worktree = i.worktree;
|
|
6884
|
+
}
|
|
6734
6885
|
try {
|
|
6735
6886
|
const subagentId = await director.spawn(cfg);
|
|
6736
|
-
return {
|
|
6887
|
+
return {
|
|
6888
|
+
subagentId,
|
|
6889
|
+
provider: cfg.provider,
|
|
6890
|
+
model: cfg.model,
|
|
6891
|
+
name: cfg.name,
|
|
6892
|
+
role: cfg.role
|
|
6893
|
+
};
|
|
6737
6894
|
} catch (err) {
|
|
6738
6895
|
if (err instanceof FleetSpawnBudgetError) {
|
|
6739
6896
|
return { error: err.message, kind: err.kind, limit: err.limit, observed: err.observed };
|
|
@@ -6754,13 +6911,402 @@ function instantiateRosterConfig2(role, base) {
|
|
|
6754
6911
|
id: `${role}-${randomUUID().slice(0, 8)}`
|
|
6755
6912
|
};
|
|
6756
6913
|
}
|
|
6914
|
+
function makeQualityGateTool(director, roster) {
|
|
6915
|
+
return {
|
|
6916
|
+
name: "quality_gate",
|
|
6917
|
+
description: "Run a first-class implementation quality gate. It can await implementer task ids, spawn independent verifier/reviewer agents, summarize their verdicts, and optionally send must-fix feedback back to an implementer until the gate passes or the repair-attempt limit is reached.",
|
|
6918
|
+
usageHint: "Use after code-changing work. Provide implementerTaskIds when available. Add repairSubagentId to iterate fixes automatically. Verdict only passes when every enabled reviewer/verifier explicitly passes.",
|
|
6919
|
+
permission: "auto",
|
|
6920
|
+
mutating: false,
|
|
6921
|
+
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
6922
|
+
inputSchema: {
|
|
6923
|
+
type: "object",
|
|
6924
|
+
properties: {
|
|
6925
|
+
task: {
|
|
6926
|
+
type: "string",
|
|
6927
|
+
description: "Original implementation task or acceptance goal being gated."
|
|
6928
|
+
},
|
|
6929
|
+
implementerTaskIds: {
|
|
6930
|
+
type: "array",
|
|
6931
|
+
items: { type: "string" },
|
|
6932
|
+
description: "Optional completed or in-flight implementer task ids to await and include as implementation evidence."
|
|
6933
|
+
},
|
|
6934
|
+
repairSubagentId: {
|
|
6935
|
+
type: "string",
|
|
6936
|
+
description: "Optional implementer subagent id. When set and the gate fails, quality_gate assigns a repair task with reviewer/verifier feedback and reruns the gate."
|
|
6937
|
+
},
|
|
6938
|
+
maxRepairAttempts: {
|
|
6939
|
+
type: "number",
|
|
6940
|
+
minimum: 0,
|
|
6941
|
+
maximum: 5,
|
|
6942
|
+
description: "Maximum automatic repair iterations. Default: 2 when repairSubagentId is set, otherwise 0."
|
|
6943
|
+
},
|
|
6944
|
+
targets: {
|
|
6945
|
+
type: "array",
|
|
6946
|
+
items: { type: "string" },
|
|
6947
|
+
description: "Files, packages, or paths that reviewer/verifier should focus on."
|
|
6948
|
+
},
|
|
6949
|
+
commands: {
|
|
6950
|
+
type: "array",
|
|
6951
|
+
items: { type: "string" },
|
|
6952
|
+
description: 'Verification commands that should pass, e.g. ["pnpm --filter @wrongstack/core typecheck"].'
|
|
6953
|
+
},
|
|
6954
|
+
expected: {
|
|
6955
|
+
type: "string",
|
|
6956
|
+
description: "Expected behavior or acceptance criteria."
|
|
6957
|
+
},
|
|
6958
|
+
evidence: {
|
|
6959
|
+
type: "string",
|
|
6960
|
+
description: "Known implementation notes, diff summary, or commands already run."
|
|
6961
|
+
},
|
|
6962
|
+
reviewer: {
|
|
6963
|
+
type: "boolean",
|
|
6964
|
+
description: "Whether to run the reviewer lane. Default true."
|
|
6965
|
+
},
|
|
6966
|
+
verifier: {
|
|
6967
|
+
type: "boolean",
|
|
6968
|
+
description: "Whether to run the verifier lane. Default true."
|
|
6969
|
+
},
|
|
6970
|
+
timeoutMs: {
|
|
6971
|
+
type: "number",
|
|
6972
|
+
minimum: 1,
|
|
6973
|
+
description: "Optional per reviewer/verifier/repair task timeout."
|
|
6974
|
+
},
|
|
6975
|
+
reviewerWorktree: {
|
|
6976
|
+
anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }],
|
|
6977
|
+
description: "Reviewer worktree override. Default off because reviewer is read-only."
|
|
6978
|
+
},
|
|
6979
|
+
verifierWorktree: {
|
|
6980
|
+
anyOf: [{ type: "boolean" }, { type: "string", enum: ["auto", "required", "off"] }],
|
|
6981
|
+
description: "Verifier worktree override. Default auto so test artifacts stay isolated when fleet policy wants it."
|
|
6982
|
+
}
|
|
6983
|
+
}
|
|
6984
|
+
},
|
|
6985
|
+
async execute(input) {
|
|
6986
|
+
const i = normalizeQualityGateInput(input);
|
|
6987
|
+
const runReviewer = i.reviewer !== false;
|
|
6988
|
+
const runVerifier = i.verifier !== false;
|
|
6989
|
+
if (!runReviewer && !runVerifier) {
|
|
6990
|
+
return {
|
|
6991
|
+
verdict: "inconclusive",
|
|
6992
|
+
passed: false,
|
|
6993
|
+
error: "quality_gate requires reviewer, verifier, or both."
|
|
6994
|
+
};
|
|
6995
|
+
}
|
|
6996
|
+
const implementerResults = i.implementerTaskIds && i.implementerTaskIds.length > 0 ? await director.awaitTasks(i.implementerTaskIds) : [];
|
|
6997
|
+
const maxRepairAttempts = clampRepairAttempts(
|
|
6998
|
+
i.maxRepairAttempts ?? (i.repairSubagentId ? 2 : 0)
|
|
6999
|
+
);
|
|
7000
|
+
const repairResults = [];
|
|
7001
|
+
const attempts = [];
|
|
7002
|
+
for (let attempt = 1; ; attempt++) {
|
|
7003
|
+
const gateTaskIds = [];
|
|
7004
|
+
const taskRoleById = /* @__PURE__ */ new Map();
|
|
7005
|
+
if (runVerifier) {
|
|
7006
|
+
const subagentId = await director.spawn(
|
|
7007
|
+
makeQualityGateSubagentConfig("verifier", roster, i.verifierWorktree ?? "auto")
|
|
7008
|
+
);
|
|
7009
|
+
const taskId = await director.assign({
|
|
7010
|
+
id: randomUUID(),
|
|
7011
|
+
subagentId,
|
|
7012
|
+
description: buildVerifierTask(i, {
|
|
7013
|
+
attempt,
|
|
7014
|
+
implementerResults,
|
|
7015
|
+
repairResults,
|
|
7016
|
+
priorAttempts: attempts
|
|
7017
|
+
}),
|
|
7018
|
+
timeoutMs: i.timeoutMs
|
|
7019
|
+
});
|
|
7020
|
+
gateTaskIds.push(taskId);
|
|
7021
|
+
taskRoleById.set(taskId, "verifier");
|
|
7022
|
+
}
|
|
7023
|
+
if (runReviewer) {
|
|
7024
|
+
const subagentId = await director.spawn(
|
|
7025
|
+
makeQualityGateSubagentConfig("reviewer", roster, i.reviewerWorktree ?? "off")
|
|
7026
|
+
);
|
|
7027
|
+
const taskId = await director.assign({
|
|
7028
|
+
id: randomUUID(),
|
|
7029
|
+
subagentId,
|
|
7030
|
+
description: buildReviewerTask(i, {
|
|
7031
|
+
attempt,
|
|
7032
|
+
implementerResults,
|
|
7033
|
+
repairResults,
|
|
7034
|
+
priorAttempts: attempts
|
|
7035
|
+
}),
|
|
7036
|
+
timeoutMs: i.timeoutMs
|
|
7037
|
+
});
|
|
7038
|
+
gateTaskIds.push(taskId);
|
|
7039
|
+
taskRoleById.set(taskId, "reviewer");
|
|
7040
|
+
}
|
|
7041
|
+
const gateResults = await director.awaitTasks(gateTaskIds);
|
|
7042
|
+
const reports = gateResults.map((r) => assessRoleResult(taskRoleById.get(r.taskId), r));
|
|
7043
|
+
const assessment = assessQualityGate(reports);
|
|
7044
|
+
attempts.push({ attempt, reports, ...assessment });
|
|
7045
|
+
if (assessment.passed || !i.repairSubagentId || attempt > maxRepairAttempts) {
|
|
7046
|
+
return {
|
|
7047
|
+
verdict: assessment.verdict,
|
|
7048
|
+
passed: assessment.passed,
|
|
7049
|
+
attempts,
|
|
7050
|
+
repairAttemptsUsed: repairResults.length,
|
|
7051
|
+
implementerResults: implementerResults.map(summarizeTaskResult),
|
|
7052
|
+
nextAction: assessment.passed ? "accept" : i.repairSubagentId && attempt > maxRepairAttempts ? "manual_intervention_or_raise_repair_limit" : "inspect_failures"
|
|
7053
|
+
};
|
|
7054
|
+
}
|
|
7055
|
+
const repairTaskId = await director.assign({
|
|
7056
|
+
id: randomUUID(),
|
|
7057
|
+
subagentId: i.repairSubagentId,
|
|
7058
|
+
description: buildRepairTask(i, attempts[attempts.length - 1], attempt),
|
|
7059
|
+
timeoutMs: i.timeoutMs
|
|
7060
|
+
});
|
|
7061
|
+
const [repairResult] = await director.awaitTasks([repairTaskId]);
|
|
7062
|
+
if (repairResult) repairResults.push(repairResult);
|
|
7063
|
+
if (repairResult?.status !== "success") {
|
|
7064
|
+
return {
|
|
7065
|
+
verdict: "fail",
|
|
7066
|
+
passed: false,
|
|
7067
|
+
attempts,
|
|
7068
|
+
repairAttemptsUsed: repairResults.length,
|
|
7069
|
+
repairResult: repairResult ? summarizeTaskResult(repairResult) : void 0,
|
|
7070
|
+
implementerResults: implementerResults.map(summarizeTaskResult),
|
|
7071
|
+
nextAction: "repair_failed"
|
|
7072
|
+
};
|
|
7073
|
+
}
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
};
|
|
7077
|
+
}
|
|
7078
|
+
function normalizeQualityGateInput(input) {
|
|
7079
|
+
const raw = input ?? {};
|
|
7080
|
+
return {
|
|
7081
|
+
task: typeof raw.task === "string" ? raw.task : void 0,
|
|
7082
|
+
implementerTaskIds: stringArray(raw.implementerTaskIds),
|
|
7083
|
+
repairSubagentId: typeof raw.repairSubagentId === "string" && raw.repairSubagentId.trim() ? raw.repairSubagentId.trim() : void 0,
|
|
7084
|
+
maxRepairAttempts: typeof raw.maxRepairAttempts === "number" ? raw.maxRepairAttempts : void 0,
|
|
7085
|
+
targets: stringArray(raw.targets),
|
|
7086
|
+
commands: stringArray(raw.commands),
|
|
7087
|
+
expected: typeof raw.expected === "string" ? raw.expected : void 0,
|
|
7088
|
+
evidence: typeof raw.evidence === "string" ? raw.evidence : void 0,
|
|
7089
|
+
reviewer: typeof raw.reviewer === "boolean" ? raw.reviewer : void 0,
|
|
7090
|
+
verifier: typeof raw.verifier === "boolean" ? raw.verifier : void 0,
|
|
7091
|
+
timeoutMs: typeof raw.timeoutMs === "number" ? raw.timeoutMs : void 0,
|
|
7092
|
+
reviewerWorktree: normalizeWorktreeOverride(raw.reviewerWorktree),
|
|
7093
|
+
verifierWorktree: normalizeWorktreeOverride(raw.verifierWorktree)
|
|
7094
|
+
};
|
|
7095
|
+
}
|
|
7096
|
+
function stringArray(value) {
|
|
7097
|
+
if (!Array.isArray(value)) return void 0;
|
|
7098
|
+
const strings = value.filter((v) => typeof v === "string" && v.trim().length > 0);
|
|
7099
|
+
return strings.length > 0 ? strings : void 0;
|
|
7100
|
+
}
|
|
7101
|
+
function normalizeWorktreeOverride(value) {
|
|
7102
|
+
if (typeof value === "boolean") return value;
|
|
7103
|
+
if (value === "auto" || value === "required" || value === "off") return value;
|
|
7104
|
+
return void 0;
|
|
7105
|
+
}
|
|
7106
|
+
function clampRepairAttempts(value) {
|
|
7107
|
+
if (!Number.isFinite(value)) return 0;
|
|
7108
|
+
return Math.max(0, Math.min(5, Math.floor(value)));
|
|
7109
|
+
}
|
|
7110
|
+
function makeQualityGateSubagentConfig(role, roster, worktree) {
|
|
7111
|
+
const base = roster?.[role] ?? getAgentDefinition(role)?.config ?? { name: role, role };
|
|
7112
|
+
return {
|
|
7113
|
+
...instantiateRosterConfig2(role, base),
|
|
7114
|
+
worktree
|
|
7115
|
+
};
|
|
7116
|
+
}
|
|
7117
|
+
function buildVerifierTask(input, state) {
|
|
7118
|
+
return [
|
|
7119
|
+
"Run the independent verification gate for this implementation.",
|
|
7120
|
+
"Return Markdown with `## Verdict` and make the first verdict word exactly `pass`, `fail`, or `blocked`.",
|
|
7121
|
+
"Do not edit code. Run the smallest meaningful command set and include exact failures.",
|
|
7122
|
+
"",
|
|
7123
|
+
`Gate attempt: ${state.attempt}`,
|
|
7124
|
+
input.task ? `Original task:
|
|
7125
|
+
${input.task}` : void 0,
|
|
7126
|
+
input.targets?.length ? `Targets:
|
|
7127
|
+
${input.targets.map((t) => `- ${t}`).join("\n")}` : void 0,
|
|
7128
|
+
input.commands?.length ? `Required or suggested commands:
|
|
7129
|
+
${input.commands.map((c) => `- ${c}`).join("\n")}` : void 0,
|
|
7130
|
+
input.expected ? `Expected behavior:
|
|
7131
|
+
${input.expected}` : void 0,
|
|
7132
|
+
input.evidence ? `Known evidence:
|
|
7133
|
+
${input.evidence}` : void 0,
|
|
7134
|
+
taskResultsBlock("Implementer results", state.implementerResults),
|
|
7135
|
+
taskResultsBlock("Repair results so far", state.repairResults),
|
|
7136
|
+
priorAttemptsBlock(state.priorAttempts)
|
|
7137
|
+
].filter((part) => !!part).join("\n\n");
|
|
7138
|
+
}
|
|
7139
|
+
function buildReviewerTask(input, state) {
|
|
7140
|
+
return [
|
|
7141
|
+
"Run independent code review for this implementation.",
|
|
7142
|
+
"Return Markdown with `## Verdict` and make the first verdict phrase exactly `approve`, `request changes`, or `needs verification`.",
|
|
7143
|
+
"Do not edit code. Treat missing proof, vague tests, and uncertainty as blocking until verifier evidence exists.",
|
|
7144
|
+
"",
|
|
7145
|
+
`Gate attempt: ${state.attempt}`,
|
|
7146
|
+
input.task ? `Original task:
|
|
7147
|
+
${input.task}` : void 0,
|
|
7148
|
+
input.targets?.length ? `Targets:
|
|
7149
|
+
${input.targets.map((t) => `- ${t}`).join("\n")}` : void 0,
|
|
7150
|
+
input.expected ? `Expected behavior:
|
|
7151
|
+
${input.expected}` : void 0,
|
|
7152
|
+
input.evidence ? `Known evidence:
|
|
7153
|
+
${input.evidence}` : void 0,
|
|
7154
|
+
taskResultsBlock("Implementer results", state.implementerResults),
|
|
7155
|
+
taskResultsBlock("Repair results so far", state.repairResults),
|
|
7156
|
+
priorAttemptsBlock(state.priorAttempts)
|
|
7157
|
+
].filter((part) => !!part).join("\n\n");
|
|
7158
|
+
}
|
|
7159
|
+
function buildRepairTask(input, attempt, attemptNumber) {
|
|
7160
|
+
return [
|
|
7161
|
+
`Repair the implementation after quality gate attempt ${attemptNumber} failed.`,
|
|
7162
|
+
"Address every must-fix item. Run relevant checks before returning.",
|
|
7163
|
+
"Do not claim done unless verifier/reviewer feedback is resolved.",
|
|
7164
|
+
"",
|
|
7165
|
+
input.task ? `Original task:
|
|
7166
|
+
${input.task}` : void 0,
|
|
7167
|
+
input.targets?.length ? `Targets:
|
|
7168
|
+
${input.targets.map((t) => `- ${t}`).join("\n")}` : void 0,
|
|
7169
|
+
input.commands?.length ? `Commands expected to pass:
|
|
7170
|
+
${input.commands.map((c) => `- ${c}`).join("\n")}` : void 0,
|
|
7171
|
+
input.expected ? `Expected behavior:
|
|
7172
|
+
${input.expected}` : void 0,
|
|
7173
|
+
attempt.mustFix.length ? `Must fix:
|
|
7174
|
+
${attempt.mustFix.map((f) => `- ${f}`).join("\n")}` : void 0,
|
|
7175
|
+
attempt.uncertaintyFlags.length ? `Uncertainty flags to resolve:
|
|
7176
|
+
${attempt.uncertaintyFlags.map((f) => `- ${f}`).join("\n")}` : void 0,
|
|
7177
|
+
`Reviewer/verifier reports:
|
|
7178
|
+
${attempt.reports.map((r) => `### ${r.role} (${r.verdict})
|
|
7179
|
+
${r.summary}`).join("\n\n")}`
|
|
7180
|
+
].filter((part) => !!part).join("\n\n");
|
|
7181
|
+
}
|
|
7182
|
+
function taskResultsBlock(title, results) {
|
|
7183
|
+
if (results.length === 0) return void 0;
|
|
7184
|
+
return `${title}:
|
|
7185
|
+
${results.map((r) => `### ${r.subagentId}/${r.taskId}
|
|
7186
|
+
${summarizeTaskResult(r).summary}`).join("\n\n")}`;
|
|
7187
|
+
}
|
|
7188
|
+
function priorAttemptsBlock(attempts) {
|
|
7189
|
+
if (attempts.length === 0) return void 0;
|
|
7190
|
+
return `Prior quality gate attempts:
|
|
7191
|
+
${attempts.map(
|
|
7192
|
+
(a) => `### Attempt ${a.attempt}
|
|
7193
|
+
${a.reports.map((r) => `- ${r.role}: ${r.verdict}${r.error ? ` (${r.error})` : ""}`).join("\n")}`
|
|
7194
|
+
).join("\n\n")}`;
|
|
7195
|
+
}
|
|
7196
|
+
function summarizeTaskResult(result) {
|
|
7197
|
+
const text = typeof result.result === "string" ? result.result : result.result !== void 0 ? JSON.stringify(result.result, null, 2) : "";
|
|
7198
|
+
const error = result.error ? `${result.error.kind}: ${result.error.message}` : void 0;
|
|
7199
|
+
return {
|
|
7200
|
+
taskId: result.taskId,
|
|
7201
|
+
subagentId: result.subagentId,
|
|
7202
|
+
status: result.status,
|
|
7203
|
+
summary: excerpt(text || error || "(no output)", 4e3),
|
|
7204
|
+
error
|
|
7205
|
+
};
|
|
7206
|
+
}
|
|
7207
|
+
function assessRoleResult(role, result) {
|
|
7208
|
+
const resolvedRole = role ?? (result.subagentId.includes("review") ? "reviewer" : "verifier");
|
|
7209
|
+
const summary = summarizeTaskResult(result);
|
|
7210
|
+
const text = summary.summary;
|
|
7211
|
+
const uncertaintyFlags = extractSection(text, "Uncertainty Flags");
|
|
7212
|
+
if (result.status !== "success") {
|
|
7213
|
+
return {
|
|
7214
|
+
role: resolvedRole,
|
|
7215
|
+
subagentId: result.subagentId,
|
|
7216
|
+
taskId: result.taskId,
|
|
7217
|
+
status: result.status,
|
|
7218
|
+
verdict: "fail",
|
|
7219
|
+
summary: text,
|
|
7220
|
+
uncertaintyFlags,
|
|
7221
|
+
error: summary.error ?? result.status
|
|
7222
|
+
};
|
|
7223
|
+
}
|
|
7224
|
+
return {
|
|
7225
|
+
role: resolvedRole,
|
|
7226
|
+
subagentId: result.subagentId,
|
|
7227
|
+
taskId: result.taskId,
|
|
7228
|
+
status: result.status,
|
|
7229
|
+
verdict: parseQualityVerdict(resolvedRole, text),
|
|
7230
|
+
summary: text,
|
|
7231
|
+
uncertaintyFlags
|
|
7232
|
+
};
|
|
7233
|
+
}
|
|
7234
|
+
function assessQualityGate(reports) {
|
|
7235
|
+
const mustFix = [];
|
|
7236
|
+
const uncertaintyFlags = [];
|
|
7237
|
+
let hasFail = false;
|
|
7238
|
+
let hasInconclusive = false;
|
|
7239
|
+
for (const report of reports) {
|
|
7240
|
+
if (report.verdict === "fail") hasFail = true;
|
|
7241
|
+
if (report.verdict === "inconclusive") hasInconclusive = true;
|
|
7242
|
+
const blocking = extractSection(report.summary, "Must Fix") || extractSection(report.summary, "Failures") || extractSection(report.summary, "Verification Gaps");
|
|
7243
|
+
if (blocking) mustFix.push(`${report.role}: ${excerpt(blocking, 1e3)}`);
|
|
7244
|
+
if (report.uncertaintyFlags) {
|
|
7245
|
+
uncertaintyFlags.push(`${report.role}: ${excerpt(report.uncertaintyFlags, 1e3)}`);
|
|
7246
|
+
}
|
|
7247
|
+
if (report.error) mustFix.push(`${report.role}: ${report.error}`);
|
|
7248
|
+
}
|
|
7249
|
+
if (hasFail) return { verdict: "fail", passed: false, mustFix, uncertaintyFlags };
|
|
7250
|
+
if (hasInconclusive || reports.length === 0) {
|
|
7251
|
+
return { verdict: "inconclusive", passed: false, mustFix, uncertaintyFlags };
|
|
7252
|
+
}
|
|
7253
|
+
return { verdict: "pass", passed: true, mustFix, uncertaintyFlags };
|
|
7254
|
+
}
|
|
7255
|
+
function parseQualityVerdict(role, text) {
|
|
7256
|
+
const normalized = text.toLowerCase();
|
|
7257
|
+
const verdictBlock = normalized.match(/(?:^|\n)\s*(?:#+\s*)?verdict\b[^\n]*(?:\n|:|-)?([\s\S]{0,500})/)?.[0] ?? normalized.slice(0, 1e3);
|
|
7258
|
+
if (role === "reviewer") {
|
|
7259
|
+
if (/\b(request\s+changes|needs\s+verification|reject|rejected|fail|failed|blocked)\b/.test(
|
|
7260
|
+
verdictBlock
|
|
7261
|
+
)) {
|
|
7262
|
+
return "fail";
|
|
7263
|
+
}
|
|
7264
|
+
if (/\b(approve|approved|pass|passed)\b/.test(verdictBlock)) return "pass";
|
|
7265
|
+
if (sectionHasBlockingContent(text, "Must Fix") || sectionHasBlockingContent(text, "Verification Gaps")) {
|
|
7266
|
+
return "fail";
|
|
7267
|
+
}
|
|
7268
|
+
return "inconclusive";
|
|
7269
|
+
}
|
|
7270
|
+
if (/\b(fail|failed|blocked|red)\b/.test(verdictBlock)) return "fail";
|
|
7271
|
+
if (/\b(pass|passed|green|approve|approved)\b/.test(verdictBlock)) return "pass";
|
|
7272
|
+
if (sectionHasBlockingContent(text, "Failures")) return "fail";
|
|
7273
|
+
return "inconclusive";
|
|
7274
|
+
}
|
|
7275
|
+
function sectionHasBlockingContent(text, heading) {
|
|
7276
|
+
const section = extractSection(text, heading);
|
|
7277
|
+
if (!section) return false;
|
|
7278
|
+
return !/^\s*(none|n\/a|no\b|no issues|empty|\(none\))\s*\.?\s*$/i.test(section.trim());
|
|
7279
|
+
}
|
|
7280
|
+
function extractSection(text, heading) {
|
|
7281
|
+
const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7282
|
+
const pattern = new RegExp(
|
|
7283
|
+
`(?:^|\\n)\\s*#{1,6}\\s*${escaped}\\s*\\n([\\s\\S]*?)(?=\\n\\s*#{1,6}\\s+|$)`,
|
|
7284
|
+
"i"
|
|
7285
|
+
);
|
|
7286
|
+
const match = text.match(pattern);
|
|
7287
|
+
const body = match?.[1]?.trim();
|
|
7288
|
+
return body ? body : void 0;
|
|
7289
|
+
}
|
|
7290
|
+
function excerpt(text, max) {
|
|
7291
|
+
if (text.length <= max) return text;
|
|
7292
|
+
return `${text.slice(0, max - 20).trimEnd()}
|
|
7293
|
+
...(truncated)`;
|
|
7294
|
+
}
|
|
6757
7295
|
function makeAssignTool(director) {
|
|
6758
7296
|
const inputSchema = {
|
|
6759
7297
|
type: "object",
|
|
6760
7298
|
properties: {
|
|
6761
7299
|
subagentId: { type: "string", minLength: 1, description: "Target subagent id. Required." },
|
|
6762
|
-
description: {
|
|
6763
|
-
|
|
7300
|
+
description: {
|
|
7301
|
+
type: "string",
|
|
7302
|
+
minLength: 1,
|
|
7303
|
+
description: "The task in natural language \u2014 what you want this subagent to do."
|
|
7304
|
+
},
|
|
7305
|
+
maxToolCalls: {
|
|
7306
|
+
type: "number",
|
|
7307
|
+
minimum: 1,
|
|
7308
|
+
description: "Optional per-task tool-call budget override."
|
|
7309
|
+
},
|
|
6764
7310
|
timeoutMs: { type: "number", minimum: 1, description: "Optional per-task timeout in ms." }
|
|
6765
7311
|
},
|
|
6766
7312
|
required: ["subagentId", "description"]
|
|
@@ -6774,7 +7320,13 @@ function makeAssignTool(director) {
|
|
|
6774
7320
|
inputSchema,
|
|
6775
7321
|
async execute(input) {
|
|
6776
7322
|
const i = input;
|
|
6777
|
-
const task = {
|
|
7323
|
+
const task = {
|
|
7324
|
+
id: randomUUID(),
|
|
7325
|
+
description: i.description,
|
|
7326
|
+
subagentId: i.subagentId,
|
|
7327
|
+
maxToolCalls: i.maxToolCalls,
|
|
7328
|
+
timeoutMs: i.timeoutMs
|
|
7329
|
+
};
|
|
6778
7330
|
const taskId = await director.assign(task);
|
|
6779
7331
|
return { taskId, subagentId: i.subagentId };
|
|
6780
7332
|
}
|
|
@@ -6790,22 +7342,39 @@ function makeAwaitTasksTool(director) {
|
|
|
6790
7342
|
inputSchema: {
|
|
6791
7343
|
type: "object",
|
|
6792
7344
|
properties: {
|
|
6793
|
-
taskIds: {
|
|
6794
|
-
|
|
6795
|
-
|
|
7345
|
+
taskIds: {
|
|
7346
|
+
type: "array",
|
|
7347
|
+
items: { type: "string" },
|
|
7348
|
+
description: "One or more task ids returned by `assign_task`."
|
|
7349
|
+
},
|
|
7350
|
+
mode: {
|
|
7351
|
+
type: "string",
|
|
7352
|
+
enum: ["all", "any"],
|
|
7353
|
+
description: '"all" (default): block until every task completes. "any": return on the first completion with the rest listed as pending.'
|
|
7354
|
+
},
|
|
7355
|
+
timeoutMs: {
|
|
7356
|
+
type: "number",
|
|
7357
|
+
minimum: 1,
|
|
7358
|
+
description: 'mode:"any" only \u2014 return {timedOut:true, completed:[]} if nothing completes within this window instead of blocking.'
|
|
7359
|
+
}
|
|
6796
7360
|
},
|
|
6797
7361
|
required: ["taskIds"]
|
|
6798
7362
|
},
|
|
6799
7363
|
async execute(input) {
|
|
6800
7364
|
const i = input;
|
|
6801
7365
|
if (i.mode === "any") {
|
|
6802
|
-
const r = await director.awaitTasksAny(
|
|
7366
|
+
const r = await director.awaitTasksAny(
|
|
7367
|
+
i.taskIds,
|
|
7368
|
+
i.timeoutMs !== void 0 ? { timeoutMs: i.timeoutMs } : void 0
|
|
7369
|
+
);
|
|
6803
7370
|
return {
|
|
6804
7371
|
mode: "any",
|
|
6805
7372
|
completed: r.completed,
|
|
6806
7373
|
pending: r.pending,
|
|
6807
7374
|
...r.timedOut ? { timedOut: true } : {},
|
|
6808
|
-
...r.pending.length > 0 ? {
|
|
7375
|
+
...r.pending.length > 0 ? {
|
|
7376
|
+
hint: 'Handle the completed results now. Re-call await_tasks with the pending ids (mode:"any") for the next finisher \u2014 or assign new work to the now-idle subagent first.'
|
|
7377
|
+
} : {}
|
|
6809
7378
|
};
|
|
6810
7379
|
}
|
|
6811
7380
|
const results = await director.awaitTasks(i.taskIds);
|
|
@@ -6823,9 +7392,17 @@ function makeAskTool(director) {
|
|
|
6823
7392
|
inputSchema: {
|
|
6824
7393
|
type: "object",
|
|
6825
7394
|
properties: {
|
|
6826
|
-
subagentId: {
|
|
7395
|
+
subagentId: {
|
|
7396
|
+
type: "string",
|
|
7397
|
+
minLength: 1,
|
|
7398
|
+
description: "Subagent to ask. Must be a previously spawned id."
|
|
7399
|
+
},
|
|
6827
7400
|
question: { type: "string", minLength: 1, description: "The question or instruction." },
|
|
6828
|
-
timeoutMs: {
|
|
7401
|
+
timeoutMs: {
|
|
7402
|
+
type: "number",
|
|
7403
|
+
minimum: 1,
|
|
7404
|
+
description: "Optional timeout in ms (default 30s)."
|
|
7405
|
+
}
|
|
6829
7406
|
},
|
|
6830
7407
|
required: ["subagentId", "question"]
|
|
6831
7408
|
},
|
|
@@ -6871,7 +7448,10 @@ function makeAskResultTool(director) {
|
|
|
6871
7448
|
const i = input;
|
|
6872
7449
|
const value = director.largeAnswerStore.retrieveAnswer(i.key);
|
|
6873
7450
|
if (value === void 0) {
|
|
6874
|
-
return {
|
|
7451
|
+
return {
|
|
7452
|
+
ok: false,
|
|
7453
|
+
error: `No stored answer found for key "${i.key}" \u2014 it may have been cleared or the key is invalid.`
|
|
7454
|
+
};
|
|
6875
7455
|
}
|
|
6876
7456
|
return { ok: true, value };
|
|
6877
7457
|
}
|
|
@@ -6887,8 +7467,16 @@ function makeRollUpTool(director) {
|
|
|
6887
7467
|
inputSchema: {
|
|
6888
7468
|
type: "object",
|
|
6889
7469
|
properties: {
|
|
6890
|
-
taskIds: {
|
|
6891
|
-
|
|
7470
|
+
taskIds: {
|
|
7471
|
+
type: "array",
|
|
7472
|
+
items: { type: "string" },
|
|
7473
|
+
description: "Completed task ids to aggregate."
|
|
7474
|
+
},
|
|
7475
|
+
style: {
|
|
7476
|
+
type: "string",
|
|
7477
|
+
enum: ["markdown", "json"],
|
|
7478
|
+
description: "Output flavor \u2014 markdown (default) or json."
|
|
7479
|
+
}
|
|
6892
7480
|
},
|
|
6893
7481
|
required: ["taskIds"]
|
|
6894
7482
|
},
|
|
@@ -6906,7 +7494,11 @@ function makeTerminateTool(director) {
|
|
|
6906
7494
|
permission: "auto",
|
|
6907
7495
|
mutating: true,
|
|
6908
7496
|
capabilities: [ToolCapabilities.SUBAGENT_SPAWN],
|
|
6909
|
-
inputSchema: {
|
|
7497
|
+
inputSchema: {
|
|
7498
|
+
type: "object",
|
|
7499
|
+
properties: { subagentId: { type: "string", description: "Subagent to abort." } },
|
|
7500
|
+
required: ["subagentId"]
|
|
7501
|
+
},
|
|
6910
7502
|
async execute(input) {
|
|
6911
7503
|
const i = input;
|
|
6912
7504
|
await director.terminate(i.subagentId);
|
|
@@ -6924,7 +7516,10 @@ function makeTerminateAllTool(director) {
|
|
|
6924
7516
|
inputSchema: { type: "object", properties: {}, required: [] },
|
|
6925
7517
|
async execute() {
|
|
6926
7518
|
await director.terminateAll();
|
|
6927
|
-
return {
|
|
7519
|
+
return {
|
|
7520
|
+
ok: true,
|
|
7521
|
+
message: `Fleet shutdown complete \u2014 all subagents stopped, pending tasks drained.`
|
|
7522
|
+
};
|
|
6928
7523
|
}
|
|
6929
7524
|
};
|
|
6930
7525
|
}
|
|
@@ -6966,7 +7561,12 @@ function makeFleetTool(director) {
|
|
|
6966
7561
|
return {
|
|
6967
7562
|
action: "status",
|
|
6968
7563
|
subagents: base.subagents,
|
|
6969
|
-
coordinatorStats: stats ? {
|
|
7564
|
+
coordinatorStats: stats ? {
|
|
7565
|
+
total: stats.total,
|
|
7566
|
+
running: stats.running,
|
|
7567
|
+
idle: stats.idle,
|
|
7568
|
+
stopped: stats.stopped
|
|
7569
|
+
} : void 0,
|
|
6970
7570
|
pending: fleetStatus?.pending ?? [],
|
|
6971
7571
|
usage: fm?.snapshot()
|
|
6972
7572
|
};
|
|
@@ -6999,7 +7599,10 @@ function makeFleetTool(director) {
|
|
|
6999
7599
|
case "session": {
|
|
7000
7600
|
const subagentId = i.subagentId;
|
|
7001
7601
|
if (!subagentId) {
|
|
7002
|
-
return {
|
|
7602
|
+
return {
|
|
7603
|
+
action: "session",
|
|
7604
|
+
error: 'fleet: subagentId is required for action: "session"'
|
|
7605
|
+
};
|
|
7003
7606
|
}
|
|
7004
7607
|
const result = await director.readSession(subagentId, i.tail);
|
|
7005
7608
|
if (!result) {
|
|
@@ -7011,7 +7614,9 @@ function makeFleetTool(director) {
|
|
|
7011
7614
|
return { action: "session", ...result };
|
|
7012
7615
|
}
|
|
7013
7616
|
default:
|
|
7014
|
-
return {
|
|
7617
|
+
return {
|
|
7618
|
+
error: `fleet: unknown action "${action}". Valid: status, usage, health, session.`
|
|
7619
|
+
};
|
|
7015
7620
|
}
|
|
7016
7621
|
}
|
|
7017
7622
|
};
|
|
@@ -7487,14 +8092,18 @@ var ROLE_TO_PHASE = (() => {
|
|
|
7487
8092
|
function phaseForRole(role) {
|
|
7488
8093
|
return role ? ROLE_TO_PHASE[role] : void 0;
|
|
7489
8094
|
}
|
|
7490
|
-
function
|
|
8095
|
+
function resolveModelMatrixResolution(matrix, role) {
|
|
7491
8096
|
if (!matrix) return void 0;
|
|
7492
|
-
if (role && matrix[role]) return matrix[role];
|
|
8097
|
+
if (role && matrix[role]) return { entry: matrix[role], source: "role", key: role };
|
|
7493
8098
|
const phase = phaseForRole(role);
|
|
7494
|
-
if (phase && matrix[phase]) return matrix[phase];
|
|
7495
|
-
if (matrix["*"]) return matrix["*"];
|
|
8099
|
+
if (phase && matrix[phase]) return { entry: matrix[phase], source: "phase", key: phase };
|
|
8100
|
+
if (matrix["*"]) return { entry: matrix["*"], source: "default", key: "*" };
|
|
7496
8101
|
return void 0;
|
|
7497
8102
|
}
|
|
8103
|
+
function roleNeedsIndependentReviewModel(role) {
|
|
8104
|
+
if (!role) return false;
|
|
8105
|
+
return role === "reviewer" || phaseForRole(role) === "review";
|
|
8106
|
+
}
|
|
7498
8107
|
|
|
7499
8108
|
// src/coordination/coordinator/error-classifier.ts
|
|
7500
8109
|
init_errors();
|
|
@@ -8518,48 +9127,201 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
8518
9127
|
}
|
|
8519
9128
|
};
|
|
8520
9129
|
|
|
8521
|
-
// src/coordination/
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
this.
|
|
8531
|
-
this.
|
|
8532
|
-
this.
|
|
9130
|
+
// src/coordination/worktree-task-runner.ts
|
|
9131
|
+
init_error();
|
|
9132
|
+
var WorktreeIntegrationError = class extends Error {
|
|
9133
|
+
taskId;
|
|
9134
|
+
subagentId;
|
|
9135
|
+
branch;
|
|
9136
|
+
conflictFiles;
|
|
9137
|
+
constructor(message, opts) {
|
|
9138
|
+
super(message);
|
|
9139
|
+
this.name = "WorktreeIntegrationError";
|
|
9140
|
+
this.taskId = opts.taskId;
|
|
9141
|
+
this.subagentId = opts.subagentId;
|
|
9142
|
+
this.branch = opts.branch;
|
|
9143
|
+
this.conflictFiles = opts.conflictFiles;
|
|
8533
9144
|
}
|
|
8534
9145
|
};
|
|
8535
|
-
var
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
9146
|
+
var MUTATING_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
9147
|
+
"write",
|
|
9148
|
+
"edit",
|
|
9149
|
+
"replace",
|
|
9150
|
+
"patch",
|
|
9151
|
+
"task",
|
|
9152
|
+
"plan",
|
|
9153
|
+
"design",
|
|
9154
|
+
"document",
|
|
9155
|
+
"scaffold",
|
|
9156
|
+
"bash",
|
|
9157
|
+
"exec",
|
|
9158
|
+
"lint",
|
|
9159
|
+
"format",
|
|
9160
|
+
"typecheck",
|
|
9161
|
+
"test",
|
|
9162
|
+
"install"
|
|
9163
|
+
]);
|
|
9164
|
+
var MUTATING_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
9165
|
+
ToolCapabilities.FS_WRITE,
|
|
9166
|
+
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
9167
|
+
ToolCapabilities.SHELL_ARBITRARY,
|
|
9168
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
9169
|
+
ToolCapabilities.SHELL_EXEC,
|
|
9170
|
+
ToolCapabilities.PACKAGE_INSTALL
|
|
9171
|
+
]);
|
|
9172
|
+
function subagentNeedsWorktree(config) {
|
|
9173
|
+
if (config.provider === "acp") return false;
|
|
9174
|
+
if (config.tools?.some((tool) => MUTATING_TOOL_NAMES.has(tool))) return true;
|
|
9175
|
+
if (config.allowedCapabilities?.some((cap) => MUTATING_CAPABILITIES.has(cap))) return true;
|
|
9176
|
+
return !config.tools && !config.allowedCapabilities;
|
|
9177
|
+
}
|
|
9178
|
+
function resolveSubagentWorktreeDecision(config, policy) {
|
|
9179
|
+
if (policy?.enabled === false || policy?.mode === "off") return "off";
|
|
9180
|
+
if (config.worktree === false || config.worktree === "off") return "off";
|
|
9181
|
+
if (config.worktree === true || config.worktree === "required") return "required";
|
|
9182
|
+
const needsIsolation = subagentNeedsWorktree(config);
|
|
9183
|
+
if (!needsIsolation) return "off";
|
|
9184
|
+
return policy?.mode === "required" ? "required" : "optional";
|
|
9185
|
+
}
|
|
9186
|
+
function wrapSubagentRunnerWithWorktrees(opts) {
|
|
9187
|
+
return async (task, ctx) => {
|
|
9188
|
+
const decision = resolveSubagentWorktreeDecision(ctx.config, opts.policy);
|
|
9189
|
+
if (decision === "off") return opts.runner(task, ctx);
|
|
9190
|
+
if (!opts.worktrees) {
|
|
9191
|
+
if (decision === "required") {
|
|
9192
|
+
throw new WorktreeIntegrationError(
|
|
9193
|
+
"worktree isolation is required but no WorktreeManager is configured",
|
|
9194
|
+
{
|
|
9195
|
+
taskId: task.id,
|
|
9196
|
+
subagentId: ctx.subagentId
|
|
9197
|
+
}
|
|
9198
|
+
);
|
|
9199
|
+
}
|
|
9200
|
+
return opts.runner(task, ctx);
|
|
9201
|
+
}
|
|
9202
|
+
const handle = await opts.worktrees.allocate(task.id, {
|
|
9203
|
+
slugHint: worktreeSlugHint(task, ctx.config),
|
|
9204
|
+
ownerLabel: worktreeOwnerLabel(task, ctx.config)
|
|
9205
|
+
});
|
|
9206
|
+
if (handle.status !== "active") {
|
|
9207
|
+
emitUpdate(opts, task, ctx, handle, {
|
|
9208
|
+
status: "fallback",
|
|
9209
|
+
error: handle.lastError ?? "worktree allocation failed"
|
|
9210
|
+
});
|
|
9211
|
+
if (decision === "required") {
|
|
9212
|
+
throw new WorktreeIntegrationError(
|
|
9213
|
+
`worktree allocation failed: ${handle.lastError ?? "unknown error"}`,
|
|
9214
|
+
{
|
|
9215
|
+
taskId: task.id,
|
|
9216
|
+
subagentId: ctx.subagentId,
|
|
9217
|
+
branch: handle.branch
|
|
9218
|
+
}
|
|
9219
|
+
);
|
|
9220
|
+
}
|
|
9221
|
+
return opts.runner(task, ctx);
|
|
9222
|
+
}
|
|
9223
|
+
emitUpdate(opts, task, ctx, handle, { status: "allocated" });
|
|
9224
|
+
const isolatedConfig = { ...ctx.config, cwd: handle.dir };
|
|
9225
|
+
const isolatedCtx = { ...ctx, config: isolatedConfig };
|
|
9226
|
+
let outcome;
|
|
9227
|
+
try {
|
|
9228
|
+
outcome = await opts.runner(task, isolatedCtx);
|
|
9229
|
+
} catch (err) {
|
|
9230
|
+
await parkFailedWorktree(opts, task, ctx, handle, err);
|
|
9231
|
+
throw err;
|
|
9232
|
+
}
|
|
9233
|
+
await integrateSuccessfulWorktree(opts, task, ctx, isolatedConfig, handle);
|
|
9234
|
+
return outcome;
|
|
9235
|
+
};
|
|
9236
|
+
}
|
|
9237
|
+
async function integrateSuccessfulWorktree(opts, task, ctx, config, handle) {
|
|
9238
|
+
const committed = await opts.worktrees.commitAll(
|
|
9239
|
+
handle,
|
|
9240
|
+
`subagent(${config.role ?? config.name}): ${task.id}`
|
|
9241
|
+
);
|
|
9242
|
+
if (!committed.committed) {
|
|
9243
|
+
await opts.worktrees.release(handle, { keep: false });
|
|
9244
|
+
emitUpdate(opts, task, ctx, handle, { status: "released" });
|
|
9245
|
+
return;
|
|
8547
9246
|
}
|
|
8548
|
-
};
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
9247
|
+
emitUpdate(opts, task, ctx, handle, { status: "committed", commitSha: handle.sha });
|
|
9248
|
+
if (opts.policy?.autoMerge === false) {
|
|
9249
|
+
await opts.worktrees.release(handle, { keep: true });
|
|
9250
|
+
emitUpdate(opts, task, ctx, handle, { status: "kept", commitSha: handle.sha });
|
|
9251
|
+
return;
|
|
9252
|
+
}
|
|
9253
|
+
const merged = await opts.worktrees.merge(handle, {
|
|
9254
|
+
squash: true,
|
|
9255
|
+
message: `merge subagent ${ctx.subagentId} task ${task.id} (squash)`,
|
|
9256
|
+
...opts.conflictResolver ? {
|
|
9257
|
+
resolve: (info) => opts.conflictResolver({
|
|
9258
|
+
task,
|
|
9259
|
+
config,
|
|
9260
|
+
conflictFiles: info.conflictFiles,
|
|
9261
|
+
cwd: info.cwd
|
|
9262
|
+
})
|
|
9263
|
+
} : {}
|
|
9264
|
+
});
|
|
9265
|
+
if (!merged.ok) {
|
|
9266
|
+
await opts.worktrees.release(handle, { keep: true }).catch(() => void 0);
|
|
9267
|
+
const conflictFiles = merged.conflictFiles ?? [];
|
|
9268
|
+
emitUpdate(opts, task, ctx, handle, {
|
|
9269
|
+
status: merged.conflict ? "conflict" : "failed",
|
|
9270
|
+
commitSha: handle.sha,
|
|
9271
|
+
conflictFiles,
|
|
9272
|
+
error: merged.stderr || (merged.conflict ? "merge conflict" : "worktree merge failed")
|
|
9273
|
+
});
|
|
9274
|
+
throw new WorktreeIntegrationError(
|
|
9275
|
+
merged.conflict ? `worktree merge conflict${conflictFiles.length ? `: ${conflictFiles.join(", ")}` : ""}` : `worktree merge failed: ${merged.stderr ?? "unknown error"}`,
|
|
9276
|
+
{
|
|
9277
|
+
taskId: task.id,
|
|
9278
|
+
subagentId: ctx.subagentId,
|
|
9279
|
+
branch: handle.branch,
|
|
9280
|
+
conflictFiles
|
|
9281
|
+
}
|
|
8556
9282
|
);
|
|
8557
|
-
this.name = "FleetContextOverflowError";
|
|
8558
|
-
this.kind = "max_context_load";
|
|
8559
|
-
this.limit = limit;
|
|
8560
|
-
this.observed = observed;
|
|
8561
9283
|
}
|
|
8562
|
-
};
|
|
9284
|
+
await opts.worktrees.release(handle, { keep: false });
|
|
9285
|
+
emitUpdate(opts, task, ctx, handle, { status: "merged", commitSha: handle.sha });
|
|
9286
|
+
}
|
|
9287
|
+
async function parkFailedWorktree(opts, task, ctx, handle, err) {
|
|
9288
|
+
let committed = false;
|
|
9289
|
+
try {
|
|
9290
|
+
const res = await opts.worktrees.commitAll(
|
|
9291
|
+
handle,
|
|
9292
|
+
`subagent(${ctx.config.role ?? ctx.config.name}) failed: ${task.id}`
|
|
9293
|
+
);
|
|
9294
|
+
committed = res.committed;
|
|
9295
|
+
} catch {
|
|
9296
|
+
committed = false;
|
|
9297
|
+
}
|
|
9298
|
+
const keep = (opts.policy?.keepFailed ?? true) && committed;
|
|
9299
|
+
await opts.worktrees.release(handle, { keep }).catch(() => void 0);
|
|
9300
|
+
emitUpdate(opts, task, ctx, handle, {
|
|
9301
|
+
status: keep ? "kept" : "released",
|
|
9302
|
+
commitSha: handle.sha,
|
|
9303
|
+
error: toErrorMessage(err)
|
|
9304
|
+
});
|
|
9305
|
+
}
|
|
9306
|
+
function emitUpdate(opts, task, ctx, handle, patch) {
|
|
9307
|
+
opts.onUpdate?.({
|
|
9308
|
+
taskId: task.id,
|
|
9309
|
+
subagentId: ctx.subagentId,
|
|
9310
|
+
handleId: handle.id,
|
|
9311
|
+
dir: handle.dir,
|
|
9312
|
+
branch: handle.branch,
|
|
9313
|
+
baseBranch: handle.baseBranch,
|
|
9314
|
+
...patch
|
|
9315
|
+
});
|
|
9316
|
+
}
|
|
9317
|
+
function worktreeSlugHint(task, config) {
|
|
9318
|
+
return `${config.role ?? config.name ?? "subagent"}-${task.id}`;
|
|
9319
|
+
}
|
|
9320
|
+
function worktreeOwnerLabel(task, config) {
|
|
9321
|
+
const who = config.name || config.role || "Subagent";
|
|
9322
|
+
const brief = task.description.trim().replace(/\s+/g, " ").slice(0, 80);
|
|
9323
|
+
return brief ? `${who}: ${brief}` : who;
|
|
9324
|
+
}
|
|
8563
9325
|
|
|
8564
9326
|
// src/coordination/director.ts
|
|
8565
9327
|
var Director = class _Director {
|
|
@@ -8691,6 +9453,8 @@ var Director = class _Director {
|
|
|
8691
9453
|
/** Resolves task descriptions back from `assign()` so completion events
|
|
8692
9454
|
* can also carry a human-readable title. */
|
|
8693
9455
|
taskDescriptions = /* @__PURE__ */ new Map();
|
|
9456
|
+
/** Latest worktree state per task id (allocated/committed/merged/conflict/etc.). */
|
|
9457
|
+
taskWorktrees = /* @__PURE__ */ new Map();
|
|
8694
9458
|
/** Snapshot of which subagent owns each task — drives state-checkpoint
|
|
8695
9459
|
* status updates without re-walking the manifest. */
|
|
8696
9460
|
taskOwners = /* @__PURE__ */ new Map();
|
|
@@ -8804,9 +9568,16 @@ var Director = class _Director {
|
|
|
8804
9568
|
(id) => this.subagentMeta.get(id)
|
|
8805
9569
|
);
|
|
8806
9570
|
}
|
|
9571
|
+
const runner = opts.runner && (opts.worktrees || opts.worktreePolicy) ? wrapSubagentRunnerWithWorktrees({
|
|
9572
|
+
runner: opts.runner,
|
|
9573
|
+
worktrees: opts.worktrees,
|
|
9574
|
+
policy: opts.worktreePolicy,
|
|
9575
|
+
conflictResolver: opts.worktreeConflictResolver,
|
|
9576
|
+
onUpdate: (update) => this.recordWorktreeTaskUpdate(update)
|
|
9577
|
+
}) : opts.runner;
|
|
8807
9578
|
this.coordinator = new DefaultMultiAgentCoordinator(
|
|
8808
9579
|
{ ...opts.config, coordinatorId: this.id },
|
|
8809
|
-
{ runner
|
|
9580
|
+
{ runner, sessionId: () => this.currentSessionId() }
|
|
8810
9581
|
);
|
|
8811
9582
|
this.coordinator.setFleetBus(this.fleet);
|
|
8812
9583
|
this.fleetManager?.setCoordinator(this.coordinator);
|
|
@@ -9176,6 +9947,17 @@ var Director = class _Director {
|
|
|
9176
9947
|
clearTimeout(this.manifestTimer);
|
|
9177
9948
|
this.manifestTimer = null;
|
|
9178
9949
|
}
|
|
9950
|
+
recordWorktreeTaskUpdate(update) {
|
|
9951
|
+
this.taskWorktrees.set(update.taskId, update);
|
|
9952
|
+
const owner = this.taskOwners.get(update.taskId) ?? update.subagentId;
|
|
9953
|
+
const entry = this.manifestEntries.get(owner);
|
|
9954
|
+
if (entry) {
|
|
9955
|
+
entry.worktrees = { ...entry.worktrees ?? {}, [update.taskId]: update };
|
|
9956
|
+
}
|
|
9957
|
+
this.stateCheckpoint?.recordTaskWorktree(update.taskId, update);
|
|
9958
|
+
this.fleetManager?.recordTaskWorktree(update);
|
|
9959
|
+
if (!this.fleetManager) this.scheduleManifest();
|
|
9960
|
+
}
|
|
9179
9961
|
/**
|
|
9180
9962
|
* Spawn a subagent. Identical to the coordinator's `spawn()` but
|
|
9181
9963
|
* captures provider/model metadata for the usage aggregator and
|
|
@@ -9197,7 +9979,8 @@ var Director = class _Director {
|
|
|
9197
9979
|
const config = { ...callerConfig };
|
|
9198
9980
|
if (!config.model && this.modelMatrix) {
|
|
9199
9981
|
const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
|
|
9200
|
-
const
|
|
9982
|
+
const resolution = resolveModelMatrixResolution(matrix, config.role);
|
|
9983
|
+
const entry = resolution?.source === "default" && roleNeedsIndependentReviewModel(config.role) ? void 0 : resolution?.entry;
|
|
9201
9984
|
if (entry?.model) {
|
|
9202
9985
|
config.model = entry.model;
|
|
9203
9986
|
if (entry.provider) config.provider = entry.provider;
|
|
@@ -9417,13 +10200,15 @@ var Director = class _Director {
|
|
|
9417
10200
|
// success/failure state.
|
|
9418
10201
|
results: e.taskIds.map((tid) => {
|
|
9419
10202
|
const r = this.completed.get(tid);
|
|
10203
|
+
const worktree = e.worktrees?.[tid];
|
|
9420
10204
|
return r ? {
|
|
9421
10205
|
taskId: tid,
|
|
9422
10206
|
status: r.status,
|
|
9423
10207
|
iterations: r.iterations,
|
|
9424
10208
|
toolCalls: r.toolCalls,
|
|
9425
|
-
durationMs: r.durationMs
|
|
9426
|
-
|
|
10209
|
+
durationMs: r.durationMs,
|
|
10210
|
+
...worktree ? { worktree } : {}
|
|
10211
|
+
} : { taskId: tid, status: "pending", ...worktree ? { worktree } : {} };
|
|
9427
10212
|
})
|
|
9428
10213
|
})),
|
|
9429
10214
|
usage: this.usage.snapshot()
|
|
@@ -9862,6 +10647,7 @@ var Director = class _Director {
|
|
|
9862
10647
|
makeAskTool(this),
|
|
9863
10648
|
makeAskResultTool(this),
|
|
9864
10649
|
makeRollUpTool(this),
|
|
10650
|
+
makeQualityGateTool(this, effectiveRoster),
|
|
9865
10651
|
makeTerminateTool(this),
|
|
9866
10652
|
makeTerminateAllTool(this),
|
|
9867
10653
|
makeFleetTool(this),
|
|
@@ -17319,11 +18105,16 @@ ${errorDetails}`,
|
|
|
17319
18105
|
ctx
|
|
17320
18106
|
);
|
|
17321
18107
|
if (post.additionalContext) {
|
|
17322
|
-
|
|
18108
|
+
if (post.contextAs === "separate") {
|
|
18109
|
+
const block = { type: "text", text: post.additionalContext };
|
|
18110
|
+
ctx.state.appendMessage({ role: "user", content: [block] });
|
|
18111
|
+
} else {
|
|
18112
|
+
const appended = `
|
|
17323
18113
|
|
|
17324
18114
|
${post.additionalContext}`;
|
|
17325
|
-
|
|
17326
|
-
|
|
18115
|
+
result = { ...result, content: `${result.content}${appended}` };
|
|
18116
|
+
budget = Math.max(0, budget - Buffer.byteLength(appended, "utf8"));
|
|
18117
|
+
}
|
|
17327
18118
|
}
|
|
17328
18119
|
}
|
|
17329
18120
|
const outputChars = typeof result.content === "string" ? result.content.length : 0;
|
|
@@ -17557,6 +18348,11 @@ ${progressTail}`;
|
|
|
17557
18348
|
break;
|
|
17558
18349
|
}
|
|
17559
18350
|
if (ev.type === "partial_output" && typeof ev.text === "string") {
|
|
18351
|
+
if (ev.data?.["livePreview"] === true) {
|
|
18352
|
+
flushProgressTail(true);
|
|
18353
|
+
emitProgress(ev);
|
|
18354
|
+
continue;
|
|
18355
|
+
}
|
|
17560
18356
|
if (!headComplete) {
|
|
17561
18357
|
const remaining = _ToolExecutor.PROGRESS_HEAD_CHARS - progressHead.length;
|
|
17562
18358
|
if (ev.text.length <= remaining) {
|
|
@@ -17602,9 +18398,9 @@ ${progressTail}`;
|
|
|
17602
18398
|
let content = `Tool "${use.name}" received arguments that were not a valid JSON object, so they could not be parsed. Re-issue the call with the arguments encoded as a single well-formed JSON object matching the tool's input schema.`;
|
|
17603
18399
|
if (raw) {
|
|
17604
18400
|
const max = 800;
|
|
17605
|
-
const
|
|
18401
|
+
const excerpt2 = raw.length > max ? `${raw.slice(0, max)}\u2026 (truncated, ${raw.length} chars total)` : raw;
|
|
17606
18402
|
content += ` Common cause: a string field (e.g. code in old_string/new_string) contains literal newlines, quotes, or backslashes that must be JSON-escaped, or the payload was cut off mid-stream. The raw arguments received were:
|
|
17607
|
-
${
|
|
18403
|
+
${excerpt2}`;
|
|
17608
18404
|
}
|
|
17609
18405
|
return {
|
|
17610
18406
|
type: "tool_result",
|
|
@@ -18376,7 +19172,7 @@ var CODEX_MODELS = [
|
|
|
18376
19172
|
{
|
|
18377
19173
|
id: "gpt-5.3-codex-spark",
|
|
18378
19174
|
name: "GPT-5.3 Codex Spark",
|
|
18379
|
-
description: "Ultra-fast coding model."
|
|
19175
|
+
description: "Ultra-fast coding model (research preview)."
|
|
18380
19176
|
}
|
|
18381
19177
|
];
|
|
18382
19178
|
var BY_ID = new Map(CODEX_MODELS.map((m) => [m.id, m]));
|
|
@@ -18653,6 +19449,7 @@ var FAMILY_BY_NPM = {
|
|
|
18653
19449
|
"@ai-sdk/gateway": "openai-compatible",
|
|
18654
19450
|
"@aihubmix/ai-sdk-provider": "openai-compatible",
|
|
18655
19451
|
"venice-ai-sdk-provider": "openai-compatible",
|
|
19452
|
+
"@ai-sdk/deepseek": "openai-compatible",
|
|
18656
19453
|
"@ai-sdk/google": "google"
|
|
18657
19454
|
};
|
|
18658
19455
|
function classifyFamily(npm) {
|
|
@@ -18752,7 +19549,9 @@ var DefaultModelsRegistry = class {
|
|
|
18752
19549
|
}
|
|
18753
19550
|
if (overlayAvailable) {
|
|
18754
19551
|
console.warn(
|
|
18755
|
-
`ModelsRegistry: models.dev unavailable (${toErrorMessage(
|
|
19552
|
+
`ModelsRegistry: models.dev unavailable (${toErrorMessage(
|
|
19553
|
+
err
|
|
19554
|
+
)}); serving curated overlay only.`
|
|
18756
19555
|
);
|
|
18757
19556
|
return {};
|
|
18758
19557
|
}
|
|
@@ -18829,11 +19628,12 @@ var DefaultModelsRegistry = class {
|
|
|
18829
19628
|
method: "GET",
|
|
18830
19629
|
headers: { accept: "application/json" }
|
|
18831
19630
|
});
|
|
18832
|
-
if (!res.ok)
|
|
18833
|
-
|
|
18834
|
-
|
|
18835
|
-
|
|
18836
|
-
|
|
19631
|
+
if (!res.ok)
|
|
19632
|
+
throw new FetchError({
|
|
19633
|
+
message: `HTTP ${res.status}`,
|
|
19634
|
+
status: res.status,
|
|
19635
|
+
context: { url: this.url, op: "refreshModels" }
|
|
19636
|
+
});
|
|
18837
19637
|
const json = await res.json();
|
|
18838
19638
|
const envelope = {
|
|
18839
19639
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -22236,6 +23036,15 @@ var WorktreeManager = class {
|
|
|
22236
23036
|
async merge(handle, opts = {}) {
|
|
22237
23037
|
const squash = opts.squash ?? true;
|
|
22238
23038
|
this.setStatus(handle, "merging");
|
|
23039
|
+
const status = await this.runGit(
|
|
23040
|
+
["status", "--porcelain", "--untracked-files=no"],
|
|
23041
|
+
this.projectRoot
|
|
23042
|
+
);
|
|
23043
|
+
if (status.stdout.trim().length > 0) {
|
|
23044
|
+
const reason = "working tree has uncommitted changes \u2014 commit or stash first";
|
|
23045
|
+
this.fail(handle, reason);
|
|
23046
|
+
return { ok: false, stderr: reason };
|
|
23047
|
+
}
|
|
22239
23048
|
const checkout = await this.runGit(["checkout", handle.baseBranch], this.projectRoot);
|
|
22240
23049
|
if (checkout.code !== 0) {
|
|
22241
23050
|
this.fail(handle, checkout.stderr || `checkout ${handle.baseBranch} failed`);
|
|
@@ -22463,7 +23272,12 @@ ${merged.stderr}`);
|
|
|
22463
23272
|
const fromIndex = await this.unmergedFiles();
|
|
22464
23273
|
const conflictFiles = [.../* @__PURE__ */ new Set([...fromOutput, ...fromIndex])];
|
|
22465
23274
|
await this.runGit(["reset", "--hard", "HEAD"], this.projectRoot).catch(() => void 0);
|
|
22466
|
-
return {
|
|
23275
|
+
return {
|
|
23276
|
+
ok: false,
|
|
23277
|
+
conflict: true,
|
|
23278
|
+
conflictFiles,
|
|
23279
|
+
reason: merged.stderr || "merge conflict"
|
|
23280
|
+
};
|
|
22467
23281
|
}
|
|
22468
23282
|
const idArgs = await this.identityArgs(this.projectRoot);
|
|
22469
23283
|
const commit = await this.runGit(
|
|
@@ -22565,7 +23379,11 @@ ${merged.stderr}`);
|
|
|
22565
23379
|
if (shas.length === 0) return { ok: true, reverted: 0, reason: "nothing to revert" };
|
|
22566
23380
|
const status = await this.runGit(["status", "--porcelain"], this.projectRoot);
|
|
22567
23381
|
if (status.stdout.trim().length > 0) {
|
|
22568
|
-
return {
|
|
23382
|
+
return {
|
|
23383
|
+
ok: false,
|
|
23384
|
+
reverted: 0,
|
|
23385
|
+
reason: "working tree has uncommitted changes \u2014 commit or stash first"
|
|
23386
|
+
};
|
|
22569
23387
|
}
|
|
22570
23388
|
const co = await this.runGit(["checkout", baseBranch], this.projectRoot);
|
|
22571
23389
|
if (co.code !== 0) {
|