@wrongstack/core 0.54.1 → 0.66.13

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 (63) hide show
  1. package/dist/{agent-bridge-Dnhw4tnM.d.ts → agent-bridge-D-j6OOBT.d.ts} +1 -1
  2. package/dist/agent-subagent-runner-DRZ9-NnR.d.ts +1042 -0
  3. package/dist/{compactor-Duhsf0ge.d.ts → compactor-D_ExJajC.d.ts} +1 -1
  4. package/dist/{config-bht0txXS.d.ts → config--86aHSln.d.ts} +112 -2
  5. package/dist/{context-DtPKqKYV.d.ts → context-y87Jc5ei.d.ts} +8 -8
  6. package/dist/coordination/index.d.ts +12 -12
  7. package/dist/coordination/index.js +87 -69
  8. package/dist/coordination/index.js.map +1 -1
  9. package/dist/defaults/index.d.ts +22 -22
  10. package/dist/defaults/index.js +365 -174
  11. package/dist/defaults/index.js.map +1 -1
  12. package/dist/{events-CbHTS4ZZ.d.ts → events-CIplI98R.d.ts} +20 -1
  13. package/dist/execution/index.d.ts +16 -385
  14. package/dist/execution/index.js +129 -61
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/extension/index.d.ts +7 -7
  17. package/dist/goal-store-C7jcumEh.d.ts +96 -0
  18. package/dist/index-DKUvyTvV.d.ts +560 -0
  19. package/dist/{index-ge5F2dnc.d.ts → index-b5uhfTSl.d.ts} +10 -8
  20. package/dist/index.d.ts +59 -33
  21. package/dist/index.js +1138 -733
  22. package/dist/index.js.map +1 -1
  23. package/dist/infrastructure/index.d.ts +6 -6
  24. package/dist/infrastructure/index.js +1 -1
  25. package/dist/infrastructure/index.js.map +1 -1
  26. package/dist/kernel/index.d.ts +9 -9
  27. package/dist/kernel/index.js +3 -1
  28. package/dist/kernel/index.js.map +1 -1
  29. package/dist/{mcp-servers-DE6gzBry.d.ts → mcp-servers-DwoNBf6r.d.ts} +3 -3
  30. package/dist/models/index.d.ts +2 -2
  31. package/dist/{multi-agent-coordinator-CjNX4uBD.d.ts → multi-agent-coordinator-CWnH-CiX.d.ts} +10 -2
  32. package/dist/{null-fleet-bus-BNiSlTna.d.ts → null-fleet-bus-VApKRxcp.d.ts} +6 -7
  33. package/dist/observability/index.d.ts +2 -2
  34. package/dist/parallel-eternal-engine-0UwotoSx.d.ts +483 -0
  35. package/dist/{path-resolver-Bax85amb.d.ts → path-resolver-DVkEcIw8.d.ts} +2 -2
  36. package/dist/{permission-Drm7LpPo.d.ts → permission-C1A5whY5.d.ts} +17 -1
  37. package/dist/{permission-policy-CU6sqWxF.d.ts → permission-policy-B2dK-T5N.d.ts} +28 -7
  38. package/dist/{plan-templates-CLRcurWN.d.ts → plan-templates-Bprrzhbu.d.ts} +4 -4
  39. package/dist/{provider-runner-BikCxGCx.d.ts → provider-runner-mXvXGSIw.d.ts} +3 -3
  40. package/dist/{retry-policy-Chtlvr5b.d.ts → retry-policy-CG3qvH_e.d.ts} +1 -1
  41. package/dist/sdd/index.d.ts +9 -9
  42. package/dist/sdd/index.js +59 -52
  43. package/dist/sdd/index.js.map +1 -1
  44. package/dist/security/index.d.ts +3 -3
  45. package/dist/security/index.js +144 -33
  46. package/dist/security/index.js.map +1 -1
  47. package/dist/{selector-BvSPdJj6.d.ts → selector-RvBR_YRW.d.ts} +1 -1
  48. package/dist/session-event-bridge-CDHxcmQU.d.ts +93 -0
  49. package/dist/{session-reader-BGhzMir4.d.ts → session-reader-BIpwM60D.d.ts} +1 -1
  50. package/dist/storage/index.d.ts +7 -6
  51. package/dist/{system-prompt-dtzV_mLm.d.ts → system-prompt-b61lOd49.d.ts} +32 -2
  52. package/dist/types/index.d.ts +23 -14
  53. package/dist/types/index.js +62 -6
  54. package/dist/types/index.js.map +1 -1
  55. package/dist/utils/index.d.ts +2 -2
  56. package/dist/utils/index.js.map +1 -1
  57. package/package.json +1 -1
  58. package/skills/multi-agent/SKILL.md +0 -2
  59. package/dist/agent-subagent-runner-By7jruZ_.d.ts +0 -182
  60. package/dist/goal-store-DwcTDDiX.d.ts +0 -188
  61. package/dist/index-CI271MjL.d.ts +0 -903
  62. package/dist/multi-agent-BmC_xiog.d.ts +0 -554
  63. package/dist/tool-executor-CgU0yWpB.d.ts +0 -110
package/dist/index.js CHANGED
@@ -93,7 +93,7 @@ async function renameWithRetry(from, to) {
93
93
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
94
94
  throw err;
95
95
  }
96
- await new Promise((resolve12) => setTimeout(resolve12, delays[i]));
96
+ await new Promise((resolve13) => setTimeout(resolve13, delays[i]));
97
97
  }
98
98
  }
99
99
  throw lastErr;
@@ -884,7 +884,9 @@ var TOKENS = {
884
884
  /** Optional git-worktree lifecycle manager (per-phase isolation in AutoPhase). */
885
885
  WorktreeManager: t("WorktreeManager"),
886
886
  /** Optional global Brain arbiter for policy/decision escalation. */
887
- BrainArbiter: t("BrainArbiter")
887
+ BrainArbiter: t("BrainArbiter"),
888
+ /** Lifecycle hook registry (shell + in-process hooks). */
889
+ HookRegistry: t("HookRegistry")
888
890
  };
889
891
 
890
892
  // src/kernel/run-controller.ts
@@ -2058,7 +2060,7 @@ var DefaultPathResolver = class {
2058
2060
  var NETWORK_ERR_RE = /ECONN|ETIMEDOUT|ETIME|ENOTFOUND|EAI_AGAIN|fetch failed/i;
2059
2061
 
2060
2062
  // src/execution/error-handler.ts
2061
- var CONTEXT_OVERFLOW_RE = /context|too long|tokens/i;
2063
+ var CONTEXT_OVERFLOW_RE = /context|too long|tokens|exceeds the context window|context window/i;
2062
2064
  function buildRecoveryStrategies(opts) {
2063
2065
  return [
2064
2066
  {
@@ -2066,7 +2068,7 @@ function buildRecoveryStrategies(opts) {
2066
2068
  compactor: opts?.compactor,
2067
2069
  async attempt(err, ctx) {
2068
2070
  if (!(err instanceof ProviderError)) return null;
2069
- if (err.status !== 413 && !CONTEXT_OVERFLOW_RE.test(err.message)) return null;
2071
+ if (err.status !== 413 && !isContextOverflowError(err)) return null;
2070
2072
  if (this.compactor) {
2071
2073
  try {
2072
2074
  const report = await this.compactor.compact(ctx, { aggressive: true });
@@ -2129,6 +2131,14 @@ function buildRecoveryStrategies(opts) {
2129
2131
  ];
2130
2132
  }
2131
2133
  var DEFAULT_RECOVERY_STRATEGIES = buildRecoveryStrategies();
2134
+ function isContextOverflowError(err) {
2135
+ return CONTEXT_OVERFLOW_RE.test([
2136
+ err.message,
2137
+ err.body?.message,
2138
+ err.body?.type,
2139
+ err.body?.raw
2140
+ ].filter(Boolean).join("\n"));
2141
+ }
2132
2142
  var DefaultErrorHandler = class {
2133
2143
  strategies;
2134
2144
  constructor(strategies = DEFAULT_RECOVERY_STRATEGIES) {
@@ -2145,7 +2155,7 @@ var DefaultErrorHandler = class {
2145
2155
  if (err.status === 429) return { kind: "rate_limit", retryable: true };
2146
2156
  if (err.status === 529) return { kind: "overloaded", retryable: true };
2147
2157
  if (err.status >= 500) return { kind: "server", retryable: true };
2148
- if (err.status === 413 || CONTEXT_OVERFLOW_RE.test(err.message)) {
2158
+ if (err.status === 413 || isContextOverflowError(err)) {
2149
2159
  return { kind: "context_overflow", retryable: false };
2150
2160
  }
2151
2161
  if (err.status >= 400) return { kind: "client", retryable: false };
@@ -3049,7 +3059,7 @@ var InMemoryAgentBridge = class {
3049
3059
  );
3050
3060
  }
3051
3061
  this.inflightGuards.add(correlationId);
3052
- return new Promise((resolve12, reject) => {
3062
+ return new Promise((resolve13, reject) => {
3053
3063
  const timer = setTimeout(() => {
3054
3064
  this.inflightGuards.delete(correlationId);
3055
3065
  this.pendingRequests.delete(correlationId);
@@ -3061,7 +3071,7 @@ var InMemoryAgentBridge = class {
3061
3071
  return;
3062
3072
  }
3063
3073
  this.pendingRequests.set(correlationId, {
3064
- resolve: resolve12,
3074
+ resolve: resolve13,
3065
3075
  reject,
3066
3076
  timer
3067
3077
  });
@@ -3254,11 +3264,11 @@ function validateAgainstSchema(value, schema) {
3254
3264
  walk2(value, schema, "", errors);
3255
3265
  return { ok: errors.length === 0, errors };
3256
3266
  }
3257
- function walk2(value, schema, path36, errors) {
3267
+ function walk2(value, schema, path37, errors) {
3258
3268
  if (schema.enum !== void 0) {
3259
3269
  if (!schema.enum.some((e) => deepEqual(e, value))) {
3260
3270
  errors.push({
3261
- path: path36 || "<root>",
3271
+ path: path37 || "<root>",
3262
3272
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
3263
3273
  });
3264
3274
  return;
@@ -3267,7 +3277,7 @@ function walk2(value, schema, path36, errors) {
3267
3277
  if (typeof schema.type === "string") {
3268
3278
  if (!checkType(value, schema.type)) {
3269
3279
  errors.push({
3270
- path: path36 || "<root>",
3280
+ path: path37 || "<root>",
3271
3281
  message: `expected ${schema.type}, got ${describeType(value)}`
3272
3282
  });
3273
3283
  return;
@@ -3277,19 +3287,19 @@ function walk2(value, schema, path36, errors) {
3277
3287
  const obj = value;
3278
3288
  for (const req of schema.required ?? []) {
3279
3289
  if (!(req in obj)) {
3280
- errors.push({ path: joinPath(path36, req), message: "required property missing" });
3290
+ errors.push({ path: joinPath(path37, req), message: "required property missing" });
3281
3291
  }
3282
3292
  }
3283
3293
  if (schema.properties) {
3284
3294
  for (const [key, subSchema] of Object.entries(schema.properties)) {
3285
3295
  if (key in obj) {
3286
- walk2(obj[key], subSchema, joinPath(path36, key), errors);
3296
+ walk2(obj[key], subSchema, joinPath(path37, key), errors);
3287
3297
  }
3288
3298
  }
3289
3299
  }
3290
3300
  }
3291
3301
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
3292
- value.forEach((item, i) => walk2(item, schema.items, `${path36}[${i}]`, errors));
3302
+ value.forEach((item, i) => walk2(item, schema.items, `${path37}[${i}]`, errors));
3293
3303
  }
3294
3304
  }
3295
3305
  function checkType(value, type) {
@@ -3385,6 +3395,29 @@ function createToolOutputSerializer(opts = {}) {
3385
3395
  }
3386
3396
  return { serialize, enforceCap, capBytes };
3387
3397
  }
3398
+ function truncateForEvent(content, max = 400) {
3399
+ if (!content) return "";
3400
+ return content.length <= max ? content : `${content.slice(0, max - 1)}\u2026`;
3401
+ }
3402
+ function sizeSignals(toolName, content) {
3403
+ if (!content || content.length === 0) {
3404
+ return { outputBytes: 0, outputTokens: 0, outputLines: void 0 };
3405
+ }
3406
+ const outputBytes = Buffer.byteLength(content, "utf8");
3407
+ const outputTokens = Math.max(1, Math.round(outputBytes / 3.5));
3408
+ let outputLines;
3409
+ if (toolName === "read") {
3410
+ const lineRe = /^\s*\d+→/gm;
3411
+ let count = 0;
3412
+ while (lineRe.exec(content) !== null) count++;
3413
+ if (count > 0) outputLines = count;
3414
+ } else if (toolName === "bash" || toolName === "shell" || toolName === "grep" || toolName === "logs") {
3415
+ let nl = 0;
3416
+ for (let i = 0; i < content.length; i++) if (content.charCodeAt(i) === 10) nl++;
3417
+ outputLines = nl + (content.endsWith("\n") ? 0 : 1);
3418
+ }
3419
+ return { outputBytes, outputTokens, outputLines };
3420
+ }
3388
3421
 
3389
3422
  // src/execution/tool-executor.ts
3390
3423
  var ToolExecutor = class {
@@ -3415,8 +3448,9 @@ var ToolExecutor = class {
3415
3448
  */
3416
3449
  async executeBatch(toolUses, ctx, strategy) {
3417
3450
  let budget = this.opts.perIterationOutputCapBytes ?? 1e5;
3418
- const runOne = async (use) => {
3451
+ const runOne = async (use0) => {
3419
3452
  const start = Date.now();
3453
+ let use = use0;
3420
3454
  const tool = this.registry.get(use.name);
3421
3455
  if (!tool) {
3422
3456
  const result = this.unknownToolResult(use, () => this.registry.list().map((t2) => t2.name));
@@ -3449,10 +3483,36 @@ Please call the tool again with arguments that match its inputSchema. You can us
3449
3483
  budget = this.decrementBudget(result, budget);
3450
3484
  return { result, tool, durationMs: Date.now() - start };
3451
3485
  }
3486
+ if (this.opts.hookRunner?.has("PreToolUse")) {
3487
+ const pre = await this.opts.hookRunner.preToolUse(tool.name, use.input, ctx);
3488
+ if (pre.block) {
3489
+ const result = this.blockedByHookResult(use, pre.reason);
3490
+ budget = this.decrementBudget(result, budget);
3491
+ return { result, tool, durationMs: Date.now() - start };
3492
+ }
3493
+ if (pre.input) {
3494
+ const reval = validateAgainstSchema(pre.input, tool.inputSchema);
3495
+ if (!reval.ok) {
3496
+ const errorDetails = reval.errors.map((e) => ` - ${e.path || "input"}: ${e.message}`).join("\n");
3497
+ const result = {
3498
+ type: "tool_result",
3499
+ tool_use_id: use.id,
3500
+ content: `A PreToolUse hook rewrote the arguments for "${tool.name}" into an invalid shape.
3501
+
3502
+ Validation errors:
3503
+ ${errorDetails}`,
3504
+ is_error: true
3505
+ };
3506
+ budget = this.decrementBudget(result, budget);
3507
+ return { result, tool, durationMs: Date.now() - start };
3508
+ }
3509
+ use = { ...use, input: pre.input };
3510
+ }
3511
+ }
3452
3512
  const decision = await this.opts.permissionPolicy.evaluate(tool, use.input, ctx);
3453
3513
  let effectivePermission = decision.permission;
3454
3514
  const policy = this.opts.permissionPolicy;
3455
- const yolo = policy.getYolo?.() === true || policy.getForceAllYolo?.() === true;
3515
+ const yolo = policy.getYolo?.() === true || policy.getYoloDestructive?.() === true || policy.getForceAllYolo?.() === true;
3456
3516
  if (toolDangerousCaps.length > 0 && effectivePermission === "auto" && !yolo) {
3457
3517
  effectivePermission = "confirm";
3458
3518
  }
@@ -3495,7 +3555,20 @@ Please call the tool again with arguments that match its inputSchema. You can us
3495
3555
  "tool.has_dangerous_capabilities": toolCapsForAudit.length > 0
3496
3556
  });
3497
3557
  try {
3498
- const result = await this.executeTool(tool, use, ctx, budget);
3558
+ let result = await this.executeTool(tool, use, ctx, budget);
3559
+ if (this.opts.hookRunner?.has("PostToolUse")) {
3560
+ const post = await this.opts.hookRunner.postToolUse(
3561
+ tool.name,
3562
+ use.input,
3563
+ { content: String(result.content), isError: !!result.is_error },
3564
+ ctx
3565
+ );
3566
+ if (post.additionalContext) {
3567
+ result = { ...result, content: `${result.content}
3568
+
3569
+ ${post.additionalContext}` };
3570
+ }
3571
+ }
3499
3572
  budget = this.decrementBudget(result, budget);
3500
3573
  span?.setAttribute("tool.is_error", !!result.is_error);
3501
3574
  span?.setAttribute(
@@ -3684,6 +3757,14 @@ ${excerpt}`;
3684
3757
  is_error: true
3685
3758
  };
3686
3759
  }
3760
+ blockedByHookResult(use, reason) {
3761
+ return {
3762
+ type: "tool_result",
3763
+ tool_use_id: use.id,
3764
+ content: `Tool "${use.name}" was blocked by a PreToolUse hook: ${reason ?? "no reason given"}`,
3765
+ is_error: true
3766
+ };
3767
+ }
3687
3768
  decrementBudget(result, budget) {
3688
3769
  const contentBytes = typeof result.content === "string" ? Buffer.byteLength(result.content, "utf8") : Buffer.byteLength(JSON.stringify(result.content), "utf8");
3689
3770
  return Math.max(0, budget - contentBytes);
@@ -6933,12 +7014,97 @@ var DirectorStateCheckpoint = class {
6933
7014
  }
6934
7015
  };
6935
7016
  init_atomic_write();
7017
+ var DESTRUCTIVE_BASH_PATTERNS = [
7018
+ /\bgit\s+(?:clean\s+-[^\s]*[xdf]|reset\s+--hard)\b/i,
7019
+ /\b(?:drop|truncate)\s+(?:table|database|schema)\b/i,
7020
+ /\bdelete\s+from\b/i,
7021
+ /\b(?:mkfs|format|diskpart|shutdown|reboot)\b/i,
7022
+ /\bchmod\s+-R\s+777\b/i,
7023
+ /\bchown\s+-R\b/i,
7024
+ /\b(?:curl|wget)\b.*\|\s*(?:sh|bash|zsh|pwsh|powershell)\b/i,
7025
+ /\b(?:powershell|pwsh)\b.*(?:-encodedcommand|-enc)\b/i,
7026
+ /:\(\)\s*\{\s*:\|:&\s*}\s*;/
7027
+ ];
7028
+ var PROJECT_ESCAPE_PATTERN = /(?:^|[\s"'])\.\.(?:[\\/]|$)/;
7029
+ var ABSOLUTE_PATH_PATTERN = /(?:^|[\s"'])(?:~[\\/]|\/[A-Za-z0-9_.-]|[A-Za-z]:[\\/])/;
7030
+ var SHELL_OPERATORS = /* @__PURE__ */ new Set(["&&", "||", "|", ";", ">", ">>", "<", "2>", "2>>"]);
7031
+ function getInputString(input, key) {
7032
+ if (!input || typeof input !== "object") return void 0;
7033
+ const value = input[key];
7034
+ return typeof value === "string" ? value : void 0;
7035
+ }
7036
+ function pathLooksInsideProject(rawPath, projectRoot) {
7037
+ if (!projectRoot) return false;
7038
+ if (rawPath === "~" || rawPath.startsWith("~/") || rawPath.startsWith("~\\")) return false;
7039
+ const resolved = path6.resolve(projectRoot, rawPath);
7040
+ const relative8 = path6.relative(projectRoot, resolved);
7041
+ return !!relative8 && !relative8.startsWith("..") && !path6.isAbsolute(relative8);
7042
+ }
7043
+ function tokenizeShell(command) {
7044
+ return command.match(/"[^"]*"|'[^']*'|\S+/g)?.map((token) => token.replace(/^['"]|['"]$/g, "")) ?? [];
7045
+ }
7046
+ function pathTokenIsOutsideProject(token, projectRoot) {
7047
+ if (!token || SHELL_OPERATORS.has(token) || token.startsWith("-")) return false;
7048
+ if (token === "/" || token === "~" || token === "." || token === "..") return token !== ".";
7049
+ if (token.includes("*")) return true;
7050
+ if (token.startsWith("..") || token.includes("../") || token.includes("..\\")) return true;
7051
+ if (path6.isAbsolute(token) || token.startsWith("~/")) return !pathLooksInsideProject(token, projectRoot);
7052
+ return false;
7053
+ }
7054
+ function hasDangerousDeleteTarget(tokens, start, projectRoot) {
7055
+ const targets = tokens.slice(start).filter((token) => !token.startsWith("-") && !SHELL_OPERATORS.has(token));
7056
+ if (targets.length === 0) return true;
7057
+ return targets.some((target) => pathTokenIsOutsideProject(target, projectRoot));
7058
+ }
7059
+ function hasDestructiveDelete(command, projectRoot) {
7060
+ const tokens = tokenizeShell(command);
7061
+ for (let i = 0; i < tokens.length; i++) {
7062
+ const token = tokens[i]?.toLowerCase();
7063
+ if (!token) continue;
7064
+ if (token === "rm") {
7065
+ const args = tokens.slice(i + 1);
7066
+ const recursiveOrForce = args.some(
7067
+ (arg) => /^-[^-]*[rf]/i.test(arg) || arg === "--recursive" || arg === "--force"
7068
+ );
7069
+ if (recursiveOrForce && hasDangerousDeleteTarget(tokens, i + 1, projectRoot)) return true;
7070
+ }
7071
+ if (token === "rmdir" || token === "rd") {
7072
+ const args = tokens.slice(i + 1);
7073
+ const recursive = args.some((arg) => arg.toLowerCase() === "/s");
7074
+ if (recursive && hasDangerousDeleteTarget(tokens, i + 1, projectRoot)) return true;
7075
+ }
7076
+ if (token === "del" || token === "erase") {
7077
+ if (hasDangerousDeleteTarget(tokens, i + 1, projectRoot)) return true;
7078
+ }
7079
+ if (token === "remove-item") {
7080
+ const args = tokens.slice(i + 1).map((arg) => arg.toLowerCase());
7081
+ const recursiveOrForce = args.includes("-recurse") || args.includes("-force");
7082
+ if (recursiveOrForce && hasDangerousDeleteTarget(tokens, i + 1, projectRoot)) return true;
7083
+ }
7084
+ }
7085
+ return false;
7086
+ }
7087
+ function isClearlyDestructiveBashCommand(command, projectRoot) {
7088
+ const trimmed = command.trim();
7089
+ if (!trimmed) return false;
7090
+ if (hasDestructiveDelete(trimmed, projectRoot)) return true;
7091
+ if (DESTRUCTIVE_BASH_PATTERNS.some((pattern) => pattern.test(trimmed))) return true;
7092
+ if (/\bcd\s+(?:\.\.|~|\/|[A-Za-z]:[\\/])/i.test(trimmed)) return true;
7093
+ if (PROJECT_ESCAPE_PATTERN.test(trimmed)) return true;
7094
+ const absolute = trimmed.match(ABSOLUTE_PATH_PATTERN)?.[0]?.trim().replace(/^['"]|['"]$/g, "");
7095
+ if (absolute && !pathLooksInsideProject(absolute, projectRoot)) return true;
7096
+ return false;
7097
+ }
7098
+
7099
+ // src/security/permission-policy.ts
6936
7100
  var DefaultPermissionPolicy = class {
6937
7101
  policy = {};
6938
7102
  loaded = false;
6939
7103
  trustFile;
6940
7104
  yolo;
6941
- forceAllYolo;
7105
+ yoloDestructive;
7106
+ /** When true, destructive ops still require confirmation even in YOLO mode. */
7107
+ confirmDestructive;
6942
7108
  /**
6943
7109
  * Session-scoped "soft deny" map. When the user presses 'n' (block once),
6944
7110
  * the tool+pattern is added here. If the LLM retries in the same session,
@@ -6971,7 +7137,8 @@ var DefaultPermissionPolicy = class {
6971
7137
  constructor(opts) {
6972
7138
  this.trustFile = opts.trustFile;
6973
7139
  this.yolo = opts.yolo ?? false;
6974
- this.forceAllYolo = opts.forceAllYolo ?? false;
7140
+ this.yoloDestructive = opts.yoloDestructive ?? opts.forceAllYolo ?? false;
7141
+ this.confirmDestructive = opts.confirmDestructive ?? false;
6975
7142
  this.promptDelegate = opts.promptDelegate;
6976
7143
  }
6977
7144
  /**
@@ -6991,13 +7158,29 @@ var DefaultPermissionPolicy = class {
6991
7158
  getYolo() {
6992
7159
  return this.yolo;
6993
7160
  }
6994
- /** Toggle force-all-YOLO at runtime. */
7161
+ /** Toggle the destructive YOLO override at runtime. */
7162
+ setYoloDestructive(enabled) {
7163
+ this.yoloDestructive = enabled;
7164
+ }
7165
+ /** Check whether the destructive YOLO override is active. */
7166
+ getYoloDestructive() {
7167
+ return this.yoloDestructive;
7168
+ }
7169
+ /** Toggle destructive confirmation gate (only meaningful when yolo is active). */
7170
+ setConfirmDestructive(enabled) {
7171
+ this.confirmDestructive = enabled;
7172
+ }
7173
+ /** Check whether destructive confirmation gate is active. */
7174
+ getConfirmDestructive() {
7175
+ return this.confirmDestructive;
7176
+ }
7177
+ /** @deprecated Use `setYoloDestructive`. */
6995
7178
  setForceAllYolo(enabled) {
6996
- this.forceAllYolo = enabled;
7179
+ this.setYoloDestructive(enabled);
6997
7180
  }
6998
- /** Check whether force-all-YOLO is active. */
7181
+ /** @deprecated Use `getYoloDestructive`. */
6999
7182
  getForceAllYolo() {
7000
- return this.forceAllYolo;
7183
+ return this.getYoloDestructive();
7001
7184
  }
7002
7185
  async reload() {
7003
7186
  try {
@@ -7044,29 +7227,28 @@ var DefaultPermissionPolicy = class {
7044
7227
  return { permission: "auto", source: "trust" };
7045
7228
  }
7046
7229
  if (this.yolo) {
7047
- if (tool.riskTier === "destructive" && !this.forceAllYolo) {
7048
- if (this.promptDelegate) {
7049
- const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
7050
- if (decision === "always") {
7051
- await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
7052
- return {
7053
- permission: "auto",
7054
- source: "user",
7055
- reason: "destructive yolo always-allowed"
7056
- };
7057
- }
7058
- if (decision === "deny") {
7059
- await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
7060
- return { permission: "deny", source: "user", reason: "user denied destructive yolo" };
7230
+ if (this.confirmDestructive) {
7231
+ const destructive = this.isDestructiveYoloCall(tool, input, ctx);
7232
+ if (destructive) {
7233
+ if (this.promptDelegate) {
7234
+ const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
7235
+ if (decision === "always") {
7236
+ await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
7237
+ return { permission: "auto", source: "user", reason: "destructive yolo always-allowed" };
7238
+ }
7239
+ if (decision === "deny") {
7240
+ await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
7241
+ return { permission: "deny", source: "user", reason: "user denied destructive yolo" };
7242
+ }
7243
+ return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
7061
7244
  }
7062
- return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
7245
+ return {
7246
+ permission: "confirm",
7247
+ source: "yolo_destructive",
7248
+ riskTier: "destructive",
7249
+ reason: "destructive tool needs explicit approval (confirmDestructive is on)"
7250
+ };
7063
7251
  }
7064
- return {
7065
- permission: "confirm",
7066
- source: "yolo_destructive",
7067
- riskTier: "destructive",
7068
- reason: "destructive tool needs explicit approval even in yolo mode"
7069
- };
7070
7252
  }
7071
7253
  return { permission: "auto", source: "yolo" };
7072
7254
  }
@@ -7096,6 +7278,18 @@ var DefaultPermissionPolicy = class {
7096
7278
  }
7097
7279
  return { permission: "confirm", source: "default" };
7098
7280
  }
7281
+ isDestructiveYoloCall(tool, input, ctx) {
7282
+ if (tool.name === "bash") {
7283
+ const command = getInputString(input, "command");
7284
+ return command ? isClearlyDestructiveBashCommand(command, ctx.projectRoot) : true;
7285
+ }
7286
+ if (tool.name === "write" || tool.name === "edit" || tool.name === "replace" || tool.name === "patch") {
7287
+ const targetPath = getInputString(input, "path") ?? getInputString(input, "file");
7288
+ if (!targetPath || !ctx.projectRoot) return false;
7289
+ return !pathLooksInsideProject(targetPath, ctx.projectRoot);
7290
+ }
7291
+ return tool.riskTier === "destructive";
7292
+ }
7099
7293
  async trust(rule) {
7100
7294
  if (!this.loaded) await this.reload();
7101
7295
  const entry = this.policy[rule.tool] ?? {};
@@ -7566,8 +7760,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events) {
7566
7760
  try {
7567
7761
  await Promise.race([
7568
7762
  Promise.resolve(iter.return?.()),
7569
- new Promise((resolve12) => {
7570
- drainTimer = setTimeout(resolve12, 500);
7763
+ new Promise((resolve13) => {
7764
+ drainTimer = setTimeout(resolve13, 500);
7571
7765
  })
7572
7766
  ]);
7573
7767
  } finally {
@@ -7628,7 +7822,7 @@ async function runProviderWithRetry(opts) {
7628
7822
  description
7629
7823
  });
7630
7824
  }
7631
- await new Promise((resolve12, reject) => {
7825
+ await new Promise((resolve13, reject) => {
7632
7826
  let settled = false;
7633
7827
  const onAbort = () => {
7634
7828
  if (settled) return;
@@ -7641,7 +7835,7 @@ async function runProviderWithRetry(opts) {
7641
7835
  settled = true;
7642
7836
  clearTimeout(t2);
7643
7837
  signal.removeEventListener("abort", onAbort);
7644
- resolve12();
7838
+ resolve13();
7645
7839
  }, delay);
7646
7840
  if (signal.aborted) {
7647
7841
  onAbort();
@@ -9191,7 +9385,8 @@ ${recentJournal}` : "No prior iterations.",
9191
9385
  " \u2022 When this iteration's Task is finished (real artifact / passing",
9192
9386
  " test / applied diff / clean output), emit `[done]` on its own line.",
9193
9387
  " \u2022 Do not stop on the first obstacle \u2014 try at least 3 distinct",
9194
- " approaches before giving up. YOLO is active; no confirmations.",
9388
+ " approaches before giving up. YOLO is active for normal project work;",
9389
+ " destructive-gated confirmations still belong to the permission flow.",
9195
9390
  "",
9196
9391
  "2. UPDATE TODO STATE (when Source is `todo`)",
9197
9392
  " \u2022 Mark this todo `in_progress` via the todos tool before tool work.",
@@ -9320,7 +9515,7 @@ ${recentJournal}` : "No prior iterations.",
9320
9515
  }
9321
9516
  };
9322
9517
  function sleep(ms) {
9323
- return new Promise((resolve12) => setTimeout(resolve12, ms));
9518
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
9324
9519
  }
9325
9520
 
9326
9521
  // src/coordination/subagent-budget.ts
@@ -9536,12 +9731,12 @@ var SubagentBudget = class _SubagentBudget {
9536
9731
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
9537
9732
  return Promise.resolve("stop");
9538
9733
  }
9539
- return new Promise((resolve12) => {
9734
+ return new Promise((resolve13) => {
9540
9735
  let resolved = false;
9541
9736
  const respond = (d) => {
9542
9737
  if (resolved) return;
9543
9738
  resolved = true;
9544
- resolve12(d);
9739
+ resolve13(d);
9545
9740
  };
9546
9741
  const fallback = setTimeout(
9547
9742
  () => respond("stop"),
@@ -9612,15 +9807,22 @@ var SubagentBudget = class _SubagentBudget {
9612
9807
  void this.checkLimits();
9613
9808
  }
9614
9809
  /**
9615
- * Wall-clock budget check. Unlike other limits, timeout is always a hard stop
9616
- * wall-clock time cannot be "extended" by the coordinator, so it throws
9617
- * synchronously rather than entering the negotiation flow.
9810
+ * Wall-clock / idle budget check. Delegates to `checkLimits(elapsed)`, so
9811
+ * `timeout` and `idle_timeout` follow the SAME negotiation path as the other
9812
+ * kinds they are NOT a special-cased hard stop. This is deliberate: a
9813
+ * heartbeat-aware policy (see `attachAutoExtend` and `CollabSession`) grants
9814
+ * a timeout extension only while the agent is making progress and denies it
9815
+ * once the agent is genuinely stuck, which is safer than an unconditional
9816
+ * hard kill of a long-but-working agent. The runner translates the resulting
9817
+ * `BudgetThresholdSignal` decision (`extend` → patch limits in place,
9818
+ * `stop` → abort) just like every other kind.
9618
9819
  *
9619
- * Decision table:
9620
- * - no `onThreshold` handler → throw `BudgetExceededError`
9621
- * - `mode === 'sync'` → throw `BudgetExceededError`
9622
- * - `mode === 'auto'` + no listener → throw `BudgetExceededError`
9623
- * - `mode === 'auto'` + listener → throw `BudgetExceededError` (timeout is not extendable)
9820
+ * Decision table (same as `checkLimits`):
9821
+ * - no `onThreshold` handler → throw `BudgetExceededError` (hard stop)
9822
+ * - `mode === 'sync'` → throw `BudgetExceededError` (hard stop)
9823
+ * - `mode === 'auto'` + no listener → throw `BudgetExceededError` (no one to ask)
9824
+ * - `mode === 'auto'` + listener → throw `BudgetThresholdSignal` (negotiated;
9825
+ * a heartbeat-aware policy may extend the timeout)
9624
9826
  */
9625
9827
  checkTimeout() {
9626
9828
  if (this.startTime === null) return;
@@ -12486,7 +12688,10 @@ var NICKNAME_POOL = {
12486
12688
  "lavoisier": { name: "Lavoisier", domain: "chemistry" },
12487
12689
  "mendeleev": { name: "Mendeleev", domain: "chemistry" }
12488
12690
  };
12489
- var ALL_NICKNAMES = Object.values(NICKNAME_POOL);
12691
+ var ALL_NICKNAMES = Object.entries(NICKNAME_POOL);
12692
+ var NAME_TO_KEY = Object.fromEntries(
12693
+ ALL_NICKNAMES.map(([key, entry]) => [entry.name, key])
12694
+ );
12490
12695
  var DOMAIN_PREFERENCES = {
12491
12696
  "security": ["shannon", "turing", "lamarr", "stallman"],
12492
12697
  "bug-hunter": ["darwin", "curie", "feynman", "fermi"],
@@ -12519,17 +12724,23 @@ function assignNickname(role, used) {
12519
12724
  for (const key of preferences) {
12520
12725
  const entry = NICKNAME_POOL[key];
12521
12726
  if (entry && !used.has(key)) {
12522
- return `${entry.name} (${formatRole(role)})`;
12727
+ return { key, display: `${entry.name} (${formatRole(role)})` };
12523
12728
  }
12524
12729
  }
12525
- for (const entry of ALL_NICKNAMES) {
12526
- const key = Object.entries(NICKNAME_POOL).find(([, v]) => v.name === entry.name)?.[0];
12527
- if (key && !used.has(key)) {
12528
- return `${entry.name} (${formatRole(role)})`;
12730
+ for (const [key, entry] of ALL_NICKNAMES) {
12731
+ if (!used.has(key)) {
12732
+ return { key, display: `${entry.name} (${formatRole(role)})` };
12529
12733
  }
12530
12734
  }
12531
12735
  const counter = used.size + 1;
12532
- return `Scientist #${counter} (${formatRole(role)})`;
12736
+ return { key: `scientist-${counter}`, display: `Scientist #${counter} (${formatRole(role)})` };
12737
+ }
12738
+ function nicknameKeyFromDisplay(display) {
12739
+ const base = display.replace(/\s*\([^)]*\)\s*$/, "").trim();
12740
+ const key = NAME_TO_KEY[base];
12741
+ if (key) return key;
12742
+ const synthesized = base.match(/^Scientist #(\d+)$/);
12743
+ return synthesized ? `scientist-${synthesized[1]}` : void 0;
12533
12744
  }
12534
12745
  function formatRole(role) {
12535
12746
  return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
@@ -12616,11 +12827,10 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
12616
12827
  const name = subagent.name?.trim() ?? "";
12617
12828
  const isPlaceholder = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
12618
12829
  if (!isPlaceholder) return subagent;
12619
- const nickname = assignNickname(role, this.usedNicknames);
12620
- const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
12621
- this.usedNicknames.add(baseKey);
12622
- this.subagentNicknames.set(subagentId, baseKey);
12623
- return { ...subagent, name: nickname };
12830
+ const { key, display } = assignNickname(role, this.usedNicknames);
12831
+ this.usedNicknames.add(key);
12832
+ this.subagentNicknames.set(subagentId, key);
12833
+ return { ...subagent, name: display };
12624
12834
  }
12625
12835
  async spawn(subagent) {
12626
12836
  const id = subagent.id || randomUUID();
@@ -12768,7 +12978,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
12768
12978
  taskIds.map((id) => {
12769
12979
  const cached = this.completedResults.find((r) => r.taskId === id);
12770
12980
  if (cached) return cached;
12771
- return new Promise((resolve12, reject) => {
12981
+ return new Promise((resolve13, reject) => {
12772
12982
  const timeout = setTimeout(() => {
12773
12983
  this.off("task.completed", handler);
12774
12984
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -12777,7 +12987,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
12777
12987
  if (result.taskId === id) {
12778
12988
  clearTimeout(timeout);
12779
12989
  this.off("task.completed", handler);
12780
- resolve12(result);
12990
+ resolve13(result);
12781
12991
  }
12782
12992
  };
12783
12993
  this.on("task.completed", handler);
@@ -12872,23 +13082,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
12872
13082
  */
12873
13083
  drainPendingAsAborted(message) {
12874
13084
  const dropped = this.pendingTasks.splice(0, this.pendingTasks.length);
12875
- for (const t2 of dropped) {
12876
- const synthetic = {
12877
- subagentId: t2.subagentId ?? "unassigned",
12878
- taskId: t2.id,
12879
- status: "stopped",
12880
- error: {
12881
- kind: "aborted_by_parent",
12882
- message,
12883
- retryable: false
12884
- },
12885
- iterations: 0,
12886
- toolCalls: 0,
12887
- durationMs: 0
12888
- };
12889
- this.completedResults.push(synthetic);
12890
- this.emit("task.completed", { task: t2, result: synthetic });
12891
- }
13085
+ for (const t2 of dropped) this.emitPendingAborted(t2, message);
13086
+ }
13087
+ /**
13088
+ * Emit a synthetic `stopped`/`aborted_by_parent` completion for a single
13089
+ * PENDING task — one that was never counted in `inFlight`. This MUST bypass
13090
+ * `recordCompletion`: that path does `inFlight--`, which for a pending task
13091
+ * steals a decrement from a genuinely in-flight task and trips the underflow
13092
+ * guard — suppressing that real task's `task.completed` and hanging its
13093
+ * `awaitTasks()` caller. Pushes the result and fires the event directly.
13094
+ */
13095
+ emitPendingAborted(task, message) {
13096
+ const synthetic = {
13097
+ subagentId: task.subagentId ?? "unassigned",
13098
+ taskId: task.id,
13099
+ status: "stopped",
13100
+ error: {
13101
+ kind: "aborted_by_parent",
13102
+ message,
13103
+ retryable: false
13104
+ },
13105
+ iterations: 0,
13106
+ toolCalls: 0,
13107
+ durationMs: 0
13108
+ };
13109
+ this.completedResults.push(synthetic);
13110
+ this.emit("task.completed", { task, result: synthetic });
12892
13111
  }
12893
13112
  async runDispatched(subagentId, task) {
12894
13113
  const subagent = this.subagents.get(subagentId);
@@ -13149,20 +13368,10 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
13149
13368
  const orphaned = this.pendingTasks.filter((t2) => t2.subagentId === subagentId);
13150
13369
  this.pendingTasks = this.pendingTasks.filter((t2) => t2.subagentId !== subagentId);
13151
13370
  for (const t2 of orphaned) {
13152
- const synthetic = {
13153
- subagentId,
13154
- taskId: t2.id,
13155
- status: "stopped",
13156
- error: {
13157
- kind: "aborted_by_parent",
13158
- message: `Subagent "${subagentId}" was removed while task "${t2.id}" was pending`,
13159
- retryable: false
13160
- },
13161
- iterations: 0,
13162
- toolCalls: 0,
13163
- durationMs: 0
13164
- };
13165
- this.recordCompletion(synthetic);
13371
+ this.emitPendingAborted(
13372
+ t2,
13373
+ `Subagent "${subagentId}" was removed while task "${t2.id}" was pending`
13374
+ );
13166
13375
  }
13167
13376
  this.fleetBus?.emit({
13168
13377
  subagentId,
@@ -13280,7 +13489,7 @@ function providerErrorToSubagentError(err, message, cause) {
13280
13489
 
13281
13490
  // src/execution/parallel-eternal-engine.ts
13282
13491
  function sleep2(ms) {
13283
- return new Promise((resolve12) => setTimeout(resolve12, ms));
13492
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
13284
13493
  }
13285
13494
  var GOAL_COMPLETE_MARKER2 = /^\s*\[goal[_\s-]?complete\]\s*$/im;
13286
13495
  var ParallelEternalEngine = class {
@@ -13459,7 +13668,8 @@ ${recentJournal}` : "No prior iterations.",
13459
13668
  "\u2500\u2500 EXECUTION PROTOCOL \u2500\u2500",
13460
13669
  "\u2022 Execute the assigned task end-to-end using multiple tool calls.",
13461
13670
  "\u2022 Emit `[done]` on its own line when the task is complete.",
13462
- "\u2022 Do not ask for confirmation \u2014 YOLO is active.",
13671
+ "\u2022 Do not ask before routine in-project tool use \u2014 YOLO is active for normal project work.",
13672
+ "\u2022 If a destructive-gated confirmation appears, wait for the permission flow.",
13463
13673
  "\u2022 If the overall Mission is accomplished, emit `[GOAL_COMPLETE]` followed by a verification recipe.",
13464
13674
  "\u2022 Keep output concise \u2014 summarize findings, do not transcribe files."
13465
13675
  ].join("\n");
@@ -13539,6 +13749,7 @@ ${personaLine}Task: ${task}
13539
13749
  } catch {
13540
13750
  results = coordinator.results().slice(-taskIds.length);
13541
13751
  }
13752
+ await Promise.allSettled(subagentIds.map((id) => coordinator.remove(id)));
13542
13753
  const allSuccessful = results.length > 0 && results.every((r) => r.status === "success");
13543
13754
  const goalComplete = results.some(
13544
13755
  (r) => r.status === "success" && typeof r.result === "string" && GOAL_COMPLETE_MARKER2.test(r.result)
@@ -13716,8 +13927,10 @@ ${journalTail.join("\n")}` : "Recent journal: (none \u2014 this is the first ite
13716
13927
  " decide.",
13717
13928
  "",
13718
13929
  "### Operating principles",
13719
- "- YOLO is active. Do NOT ask for confirmation, do NOT propose",
13720
- " options. Pick the best path and execute it.",
13930
+ "- YOLO is active for normal project work. Proceed with routine",
13931
+ " in-project tool use without pre-confirming; pick the best path and execute it.",
13932
+ " If the permission system raises a destructive-gated confirmation, wait",
13933
+ " for that flow instead of trying to bypass it.",
13721
13934
  "- Use tools freely; multiple calls per turn are normal and expected.",
13722
13935
  "- When working on a todo, mark it `in_progress` via the todos tool",
13723
13936
  " before tool work and `completed` (or `cancelled` with a reason)",
@@ -14332,6 +14545,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
14332
14545
  return lines.join("\n");
14333
14546
  }
14334
14547
  cleanup() {
14548
+ if (this._timeoutTimer) {
14549
+ clearTimeout(this._timeoutTimer);
14550
+ this._timeoutTimer = void 0;
14551
+ }
14335
14552
  for (const dispose of this.disposers) dispose();
14336
14553
  this.disposers.length = 0;
14337
14554
  }
@@ -15461,18 +15678,20 @@ var Director = class _Director {
15461
15678
  if (e.subagentId.startsWith("bug-hunter-") || e.subagentId.startsWith("refactor-planner-") || e.subagentId.startsWith("critic-")) {
15462
15679
  return;
15463
15680
  }
15464
- if (payload.kind === "timeout") {
15681
+ if (payload.kind === "timeout" || payload.kind === "idle_timeout") {
15682
+ const heartbeatKey = `${e.subagentId}:${payload.kind}`;
15465
15683
  const progress = progressBySubagent.get(e.subagentId) ?? 0;
15466
- const lastProgress = lastTimeoutProgress.get(e.subagentId) ?? -1;
15684
+ const lastProgress = lastTimeoutProgress.get(heartbeatKey) ?? -1;
15467
15685
  if (progress <= lastProgress) {
15468
15686
  payload.deny();
15469
15687
  return;
15470
15688
  }
15471
- lastTimeoutProgress.set(e.subagentId, progress);
15689
+ lastTimeoutProgress.set(heartbeatKey, progress);
15690
+ const field = payload.kind === "timeout" ? "timeoutMs" : "idleTimeoutMs";
15472
15691
  setImmediate(() => {
15473
15692
  const newLimit = Math.min(Math.ceil(payload.limit * 2), 24 * 60 * 6e4);
15474
- this.recordExtension(e.subagentId, e.taskId, "timeout", newLimit);
15475
- payload.extend({ timeoutMs: newLimit });
15693
+ this.recordExtension(e.subagentId, e.taskId, payload.kind, newLimit);
15694
+ payload.extend({ [field]: newLimit });
15476
15695
  });
15477
15696
  return;
15478
15697
  }
@@ -15788,10 +16007,9 @@ var Director = class _Director {
15788
16007
  if (this.fleetManager) {
15789
16008
  this.fleetManager.assignNicknameAndRecord(config);
15790
16009
  } else {
15791
- config.name = assignNickname(role, this._usedNicknames);
15792
- this._usedNicknames.add(
15793
- config.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-")
15794
- );
16010
+ const { key, display } = assignNickname(role, this._usedNicknames);
16011
+ config.name = display;
16012
+ this._usedNicknames.add(key);
15795
16013
  }
15796
16014
  }
15797
16015
  result = await this.coordinator.spawn(config);
@@ -16085,11 +16303,11 @@ var Director = class _Director {
16085
16303
  if (cached) return cached;
16086
16304
  const existing = this.taskWaiters.get(id);
16087
16305
  if (existing) return existing.promise;
16088
- let resolve12;
16306
+ let resolve13;
16089
16307
  const promise = new Promise((res) => {
16090
- resolve12 = res;
16308
+ resolve13 = res;
16091
16309
  });
16092
- this.taskWaiters.set(id, { promise, resolve: resolve12 });
16310
+ this.taskWaiters.set(id, { promise, resolve: resolve13 });
16093
16311
  return promise;
16094
16312
  })
16095
16313
  );
@@ -16113,8 +16331,8 @@ var Director = class _Director {
16113
16331
  } else {
16114
16332
  const entry = this.manifestEntries.get(subagentId);
16115
16333
  if (entry?.name) {
16116
- const nicknameKey = entry.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
16117
- this._usedNicknames.delete(nicknameKey);
16334
+ const nicknameKey = nicknameKeyFromDisplay(entry.name);
16335
+ if (nicknameKey) this._usedNicknames.delete(nicknameKey);
16118
16336
  }
16119
16337
  }
16120
16338
  this.manifestEntries.delete(subagentId);
@@ -16474,7 +16692,7 @@ function createDelegateTool(opts) {
16474
16692
  subagentId
16475
16693
  });
16476
16694
  const dir = director;
16477
- const result = await new Promise((resolve12) => {
16695
+ const result = await new Promise((resolve13) => {
16478
16696
  let settled = false;
16479
16697
  let timer;
16480
16698
  const finish = (value) => {
@@ -16484,7 +16702,7 @@ function createDelegateTool(opts) {
16484
16702
  offTool();
16485
16703
  offIter();
16486
16704
  offProgress();
16487
- resolve12(value);
16705
+ resolve13(value);
16488
16706
  };
16489
16707
  const arm = () => {
16490
16708
  if (timer) clearTimeout(timer);
@@ -18114,9 +18332,9 @@ var AISpecBuilder = class {
18114
18332
  if (!this.sessionPath) return;
18115
18333
  try {
18116
18334
  const fsp20 = await import('fs/promises');
18117
- const path36 = await import('path');
18335
+ const path37 = await import('path');
18118
18336
  const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
18119
- await fsp20.mkdir(path36.dirname(this.sessionPath), { recursive: true });
18337
+ await fsp20.mkdir(path37.dirname(this.sessionPath), { recursive: true });
18120
18338
  await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
18121
18339
  } catch {
18122
18340
  }
@@ -18826,15 +19044,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
18826
19044
  maxId = id;
18827
19045
  }
18828
19046
  }
18829
- const path36 = [];
19047
+ const path37 = [];
18830
19048
  let current = maxId;
18831
19049
  const visited = /* @__PURE__ */ new Set();
18832
19050
  while (current && !visited.has(current)) {
18833
19051
  visited.add(current);
18834
- path36.unshift(current);
19052
+ path37.unshift(current);
18835
19053
  current = prev.get(current) ?? null;
18836
19054
  }
18837
- return path36;
19055
+ return path37;
18838
19056
  }
18839
19057
  function computeParallelGroups(graph, blockedByMap) {
18840
19058
  const groups = [];
@@ -19384,7 +19602,7 @@ var SddParallelRun = class {
19384
19602
  "\u2500\u2500 EXECUTION PROTOCOL \u2500\u2500",
19385
19603
  "\u2022 Execute the assigned SDD task end-to-end using multiple tool calls.",
19386
19604
  "\u2022 Mark the task [done] in the tracker when complete.",
19387
- "\u2022 Do not ask for confirmation.",
19605
+ "\u2022 Do not ask before routine in-project tool use; if a permission gate appears, wait for that flow.",
19388
19606
  "\u2022 Keep output concise \u2014 summarize changes, do not transcribe files."
19389
19607
  ].join("\n");
19390
19608
  const spawns = subagentIds.map(
@@ -19627,9 +19845,9 @@ var DefaultHealthRegistry = class {
19627
19845
  }
19628
19846
  async runOne(check) {
19629
19847
  let timer = null;
19630
- const timeout = new Promise((resolve12) => {
19848
+ const timeout = new Promise((resolve13) => {
19631
19849
  timer = setTimeout(
19632
- () => resolve12({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
19850
+ () => resolve13({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
19633
19851
  this.timeoutMs
19634
19852
  );
19635
19853
  });
@@ -19812,7 +20030,7 @@ async function startMetricsServer(opts) {
19812
20030
  const tls = opts.tls;
19813
20031
  const useHttps = !!(tls?.cert && tls?.key);
19814
20032
  const host = opts.host ?? "127.0.0.1";
19815
- const path36 = opts.path ?? "/metrics";
20033
+ const path37 = opts.path ?? "/metrics";
19816
20034
  const healthPath = opts.healthPath ?? "/healthz";
19817
20035
  const healthRegistry = opts.healthRegistry;
19818
20036
  const listener = (req, res) => {
@@ -19822,7 +20040,7 @@ async function startMetricsServer(opts) {
19822
20040
  return;
19823
20041
  }
19824
20042
  const url = req.url.split("?")[0];
19825
- if (url === path36) {
20043
+ if (url === path37) {
19826
20044
  let body;
19827
20045
  try {
19828
20046
  body = renderPrometheus(opts.sink.snapshot());
@@ -19868,14 +20086,14 @@ async function startMetricsServer(opts) {
19868
20086
  const { createServer } = await import('http');
19869
20087
  server = createServer(listener);
19870
20088
  }
19871
- await new Promise((resolve12, reject) => {
20089
+ await new Promise((resolve13, reject) => {
19872
20090
  const onError = (err) => {
19873
20091
  server.off("listening", onListening);
19874
20092
  reject(err);
19875
20093
  };
19876
20094
  const onListening = () => {
19877
20095
  server.off("error", onError);
19878
- resolve12();
20096
+ resolve13();
19879
20097
  };
19880
20098
  server.once("error", onError);
19881
20099
  server.once("listening", onListening);
@@ -19886,9 +20104,9 @@ async function startMetricsServer(opts) {
19886
20104
  const protocol = useHttps ? "https" : "http";
19887
20105
  return {
19888
20106
  port: boundPort,
19889
- url: `${protocol}://${host}:${boundPort}${path36}`,
19890
- close: () => new Promise((resolve12, reject) => {
19891
- server.close((err) => err ? reject(err) : resolve12());
20107
+ url: `${protocol}://${host}:${boundPort}${path37}`,
20108
+ close: () => new Promise((resolve13, reject) => {
20109
+ server.close((err) => err ? reject(err) : resolve13());
19892
20110
  })
19893
20111
  };
19894
20112
  }
@@ -20437,7 +20655,7 @@ var context7Server = () => ({
20437
20655
  name: "context7",
20438
20656
  description: "Codebase-aware documentation and Q&A (context7.ai)",
20439
20657
  transport: "streamable-http",
20440
- url: "https://server.context7.ai/mcp",
20658
+ url: "https://mcp.context7.com/mcp",
20441
20659
  permission: "confirm"
20442
20660
  });
20443
20661
  var braveSearchServer = () => ({
@@ -22215,8 +22433,8 @@ function resolvePulledCategoryPath(cat, localPath, rel, remotePath) {
22215
22433
  }
22216
22434
  const dest = path6.resolve(localPath, normalizedRel);
22217
22435
  const root = path6.resolve(localPath);
22218
- const relative7 = path6.relative(root, dest);
22219
- if (relative7.startsWith("..") || path6.isAbsolute(relative7)) {
22436
+ const relative8 = path6.relative(root, dest);
22437
+ if (relative8.startsWith("..") || path6.isAbsolute(relative8)) {
22220
22438
  throw new Error(`Refusing CloudSync path outside category root: ${remotePath}`);
22221
22439
  }
22222
22440
  return dest;
@@ -23389,7 +23607,7 @@ var SecurityScannerOrchestrator = class {
23389
23607
  const delay = Math.round(policy.delayMs(attempt));
23390
23608
  const status = isProviderErr ? err.status : 0;
23391
23609
  console.warn(`[SecurityScanner] retry ${attempt + 1} after ${delay}ms (status=${status}) \u2014 ${errAsErr.message}`);
23392
- await new Promise((resolve12) => setTimeout(resolve12, delay));
23610
+ await new Promise((resolve13) => setTimeout(resolve13, delay));
23393
23611
  return this.completeWithRetry(provider, request, abortController, attempt + 1);
23394
23612
  }
23395
23613
  }
@@ -24248,11 +24466,10 @@ var FleetManager = class {
24248
24466
  */
24249
24467
  assignNicknameAndRecord(config) {
24250
24468
  const role = config.role ?? "subagent";
24251
- const nickname = assignNickname(role, this._usedNicknames);
24252
- const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
24253
- this._usedNicknames.add(baseKey);
24254
- config.name = nickname;
24255
- return nickname;
24469
+ const { key, display } = assignNickname(role, this._usedNicknames);
24470
+ this._usedNicknames.add(key);
24471
+ config.name = display;
24472
+ return display;
24256
24473
  }
24257
24474
  /**
24258
24475
  * Returns the set of already-assigned nickname keys — useful for debugging
@@ -24437,8 +24654,8 @@ var FleetManager = class {
24437
24654
  removeSubagent(subagentId) {
24438
24655
  const entry = this.manifestEntries.get(subagentId);
24439
24656
  if (entry?.name) {
24440
- const nicknameKey = entry.name.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
24441
- this._usedNicknames.delete(nicknameKey);
24657
+ const nicknameKey = nicknameKeyFromDisplay(entry.name);
24658
+ if (nicknameKey) this._usedNicknames.delete(nicknameKey);
24442
24659
  }
24443
24660
  for (const [taskId, task] of this.pendingTasks) {
24444
24661
  if (task.subagentId === subagentId) {
@@ -24885,37 +25102,146 @@ var ExtensionRegistry = class {
24885
25102
  }
24886
25103
  };
24887
25104
 
24888
- // src/core/btw.ts
24889
- var META_KEY = "_btwNotes";
24890
- function readQueue(ctx) {
24891
- const raw = ctx.meta[META_KEY];
24892
- return Array.isArray(raw) ? raw : [];
24893
- }
24894
- function setBtwNote(ctx, text) {
24895
- const trimmed = text.trim();
24896
- if (!trimmed) return readQueue(ctx).length;
24897
- const next = [...readQueue(ctx), trimmed].slice(-20);
24898
- ctx.meta[META_KEY] = next;
24899
- return next.length;
24900
- }
24901
- function pendingBtwCount(ctx) {
24902
- return readQueue(ctx).length;
24903
- }
24904
- function consumeBtwNotes(ctx) {
24905
- const notes = readQueue(ctx);
24906
- if (notes.length > 0) delete ctx.meta[META_KEY];
24907
- return notes;
24908
- }
24909
- function buildBtwBlock(notes) {
24910
- const body = notes.map((n) => `- ${n}`).join("\n");
24911
- return [
24912
- "[BY THE WAY \u2014 the user added this while you were working. Fold it into",
24913
- "your current task; do not restart from scratch unless it contradicts the",
24914
- "goal:",
24915
- "",
24916
- body,
24917
- "]"
24918
- ].join("\n");
25105
+ // src/core/agent-tools.ts
25106
+ function createAgentToolHandler(a) {
25107
+ async function executeSingleWithDecision(tool, use) {
25108
+ const start = Date.now();
25109
+ try {
25110
+ const result = await a.toolExecutor.executeTool(
25111
+ tool,
25112
+ use,
25113
+ a.ctx,
25114
+ a.perIterationOutputCapBytes
25115
+ );
25116
+ return { result, durationMs: Date.now() - start };
25117
+ } catch (err) {
25118
+ const msg = err instanceof Error ? err.message : String(err);
25119
+ return {
25120
+ result: {
25121
+ type: "tool_result",
25122
+ tool_use_id: use.id,
25123
+ content: `Tool "${tool.name}" threw: ${msg}`,
25124
+ is_error: true
25125
+ },
25126
+ durationMs: Date.now() - start
25127
+ };
25128
+ }
25129
+ }
25130
+ function waitForConfirm(info) {
25131
+ return new Promise((resolve13) => {
25132
+ a.events.emit("tool.confirm_needed", {
25133
+ tool: info.tool,
25134
+ input: info.input,
25135
+ toolUseId: info.toolUseId,
25136
+ suggestedPattern: info.suggestedPattern,
25137
+ resolve: resolve13
25138
+ });
25139
+ });
25140
+ }
25141
+ function emitToolExecuted(toolUseId, toolName, durationMs, ok, input, content) {
25142
+ const sig = sizeSignals(toolName, content);
25143
+ a.events.emit("tool.executed", {
25144
+ id: toolUseId,
25145
+ name: toolName,
25146
+ durationMs,
25147
+ ok,
25148
+ input,
25149
+ output: truncateForEvent(content),
25150
+ outputBytes: sig.outputBytes,
25151
+ outputTokens: sig.outputTokens,
25152
+ outputLines: sig.outputLines
25153
+ });
25154
+ }
25155
+ async function executeTools(toolUses) {
25156
+ const selectedToolUses = await a.extensions.runBeforeToolExecution(a.ctx, toolUses);
25157
+ const { outputs } = await a.toolExecutor.executeBatch(
25158
+ selectedToolUses,
25159
+ a.ctx,
25160
+ a.executionStrategy
25161
+ );
25162
+ const useById = new Map(selectedToolUses.map((u) => [u.id, u]));
25163
+ const resultsForMessage = [];
25164
+ for (const { result, tool, durationMs } of outputs) {
25165
+ if (result.type === "tool_confirm_pending") {
25166
+ const decision = await waitForConfirm({
25167
+ tool,
25168
+ input: result.input,
25169
+ toolUseId: result.toolUseId,
25170
+ suggestedPattern: result.suggestedPattern
25171
+ });
25172
+ if (decision === "always") {
25173
+ try {
25174
+ await a.permission.trust({ tool: tool.name, pattern: result.suggestedPattern });
25175
+ a.events.emit("trust.persisted", { tool: tool.name, pattern: result.suggestedPattern, decision });
25176
+ } catch {
25177
+ }
25178
+ } else if (decision === "deny") {
25179
+ try {
25180
+ await a.permission.deny({ tool: tool.name, pattern: result.suggestedPattern });
25181
+ a.events.emit("trust.persisted", { tool: tool.name, pattern: result.suggestedPattern, decision });
25182
+ } catch {
25183
+ }
25184
+ }
25185
+ if (decision === "yes") {
25186
+ const p = a.permission;
25187
+ p.allowOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
25188
+ } else if (decision === "no") {
25189
+ const p = a.permission;
25190
+ p.denyOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
25191
+ }
25192
+ const reRunResult = decision === "yes" || decision === "always" ? await executeSingleWithDecision(tool, {
25193
+ id: result.toolUseId,
25194
+ name: tool.name,
25195
+ input: result.input
25196
+ }) : {
25197
+ result: {
25198
+ type: "tool_result",
25199
+ tool_use_id: result.toolUseId,
25200
+ content: decision === "deny" ? `Tool "${tool.name}" denied and blocked for this pattern.` : `Tool "${tool.name}" denied by user.`,
25201
+ is_error: true
25202
+ },
25203
+ durationMs: 0
25204
+ };
25205
+ const use2 = useById.get(reRunResult.result.tool_use_id);
25206
+ if (use2) {
25207
+ await a.pipelines.toolCall.run({ toolUse: use2, result: reRunResult.result, ctx: a.ctx, tool });
25208
+ await a.ctx.session.append({
25209
+ type: "tool_result",
25210
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
25211
+ id: reRunResult.result.tool_use_id,
25212
+ content: reRunResult.result.content,
25213
+ isError: !!reRunResult.result.is_error
25214
+ });
25215
+ emitToolExecuted(
25216
+ reRunResult.result.tool_use_id,
25217
+ tool.name,
25218
+ reRunResult.durationMs,
25219
+ !reRunResult.result.is_error,
25220
+ result.input,
25221
+ reRunResult.result.content
25222
+ );
25223
+ }
25224
+ resultsForMessage.push(reRunResult.result);
25225
+ continue;
25226
+ }
25227
+ resultsForMessage.push(result);
25228
+ const use = useById.get(result.tool_use_id);
25229
+ if (!use) continue;
25230
+ await a.pipelines.toolCall.run({ toolUse: use, result, ctx: a.ctx, tool: tool ?? void 0 });
25231
+ await a.ctx.session.append({
25232
+ type: "tool_result",
25233
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
25234
+ id: result.tool_use_id,
25235
+ content: result.content,
25236
+ isError: !!result.is_error
25237
+ });
25238
+ emitToolExecuted(result.tool_use_id, use.name, durationMs, !result.is_error, use.input, result.content);
25239
+ }
25240
+ a.ctx.state.appendMessage({ role: "user", content: resultsForMessage });
25241
+ await a.extensions.runAfterToolExecution(a.ctx, outputs);
25242
+ return resultsForMessage;
25243
+ }
25244
+ return { executeTools, executeSingleWithDecision };
24919
25245
  }
24920
25246
 
24921
25247
  // src/core/continue-to-next-iteration.ts
@@ -24933,13 +25259,13 @@ function parseContinueDirective(text) {
24933
25259
  }
24934
25260
  return lastDirective;
24935
25261
  }
24936
- var META_KEY2 = "_autonomousContinue";
25262
+ var META_KEY = "_autonomousContinue";
24937
25263
  function setAutonomousContinue(ctx) {
24938
- ctx.meta[META_KEY2] = true;
25264
+ ctx.meta[META_KEY] = true;
24939
25265
  }
24940
25266
  function consumeAutonomousContinue(ctx) {
24941
- const val = ctx.meta[META_KEY2] === true;
24942
- delete ctx.meta[META_KEY2];
25267
+ const val = ctx.meta[META_KEY] === true;
25268
+ delete ctx.meta[META_KEY];
24943
25269
  return val;
24944
25270
  }
24945
25271
  function makeContinueToNextIterationTool() {
@@ -24962,31 +25288,129 @@ function makeContinueToNextIterationTool() {
24962
25288
  };
24963
25289
  }
24964
25290
 
24965
- // src/core/iteration-limit.ts
24966
- function requestLimitExtension(opts) {
24967
- const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
24968
- return new Promise((resolve12) => {
24969
- let resolved = false;
24970
- const timerFired = () => {
24971
- if (!resolved) {
24972
- resolved = true;
24973
- resolve12(0);
24974
- }
24975
- };
24976
- const timer = setTimeout(timerFired, timeoutMs);
24977
- timer.unref();
24978
- const deny = () => {
24979
- if (!resolved) {
24980
- resolved = true;
24981
- clearTimeout(timer);
24982
- resolve12(0);
24983
- }
25291
+ // src/core/agent-response.ts
25292
+ function createAgentResponseHandler(a) {
25293
+ async function buildAndRunRequestPipeline(opts) {
25294
+ const repaired = repairToolUseAdjacency(a.ctx.messages);
25295
+ if (repaired.report.changed) {
25296
+ a.ctx.state.replaceMessages(repaired.messages);
25297
+ a.events.emit("context.repaired", {
25298
+ ctx: a.ctx,
25299
+ ...repaired.report
25300
+ });
25301
+ a.logger.warn(
25302
+ `Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ${repaired.report.removedToolResults.length} tool_result block(s), ${repaired.report.removedMessages} empty message(s)`
25303
+ );
25304
+ }
25305
+ const baseReq = {
25306
+ model: opts.model ?? a.ctx.model,
25307
+ system: a.ctx.systemPrompt,
25308
+ messages: a.ctx.messages,
25309
+ tools: a.tools.list(),
25310
+ maxTokens: 8192
24984
25311
  };
24985
- const grant = (extra) => {
24986
- if (!resolved) {
24987
- resolved = true;
25312
+ return a.pipelines.request.run(baseReq);
25313
+ }
25314
+ async function processResponse(raw, req) {
25315
+ let res = raw;
25316
+ res = await a.pipelines.response.run(res);
25317
+ a.events.emit("provider.response", {
25318
+ ctx: a.ctx,
25319
+ usage: res.usage,
25320
+ stopReason: res.stopReason
25321
+ });
25322
+ a.ctx.tokenCounter.account(res.usage, req.model);
25323
+ a.ctx.state.appendMessage({ role: "assistant", content: res.content });
25324
+ await a.ctx.session.append({
25325
+ type: "llm_response",
25326
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
25327
+ content: res.content,
25328
+ stopReason: res.stopReason,
25329
+ usage: res.usage
25330
+ });
25331
+ if (a.ctx.signal.aborted) {
25332
+ let finalText2 = "";
25333
+ for (const block of res.content) {
25334
+ if (isTextBlock(block)) finalText2 += block.text;
25335
+ }
25336
+ return { finalText: finalText2, aborted: true, done: false };
25337
+ }
25338
+ let finalText = "";
25339
+ const streamed = a.ctx.provider.capabilities.streaming;
25340
+ for (const block of res.content) {
25341
+ if (isTextBlock(block)) {
25342
+ const rendered = await a.pipelines.assistantOutput.run(block);
25343
+ finalText += rendered.text;
25344
+ if (!streamed) a.renderer?.write(rendered);
25345
+ }
25346
+ }
25347
+ let directive = "none";
25348
+ if (finalText) {
25349
+ directive = parseContinueDirective(finalText);
25350
+ }
25351
+ return { finalText, aborted: false, done: false, directive };
25352
+ }
25353
+ return { buildAndRunRequestPipeline, processResponse };
25354
+ }
25355
+
25356
+ // src/core/btw.ts
25357
+ var META_KEY2 = "_btwNotes";
25358
+ function readQueue(ctx) {
25359
+ const raw = ctx.meta[META_KEY2];
25360
+ return Array.isArray(raw) ? raw : [];
25361
+ }
25362
+ function setBtwNote(ctx, text) {
25363
+ const trimmed = text.trim();
25364
+ if (!trimmed) return readQueue(ctx).length;
25365
+ const next = [...readQueue(ctx), trimmed].slice(-20);
25366
+ ctx.meta[META_KEY2] = next;
25367
+ return next.length;
25368
+ }
25369
+ function pendingBtwCount(ctx) {
25370
+ return readQueue(ctx).length;
25371
+ }
25372
+ function consumeBtwNotes(ctx) {
25373
+ const notes = readQueue(ctx);
25374
+ if (notes.length > 0) delete ctx.meta[META_KEY2];
25375
+ return notes;
25376
+ }
25377
+ function buildBtwBlock(notes) {
25378
+ const body = notes.map((n) => `- ${n}`).join("\n");
25379
+ return [
25380
+ "[BY THE WAY \u2014 the user added this while you were working. Fold it into",
25381
+ "your current task; do not restart from scratch unless it contradicts the",
25382
+ "goal:",
25383
+ "",
25384
+ body,
25385
+ "]"
25386
+ ].join("\n");
25387
+ }
25388
+
25389
+ // src/core/iteration-limit.ts
25390
+ function requestLimitExtension(opts) {
25391
+ const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
25392
+ return new Promise((resolve13) => {
25393
+ let resolved = false;
25394
+ const timerFired = () => {
25395
+ if (!resolved) {
25396
+ resolved = true;
25397
+ resolve13(0);
25398
+ }
25399
+ };
25400
+ const timer = setTimeout(timerFired, timeoutMs);
25401
+ timer.unref();
25402
+ const deny = () => {
25403
+ if (!resolved) {
25404
+ resolved = true;
24988
25405
  clearTimeout(timer);
24989
- resolve12(Math.max(0, extra));
25406
+ resolve13(0);
25407
+ }
25408
+ };
25409
+ const grant = (extra) => {
25410
+ if (!resolved) {
25411
+ resolved = true;
25412
+ clearTimeout(timer);
25413
+ resolve13(Math.max(0, extra));
24990
25414
  }
24991
25415
  };
24992
25416
  events.emit("iteration.limit_reached", {
@@ -25000,216 +25424,113 @@ function requestLimitExtension(opts) {
25000
25424
  if (!resolved) {
25001
25425
  resolved = true;
25002
25426
  clearTimeout(timer);
25003
- resolve12(100);
25427
+ resolve13(100);
25004
25428
  }
25005
25429
  });
25006
25430
  }
25007
25431
  });
25008
25432
  }
25009
25433
 
25010
- // src/core/agent.ts
25011
- var DEFAULT_MAX_ITERATIONS = 100;
25012
- function normalizeInput(input) {
25013
- if (typeof input === "string") {
25014
- return { blocks: [{ type: "text", text: input }], text: input };
25015
- }
25016
- const text = input.filter(isTextBlock).map((b) => b.text).join("");
25017
- return { blocks: input, text };
25018
- }
25019
- function createDefaultPipelines() {
25020
- return {
25021
- request: new Pipeline(),
25022
- response: new Pipeline(),
25023
- toolCall: new Pipeline(),
25024
- userInput: new Pipeline(),
25025
- assistantOutput: new Pipeline(),
25026
- contextWindow: new Pipeline()
25027
- };
25434
+ // src/core/agent-loop.ts
25435
+ function toError(err) {
25436
+ return err instanceof Error ? err : new Error(String(err));
25028
25437
  }
25029
- var Agent = class {
25030
- container;
25031
- tools;
25032
- providers;
25033
- events;
25034
- pipelines;
25035
- ctx;
25036
- maxIterations;
25037
- executionStrategy;
25038
- perIterationOutputCapBytes;
25039
- plugins = [];
25040
- toolExecutor;
25041
- autoExtendLimit;
25042
- /** Enables autonomous continue: model can signal `[continue]` or call continue_to_next_iteration() to re-run. */
25043
- autonomousContinue;
25044
- tracer;
25045
- extensions;
25046
- constructor(init) {
25047
- this.container = init.container;
25048
- this.tools = init.tools;
25049
- this.providers = init.providers;
25050
- this.events = init.events;
25051
- this.pipelines = init.pipelines;
25052
- this.ctx = init.context;
25053
- this.maxIterations = init.maxIterations ?? DEFAULT_MAX_ITERATIONS;
25054
- this.executionStrategy = init.executionStrategy ?? "smart";
25055
- this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
25056
- this.autoExtendLimit = init.autoExtendLimit ?? true;
25057
- this.autonomousContinue = init.autonomousContinue ?? false;
25058
- this.tracer = init.tracer;
25059
- this.extensions = init.extensions ?? new ExtensionRegistry();
25060
- this.extensions.setLogger(this.container.resolve(TOKENS.Logger));
25061
- this.toolExecutor = init.toolExecutor;
25062
- }
25063
- get logger() {
25064
- return this.container.resolve(TOKENS.Logger);
25065
- }
25066
- get retry() {
25067
- return this.container.resolve(TOKENS.RetryPolicy);
25068
- }
25069
- get errorHandler() {
25070
- return this.container.resolve(TOKENS.ErrorHandler);
25071
- }
25072
- get permission() {
25073
- return this.container.resolve(TOKENS.PermissionPolicy);
25438
+ function createAgentLoopHandler(a, handlers) {
25439
+ async function compactContextIfNeeded() {
25440
+ await a.pipelines.contextWindow.run(a.ctx);
25074
25441
  }
25075
- get renderer() {
25076
- return this.container.has(TOKENS.Renderer) ? this.container.resolve(TOKENS.Renderer) : void 0;
25442
+ function emitContextPct() {
25443
+ const maxContext = a.ctx.provider.capabilities.maxContext ?? 2e5;
25444
+ const { total } = estimateRequestTokens(a.ctx.messages, a.ctx.systemPrompt, a.ctx.tools ?? []);
25445
+ a.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
25077
25446
  }
25078
- /**
25079
- * Switch from inline CLI prompts to event-driven confirmation.
25080
- * Clears both the ToolExecutor's confirmAwaiter (so it returns
25081
- * `ToolConfirmPendingResult`) and the permission policy's
25082
- * promptDelegate (so `evaluate()` returns `confirm`).
25083
- *
25084
- * Call this before entering TUI or any mode where stdin is owned
25085
- * by a UI framework (Ink, WebUI WS bridge). Without this, the
25086
- * inline prompt writes to stdout and blocks on stdin — both of
25087
- * which are owned by the framework — making the prompt invisible
25088
- * and the input deadlocked.
25089
- */
25090
- disableInteractiveConfirmation() {
25091
- this.toolExecutor.clearConfirmAwaiter();
25092
- const policy = this.permission;
25093
- if (typeof policy.setPromptDelegate === "function") {
25094
- policy.setPromptDelegate(void 0);
25447
+ function injectPendingBtwNotes() {
25448
+ const notes = consumeBtwNotes(a.ctx);
25449
+ if (notes.length === 0) return;
25450
+ const block = { type: "text", text: buildBtwBlock(notes) };
25451
+ const messages = a.ctx.messages;
25452
+ const last = messages[messages.length - 1];
25453
+ if (last && last.role === "user") {
25454
+ const content = typeof last.content === "string" ? [{ type: "text", text: last.content }, block] : [...last.content, block];
25455
+ a.ctx.state.replaceMessages([...messages.slice(0, -1), { ...last, content }]);
25456
+ } else {
25457
+ a.ctx.state.appendMessage({ role: "user", content: [block] });
25095
25458
  }
25096
25459
  }
25097
- register(tool) {
25098
- this.tools.register(tool);
25099
- }
25100
- async use(plugin, api) {
25101
- await plugin.setup(api);
25102
- this.plugins.push({ plugin, api });
25103
- }
25104
- /** Tear down all plugins in reverse order, calling their teardown hooks. */
25105
- async teardown() {
25106
- const errors = [];
25107
- for (const { plugin, api } of this.plugins.toReversed()) {
25108
- if (typeof plugin.teardown !== "function") continue;
25109
- try {
25110
- await plugin.teardown(api);
25111
- } catch (err) {
25112
- errors.push(err);
25460
+ async function checkIterationLimit(iterationIndex, limit, hasHardLimit, currentIterations, delegateSummaries) {
25461
+ if (hasHardLimit && iterationIndex >= limit) {
25462
+ const extendBy = await requestLimitExtension({
25463
+ events: a.events,
25464
+ currentIterations,
25465
+ currentLimit: limit,
25466
+ autoExtend: a.autoExtendLimit
25467
+ });
25468
+ if (extendBy > 0) {
25469
+ const newLimit = limit + extendBy;
25470
+ a.logger.info(`Iteration limit extended by ${extendBy} (new limit: ${newLimit})`);
25471
+ return { limit: newLimit };
25113
25472
  }
25473
+ return { limit, exit: { status: "max_iterations", iterations: currentIterations, delegateSummaries } };
25114
25474
  }
25115
- this.plugins.length = 0;
25116
- if (errors.length > 0) {
25117
- throw new Error(`Agent teardown failed: ${errors.map(String).join("; ")}`);
25118
- }
25119
- }
25120
- async run(userInput, opts = {}) {
25121
- const controller = new RunController({ parentSignal: opts.signal });
25122
- const signal = controller.signal;
25123
- this.ctx.signal = signal;
25124
- controller.onAbort(() => this.ctx.drainAbortHooks());
25125
- const span = this.tracer?.startSpan("agent.run", {
25126
- "agent.model": opts.model ?? this.ctx.model,
25127
- "agent.executionStrategy": opts.executionStrategy ?? this.executionStrategy
25128
- });
25129
- const { blocks, text } = normalizeInput(userInput);
25130
- const inputPayload = { content: blocks, text, ctx: this.ctx };
25131
- await this.extensions.runBeforeRun(this.ctx, inputPayload);
25132
- try {
25133
- const result = await this.runInner(inputPayload, opts, controller);
25134
- span?.setAttribute("agent.status", result.status);
25135
- span?.setAttribute("agent.iterations", result.iterations);
25136
- await this.extensions.runAfterRun(this.ctx, result);
25137
- return result;
25138
- } catch (err) {
25139
- const wse = err instanceof AgentError ? err : toWrongStackError(err);
25140
- this.events.emit("error", { err: toError(err), phase: "agent" });
25141
- if (err instanceof Error) span?.recordError(err);
25142
- span?.setAttribute("agent.status", "failed");
25143
- const result = {
25144
- status: signal.aborted ? "aborted" : "failed",
25145
- iterations: 0,
25146
- error: wse
25147
- };
25148
- await this.extensions.runAfterRun(this.ctx, result);
25149
- return result;
25150
- } finally {
25151
- span?.end();
25152
- await controller.dispose();
25153
- }
25475
+ return { limit };
25154
25476
  }
25155
- async runInner(inputPayload, opts, controller) {
25156
- await this.pipelines.userInput.run(inputPayload);
25157
- this.ctx.state.appendMessage({ role: "user", content: inputPayload.content });
25158
- await this.ctx.session.append({
25477
+ async function runInner(inputPayload, opts, controller, autonomousContinue) {
25478
+ await a.pipelines.userInput.run(inputPayload);
25479
+ a.ctx.state.appendMessage({ role: "user", content: inputPayload.content });
25480
+ await a.ctx.session.append({
25159
25481
  type: "user_input",
25160
25482
  ts: (/* @__PURE__ */ new Date()).toISOString(),
25161
25483
  content: inputPayload.content
25162
25484
  });
25163
- const promptIndex = this.ctx.messages.filter((m) => m.role === "user").length - 1;
25485
+ const promptIndex = a.ctx.messages.filter((m) => m.role === "user").length - 1;
25164
25486
  const preview = inputPayload.text.slice(0, 80) + (inputPayload.text.length > 80 ? "\u2026" : "");
25165
- await this.ctx.session.writeCheckpoint(promptIndex, preview);
25487
+ await a.ctx.session.writeCheckpoint(promptIndex, preview);
25166
25488
  let finalText = "";
25167
25489
  let iterations = 0;
25168
25490
  const delegateSummaries = [];
25169
- let effectiveLimit = opts.maxIterations ?? this.maxIterations;
25491
+ let effectiveLimit = opts.maxIterations ?? a.maxIterations;
25170
25492
  const hasHardLimit = effectiveLimit > 0 && Number.isFinite(effectiveLimit);
25171
25493
  let recoveryRetries = 0;
25172
- const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
25173
25494
  const onSubagentDone = ({ summary, ok }) => {
25174
25495
  delegateSummaries.push({ summary, ok });
25175
25496
  };
25176
- const offSubagentDone = this.events.on("subagent.done", onSubagentDone);
25177
- const diRunner = this.container.has(TOKENS.ProviderRunner) ? this.container.resolve(TOKENS.ProviderRunner) : null;
25497
+ const offSubagentDone = a.events.on("subagent.done", onSubagentDone);
25498
+ const diRunner = a.container.has(TOKENS.ProviderRunner) ? a.container.resolve(TOKENS.ProviderRunner) : null;
25178
25499
  const baseRunner = diRunner ? (ctx, req) => diRunner.run({
25179
25500
  provider: ctx.provider,
25180
25501
  request: req,
25181
25502
  signal: controller.signal,
25182
25503
  ctx,
25183
- events: this.events,
25184
- retry: this.retry,
25185
- logger: this.logger,
25186
- tracer: this.tracer
25504
+ events: a.events,
25505
+ retry: a.retry,
25506
+ logger: a.logger,
25507
+ tracer: a.tracer
25187
25508
  }) : async (ctx, req) => runProviderWithRetry({
25188
25509
  provider: ctx.provider,
25189
25510
  request: req,
25190
25511
  signal: controller.signal,
25191
25512
  ctx,
25192
- events: this.events,
25193
- retry: this.retry,
25194
- logger: this.logger,
25195
- tracer: this.tracer
25513
+ events: a.events,
25514
+ retry: a.retry,
25515
+ logger: a.logger,
25516
+ tracer: a.tracer
25196
25517
  });
25197
- const customRunner = this.extensions.wrapProviderRunner(baseRunner);
25518
+ const customRunner = a.extensions.wrapProviderRunner(baseRunner);
25198
25519
  try {
25199
25520
  for (let i = 0; ; i++) {
25200
25521
  iterations = i + 1;
25201
25522
  if (controller.signal.aborted) {
25202
25523
  return { status: "aborted", iterations };
25203
25524
  }
25204
- await this.ctx.session.writeInFlightMarker(`iteration ${i} / max ${this.maxIterations}`).catch((err) => {
25205
- this.logger.debug?.(
25525
+ await a.ctx.session.writeInFlightMarker(`iteration ${i} / max ${a.maxIterations}`).catch((err) => {
25526
+ a.logger.debug?.(
25206
25527
  `in-flight marker write failed: ${err instanceof Error ? err.message : String(err)}`
25207
25528
  );
25208
25529
  });
25209
25530
  if (autonomousContinue) {
25210
- consumeAutonomousContinue(this.ctx);
25531
+ consumeAutonomousContinue(a.ctx);
25211
25532
  }
25212
- const limitCheck = await this.checkIterationLimit(
25533
+ const limitCheck = await checkIterationLimit(
25213
25534
  i,
25214
25535
  effectiveLimit,
25215
25536
  hasHardLimit,
@@ -25220,11 +25541,11 @@ var Agent = class {
25220
25541
  if (limitCheck.exit) {
25221
25542
  return { ...limitCheck.exit, finalText };
25222
25543
  }
25223
- await this.extensions.runBeforeIteration(this.ctx, i);
25224
- this.events.emit("iteration.started", { ctx: this.ctx, index: i });
25225
- this.injectPendingBtwNotes();
25226
- const req = await this.buildAndRunRequestPipeline(opts);
25227
- await this.ctx.session.append({
25544
+ await a.extensions.runBeforeIteration(a.ctx, i);
25545
+ a.events.emit("iteration.started", { ctx: a.ctx, index: i });
25546
+ injectPendingBtwNotes();
25547
+ const req = await handlers.response.buildAndRunRequestPipeline(opts);
25548
+ await a.ctx.session.append({
25228
25549
  type: "llm_request",
25229
25550
  ts: (/* @__PURE__ */ new Date()).toISOString(),
25230
25551
  model: req.model,
@@ -25235,39 +25556,39 @@ var Agent = class {
25235
25556
  });
25236
25557
  let res;
25237
25558
  try {
25238
- res = await customRunner(this.ctx, req);
25559
+ res = await customRunner(a.ctx, req);
25239
25560
  const calibratedEstimate = estimateRequestTokensCalibrated(req.messages, req.system, req.tools ?? []).total;
25240
25561
  recordActualUsage(res.usage.input, calibratedEstimate);
25241
25562
  recoveryRetries = 0;
25242
25563
  } catch (err) {
25243
25564
  if (controller.signal.aborted) {
25244
- this.events.emit("error", { err: toError(err), phase: "provider" });
25565
+ a.events.emit("error", { err: toError(err), phase: "provider" });
25245
25566
  return { status: "aborted", iterations, error: toWrongStackError(err, "AGENT_ABORTED") };
25246
25567
  }
25247
- const extDecision = await this.extensions.runOnError(this.ctx, err, "provider", i);
25568
+ const extDecision = await a.extensions.runOnError(a.ctx, err, "provider", i);
25248
25569
  if (extDecision) {
25249
25570
  if (extDecision.action === "fail") {
25250
- this.events.emit("error", { err: toError(err), phase: "provider" });
25571
+ a.events.emit("error", { err: toError(err), phase: "provider" });
25251
25572
  return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
25252
25573
  }
25253
25574
  if (extDecision.action === "continue") {
25254
- await this.extensions.runAfterIteration(this.ctx, i);
25575
+ await a.extensions.runAfterIteration(a.ctx, i);
25255
25576
  continue;
25256
25577
  }
25257
25578
  if (extDecision.action === "retry") {
25258
25579
  recoveryRetries++;
25259
25580
  if (recoveryRetries > 2) {
25260
- this.events.emit("error", { err: toError(err), phase: "provider" });
25581
+ a.events.emit("error", { err: toError(err), phase: "provider" });
25261
25582
  return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
25262
25583
  }
25263
- if (extDecision.model) this.ctx.model = extDecision.model;
25264
- this.logger.info("Extension requested retry; retrying turn");
25584
+ if (extDecision.model) a.ctx.model = extDecision.model;
25585
+ a.logger.info("Extension requested retry; retrying turn");
25265
25586
  continue;
25266
25587
  }
25267
25588
  }
25268
- const recovered = await this.errorHandler.recover(err, this.ctx);
25589
+ const recovered = await a.errorHandler.recover(err, a.ctx);
25269
25590
  if (!recovered || recovered.action === "fail") {
25270
- this.events.emit("error", { err: toError(err), phase: "provider" });
25591
+ a.events.emit("error", { err: toError(err), phase: "provider" });
25271
25592
  return {
25272
25593
  status: "failed",
25273
25594
  iterations,
@@ -25278,17 +25599,17 @@ var Agent = class {
25278
25599
  if (recovered.action === "retry") {
25279
25600
  recoveryRetries++;
25280
25601
  if (recoveryRetries > 2) {
25281
- this.events.emit("error", { err: toError(err), phase: "provider" });
25602
+ a.events.emit("error", { err: toError(err), phase: "provider" });
25282
25603
  return { status: "failed", iterations, error: toWrongStackError(err) };
25283
25604
  }
25284
- if (recovered.model) this.ctx.model = recovered.model;
25285
- this.logger.info(`Recovered provider error via ${recovered.reason}; retrying turn`);
25605
+ if (recovered.model) a.ctx.model = recovered.model;
25606
+ a.logger.info(`Recovered provider error via ${recovered.reason}; retrying turn`);
25286
25607
  continue;
25287
25608
  }
25288
25609
  recoveryRetries = 0;
25289
25610
  res = recovered.response;
25290
25611
  }
25291
- const responseResult = await this.processResponse(res, req);
25612
+ const responseResult = await handlers.response.processResponse(res, req);
25292
25613
  if (responseResult.aborted) {
25293
25614
  return { status: "aborted", iterations, finalText: responseResult.finalText, delegateSummaries };
25294
25615
  }
@@ -25298,11 +25619,11 @@ var Agent = class {
25298
25619
  finalText = responseResult.finalText;
25299
25620
  const toolUses = res.content.filter(isToolUseBlock);
25300
25621
  if (toolUses.length === 0) {
25301
- this.emitContextPct();
25302
- this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
25622
+ emitContextPct();
25623
+ a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
25303
25624
  if (autonomousContinue && responseResult.directive === "continue") {
25304
- await this.compactContextIfNeeded();
25305
- await this.extensions.runAfterIteration(this.ctx, i);
25625
+ await compactContextIfNeeded();
25626
+ await a.extensions.runAfterIteration(a.ctx, i);
25306
25627
  continue;
25307
25628
  }
25308
25629
  if (autonomousContinue && responseResult.directive === "stop") {
@@ -25310,18 +25631,18 @@ var Agent = class {
25310
25631
  }
25311
25632
  return { status: "done", iterations, finalText, delegateSummaries };
25312
25633
  }
25313
- await this.executeTools(toolUses);
25314
- if (autonomousContinue && consumeAutonomousContinue(this.ctx)) {
25315
- this.emitContextPct();
25316
- this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
25317
- await this.compactContextIfNeeded();
25318
- await this.extensions.runAfterIteration(this.ctx, i);
25634
+ await handlers.tools.executeTools(toolUses);
25635
+ if (autonomousContinue && consumeAutonomousContinue(a.ctx)) {
25636
+ emitContextPct();
25637
+ a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
25638
+ await compactContextIfNeeded();
25639
+ await a.extensions.runAfterIteration(a.ctx, i);
25319
25640
  continue;
25320
25641
  }
25321
- this.emitContextPct();
25322
- this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
25323
- await this.compactContextIfNeeded();
25324
- await this.extensions.runAfterIteration(this.ctx, i);
25642
+ emitContextPct();
25643
+ a.events.emit("iteration.completed", { ctx: a.ctx, index: i });
25644
+ await compactContextIfNeeded();
25645
+ await a.extensions.runAfterIteration(a.ctx, i);
25325
25646
  if (autonomousContinue && responseResult.directive === "continue") {
25326
25647
  continue;
25327
25648
  }
@@ -25330,348 +25651,417 @@ var Agent = class {
25330
25651
  }
25331
25652
  }
25332
25653
  } finally {
25333
- offSubagentDone();
25334
- const reason = controller.signal.aborted ? "aborted" : "clean";
25335
- await this.ctx.session.clearInFlightMarker(reason).catch((err) => {
25336
- this.logger.debug?.(
25337
- `in-flight marker clear failed: ${err instanceof Error ? err.message : String(err)}`
25338
- );
25339
- });
25654
+ offSubagentDone();
25655
+ const reason = controller.signal.aborted ? "aborted" : "clean";
25656
+ await a.ctx.session.clearInFlightMarker(reason).catch((err) => {
25657
+ a.logger.debug?.(
25658
+ `in-flight marker clear failed: ${err instanceof Error ? err.message : String(err)}`
25659
+ );
25660
+ });
25661
+ }
25662
+ }
25663
+ return { runInner };
25664
+ }
25665
+
25666
+ // src/core/agent-types.ts
25667
+ var DEFAULT_MAX_ITERATIONS = 100;
25668
+ function normalizeInput(input) {
25669
+ if (typeof input === "string") {
25670
+ return { blocks: [{ type: "text", text: input }], text: input };
25671
+ }
25672
+ const text = input.filter(isTextBlock).map((b) => b.text).join("");
25673
+ return { blocks: input, text };
25674
+ }
25675
+ function createDefaultPipelines() {
25676
+ return {
25677
+ request: new Pipeline(),
25678
+ response: new Pipeline(),
25679
+ toolCall: new Pipeline(),
25680
+ userInput: new Pipeline(),
25681
+ assistantOutput: new Pipeline(),
25682
+ contextWindow: new Pipeline()
25683
+ };
25684
+ }
25685
+
25686
+ // src/core/agent.ts
25687
+ var Agent = class {
25688
+ container;
25689
+ tools;
25690
+ providers;
25691
+ events;
25692
+ pipelines;
25693
+ ctx;
25694
+ maxIterations;
25695
+ executionStrategy;
25696
+ perIterationOutputCapBytes;
25697
+ plugins = [];
25698
+ toolExecutor;
25699
+ autoExtendLimit;
25700
+ autonomousContinue;
25701
+ tracer;
25702
+ extensions;
25703
+ _toolHandler;
25704
+ _responseHandler;
25705
+ _loopHandler;
25706
+ constructor(init) {
25707
+ this.container = init.container;
25708
+ this.tools = init.tools;
25709
+ this.providers = init.providers;
25710
+ this.events = init.events;
25711
+ this.pipelines = init.pipelines;
25712
+ this.ctx = init.context;
25713
+ this.maxIterations = init.maxIterations ?? DEFAULT_MAX_ITERATIONS;
25714
+ this.executionStrategy = init.executionStrategy ?? "smart";
25715
+ this.perIterationOutputCapBytes = init.perIterationOutputCapBytes ?? 1e5;
25716
+ this.autoExtendLimit = init.autoExtendLimit ?? true;
25717
+ this.autonomousContinue = init.autonomousContinue ?? false;
25718
+ this.tracer = init.tracer;
25719
+ this.extensions = init.extensions ?? new ExtensionRegistry();
25720
+ this.extensions.setLogger(this.container.resolve(TOKENS.Logger));
25721
+ this.toolExecutor = init.toolExecutor;
25722
+ this._toolHandler = createAgentToolHandler(this);
25723
+ this._responseHandler = createAgentResponseHandler(this);
25724
+ this._loopHandler = createAgentLoopHandler(this, {
25725
+ tools: this._toolHandler,
25726
+ response: this._responseHandler
25727
+ });
25728
+ }
25729
+ get logger() {
25730
+ return this.container.resolve(TOKENS.Logger);
25731
+ }
25732
+ get retry() {
25733
+ return this.container.resolve(TOKENS.RetryPolicy);
25734
+ }
25735
+ get errorHandler() {
25736
+ return this.container.resolve(TOKENS.ErrorHandler);
25737
+ }
25738
+ get permission() {
25739
+ return this.container.resolve(TOKENS.PermissionPolicy);
25740
+ }
25741
+ get renderer() {
25742
+ return this.container.has(TOKENS.Renderer) ? this.container.resolve(TOKENS.Renderer) : void 0;
25743
+ }
25744
+ disableInteractiveConfirmation() {
25745
+ this.toolExecutor.clearConfirmAwaiter();
25746
+ const policy = this.permission;
25747
+ if (typeof policy.setPromptDelegate === "function") {
25748
+ policy.setPromptDelegate(void 0);
25749
+ }
25750
+ }
25751
+ register(tool) {
25752
+ this.tools.register(tool);
25753
+ }
25754
+ async use(plugin, api) {
25755
+ await plugin.setup(api);
25756
+ this.plugins.push({ plugin, api });
25757
+ }
25758
+ async teardown() {
25759
+ const errors = [];
25760
+ for (const { plugin, api } of this.plugins.toReversed()) {
25761
+ if (typeof plugin.teardown !== "function") continue;
25762
+ try {
25763
+ await plugin.teardown(api);
25764
+ } catch (err) {
25765
+ errors.push(err);
25766
+ }
25767
+ }
25768
+ this.plugins.length = 0;
25769
+ if (errors.length > 0) {
25770
+ throw new Error(`Agent teardown failed: ${errors.map(String).join("; ")}`);
25771
+ }
25772
+ }
25773
+ async run(userInput, opts = {}) {
25774
+ const controller = new RunController({ parentSignal: opts.signal });
25775
+ const signal = controller.signal;
25776
+ this.ctx.signal = signal;
25777
+ controller.onAbort(() => this.ctx.drainAbortHooks());
25778
+ const span = this.tracer?.startSpan("agent.run", {
25779
+ "agent.model": opts.model ?? this.ctx.model,
25780
+ "agent.executionStrategy": opts.executionStrategy ?? this.executionStrategy
25781
+ });
25782
+ const { blocks, text } = normalizeInput(userInput);
25783
+ const inputPayload = { content: blocks, text, ctx: this.ctx };
25784
+ await this.extensions.runBeforeRun(this.ctx, inputPayload);
25785
+ try {
25786
+ const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
25787
+ const result = await this._loopHandler.runInner(inputPayload, opts, controller, autonomousContinue);
25788
+ span?.setAttribute("agent.status", result.status);
25789
+ span?.setAttribute("agent.iterations", result.iterations);
25790
+ await this.extensions.runAfterRun(this.ctx, result);
25791
+ return result;
25792
+ } catch (err) {
25793
+ const wse = err instanceof AgentError ? err : toWrongStackError(err);
25794
+ this.events.emit("error", { err: err instanceof Error ? err : new Error(String(err)), phase: "agent" });
25795
+ if (err instanceof Error) span?.recordError(err);
25796
+ span?.setAttribute("agent.status", "failed");
25797
+ const result = {
25798
+ status: signal.aborted ? "aborted" : "failed",
25799
+ iterations: 0,
25800
+ error: wse
25801
+ };
25802
+ await this.extensions.runAfterRun(this.ctx, result);
25803
+ return result;
25804
+ } finally {
25805
+ span?.end();
25806
+ await controller.dispose();
25340
25807
  }
25341
25808
  }
25342
- /**
25343
- * Check if iteration limit has been reached and request extension if needed.
25344
- * Returns the new effective limit (possibly extended) and a RunResult if
25345
- * the loop should exit. Returns `{ limit }` with no result when the
25346
- * iteration may proceed.
25347
- */
25348
- async checkIterationLimit(iterationIndex, limit, hasHardLimit, currentIterations, delegateSummaries) {
25349
- if (hasHardLimit && iterationIndex >= limit) {
25350
- const extendBy = await requestLimitExtension({
25351
- events: this.events,
25352
- currentIterations,
25353
- currentLimit: limit,
25354
- autoExtend: this.autoExtendLimit
25355
- });
25356
- if (extendBy > 0) {
25357
- const newLimit = limit + extendBy;
25358
- this.logger.info(`Iteration limit extended by ${extendBy} (new limit: ${newLimit})`);
25359
- return { limit: newLimit };
25809
+ // ── Tool + response execution handled by AgentToolHandler / AgentResponseHandler ──
25810
+ };
25811
+
25812
+ // src/hooks/registry.ts
25813
+ var HookRegistry = class {
25814
+ entries = [];
25815
+ /** Register an in-process hook. Returns an unsubscribe function. */
25816
+ registerInProcess(event, matcher, hook, owner) {
25817
+ const entry = {
25818
+ kind: "inprocess",
25819
+ event,
25820
+ matcher: matcher ?? "*",
25821
+ hook,
25822
+ owner
25823
+ };
25824
+ this.entries.push(entry);
25825
+ return () => this.remove(entry);
25826
+ }
25827
+ /** Register a single shell hook. Returns an unsubscribe function. */
25828
+ registerShell(event, hook) {
25829
+ const entry = {
25830
+ kind: "shell",
25831
+ event,
25832
+ matcher: hook.matcher ?? "*",
25833
+ command: hook.command,
25834
+ timeoutMs: hook.timeoutMs
25835
+ };
25836
+ this.entries.push(entry);
25837
+ return () => this.remove(entry);
25838
+ }
25839
+ /** Bulk-load shell hooks from a `config.hooks` map. */
25840
+ loadShellHooks(hooks) {
25841
+ if (!hooks) return;
25842
+ for (const [event, list] of Object.entries(hooks)) {
25843
+ for (const h of list ?? []) {
25844
+ if (h?.command) this.registerShell(event, h);
25360
25845
  }
25361
- return { limit, exit: { status: "max_iterations", iterations: currentIterations, delegateSummaries } };
25362
25846
  }
25363
- return { limit };
25364
25847
  }
25365
- /**
25366
- * Build request and run through request pipeline.
25367
- */
25368
- async buildAndRunRequestPipeline(opts) {
25369
- const repaired = repairToolUseAdjacency(this.ctx.messages);
25370
- if (repaired.report.changed) {
25371
- this.ctx.state.replaceMessages(repaired.messages);
25372
- this.events.emit("context.repaired", {
25373
- ctx: this.ctx,
25374
- ...repaired.report
25848
+ /** All entries registered for an event, in registration order. */
25849
+ list(event) {
25850
+ return this.entries.filter((e) => e.event === event);
25851
+ }
25852
+ /** True when any entry is registered for the event. */
25853
+ has(event) {
25854
+ return this.entries.some((e) => e.event === event);
25855
+ }
25856
+ /** Drop every registered hook (used in teardown / tests). */
25857
+ clear() {
25858
+ this.entries.length = 0;
25859
+ }
25860
+ remove(entry) {
25861
+ const i = this.entries.indexOf(entry);
25862
+ if (i >= 0) this.entries.splice(i, 1);
25863
+ }
25864
+ };
25865
+ function hookMatcherMatches(matcher, toolName) {
25866
+ if (!matcher || matcher === "*") return true;
25867
+ if (toolName === void 0) return true;
25868
+ const target = toolName.toLowerCase();
25869
+ return matcher.split("|").map((s) => s.trim().toLowerCase()).filter(Boolean).includes(target);
25870
+ }
25871
+ var DEFAULT_TIMEOUT_MS3 = 5e3;
25872
+ var MAX_OUTPUT_BYTES = 64 * 1024;
25873
+ async function runShellHook(spec, input, logger) {
25874
+ const timeoutMs = spec.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
25875
+ return await new Promise((resolve13) => {
25876
+ let settled = false;
25877
+ const done = (v) => {
25878
+ if (settled) return;
25879
+ settled = true;
25880
+ clearTimeout(timer);
25881
+ resolve13(v);
25882
+ };
25883
+ let child;
25884
+ try {
25885
+ child = spawn(spec.command, {
25886
+ cwd: input.cwd,
25887
+ env: process.env,
25888
+ stdio: ["pipe", "pipe", "pipe"],
25889
+ shell: true
25375
25890
  });
25376
- this.logger.warn(
25377
- `Repaired context tool adjacency: removed ${repaired.report.removedToolUses.length} tool_use block(s), ${repaired.report.removedToolResults.length} tool_result block(s), ${repaired.report.removedMessages} empty message(s)`
25378
- );
25891
+ } catch (err2) {
25892
+ logger?.warn?.(`hook spawn failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
25893
+ return resolve13(null);
25379
25894
  }
25380
- const baseReq = {
25381
- model: opts.model ?? this.ctx.model,
25382
- system: this.ctx.systemPrompt,
25383
- messages: this.ctx.messages,
25384
- tools: this.tools.list(),
25385
- maxTokens: 8192
25386
- };
25387
- return this.pipelines.request.run(baseReq);
25388
- }
25389
- /**
25390
- * Process the provider response: run response pipeline, emit events,
25391
- * update session, render text, handle abort.
25392
- */
25393
- async processResponse(raw, req) {
25394
- let res = raw;
25395
- res = await this.pipelines.response.run(res);
25396
- this.events.emit("provider.response", {
25397
- ctx: this.ctx,
25398
- usage: res.usage,
25399
- stopReason: res.stopReason
25895
+ const timer = setTimeout(() => {
25896
+ logger?.warn?.(`hook command timed out after ${timeoutMs}ms: ${spec.command}`);
25897
+ try {
25898
+ child.kill("SIGKILL");
25899
+ } catch {
25900
+ }
25901
+ done(null);
25902
+ }, timeoutMs);
25903
+ let out = "";
25904
+ let err = "";
25905
+ let outTrunc = false;
25906
+ child.stdout?.on("data", (d) => {
25907
+ if (outTrunc) return;
25908
+ out += d.toString("utf8");
25909
+ if (Buffer.byteLength(out, "utf8") > MAX_OUTPUT_BYTES) {
25910
+ out = out.slice(0, MAX_OUTPUT_BYTES);
25911
+ outTrunc = true;
25912
+ }
25400
25913
  });
25401
- this.ctx.tokenCounter.account(res.usage, req.model);
25402
- this.ctx.state.appendMessage({ role: "assistant", content: res.content });
25403
- await this.ctx.session.append({
25404
- type: "llm_response",
25405
- ts: (/* @__PURE__ */ new Date()).toISOString(),
25406
- content: res.content,
25407
- stopReason: res.stopReason,
25408
- usage: res.usage
25914
+ child.stderr?.on("data", (d) => {
25915
+ if (err.length < MAX_OUTPUT_BYTES) err += d.toString("utf8");
25409
25916
  });
25410
- if (this.ctx.signal.aborted) {
25411
- let finalText2 = "";
25412
- for (const block of res.content) {
25413
- if (isTextBlock(block)) finalText2 += block.text;
25917
+ child.on("error", (e) => {
25918
+ logger?.warn?.(`hook command error: ${e instanceof Error ? e.message : String(e)}`);
25919
+ done(null);
25920
+ });
25921
+ child.on("close", (code) => {
25922
+ if (code === 2) {
25923
+ const reason = (err.trim() || out.trim() || "blocked by hook").slice(0, 2e3);
25924
+ return done({ decision: "block", reason });
25414
25925
  }
25415
- return { finalText: finalText2, aborted: true, done: false };
25926
+ const parsed = parseOutcome(out);
25927
+ done(parsed);
25928
+ });
25929
+ try {
25930
+ child.stdin?.end(`${JSON.stringify(input)}
25931
+ `);
25932
+ } catch {
25416
25933
  }
25417
- let finalText = "";
25418
- const streamed = this.ctx.provider.capabilities.streaming;
25419
- for (const block of res.content) {
25420
- if (isTextBlock(block)) {
25421
- const rendered = await this.pipelines.assistantOutput.run(block);
25422
- finalText += rendered.text;
25423
- if (!streamed) this.renderer?.write(rendered);
25424
- }
25934
+ });
25935
+ }
25936
+ function parseOutcome(stdout) {
25937
+ const trimmed = stdout.trim();
25938
+ if (!trimmed || trimmed[0] !== "{") return null;
25939
+ try {
25940
+ const obj = JSON.parse(trimmed);
25941
+ const outcome = {};
25942
+ if (obj["decision"] === "block" || obj["decision"] === "allow") {
25943
+ outcome.decision = obj["decision"];
25425
25944
  }
25426
- let directive = "none";
25427
- if (finalText) {
25428
- directive = parseContinueDirective(finalText);
25945
+ if (typeof obj["reason"] === "string") outcome.reason = obj["reason"];
25946
+ if (typeof obj["additionalContext"] === "string") {
25947
+ outcome.additionalContext = obj["additionalContext"];
25429
25948
  }
25430
- return { finalText, aborted: false, done: false, directive };
25949
+ if (obj["modifiedInput"] && typeof obj["modifiedInput"] === "object") {
25950
+ outcome.modifiedInput = obj["modifiedInput"];
25951
+ }
25952
+ return outcome;
25953
+ } catch {
25954
+ return null;
25431
25955
  }
25432
- /**
25433
- * Execute tools and append tool results to context.
25434
- * When a tool returns `tool_confirm_pending` (no confirmAwaiter set),
25435
- * we pause and emit `tool.confirm_needed`. The run is blocked until
25436
- * the event listener resolves the confirmation, then we re-run the
25437
- * single tool.
25438
- */
25439
- async executeTools(toolUses) {
25440
- const selectedToolUses = await this.extensions.runBeforeToolExecution(this.ctx, toolUses);
25441
- const { outputs } = await this.toolExecutor.executeBatch(
25442
- selectedToolUses,
25443
- this.ctx,
25444
- this.executionStrategy
25445
- );
25446
- const useById = new Map(selectedToolUses.map((u) => [u.id, u]));
25447
- const resultsForMessage = [];
25448
- for (const { result, tool, durationMs } of outputs) {
25449
- if (result.type === "tool_confirm_pending") {
25450
- const decision = await this.waitForConfirm({
25451
- tool,
25452
- input: result.input,
25453
- toolUseId: result.toolUseId,
25454
- suggestedPattern: result.suggestedPattern
25455
- });
25456
- if (decision === "always") {
25457
- try {
25458
- await this.permission.trust({
25459
- tool: tool.name,
25460
- pattern: result.suggestedPattern
25461
- });
25462
- this.events.emit("trust.persisted", {
25463
- tool: tool.name,
25464
- pattern: result.suggestedPattern,
25465
- decision
25466
- });
25467
- } catch {
25468
- }
25469
- } else if (decision === "deny") {
25470
- try {
25471
- await this.permission.deny({
25472
- tool: tool.name,
25473
- pattern: result.suggestedPattern
25474
- });
25475
- this.events.emit("trust.persisted", {
25476
- tool: tool.name,
25477
- pattern: result.suggestedPattern,
25478
- decision
25479
- });
25480
- } catch {
25481
- }
25482
- }
25483
- if (decision === "yes") {
25484
- const p = this.permission;
25485
- p.allowOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
25486
- } else if (decision === "no") {
25487
- const p = this.permission;
25488
- p.denyOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
25489
- }
25490
- const reRunResult = decision === "yes" || decision === "always" ? await this.executeSingleWithDecision(
25491
- tool,
25492
- { id: result.toolUseId, name: tool.name, input: result.input }
25493
- ) : {
25494
- result: {
25495
- type: "tool_result",
25496
- tool_use_id: result.toolUseId,
25497
- content: decision === "deny" ? `Tool "${tool.name}" denied and blocked for this pattern.` : `Tool "${tool.name}" denied by user.`,
25498
- is_error: true
25499
- },
25500
- durationMs: 0
25501
- };
25502
- const use2 = useById.get(reRunResult.result.tool_use_id);
25503
- if (use2) {
25504
- await this.pipelines.toolCall.run({
25505
- toolUse: use2,
25506
- result: reRunResult.result,
25507
- ctx: this.ctx,
25508
- tool
25509
- });
25510
- await this.ctx.session.append({
25511
- type: "tool_result",
25512
- ts: (/* @__PURE__ */ new Date()).toISOString(),
25513
- id: reRunResult.result.tool_use_id,
25514
- content: reRunResult.result.content,
25515
- isError: !!reRunResult.result.is_error
25516
- });
25517
- {
25518
- const sig = sizeSignals(tool?.name, reRunResult.result.content);
25519
- this.events.emit("tool.executed", {
25520
- id: reRunResult.result.tool_use_id,
25521
- name: tool.name,
25522
- durationMs: reRunResult.durationMs,
25523
- ok: !reRunResult.result.is_error,
25524
- input: result.input,
25525
- output: truncateForEvent(reRunResult.result.content),
25526
- outputBytes: sig.outputBytes,
25527
- outputTokens: sig.outputTokens,
25528
- outputLines: sig.outputLines
25529
- });
25530
- }
25531
- }
25532
- resultsForMessage.push(reRunResult.result);
25533
- continue;
25956
+ }
25957
+
25958
+ // src/hooks/runner.ts
25959
+ var HookRunner = class {
25960
+ constructor(opts) {
25961
+ this.opts = opts;
25962
+ this.registry = opts.registry;
25963
+ }
25964
+ opts;
25965
+ registry;
25966
+ /** Cheap guard so callers can skip building payloads when nothing listens. */
25967
+ has(event) {
25968
+ return this.registry.has(event);
25969
+ }
25970
+ async preToolUse(toolName, toolInput, env) {
25971
+ const entries = this.matching("PreToolUse", toolName);
25972
+ if (entries.length === 0) return {};
25973
+ let current = toolInput;
25974
+ let mutated = false;
25975
+ for (const entry of entries) {
25976
+ const payload = {
25977
+ event: "PreToolUse",
25978
+ toolName,
25979
+ toolInput: current,
25980
+ ...this.base(env)
25981
+ };
25982
+ const outcome = await this.invoke(entry, payload);
25983
+ if (!outcome) continue;
25984
+ if (outcome.modifiedInput && typeof outcome.modifiedInput === "object") {
25985
+ current = outcome.modifiedInput;
25986
+ mutated = true;
25534
25987
  }
25535
- resultsForMessage.push(result);
25536
- const use = useById.get(result.tool_use_id);
25537
- if (!use) continue;
25538
- await this.pipelines.toolCall.run({
25539
- toolUse: use,
25540
- result,
25541
- ctx: this.ctx,
25542
- tool: tool ?? void 0
25543
- });
25544
- await this.ctx.session.append({
25545
- type: "tool_result",
25546
- ts: (/* @__PURE__ */ new Date()).toISOString(),
25547
- id: result.tool_use_id,
25548
- content: result.content,
25549
- isError: !!result.is_error
25550
- });
25551
- {
25552
- const sig = sizeSignals(use.name, result.content);
25553
- this.events.emit("tool.executed", {
25554
- id: result.tool_use_id,
25555
- name: use.name,
25556
- durationMs,
25557
- ok: !result.is_error,
25558
- input: use.input,
25559
- output: truncateForEvent(result.content),
25560
- outputBytes: sig.outputBytes,
25561
- outputTokens: sig.outputTokens,
25562
- outputLines: sig.outputLines
25563
- });
25988
+ if (outcome.decision === "block") {
25989
+ return { block: true, reason: outcome.reason ?? `Blocked by ${entry.event} hook` };
25564
25990
  }
25565
25991
  }
25566
- this.ctx.state.appendMessage({
25567
- role: "user",
25568
- content: resultsForMessage
25569
- });
25570
- await this.extensions.runAfterToolExecution(this.ctx, outputs);
25992
+ return mutated ? { input: current } : {};
25571
25993
  }
25572
- /**
25573
- * Fold any pending `/btw` notes into the conversation. Called at the top of
25574
- * each iteration so a note set mid-run reaches the model on its next turn.
25575
- *
25576
- * To stay valid across all provider wire families we never create two
25577
- * consecutive user messages: if the last message is a user turn (e.g. the
25578
- * tool_result block from the previous iteration) we append the note as an
25579
- * extra text block on that turn; otherwise we add a fresh user message.
25580
- */
25581
- injectPendingBtwNotes() {
25582
- const notes = consumeBtwNotes(this.ctx);
25583
- if (notes.length === 0) return;
25584
- const block = { type: "text", text: buildBtwBlock(notes) };
25585
- const messages = this.ctx.messages;
25586
- const last = messages[messages.length - 1];
25587
- if (last && last.role === "user") {
25588
- const content = typeof last.content === "string" ? [{ type: "text", text: last.content }, block] : [...last.content, block];
25589
- this.ctx.state.replaceMessages([...messages.slice(0, -1), { ...last, content }]);
25590
- } else {
25591
- this.ctx.state.appendMessage({ role: "user", content: [block] });
25994
+ async postToolUse(toolName, toolInput, result, env) {
25995
+ const payload = {
25996
+ event: "PostToolUse",
25997
+ toolName,
25998
+ toolInput,
25999
+ toolResult: result,
26000
+ ...this.base(env)
26001
+ };
26002
+ return { additionalContext: await this.collectContext("PostToolUse", toolName, payload) };
26003
+ }
26004
+ async userPromptSubmit(prompt, env) {
26005
+ const entries = this.matching("UserPromptSubmit", void 0);
26006
+ if (entries.length === 0) return {};
26007
+ const payload = { event: "UserPromptSubmit", prompt, ...this.base(env) };
26008
+ const parts = [];
26009
+ for (const entry of entries) {
26010
+ const outcome = await this.invoke(entry, payload);
26011
+ if (!outcome) continue;
26012
+ if (outcome.decision === "block") {
26013
+ return { block: true, reason: outcome.reason ?? "Blocked by UserPromptSubmit hook" };
26014
+ }
26015
+ if (outcome.additionalContext) parts.push(outcome.additionalContext);
25592
26016
  }
26017
+ return parts.length ? { additionalContext: parts.join("\n") } : {};
25593
26018
  }
25594
- waitForConfirm(info) {
25595
- return new Promise((resolve12) => {
25596
- this.events.emit("tool.confirm_needed", {
25597
- tool: info.tool,
25598
- input: info.input,
25599
- toolUseId: info.toolUseId,
25600
- suggestedPattern: info.suggestedPattern,
25601
- resolve: resolve12
25602
- });
25603
- });
26019
+ async sessionStart(env) {
26020
+ const payload = { event: "SessionStart", ...this.base(env) };
26021
+ return { additionalContext: await this.collectContext("SessionStart", void 0, payload) };
25604
26022
  }
25605
- async executeSingleWithDecision(tool, use) {
25606
- const start = Date.now();
26023
+ async stop(env) {
26024
+ const payload = { event: "Stop", ...this.base(env) };
26025
+ await this.collectContext("Stop", void 0, payload);
26026
+ }
26027
+ // ── internals ──────────────────────────────────────────────────────
26028
+ base(env) {
26029
+ const sessionId = this.opts.sessionId?.();
26030
+ return sessionId ? { cwd: env.cwd, sessionId } : { cwd: env.cwd };
26031
+ }
26032
+ matching(event, toolName) {
26033
+ return this.registry.list(event).filter((e) => hookMatcherMatches(e.matcher, toolName));
26034
+ }
26035
+ async invoke(entry, payload) {
25607
26036
  try {
25608
- const result = await this.toolExecutor.executeTool(
25609
- tool,
25610
- use,
25611
- this.ctx,
25612
- this.perIterationOutputCapBytes
26037
+ if (entry.kind === "inprocess") {
26038
+ const r = await entry.hook(payload);
26039
+ return r ?? null;
26040
+ }
26041
+ if (this.opts.allowShell === false) return null;
26042
+ return await runShellHook(
26043
+ { command: entry.command, timeoutMs: entry.timeoutMs },
26044
+ payload,
26045
+ this.opts.logger
25613
26046
  );
25614
- return { result, durationMs: Date.now() - start };
25615
26047
  } catch (err) {
25616
- const msg = err instanceof Error ? err.message : String(err);
25617
- return {
25618
- result: {
25619
- type: "tool_result",
25620
- tool_use_id: use.id,
25621
- content: `Tool "${tool.name}" threw: ${msg}`,
25622
- is_error: true
25623
- },
25624
- durationMs: Date.now() - start
25625
- };
26048
+ this.opts.logger?.warn?.(
26049
+ `${payload.event} hook threw: ${err instanceof Error ? err.message : String(err)}`
26050
+ );
26051
+ return null;
25626
26052
  }
25627
26053
  }
25628
- /**
25629
- * Run context window pipeline. The pipeline may be empty, or it may contain
25630
- * middleware with its own injected dependencies.
25631
- */
25632
- async compactContextIfNeeded() {
25633
- await this.pipelines.contextWindow.run(this.ctx);
25634
- }
25635
- /**
25636
- * Emit the current context window load as a `ctx.pct` event so subscribers
25637
- * (FleetBus → TUI) can render a live fill bar per agent.
25638
- */
25639
- emitContextPct() {
25640
- const maxContext = this.ctx.provider.capabilities.maxContext ?? 2e5;
25641
- const { total } = estimateRequestTokens(
25642
- this.ctx.messages,
25643
- this.ctx.systemPrompt,
25644
- this.ctx.tools ?? []
25645
- );
25646
- this.events.emit("ctx.pct", { load: total / maxContext, tokens: total, maxContext });
26054
+ async collectContext(event, toolName, payload) {
26055
+ const entries = this.matching(event, toolName);
26056
+ if (entries.length === 0) return void 0;
26057
+ const parts = [];
26058
+ for (const entry of entries) {
26059
+ const outcome = await this.invoke(entry, payload);
26060
+ if (outcome?.additionalContext) parts.push(outcome.additionalContext);
26061
+ }
26062
+ return parts.length ? parts.join("\n") : void 0;
25647
26063
  }
25648
26064
  };
25649
- function toError(err) {
25650
- return err instanceof Error ? err : new Error(String(err));
25651
- }
25652
- function truncateForEvent(content, max = 400) {
25653
- if (!content) return "";
25654
- return content.length <= max ? content : `${content.slice(0, max - 1)}\u2026`;
25655
- }
25656
- function sizeSignals(toolName, content) {
25657
- if (typeof content !== "string" || content.length === 0) {
25658
- return { outputBytes: 0, outputTokens: 0, outputLines: void 0 };
25659
- }
25660
- const outputBytes = Buffer.byteLength(content, "utf8");
25661
- const outputTokens = Math.max(1, Math.round(outputBytes / 3.5));
25662
- let outputLines;
25663
- if (toolName === "read") {
25664
- const lineRe = /^\s*\d+→/gm;
25665
- let count = 0;
25666
- while (lineRe.exec(content) !== null) count++;
25667
- if (count > 0) outputLines = count;
25668
- } else if (toolName === "bash" || toolName === "shell" || toolName === "grep" || toolName === "logs") {
25669
- let nl = 0;
25670
- for (let i = 0; i < content.length; i++) if (content.charCodeAt(i) === 10) nl++;
25671
- outputLines = nl + (content.endsWith("\n") ? 0 : 1);
25672
- }
25673
- return { outputBytes, outputTokens, outputLines };
25674
- }
25675
26065
  async function bootConfig(options = {}) {
25676
26066
  const { flags = {}, appLabel = "wstack", loadSyncConfig = true } = options;
25677
26067
  const cwd = typeof flags["cwd"] === "string" ? path6.resolve(flags["cwd"]) : process.cwd();
@@ -25719,6 +26109,10 @@ function flagsToConfigPatch(flags) {
25719
26109
  const patch = {};
25720
26110
  if (typeof flags["provider"] === "string") patch.provider = flags["provider"];
25721
26111
  if (typeof flags["model"] === "string") patch.model = flags["model"];
26112
+ if (typeof flags["fallback-model"] === "string") {
26113
+ const list = flags["fallback-model"].split(",").map((s) => s.trim()).filter(Boolean);
26114
+ if (list.length > 0) patch.fallbackModels = list;
26115
+ }
25722
26116
  if (typeof flags["cwd"] === "string") patch.cwd = flags["cwd"];
25723
26117
  if (typeof flags["log-level"] === "string") {
25724
26118
  patch.log = { level: flags["log-level"] };
@@ -26050,8 +26444,8 @@ var InputBuilder = class {
26050
26444
  async registerFile(input) {
26051
26445
  const ref = await this.store.add({ ...input, kind: "file" });
26052
26446
  this.refs.push(ref);
26053
- const path36 = ref.meta.filename ?? ref.meta.label ?? String(ref.seq);
26054
- return `[file:${path36}]`;
26447
+ const path37 = ref.meta.filename ?? ref.meta.label ?? String(ref.seq);
26448
+ return `[file:${path37}]`;
26055
26449
  }
26056
26450
  /**
26057
26451
  * Whether `appendPaste(text)` would collapse the text to a placeholder
@@ -26451,12 +26845,12 @@ ${mem}`);
26451
26845
  }
26452
26846
  }
26453
26847
  async gitStatus(root) {
26454
- return new Promise((resolve12) => {
26848
+ return new Promise((resolve13) => {
26455
26849
  let settled = false;
26456
26850
  const finish = (s) => {
26457
26851
  if (settled) return;
26458
26852
  settled = true;
26459
- resolve12(s);
26853
+ resolve13(s);
26460
26854
  };
26461
26855
  let proc;
26462
26856
  const timer = setTimeout(() => {
@@ -26851,9 +27245,13 @@ var DefaultPluginAPI = class {
26851
27245
  config;
26852
27246
  log;
26853
27247
  configStore;
27248
+ hookRegistry;
27249
+ ownerName;
26854
27250
  pluginCleanupFns = [];
26855
27251
  constructor(init) {
26856
27252
  const owner = init.ownerName;
27253
+ this.ownerName = owner;
27254
+ this.hookRegistry = init.hookRegistry;
26857
27255
  this.container = init.container;
26858
27256
  this.events = init.events;
26859
27257
  this.config = init.config;
@@ -26940,6 +27338,13 @@ var DefaultPluginAPI = class {
26940
27338
  registerSystemPromptContributor(c) {
26941
27339
  return this.extensions.registerSystemPromptContributor(c);
26942
27340
  }
27341
+ registerHook(event, matcher, hook) {
27342
+ if (!this.hookRegistry) return () => {
27343
+ };
27344
+ const off = this.hookRegistry.registerInProcess(event, matcher, hook, this.ownerName);
27345
+ this.pluginCleanupFns.push(off);
27346
+ return off;
27347
+ }
26943
27348
  };
26944
27349
  var noopMcp = {
26945
27350
  start: async () => void 0,
@@ -27662,7 +28067,7 @@ var PhaseOrchestrator = class {
27662
28067
  async mergeOne(phase, handle) {
27663
28068
  if (!this.worktrees) return;
27664
28069
  try {
27665
- const resolve12 = this.ctx.resolveConflict ? async (info) => {
28070
+ const resolve13 = this.ctx.resolveConflict ? async (info) => {
27666
28071
  const shouldResolve = await this.shouldAttemptConflictResolution(phase, info);
27667
28072
  if (!shouldResolve) return false;
27668
28073
  this.emit("phase.conflictResolving", {
@@ -27672,7 +28077,7 @@ var PhaseOrchestrator = class {
27672
28077
  });
27673
28078
  return this.ctx.resolveConflict(phase, info);
27674
28079
  } : void 0;
27675
- const result = await this.worktrees.merge(handle, { squash: true, resolve: resolve12 });
28080
+ const result = await this.worktrees.merge(handle, { squash: true, resolve: resolve13 });
27676
28081
  if (result.resolved) {
27677
28082
  this.emit("phase.conflictResolved", { phaseId: phase.id, name: phase.name });
27678
28083
  }
@@ -28022,7 +28427,7 @@ var PhaseOrchestrator = class {
28022
28427
  }
28023
28428
  }
28024
28429
  delay(ms) {
28025
- return new Promise((resolve12) => setTimeout(resolve12, ms));
28430
+ return new Promise((resolve13) => setTimeout(resolve13, ms));
28026
28431
  }
28027
28432
  };
28028
28433
 
@@ -29059,12 +29464,12 @@ var BrainDecisionQueue = class {
29059
29464
  options: request.options,
29060
29465
  rationale: "Decision escalated to human authority."
29061
29466
  };
29062
- const pending = new Promise((resolve12) => {
29063
- const entry = { request, resolve: resolve12 };
29467
+ const pending = new Promise((resolve13) => {
29468
+ const entry = { request, resolve: resolve13 };
29064
29469
  if (this.opts.timeoutMs && this.opts.timeoutMs > 0) {
29065
29470
  entry.timer = setTimeout(() => {
29066
29471
  this.pending.delete(request.id);
29067
- resolve12({ type: "deny", reason: "Brain human decision timed out." });
29472
+ resolve13({ type: "deny", reason: "Brain human decision timed out." });
29068
29473
  }, this.opts.timeoutMs);
29069
29474
  }
29070
29475
  this.pending.set(request.id, entry);
@@ -29179,8 +29584,8 @@ var CollaborationBus = class {
29179
29584
  if (this.isPaused()) return false;
29180
29585
  this.pausedAtMs = Date.now();
29181
29586
  this.pausedBy = byParticipant;
29182
- this.pausePromise = new Promise((resolve12) => {
29183
- this.pauseResolve = resolve12;
29587
+ this.pausePromise = new Promise((resolve13) => {
29588
+ this.pauseResolve = resolve13;
29184
29589
  });
29185
29590
  return true;
29186
29591
  }
@@ -29216,8 +29621,8 @@ var CollaborationBus = class {
29216
29621
  return true;
29217
29622
  }
29218
29623
  let timer;
29219
- const timeoutPromise = new Promise((resolve12) => {
29220
- timer = setTimeout(() => resolve12("timeout"), timeoutMs);
29624
+ const timeoutPromise = new Promise((resolve13) => {
29625
+ timer = setTimeout(() => resolve13("timeout"), timeoutMs);
29221
29626
  });
29222
29627
  const resumedPromise = this.pausePromise.then(() => "resumed");
29223
29628
  const winner = await Promise.race([resumedPromise, timeoutPromise]);
@@ -29708,7 +30113,7 @@ function createGitPlugin() {
29708
30113
  }
29709
30114
  async function runGit(args, cwd) {
29710
30115
  try {
29711
- return await new Promise((resolve12, reject) => {
30116
+ return await new Promise((resolve13, reject) => {
29712
30117
  const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
29713
30118
  let stdout = "";
29714
30119
  let stderr = "";
@@ -29729,7 +30134,7 @@ async function runGit(args, cwd) {
29729
30134
  })
29730
30135
  );
29731
30136
  });
29732
- child.on("close", (code) => resolve12({ stdout, stderr, code: code ?? 0 }));
30137
+ child.on("close", (code) => resolve13({ stdout, stderr, code: code ?? 0 }));
29733
30138
  });
29734
30139
  } catch (err) {
29735
30140
  if (err instanceof WrongStackError) throw err;
@@ -30405,6 +30810,6 @@ ${formatPlan(updated)}`
30405
30810
  };
30406
30811
  }
30407
30812
 
30408
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, onResize, parseContinueDirective, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, setRawMode, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, writeErr, writeOut, zaiVisionServer };
30813
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BudgetExceededError, CONTEXT_WINDOW_MODES, CORE_RECONSTRUCT_EVENTS, CheckpointManager, CloudSync, CollaborationBus, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_AUTONOMY_CONFIG, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_CONFIG, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SESSION_LOGGING_CONFIG, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, NULL_FLEET_BUS, NoopMetricsSink, NoopTracer, OTelTracer, ObservableBrainArbiter, PROMETHEUS_CONTENT_TYPE, ParallelEternalEngine, PhaseGraphBuilder, PhaseOrchestrator, PhaseStore, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, ReplayLogStore, ReplayProviderRunner, ReportGenerator, RunController, SECURITY_SCANNER_AGENT, SPEC_TEMPLATES, STANDARD_AUDIT_EVENTS, ScopedEventBus, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionRecovery, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertSafePath, atomicWrite, attachAutoExtend, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, consumeBtwNotes, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createSyncPlugin, createToolOutputSerializer, decryptConfigSecrets, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, dispatchAgent, downloadGitHubTarball, emptyGoal, emptyPlan, encryptConfigSecrets, ensureDir, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, extractRunEnv, filesystemServer, findCriticalPath, flagsToConfigPatch, formatContextWindowModeList, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getPlanTemplate, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hashRequest, hookMatcherMatches, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, onResize, parseContinueDirective, parseSkillRef, pendingBtwCount, phaseForRole, projectHash, recordActualUsage, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveContextWindowPolicy, resolveModelMatrix, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTodosCheckpoint, scoreAgents, securitySlashCommand, sentinelServer, setBtwNote, setPlanItemStatus, setRawMode, slackServer, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, writeErr, writeOut, zaiVisionServer };
30409
30814
  //# sourceMappingURL=index.js.map
30410
30815
  //# sourceMappingURL=index.js.map