@wrongstack/core 0.6.0 → 0.6.3

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 (41) hide show
  1. package/dist/{agent-bridge-BKNiE1VH.d.ts → agent-bridge-eb7qnNrd.d.ts} +1 -1
  2. package/dist/coordination/index.d.ts +5 -5
  3. package/dist/coordination/index.js +183 -87
  4. package/dist/coordination/index.js.map +1 -1
  5. package/dist/defaults/index.d.ts +10 -10
  6. package/dist/defaults/index.js +596 -114
  7. package/dist/defaults/index.js.map +1 -1
  8. package/dist/{events-DPQKFX7W.d.ts → events-BHuIHekD.d.ts} +27 -4
  9. package/dist/execution/index.d.ts +149 -6
  10. package/dist/execution/index.js +401 -21
  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-BOn9NK7D.d.ts} +1 -1
  15. package/dist/{index-DkdRz6yK.d.ts → index-CPcDqvZh.d.ts} +11 -4
  16. package/dist/index.d.ts +50 -16
  17. package/dist/index.js +831 -125
  18. package/dist/index.js.map +1 -1
  19. package/dist/infrastructure/index.d.ts +2 -2
  20. package/dist/infrastructure/index.js +1 -1
  21. package/dist/infrastructure/index.js.map +1 -1
  22. package/dist/kernel/index.d.ts +2 -2
  23. package/dist/kernel/index.js.map +1 -1
  24. package/dist/{multi-agent-Cpp7FXUl.d.ts → multi-agent-CxSb-9dQ.d.ts} +30 -5
  25. package/dist/observability/index.d.ts +1 -1
  26. package/dist/observability/index.js +1 -1
  27. package/dist/observability/index.js.map +1 -1
  28. package/dist/{path-resolver--g_hKJ7V.d.ts → path-resolver-CMGNadvq.d.ts} +1 -1
  29. package/dist/{plan-templates-CKJs_sYh.d.ts → plan-templates-BJflQY2i.d.ts} +1 -1
  30. package/dist/{provider-runner-hl4Il3xS.d.ts → provider-runner-BFgNXpaP.d.ts} +1 -1
  31. package/dist/sdd/index.d.ts +2 -2
  32. package/dist/storage/index.d.ts +3 -3
  33. package/dist/storage/index.js +3 -0
  34. package/dist/storage/index.js.map +1 -1
  35. package/dist/{tool-executor-B03CRwu-.d.ts → tool-executor-FoxBjULX.d.ts} +1 -1
  36. package/dist/types/index.d.ts +7 -7
  37. package/dist/types/index.js +30 -4
  38. package/dist/types/index.js.map +1 -1
  39. package/dist/utils/index.js +1 -1
  40. package/dist/utils/index.js.map +1 -1
  41. 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-CxSb-9dQ.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-CPcDqvZh.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-CxSb-9dQ.js';
3
+ export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-eb7qnNrd.js';
4
4
  import '../director-state-BmYi3DGA.js';
5
5
  import '../context-CDRyrkKQ.js';
6
- import '../events-DPQKFX7W.js';
6
+ import '../events-BHuIHekD.js';
7
7
  import 'node:events';
8
- import '../index-B0qTujQW.js';
8
+ import '../index-BOn9NK7D.js';
9
9
  import '../logger-BMQgxvdy.js';
10
10
  import '../system-prompt-Dl2QY1_B.js';
11
11
  import '../observability-BhnVLBLS.js';
@@ -492,40 +492,12 @@ var FleetBus = class {
492
492
  * subagent teardown so the listeners don't outlive the run.
493
493
  */
494
494
  attach(subagentId, bus, taskId) {
495
- const FORWARDED_TYPES = [
496
- "tool.started",
497
- "tool.executed",
498
- "tool.progress",
499
- "tool.confirm_needed",
500
- "iteration.started",
501
- "iteration.completed",
502
- "provider.text_delta",
503
- // Subagent extended-thinking output. Forwarded so the FleetPanel /
504
- // /fleet log can surface "the planner is thinking…" instead of a
505
- // silent gap between iteration.started and the first text_delta.
506
- "provider.thinking_delta",
507
- "provider.response",
508
- "provider.retry",
509
- "provider.error",
510
- "session.started",
511
- "session.ended",
512
- "session.damaged",
513
- "compaction.fired",
514
- "compaction.failed",
515
- "token.threshold",
516
- // Subagent hit a soft budget limit — coordinator can extend or stop.
517
- "budget.threshold_reached"
518
- ];
519
- const offs = [];
520
- for (const t of FORWARDED_TYPES) {
521
- offs.push(
522
- bus.on(t, (payload) => {
523
- this.emit({ subagentId, taskId, ts: Date.now(), type: t, payload });
524
- })
525
- );
526
- }
495
+ const off = bus.onPattern("*", (type, payload) => {
496
+ if (type.startsWith("subagent.")) return;
497
+ this.emit({ subagentId, taskId, ts: Date.now(), type, payload });
498
+ });
527
499
  return () => {
528
- for (const off of offs) off();
500
+ off();
529
501
  };
530
502
  }
531
503
  /** Subscribe to every event from one subagent. */
@@ -792,7 +764,7 @@ var BudgetThresholdSignal = class extends Error {
792
764
  this.decision = decision;
793
765
  }
794
766
  };
795
- var SubagentBudget = class {
767
+ var SubagentBudget = class _SubagentBudget {
796
768
  limits;
797
769
  iterations = 0;
798
770
  toolCalls = 0;
@@ -801,6 +773,26 @@ var SubagentBudget = class {
801
773
  costUsd = 0;
802
774
  startTime = null;
803
775
  _onThreshold;
776
+ /**
777
+ * Tracks which budget kinds currently have an extension request
778
+ * in flight. While a kind is here, further `checkLimit` calls for the
779
+ * same kind are no-ops — without this dedup, every `recordIteration`
780
+ * after the limit is reached spawns a fresh decision Promise (until
781
+ * the first one lands and patches limits), flooding the FleetBus
782
+ * with redundant threshold events. Cleared in `checkLimitAsync`'s
783
+ * `finally`.
784
+ */
785
+ pendingExtensions = /* @__PURE__ */ new Set();
786
+ /**
787
+ * Hard cap on how long `checkLimitAsync` waits for the coordinator to
788
+ * respond before defaulting to 'stop'. Without this fallback an absent
789
+ * or hung listener (Director not built / event filter detached mid-run)
790
+ * leaves the budget over-limit and never enforces anything, since
791
+ * `checkLimit` returns synchronously via `void this.checkLimitAsync`.
792
+ * Hardcoded for now — most fleets set their own per-task timeout that
793
+ * dwarfs this anyway. 30 s matches the existing event-emit timeoutMs.
794
+ */
795
+ static DECISION_TIMEOUT_MS = 3e4;
804
796
  /**
805
797
  * Injected by the runner when wiring the budget to its EventBus.
806
798
  * Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
@@ -843,56 +835,92 @@ var SubagentBudget = class {
843
835
  if (kind === "timeout" || !this._onThreshold) {
844
836
  throw new BudgetExceededError(kind, limit, used);
845
837
  }
846
- void this.checkLimitAsync(kind, used, limit);
838
+ const bus = this._events;
839
+ if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
840
+ throw new BudgetExceededError(kind, limit, used);
841
+ }
842
+ if (this.pendingExtensions.has(kind)) return;
843
+ this.pendingExtensions.add(kind);
844
+ const decision = this.negotiateExtension(kind, used, limit);
845
+ throw new BudgetThresholdSignal(kind, limit, used, decision);
847
846
  }
848
847
  /**
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`.
848
+ * Drive the threshold handler to a decision. Resolves with `'stop'`
849
+ * (signal the runner to abort) or `{ extend: ... }` (limits already
850
+ * patched in-place; the runner should not abort). Always releases the
851
+ * `pendingExtensions` slot in `finally`.
852
+ *
853
+ * The 'continue' return from a sync handler is treated as
854
+ * `{ extend: {} }` — keep going without patching, next overrun will
855
+ * fire a fresh signal.
852
856
  */
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")
857
+ async negotiateExtension(kind, used, limit) {
858
+ try {
859
+ const result = this._onThreshold({
860
+ kind,
861
+ used,
862
+ limit,
863
+ // Inject a requestDecision helper the handler can call to emit the
864
+ // budget.threshold_reached event and wait for the coordinator's verdict.
865
+ // A hard fallback timer guarantees the promise eventually resolves
866
+ // even if no listener responds — without it, an absent/detached
867
+ // Director would leave the budget permanently in "asking" state.
868
+ requestDecision: () => {
869
+ const bus = this._events;
870
+ if (!bus || bus.listenerCount("budget.threshold_reached") === 0) {
871
+ return Promise.resolve("stop");
872
+ }
873
+ return new Promise((resolve) => {
874
+ let resolved = false;
875
+ const respond = (d) => {
876
+ if (resolved) return;
877
+ resolved = true;
878
+ resolve(d);
879
+ };
880
+ const fallback = setTimeout(
881
+ () => respond("stop"),
882
+ _SubagentBudget.DECISION_TIMEOUT_MS
883
+ );
884
+ bus.emit("budget.threshold_reached", {
885
+ kind,
886
+ used,
887
+ limit,
888
+ timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
889
+ extend: (extra) => {
890
+ clearTimeout(fallback);
891
+ respond({ extend: extra });
892
+ },
893
+ deny: () => {
894
+ clearTimeout(fallback);
895
+ respond("stop");
896
+ }
897
+ });
870
898
  });
871
- });
899
+ }
900
+ });
901
+ if (result === "throw") return "stop";
902
+ if (result === "continue") return { extend: {} };
903
+ const decision = await result;
904
+ if (decision === "stop") return "stop";
905
+ const ext = decision.extend;
906
+ if (ext.maxIterations !== void 0) {
907
+ this.limits.maxIterations = ext.maxIterations;
872
908
  }
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;
909
+ if (ext.maxToolCalls !== void 0) {
910
+ this.limits.maxToolCalls = ext.maxToolCalls;
911
+ }
912
+ if (ext.maxTokens !== void 0) {
913
+ this.limits.maxTokens = ext.maxTokens;
914
+ }
915
+ if (ext.maxCostUsd !== void 0) {
916
+ this.limits.maxCostUsd = ext.maxCostUsd;
917
+ }
918
+ if (ext.timeoutMs !== void 0) {
919
+ this.limits.timeoutMs = ext.timeoutMs;
920
+ }
921
+ return decision;
922
+ } finally {
923
+ this.pendingExtensions.delete(kind);
896
924
  }
897
925
  }
898
926
  recordIteration() {
@@ -1175,6 +1203,19 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
1175
1203
  setRunner(runner) {
1176
1204
  this.runner = runner;
1177
1205
  }
1206
+ /**
1207
+ * Change the in-flight dispatch ceiling at runtime. Lowering does NOT
1208
+ * preempt running tasks — already-dispatched subagents finish their
1209
+ * current task; only future dispatches respect the new cap. Raising
1210
+ * immediately tries to fill the freed slots from the pending queue.
1211
+ */
1212
+ setMaxConcurrent(n) {
1213
+ if (!Number.isFinite(n) || n < 1) {
1214
+ throw new Error(`maxConcurrent must be a finite integer >= 1, got ${n}`);
1215
+ }
1216
+ this.config.maxConcurrent = Math.floor(n);
1217
+ this.tryDispatchNext();
1218
+ }
1178
1219
  async spawn(subagent) {
1179
1220
  const id = subagent.id || randomUUID();
1180
1221
  if (this.subagents.has(id)) {
@@ -1434,14 +1475,65 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
1434
1475
  this.recordCompletion(result);
1435
1476
  }
1436
1477
  async executeWithTimeout(runner, task, ctx, budget) {
1437
- const timeoutMs = budget.limits.timeoutMs;
1438
- if (timeoutMs === void 0) return runner(task, ctx);
1478
+ const initialTimeoutMs = budget.limits.timeoutMs;
1479
+ if (initialTimeoutMs === void 0) return runner(task, ctx);
1480
+ const start = Date.now();
1439
1481
  let timer = null;
1440
1482
  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);
1483
+ const armFor = (ms) => {
1484
+ if (timer) clearTimeout(timer);
1485
+ timer = setTimeout(async () => {
1486
+ const elapsed = Date.now() - start;
1487
+ const limit = budget.limits.timeoutMs ?? initialTimeoutMs;
1488
+ if (!budget.onThreshold) {
1489
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1490
+ reject(new BudgetExceededError("timeout", limit, elapsed));
1491
+ return;
1492
+ }
1493
+ try {
1494
+ const result = budget.onThreshold({
1495
+ kind: "timeout",
1496
+ used: elapsed,
1497
+ limit,
1498
+ requestDecision: () => new Promise((resolveDecision) => {
1499
+ budget._events?.emit("budget.threshold_reached", {
1500
+ kind: "timeout",
1501
+ used: elapsed,
1502
+ limit,
1503
+ timeoutMs: 3e4,
1504
+ extend: (extra) => resolveDecision({ extend: extra }),
1505
+ deny: () => resolveDecision("stop")
1506
+ });
1507
+ })
1508
+ });
1509
+ const decision = typeof result === "string" ? result : await result;
1510
+ if (decision === "continue") {
1511
+ armFor(Math.max(1e3, limit));
1512
+ return;
1513
+ }
1514
+ if (decision === "throw" || decision === "stop") {
1515
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1516
+ reject(new BudgetExceededError("timeout", limit, elapsed));
1517
+ return;
1518
+ }
1519
+ if (decision.extend.timeoutMs !== void 0) {
1520
+ budget.limits.timeoutMs = decision.extend.timeoutMs;
1521
+ const newLimit = decision.extend.timeoutMs;
1522
+ const remaining = Math.max(1e3, newLimit - elapsed);
1523
+ armFor(remaining);
1524
+ return;
1525
+ }
1526
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1527
+ reject(new BudgetExceededError("timeout", limit, elapsed));
1528
+ } catch (err) {
1529
+ this.subagents.get(ctx.subagentId)?.abortController.abort();
1530
+ reject(
1531
+ err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
1532
+ );
1533
+ }
1534
+ }, ms);
1535
+ };
1536
+ armFor(initialTimeoutMs);
1445
1537
  });
1446
1538
  try {
1447
1539
  return await Promise.race([runner(task, ctx), timeoutPromise]);
@@ -2052,7 +2144,7 @@ var Director = class {
2052
2144
  return;
2053
2145
  }
2054
2146
  extendCounts.set(guardKey, prior + 1);
2055
- setTimeout(() => {
2147
+ setImmediate(() => {
2056
2148
  const extra = {};
2057
2149
  switch (payload.kind) {
2058
2150
  case "iterations":
@@ -2067,9 +2159,12 @@ var Director = class {
2067
2159
  case "cost":
2068
2160
  extra.maxCostUsd = Math.min(payload.limit * 1.5, 10);
2069
2161
  break;
2162
+ case "timeout":
2163
+ extra.timeoutMs = Math.min(Math.ceil(payload.limit * 1.5), 60 * 6e4);
2164
+ break;
2070
2165
  }
2071
2166
  payload.extend(extra);
2072
- }, Math.min(payload.timeoutMs, 3e4));
2167
+ });
2073
2168
  });
2074
2169
  }
2075
2170
  /** Best-effort session-writer append. Swallows failures — the director
@@ -2864,6 +2959,7 @@ function makeAgentSubagentRunner(opts) {
2864
2959
  const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
2865
2960
  const aborter = new AbortController();
2866
2961
  ctx.budget._events = events;
2962
+ ctx.budget.onThreshold = ({ requestDecision }) => requestDecision();
2867
2963
  let budgetError = null;
2868
2964
  const onBudgetError = (err) => {
2869
2965
  if (err instanceof BudgetThresholdSignal) {