@wrongstack/core 0.268.0 → 0.270.0

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 (75) hide show
  1. package/dist/{agent-bridge-UhojbpWx.d.ts → agent-bridge-PcHQl_UQ.d.ts} +1 -1
  2. package/dist/{agent-subagent-runner-Bvtf1o9K.d.ts → agent-subagent-runner-SHJW7t8q.d.ts} +8 -8
  3. package/dist/{brain-69wzMKp1.d.ts → brain-BYcK__Ym.d.ts} +1 -1
  4. package/dist/{compactor-CBQAJoDc.d.ts → compactor-C2RKEBtC.d.ts} +1 -1
  5. package/dist/{config-VKfOZ-6X.d.ts → config-C_ae2k86.d.ts} +11 -2
  6. package/dist/{context-C0U8B9NF.d.ts → context-Dp87Bcaq.d.ts} +24 -1
  7. package/dist/coordination/index.d.ts +22 -16
  8. package/dist/coordination/index.js +233 -86
  9. package/dist/coordination/index.js.map +1 -1
  10. package/dist/defaults/index.d.ts +25 -25
  11. package/dist/defaults/index.js +272 -69
  12. package/dist/defaults/index.js.map +1 -1
  13. package/dist/execution/index.d.ts +15 -15
  14. package/dist/execution/index.js +116 -19
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/prompt-enhancer.d.ts +1 -1
  17. package/dist/extension/index.d.ts +6 -6
  18. package/dist/{global-mailbox-KByEFFBa.d.ts → global-mailbox-Bvrz1P3f.d.ts} +2 -1
  19. package/dist/{goal-preamble-CrYjmdw4.d.ts → goal-preamble-CA_4yiGQ.d.ts} +9 -9
  20. package/dist/{goal-store-Y_zdLZ3q.d.ts → goal-store-DhuJoUNG.d.ts} +1 -1
  21. package/dist/hq/index.d.ts +15 -6
  22. package/dist/hq/index.js +58 -11
  23. package/dist/hq/index.js.map +1 -1
  24. package/dist/{index-CtQnmkaS.d.ts → index-CZQ6Pwbs.d.ts} +8 -8
  25. package/dist/{index-gCv830d7.d.ts → index-W4VJCzHa.d.ts} +5 -5
  26. package/dist/{index-BfaS-f_m.d.ts → index-whDfTANu.d.ts} +2 -2
  27. package/dist/index.d.ts +41 -41
  28. package/dist/index.js +534 -170
  29. package/dist/index.js.map +1 -1
  30. package/dist/infrastructure/index.d.ts +6 -6
  31. package/dist/infrastructure/index.js +3 -3
  32. package/dist/infrastructure/index.js.map +1 -1
  33. package/dist/kernel/index.d.ts +9 -9
  34. package/dist/{mcp-servers-HT3Fi7Bl.d.ts → mcp-servers-DJdZiRcv.d.ts} +3 -3
  35. package/dist/models/index.d.ts +5 -5
  36. package/dist/models/index.js +1 -1
  37. package/dist/models/index.js.map +1 -1
  38. package/dist/{models-registry-Bvcl3Vaa.d.ts → models-registry-C3a-2-Yd.d.ts} +1 -1
  39. package/dist/{multi-agent-coordinator-BACjsmkC.d.ts → multi-agent-coordinator-CJSpTe5O.d.ts} +1 -1
  40. package/dist/{null-fleet-bus-DA7fvhUg.d.ts → null-fleet-bus-QVshIsDx.d.ts} +6 -6
  41. package/dist/observability/index.d.ts +2 -2
  42. package/dist/{parallel-eternal-engine-Ci71gYu_.d.ts → parallel-eternal-engine-D9y5Pkcc.d.ts} +9 -9
  43. package/dist/{path-resolver-O1IJnmKE.d.ts → path-resolver-CnQ8SIfh.d.ts} +3 -3
  44. package/dist/{permission-Bd-57Lbl.d.ts → permission-CvYQNUqZ.d.ts} +1 -1
  45. package/dist/{permission-policy-uNXC6Kge.d.ts → permission-policy-D5Ss8j4B.d.ts} +2 -2
  46. package/dist/{pipeline-BDNvENyV.d.ts → pipeline-l_zzFRh3.d.ts} +2 -2
  47. package/dist/{plan-templates-EMsalEtN.d.ts → plan-templates-NtPgyeJA.d.ts} +6 -5
  48. package/dist/{provider-model-resolve-CEb9x886.d.ts → provider-model-resolve-d5poT5y0.d.ts} +3 -3
  49. package/dist/{provider-runner-DWJbpo70.d.ts → provider-runner-gkctlQV_.d.ts} +3 -3
  50. package/dist/{retry-policy-C3s_lvdK.d.ts → retry-policy-CtFhfwa8.d.ts} +1 -1
  51. package/dist/sdd/index.d.ts +8 -8
  52. package/dist/sdd/index.js +1 -1
  53. package/dist/sdd/index.js.map +1 -1
  54. package/dist/{secret-vault-Cgduf5xL.d.ts → secret-vault-BLsVmTIK.d.ts} +1 -1
  55. package/dist/security/index.d.ts +5 -5
  56. package/dist/security/index.js.map +1 -1
  57. package/dist/{selector-47LBnBVk.d.ts → selector-CXl2_y9W.d.ts} +1 -1
  58. package/dist/{session-event-bridge-Cw7oqmW2.d.ts → session-event-bridge-Ccud20CC.d.ts} +1 -1
  59. package/dist/{session-reader-DD4v2Obw.d.ts → session-reader-ZeXQmsmE.d.ts} +1 -1
  60. package/dist/skills/index.js.map +1 -1
  61. package/dist/storage/index.d.ts +13 -11
  62. package/dist/storage/index.js +210 -64
  63. package/dist/storage/index.js.map +1 -1
  64. package/dist/tools/index.d.ts +2 -2
  65. package/dist/tools/index.js.map +1 -1
  66. package/dist/types/index.d.ts +21 -21
  67. package/dist/types/index.js +110 -19
  68. package/dist/types/index.js.map +1 -1
  69. package/dist/utils/index.d.ts +2 -2
  70. package/dist/utils/index.js +58 -24
  71. package/dist/utils/index.js.map +1 -1
  72. package/package.json +1 -1
  73. package/skills/chimera/SKILL.md +1 -1
  74. package/skills/typescript-strict/SKILL.md +3 -3
  75. package/skills/typescript-strict/SKILL.save.md +1 -1
@@ -331,6 +331,9 @@ function formatCtx(ctx) {
331
331
  }
332
332
  }
333
333
 
334
+ // src/storage/session-store.ts
335
+ init_atomic_write();
336
+
334
337
  // src/utils/expect-defined.ts
335
338
  function expectDefined(value, label) {
336
339
  if (value === null || value === void 0) {
@@ -341,9 +344,6 @@ function expectDefined(value, label) {
341
344
  return value;
342
345
  }
343
346
 
344
- // src/storage/session-store.ts
345
- init_atomic_write();
346
-
347
347
  // src/utils/message-invariants.ts
348
348
  function repairToolUseAdjacency(messages) {
349
349
  const removedToolUses = [];
@@ -406,7 +406,7 @@ function hasToolResult(msg) {
406
406
  }
407
407
  function toolUseIds(msg) {
408
408
  const ids = /* @__PURE__ */ new Set();
409
- if (!msg || msg.role !== "assistant") return ids;
409
+ if (msg?.role !== "assistant") return ids;
410
410
  for (const block of contentBlocks(msg)) {
411
411
  if (block.type === "tool_use") ids.add(block.id);
412
412
  }
@@ -414,7 +414,7 @@ function toolUseIds(msg) {
414
414
  }
415
415
  function toolResultIds(msg) {
416
416
  const ids = /* @__PURE__ */ new Set();
417
- if (!msg || msg.role !== "user") return ids;
417
+ if (msg?.role !== "user") return ids;
418
418
  for (const block of contentBlocks(msg)) {
419
419
  if (block.type === "tool_result") ids.add(block.tool_use_id);
420
420
  }
@@ -1206,8 +1206,19 @@ function calState(key) {
1206
1206
  return state;
1207
1207
  }
1208
1208
  var MIN_SAMPLES_FOR_CALIBRATION = 3;
1209
+ var MODEL_FAMILY_RATIO = {
1210
+ // Anthropic: ~3.8-4.0 chars/token depending on model
1211
+ claude: 3.8,
1212
+ // OpenAI: ~4.0 chars/token
1213
+ "gpt-4": 4,
1214
+ "gpt-3.5": 4,
1215
+ // Google: ~3.5 chars/token
1216
+ gemini: 3.5,
1217
+ // DeepSeek: ~3.5 chars/token
1218
+ deepseek: 3.5
1219
+ };
1209
1220
  var ESTIMATE_CACHE = /* @__PURE__ */ new Map();
1210
- var ESTIMATE_CACHE_MAX_SIZE = 1e4;
1221
+ var ESTIMATE_CACHE_MAX_SIZE = 5e4;
1211
1222
  function getCachedEstimate(key, compute) {
1212
1223
  const existing = ESTIMATE_CACHE.get(key);
1213
1224
  if (existing !== void 0) return existing;
@@ -1335,8 +1346,24 @@ function estimateRequestTokensCalibrated(messages, systemPrompt, tools, calibrat
1335
1346
  total: Math.round(result.total * safeRatio)
1336
1347
  };
1337
1348
  }
1349
+ const fallbackRatio = getModelFamilyRatio(calibrationKey);
1350
+ if (fallbackRatio !== null) {
1351
+ return {
1352
+ messages: Math.round(result.messages * fallbackRatio),
1353
+ systemPrompt: Math.round(result.systemPrompt * fallbackRatio),
1354
+ tools: Math.round(result.tools * fallbackRatio),
1355
+ total: Math.round(result.total * fallbackRatio)
1356
+ };
1357
+ }
1338
1358
  return result;
1339
1359
  }
1360
+ function getModelFamilyRatio(calibrationKey) {
1361
+ const lower = calibrationKey.toLowerCase();
1362
+ for (const [family, ratio] of Object.entries(MODEL_FAMILY_RATIO)) {
1363
+ if (lower.includes(family)) return ratio / 3.5;
1364
+ }
1365
+ return null;
1366
+ }
1340
1367
 
1341
1368
  // src/utils/tool-output-serializer.ts
1342
1369
  var DEFAULT_LIST_LIMIT = 500;
@@ -1347,6 +1374,7 @@ var GREP_MATCHES_PER_FILE = 3;
1347
1374
  var DIFF_INLINE_LINE_LIMIT = 260;
1348
1375
  var DIFF_HUNK_LIMIT = 8;
1349
1376
  var DIFF_HUNK_CONTEXT = 14;
1377
+ var GREP_LINE_RE = /^(.+?):(\d+):(.*)$/;
1350
1378
  function createToolOutputSerializer(opts = {}) {
1351
1379
  const capBytes = opts.perIterationOutputCapBytes ?? 1e5;
1352
1380
  function serialize(value, context = {}) {
@@ -1732,7 +1760,7 @@ ${renderStringList(passthrough, "", 50)}`);
1732
1760
  return sections.join("\n");
1733
1761
  }
1734
1762
  function parseGrepContentLine(line) {
1735
- const match = /^(.+?):(\d+):(.*)$/.exec(line);
1763
+ const match = GREP_LINE_RE.exec(line);
1736
1764
  if (!match?.[1] || !match[2]) return void 0;
1737
1765
  return { file: match[1], line: match[2], text: match[3] ?? "" };
1738
1766
  }
@@ -1750,22 +1778,20 @@ function compactDiff(diff) {
1750
1778
  const hunks = lines.filter((line) => line.startsWith("@@")).length;
1751
1779
  const added = lines.filter((line) => line.startsWith("+") && !line.startsWith("+++")).length;
1752
1780
  const removed = lines.filter((line) => line.startsWith("-") && !line.startsWith("---")).length;
1753
- const selected = /* @__PURE__ */ new Set();
1781
+ const intervals = [];
1754
1782
  let hunkCount = 0;
1755
1783
  for (let i = 0; i < lines.length; i++) {
1756
1784
  const line = lines[i] ?? "";
1757
1785
  if (line.startsWith("diff --git") || line.startsWith("--- ") || line.startsWith("+++ ")) {
1758
- selected.add(i);
1786
+ intervals.push([i, i]);
1759
1787
  continue;
1760
1788
  }
1761
1789
  if (!line.startsWith("@@")) continue;
1762
1790
  if (hunkCount >= DIFF_HUNK_LIMIT) continue;
1763
1791
  hunkCount++;
1764
- for (let j = i; j <= Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT); j++) {
1765
- selected.add(j);
1766
- }
1792
+ intervals.push([i, Math.min(lines.length - 1, i + DIFF_HUNK_CONTEXT)]);
1767
1793
  }
1768
- if (selected.size === 0) {
1794
+ if (intervals.length === 0) {
1769
1795
  return joinSections([
1770
1796
  renderHeader("diff_summary", {
1771
1797
  files: fileCount,
@@ -1778,17 +1804,29 @@ function compactDiff(diff) {
1778
1804
  `[serializer omitted ${Math.max(0, lines.length - DIFF_INLINE_LINE_LIMIT)} diff line(s)]`
1779
1805
  ]);
1780
1806
  }
1807
+ const merged = [intervals[0]];
1808
+ for (let i = 1; i < intervals.length; i++) {
1809
+ const last = merged[merged.length - 1];
1810
+ const current = intervals[i];
1811
+ if (current[0] <= last[1] + 1) {
1812
+ last[1] = Math.max(last[1], current[1]);
1813
+ } else {
1814
+ merged.push(current);
1815
+ }
1816
+ }
1781
1817
  const excerpt = [];
1782
- let previous = -1;
1783
- for (const index of [...selected].sort((a, b) => a - b)) {
1784
- if (index > previous + 1) {
1785
- const omitted = previous === -1 ? index : index - previous - 1;
1818
+ let prevLine = -1;
1819
+ for (const [start, end] of merged) {
1820
+ if (start > prevLine + 1) {
1821
+ const omitted = prevLine === -1 ? start : start - prevLine - 1;
1786
1822
  excerpt.push(`[serializer omitted ${omitted} diff line(s)]`);
1787
1823
  }
1788
- excerpt.push(lines[index] ?? "");
1789
- previous = index;
1824
+ for (let j = start; j <= end; j++) {
1825
+ excerpt.push(lines[j] ?? "");
1826
+ }
1827
+ prevLine = end;
1790
1828
  }
1791
- const trailing = lines.length - previous - 1;
1829
+ const trailing = lines.length - prevLine - 1;
1792
1830
  if (trailing > 0) excerpt.push(`[serializer omitted ${trailing} trailing diff line(s)]`);
1793
1831
  return joinSections([
1794
1832
  renderHeader("diff_summary", {
@@ -2062,6 +2100,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2062
2100
  * processes. When the limit is reached, the oldest entry is evicted.
2063
2101
  */
2064
2102
  _loadCache = /* @__PURE__ */ new Map();
2103
+ _indexCache = null;
2065
2104
  static LOAD_CACHE_MAX_ENTRIES = 50;
2066
2105
  constructor(opts) {
2067
2106
  this.dir = opts.dir;
@@ -2223,13 +2262,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
2223
2262
  let errorMsg;
2224
2263
  let cacheHit = false;
2225
2264
  try {
2226
- let stat6;
2227
- try {
2228
- const s = await fsp2.stat(file);
2229
- stat6 = { mtimeMs: s.mtimeMs, size: s.size };
2230
- } catch (err) {
2231
- throw err;
2232
- }
2265
+ const s = await fsp2.stat(file);
2266
+ const stat6 = { mtimeMs: s.mtimeMs, size: s.size };
2233
2267
  const cached = this._loadCache.get(id);
2234
2268
  if (cached && cached.mtimeMs === stat6.mtimeMs && cached.size === stat6.size) {
2235
2269
  cacheHit = true;
@@ -2320,6 +2354,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2320
2354
  await ensureDir(this.dir);
2321
2355
  const line = JSON.stringify(summary) + "\n";
2322
2356
  await fsp2.appendFile(this.indexFile, line, "utf8");
2357
+ this._indexCache = null;
2323
2358
  this.indexAppendCount++;
2324
2359
  if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
2325
2360
  await this.compactIndex();
@@ -2334,6 +2369,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2334
2369
  await ensureDir(this.dir);
2335
2370
  const line = JSON.stringify({ action: "delete", id }) + "\n";
2336
2371
  await fsp2.appendFile(this.indexFile, line, "utf8");
2372
+ this._indexCache = null;
2337
2373
  this.indexAppendCount++;
2338
2374
  } catch {
2339
2375
  }
@@ -2353,6 +2389,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2353
2389
  const lines = entries.map((s) => JSON.stringify(s)).join("\n") + "\n";
2354
2390
  await fsp2.writeFile(tmp, lines, "utf8");
2355
2391
  await fsp2.rename(tmp, this.indexFile);
2392
+ this._indexCache = null;
2356
2393
  } catch (err) {
2357
2394
  outcome = "failure";
2358
2395
  errorMsg = toErrorMessage(err);
@@ -2366,10 +2403,22 @@ var DefaultSessionStore = class _DefaultSessionStore {
2366
2403
  * Returns empty array when the index doesn't exist or is corrupt.
2367
2404
  */
2368
2405
  async readIndex() {
2406
+ let stat6;
2407
+ try {
2408
+ const s = await fsp2.stat(this.indexFile);
2409
+ stat6 = { mtimeMs: s.mtimeMs, size: s.size };
2410
+ } catch {
2411
+ this._indexCache = null;
2412
+ return [];
2413
+ }
2414
+ if (this._indexCache !== null && this._indexCache.mtimeMs === stat6.mtimeMs && this._indexCache.size === stat6.size) {
2415
+ return [...this._indexCache.summaries];
2416
+ }
2369
2417
  let raw;
2370
2418
  try {
2371
2419
  raw = await fsp2.readFile(this.indexFile, "utf8");
2372
2420
  } catch {
2421
+ this._indexCache = null;
2373
2422
  return [];
2374
2423
  }
2375
2424
  const deleted = /* @__PURE__ */ new Set();
@@ -2389,7 +2438,9 @@ var DefaultSessionStore = class _DefaultSessionStore {
2389
2438
  } catch {
2390
2439
  }
2391
2440
  }
2392
- return Array.from(seen.values());
2441
+ const summaries = Array.from(seen.values());
2442
+ this._indexCache = { ...stat6, summaries };
2443
+ return [...summaries];
2393
2444
  }
2394
2445
  /**
2395
2446
  * Rebuild the index from disk by scanning all sessions and writing a
@@ -2403,6 +2454,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
2403
2454
  const lines = valid.map((s) => JSON.stringify(s)).join("\n") + "\n";
2404
2455
  await fsp2.writeFile(tmp, lines, "utf8");
2405
2456
  await fsp2.rename(tmp, this.indexFile);
2457
+ this._indexCache = null;
2406
2458
  return valid.length;
2407
2459
  }
2408
2460
  /** Recursively collect session IDs from date-shard subdirectories.
@@ -3102,6 +3154,12 @@ var FileSessionWriter = class _FileSessionWriter {
3102
3154
  files
3103
3155
  });
3104
3156
  }
3157
+ /**
3158
+ * Truncate the session file to the checkpoint with the given promptIndex,
3159
+ * removing all events that follow it. Uses a single-pass byte-offset scan
3160
+ * so post-checkpoint content is never read or parsed — O(1) memory instead
3161
+ * of O(N) JSON.parse calls over the full file.
3162
+ */
3105
3163
  async truncateToCheckpoint(targetPromptIndex) {
3106
3164
  if (!this.filePath) return 0;
3107
3165
  if (this.flushTimer) {
@@ -3110,51 +3168,118 @@ var FileSessionWriter = class _FileSessionWriter {
3110
3168
  }
3111
3169
  await this.flushBuffer();
3112
3170
  await this.writeChain;
3113
- const raw = await fsp2.readFile(this.filePath, "utf8");
3114
- const lines = raw.split("\n");
3115
- const kept = [];
3171
+ const CHUNK_SIZE = 65536;
3172
+ let fd;
3173
+ let fileOffset = 0;
3174
+ let lineStartOffset = 0;
3175
+ let checkpointByteOffset = -1;
3116
3176
  let removedCount = 0;
3117
- let targetCheckpointLine = -1;
3118
- let afterTarget = false;
3119
- for (let i = 0; i < lines.length; i++) {
3120
- const line = expectDefined(lines[i]);
3121
- if (!line.trim()) continue;
3122
- let event;
3123
- try {
3124
- event = JSON.parse(line);
3125
- } catch {
3126
- kept.push(line);
3127
- continue;
3128
- }
3129
- if (event.type === "checkpoint") {
3130
- if (event.promptIndex === targetPromptIndex) {
3131
- targetCheckpointLine = kept.length;
3132
- afterTarget = true;
3133
- } else if (event.promptIndex > targetPromptIndex) {
3134
- afterTarget = true;
3177
+ let targetCheckpointSeen = false;
3178
+ try {
3179
+ fd = await fsp2.open(this.filePath, "r", 384);
3180
+ while (true) {
3181
+ const buf = Buffer.alloc(CHUNK_SIZE);
3182
+ const { bytesRead } = await fd.read(buf, 0, CHUNK_SIZE, fileOffset);
3183
+ if (bytesRead === 0) break;
3184
+ let chunkPos = 0;
3185
+ while (chunkPos < bytesRead) {
3186
+ const idx = buf.indexOf("\n", chunkPos);
3187
+ if (idx === -1) {
3188
+ lineStartOffset = fileOffset + chunkPos;
3189
+ break;
3190
+ }
3191
+ if (checkpointByteOffset !== -1) {
3192
+ removedCount++;
3193
+ } else {
3194
+ const lineBytes = buf.subarray(chunkPos, idx);
3195
+ const line = new TextDecoder("utf-8", { fatal: false }).decode(lineBytes);
3196
+ if (line.trim()) {
3197
+ try {
3198
+ const event = JSON.parse(line);
3199
+ if (event.type === "checkpoint") {
3200
+ if (event.promptIndex === targetPromptIndex) {
3201
+ checkpointByteOffset = lineStartOffset;
3202
+ targetCheckpointSeen = true;
3203
+ } else if (event.promptIndex !== void 0 && event.promptIndex > targetPromptIndex) {
3204
+ checkpointByteOffset = lineStartOffset;
3205
+ }
3206
+ } else if (targetCheckpointSeen && event.promptIndex !== void 0 && event.promptIndex > targetPromptIndex) {
3207
+ removedCount++;
3208
+ } else if (targetCheckpointSeen && event.promptIndex === void 0) {
3209
+ removedCount++;
3210
+ } else if (!targetCheckpointSeen && event.promptIndex === void 0) {
3211
+ removedCount++;
3212
+ } else if (!targetCheckpointSeen && event.promptIndex !== void 0 && event.promptIndex > targetPromptIndex) {
3213
+ removedCount++;
3214
+ }
3215
+ } catch {
3216
+ }
3217
+ }
3218
+ }
3219
+ chunkPos = idx + 1;
3220
+ lineStartOffset = fileOffset + chunkPos;
3135
3221
  }
3136
- }
3137
- if (event.promptIndex !== void 0 && event.promptIndex > targetPromptIndex) {
3138
- removedCount++;
3139
- } else if (event.promptIndex === void 0) {
3140
- if (!afterTarget || targetCheckpointLine === -1) {
3141
- kept.push(line);
3142
- } else {
3143
- removedCount++;
3222
+ fileOffset += bytesRead;
3223
+ if (chunkPos >= bytesRead) {
3224
+ lineStartOffset = fileOffset;
3144
3225
  }
3145
- } else {
3146
- kept.push(line);
3147
3226
  }
3227
+ } finally {
3228
+ await fd?.close();
3148
3229
  }
3149
- const truncated = kept.join("\n");
3230
+ if (checkpointByteOffset === -1) return 0;
3231
+ await this.writeChain;
3232
+ await this.handle.close();
3150
3233
  const tmpPath = `${this.filePath}.rewind.tmp`;
3151
- await fsp2.writeFile(tmpPath, truncated + "\n", "utf8");
3234
+ const src = await fsp2.open(this.filePath, "r", 384);
3152
3235
  try {
3153
- await this.handle.close();
3236
+ const statResult = await src.stat();
3237
+ const totalSize = statResult.size;
3238
+ const prefixBytes = checkpointByteOffset;
3239
+ let newlineAfterCheckpoint = prefixBytes;
3240
+ if (prefixBytes < totalSize) {
3241
+ const probeBuf = Buffer.alloc(Math.min(CHUNK_SIZE, totalSize - prefixBytes));
3242
+ const { bytesRead: probeRead } = await src.read(probeBuf, 0, probeBuf.length, prefixBytes);
3243
+ if (probeRead > 0) {
3244
+ const nl = probeBuf.indexOf("\n");
3245
+ newlineAfterCheckpoint = nl !== -1 ? prefixBytes + nl + 1 : totalSize;
3246
+ }
3247
+ } else {
3248
+ newlineAfterCheckpoint = totalSize;
3249
+ }
3250
+ const writeFd = await fsp2.open(tmpPath, "w", 384);
3251
+ try {
3252
+ let copied = 0;
3253
+ let readOffset = 0;
3254
+ while (readOffset < newlineAfterCheckpoint) {
3255
+ const toCopy = Math.min(CHUNK_SIZE, newlineAfterCheckpoint - readOffset);
3256
+ const copyBuf = Buffer.alloc(toCopy);
3257
+ const { bytesRead: r } = await src.read(copyBuf, 0, toCopy, readOffset);
3258
+ if (r === 0) break;
3259
+ await writeFd.write(copyBuf, 0, r);
3260
+ readOffset += r;
3261
+ copied += r;
3262
+ }
3263
+ const raw = await fsp2.readFile(this.filePath);
3264
+ const tail = raw.subarray(newlineAfterCheckpoint).toString("utf8");
3265
+ for (const line of tail.split("\n")) {
3266
+ if (!line.trim()) continue;
3267
+ try {
3268
+ JSON.parse(line);
3269
+ } catch {
3270
+ await writeFd.write(`${line}
3271
+ `, void 0, "utf8");
3272
+ }
3273
+ }
3274
+ } finally {
3275
+ await writeFd.close();
3276
+ }
3277
+ await src.close();
3154
3278
  await fsp2.rename(tmpPath, this.filePath);
3155
3279
  this.handle = await fsp2.open(this.filePath, "a", 384);
3156
3280
  } catch (err) {
3157
3281
  await fsp2.unlink(tmpPath).catch(() => void 0);
3282
+ this.handle = await fsp2.open(this.filePath, "a", 384).catch(() => this.handle);
3158
3283
  throw err;
3159
3284
  }
3160
3285
  await this.append({
@@ -3554,6 +3679,7 @@ var MEMORY_TYPE_LABELS = {
3554
3679
  init_atomic_write();
3555
3680
  var TYPE_PRIORITY_RE = /^\[(\w+)\|(\w+)\]\s+/;
3556
3681
  var TAG_RE = /#([\w-]+)/g;
3682
+ var MAX_MEMORY_CONSOLIDATE_BACKUPS = 5;
3557
3683
  function formatMetadata(entry) {
3558
3684
  const parts = [];
3559
3685
  if (entry.type && entry.priority) {
@@ -3746,6 +3872,7 @@ ${line}`;
3746
3872
  const backup = `${file}.bak.${Date.now()}`;
3747
3873
  try {
3748
3874
  await fsp2.copyFile(file, backup);
3875
+ await pruneConsolidateBackups(file);
3749
3876
  } catch {
3750
3877
  }
3751
3878
  try {
@@ -3756,6 +3883,20 @@ ${line}`;
3756
3883
  return removed;
3757
3884
  }
3758
3885
  };
3886
+ async function pruneConsolidateBackups(file) {
3887
+ const dir = path4.dirname(file);
3888
+ const base = path4.basename(file);
3889
+ const prefix = `${base}.bak.`;
3890
+ const backups = (await fsp2.readdir(dir)).filter((name) => name.startsWith(prefix)).sort().reverse();
3891
+ await Promise.all(
3892
+ backups.slice(MAX_MEMORY_CONSOLIDATE_BACKUPS).map(async (name) => {
3893
+ try {
3894
+ await fsp2.unlink(path4.join(dir, name));
3895
+ } catch {
3896
+ }
3897
+ })
3898
+ );
3899
+ }
3759
3900
  function parseEntries(raw, scope = "project-memory") {
3760
3901
  const entries = [];
3761
3902
  for (const line of raw.split("\n")) {
@@ -4935,7 +5076,9 @@ var BEHAVIOR_DEFAULTS = {
4935
5076
  plugins: true,
4936
5077
  memory: true,
4937
5078
  modelsRegistry: true,
4938
- skills: true
5079
+ skills: true,
5080
+ tokenSavingMode: "off",
5081
+ allowOutsideProjectRoot: true
4939
5082
  },
4940
5083
  mcpServers: {},
4941
5084
  indexing: {
@@ -4944,7 +5087,8 @@ var BEHAVIOR_DEFAULTS = {
4944
5087
  watchExternal: true,
4945
5088
  debounceMs: 400
4946
5089
  },
4947
- session: { ...DEFAULT_SESSION_LOGGING_CONFIG }
5090
+ session: { ...DEFAULT_SESSION_LOGGING_CONFIG },
5091
+ autonomy: { autoProceedDelayMs: DEFAULT_AUTONOMY_CONFIG.autoProceedDelayMs }
4948
5092
  };
4949
5093
  function envBool(v) {
4950
5094
  return !/^(0|false|no|off)$/i.test(v.trim());
@@ -5951,7 +6095,7 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
5951
6095
  if (!shouldSample(event)) return;
5952
6096
  try {
5953
6097
  await target.append(event);
5954
- } catch (err) {
6098
+ } catch (_err) {
5955
6099
  }
5956
6100
  },
5957
6101
  async appendBatch(events) {
@@ -9625,6 +9769,7 @@ ${post.additionalContext}`;
9625
9769
  } catch (err) {
9626
9770
  const msg = toErrorMessage(err);
9627
9771
  const scrubbed = this.opts.secretScrubber.scrub(msg);
9772
+ const { category, retryable, detail } = classifyToolError(err);
9628
9773
  this.opts.renderer?.writeToolResult(tool.name, scrubbed, true);
9629
9774
  const result = {
9630
9775
  type: "tool_result",
@@ -9635,6 +9780,9 @@ ${post.additionalContext}`;
9635
9780
  budget = this.budgetForString(result.content, budget);
9636
9781
  if (err instanceof Error) span?.recordError(err);
9637
9782
  span?.setAttribute("tool.is_error", true);
9783
+ span?.setAttribute("tool.error_category", category);
9784
+ span?.setAttribute("tool.error_retryable", retryable);
9785
+ if (detail) span?.setAttribute("tool.error_detail", detail);
9638
9786
  return { result, tool, durationMs: Date.now() - start };
9639
9787
  } finally {
9640
9788
  span?.end();
@@ -9646,6 +9794,9 @@ ${post.additionalContext}`;
9646
9794
  } catch (err) {
9647
9795
  const msg = toErrorMessage(err);
9648
9796
  const scrubbed = this.opts.secretScrubber.scrub(msg);
9797
+ const { category, retryable, detail } = classifyToolError(err);
9798
+ const tool = this.registry.get(use.name);
9799
+ this.opts.renderer?.writeToolResult(tool?.name ?? use.name, scrubbed, true);
9649
9800
  const result = {
9650
9801
  type: "tool_result",
9651
9802
  tool_use_id: use.id,
@@ -9653,7 +9804,7 @@ ${post.additionalContext}`;
9653
9804
  is_error: true
9654
9805
  };
9655
9806
  budget = this.budgetForString(result.content, budget);
9656
- return { result, tool: this.registry.get(use.name), durationMs: 0 };
9807
+ return { result, tool, durationMs: 0 };
9657
9808
  }
9658
9809
  };
9659
9810
  if (strategy === "sequential") {
@@ -9885,6 +10036,58 @@ function extractMalformedRaw(input) {
9885
10036
  }
9886
10037
  }
9887
10038
  var TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES = 64 * 1024;
10039
+ function classifyToolError(err) {
10040
+ if (err instanceof Error && err.name === "AbortError") {
10041
+ return { category: "fatal" /* FATAL */, retryable: false, detail: "aborted" };
10042
+ }
10043
+ if (err instanceof Error && "code" in err) {
10044
+ const code = err.code;
10045
+ switch (code) {
10046
+ case "ETIMEDOUT":
10047
+ case "ECONNRESET":
10048
+ case "ECONNREFUSED":
10049
+ case "ENETUNREACH":
10050
+ case "EHOSTUNREACH":
10051
+ return { category: "transient" /* TRANSIENT */, retryable: true, detail: code };
10052
+ case "ENOENT":
10053
+ case "ENOTDIR":
10054
+ return { category: "not_found" /* NOT_FOUND */, retryable: false, detail: code };
10055
+ case "EACCES":
10056
+ case "EPERM":
10057
+ return { category: "permission" /* PERMISSION */, retryable: false, detail: code };
10058
+ case "EBUSY":
10059
+ case "EMFILE":
10060
+ case "ENFILE":
10061
+ return { category: "transient" /* TRANSIENT */, retryable: true, detail: code };
10062
+ }
10063
+ }
10064
+ if (err instanceof Error && "response" in err) {
10065
+ const response = err.response;
10066
+ const status = response?.status;
10067
+ if (status !== void 0) {
10068
+ if (status === 429 || status === 503 || status === 502 || status === 504) {
10069
+ return { category: "transient" /* TRANSIENT */, retryable: true, detail: `HTTP ${status}` };
10070
+ }
10071
+ if (status === 404 || status === 410) {
10072
+ return { category: "not_found" /* NOT_FOUND */, retryable: false, detail: `HTTP ${status}` };
10073
+ }
10074
+ if (status === 401 || status === 403) {
10075
+ return { category: "permission" /* PERMISSION */, retryable: false, detail: `HTTP ${status}` };
10076
+ }
10077
+ if (status === 400) {
10078
+ return { category: "validation" /* VALIDATION */, retryable: false, detail: `HTTP ${status}` };
10079
+ }
10080
+ }
10081
+ }
10082
+ if (err instanceof Error && err.message.includes("validation")) {
10083
+ return { category: "validation" /* VALIDATION */, retryable: false, detail: "validation" };
10084
+ }
10085
+ return {
10086
+ category: "fatal" /* FATAL */,
10087
+ retryable: false,
10088
+ detail: err instanceof Error ? err.message.slice(0, 100) : String(err).slice(0, 100)
10089
+ };
10090
+ }
9888
10091
  async function maybePersistLargeToolOutput(toolName, content, budget) {
9889
10092
  const bytes = Buffer.byteLength(content, "utf8");
9890
10093
  if (bytes <= Math.min(TOOL_OUTPUT_ARTIFACT_THRESHOLD_BYTES, Math.max(0, budget))) {
@@ -10827,8 +11030,8 @@ ${recentJournal}` : "No prior iterations.",
10827
11030
  await saveGoal(this.goalPath, abandoned, this.opts.events);
10828
11031
  }
10829
11032
  try {
10830
- const { unlink: unlink10 } = await import('fs/promises');
10831
- await unlink10(this.goalPath);
11033
+ const { unlink: unlink11 } = await import('fs/promises');
11034
+ await unlink11(this.goalPath);
10832
11035
  } catch {
10833
11036
  }
10834
11037
  this.opts.onEternalStop?.();
@@ -11211,7 +11414,7 @@ var SubagentBudget = class _SubagentBudget {
11211
11414
  */
11212
11415
  _busRequestDecision(entry) {
11213
11416
  const bus = this._events;
11214
- if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
11417
+ if (!bus?.hasListenerFor("budget.threshold_reached")) {
11215
11418
  return Promise.resolve("stop");
11216
11419
  }
11217
11420
  return new Promise((resolve6) => {