@wrongstack/core 0.87.0 → 0.89.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 (83) hide show
  1. package/dist/{agent-bridge-C9P_HPez.d.ts → agent-bridge-DbVe1dHT.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-2Aq0jOSj.d.ts → agent-subagent-runner-C6eqID1t.d.ts} +477 -476
  3. package/dist/{events-DnRqXaZ3.d.ts → brain-s9DyD_Vf.d.ts} +185 -92
  4. package/dist/{compactor-CJq7LQev.d.ts → compactor-DXLxLcmU.d.ts} +2 -2
  5. package/dist/{config-_DZ7dN-T.d.ts → config-84VaZpH6.d.ts} +45 -38
  6. package/dist/{context-ToHAp4-U.d.ts → context-CNRYfhUv.d.ts} +1 -1
  7. package/dist/coordination/index.d.ts +16 -17
  8. package/dist/coordination/index.js +208 -93
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/{default-config-DEXI4jsl.d.ts → default-config-CXsDvOmP.d.ts} +3 -1
  11. package/dist/defaults/index.d.ts +32 -34
  12. package/dist/defaults/index.js +744 -274
  13. package/dist/defaults/index.js.map +1 -1
  14. package/dist/{director-state-CgIc30qi.d.ts → director-state-BfeCUbmk.d.ts} +1 -1
  15. package/dist/execution/index.d.ts +18 -19
  16. package/dist/execution/index.js +79 -64
  17. package/dist/execution/index.js.map +1 -1
  18. package/dist/extension/index.d.ts +9 -10
  19. package/dist/extension/index.js +8 -6
  20. package/dist/extension/index.js.map +1 -1
  21. package/dist/{index-BNOLadHw.d.ts → index-CqrroYcU.d.ts} +13 -15
  22. package/dist/{index-N0_c4bHQ.d.ts → index-D3Nax3YU.d.ts} +11 -10
  23. package/dist/index.d.ts +191 -178
  24. package/dist/index.js +1419 -526
  25. package/dist/index.js.map +1 -1
  26. package/dist/infrastructure/index.d.ts +9 -9
  27. package/dist/infrastructure/index.js +16 -5
  28. package/dist/infrastructure/index.js.map +1 -1
  29. package/dist/kernel/index.d.ts +14 -16
  30. package/dist/kernel/index.js +17 -0
  31. package/dist/kernel/index.js.map +1 -1
  32. package/dist/{logger-B72yyPc6.d.ts → logger-B63L5bTg.d.ts} +1 -1
  33. package/dist/{logger-C_27pj9i.d.ts → logger-B9J5puGM.d.ts} +1 -1
  34. package/dist/{mcp-servers-Dck3T85_.d.ts → mcp-servers-D3E5ixAR.d.ts} +3 -3
  35. package/dist/{mode-CHo2XtHs.d.ts → mode-ARA3HrkY.d.ts} +1 -1
  36. package/dist/models/index.d.ts +5 -5
  37. package/dist/models/index.js +8 -6
  38. package/dist/models/index.js.map +1 -1
  39. package/dist/{models-registry-Be3osGt5.d.ts → models-registry-B6_KfS65.d.ts} +1 -1
  40. package/dist/{models-registry-Boz639EI.d.ts → models-registry-DU64QxQa.d.ts} +1 -1
  41. package/dist/{multi-agent-coordinator-DllpCVkF.d.ts → multi-agent-coordinator-DHNrjPaA.d.ts} +2 -2
  42. package/dist/{null-fleet-bus-BY0AN-sr.d.ts → null-fleet-bus-BzzciAc4.d.ts} +39 -32
  43. package/dist/observability/index.d.ts +3 -3
  44. package/dist/{observability-CoSNZdhX.d.ts → observability-D-HZN_mF.d.ts} +1 -1
  45. package/dist/{parallel-eternal-engine-D402RASp.d.ts → parallel-eternal-engine-CTpcrb9O.d.ts} +34 -33
  46. package/dist/{path-resolver-UPFTsDyD.d.ts → path-resolver-B7hgMAVe.d.ts} +3 -3
  47. package/dist/{permission-14CChMmO.d.ts → permission-BDv7z0mk.d.ts} +7 -2
  48. package/dist/{permission-policy-gW5htOo1.d.ts → permission-policy-dF74EpDp.d.ts} +2 -2
  49. package/dist/{system-prompt-C0rLCeyn.d.ts → pipeline-C1Ad9OjI.d.ts} +73 -73
  50. package/dist/{plan-templates-DRvPgkfZ.d.ts → plan-templates-CNphsz0n.d.ts} +79 -11
  51. package/dist/{provider-runner-COAJM8tC.d.ts → provider-runner-cqvlB_oS.d.ts} +5 -5
  52. package/dist/{retry-policy-DSu6O6rD.d.ts → retry-policy-BcmuT_V0.d.ts} +2 -2
  53. package/dist/sdd/index.d.ts +12 -33
  54. package/dist/sdd/index.js +110 -50
  55. package/dist/sdd/index.js.map +1 -1
  56. package/dist/{secret-scrubber-yGBFQYju.d.ts → secret-vault-DrOhc2i5.d.ts} +7 -7
  57. package/dist/security/index.d.ts +4 -5
  58. package/dist/security/index.js +22 -4
  59. package/dist/security/index.js.map +1 -1
  60. package/dist/{selector-11-fm95U.d.ts → selector-C7wcdqMA.d.ts} +1 -1
  61. package/dist/{session-event-bridge-D0u-x576.d.ts → session-event-bridge-IVzs2GpE.d.ts} +1 -1
  62. package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
  63. package/dist/{skill-BJeF2DwY.d.ts → skill-BJxzIyyN.d.ts} +1 -1
  64. package/dist/skills/index.d.ts +1 -1
  65. package/dist/skills/index.js +3 -9
  66. package/dist/skills/index.js.map +1 -1
  67. package/dist/storage/index.d.ts +134 -14
  68. package/dist/storage/index.js +982 -248
  69. package/dist/storage/index.js.map +1 -1
  70. package/dist/{task-graph-CikNdRTG.d.ts → task-graph-DJBqO0EY.d.ts} +1 -1
  71. package/dist/types/index.d.ts +26 -28
  72. package/dist/types/index.js +62 -37
  73. package/dist/types/index.js.map +1 -1
  74. package/dist/utils/expect-defined.d.ts +7 -0
  75. package/dist/utils/expect-defined.js +11 -0
  76. package/dist/utils/expect-defined.js.map +1 -0
  77. package/dist/utils/index.d.ts +11 -20
  78. package/dist/utils/index.js +61 -43
  79. package/dist/utils/index.js.map +1 -1
  80. package/dist/{wstack-paths-BQMvEllz.d.ts → wstack-paths-_lqjzErq.d.ts} +1 -1
  81. package/package.json +5 -1
  82. package/dist/memory-CEXuo7sz.d.ts +0 -16
  83. package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
@@ -2,15 +2,9 @@ import { randomUUID, randomBytes } from 'crypto';
2
2
  import * as fsp6 from 'fs/promises';
3
3
  import * as path4 from 'path';
4
4
  import { isAbsolute, resolve } from 'path';
5
+ import { hostname } from 'os';
5
6
  import { EventEmitter } from 'events';
6
7
 
7
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
- }) : x)(function(x) {
10
- if (typeof require !== "undefined") return require.apply(this, arguments);
11
- throw Error('Dynamic require of "' + x + '" is not supported');
12
- });
13
-
14
8
  // src/coordination/brain.ts
15
9
  var ObservableBrainArbiter = class {
16
10
  constructor(inner, events) {
@@ -172,8 +166,8 @@ async function atomicWrite(targetPath, content, opts = {}) {
172
166
  }
173
167
  let mode;
174
168
  try {
175
- const stat4 = await fsp6.stat(targetPath);
176
- mode = stat4.mode & 511;
169
+ const stat5 = await fsp6.stat(targetPath);
170
+ mode = stat5.mode & 511;
177
171
  } catch {
178
172
  mode = opts.mode;
179
173
  }
@@ -236,7 +230,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
236
230
  }
237
231
  const lock = {
238
232
  pid: processId,
239
- hostname: __require("os").hostname(),
233
+ hostname: hostname(),
240
234
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
241
235
  };
242
236
  await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
@@ -381,6 +375,21 @@ var DirectorStateCheckpoint = class {
381
375
  }
382
376
  };
383
377
 
378
+ // src/utils/safe-json.ts
379
+ function safeParse(input, maxBytes = 5e6) {
380
+ if (input.length > maxBytes) {
381
+ return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
382
+ }
383
+ try {
384
+ return { ok: true, value: JSON.parse(input) };
385
+ } catch (err) {
386
+ return {
387
+ ok: false,
388
+ error: err instanceof Error ? err.message : String(err)
389
+ };
390
+ }
391
+ }
392
+
384
393
  // src/coordination/in-memory-transport.ts
385
394
  var InMemoryBridgeTransport = class {
386
395
  subs = /* @__PURE__ */ new Map();
@@ -528,7 +537,9 @@ function createMessage(type, from, payload, to) {
528
537
  priority: "normal"
529
538
  };
530
539
  }
531
- function expectDefined(value) {
540
+
541
+ // src/utils/expect-defined.ts
542
+ function expectDefined(value, label) {
532
543
  if (value === null || value === void 0) {
533
544
  throw new Error("Expected value to be defined");
534
545
  }
@@ -621,8 +632,8 @@ async function expandGlob(pattern) {
621
632
  for (const e of entries) {
622
633
  const full = `${dir}${SEP}${e}`;
623
634
  try {
624
- const stat4 = await fsp6.stat(full);
625
- if (stat4.isDirectory()) await walk(full, rest);
635
+ const stat5 = await fsp6.stat(full);
636
+ if (stat5.isDirectory()) await walk(full, rest);
626
637
  } catch {
627
638
  }
628
639
  }
@@ -639,8 +650,8 @@ async function expandGlob(pattern) {
639
650
  if (entries.includes(seg)) {
640
651
  const full = `${dir}${SEP}${seg}`;
641
652
  try {
642
- const stat4 = await fsp6.stat(full);
643
- if (stat4.isDirectory()) await walk(full, rest);
653
+ const stat5 = await fsp6.stat(full);
654
+ if (stat5.isDirectory()) await walk(full, rest);
644
655
  } catch {
645
656
  }
646
657
  }
@@ -673,7 +684,9 @@ var CollabSession = class extends EventEmitter {
673
684
  settled = false;
674
685
  timeoutMs;
675
686
  cancelled = false;
687
+ _raceResolved = false;
676
688
  alerts = [];
689
+ snapshotWarnings = [];
677
690
  /** Tracks tool call counts per subagent for progress-based timeout decisions. */
678
691
  progressBySubagent = /* @__PURE__ */ new Map();
679
692
  /** Last tool call count when a timeout warning was handled. */
@@ -743,10 +756,19 @@ var CollabSession = class extends EventEmitter {
743
756
  }
744
757
  for (const filePath of allFiles) {
745
758
  try {
746
- const content = await fsp6.readFile(filePath, "utf8");
759
+ const [content, stat5] = await Promise.all([
760
+ fsp6.readFile(filePath, "utf8"),
761
+ fsp6.stat(filePath)
762
+ ]);
747
763
  const ext = filePath.split(".").pop() ?? "";
748
764
  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 });
765
+ this.snapshot.files.push({
766
+ path: filePath,
767
+ content,
768
+ language,
769
+ snapshotMtimeMs: stat5.mtimeMs,
770
+ snapshotSizeBytes: stat5.size
771
+ });
750
772
  } catch {
751
773
  this.snapshot.files.push({ path: filePath, content: "", language: void 0 });
752
774
  }
@@ -759,7 +781,7 @@ var CollabSession = class extends EventEmitter {
759
781
  * Safe to call multiple times (idempotent after first call).
760
782
  */
761
783
  cancel(reason = "Director cancelled collab session") {
762
- if (this.settled) return;
784
+ if (this.cancelled) return;
763
785
  this.cancelled = true;
764
786
  if (this._timeoutTimer) {
765
787
  clearTimeout(this._timeoutTimer);
@@ -769,7 +791,11 @@ var CollabSession = class extends EventEmitter {
769
791
  subagentId: this.director.id,
770
792
  ts: Date.now(),
771
793
  type: "director.cancel_collab",
772
- payload: { sessionId: this.sessionId, reason, cancelledAt: (/* @__PURE__ */ new Date()).toISOString() }
794
+ payload: {
795
+ sessionId: this.sessionId,
796
+ reason,
797
+ cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
798
+ }
773
799
  });
774
800
  this.fleetBus.emit({
775
801
  subagentId: this.director.id,
@@ -793,6 +819,7 @@ var CollabSession = class extends EventEmitter {
793
819
  this.subagentIds.set("critic", criticId);
794
820
  const timeout = new Promise((_, reject) => {
795
821
  this._timeoutTimer = setTimeout(() => {
822
+ if (this._raceResolved) return;
796
823
  this.cancel("Session-level timeout reached");
797
824
  reject(new Error(`CollabSession timed out after ${this.timeoutMs}ms`));
798
825
  }, this.timeoutMs);
@@ -808,6 +835,7 @@ var CollabSession = class extends EventEmitter {
808
835
  timeout
809
836
  ]);
810
837
  } catch (err) {
838
+ this._raceResolved = true;
811
839
  if (this._timeoutTimer) {
812
840
  clearTimeout(this._timeoutTimer);
813
841
  this._timeoutTimer = void 0;
@@ -820,7 +848,9 @@ var CollabSession = class extends EventEmitter {
820
848
  for (const result of results?.flat() ?? []) {
821
849
  await this.parseAndEmit(result);
822
850
  }
851
+ this.snapshotWarnings = await this.checkSnapshotFreshness();
823
852
  const report = this.assembleReport();
853
+ this._raceResolved = true;
824
854
  this.cleanup();
825
855
  this.emit("session.done", report);
826
856
  return 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;
@@ -7471,15 +7598,7 @@ function makeAgentSubagentRunner(opts) {
7471
7598
  let budgetError = null;
7472
7599
  const onBudgetError = (err) => {
7473
7600
  if (err instanceof BudgetThresholdSignal) {
7474
- err.decision.then((decision) => {
7475
- if (decision === "stop") {
7476
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
7477
- aborter.abort();
7478
- }
7479
- }).catch(() => {
7480
- budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
7481
- aborter.abort();
7482
- });
7601
+ budgetError = err;
7483
7602
  return;
7484
7603
  }
7485
7604
  aborter.abort();
@@ -7587,15 +7706,23 @@ function makeAgentSubagentRunner(opts) {
7587
7706
  }
7588
7707
  if (budgetError) {
7589
7708
  if ("decision" in budgetError) {
7590
- const decision = await budgetError.decision;
7591
- if (decision === "stop") {
7709
+ try {
7710
+ const decision = await budgetError.decision;
7711
+ if (decision === "stop") {
7712
+ budgetError = new BudgetExceededError(
7713
+ budgetError.kind,
7714
+ budgetError.limit,
7715
+ budgetError.used
7716
+ );
7717
+ } else {
7718
+ budgetError = null;
7719
+ }
7720
+ } catch {
7592
7721
  budgetError = new BudgetExceededError(
7593
7722
  budgetError.kind,
7594
7723
  budgetError.limit,
7595
7724
  budgetError.used
7596
7725
  );
7597
- } else {
7598
- budgetError = null;
7599
7726
  }
7600
7727
  }
7601
7728
  if (budgetError) throw budgetError;
@@ -7629,12 +7756,6 @@ function defaultFormatTaskInput(task) {
7629
7756
  }
7630
7757
 
7631
7758
  // src/utils/message-invariants.ts
7632
- function expectDefined2(value) {
7633
- if (value === null || value === void 0) {
7634
- throw new Error("Expected value to be defined");
7635
- }
7636
- return value;
7637
- }
7638
7759
  function repairToolUseAdjacency(messages) {
7639
7760
  const removedToolUses = [];
7640
7761
  const removedToolResults = [];
@@ -7642,7 +7763,7 @@ function repairToolUseAdjacency(messages) {
7642
7763
  let changed = false;
7643
7764
  const out = [];
7644
7765
  for (let i = 0; i < messages.length; i++) {
7645
- const original = expectDefined2(messages[i]);
7766
+ const original = expectDefined(messages[i]);
7646
7767
  let msg = original;
7647
7768
  if (hasToolUse(msg)) {
7648
7769
  const nextIds = toolResultIds(messages[i + 1]);
@@ -7727,12 +7848,6 @@ function isEmptyMessage(msg) {
7727
7848
  }
7728
7849
 
7729
7850
  // src/storage/session-store.ts
7730
- function expectDefined3(value) {
7731
- if (value === null || value === void 0) {
7732
- throw new Error("Expected value to be defined");
7733
- }
7734
- return value;
7735
- }
7736
7851
  function sanitizeModel(model) {
7737
7852
  return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
7738
7853
  }
@@ -7998,8 +8113,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
7998
8113
  return JSON.parse(raw);
7999
8114
  } catch {
8000
8115
  const full = this.sessionPath(id, ".jsonl");
8001
- const stat4 = await fsp6.stat(full);
8002
- const summary = await this.summarize(id, stat4.mtime.toISOString());
8116
+ const stat5 = await fsp6.stat(full);
8117
+ const summary = await this.summarize(id, stat5.mtime.toISOString());
8003
8118
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
8004
8119
  console.warn(
8005
8120
  `[session-store] Failed to write manifest for "${id}":`,
@@ -8047,8 +8162,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
8047
8162
  if (!file.isFile() || !file.name.endsWith(".jsonl")) continue;
8048
8163
  const jsonlPath = path4.join(dateDir, file.name);
8049
8164
  try {
8050
- const stat4 = await fsp6.stat(jsonlPath);
8051
- if (stat4.mtimeMs >= cutoff) continue;
8165
+ const stat5 = await fsp6.stat(jsonlPath);
8166
+ if (stat5.mtimeMs >= cutoff) continue;
8052
8167
  } catch {
8053
8168
  continue;
8054
8169
  }
@@ -8443,7 +8558,7 @@ var FileSessionWriter = class {
8443
8558
  let targetCheckpointLine = -1;
8444
8559
  let afterTarget = false;
8445
8560
  for (let i = 0; i < lines.length; i++) {
8446
- const line = expectDefined3(lines[i]);
8561
+ const line = expectDefined(lines[i]);
8447
8562
  if (!line.trim()) continue;
8448
8563
  let event;
8449
8564
  try {