@wrongstack/core 0.89.1 → 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.
- package/dist/{agent-bridge-B8F2ORwg.d.ts → agent-bridge-DbVe1dHT.d.ts} +1 -1
- package/dist/{agent-subagent-runner-CQaLFNnA.d.ts → agent-subagent-runner-C6eqID1t.d.ts} +433 -433
- package/dist/{events-DzyLtja6.d.ts → brain-s9DyD_Vf.d.ts} +179 -92
- package/dist/{compactor-CJq7LQev.d.ts → compactor-DXLxLcmU.d.ts} +2 -2
- package/dist/{config-_DZ7dN-T.d.ts → config-84VaZpH6.d.ts} +45 -38
- package/dist/{context-ToHAp4-U.d.ts → context-CNRYfhUv.d.ts} +1 -1
- package/dist/coordination/index.d.ts +16 -17
- package/dist/coordination/index.js +25 -37
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +31 -33
- package/dist/defaults/index.js +476 -206
- package/dist/defaults/index.js.map +1 -1
- package/dist/{director-state-CgIc30qi.d.ts → director-state-BfeCUbmk.d.ts} +1 -1
- package/dist/execution/index.d.ts +18 -19
- package/dist/execution/index.js +23 -45
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +9 -10
- package/dist/extension/index.js +8 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/{index-Ca_lOmKl.d.ts → index-CqrroYcU.d.ts} +13 -15
- package/dist/{index-Dx-CnrDN.d.ts → index-D3Nax3YU.d.ts} +9 -10
- package/dist/index.d.ts +184 -174
- package/dist/index.js +973 -349
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +9 -9
- package/dist/infrastructure/index.js +4 -2
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +14 -16
- package/dist/kernel/index.js.map +1 -1
- package/dist/{logger-B72yyPc6.d.ts → logger-B63L5bTg.d.ts} +1 -1
- package/dist/{logger-C_27pj9i.d.ts → logger-B9J5puGM.d.ts} +1 -1
- package/dist/{mcp-servers-Dck3T85_.d.ts → mcp-servers-D3E5ixAR.d.ts} +3 -3
- package/dist/{mode-CHo2XtHs.d.ts → mode-ARA3HrkY.d.ts} +1 -1
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +8 -6
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-Be3osGt5.d.ts → models-registry-B6_KfS65.d.ts} +1 -1
- package/dist/{models-registry-Boz639EI.d.ts → models-registry-DU64QxQa.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-Da9nd8Ic.d.ts → multi-agent-coordinator-DHNrjPaA.d.ts} +2 -2
- package/dist/{null-fleet-bus-CHJOXv3t.d.ts → null-fleet-bus-BzzciAc4.d.ts} +32 -31
- package/dist/observability/index.d.ts +3 -3
- package/dist/{observability-CoSNZdhX.d.ts → observability-D-HZN_mF.d.ts} +1 -1
- package/dist/{parallel-eternal-engine-DyMCj2Pw.d.ts → parallel-eternal-engine-CTpcrb9O.d.ts} +33 -33
- package/dist/{path-resolver-TjBlRKyH.d.ts → path-resolver-B7hgMAVe.d.ts} +3 -3
- package/dist/{permission-14CChMmO.d.ts → permission-BDv7z0mk.d.ts} +7 -2
- package/dist/{permission-policy-gW5htOo1.d.ts → permission-policy-dF74EpDp.d.ts} +2 -2
- package/dist/{system-prompt-C0rLCeyn.d.ts → pipeline-C1Ad9OjI.d.ts} +73 -73
- package/dist/{plan-templates-D9UhwoGh.d.ts → plan-templates-CNphsz0n.d.ts} +79 -11
- package/dist/{provider-runner-BEPWwkxH.d.ts → provider-runner-cqvlB_oS.d.ts} +5 -5
- package/dist/{retry-policy-DSu6O6rD.d.ts → retry-policy-BcmuT_V0.d.ts} +2 -2
- package/dist/sdd/index.d.ts +12 -33
- package/dist/sdd/index.js +21 -31
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-scrubber-yGBFQYju.d.ts → secret-vault-DrOhc2i5.d.ts} +7 -7
- package/dist/security/index.d.ts +4 -5
- package/dist/security/index.js +4 -2
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-11-fm95U.d.ts → selector-C7wcdqMA.d.ts} +1 -1
- package/dist/{session-event-bridge-D0u-x576.d.ts → session-event-bridge-IVzs2GpE.d.ts} +1 -1
- package/dist/{session-reader-BQU-toaN.d.ts → session-reader-DDz1Ek4V.d.ts} +2 -2
- package/dist/{skill-BJeF2DwY.d.ts → skill-BJxzIyyN.d.ts} +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.js +3 -9
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +132 -12
- package/dist/storage/index.js +834 -159
- package/dist/storage/index.js.map +1 -1
- package/dist/{task-graph-CikNdRTG.d.ts → task-graph-DJBqO0EY.d.ts} +1 -1
- package/dist/types/index.d.ts +25 -27
- package/dist/types/index.js +19 -31
- package/dist/types/index.js.map +1 -1
- package/dist/utils/expect-defined.d.ts +7 -0
- package/dist/utils/expect-defined.js +11 -0
- package/dist/utils/expect-defined.js.map +1 -0
- package/dist/utils/index.d.ts +6 -20
- package/dist/utils/index.js +11 -41
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-BQMvEllz.d.ts → wstack-paths-_lqjzErq.d.ts} +1 -1
- package/package.json +5 -1
- package/dist/memory-CEXuo7sz.d.ts +0 -16
- package/dist/secret-scrubber-3MHDDAtm.d.ts +0 -6
package/dist/storage/index.js
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 });
|
|
@@ -118,12 +120,6 @@ async function renameWithRetry(from, to) {
|
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
// src/utils/message-invariants.ts
|
|
121
|
-
function expectDefined(value) {
|
|
122
|
-
if (value === null || value === void 0) {
|
|
123
|
-
throw new Error("Expected value to be defined");
|
|
124
|
-
}
|
|
125
|
-
return value;
|
|
126
|
-
}
|
|
127
123
|
function repairToolUseAdjacency(messages) {
|
|
128
124
|
const removedToolUses = [];
|
|
129
125
|
const removedToolResults = [];
|
|
@@ -216,12 +212,6 @@ function isEmptyMessage(msg) {
|
|
|
216
212
|
}
|
|
217
213
|
|
|
218
214
|
// src/storage/session-store.ts
|
|
219
|
-
function expectDefined2(value) {
|
|
220
|
-
if (value === null || value === void 0) {
|
|
221
|
-
throw new Error("Expected value to be defined");
|
|
222
|
-
}
|
|
223
|
-
return value;
|
|
224
|
-
}
|
|
225
215
|
function sanitizeModel(model) {
|
|
226
216
|
return model.replace(/[^a-zA-Z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
227
217
|
}
|
|
@@ -932,7 +922,7 @@ var FileSessionWriter = class {
|
|
|
932
922
|
let targetCheckpointLine = -1;
|
|
933
923
|
let afterTarget = false;
|
|
934
924
|
for (let i = 0; i < lines.length; i++) {
|
|
935
|
-
const line =
|
|
925
|
+
const line = expectDefined(lines[i]);
|
|
936
926
|
if (!line.trim()) continue;
|
|
937
927
|
let event;
|
|
938
928
|
try {
|
|
@@ -1221,9 +1211,235 @@ function mergeAdjacentText(blocks) {
|
|
|
1221
1211
|
}
|
|
1222
1212
|
return out;
|
|
1223
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
|
|
1224
1438
|
var MAX_BYTES_TOTAL = 32e3;
|
|
1225
1439
|
var DefaultMemoryStore = class {
|
|
1226
1440
|
files;
|
|
1441
|
+
events;
|
|
1442
|
+
backend;
|
|
1227
1443
|
/**
|
|
1228
1444
|
* Per-scope serialization queue. `remember` / `forget` / `consolidate` /
|
|
1229
1445
|
* `clear` are read-modify-write against a single file; without a lock,
|
|
@@ -1240,12 +1456,29 @@ var DefaultMemoryStore = class {
|
|
|
1240
1456
|
writeChain = /* @__PURE__ */ new Map();
|
|
1241
1457
|
/** Last write error per scope — surfaced as warnings on the next readAll(). */
|
|
1242
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;
|
|
1243
1467
|
constructor(opts) {
|
|
1244
1468
|
this.files = {
|
|
1245
1469
|
"project-agents": opts.paths.inProjectAgentsFile,
|
|
1246
1470
|
"project-memory": opts.paths.projectMemory,
|
|
1247
1471
|
"user-memory": opts.paths.globalMemory
|
|
1248
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;
|
|
1249
1482
|
}
|
|
1250
1483
|
async runSerialized(scope, work) {
|
|
1251
1484
|
const prior = this.writeChain.get(scope) ?? Promise.resolve();
|
|
@@ -1272,7 +1505,7 @@ var DefaultMemoryStore = class {
|
|
|
1272
1505
|
if (writeErr) {
|
|
1273
1506
|
parts.push(`> \u26A0\uFE0F Memory write error (${labelOf(scope)}): ${writeErr.message}`);
|
|
1274
1507
|
}
|
|
1275
|
-
const body = await this.
|
|
1508
|
+
const body = await this.backend.readAll(scope, this.files[scope]);
|
|
1276
1509
|
if (body.trim()) parts.push(`## ${labelOf(scope)}
|
|
1277
1510
|
|
|
1278
1511
|
${body.trim()}`);
|
|
@@ -1280,117 +1513,214 @@ ${body.trim()}`);
|
|
|
1280
1513
|
return parts.join("\n\n");
|
|
1281
1514
|
}
|
|
1282
1515
|
async read(scope) {
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
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);
|
|
1287
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);
|
|
1288
1537
|
}
|
|
1289
|
-
async remember(text, scope = "project-memory") {
|
|
1538
|
+
async remember(text, scope = "project-memory", metadata) {
|
|
1539
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
1290
1540
|
return this.runSerialized(scope, async () => {
|
|
1291
|
-
const
|
|
1292
|
-
await
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
`;
|
|
1303
|
-
const next = existing.trim() ? existing.replace(/\n+$/, "") + entry : `# WrongStack Memory
|
|
1304
|
-
${entry}`;
|
|
1305
|
-
await atomicWrite(file, next);
|
|
1306
|
-
const buf = Buffer.byteLength(next, "utf8");
|
|
1307
|
-
if (buf > MAX_BYTES_TOTAL) {
|
|
1308
|
-
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
|
+
}
|
|
1309
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
|
+
});
|
|
1310
1562
|
});
|
|
1311
1563
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
const
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
const
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
if (
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
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;
|
|
1338
1590
|
}
|
|
1339
1591
|
}
|
|
1340
|
-
if (
|
|
1341
|
-
|
|
1342
|
-
|
|
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
|
+
});
|
|
1343
1659
|
}
|
|
1344
|
-
return true;
|
|
1345
|
-
});
|
|
1346
|
-
if (removed > 0) {
|
|
1347
|
-
await atomicWrite(file, lines.join("\n"));
|
|
1348
1660
|
}
|
|
1349
|
-
|
|
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));
|
|
1350
1667
|
}
|
|
1351
|
-
async
|
|
1352
|
-
return this.runSerialized(scope, async () =>
|
|
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
|
+
});
|
|
1353
1681
|
}
|
|
1354
|
-
async
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
const trimmed = line.trim();
|
|
1365
|
-
if (!trimmed.startsWith("- ")) return true;
|
|
1366
|
-
const norm = trimmed.replace(/\[[^\]]+\]/, "").replace(/\bmem_\d+_\w+\s*/, "").trim().toLowerCase();
|
|
1367
|
-
if (seen.has(norm)) return false;
|
|
1368
|
-
seen.add(norm);
|
|
1369
|
-
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
|
+
}
|
|
1370
1692
|
});
|
|
1371
|
-
const next = lines.join("\n");
|
|
1372
|
-
const backup = `${file}.bak.${Date.now()}`;
|
|
1373
|
-
try {
|
|
1374
|
-
await fsp.copyFile(file, backup);
|
|
1375
|
-
} catch {
|
|
1376
|
-
}
|
|
1377
|
-
try {
|
|
1378
|
-
await atomicWrite(file, next);
|
|
1379
|
-
} catch {
|
|
1380
|
-
return;
|
|
1381
|
-
}
|
|
1382
1693
|
}
|
|
1383
1694
|
async clear(scope) {
|
|
1384
1695
|
if (scope) {
|
|
1385
|
-
await this.runSerialized(scope, async () =>
|
|
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
|
+
});
|
|
1386
1701
|
return;
|
|
1387
1702
|
}
|
|
1388
1703
|
await Promise.all(
|
|
1389
1704
|
["project-agents", "project-memory", "user-memory"].map(
|
|
1390
|
-
(s) => this.runSerialized(s, async () =>
|
|
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
|
+
})
|
|
1391
1710
|
)
|
|
1392
1711
|
);
|
|
1393
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
|
+
}
|
|
1394
1724
|
};
|
|
1395
1725
|
function labelOf(scope) {
|
|
1396
1726
|
switch (scope) {
|
|
@@ -1402,6 +1732,384 @@ function labelOf(scope) {
|
|
|
1402
1732
|
return "User memory";
|
|
1403
1733
|
}
|
|
1404
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
|
+
};
|
|
1405
2113
|
|
|
1406
2114
|
// src/storage/config-store.ts
|
|
1407
2115
|
function stripEphemeralFields(cfg) {
|
|
@@ -2097,12 +2805,6 @@ function compileUserRegex(pattern, flags) {
|
|
|
2097
2805
|
}
|
|
2098
2806
|
|
|
2099
2807
|
// src/storage/session-reader.ts
|
|
2100
|
-
function expectDefined3(value) {
|
|
2101
|
-
if (value === null || value === void 0) {
|
|
2102
|
-
throw new Error("Expected value to be defined");
|
|
2103
|
-
}
|
|
2104
|
-
return value;
|
|
2105
|
-
}
|
|
2106
2808
|
var DefaultSessionReader = class {
|
|
2107
2809
|
store;
|
|
2108
2810
|
constructor(opts) {
|
|
@@ -2164,7 +2866,7 @@ var DefaultSessionReader = class {
|
|
|
2164
2866
|
continue;
|
|
2165
2867
|
}
|
|
2166
2868
|
for (let i = 0; i < data.events.length; i++) {
|
|
2167
|
-
const ev =
|
|
2869
|
+
const ev = expectDefined(data.events[i]);
|
|
2168
2870
|
if (allowedTypes && !allowedTypes.has(ev.type)) continue;
|
|
2169
2871
|
const text = eventText(ev);
|
|
2170
2872
|
if (text === null) continue;
|
|
@@ -2377,12 +3079,6 @@ function renderPlainText(meta, events) {
|
|
|
2377
3079
|
}
|
|
2378
3080
|
return lines.join("\n");
|
|
2379
3081
|
}
|
|
2380
|
-
function expectDefined4(value) {
|
|
2381
|
-
if (value === null || value === void 0) {
|
|
2382
|
-
throw new Error("Expected value to be defined");
|
|
2383
|
-
}
|
|
2384
|
-
return value;
|
|
2385
|
-
}
|
|
2386
3082
|
var FILE_VERSION = 1;
|
|
2387
3083
|
var MAX_TEXT_LENGTH = 2e3;
|
|
2388
3084
|
var MAX_ANNOTATIONS = 1e3;
|
|
@@ -2475,7 +3171,7 @@ var AnnotationsStore = class {
|
|
|
2475
3171
|
return;
|
|
2476
3172
|
}
|
|
2477
3173
|
const next = {
|
|
2478
|
-
...
|
|
3174
|
+
...expectDefined(all[idx]),
|
|
2479
3175
|
resolved: true,
|
|
2480
3176
|
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2481
3177
|
resolvedBy: input.resolvedBy
|
|
@@ -2710,12 +3406,6 @@ var ReplayLogStore = class {
|
|
|
2710
3406
|
return next;
|
|
2711
3407
|
}
|
|
2712
3408
|
};
|
|
2713
|
-
function expectDefined5(value) {
|
|
2714
|
-
if (value === null || value === void 0) {
|
|
2715
|
-
throw new Error("Expected value to be defined");
|
|
2716
|
-
}
|
|
2717
|
-
return value;
|
|
2718
|
-
}
|
|
2719
3409
|
var SessionRecovery = class {
|
|
2720
3410
|
constructor(dir) {
|
|
2721
3411
|
this.dir = dir;
|
|
@@ -2756,7 +3446,7 @@ var SessionRecovery = class {
|
|
|
2756
3446
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2757
3447
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2758
3448
|
try {
|
|
2759
|
-
const ev = JSON.parse(
|
|
3449
|
+
const ev = JSON.parse(expectDefined(lines[i]));
|
|
2760
3450
|
if (ev.type === "in_flight_start") {
|
|
2761
3451
|
return {
|
|
2762
3452
|
sessionId,
|
|
@@ -2809,12 +3499,12 @@ var SessionRecovery = class {
|
|
|
2809
3499
|
let lastCheckpointIdx = -1;
|
|
2810
3500
|
for (let i = 0; i < events.length; i++) {
|
|
2811
3501
|
if (events[i]?.type === "checkpoint") {
|
|
2812
|
-
lastCheckpoint =
|
|
3502
|
+
lastCheckpoint = expectDefined(events[i]);
|
|
2813
3503
|
lastCheckpointIdx = i;
|
|
2814
3504
|
}
|
|
2815
3505
|
}
|
|
2816
3506
|
const pendingEvents = lastCheckpointIdx >= 0 ? events.slice(lastCheckpointIdx + 1) : events;
|
|
2817
|
-
const lastEv =
|
|
3507
|
+
const lastEv = expectDefined(events[events.length - 1]);
|
|
2818
3508
|
const inFlightStart = lastEv.type === "in_flight_start" ? lastEv : null;
|
|
2819
3509
|
const context = inFlightStart && inFlightStart.type === "in_flight_start" ? inFlightStart.context : null;
|
|
2820
3510
|
return {
|
|
@@ -2857,12 +3547,6 @@ var SessionRecovery = class {
|
|
|
2857
3547
|
return path2.join(this.dir, `${sessionId}.jsonl`);
|
|
2858
3548
|
}
|
|
2859
3549
|
};
|
|
2860
|
-
function expectDefined6(value) {
|
|
2861
|
-
if (value === null || value === void 0) {
|
|
2862
|
-
throw new Error("Expected value to be defined");
|
|
2863
|
-
}
|
|
2864
|
-
return value;
|
|
2865
|
-
}
|
|
2866
3550
|
var GENESIS_PREV = "0".repeat(64);
|
|
2867
3551
|
var DEFAULT_FSYNC_EVERY = 100;
|
|
2868
3552
|
var ToolAuditLog = class {
|
|
@@ -2943,7 +3627,7 @@ var ToolAuditLog = class {
|
|
|
2943
3627
|
}
|
|
2944
3628
|
let prevHash = GENESIS_PREV;
|
|
2945
3629
|
for (let i = 0; i < entries.length; i++) {
|
|
2946
|
-
const e =
|
|
3630
|
+
const e = expectDefined(entries[i]);
|
|
2947
3631
|
if (e.prevHash !== prevHash) {
|
|
2948
3632
|
return {
|
|
2949
3633
|
ok: false,
|
|
@@ -3160,12 +3844,6 @@ var SessionAnalyzer = class {
|
|
|
3160
3844
|
return last - first;
|
|
3161
3845
|
}
|
|
3162
3846
|
};
|
|
3163
|
-
function expectDefined7(value) {
|
|
3164
|
-
if (value === null || value === void 0) {
|
|
3165
|
-
throw new Error("Expected value to be defined");
|
|
3166
|
-
}
|
|
3167
|
-
return value;
|
|
3168
|
-
}
|
|
3169
3847
|
var DefaultSessionRewinder = class {
|
|
3170
3848
|
constructor(sessionsDir, projectRoot) {
|
|
3171
3849
|
this.sessionsDir = sessionsDir;
|
|
@@ -3204,7 +3882,7 @@ var DefaultSessionRewinder = class {
|
|
|
3204
3882
|
const events = parseEvents(raw);
|
|
3205
3883
|
let targetIdx = -1;
|
|
3206
3884
|
for (let i = 0; i < events.length; i++) {
|
|
3207
|
-
const event =
|
|
3885
|
+
const event = expectDefined(events[i]);
|
|
3208
3886
|
if (event.type === "checkpoint") {
|
|
3209
3887
|
const checkpointEvent = event;
|
|
3210
3888
|
if (checkpointEvent.promptIndex === checkpointIndex) {
|
|
@@ -3218,7 +3896,7 @@ var DefaultSessionRewinder = class {
|
|
|
3218
3896
|
}
|
|
3219
3897
|
const snapshotsToRevert = [];
|
|
3220
3898
|
for (let i = targetIdx + 1; i < events.length; i++) {
|
|
3221
|
-
const event =
|
|
3899
|
+
const event = expectDefined(events[i]);
|
|
3222
3900
|
if (event.type === "checkpoint") {
|
|
3223
3901
|
break;
|
|
3224
3902
|
}
|
|
@@ -3365,7 +4043,10 @@ function attachTodosCheckpoint(state, filePath, sessionId) {
|
|
|
3365
4043
|
let pending = null;
|
|
3366
4044
|
let writeChain = Promise.resolve();
|
|
3367
4045
|
const enqueueWrite = (todos) => {
|
|
3368
|
-
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
|
+
});
|
|
3369
4050
|
return writeChain;
|
|
3370
4051
|
};
|
|
3371
4052
|
const flush = () => {
|
|
@@ -3663,7 +4344,7 @@ async function acquireDirectorStateLock(lockPath, processId = process.pid) {
|
|
|
3663
4344
|
}
|
|
3664
4345
|
const lock = {
|
|
3665
4346
|
pid: processId,
|
|
3666
|
-
hostname:
|
|
4347
|
+
hostname: hostname(),
|
|
3667
4348
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3668
4349
|
};
|
|
3669
4350
|
await atomicWrite(lockPath, JSON.stringify(lock), { mode: 384 });
|
|
@@ -4033,12 +4714,6 @@ var DefaultPromptStore = class {
|
|
|
4033
4714
|
};
|
|
4034
4715
|
}
|
|
4035
4716
|
};
|
|
4036
|
-
function expectDefined8(value) {
|
|
4037
|
-
if (value === null || value === void 0) {
|
|
4038
|
-
throw new Error("Expected value to be defined");
|
|
4039
|
-
}
|
|
4040
|
-
return value;
|
|
4041
|
-
}
|
|
4042
4717
|
var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
|
|
4043
4718
|
var CloudSync = class {
|
|
4044
4719
|
constructor(paths, getConfig, setConfig) {
|
|
@@ -4081,8 +4756,8 @@ var CloudSync = class {
|
|
|
4081
4756
|
const cfg = this.getConfig();
|
|
4082
4757
|
if (!cfg?.enabled) return { ok: false, action: "push", categories: [], message: "Not enabled." };
|
|
4083
4758
|
const parts = cfg.repo.split("/");
|
|
4084
|
-
const owner =
|
|
4085
|
-
const repoName =
|
|
4759
|
+
const owner = expectDefined(parts[0]);
|
|
4760
|
+
const repoName = expectDefined(parts[1]);
|
|
4086
4761
|
const branch = "main";
|
|
4087
4762
|
const baseTreeSha = this.state?.sha;
|
|
4088
4763
|
const { treeEntries, rev } = await this.buildLocalTree(cfg.categories);
|
|
@@ -4134,8 +4809,8 @@ var CloudSync = class {
|
|
|
4134
4809
|
const cfg = this.getConfig();
|
|
4135
4810
|
if (!cfg?.enabled) return { ok: false, action: "pull", categories: [], message: "Not enabled." };
|
|
4136
4811
|
const pullParts = cfg.repo.split("/");
|
|
4137
|
-
const owner =
|
|
4138
|
-
const repoName =
|
|
4812
|
+
const owner = expectDefined(pullParts[0]);
|
|
4813
|
+
const repoName = expectDefined(pullParts[1]);
|
|
4139
4814
|
const branchData = await this.getRef(token, owner, repoName, "main");
|
|
4140
4815
|
const currentSha = branchData.object.sha;
|
|
4141
4816
|
const commitData = await this.getCommit(token, owner, repoName, currentSha);
|
|
@@ -4452,6 +5127,6 @@ function resolveSessionLoggingConfig(cfg) {
|
|
|
4452
5127
|
};
|
|
4453
5128
|
}
|
|
4454
5129
|
|
|
4455
|
-
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 };
|
|
4456
5131
|
//# sourceMappingURL=index.js.map
|
|
4457
5132
|
//# sourceMappingURL=index.js.map
|