@wrongstack/core 0.84.1 → 0.89.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 (49) hide show
  1. package/dist/{agent-bridge-C9P_HPez.d.ts → agent-bridge-B8F2ORwg.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-2Aq0jOSj.d.ts → agent-subagent-runner-CQaLFNnA.d.ts} +3 -2
  3. package/dist/coordination/index.d.ts +7 -7
  4. package/dist/coordination/index.js +183 -56
  5. package/dist/coordination/index.js.map +1 -1
  6. package/dist/{default-config-DEXI4jsl.d.ts → default-config-CXsDvOmP.d.ts} +3 -1
  7. package/dist/defaults/index.d.ts +10 -10
  8. package/dist/defaults/index.js +268 -68
  9. package/dist/defaults/index.js.map +1 -1
  10. package/dist/{events-DnRqXaZ3.d.ts → events-DzyLtja6.d.ts} +6 -0
  11. package/dist/execution/index.d.ts +5 -5
  12. package/dist/execution/index.js +56 -19
  13. package/dist/execution/index.js.map +1 -1
  14. package/dist/extension/index.d.ts +2 -2
  15. package/dist/{index-BNOLadHw.d.ts → index-Ca_lOmKl.d.ts} +2 -2
  16. package/dist/{index-N0_c4bHQ.d.ts → index-Dx-CnrDN.d.ts} +3 -1
  17. package/dist/index.d.ts +24 -21
  18. package/dist/index.js +448 -179
  19. package/dist/index.js.map +1 -1
  20. package/dist/infrastructure/index.d.ts +2 -2
  21. package/dist/infrastructure/index.js +12 -3
  22. package/dist/infrastructure/index.js.map +1 -1
  23. package/dist/kernel/index.d.ts +3 -3
  24. package/dist/kernel/index.js +17 -0
  25. package/dist/kernel/index.js.map +1 -1
  26. package/dist/models/index.js.map +1 -1
  27. package/dist/{multi-agent-coordinator-DllpCVkF.d.ts → multi-agent-coordinator-Da9nd8Ic.d.ts} +1 -1
  28. package/dist/{null-fleet-bus-BY0AN-sr.d.ts → null-fleet-bus-CHJOXv3t.d.ts} +11 -5
  29. package/dist/observability/index.d.ts +1 -1
  30. package/dist/{parallel-eternal-engine-D402RASp.d.ts → parallel-eternal-engine-DyMCj2Pw.d.ts} +4 -3
  31. package/dist/{path-resolver-UPFTsDyD.d.ts → path-resolver-TjBlRKyH.d.ts} +1 -1
  32. package/dist/{plan-templates-DRvPgkfZ.d.ts → plan-templates-D9UhwoGh.d.ts} +1 -1
  33. package/dist/{provider-runner-COAJM8tC.d.ts → provider-runner-BEPWwkxH.d.ts} +1 -1
  34. package/dist/sdd/index.d.ts +3 -3
  35. package/dist/sdd/index.js +89 -19
  36. package/dist/sdd/index.js.map +1 -1
  37. package/dist/security/index.js +18 -2
  38. package/dist/security/index.js.map +1 -1
  39. package/dist/skills/index.js.map +1 -1
  40. package/dist/storage/index.d.ts +4 -4
  41. package/dist/storage/index.js +149 -90
  42. package/dist/storage/index.js.map +1 -1
  43. package/dist/types/index.d.ts +10 -10
  44. package/dist/types/index.js +44 -7
  45. package/dist/types/index.js.map +1 -1
  46. package/dist/utils/index.d.ts +6 -1
  47. package/dist/utils/index.js +51 -3
  48. package/dist/utils/index.js.map +1 -1
  49. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { f as BridgeTransport, B as BridgeMessage, a as AgentBridge, b as AgentBridgeConfig } from './agent-subagent-runner-2Aq0jOSj.js';
1
+ import { f as BridgeTransport, B as BridgeMessage, a as AgentBridge, b as AgentBridgeConfig } from './agent-subagent-runner-CQaLFNnA.js';
2
2
 
3
3
  /**
4
4
  * In-memory pub/sub transport for agent-to-agent messaging.
@@ -1,6 +1,6 @@
1
- import { t as ToolRegistry, m as ProviderRegistry, f as AgentPipelines, q as ToolExecutorLike, E as ExtensionRegistry, d as AgentInit, e as AgentInput, R as RunResult, p as ToolCallPipelinePayload, u as ToolWrapper, S as SystemPromptContributor } from './index-N0_c4bHQ.js';
1
+ import { t as ToolRegistry, m as ProviderRegistry, f as AgentPipelines, q as ToolExecutorLike, E as ExtensionRegistry, d as AgentInit, e as AgentInput, R as RunResult, p as ToolCallPipelinePayload, u as ToolWrapper, S as SystemPromptContributor } from './index-Dx-CnrDN.js';
2
2
  import { C as Container, e as Renderer, R as ReadonlyPipeline } from './system-prompt-C0rLCeyn.js';
3
- import { E as EventBus, k as EventName, L as Listener } from './events-DnRqXaZ3.js';
3
+ import { E as EventBus, k as EventName, L as Listener } from './events-DzyLtja6.js';
4
4
  import { a as RetryPolicy, E as ErrorHandler } from './retry-policy-DSu6O6rD.js';
5
5
  import { a as Logger } from './logger-B72yyPc6.js';
6
6
  import { T as Tracer } from './observability-CoSNZdhX.js';
@@ -925,6 +925,7 @@ declare class FleetBus {
925
925
  /** Subscribe to literally everything. The fleet roll-up uses this. */
926
926
  onAny(handler: FleetHandler): () => void;
927
927
  emit(event: FleetEvent): void;
928
+ clear(): void;
928
929
  }
929
930
  /**
930
931
  * Roll-up of token usage + cost across an entire director run. The
@@ -1,15 +1,15 @@
1
- export { B as BrainArbiter, a as BrainDecision, b as BrainDecisionOption, c as BrainDecisionQueue, d as BrainDecisionRequest, e as BrainDecisionSource, f as BrainFallback, g as BrainRisk, D as DefaultBrainArbiter, h as DefaultBrainArbiterOptions, H as HumanEscalatingBrainArbiter, O as ObservableBrainArbiter, l as formatHumanPrompt } from '../events-DnRqXaZ3.js';
2
- export { A as ACP_AGENTS, a as AGENTS_BY_PHASE, b as AGENT_CATALOG, c as ALL_AGENT_DEFINITIONS, d as ALL_FLEET_AGENTS, e as AUDIT_LOG_AGENT, f as AutoExtendCeiling, g as AutoExtendPolicy, B as BUG_HUNTER_AGENT, h as BugFinding, C as CollabBudgetConfig, i as CollabBudgetOverrides, j as CollabBudgetWarningPayload, k as CollabDebugReport, l as CollabSession, m as CollabSessionOptions, n as CreateDelegateToolOptions, o as CriticConcern, p as CriticEvaluation, D as DEFAULT_DIRECTOR_PREAMBLE, q as DEFAULT_SUBAGENT_BASELINE, r as DelegateHost, s as Director, t as DirectorAlert, u as DirectorAlertLevel, v as DirectorCancelCollabPayload, w as DirectorPromptParts, x as DirectorSessionFactory, y as DirectorSessionFactoryOptions, F as FLEET_ROSTER, z as FLEET_ROSTER_BUDGETS, E as FLEET_ROSTER_WITHACP, G as FleetCostCapError, H as FleetManager, I as FleetManagerOptions, J as FleetRosterBudget, K as FleetSpawnBudgetError, L as ICoordinator, M as IFleetManager, N as LargeAnswerStore, O as NULL_FLEET_BUS, R as REFACTOR_PLANNER_AGENT, P as RefactorPhase, Q as RefactorPlan, S as SECURITY_SCANNER_AGENT, T as SharedFileEntry, U as SharedFileSnapshot, V as SubagentPromptParts, W as applyRosterBudget, X as attachAutoExtend, Y as composeDirectorPrompt, Z as composeSubagentPrompt, _ as createDelegateTool, $ as getAgentDefinition, a0 as makeAskResultTool, a1 as makeAskTool, a2 as makeAssignTool, a3 as makeAwaitTasksTool, a4 as makeCollabDebugTool, a5 as makeDirectorSessionFactory, a6 as makeFleetEmitTool, a7 as makeFleetHealthTool, a8 as makeFleetSessionTool, a9 as makeFleetStatusTool, aa as makeFleetUsageTool, ab as makeRollUpTool, ac as makeSpawnTool, ad as makeTerminateTool, ae as makeWorkCompleteTool, af as rosterSummaryFromConfigs } from '../null-fleet-bus-BY0AN-sr.js';
3
- import { b as AgentDefinition } from '../multi-agent-coordinator-DllpCVkF.js';
4
- export { T as AGENT_TOOL_PRESETS, A as AgentBudgetTier, a as AgentCapability, c as AgentPhase, D as DEFAULT_DISPATCH_ROLE, d as DefaultMultiAgentCoordinator, e as DispatchCandidate, f as DispatchClassifier, g as DispatchMethod, h as DispatchOptions, i as DispatchResult, H as HEAVY_BUDGET, L as LIGHT_BUDGET, M as MEDIUM_BUDGET, j as MultiAgentCoordinatorOptions, k as dispatchAgent, m as makeLLMClassifier, s as scoreAgents } from '../multi-agent-coordinator-DllpCVkF.js';
5
- export { c as AgentFactory, d as AgentFactoryResult, e as AgentRunnerOptions, g as BudgetExceededError, h as BudgetKind, i as BudgetLimits, j as BudgetNegotiationMode, k as BudgetThresholdDecision, l as BudgetThresholdHandler, m as BudgetThresholdSignal, n as BudgetUsage, F as FleetBus, p as FleetEvent, q as FleetHandler, r as FleetUsage, s as FleetUsageAggregator, K as SubagentBudget, V as SubagentUsageSnapshot, _ as makeAgentSubagentRunner } from '../agent-subagent-runner-2Aq0jOSj.js';
6
- export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-C9P_HPez.js';
1
+ export { B as BrainArbiter, a as BrainDecision, b as BrainDecisionOption, c as BrainDecisionQueue, d as BrainDecisionRequest, e as BrainDecisionSource, f as BrainFallback, g as BrainRisk, D as DefaultBrainArbiter, h as DefaultBrainArbiterOptions, H as HumanEscalatingBrainArbiter, O as ObservableBrainArbiter, l as formatHumanPrompt } from '../events-DzyLtja6.js';
2
+ export { A as ACP_AGENTS, a as AGENTS_BY_PHASE, b as AGENT_CATALOG, c as ALL_AGENT_DEFINITIONS, d as ALL_FLEET_AGENTS, e as AUDIT_LOG_AGENT, f as AutoExtendCeiling, g as AutoExtendPolicy, B as BUG_HUNTER_AGENT, h as BugFinding, C as CollabBudgetConfig, i as CollabBudgetOverrides, j as CollabBudgetWarningPayload, k as CollabDebugReport, l as CollabSession, m as CollabSessionOptions, n as CreateDelegateToolOptions, o as CriticConcern, p as CriticEvaluation, D as DEFAULT_DIRECTOR_PREAMBLE, q as DEFAULT_SUBAGENT_BASELINE, r as DelegateHost, s as Director, t as DirectorAlert, u as DirectorAlertLevel, v as DirectorCancelCollabPayload, w as DirectorPromptParts, x as DirectorSessionFactory, y as DirectorSessionFactoryOptions, F as FLEET_ROSTER, z as FLEET_ROSTER_BUDGETS, E as FLEET_ROSTER_WITHACP, G as FleetCostCapError, H as FleetManager, I as FleetManagerOptions, J as FleetRosterBudget, K as FleetSpawnBudgetError, L as ICoordinator, M as IFleetManager, N as LargeAnswerStore, O as NULL_FLEET_BUS, R as REFACTOR_PLANNER_AGENT, P as RefactorPhase, Q as RefactorPlan, S as SECURITY_SCANNER_AGENT, T as SharedFileEntry, U as SharedFileSnapshot, V as SubagentPromptParts, W as applyRosterBudget, X as attachAutoExtend, Y as composeDirectorPrompt, Z as composeSubagentPrompt, _ as createDelegateTool, $ as getAgentDefinition, a0 as makeAskResultTool, a1 as makeAskTool, a2 as makeAssignTool, a3 as makeAwaitTasksTool, a4 as makeCollabDebugTool, a5 as makeDirectorSessionFactory, a6 as makeFleetEmitTool, a7 as makeFleetHealthTool, a8 as makeFleetSessionTool, a9 as makeFleetStatusTool, aa as makeFleetUsageTool, ab as makeRollUpTool, ac as makeSpawnTool, ad as makeTerminateTool, ae as makeWorkCompleteTool, af as rosterSummaryFromConfigs } from '../null-fleet-bus-CHJOXv3t.js';
3
+ import { b as AgentDefinition } from '../multi-agent-coordinator-Da9nd8Ic.js';
4
+ export { T as AGENT_TOOL_PRESETS, A as AgentBudgetTier, a as AgentCapability, c as AgentPhase, D as DEFAULT_DISPATCH_ROLE, d as DefaultMultiAgentCoordinator, e as DispatchCandidate, f as DispatchClassifier, g as DispatchMethod, h as DispatchOptions, i as DispatchResult, H as HEAVY_BUDGET, L as LIGHT_BUDGET, M as MEDIUM_BUDGET, j as MultiAgentCoordinatorOptions, k as dispatchAgent, m as makeLLMClassifier, s as scoreAgents } from '../multi-agent-coordinator-Da9nd8Ic.js';
5
+ export { c as AgentFactory, d as AgentFactoryResult, e as AgentRunnerOptions, g as BudgetExceededError, h as BudgetKind, i as BudgetLimits, j as BudgetNegotiationMode, k as BudgetThresholdDecision, l as BudgetThresholdHandler, m as BudgetThresholdSignal, n as BudgetUsage, F as FleetBus, p as FleetEvent, q as FleetHandler, r as FleetUsage, s as FleetUsageAggregator, K as SubagentBudget, V as SubagentUsageSnapshot, _ as makeAgentSubagentRunner } from '../agent-subagent-runner-CQaLFNnA.js';
6
+ export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-B8F2ORwg.js';
7
7
  import '../context-ToHAp4-U.js';
8
8
  import 'node:events';
9
9
  import '../director-state-CgIc30qi.js';
10
10
  import '../config-_DZ7dN-T.js';
11
11
  import '../models-registry-Be3osGt5.js';
12
- import '../index-N0_c4bHQ.js';
12
+ import '../index-Dx-CnrDN.js';
13
13
  import '../logger-B72yyPc6.js';
14
14
  import '../system-prompt-C0rLCeyn.js';
15
15
  import '../observability-CoSNZdhX.js';
@@ -172,8 +172,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
172
172
  }
173
173
  let mode;
174
174
  try {
175
- const stat4 = await fsp6.stat(targetPath);
176
- mode = stat4.mode & 511;
175
+ const stat5 = await fsp6.stat(targetPath);
176
+ mode = stat5.mode & 511;
177
177
  } catch {
178
178
  mode = opts.mode;
179
179
  }
@@ -381,6 +381,21 @@ var DirectorStateCheckpoint = class {
381
381
  }
382
382
  };
383
383
 
384
+ // src/utils/safe-json.ts
385
+ function safeParse(input, maxBytes = 5e6) {
386
+ if (input.length > maxBytes) {
387
+ return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
388
+ }
389
+ try {
390
+ return { ok: true, value: JSON.parse(input) };
391
+ } catch (err) {
392
+ return {
393
+ ok: false,
394
+ error: err instanceof Error ? err.message : String(err)
395
+ };
396
+ }
397
+ }
398
+
384
399
  // src/coordination/in-memory-transport.ts
385
400
  var InMemoryBridgeTransport = class {
386
401
  subs = /* @__PURE__ */ new Map();
@@ -621,8 +636,8 @@ async function expandGlob(pattern) {
621
636
  for (const e of entries) {
622
637
  const full = `${dir}${SEP}${e}`;
623
638
  try {
624
- const stat4 = await fsp6.stat(full);
625
- if (stat4.isDirectory()) await walk(full, rest);
639
+ const stat5 = await fsp6.stat(full);
640
+ if (stat5.isDirectory()) await walk(full, rest);
626
641
  } catch {
627
642
  }
628
643
  }
@@ -639,8 +654,8 @@ async function expandGlob(pattern) {
639
654
  if (entries.includes(seg)) {
640
655
  const full = `${dir}${SEP}${seg}`;
641
656
  try {
642
- const stat4 = await fsp6.stat(full);
643
- if (stat4.isDirectory()) await walk(full, rest);
657
+ const stat5 = await fsp6.stat(full);
658
+ if (stat5.isDirectory()) await walk(full, rest);
644
659
  } catch {
645
660
  }
646
661
  }
@@ -674,6 +689,7 @@ var CollabSession = class extends EventEmitter {
674
689
  timeoutMs;
675
690
  cancelled = false;
676
691
  alerts = [];
692
+ snapshotWarnings = [];
677
693
  /** Tracks tool call counts per subagent for progress-based timeout decisions. */
678
694
  progressBySubagent = /* @__PURE__ */ new Map();
679
695
  /** Last tool call count when a timeout warning was handled. */
@@ -743,10 +759,19 @@ var CollabSession = class extends EventEmitter {
743
759
  }
744
760
  for (const filePath of allFiles) {
745
761
  try {
746
- const content = await fsp6.readFile(filePath, "utf8");
762
+ const [content, stat5] = await Promise.all([
763
+ fsp6.readFile(filePath, "utf8"),
764
+ fsp6.stat(filePath)
765
+ ]);
747
766
  const ext = filePath.split(".").pop() ?? "";
748
767
  const language = ext === "ts" || ext === "tsx" ? "typescript" : ext === "js" || ext === "jsx" ? "javascript" : ext === "md" ? "markdown" : ext === "json" ? "json" : void 0;
749
- this.snapshot.files.push({ path: filePath, content, language });
768
+ this.snapshot.files.push({
769
+ path: filePath,
770
+ content,
771
+ language,
772
+ snapshotMtimeMs: stat5.mtimeMs,
773
+ snapshotSizeBytes: stat5.size
774
+ });
750
775
  } catch {
751
776
  this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
752
777
  }
@@ -769,7 +794,11 @@ var CollabSession = class extends EventEmitter {
769
794
  subagentId: this.director.id,
770
795
  ts: Date.now(),
771
796
  type: "director.cancel_collab",
772
- payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
797
+ payload: {
798
+ sessionId: this.sessionId,
799
+ reason,
800
+ cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
801
+ }
773
802
  });
774
803
  this.fleetBus.emit({
775
804
  subagentId: this.director.id,
@@ -820,6 +849,7 @@ var CollabSession = class extends EventEmitter {
820
849
  for (const result of results?.flat() ?? []) {
821
850
  await this.parseAndEmit(result);
822
851
  }
852
+ this.snapshotWarnings = await this.checkSnapshotFreshness();
823
853
  const report = this.assembleReport();
824
854
  this.cleanup();
825
855
  this.emit("session.done", report);
@@ -883,7 +913,7 @@ var CollabSession = class extends EventEmitter {
883
913
  const defaults = {
884
914
  "bug-hunter": { maxIterations: 2e3, maxToolCalls: 5e3, timeoutMs: 10 * 60 * 1e3 },
885
915
  "refactor-planner": { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 },
886
- "critic": { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
916
+ critic: { maxIterations: 1e3, maxToolCalls: 3e3, timeoutMs: 6 * 60 * 1e3 }
887
917
  };
888
918
  return defaults[role] ?? { maxIterations: 1500, maxToolCalls: 4e3, timeoutMs: 8 * 60 * 1e3 };
889
919
  }
@@ -963,7 +993,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
963
993
  }
964
994
  wireFleetBus() {
965
995
  const dTool = this.fleetBus.filter("tool.executed", (e) => {
966
- this.progressBySubagent.set(e.subagentId, (this.progressBySubagent.get(e.subagentId) ?? 0) + 1);
996
+ this.progressBySubagent.set(
997
+ e.subagentId,
998
+ (this.progressBySubagent.get(e.subagentId) ?? 0) + 1
999
+ );
967
1000
  });
968
1001
  this.disposers.push(dTool);
969
1002
  const dBudget = this.fleetBus.filter("budget.threshold_reached", (e) => {
@@ -1002,7 +1035,10 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
1002
1035
  return;
1003
1036
  }
1004
1037
  this.lastTimeoutProgress.set(e.subagentId, progress);
1005
- const newLimit = Math.min(Math.ceil((payload.timeoutMs ?? payload.limit) * 2), 24 * 60 * 6e4);
1038
+ const newLimit = Math.min(
1039
+ Math.ceil((payload.timeoutMs ?? payload.limit) * 2),
1040
+ 24 * 60 * 6e4
1041
+ );
1006
1042
  setImmediate(() => {
1007
1043
  payload.extend({ timeoutMs: newLimit });
1008
1044
  });
@@ -1112,15 +1148,18 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
1112
1148
  needs_revision: 1,
1113
1149
  reject: 2
1114
1150
  };
1115
- const overallVerdict = evalList.reduce(
1116
- (worst, eval_) => {
1117
- const w = verdictOrder[worst];
1118
- const c = verdictOrder[eval_.verdict];
1119
- return c > w ? eval_.verdict : worst;
1120
- },
1121
- "approve"
1151
+ const overallVerdict = evalList.reduce((worst, eval_) => {
1152
+ const w = verdictOrder[worst];
1153
+ const c = verdictOrder[eval_.verdict];
1154
+ return c > w ? eval_.verdict : worst;
1155
+ }, "approve");
1156
+ const summary = this.buildMarkdownSummary(
1157
+ bugList,
1158
+ planList,
1159
+ evalList,
1160
+ overallVerdict,
1161
+ disposition
1122
1162
  );
1123
- const summary = this.buildMarkdownSummary(bugList, planList, evalList, overallVerdict, disposition);
1124
1163
  return {
1125
1164
  sessionId: this.sessionId,
1126
1165
  startedAt: this.snapshot.createdAt,
@@ -1131,10 +1170,28 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
1131
1170
  refactorPlans: planList,
1132
1171
  evaluations: evalList,
1133
1172
  alerts: [...this.alerts],
1173
+ ...this.snapshotWarnings.length > 0 ? { snapshotWarnings: this.snapshotWarnings } : {},
1134
1174
  overallVerdict,
1135
1175
  summary
1136
1176
  };
1137
1177
  }
1178
+ async checkSnapshotFreshness() {
1179
+ const warnings = [];
1180
+ for (const file of this.snapshot.files) {
1181
+ if (file.snapshotMtimeMs === void 0 && file.snapshotSizeBytes === void 0) continue;
1182
+ try {
1183
+ const stat5 = await fsp6.stat(file.path);
1184
+ const mtimeChanged = file.snapshotMtimeMs !== void 0 && stat5.mtimeMs > file.snapshotMtimeMs + 1;
1185
+ const sizeChanged = file.snapshotSizeBytes !== void 0 && stat5.size !== file.snapshotSizeBytes;
1186
+ if (mtimeChanged || sizeChanged) {
1187
+ warnings.push(`${file.path} changed after the collab snapshot was captured.`);
1188
+ }
1189
+ } catch {
1190
+ warnings.push(`${file.path} could not be checked after the collab snapshot was captured.`);
1191
+ }
1192
+ }
1193
+ return warnings;
1194
+ }
1138
1195
  buildMarkdownSummary(bugs, plans, evals, overallVerdict, disposition) {
1139
1196
  const lines = [
1140
1197
  `## Collaborative Debugging Report \u2014 ${this.sessionId}`,
@@ -1151,10 +1208,19 @@ Emit each evaluation immediately. Do not wait until you have read all reports.`;
1151
1208
  }
1152
1209
  lines.push("");
1153
1210
  }
1211
+ if (this.snapshotWarnings.length > 0) {
1212
+ lines.push("### Snapshot Warnings", "");
1213
+ for (const warning of this.snapshotWarnings) {
1214
+ lines.push(`- ${warning}`);
1215
+ }
1216
+ lines.push("");
1217
+ }
1154
1218
  if (bugs.length > 0) {
1155
1219
  lines.push("### Bugs Found", "");
1156
1220
  for (const b of bugs) {
1157
- lines.push(`- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`);
1221
+ lines.push(
1222
+ `- **[${b.severity.toUpperCase()}]** \`${b.location.file}:${b.location.line}\` \u2014 ${b.description}`
1223
+ );
1158
1224
  }
1159
1225
  lines.push("");
1160
1226
  }
@@ -3579,16 +3645,17 @@ Do not add prose, markdown, or code fences.`;
3579
3645
  const raw = (await complete(prompt)).trim();
3580
3646
  const match = raw.match(/\{[\s\S]*\}/);
3581
3647
  if (!match) return null;
3582
- try {
3583
- const parsed = JSON.parse(match[0]);
3584
- if (typeof parsed.role !== "string") return null;
3585
- const role = parsed.role.trim();
3586
- const valid = candidates.some((c) => c.role === role);
3587
- if (!valid) return null;
3588
- return { role, reason: typeof parsed.reason === "string" ? parsed.reason : void 0 };
3589
- } catch {
3590
- return null;
3591
- }
3648
+ const parsed = safeParse(
3649
+ match[0]
3650
+ );
3651
+ if (!parsed.ok || !parsed.value || typeof parsed.value.role !== "string") return null;
3652
+ const role = parsed.value.role.trim();
3653
+ const valid = candidates.some((c) => c.role === role);
3654
+ if (!valid) return null;
3655
+ return {
3656
+ role,
3657
+ reason: typeof parsed.value.reason === "string" ? parsed.value.reason : void 0
3658
+ };
3592
3659
  };
3593
3660
  }
3594
3661
 
@@ -4061,6 +4128,7 @@ var FleetBus = class {
4061
4128
  set.add(handler);
4062
4129
  return () => {
4063
4130
  set.delete(handler);
4131
+ if (set.size === 0) this.byId.delete(subagentId);
4064
4132
  };
4065
4133
  }
4066
4134
  /** Subscribe to one event type across all subagents. */
@@ -4073,6 +4141,7 @@ var FleetBus = class {
4073
4141
  set.add(handler);
4074
4142
  return () => {
4075
4143
  set.delete(handler);
4144
+ if (set.size === 0) this.byType.delete(type);
4076
4145
  };
4077
4146
  }
4078
4147
  /** Subscribe to literally everything. The fleet roll-up uses this. */
@@ -4106,6 +4175,11 @@ var FleetBus = class {
4106
4175
  }
4107
4176
  }
4108
4177
  }
4178
+ clear() {
4179
+ this.byId.clear();
4180
+ this.byType.clear();
4181
+ this.any.clear();
4182
+ }
4109
4183
  };
4110
4184
  var FleetUsageAggregator = class {
4111
4185
  constructor(bus, priceLookup, metaLookup) {
@@ -4301,6 +4375,7 @@ function resolveModelMatrix(matrix, role) {
4301
4375
  }
4302
4376
 
4303
4377
  // src/coordination/subagent-budget.ts
4378
+ var TIMEOUT_PREEMPT_FRACTION = 0.85;
4304
4379
  var BudgetExceededError = class extends Error {
4305
4380
  kind;
4306
4381
  limit;
@@ -5704,15 +5779,38 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
5704
5779
  }
5705
5780
  const start = Date.now();
5706
5781
  let timer = null;
5782
+ let preemptedForLimit = null;
5707
5783
  const timeoutPromise = new Promise((_, reject) => {
5708
5784
  const armFor = (ms) => {
5709
5785
  if (timer) clearTimeout(timer);
5710
5786
  timer = setTimeout(onTick, Math.max(0, ms));
5711
5787
  };
5712
5788
  const scheduleNext = () => {
5713
- const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.timeoutMs ?? initialTimeoutMs) - (Date.now() - start);
5789
+ const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
5790
+ const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
5714
5791
  const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
5715
- armFor(Math.max(25, Math.min(wallRemaining, idleRemaining)));
5792
+ const preemptRemaining = initialTimeoutMs === void 0 || preemptedForLimit === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * TIMEOUT_PREEMPT_FRACTION - (Date.now() - start);
5793
+ armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
5794
+ };
5795
+ const negotiateTimeout = async (used, limit) => {
5796
+ const handler = budget.onThreshold;
5797
+ if (!handler) return "stop";
5798
+ const result = handler({
5799
+ kind: "timeout",
5800
+ used,
5801
+ limit,
5802
+ requestDecision: () => new Promise((resolveDecision) => {
5803
+ budget._events?.emit("budget.threshold_reached", {
5804
+ kind: "timeout",
5805
+ used,
5806
+ limit,
5807
+ timeoutMs: 6e4,
5808
+ extend: (extra) => resolveDecision({ extend: extra }),
5809
+ deny: () => resolveDecision("stop")
5810
+ });
5811
+ })
5812
+ });
5813
+ return typeof result === "string" ? result : await result;
5716
5814
  };
5717
5815
  const onTick = async () => {
5718
5816
  const elapsed = Date.now() - start;
@@ -5725,6 +5823,21 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
5725
5823
  reject(new BudgetExceededError("timeout", idleLimit ?? 0, budget.idleMs()));
5726
5824
  return;
5727
5825
  }
5826
+ if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptedForLimit !== wallLimit && elapsed >= wallLimit * TIMEOUT_PREEMPT_FRACTION) {
5827
+ try {
5828
+ const decision = await negotiateTimeout(elapsed, wallLimit);
5829
+ if (typeof decision !== "string" && decision.extend.timeoutMs !== void 0) {
5830
+ budget.limits.timeoutMs = decision.extend.timeoutMs;
5831
+ preemptedForLimit = null;
5832
+ } else {
5833
+ preemptedForLimit = wallLimit;
5834
+ }
5835
+ } catch {
5836
+ preemptedForLimit = wallLimit;
5837
+ }
5838
+ scheduleNext();
5839
+ return;
5840
+ }
5728
5841
  if (!wallExceeded) {
5729
5842
  scheduleNext();
5730
5843
  return;
@@ -5736,28 +5849,15 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
5736
5849
  return;
5737
5850
  }
5738
5851
  try {
5739
- const result = budget.onThreshold({
5740
- kind: "timeout",
5741
- used: elapsed,
5742
- limit,
5743
- requestDecision: () => new Promise((resolveDecision) => {
5744
- budget._events?.emit("budget.threshold_reached", {
5745
- kind: "timeout",
5746
- used: elapsed,
5747
- limit,
5748
- timeoutMs: 6e4,
5749
- extend: (extra) => resolveDecision({ extend: extra }),
5750
- deny: () => resolveDecision("stop")
5751
- });
5752
- })
5753
- });
5754
- const decision = typeof result === "string" ? result : await result;
5852
+ const decision = await negotiateTimeout(elapsed, limit);
5755
5853
  if (decision === "continue" || decision === "throw" || decision === "stop") {
5854
+ preemptedForLimit = null;
5756
5855
  armFor(Math.max(1e3, limit));
5757
5856
  return;
5758
5857
  }
5759
5858
  if (decision.extend.timeoutMs !== void 0) {
5760
5859
  budget.limits.timeoutMs = decision.extend.timeoutMs;
5860
+ preemptedForLimit = null;
5761
5861
  scheduleNext();
5762
5862
  return;
5763
5863
  }
@@ -6925,7 +7025,9 @@ var Director = class _Director {
6925
7025
  let toolUses = 0;
6926
7026
  for (const line of targetLines) {
6927
7027
  try {
6928
- const ev = JSON.parse(line);
7028
+ const parsed = safeParse(line);
7029
+ if (!parsed.ok || !parsed.value) continue;
7030
+ const ev = parsed.value;
6929
7031
  if (ev.type === "assistant" && typeof ev.text === "string") {
6930
7032
  lastAssistantText = ev.text;
6931
7033
  } else if (ev.type === "stop" && ev.stopReason) {
@@ -7239,7 +7341,7 @@ function createDelegateTool(opts) {
7239
7341
  const offIter = dir.fleet.filter("iteration.started", bump);
7240
7342
  const offProgress = dir.fleet.filter("tool.progress", bump);
7241
7343
  arm();
7242
- dir.awaitTasks([taskId]).then((r) => finish(r[0] ?? { __timeout: true })).catch(() => finish({ __timeout: true }));
7344
+ dir.awaitTasks([taskId]).then((r) => finish(r[0] ?? { __emptyResult: true })).catch(() => finish({ __timeout: true }));
7243
7345
  });
7244
7346
  if ("__timeout" in result) {
7245
7347
  const partial2 = await readSubagentPartial(opts, subagentId);
@@ -7264,6 +7366,29 @@ function createDelegateTool(opts) {
7264
7366
  partial: partial2
7265
7367
  };
7266
7368
  }
7369
+ if ("__emptyResult" in result) {
7370
+ const partial2 = await readSubagentPartial(opts, subagentId);
7371
+ opts.events?.emit("delegate.completed", {
7372
+ target,
7373
+ task: i.task,
7374
+ ok: false,
7375
+ status: "empty_result",
7376
+ summary: `[${target}] completed without a task result`,
7377
+ durationMs: 0,
7378
+ iterations: partial2?.events ?? 0,
7379
+ toolCalls: partial2?.toolUsesObserved ?? 0,
7380
+ subagentId
7381
+ });
7382
+ return {
7383
+ ok: false,
7384
+ stopReason: "error",
7385
+ error: "Director returned no task result for the delegated task.",
7386
+ hint: "Check fleet state with /fleet status, then retry or reassign the task.",
7387
+ subagentId,
7388
+ taskId,
7389
+ partial: partial2
7390
+ };
7391
+ }
7267
7392
  const baseStopReason = result.status === "success" ? "end_turn" : result.status === "timeout" ? "subagent_timeout" : result.status === "stopped" ? "aborted" : "budget_exhausted";
7268
7393
  const partial = result.status === "success" ? void 0 : await readSubagentPartial(opts, subagentId);
7269
7394
  const errorKind = result.error?.kind;
@@ -7436,7 +7561,9 @@ async function readSubagentPartial(opts, subagentId) {
7436
7561
  let toolUses = 0;
7437
7562
  for (const line of lines) {
7438
7563
  try {
7439
- const ev = JSON.parse(line);
7564
+ const parsed = safeParse(line);
7565
+ if (!parsed.ok || !parsed.value) continue;
7566
+ const ev = parsed.value;
7440
7567
  if (ev.type === "tool_use") toolUses += 1;
7441
7568
  if (ev.type === "llm_response") {
7442
7569
  if (typeof ev.stopReason === "string") lastStopReason = ev.stopReason;
@@ -7998,8 +8125,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
7998
8125
  return JSON.parse(raw);
7999
8126
  } catch {
8000
8127
  const full = this.sessionPath(id, ".jsonl");
8001
- const stat4 = await fsp6.stat(full);
8002
- const summary = await this.summarize(id, stat4.mtime.toISOString());
8128
+ const stat5 = await fsp6.stat(full);
8129
+ const summary = await this.summarize(id, stat5.mtime.toISOString());
8003
8130
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
8004
8131
  console.warn(
8005
8132
  `[session-store] Failed to write manifest for "${id}":`,
@@ -8047,8 +8174,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
8047
8174
  if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
8048
8175
  const jsonlPath = path4.join(dateDir, file.name);
8049
8176
  try {
8050
- const stat4 = await fsp6.stat(jsonlPath);
8051
- if (stat4.mtimeMs >= cutoff) continue;
8177
+ const stat5 = await fsp6.stat(jsonlPath);
8178
+ if (stat5.mtimeMs >= cutoff) continue;
8052
8179
  } catch {
8053
8180
  continue;
8054
8181
  }