@wrongstack/core 0.272.0 → 0.272.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 (47) hide show
  1. package/dist/{agent-bridge-jVSZiygR.d.ts → agent-bridge-DFQYEeXf.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-DOLIwBRo.d.ts → agent-subagent-runner-BZa_IEcd.d.ts} +4 -4
  3. package/dist/{brain-CdbbJWi3.d.ts → brain-etbcbRwV.d.ts} +24 -1
  4. package/dist/{config-D2DGoGSQ.d.ts → config-rRS8yorV.d.ts} +43 -1
  5. package/dist/coordination/index.d.ts +74 -14
  6. package/dist/coordination/index.js +285 -97
  7. package/dist/coordination/index.js.map +1 -1
  8. package/dist/defaults/index.d.ts +16 -16
  9. package/dist/defaults/index.js +209 -140
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/execution/index.d.ts +9 -9
  12. package/dist/extension/index.d.ts +4 -4
  13. package/dist/{global-mailbox-CQj_C9Dp.d.ts → global-mailbox-DJ4EoRr0.d.ts} +7 -3
  14. package/dist/{goal-preamble-ZXDjjR1y.d.ts → goal-preamble-hM8BH7TK.d.ts} +5 -5
  15. package/dist/{goal-store-CcJBd-g1.d.ts → goal-store-CWlbT0TO.d.ts} +1 -1
  16. package/dist/hq/index.d.ts +6 -4
  17. package/dist/hq/index.js +14 -6
  18. package/dist/hq/index.js.map +1 -1
  19. package/dist/{index-Qo4kTzgw.d.ts → index-DWm_PE9L.d.ts} +3 -3
  20. package/dist/{index-BL7BAx0p.d.ts → index-DqW4o62H.d.ts} +4 -4
  21. package/dist/index.d.ts +27 -27
  22. package/dist/index.js +571 -215
  23. package/dist/index.js.map +1 -1
  24. package/dist/infrastructure/index.d.ts +4 -4
  25. package/dist/kernel/index.d.ts +5 -5
  26. package/dist/kernel/index.js.map +1 -1
  27. package/dist/{mcp-servers-DS-YUXvF.d.ts → mcp-servers-BpWHTKlE.d.ts} +1 -1
  28. package/dist/models/index.d.ts +3 -3
  29. package/dist/{models-registry-DP6pGHet.d.ts → models-registry-CXQFUn5t.d.ts} +1 -1
  30. package/dist/{multi-agent-coordinator-BvbdNQ14.d.ts → multi-agent-coordinator-jyimfo7D.d.ts} +1 -1
  31. package/dist/{null-fleet-bus-BxTfXBKo.d.ts → null-fleet-bus-DOGQcvrY.d.ts} +5 -5
  32. package/dist/observability/index.d.ts +1 -1
  33. package/dist/{parallel-eternal-engine-Cf-GTegR.d.ts → parallel-eternal-engine-rItJBYp9.d.ts} +6 -6
  34. package/dist/{path-resolver-DztfnFcv.d.ts → path-resolver-DrpF5MGK.d.ts} +2 -2
  35. package/dist/{pipeline-sNIkhXeB.d.ts → pipeline-Ckkn3AOA.d.ts} +1 -1
  36. package/dist/{plan-templates-DYiKFmEb.d.ts → plan-templates-BvHw5Znw.d.ts} +24 -6
  37. package/dist/{provider-model-resolve-dYAbTs_i.d.ts → provider-model-resolve-nZqnCeaR.d.ts} +1 -1
  38. package/dist/{provider-runner-Dw8x0F7u.d.ts → provider-runner-zVOn1p67.d.ts} +1 -1
  39. package/dist/sdd/index.d.ts +5 -5
  40. package/dist/storage/index.d.ts +12 -7
  41. package/dist/storage/index.js +250 -111
  42. package/dist/storage/index.js.map +1 -1
  43. package/dist/tools/index.d.ts +1 -1
  44. package/dist/types/index.d.ts +13 -13
  45. package/dist/types/index.js.map +1 -1
  46. package/dist/utils/index.d.ts +1 -1
  47. package/package.json +1 -1
@@ -107,7 +107,7 @@ async function withFileLock(targetPath, fn, opts = {}) {
107
107
  if (Date.now() - started >= timeoutMs) {
108
108
  throw new Error(`Timed out waiting for file lock: ${targetPath}`);
109
109
  }
110
- await new Promise((resolve6) => setTimeout(resolve6, 25));
110
+ await new Promise((resolve7) => setTimeout(resolve7, 25));
111
111
  }
112
112
  }
113
113
  try {
@@ -140,7 +140,7 @@ async function renameWithRetry(from, to) {
140
140
  if (!code || !TRANSIENT_RENAME_CODES.has(code) || i === delays.length) {
141
141
  throw err;
142
142
  }
143
- await new Promise((resolve6) => setTimeout(resolve6, delays[i]));
143
+ await new Promise((resolve7) => setTimeout(resolve7, delays[i]));
144
144
  }
145
145
  }
146
146
  throw lastErr;
@@ -882,11 +882,11 @@ function validateAgainstSchema(value, schema) {
882
882
  walk(value, schema, "", errors);
883
883
  return { ok: errors.length === 0, errors };
884
884
  }
885
- function walk(value, schema, path21, errors) {
885
+ function walk(value, schema, path22, errors) {
886
886
  if (schema.enum !== void 0) {
887
887
  if (!schema.enum.some((e) => deepEqual(e, value))) {
888
888
  errors.push({
889
- path: path21 || "<root>",
889
+ path: path22 || "<root>",
890
890
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
891
891
  });
892
892
  return;
@@ -895,7 +895,7 @@ function walk(value, schema, path21, errors) {
895
895
  if (typeof schema.type === "string") {
896
896
  if (!checkType(value, schema.type)) {
897
897
  errors.push({
898
- path: path21 || "<root>",
898
+ path: path22 || "<root>",
899
899
  message: `expected ${schema.type}, got ${describeType(value)}`
900
900
  });
901
901
  return;
@@ -905,20 +905,20 @@ function walk(value, schema, path21, errors) {
905
905
  const obj = value;
906
906
  for (const req of schema.required ?? []) {
907
907
  if (!(req in obj)) {
908
- errors.push({ path: joinPath(path21, req), message: "required property missing" });
908
+ errors.push({ path: joinPath(path22, req), message: "required property missing" });
909
909
  }
910
910
  }
911
911
  if (schema.properties) {
912
912
  for (const [key, subSchema] of Object.entries(schema.properties)) {
913
913
  if (key in obj) {
914
- walk(obj[key], subSchema, joinPath(path21, key), errors);
914
+ walk(obj[key], subSchema, joinPath(path22, key), errors);
915
915
  }
916
916
  }
917
917
  }
918
918
  }
919
919
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
920
920
  for (let i = 0; i < value.length; i++) {
921
- walk(value[i], schema.items, `${path21}[${i}]`, errors);
921
+ walk(value[i], schema.items, `${path22}[${i}]`, errors);
922
922
  }
923
923
  }
924
924
  }
@@ -1088,7 +1088,7 @@ function safeParse(input, maxBytes = 5e6) {
1088
1088
 
1089
1089
  // src/utils/sleep.ts
1090
1090
  function sleep(ms) {
1091
- return new Promise((resolve6) => setTimeout(resolve6, ms));
1091
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
1092
1092
  }
1093
1093
 
1094
1094
  // src/utils/string.ts
@@ -2129,7 +2129,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2129
2129
  this._loadCache.clear();
2130
2130
  }
2131
2131
  }
2132
- // ── Storage event helpers ───────────────────────────────────────────────────
2132
+ // ── Storage event helpers ───────────────────────────────────────────────────
2133
2133
  emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
2134
2134
  this.events?.emit("storage.read", {
2135
2135
  sessionId,
@@ -2244,7 +2244,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2244
2244
  this.events,
2245
2245
  {
2246
2246
  resumed: true,
2247
- // Shard directory (sessions/<date>/) must match create() so the
2247
+ // Shard directory (sessions/<date>/) — must match create() so the
2248
2248
  // .summary.json sidecar lands next to the JSONL instead of the
2249
2249
  // sessions root (where summaryFor() would never find it).
2250
2250
  dir: path4.dirname(file),
@@ -2285,19 +2285,93 @@ var DefaultSessionStore = class _DefaultSessionStore {
2285
2285
  const raw = await fsp2.readFile(file, "utf8");
2286
2286
  const lines = raw.split("\n").filter((l) => l.trim());
2287
2287
  const events = [];
2288
+ let sessionStartEvent;
2289
+ let sessionEndEvent;
2290
+ let sessionModel;
2291
+ let sessionProvider;
2292
+ let sessionPendingToolUses;
2293
+ const messages = [];
2294
+ const openToolUses = /* @__PURE__ */ new Set();
2295
+ let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
2288
2296
  for (const line of lines) {
2289
2297
  try {
2290
2298
  const parsed = JSON.parse(line);
2291
2299
  if (parsed !== null && typeof parsed === "object" && typeof parsed.type === "string" && typeof parsed.ts === "string") {
2292
- events.push(parsed);
2300
+ const ev = parsed;
2301
+ events.push(ev);
2302
+ if (ev.type === "session_start" && !sessionStartEvent) {
2303
+ sessionStartEvent = ev;
2304
+ sessionModel = ev.model;
2305
+ sessionProvider = ev.provider;
2306
+ }
2307
+ if (ev.type === "session_end") {
2308
+ sessionEndEvent = ev;
2309
+ sessionPendingToolUses = ev.pendingToolUses;
2310
+ }
2311
+ if (ev.type === "user_input") {
2312
+ openToolUses.clear();
2313
+ messages.push({ role: "user", content: ev.content, ts: ev.ts });
2314
+ } else if (ev.type === "llm_response") {
2315
+ messages.push({ role: "assistant", content: ev.content, ts: ev.ts });
2316
+ for (const b of ev.content) {
2317
+ if (b.type === "tool_use") openToolUses.add(b.id);
2318
+ }
2319
+ usage = {
2320
+ input: usage.input + (ev.usage.input ?? 0),
2321
+ output: usage.output + (ev.usage.output ?? 0),
2322
+ cacheRead: (usage.cacheRead ?? 0) + (ev.usage.cacheRead ?? 0),
2323
+ cacheWrite: (usage.cacheWrite ?? 0) + (ev.usage.cacheWrite ?? 0)
2324
+ };
2325
+ } else if (ev.type === "tool_result") {
2326
+ if (!openToolUses.has(ev.id)) {
2327
+ this.events?.emit("session.damaged", {
2328
+ sessionId: id,
2329
+ detail: `Orphan tool_result "${ev.id}" has no matching tool_use`
2330
+ });
2331
+ continue;
2332
+ }
2333
+ openToolUses.delete(ev.id);
2334
+ const resultBlock = {
2335
+ type: "tool_result",
2336
+ tool_use_id: ev.id,
2337
+ content: typeof ev.content === "string" ? ev.content : JSON.stringify(ev.content),
2338
+ is_error: ev.isError
2339
+ };
2340
+ const last = messages[messages.length - 1];
2341
+ const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
2342
+ if (lastIsToolResultUser && Array.isArray(last.content)) {
2343
+ last.content.push(resultBlock);
2344
+ } else {
2345
+ messages.push({ role: "user", content: [resultBlock], ts: ev.ts });
2346
+ }
2347
+ }
2293
2348
  }
2294
2349
  } catch {
2295
2350
  }
2296
2351
  }
2297
- const meta = this.metaFromEvents(id, events);
2298
- const { messages, usage } = this.replay(events, id);
2352
+ if (openToolUses.size > 0) {
2353
+ this.events?.emit("session.damaged", {
2354
+ sessionId: id,
2355
+ detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
2356
+ });
2357
+ }
2358
+ const repaired = repairToolUseAdjacency(messages);
2359
+ if (repaired.report.changed) {
2360
+ this.events?.emit("session.damaged", {
2361
+ sessionId: id,
2362
+ detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
2363
+ });
2364
+ }
2365
+ const meta = {
2366
+ id,
2367
+ startedAt: sessionStartEvent?.ts ?? (/* @__PURE__ */ new Date(0)).toISOString(),
2368
+ endedAt: sessionEndEvent?.ts,
2369
+ model: sessionModel,
2370
+ provider: sessionProvider,
2371
+ pendingToolUses: sessionPendingToolUses
2372
+ };
2299
2373
  const toolCallEnds = extractToolCallEnds(events);
2300
- const data = { metadata: meta, events, messages, usage, toolCallEnds };
2374
+ const data = { metadata: meta, events, messages: repaired.messages, usage, toolCallEnds };
2301
2375
  if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
2302
2376
  const oldest = this._loadCache.keys().next().value;
2303
2377
  if (oldest !== void 0) {
@@ -2340,7 +2414,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2340
2414
  return [];
2341
2415
  }
2342
2416
  }
2343
- // ── Session index (_index.jsonl) ─────────────────────────────────────────
2417
+ // ── Session index (_index.jsonl) ─────────────────────────────────────────
2344
2418
  //
2345
2419
  // One JSON line per closed session, appended atomically on close().
2346
2420
  // When a session is deleted, a tombstone {action:"delete",id:"..."} is
@@ -2516,7 +2590,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2516
2590
  return [...childFileArrays.flat(), ...files];
2517
2591
  }
2518
2592
  /** Recursively collect session IDs from date-shard subdirectories.
2519
- * IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_").
2593
+ * IDs include the date-prefix path (e.g. "2026-06-06/17-46-57Z_…").
2520
2594
  * Skips `.jsonl`/`.summary.json` root files, dot-files, and
2521
2595
  * sub-directories that belong to fleet/subagent sessions. */
2522
2596
  async collectSessionIds(dir, prefix = "", depth = 0) {
@@ -2571,7 +2645,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2571
2645
  }));
2572
2646
  });
2573
2647
  outcome = "failure";
2574
- errorMsg = "summary fallback \u2014 manifest rebuilt";
2648
+ errorMsg = "summary fallback \xE2\u20AC\u201D manifest rebuilt";
2575
2649
  this.emitRead(id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
2576
2650
  return summary;
2577
2651
  } catch (err) {
@@ -2863,76 +2937,6 @@ var DefaultSessionStore = class _DefaultSessionStore {
2863
2937
  stream.destroy();
2864
2938
  }
2865
2939
  }
2866
- metaFromEvents(id, events) {
2867
- const start = events.find((e) => e.type === "session_start");
2868
- const end = events.findLast((e) => e.type === "session_end");
2869
- return {
2870
- id,
2871
- startedAt: start?.ts ?? (/* @__PURE__ */ new Date(0)).toISOString(),
2872
- endedAt: end?.ts,
2873
- model: start?.model,
2874
- provider: start?.provider,
2875
- pendingToolUses: end?.pendingToolUses
2876
- };
2877
- }
2878
- replay(events, sessionId = "unknown") {
2879
- const messages = [];
2880
- let usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
2881
- const openToolUses = /* @__PURE__ */ new Set();
2882
- for (const e of events) {
2883
- if (e.type === "user_input") {
2884
- openToolUses.clear();
2885
- messages.push({ role: "user", content: e.content, ts: e.ts });
2886
- } else if (e.type === "llm_response") {
2887
- messages.push({ role: "assistant", content: e.content, ts: e.ts });
2888
- for (const b of e.content) {
2889
- if (b.type === "tool_use") openToolUses.add(b.id);
2890
- }
2891
- usage = {
2892
- input: usage.input + (e.usage.input ?? 0),
2893
- output: usage.output + (e.usage.output ?? 0),
2894
- cacheRead: (usage.cacheRead ?? 0) + (e.usage.cacheRead ?? 0),
2895
- cacheWrite: (usage.cacheWrite ?? 0) + (e.usage.cacheWrite ?? 0)
2896
- };
2897
- } else if (e.type === "tool_result") {
2898
- if (!openToolUses.has(e.id)) {
2899
- this.events?.emit("session.damaged", {
2900
- sessionId,
2901
- detail: `Orphan tool_result "${e.id}" has no matching tool_use`
2902
- });
2903
- continue;
2904
- }
2905
- openToolUses.delete(e.id);
2906
- const resultBlock = {
2907
- type: "tool_result",
2908
- tool_use_id: e.id,
2909
- content: typeof e.content === "string" ? e.content : JSON.stringify(e.content),
2910
- is_error: e.isError
2911
- };
2912
- const last = messages[messages.length - 1];
2913
- const lastIsToolResultUser = last?.role === "user" && Array.isArray(last.content) && last.content.every((b) => b.type === "tool_result");
2914
- if (lastIsToolResultUser && Array.isArray(last.content)) {
2915
- last.content.push(resultBlock);
2916
- } else {
2917
- messages.push({ role: "user", content: [resultBlock], ts: e.ts });
2918
- }
2919
- }
2920
- }
2921
- if (openToolUses.size > 0) {
2922
- this.events?.emit("session.damaged", {
2923
- sessionId,
2924
- detail: `${openToolUses.size} tool_use blocks without matching results - replay repaired`
2925
- });
2926
- }
2927
- const repaired = repairToolUseAdjacency(messages);
2928
- if (repaired.report.changed) {
2929
- this.events?.emit("session.damaged", {
2930
- sessionId,
2931
- detail: `Repaired replay adjacency: removed ${repaired.report.removedToolUses.length} tool_use, ${repaired.report.removedToolResults.length} tool_result, ${repaired.report.removedMessages} empty messages`
2932
- });
2933
- }
2934
- return { messages: repaired.messages, usage };
2935
- }
2936
2940
  };
2937
2941
  function extractToolCallEnds(events) {
2938
2942
  const result = [];
@@ -2991,7 +2995,7 @@ var FileSessionWriter = class _FileSessionWriter {
2991
2995
  /**
2992
2996
  * Lazy session_start/session_resumed init, shared by all appenders.
2993
2997
  * A single promise (not a boolean) so a second append racing the first
2994
- * can't push its event into the buffer BEFORE the first append's event
2998
+ * can't push its event into the buffer BEFORE the first append's event —
2995
2999
  * every appender awaits the same init and resumes in FIFO call order.
2996
3000
  */
2997
3001
  initPromise = null;
@@ -3004,24 +3008,24 @@ var FileSessionWriter = class _FileSessionWriter {
3004
3008
  lastAppendWarnAt = 0;
3005
3009
  secretScrubber;
3006
3010
  onCloseCb;
3007
- /** Implements SessionWriter.traceId propagated from ContextInit.traceId. */
3011
+ /** Implements SessionWriter.traceId — propagated from ContextInit.traceId. */
3008
3012
  traceId;
3009
- // ── Write buffer batches events to reduce per-event disk I/O ─────────
3013
+ // ── Write buffer — batches events to reduce per-event disk I/O ─────────
3010
3014
  //
3011
3015
  // Every append() pushes the scrubbed event into an in-memory buffer instead
3012
3016
  // of calling handle.appendFile() synchronously. The buffer flushes to disk
3013
3017
  // when it reaches FLUSH_SIZE events OR after FLUSH_INTERVAL_MS of inactivity.
3014
3018
  // This cuts the number of disk writes by ~95% without changing the on-disk
3015
- // format the JSONL is still one JSON object per line.
3019
+ // format — the JSONL is still one JSON object per line.
3016
3020
  writeBuffer = [];
3017
3021
  flushTimer = null;
3018
3022
  static FLUSH_INTERVAL_MS = 500;
3019
3023
  static FLUSH_SIZE = 50;
3020
- // ── Write serialization ─────────────────────────────────────────────────
3024
+ // ── Write serialization ─────────────────────────────────────────────────
3021
3025
  //
3022
3026
  // All disk writes are funneled through a FIFO promise chain. Without it,
3023
3027
  // a timer-driven flush racing an explicit flush()/close() issues two
3024
- // concurrent appendFile() calls on the shared O_APPEND handle the kernel
3028
+ // concurrent appendFile() calls on the shared O_APPEND handle — the kernel
3025
3029
  // may complete them out of order (chronology breaks) or, for large
3026
3030
  // batches, interleave partial writes (torn JSONL lines). The chain keeps
3027
3031
  // exactly one write in flight; failures don't break the chain.
@@ -3035,7 +3039,7 @@ var FileSessionWriter = class _FileSessionWriter {
3035
3039
  );
3036
3040
  return write;
3037
3041
  }
3038
- // ── Enriched summary tracking ──────────────────────────────────────────
3042
+ // ── Enriched summary tracking ──────────────────────────────────────────
3039
3043
  iterationCount = 0;
3040
3044
  toolCallCount = 0;
3041
3045
  toolErrorCount = 0;
@@ -3127,7 +3131,7 @@ var FileSessionWriter = class _FileSessionWriter {
3127
3131
  * (user_input, llm_response) call this so they survive SIGKILL/crash
3128
3132
  * instead of sitting in the in-memory buffer for up to 500ms.
3129
3133
  *
3130
- * Idempotent cancels any pending timer and writes whatever has
3134
+ * Idempotent — cancels any pending timer and writes whatever has
3131
3135
  * accumulated in the buffer. Safe to call even when the buffer
3132
3136
  * is empty (no-op).
3133
3137
  */
@@ -3150,7 +3154,7 @@ var FileSessionWriter = class _FileSessionWriter {
3150
3154
  /**
3151
3155
  * Flush all buffered events to disk as a single appendFile call.
3152
3156
  * Errors use the same throttled-warning pattern the old per-event
3153
- * append path used one warning every 5s with a suppressed count.
3157
+ * append path used — one warning every 5s with a suppressed count.
3154
3158
  * On failure the buffer is cleared (events are best-effort, same as
3155
3159
  * the old per-event path where a failed write was silently dropped).
3156
3160
  */
@@ -3333,7 +3337,7 @@ var FileSessionWriter = class _FileSessionWriter {
3333
3337
  /**
3334
3338
  * Truncate the session file to the checkpoint with the given promptIndex,
3335
3339
  * removing all events that follow it. Uses a single-pass byte-offset scan
3336
- * so post-checkpoint content is never read or parsed O(1) memory instead
3340
+ * so post-checkpoint content is never read or parsed — O(1) memory instead
3337
3341
  * of O(N) JSON.parse calls over the full file.
3338
3342
  */
3339
3343
  async truncateToCheckpoint(targetPromptIndex) {
@@ -3490,7 +3494,7 @@ var FileSessionWriter = class _FileSessionWriter {
3490
3494
  await fsp2.writeFile(this.filePath, record, "utf8");
3491
3495
  }
3492
3496
  /**
3493
- * Idea #1 write an in-flight marker. The agent loop should call
3497
+ * Idea #1 — write an in-flight marker. The agent loop should call
3494
3498
  * this at the start of each long-running operation; a matching
3495
3499
  * `clearInFlightMarker` follows on clean exit. A stale marker
3496
3500
  * (no end) is what `SessionRecovery.detectStale` looks for.
@@ -3507,9 +3511,9 @@ var FileSessionWriter = class _FileSessionWriter {
3507
3511
  this.events?.emit("in_flight.started", { context, ts: (/* @__PURE__ */ new Date()).toISOString() });
3508
3512
  }
3509
3513
  /**
3510
- * Idea #1 close the in-flight marker. Idempotent in spirit
3514
+ * Idea #1 — close the in-flight marker. Idempotent in spirit
3511
3515
  * (you can call it after a successful iteration even if you
3512
- * didn't open one this round) but the session log records
3516
+ * didn't open one this round) — but the session log records
3513
3517
  * every call so postmortem tooling can see "the agent finished
3514
3518
  * cleanly X times, then died without finishing Y".
3515
3519
  */
@@ -4134,6 +4138,20 @@ var DefaultMemoryStore = class {
4134
4138
  */
4135
4139
  persistBackup;
4136
4140
  backupDir;
4141
+ /**
4142
+ * Per-scope tracked byte sizes — incremented on `remember()`, decremented on
4143
+ * `forget()`, recalculated after `consolidate()`. Eliminates the redundant
4144
+ * readAll() call that previously checked the file size after every write.
4145
+ */
4146
+ _trackedByteSizes = {};
4147
+ /** Result cache for scoreRelevant() — keyed by scope + context hash, TTL 30s. */
4148
+ _scoreCache = /* @__PURE__ */ new Map();
4149
+ /**
4150
+ * Per-entry cached lowercase strings — computed once per scoreRelevant() call,
4151
+ * stored here so repeated scoring of the same entries avoids re-computation.
4152
+ * Cleared on every mutation (remember/forget/consolidate/clear).
4153
+ */
4154
+ _cachedLower = null;
4137
4155
  constructor(opts) {
4138
4156
  this.files = {
4139
4157
  "project-agents": opts.paths.inProjectAgentsFile,
@@ -4167,6 +4185,20 @@ var DefaultMemoryStore = class {
4167
4185
  }
4168
4186
  }
4169
4187
  }
4188
+ /**
4189
+ * Recalculate the tracked byte size for a scope by re-reading the file and
4190
+ * summing the serialized byte length of each line. Called after consolidate()
4191
+ * (which modifies the file) to keep the tracker accurate.
4192
+ */
4193
+ async _recalcTrackedByteSize(scope) {
4194
+ const raw = await this.backend.readAll(scope, this.files[scope]);
4195
+ let total = 0;
4196
+ for (const line of raw.split("\n")) {
4197
+ if (line.trim()) total += Buffer.byteLength(line, "utf8");
4198
+ }
4199
+ this._trackedByteSizes[scope] = total;
4200
+ return total;
4201
+ }
4170
4202
  async readAll() {
4171
4203
  const parts = [];
4172
4204
  for (const scope of ["project-agents", "project-memory", "user-memory"]) {
@@ -4267,6 +4299,7 @@ ${body.trim()}`);
4267
4299
  const t0 = Date.now();
4268
4300
  try {
4269
4301
  await this.backend.remember(scope, entry, filePath);
4302
+ this._scoreCache.clear();
4270
4303
  const dur = Date.now() - t0;
4271
4304
  this.events?.emit("storage.write", {
4272
4305
  sessionId: "~memory~",
@@ -4291,16 +4324,21 @@ ${body.trim()}`);
4291
4324
  });
4292
4325
  throw err;
4293
4326
  }
4294
- const raw = await this.backend.readAll(scope, this.files[scope]);
4295
- if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
4327
+ let trackedSize = this._trackedByteSizes[scope];
4328
+ if (trackedSize === void 0) {
4329
+ trackedSize = await this._recalcTrackedByteSize(scope);
4330
+ }
4331
+ if (trackedSize > MAX_BYTES_TOTAL) {
4296
4332
  const removed = await this.backend.consolidate(scope, this.files[scope]);
4297
4333
  if (removed > 0) {
4298
4334
  this.events?.emit("memory.consolidated", {
4299
4335
  scope,
4300
4336
  removed
4301
4337
  });
4338
+ await this._recalcTrackedByteSize(scope);
4302
4339
  }
4303
4340
  }
4341
+ this._trackedByteSizes[scope] = (this._trackedByteSizes[scope] ?? 0) + Buffer.byteLength(JSON.stringify(entry), "utf8");
4304
4342
  await this.mirrorBackup(scope);
4305
4343
  this.events?.emit("memory.remembered", {
4306
4344
  scope,
@@ -4319,16 +4357,30 @@ ${body.trim()}`);
4319
4357
  async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
4320
4358
  const all = await this.list(scope);
4321
4359
  if (all.length === 0) return [];
4360
+ const ctxHash = `${scope}|${ctx.currentTask}|${(ctx.activeSkills ?? []).join(",")}|${(ctx.toolNames ?? []).join(",")}`;
4361
+ const now = Date.now();
4362
+ const TTL_MS = 3e4;
4363
+ const cached = this._scoreCache.get(ctxHash);
4364
+ if (cached && cached.expiresAt > now && cached.entries === all) {
4365
+ return cached.scored.slice(0, Math.min(limit, 15));
4366
+ }
4322
4367
  const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
4323
4368
  const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
4324
4369
  const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
4325
- const now = Date.now();
4370
+ this._cachedLower = /* @__PURE__ */ new WeakMap();
4326
4371
  const scored = [];
4327
4372
  for (const entry of all) {
4328
4373
  let score = 0;
4329
4374
  const reasons = [];
4330
- const textLower = entry.text.toLowerCase();
4331
- const tagsLower = (entry.tags ?? []).map((t) => t.toLowerCase());
4375
+ let cachedLower = this._cachedLower.get(entry);
4376
+ if (!cachedLower) {
4377
+ cachedLower = {
4378
+ textLower: entry.text.toLowerCase(),
4379
+ tagsLower: (entry.tags ?? []).map((t) => t.toLowerCase())
4380
+ };
4381
+ this._cachedLower.set(entry, cachedLower);
4382
+ }
4383
+ const { textLower, tagsLower } = cachedLower;
4332
4384
  let taskHits = 0;
4333
4385
  for (const w of taskWords) {
4334
4386
  if (textLower.includes(w)) {
@@ -4414,6 +4466,7 @@ ${body.trim()}`);
4414
4466
  const relevant = scored.filter(
4415
4467
  (s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
4416
4468
  );
4469
+ this._scoreCache.set(ctxHash, { entries: all, scored: relevant, expiresAt: now + TTL_MS });
4417
4470
  return relevant.slice(0, Math.min(limit, 15));
4418
4471
  }
4419
4472
  async forget(query, scope = "project-memory") {
@@ -4423,6 +4476,7 @@ ${body.trim()}`);
4423
4476
  let removed = 0;
4424
4477
  try {
4425
4478
  removed = await this.backend.forget(scope, query, filePath);
4479
+ this._scoreCache.clear();
4426
4480
  const dur = Date.now() - t0;
4427
4481
  this.events?.emit("storage.write", {
4428
4482
  sessionId: "~memory~",
@@ -4454,6 +4508,7 @@ ${body.trim()}`);
4454
4508
  removed
4455
4509
  });
4456
4510
  await this.mirrorBackup(scope);
4511
+ await this._recalcTrackedByteSize(scope);
4457
4512
  }
4458
4513
  return removed;
4459
4514
  });
@@ -4465,6 +4520,7 @@ ${body.trim()}`);
4465
4520
  let removed = 0;
4466
4521
  try {
4467
4522
  removed = await this.backend.consolidate(scope, filePath);
4523
+ this._scoreCache.clear();
4468
4524
  const dur = Date.now() - t0;
4469
4525
  this.events?.emit("storage.write", {
4470
4526
  sessionId: "~memory~",
@@ -4495,6 +4551,7 @@ ${body.trim()}`);
4495
4551
  removed
4496
4552
  });
4497
4553
  await this.mirrorBackup(scope);
4554
+ await this._recalcTrackedByteSize(scope);
4498
4555
  }
4499
4556
  });
4500
4557
  }
@@ -4505,6 +4562,7 @@ ${body.trim()}`);
4505
4562
  const t0 = Date.now();
4506
4563
  try {
4507
4564
  await this.backend.clear(scope, filePath);
4565
+ this._scoreCache.clear();
4508
4566
  const dur = Date.now() - t0;
4509
4567
  this.events?.emit("storage.write", {
4510
4568
  sessionId: "~memory~",
@@ -4531,6 +4589,7 @@ ${body.trim()}`);
4531
4589
  }
4532
4590
  this.events?.emit("memory.cleared", { scope });
4533
4591
  await this.mirrorBackup(scope);
4592
+ this._trackedByteSizes[scope] = 0;
4534
4593
  });
4535
4594
  return;
4536
4595
  }
@@ -5492,6 +5551,15 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
5492
5551
  }
5493
5552
  return out;
5494
5553
  }
5554
+ function samePath(a, b) {
5555
+ let ra = path4.resolve(a);
5556
+ let rb = path4.resolve(b);
5557
+ if (process.platform === "win32" || process.platform === "darwin") {
5558
+ ra = ra.toLowerCase();
5559
+ rb = rb.toLowerCase();
5560
+ }
5561
+ return ra === rb;
5562
+ }
5495
5563
  function deepMerge2(base, patch) {
5496
5564
  const opts = { arrayMode: "concat-primitives" };
5497
5565
  if (envBoolOptional(process.env.WRONGSTACK_DEBUG_CONFIG)) {
@@ -5520,10 +5588,11 @@ var DefaultConfigLoader = class {
5520
5588
  }
5521
5589
  async load(opts = {}) {
5522
5590
  let cfg = { ...BEHAVIOR_DEFAULTS };
5591
+ const inProjectCollides = samePath(this.paths.inProjectConfig, this.paths.globalConfig) || samePath(this.paths.inProjectConfig, this.paths.projectLocalConfig);
5523
5592
  const [global, local, inProject] = await Promise.all([
5524
5593
  this.readJson(this.paths.globalConfig),
5525
5594
  this.readJson(this.paths.projectLocalConfig),
5526
- this.readJson(this.paths.inProjectConfig)
5595
+ inProjectCollides ? Promise.resolve({}) : this.readJson(this.paths.inProjectConfig)
5527
5596
  ]);
5528
5597
  cfg = deepMerge2(cfg, global);
5529
5598
  cfg = deepMerge2(cfg, local);
@@ -8399,8 +8468,8 @@ async function streamProviderToResponse(provider, req, signal, ctx, events, logg
8399
8468
  });
8400
8469
  await Promise.race([
8401
8470
  drainPromise,
8402
- new Promise((resolve6) => {
8403
- drainTimer = setTimeout(resolve6, STREAM_DRAIN_TIMEOUT_MS);
8471
+ new Promise((resolve7) => {
8472
+ drainTimer = setTimeout(resolve7, STREAM_DRAIN_TIMEOUT_MS);
8404
8473
  })
8405
8474
  ]);
8406
8475
  } finally {
@@ -8507,7 +8576,7 @@ async function runProviderWithRetry(opts) {
8507
8576
  description
8508
8577
  });
8509
8578
  }
8510
- await new Promise((resolve6, reject) => {
8579
+ await new Promise((resolve7, reject) => {
8511
8580
  let settled = false;
8512
8581
  const cleanup = () => {
8513
8582
  clearTimeout(t);
@@ -8523,7 +8592,7 @@ async function runProviderWithRetry(opts) {
8523
8592
  if (settled) return;
8524
8593
  settled = true;
8525
8594
  cleanup();
8526
- resolve6();
8595
+ resolve7();
8527
8596
  }, delay);
8528
8597
  if (signal.aborted) {
8529
8598
  onAbort();
@@ -11823,13 +11892,13 @@ var SubagentBudget = class _SubagentBudget {
11823
11892
  if (!bus?.hasListenerFor("budget.threshold_reached")) {
11824
11893
  return Promise.resolve("stop");
11825
11894
  }
11826
- return new Promise((resolve6) => {
11895
+ return new Promise((resolve7) => {
11827
11896
  let resolved = false;
11828
11897
  const respond = (d) => {
11829
11898
  if (resolved) return;
11830
11899
  resolved = true;
11831
11900
  clearTimeout(fallback);
11832
- resolve6(d);
11901
+ resolve7(d);
11833
11902
  };
11834
11903
  const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
11835
11904
  bus.emit("budget.threshold_reached", {
@@ -15734,7 +15803,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
15734
15803
  taskIds.map((id) => {
15735
15804
  const cached = this.completedResults.find((r) => r.taskId === id);
15736
15805
  if (cached) return cached;
15737
- return new Promise((resolve6, reject) => {
15806
+ return new Promise((resolve7, reject) => {
15738
15807
  const timeout = setTimeout(() => {
15739
15808
  this.off("task.completed", handler);
15740
15809
  reject(new Error(`awaitTasks timed out waiting for task "${id}"`));
@@ -15743,7 +15812,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
15743
15812
  if (result.taskId === id) {
15744
15813
  clearTimeout(timeout);
15745
15814
  this.off("task.completed", handler);
15746
- resolve6(result);
15815
+ resolve7(result);
15747
15816
  }
15748
15817
  };
15749
15818
  this.on("task.completed", handler);
@@ -16011,12 +16080,12 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
16011
16080
  }
16012
16081
  return new Promise((resolveDecision) => {
16013
16082
  let settled = false;
16014
- const resolve6 = (d) => {
16083
+ const resolve7 = (d) => {
16015
16084
  if (settled) return;
16016
16085
  settled = true;
16017
16086
  resolveDecision(d);
16018
16087
  };
16019
- const fallback = setTimeout(() => resolve6("stop"), DECISION_TIMEOUT_MS);
16088
+ const fallback = setTimeout(() => resolve7("stop"), DECISION_TIMEOUT_MS);
16020
16089
  budget._events?.emit("budget.threshold_reached", {
16021
16090
  kind: "timeout",
16022
16091
  used,
@@ -16032,11 +16101,11 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
16032
16101
  // disagreeing, resolves as a stop). Async grants still resolve.
16033
16102
  extend: (extra) => {
16034
16103
  clearTimeout(fallback);
16035
- queueMicrotask(() => resolve6({ extend: extra }));
16104
+ queueMicrotask(() => resolve7({ extend: extra }));
16036
16105
  },
16037
16106
  deny: () => {
16038
16107
  clearTimeout(fallback);
16039
- resolve6("stop");
16108
+ resolve7("stop");
16040
16109
  }
16041
16110
  });
16042
16111
  });
@@ -16946,7 +17015,7 @@ var InMemoryAgentBridge = class {
16946
17015
  });
16947
17016
  }
16948
17017
  this.inflightGuards.add(correlationId);
16949
- return new Promise((resolve6, reject) => {
17018
+ return new Promise((resolve7, reject) => {
16950
17019
  const timer = setTimeout(() => {
16951
17020
  this.inflightGuards.delete(correlationId);
16952
17021
  this.pendingRequests.delete(correlationId);
@@ -16965,7 +17034,7 @@ var InMemoryAgentBridge = class {
16965
17034
  return;
16966
17035
  }
16967
17036
  this.pendingRequests.set(correlationId, {
16968
- resolve: resolve6,
17037
+ resolve: resolve7,
16969
17038
  reject,
16970
17039
  timer
16971
17040
  });
@@ -19427,11 +19496,11 @@ var Director = class _Director {
19427
19496
  if (cached) return cached;
19428
19497
  const existing = this.taskWaiters.get(id);
19429
19498
  if (existing) return existing.promise;
19430
- let resolve6;
19499
+ let resolve7;
19431
19500
  const promise = new Promise((res) => {
19432
- resolve6 = res;
19501
+ resolve7 = res;
19433
19502
  });
19434
- this.taskWaiters.set(id, { promise, resolve: resolve6 });
19503
+ this.taskWaiters.set(id, { promise, resolve: resolve7 });
19435
19504
  return promise;
19436
19505
  })
19437
19506
  );
@@ -19827,7 +19896,7 @@ function createDelegateTool(opts) {
19827
19896
  subagentId
19828
19897
  });
19829
19898
  const dir = director;
19830
- const result = await new Promise((resolve6) => {
19899
+ const result = await new Promise((resolve7) => {
19831
19900
  let settled = false;
19832
19901
  let timer;
19833
19902
  const finish = (value) => {
@@ -19837,7 +19906,7 @@ function createDelegateTool(opts) {
19837
19906
  offTool();
19838
19907
  offIter();
19839
19908
  offProgress();
19840
- resolve6(value);
19909
+ resolve7(value);
19841
19910
  };
19842
19911
  const arm = () => {
19843
19912
  if (timer) clearTimeout(timer);
@@ -22290,9 +22359,9 @@ var AISpecBuilder = class {
22290
22359
  if (!this.sessionPath) return;
22291
22360
  try {
22292
22361
  const fsp16 = await import('fs/promises');
22293
- const path21 = await import('path');
22362
+ const path22 = await import('path');
22294
22363
  const { atomicWrite: atomicWrite2 } = await Promise.resolve().then(() => (init_atomic_write(), atomic_write_exports));
22295
- await fsp16.mkdir(path21.dirname(this.sessionPath), { recursive: true });
22364
+ await fsp16.mkdir(path22.dirname(this.sessionPath), { recursive: true });
22296
22365
  await atomicWrite2(this.sessionPath, JSON.stringify(this.session, null, 2));
22297
22366
  } catch {
22298
22367
  }
@@ -23019,15 +23088,15 @@ function computeCriticalPath(graph, _topoOrder, blockedByMap) {
23019
23088
  maxId = id;
23020
23089
  }
23021
23090
  }
23022
- const path21 = [];
23091
+ const path22 = [];
23023
23092
  let current = maxId;
23024
23093
  const visited = /* @__PURE__ */ new Set();
23025
23094
  while (current && !visited.has(current)) {
23026
23095
  visited.add(current);
23027
- path21.unshift(current);
23096
+ path22.unshift(current);
23028
23097
  current = prev.get(current) ?? null;
23029
23098
  }
23030
- return path21;
23099
+ return path22;
23031
23100
  }
23032
23101
  function computeParallelGroups(graph, blockedByMap) {
23033
23102
  const groups = [];
@@ -23850,9 +23919,9 @@ var DefaultHealthRegistry = class {
23850
23919
  }
23851
23920
  async runOne(check) {
23852
23921
  let timer = null;
23853
- const timeout = new Promise((resolve6) => {
23922
+ const timeout = new Promise((resolve7) => {
23854
23923
  timer = setTimeout(
23855
- () => resolve6({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
23924
+ () => resolve7({ status: "unhealthy", detail: `timeout after ${this.timeoutMs}ms` }),
23856
23925
  this.timeoutMs
23857
23926
  );
23858
23927
  });
@@ -24035,7 +24104,7 @@ async function startMetricsServer(opts) {
24035
24104
  const tls = opts.tls;
24036
24105
  const useHttps = !!(tls?.cert && tls?.key);
24037
24106
  const host = opts.host ?? "127.0.0.1";
24038
- const path21 = opts.path ?? "/metrics";
24107
+ const path22 = opts.path ?? "/metrics";
24039
24108
  const healthPath = opts.healthPath ?? "/healthz";
24040
24109
  const healthRegistry = opts.healthRegistry;
24041
24110
  const listener = (req, res) => {
@@ -24045,7 +24114,7 @@ async function startMetricsServer(opts) {
24045
24114
  return;
24046
24115
  }
24047
24116
  const url = req.url.split("?")[0];
24048
- if (url === path21) {
24117
+ if (url === path22) {
24049
24118
  let body;
24050
24119
  try {
24051
24120
  body = renderPrometheus(opts.sink.snapshot());
@@ -24091,14 +24160,14 @@ async function startMetricsServer(opts) {
24091
24160
  const { createServer } = await import('http');
24092
24161
  server = createServer(listener);
24093
24162
  }
24094
- await new Promise((resolve6, reject) => {
24163
+ await new Promise((resolve7, reject) => {
24095
24164
  const onError = (err) => {
24096
24165
  server.off("listening", onListening);
24097
24166
  reject(err);
24098
24167
  };
24099
24168
  const onListening = () => {
24100
24169
  server.off("error", onError);
24101
- resolve6();
24170
+ resolve7();
24102
24171
  };
24103
24172
  server.once("error", onError);
24104
24173
  server.once("listening", onListening);
@@ -24109,9 +24178,9 @@ async function startMetricsServer(opts) {
24109
24178
  const protocol = useHttps ? "https" : "http";
24110
24179
  return {
24111
24180
  port: boundPort,
24112
- url: `${protocol}://${host}:${boundPort}${path21}`,
24113
- close: () => new Promise((resolve6, reject) => {
24114
- server.close((err) => err ? reject(err) : resolve6());
24181
+ url: `${protocol}://${host}:${boundPort}${path22}`,
24182
+ close: () => new Promise((resolve7, reject) => {
24183
+ server.close((err) => err ? reject(err) : resolve7());
24115
24184
  })
24116
24185
  };
24117
24186
  }