@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,13 +2,15 @@ import { randomBytes, randomUUID, createHash } from 'crypto';
2
2
  import * as fsp from 'fs/promises';
3
3
  import * as path2 from 'path';
4
4
  import * as os from 'os';
5
+ import { hostname } from 'os';
5
6
 
6
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
7
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
8
- }) : x)(function(x) {
9
- if (typeof require !== "undefined") return require.apply(this, arguments);
10
- throw Error('Dynamic require of "' + x + '" is not supported');
11
- });
7
+ // src/utils/expect-defined.ts
8
+ function expectDefined(value, label) {
9
+ if (value === null || value === void 0) {
10
+ throw new Error("Expected value to be defined");
11
+ }
12
+ return value;
13
+ }
12
14
  async function atomicWrite(targetPath, content, opts = {}) {
13
15
  const dir = path2.dirname(targetPath);
14
16
  await fsp.mkdir(dir, { recursive: true });
@@ -50,6 +52,49 @@ async function atomicWrite(targetPath, content, opts = {}) {
50
52
  async function ensureDir(dir) {
51
53
  await fsp.mkdir(dir, { recursive: true });
52
54
  }
55
+ async function withFileLock(targetPath, fn, opts = {}) {
56
+ const dir = path2.dirname(targetPath);
57
+ await fsp.mkdir(dir, { recursive: true });
58
+ const lockPath = path2.join(dir, `.${path2.basename(targetPath)}.lock`);
59
+ const timeoutMs = opts.timeoutMs ?? 5e3;
60
+ const staleMs = opts.staleMs ?? 3e4;
61
+ const started = Date.now();
62
+ let handle;
63
+ for (; ; ) {
64
+ try {
65
+ handle = await fsp.open(lockPath, "wx");
66
+ await handle.writeFile(`${process.pid}:${Date.now()}`);
67
+ break;
68
+ } catch (err) {
69
+ if (err.code !== "EEXIST") throw err;
70
+ try {
71
+ const stat5 = await fsp.stat(lockPath);
72
+ if (Date.now() - stat5.mtimeMs > staleMs) {
73
+ await fsp.unlink(lockPath);
74
+ continue;
75
+ }
76
+ } catch {
77
+ continue;
78
+ }
79
+ if (Date.now() - started >= timeoutMs) {
80
+ throw new Error(`Timed out waiting for file lock: ${targetPath}`);
81
+ }
82
+ await new Promise((resolve4) => setTimeout(resolve4, 25));
83
+ }
84
+ }
85
+ try {
86
+ return await fn();
87
+ } finally {
88
+ try {
89
+ await handle?.close();
90
+ } catch {
91
+ }
92
+ try {
93
+ await fsp.unlink(lockPath);
94
+ } catch {
95
+ }
96
+ }
97
+ }
53
98
  var TRANSIENT_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOTEMPTY"]);
54
99
  async function renameWithRetry(from, to) {
55
100
  if (process.platform !== "win32") {
@@ -75,12 +120,6 @@ async function renameWithRetry(from, to) {
75
120
  }
76
121
 
77
122
  // src/utils/message-invariants.ts
78
- function expectDefined(value) {
79
- if (value === null || value === void 0) {
80
- throw new Error("Expected value to be defined");
81
- }
82
- return value;
83
- }
84
123
  function repairToolUseAdjacency(messages) {
85
124
  const removedToolUses = [];
86
125
  const removedToolResults = [];
@@ -173,12 +212,6 @@ function isEmptyMessage(msg) {
173
212
  }
174
213
 
175
214
  // src/storage/session-store.ts
176
- function expectDefined2(value) {
177
- if (value === null || value === void 0) {
178
- throw new Error("Expected value to be defined");
179
- }
180
- return value;
181
- }
182
215
  function sanitizeModel(model) {
183
216
  return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
184
217
  }
@@ -889,7 +922,7 @@ var FileSessionWriter = class {
889
922
  let targetCheckpointLine = -1;
890
923
  let afterTarget = false;
891
924
  for (let i = 0; i < lines.length; i++) {
892
- const line = expectDefined2(lines[i]);
925
+ const line = expectDefined(lines[i]);
893
926
  if (!line.trim()) continue;
894
927
  let event;
895
928
  try {
@@ -1178,9 +1211,235 @@ function mergeAdjacentText(blocks) {
1178
1211
  }
1179
1212
  return out;
1180
1213
  }
1214
+
1215
+ // src/types/memory.ts
1216
+ var MEMORY_TYPE_LABELS = {
1217
+ fact: "Fact",
1218
+ decision: "Decision",
1219
+ convention: "Convention",
1220
+ preference: "Preference",
1221
+ reference: "Reference",
1222
+ anti_pattern: "Anti-pattern"
1223
+ };
1224
+
1225
+ // src/storage/memory-backend.ts
1226
+ var TYPE_PRIORITY_RE = /^\[(\w+)\|(\w+)\]\s+/;
1227
+ var TAG_RE = /#([\w-]+)/g;
1228
+ function formatMetadata(entry) {
1229
+ const parts = [];
1230
+ if (entry.type && entry.priority) {
1231
+ parts.push(`[${entry.type}|${entry.priority}]`);
1232
+ } else if (entry.type) {
1233
+ parts.push(`[${entry.type}]`);
1234
+ } else if (entry.priority) {
1235
+ parts.push(`[${entry.priority}]`);
1236
+ }
1237
+ if (entry.tags && entry.tags.length > 0) {
1238
+ parts.push(entry.tags.map((t) => `#${t}`).join(" "));
1239
+ }
1240
+ return parts.length > 0 ? ` ${parts.join(" ")}` : "";
1241
+ }
1242
+ function lineToEntry(line, scope) {
1243
+ const trimmed = line.trim();
1244
+ if (!trimmed.startsWith("- [")) return null;
1245
+ const tsMatch = trimmed.match(/^-\s*\[([^\]]+)\]/);
1246
+ if (!tsMatch) return null;
1247
+ const ts = tsMatch[1] ?? "";
1248
+ let rest = trimmed.slice(tsMatch[0].length).trim();
1249
+ let type;
1250
+ let priority;
1251
+ const tpMatch = rest.match(TYPE_PRIORITY_RE);
1252
+ if (tpMatch) {
1253
+ const a = tpMatch[1] ?? "";
1254
+ const b = tpMatch[2] ?? "";
1255
+ if (isMemoryType(a)) {
1256
+ type = a;
1257
+ priority = isPriority(b) ? b : void 0;
1258
+ } else if (isPriority(a)) {
1259
+ priority = a;
1260
+ }
1261
+ rest = rest.slice(tpMatch[0].length).trim();
1262
+ }
1263
+ const idMatch = rest.match(/^mem_\d+_\w+\s+/);
1264
+ let text;
1265
+ if (idMatch) {
1266
+ text = rest.slice(idMatch[0].length).trim();
1267
+ } else {
1268
+ text = rest.trim();
1269
+ }
1270
+ const tags = [];
1271
+ let tagMatch;
1272
+ TAG_RE.lastIndex = 0;
1273
+ while ((tagMatch = TAG_RE.exec(text)) !== null) {
1274
+ tags.push(tagMatch[1] ?? "");
1275
+ }
1276
+ const cleanText = text.replace(TAG_RE, "").replace(/\s{2,}/g, " ").trim();
1277
+ if (!cleanText) return null;
1278
+ return {
1279
+ scope,
1280
+ text: cleanText,
1281
+ ts,
1282
+ type,
1283
+ priority,
1284
+ tags: tags.length > 0 ? tags : void 0
1285
+ };
1286
+ }
1287
+ function isMemoryType(s) {
1288
+ return s in MEMORY_TYPE_LABELS;
1289
+ }
1290
+ function isPriority(s) {
1291
+ return s === "critical" || s === "high" || s === "medium" || s === "low";
1292
+ }
1293
+ var FileMemoryBackend = class {
1294
+ kind = "file";
1295
+ files;
1296
+ constructor(opts) {
1297
+ this.files = {
1298
+ "project-agents": opts.paths.inProjectAgentsFile,
1299
+ "project-memory": opts.paths.projectMemory,
1300
+ "user-memory": opts.paths.globalMemory
1301
+ };
1302
+ }
1303
+ resolveFile(filePath, scope) {
1304
+ return filePath || this.files[scope];
1305
+ }
1306
+ async remember(scope, entry, filePath) {
1307
+ const file = this.resolveFile(filePath, scope);
1308
+ await ensureDir(path2.dirname(file));
1309
+ let existing = "";
1310
+ try {
1311
+ existing = await fsp.readFile(file, "utf8");
1312
+ } catch {
1313
+ }
1314
+ const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
1315
+ const meta = formatMetadata(entry);
1316
+ const line = `
1317
+ - [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
1318
+ `;
1319
+ const next = existing.trim() ? existing.replace(/\n+$/, "") + line : `# WrongStack Memory
1320
+ ${line}`;
1321
+ await atomicWrite(file, next);
1322
+ }
1323
+ async forget(scope, query, filePath) {
1324
+ const file = this.resolveFile(filePath, scope);
1325
+ let existing;
1326
+ try {
1327
+ existing = await fsp.readFile(file, "utf8");
1328
+ } catch {
1329
+ return 0;
1330
+ }
1331
+ const needle = query.toLowerCase();
1332
+ const idMatcher = /mem_\d+_\w+/;
1333
+ let removed = 0;
1334
+ const lines = existing.split("\n").filter((line) => {
1335
+ const trimmed = line.trim();
1336
+ if (!trimmed.startsWith("- ")) return true;
1337
+ if (idMatcher.test(query)) {
1338
+ const entryIdMatch = /mem_\d+_\w+/.exec(trimmed);
1339
+ if (entryIdMatch && entryIdMatch[0] === query) {
1340
+ removed++;
1341
+ return false;
1342
+ }
1343
+ }
1344
+ if (trimmed.toLowerCase().includes(needle)) {
1345
+ removed++;
1346
+ return false;
1347
+ }
1348
+ return true;
1349
+ });
1350
+ if (removed > 0) {
1351
+ if (lines.length === 0 || lines.length === 1 && !lines[0]?.trim()) {
1352
+ await atomicWrite(file, "");
1353
+ } else {
1354
+ await atomicWrite(file, lines.join("\n"));
1355
+ }
1356
+ }
1357
+ return removed;
1358
+ }
1359
+ async readAll(scope, filePath) {
1360
+ const file = this.resolveFile(filePath, scope);
1361
+ try {
1362
+ return await fsp.readFile(file, "utf8");
1363
+ } catch {
1364
+ return "";
1365
+ }
1366
+ }
1367
+ async list(scope, filePath, limit) {
1368
+ const raw = await this.readAll(scope, filePath);
1369
+ if (!raw.trim()) return [];
1370
+ const entries = parseEntries(raw, scope);
1371
+ return limit ? entries.slice(0, limit) : entries;
1372
+ }
1373
+ async search(scope, query, filePath, limit) {
1374
+ const entries = await this.list(scope, filePath);
1375
+ const needle = query.toLowerCase().split(/\s+/);
1376
+ const scored = entries.map((e) => {
1377
+ const words = e.text.toLowerCase().split(/\s+/);
1378
+ let score = 0;
1379
+ for (const n of needle) {
1380
+ if (words.some((w) => w.includes(n))) score += 1;
1381
+ if (e.tags?.some((t) => t.toLowerCase().includes(n))) score += 2;
1382
+ }
1383
+ return { entry: e, score };
1384
+ });
1385
+ scored.sort((a, b) => b.score - a.score);
1386
+ const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
1387
+ return limit ? matched.slice(0, limit) : matched;
1388
+ }
1389
+ async clear(scope, filePath) {
1390
+ const file = this.resolveFile(filePath, scope);
1391
+ await atomicWrite(file, "");
1392
+ }
1393
+ async consolidate(scope, filePath) {
1394
+ const file = this.resolveFile(filePath, scope);
1395
+ let existing;
1396
+ try {
1397
+ existing = await fsp.readFile(file, "utf8");
1398
+ } catch {
1399
+ return 0;
1400
+ }
1401
+ const seen = /* @__PURE__ */ new Set();
1402
+ let removed = 0;
1403
+ const lines = existing.split("\n").filter((line) => {
1404
+ const trimmed = line.trim();
1405
+ if (!trimmed.startsWith("- ")) return true;
1406
+ const norm = trimmed.replace(/\[[^\]]+\]/g, "").replace(/\bmem_\d+_\w+\s*/, "").replace(/#[\w-]+/g, "").replace(/\s+/g, " ").trim().toLowerCase();
1407
+ if (seen.has(norm)) {
1408
+ removed++;
1409
+ return false;
1410
+ }
1411
+ seen.add(norm);
1412
+ return true;
1413
+ });
1414
+ const next = lines.join("\n");
1415
+ const backup = `${file}.bak.${Date.now()}`;
1416
+ try {
1417
+ await fsp.copyFile(file, backup);
1418
+ } catch {
1419
+ }
1420
+ try {
1421
+ await atomicWrite(file, next);
1422
+ } catch {
1423
+ return 0;
1424
+ }
1425
+ return removed;
1426
+ }
1427
+ };
1428
+ function parseEntries(raw, scope = "project-memory") {
1429
+ const entries = [];
1430
+ for (const line of raw.split("\n")) {
1431
+ const entry = lineToEntry(line, scope);
1432
+ if (entry) entries.push(entry);
1433
+ }
1434
+ return entries.reverse();
1435
+ }
1436
+
1437
+ // src/storage/memory-store.ts
1181
1438
  var MAX_BYTES_TOTAL = 32e3;
1182
1439
  var DefaultMemoryStore = class {
1183
1440
  files;
1441
+ events;
1442
+ backend;
1184
1443
  /**
1185
1444
  * Per-scope serialization queue. `remember` / `forget` / `consolidate` /
1186
1445
  * `clear` are read-modify-write against a single file; without a lock,
@@ -1197,12 +1456,29 @@ var DefaultMemoryStore = class {
1197
1456
  writeChain = /* @__PURE__ */ new Map();
1198
1457
  /** Last write error per scope — surfaced as warnings on the next readAll(). */
1199
1458
  writeErrors = /* @__PURE__ */ new Map();
1459
+ /**
1460
+ * When the global root is a temporary directory (opencode, CI sandboxes),
1461
+ * memory files are also mirrored to the project tree so they survive
1462
+ * session cleanup. The primary path stays in the temp root for isolation;
1463
+ * this backup ensures memory is never lost.
1464
+ */
1465
+ persistBackup;
1466
+ backupDir;
1200
1467
  constructor(opts) {
1201
1468
  this.files = {
1202
1469
  "project-agents": opts.paths.inProjectAgentsFile,
1203
1470
  "project-memory": opts.paths.projectMemory,
1204
1471
  "user-memory": opts.paths.globalMemory
1205
1472
  };
1473
+ this.events = opts.events;
1474
+ this.backend = opts.backend ?? new FileMemoryBackend({ paths: opts.paths });
1475
+ const root = opts.paths.globalRoot.toLowerCase();
1476
+ this.persistBackup = /[/\\](tmp|temp|cache)[/\\]/.test(root);
1477
+ this.backupDir = this.persistBackup ? opts.paths.inProjectAgentsFile.replace(/AGENTS\.md$/, "memory-persist") : "";
1478
+ }
1479
+ /** Expose the backend for plugin introspection and advanced queries. */
1480
+ getBackend() {
1481
+ return this.backend;
1206
1482
  }
1207
1483
  async runSerialized(scope, work) {
1208
1484
  const prior = this.writeChain.get(scope) ?? Promise.resolve();
@@ -1229,7 +1505,7 @@ var DefaultMemoryStore = class {
1229
1505
  if (writeErr) {
1230
1506
  parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr.message}`);
1231
1507
  }
1232
- const body = await this.read(scope);
1508
+ const body = await this.backend.readAll(scope, this.files[scope]);
1233
1509
  if (body.trim()) parts.push(`## ${labelOf(scope)}
1234
1510
 
1235
1511
  ${body.trim()}`);
@@ -1237,117 +1513,214 @@ ${body.trim()}`);
1237
1513
  return parts.join("\n\n");
1238
1514
  }
1239
1515
  async read(scope) {
1240
- try {
1241
- return await fsp.readFile(this.files[scope], "utf8");
1242
- } catch {
1243
- return "";
1516
+ return this.backend.readAll(scope, this.files[scope]);
1517
+ }
1518
+ /**
1519
+ * List entries from a scope, newest first. Delegates to the backend
1520
+ * so graph/semantic backends can return enriched or filtered results.
1521
+ */
1522
+ async list(scope = "project-memory", limit) {
1523
+ return this.backend.list(scope, this.files[scope], limit);
1524
+ }
1525
+ /**
1526
+ * Find memories related to the given text via graph traversal.
1527
+ * Falls back to content search when no graph backend is available.
1528
+ */
1529
+ async findRelated(text, scope = "project-memory", limit = 5) {
1530
+ if (this.backend.findRelated) {
1531
+ return this.backend.findRelated(scope, this.files[scope], text, limit);
1244
1532
  }
1533
+ return this.search(text, scope, limit);
1534
+ }
1535
+ async search(query, scope = "project-memory", limit) {
1536
+ return this.backend.search(scope, query, this.files[scope], limit);
1245
1537
  }
1246
- async remember(text, scope = "project-memory") {
1538
+ async remember(text, scope = "project-memory", metadata) {
1539
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
1247
1540
  return this.runSerialized(scope, async () => {
1248
- const file = this.files[scope];
1249
- await ensureDir(path2.dirname(file));
1250
- let existing = "";
1251
- try {
1252
- existing = await fsp.readFile(file, "utf8");
1253
- } catch {
1254
- }
1255
- const ts = (/* @__PURE__ */ new Date()).toISOString();
1256
- const id = `mem_${Date.now()}_${randomUUID().slice(0, 8)}`;
1257
- const entry = `
1258
- - [${ts}] ${id} ${text.replace(/\n/g, " ")}
1259
- `;
1260
- const next = existing.trim() ? existing.replace(/\n+$/, "") + entry : `# WrongStack Memory
1261
- ${entry}`;
1262
- await atomicWrite(file, next);
1263
- const buf = Buffer.byteLength(next, "utf8");
1264
- if (buf > MAX_BYTES_TOTAL) {
1265
- await this.consolidateUnsafe(scope);
1541
+ const entry = { scope, text, ts, ...metadata };
1542
+ await this.backend.remember(scope, entry, this.files[scope]);
1543
+ const raw = await this.backend.readAll(scope, this.files[scope]);
1544
+ if (Buffer.byteLength(raw, "utf8") > MAX_BYTES_TOTAL) {
1545
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
1546
+ if (removed > 0) {
1547
+ this.events?.emit("memory.consolidated", {
1548
+ scope,
1549
+ removed
1550
+ });
1551
+ }
1266
1552
  }
1553
+ await this.mirrorBackup(scope);
1554
+ this.events?.emit("memory.remembered", {
1555
+ scope,
1556
+ text,
1557
+ ts,
1558
+ type: entry.type,
1559
+ tags: entry.tags,
1560
+ priority: entry.priority
1561
+ });
1267
1562
  });
1268
1563
  }
1269
- async forget(query, scope = "project-memory") {
1270
- return this.runSerialized(scope, async () => this.forgetUnsafe(query, scope));
1271
- }
1272
- async forgetUnsafe(query, scope) {
1273
- const file = this.files[scope];
1274
- let existing;
1275
- try {
1276
- existing = await fsp.readFile(file, "utf8");
1277
- } catch {
1278
- return 0;
1279
- }
1280
- const needle = query.toLowerCase();
1281
- const idMatcher = /mem_\d+_\w+/;
1282
- let removed = 0;
1283
- const lines = existing.split("\n").filter((line) => {
1284
- const trimmed = line.trim();
1285
- if (!trimmed.startsWith("- ")) return true;
1286
- if (idMatcher.test(query)) {
1287
- const afterBracket = trimmed.indexOf("] ");
1288
- if (afterBracket !== -1) {
1289
- const afterTs = trimmed.slice(afterBracket + 2);
1290
- const entryIdMatch = /^mem_\d+_\w+/.exec(afterTs);
1291
- if (entryIdMatch && entryIdMatch[0] === query) {
1292
- removed++;
1293
- return false;
1294
- }
1564
+ /**
1565
+ * Score and rank memories by relevance to the current context.
1566
+ * Returns entries with score >= MIN_RELEVANCE_SCORE, sorted highest first.
1567
+ */
1568
+ async scoreRelevant(ctx, scope = "project-memory", limit = 8) {
1569
+ const all = await this.list(scope);
1570
+ if (all.length === 0) return [];
1571
+ const taskWords = ctx.currentTask.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
1572
+ const skillWords = (ctx.activeSkills ?? []).flatMap((s) => s.split("-"));
1573
+ const toolWords = (ctx.toolNames ?? []).flatMap((t) => t.toLowerCase().split("_"));
1574
+ const now = Date.now();
1575
+ const scored = [];
1576
+ for (const entry of all) {
1577
+ let score = 0;
1578
+ const reasons = [];
1579
+ const textLower = entry.text.toLowerCase();
1580
+ const tagsLower = (entry.tags ?? []).map((t) => t.toLowerCase());
1581
+ let taskHits = 0;
1582
+ for (const w of taskWords) {
1583
+ if (textLower.includes(w)) {
1584
+ taskHits++;
1585
+ score += 2;
1586
+ }
1587
+ if (tagsLower.some((t) => t.includes(w))) {
1588
+ taskHits++;
1589
+ score += 3;
1295
1590
  }
1296
1591
  }
1297
- if (trimmed.toLowerCase().includes(needle)) {
1298
- removed++;
1299
- return false;
1592
+ if (taskHits > 0) reasons.push(`task match (${taskHits})`);
1593
+ let skillHits = 0;
1594
+ for (const w of skillWords) {
1595
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
1596
+ skillHits++;
1597
+ }
1598
+ }
1599
+ score += skillHits;
1600
+ if (skillHits > 0) reasons.push(`skill match (${skillHits})`);
1601
+ for (const w of toolWords) {
1602
+ if (w.length > 2 && (textLower.includes(w) || tagsLower.some((t) => t.includes(w)))) {
1603
+ score += 1;
1604
+ reasons.push(`tool mention: ${w}`);
1605
+ }
1606
+ }
1607
+ switch (entry.priority) {
1608
+ case "critical":
1609
+ score += 5;
1610
+ reasons.push("critical");
1611
+ break;
1612
+ case "high":
1613
+ score += 3;
1614
+ reasons.push("high priority");
1615
+ break;
1616
+ case "medium":
1617
+ score += 1;
1618
+ break;
1619
+ case "low":
1620
+ score -= 2;
1621
+ reasons.push("low priority");
1622
+ break;
1623
+ }
1624
+ switch (entry.type) {
1625
+ case "decision":
1626
+ score += 2;
1627
+ reasons.push("decision");
1628
+ break;
1629
+ case "convention":
1630
+ score += 2;
1631
+ reasons.push("convention");
1632
+ break;
1633
+ case "anti_pattern":
1634
+ score += 3;
1635
+ reasons.push("anti-pattern");
1636
+ break;
1637
+ case "preference":
1638
+ score += 1;
1639
+ reasons.push("preference");
1640
+ break;
1641
+ }
1642
+ const ageDays = (now - new Date(entry.ts).getTime()) / (1e3 * 60 * 60 * 24);
1643
+ if (ageDays < 1) score += 1;
1644
+ else if (ageDays > 30) score -= 1;
1645
+ if (entry.confidence !== void 0 && entry.confidence < 0.5) {
1646
+ score -= 2;
1647
+ reasons.push("low confidence");
1648
+ }
1649
+ if (entry.lastAccessed) {
1650
+ const hoursSinceAccess = (now - new Date(entry.lastAccessed).getTime()) / (1e3 * 60 * 60);
1651
+ if (hoursSinceAccess < 1) score -= 1;
1652
+ }
1653
+ if (score > 0) {
1654
+ scored.push({
1655
+ ...entry,
1656
+ score,
1657
+ matchReason: reasons.join(", ") || "keyword match"
1658
+ });
1300
1659
  }
1301
- return true;
1302
- });
1303
- if (removed > 0) {
1304
- await atomicWrite(file, lines.join("\n"));
1305
1660
  }
1306
- return removed;
1661
+ scored.sort((a, b) => b.score - a.score);
1662
+ const threshold = 2;
1663
+ const relevant = scored.filter(
1664
+ (s) => s.score >= threshold || s.priority === "critical" || s.priority === "high"
1665
+ );
1666
+ return relevant.slice(0, Math.min(limit, 15));
1307
1667
  }
1308
- async consolidate(scope) {
1309
- return this.runSerialized(scope, async () => this.consolidateUnsafe(scope));
1668
+ async forget(query, scope = "project-memory") {
1669
+ return this.runSerialized(scope, async () => {
1670
+ const removed = await this.backend.forget(scope, query, this.files[scope]);
1671
+ if (removed > 0) {
1672
+ this.events?.emit("memory.forgotten", {
1673
+ scope,
1674
+ query,
1675
+ removed
1676
+ });
1677
+ await this.mirrorBackup(scope);
1678
+ }
1679
+ return removed;
1680
+ });
1310
1681
  }
1311
- async consolidateUnsafe(scope) {
1312
- const file = this.files[scope];
1313
- let existing;
1314
- try {
1315
- existing = await fsp.readFile(file, "utf8");
1316
- } catch {
1317
- return;
1318
- }
1319
- const seen = /* @__PURE__ */ new Set();
1320
- const lines = existing.split("\n").filter((line) => {
1321
- const trimmed = line.trim();
1322
- if (!trimmed.startsWith("- ")) return true;
1323
- const norm = trimmed.replace(/\[[^\]]+\]/, "").replace(/\bmem_\d+_\w+\s*/, "").trim().toLowerCase();
1324
- if (seen.has(norm)) return false;
1325
- seen.add(norm);
1326
- return true;
1682
+ async consolidate(scope) {
1683
+ return this.runSerialized(scope, async () => {
1684
+ const removed = await this.backend.consolidate(scope, this.files[scope]);
1685
+ if (removed > 0) {
1686
+ this.events?.emit("memory.consolidated", {
1687
+ scope,
1688
+ removed
1689
+ });
1690
+ await this.mirrorBackup(scope);
1691
+ }
1327
1692
  });
1328
- const next = lines.join("\n");
1329
- const backup = `${file}.bak.${Date.now()}`;
1330
- try {
1331
- await fsp.copyFile(file, backup);
1332
- } catch {
1333
- }
1334
- try {
1335
- await atomicWrite(file, next);
1336
- } catch {
1337
- return;
1338
- }
1339
1693
  }
1340
1694
  async clear(scope) {
1341
1695
  if (scope) {
1342
- await this.runSerialized(scope, async () => atomicWrite(this.files[scope], ""));
1696
+ await this.runSerialized(scope, async () => {
1697
+ await this.backend.clear(scope, this.files[scope]);
1698
+ this.events?.emit("memory.cleared", { scope });
1699
+ await this.mirrorBackup(scope);
1700
+ });
1343
1701
  return;
1344
1702
  }
1345
1703
  await Promise.all(
1346
1704
  ["project-agents", "project-memory", "user-memory"].map(
1347
- (s) => this.runSerialized(s, async () => atomicWrite(this.files[s], ""))
1705
+ (s) => this.runSerialized(s, async () => {
1706
+ await this.backend.clear(s, this.files[s]);
1707
+ this.events?.emit("memory.cleared", { scope: s });
1708
+ await this.mirrorBackup(s);
1709
+ })
1348
1710
  )
1349
1711
  );
1350
1712
  }
1713
+ /** Mirror current memory content to the persistent backup directory. */
1714
+ async mirrorBackup(scope) {
1715
+ if (!this.persistBackup || scope === "project-agents") return;
1716
+ try {
1717
+ const content = await this.backend.readAll(scope, this.files[scope]);
1718
+ const { writeFile: writeFile6, mkdir: mkdir5 } = await import('fs/promises');
1719
+ await mkdir5(this.backupDir, { recursive: true });
1720
+ await writeFile6(`${this.backupDir}/${scope}.md`, content, "utf8");
1721
+ } catch {
1722
+ }
1723
+ }
1351
1724
  };
1352
1725
  function labelOf(scope) {
1353
1726
  switch (scope) {
@@ -1359,6 +1732,384 @@ function labelOf(scope) {
1359
1732
  return "User memory";
1360
1733
  }
1361
1734
  }
1735
+ var GraphMemoryBackend = class {
1736
+ kind = "graph";
1737
+ file;
1738
+ graphFile;
1739
+ // In-memory graph state — lazily loaded, saved on mutation.
1740
+ nodes = /* @__PURE__ */ new Map();
1741
+ edges = [];
1742
+ loadedScope = null;
1743
+ loaded = false;
1744
+ constructor(opts) {
1745
+ this.file = new FileMemoryBackend({ paths: opts.paths });
1746
+ this.graphFile = opts.graphPath ?? `${opts.paths.projectDir}/memory-graph.json`;
1747
+ }
1748
+ // ── Backend interface ──────────────────────────────────────────────
1749
+ async remember(scope, entry, filePath) {
1750
+ await this.file.remember(scope, entry, filePath);
1751
+ await this.loadGraph(scope);
1752
+ const nodeId = this.nodeId(entry);
1753
+ const existing = this.nodes.get(nodeId);
1754
+ if (existing) {
1755
+ existing.count++;
1756
+ existing.entry = entry;
1757
+ existing.type = entry.type;
1758
+ existing.tags = entry.tags;
1759
+ existing.priority = entry.priority;
1760
+ } else {
1761
+ this.nodes.set(nodeId, {
1762
+ id: nodeId,
1763
+ entry,
1764
+ firstSeen: entry.ts,
1765
+ count: 1,
1766
+ type: entry.type,
1767
+ tags: entry.tags,
1768
+ priority: entry.priority
1769
+ });
1770
+ for (const [, other] of this.nodes) {
1771
+ if (other.id === nodeId) continue;
1772
+ const sim = wordOverlap(entry.text, other.entry.text);
1773
+ const tagSim = sharedTags(entry.tags ?? [], other.tags ?? []);
1774
+ const weight = Math.max(sim, tagSim * 0.5);
1775
+ if (weight > 0.15) {
1776
+ this.edges.push({
1777
+ from: nodeId,
1778
+ to: other.id,
1779
+ relation: sim >= tagSim ? "similar" : "same_turn",
1780
+ weight,
1781
+ ts: entry.ts
1782
+ });
1783
+ }
1784
+ }
1785
+ }
1786
+ await this.saveGraph(scope);
1787
+ }
1788
+ async forget(scope, query, filePath) {
1789
+ const removed = await this.file.forget(scope, query, filePath);
1790
+ if (removed > 0) {
1791
+ await this.loadGraph(scope);
1792
+ const n = query.toLowerCase();
1793
+ const toRemove = [];
1794
+ for (const [id, node] of this.nodes) {
1795
+ if (node.entry.text.toLowerCase().includes(n)) {
1796
+ toRemove.push(id);
1797
+ }
1798
+ }
1799
+ for (const id of toRemove) this.nodes.delete(id);
1800
+ this.edges = this.edges.filter((e) => !toRemove.includes(e.from) && !toRemove.includes(e.to));
1801
+ await this.saveGraph(scope);
1802
+ }
1803
+ return removed;
1804
+ }
1805
+ async readAll(scope, filePath) {
1806
+ return this.file.readAll(scope, filePath);
1807
+ }
1808
+ async list(scope, filePath, limit) {
1809
+ await this.loadGraph(scope);
1810
+ const fileEntries = await this.file.list(scope, filePath);
1811
+ const nodeMap = new Map(this.nodes.entries());
1812
+ const enriched = fileEntries.map((fe) => {
1813
+ const nodeId = this.nodeId(fe);
1814
+ const node = nodeMap.get(nodeId);
1815
+ if (node) {
1816
+ return {
1817
+ ...fe,
1818
+ type: node.type ?? fe.type,
1819
+ tags: node.tags ?? fe.tags,
1820
+ priority: node.priority ?? fe.priority
1821
+ };
1822
+ }
1823
+ return fe;
1824
+ });
1825
+ const fileIds = new Set(fileEntries.map((e) => this.nodeId(e)));
1826
+ for (const [id, node] of this.nodes) {
1827
+ if (!fileIds.has(id)) {
1828
+ enriched.push(node.entry);
1829
+ }
1830
+ }
1831
+ enriched.sort((a, b) => b.ts.localeCompare(a.ts));
1832
+ return limit ? enriched.slice(0, limit) : enriched;
1833
+ }
1834
+ async search(scope, query, filePath, limit) {
1835
+ await this.loadGraph(scope);
1836
+ const needle = query.toLowerCase().split(/\s+/);
1837
+ const all = await this.list(scope, filePath);
1838
+ const scored = all.map((entry) => {
1839
+ const words = entry.text.toLowerCase().split(/\s+/);
1840
+ let score = 0;
1841
+ for (const n of needle) {
1842
+ if (words.some((w) => w.includes(n))) score += 1;
1843
+ if (entry.tags?.some((t) => t.toLowerCase().includes(n))) score += 2;
1844
+ }
1845
+ const node = this.nodes.get(this.nodeId(entry));
1846
+ if (node) {
1847
+ if (node.priority === "critical") score += 3;
1848
+ else if (node.priority === "high") score += 2;
1849
+ score += node.count * 0.5;
1850
+ }
1851
+ return { entry, score };
1852
+ });
1853
+ scored.sort((a, b) => b.score - a.score);
1854
+ const matched = scored.filter((s) => s.score > 0).map((s) => s.entry);
1855
+ return limit ? matched.slice(0, limit) : matched;
1856
+ }
1857
+ async clear(scope, filePath) {
1858
+ await this.file.clear(scope, filePath);
1859
+ this.nodes.clear();
1860
+ this.edges = [];
1861
+ this.loadedScope = scope;
1862
+ this.loaded = true;
1863
+ try {
1864
+ await fsp.unlink(this.graphFile);
1865
+ } catch {
1866
+ }
1867
+ }
1868
+ async consolidate(scope, filePath) {
1869
+ return this.file.consolidate(scope, filePath);
1870
+ }
1871
+ // ── Graph-specific queries ─────────────────────────────────────────
1872
+ /**
1873
+ * Find memories related to the given entry, ordered by edge weight.
1874
+ */
1875
+ async findRelated(scope, _filePath, entryText, limit = 5) {
1876
+ await this.loadGraph(scope);
1877
+ const targetId = this.nodeId({ scope, text: entryText, ts: "" });
1878
+ const related = this.edges.filter((e) => e.from === targetId || e.to === targetId).sort((a, b) => b.weight - a.weight).slice(0, limit);
1879
+ const result = [];
1880
+ for (const edge of related) {
1881
+ const otherId = edge.from === targetId ? edge.to : edge.from;
1882
+ const node = this.nodes.get(otherId);
1883
+ if (node) result.push(node.entry);
1884
+ }
1885
+ return result;
1886
+ }
1887
+ /**
1888
+ * Get all edges for visualization or traversal.
1889
+ */
1890
+ getGraph() {
1891
+ return {
1892
+ nodes: [...this.nodes.values()],
1893
+ edges: [...this.edges]
1894
+ };
1895
+ }
1896
+ // ── Persistence ────────────────────────────────────────────────────
1897
+ nodeId(entry) {
1898
+ const norm = entry.text.toLowerCase().trim().replace(/\s+/g, " ");
1899
+ return `${entry.scope ?? "mem"}::${simpleHash(norm)}`;
1900
+ }
1901
+ async loadGraph(scope) {
1902
+ if (this.loaded && this.loadedScope === scope) return;
1903
+ try {
1904
+ const raw = await fsp.readFile(this.graphFile, "utf8");
1905
+ const data = JSON.parse(raw);
1906
+ this.nodes = new Map(data.nodes);
1907
+ this.edges = data.edges;
1908
+ } catch {
1909
+ this.nodes = /* @__PURE__ */ new Map();
1910
+ this.edges = [];
1911
+ }
1912
+ this.loadedScope = scope;
1913
+ this.loaded = true;
1914
+ }
1915
+ async saveGraph(scope) {
1916
+ this.loadedScope = scope;
1917
+ this.loaded = true;
1918
+ try {
1919
+ const data = {
1920
+ nodes: [...this.nodes.entries()],
1921
+ edges: this.edges
1922
+ };
1923
+ await fsp.mkdir(
1924
+ this.graphFile.substring(0, this.graphFile.lastIndexOf("/")),
1925
+ { recursive: true }
1926
+ );
1927
+ const tmp = `${this.graphFile}.tmp`;
1928
+ await fsp.writeFile(tmp, JSON.stringify(data));
1929
+ await fsp.rename(tmp, this.graphFile);
1930
+ } catch {
1931
+ }
1932
+ }
1933
+ };
1934
+ function wordOverlap(a, b) {
1935
+ const wordsA = new Set(a.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
1936
+ const wordsB = new Set(b.toLowerCase().split(/\s+/).filter((w) => w.length > 2));
1937
+ if (wordsA.size === 0 || wordsB.size === 0) return 0;
1938
+ let intersection = 0;
1939
+ for (const w of wordsA) {
1940
+ if (wordsB.has(w)) intersection++;
1941
+ }
1942
+ return intersection / Math.max(wordsA.size, wordsB.size);
1943
+ }
1944
+ function sharedTags(a, b) {
1945
+ if (a.length === 0 || b.length === 0) return 0;
1946
+ const setB = new Set(b);
1947
+ let shared = 0;
1948
+ for (const t of a) if (setB.has(t)) shared++;
1949
+ return shared / Math.max(a.length, b.length);
1950
+ }
1951
+ function simpleHash(s) {
1952
+ let h = 0;
1953
+ for (let i = 0; i < s.length; i++) {
1954
+ h = (h << 5) - h + s.charCodeAt(i) | 0;
1955
+ }
1956
+ return Math.abs(h).toString(36);
1957
+ }
1958
+
1959
+ // src/storage/memory-consolidator.ts
1960
+ function buildConsolidationPrompt(finalText, iterations, existingEntries) {
1961
+ const existingBlock = existingEntries.length > 0 ? `
1962
+
1963
+ Existing memory entries:
1964
+ ${existingEntries.map((e) => `- [${e.ts.slice(0, 10)}] ${e.text}`).join("\n")}` : "";
1965
+ return `You are a memory consolidator. Review the following session summary and decide what key facts, conventions, decisions, or learnings should be persisted to long-term memory.
1966
+
1967
+ Session summary (${iterations} iterations):
1968
+ ${finalText.slice(0, 3e3)}${existingBlock}
1969
+
1970
+ Return a JSON object with an "operations" array. Each operation must have an "action" field:
1971
+ - "add": create a new memory entry. Include "text", and optionally "type", "tags", "priority".
1972
+ - "edit": replace an existing entry. Include "query" (to match) and "text" (replacement).
1973
+ - "delete": remove an entry. Include "query" (to match).
1974
+
1975
+ Memory types:
1976
+ - "fact": Objective truth about the project (e.g. "uses pnpm workspaces")
1977
+ - "decision": A choice that was made (e.g. "decided to use biome over eslint")
1978
+ - "convention": A recurring pattern or standard (e.g. "commit messages use conventional format")
1979
+ - "preference": User or team preference (e.g. "prefers short variable names")
1980
+ - "reference": Pointer to a file or location (e.g. "auth logic in packages/core/src/auth/")
1981
+ - "anti_pattern": Something to avoid (e.g. "never use any in TypeScript")
1982
+
1983
+ Priority levels:
1984
+ - "critical": Must always be known (e.g. security constraints)
1985
+ - "high": Important for most tasks
1986
+ - "medium": Useful context
1987
+ - "low": Nice to know
1988
+
1989
+ Rules:
1990
+ - Only persist facts likely useful across multiple future sessions.
1991
+ - Do NOT persist task progress, temporary state, or one-off observations.
1992
+ - Prefer "add" over "edit" unless the existing entry is clearly outdated.
1993
+ - Assign a type and priority to every "add" operation.
1994
+ - Use 1-3 hashtag tags for each entry (e.g. #typescript #build).
1995
+ - Be concise \u2014 each memory entry should be one clear sentence.
1996
+
1997
+ Return ONLY valid JSON, no markdown, no explanation:
1998
+ {
1999
+ "operations": [
2000
+ {
2001
+ "action": "add",
2002
+ "text": "Project uses pnpm workspaces with TypeScript strict mode",
2003
+ "type": "convention",
2004
+ "priority": "high",
2005
+ "tags": ["pnpm", "typescript", "build"]
2006
+ },
2007
+ {
2008
+ "action": "edit",
2009
+ "query": "pnpm",
2010
+ "text": "Project uses pnpm v9+ with ESM-only modules",
2011
+ "type": "fact",
2012
+ "priority": "medium"
2013
+ },
2014
+ { "action": "delete", "query": "outdated convention" }
2015
+ ]
2016
+ }`;
2017
+ }
2018
+ var SessionMemoryConsolidator = class {
2019
+ name = "session-memory-consolidator";
2020
+ owner = "core";
2021
+ memoryStore;
2022
+ provider;
2023
+ model;
2024
+ minIterations;
2025
+ maxExistingEntries;
2026
+ constructor(opts) {
2027
+ this.memoryStore = opts.memoryStore;
2028
+ this.provider = opts.provider;
2029
+ this.model = opts.model;
2030
+ this.minIterations = opts.minIterations ?? 2;
2031
+ this.maxExistingEntries = opts.maxExistingEntries ?? 15;
2032
+ }
2033
+ afterRun = async (ctx, result) => {
2034
+ if (result.status !== "done") return;
2035
+ if (!result.finalText || result.finalText.trim().length < 20) return;
2036
+ if (result.iterations < this.minIterations) return;
2037
+ const provider = this.provider ?? ctx.provider;
2038
+ if (!provider || !provider.complete) return;
2039
+ try {
2040
+ const existingEntries = await this.memoryStore.list("project-memory", this.maxExistingEntries);
2041
+ const prompt = buildConsolidationPrompt(
2042
+ result.finalText,
2043
+ result.iterations,
2044
+ existingEntries
2045
+ );
2046
+ const signal = AbortSignal.timeout(15e3);
2047
+ const response = await provider.complete(
2048
+ {
2049
+ model: this.model ?? ctx.model,
2050
+ system: [{ type: "text", text: prompt }],
2051
+ messages: [
2052
+ { role: "user", content: "Review the session and return memory operations as JSON." }
2053
+ ],
2054
+ maxTokens: 500
2055
+ },
2056
+ { signal }
2057
+ );
2058
+ const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
2059
+ if (!text) return;
2060
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
2061
+ if (!jsonMatch) return;
2062
+ const parsed = JSON.parse(jsonMatch[0]);
2063
+ if (!Array.isArray(parsed.operations) || parsed.operations.length === 0) return;
2064
+ let added = 0;
2065
+ let edited = 0;
2066
+ let deleted = 0;
2067
+ for (const op of parsed.operations) {
2068
+ switch (op.action) {
2069
+ case "add": {
2070
+ if (op.text && op.text.trim()) {
2071
+ await this.memoryStore.remember(op.text.trim(), void 0, {
2072
+ type: op.type,
2073
+ tags: op.tags,
2074
+ priority: op.priority
2075
+ });
2076
+ added++;
2077
+ }
2078
+ break;
2079
+ }
2080
+ case "edit": {
2081
+ if (op.query && op.text && op.text.trim()) {
2082
+ await this.memoryStore.forget(op.query);
2083
+ await this.memoryStore.remember(op.text.trim(), void 0, {
2084
+ type: op.type,
2085
+ tags: op.tags,
2086
+ priority: op.priority
2087
+ });
2088
+ edited++;
2089
+ }
2090
+ break;
2091
+ }
2092
+ case "delete": {
2093
+ if (op.query) {
2094
+ const n = await this.memoryStore.forget(op.query);
2095
+ deleted += n;
2096
+ }
2097
+ break;
2098
+ }
2099
+ }
2100
+ }
2101
+ if (added > 0 || edited > 0 || deleted > 0) {
2102
+ const parts = [];
2103
+ if (added) parts.push(`${added} added`);
2104
+ if (edited) parts.push(`${edited} edited`);
2105
+ if (deleted) parts.push(`${deleted} deleted`);
2106
+ process.stderr.write(`[memory] Session consolidation: ${parts.join(", ")}
2107
+ `);
2108
+ }
2109
+ } catch {
2110
+ }
2111
+ };
2112
+ };
1362
2113
 
1363
2114
  // src/storage/config-store.ts
1364
2115
  function stripEphemeralFields(cfg) {
@@ -1589,6 +2340,13 @@ var BEHAVIOR_DEFAULTS = {
1589
2340
  function envBool(v) {
1590
2341
  return !/^(0|false|no|off)$/i.test(v.trim());
1591
2342
  }
2343
+ function envBoolOptional(v) {
2344
+ return v !== void 0 && envBool(v);
2345
+ }
2346
+ var LOG_LEVELS = /* @__PURE__ */ new Set(["error", "warn", "info", "debug", "trace"]);
2347
+ function envLogLevel(v) {
2348
+ return LOG_LEVELS.has(v) ? v : "info";
2349
+ }
1592
2350
  var ENV_MAP = {
1593
2351
  WRONGSTACK_PROVIDER: (c, v) => {
1594
2352
  c.provider = v;
@@ -1612,7 +2370,7 @@ var ENV_MAP = {
1612
2370
  },
1613
2371
  WRONGSTACK_LOG_LEVEL: (c, v) => {
1614
2372
  if (!c.log) c.log = { level: "info" };
1615
- c.log.level = v;
2373
+ c.log.level = envLogLevel(v);
1616
2374
  },
1617
2375
  WRONGSTACK_INDEX_ON_START: (c, v) => {
1618
2376
  c.indexing = { ...defaultIndexing, ...c.indexing, onSessionStart: envBool(v) };
@@ -1654,7 +2412,7 @@ function deepMerge(base, patch) {
1654
2412
  out[k] = [.../* @__PURE__ */ new Set([...existing, ...v])];
1655
2413
  } else {
1656
2414
  out[k] = v;
1657
- if (process.env.WRONGSTACK_DEBUG_CONFIG) {
2415
+ if (envBoolOptional(process.env.WRONGSTACK_DEBUG_CONFIG)) {
1658
2416
  console.warn(
1659
2417
  `[config] Non-primitive array for "${k}" replaced (global + local config merge). Global entries: ${existing?.length ?? 0}, local entries: ${v.length}.`
1660
2418
  );
@@ -2047,12 +2805,6 @@ function compileUserRegex(pattern, flags) {
2047
2805
  }
2048
2806
 
2049
2807
  // src/storage/session-reader.ts
2050
- function expectDefined3(value) {
2051
- if (value === null || value === void 0) {
2052
- throw new Error("Expected value to be defined");
2053
- }
2054
- return value;
2055
- }
2056
2808
  var DefaultSessionReader = class {
2057
2809
  store;
2058
2810
  constructor(opts) {
@@ -2114,7 +2866,7 @@ var DefaultSessionReader = class {
2114
2866
  continue;
2115
2867
  }
2116
2868
  for (let i = 0; i < data.events.length; i++) {
2117
- const ev = expectDefined3(data.events[i]);
2869
+ const ev = expectDefined(data.events[i]);
2118
2870
  if (allowedTypes && !allowedTypes.has(ev.type)) continue;
2119
2871
  const text = eventText(ev);
2120
2872
  if (text === null) continue;
@@ -2327,12 +3079,6 @@ function renderPlainText(meta, events) {
2327
3079
  }
2328
3080
  return lines.join("\n");
2329
3081
  }
2330
- function expectDefined4(value) {
2331
- if (value === null || value === void 0) {
2332
- throw new Error("Expected value to be defined");
2333
- }
2334
- return value;
2335
- }
2336
3082
  var FILE_VERSION = 1;
2337
3083
  var MAX_TEXT_LENGTH = 2e3;
2338
3084
  var MAX_ANNOTATIONS = 1e3;
@@ -2373,9 +3119,7 @@ var AnnotationsStore = class {
2373
3119
  throw new Error("Annotation text must be non-empty");
2374
3120
  }
2375
3121
  if (text.length > MAX_TEXT_LENGTH) {
2376
- throw new Error(
2377
- `Annotation text exceeds ${MAX_TEXT_LENGTH} chars (got ${text.length})`
2378
- );
3122
+ throw new Error(`Annotation text exceeds ${MAX_TEXT_LENGTH} chars (got ${text.length})`);
2379
3123
  }
2380
3124
  if (!Number.isInteger(input.atEventIndex) || input.atEventIndex < 0) {
2381
3125
  throw new Error("atEventIndex must be a non-negative integer");
@@ -2391,20 +3135,22 @@ var AnnotationsStore = class {
2391
3135
  resolved: false
2392
3136
  };
2393
3137
  await this.enqueue(input.sessionId, async () => {
2394
- const all = await this.list(input.sessionId);
2395
- all.push(annotation);
2396
- if (all.length > MAX_ANNOTATIONS) {
2397
- const sorted = all.map((a, i) => ({ a, i })).sort((x, y) => {
2398
- if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;
2399
- return x.a.createdAt.localeCompare(y.a.createdAt);
2400
- });
2401
- const evictCount = all.length - MAX_ANNOTATIONS;
2402
- const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));
2403
- const kept = all.filter((a) => !toEvict.has(a.id));
2404
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });
2405
- } else {
2406
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
2407
- }
3138
+ await withFileLock(this.filePath(input.sessionId), async () => {
3139
+ const all = await this.list(input.sessionId);
3140
+ all.push(annotation);
3141
+ if (all.length > MAX_ANNOTATIONS) {
3142
+ const sorted = all.map((a, i) => ({ a, i })).sort((x, y) => {
3143
+ if (x.a.resolved !== y.a.resolved) return x.a.resolved ? 1 : -1;
3144
+ return x.a.createdAt.localeCompare(y.a.createdAt);
3145
+ });
3146
+ const evictCount = all.length - MAX_ANNOTATIONS;
3147
+ const toEvict = new Set(sorted.slice(0, evictCount).map((s) => s.a.id));
3148
+ const kept = all.filter((a) => !toEvict.has(a.id));
3149
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: kept });
3150
+ } else {
3151
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
3152
+ }
3153
+ });
2408
3154
  });
2409
3155
  return annotation;
2410
3156
  }
@@ -2417,21 +3163,23 @@ var AnnotationsStore = class {
2417
3163
  async resolve(input) {
2418
3164
  let updated = null;
2419
3165
  await this.enqueue(input.sessionId, async () => {
2420
- const all = await this.list(input.sessionId);
2421
- const idx = all.findIndex((a) => a.id === input.annotationId);
2422
- if (idx === -1) {
2423
- updated = null;
2424
- return;
2425
- }
2426
- const next = {
2427
- ...expectDefined4(all[idx]),
2428
- resolved: true,
2429
- resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
2430
- resolvedBy: input.resolvedBy
2431
- };
2432
- all[idx] = next;
2433
- await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
2434
- updated = next;
3166
+ await withFileLock(this.filePath(input.sessionId), async () => {
3167
+ const all = await this.list(input.sessionId);
3168
+ const idx = all.findIndex((a) => a.id === input.annotationId);
3169
+ if (idx === -1) {
3170
+ updated = null;
3171
+ return;
3172
+ }
3173
+ const next = {
3174
+ ...expectDefined(all[idx]),
3175
+ resolved: true,
3176
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
3177
+ resolvedBy: input.resolvedBy
3178
+ };
3179
+ all[idx] = next;
3180
+ await this.writeFile(input.sessionId, { version: FILE_VERSION, annotations: all });
3181
+ updated = next;
3182
+ });
2435
3183
  });
2436
3184
  return updated;
2437
3185
  }
@@ -2529,22 +3277,22 @@ var ReplayLogStore = class {
2529
3277
  async record(input) {
2530
3278
  const hash = hashRequest(input.request);
2531
3279
  await this.enqueue(input.sessionId, async () => {
2532
- const cache = await this.ensureCache(input.sessionId);
2533
- if (cache.has(hash)) return;
2534
- const entry = {
2535
- hash,
2536
- ts: (/* @__PURE__ */ new Date()).toISOString(),
2537
- request: input.request,
2538
- response: input.response
2539
- };
2540
- cache.set(hash, entry);
2541
- const count = (this.diskCount.get(input.sessionId) ?? 0) + 1;
2542
- if (count > this.maxEntries) {
2543
- await this.compact(input.sessionId, cache);
2544
- } else {
2545
- await fsp.appendFile(this.filePath(input.sessionId), JSON.stringify(entry) + "\n", "utf8");
2546
- this.diskCount.set(input.sessionId, count);
2547
- }
3280
+ await withFileLock(this.filePath(input.sessionId), async () => {
3281
+ const entries = await this.readAll(input.sessionId);
3282
+ if (entries.some((entry2) => entry2.hash === hash)) return;
3283
+ const entry = {
3284
+ hash,
3285
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
3286
+ request: input.request,
3287
+ response: input.response
3288
+ };
3289
+ entries.push(entry);
3290
+ const keep = entries.slice(-this.maxEntries);
3291
+ const cache = /* @__PURE__ */ new Map();
3292
+ for (const e of keep) cache.set(e.hash, e);
3293
+ this.cache.set(input.sessionId, cache);
3294
+ await this.rewriteCache(input.sessionId, cache);
3295
+ });
2548
3296
  });
2549
3297
  return hash;
2550
3298
  }
@@ -2553,7 +3301,7 @@ var ReplayLogStore = class {
2553
3301
  * Called when entry count exceeds the cap. Rewrites the entire file
2554
3302
  * but only happens O(n / maxEntries) times per session.
2555
3303
  */
2556
- async compact(sessionId, cache) {
3304
+ async rewriteCache(sessionId, cache) {
2557
3305
  const all = [...cache.values()];
2558
3306
  const keep = all.slice(-this.maxEntries);
2559
3307
  await this.writeAll(sessionId, keep);
@@ -2617,11 +3365,12 @@ var ReplayLogStore = class {
2617
3365
  for (const line of raw.split("\n")) {
2618
3366
  if (!line.trim()) continue;
2619
3367
  try {
2620
- const parsed = JSON.parse(line);
2621
- if ("entry" in parsed && parsed.entry) {
2622
- out.push(parsed.entry);
3368
+ const parsed = safeParse(line);
3369
+ if (!parsed.ok || !parsed.value) continue;
3370
+ if ("entry" in parsed.value && parsed.value.entry) {
3371
+ out.push(parsed.value.entry);
2623
3372
  } else {
2624
- out.push(parsed);
3373
+ out.push(parsed.value);
2625
3374
  }
2626
3375
  } catch {
2627
3376
  }
@@ -2657,12 +3406,6 @@ var ReplayLogStore = class {
2657
3406
  return next;
2658
3407
  }
2659
3408
  };
2660
- function expectDefined5(value) {
2661
- if (value === null || value === void 0) {
2662
- throw new Error("Expected value to be defined");
2663
- }
2664
- return value;
2665
- }
2666
3409
  var SessionRecovery = class {
2667
3410
  constructor(dir) {
2668
3411
  this.dir = dir;
@@ -2703,7 +3446,7 @@ var SessionRecovery = class {
2703
3446
  const lines = raw.split("\n").filter((l) => l.trim());
2704
3447
  for (let i = lines.length - 1; i >= 0; i--) {
2705
3448
  try {
2706
- const ev = JSON.parse(expectDefined5(lines[i]));
3449
+ const ev = JSON.parse(expectDefined(lines[i]));
2707
3450
  if (ev.type === "in_flight_start") {
2708
3451
  return {
2709
3452
  sessionId,
@@ -2756,12 +3499,12 @@ var SessionRecovery = class {
2756
3499
  let lastCheckpointIdx = -1;
2757
3500
  for (let i = 0; i < events.length; i++) {
2758
3501
  if (events[i]?.type === "checkpoint") {
2759
- lastCheckpoint = expectDefined5(events[i]);
3502
+ lastCheckpoint = expectDefined(events[i]);
2760
3503
  lastCheckpointIdx = i;
2761
3504
  }
2762
3505
  }
2763
3506
  const pendingEvents = lastCheckpointIdx >= 0 ? events.slice(lastCheckpointIdx + 1) : events;
2764
- const lastEv = expectDefined5(events[events.length - 1]);
3507
+ const lastEv = expectDefined(events[events.length - 1]);
2765
3508
  const inFlightStart = lastEv.type === "in_flight_start" ? lastEv : null;
2766
3509
  const context = inFlightStart && inFlightStart.type === "in_flight_start" ? inFlightStart.context : null;
2767
3510
  return {
@@ -2804,12 +3547,6 @@ var SessionRecovery = class {
2804
3547
  return path2.join(this.dir, `${sessionId}.jsonl`);
2805
3548
  }
2806
3549
  };
2807
- function expectDefined6(value) {
2808
- if (value === null || value === void 0) {
2809
- throw new Error("Expected value to be defined");
2810
- }
2811
- return value;
2812
- }
2813
3550
  var GENESIS_PREV = "0".repeat(64);
2814
3551
  var DEFAULT_FSYNC_EVERY = 100;
2815
3552
  var ToolAuditLog = class {
@@ -2835,37 +3572,42 @@ var ToolAuditLog = class {
2835
3572
  async record(input) {
2836
3573
  let entry = null;
2837
3574
  await this.enqueue(input.sessionId, async () => {
2838
- const prevHash = this.tailHash.get(input.sessionId) ?? GENESIS_PREV;
2839
- const index = this.tailIndex.get(input.sessionId) ?? 0;
2840
- const id = randomUUID();
2841
- const ts = (/* @__PURE__ */ new Date()).toISOString();
2842
- const content = {
2843
- id,
2844
- ts,
2845
- prevHash,
2846
- toolName: input.toolName,
2847
- toolUseId: input.toolUseId,
2848
- input: input.input,
2849
- output: input.output,
2850
- isError: input.isError,
2851
- index
2852
- };
2853
- const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
2854
- entry = {
2855
- id,
2856
- ts,
2857
- prevHash,
2858
- hash,
2859
- toolName: input.toolName,
2860
- toolUseId: input.toolUseId,
2861
- input: input.input,
2862
- output: input.output,
2863
- isError: input.isError,
2864
- index
2865
- };
2866
- await this.appendLine(input.sessionId, entry);
2867
- this.tailHash.set(input.sessionId, hash);
2868
- this.tailIndex.set(input.sessionId, index + 1);
3575
+ await withFileLock(this.filePath(input.sessionId), async () => {
3576
+ const entries = await this.readAll(input.sessionId);
3577
+ const prev = entries.at(-1);
3578
+ const prevHash = prev?.hash ?? GENESIS_PREV;
3579
+ const index = prev ? prev.index + 1 : 0;
3580
+ const id = randomUUID();
3581
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
3582
+ const content = {
3583
+ id,
3584
+ ts,
3585
+ prevHash,
3586
+ toolName: input.toolName,
3587
+ toolUseId: input.toolUseId,
3588
+ input: input.input,
3589
+ output: input.output,
3590
+ isError: input.isError,
3591
+ index
3592
+ };
3593
+ const hash = createHash("sha256").update(stableStringify2(content), "utf8").digest("hex");
3594
+ entry = {
3595
+ id,
3596
+ ts,
3597
+ prevHash,
3598
+ hash,
3599
+ toolName: input.toolName,
3600
+ toolUseId: input.toolUseId,
3601
+ input: input.input,
3602
+ output: input.output,
3603
+ isError: input.isError,
3604
+ index
3605
+ };
3606
+ entries.push(entry);
3607
+ await this.writeAll(input.sessionId, entries);
3608
+ this.tailHash.set(input.sessionId, hash);
3609
+ this.tailIndex.set(input.sessionId, index + 1);
3610
+ });
2869
3611
  });
2870
3612
  return entry;
2871
3613
  }
@@ -2885,7 +3627,7 @@ var ToolAuditLog = class {
2885
3627
  }
2886
3628
  let prevHash = GENESIS_PREV;
2887
3629
  for (let i = 0; i < entries.length; i++) {
2888
- const e = expectDefined6(entries[i]);
3630
+ const e = expectDefined(entries[i]);
2889
3631
  if (e.prevHash !== prevHash) {
2890
3632
  return {
2891
3633
  ok: false,
@@ -2935,7 +3677,8 @@ var ToolAuditLog = class {
2935
3677
  for (const line of raw.split("\n")) {
2936
3678
  if (!line.trim()) continue;
2937
3679
  try {
2938
- out.push(JSON.parse(line));
3680
+ const parsed = safeParse(line);
3681
+ if (parsed.ok && parsed.value) out.push(parsed.value);
2939
3682
  } catch {
2940
3683
  }
2941
3684
  }
@@ -2945,10 +3688,10 @@ var ToolAuditLog = class {
2945
3688
  return [];
2946
3689
  }
2947
3690
  }
2948
- async appendLine(sessionId, entry) {
3691
+ async writeAll(sessionId, entries) {
2949
3692
  const fp = this.filePath(sessionId);
2950
- const line = JSON.stringify(entry) + "\n";
2951
- await fsp.appendFile(fp, line, "utf8");
3693
+ const line = entries.map((e) => JSON.stringify(e)).join("\n") + (entries.length ? "\n" : "");
3694
+ await atomicWrite(fp, line, { mode: 384 });
2952
3695
  const count = (this.unSyncedWrites.get(sessionId) ?? 0) + 1;
2953
3696
  this.unSyncedWrites.set(sessionId, count);
2954
3697
  if (this.fsyncEvery !== Number.POSITIVE_INFINITY && count % this.fsyncEvery === 0) {
@@ -3101,12 +3844,6 @@ var SessionAnalyzer = class {
3101
3844
  return last - first;
3102
3845
  }
3103
3846
  };
3104
- function expectDefined7(value) {
3105
- if (value === null || value === void 0) {
3106
- throw new Error("Expected value to be defined");
3107
- }
3108
- return value;
3109
- }
3110
3847
  var DefaultSessionRewinder = class {
3111
3848
  constructor(sessionsDir, projectRoot) {
3112
3849
  this.sessionsDir = sessionsDir;
@@ -3145,7 +3882,7 @@ var DefaultSessionRewinder = class {
3145
3882
  const events = parseEvents(raw);
3146
3883
  let targetIdx = -1;
3147
3884
  for (let i = 0; i < events.length; i++) {
3148
- const event = expectDefined7(events[i]);
3885
+ const event = expectDefined(events[i]);
3149
3886
  if (event.type === "checkpoint") {
3150
3887
  const checkpointEvent = event;
3151
3888
  if (checkpointEvent.promptIndex === checkpointIndex) {
@@ -3159,7 +3896,7 @@ var DefaultSessionRewinder = class {
3159
3896
  }
3160
3897
  const snapshotsToRevert = [];
3161
3898
  for (let i = targetIdx + 1; i < events.length; i++) {
3162
- const event = expectDefined7(events[i]);
3899
+ const event = expectDefined(events[i]);
3163
3900
  if (event.type === "checkpoint") {
3164
3901
  break;
3165
3902
  }
@@ -3306,7 +4043,10 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
3306
4043
  let pending = null;
3307
4044
  let writeChain = Promise.resolve();
3308
4045
  const enqueueWrite = (todos) => {
3309
- writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos));
4046
+ writeChain = writeChain.then(() => saveTodosCheckpoint(filePath, sessionId, todos)).catch((err) => {
4047
+ const msg = err instanceof Error ? err.message : String(err);
4048
+ console.error(`[TodosCheckpoint] save failed for session ${sessionId}: ${msg}`);
4049
+ });
3310
4050
  return writeChain;
3311
4051
  };
3312
4052
  const flush = () => {
@@ -3604,7 +4344,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
3604
4344
  }
3605
4345
  const lock = {
3606
4346
  pid: processId,
3607
- hostname: __require("os").hostname(),
4347
+ hostname: hostname(),
3608
4348
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
3609
4349
  };
3610
4350
  await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
@@ -3974,12 +4714,6 @@ var DefaultPromptStore = class {
3974
4714
  };
3975
4715
  }
3976
4716
  };
3977
- function expectDefined8(value) {
3978
- if (value === null || value === void 0) {
3979
- throw new Error("Expected value to be defined");
3980
- }
3981
- return value;
3982
- }
3983
4717
  var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
3984
4718
  var CloudSync = class {
3985
4719
  constructor(paths, getConfig, setConfig) {
@@ -4022,8 +4756,8 @@ var CloudSync = class {
4022
4756
  const cfg = this.getConfig();
4023
4757
  if (!cfg?.enabled) return { ok: false, action: "push", categories: [], message: "Not enabled." };
4024
4758
  const parts = cfg.repo.split("/");
4025
- const owner = expectDefined8(parts[0]);
4026
- const repoName = expectDefined8(parts[1]);
4759
+ const owner = expectDefined(parts[0]);
4760
+ const repoName = expectDefined(parts[1]);
4027
4761
  const branch = "main";
4028
4762
  const baseTreeSha = this.state?.sha;
4029
4763
  const { treeEntries, rev } = await this.buildLocalTree(cfg.categories);
@@ -4075,8 +4809,8 @@ var CloudSync = class {
4075
4809
  const cfg = this.getConfig();
4076
4810
  if (!cfg?.enabled) return { ok: false, action: "pull", categories: [], message: "Not enabled." };
4077
4811
  const pullParts = cfg.repo.split("/");
4078
- const owner = expectDefined8(pullParts[0]);
4079
- const repoName = expectDefined8(pullParts[1]);
4812
+ const owner = expectDefined(pullParts[0]);
4813
+ const repoName = expectDefined(pullParts[1]);
4080
4814
  const branchData = await this.getRef(token, owner, repoName, "main");
4081
4815
  const currentSha = branchData.object.sha;
4082
4816
  const commitData = await this.getCommit(token, owner, repoName, currentSha);
@@ -4393,6 +5127,6 @@ function resolveSessionLoggingConfig(cfg) {
4393
5127
  };
4394
5128
  }
4395
5129
 
4396
- export { ALL_SYNC_CATEGORIES, AnnotationsStore, CORE_RECONSTRUCT_EVENTS, CloudSync, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultMemoryStore, DefaultPromptStore, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DirectorStateCheckpoint, MAX_JOURNAL_ENTRIES, QueueStore, RecoveryLock, ReplayLogStore, STANDARD_AUDIT_EVENTS, SessionAnalyzer, SessionRecovery, ToolAuditLog, addPlanItem, appendJournal, attachPlanCheckpoint, attachTodosCheckpoint, clearPlan, createSessionEventBridge, deriveTodosFromPlanItem, emptyGoal, emptyPlan, formatGoal, formatPlan, formatPlanTemplates, getPlanTemplate, goalFilePath, listPlanTemplates, loadDirectorState, loadGoal, loadPlan, loadTodosCheckpoint, removePlanItem, resolveAuditLevel, resolveSessionLoggingConfig, runConfigMigrations, saveGoal, savePlan, saveTodosCheckpoint, setPlanItemStatus, summarizeUsage };
5130
+ export { ALL_SYNC_CATEGORIES, AnnotationsStore, CORE_RECONSTRUCT_EVENTS, CloudSync, ConfigMigrationError, DEFAULT_CONFIG_MIGRATIONS, DefaultAttachmentStore, DefaultConfigLoader, DefaultConfigStore, DefaultMemoryStore, DefaultPromptStore, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, DirectorStateCheckpoint, FileMemoryBackend, GraphMemoryBackend, MAX_JOURNAL_ENTRIES, QueueStore, RecoveryLock, ReplayLogStore, STANDARD_AUDIT_EVENTS, SessionAnalyzer, SessionMemoryConsolidator, SessionRecovery, ToolAuditLog, addPlanItem, appendJournal, attachPlanCheckpoint, attachTodosCheckpoint, clearPlan, createSessionEventBridge, deriveTodosFromPlanItem, emptyGoal, emptyPlan, formatGoal, formatPlan, formatPlanTemplates, getPlanTemplate, goalFilePath, listPlanTemplates, loadDirectorState, loadGoal, loadPlan, loadTodosCheckpoint, parseEntries, removePlanItem, resolveAuditLevel, resolveSessionLoggingConfig, runConfigMigrations, saveGoal, savePlan, saveTodosCheckpoint, setPlanItemStatus, summarizeUsage };
4397
5131
  //# sourceMappingURL=index.js.map
4398
5132
  //# sourceMappingURL=index.js.map