@wrongstack/core 0.250.0 → 0.256.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/{agent-bridge-4gc0vfW2.d.ts → agent-bridge-BrxWHEOm.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-Dz-9kiE6.d.ts → agent-subagent-runner-US741uBH.d.ts} +17 -8
  3. package/dist/{brain-sCZ3lCjq.d.ts → brain-TjEEwSpw.d.ts} +18 -1
  4. package/dist/{compactor-BRfg3QPd.d.ts → compactor-C5sT4U7I.d.ts} +1 -1
  5. package/dist/{config-eSsrto5d.d.ts → config-DuAu23zm.d.ts} +16 -1
  6. package/dist/{context-CLz3z_E8.d.ts → context-CGdgA0q6.d.ts} +13 -0
  7. package/dist/coordination/index.d.ts +14 -14
  8. package/dist/coordination/index.js +153 -2
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +238 -42
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +15 -15
  14. package/dist/execution/index.js +121 -22
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +6 -6
  18. package/dist/{goal-preamble-BjJpnLW4.d.ts → goal-preamble-UiEkbNmW.d.ts} +21 -10
  19. package/dist/{index-Dy8OwfBD.d.ts → index-CC0Mcm05.d.ts} +9 -9
  20. package/dist/{index-IehiNryU.d.ts → index-CitPrI3a.d.ts} +20 -7
  21. package/dist/index.d.ts +112 -42
  22. package/dist/index.js +759 -114
  23. package/dist/index.js.map +1 -1
  24. package/dist/infrastructure/index.d.ts +6 -6
  25. package/dist/infrastructure/index.js +12 -0
  26. package/dist/infrastructure/index.js.map +1 -1
  27. package/dist/kernel/index.d.ts +10 -10
  28. package/dist/kernel/index.js.map +1 -1
  29. package/dist/{llm-selector-D22R4AFz.d.ts → llm-selector-CJ4SyAFE.d.ts} +2 -2
  30. package/dist/{mcp-servers-DfXxCASH.d.ts → mcp-servers-D8YnLaEp.d.ts} +3 -3
  31. package/dist/models/index.d.ts +5 -5
  32. package/dist/{models-registry-DpanBg8D.d.ts → models-registry-ByZCdFuQ.d.ts} +1 -1
  33. package/dist/{multi-agent-coordinator-CnbEqpv0.d.ts → multi-agent-coordinator-DqTUEAeC.d.ts} +1 -1
  34. package/dist/{null-fleet-bus-Do1OLYpj.d.ts → null-fleet-bus-B5mfTJXT.d.ts} +17 -6
  35. package/dist/observability/index.d.ts +2 -2
  36. package/dist/{package-outdated-watcher-CA5GGB4C.d.ts → package-outdated-watcher-BSgR_kK-.d.ts} +24 -3
  37. package/dist/{parallel-eternal-engine-UZg1xOzE.d.ts → parallel-eternal-engine-C0juOszP.d.ts} +24 -10
  38. package/dist/{path-resolver-BaP06Owy.d.ts → path-resolver-CbkT-RMU.d.ts} +3 -3
  39. package/dist/{permission-DbWPbuoA.d.ts → permission-CwBBpCoF.d.ts} +1 -1
  40. package/dist/{permission-policy-AOk0LVsV.d.ts → permission-policy-B8rSu908.d.ts} +39 -2
  41. package/dist/{pipeline-D1n-gQI-.d.ts → pipeline-JG8XoudC.d.ts} +43 -3
  42. package/dist/{plan-templates-BUVRY0pU.d.ts → plan-templates-DPiQMkBz.d.ts} +5 -5
  43. package/dist/{provider-runner-D0HgUqwV.d.ts → provider-runner-hM7EXlLI.d.ts} +3 -3
  44. package/dist/{retry-policy-BVnkbMET.d.ts → retry-policy-Tg7LXkoK.d.ts} +1 -1
  45. package/dist/sdd/index.d.ts +8 -8
  46. package/dist/{secret-vault-CeVNiy_f.d.ts → secret-vault-BkYkJWQs.d.ts} +1 -1
  47. package/dist/security/index.d.ts +4 -4
  48. package/dist/security/index.js +89 -18
  49. package/dist/security/index.js.map +1 -1
  50. package/dist/{selector-Cb4_9-hf.d.ts → selector-DWsqVjGf.d.ts} +1 -1
  51. package/dist/{session-event-bridge-BhtkkFFy.d.ts → session-event-bridge-BAFWdgQ3.d.ts} +1 -1
  52. package/dist/{session-reader-CCOssnBS.d.ts → session-reader-CqRvaL5v.d.ts} +1 -1
  53. package/dist/{skill-Bj6Ezqb8.d.ts → skill-DGIXCtdv.d.ts} +6 -0
  54. package/dist/skills/index.d.ts +1 -1
  55. package/dist/storage/index.d.ts +10 -10
  56. package/dist/storage/index.js +8 -1
  57. package/dist/storage/index.js.map +1 -1
  58. package/dist/types/index.d.ts +19 -19
  59. package/dist/types/index.js +95 -25
  60. package/dist/types/index.js.map +1 -1
  61. package/dist/utils/index.d.ts +2 -2
  62. package/dist/utils/index.js +5 -0
  63. package/dist/utils/index.js.map +1 -1
  64. package/package.json +2 -2
  65. package/skills/api-design/SKILL.md +1 -0
  66. package/skills/api-design/SKILL.save.md +26 -0
  67. package/skills/audit-log/SKILL.md +9 -2
  68. package/skills/audit-log/SKILL.save.md +22 -0
  69. package/skills/bug-hunter/SKILL.md +10 -2
  70. package/skills/bug-hunter/SKILL.save.md +33 -0
  71. package/skills/chimera/SKILL.md +12 -18
  72. package/skills/chimera/SKILL.save.md +26 -0
  73. package/skills/docker-deploy/SKILL.md +1 -0
  74. package/skills/docker-deploy/SKILL.save.md +23 -0
  75. package/skills/git-flow/SKILL.md +23 -2
  76. package/skills/git-flow/SKILL.save.md +25 -0
  77. package/skills/multi-agent/SKILL.md +23 -2
  78. package/skills/multi-agent/SKILL.save.md +26 -0
  79. package/skills/node-modern/SKILL.md +2 -1
  80. package/skills/node-modern/SKILL.save.md +21 -0
  81. package/skills/observability/SKILL.md +1 -0
  82. package/skills/observability/SKILL.save.md +34 -0
  83. package/skills/output-standards/SKILL.md +133 -0
  84. package/skills/output-standards/SKILL.save.md +21 -0
  85. package/skills/prompt-engineering/SKILL.md +2 -1
  86. package/skills/prompt-engineering/SKILL.save.md +29 -0
  87. package/skills/react-modern/SKILL.md +2 -1
  88. package/skills/react-modern/SKILL.save.md +24 -0
  89. package/skills/refactor-planner/SKILL.md +9 -2
  90. package/skills/refactor-planner/SKILL.save.md +26 -0
  91. package/skills/research-web/SKILL.md +1 -0
  92. package/skills/research-web/SKILL.save.md +25 -0
  93. package/skills/sdd/SKILL.md +2 -1
  94. package/skills/sdd/SKILL.save.md +19 -0
  95. package/skills/security-scanner/SKILL.md +10 -3
  96. package/skills/security-scanner/SKILL.save.md +23 -0
  97. package/skills/skill-creator/SKILL.md +2 -1
  98. package/skills/skill-creator/SKILL.save.md +20 -0
  99. package/skills/tech-stack/SKILL.md +13 -226
  100. package/skills/tech-stack/SKILL.save.md +25 -0
  101. package/skills/testing/SKILL.md +1 -0
  102. package/skills/testing/SKILL.save.md +22 -0
  103. package/skills/typescript-strict/SKILL.md +2 -1
  104. package/skills/typescript-strict/SKILL.save.md +19 -0
package/dist/index.js CHANGED
@@ -190,6 +190,10 @@ var init_session_registry = __esm({
190
190
  * Starts the heartbeat timer.
191
191
  */
192
192
  async register(entry) {
193
+ if (this.heartbeatTimer) {
194
+ clearInterval(this.heartbeatTimer);
195
+ this.heartbeatTimer = null;
196
+ }
193
197
  this.currentSessionId = entry.sessionId;
194
198
  const full = {
195
199
  ...entry,
@@ -201,7 +205,10 @@ var init_session_registry = __esm({
201
205
  await this.atomicUpdate((registry) => {
202
206
  const now = Date.now();
203
207
  for (const [id, existing] of Object.entries(registry)) {
204
- if (existing.pid === entry.pid) continue;
208
+ if (existing.pid === entry.pid) {
209
+ if (id !== entry.sessionId) delete registry[id];
210
+ continue;
211
+ }
205
212
  const heartbeatAge = now - new Date(existing.lastHeartbeatAt).getTime();
206
213
  if (heartbeatAge > STALE_TIMEOUT_MS && !pidAlive(existing.pid)) {
207
214
  delete registry[id];
@@ -2045,6 +2052,10 @@ var DefaultTokenCounter = class {
2045
2052
  const price = model ? this.priceCache.get(model) : void 0;
2046
2053
  if (price) {
2047
2054
  this.applyPrice(usage, price);
2055
+ this.events?.emit("token.accounted", {
2056
+ usage: this.total(),
2057
+ cost: { input: this.costInput, output: this.costOutput, total: this.costInput + this.costOutput }
2058
+ });
2048
2059
  } else if (this.registry && this.providerId && model) {
2049
2060
  if (this.priceCache.size >= PRICE_CACHE_MAX_SIZE) {
2050
2061
  const keys = [...this.priceCache.keys()];
@@ -2055,6 +2066,10 @@ var DefaultTokenCounter = class {
2055
2066
  const p = priceFromModel(m);
2056
2067
  this.priceCache.set(model, p);
2057
2068
  this.applyPrice(usage, p);
2069
+ this.events?.emit("token.accounted", {
2070
+ usage: this.total(),
2071
+ cost: { input: this.costInput, output: this.costOutput, total: this.costInput + this.costOutput }
2072
+ });
2058
2073
  }
2059
2074
  }).catch(() => {
2060
2075
  this.events?.emit("token.cost_estimate_unavailable", { model: model ?? "<unknown>" });
@@ -2077,6 +2092,10 @@ var DefaultTokenCounter = class {
2077
2092
  }
2078
2093
  this.priceCache.set(resolved.modelId, price);
2079
2094
  this.applyPrice(usage, price);
2095
+ this.events?.emit("token.accounted", {
2096
+ usage: this.total(),
2097
+ cost: { input: this.costInput, output: this.costOutput, total: this.costInput + this.costOutput }
2098
+ });
2080
2099
  }
2081
2100
  total() {
2082
2101
  return {
@@ -3042,9 +3061,28 @@ var PATTERNS = [
3042
3061
  }
3043
3062
  ];
3044
3063
  var SCRUB_CHUNK_BYTES = 64 * 1024;
3064
+ function hasCredentialAnchors(text) {
3065
+ return text.includes("-----BEGIN") || // Private keys (most unique → cheap reject)
3066
+ text.includes("sk-") || // Anthropic + OpenAI keys
3067
+ text.includes("sk_") || // Stripe live/test keys
3068
+ text.includes("ghp_") || // GitHub PAT v1
3069
+ text.includes("github_pat_") || // GitHub PAT v2
3070
+ text.includes("eyJ") || // JWT
3071
+ text.includes("AKIA") || // AWS access key
3072
+ text.includes("AIza") || // GCP service key
3073
+ text.includes("xox") || // Slack token (xoxa/xoxb/xoxp/xoxo/xoxs)
3074
+ text.includes("Bearer ") || // Bearer token (space suffix reduces false positives)
3075
+ text.includes("/bot") || // Telegram bot token (URL path pattern)
3076
+ text.includes("_KEY=") || // High-entropy env vars: API_KEY=, SECRET_KEY=, ...
3077
+ text.includes("_TOKEN=") || // ACCESS_TOKEN=, AUTH_TOKEN=, ...
3078
+ text.includes("_SECRET=") || // API_SECRET=, CLIENT_SECRET=, ...
3079
+ text.includes("_PASSWORD=") || // DB_PASSWORD=, ROOT_PASSWORD=, ...
3080
+ text.includes("mongodb://") || text.includes("mongodb+srv://") || text.includes("postgres://") || text.includes("postgresql://") || text.includes("mysql://") || text.includes("redis://");
3081
+ }
3045
3082
  var DefaultSecretScrubber = class {
3046
3083
  scrub(text) {
3047
3084
  if (!text) return text;
3085
+ if (!hasCredentialAnchors(text)) return text;
3048
3086
  if (text.length <= SCRUB_CHUNK_BYTES) {
3049
3087
  return this.scrubOne(text);
3050
3088
  }
@@ -3062,6 +3100,7 @@ var DefaultSecretScrubber = class {
3062
3100
  return out.join("");
3063
3101
  }
3064
3102
  scrubOne(text) {
3103
+ if (!hasCredentialAnchors(text)) return text;
3065
3104
  let out = text;
3066
3105
  for (const p of PATTERNS) {
3067
3106
  out = out.replace(p.regex, (_match, group1, group2) => {
@@ -4099,10 +4138,14 @@ var ToolCapabilities = {
4099
4138
  SHELL_ARBITRARY: "shell.arbitrary",
4100
4139
  /** Can execute a restricted set of commands (the `exec` tool). */
4101
4140
  SHELL_RESTRICTED: "shell.restricted",
4141
+ /** Can read files inside the project (and possibly outside via symlinks if not guarded). */
4142
+ FS_READ: "fs.read",
4102
4143
  /** Can write / modify / delete files inside the project. */
4103
4144
  FS_WRITE: "fs.write",
4104
4145
  /** Can write files outside the current project root (very high risk). */
4105
4146
  FS_WRITE_OUTSIDE_PROJECT: "fs.write.outside-project",
4147
+ /** Can perform outbound network requests. */
4148
+ NET_OUTBOUND: "net.outbound",
4106
4149
  /** Proxies tools from external MCP servers (unknown capability). */
4107
4150
  MCP_PROXY: "mcp.proxy",
4108
4151
  /** Can spawn or manage subagents / multi-agent tasks. */
@@ -4339,7 +4382,7 @@ var ToolExecutor = class _ToolExecutor {
4339
4382
  const tool = this.registry.get(use.name);
4340
4383
  if (!tool) {
4341
4384
  const result = this.unknownToolResult(use, () => this.registry.list().map((t2) => t2.name));
4342
- budget = this.decrementBudget(result, budget);
4385
+ budget = this.budgetForString(result.content, budget);
4343
4386
  return { result, tool, durationMs: Date.now() - start };
4344
4387
  }
4345
4388
  const validation = validateAgainstSchema(use.input, tool.inputSchema);
@@ -4356,20 +4399,20 @@ ${errorDetails}
4356
4399
  Please call the tool again with arguments that match its inputSchema. You can use the "tool-help" tool with name="${tool.name}" to see the exact expected schema.`,
4357
4400
  is_error: true
4358
4401
  };
4359
- budget = this.decrementBudget(result, budget);
4402
+ budget = this.budgetForString(result.content, budget);
4360
4403
  return { result, tool, durationMs: Date.now() - start };
4361
4404
  }
4362
4405
  const toolDangerousCaps = getDangerousCapabilities(tool);
4363
4406
  if (hasMalformedArguments(use.input)) {
4364
4407
  const result = this.malformedInputResult(use, extractMalformedRaw(use.input));
4365
- budget = this.decrementBudget(result, budget);
4408
+ budget = this.budgetForString(result.content, budget);
4366
4409
  return { result, tool, durationMs: Date.now() - start };
4367
4410
  }
4368
4411
  if (this.opts.hookRunner?.has("PreToolUse")) {
4369
4412
  const pre = await this.opts.hookRunner.preToolUse(tool.name, use.input, ctx);
4370
4413
  if (pre.block) {
4371
4414
  const result = this.blockedByHookResult(use, pre.reason);
4372
- budget = this.decrementBudget(result, budget);
4415
+ budget = this.budgetForString(result.content, budget);
4373
4416
  return { result, tool, durationMs: Date.now() - start };
4374
4417
  }
4375
4418
  if (pre.input) {
@@ -4385,7 +4428,7 @@ Validation errors:
4385
4428
  ${errorDetails}`,
4386
4429
  is_error: true
4387
4430
  };
4388
- budget = this.decrementBudget(result, budget);
4431
+ budget = this.budgetForString(result.content, budget);
4389
4432
  return { result, tool, durationMs: Date.now() - start };
4390
4433
  }
4391
4434
  use = { ...use, input: pre.input };
@@ -4400,7 +4443,7 @@ ${errorDetails}`,
4400
4443
  }
4401
4444
  if (effectivePermission === "deny") {
4402
4445
  const result = this.deniedResult(use, decision.reason);
4403
- budget = this.decrementBudget(result, budget);
4446
+ budget = this.budgetForString(result.content, budget);
4404
4447
  return { result, tool, durationMs: Date.now() - start };
4405
4448
  }
4406
4449
  if (effectivePermission === "confirm") {
@@ -4413,7 +4456,7 @@ ${errorDetails}`,
4413
4456
  content: `Tool "${tool.name}" denied by user.`,
4414
4457
  is_error: true
4415
4458
  };
4416
- budget = this.decrementBudget(result, budget);
4459
+ budget = this.budgetForString(result.content, budget);
4417
4460
  return { result, tool, durationMs: Date.now() - start };
4418
4461
  }
4419
4462
  } else {
@@ -4437,7 +4480,8 @@ ${errorDetails}`,
4437
4480
  "tool.has_dangerous_capabilities": toolCapsForAudit.length > 0
4438
4481
  });
4439
4482
  try {
4440
- let result = await this.executeTool(tool, use, ctx, budget);
4483
+ let { block: result, bytes } = await this.executeTool(tool, use, ctx, budget);
4484
+ budget -= bytes;
4441
4485
  if (this.opts.hookRunner?.has("PostToolUse")) {
4442
4486
  const post = await this.opts.hookRunner.postToolUse(
4443
4487
  tool.name,
@@ -4446,12 +4490,13 @@ ${errorDetails}`,
4446
4490
  ctx
4447
4491
  );
4448
4492
  if (post.additionalContext) {
4449
- result = { ...result, content: `${result.content}
4493
+ const appended = `
4450
4494
 
4451
- ${post.additionalContext}` };
4495
+ ${post.additionalContext}`;
4496
+ result = { ...result, content: `${result.content}${appended}` };
4497
+ budget = Math.max(0, budget - Buffer.byteLength(appended, "utf8"));
4452
4498
  }
4453
4499
  }
4454
- budget = this.decrementBudget(result, budget);
4455
4500
  span?.setAttribute("tool.is_error", !!result.is_error);
4456
4501
  span?.setAttribute(
4457
4502
  "tool.output_bytes",
@@ -4468,7 +4513,7 @@ ${post.additionalContext}` };
4468
4513
  content: `Tool "${tool.name}" threw: ${scrubbed}`,
4469
4514
  is_error: true
4470
4515
  };
4471
- budget = this.decrementBudget(result, budget);
4516
+ budget = this.budgetForString(result.content, budget);
4472
4517
  if (err instanceof Error) span?.recordError(err);
4473
4518
  span?.setAttribute("tool.is_error", true);
4474
4519
  return { result, tool, durationMs: Date.now() - start };
@@ -4488,7 +4533,7 @@ ${post.additionalContext}` };
4488
4533
  content: `Tool "${use.name}" execution failed: ${scrubbed}`,
4489
4534
  is_error: true
4490
4535
  };
4491
- budget = this.decrementBudget(result, budget);
4536
+ budget = this.budgetForString(result.content, budget);
4492
4537
  return { result, tool: this.registry.get(use.name), durationMs: 0 };
4493
4538
  }
4494
4539
  };
@@ -4537,14 +4582,20 @@ ${post.additionalContext}` };
4537
4582
  const output = await this.runWithTimeout(tool, use.input, ctx.signal, ctx, use.id);
4538
4583
  const text = this.serializer.serialize(output);
4539
4584
  const scrubbed = this.opts.secretScrubber.scrub(text);
4540
- const { text: capped } = this.serializer.enforceCap(scrubbed, budget);
4585
+ const { text: capped, newBudget } = this.serializer.enforceCap(scrubbed, budget);
4541
4586
  this.opts.renderer?.writeToolResult(tool.name, capped, false);
4542
4587
  return {
4543
- type: "tool_result",
4544
- tool_use_id: use.id,
4545
- name: tool.name,
4546
- content: capped,
4547
- is_error: false
4588
+ block: {
4589
+ type: "tool_result",
4590
+ tool_use_id: use.id,
4591
+ name: tool.name,
4592
+ content: capped,
4593
+ is_error: false
4594
+ },
4595
+ // `budget - newBudget` is the exact byte count enforceCap spent
4596
+ // (capped at `budget` so a truncated output shows as `budget`
4597
+ // consumed, matching the pre-fix `decrementBudget` semantics).
4598
+ bytes: budget - newBudget
4548
4599
  };
4549
4600
  }
4550
4601
  async runWithTimeout(tool, input, parentSignal, ctx, toolUseId) {
@@ -4684,9 +4735,19 @@ ${excerpt}`;
4684
4735
  is_error: true
4685
4736
  };
4686
4737
  }
4687
- decrementBudget(result, budget) {
4688
- const contentBytes = typeof result.content === "string" ? Buffer.byteLength(result.content, "utf8") : Buffer.byteLength(JSON.stringify(result.content), "utf8");
4689
- return Math.max(0, budget - contentBytes);
4738
+ /**
4739
+ * Subtract a string-content result's UTF-8 byte length from the
4740
+ * iteration output budget. Used for synthesized results (unknown tool,
4741
+ * validation error, blocked, threw) where the content is a small
4742
+ * string built in the executor. The success path no longer goes
4743
+ * through here — `executeTool` carries the exact byte count it spent
4744
+ * in its return value, derived from `enforceCap`'s `newBudget`.
4745
+ *
4746
+ * Floors the result at 0 to match the pre-fix `decrementBudget`
4747
+ * semantics (over-budget spends don't underflow the running total).
4748
+ */
4749
+ budgetForString(content, budget) {
4750
+ return Math.max(0, budget - Buffer.byteLength(content, "utf8"));
4690
4751
  }
4691
4752
  /**
4692
4753
  * Compute the suggestedPattern string for a tool+input pair.
@@ -4792,10 +4853,19 @@ var ConversationState = class {
4792
4853
  this.emit({ kind: "message_appended", message });
4793
4854
  }
4794
4855
  replaceMessages(messages) {
4856
+ let hasToolBlock = false;
4795
4857
  for (const m of messages) {
4796
4858
  if (m._estTokens === void 0) {
4797
4859
  m._estTokens = computeMessageTokens(m);
4798
4860
  }
4861
+ if (!hasToolBlock && Array.isArray(m.content)) {
4862
+ for (const b of m.content) {
4863
+ if (b.type === "tool_use" || b.type === "tool_result") {
4864
+ hasToolBlock = true;
4865
+ break;
4866
+ }
4867
+ }
4868
+ }
4799
4869
  }
4800
4870
  const arr = this.ctx.messages;
4801
4871
  if (messages.length < arr.length) {
@@ -4804,9 +4874,7 @@ var ConversationState = class {
4804
4874
  for (let i = 0; i < messages.length; i++) {
4805
4875
  arr[i] = messages[i];
4806
4876
  }
4807
- if (messages.some(
4808
- (m) => Array.isArray(m.content) && m.content.some((b) => b.type === "tool_use" || b.type === "tool_result")
4809
- )) {
4877
+ if (hasToolBlock) {
4810
4878
  this.ctx.toolAdjacencyDirty = true;
4811
4879
  }
4812
4880
  this.emit({ kind: "messages_replaced", messages: [...messages] });
@@ -4885,6 +4953,19 @@ var Context = class {
4885
4953
  * on iterations where no tool content was added (pure text responses).
4886
4954
  */
4887
4955
  toolAdjacencyDirty = false;
4956
+ /**
4957
+ * H1: pre-computed total-request token estimate from the most recent
4958
+ * `estimateRequestTokens()` call in the agent loop's pre-flight step.
4959
+ * The middleware that decides when to compact, the `emitContextPct`
4960
+ * helper that drives the live context-fill bar, and the pre-flight
4961
+ * itself all need this number; previously each one walked the same
4962
+ * messages/system/tools arrays independently. Stashing it here lets
4963
+ * the three call sites share a single compute per iteration.
4964
+ *
4965
+ * The value is the **uncalibrated** total. Callers that want the
4966
+ * calibrated number apply the per-(provider,model) ratio themselves.
4967
+ */
4968
+ lastRequestTokens = void 0;
4888
4969
  constructor(init) {
4889
4970
  this.systemPrompt = init.systemPrompt;
4890
4971
  this.provider = init.provider;
@@ -5921,8 +6002,10 @@ function buildChildEnv(optsOrSessionId) {
5921
6002
  );
5922
6003
  }
5923
6004
  const out = {};
6005
+ const nodeEnvDefaulted = process.env["WRONGSTACK_NODE_ENV_DEFAULTED"] === "1";
5924
6006
  for (const [k, v] of Object.entries(process.env)) {
5925
6007
  if (v === void 0) continue;
6008
+ if (nodeEnvDefaulted && (k === "NODE_ENV" || k === "WRONGSTACK_NODE_ENV_DEFAULTED")) continue;
5926
6009
  if (passthrough) {
5927
6010
  out[k] = v;
5928
6011
  continue;
@@ -6081,6 +6164,9 @@ async function expandGlob(pattern) {
6081
6164
  function completePartialObject(s) {
6082
6165
  if (!s.trim().startsWith("{")) return s;
6083
6166
  if (tryParse(s).ok) return s;
6167
+ return repairTruncated(s);
6168
+ }
6169
+ function repairTruncated(s) {
6084
6170
  const stack = [];
6085
6171
  let inString = false;
6086
6172
  let escaped = false;
@@ -9314,6 +9400,21 @@ var DefaultPermissionPolicy = class {
9314
9400
  promptDelegate;
9315
9401
  /** Pre-compiled wildcard patterns — rebuilt on reload for O(1) lookup. */
9316
9402
  wildcardEntries = [];
9403
+ /**
9404
+ * Evaluate-result cache. Keyed by `tool.name::subject` so repeated calls
9405
+ * with the same tool+input skip namespace matching, subject computation,
9406
+ * pattern matching (matchAny), and YOLO destructive gating.
9407
+ *
9408
+ * Cleared on any state change (reload, trust, deny, yolo toggle) because
9409
+ * the result depends on the full policy state. The write-tool smart-bypass
9410
+ * (step 7 in `evaluate()`) is not cached since `ctx.hasRead()` changes
9411
+ * dynamically within a session.
9412
+ *
9413
+ * LRU eviction is not needed — the cache is cleared on state changes
9414
+ * that are rare (trust file ops, user confirm) and the number of unique
9415
+ * tool+subject pairs per iteration is small (<50).
9416
+ */
9417
+ _evalCache = /* @__PURE__ */ new Map();
9317
9418
  constructor(opts) {
9318
9419
  this.trustFile = opts.trustFile;
9319
9420
  this.yolo = opts.yolo ?? false;
@@ -9332,6 +9433,7 @@ var DefaultPermissionPolicy = class {
9332
9433
  }
9333
9434
  /** Toggle YOLO (auto-approve) mode at runtime. */
9334
9435
  setYolo(enabled) {
9436
+ if (this.yolo !== enabled) this._evalCache.clear();
9335
9437
  this.yolo = enabled;
9336
9438
  }
9337
9439
  /** Check whether YOLO mode is currently active. */
@@ -9340,6 +9442,7 @@ var DefaultPermissionPolicy = class {
9340
9442
  }
9341
9443
  /** Toggle the destructive YOLO override at runtime. */
9342
9444
  setYoloDestructive(enabled) {
9445
+ if (this.yoloDestructive !== enabled) this._evalCache.clear();
9343
9446
  this.yoloDestructive = enabled;
9344
9447
  }
9345
9448
  /** Check whether the destructive YOLO override is active. */
@@ -9348,6 +9451,7 @@ var DefaultPermissionPolicy = class {
9348
9451
  }
9349
9452
  /** Toggle destructive confirmation gate (only meaningful when yolo is active). */
9350
9453
  setConfirmDestructive(enabled) {
9454
+ if (this.confirmDestructive !== enabled) this._evalCache.clear();
9351
9455
  this.confirmDestructive = enabled;
9352
9456
  }
9353
9457
  /** Check whether destructive confirmation gate is active. */
@@ -9368,6 +9472,7 @@ var DefaultPermissionPolicy = class {
9368
9472
  }
9369
9473
  this.sessionDenied.clear();
9370
9474
  this.sessionAllowed.clear();
9475
+ this._evalCache.clear();
9371
9476
  this.loaded = true;
9372
9477
  }
9373
9478
  async evaluate(tool, input, ctx) {
@@ -9375,44 +9480,60 @@ var DefaultPermissionPolicy = class {
9375
9480
  const namespaceEntry = this.findNamespaceEntry(tool.name);
9376
9481
  const entry = this.policy[tool.name] ?? namespaceEntry;
9377
9482
  const subject = this.subjectFor(tool.name, input, tool.subjectKey);
9378
- const subjectKey = `${tool.name}::${subject ?? tool.name}`;
9379
- if (this.sessionDenied.has(subjectKey)) {
9380
- return { permission: "deny", source: "deny", reason: "session soft deny (user pressed no)" };
9483
+ const cacheKey = `${tool.name}::${subject ?? tool.name}`;
9484
+ if (tool.name !== "write") {
9485
+ const cached = this._evalCache.get(cacheKey);
9486
+ if (cached !== void 0) return cached;
9487
+ }
9488
+ if (this.sessionDenied.has(cacheKey)) {
9489
+ const decision = { permission: "deny", source: "deny", reason: "session soft deny (user pressed no)" };
9490
+ this._evalCache.set(cacheKey, decision);
9491
+ return decision;
9381
9492
  }
9382
- if (this.sessionAllowed.has(subjectKey)) {
9383
- return {
9493
+ if (this.sessionAllowed.has(cacheKey)) {
9494
+ const decision = {
9384
9495
  permission: "auto",
9385
9496
  source: "trust",
9386
9497
  reason: "session soft allow (user pressed yes)"
9387
9498
  };
9499
+ this._evalCache.set(cacheKey, decision);
9500
+ return decision;
9388
9501
  }
9389
9502
  if (entry?.deny && subject && matchAny(entry.deny, subject)) {
9390
- return { permission: "deny", source: "deny", reason: "matched deny pattern" };
9503
+ const decision = { permission: "deny", source: "deny", reason: "matched deny pattern" };
9504
+ this._evalCache.set(cacheKey, decision);
9505
+ return decision;
9391
9506
  }
9392
9507
  if (tool.permission === "deny") {
9393
- return { permission: "deny", source: "default", reason: "tool default deny" };
9508
+ const decision = { permission: "deny", source: "default", reason: "tool default deny" };
9509
+ this._evalCache.set(cacheKey, decision);
9510
+ return decision;
9394
9511
  }
9395
9512
  if (entry?.allow && subject && matchAny(entry.allow, subject)) {
9396
- return { permission: "auto", source: "trust", reason: "matched allow pattern" };
9513
+ const decision = { permission: "auto", source: "trust", reason: "matched allow pattern" };
9514
+ this._evalCache.set(cacheKey, decision);
9515
+ return decision;
9397
9516
  }
9398
9517
  if (entry?.auto) {
9399
- return { permission: "auto", source: "trust" };
9518
+ const decision = { permission: "auto", source: "trust" };
9519
+ this._evalCache.set(cacheKey, decision);
9520
+ return decision;
9400
9521
  }
9401
9522
  if (this.yolo) {
9402
9523
  if (this.confirmDestructive) {
9403
9524
  const destructive = this.isDestructiveYoloCall(tool, input, ctx);
9404
9525
  if (destructive) {
9405
9526
  if (this.promptDelegate) {
9406
- const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
9407
- if (decision === "always") {
9527
+ const decision2 = await this.promptDelegate(tool, input, subject ?? tool.name);
9528
+ if (decision2 === "always") {
9408
9529
  await this.trust({ tool: tool.name, pattern: subject ?? tool.name });
9409
9530
  return { permission: "auto", source: "user", reason: "destructive yolo always-allowed" };
9410
9531
  }
9411
- if (decision === "deny") {
9532
+ if (decision2 === "deny") {
9412
9533
  await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
9413
9534
  return { permission: "deny", source: "user", reason: "user denied destructive yolo" };
9414
9535
  }
9415
- return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
9536
+ return { permission: decision2 === "yes" ? "auto" : "deny", source: "user" };
9416
9537
  }
9417
9538
  return {
9418
9539
  permission: "confirm",
@@ -9422,7 +9543,9 @@ var DefaultPermissionPolicy = class {
9422
9543
  };
9423
9544
  }
9424
9545
  }
9425
- return { permission: "auto", source: "yolo" };
9546
+ const decision = { permission: "auto", source: "yolo" };
9547
+ this._evalCache.set(cacheKey, decision);
9548
+ return decision;
9426
9549
  }
9427
9550
  if (tool.name === "write" && subject) {
9428
9551
  if (ctx.hasRead(subject)) {
@@ -9434,7 +9557,9 @@ var DefaultPermissionPolicy = class {
9434
9557
  }
9435
9558
  }
9436
9559
  if (tool.permission === "auto" && !tool.mutating) {
9437
- return { permission: "auto", source: "default" };
9560
+ const decision = { permission: "auto", source: "default" };
9561
+ this._evalCache.set(cacheKey, decision);
9562
+ return decision;
9438
9563
  }
9439
9564
  if (this.promptDelegate) {
9440
9565
  const decision = await this.promptDelegate(tool, input, subject ?? tool.name);
@@ -9467,6 +9592,7 @@ var DefaultPermissionPolicy = class {
9467
9592
  const entry = this.policy[rule.tool] ?? {};
9468
9593
  entry.allow = Array.from(/* @__PURE__ */ new Set([...entry.allow ?? [], rule.pattern]));
9469
9594
  this.policy[rule.tool] = entry;
9595
+ this._evalCache.clear();
9470
9596
  try {
9471
9597
  await atomicWrite(this.trustFile, JSON.stringify(this.policy, null, 2));
9472
9598
  } catch (err) {
@@ -9484,6 +9610,7 @@ var DefaultPermissionPolicy = class {
9484
9610
  const entry = this.policy[rule.tool] ?? {};
9485
9611
  entry.deny = Array.from(/* @__PURE__ */ new Set([...entry.deny ?? [], rule.pattern]));
9486
9612
  this.policy[rule.tool] = entry;
9613
+ this._evalCache.clear();
9487
9614
  try {
9488
9615
  await atomicWrite(this.trustFile, JSON.stringify(this.policy, null, 2));
9489
9616
  } catch (err) {
@@ -9498,10 +9625,12 @@ var DefaultPermissionPolicy = class {
9498
9625
  /** Block this tool+pattern for the rest of this session (no trust file). */
9499
9626
  denyOnce(rule) {
9500
9627
  this.sessionDenied.set(`${rule.tool}::${rule.pattern}`, true);
9628
+ this._evalCache.clear();
9501
9629
  }
9502
9630
  /** Auto-approve this tool+pattern for the rest of this session (no trust file). */
9503
9631
  allowOnce(rule) {
9504
9632
  this.sessionAllowed.set(`${rule.tool}::${rule.pattern}`, true);
9633
+ this._evalCache.clear();
9505
9634
  }
9506
9635
  subjectFor(toolName, input, subjectKey) {
9507
9636
  if (!input || typeof input !== "object") return void 0;
@@ -9537,15 +9666,23 @@ var DefaultPermissionPolicy = class {
9537
9666
  }
9538
9667
  };
9539
9668
  var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
9669
+ allowedCapabilities;
9670
+ constructor(allowedCapabilities) {
9671
+ this.allowedCapabilities = allowedCapabilities ?? [
9672
+ ToolCapabilities.FS_READ,
9673
+ ToolCapabilities.NET_OUTBOUND
9674
+ ];
9675
+ }
9540
9676
  static isMcpTool(name) {
9541
9677
  return name.startsWith("mcp__");
9542
9678
  }
9543
9679
  async evaluate(tool) {
9544
- const hasDangerousCap = hasDangerousCapabilityForSubagents(tool);
9680
+ const caps = tool.capabilities ?? [];
9681
+ const hasAllowedCap = caps.some((c) => this.allowedCapabilities.includes(c));
9545
9682
  const isMcp = _AutoApprovePermissionPolicy.isMcpTool(tool.name);
9546
- const blocked = tool.permission === "deny" || hasDangerousCap || isMcp;
9683
+ const blocked = tool.permission === "deny" || isMcp || !hasAllowedCap;
9547
9684
  if (blocked) {
9548
- const reason = hasDangerousCap ? `tool declares dangerous capability (${tool.capabilities?.join(", ")}) \u2014 not auto-approved for subagents` : isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : "tool default deny";
9685
+ const reason = isMcp ? `MCP tool ${tool.name} is not auto-approved for subagents \u2014 ask the leader to allow it explicitly` : tool.permission === "deny" ? "tool default deny" : `tool lacks allowed capability (has: ${caps.join(", ") || "none"}, allowed: ${this.allowedCapabilities.join(", ")})`;
9549
9686
  return {
9550
9687
  permission: "deny",
9551
9688
  source: "subagent_guard",
@@ -9565,6 +9702,35 @@ var AutoApprovePermissionPolicy = class _AutoApprovePermissionPolicy {
9565
9702
  async reload() {
9566
9703
  }
9567
9704
  };
9705
+ function stripFrontmatter(raw) {
9706
+ if (!raw.startsWith("---")) return raw;
9707
+ const end = raw.indexOf("\n---", 4);
9708
+ if (end === -1) return raw;
9709
+ let body = raw.slice(end + 4);
9710
+ if (body.startsWith("\n")) body = body.slice(1);
9711
+ return body;
9712
+ }
9713
+ function compactSkillBody(body) {
9714
+ const sections = [];
9715
+ const overviewMatch = body.match(/##\s*Overview\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
9716
+ const overview = overviewMatch?.[1];
9717
+ if (overview?.trim()) {
9718
+ sections.push(overview.trim().slice(0, 200));
9719
+ }
9720
+ const rulesMatch = body.match(/##\s*Rules\s*\n([\s\S]*?)(?=\n##|\n$|$)/i);
9721
+ const rules = rulesMatch?.[1];
9722
+ if (rules?.trim()) {
9723
+ const trimmed = rules.trim().slice(0, 350);
9724
+ const ruleLines = trimmed.split("\n").filter((l) => /^\s*[-*]\s/.test(l) || /^\s*\d+[.)]\s/.test(l)).slice(0, 6).join("\n");
9725
+ if (ruleLines) sections.push(ruleLines);
9726
+ }
9727
+ if (sections.length === 0) {
9728
+ const first = body.trim().slice(0, 200);
9729
+ if (first) sections.push(first);
9730
+ }
9731
+ const result = sections.join("\n\n");
9732
+ return result.length > 450 ? result.slice(0, 447) + "\u2026" : result;
9733
+ }
9568
9734
  var DefaultSkillLoader = class {
9569
9735
  dirs;
9570
9736
  cache;
@@ -9646,6 +9812,24 @@ var DefaultSkillLoader = class {
9646
9812
  if (!m) throw new Error(`Skill "${name}" not found`);
9647
9813
  return fsp3.readFile(m.path, "utf8");
9648
9814
  }
9815
+ async readSaveBody(name) {
9816
+ const m = await this.find(name);
9817
+ if (!m) throw new Error(`Skill "${name}" not found`);
9818
+ const savePath = path7.join(path7.dirname(m.path), "SKILL.save.md");
9819
+ try {
9820
+ return await fsp3.readFile(savePath, "utf8");
9821
+ } catch {
9822
+ const full = await fsp3.readFile(m.path, "utf8");
9823
+ const body = stripFrontmatter(full);
9824
+ const compact = compactSkillBody(body);
9825
+ if (compact) {
9826
+ return `## Overview
9827
+
9828
+ ${compact}`;
9829
+ }
9830
+ return body.trim().slice(0, 300);
9831
+ }
9832
+ }
9649
9833
  };
9650
9834
  function parseFrontmatter(raw) {
9651
9835
  if (!raw.startsWith("---")) return {};
@@ -10726,6 +10910,13 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
10726
10910
  tokens = this._estimator(ctx);
10727
10911
  } else if (msgCount === this._cachedMsgCount && toolCount === this._cachedToolCount && this._cachedTokens >= 0) {
10728
10912
  tokens = this._cachedTokens;
10913
+ } else if (this.tryStashedTokens(ctx, msgCount, toolCount) !== null) {
10914
+ const stashed = this.tryStashedTokens(ctx, msgCount, toolCount);
10915
+ const cal = getCalibrationState(`${ctx.provider?.id ?? "unknown"}/${ctx.model}`);
10916
+ tokens = cal.calibrated ? Math.round(stashed * Math.min(1.5, Math.max(0.5, cal.ratio))) : stashed;
10917
+ this._cachedTokens = tokens;
10918
+ this._cachedMsgCount = msgCount;
10919
+ this._cachedToolCount = toolCount;
10729
10920
  } else {
10730
10921
  tokens = estimateRequestTokensCalibrated(
10731
10922
  ctx.messages,
@@ -10758,6 +10949,25 @@ var AutoCompactionMiddleware = class _AutoCompactionMiddleware {
10758
10949
  return next(ctx);
10759
10950
  };
10760
10951
  }
10952
+ /**
10953
+ * H1: try to read a pre-computed token total from `ctx.lastRequestTokens`
10954
+ * (set by the agent loop's pre-flight or its restash in emitContextPct).
10955
+ * Returns the uncalibrated total when the stash is valid for the current
10956
+ * context shape (positive number, and the message count it was computed
10957
+ * at matches the current one — otherwise tool results have been appended
10958
+ * since and the value is stale). Returns null when missing or stale so
10959
+ * the caller falls back to a fresh walk.
10960
+ */
10961
+ tryStashedTokens(ctx, msgCount, toolCount) {
10962
+ const stashed = ctx.lastRequestTokens;
10963
+ if (typeof stashed !== "number" || stashed <= 0) return null;
10964
+ const stashedAt = ctx.meta?.["lastRequestTokensAt"];
10965
+ if (typeof stashedAt !== "object" || stashedAt === null) return null;
10966
+ const meta = stashedAt;
10967
+ if (meta.msgCount !== msgCount) return null;
10968
+ if (typeof meta.toolCount === "number" && meta.toolCount !== toolCount) return null;
10969
+ return stashed;
10970
+ }
10761
10971
  /**
10762
10972
  * Returns true when the previous compaction at the same or higher pressure
10763
10973
  * level reduced nothing and context has not grown materially since. Prevents
@@ -18665,6 +18875,17 @@ var Director = class _Director {
18665
18875
  * default cap.
18666
18876
  */
18667
18877
  taskCompletedListener = null;
18878
+ /**
18879
+ * Unsub handles for the two `FleetBus.filter()` calls installed in the
18880
+ * constructor for timeout-heartbeat tracking. Without capturing these
18881
+ * and calling them in `shutdown()`, repeated Director construction
18882
+ * (tests, hot reloads, `--director` restarts) accumulates 2 dangling
18883
+ * listeners per Director on the FleetBus, slowly drifting the
18884
+ * EventEmitter past its default cap. Mirrors the rationale on
18885
+ * `taskCompletedListener` above.
18886
+ */
18887
+ toolExecFilter = null;
18888
+ budgetFilter = null;
18668
18889
  /** Optional LLM classifier for smart dispatch. Passed from options. */
18669
18890
  dispatchClassifier;
18670
18891
  /** Leader agent's current context pressure (full request tokens). */
@@ -18802,10 +19023,10 @@ var Director = class _Director {
18802
19023
  const extendCounts = /* @__PURE__ */ new Map();
18803
19024
  const progressBySubagent = /* @__PURE__ */ new Map();
18804
19025
  const lastTimeoutProgress = /* @__PURE__ */ new Map();
18805
- this.fleet.filter("tool.executed", (e) => {
19026
+ this.toolExecFilter = this.fleet.filter("tool.executed", (e) => {
18806
19027
  progressBySubagent.set(e.subagentId, (progressBySubagent.get(e.subagentId) ?? 0) + 1);
18807
19028
  });
18808
- this.fleet.filter("budget.threshold_reached", (e) => {
19029
+ this.budgetFilter = this.fleet.filter("budget.threshold_reached", (e) => {
18809
19030
  const payload = e.payload;
18810
19031
  if (e.subagentId.startsWith("bug-hunter-") || e.subagentId.startsWith("refactor-planner-") || e.subagentId.startsWith("critic-")) {
18811
19032
  return;
@@ -19336,6 +19557,14 @@ var Director = class _Director {
19336
19557
  this.coordinator.off("task.completed", this.taskCompletedListener);
19337
19558
  this.taskCompletedListener = null;
19338
19559
  }
19560
+ if (this.toolExecFilter) {
19561
+ this.toolExecFilter();
19562
+ this.toolExecFilter = null;
19563
+ }
19564
+ if (this.budgetFilter) {
19565
+ this.budgetFilter();
19566
+ this.budgetFilter = null;
19567
+ }
19339
19568
  await this.coordinator.stopAll();
19340
19569
  for (const b of this.subagentBridges.values()) {
19341
19570
  await b.stop().catch((err) => this.logShutdownError("subagent_bridge_stop", err));
@@ -28714,15 +28943,15 @@ function createMcpControlTool(opts) {
28714
28943
  properties: {
28715
28944
  action: {
28716
28945
  type: "string",
28717
- enum: ["list", "search", "enable", "disable", "restart"],
28718
- description: "The management action to perform."
28946
+ enum: ["list", "search", "enable", "disable", "restart", "activate", "deactivate"],
28947
+ description: "The management action to perform. activate/deactivate toggle tool registration ephemerally without disconnecting."
28719
28948
  },
28720
28949
  /** Filter for `search`. Matches server name or description case-insensitively. */
28721
28950
  query: {
28722
28951
  type: "string",
28723
28952
  description: "Search term for `search` action. Matches server name or description."
28724
28953
  },
28725
- /** Target server name for `enable`, `disable`, `restart`. */
28954
+ /** Target server name for `enable`, `disable`, `restart`, `activate`, `deactivate`. */
28726
28955
  server: {
28727
28956
  type: "string",
28728
28957
  description: 'Server name (e.g. "github", "filesystem", "brave-search").'
@@ -28732,7 +28961,7 @@ function createMcpControlTool(opts) {
28732
28961
  };
28733
28962
  return {
28734
28963
  name: "mcp_control",
28735
- description: "Manage MCP server lifecycle: list available servers, search by name or capability, enable or disable servers at runtime, restart running servers.",
28964
+ description: "Manage MCP server lifecycle: list available servers, search by name or capability, enable or disable servers at runtime, restart running servers. Use activate/deactivate to ephemerally toggle tool registration without disconnecting \u2014 ideal for token-saving mode where MCP tools are lazy-loaded on demand.",
28736
28965
  category: "mcp",
28737
28966
  permission: "auto",
28738
28967
  mutating: true,
@@ -28757,8 +28986,12 @@ async function mcpControlDispatch(input, deps) {
28757
28986
  return server ? runDisable(server, deps) : "`server` is required for disable.";
28758
28987
  case "restart":
28759
28988
  return server ? runRestart(server, deps) : "`server` is required for restart.";
28989
+ case "activate":
28990
+ return server ? runActivate(server, deps) : "`server` is required for activate.";
28991
+ case "deactivate":
28992
+ return server ? runDeactivate(server, deps) : "`server` is required for deactivate.";
28760
28993
  default:
28761
- return `Unknown action "${action}". Use one of: list, search, enable, disable, restart.`;
28994
+ return `Unknown action "${action}". Use one of: list, search, enable, disable, restart, activate, deactivate.`;
28762
28995
  }
28763
28996
  }
28764
28997
  function renderList(deps) {
@@ -28883,6 +29116,36 @@ async function runRestart(name, deps) {
28883
29116
  return `${red("\u2717 Restart failed")} for "${name}": ${err instanceof Error ? err.message : String(err)}`;
28884
29117
  }
28885
29118
  }
29119
+ async function runActivate(name, deps) {
29120
+ if (!name) return "`server` is required for activate.";
29121
+ if (!deps.registry.activateServer) {
29122
+ return `Registry does not support ephemeral activation. Use \`enable\` to start "${name}" instead.`;
29123
+ }
29124
+ const live = deps.registry.describe().find((s) => s.name === name);
29125
+ if (!live) {
29126
+ return `Server "${name}" is not registered. Use \`mcp_control({ action: "enable", server: "${name}" })\` first.`;
29127
+ }
29128
+ if (live.state !== "connected") {
29129
+ return `Server "${name}" is not connected (state: ${live.state}). Use \`enable\` to start it first.`;
29130
+ }
29131
+ if (deps.registry.isActivated?.(name)) {
29132
+ return `${green("\u25CF")} Server "${name}" tools are already active. Use \`deactivate\` to hide them.`;
29133
+ }
29134
+ deps.registry.activateServer(name);
29135
+ const updated = deps.registry.describe().find((s) => s.name === name);
29136
+ return `${green("\u2713 Activated")} "${name}" \u2014 ${updated?.toolCount ?? 0} tool(s) now registered. Use \`mcp_control({ action: "deactivate", server: "${name}" })\` to hide them when done.`;
29137
+ }
29138
+ async function runDeactivate(name, deps) {
29139
+ if (!name) return "`server` is required for deactivate.";
29140
+ if (!deps.registry.deactivateServer) {
29141
+ return `Registry does not support ephemeral deactivation. Use \`disable\` to stop "${name}" instead.`;
29142
+ }
29143
+ if (!deps.registry.isActivated?.(name)) {
29144
+ return `Server "${name}" tools are not currently active.`;
29145
+ }
29146
+ const count = deps.registry.deactivateServer(name);
29147
+ return `${yellow("\u25CB Deactivated")} "${name}" \u2014 ${count} tool(s) unregistered. Server stays connected.`;
29148
+ }
28886
29149
  async function readConfig(p) {
28887
29150
  try {
28888
29151
  return JSON.parse(await fsp3.readFile(p, "utf8"));
@@ -28928,6 +29191,70 @@ function badge(state) {
28928
29191
  }
28929
29192
  }
28930
29193
 
29194
+ // src/tools/mcp-use.ts
29195
+ function createMcpUseTool(opts) {
29196
+ const { registry, toolRegistry } = opts;
29197
+ const inputSchema = {
29198
+ type: "object",
29199
+ properties: {
29200
+ server: {
29201
+ type: "string",
29202
+ description: 'MCP server name (e.g. "github", "filesystem", "brave-search"). Use mcp_control list or search first to discover available servers.'
29203
+ },
29204
+ tool: {
29205
+ type: "string",
29206
+ description: "Tool name on the MCP server to call (without the mcp__server__ prefix \u2014 just the bare tool name)."
29207
+ },
29208
+ input: {
29209
+ type: "object",
29210
+ description: "JSON input to pass to the tool. Use the tool's own input schema \u2014 check with mcp_control describe or the server's documentation.",
29211
+ properties: {},
29212
+ additionalProperties: true
29213
+ }
29214
+ },
29215
+ required: ["server", "tool", "input"]
29216
+ };
29217
+ return {
29218
+ name: "mcp_use",
29219
+ description: "Call an MCP tool on a lazy-loaded server. Activates the server temporarily, calls the tool, returns the result, and deactivates. Use this instead of the manual activate\u2192use\u2192deactivate cycle. First call mcp_control list/search to find the right server and tool name.",
29220
+ category: "mcp",
29221
+ permission: "auto",
29222
+ mutating: true,
29223
+ riskTier: "standard",
29224
+ inputSchema,
29225
+ async execute(raw) {
29226
+ const input = raw;
29227
+ const { server: serverName, tool: toolName, input: toolInput } = input;
29228
+ const servers = registry.describe();
29229
+ const serverInfo = servers.find((s) => s.name === serverName);
29230
+ if (!serverInfo) {
29231
+ return `Server "${serverName}" not found. Available: ${servers.map((s) => s.name).join(", ") || "none"}.`;
29232
+ }
29233
+ if (serverInfo.state !== "connected") {
29234
+ return `Server "${serverName}" is not connected (state: ${serverInfo.state}). Use \`mcp_control({ action: "enable", server: "${serverName}" })\` first.`;
29235
+ }
29236
+ if (registry.activateServer) {
29237
+ registry.activateServer(serverName);
29238
+ }
29239
+ try {
29240
+ const qualifiedName = `mcp__${serverName}__${toolName}`;
29241
+ const mcpTool = toolRegistry.get(qualifiedName);
29242
+ if (!mcpTool) {
29243
+ const allTools = toolRegistry.list().filter((t2) => t2.name.startsWith(`mcp__${serverName}__`)).map((t2) => t2.name.replace(`mcp__${serverName}__`, ""));
29244
+ const hint = allTools.length > 0 ? `Available tools on "${serverName}": ${allTools.join(", ")}.` : `No tools found on "${serverName}". The server may not have published any tools.`;
29245
+ return `Tool "${toolName}" not found on server "${serverName}". ${hint}`;
29246
+ }
29247
+ const result = await mcpTool.execute(toolInput ?? {}, {}, {});
29248
+ return result;
29249
+ } finally {
29250
+ if (registry.deactivateServer) {
29251
+ registry.deactivateServer(serverName);
29252
+ }
29253
+ }
29254
+ }
29255
+ };
29256
+ }
29257
+
28931
29258
  // src/extension/registry.ts
28932
29259
  var ExtensionRegistry = class {
28933
29260
  extensions = [];
@@ -29154,7 +29481,7 @@ function createAgentToolHandler(a) {
29154
29481
  a.ctx,
29155
29482
  a.perIterationOutputCapBytes
29156
29483
  );
29157
- return { result, durationMs: Date.now() - start };
29484
+ return { result: result.block, durationMs: Date.now() - start };
29158
29485
  } catch (err) {
29159
29486
  const msg = err instanceof Error ? err.message : String(err);
29160
29487
  return {
@@ -29296,9 +29623,10 @@ function createAgentToolHandler(a) {
29296
29623
  // src/core/continue-to-next-iteration.ts
29297
29624
  function parseContinueDirective(text) {
29298
29625
  const LINE_MARKERS = /^\s*\[(continue|next step|proceed|done)\]\s*$/gim;
29626
+ const tail = text.length <= DIRECTIVE_SCAN_WINDOW ? text : text.slice(text.length - DIRECTIVE_SCAN_WINDOW);
29299
29627
  let match;
29300
29628
  let lastDirective = "none";
29301
- while ((match = LINE_MARKERS.exec(text)) !== null) {
29629
+ while ((match = LINE_MARKERS.exec(tail)) !== null) {
29302
29630
  const value = (match[1] ?? "").toLowerCase();
29303
29631
  if (value === "continue" || value === "next step" || value === "proceed") {
29304
29632
  lastDirective = "continue";
@@ -29308,6 +29636,7 @@ function parseContinueDirective(text) {
29308
29636
  }
29309
29637
  return lastDirective;
29310
29638
  }
29639
+ var DIRECTIVE_SCAN_WINDOW = 2048;
29311
29640
  var META_KEY = "_autonomousContinue";
29312
29641
  function setAutonomousContinue(ctx) {
29313
29642
  ctx.meta[META_KEY] = true;
@@ -29390,21 +29719,22 @@ function createAgentResponseHandler(a) {
29390
29719
  });
29391
29720
  await a.ctx.session.flush();
29392
29721
  if (a.ctx.signal.aborted) {
29393
- let finalText2 = "";
29722
+ const parts2 = [];
29394
29723
  for (const block of res.content) {
29395
- if (isTextBlock(block)) finalText2 += block.text;
29724
+ if (isTextBlock(block)) parts2.push(block.text);
29396
29725
  }
29397
- return { finalText: finalText2, aborted: true, done: false };
29726
+ return { finalText: parts2.join(""), aborted: true, done: false };
29398
29727
  }
29399
- let finalText = "";
29728
+ const parts = [];
29400
29729
  const streamed = a.ctx.provider.capabilities.streaming;
29401
29730
  for (const block of res.content) {
29402
29731
  if (isTextBlock(block)) {
29403
29732
  const rendered = await a.pipelines.assistantOutput.run(block);
29404
- finalText += rendered.text;
29733
+ parts.push(rendered.text);
29405
29734
  if (!streamed) a.renderer?.write(rendered);
29406
29735
  }
29407
29736
  }
29737
+ const finalText = parts.join("");
29408
29738
  let directive = "none";
29409
29739
  if (finalText) {
29410
29740
  directive = parseContinueDirective(finalText);
@@ -29514,7 +29844,9 @@ function normalizeRecipient(to) {
29514
29844
 
29515
29845
  // src/coordination/global-mailbox.ts
29516
29846
  var MAILBOX_FILE = "_mailbox.jsonl";
29847
+ var CLIENT_REGISTRY_FILE = "_mailbox.clients.json";
29517
29848
  var AGENT_STALE_MS = 6e4;
29849
+ var CLIENT_STALE_MS = 6e4;
29518
29850
  var HEARTBEAT_THROTTLE_MS = 5e3;
29519
29851
  var REGISTRY_CACHE_TTL_MS = 2e3;
29520
29852
  var LINE_SEPARATOR = "\n";
@@ -29526,6 +29858,8 @@ var GlobalMailbox = class {
29526
29858
  messagePath;
29527
29859
  /** Path to the JSON agent registry file. */
29528
29860
  registryPath;
29861
+ /** Path to the JSON client registry file. */
29862
+ clientRegistryPath;
29529
29863
  /** Optional event bus for emitting agent registration/heartbeat events. */
29530
29864
  _events;
29531
29865
  /**
@@ -29537,8 +29871,17 @@ var GlobalMailbox = class {
29537
29871
  _registryCache = null;
29538
29872
  /** When the registry cache was last refreshed from disk (epoch ms). */
29539
29873
  _registryCacheAt = 0;
29874
+ /**
29875
+ * Local cache of the client registry to avoid re-reading on every call.
29876
+ * Same reasoning as agent registry cache.
29877
+ */
29878
+ _clientRegistryCache = null;
29879
+ /** When the client registry cache was last refreshed from disk (epoch ms). */
29880
+ _clientRegistryCacheAt = 0;
29540
29881
  /** Last time each local agent sent a heartbeat (throttle). */
29541
29882
  _lastHeartbeat = /* @__PURE__ */ new Map();
29883
+ /** Last time each local client sent a heartbeat (throttle). */
29884
+ _lastClientHeartbeat = /* @__PURE__ */ new Map();
29542
29885
  /**
29543
29886
  * @param projectDir — `~/.wrongstack/projects/<slug>/`
29544
29887
  * @param events — optional EventBus for real-time TUI/WebUI notifications
@@ -29546,6 +29889,7 @@ var GlobalMailbox = class {
29546
29889
  constructor(projectDir, events) {
29547
29890
  this.messagePath = path7.join(projectDir, MAILBOX_FILE);
29548
29891
  this.registryPath = path7.join(projectDir, "_mailbox.registry.json");
29892
+ this.clientRegistryPath = path7.join(projectDir, CLIENT_REGISTRY_FILE);
29549
29893
  this._events = events;
29550
29894
  }
29551
29895
  // ── Messages ────────────────────────────────────────────────────────────
@@ -29727,9 +30071,74 @@ var GlobalMailbox = class {
29727
30071
  const all = await this.getAgentStatuses();
29728
30072
  return all.filter((a) => a.online);
29729
30073
  }
30074
+ // ── Client registry ─────────────────────────────────────────────────────
30075
+ async registerClient(input) {
30076
+ await this._ensureClientRegistry();
30077
+ const now = (/* @__PURE__ */ new Date()).toISOString();
30078
+ const client = {
30079
+ clientId: input.clientId,
30080
+ sessionId: input.sessionId,
30081
+ name: input.name,
30082
+ source: input.source,
30083
+ registeredAt: now,
30084
+ lastSeenAt: now,
30085
+ pid: input.pid
30086
+ };
30087
+ await withFileLock(this.clientRegistryPath, async () => {
30088
+ const registry = await this._readClientRegistry({ fresh: true });
30089
+ this._pruneStaleClientsInPlace(registry);
30090
+ registry.set(input.clientId, client);
30091
+ this._clientRegistryCache = registry;
30092
+ this._clientRegistryCacheAt = Date.now();
30093
+ await this._writeClientRegistry(registry);
30094
+ });
30095
+ this._events?.emitCustom("mailbox.client_registered", {
30096
+ clientId: input.clientId,
30097
+ sessionId: input.sessionId,
30098
+ name: input.name,
30099
+ source: input.source
30100
+ });
30101
+ }
30102
+ async clientHeartbeat(input) {
30103
+ const last = this._lastClientHeartbeat.get(input.clientId) ?? 0;
30104
+ const now = Date.now();
30105
+ if (now - last < HEARTBEAT_THROTTLE_MS) return;
30106
+ this._lastClientHeartbeat.set(input.clientId, now);
30107
+ await this._ensureClientRegistry();
30108
+ await withFileLock(this.clientRegistryPath, async () => {
30109
+ const registry = await this._readClientRegistry({ fresh: true });
30110
+ this._pruneStaleClientsInPlace(registry);
30111
+ const client = registry.get(input.clientId);
30112
+ if (client) {
30113
+ client.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
30114
+ }
30115
+ this._clientRegistryCache = registry;
30116
+ this._clientRegistryCacheAt = Date.now();
30117
+ await this._writeClientRegistry(registry);
30118
+ });
30119
+ this._events?.emitCustom("mailbox.client_heartbeat", {
30120
+ clientId: input.clientId
30121
+ });
30122
+ }
30123
+ async getClientStatuses() {
30124
+ await this._ensureClientRegistry();
30125
+ const registry = await this._readClientRegistry();
30126
+ this._pruneStaleClientsInPlace(registry);
30127
+ const now = Date.now();
30128
+ return Array.from(registry.values()).map((c) => ({
30129
+ clientId: c.clientId,
30130
+ name: c.name,
30131
+ source: c.source,
30132
+ sessionId: c.sessionId,
30133
+ lastSeenAt: c.lastSeenAt,
30134
+ online: now - new Date(c.lastSeenAt).getTime() < CLIENT_STALE_MS,
30135
+ pid: c.pid
30136
+ })).sort((a, b) => b.lastSeenAt.localeCompare(a.lastSeenAt));
30137
+ }
29730
30138
  // ── Lifecycle ───────────────────────────────────────────────────────────
29731
30139
  async close() {
29732
30140
  this._registryCache = null;
30141
+ this._clientRegistryCache = null;
29733
30142
  }
29734
30143
  async clearAll() {
29735
30144
  await withFileLock(this.messagePath, async () => {
@@ -29808,6 +30217,51 @@ var GlobalMailbox = class {
29808
30217
  await fsp3.writeFile(tmp, JSON.stringify(obj, null, 2), "utf8");
29809
30218
  await fsp3.rename(tmp, this.registryPath);
29810
30219
  }
30220
+ // ── Client registry internals ───────────────────────────────────────────
30221
+ async _ensureClientRegistry() {
30222
+ await fsp3.mkdir(path7.dirname(this.clientRegistryPath), { recursive: true });
30223
+ }
30224
+ async _readClientRegistry(opts) {
30225
+ if (!opts?.fresh && this._clientRegistryCache && Date.now() - this._clientRegistryCacheAt < REGISTRY_CACHE_TTL_MS) {
30226
+ return new Map(this._clientRegistryCache);
30227
+ }
30228
+ try {
30229
+ const raw = await fsp3.readFile(this.clientRegistryPath, "utf8");
30230
+ const data = JSON.parse(raw);
30231
+ const map = /* @__PURE__ */ new Map();
30232
+ for (const [id, client] of Object.entries(data)) {
30233
+ map.set(id, client);
30234
+ }
30235
+ this._clientRegistryCache = map;
30236
+ this._clientRegistryCacheAt = Date.now();
30237
+ return new Map(map);
30238
+ } catch (err) {
30239
+ if (err.code === "ENOENT") {
30240
+ const empty = /* @__PURE__ */ new Map();
30241
+ this._clientRegistryCache = empty;
30242
+ this._clientRegistryCacheAt = Date.now();
30243
+ return empty;
30244
+ }
30245
+ throw err;
30246
+ }
30247
+ }
30248
+ _pruneStaleClientsInPlace(registry) {
30249
+ const cutoff = Date.now() - CLIENT_STALE_MS;
30250
+ for (const client of registry.values()) {
30251
+ if (new Date(client.lastSeenAt).getTime() < cutoff) {
30252
+ client.lastSeenAt = new Date(cutoff).toISOString();
30253
+ }
30254
+ }
30255
+ }
30256
+ async _writeClientRegistry(registry) {
30257
+ const obj = {};
30258
+ for (const [id, client] of registry) {
30259
+ obj[id] = client;
30260
+ }
30261
+ const tmp = `${this.clientRegistryPath}.${randomUUID().slice(0, 8)}.tmp`;
30262
+ await fsp3.writeFile(tmp, JSON.stringify(obj, null, 2), "utf8");
30263
+ await fsp3.rename(tmp, this.clientRegistryPath);
30264
+ }
29811
30265
  };
29812
30266
  function defaultResolveProjectDir(ctx) {
29813
30267
  return resolveProjectDir(ctx.projectRoot, wstackGlobalRoot());
@@ -30298,7 +30752,16 @@ function signalAbortReason(signal) {
30298
30752
  function createAgentLoopHandler(a, handlers) {
30299
30753
  const checkMailbox = attachMailboxChecker(a);
30300
30754
  async function compactContextIfNeeded() {
30755
+ const msgCount = a.ctx.messages.length;
30756
+ if (_lastCompactionMsgCount === msgCount && _lastCompactionWasNoop && _maxContext > 0) {
30757
+ return;
30758
+ }
30301
30759
  await a.pipelines.contextWindow.run(a.ctx);
30760
+ _lastCompactionMsgCount = msgCount;
30761
+ const stashed = a.ctx.lastRequestTokens;
30762
+ const tokens = typeof stashed === "number" && stashed > 0 ? stashed : 0;
30763
+ const load = _maxContext > 0 ? tokens / _maxContext : 0;
30764
+ _lastCompactionWasNoop = tokens > 0 && load < 0.5;
30302
30765
  }
30303
30766
  const calibrationKey = (model = a.ctx.model) => `${a.ctx.provider?.id ?? "unknown"}/${model}`;
30304
30767
  function emitContextPct() {
@@ -30309,22 +30772,42 @@ function createAgentLoopHandler(a, handlers) {
30309
30772
  }
30310
30773
  _lastEmittedMsgCount = msgCount;
30311
30774
  _lastEmittedToolCount = toolCount;
30775
+ if (msgCount !== _lastPreFlightMsgCount) {
30776
+ a.ctx.lastRequestTokens = estimateRequestTokens(
30777
+ a.ctx.messages,
30778
+ a.ctx.systemPrompt,
30779
+ a.ctx.tools ?? []
30780
+ ).total;
30781
+ _lastPreFlightMsgCount = msgCount;
30782
+ a.ctx.meta["lastRequestTokensAt"] = { msgCount, toolCount };
30783
+ }
30312
30784
  if (!_maxContext) {
30313
30785
  const metaLimit = a.ctx.meta?.["effectiveMaxContext"];
30314
30786
  const providerMax = a.ctx.provider.capabilities.maxContext;
30315
30787
  _maxContext = typeof metaLimit === "number" && metaLimit > 0 ? metaLimit : typeof providerMax === "number" && providerMax > 0 ? providerMax : 2e5;
30316
30788
  }
30317
- const { total } = estimateRequestTokensCalibrated(
30318
- a.ctx.messages,
30319
- a.ctx.systemPrompt,
30320
- a.ctx.tools ?? [],
30321
- calibrationKey()
30322
- );
30789
+ let total;
30790
+ const stashed = a.ctx.lastRequestTokens;
30791
+ if (typeof stashed === "number" && stashed > 0) {
30792
+ const cal = getCalibrationState(calibrationKey());
30793
+ total = cal.calibrated ? Math.round(stashed * Math.min(1.5, Math.max(0.5, cal.ratio))) : stashed;
30794
+ } else {
30795
+ const est = estimateRequestTokensCalibrated(
30796
+ a.ctx.messages,
30797
+ a.ctx.systemPrompt,
30798
+ a.ctx.tools ?? [],
30799
+ calibrationKey()
30800
+ );
30801
+ total = est.total;
30802
+ }
30323
30803
  a.events.emit("ctx.pct", { load: total / _maxContext, tokens: total, maxContext: _maxContext });
30324
30804
  }
30325
30805
  let _maxContext = 0;
30326
30806
  let _lastEmittedMsgCount = -1;
30327
30807
  let _lastEmittedToolCount = -1;
30808
+ let _lastPreFlightMsgCount = -1;
30809
+ let _lastCompactionMsgCount = -1;
30810
+ let _lastCompactionWasNoop = false;
30328
30811
  function foldBlockIntoConversation(block) {
30329
30812
  const messages = a.ctx.messages;
30330
30813
  const last = messages[messages.length - 1];
@@ -30442,6 +30925,12 @@ function createAgentLoopHandler(a, handlers) {
30442
30925
  });
30443
30926
  const req = await handlers.response.buildAndRunRequestPipeline(opts);
30444
30927
  const preFlight = estimateRequestTokens(req.messages, req.system, req.tools ?? []);
30928
+ a.ctx.lastRequestTokens = preFlight.total;
30929
+ _lastPreFlightMsgCount = req.messages.length;
30930
+ a.ctx.meta["lastRequestTokensAt"] = {
30931
+ msgCount: req.messages.length,
30932
+ toolCount: (req.tools ?? []).length
30933
+ };
30445
30934
  await a.ctx.session.append({
30446
30935
  type: "llm_request",
30447
30936
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -31137,6 +31626,10 @@ function flagsToConfigPatch(flags) {
31137
31626
  skills: false
31138
31627
  };
31139
31628
  }
31629
+ if (flags["token-saving-mode"]) {
31630
+ patch.features ??= {};
31631
+ patch.features.tokenSavingMode = true;
31632
+ }
31140
31633
  return patch;
31141
31634
  }
31142
31635
  async function writeProjectMeta(paths, projectRoot) {
@@ -31438,14 +31931,15 @@ Never silently skip a failure \u2014 always report it, even when you choose not
31438
31931
  ## After-task suggestions
31439
31932
 
31440
31933
  After completing a significant task, end your response with 2\u20134 suggested next
31441
- actions under a \`\u{1F4A1} Next steps\` heading. Use this format so the user can
31934
+ actions in a \`<next_steps>\` block. Use this exact format so the user can
31442
31935
  select them with \`/next 1\`, \`/next 2\`, or \`/next 1 2 3\`:
31443
31936
 
31444
31937
  \`\`\`
31445
- \u{1F4A1} Next steps
31938
+ <next_steps>
31446
31939
  1. First suggestion \u2014 imperative, specific, actionable
31447
31940
  2. Second suggestion
31448
31941
  3. Third suggestion
31942
+ </next_steps>
31449
31943
  \`\`\`
31450
31944
 
31451
31945
  Rules:
@@ -31483,6 +31977,10 @@ var DefaultSystemPromptBuilder = class {
31483
31977
  skillBodyCache;
31484
31978
  /** Tools from last build — used for memory relevance scoring. */
31485
31979
  _lastBuildTools;
31980
+ /** Cached rendered online agents string, keyed by array reference. */
31981
+ _lastOnlineAgents;
31982
+ /** Cached full buildToolUsage output — keyed by tools array + online agents refs. */
31983
+ _toolsUsageCache;
31486
31984
  async build(ctx) {
31487
31985
  this._lastBuildTools = ctx.tools;
31488
31986
  if (this.opts.skillLoader && !this.skillCache) {
@@ -31618,6 +32116,9 @@ var DefaultSystemPromptBuilder = class {
31618
32116
  }
31619
32117
  buildToolUsage(tools, ctx) {
31620
32118
  if (tools.length === 0) return "## Tool usage\n\nNo tools registered.";
32119
+ if (this._toolsUsageCache?.toolsRef === tools && this._toolsUsageCache?.agentsRef === ctx.onlineAgents) {
32120
+ return this._toolsUsageCache.text;
32121
+ }
31621
32122
  const byCat = /* @__PURE__ */ new Map();
31622
32123
  const uncategorized = [];
31623
32124
  for (const t2 of tools) {
@@ -31638,7 +32139,7 @@ var DefaultSystemPromptBuilder = class {
31638
32139
  ### ${cat}`);
31639
32140
  for (const t2 of catTools) {
31640
32141
  const hint = t2.usageHint ?? t2.description;
31641
- const desc = hint.length > 80 ? `${hint.slice(0, 77)}...` : hint.trim();
32142
+ const desc = this.opts.tokenSavingMode ? hint.length > 60 ? hint.slice(0, hint.indexOf(".", 20) + 1 || 60) + (hint.length > 60 ? "\u2026" : "") : hint.trim() : hint.length > 80 ? `${hint.slice(0, 77)}...` : hint.trim();
31642
32143
  lines.push(`- **${t2.name}** \u2014 ${desc}`);
31643
32144
  }
31644
32145
  }
@@ -31651,7 +32152,8 @@ var DefaultSystemPromptBuilder = class {
31651
32152
  ${hint.trim()}`);
31652
32153
  }
31653
32154
  }
31654
- lines.push(`
32155
+ if (!this.opts.tokenSavingMode) {
32156
+ lines.push(`
31655
32157
  ## Common patterns
31656
32158
 
31657
32159
  - **Inspect before edit:** \`read\`/\`glob\`/\`grep\` \u2192 locate target \u2192 \`edit\`
@@ -31661,6 +32163,7 @@ ${hint.trim()}`);
31661
32163
  - **Batch ops:** Use \`replace\` with glob patterns for multi-file surgical changes
31662
32164
 
31663
32165
  When unsure about a file's current state, read it first rather than assuming.`);
32166
+ }
31664
32167
  const hasDelegate = tools.some((t2) => t2.name === "delegate");
31665
32168
  if (hasDelegate) {
31666
32169
  const delegateTool = tools.find((t2) => t2.name === "delegate");
@@ -31669,7 +32172,12 @@ When unsure about a file's current state, read it first rather than assuming.`);
31669
32172
  return Array.isArray(role) ? role.filter((r) => typeof r === "string") : [];
31670
32173
  })();
31671
32174
  const roleList = enumValues.length > 0 ? enumValues.join(", ") : "(no roster configured)";
31672
- lines.push(`
32175
+ if (this.opts.tokenSavingMode) {
32176
+ lines.push(`## Delegation
32177
+
32178
+ Use \`delegate\` to hand work to a subagent (roles: ${roleList}).`);
32179
+ } else {
32180
+ lines.push(`
31673
32181
  ## Delegation
31674
32182
 
31675
32183
  You have a \`delegate\` tool that hands a discrete piece of work to a
@@ -31732,21 +32240,20 @@ it's called \u2014 you do not need to call any setup tool. For fine-grained
31732
32240
  control over a long-running fleet (spawn N workers, hand them tasks
31733
32241
  one by one, roll up results), use \`spawn_subagent\` + \`assign_task\` +
31734
32242
  \`await_tasks\` directly; \`delegate\` is the one-call shortcut.`);
32243
+ }
31735
32244
  }
31736
32245
  const hasMailbox = tools.some(
31737
32246
  (t2) => t2.name === "mailbox" || t2.name === "mail_send" || t2.name === "mail_inbox"
31738
32247
  );
31739
32248
  if (hasMailbox) {
31740
- let onlineAgentsInfo = "";
31741
- if (ctx.onlineAgents && ctx.onlineAgents.length > 0) {
31742
- const totalCount = ctx.onlineAgents.length;
31743
- const agentList = ctx.onlineAgents.map((a) => `- **${a.name}** (${a.source ?? "unknown"}${a.sessionId ? `, session: ${a.sessionId.slice(0, 8)}` : ""})`).join("\n");
31744
- onlineAgentsInfo = `
32249
+ const onlineAgentsInfo = this.renderOnlineAgents(ctx.onlineAgents);
32250
+ if (this.opts.tokenSavingMode) {
32251
+ lines.push(`
32252
+ ## Inter-agent mailbox${onlineAgentsInfo}
31745
32253
 
31746
- **Currently online (${totalCount} agent${totalCount !== 1 ? "s" : ""}):**
31747
- ${agentList}`;
31748
- }
31749
- lines.push(`
32254
+ Use \`mail_inbox\` for new messages, \`mail_send\` to communicate with other agents.`);
32255
+ } else {
32256
+ lines.push(`
31750
32257
  ## Inter-agent mailbox${onlineAgentsInfo}
31751
32258
 
31752
32259
  You share a persistent project mailbox with every other agent working on
@@ -31805,9 +32312,52 @@ inline, the rest as a summary. To catch up explicitly:
31805
32312
 
31806
32313
  - \`mailbox action=ack messageId=<id> completed=true outcome="What you did"\`
31807
32314
  - Messages you \`check\` are auto-marked as read; use \`ack\` to mark complete.`);
32315
+ }
32316
+ }
32317
+ const hasMcpControl = tools.some((t2) => t2.name === "mcp_control");
32318
+ const hasMcpUse = tools.some((t2) => t2.name === "mcp_use");
32319
+ if (hasMcpControl && this.opts.tokenSavingMode) {
32320
+ if (hasMcpUse) {
32321
+ lines.push(`
32322
+ ## MCP tools (lazy-loaded)
32323
+
32324
+ MCP server tools are NOT registered by default in token-saving mode to keep
32325
+ the prompt compact. Each server's process is running in the background; only
32326
+ tool registration is deferred.
32327
+
32328
+ **Preferred approach** \u2014 one-shot meta-tool:
32329
+ \`mcp_use({ server: "<name>", tool: "<bare-tool>", input: { ... } })\`
32330
+ This activates the server, calls the tool, returns the result, and
32331
+ deactivates \u2014 all in one call. No need to track activate/deactivate state.
32332
+
32333
+ **Manual approach** (for exploration):
32334
+ 1. \`mcp_control({ action: "list" })\` \u2014 see which servers are connected
32335
+ 2. \`mcp_control({ action: "activate", server: "<name>" })\` \u2014 register tools
32336
+ 3. Use the tools normally
32337
+ 4. \`mcp_control({ action: "deactivate", server: "<name>" })\` \u2014 clean up
32338
+
32339
+ Activation/deactivation is ephemeral (no config writes) and does NOT affect
32340
+ the server connection \u2014 only tool visibility changes.`);
32341
+ } else {
32342
+ lines.push(`
32343
+ ## MCP tools (lazy-loaded)
32344
+
32345
+ MCP server tools are NOT registered by default in token-saving mode to keep
32346
+ the prompt compact. Each server's process is running in the background; only
32347
+ tool registration is deferred.
32348
+
32349
+ When you need a specific MCP server's tools:
32350
+ 1. \`mcp_control({ action: "list" })\` \u2014 see which servers are connected
32351
+ 2. \`mcp_control({ action: "activate", server: "<name>" })\` \u2014 register its tools
32352
+ 3. Use the tools as needed
32353
+ 4. \`mcp_control({ action: "deactivate", server: "<name>" })\` \u2014 unregister when done
32354
+
32355
+ Activation/deactivation is ephemeral (no config writes) and does NOT affect
32356
+ the server connection \u2014 only tool visibility changes.`);
32357
+ }
31808
32358
  }
31809
32359
  const hasContextManager = tools.some((t2) => t2.name === "context_manager");
31810
- if (hasContextManager) {
32360
+ if (hasContextManager && !this.opts.tokenSavingMode) {
31811
32361
  const maxCtx = this.opts.modelCapabilities?.maxContextTokens ?? 128e3;
31812
32362
  const threshold = maxCtx <= 32e3 ? "50" : "70";
31813
32363
  lines.push(`
@@ -31824,7 +32374,32 @@ use the context_manager tool proactively \u2014 do NOT wait to be told:
31824
32374
  **Never** stuff redundant information into a tool result. If you summarize a file, do not paste its full content \u2014
31825
32375
  summarize it, and let the tool result hold only the summary.`);
31826
32376
  }
31827
- return lines.join("\n");
32377
+ const text = lines.join("\n");
32378
+ this._toolsUsageCache = { toolsRef: tools, agentsRef: ctx.onlineAgents, text };
32379
+ return text;
32380
+ }
32381
+ /**
32382
+ * Render the online agents list, cached by array reference. The agents
32383
+ * list changes at join/leave pace (seconds to minutes), not every prompt
32384
+ * build turn (hundreds of ms). Reference equality avoids re-stringifying
32385
+ * the same array on every iteration while still being correct when the
32386
+ * caller passes a fresh array.
32387
+ */
32388
+ renderOnlineAgents(agents) {
32389
+ if (!agents || agents.length === 0) return "";
32390
+ if (this._lastOnlineAgents?.ref === agents) {
32391
+ return this._lastOnlineAgents.text;
32392
+ }
32393
+ const totalCount = agents.length;
32394
+ const agentList = agents.map(
32395
+ (a) => `- **${a.name}** (${a.source ?? "unknown"}${a.sessionId ? `, session: ${a.sessionId.slice(0, 8)}` : ""})`
32396
+ ).join("\n");
32397
+ const text = `
32398
+
32399
+ **Currently online (${totalCount} agent${totalCount !== 1 ? "s" : ""}):**
32400
+ ${agentList}`;
32401
+ this._lastOnlineAgents = { ref: agents, text };
32402
+ return text;
31828
32403
  }
31829
32404
  async buildEnvironment(ctx) {
31830
32405
  const cached = this.envCacheByRoot.get(ctx.projectRoot);
@@ -31867,7 +32442,7 @@ summarize it, and let the tool result hold only the summary.`);
31867
32442
  "## Skills in scope for this session",
31868
32443
  this.skillCache,
31869
32444
  "",
31870
- "Full skill instructions are injected in the Active Skills block below."
32445
+ this.opts.tokenSavingMode ? "Compact skill instructions are injected in the Active Skills block below (Overview + Rules only)." : "Full skill instructions are injected in the Active Skills block below."
31871
32446
  );
31872
32447
  }
31873
32448
  const text = lines.join("\n");
@@ -31906,41 +32481,83 @@ ${mem}`);
31906
32481
  } catch {
31907
32482
  }
31908
32483
  }
31909
- if (this.opts.skillLoader && this.skillBodyCache === void 0) {
31910
- try {
31911
- const skills = await this.opts.skillLoader.list();
31912
- if (skills.length > 0) {
31913
- const bodies = [];
31914
- for (const s of skills) {
31915
- try {
31916
- const raw = await this.opts.skillLoader.readBody(s.name);
31917
- const body = stripFrontmatter(raw);
31918
- if (body.trim()) {
31919
- bodies.push(`## Skill: ${s.name}
32484
+ if (this.opts.skillLoader) {
32485
+ if (this.opts.tokenSavingMode) {
32486
+ if (this.skillBodyCache === void 0) {
32487
+ await this.buildCompactSkillBodies();
32488
+ }
32489
+ } else {
32490
+ if (this.skillBodyCache === void 0) {
32491
+ await this.buildFullSkillBodies();
32492
+ }
32493
+ }
32494
+ }
32495
+ if (this.skillBodyCache) {
32496
+ parts.push(`# Active Skills
32497
+
32498
+ ${this.skillBodyCache}`);
32499
+ }
32500
+ return parts.join("\n\n");
32501
+ }
32502
+ /** Build full skill bodies (token-saving OFF). */
32503
+ async buildFullSkillBodies() {
32504
+ try {
32505
+ const skills = await this.opts.skillLoader.list();
32506
+ if (skills.length > 0) {
32507
+ const bodies = [];
32508
+ for (const s of skills) {
32509
+ try {
32510
+ const raw = await this.opts.skillLoader.readBody(s.name);
32511
+ const body = stripFrontmatter2(raw);
32512
+ if (body.trim()) {
32513
+ bodies.push(`## Skill: ${s.name}
31920
32514
 
31921
32515
  ${body.trim()}`);
31922
- }
31923
- } catch {
31924
32516
  }
32517
+ } catch {
31925
32518
  }
31926
- if (bodies.length > 0) {
31927
- this.skillBodyCache = bodies.join("\n\n---\n\n");
31928
- } else {
31929
- this.skillBodyCache = "";
31930
- }
31931
- } else {
31932
- this.skillBodyCache = "";
31933
32519
  }
31934
- } catch {
32520
+ this.skillBodyCache = bodies.length > 0 ? bodies.join("\n\n---\n\n") : "";
32521
+ } else {
31935
32522
  this.skillBodyCache = "";
31936
32523
  }
32524
+ } catch {
32525
+ this.skillBodyCache = "";
31937
32526
  }
31938
- if (this.skillBodyCache) {
31939
- parts.push(`# Active Skills
32527
+ }
32528
+ /**
32529
+ * Build compact skill bodies for token-saving mode.
32530
+ * Uses `readSaveBody` from the skill loader which tries `SKILL.save.md`
32531
+ * first, then falls back to auto-compaction.
32532
+ */
32533
+ async buildCompactSkillBodies() {
32534
+ if (!this.opts.skillLoader) {
32535
+ this.skillBodyCache = "";
32536
+ return;
32537
+ }
32538
+ try {
32539
+ const skills = await this.opts.skillLoader.list();
32540
+ if (skills.length > 0) {
32541
+ const bodies = [];
32542
+ for (const s of skills) {
32543
+ try {
32544
+ const saveBody = await this.opts.skillLoader.readSaveBody(s.name);
32545
+ const clean = stripFrontmatter2(saveBody);
32546
+ if (clean.trim()) {
32547
+ bodies.push(`## Skill: ${s.name}
31940
32548
 
31941
- ${this.skillBodyCache}`);
32549
+ ${clean.trim()}`);
32550
+ }
32551
+ } catch {
32552
+ }
32553
+ }
32554
+ this.skillBodyCache = bodies.length > 0 ? bodies.join("\n\n---\n\n") : "";
32555
+ } else {
32556
+ this.skillBodyCache = "";
32557
+ }
32558
+ } catch {
32559
+ this.skillBodyCache = "";
31942
32560
  }
31943
- return parts.join("\n\n");
31944
32561
  }
31945
32562
  async buildMode() {
31946
32563
  if (this.opts.modePrompt) return this.opts.modePrompt;
@@ -32030,7 +32647,7 @@ ${this.skillBodyCache}`);
32030
32647
  return langs.size === 0 ? "unknown" : Array.from(langs).join(", ");
32031
32648
  }
32032
32649
  };
32033
- function stripFrontmatter(raw) {
32650
+ function stripFrontmatter2(raw) {
32034
32651
  if (!raw.startsWith("---")) return raw;
32035
32652
  const end = raw.indexOf("\n---", 4);
32036
32653
  if (end === -1) return raw;
@@ -32050,6 +32667,11 @@ function compactTrigger(trigger) {
32050
32667
  // src/registry/tool-registry.ts
32051
32668
  var ToolRegistry = class _ToolRegistry {
32052
32669
  tools = /* @__PURE__ */ new Map();
32670
+ /** Monotonic version bumped on every registry mutation. */
32671
+ _version = 0;
32672
+ /** Cached `list()` result, frozen after build. Invalidated on _version change. */
32673
+ _listSnapshot;
32674
+ _listSnapshotVersion = -1;
32053
32675
  /** Pre-compute tool definition token estimate once at registration time. */
32054
32676
  _stampDefTokens(tool) {
32055
32677
  if (tool._estDefTokens === void 0) {
@@ -32075,6 +32697,7 @@ var ToolRegistry = class _ToolRegistry {
32075
32697
  }
32076
32698
  this._stampDefTokens(tool);
32077
32699
  this.tools.set(tool.name, { tool, owner });
32700
+ this._version++;
32078
32701
  }
32079
32702
  /**
32080
32703
  * Attempt to register a tool. Returns true if successful, false if a tool
@@ -32088,6 +32711,7 @@ var ToolRegistry = class _ToolRegistry {
32088
32711
  }
32089
32712
  this._stampDefTokens(tool);
32090
32713
  this.tools.set(tool.name, { tool, owner });
32714
+ this._version++;
32091
32715
  return true;
32092
32716
  }
32093
32717
  /**
@@ -32159,6 +32783,7 @@ var ToolRegistry = class _ToolRegistry {
32159
32783
  wrapped._estDefTokens = void 0;
32160
32784
  this._stampDefTokens(wrapped);
32161
32785
  this.tools.set(name, { tool: wrapped, owner: `${entry.owner}+${owner}` });
32786
+ this._version++;
32162
32787
  }
32163
32788
  get(name) {
32164
32789
  return this.tools.get(name)?.tool;
@@ -32167,7 +32792,13 @@ var ToolRegistry = class _ToolRegistry {
32167
32792
  return this.tools.get(name)?.owner;
32168
32793
  }
32169
32794
  list() {
32170
- return Array.from(this.tools.values()).map((e) => e.tool);
32795
+ if (this._listSnapshot && this._version === this._listSnapshotVersion) {
32796
+ return this._listSnapshot;
32797
+ }
32798
+ const arr = Array.from(this.tools.values()).map((e) => e.tool);
32799
+ this._listSnapshot = arr;
32800
+ this._listSnapshotVersion = this._version;
32801
+ return arr;
32171
32802
  }
32172
32803
  /**
32173
32804
  * Group tools by their `category` field. Tools without a category
@@ -32415,14 +33046,19 @@ var DefaultPluginAPI = class {
32415
33046
  this.pipelines = readonlyPipelines;
32416
33047
  const tr = init.toolRegistry;
32417
33048
  const isOfficial = init.official === true;
33049
+ const capabilities = init.capabilities;
32418
33050
  const assertCanMutateTool = (name, op) => {
32419
33051
  if (isOfficial) return;
32420
33052
  const currentOwner = tr.ownerOf(name);
32421
33053
  if (currentOwner === void 0) return;
32422
33054
  const ownedSolelyByMe = currentOwner.split("+").every((seg) => seg === owner);
32423
- if (!ownedSolelyByMe) {
33055
+ if (ownedSolelyByMe) return;
33056
+ const toolCaps = tr.get(name)?.capabilities ?? [];
33057
+ const pluginMutateCaps = capabilities?.toolMutateCapabilities ?? [];
33058
+ const hasRequiredCap = toolCaps.some((c) => pluginMutateCaps.includes(c));
33059
+ if (!hasRequiredCap) {
32424
33060
  throw new Error(
32425
- `Plugin "${owner}" may not ${op} tool "${name}" \u2014 it is owned by "${currentOwner}". Only official (first-party) plugins may modify tools they do not own.`
33061
+ `Plugin "${owner}" may not ${op} tool "${name}" \u2014 it is owned by "${currentOwner}". Tool capabilities: [${toolCaps.join(", ") || "none"}]. Plugin toolMutateCapabilities: [${pluginMutateCaps.join(", ") || "none"}]. Missing required capability to mutate this tool.`
32426
33062
  );
32427
33063
  }
32428
33064
  };
@@ -34714,6 +35350,7 @@ ${check.stderr}`);
34714
35350
  return new Promise((res) => {
34715
35351
  let stdout = "";
34716
35352
  let stderr = "";
35353
+ const MAX_GIT_OUTPUT = 1e6;
34717
35354
  const child = spawn(this.gitBin, args, {
34718
35355
  cwd,
34719
35356
  env: buildChildEnv(),
@@ -34722,10 +35359,10 @@ ${check.stderr}`);
34722
35359
  windowsHide: true
34723
35360
  });
34724
35361
  child.stdout?.on("data", (c) => {
34725
- stdout += c.toString();
35362
+ if (stdout.length < MAX_GIT_OUTPUT) stdout += c.toString();
34726
35363
  });
34727
35364
  child.stderr?.on("data", (c) => {
34728
- stderr += c.toString();
35365
+ if (stderr.length < MAX_GIT_OUTPUT) stderr += c.toString();
34729
35366
  });
34730
35367
  child.on("error", (err) => res({ code: 1, stdout, stderr: err.message }));
34731
35368
  child.on("close", (code) => res({ code: code ?? 1, stdout, stderr }));
@@ -35093,7 +35730,7 @@ var CollaborationBus = class {
35093
35730
  const timeoutPromise = new Promise((resolve14) => {
35094
35731
  timer = setTimeout(() => resolve14("timeout"), timeoutMs);
35095
35732
  });
35096
- const resumedPromise = this.pausePromise.then(() => "resumed");
35733
+ const resumedPromise = this.pausePromise.then(() => "resumed").catch(() => "resumed");
35097
35734
  const winner = await Promise.race([resumedPromise, timeoutPromise]);
35098
35735
  if (timer) clearTimeout(timer);
35099
35736
  if (winner === "timeout") {
@@ -35285,6 +35922,14 @@ var DefaultMailbox = class {
35285
35922
  await fsp3.writeFile(this.filePath, "", "utf8");
35286
35923
  });
35287
35924
  }
35925
+ // ── Client registry stubs (not applicable per-session) ─────────────────
35926
+ async registerClient(_input) {
35927
+ }
35928
+ async clientHeartbeat(_input) {
35929
+ }
35930
+ async getClientStatuses() {
35931
+ return [];
35932
+ }
35288
35933
  // ── Internal ──────────────────────────────────────────────────────────
35289
35934
  async _readAll() {
35290
35935
  try {
@@ -37600,6 +38245,6 @@ function createChimeraPlugin() {
37600
38245
  };
37601
38246
  }
37602
38247
 
37603
- export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AgentStatusTracker, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BrainMonitor, BudgetExceededError, CHIMERA_REVIEW_PROMPT, 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_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DEPENDENCY_FILE_PATTERNS, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMailbox, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FORBIDDEN_PROTO_KEYS, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GlobalMailbox, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MEMORY_TYPE_LABELS, 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, SddError, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, StreamHangError, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertNever, assertSafePath, atomicWrite, attachAutoExtend, attachDepWatcherBridge, attachMailboxChecker, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildLosslessDigest, buildMailboxBlock, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildQueuedMessagesBlock, buildRecoveryStrategies, buildSmartDigest, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compactLog, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeMessageTokens, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, consumeQueuedMessagesUpdate, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createAutonomyBrain, createChimeraPlugin, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMailboxChecker, createMcpControlTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createStrategyCompactor, createSyncPlugin, createTieredBrainArbiter, createToolOutputSerializer, decryptConfigSecrets, deepMerge, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, detectEcosystem as detectPackageEcosystem, dispatchAgent, downloadGitHubTarball, eliseOldToolResults, emptyGoal, emptyPlan, emptyTaskFile, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateMessageTokens, estimateMessages, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, extractText, filesystemServer, findCriticalPath, findPreserveStart, flagsToConfigPatch, formatContextWindowModeList, formatDecisionSummary, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getFileHistory, getFilesByAgent, getFullLog, getFullPackageLog, getLastAuthor, getManifestPackages, getPackageAuthor, getPackagesByAgent, getPlanTemplate, getSessionRegistry, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hasSessionRegistry, hasTextContent, hashRequest, hookMatcherMatches, injectPendingMailboxMessages, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isPrimitiveArray, isSddError, isSecretField, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, mailboxSessionTag, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, mutatePlan, mutateTasks, noOpLogger, noOpVault, normalizeRecipient, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseProgressFromText, parseSkillRef, peekQueuedMessages, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, recordFileAction, recordPackageAction, recordProgress, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveChimeraConfig, resolveContextWindowPolicy, resolveMailboxIdentity, resolveModelMatrix, resolveProjectDir, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, scoreMessage, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setProgress, setQueuedMessagesSnapshot, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startPackageOutdatedWatcher, startTechStackConsumer, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, truncate, unifiedDiff, unloadPlugins, updatePackageOutdatedStatus, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, wstackGlobalRoot, zaiVisionServer };
38248
+ export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, AISpecBuilder, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, ALL_SYNC_CATEGORIES, AUDIT_LOG_AGENT, Agent, AgentError, AgentStatusTracker, AnnotationsStore, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutoExecutor, AutoPhasePlanner, AutoPhaseRunner, AutonomousRunner, BUG_HUNTER_AGENT, BrainDecisionQueue, BrainMonitor, BudgetExceededError, CHIMERA_REVIEW_PROMPT, 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_SESSION_PRUNE_DAYS, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DEFAULT_TOOLS_CONFIG, DEPENDENCY_FILE_PATTERNS, DefaultAttachmentStore, DefaultBrainArbiter, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMailbox, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultPromptStore, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorStateCheckpoint, DoneConditionChecker, ENHANCER_SYSTEM_PROMPT, ERROR_CODES, EternalAutonomyEngine, EventBus, ExtensionRegistry, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FORBIDDEN_PROTO_KEYS, FileMemoryBackend, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, FsError, GitignoreUpdater, GlobalMailbox, GraphMemoryBackend, HookRegistry, HookRunner, HumanEscalatingBrainArbiter, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, MATRIX_PHASE_KEYS, MAX_JOURNAL_ENTRIES, MAX_PROGRESS_HISTORY, MEMORY_TYPE_LABELS, 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, SddError, SddParallelRun, SddTaskDecomposer, SecurityScanner, SecurityScannerOrchestrator, SelectiveCompactor, SessionAnalyzer, SessionError, SessionMemoryConsolidator, SessionRecovery, SessionRegistry, SkillGenerator, SkillInstaller, SkillManifestStore, SlashCommandRegistry, SpecDrivenDev, SpecParser, SpecStore, SpecVersioning, StreamHangError, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskGraphStore, TaskTracker, TechStackDetector, ToolAuditLog, ToolError, ToolExecutor, ToolRegistry, WorktreeManager, WrongStackError, addPlanItem, allServers, analyzeCriticalPath, appendJournal, applyRosterBudget, asBlocks, asText, assertNever, assertSafePath, atomicWrite, attachAutoExtend, attachDepWatcherBridge, attachMailboxChecker, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, bootConfig, braveSearchServer, buildBtwBlock, buildChildEnv, buildGoalPreamble, buildLosslessDigest, buildMailboxBlock, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildQueuedMessagesBlock, buildRecoveryStrategies, buildSmartDigest, classifyFamily, clearPlan, collabInjectMiddleware, collabPauseMiddleware, color, compactLog, compileGlob, compileUserRegex, completePartialObject, composeDirectorPrompt, composeSubagentPrompt, computeMessageTokens, computeTaskItemProgress, computeTaskProgress, consumeBtwNotes, consumeQueuedMessagesUpdate, context7Server, contextManagerTool, createAutoExecutor, createAutoPhaseFromTaskGraph, createAutonomyBrain, createChimeraPlugin, createContextManagerTool, createDefaultPipelines, createDelegateTool, createGitPlugin, createMailboxChecker, createMcpControlTool, createMcpUseTool, createMessage, createObservabilityPlugin, createPlanPlugin, createPromptsPlugin, createSecurityPlugin, createSecuritySlashCommand, createSessionEventBridge, createSkillsPlugin, createStrategyCompactor, createSyncPlugin, createTieredBrainArbiter, createToolOutputSerializer, decryptConfigSecrets, deepMerge, defaultGitignoreUpdater, defaultOrchestrator, defaultReportGenerator, defaultSecurityScanner, defaultSkillGenerator, defaultTechStackDetector, deriveTodosFromPlanItem, detectNewlineStyle, detectEcosystem as detectPackageEcosystem, dispatchAgent, downloadGitHubTarball, eliseOldToolResults, emptyGoal, emptyPlan, emptyTaskFile, encryptConfigSecrets, enhanceUserPrompt, ensureDir, estimateMessageTokens, estimateMessages, estimateRequestTokens, estimateRequestTokensCalibrated, estimateTextTokens, estimateToolDefTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, expandGlob, expectDefined, extractRunEnv, extractText, filesystemServer, findCriticalPath, findPreserveStart, flagsToConfigPatch, formatContextWindowModeList, formatDecisionSummary, formatGoal, formatHumanPrompt, formatPlan, formatPlanTemplates, formatTaskList, formatTaskProgress, formatTodosList, getAgentDefinition, getCalibrationState, getContextWindowMode, getFileHistory, getFilesByAgent, getFullLog, getFullPackageLog, getLastAuthor, getManifestPackages, getPackageAuthor, getPackagesByAgent, getPlanTemplate, getSessionRegistry, getTemplate, getTermSize, githubServer, goalFilePath, googleMapsServer, hasSessionRegistry, hasTextContent, hashRequest, hookMatcherMatches, injectPendingMailboxMessages, isAgentError, isConfigError, isContextWindowModeId, isFsError, isImageBlock, isInteractive, isPluginError, isPrimitiveArray, isSddError, isSecretField, isSessionError, isStdinTTY, isStdoutTTY, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isValidMatrixKey, isWrongStackError, listContextWindowModes, listPlanTemplates, listTemplates, loadDirectorState, loadGoal, loadPlan, loadPlugins, loadProjectModes, loadTasks, loadTodosCheckpoint, loadUserModes, mailboxSessionTag, makeAgentSubagentRunner, makeAskTool, makeAssignTool, makeAutonomyPromptContributor, makeAwaitTasksTool, makeCollabDebugTool, makeContinueToNextIterationTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, matchAny, matchGlob, matrixKeyKind, mergeCustomModelDefs, mergeModelsPayload, migratePlaintextSecrets, miniMaxVisionServer, mutatePlan, mutateTasks, noOpLogger, noOpVault, normalizeRecipient, normalizeToLf, normalizedEqual, onResize, parseContinueDirective, parseEntries, parseProgressFromText, parseSkillRef, peekQueuedMessages, pendingBtwCount, phaseForRole, projectHash, projectSlug, recentTextTurns, recordActualUsage, recordFileAction, recordPackageAction, recordProgress, removePlanItem, renderProgress, renderPrometheus, renderSpecAnalysis, renderTaskGraph, renderTaskList, repairToolUseAdjacency, resetCalibration, resolveAuditLevel, resolveChimeraConfig, resolveContextWindowPolicy, resolveMailboxIdentity, resolveModelMatrix, resolveProjectDir, resolveSessionLoggingConfig, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, runProviderWithRetry, runShellHook, safeParse, safeStringify, sanitizeJsonString, saveGoal, savePlan, saveTasks, saveTodosCheckpoint, scoreAgents, scoreMessage, securitySlashCommand, sentinelServer, setBtwNote, setOutputLineGuard, setPlanItemStatus, setProgress, setQueuedMessagesSnapshot, setRawMode, shouldEnhance, slackServer, sleep, stableStringify, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, startPackageOutdatedWatcher, startTechStackConsumer, stripAnsi, summarizeUsage, templateToMarkdown, toStyle, toWrongStackError, topologicalSort, truncate, unifiedDiff, unloadPlugins, updatePackageOutdatedStatus, validateAgainstSchema, wireMetricsToEvents, withFileLock, wrapAsState, writeErr, writeOut, wstackGlobalRoot, zaiVisionServer };
37604
38249
  //# sourceMappingURL=index.js.map
37605
38250
  //# sourceMappingURL=index.js.map