@wrongstack/core 0.3.3 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/{agent-bridge-C3DUGjSb.d.ts → agent-bridge-DaOnnHNW.d.ts} +1 -1
  2. package/dist/{compactor-DpJBI1YH.d.ts → compactor-CFky6JKM.d.ts} +1 -1
  3. package/dist/{config-D2qvAxVd.d.ts → config-RlhKLjme.d.ts} +2 -1
  4. package/dist/{context-IovtuTf8.d.ts → context-B1kBj1lY.d.ts} +4 -3
  5. package/dist/coordination/index.d.ts +12 -10
  6. package/dist/coordination/index.js +50 -9
  7. package/dist/coordination/index.js.map +1 -1
  8. package/dist/defaults/index.d.ts +16 -16
  9. package/dist/defaults/index.js +213 -17
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/{events-BHIQs4o1.d.ts → events-BBaKeMfb.d.ts} +11 -1
  12. package/dist/execution/index.d.ts +11 -11
  13. package/dist/execution/index.js +54 -1
  14. package/dist/execution/index.js.map +1 -1
  15. package/dist/extension/index.d.ts +6 -6
  16. package/dist/{index-hWNybrNZ.d.ts → index-meJRuQtc.d.ts} +26 -5
  17. package/dist/index.d.ts +26 -26
  18. package/dist/index.js +306 -39
  19. package/dist/index.js.map +1 -1
  20. package/dist/infrastructure/index.d.ts +6 -6
  21. package/dist/kernel/index.d.ts +9 -9
  22. package/dist/kernel/index.js.map +1 -1
  23. package/dist/{mcp-servers-C2OopXOn.d.ts → mcp-servers-Cf4-bJnd.d.ts} +3 -3
  24. package/dist/models/index.d.ts +2 -2
  25. package/dist/models/index.js +1 -0
  26. package/dist/models/index.js.map +1 -1
  27. package/dist/{multi-agent-B9a6sflH.d.ts → multi-agent-D5m66hzB.d.ts} +1 -1
  28. package/dist/observability/index.d.ts +2 -2
  29. package/dist/{path-resolver--59rCou3.d.ts → path-resolver-Bg4OP5fi.d.ts} +2 -2
  30. package/dist/{provider-runner-B39miKRw.d.ts → provider-runner-CU9gnU2E.d.ts} +3 -3
  31. package/dist/sdd/index.d.ts +3 -3
  32. package/dist/secret-scrubber-DyUAWS09.d.ts +54 -0
  33. package/dist/{secret-scrubber-Cuy5afaQ.d.ts → secret-scrubber-Dyh5LVYL.d.ts} +1 -1
  34. package/dist/security/index.d.ts +58 -5
  35. package/dist/security/index.js +95 -6
  36. package/dist/security/index.js.map +1 -1
  37. package/dist/{selector-wT2fv9Fg.d.ts → selector-DBEiwXBk.d.ts} +1 -1
  38. package/dist/{session-reader-CcPi4BQ8.d.ts → session-reader-D-z0AgHs.d.ts} +1 -1
  39. package/dist/{skill-C_7znCIC.d.ts → skill-DayhFUBb.d.ts} +1 -1
  40. package/dist/storage/index.d.ts +5 -5
  41. package/dist/storage/index.js +53 -1
  42. package/dist/storage/index.js.map +1 -1
  43. package/dist/{system-prompt-Dk1qm8ey.d.ts → system-prompt-DNetCecu.d.ts} +1 -1
  44. package/dist/{tool-executor-HsBLGRaA.d.ts → tool-executor-B5bgmEgE.d.ts} +12 -5
  45. package/dist/types/index.d.ts +15 -15
  46. package/dist/types/index.js +59 -2
  47. package/dist/types/index.js.map +1 -1
  48. package/dist/utils/index.d.ts +23 -2
  49. package/dist/utils/index.js +41 -1
  50. package/dist/utils/index.js.map +1 -1
  51. package/package.json +1 -1
  52. package/dist/secret-scrubber-CgG2tV2B.d.ts +0 -31
package/dist/index.js CHANGED
@@ -806,7 +806,7 @@ function walk(node, vault, transform) {
806
806
  if (Array.isArray(node)) {
807
807
  return node.map((item) => walk(item, vault, transform));
808
808
  }
809
- const out = {};
809
+ const out = /* @__PURE__ */ Object.create(null);
810
810
  for (const [k, v] of Object.entries(node)) {
811
811
  if (typeof v === "string" && isSecretField(k)) {
812
812
  out[k] = transform(v, k);
@@ -1770,6 +1770,7 @@ var FAMILY_BY_NPM = {
1770
1770
  "@ai-sdk/xai": "openai-compatible",
1771
1771
  "@ai-sdk/cerebras": "openai-compatible",
1772
1772
  "@ai-sdk/togetherai": "openai-compatible",
1773
+ "@ai-sdk/mistral": "openai-compatible",
1773
1774
  "@ai-sdk/perplexity": "openai-compatible",
1774
1775
  "@ai-sdk/deepinfra": "openai-compatible",
1775
1776
  "@openrouter/ai-sdk-provider": "openai-compatible",
@@ -2241,6 +2242,13 @@ var InMemoryAgentBridge = class {
2241
2242
  this.pendingRequests.delete(correlationId);
2242
2243
  reject(new Error(`Request ${correlationId} timed out after ${timeout}ms`));
2243
2244
  }, timeout);
2245
+ if (this.stopped) {
2246
+ clearTimeout(timer);
2247
+ this.inflightGuards.delete(correlationId);
2248
+ this.pendingRequests.delete(correlationId);
2249
+ reject(new Error("Bridge stopped"));
2250
+ return;
2251
+ }
2244
2252
  this.pendingRequests.set(correlationId, {
2245
2253
  resolve: resolve4,
2246
2254
  reject,
@@ -2444,6 +2452,15 @@ var ToolExecutor = class {
2444
2452
  opts;
2445
2453
  serializer;
2446
2454
  iterationTimeoutMs;
2455
+ /**
2456
+ * Clear the interactive confirm awaiter so the executor returns
2457
+ * `ToolConfirmPendingResult` instead of blocking on stdin. Used by
2458
+ * the CLI to switch from inline prompts (REPL) to event-driven
2459
+ * confirmation (TUI) at runtime.
2460
+ */
2461
+ clearConfirmAwaiter() {
2462
+ this.opts.confirmAwaiter = void 0;
2463
+ }
2447
2464
  /**
2448
2465
  * Execute a batch of tool uses using the configured strategy.
2449
2466
  * Returns the execution results and the remaining output budget.
@@ -2692,6 +2709,42 @@ var ToolExecutor = class {
2692
2709
  }
2693
2710
  };
2694
2711
 
2712
+ // src/utils/regex-guard.ts
2713
+ var MAX_PATTERN_LEN = 512;
2714
+ var DANGEROUS_PATTERNS = [
2715
+ /(\([^)]*[+*][^)]*\))[+*]/,
2716
+ // (a+)+, (.*)+, etc
2717
+ /(\(\?:[^)]*[+*][^)]*\))[+*]/
2718
+ // same, with non-capturing group
2719
+ ];
2720
+ function compileUserRegex(pattern, flags) {
2721
+ if (typeof pattern !== "string") {
2722
+ return { ok: false, reason: "pattern must be a string" };
2723
+ }
2724
+ if (pattern.length === 0) {
2725
+ return { ok: false, reason: "pattern is empty" };
2726
+ }
2727
+ if (pattern.length > MAX_PATTERN_LEN) {
2728
+ return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };
2729
+ }
2730
+ for (const rx of DANGEROUS_PATTERNS) {
2731
+ if (rx.test(pattern)) {
2732
+ return {
2733
+ ok: false,
2734
+ reason: "pattern looks vulnerable to catastrophic backtracking \u2014 rewrite without nested quantifiers"
2735
+ };
2736
+ }
2737
+ }
2738
+ try {
2739
+ return { ok: true, regex: new RegExp(pattern, flags) };
2740
+ } catch (err) {
2741
+ return {
2742
+ ok: false,
2743
+ reason: err instanceof Error ? err.message : "invalid regex"
2744
+ };
2745
+ }
2746
+ }
2747
+
2695
2748
  // src/storage/session-reader.ts
2696
2749
  var DefaultSessionReader = class {
2697
2750
  store;
@@ -2786,7 +2839,11 @@ function buildMatcher(q) {
2786
2839
  const ci = q.caseInsensitive ?? true;
2787
2840
  if (q.regex) {
2788
2841
  const flags = ci ? "i" : "";
2789
- const re = new RegExp(q.query, flags);
2842
+ const compiled = compileUserRegex(q.query, flags);
2843
+ if (!compiled.ok) {
2844
+ throw new Error(`Invalid search regex "${q.query}": ${compiled.reason}`);
2845
+ }
2846
+ const re = compiled.regex;
2790
2847
  return (text) => {
2791
2848
  const m = re.exec(text);
2792
2849
  return m ? { start: m.index, end: m.index + m[0].length } : null;
@@ -3079,7 +3136,11 @@ function getCachedGlob(pattern) {
3079
3136
  COMPILED_GLOB_CACHE.set(pattern, re);
3080
3137
  return re;
3081
3138
  }
3139
+ var MAX_GLOB_PATTERN_LEN = 1024;
3082
3140
  function compileGlob(pattern) {
3141
+ if (pattern.length > MAX_GLOB_PATTERN_LEN) {
3142
+ throw new Error(`Glob pattern exceeds ${MAX_GLOB_PATTERN_LEN} characters`);
3143
+ }
3083
3144
  let i = 0;
3084
3145
  let re = "^";
3085
3146
  while (i < pattern.length) {
@@ -3842,6 +3903,7 @@ var QueueStore = class {
3842
3903
  } catch (err) {
3843
3904
  const code = err.code;
3844
3905
  if (code === "ENOENT") return [];
3906
+ console.warn(`[QueueStore] failed to read queue file "${this.file}":`, err instanceof Error ? err.message : String(err));
3845
3907
  return [];
3846
3908
  }
3847
3909
  let parsed;
@@ -3941,6 +4003,13 @@ var DefaultAttachmentStore = class {
3941
4003
  return mergeAdjacentText(blocks);
3942
4004
  }
3943
4005
  async clear() {
4006
+ if (this.spoolDir) {
4007
+ const toDelete = [];
4008
+ for (const att of this.items.values()) {
4009
+ if (att.path) toDelete.push(att.path);
4010
+ }
4011
+ await Promise.all(toDelete.map((p) => fsp2.unlink(p).catch(() => void 0)));
4012
+ }
3944
4013
  this.items.clear();
3945
4014
  this.refs.length = 0;
3946
4015
  this.nextSeq = { text: 0, image: 0, file: 0 };
@@ -4956,6 +5025,10 @@ var DirectorStateCheckpoint = class {
4956
5025
  this.timer = null;
4957
5026
  }
4958
5027
  await this.persist();
5028
+ if (this.rewriteRequested) {
5029
+ this.rewriteRequested = false;
5030
+ await this.persist();
5031
+ }
4959
5032
  }
4960
5033
  bumpUpdatedAt() {
4961
5034
  this.snapshot = { ...this.snapshot, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
@@ -4996,12 +5069,49 @@ var DefaultPermissionPolicy = class {
4996
5069
  loaded = false;
4997
5070
  trustFile;
4998
5071
  yolo;
5072
+ /**
5073
+ * Session-scoped "soft deny" map. When the user presses 'n' (block once),
5074
+ * the tool+pattern is added here. If the LLM retries in the same session,
5075
+ * we return deny directly without asking again.
5076
+ *
5077
+ * Cleared on reload() since reload = fresh trust file snapshot.
5078
+ */
5079
+ sessionDenied = /* @__PURE__ */ new Map();
5080
+ /**
5081
+ * Session-scoped "soft trust" map. When the user presses 'a' (allow once),
5082
+ * the tool+pattern is added here. If the LLM retries in the same session,
5083
+ * we return auto directly without asking again.
5084
+ *
5085
+ * Cleared on reload().
5086
+ */
5087
+ sessionAllowed = /* @__PURE__ */ new Map();
5088
+ /**
5089
+ * Interactive prompt delegate. When set, `evaluate()` calls it to get a
5090
+ * user decision synchronously (CLI REPL path). When cleared (TUI / WebUI),
5091
+ * `evaluate()` returns `confirm` so the caller can emit
5092
+ * `tool.confirm_needed` for the UI layer to handle.
5093
+ *
5094
+ * Mutable so the host can switch from CLI-prompt to event-driven
5095
+ * confirmation at runtime (e.g. when `--goal` forces TUI mode after
5096
+ * the agent was already constructed).
5097
+ */
4999
5098
  promptDelegate;
5099
+ /** Pre-compiled wildcard patterns — rebuilt on reload for O(1) lookup. */
5100
+ wildcardEntries = [];
5000
5101
  constructor(opts) {
5001
5102
  this.trustFile = opts.trustFile;
5002
5103
  this.yolo = opts.yolo ?? false;
5003
5104
  this.promptDelegate = opts.promptDelegate;
5004
5105
  }
5106
+ /**
5107
+ * Replace (or clear) the interactive prompt delegate at runtime.
5108
+ * Used by the CLI to switch from inline prompts (REPL) to event-driven
5109
+ * confirmation (TUI) when the run mode is determined after the policy
5110
+ * was constructed (e.g. `--goal` auto-flipping to TUI).
5111
+ */
5112
+ setPromptDelegate(delegate) {
5113
+ this.promptDelegate = delegate;
5114
+ }
5005
5115
  async reload() {
5006
5116
  try {
5007
5117
  const raw = await fsp2.readFile(this.trustFile, "utf8");
@@ -5010,13 +5120,26 @@ var DefaultPermissionPolicy = class {
5010
5120
  } catch {
5011
5121
  this.policy = {};
5012
5122
  }
5123
+ this.wildcardEntries = [];
5124
+ for (const [key, val] of Object.entries(this.policy)) {
5125
+ if (key.includes("*")) this.wildcardEntries.push({ pattern: key, value: val });
5126
+ }
5127
+ this.sessionDenied.clear();
5128
+ this.sessionAllowed.clear();
5013
5129
  this.loaded = true;
5014
5130
  }
5015
- async evaluate(tool, input, _ctx) {
5131
+ async evaluate(tool, input, ctx) {
5016
5132
  if (!this.loaded) await this.reload();
5017
5133
  const namespaceEntry = this.findNamespaceEntry(tool.name);
5018
5134
  const entry = this.policy[tool.name] ?? namespaceEntry;
5019
5135
  const subject = this.subjectFor(tool.name, input, tool.subjectKey);
5136
+ const subjectKey = `${tool.name}::${subject ?? tool.name}`;
5137
+ if (this.sessionDenied.has(subjectKey)) {
5138
+ return { permission: "deny", source: "deny", reason: "session soft deny (user pressed no)" };
5139
+ }
5140
+ if (this.sessionAllowed.has(subjectKey)) {
5141
+ return { permission: "auto", source: "trust", reason: "session soft allow (user pressed yes)" };
5142
+ }
5020
5143
  if (entry?.deny && subject && matchAny(entry.deny, subject)) {
5021
5144
  return { permission: "deny", source: "deny", reason: "matched deny pattern" };
5022
5145
  }
@@ -5032,6 +5155,11 @@ var DefaultPermissionPolicy = class {
5032
5155
  if (this.yolo) {
5033
5156
  return { permission: "auto", source: "yolo" };
5034
5157
  }
5158
+ if (tool.name === "write" && subject) {
5159
+ if (ctx.hasRead(subject)) {
5160
+ return { permission: "auto", source: "context", reason: "file already read in this session" };
5161
+ }
5162
+ }
5035
5163
  if (tool.permission === "auto") {
5036
5164
  return { permission: "auto", source: "default" };
5037
5165
  }
@@ -5042,6 +5170,7 @@ var DefaultPermissionPolicy = class {
5042
5170
  return { permission: "auto", source: "user", reason: "user always-allowed" };
5043
5171
  }
5044
5172
  if (decision === "deny") {
5173
+ await this.deny({ tool: tool.name, pattern: subject ?? tool.name });
5045
5174
  return { permission: "deny", source: "user", reason: "user denied" };
5046
5175
  }
5047
5176
  return { permission: decision === "yes" ? "auto" : "deny", source: "user" };
@@ -5064,6 +5193,31 @@ var DefaultPermissionPolicy = class {
5064
5193
  throw err;
5065
5194
  }
5066
5195
  }
5196
+ /** Persist a deny rule — this tool+pattern pair is permanently blocked. */
5197
+ async deny(rule) {
5198
+ if (!this.loaded) await this.reload();
5199
+ const entry = this.policy[rule.tool] ?? {};
5200
+ entry.deny = Array.from(/* @__PURE__ */ new Set([...entry.deny ?? [], rule.pattern]));
5201
+ this.policy[rule.tool] = entry;
5202
+ try {
5203
+ await atomicWrite(this.trustFile, JSON.stringify(this.policy, null, 2));
5204
+ } catch (err) {
5205
+ const existing = this.policy[rule.tool];
5206
+ if (existing?.deny) {
5207
+ const idx = existing.deny.indexOf(rule.pattern);
5208
+ if (idx !== -1) existing.deny.splice(idx, 1);
5209
+ }
5210
+ throw err;
5211
+ }
5212
+ }
5213
+ /** Block this tool+pattern for the rest of this session (no trust file). */
5214
+ denyOnce(rule) {
5215
+ this.sessionDenied.set(`${rule.tool}::${rule.pattern}`, true);
5216
+ }
5217
+ /** Auto-approve this tool+pattern for the rest of this session (no trust file). */
5218
+ allowOnce(rule) {
5219
+ this.sessionAllowed.set(`${rule.tool}::${rule.pattern}`, true);
5220
+ }
5067
5221
  subjectFor(toolName, input, subjectKey) {
5068
5222
  if (!input || typeof input !== "object") return void 0;
5069
5223
  const obj = input;
@@ -5091,10 +5245,8 @@ var DefaultPermissionPolicy = class {
5091
5245
  return void 0;
5092
5246
  }
5093
5247
  findNamespaceEntry(toolName) {
5094
- for (const key of Object.keys(this.policy)) {
5095
- if (key.includes("*") && matchGlob(key, toolName)) {
5096
- return this.policy[key];
5097
- }
5248
+ for (const { pattern, value } of this.wildcardEntries) {
5249
+ if (matchGlob(pattern, toolName)) return value;
5098
5250
  }
5099
5251
  return void 0;
5100
5252
  }
@@ -5108,6 +5260,12 @@ var AutoApprovePermissionPolicy = class {
5108
5260
  }
5109
5261
  async trust() {
5110
5262
  }
5263
+ async deny() {
5264
+ }
5265
+ denyOnce() {
5266
+ }
5267
+ allowOnce() {
5268
+ }
5111
5269
  async reload() {
5112
5270
  }
5113
5271
  };
@@ -6224,7 +6382,15 @@ var AutoCompactionMiddleware = class {
6224
6382
  var DoneConditionChecker = class {
6225
6383
  constructor(condition) {
6226
6384
  this.condition = condition;
6227
- this.compiledRegex = condition.type === "output_match" && condition.pattern ? new RegExp(condition.pattern) : null;
6385
+ if (condition.type === "output_match" && condition.pattern) {
6386
+ const result = compileUserRegex(condition.pattern, "");
6387
+ this.compiledRegex = result.ok ? result.regex : null;
6388
+ if (!result.ok) {
6389
+ console.warn(`[DoneConditionChecker] Invalid regex pattern "${condition.pattern}": ${result.reason}`);
6390
+ }
6391
+ } else {
6392
+ this.compiledRegex = null;
6393
+ }
6228
6394
  }
6229
6395
  condition;
6230
6396
  compiledRegex;
@@ -6842,8 +7008,8 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
6842
7008
  // --- internal dispatching ---------------------------------------------
6843
7009
  tryDispatchNext() {
6844
7010
  while (this.canDispatch()) {
6845
- const subagentId = this.findIdleSubagent();
6846
- if (!subagentId) {
7011
+ const dispatchable = this.takeNextDispatchableTask();
7012
+ if (!dispatchable) {
6847
7013
  if (this.pendingTasks.length > 0 && !this.hasLiveSubagent()) {
6848
7014
  this.drainPendingAsAborted(
6849
7015
  "No live subagent available \u2014 all stopped or mid-termination"
@@ -6851,8 +7017,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
6851
7017
  }
6852
7018
  return;
6853
7019
  }
6854
- const task = this.pendingTasks.shift();
6855
- if (!task) return;
7020
+ const { subagentId, task } = dispatchable;
6856
7021
  this.runDispatched(subagentId, task).catch((err) => {
6857
7022
  this.recordCompletion({
6858
7023
  subagentId,
@@ -6870,12 +7035,26 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
6870
7035
  const max = this.config.maxConcurrent ?? 4;
6871
7036
  return this.inFlight < max && this.pendingTasks.length > 0;
6872
7037
  }
7038
+ takeNextDispatchableTask() {
7039
+ for (let i = 0; i < this.pendingTasks.length; i++) {
7040
+ const task = this.pendingTasks[i];
7041
+ const subagentId = task.subagentId ? this.isIdleSubagent(task.subagentId) ? task.subagentId : null : this.findIdleSubagent();
7042
+ if (!subagentId) continue;
7043
+ this.pendingTasks.splice(i, 1);
7044
+ return { subagentId, task };
7045
+ }
7046
+ return null;
7047
+ }
6873
7048
  findIdleSubagent() {
6874
7049
  for (const [id, s] of this.subagents) {
6875
7050
  if (s.status === "idle" && !this.terminating.has(id)) return id;
6876
7051
  }
6877
7052
  return null;
6878
7053
  }
7054
+ isIdleSubagent(id) {
7055
+ const subagent = this.subagents.get(id);
7056
+ return !!subagent && subagent.status === "idle" && !this.terminating.has(id);
7057
+ }
6879
7058
  /**
6880
7059
  * Returns true iff at least one spawned subagent could still
6881
7060
  * process a task. A "live" subagent is one that is not stopped
@@ -7060,10 +7239,11 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
7060
7239
  };
7061
7240
  function classifySubagentError(err, hints = {}) {
7062
7241
  const cause = err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : void 0;
7063
- const baseMessage = err instanceof Error ? err.message : String(err);
7064
7242
  if (err instanceof ProviderError) {
7065
- return providerErrorToSubagentError(err, baseMessage, cause);
7243
+ const baseMessage2 = err.describe();
7244
+ return providerErrorToSubagentError(err, baseMessage2, cause);
7066
7245
  }
7246
+ const baseMessage = err instanceof Error ? err.message : String(err);
7067
7247
  if (err instanceof BudgetExceededError) {
7068
7248
  const map = {
7069
7249
  iterations: "budget_iterations",
@@ -7180,7 +7360,7 @@ function makeSpawnTool(director, roster) {
7180
7360
  if (role && !base) {
7181
7361
  return { error: `unknown role "${role}". roster has: ${roster ? Object.keys(roster).join(", ") : "(empty)"}` };
7182
7362
  }
7183
- const cfg = { ...base ?? { name: i.name ?? "subagent" } };
7363
+ const cfg = base ? instantiateRosterConfig(role, base) : { name: i.name ?? "subagent" };
7184
7364
  if (typeof i.name === "string") cfg.name = i.name;
7185
7365
  if (typeof i.provider === "string") cfg.provider = i.provider;
7186
7366
  if (typeof i.model === "string") cfg.model = i.model;
@@ -7200,6 +7380,14 @@ function makeSpawnTool(director, roster) {
7200
7380
  }
7201
7381
  };
7202
7382
  }
7383
+ function instantiateRosterConfig(role, base) {
7384
+ return {
7385
+ ...base,
7386
+ // Roster entries are templates. A director may spawn several
7387
+ // workers with the same role, so never reuse the template id.
7388
+ id: `${role}-${randomUUID().slice(0, 8)}`
7389
+ };
7390
+ }
7203
7391
  function makeAssignTool(director) {
7204
7392
  const inputSchema = {
7205
7393
  type: "object",
@@ -7517,8 +7705,8 @@ var Director = class {
7517
7705
  if (this.spawnCount >= this.maxSpawns) {
7518
7706
  throw new DirectorBudgetError("max_spawns", this.maxSpawns, this.spawnCount + 1);
7519
7707
  }
7520
- this.spawnCount += 1;
7521
7708
  const result = await this.coordinator.spawn(config);
7709
+ this.spawnCount += 1;
7522
7710
  this.subagentMeta.set(result.subagentId, {
7523
7711
  provider: config.provider,
7524
7712
  model: config.model
@@ -7961,7 +8149,7 @@ function createDelegateTool(opts) {
7961
8149
  error: `Unknown role "${i.role}". Available: ${rosterIds.join(", ") || "(no roster configured)"}.`
7962
8150
  };
7963
8151
  }
7964
- cfg = { ...base };
8152
+ cfg = instantiateRosterConfig2(i.role, base);
7965
8153
  if (i.systemPromptOverride) cfg.systemPromptOverride = i.systemPromptOverride;
7966
8154
  if (i.provider) cfg.provider = i.provider;
7967
8155
  if (i.model) cfg.model = i.model;
@@ -8047,6 +8235,14 @@ function createDelegateTool(opts) {
8047
8235
  }
8048
8236
  };
8049
8237
  }
8238
+ function instantiateRosterConfig2(role, base) {
8239
+ return {
8240
+ ...base,
8241
+ // Roster entries are templates. Give each spawn a fresh id so
8242
+ // parallel or repeated delegates can use the same role safely.
8243
+ id: `${role}-${randomUUID().slice(0, 8)}`
8244
+ };
8245
+ }
8050
8246
  function hintForKind(kind, retryable, backoffMs) {
8051
8247
  if (!kind) return void 0;
8052
8248
  switch (kind) {
@@ -10616,12 +10812,14 @@ function requestLimitExtension(opts) {
10616
10812
  const { events, currentIterations, currentLimit, autoExtend, timeoutMs = 3e4 } = opts;
10617
10813
  return new Promise((resolve4) => {
10618
10814
  let resolved = false;
10619
- const timer = setTimeout(() => {
10815
+ const timerFired = () => {
10620
10816
  if (!resolved) {
10621
10817
  resolved = true;
10622
10818
  resolve4(0);
10623
10819
  }
10624
- }, timeoutMs);
10820
+ };
10821
+ const timer = setTimeout(timerFired, timeoutMs);
10822
+ timer.unref();
10625
10823
  const deny = () => {
10626
10824
  if (!resolved) {
10627
10825
  resolved = true;
@@ -10733,6 +10931,25 @@ var Agent = class {
10733
10931
  get renderer() {
10734
10932
  return this.container.has(TOKENS.Renderer) ? this.container.resolve(TOKENS.Renderer) : void 0;
10735
10933
  }
10934
+ /**
10935
+ * Switch from inline CLI prompts to event-driven confirmation.
10936
+ * Clears both the ToolExecutor's confirmAwaiter (so it returns
10937
+ * `ToolConfirmPendingResult`) and the permission policy's
10938
+ * promptDelegate (so `evaluate()` returns `confirm`).
10939
+ *
10940
+ * Call this before entering TUI or any mode where stdin is owned
10941
+ * by a UI framework (Ink, WebUI WS bridge). Without this, the
10942
+ * inline prompt writes to stdout and blocks on stdin — both of
10943
+ * which are owned by the framework — making the prompt invisible
10944
+ * and the input deadlocked.
10945
+ */
10946
+ disableInteractiveConfirmation() {
10947
+ this.toolExecutor.clearConfirmAwaiter();
10948
+ const policy = this.permission;
10949
+ if (typeof policy.setPromptDelegate === "function") {
10950
+ policy.setPromptDelegate(void 0);
10951
+ }
10952
+ }
10736
10953
  register(tool) {
10737
10954
  this.tools.register(tool);
10738
10955
  }
@@ -10740,6 +10957,22 @@ var Agent = class {
10740
10957
  await plugin.setup(api);
10741
10958
  this.plugins.push({ plugin, api });
10742
10959
  }
10960
+ /** Tear down all plugins in reverse order, calling their teardown hooks. */
10961
+ async teardown() {
10962
+ const errors = [];
10963
+ for (const { plugin, api } of this.plugins.toReversed()) {
10964
+ if (typeof plugin.teardown !== "function") continue;
10965
+ try {
10966
+ await plugin.teardown(api);
10967
+ } catch (err) {
10968
+ errors.push(err);
10969
+ }
10970
+ }
10971
+ this.plugins.length = 0;
10972
+ if (errors.length > 0) {
10973
+ throw new Error(`Agent teardown failed: ${errors.map(String).join("; ")}`);
10974
+ }
10975
+ }
10743
10976
  async run(userInput, opts = {}) {
10744
10977
  const controller = new RunController({ parentSignal: opts.signal });
10745
10978
  const signal = controller.signal;
@@ -11007,11 +11240,52 @@ var Agent = class {
11007
11240
  toolUseId: result.toolUseId,
11008
11241
  suggestedPattern: result.suggestedPattern
11009
11242
  });
11010
- const reRunResult = await this.executeSingleWithDecision(
11243
+ if (decision === "always") {
11244
+ try {
11245
+ await this.permission.trust({
11246
+ tool: tool.name,
11247
+ pattern: result.suggestedPattern
11248
+ });
11249
+ this.events.emit("trust.persisted", {
11250
+ tool: tool.name,
11251
+ pattern: result.suggestedPattern,
11252
+ decision
11253
+ });
11254
+ } catch {
11255
+ }
11256
+ } else if (decision === "deny") {
11257
+ try {
11258
+ await this.permission.deny({
11259
+ tool: tool.name,
11260
+ pattern: result.suggestedPattern
11261
+ });
11262
+ this.events.emit("trust.persisted", {
11263
+ tool: tool.name,
11264
+ pattern: result.suggestedPattern,
11265
+ decision
11266
+ });
11267
+ } catch {
11268
+ }
11269
+ }
11270
+ if (decision === "yes") {
11271
+ const p = this.permission;
11272
+ p.allowOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
11273
+ } else if (decision === "no") {
11274
+ const p = this.permission;
11275
+ p.denyOnce?.({ tool: tool.name, pattern: result.suggestedPattern });
11276
+ }
11277
+ const reRunResult = decision === "yes" || decision === "always" ? await this.executeSingleWithDecision(
11011
11278
  tool,
11012
- { id: result.toolUseId, name: tool.name, input: result.input },
11013
- decision
11014
- );
11279
+ { id: result.toolUseId, name: tool.name, input: result.input }
11280
+ ) : {
11281
+ result: {
11282
+ type: "tool_result",
11283
+ tool_use_id: result.toolUseId,
11284
+ content: decision === "deny" ? `Tool "${tool.name}" denied and blocked for this pattern.` : `Tool "${tool.name}" denied by user.`,
11285
+ is_error: true
11286
+ },
11287
+ durationMs: 0
11288
+ };
11015
11289
  const use2 = useById.get(reRunResult.result.tool_use_id);
11016
11290
  if (use2) {
11017
11291
  await this.pipelines.toolCall.run({
@@ -11091,19 +11365,8 @@ var Agent = class {
11091
11365
  });
11092
11366
  });
11093
11367
  }
11094
- async executeSingleWithDecision(tool, use, decision) {
11368
+ async executeSingleWithDecision(tool, use) {
11095
11369
  const start = Date.now();
11096
- if (decision === "no" || decision === "deny") {
11097
- return {
11098
- result: {
11099
- type: "tool_result",
11100
- tool_use_id: use.id,
11101
- content: `Tool "${tool.name}" denied by user.`,
11102
- is_error: true
11103
- },
11104
- durationMs: Date.now() - start
11105
- };
11106
- }
11107
11370
  try {
11108
11371
  const result = await this.toolExecutor.executeTool(
11109
11372
  tool,
@@ -11291,9 +11554,10 @@ var Context = class {
11291
11554
  }
11292
11555
  /**
11293
11556
  * Register a teardown hook tied to the current run's abort signal. The
11294
- * hook fires when the run aborts OR ends normally — Agent.run wires
11295
- * this through a RunController. When no run is active the hook fires
11296
- * immediately so callers don't leak resources.
11557
+ * hook fires when the run aborts or ends normally — Agent.run wires this
11558
+ * through a RunController. Hooks registered before a run starts are stored
11559
+ * and fired when the next run ends; there is no "immediate fire" when no
11560
+ * run is active.
11297
11561
  *
11298
11562
  * **Scope:** these hooks fire on the **whole agent run's** abort, not on
11299
11563
  * an individual tool call. For per-tool teardown of resources owned by
@@ -11463,6 +11727,7 @@ You operate inside the user's terminal with direct read and write access to thei
11463
11727
  - **Task is clear, approach is unknown** \u2192 try one approach, report what happened
11464
11728
  - **Tool fails** \u2192 retry once with adjusted params, then report failure
11465
11729
  - **Permission prompt shown** \u2192 wait for user, do not act unilaterally
11730
+ - **Tool denied by user** \u2192 do NOT retry the same tool in the next iteration. If the user denies a write, bash, or any tool, respect that decision. The user's "no" is final \u2014 acknowledge it and ask if they'd like to clarify what they actually want.
11466
11731
  - **Context window filling up** \u2192 use context_manager proactively; don't wait to be told
11467
11732
 
11468
11733
  ## How you work
@@ -11471,6 +11736,8 @@ You operate inside the user's terminal with direct read and write access to thei
11471
11736
  - **Comment with purpose.** Add comments only when they explain why, not what. The code already says what.
11472
11737
  - **Own your output.** Never call work "production-ready" or "fully tested" \u2014 the user makes that call.
11473
11738
  - **Move on from mistakes.** When something fails, report what happened and what you'll do next. No apologies, no hand-wringing.
11739
+ - **Respect denied tools.** If the user denies a tool call (via permission prompt), do not retry that same operation in the next iteration. The user's "no" means "find another way or ask". Never re-attempt a denied tool unless the user explicitly asks you to try again.
11740
+ - **When denied, ask.** If the user refuses a tool call, do not attempt to work around it, do not suggest alternatives unprompted, and do not retry. Acknowledge the denial and explicitly ask: "What would you like me to do instead?"
11474
11741
  - **Stay in your lane.** Don't lecture about software engineering principles unless explicitly asked \u2014 the user is the expert on their codebase.`;
11475
11742
  var DefaultSystemPromptBuilder = class {
11476
11743
  constructor(opts = {}) {
@@ -12535,6 +12802,6 @@ function wrapApiForCapabilityCheck(plugin, api, log, enforce = false) {
12535
12802
  });
12536
12803
  }
12537
12804
 
12538
- export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorBudgetError, DirectorStateCheckpoint, DoneConditionChecker, EventBus, ExtensionRegistry, FLEET_ROSTER, FleetBus, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, RunController, SECURITY_SCANNER_AGENT, SelectiveCompactor, SessionAnalyzer, SessionError, SlashCommandRegistry, SpecDrivenDev, SpecParser, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskTracker, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMessage, createToolOutputSerializer, decryptConfigSecrets, detectNewlineStyle, emptyPlan, encryptConfigSecrets, ensureDir, estimateTextTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatPlan, formatTodosList, getContextWindowMode, githubServer, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, loadDirectorState, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, projectHash, removePlanItem, renderPrometheus, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, savePlan, saveTodosCheckpoint, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
12805
+ export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, Agent, AgentError, AutoApprovePermissionPolicy, AutoCompactionMiddleware, AutonomousRunner, BUG_HUNTER_AGENT, BudgetExceededError, CONTEXT_WINDOW_MODES, ConfigError, ConfigMigrationError, Container, Context, ConversationState, DEFAULT_CONFIG_MIGRATIONS, DEFAULT_CONTEXT_WINDOW_MODE_ID, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_MAX_ITERATIONS, DEFAULT_MODES, DEFAULT_RECOVERY_STRATEGIES, DEFAULT_SPEC_TEMPLATE, DEFAULT_SUBAGENT_BASELINE, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultErrorHandler, DefaultHealthRegistry, DefaultLogger, DefaultMemoryStore, DefaultModeStore, DefaultModelsRegistry, DefaultMultiAgentCoordinator, DefaultPathResolver, DefaultPermissionPolicy, DefaultPluginAPI, DefaultProviderRunner, DefaultRetryPolicy, DefaultSecretScrubber, DefaultSecretVault, DefaultSessionReader, DefaultSessionStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultTaskStore, DefaultTokenCounter, Director, DirectorBudgetError, DirectorStateCheckpoint, DoneConditionChecker, EventBus, ExtensionRegistry, FLEET_ROSTER, FleetBus, FleetUsageAggregator, HybridCompactor, InMemoryAgentBridge, InMemoryBridgeTransport, InMemoryMetricsSink, InputBuilder, IntelligentCompactor, KERNEL_API_VERSION, LAYER_1_IDENTITY, LLMSelector, NoopMetricsSink, NoopTracer, OTelTracer, PROMETHEUS_CONTENT_TYPE, Pipeline, PluginError, ProviderError, ProviderRegistry, QueueStore, REFACTOR_PLANNER_AGENT, RecoveryLock, RunController, SECURITY_SCANNER_AGENT, SelectiveCompactor, SessionAnalyzer, SessionError, SlashCommandRegistry, SpecDrivenDev, SpecParser, SubagentBudget, TOKENS, TaskFlow, TaskGenerator, TaskTracker, ToolError, ToolExecutor, ToolRegistry, WrongStackError, addPlanItem, allServers, asBlocks, asText, atomicWrite, attachPlanCheckpoint, attachTodosCheckpoint, awsServer, blockServer, braveSearchServer, buildChildEnv, buildOtlpMetricsRequest, buildOtlpTracesRequest, buildRecoveryStrategies, classifyFamily, clearPlan, color, compileGlob, compileUserRegex, composeDirectorPrompt, composeSubagentPrompt, computeTaskProgress, context7Server, contextManagerTool, createContextManagerTool, createDefaultPipelines, createDelegateTool, createMessage, createToolOutputSerializer, decryptConfigSecrets, detectNewlineStyle, emptyPlan, encryptConfigSecrets, ensureDir, estimateTextTokens, estimateToolInputTokens, estimateToolResultTokens, everArtServer, extractRunEnv, filesystemServer, findCriticalPath, formatContextWindowModeList, formatPlan, formatTodosList, getContextWindowMode, githubServer, googleMapsServer, isAgentError, isConfigError, isContextWindowModeId, isImageBlock, isPluginError, isSessionError, isTextBlock, isThinkingBlock, isToolError, isToolResultBlock, isToolUseBlock, isWrongStackError, listContextWindowModes, loadDirectorState, loadPlan, loadPlugins, loadProjectModes, loadTodosCheckpoint, loadUserModes, makeAgentSubagentRunner, makeDirectorSessionFactory, matchAny, matchGlob, migratePlaintextSecrets, miniMaxVisionServer, normalizeToLf, projectHash, removePlanItem, renderPrometheus, repairToolUseAdjacency, resolveContextWindowPolicy, resolveWstackPaths, rewriteConfigEncrypted, rosterSummaryFromConfigs, runConfigMigrations, safeParse, safeStringify, sanitizeJsonString, savePlan, saveTodosCheckpoint, sentinelServer, setPlanItemStatus, slackServer, startMetricsServer, startOtlpMetricsExporter, startOtlpTraceExporter, stripAnsi, toStyle, toWrongStackError, topologicalSort, unifiedDiff, unloadPlugins, validateAgainstSchema, wireMetricsToEvents, wrapAsState, zaiVisionServer };
12539
12806
  //# sourceMappingURL=index.js.map
12540
12807
  //# sourceMappingURL=index.js.map