@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.
Files changed (55) hide show
  1. package/dist/{agent-subagent-runner-DyOSK2Wy.d.ts → agent-subagent-runner-Dksf0QVU.d.ts} +15 -4
  2. package/dist/{config-CN-vdDEI.d.ts → config-BCAEJicb.d.ts} +33 -1
  3. package/dist/coordination/index.d.ts +147 -48
  4. package/dist/coordination/index.js +1244 -274
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/defaults/index.d.ts +18 -18
  7. package/dist/defaults/index.js +991 -173
  8. package/dist/defaults/index.js.map +1 -1
  9. package/dist/{director-state-BfeCUbmk.d.ts → director-state-B_GPa78Q.d.ts} +14 -0
  10. package/dist/{events-C5hLU0AT.d.ts → events-CovDylaH.d.ts} +2 -1
  11. package/dist/execution/index.d.ts +10 -10
  12. package/dist/execution/index.js +69 -3
  13. package/dist/execution/index.js.map +1 -1
  14. package/dist/extension/index.d.ts +4 -4
  15. package/dist/{global-mailbox-CHM1QkCi.d.ts → global-mailbox-CNfgvRw3.d.ts} +1 -1
  16. package/dist/{goal-store-BLGZq16t.d.ts → goal-store-CBd749M4.d.ts} +1 -1
  17. package/dist/hq/index.d.ts +4 -4
  18. package/dist/{index-C1E-_71T.d.ts → index-D5HB7WkV.d.ts} +4 -3
  19. package/dist/index.d.ts +3005 -2704
  20. package/dist/index.js +51869 -49930
  21. package/dist/index.js.map +1 -1
  22. package/dist/infrastructure/index.d.ts +3 -3
  23. package/dist/kernel/index.d.ts +7 -7
  24. package/dist/kernel/index.js.map +1 -1
  25. package/dist/{mcp-servers-EKRAcJDi.d.ts → mcp-servers-DJYwZXyH.d.ts} +1 -1
  26. package/dist/models/index.d.ts +2 -2
  27. package/dist/models/index.js +111 -21
  28. package/dist/models/index.js.map +1 -1
  29. package/dist/{multi-agent-coordinator-IpHCuHUa.d.ts → multi-agent-coordinator-4DC31QWu.d.ts} +1 -1
  30. package/dist/{null-fleet-bus-ZBkKGR9j.d.ts → null-fleet-bus-DuvA8PIR.d.ts} +74 -6
  31. package/dist/observability/index.d.ts +1 -1
  32. package/dist/{parallel-eternal-engine-Cc2CNgmv.d.ts → parallel-eternal-engine-BwcA0bAt.d.ts} +4 -4
  33. package/dist/{pipeline-E7_jqGyV.d.ts → pipeline-DM-CPTUs.d.ts} +1 -1
  34. package/dist/{provider-model-resolve-KgIdiEQ6.d.ts → provider-model-resolve-hNUbewMd.d.ts} +1 -1
  35. package/dist/{provider-runner-DxWXNHL1.d.ts → provider-runner-Bz4GyUqo.d.ts} +1 -1
  36. package/dist/sdd/index.d.ts +6 -6
  37. package/dist/sdd/index.js +76 -2
  38. package/dist/sdd/index.js.map +1 -1
  39. package/dist/{session-event-bridge-vA2fLV58.d.ts → session-event-bridge-Bkp7ORfc.d.ts} +2 -2
  40. package/dist/skills/index.d.ts +84 -84
  41. package/dist/storage/index.d.ts +168 -168
  42. package/dist/storage/index.js +19 -7
  43. package/dist/storage/index.js.map +1 -1
  44. package/dist/{todos-checkpoint-B1QVkoa8.d.ts → todos-checkpoint-addCdQ9f.d.ts} +2 -2
  45. package/dist/{tool-executor-BWtYwuPI.d.ts → tool-executor-B3spV_Q6.d.ts} +7 -7
  46. package/dist/tools/index.d.ts +1 -1
  47. package/dist/types/index.d.ts +33 -33
  48. package/dist/types/index.js.map +1 -1
  49. package/dist/utils/index.d.ts +113 -113
  50. package/dist/{worktree-manager-C6CIilTT.d.ts → worktree-manager-BBXdJq-Y.d.ts} +1 -1
  51. package/instructions/agents/reviewer.md +25 -0
  52. package/instructions/agents/verifier.md +24 -0
  53. package/instructions/coordination/director-preamble.md +24 -2
  54. package/instructions/coordination/subagent-baseline.md +8 -1
  55. package/package.json +1 -1
@@ -2052,20 +2052,20 @@ function compactDiff(diff) {
2052
2052
  merged.push(current);
2053
2053
  }
2054
2054
  }
2055
- const excerpt = [];
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
- excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);
2060
+ excerpt2.push(`[serializer omitted ${omitted} diff line(s)]`);
2061
2061
  }
2062
2062
  for (let j = start; j <= end; j++) {
2063
- excerpt.push(lines[j] ?? "");
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) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);
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
- excerpt.join("\n")
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
- (t) => t.taskId === taskId ? { ...t, ...patch } : t
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: { type: "string", description: "Roster role id. When set, the spawn uses the matching config from the roster and ignores other fields." },
6674
- description: { type: "string", 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." },
6675
- name: { type: "string", description: "Display name for the subagent. Used as a fallback when description-based dispatch does not resolve a role." },
6676
- provider: { type: "string", description: 'Provider id (e.g. "anthropic", "openai"). Defaults to the leader provider when omitted.' },
6677
- model: { type: "string", description: "Model id within the provider. Defaults to the leader model when omitted." },
6678
- systemPromptOverride: { type: "string", description: "Extra prompt text appended after the role-base prompt." },
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: { type: "number", minimum: 1, description: "Hard wall-clock cap in milliseconds. Defaults to none (idle timeout is the default reaper)." },
6683
- idleTimeoutMs: { type: "number", minimum: 1, 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." },
6684
- maxTokens: { type: "number", minimum: 1, description: "Maximum total tokens (input + output) the subagent may use." }
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) return { error: `unknown role "${role}". roster has: ${Object.keys(roster).join(", ")}` };
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") cfg.systemPromptOverride = i.systemPromptOverride;
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 { subagentId, provider: cfg.provider, model: cfg.model, name: cfg.name, role: cfg.role };
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: { type: "string", minLength: 1, description: "The task in natural language \u2014 what you want this subagent to do." },
6763
- maxToolCalls: { type: "number", minimum: 1, description: "Optional per-task tool-call budget override." },
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 = { id: randomUUID(), description: i.description, subagentId: i.subagentId, maxToolCalls: i.maxToolCalls, timeoutMs: i.timeoutMs };
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: { type: "array", items: { type: "string" }, description: "One or more task ids returned by `assign_task`." },
6794
- mode: { type: "string", enum: ["all", "any"], description: '"all" (default): block until every task completes. "any": return on the first completion with the rest listed as pending.' },
6795
- timeoutMs: { type: "number", minimum: 1, description: 'mode:"any" only \u2014 return {timedOut:true, completed:[]} if nothing completes within this window instead of blocking.' }
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(i.taskIds, i.timeoutMs !== void 0 ? { timeoutMs: i.timeoutMs } : void 0);
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 ? { 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.' } : {}
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: { type: "string", minLength: 1, description: "Subagent to ask. Must be a previously spawned id." },
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: { type: "number", minimum: 1, description: "Optional timeout in ms (default 30s)." }
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 { ok: false, error: `No stored answer found for key "${i.key}" \u2014 it may have been cleared or the key is invalid.` };
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: { type: "array", items: { type: "string" }, description: "Completed task ids to aggregate." },
6891
- style: { type: "string", enum: ["markdown", "json"], description: "Output flavor \u2014 markdown (default) or json." }
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: { type: "object", properties: { subagentId: { type: "string", description: "Subagent to abort." } }, required: ["subagentId"] },
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 { ok: true, message: `Fleet shutdown complete \u2014 all subagents stopped, pending tasks drained.` };
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 ? { total: stats.total, running: stats.running, idle: stats.idle, stopped: stats.stopped } : void 0,
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 { action: "session", error: 'fleet: subagentId is required for action: "session"' };
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 { error: `fleet: unknown action "${action}". Valid: status, usage, health, session.` };
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 resolveModelMatrix(matrix, role) {
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/director/director-errors.ts
8522
- var FleetSpawnBudgetError = class extends Error {
8523
- kind;
8524
- limit;
8525
- observed;
8526
- constructor(kind, limit, observed, message) {
8527
- 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}`;
8528
- super(message ?? defaultMsg);
8529
- this.name = "FleetSpawnBudgetError";
8530
- this.kind = kind;
8531
- this.limit = limit;
8532
- this.observed = observed;
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 FleetCostCapError = class extends Error {
8536
- kind;
8537
- limit;
8538
- observed;
8539
- constructor(limit, observed) {
8540
- super(
8541
- `Director cost cap exceeded: total fleet spend ${observed.toFixed(4)} exceeds maxCostUsd ${limit.toFixed(4)}`
8542
- );
8543
- this.name = "FleetCostCapError";
8544
- this.kind = "max_cost_usd";
8545
- this.limit = limit;
8546
- this.observed = observed;
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
- var FleetContextOverflowError = class extends Error {
8550
- kind;
8551
- limit;
8552
- observed;
8553
- constructor(limit, observed) {
8554
- super(
8555
- `Leader context overflow: leader has ${observed} tokens in flight (limit: ${limit}). Compact the leader context before spawning more subagents.`
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: opts.runner, sessionId: () => this.currentSessionId() }
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 entry = resolveModelMatrix(matrix, config.role);
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
- } : { taskId: tid, status: "pending" };
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
- const appended = `
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
- result = { ...result, content: `${result.content}${appended}` };
17326
- budget = Math.max(0, budget - Buffer.byteLength(appended, "utf8"));
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 excerpt = raw.length > max ? `${raw.slice(0, max)}\u2026 (truncated, ${raw.length} chars total)` : raw;
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
- ${excerpt}`;
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(err)}); serving curated overlay only.`
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) throw new FetchError({
18833
- message: `HTTP ${res.status}`,
18834
- status: res.status,
18835
- context: { url: this.url, op: "refreshModels" }
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 { ok: false, conflict: true, conflictFiles, reason: merged.stderr || "merge conflict" };
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 { ok: false, reverted: 0, reason: "working tree has uncommitted changes \u2014 commit or stash first" };
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) {