@wrongstack/core 0.9.7 → 0.9.20

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 (67) hide show
  1. package/dist/{agent-bridge-DMVOX0cF.d.ts → agent-bridge-Dti3KXGk.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DaF_EgRG.d.ts → agent-subagent-runner-U-rs7kk7.d.ts} +3 -4
  3. package/dist/compactor-D7X96RLZ.d.ts +41 -0
  4. package/dist/{config-SkMIDN9L.d.ts → config-CLXMDOSs.d.ts} +5 -1
  5. package/dist/{context-BRNbHmRM.d.ts → context-zkZeILpr.d.ts} +46 -0
  6. package/dist/coordination/index.d.ts +14 -13
  7. package/dist/coordination/index.js +1263 -123
  8. package/dist/coordination/index.js.map +1 -1
  9. package/dist/defaults/index.d.ts +22 -21
  10. package/dist/defaults/index.js +1477 -152
  11. package/dist/defaults/index.js.map +1 -1
  12. package/dist/{events-CiG9qUM_.d.ts → events-DH-9r-_C.d.ts} +42 -1
  13. package/dist/execution/index.d.ts +42 -30
  14. package/dist/execution/index.js +359 -112
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/extension/index.d.ts +7 -6
  17. package/dist/extension/index.js.map +1 -1
  18. package/dist/{index-CP8638Wm.d.ts → index-BIHJ4uII.d.ts} +12 -8
  19. package/dist/{index-Bsha5K4D.d.ts → index-CFO9QmJo.d.ts} +9 -8
  20. package/dist/index.d.ts +319 -37
  21. package/dist/index.js +2571 -324
  22. package/dist/index.js.map +1 -1
  23. package/dist/infrastructure/index.d.ts +6 -6
  24. package/dist/infrastructure/index.js +36 -0
  25. package/dist/infrastructure/index.js.map +1 -1
  26. package/dist/kernel/index.d.ts +10 -9
  27. package/dist/kernel/index.js.map +1 -1
  28. package/dist/{mcp-servers-BouUWYW6.d.ts → mcp-servers-DkESgh0G.d.ts} +25 -3
  29. package/dist/models/index.d.ts +2 -2
  30. package/dist/models/index.js +1 -1
  31. package/dist/models/index.js.map +1 -1
  32. package/dist/{multi-agent-3ZnTB1aT.d.ts → multi-agent-DNp6lAzg.d.ts} +35 -23
  33. package/dist/{multi-agent-coordinator-DTXF2aAl.d.ts → multi-agent-coordinator-CAhsegPz.d.ts} +20 -2
  34. package/dist/{null-fleet-bus-Chrc_3Pp.d.ts → null-fleet-bus-Dnl19vmf.d.ts} +496 -15
  35. package/dist/observability/index.d.ts +2 -2
  36. package/dist/{path-resolver-TcJfc29Y.d.ts → path-resolver-CHiBL0DD.d.ts} +2 -2
  37. package/dist/{secret-scrubber-DttNiGYA.d.ts → permission-H35s9Evv.d.ts} +2 -7
  38. package/dist/{permission-policy-BpCGYBud.d.ts → permission-policy-CT-nRmTn.d.ts} +9 -2
  39. package/dist/{plan-templates-envSmNlZ.d.ts → plan-templates-Bs8iRwi6.d.ts} +28 -4
  40. package/dist/{provider-runner-BEpikbbN.d.ts → provider-runner-BZdDrWrS.d.ts} +3 -3
  41. package/dist/{retry-policy-BYkq0ugs.d.ts → retry-policy-J9N_PM40.d.ts} +1 -1
  42. package/dist/sdd/index.d.ts +10 -10
  43. package/dist/sdd/index.js +225 -68
  44. package/dist/sdd/index.js.map +1 -1
  45. package/dist/secret-scrubber-3MHDDAtm.d.ts +6 -0
  46. package/dist/{secret-scrubber-QSeI0ADi.d.ts → secret-scrubber-7rSC_emZ.d.ts} +1 -1
  47. package/dist/security/index.d.ts +5 -4
  48. package/dist/security/index.js +37 -6
  49. package/dist/security/index.js.map +1 -1
  50. package/dist/{selector-pox8abg0.d.ts → selector-CFTh3Z6p.d.ts} +1 -1
  51. package/dist/{session-reader-CSWcb5Ga.d.ts → session-reader-C7JJlxOw.d.ts} +2 -2
  52. package/dist/skills/index.d.ts +1 -1
  53. package/dist/skills/index.js +1 -1
  54. package/dist/skills/index.js.map +1 -1
  55. package/dist/storage/index.d.ts +391 -6
  56. package/dist/storage/index.js +760 -40
  57. package/dist/storage/index.js.map +1 -1
  58. package/dist/{system-prompt-Bs-Wliab.d.ts → system-prompt-CneIxVip.d.ts} +1 -1
  59. package/dist/{tool-executor-CsktM3h9.d.ts → tool-executor-flTuxsqO.d.ts} +9 -4
  60. package/dist/types/index.d.ts +16 -15
  61. package/dist/types/index.js +60 -0
  62. package/dist/types/index.js.map +1 -1
  63. package/dist/utils/index.d.ts +25 -2
  64. package/dist/utils/index.js +90 -1
  65. package/dist/utils/index.js.map +1 -1
  66. package/package.json +1 -1
  67. package/dist/compactor-DVTKL7XD.d.ts +0 -23
@@ -1,6 +1,6 @@
1
- import { Q as Tool, d as Context } from './context-BRNbHmRM.js';
1
+ import { Q as Tool, d as Context } from './context-zkZeILpr.js';
2
2
  import { I as InputReader } from './input-reader-E-ffP2ee.js';
3
- import { a as PermissionPolicy, P as PermissionDecision } from './secret-scrubber-DttNiGYA.js';
3
+ import { a as PermissionPolicy, P as PermissionDecision } from './permission-H35s9Evv.js';
4
4
 
5
5
  interface PermissionPolicyOptions {
6
6
  trustFile: string;
@@ -109,6 +109,13 @@ declare class AutoApprovePermissionPolicy implements PermissionPolicy {
109
109
  * level so the leader must explicitly allow them per-spawn.
110
110
  */
111
111
  private static readonly DENY;
112
+ /**
113
+ * Tools from MCP servers (`mcp__<server>__<tool>`) are external code of
114
+ * unknown capability — they may wrap a shell or filesystem. They are
115
+ * fail-closed here: not auto-approved for subagents by default, so the
116
+ * leader must allow them explicitly per-spawn.
117
+ */
118
+ private static isMcpTool;
112
119
  evaluate(tool: Tool): Promise<PermissionDecision>;
113
120
  trust(): Promise<void>;
114
121
  deny(): Promise<void>;
@@ -1,19 +1,30 @@
1
- import { E as EventBus } from './events-CiG9qUM_.js';
2
- import { y as SessionStore, x as SessionMetadata, B as SessionWriter, r as ResumedSession, S as SessionData, z as SessionSummary, c as ContentBlock, w as SessionEvent, N as TodoItem, f as ConversationState } from './context-BRNbHmRM.js';
3
- import { e as AttachmentStore, A as AddAttachmentInput, d as AttachmentRef, a as Attachment } from './session-reader-CSWcb5Ga.js';
1
+ import { E as EventBus } from './events-DH-9r-_C.js';
2
+ import { S as SecretScrubber } from './secret-scrubber-3MHDDAtm.js';
3
+ import { y as SessionStore, x as SessionMetadata, B as SessionWriter, r as ResumedSession, S as SessionData, z as SessionSummary, c as ContentBlock, w as SessionEvent, N as TodoItem, f as ConversationState } from './context-zkZeILpr.js';
4
+ import { e as AttachmentStore, A as AddAttachmentInput, d as AttachmentRef, a as Attachment } from './session-reader-C7JJlxOw.js';
4
5
  import { b as MemoryStore, a as MemoryScope } from './memory-CEXuo7sz.js';
5
6
  import { a as WstackPaths } from './wstack-paths-BCgmTNlG.js';
6
- import { c as ConfigStore, a as Config, b as ConfigLoader } from './config-SkMIDN9L.js';
7
+ import { c as ConfigStore, a as Config, b as ConfigLoader, m as SyncConfig } from './config-CLXMDOSs.js';
7
8
  import { S as SecretVault } from './secret-vault-DoISxaKO.js';
8
9
 
9
10
  interface SessionStoreOptions {
10
11
  dir: string;
11
12
  /** Optional EventBus for emitting session diagnostics. */
12
13
  events?: EventBus;
14
+ /**
15
+ * Optional secret scrubber. When set, `user_input` and `llm_response` event
16
+ * content is scrubbed before being persisted to the JSONL log and the
17
+ * summary sidecar — so a secret a user pastes or the model echoes does not
18
+ * sit in cleartext on disk (and does not ride along in history cloud-sync).
19
+ * Tool output is already scrubbed upstream by the executor; this closes the
20
+ * conversation-turn gap (finding F-06).
21
+ */
22
+ secretScrubber?: SecretScrubber;
13
23
  }
14
24
  declare class DefaultSessionStore implements SessionStore {
15
25
  private readonly dir;
16
26
  private readonly events?;
27
+ private readonly secretScrubber?;
17
28
  constructor(opts: SessionStoreOptions);
18
29
  /** Join session ID to its absolute path within the store directory. */
19
30
  private sessionPath;
@@ -185,6 +196,19 @@ declare class DefaultConfigLoader implements ConfigLoader {
185
196
  cliFlags?: Partial<Config>;
186
197
  cwd?: string;
187
198
  }): Promise<Config>;
199
+ /**
200
+ * Persist a sync config to ~/.wrongstack/sync.json, with the token encrypted
201
+ * by the vault (if provided). The file is isolated from the main config
202
+ * hierarchy to prevent accidental commits.
203
+ */
204
+ persistSyncConfig(cfg: SyncConfig): Promise<void>;
205
+ /**
206
+ * Read ~/.wrongstack/sync.json (encrypted GitHub token storage) and decrypt
207
+ * the token if a vault is available. Returns null if the file doesn't exist.
208
+ * This is separate from main config loading because sync.json is intentionally
209
+ * isolated — it should never be part of project-local or env-driven config.
210
+ */
211
+ loadSyncConfig(): Promise<SyncConfig | null>;
188
212
  private readJson;
189
213
  private validateBehavior;
190
214
  private validateIdentity;
@@ -1,8 +1,8 @@
1
- import { E as EventBus } from './events-CiG9qUM_.js';
1
+ import { E as EventBus } from './events-DH-9r-_C.js';
2
2
  import { a as Logger } from './logger-DDd5C--Z.js';
3
3
  import { T as Tracer } from './observability-BhnVLBLS.js';
4
- import { m as Provider, p as Request, d as Context, q as Response } from './context-BRNbHmRM.js';
5
- import { a as RetryPolicy } from './retry-policy-BYkq0ugs.js';
4
+ import { m as Provider, p as Request, d as Context, q as Response } from './context-zkZeILpr.js';
5
+ import { a as RetryPolicy } from './retry-policy-J9N_PM40.js';
6
6
 
7
7
  /**
8
8
  * Options passed to a ProviderRunner when calling the provider.
@@ -1,4 +1,4 @@
1
- import { q as Response, d as Context, n as ProviderError } from './context-BRNbHmRM.js';
1
+ import { q as Response, d as Context, n as ProviderError } from './context-zkZeILpr.js';
2
2
 
3
3
  type RecoveryDecision = {
4
4
  /**
@@ -1,14 +1,15 @@
1
1
  import { h as Specification, S as SpecAnalysis, g as SpecValidationResult, l as TaskGraph, m as TaskNode, k as TaskFilter, p as TaskSort, o as TaskProgress, r as TaskType, n as TaskPriority, e as SpecStatus, f as SpecTemplate, b as SpecRequirement } from '../task-graph-D1YQbpxF.js';
2
- import { E as EventBus } from '../events-CiG9qUM_.js';
3
- import { D as DoneCondition, u as TaskResult } from '../multi-agent-3ZnTB1aT.js';
4
- import { c as Agent } from '../index-CP8638Wm.js';
5
- import { A as AgentFactory } from '../agent-subagent-runner-DaF_EgRG.js';
6
- import '../context-BRNbHmRM.js';
2
+ import { E as EventBus } from '../events-DH-9r-_C.js';
3
+ import { D as DoneCondition, u as TaskResult } from '../multi-agent-DNp6lAzg.js';
4
+ import { c as Agent } from '../index-BIHJ4uII.js';
5
+ import { A as AgentFactory } from '../agent-subagent-runner-U-rs7kk7.js';
6
+ import '../context-zkZeILpr.js';
7
7
  import '../logger-DDd5C--Z.js';
8
- import '../system-prompt-Bs-Wliab.js';
8
+ import '../system-prompt-CneIxVip.js';
9
9
  import '../observability-BhnVLBLS.js';
10
- import '../secret-scrubber-DttNiGYA.js';
11
- import '../config-SkMIDN9L.js';
10
+ import '../secret-scrubber-3MHDDAtm.js';
11
+ import '../permission-H35s9Evv.js';
12
+ import '../config-CLXMDOSs.js';
12
13
  import '../models-registry-BcYJDKLm.js';
13
14
 
14
15
  declare class SpecParser {
@@ -674,10 +675,9 @@ interface TaskBatch {
674
675
  }
675
676
  declare class SddTaskDecomposer {
676
677
  private readonly tracker;
677
- private readonly graph;
678
678
  private readonly slots;
679
679
  private wave;
680
- constructor(tracker: TaskTracker, graph: TaskGraph, opts?: SddTaskDecomposerOptions);
680
+ constructor(tracker: TaskTracker, _graph: TaskGraph, opts?: SddTaskDecomposerOptions);
681
681
  /**
682
682
  * Return the next batch of runnable tasks.
683
683
  * Returns `allDone: true` when every node is completed.
package/dist/sdd/index.js CHANGED
@@ -2058,7 +2058,7 @@ function analyzeCriticalPath(graph) {
2058
2058
  blockedTasks
2059
2059
  };
2060
2060
  }
2061
- function getTransitiveBlocked(graph, taskId, blocksMap) {
2061
+ function getTransitiveBlocked(_graph, taskId, blocksMap) {
2062
2062
  const visited = /* @__PURE__ */ new Set();
2063
2063
  const queue = [taskId];
2064
2064
  while (queue.length > 0) {
@@ -2074,7 +2074,7 @@ function getTransitiveBlocked(graph, taskId, blocksMap) {
2074
2074
  }
2075
2075
  return visited;
2076
2076
  }
2077
- function computeCriticalPath(graph, topoOrder, blockedByMap) {
2077
+ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
2078
2078
  const allIds = Array.from(graph.nodes.keys());
2079
2079
  if (allIds.length === 0) return [];
2080
2080
  const dist = /* @__PURE__ */ new Map();
@@ -2492,13 +2492,11 @@ function createAutoExecutor(opts) {
2492
2492
 
2493
2493
  // src/sdd/sdd-task-decomposer.ts
2494
2494
  var SddTaskDecomposer = class {
2495
- constructor(tracker, graph, opts = {}) {
2495
+ constructor(tracker, _graph, opts = {}) {
2496
2496
  this.tracker = tracker;
2497
- this.graph = graph;
2498
2497
  this.slots = Math.min(16, Math.max(1, opts.parallelSlots ?? 4));
2499
2498
  }
2500
2499
  tracker;
2501
- graph;
2502
2500
  slots;
2503
2501
  wave = 0;
2504
2502
  // -------------------------------------------------------------------
@@ -2618,17 +2616,7 @@ var SubagentBudget = class _SubagentBudget {
2618
2616
  startTime = null;
2619
2617
  _onThreshold;
2620
2618
  /**
2621
- * Tracks which budget kinds currently have an extension request
2622
- * in flight. While a kind is here, further `checkLimit` calls for the
2623
- * same kind are no-ops — without this dedup, every `recordIteration`
2624
- * after the limit is reached spawns a fresh decision Promise (until
2625
- * the first one lands and patches limits), flooding the FleetBus
2626
- * with redundant threshold events. Cleared in `negotiateExtension`'s
2627
- * `finally`.
2628
- */
2629
- pendingExtensions = /* @__PURE__ */ new Set();
2630
- /**
2631
- * Hard cap on how long `negotiateExtension` waits for the coordinator to
2619
+ * Hard cap on how long `_negotiateExtension` waits for the coordinator to
2632
2620
  * respond before defaulting to 'stop'. Without this fallback an absent
2633
2621
  * or hung listener (Director not built / event filter detached mid-run)
2634
2622
  * leaves the budget over-limit and never enforces anything.
@@ -2690,38 +2678,83 @@ var SubagentBudget = class _SubagentBudget {
2690
2678
  * - `mode === 'auto'` + no listener → throw `BudgetExceededError` (hard stop; no one to ask)
2691
2679
  * - `mode === 'auto'` + listener → throw `BudgetThresholdSignal` with async decision promise
2692
2680
  */
2693
- checkLimit(kind, used, limit) {
2681
+ /**
2682
+ * Collects all exceeded budget kinds into a single NOOP-free negotiation.
2683
+ * Called by recordIteration / recordToolCall / recordUsage — each may call
2684
+ * this for its own kind. The first call starts the negotiation and stores
2685
+ * the Promise in _pendingNegotiation. Subsequent calls for DIFFERENT
2686
+ * kinds (while a negotiation is in flight) are NOOPs — they don't start
2687
+ * new conversations with the coordinator. This prevents an EventBus flood
2688
+ * when multiple budget kinds are exceeded simultaneously in one iteration.
2689
+ *
2690
+ * Returns the kinds that were found to be exceeded (for logging/debugging).
2691
+ */
2692
+ checkLimits(elapsedMs) {
2693
+ const exceeded = [];
2694
+ if (this.limits.maxIterations !== void 0 && this.iterations > this.limits.maxIterations) {
2695
+ exceeded.push({ kind: "iterations", used: this.iterations, limit: this.limits.maxIterations });
2696
+ }
2697
+ if (this.limits.maxToolCalls !== void 0 && this.toolCalls > this.limits.maxToolCalls) {
2698
+ exceeded.push({ kind: "tool_calls", used: this.toolCalls, limit: this.limits.maxToolCalls });
2699
+ }
2700
+ const totalTokens = this.tokenInput + this.tokenOutput;
2701
+ if (this.limits.maxTokens !== void 0 && totalTokens > this.limits.maxTokens) {
2702
+ exceeded.push({ kind: "tokens", used: totalTokens, limit: this.limits.maxTokens });
2703
+ }
2704
+ if (this.limits.maxCostUsd !== void 0 && this.costUsd > this.limits.maxCostUsd) {
2705
+ exceeded.push({ kind: "cost", used: this.costUsd, limit: this.limits.maxCostUsd });
2706
+ }
2707
+ if (elapsedMs !== void 0 && this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs) {
2708
+ exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
2709
+ }
2710
+ if (exceeded.length === 0) return [];
2694
2711
  if (!this._onThreshold) {
2695
- throw new BudgetExceededError(kind, limit, used);
2712
+ const first2 = exceeded[0];
2713
+ throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
2696
2714
  }
2697
2715
  if (this._mode === "sync") {
2698
- throw new BudgetExceededError(kind, limit, used);
2716
+ const first2 = exceeded[0];
2717
+ throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
2699
2718
  }
2700
2719
  const bus = this._events;
2701
2720
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
2702
- throw new BudgetExceededError(kind, limit, used);
2721
+ const first2 = exceeded[0];
2722
+ throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
2703
2723
  }
2704
- if (this.pendingExtensions.has(kind)) return;
2705
- this.pendingExtensions.add(kind);
2706
- const decision = this.negotiateExtension(kind, used, limit);
2707
- throw new BudgetThresholdSignal(kind, limit, used, decision);
2724
+ for (const entry of exceeded) {
2725
+ if (this._pendingNegotiations.has(entry.kind)) continue;
2726
+ const decision2 = this._negotiateExtension(entry.kind, exceeded);
2727
+ this._pendingNegotiations.set(entry.kind, decision2);
2728
+ }
2729
+ const first = exceeded[0];
2730
+ const decision = this._pendingNegotiations.get(first.kind);
2731
+ throw new BudgetThresholdSignal(first.kind, first.limit, first.used, decision);
2708
2732
  }
2733
+ /**
2734
+ * Per-kind in-flight negotiation Promises. Each budget kind can have its
2735
+ * own concurrent negotiation — e.g. iterations and timeout can both
2736
+ * be exceeded simultaneously without blocking each other. The same kind
2737
+ * cannot start two concurrent negotiations (dedup guard).
2738
+ * Cleared in `_negotiateExtension`'s `finally` block.
2739
+ */
2740
+ _pendingNegotiations = /* @__PURE__ */ new Map();
2709
2741
  /**
2710
2742
  * Drive the threshold handler to a decision. Resolves with `'stop'`
2711
2743
  * (signal the runner to abort) or `{ extend: ... }` (limits already
2712
- * patched in-place; the runner should not abort). Always releases the
2713
- * `pendingExtensions` slot in `finally`.
2744
+ * patched in-place; the runner should not abort). Clears the
2745
+ * per-kind slot in `_pendingNegotiations` in `finally`.
2714
2746
  *
2715
2747
  * The 'continue' return from a sync handler is treated as
2716
2748
  * `{ extend: {} }` — keep going without patching; next overrun fires
2717
2749
  * a fresh signal.
2718
2750
  */
2719
- async negotiateExtension(kind, used, limit) {
2751
+ async _negotiateExtension(kind, exceeded) {
2720
2752
  try {
2753
+ const first = exceeded[0];
2721
2754
  const result = this._onThreshold({
2722
- kind,
2723
- used,
2724
- limit,
2755
+ kind: first.kind,
2756
+ used: first.used,
2757
+ limit: first.limit,
2725
2758
  requestDecision: () => {
2726
2759
  const bus = this._events;
2727
2760
  if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
@@ -2738,20 +2771,22 @@ var SubagentBudget = class _SubagentBudget {
2738
2771
  () => respond("stop"),
2739
2772
  _SubagentBudget.DECISION_TIMEOUT_MS
2740
2773
  );
2741
- bus.emit("budget.threshold_reached", {
2742
- kind,
2743
- used,
2744
- limit,
2745
- timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
2746
- extend: (extra) => {
2747
- clearTimeout(fallback);
2748
- respond({ extend: extra });
2749
- },
2750
- deny: () => {
2751
- clearTimeout(fallback);
2752
- respond("stop");
2753
- }
2754
- });
2774
+ for (const { kind: kind2, used, limit } of exceeded) {
2775
+ bus.emit("budget.threshold_reached", {
2776
+ kind: kind2,
2777
+ used,
2778
+ limit,
2779
+ timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
2780
+ extend: (extra) => {
2781
+ clearTimeout(fallback);
2782
+ respond({ extend: extra });
2783
+ },
2784
+ deny: () => {
2785
+ clearTimeout(fallback);
2786
+ respond("stop");
2787
+ }
2788
+ });
2789
+ }
2755
2790
  });
2756
2791
  }
2757
2792
  });
@@ -2777,47 +2812,39 @@ var SubagentBudget = class _SubagentBudget {
2777
2812
  }
2778
2813
  return decision;
2779
2814
  } finally {
2780
- this.pendingExtensions.delete(kind);
2815
+ this._pendingNegotiations.delete(kind);
2781
2816
  }
2782
2817
  }
2783
2818
  recordIteration() {
2784
2819
  this.iterations++;
2785
- if (this.limits.maxIterations !== void 0 && this.iterations > this.limits.maxIterations) {
2786
- void this.checkLimit("iterations", this.iterations, this.limits.maxIterations);
2787
- }
2820
+ void this.checkLimits();
2788
2821
  }
2789
2822
  recordToolCall() {
2790
2823
  this.toolCalls++;
2791
- if (this.limits.maxToolCalls !== void 0 && this.toolCalls > this.limits.maxToolCalls) {
2792
- void this.checkLimit("tool_calls", this.toolCalls, this.limits.maxToolCalls);
2793
- }
2824
+ void this.checkLimits();
2794
2825
  }
2795
2826
  recordUsage(usage, costUsd = 0) {
2796
2827
  this.tokenInput += usage.input;
2797
2828
  this.tokenOutput += usage.output;
2798
2829
  this.costUsd += costUsd;
2799
- const totalTokens = this.tokenInput + this.tokenOutput;
2800
- if (this.limits.maxTokens !== void 0 && totalTokens > this.limits.maxTokens) {
2801
- void this.checkLimit("tokens", totalTokens, this.limits.maxTokens);
2802
- }
2803
- if (this.limits.maxCostUsd !== void 0 && this.costUsd > this.limits.maxCostUsd) {
2804
- void this.checkLimit("cost", this.costUsd, this.limits.maxCostUsd);
2805
- }
2830
+ void this.checkLimits();
2806
2831
  }
2807
2832
  /**
2808
- * Wall-clock budget check. Unlike other limits, timeout check passes through
2809
- * `checkLimit` and is subject to the same negotiation-mode decision table.
2810
- * In practice, `'sync'` mode (the usual test configuration) means a timeout
2811
- * immediately throws `BudgetExceededError`. In production with a coordinator,
2812
- * a timeout emits `budget.threshold_reached` so the Director can decide whether
2813
- * to extend or abort.
2833
+ * Wall-clock budget check. Unlike other limits, timeout is always a hard stop
2834
+ * wall-clock time cannot be "extended" by the coordinator, so it throws
2835
+ * synchronously rather than entering the negotiation flow.
2836
+ *
2837
+ * Decision table:
2838
+ * - no `onThreshold` handler → throw `BudgetExceededError`
2839
+ * - `mode === 'sync'` → throw `BudgetExceededError`
2840
+ * - `mode === 'auto'` + no listener → throw `BudgetExceededError`
2841
+ * - `mode === 'auto'` + listener → throw `BudgetExceededError` (timeout is not extendable)
2814
2842
  */
2815
2843
  checkTimeout() {
2816
2844
  if (this.startTime === null || this.limits.timeoutMs === void 0) return;
2817
2845
  const elapsed = Date.now() - this.startTime;
2818
- if (elapsed > this.limits.timeoutMs) {
2819
- void this.checkLimit("timeout", elapsed, this.limits.timeoutMs);
2820
- }
2846
+ if (elapsed <= this.limits.timeoutMs) return;
2847
+ void this.checkLimits(elapsed);
2821
2848
  }
2822
2849
  /** Returns true if a timeout has occurred without throwing. Useful for races. */
2823
2850
  isTimedOut() {
@@ -5287,6 +5314,7 @@ var FLEET_ROSTER_BUDGETS = {
5287
5314
  "bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
5288
5315
  "refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
5289
5316
  "security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
5317
+ "critic": { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
5290
5318
  ...Object.fromEntries(
5291
5319
  ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
5292
5320
  )
@@ -5370,6 +5398,107 @@ FLEET_ROSTER_BUDGETS["goose"] = { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations:
5370
5398
  ...Object.fromEntries(ACP_AGENTS.map((a) => [a.role, a]))
5371
5399
  });
5372
5400
 
5401
+ // src/coordination/subagent-nicknames.ts
5402
+ var NICKNAME_POOL = {
5403
+ // Physics & fundamental sciences
5404
+ "einstein": { name: "Einstein", domain: "physics" },
5405
+ "newton": { name: "Newton", domain: "physics" },
5406
+ "feynman": { name: "Feynman", domain: "physics" },
5407
+ "dirac": { name: "Dirac", domain: "physics" },
5408
+ "bohr": { name: "Bohr", domain: "physics" },
5409
+ "planck": { name: "Planck", domain: "physics" },
5410
+ "curie": { name: "Curie", domain: "physics" },
5411
+ "fermi": { name: "Fermi", domain: "physics" },
5412
+ "heisenberg": { name: "Heisenberg", domain: "physics" },
5413
+ "schrodinger": { name: "Schr\xF6dinger", domain: "physics" },
5414
+ // Mathematics
5415
+ "euclid": { name: "Euclid", domain: "math" },
5416
+ "gauss": { name: "Gauss", domain: "math" },
5417
+ "turing": { name: "Turing", domain: "math" },
5418
+ "poincare": { name: "Poincar\xE9", domain: "math" },
5419
+ "riemann": { name: "Riemann", domain: "math" },
5420
+ "hilbert": { name: "Hilbert", domain: "math" },
5421
+ "pythagoras": { name: "Pythagoras", domain: "math" },
5422
+ // Computing & information theory
5423
+ "von-neumann": { name: "Von Neumann", domain: "computing" },
5424
+ "shannon": { name: "Shannon", domain: "computing" },
5425
+ "hopper": { name: "Hopper", domain: "computing" },
5426
+ "backus": { name: "Backus", domain: "computing" },
5427
+ "knuth": { name: "Knuth", domain: "computing" },
5428
+ "torvalds": { name: "Torvalds", domain: "computing" },
5429
+ "stallman": { name: "Stallman", domain: "computing" },
5430
+ "berners-lee": { name: "Berners-Lee", domain: "computing" },
5431
+ "babbage": { name: "Babbage", domain: "computing" },
5432
+ "lovelace": { name: "Lovelace", domain: "computing" },
5433
+ "klein": { name: "Klein", domain: "computing" },
5434
+ // Electronics & electrical engineering
5435
+ "edison": { name: "Edison", domain: "ee" },
5436
+ "tesla": { name: "Tesla", domain: "ee" },
5437
+ "faraday": { name: "Faraday", domain: "ee" },
5438
+ "maxwell": { name: "Maxwell", domain: "ee" },
5439
+ "ohm": { name: "Ohm", domain: "ee" },
5440
+ "bell": { name: "Bell", domain: "ee" },
5441
+ "marconi": { name: "Marconi", domain: "ee" },
5442
+ "lamarr": { name: "Lamarr", domain: "ee" },
5443
+ // General science / multi-disciplinary
5444
+ "darwin": { name: "Darwin", domain: "biology" },
5445
+ "mendel": { name: "Mendel", domain: "biology" },
5446
+ "pasteur": { name: "Pasteur", domain: "biology" },
5447
+ "hawking": { name: "Hawking", domain: "cosmology" },
5448
+ "sagan": { name: "Sagan", domain: "cosmology" },
5449
+ // Chemistry / materials
5450
+ "lavoisier": { name: "Lavoisier", domain: "chemistry" },
5451
+ "mendeleev": { name: "Mendeleev", domain: "chemistry" }
5452
+ };
5453
+ var ALL_NICKNAMES = Object.values(NICKNAME_POOL);
5454
+ var DOMAIN_PREFERENCES = {
5455
+ "security": ["shannon", "turing", "lamarr", "stallman"],
5456
+ "bug-hunter": ["darwin", "curie", "feynman", "fermi"],
5457
+ "refactor": ["gauss", "hilbert", "euclid", "planck"],
5458
+ "audit-log": ["sagan", "hawking", "poincare", "newton"],
5459
+ "planner": ["hilbert", "gauss", "turing", "euclid"],
5460
+ "researcher": ["sagan", "hawking", "darwin", "pasteur"],
5461
+ "explorer": ["marconi", "bell", "columbus", "polo"],
5462
+ "testing": ["pasteur", "curie", "fermi", "bohr"],
5463
+ "frontend": ["lovelace", "hopper", "babbage", "backus"],
5464
+ "backend": ["torvalds", "stallman", "von-neumann", "backus"],
5465
+ "database": ["turing", "shannon", "backus", "knuth"],
5466
+ "devops": ["tesla", "edison", "faraday", "bell"],
5467
+ "security-scanner": ["shannon", "turing", "lamarr", "stallman"],
5468
+ "refactor-planner": ["gauss", "hilbert", "planck", "newton"],
5469
+ "architect": ["von-neumann", "turing", "gauss", "hilbert"],
5470
+ "critic": ["einstein", "feynman", "dirac", "bohr"],
5471
+ "e2e": ["hopper", "bell", "marconi", "tesla"],
5472
+ "performance": ["knuth", "gauss", "planck", "feynman"],
5473
+ "chaos": ["tesla", "edison", "curie", "fermi"],
5474
+ "cost": ["ohm", "bell", "marconi", "tesla"],
5475
+ // default fallback
5476
+ "default": ["einstein", "newton", "curie", "tesla", "edison", "turing", "shannon", "hopper", "knuth", "stallman"]
5477
+ };
5478
+ function assignNickname(role, used) {
5479
+ const preferences = [
5480
+ ...DOMAIN_PREFERENCES[role] ?? [],
5481
+ ...DOMAIN_PREFERENCES["default"] ?? []
5482
+ ];
5483
+ for (const key of preferences) {
5484
+ const entry = NICKNAME_POOL[key];
5485
+ if (entry && !used.has(key)) {
5486
+ return `${entry.name} (${formatRole(role)})`;
5487
+ }
5488
+ }
5489
+ for (const entry of ALL_NICKNAMES) {
5490
+ const key = Object.entries(NICKNAME_POOL).find(([, v]) => v.name === entry.name)?.[0];
5491
+ if (key && !used.has(key)) {
5492
+ return `${entry.name} (${formatRole(role)})`;
5493
+ }
5494
+ }
5495
+ const counter = used.size + 1;
5496
+ return `Scientist #${counter} (${formatRole(role)})`;
5497
+ }
5498
+ function formatRole(role) {
5499
+ return role.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
5500
+ }
5501
+
5373
5502
  // src/coordination/multi-agent-coordinator.ts
5374
5503
  var DefaultMultiAgentCoordinator = class extends EventEmitter {
5375
5504
  coordinatorId;
@@ -5377,6 +5506,15 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5377
5506
  runner;
5378
5507
  fleetBus;
5379
5508
  subagents = /* @__PURE__ */ new Map();
5509
+ /**
5510
+ * Base nickname keys already handed out this run (e.g. `einstein`, `tesla`).
5511
+ * Prevents two workers sharing a name. Direct `coordinator.spawn()` callers
5512
+ * (parallel/eternal engine, SDD parallel run) don't go through
5513
+ * `Director.spawn()` where nicknames are normally assigned, so the
5514
+ * coordinator upgrades placeholder names ("Executor", "slot-ab12cd", role
5515
+ * names) to memorable ones here — that's what surfaces in the fleet monitor.
5516
+ */
5517
+ usedNicknames = /* @__PURE__ */ new Set();
5380
5518
  pendingTasks = [];
5381
5519
  completedResults = [];
5382
5520
  totalIterations = 0;
@@ -5425,7 +5563,26 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
5425
5563
  this.config.maxConcurrent = Math.floor(n);
5426
5564
  this.tryDispatchNext();
5427
5565
  }
5566
+ /**
5567
+ * Upgrade a placeholder/role-derived name to a memorable scientist nickname
5568
+ * (e.g. "Einstein (Executor)"). A name is treated as a placeholder when it is
5569
+ * empty, equals the role (case-insensitive), is a generic default
5570
+ * ("subagent"/"adhoc"/"generic"), or is an auto-generated `slot-…` id.
5571
+ * Explicit, human-chosen names — including nicknames already assigned by
5572
+ * `Director.spawn()` — are left untouched, so this never double-assigns.
5573
+ */
5574
+ withNickname(subagent) {
5575
+ const role = subagent.role ?? "subagent";
5576
+ const name = subagent.name?.trim() ?? "";
5577
+ const isPlaceholder = name === "" || name.toLowerCase() === role.toLowerCase() || name === "subagent" || name === "adhoc" || name === "generic" || /^slot-/.test(name);
5578
+ if (!isPlaceholder) return subagent;
5579
+ const nickname = assignNickname(role, this.usedNicknames);
5580
+ const baseKey = nickname.split(" ")[0].toLowerCase().replace(/[^a-z0-9-]/g, "-");
5581
+ this.usedNicknames.add(baseKey);
5582
+ return { ...subagent, name: nickname };
5583
+ }
5428
5584
  async spawn(subagent) {
5585
+ subagent = this.withNickname(subagent);
5429
5586
  const id = subagent.id || randomUUID();
5430
5587
  if (this.subagents.has(id)) {
5431
5588
  throw new Error(`Subagent id "${id}" already exists \u2014 refusing to overwrite`);
@@ -6084,7 +6241,7 @@ var SddParallelRun = class {
6084
6241
  this.coordinator.setRunner?.(runner);
6085
6242
  }
6086
6243
  defaultFactory() {
6087
- return async (config) => ({
6244
+ return async (_config) => ({
6088
6245
  agent: this.opts.agent,
6089
6246
  events: this.opts.agent.events
6090
6247
  });