@wrongstack/core 0.6.0 → 0.6.1

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 (35) hide show
  1. package/dist/{agent-bridge-BKNiE1VH.d.ts → agent-bridge-DaCvA_uK.d.ts} +1 -1
  2. package/dist/coordination/index.d.ts +5 -5
  3. package/dist/coordination/index.js +178 -54
  4. package/dist/coordination/index.js.map +1 -1
  5. package/dist/defaults/index.d.ts +10 -10
  6. package/dist/defaults/index.js +534 -75
  7. package/dist/defaults/index.js.map +1 -1
  8. package/dist/{events-DPQKFX7W.d.ts → events-CzkeaVVl.d.ts} +8 -2
  9. package/dist/execution/index.d.ts +149 -6
  10. package/dist/execution/index.js +345 -16
  11. package/dist/execution/index.js.map +1 -1
  12. package/dist/extension/index.d.ts +2 -2
  13. package/dist/{goal-store-BQ3YX1h1.d.ts → goal-store-DVCfj7Ff.d.ts} +20 -0
  14. package/dist/{index-B0qTujQW.d.ts → index-BkKLQjea.d.ts} +1 -1
  15. package/dist/{index-DkdRz6yK.d.ts → index-i9rPR53g.d.ts} +11 -4
  16. package/dist/index.d.ts +50 -16
  17. package/dist/index.js +769 -86
  18. package/dist/index.js.map +1 -1
  19. package/dist/infrastructure/index.d.ts +2 -2
  20. package/dist/kernel/index.d.ts +2 -2
  21. package/dist/kernel/index.js.map +1 -1
  22. package/dist/{multi-agent-Cpp7FXUl.d.ts → multi-agent-MfI6dmv-.d.ts} +30 -5
  23. package/dist/observability/index.d.ts +1 -1
  24. package/dist/{path-resolver--g_hKJ7V.d.ts → path-resolver-CWINz5XR.d.ts} +1 -1
  25. package/dist/{plan-templates-CKJs_sYh.d.ts → plan-templates-Bne1pB4d.d.ts} +1 -1
  26. package/dist/{provider-runner-hl4Il3xS.d.ts → provider-runner-CZYIzeBp.d.ts} +1 -1
  27. package/dist/sdd/index.d.ts +2 -2
  28. package/dist/storage/index.d.ts +3 -3
  29. package/dist/storage/index.js +3 -0
  30. package/dist/storage/index.js.map +1 -1
  31. package/dist/{tool-executor-B03CRwu-.d.ts → tool-executor-40Q6shR-.d.ts} +1 -1
  32. package/dist/types/index.d.ts +7 -7
  33. package/dist/types/index.js +16 -1
  34. package/dist/types/index.js.map +1 -1
  35. package/package.json +5 -1
@@ -1,4 +1,4 @@
1
- import { B as BridgeTransport, l as BridgeMessage, A as AgentBridge, m as AgentBridgeConfig } from './multi-agent-Cpp7FXUl.js';
1
+ import { B as BridgeTransport, l as BridgeMessage, A as AgentBridge, m as AgentBridgeConfig } from './multi-agent-MfI6dmv-.js';
2
2
 
3
3
  /**
4
4
  * In-memory pub/sub transport for agent-to-agent messaging.
@@ -1,11 +1,11 @@
1
- export { A as ALL_FLEET_AGENTS, a as AUDIT_LOG_AGENT, b as AgentFactory, c as AgentFactoryResult, d as AgentRunnerOptions, B as BUG_HUNTER_AGENT, C as CreateDelegateToolOptions, D as DEFAULT_DIRECTOR_PREAMBLE, e as DEFAULT_SUBAGENT_BASELINE, f as DefaultMultiAgentCoordinator, g as DelegateHost, h as Director, i as DirectorPromptParts, j as DirectorSessionFactory, k as DirectorSessionFactoryOptions, F as FLEET_ROSTER, m as FleetBus, K as FleetCostCapError, n as FleetEvent, o as FleetHandler, p as FleetManager, q as FleetManagerOptions, r as FleetSpawnBudgetError, s as FleetUsage, t as FleetUsageAggregator, I as ICoordinator, u as IFleetManager, M as MultiAgentCoordinatorOptions, N as NULL_FLEET_BUS, R as REFACTOR_PLANNER_AGENT, S as SECURITY_SCANNER_AGENT, v as SubagentPromptParts, w as SubagentUsageSnapshot, y as composeDirectorPrompt, z as composeSubagentPrompt, E as createDelegateTool, G as makeAgentSubagentRunner, H as makeDirectorSessionFactory, J as rosterSummaryFromConfigs } from '../index-DkdRz6yK.js';
2
- export { n as BudgetExceededError, o as BudgetKind, p as BudgetLimits, s as BudgetThresholdDecision, t as BudgetThresholdHandler, u as BudgetThresholdSignal, q as BudgetUsage, r as SubagentBudget } from '../multi-agent-Cpp7FXUl.js';
3
- export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-BKNiE1VH.js';
1
+ export { A as ALL_FLEET_AGENTS, a as AUDIT_LOG_AGENT, b as AgentFactory, c as AgentFactoryResult, d as AgentRunnerOptions, B as BUG_HUNTER_AGENT, C as CreateDelegateToolOptions, D as DEFAULT_DIRECTOR_PREAMBLE, e as DEFAULT_SUBAGENT_BASELINE, f as DefaultMultiAgentCoordinator, g as DelegateHost, h as Director, i as DirectorPromptParts, j as DirectorSessionFactory, k as DirectorSessionFactoryOptions, F as FLEET_ROSTER, m as FleetBus, K as FleetCostCapError, n as FleetEvent, o as FleetHandler, p as FleetManager, q as FleetManagerOptions, r as FleetSpawnBudgetError, s as FleetUsage, t as FleetUsageAggregator, I as ICoordinator, u as IFleetManager, M as MultiAgentCoordinatorOptions, N as NULL_FLEET_BUS, R as REFACTOR_PLANNER_AGENT, S as SECURITY_SCANNER_AGENT, v as SubagentPromptParts, w as SubagentUsageSnapshot, y as composeDirectorPrompt, z as composeSubagentPrompt, E as createDelegateTool, G as makeAgentSubagentRunner, H as makeDirectorSessionFactory, J as rosterSummaryFromConfigs } from '../index-i9rPR53g.js';
2
+ export { n as BudgetExceededError, o as BudgetKind, p as BudgetLimits, s as BudgetThresholdDecision, t as BudgetThresholdHandler, u as BudgetThresholdSignal, q as BudgetUsage, r as SubagentBudget } from '../multi-agent-MfI6dmv-.js';
3
+ export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-DaCvA_uK.js';
4
4
  import '../director-state-BmYi3DGA.js';
5
5
  import '../context-CDRyrkKQ.js';
6
- import '../events-DPQKFX7W.js';
6
+ import '../events-CzkeaVVl.js';
7
7
  import 'node:events';
8
- import '../index-B0qTujQW.js';
8
+ import '../index-BkKLQjea.js';
9
9
  import '../logger-BMQgxvdy.js';
10
10
  import '../system-prompt-Dl2QY1_B.js';
11
11
  import '../observability-BhnVLBLS.js';
@@ -792,7 +792,7 @@ var BudgetThresholdSignal = class extends Error {
792
792
  this.decision = decision;
793
793
  }
794
794
  };
795
- var SubagentBudget = class {
795
+ var SubagentBudget = class _SubagentBudget {
796
796
  limits;
797
797
  iterations = 0;
798
798
  toolCalls = 0;
@@ -801,6 +801,26 @@ var SubagentBudget = class {
801
801
  costUsd = 0;
802
802
  startTime = null;
803
803
  _onThreshold;
804
+ /**
805
+ * Tracks which budget kinds currently have an extension request
806
+ * in flight. While a kind is here, further `checkLimit` calls for the
807
+ * same kind are no-ops — without this dedup, every `recordIteration`
808
+ * after the limit is reached spawns a fresh decision Promise (until
809
+ * the first one lands and patches limits), flooding the FleetBus
810
+ * with redundant threshold events. Cleared in `checkLimitAsync`'s
811
+ * `finally`.
812
+ */
813
+ pendingExtensions = /* @__PURE__ */ new Set();
814
+ /**
815
+ * Hard cap on how long `checkLimitAsync` waits for the coordinator to
816
+ * respond before defaulting to 'stop'. Without this fallback an absent
817
+ * or hung listener (Director not built / event filter detached mid-run)
818
+ * leaves the budget over-limit and never enforces anything, since
819
+ * `checkLimit` returns synchronously via `void this.checkLimitAsync`.
820
+ * Hardcoded for now — most fleets set their own per-task timeout that
821
+ * dwarfs this anyway. 30 s matches the existing event-emit timeoutMs.
822
+ */
823
+ static DECISION_TIMEOUT_MS = 3e4;
804
824
  /**
805
825
  * Injected by the runner when wiring the budget to its EventBus.
806
826
  * Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
@@ -843,56 +863,92 @@ var SubagentBudget = class {
843
863
  if (kind === "timeout" || !this._onThreshold) {
844
864
  throw new BudgetExceededError(kind, limit, used);
845
865
  }
846
- void this.checkLimitAsync(kind, used, limit);
866
+ const bus = this._events;
867
+ if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
868
+ throw new BudgetExceededError(kind, limit, used);
869
+ }
870
+ if (this.pendingExtensions.has(kind)) return;
871
+ this.pendingExtensions.add(kind);
872
+ const decision = this.negotiateExtension(kind, used, limit);
873
+ throw new BudgetThresholdSignal(kind, limit, used, decision);
847
874
  }
848
875
  /**
849
- * Async threshold negotiation with the coordinator. Fire-and-forget
850
- * any error thrown here becomes an unhandled rejection in the test environment
851
- * because the runner's catch only handles the synchronous throw from `checkLimit`.
876
+ * Drive the threshold handler to a decision. Resolves with `'stop'`
877
+ * (signal the runner to abort) or `{ extend: ... }` (limits already
878
+ * patched in-place; the runner should not abort). Always releases the
879
+ * `pendingExtensions` slot in `finally`.
880
+ *
881
+ * The 'continue' return from a sync handler is treated as
882
+ * `{ extend: {} }` — keep going without patching, next overrun will
883
+ * fire a fresh signal.
852
884
  */
853
- async checkLimitAsync(kind, used, limit) {
854
- const result = this._onThreshold({
855
- kind,
856
- used,
857
- limit,
858
- // Inject a requestDecision helper the handler can call to emit the
859
- // budget.threshold_reached event and wait for the coordinator's verdict.
860
- // The runner wires this by injecting its EventBus into ctx.budget._events.
861
- requestDecision: () => {
862
- return new Promise((resolve) => {
863
- this._events?.emit("budget.threshold_reached", {
864
- kind,
865
- used,
866
- limit,
867
- timeoutMs: 3e4,
868
- extend: (extra) => resolve({ extend: extra }),
869
- deny: () => resolve("stop")
885
+ async negotiateExtension(kind, used, limit) {
886
+ try {
887
+ const result = this._onThreshold({
888
+ kind,
889
+ used,
890
+ limit,
891
+ // Inject a requestDecision helper the handler can call to emit the
892
+ // budget.threshold_reached event and wait for the coordinator's verdict.
893
+ // A hard fallback timer guarantees the promise eventually resolves
894
+ // even if no listener responds — without it, an absent/detached
895
+ // Director would leave the budget permanently in "asking" state.
896
+ requestDecision: () => {
897
+ const bus = this._events;
898
+ if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
899
+ return Promise.resolve("stop");
900
+ }
901
+ return new Promise((resolve) => {
902
+ let resolved = false;
903
+ const respond = (d) => {
904
+ if (resolved) return;
905
+ resolved = true;
906
+ resolve(d);
907
+ };
908
+ const fallback = setTimeout(
909
+ () => respond("stop"),
910
+ _SubagentBudget.DECISION_TIMEOUT_MS
911
+ );
912
+ bus.emit("budget.threshold_reached", {
913
+ kind,
914
+ used,
915
+ limit,
916
+ timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
917
+ extend: (extra) => {
918
+ clearTimeout(fallback);
919
+ respond({ extend: extra });
920
+ },
921
+ deny: () => {
922
+ clearTimeout(fallback);
923
+ respond("stop");
924
+ }
925
+ });
870
926
  });
871
- });
927
+ }
928
+ });
929
+ if (result === "throw") return "stop";
930
+ if (result === "continue") return { extend: {} };
931
+ const decision = await result;
932
+ if (decision === "stop") return "stop";
933
+ const ext = decision.extend;
934
+ if (ext.maxIterations !== void 0) {
935
+ this.limits.maxIterations = ext.maxIterations;
872
936
  }
873
- });
874
- if (result === "throw") {
875
- throw new BudgetExceededError(kind, limit, used);
876
- }
877
- if (result === "continue") {
878
- return;
879
- }
880
- const decision = await result;
881
- if (decision === "stop") {
882
- throw new BudgetExceededError(kind, limit, used);
883
- }
884
- const ext = decision.extend;
885
- if (ext.maxIterations !== void 0) {
886
- this.limits.maxIterations = ext.maxIterations;
887
- }
888
- if (ext.maxToolCalls !== void 0) {
889
- this.limits.maxToolCalls = ext.maxToolCalls;
890
- }
891
- if (ext.maxTokens !== void 0) {
892
- this.limits.maxTokens = ext.maxTokens;
893
- }
894
- if (ext.maxCostUsd !== void 0) {
895
- this.limits.maxCostUsd = ext.maxCostUsd;
937
+ if (ext.maxToolCalls !== void 0) {
938
+ this.limits.maxToolCalls = ext.maxToolCalls;
939
+ }
940
+ if (ext.maxTokens !== void 0) {
941
+ this.limits.maxTokens = ext.maxTokens;
942
+ }
943
+ if (ext.maxCostUsd !== void 0) {
944
+ this.limits.maxCostUsd = ext.maxCostUsd;
945
+ }
946
+ if (ext.timeoutMs !== void 0) {
947
+ this.limits.timeoutMs = ext.timeoutMs;
948
+ }
949
+ return decision;
950
+ } finally {
951
+ this.pendingExtensions.delete(kind);
896
952
  }
897
953
  }
898
954
  recordIteration() {
@@ -1175,6 +1231,19 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
1175
1231
  setRunner(runner) {
1176
1232
  this.runner = runner;
1177
1233
  }
1234
+ /**
1235
+ * Change the in-flight dispatch ceiling at runtime. Lowering does NOT
1236
+ * preempt running tasks — already-dispatched subagents finish their
1237
+ * current task; only future dispatches respect the new cap. Raising
1238
+ * immediately tries to fill the freed slots from the pending queue.
1239
+ */
1240
+ setMaxConcurrent(n) {
1241
+ if (!Number.isFinite(n) || n < 1) {
1242
+ throw new Error(`maxConcurrent must be a finite integer >= 1, got ${n}`);
1243
+ }
1244
+ this.config.maxConcurrent = Math.floor(n);
1245
+ this.tryDispatchNext();
1246
+ }
1178
1247
  async spawn(subagent) {
1179
1248
  const id = subagent.id || randomUUID();
1180
1249
  if (this.subagents.has(id)) {
@@ -1434,14 +1503,65 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
1434
1503
  this.recordCompletion(result);
1435
1504
  }
1436
1505
  async executeWithTimeout(runner, task, ctx, budget) {
1437
- const timeoutMs = budget.limits.timeoutMs;
1438
- if (timeoutMs === void 0) return runner(task, ctx);
1506
+ const initialTimeoutMs = budget.limits.timeoutMs;
1507
+ if (initialTimeoutMs === void 0) return runner(task, ctx);
1508
+ const start = Date.now();
1439
1509
  let timer = null;
1440
1510
  const timeoutPromise = new Promise((_, reject) => {
1441
- timer = setTimeout(() => {
1442
- this.subagents.get(ctx.subagentId)?.abortController.abort();
1443
- reject(new BudgetExceededError("timeout", timeoutMs, Date.now()));
1444
- }, timeoutMs);
1511
+ const armFor = (ms) => {
1512
+ if (timer) clearTimeout(timer);
1513
+ timer = setTimeout(async () => {
1514
+ const elapsed = Date.now() - start;
1515
+ const limit = budget.limits.timeoutMs ?? initialTimeoutMs;
1516
+ if (!budget.onThreshold) {
1517
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1518
+ reject(new BudgetExceededError("timeout", limit, elapsed));
1519
+ return;
1520
+ }
1521
+ try {
1522
+ const result = budget.onThreshold({
1523
+ kind: "timeout",
1524
+ used: elapsed,
1525
+ limit,
1526
+ requestDecision: () => new Promise((resolveDecision) => {
1527
+ budget._events?.emit("budget.threshold_reached", {
1528
+ kind: "timeout",
1529
+ used: elapsed,
1530
+ limit,
1531
+ timeoutMs: 3e4,
1532
+ extend: (extra) => resolveDecision({ extend: extra }),
1533
+ deny: () => resolveDecision("stop")
1534
+ });
1535
+ })
1536
+ });
1537
+ const decision = typeof result === "string" ? result : await result;
1538
+ if (decision === "continue") {
1539
+ armFor(Math.max(1e3, limit));
1540
+ return;
1541
+ }
1542
+ if (decision === "throw" || decision === "stop") {
1543
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1544
+ reject(new BudgetExceededError("timeout", limit, elapsed));
1545
+ return;
1546
+ }
1547
+ if (decision.extend.timeoutMs !== void 0) {
1548
+ budget.limits.timeoutMs = decision.extend.timeoutMs;
1549
+ const newLimit = decision.extend.timeoutMs;
1550
+ const remaining = Math.max(1e3, newLimit - elapsed);
1551
+ armFor(remaining);
1552
+ return;
1553
+ }
1554
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1555
+ reject(new BudgetExceededError("timeout", limit, elapsed));
1556
+ } catch (err) {
1557
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1558
+ reject(
1559
+ err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
1560
+ );
1561
+ }
1562
+ }, ms);
1563
+ };
1564
+ armFor(initialTimeoutMs);
1445
1565
  });
1446
1566
  try {
1447
1567
  return await Promise.race([runner(task, ctx), timeoutPromise]);
@@ -2052,7 +2172,7 @@ var Director = class {
2052
2172
  return;
2053
2173
  }
2054
2174
  extendCounts.set(guardKey, prior + 1);
2055
- setTimeout(() => {
2175
+ setImmediate(() => {
2056
2176
  const extra = {};
2057
2177
  switch (payload.kind) {
2058
2178
  case "iterations":
@@ -2067,9 +2187,12 @@ var Director = class {
2067
2187
  case "cost":
2068
2188
  extra.maxCostUsd = Math.min(payload.limit * 1.5, 10);
2069
2189
  break;
2190
+ case "timeout":
2191
+ extra.timeoutMs = Math.min(Math.ceil(payload.limit * 1.5), 60 * 6e4);
2192
+ break;
2070
2193
  }
2071
2194
  payload.extend(extra);
2072
- }, Math.min(payload.timeoutMs, 3e4));
2195
+ });
2073
2196
  });
2074
2197
  }
2075
2198
  /** Best-effort session-writer append. Swallows failures — the director
@@ -2864,6 +2987,7 @@ function makeAgentSubagentRunner(opts) {
2864
2987
  const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
2865
2988
  const aborter = new AbortController();
2866
2989
  ctx.budget._events = events;
2990
+ ctx.budget.onThreshold = ({ requestDecision }) => requestDecision();
2867
2991
  let budgetError = null;
2868
2992
  const onBudgetError = (err) => {
2869
2993
  if (err instanceof BudgetThresholdSignal) {