@agenr/openclaw-plugin 0.14.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +5 -5
- package/dist/index.js +1954 -1548
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -827,6 +827,9 @@ function formatShadowMismatch(stats) {
|
|
|
827
827
|
}
|
|
828
828
|
return details.join(", ");
|
|
829
829
|
}
|
|
830
|
+
function formatIntegrityDetail(embeddingCount, shadowCount, missingRowids) {
|
|
831
|
+
return `active=${embeddingCount}, shadow=${shadowCount}, missing=${missingRowids}`;
|
|
832
|
+
}
|
|
830
833
|
async function rollbackQuietly(db) {
|
|
831
834
|
try {
|
|
832
835
|
await db.execute("ROLLBACK");
|
|
@@ -919,6 +922,18 @@ async function getVectorIndexShadowStats(db) {
|
|
|
919
922
|
missingRowids
|
|
920
923
|
};
|
|
921
924
|
}
|
|
925
|
+
async function checkVectorIntegrity(db) {
|
|
926
|
+
const stats = await getVectorIndexShadowStats(db);
|
|
927
|
+
const missingRowids = stats.missingRowids.length;
|
|
928
|
+
return {
|
|
929
|
+
drifted: stats.embeddingCount !== stats.shadowCount || missingRowids > 0,
|
|
930
|
+
detail: formatIntegrityDetail(stats.embeddingCount, stats.shadowCount, missingRowids),
|
|
931
|
+
activeEntries: stats.embeddingCount,
|
|
932
|
+
shadowRows: stats.shadowCount,
|
|
933
|
+
missingRowids,
|
|
934
|
+
missingRowidValues: stats.missingRowids
|
|
935
|
+
};
|
|
936
|
+
}
|
|
922
937
|
async function rebuildVectorIndex(db, options) {
|
|
923
938
|
const start = Date.now();
|
|
924
939
|
const onLog = options?.onLog ?? (() => void 0);
|
|
@@ -1088,6 +1103,287 @@ var init_version = __esm({
|
|
|
1088
1103
|
}
|
|
1089
1104
|
});
|
|
1090
1105
|
|
|
1106
|
+
// src/shared/utils/entry-utils.ts
|
|
1107
|
+
function toNumber2(value) {
|
|
1108
|
+
if (typeof value === "number") {
|
|
1109
|
+
return value;
|
|
1110
|
+
}
|
|
1111
|
+
if (typeof value === "bigint") {
|
|
1112
|
+
return Number(value);
|
|
1113
|
+
}
|
|
1114
|
+
if (typeof value === "string" && value.trim()) {
|
|
1115
|
+
return Number(value);
|
|
1116
|
+
}
|
|
1117
|
+
return Number.NaN;
|
|
1118
|
+
}
|
|
1119
|
+
function toStringValue(value) {
|
|
1120
|
+
if (typeof value === "string") {
|
|
1121
|
+
return value;
|
|
1122
|
+
}
|
|
1123
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
1124
|
+
return String(value);
|
|
1125
|
+
}
|
|
1126
|
+
return "";
|
|
1127
|
+
}
|
|
1128
|
+
function toRowsAffected(value) {
|
|
1129
|
+
if (typeof value === "number") {
|
|
1130
|
+
return value;
|
|
1131
|
+
}
|
|
1132
|
+
if (typeof value === "bigint") {
|
|
1133
|
+
return Number(value);
|
|
1134
|
+
}
|
|
1135
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
1136
|
+
return Number(value);
|
|
1137
|
+
}
|
|
1138
|
+
return 0;
|
|
1139
|
+
}
|
|
1140
|
+
function parseDaysBetween(now, pastIso) {
|
|
1141
|
+
if (!pastIso) {
|
|
1142
|
+
return 0;
|
|
1143
|
+
}
|
|
1144
|
+
const parsed = new Date(pastIso);
|
|
1145
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
1146
|
+
return 0;
|
|
1147
|
+
}
|
|
1148
|
+
const delta = (now.getTime() - parsed.getTime()) / MILLISECONDS_PER_DAY;
|
|
1149
|
+
if (!Number.isFinite(delta)) {
|
|
1150
|
+
return 0;
|
|
1151
|
+
}
|
|
1152
|
+
return Math.max(delta, 0);
|
|
1153
|
+
}
|
|
1154
|
+
var MILLISECONDS_PER_DAY;
|
|
1155
|
+
var init_entry_utils = __esm({
|
|
1156
|
+
"src/shared/utils/entry-utils.ts"() {
|
|
1157
|
+
"use strict";
|
|
1158
|
+
MILLISECONDS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// src/shared/infrastructure/db/co-recall.ts
|
|
1163
|
+
function normalizePair(a, b) {
|
|
1164
|
+
return a < b ? [a, b] : [b, a];
|
|
1165
|
+
}
|
|
1166
|
+
async function strengthenEdges(db, usedEntryIds, timestamp2, edgeType) {
|
|
1167
|
+
const uniqueIds = Array.from(
|
|
1168
|
+
new Set(
|
|
1169
|
+
usedEntryIds.map((id) => id.trim()).filter((id) => id.length > 0)
|
|
1170
|
+
)
|
|
1171
|
+
).slice(0, MAX_USED_ENTRIES);
|
|
1172
|
+
if (uniqueIds.length < 2) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
const now = timestamp2 || (/* @__PURE__ */ new Date()).toISOString();
|
|
1176
|
+
const pairs = [];
|
|
1177
|
+
for (let i = 0; i < uniqueIds.length; i += 1) {
|
|
1178
|
+
const a = uniqueIds[i];
|
|
1179
|
+
if (!a) {
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
for (let j = i + 1; j < uniqueIds.length; j += 1) {
|
|
1183
|
+
const b = uniqueIds[j];
|
|
1184
|
+
if (!b || a === b) {
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
pairs.push(normalizePair(a, b));
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (pairs.length === 0) {
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
await db.execute("BEGIN");
|
|
1194
|
+
try {
|
|
1195
|
+
for (const [entryA, entryB] of pairs) {
|
|
1196
|
+
await db.execute({
|
|
1197
|
+
sql: `
|
|
1198
|
+
INSERT INTO co_recall_edges (
|
|
1199
|
+
entry_a, entry_b, edge_type, weight, session_count, last_co_recalled, created_at
|
|
1200
|
+
)
|
|
1201
|
+
VALUES (?, ?, ?, ?, 1, ?, ?)
|
|
1202
|
+
ON CONFLICT
|
|
1203
|
+
DO UPDATE SET
|
|
1204
|
+
weight = MIN(co_recall_edges.weight + excluded.weight, 1.0),
|
|
1205
|
+
session_count = co_recall_edges.session_count + 1,
|
|
1206
|
+
last_co_recalled = excluded.last_co_recalled
|
|
1207
|
+
`,
|
|
1208
|
+
args: [
|
|
1209
|
+
entryA,
|
|
1210
|
+
entryB,
|
|
1211
|
+
edgeType,
|
|
1212
|
+
DEFAULT_EDGE_INCREMENT,
|
|
1213
|
+
now,
|
|
1214
|
+
now
|
|
1215
|
+
]
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
await db.execute("COMMIT");
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
try {
|
|
1221
|
+
await db.execute("ROLLBACK");
|
|
1222
|
+
} catch {
|
|
1223
|
+
}
|
|
1224
|
+
throw error;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
async function strengthenCoRecallEdges(db, usedEntryIds, timestamp2) {
|
|
1228
|
+
await strengthenEdges(db, usedEntryIds, timestamp2, CO_RECALL_EDGE_TYPE);
|
|
1229
|
+
}
|
|
1230
|
+
async function getCoRecallNeighbors(db, entryId, minWeight = 0.1, limit = 10, edgeType = CO_RECALL_EDGE_TYPE) {
|
|
1231
|
+
const normalizedId = entryId.trim();
|
|
1232
|
+
if (!normalizedId) {
|
|
1233
|
+
return [];
|
|
1234
|
+
}
|
|
1235
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 10;
|
|
1236
|
+
const safeMinWeight = Number.isFinite(minWeight) ? Math.max(minWeight, 0) : 0.1;
|
|
1237
|
+
const result = await db.execute({
|
|
1238
|
+
sql: `
|
|
1239
|
+
SELECT
|
|
1240
|
+
CASE WHEN entry_a = ? THEN entry_b ELSE entry_a END AS neighbor_id,
|
|
1241
|
+
weight,
|
|
1242
|
+
session_count,
|
|
1243
|
+
last_co_recalled
|
|
1244
|
+
FROM co_recall_edges
|
|
1245
|
+
WHERE edge_type = ?
|
|
1246
|
+
AND (entry_a = ? OR entry_b = ?)
|
|
1247
|
+
AND weight >= ?
|
|
1248
|
+
ORDER BY weight DESC, session_count DESC, last_co_recalled DESC
|
|
1249
|
+
LIMIT ?
|
|
1250
|
+
`,
|
|
1251
|
+
args: [normalizedId, edgeType, normalizedId, normalizedId, safeMinWeight, safeLimit]
|
|
1252
|
+
});
|
|
1253
|
+
return result.rows.map((row) => ({
|
|
1254
|
+
entryId: toStringValue(row.neighbor_id),
|
|
1255
|
+
weight: toNumber2(row.weight),
|
|
1256
|
+
sessionCount: toNumber2(row.session_count),
|
|
1257
|
+
lastCoRecalled: toStringValue(row.last_co_recalled)
|
|
1258
|
+
}));
|
|
1259
|
+
}
|
|
1260
|
+
async function getCoRecallEdgeCounts(db, edgeType = CO_RECALL_EDGE_TYPE) {
|
|
1261
|
+
const result = await db.execute({
|
|
1262
|
+
sql: `
|
|
1263
|
+
SELECT entry_id, COUNT(*) AS edge_count
|
|
1264
|
+
FROM (
|
|
1265
|
+
SELECT entry_a AS entry_id
|
|
1266
|
+
FROM co_recall_edges
|
|
1267
|
+
WHERE edge_type = ?
|
|
1268
|
+
UNION ALL
|
|
1269
|
+
SELECT entry_b AS entry_id
|
|
1270
|
+
FROM co_recall_edges
|
|
1271
|
+
WHERE edge_type = ?
|
|
1272
|
+
)
|
|
1273
|
+
GROUP BY entry_id
|
|
1274
|
+
`,
|
|
1275
|
+
args: [edgeType, edgeType]
|
|
1276
|
+
});
|
|
1277
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1278
|
+
for (const row of result.rows) {
|
|
1279
|
+
const entryId = toStringValue(row.entry_id).trim();
|
|
1280
|
+
if (!entryId) {
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
counts.set(entryId, Math.max(0, toNumber2(row.edge_count)));
|
|
1284
|
+
}
|
|
1285
|
+
return counts;
|
|
1286
|
+
}
|
|
1287
|
+
async function getTopCoRecallEdges(db, limit = 20, edgeType = CO_RECALL_EDGE_TYPE) {
|
|
1288
|
+
const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 20;
|
|
1289
|
+
const result = await db.execute({
|
|
1290
|
+
sql: `
|
|
1291
|
+
SELECT entry_a, entry_b, weight, session_count, last_co_recalled
|
|
1292
|
+
FROM co_recall_edges
|
|
1293
|
+
WHERE edge_type = ?
|
|
1294
|
+
ORDER BY weight DESC, session_count DESC, last_co_recalled DESC
|
|
1295
|
+
LIMIT ?
|
|
1296
|
+
`,
|
|
1297
|
+
args: [edgeType, safeLimit]
|
|
1298
|
+
});
|
|
1299
|
+
return result.rows.map((row) => ({
|
|
1300
|
+
entryA: toStringValue(row.entry_a),
|
|
1301
|
+
entryB: toStringValue(row.entry_b),
|
|
1302
|
+
weight: toNumber2(row.weight),
|
|
1303
|
+
sessionCount: toNumber2(row.session_count),
|
|
1304
|
+
lastCoRecalled: toStringValue(row.last_co_recalled)
|
|
1305
|
+
}));
|
|
1306
|
+
}
|
|
1307
|
+
var DEFAULT_EDGE_INCREMENT, MAX_USED_ENTRIES, CO_RECALL_EDGE_TYPE, CO_INGEST_EDGE_TYPE, ALL_EDGE_TYPES, ALL_EDGE_TYPES_PLACEHOLDERS;
|
|
1308
|
+
var init_co_recall = __esm({
|
|
1309
|
+
"src/shared/infrastructure/db/co-recall.ts"() {
|
|
1310
|
+
"use strict";
|
|
1311
|
+
init_entry_utils();
|
|
1312
|
+
DEFAULT_EDGE_INCREMENT = 0.1;
|
|
1313
|
+
MAX_USED_ENTRIES = 20;
|
|
1314
|
+
CO_RECALL_EDGE_TYPE = "co_recalled";
|
|
1315
|
+
CO_INGEST_EDGE_TYPE = "co_ingested";
|
|
1316
|
+
ALL_EDGE_TYPES = [CO_RECALL_EDGE_TYPE, CO_INGEST_EDGE_TYPE];
|
|
1317
|
+
ALL_EDGE_TYPES_PLACEHOLDERS = ALL_EDGE_TYPES.map(() => "?").join(", ");
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
// src/shared/infrastructure/db/meta.ts
|
|
1322
|
+
function normalizeIsoTimestamp(value) {
|
|
1323
|
+
if (value instanceof Date) {
|
|
1324
|
+
return value.toISOString();
|
|
1325
|
+
}
|
|
1326
|
+
const parsed = Date.parse(value);
|
|
1327
|
+
if (!Number.isFinite(parsed)) {
|
|
1328
|
+
throw new Error(`Invalid ISO timestamp: ${value}`);
|
|
1329
|
+
}
|
|
1330
|
+
return new Date(parsed).toISOString();
|
|
1331
|
+
}
|
|
1332
|
+
async function getMetaValue(db, key) {
|
|
1333
|
+
const result = await db.execute({
|
|
1334
|
+
sql: "SELECT value FROM _meta WHERE key = ? LIMIT 1",
|
|
1335
|
+
args: [key]
|
|
1336
|
+
});
|
|
1337
|
+
const raw = result.rows[0];
|
|
1338
|
+
const value = raw?.value ?? (raw ? Object.values(raw)[0] : void 0);
|
|
1339
|
+
if (typeof value !== "string") {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
const trimmed = value.trim();
|
|
1343
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1344
|
+
}
|
|
1345
|
+
async function setMetaValue(db, input) {
|
|
1346
|
+
const updatedAt = normalizeIsoTimestamp(input.updatedAt ?? /* @__PURE__ */ new Date());
|
|
1347
|
+
await db.execute({
|
|
1348
|
+
sql: `
|
|
1349
|
+
INSERT INTO _meta (key, value, updated_at)
|
|
1350
|
+
VALUES (?, ?, ?)
|
|
1351
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
|
1352
|
+
`,
|
|
1353
|
+
args: [input.key, input.value, updatedAt]
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
async function getLastCorpusRebuildAt(db) {
|
|
1357
|
+
return getMetaValue(db, LAST_CORPUS_REBUILD_AT_META_KEY);
|
|
1358
|
+
}
|
|
1359
|
+
async function setLastCorpusRebuildAt(db, value) {
|
|
1360
|
+
const normalized = normalizeIsoTimestamp(value);
|
|
1361
|
+
await setMetaValue(db, {
|
|
1362
|
+
key: LAST_CORPUS_REBUILD_AT_META_KEY,
|
|
1363
|
+
value: normalized,
|
|
1364
|
+
updatedAt: normalized
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
async function initializeLastCorpusRebuildAtForEmptyCorpus(db, at = /* @__PURE__ */ new Date()) {
|
|
1368
|
+
const existing = await getLastCorpusRebuildAt(db);
|
|
1369
|
+
if (existing) {
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const result = await db.execute("SELECT COUNT(*) AS count FROM entries");
|
|
1373
|
+
const count = Number(result.rows[0]?.count ?? 0);
|
|
1374
|
+
if (count > 0) {
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
await setLastCorpusRebuildAt(db, at);
|
|
1378
|
+
}
|
|
1379
|
+
var LAST_CORPUS_REBUILD_AT_META_KEY;
|
|
1380
|
+
var init_meta = __esm({
|
|
1381
|
+
"src/shared/infrastructure/db/meta.ts"() {
|
|
1382
|
+
"use strict";
|
|
1383
|
+
LAST_CORPUS_REBUILD_AT_META_KEY = "last_corpus_rebuild_at";
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1091
1387
|
// src/shared/infrastructure/db/migrations/reflection-importance-cap.ts
|
|
1092
1388
|
async function applyReflectionImportanceCapMigration(db) {
|
|
1093
1389
|
await db.execute("BEGIN IMMEDIATE");
|
|
@@ -1602,25 +1898,55 @@ async function backfillLegacyImportanceFromConfidence(client) {
|
|
|
1602
1898
|
} catch {
|
|
1603
1899
|
}
|
|
1604
1900
|
}
|
|
1605
|
-
async function
|
|
1901
|
+
async function retypeLegacyCoRecallEdges(client) {
|
|
1902
|
+
const edgeInfo = await client.execute("PRAGMA table_info(co_recall_edges)");
|
|
1903
|
+
const edgeColumns = readColumnNames(edgeInfo.rows);
|
|
1904
|
+
if (!edgeColumns.has("edge_type")) {
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
const sentinel = await client.execute({
|
|
1908
|
+
sql: "SELECT 1 AS found FROM _meta WHERE key = ? LIMIT 1",
|
|
1909
|
+
args: [CO_INGEST_EDGE_RETYPE_META_KEY]
|
|
1910
|
+
});
|
|
1911
|
+
if (sentinel.rows.length > 0) {
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1606
1914
|
await client.execute({
|
|
1607
1915
|
sql: `
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1916
|
+
UPDATE co_recall_edges
|
|
1917
|
+
SET edge_type = ?
|
|
1918
|
+
WHERE edge_type = ?
|
|
1611
1919
|
`,
|
|
1612
|
-
args: []
|
|
1920
|
+
args: [CO_INGEST_EDGE_TYPE, CO_RECALL_EDGE_TYPE]
|
|
1613
1921
|
});
|
|
1614
1922
|
await client.execute({
|
|
1615
1923
|
sql: `
|
|
1616
1924
|
INSERT INTO _meta (key, value, updated_at)
|
|
1617
|
-
VALUES ('
|
|
1618
|
-
ON CONFLICT(key) DO
|
|
1925
|
+
VALUES (?, datetime('now'), datetime('now'))
|
|
1926
|
+
ON CONFLICT(key) DO NOTHING
|
|
1619
1927
|
`,
|
|
1620
|
-
args: [
|
|
1928
|
+
args: [CO_INGEST_EDGE_RETYPE_META_KEY]
|
|
1621
1929
|
});
|
|
1622
1930
|
}
|
|
1623
|
-
async function
|
|
1931
|
+
async function stampSchemaMetadata(client) {
|
|
1932
|
+
await client.execute({
|
|
1933
|
+
sql: `
|
|
1934
|
+
INSERT INTO _meta (key, value, updated_at)
|
|
1935
|
+
VALUES ('db_created_at', datetime('now'), datetime('now'))
|
|
1936
|
+
ON CONFLICT(key) DO NOTHING
|
|
1937
|
+
`,
|
|
1938
|
+
args: []
|
|
1939
|
+
});
|
|
1940
|
+
await client.execute({
|
|
1941
|
+
sql: `
|
|
1942
|
+
INSERT INTO _meta (key, value, updated_at)
|
|
1943
|
+
VALUES ('schema_version', ?, datetime('now'))
|
|
1944
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
|
1945
|
+
`,
|
|
1946
|
+
args: [APP_VERSION]
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
async function repairVectorIndexIfNeeded(client) {
|
|
1624
1950
|
try {
|
|
1625
1951
|
const hasEntries = await client.execute(
|
|
1626
1952
|
"SELECT 1 FROM entries WHERE embedding IS NOT NULL AND retired = 0 AND superseded_by IS NULL LIMIT 1"
|
|
@@ -1658,17 +1984,21 @@ async function initSchema(client) {
|
|
|
1658
1984
|
await applyReflectionImportanceCapMigration(client);
|
|
1659
1985
|
await applyReflectionRemovalMigration(client);
|
|
1660
1986
|
await backfillLegacyImportanceFromConfidence(client);
|
|
1987
|
+
await retypeLegacyCoRecallEdges(client);
|
|
1661
1988
|
for (const statement of CREATE_INDEX_STATEMENTS) {
|
|
1662
1989
|
await client.execute(statement);
|
|
1663
1990
|
}
|
|
1664
1991
|
await repairVectorIndexIfNeeded(client);
|
|
1665
1992
|
await stampSchemaMetadata(client);
|
|
1993
|
+
await initializeLastCorpusRebuildAtForEmptyCorpus(client);
|
|
1666
1994
|
}
|
|
1667
|
-
var LEGACY_IMPORTANCE_BACKFILL_META_KEY;
|
|
1995
|
+
var LEGACY_IMPORTANCE_BACKFILL_META_KEY, CO_INGEST_EDGE_RETYPE_META_KEY;
|
|
1668
1996
|
var init_init = __esm({
|
|
1669
1997
|
"src/shared/infrastructure/db/schema/init.ts"() {
|
|
1670
1998
|
"use strict";
|
|
1671
1999
|
init_version();
|
|
2000
|
+
init_co_recall();
|
|
2001
|
+
init_meta();
|
|
1672
2002
|
init_reflection_importance_cap();
|
|
1673
2003
|
init_reflection_removal();
|
|
1674
2004
|
init_vector_index();
|
|
@@ -1677,6 +2007,7 @@ var init_init = __esm({
|
|
|
1677
2007
|
init_fts();
|
|
1678
2008
|
init_recall_events();
|
|
1679
2009
|
LEGACY_IMPORTANCE_BACKFILL_META_KEY = "legacy_importance_backfill_from_confidence_v1";
|
|
2010
|
+
CO_INGEST_EDGE_RETYPE_META_KEY = "co_recall_edges_retyped_to_co_ingested_v1";
|
|
1680
2011
|
}
|
|
1681
2012
|
});
|
|
1682
2013
|
|
|
@@ -2039,6 +2370,52 @@ var init_client = __esm({
|
|
|
2039
2370
|
}
|
|
2040
2371
|
});
|
|
2041
2372
|
|
|
2373
|
+
// src/shared/domain/types.ts
|
|
2374
|
+
var KNOWLEDGE_TYPES, IMPORTANCE_MIN, IMPORTANCE_MAX, EXPIRY_LEVELS, SCOPE_LEVELS, KNOWLEDGE_PLATFORMS, REJECTED_CONFLICT_ENTRY_ID;
|
|
2375
|
+
var init_types = __esm({
|
|
2376
|
+
"src/shared/domain/types.ts"() {
|
|
2377
|
+
"use strict";
|
|
2378
|
+
KNOWLEDGE_TYPES = [
|
|
2379
|
+
"fact",
|
|
2380
|
+
"decision",
|
|
2381
|
+
"preference",
|
|
2382
|
+
"todo",
|
|
2383
|
+
"relationship",
|
|
2384
|
+
"event",
|
|
2385
|
+
"lesson",
|
|
2386
|
+
"reflection"
|
|
2387
|
+
];
|
|
2388
|
+
IMPORTANCE_MIN = 1;
|
|
2389
|
+
IMPORTANCE_MAX = 10;
|
|
2390
|
+
EXPIRY_LEVELS = ["core", "permanent", "temporary"];
|
|
2391
|
+
SCOPE_LEVELS = ["private", "personal", "public"];
|
|
2392
|
+
KNOWLEDGE_PLATFORMS = ["openclaw", "claude-code", "codex", "plaud"];
|
|
2393
|
+
REJECTED_CONFLICT_ENTRY_ID = "rejected";
|
|
2394
|
+
}
|
|
2395
|
+
});
|
|
2396
|
+
|
|
2397
|
+
// src/shared/domain/subject-key.ts
|
|
2398
|
+
function isGenericSubjectKey(subjectKey2, entryType) {
|
|
2399
|
+
const normalizedSubjectKey = subjectKey2?.trim().toLowerCase();
|
|
2400
|
+
const normalizedEntryType = entryType.trim().toLowerCase();
|
|
2401
|
+
if (!normalizedSubjectKey || !normalizedEntryType || !ENTRY_TYPE_ATTRIBUTES.has(normalizedEntryType)) {
|
|
2402
|
+
return false;
|
|
2403
|
+
}
|
|
2404
|
+
const slashIndex = normalizedSubjectKey.lastIndexOf("/");
|
|
2405
|
+
if (slashIndex < 0 || slashIndex === normalizedSubjectKey.length - 1) {
|
|
2406
|
+
return false;
|
|
2407
|
+
}
|
|
2408
|
+
return normalizedSubjectKey.slice(slashIndex + 1) === normalizedEntryType;
|
|
2409
|
+
}
|
|
2410
|
+
var ENTRY_TYPE_ATTRIBUTES;
|
|
2411
|
+
var init_subject_key = __esm({
|
|
2412
|
+
"src/shared/domain/subject-key.ts"() {
|
|
2413
|
+
"use strict";
|
|
2414
|
+
init_types();
|
|
2415
|
+
ENTRY_TYPE_ATTRIBUTES = new Set(KNOWLEDGE_TYPES);
|
|
2416
|
+
}
|
|
2417
|
+
});
|
|
2418
|
+
|
|
2042
2419
|
// src/modules/store/domain/structured-claim.ts
|
|
2043
2420
|
function normalizeLower(value) {
|
|
2044
2421
|
return value?.trim().toLowerCase() ?? "";
|
|
@@ -2256,6 +2633,34 @@ function buildSubjectKey(subjectEntity, subjectAttribute, fallback) {
|
|
|
2256
2633
|
}
|
|
2257
2634
|
return fallback;
|
|
2258
2635
|
}
|
|
2636
|
+
function extractTopicSlug(text) {
|
|
2637
|
+
if (!text || text.trim().length === 0) {
|
|
2638
|
+
return null;
|
|
2639
|
+
}
|
|
2640
|
+
const tokens = text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((token) => token.length > 1 && !TOPIC_SLUG_STOP_WORDS.has(token));
|
|
2641
|
+
const slug = tokens.slice(0, TOPIC_SLUG_MAX_TOKENS).join("_").slice(0, TOPIC_SLUG_MAX_LENGTH);
|
|
2642
|
+
return slug.length >= TOPIC_SLUG_MIN_LENGTH ? slug : null;
|
|
2643
|
+
}
|
|
2644
|
+
function deriveTopicAttribute(entry, fallback) {
|
|
2645
|
+
const normalizedFallback = normalizeLower(fallback);
|
|
2646
|
+
const normalizedType = normalizeLower(entry.type);
|
|
2647
|
+
const objectSlug = extractTopicSlug(entry.claimObject);
|
|
2648
|
+
if (objectSlug && objectSlug !== normalizedFallback && objectSlug !== normalizedType) {
|
|
2649
|
+
return objectSlug;
|
|
2650
|
+
}
|
|
2651
|
+
const subjectSlug = extractTopicSlug(entry.subject);
|
|
2652
|
+
if (subjectSlug && subjectSlug !== normalizedFallback && subjectSlug !== normalizedType) {
|
|
2653
|
+
return subjectSlug;
|
|
2654
|
+
}
|
|
2655
|
+
return fallback;
|
|
2656
|
+
}
|
|
2657
|
+
function resolveSubjectAttribute(entry, subjectEntity, subjectAttribute, fallbackSubjectKey) {
|
|
2658
|
+
const subjectKey2 = buildSubjectKey(subjectEntity, subjectAttribute, fallbackSubjectKey);
|
|
2659
|
+
if (!isGenericSubjectKey(subjectKey2, entry.type)) {
|
|
2660
|
+
return subjectAttribute;
|
|
2661
|
+
}
|
|
2662
|
+
return deriveTopicAttribute(entry, subjectAttribute);
|
|
2663
|
+
}
|
|
2259
2664
|
function normalizeStructuredClaimFields(entry) {
|
|
2260
2665
|
const normalizedSubjectEntity = normalizeLower(entry.subjectEntity);
|
|
2261
2666
|
const normalizedSubjectKey = normalizeLower(entry.subjectKey);
|
|
@@ -2272,6 +2677,16 @@ function normalizeStructuredClaimFields(entry) {
|
|
|
2272
2677
|
claimRole: entry.claimRole
|
|
2273
2678
|
});
|
|
2274
2679
|
const normalizedAttribute = detectedState.normalizedAttribute ?? subjectAttribute;
|
|
2680
|
+
const resolvedAttribute = resolveSubjectAttribute(
|
|
2681
|
+
{
|
|
2682
|
+
type: entry.type,
|
|
2683
|
+
subject: entry.subject,
|
|
2684
|
+
claimObject: entry.claimObject
|
|
2685
|
+
},
|
|
2686
|
+
subjectEntity,
|
|
2687
|
+
normalizedAttribute,
|
|
2688
|
+
normalizedSubjectKey
|
|
2689
|
+
);
|
|
2275
2690
|
const transitionClaim = detectedState.role ? void 0 : detectTransitionClaim({
|
|
2276
2691
|
content: entry.content,
|
|
2277
2692
|
claimObject: entry.claimObject,
|
|
@@ -2280,11 +2695,11 @@ function normalizeStructuredClaimFields(entry) {
|
|
|
2280
2695
|
if (subjectEntity) {
|
|
2281
2696
|
entry.subjectEntity = subjectEntity;
|
|
2282
2697
|
}
|
|
2283
|
-
if (
|
|
2284
|
-
entry.subjectAttribute =
|
|
2698
|
+
if (resolvedAttribute) {
|
|
2699
|
+
entry.subjectAttribute = resolvedAttribute;
|
|
2285
2700
|
}
|
|
2286
|
-
if (subjectEntity ||
|
|
2287
|
-
entry.subjectKey = buildSubjectKey(subjectEntity,
|
|
2701
|
+
if (subjectEntity || resolvedAttribute) {
|
|
2702
|
+
entry.subjectKey = buildSubjectKey(subjectEntity, resolvedAttribute, normalizedSubjectKey);
|
|
2288
2703
|
}
|
|
2289
2704
|
if (transitionClaim) {
|
|
2290
2705
|
entry.claimPredicate = STATE_TRANSITION_PREDICATE;
|
|
@@ -2330,10 +2745,11 @@ function buildStructuredClaimIdentity(entry) {
|
|
|
2330
2745
|
cardinality: "exact_value_only"
|
|
2331
2746
|
};
|
|
2332
2747
|
}
|
|
2333
|
-
var STRUCTURED_CLAIM_IDENTITY_TYPES, STRUCTURED_CLAIM_CONFIDENCE_FLOOR, STATE_ANCHOR_PREDICATE, STATE_TRANSITION_PREDICATE, DECISION_COMMITMENT_MARKERS, DECISION_NON_COMMITMENT_MARKERS, EXPLICIT_STATE_ROLE_SUFFIXES, CURRENT_STATE_SUBJECT_MARKERS, PRIOR_STATE_SUBJECT_MARKERS, CURRENT_STATE_CONTENT_MARKERS, PRIOR_STATE_CONTENT_MARKERS, STATE_NON_COMMITMENT_MARKERS, TRANSITION_PATTERNS, TRANSITION_MULTI_STEP_MARKERS;
|
|
2748
|
+
var STRUCTURED_CLAIM_IDENTITY_TYPES, STRUCTURED_CLAIM_CONFIDENCE_FLOOR, STATE_ANCHOR_PREDICATE, STATE_TRANSITION_PREDICATE, DECISION_COMMITMENT_MARKERS, DECISION_NON_COMMITMENT_MARKERS, EXPLICIT_STATE_ROLE_SUFFIXES, CURRENT_STATE_SUBJECT_MARKERS, PRIOR_STATE_SUBJECT_MARKERS, CURRENT_STATE_CONTENT_MARKERS, PRIOR_STATE_CONTENT_MARKERS, STATE_NON_COMMITMENT_MARKERS, TRANSITION_PATTERNS, TRANSITION_MULTI_STEP_MARKERS, TOPIC_SLUG_STOP_WORDS, TOPIC_SLUG_MAX_TOKENS, TOPIC_SLUG_MAX_LENGTH, TOPIC_SLUG_MIN_LENGTH;
|
|
2334
2749
|
var init_structured_claim = __esm({
|
|
2335
2750
|
"src/modules/store/domain/structured-claim.ts"() {
|
|
2336
2751
|
"use strict";
|
|
2752
|
+
init_subject_key();
|
|
2337
2753
|
STRUCTURED_CLAIM_IDENTITY_TYPES = /* @__PURE__ */ new Set(["fact", "preference", "decision"]);
|
|
2338
2754
|
STRUCTURED_CLAIM_CONFIDENCE_FLOOR = 0.8;
|
|
2339
2755
|
STATE_ANCHOR_PREDICATE = "state_is";
|
|
@@ -2427,6 +2843,48 @@ var init_structured_claim = __esm({
|
|
|
2427
2843
|
/\bafter that\b/i,
|
|
2428
2844
|
/\bthen\b.{0,24}\b(?:move|moved|switch|switched|replace|replaced|migrate|migrated|transition|transitioned)\b/i
|
|
2429
2845
|
];
|
|
2846
|
+
TOPIC_SLUG_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
2847
|
+
"the",
|
|
2848
|
+
"a",
|
|
2849
|
+
"an",
|
|
2850
|
+
"for",
|
|
2851
|
+
"and",
|
|
2852
|
+
"or",
|
|
2853
|
+
"to",
|
|
2854
|
+
"in",
|
|
2855
|
+
"of",
|
|
2856
|
+
"on",
|
|
2857
|
+
"at",
|
|
2858
|
+
"by",
|
|
2859
|
+
"with",
|
|
2860
|
+
"from",
|
|
2861
|
+
"that",
|
|
2862
|
+
"this",
|
|
2863
|
+
"is",
|
|
2864
|
+
"was",
|
|
2865
|
+
"are",
|
|
2866
|
+
"were",
|
|
2867
|
+
"be",
|
|
2868
|
+
"been",
|
|
2869
|
+
"has",
|
|
2870
|
+
"have",
|
|
2871
|
+
"had",
|
|
2872
|
+
"it",
|
|
2873
|
+
"its",
|
|
2874
|
+
"create",
|
|
2875
|
+
"creating",
|
|
2876
|
+
"document",
|
|
2877
|
+
"documenting",
|
|
2878
|
+
"turn",
|
|
2879
|
+
"turning",
|
|
2880
|
+
"into",
|
|
2881
|
+
"concrete",
|
|
2882
|
+
"investigate",
|
|
2883
|
+
"investigating"
|
|
2884
|
+
]);
|
|
2885
|
+
TOPIC_SLUG_MAX_TOKENS = 6;
|
|
2886
|
+
TOPIC_SLUG_MAX_LENGTH = 60;
|
|
2887
|
+
TOPIC_SLUG_MIN_LENGTH = 3;
|
|
2430
2888
|
}
|
|
2431
2889
|
});
|
|
2432
2890
|
|
|
@@ -2706,212 +3164,274 @@ var init_client2 = __esm({
|
|
|
2706
3164
|
}
|
|
2707
3165
|
});
|
|
2708
3166
|
|
|
2709
|
-
// src/shared/
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
return Number(value);
|
|
2716
|
-
}
|
|
2717
|
-
if (typeof value === "string" && value.trim()) {
|
|
2718
|
-
return Number(value);
|
|
2719
|
-
}
|
|
2720
|
-
return Number.NaN;
|
|
2721
|
-
}
|
|
2722
|
-
function toStringValue(value) {
|
|
2723
|
-
if (typeof value === "string") {
|
|
2724
|
-
return value;
|
|
2725
|
-
}
|
|
2726
|
-
if (typeof value === "number" || typeof value === "bigint") {
|
|
2727
|
-
return String(value);
|
|
3167
|
+
// src/shared/infrastructure/config/schema.ts
|
|
3168
|
+
import os3 from "os";
|
|
3169
|
+
import path3 from "path";
|
|
3170
|
+
function resolveClaimExtractionBatchSize(value) {
|
|
3171
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
3172
|
+
return DEFAULT_CLAIM_EXTRACTION_BATCH_SIZE;
|
|
2728
3173
|
}
|
|
2729
|
-
return
|
|
3174
|
+
return Math.min(Math.floor(value), MAX_CLAIM_EXTRACTION_BATCH_SIZE);
|
|
2730
3175
|
}
|
|
2731
|
-
function
|
|
2732
|
-
if (typeof value
|
|
2733
|
-
return
|
|
2734
|
-
}
|
|
2735
|
-
if (typeof value === "bigint") {
|
|
2736
|
-
return Number(value);
|
|
2737
|
-
}
|
|
2738
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
2739
|
-
return Number(value);
|
|
3176
|
+
function resolveClaimExtractionConcurrency(value) {
|
|
3177
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
3178
|
+
return DEFAULT_CLAIM_EXTRACTION_CONCURRENCY;
|
|
2740
3179
|
}
|
|
2741
|
-
return
|
|
3180
|
+
return Math.min(Math.floor(value), MAX_CLAIM_EXTRACTION_CONCURRENCY);
|
|
2742
3181
|
}
|
|
2743
|
-
function
|
|
2744
|
-
|
|
2745
|
-
return 0;
|
|
2746
|
-
}
|
|
2747
|
-
const parsed = new Date(pastIso);
|
|
2748
|
-
if (Number.isNaN(parsed.getTime())) {
|
|
2749
|
-
return 0;
|
|
2750
|
-
}
|
|
2751
|
-
const delta = (now.getTime() - parsed.getTime()) / MILLISECONDS_PER_DAY;
|
|
2752
|
-
if (!Number.isFinite(delta)) {
|
|
2753
|
-
return 0;
|
|
2754
|
-
}
|
|
2755
|
-
return Math.max(delta, 0);
|
|
3182
|
+
function isModelTask(value) {
|
|
3183
|
+
return MODEL_TASK_KEYS.includes(value);
|
|
2756
3184
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
"src/shared/utils/entry-utils.ts"() {
|
|
2760
|
-
"use strict";
|
|
2761
|
-
MILLISECONDS_PER_DAY = 1e3 * 60 * 60 * 24;
|
|
2762
|
-
}
|
|
2763
|
-
});
|
|
2764
|
-
|
|
2765
|
-
// src/shared/infrastructure/db/co-recall.ts
|
|
2766
|
-
function normalizePair(a, b) {
|
|
2767
|
-
return a < b ? [a, b] : [b, a];
|
|
3185
|
+
function isScoringSetKey(value) {
|
|
3186
|
+
return SCORING_SET_KEYS.includes(value);
|
|
2768
3187
|
}
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
new Set(
|
|
2772
|
-
usedEntryIds.map((id) => id.trim()).filter((id) => id.length > 0)
|
|
2773
|
-
)
|
|
2774
|
-
).slice(0, MAX_USED_ENTRIES);
|
|
2775
|
-
if (uniqueIds.length < 2) {
|
|
2776
|
-
return;
|
|
2777
|
-
}
|
|
2778
|
-
const now = timestamp2 || (/* @__PURE__ */ new Date()).toISOString();
|
|
2779
|
-
const pairs = [];
|
|
2780
|
-
for (let i = 0; i < uniqueIds.length; i += 1) {
|
|
2781
|
-
const a = uniqueIds[i];
|
|
2782
|
-
if (!a) {
|
|
2783
|
-
continue;
|
|
2784
|
-
}
|
|
2785
|
-
for (let j = i + 1; j < uniqueIds.length; j += 1) {
|
|
2786
|
-
const b = uniqueIds[j];
|
|
2787
|
-
if (!b || a === b) {
|
|
2788
|
-
continue;
|
|
2789
|
-
}
|
|
2790
|
-
pairs.push(normalizePair(a, b));
|
|
2791
|
-
}
|
|
2792
|
-
}
|
|
2793
|
-
if (pairs.length === 0) {
|
|
2794
|
-
return;
|
|
2795
|
-
}
|
|
2796
|
-
await db.execute("BEGIN");
|
|
2797
|
-
try {
|
|
2798
|
-
for (const [entryA, entryB] of pairs) {
|
|
2799
|
-
await db.execute({
|
|
2800
|
-
sql: `
|
|
2801
|
-
INSERT INTO co_recall_edges (
|
|
2802
|
-
entry_a, entry_b, edge_type, weight, session_count, last_co_recalled, created_at
|
|
2803
|
-
)
|
|
2804
|
-
VALUES (?, ?, ?, ?, 1, ?, ?)
|
|
2805
|
-
ON CONFLICT
|
|
2806
|
-
DO UPDATE SET
|
|
2807
|
-
weight = MIN(co_recall_edges.weight + excluded.weight, 1.0),
|
|
2808
|
-
session_count = co_recall_edges.session_count + 1,
|
|
2809
|
-
last_co_recalled = excluded.last_co_recalled
|
|
2810
|
-
`,
|
|
2811
|
-
args: [
|
|
2812
|
-
entryA,
|
|
2813
|
-
entryB,
|
|
2814
|
-
CO_RECALL_EDGE_TYPE,
|
|
2815
|
-
DEFAULT_EDGE_INCREMENT,
|
|
2816
|
-
now,
|
|
2817
|
-
now
|
|
2818
|
-
]
|
|
2819
|
-
});
|
|
2820
|
-
}
|
|
2821
|
-
await db.execute("COMMIT");
|
|
2822
|
-
} catch (error) {
|
|
2823
|
-
try {
|
|
2824
|
-
await db.execute("ROLLBACK");
|
|
2825
|
-
} catch {
|
|
2826
|
-
}
|
|
2827
|
-
throw error;
|
|
2828
|
-
}
|
|
3188
|
+
function isAgenrAuthMethod(value) {
|
|
3189
|
+
return AUTH_METHOD_SET.has(value);
|
|
2829
3190
|
}
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
if (!normalizedId) {
|
|
2833
|
-
return [];
|
|
2834
|
-
}
|
|
2835
|
-
const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.floor(limit) : 10;
|
|
2836
|
-
const safeMinWeight = Number.isFinite(minWeight) ? Math.max(minWeight, 0) : 0.1;
|
|
2837
|
-
const result = await db.execute({
|
|
2838
|
-
sql: `
|
|
2839
|
-
SELECT
|
|
2840
|
-
CASE WHEN entry_a = ? THEN entry_b ELSE entry_a END AS neighbor_id,
|
|
2841
|
-
weight,
|
|
2842
|
-
session_count,
|
|
2843
|
-
last_co_recalled
|
|
2844
|
-
FROM co_recall_edges
|
|
2845
|
-
WHERE edge_type = ?
|
|
2846
|
-
AND (entry_a = ? OR entry_b = ?)
|
|
2847
|
-
AND weight >= ?
|
|
2848
|
-
ORDER BY weight DESC, session_count DESC, last_co_recalled DESC
|
|
2849
|
-
LIMIT ?
|
|
2850
|
-
`,
|
|
2851
|
-
args: [normalizedId, CO_RECALL_EDGE_TYPE, normalizedId, normalizedId, safeMinWeight, safeLimit]
|
|
2852
|
-
});
|
|
2853
|
-
return result.rows.map((row) => ({
|
|
2854
|
-
entryId: toStringValue(row.neighbor_id),
|
|
2855
|
-
weight: toNumber2(row.weight),
|
|
2856
|
-
sessionCount: toNumber2(row.session_count),
|
|
2857
|
-
lastCoRecalled: toStringValue(row.last_co_recalled)
|
|
2858
|
-
}));
|
|
3191
|
+
function resolveDefaultKnowledgeDbPath() {
|
|
3192
|
+
return path3.join(os3.homedir(), ".agenr", "knowledge.db");
|
|
2859
3193
|
}
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
3194
|
+
var SCORING_SET_KEYS, AUTH_METHOD_DEFINITIONS, AUTH_METHOD_SET, MODEL_TASK_KEYS, DEFAULT_EMBEDDING_PROVIDER, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_DIMENSIONS, DEFAULT_FORGETTING_SCORE_THRESHOLD, DEFAULT_FORGETTING_MAX_AGE_DAYS, DEFAULT_SCORING_FTS_BOOST, DEFAULT_SCORING_VECTOR_EXPONENT, DEFAULT_SCORING_IMPORTANCE_FLOOR, DEFAULT_SCORING_FRESHNESS_HIGH_HOURS, DEFAULT_SCORING_FRESHNESS_MEDIUM_HOURS, DEFAULT_SCORING_FRESHNESS_LOW_HOURS, DEFAULT_SCORING_RECENCY_HALFLIFE_PERMANENT, DEFAULT_SCORING_RECENCY_HALFLIFE_TEMPORARY, DEFAULT_SCORING_FRESHNESS_MIN_IMPORTANCE, DEFAULT_SCORING_CONTRADICTION_PENALTY, DEFAULT_SCORING_QUALITY_MIN, DEFAULT_SCORING_QUALITY_MAX, DEFAULT_SCORING_IMPORTANCE_BLEND, DEFAULT_SCORING_RECALL_BLEND, DEFAULT_SCORING_TODO_HALFLIFE_DAYS, DEFAULT_SCORING_TODO_FLOOR_HIGH, DEFAULT_SCORING_TODO_FLOOR_LOW, DEFAULT_QUALITY_EVOLUTION_RECALL_SATURATION, DEFAULT_QUALITY_EVOLUTION_EDGE_SATURATION, DEFAULT_QUALITY_EVOLUTION_RECENCY_HALF_LIFE_DAYS, DEFAULT_QUALITY_EVOLUTION_NEUTRAL_FLOOR, DEFAULT_QUALITY_EVOLUTION_GRACE_MULTIPLIER, DEFAULT_QUALITY_EVOLUTION_DECAY_STEEPNESS, DEFAULT_QUALITY_EVOLUTION_EMA_ALPHA, DEFAULT_CONTRADICTION_ENABLED, DEFAULT_TASK_MODEL, DEFAULT_SURGEON_MODEL, DEFAULT_AUTO_SUPERSEDE_CONFIDENCE, DEFAULT_CONTRADICTION_BLOCKING_THRESHOLD, DEFAULT_CLAIM_EXTRACTION_BATCH_SIZE, MAX_CLAIM_EXTRACTION_BATCH_SIZE, DEFAULT_CLAIM_EXTRACTION_CONCURRENCY, MAX_CLAIM_EXTRACTION_CONCURRENCY, DEFAULT_SURGEON_CONTEXT_LIMIT, DEFAULT_SURGEON_COST_CAP, DEFAULT_SURGEON_DAILY_COST_CAP, DEFAULT_SURGEON_RETIREMENT_PROTECT_RECALLED_DAYS, DEFAULT_SURGEON_RETIREMENT_PROTECT_MIN_IMPORTANCE, DEFAULT_SURGEON_RETIREMENT_MAX_ACTIONS_PER_RUN, DEFAULT_SURGEON_SKIP_RECENTLY_EVALUATED_DAYS, DEFAULT_SURGEON_DEDUP_MAX_MERGES_PER_RUN, DEFAULT_SURGEON_CONTRADICTIONS_MAX_ACTIONS_PER_RUN;
|
|
3195
|
+
var init_schema2 = __esm({
|
|
3196
|
+
"src/shared/infrastructure/config/schema.ts"() {
|
|
3197
|
+
"use strict";
|
|
3198
|
+
SCORING_SET_KEYS = [
|
|
3199
|
+
"scoring.ftsBoost",
|
|
3200
|
+
"scoring.vectorExponent",
|
|
3201
|
+
"scoring.importanceFloor",
|
|
3202
|
+
"scoring.freshnessWindow.high",
|
|
3203
|
+
"scoring.freshnessWindow.medium",
|
|
3204
|
+
"scoring.freshnessWindow.low",
|
|
3205
|
+
"scoring.recencyHalfLife.permanent",
|
|
3206
|
+
"scoring.recencyHalfLife.temporary",
|
|
3207
|
+
"scoring.freshnessMinImportance",
|
|
3208
|
+
"scoring.contradictionPenalty",
|
|
3209
|
+
"scoring.qualityRange.min",
|
|
3210
|
+
"scoring.qualityRange.max",
|
|
3211
|
+
"scoring.importanceBlend.importance",
|
|
3212
|
+
"scoring.importanceBlend.recall",
|
|
3213
|
+
"scoring.todoPenalty.halfLifeDays",
|
|
3214
|
+
"scoring.todoPenalty.floorHigh",
|
|
3215
|
+
"scoring.todoPenalty.floorLow"
|
|
3216
|
+
];
|
|
3217
|
+
AUTH_METHOD_DEFINITIONS = [
|
|
3218
|
+
{
|
|
3219
|
+
id: "anthropic-oauth",
|
|
3220
|
+
provider: "anthropic",
|
|
3221
|
+
title: "Anthropic - Claude subscription (OAuth)",
|
|
3222
|
+
setupDescription: "Uses your Claude Pro/Team subscription via Claude CLI credentials. No per-token cost. Requires Claude Code CLI.",
|
|
3223
|
+
preferredModels: ["claude-opus-4-6", "claude-sonnet-4-20250514", "claude-haiku-3-5-20241022"]
|
|
3224
|
+
},
|
|
3225
|
+
{
|
|
3226
|
+
id: "anthropic-token",
|
|
3227
|
+
provider: "anthropic",
|
|
3228
|
+
title: "Anthropic - Claude subscription (long-lived token)",
|
|
3229
|
+
setupDescription: "Uses a long-lived token from `claude setup-token`. No per-token cost. Simpler setup.",
|
|
3230
|
+
preferredModels: ["claude-opus-4-6", "claude-sonnet-4-20250514", "claude-haiku-3-5-20241022"]
|
|
3231
|
+
},
|
|
3232
|
+
{
|
|
3233
|
+
id: "anthropic-api-key",
|
|
3234
|
+
provider: "anthropic",
|
|
3235
|
+
title: "Anthropic - API key",
|
|
3236
|
+
setupDescription: "Standard API key from console.anthropic.com. Pay per token.",
|
|
3237
|
+
preferredModels: ["claude-sonnet-4-20250514", "claude-opus-4-6", "claude-haiku-3-5-20241022"]
|
|
3238
|
+
},
|
|
3239
|
+
{
|
|
3240
|
+
id: "openai-subscription",
|
|
3241
|
+
provider: "openai-codex",
|
|
3242
|
+
title: "OpenAI - Subscription (via Codex CLI)",
|
|
3243
|
+
setupDescription: "Uses your ChatGPT Plus subscription via Codex CLI credentials. No per-token cost. Requires Codex CLI.",
|
|
3244
|
+
preferredModels: ["gpt-5.3-codex", "o3-codex"]
|
|
3245
|
+
},
|
|
3246
|
+
{
|
|
3247
|
+
id: "openai-api-key",
|
|
3248
|
+
provider: "openai",
|
|
3249
|
+
title: "OpenAI - API key",
|
|
3250
|
+
setupDescription: "Standard API key from https://platform.openai.com/api-keys. Pay per token.",
|
|
3251
|
+
preferredModels: ["gpt-5.4-mini", "gpt-5.4", "gpt-4.1-mini"]
|
|
3252
|
+
}
|
|
3253
|
+
];
|
|
3254
|
+
AUTH_METHOD_SET = new Set(AUTH_METHOD_DEFINITIONS.map((item) => item.id));
|
|
3255
|
+
MODEL_TASK_KEYS = [
|
|
3256
|
+
"extraction",
|
|
3257
|
+
"claimExtraction",
|
|
3258
|
+
"contradictionJudge",
|
|
3259
|
+
"handoffSummary"
|
|
3260
|
+
];
|
|
3261
|
+
DEFAULT_EMBEDDING_PROVIDER = "openai";
|
|
3262
|
+
DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
|
|
3263
|
+
DEFAULT_EMBEDDING_DIMENSIONS = 1024;
|
|
3264
|
+
DEFAULT_FORGETTING_SCORE_THRESHOLD = 0.05;
|
|
3265
|
+
DEFAULT_FORGETTING_MAX_AGE_DAYS = 60;
|
|
3266
|
+
DEFAULT_SCORING_FTS_BOOST = 1.3;
|
|
3267
|
+
DEFAULT_SCORING_VECTOR_EXPONENT = 0.7;
|
|
3268
|
+
DEFAULT_SCORING_IMPORTANCE_FLOOR = 0.55;
|
|
3269
|
+
DEFAULT_SCORING_FRESHNESS_HIGH_HOURS = 1;
|
|
3270
|
+
DEFAULT_SCORING_FRESHNESS_MEDIUM_HOURS = 6;
|
|
3271
|
+
DEFAULT_SCORING_FRESHNESS_LOW_HOURS = 24;
|
|
3272
|
+
DEFAULT_SCORING_RECENCY_HALFLIFE_PERMANENT = 365;
|
|
3273
|
+
DEFAULT_SCORING_RECENCY_HALFLIFE_TEMPORARY = 30;
|
|
3274
|
+
DEFAULT_SCORING_FRESHNESS_MIN_IMPORTANCE = 6;
|
|
3275
|
+
DEFAULT_SCORING_CONTRADICTION_PENALTY = 0.8;
|
|
3276
|
+
DEFAULT_SCORING_QUALITY_MIN = 0.7;
|
|
3277
|
+
DEFAULT_SCORING_QUALITY_MAX = 1.3;
|
|
3278
|
+
DEFAULT_SCORING_IMPORTANCE_BLEND = 0.7;
|
|
3279
|
+
DEFAULT_SCORING_RECALL_BLEND = 0.3;
|
|
3280
|
+
DEFAULT_SCORING_TODO_HALFLIFE_DAYS = 7;
|
|
3281
|
+
DEFAULT_SCORING_TODO_FLOOR_HIGH = 0.4;
|
|
3282
|
+
DEFAULT_SCORING_TODO_FLOOR_LOW = 0.1;
|
|
3283
|
+
DEFAULT_QUALITY_EVOLUTION_RECALL_SATURATION = 10;
|
|
3284
|
+
DEFAULT_QUALITY_EVOLUTION_EDGE_SATURATION = 8;
|
|
3285
|
+
DEFAULT_QUALITY_EVOLUTION_RECENCY_HALF_LIFE_DAYS = 30;
|
|
3286
|
+
DEFAULT_QUALITY_EVOLUTION_NEUTRAL_FLOOR = 0.35;
|
|
3287
|
+
DEFAULT_QUALITY_EVOLUTION_GRACE_MULTIPLIER = 1;
|
|
3288
|
+
DEFAULT_QUALITY_EVOLUTION_DECAY_STEEPNESS = 0.1;
|
|
3289
|
+
DEFAULT_QUALITY_EVOLUTION_EMA_ALPHA = 0.3;
|
|
3290
|
+
DEFAULT_CONTRADICTION_ENABLED = true;
|
|
3291
|
+
DEFAULT_TASK_MODEL = "gpt-5.4-mini";
|
|
3292
|
+
DEFAULT_SURGEON_MODEL = "gpt-5.4";
|
|
3293
|
+
DEFAULT_AUTO_SUPERSEDE_CONFIDENCE = 0.85;
|
|
3294
|
+
DEFAULT_CONTRADICTION_BLOCKING_THRESHOLD = 0.85;
|
|
3295
|
+
DEFAULT_CLAIM_EXTRACTION_BATCH_SIZE = 10;
|
|
3296
|
+
MAX_CLAIM_EXTRACTION_BATCH_SIZE = 25;
|
|
3297
|
+
DEFAULT_CLAIM_EXTRACTION_CONCURRENCY = 5;
|
|
3298
|
+
MAX_CLAIM_EXTRACTION_CONCURRENCY = 50;
|
|
3299
|
+
DEFAULT_SURGEON_CONTEXT_LIMIT = null;
|
|
3300
|
+
DEFAULT_SURGEON_COST_CAP = 5;
|
|
3301
|
+
DEFAULT_SURGEON_DAILY_COST_CAP = 15;
|
|
3302
|
+
DEFAULT_SURGEON_RETIREMENT_PROTECT_RECALLED_DAYS = 14;
|
|
3303
|
+
DEFAULT_SURGEON_RETIREMENT_PROTECT_MIN_IMPORTANCE = 8;
|
|
3304
|
+
DEFAULT_SURGEON_RETIREMENT_MAX_ACTIONS_PER_RUN = 100;
|
|
3305
|
+
DEFAULT_SURGEON_SKIP_RECENTLY_EVALUATED_DAYS = 7;
|
|
3306
|
+
DEFAULT_SURGEON_DEDUP_MAX_MERGES_PER_RUN = 100;
|
|
3307
|
+
DEFAULT_SURGEON_CONTRADICTIONS_MAX_ACTIONS_PER_RUN = 100;
|
|
2884
3308
|
}
|
|
2885
|
-
|
|
3309
|
+
});
|
|
3310
|
+
|
|
3311
|
+
// src/shared/utils/source-classification.ts
|
|
3312
|
+
function normalizeSourceToken(sourceFile) {
|
|
3313
|
+
return sourceFile?.trim().toLowerCase() ?? "";
|
|
2886
3314
|
}
|
|
2887
|
-
|
|
2888
|
-
const
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
`,
|
|
2897
|
-
args: [CO_RECALL_EDGE_TYPE, safeLimit]
|
|
2898
|
-
});
|
|
2899
|
-
return result.rows.map((row) => ({
|
|
2900
|
-
entryA: toStringValue(row.entry_a),
|
|
2901
|
-
entryB: toStringValue(row.entry_b),
|
|
2902
|
-
weight: toNumber2(row.weight),
|
|
2903
|
-
sessionCount: toNumber2(row.session_count),
|
|
2904
|
-
lastCoRecalled: toStringValue(row.last_co_recalled)
|
|
2905
|
-
}));
|
|
3315
|
+
function normalizeSourceClass(value) {
|
|
3316
|
+
const normalized = value?.trim().toLowerCase() ?? "";
|
|
3317
|
+
if (!normalized) {
|
|
3318
|
+
return null;
|
|
3319
|
+
}
|
|
3320
|
+
if (normalized === "agent") {
|
|
3321
|
+
return "tool";
|
|
3322
|
+
}
|
|
3323
|
+
return SOURCE_CLASS_SET.has(normalized) ? normalized : null;
|
|
2906
3324
|
}
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
3325
|
+
function isToolSourceFile(sourceFile) {
|
|
3326
|
+
return normalizeSourceToken(sourceFile).startsWith(TOOL_SOURCE_PREFIX);
|
|
3327
|
+
}
|
|
3328
|
+
function classifyEntrySource(sourceFile) {
|
|
3329
|
+
const normalized = normalizeSourceToken(sourceFile);
|
|
3330
|
+
if (!normalized) {
|
|
3331
|
+
return "system";
|
|
3332
|
+
}
|
|
3333
|
+
if (isToolSourceFile(normalized)) {
|
|
3334
|
+
return "tool";
|
|
3335
|
+
}
|
|
3336
|
+
if (SYSTEM_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
3337
|
+
return "system";
|
|
3338
|
+
}
|
|
3339
|
+
if (IMPORT_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
3340
|
+
return "import";
|
|
3341
|
+
}
|
|
3342
|
+
if (CLI_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
3343
|
+
return "cli";
|
|
3344
|
+
}
|
|
3345
|
+
if (WATCHER_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix)) || normalized.includes("/") || normalized.includes("\\")) {
|
|
3346
|
+
return "watcher";
|
|
3347
|
+
}
|
|
3348
|
+
return "system";
|
|
3349
|
+
}
|
|
3350
|
+
function sourceClassTrustLevel(sourceClass) {
|
|
3351
|
+
return sourceClass === "tool" ? "high" : "low";
|
|
3352
|
+
}
|
|
3353
|
+
var TOOL_SOURCE_PREFIX, SOURCE_CLASSES, SOURCE_CLASS_SET, SYSTEM_SOURCE_PREFIXES, CLI_SOURCE_PREFIXES, WATCHER_SOURCE_PREFIXES, IMPORT_SOURCE_PREFIXES;
|
|
3354
|
+
var init_source_classification = __esm({
|
|
3355
|
+
"src/shared/utils/source-classification.ts"() {
|
|
2910
3356
|
"use strict";
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
3357
|
+
TOOL_SOURCE_PREFIX = "stdin:tool";
|
|
3358
|
+
SOURCE_CLASSES = [
|
|
3359
|
+
"tool",
|
|
3360
|
+
"watcher",
|
|
3361
|
+
"cli",
|
|
3362
|
+
"import",
|
|
3363
|
+
"system"
|
|
3364
|
+
];
|
|
3365
|
+
SOURCE_CLASS_SET = new Set(SOURCE_CLASSES);
|
|
3366
|
+
SYSTEM_SOURCE_PREFIXES = [
|
|
3367
|
+
"stdin:migration-",
|
|
3368
|
+
"stdin:system-",
|
|
3369
|
+
"migration:",
|
|
3370
|
+
"system:",
|
|
3371
|
+
"_meta:"
|
|
3372
|
+
];
|
|
3373
|
+
CLI_SOURCE_PREFIXES = [
|
|
3374
|
+
"stdin:",
|
|
3375
|
+
"cli:"
|
|
3376
|
+
];
|
|
3377
|
+
WATCHER_SOURCE_PREFIXES = [
|
|
3378
|
+
"file:",
|
|
3379
|
+
"session:",
|
|
3380
|
+
"conversation"
|
|
3381
|
+
];
|
|
3382
|
+
IMPORT_SOURCE_PREFIXES = [
|
|
3383
|
+
"import:"
|
|
3384
|
+
];
|
|
3385
|
+
}
|
|
3386
|
+
});
|
|
3387
|
+
|
|
3388
|
+
// src/shared/domain/retirement-thresholds.ts
|
|
3389
|
+
function parseIsoTime(value) {
|
|
3390
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
3391
|
+
return null;
|
|
3392
|
+
}
|
|
3393
|
+
const parsed = Date.parse(value);
|
|
3394
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
3395
|
+
}
|
|
3396
|
+
function computeCorpusExposureAgeDays(input) {
|
|
3397
|
+
const createdAtMs = parseIsoTime(input.createdAt);
|
|
3398
|
+
if (createdAtMs === null) {
|
|
3399
|
+
return null;
|
|
3400
|
+
}
|
|
3401
|
+
const rebuildAtMs = parseIsoTime(input.lastCorpusRebuildAt);
|
|
3402
|
+
const exposureStartMs = rebuildAtMs === null ? createdAtMs : Math.max(createdAtMs, rebuildAtMs);
|
|
3403
|
+
const ageDays = (input.now.getTime() - exposureStartMs) / DAY_MS;
|
|
3404
|
+
return Number.isFinite(ageDays) ? Math.max(0, ageDays) : null;
|
|
3405
|
+
}
|
|
3406
|
+
var DAY_MS, DEFAULT_TEMPORARY_ENTRY_TTL_ENABLED, DEFAULT_TEMPORARY_ENTRY_TTL_DAYS, DEFAULT_TEMPORARY_ENTRY_TTL_MAX_IMPORTANCE, DEFAULT_TEMPORARY_ENTRY_TTL_REQUIRE_ZERO_RECALL;
|
|
3407
|
+
var init_retirement_thresholds = __esm({
|
|
3408
|
+
"src/shared/domain/retirement-thresholds.ts"() {
|
|
3409
|
+
"use strict";
|
|
3410
|
+
init_source_classification();
|
|
3411
|
+
DAY_MS = 24 * 60 * 60 * 1e3;
|
|
3412
|
+
DEFAULT_TEMPORARY_ENTRY_TTL_ENABLED = true;
|
|
3413
|
+
DEFAULT_TEMPORARY_ENTRY_TTL_DAYS = 21;
|
|
3414
|
+
DEFAULT_TEMPORARY_ENTRY_TTL_MAX_IMPORTANCE = 6;
|
|
3415
|
+
DEFAULT_TEMPORARY_ENTRY_TTL_REQUIRE_ZERO_RECALL = true;
|
|
3416
|
+
}
|
|
3417
|
+
});
|
|
3418
|
+
|
|
3419
|
+
// src/shared/domain/quality-tiers.ts
|
|
3420
|
+
function qualityTier(score) {
|
|
3421
|
+
if (score >= QUALITY_TIER_HIGH) {
|
|
3422
|
+
return "high";
|
|
3423
|
+
}
|
|
3424
|
+
if (score >= QUALITY_TIER_MEDIUM) {
|
|
3425
|
+
return "medium";
|
|
3426
|
+
}
|
|
3427
|
+
return "low";
|
|
3428
|
+
}
|
|
3429
|
+
var QUALITY_TIER_HIGH, QUALITY_TIER_MEDIUM;
|
|
3430
|
+
var init_quality_tiers = __esm({
|
|
3431
|
+
"src/shared/domain/quality-tiers.ts"() {
|
|
3432
|
+
"use strict";
|
|
3433
|
+
QUALITY_TIER_HIGH = 0.7;
|
|
3434
|
+
QUALITY_TIER_MEDIUM = 0.4;
|
|
2915
3435
|
}
|
|
2916
3436
|
});
|
|
2917
3437
|
|
|
@@ -3134,18 +3654,39 @@ function qualityFloorByType(type) {
|
|
|
3134
3654
|
return 0.35;
|
|
3135
3655
|
}
|
|
3136
3656
|
if (type === "decision" || type === "lesson") {
|
|
3657
|
+
return 0.3;
|
|
3658
|
+
}
|
|
3659
|
+
if (type === "reflection") {
|
|
3660
|
+
return 0.3;
|
|
3661
|
+
}
|
|
3662
|
+
if (type === "relationship") {
|
|
3137
3663
|
return 0.25;
|
|
3138
3664
|
}
|
|
3139
|
-
if (type === "todo" || type === "event"
|
|
3140
|
-
return 0.
|
|
3665
|
+
if (type === "todo" || type === "event") {
|
|
3666
|
+
return 0.2;
|
|
3141
3667
|
}
|
|
3142
|
-
return 0.
|
|
3668
|
+
return 0.2;
|
|
3143
3669
|
}
|
|
3144
|
-
function
|
|
3145
|
-
|
|
3146
|
-
|
|
3670
|
+
function expectedRecallCycleDays(type) {
|
|
3671
|
+
switch (type) {
|
|
3672
|
+
case "preference":
|
|
3673
|
+
return 14;
|
|
3674
|
+
case "fact":
|
|
3675
|
+
return 21;
|
|
3676
|
+
case "decision":
|
|
3677
|
+
case "lesson":
|
|
3678
|
+
return 60;
|
|
3679
|
+
case "event":
|
|
3680
|
+
return 14;
|
|
3681
|
+
case "todo":
|
|
3682
|
+
return 7;
|
|
3683
|
+
case "relationship":
|
|
3684
|
+
return 30;
|
|
3685
|
+
case "reflection":
|
|
3686
|
+
return 90;
|
|
3687
|
+
default:
|
|
3688
|
+
return 30;
|
|
3147
3689
|
}
|
|
3148
|
-
return Math.max(0.3, 1 - (daysSinceLastRecall - 60) / 180);
|
|
3149
3690
|
}
|
|
3150
3691
|
function extractTextFromContent(content, separator) {
|
|
3151
3692
|
if (typeof content === "string") {
|
|
@@ -3378,23 +3919,23 @@ async function evolveQualityScores(db, options = {}, config) {
|
|
|
3378
3919
|
};
|
|
3379
3920
|
}
|
|
3380
3921
|
const qualityEvolutionConfig = config?.scoring?.qualityEvolution;
|
|
3381
|
-
const recallSaturationRaw = qualityEvolutionConfig?.recallSaturation ??
|
|
3382
|
-
const edgeSaturationRaw = qualityEvolutionConfig?.edgeSaturation ??
|
|
3383
|
-
const recallSaturation = Number.isFinite(recallSaturationRaw) && recallSaturationRaw > 0 ? recallSaturationRaw :
|
|
3384
|
-
const edgeSaturation = Number.isFinite(edgeSaturationRaw) && edgeSaturationRaw > 0 ? edgeSaturationRaw :
|
|
3385
|
-
const
|
|
3386
|
-
const
|
|
3387
|
-
const
|
|
3388
|
-
const
|
|
3389
|
-
const
|
|
3390
|
-
const
|
|
3391
|
-
const
|
|
3392
|
-
const
|
|
3393
|
-
const
|
|
3394
|
-
const
|
|
3395
|
-
const
|
|
3396
|
-
const edgeCounts = await getCoRecallEdgeCounts(db);
|
|
3922
|
+
const recallSaturationRaw = qualityEvolutionConfig?.recallSaturation ?? DEFAULT_QUALITY_EVOLUTION_RECALL_SATURATION;
|
|
3923
|
+
const edgeSaturationRaw = qualityEvolutionConfig?.edgeSaturation ?? DEFAULT_QUALITY_EVOLUTION_EDGE_SATURATION;
|
|
3924
|
+
const recallSaturation = Number.isFinite(recallSaturationRaw) && recallSaturationRaw > 0 ? recallSaturationRaw : DEFAULT_QUALITY_EVOLUTION_RECALL_SATURATION;
|
|
3925
|
+
const edgeSaturation = Number.isFinite(edgeSaturationRaw) && edgeSaturationRaw > 0 ? edgeSaturationRaw : DEFAULT_QUALITY_EVOLUTION_EDGE_SATURATION;
|
|
3926
|
+
const recencyHalfLifeDaysRaw = qualityEvolutionConfig?.recencyHalfLifeDays ?? DEFAULT_QUALITY_EVOLUTION_RECENCY_HALF_LIFE_DAYS;
|
|
3927
|
+
const recencyHalfLifeDays = Number.isFinite(recencyHalfLifeDaysRaw) && recencyHalfLifeDaysRaw > 0 ? recencyHalfLifeDaysRaw : DEFAULT_QUALITY_EVOLUTION_RECENCY_HALF_LIFE_DAYS;
|
|
3928
|
+
const neutralFloorRaw = qualityEvolutionConfig?.neutralFloor ?? DEFAULT_QUALITY_EVOLUTION_NEUTRAL_FLOOR;
|
|
3929
|
+
const neutralFloor = Number.isFinite(neutralFloorRaw) ? clamp(neutralFloorRaw, 0, 1) : DEFAULT_QUALITY_EVOLUTION_NEUTRAL_FLOOR;
|
|
3930
|
+
const graceMultiplierRaw = qualityEvolutionConfig?.graceMultiplier ?? DEFAULT_QUALITY_EVOLUTION_GRACE_MULTIPLIER;
|
|
3931
|
+
const graceMultiplier = Number.isFinite(graceMultiplierRaw) && graceMultiplierRaw > 0 ? graceMultiplierRaw : DEFAULT_QUALITY_EVOLUTION_GRACE_MULTIPLIER;
|
|
3932
|
+
const decaySteepnessRaw = qualityEvolutionConfig?.decaySteepness ?? DEFAULT_QUALITY_EVOLUTION_DECAY_STEEPNESS;
|
|
3933
|
+
const decaySteepness = Number.isFinite(decaySteepnessRaw) && decaySteepnessRaw >= 0 ? decaySteepnessRaw : DEFAULT_QUALITY_EVOLUTION_DECAY_STEEPNESS;
|
|
3934
|
+
const emaAlphaRaw = qualityEvolutionConfig?.emaAlpha ?? DEFAULT_QUALITY_EVOLUTION_EMA_ALPHA;
|
|
3935
|
+
const emaAlpha = Number.isFinite(emaAlphaRaw) ? clamp(emaAlphaRaw, 0, 1) : DEFAULT_QUALITY_EVOLUTION_EMA_ALPHA;
|
|
3936
|
+
const edgeCounts = await getCoRecallEdgeCounts(db, CO_RECALL_EDGE_TYPE);
|
|
3397
3937
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
3938
|
+
const lastCorpusRebuildAt = await getLastCorpusRebuildAt(db);
|
|
3398
3939
|
let high = 0;
|
|
3399
3940
|
let medium = 0;
|
|
3400
3941
|
let low = 0;
|
|
@@ -3402,33 +3943,60 @@ async function evolveQualityScores(db, options = {}, config) {
|
|
|
3402
3943
|
let totalAfter = 0;
|
|
3403
3944
|
const updates = [];
|
|
3404
3945
|
for (const row of rows) {
|
|
3405
|
-
const recallSignal = row.recallCount > 0 ? clamp(Math.log1p(row.recallCount) / Math.log1p(recallSaturation), 0, 1) : 0;
|
|
3406
3946
|
const edgeCount = edgeCounts.get(row.id) ?? 0;
|
|
3407
|
-
const edgeSignal = edgeCount > 0 ? clamp(Math.log1p(edgeCount) / Math.log1p(edgeSaturation), 0, 1) : 0;
|
|
3408
|
-
const confirmSignal = clamp(row.confirmations / 3, 0, 1);
|
|
3409
|
-
const importanceNormalized = clamp(row.importance / 10, 0, 1);
|
|
3410
|
-
const sourceDate = row.lastRecalledAt || row.createdAt;
|
|
3411
|
-
const daysSinceLastRecall = parseDaysBetween(now, sourceDate || void 0);
|
|
3412
|
-
const decayPenalty = row.recallCount > 0 ? computeDecayPenalty(daysSinceLastRecall) : Math.max(neverRecalledDecayFloor, computeDecayPenalty(daysSinceLastRecall));
|
|
3413
|
-
const rawQuality = (recallWeight * recallSignal + edgeWeight * edgeSignal + confirmWeight * confirmSignal + importanceWeight * importanceNormalized) * decayPenalty;
|
|
3414
|
-
const evolved = clamp(rawQuality, 0.05, 1);
|
|
3415
3947
|
const existingQuality = clamp(Number.isFinite(row.qualityScore) ? row.qualityScore : 0.5, 0, 1);
|
|
3416
|
-
const
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
const
|
|
3422
|
-
const
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3948
|
+
const effectiveObservationDays = computeCorpusExposureAgeDays({
|
|
3949
|
+
createdAt: row.createdAt,
|
|
3950
|
+
now,
|
|
3951
|
+
lastCorpusRebuildAt
|
|
3952
|
+
}) ?? 0;
|
|
3953
|
+
const expectedCycleDays = expectedRecallCycleDays(row.type);
|
|
3954
|
+
const graceWindowDays = expectedCycleDays * graceMultiplier;
|
|
3955
|
+
const inGracePeriod = effectiveObservationDays < graceWindowDays;
|
|
3956
|
+
const hasAnyPositiveSignal = row.recallCount > 0 || row.confirmations > 0 || edgeCount > 0;
|
|
3957
|
+
let finalQuality = existingQuality;
|
|
3958
|
+
if (inGracePeriod && !hasAnyPositiveSignal) {
|
|
3959
|
+
finalQuality = existingQuality;
|
|
3960
|
+
} else if (!hasAnyPositiveSignal) {
|
|
3961
|
+
const importanceDampener = 1 + clamp(row.importance / 10, 0, 1) * 2;
|
|
3962
|
+
const excessDays = Math.max(0, effectiveObservationDays - graceWindowDays);
|
|
3963
|
+
const baselineQuality = Math.max(existingQuality, neutralFloor);
|
|
3964
|
+
const decayRate = excessDays / (expectedCycleDays * importanceDampener);
|
|
3965
|
+
const decayFactor = Math.exp(-decayRate * decaySteepness);
|
|
3966
|
+
finalQuality = neutralFloor + (baselineQuality - neutralFloor) * decayFactor;
|
|
3429
3967
|
} else {
|
|
3430
|
-
|
|
3431
|
-
|
|
3968
|
+
const recallSignal = row.recallCount > 0 ? clamp(Math.log1p(row.recallCount) / Math.log1p(recallSaturation), 0, 1) : 0;
|
|
3969
|
+
const daysSinceLastRecall = row.lastRecalledAt ? parseDaysBetween(now, row.lastRecalledAt) : Number.POSITIVE_INFINITY;
|
|
3970
|
+
const recencySignal = Number.isFinite(daysSinceLastRecall) ? Math.exp(-daysSinceLastRecall / recencyHalfLifeDays) : 0;
|
|
3971
|
+
const confirmSignal = clamp(row.confirmations / 3, 0, 1);
|
|
3972
|
+
const edgeSignal = edgeCount > 0 ? clamp(Math.log1p(edgeCount) / Math.log1p(edgeSaturation), 0, 1) : 0;
|
|
3973
|
+
const rawEvidence = clamp(
|
|
3974
|
+
EVIDENCE_WEIGHTS.recall * recallSignal + EVIDENCE_WEIGHTS.recency * recencySignal + EVIDENCE_WEIGHTS.confirm * confirmSignal + EVIDENCE_WEIGHTS.edge * edgeSignal,
|
|
3975
|
+
0,
|
|
3976
|
+
1
|
|
3977
|
+
);
|
|
3978
|
+
const evidenceQuality = clamp(
|
|
3979
|
+
EVIDENCE_QUALITY_BASELINE + rawEvidence * EVIDENCE_QUALITY_RANGE,
|
|
3980
|
+
EVIDENCE_QUALITY_BASELINE,
|
|
3981
|
+
0.95
|
|
3982
|
+
);
|
|
3983
|
+
const blendedEvidence = emaAlpha * evidenceQuality + (1 - emaAlpha) * existingQuality;
|
|
3984
|
+
finalQuality = Math.max(existingQuality, blendedEvidence);
|
|
3985
|
+
}
|
|
3986
|
+
finalQuality = clamp(Math.max(finalQuality, qualityFloorByType(row.type)), 0.1, 0.98);
|
|
3987
|
+
totalBefore += existingQuality;
|
|
3988
|
+
totalAfter += finalQuality;
|
|
3989
|
+
switch (qualityTier(finalQuality)) {
|
|
3990
|
+
case "high":
|
|
3991
|
+
high += 1;
|
|
3992
|
+
break;
|
|
3993
|
+
case "medium":
|
|
3994
|
+
medium += 1;
|
|
3995
|
+
break;
|
|
3996
|
+
default:
|
|
3997
|
+
low += 1;
|
|
3998
|
+
break;
|
|
3999
|
+
}
|
|
3432
4000
|
updates.push({ id: row.id, qualityScore: finalQuality });
|
|
3433
4001
|
}
|
|
3434
4002
|
if (updates.length > 0) {
|
|
@@ -3565,12 +4133,16 @@ async function computeRecallFeedback(db, sessionKey, messages, recalledEntryIds,
|
|
|
3565
4133
|
updatedIds: updates.map((update) => update.id)
|
|
3566
4134
|
};
|
|
3567
4135
|
}
|
|
3568
|
-
var RESPONSE_MIN_CHARS, RESPONSE_MAX_CHARS;
|
|
4136
|
+
var RESPONSE_MIN_CHARS, RESPONSE_MAX_CHARS, EVIDENCE_QUALITY_BASELINE, EVIDENCE_QUALITY_RANGE, EVIDENCE_WEIGHTS;
|
|
3569
4137
|
var init_feedback = __esm({
|
|
3570
4138
|
"src/shared/infrastructure/db/feedback.ts"() {
|
|
3571
4139
|
"use strict";
|
|
3572
4140
|
init_client2();
|
|
4141
|
+
init_schema2();
|
|
4142
|
+
init_retirement_thresholds();
|
|
4143
|
+
init_quality_tiers();
|
|
3573
4144
|
init_co_recall();
|
|
4145
|
+
init_meta();
|
|
3574
4146
|
init_metrics();
|
|
3575
4147
|
init_entry_utils();
|
|
3576
4148
|
init_errors();
|
|
@@ -3578,6 +4150,14 @@ var init_feedback = __esm({
|
|
|
3578
4150
|
init_vector();
|
|
3579
4151
|
RESPONSE_MIN_CHARS = 50;
|
|
3580
4152
|
RESPONSE_MAX_CHARS = 8e3;
|
|
4153
|
+
EVIDENCE_QUALITY_BASELINE = 0.45;
|
|
4154
|
+
EVIDENCE_QUALITY_RANGE = 0.5;
|
|
4155
|
+
EVIDENCE_WEIGHTS = {
|
|
4156
|
+
recall: 0.35,
|
|
4157
|
+
recency: 0.25,
|
|
4158
|
+
confirm: 0.2,
|
|
4159
|
+
edge: 0.2
|
|
4160
|
+
};
|
|
3581
4161
|
}
|
|
3582
4162
|
});
|
|
3583
4163
|
|
|
@@ -3867,144 +4447,6 @@ var init_stream = __esm({
|
|
|
3867
4447
|
}
|
|
3868
4448
|
});
|
|
3869
4449
|
|
|
3870
|
-
// src/shared/infrastructure/config/schema.ts
|
|
3871
|
-
import os3 from "os";
|
|
3872
|
-
import path3 from "path";
|
|
3873
|
-
function resolveClaimExtractionBatchSize(value) {
|
|
3874
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
3875
|
-
return DEFAULT_CLAIM_EXTRACTION_BATCH_SIZE;
|
|
3876
|
-
}
|
|
3877
|
-
return Math.min(Math.floor(value), MAX_CLAIM_EXTRACTION_BATCH_SIZE);
|
|
3878
|
-
}
|
|
3879
|
-
function resolveClaimExtractionConcurrency(value) {
|
|
3880
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
3881
|
-
return DEFAULT_CLAIM_EXTRACTION_CONCURRENCY;
|
|
3882
|
-
}
|
|
3883
|
-
return Math.min(Math.floor(value), MAX_CLAIM_EXTRACTION_CONCURRENCY);
|
|
3884
|
-
}
|
|
3885
|
-
function isModelTask(value) {
|
|
3886
|
-
return MODEL_TASK_KEYS.includes(value);
|
|
3887
|
-
}
|
|
3888
|
-
function isScoringSetKey(value) {
|
|
3889
|
-
return SCORING_SET_KEYS.includes(value);
|
|
3890
|
-
}
|
|
3891
|
-
function isAgenrAuthMethod(value) {
|
|
3892
|
-
return AUTH_METHOD_SET.has(value);
|
|
3893
|
-
}
|
|
3894
|
-
function resolveDefaultKnowledgeDbPath() {
|
|
3895
|
-
return path3.join(os3.homedir(), ".agenr", "knowledge.db");
|
|
3896
|
-
}
|
|
3897
|
-
var SCORING_SET_KEYS, AUTH_METHOD_DEFINITIONS, AUTH_METHOD_SET, MODEL_TASK_KEYS, DEFAULT_EMBEDDING_PROVIDER, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_DIMENSIONS, DEFAULT_FORGETTING_SCORE_THRESHOLD, DEFAULT_FORGETTING_MAX_AGE_DAYS, DEFAULT_FORGETTING_LOW_SCORE_TRIGGER_COUNT, DEFAULT_SCORING_FTS_BOOST, DEFAULT_SCORING_VECTOR_EXPONENT, DEFAULT_SCORING_IMPORTANCE_FLOOR, DEFAULT_SCORING_FRESHNESS_HIGH_HOURS, DEFAULT_SCORING_FRESHNESS_MEDIUM_HOURS, DEFAULT_SCORING_FRESHNESS_LOW_HOURS, DEFAULT_SCORING_RECENCY_HALFLIFE_PERMANENT, DEFAULT_SCORING_RECENCY_HALFLIFE_TEMPORARY, DEFAULT_SCORING_FRESHNESS_MIN_IMPORTANCE, DEFAULT_SCORING_CONTRADICTION_PENALTY, DEFAULT_SCORING_QUALITY_MIN, DEFAULT_SCORING_QUALITY_MAX, DEFAULT_SCORING_IMPORTANCE_BLEND, DEFAULT_SCORING_RECALL_BLEND, DEFAULT_SCORING_TODO_HALFLIFE_DAYS, DEFAULT_SCORING_TODO_FLOOR_HIGH, DEFAULT_SCORING_TODO_FLOOR_LOW, DEFAULT_CONTRADICTION_ENABLED, DEFAULT_TASK_MODEL, DEFAULT_SURGEON_MODEL, DEFAULT_AUTO_SUPERSEDE_CONFIDENCE, DEFAULT_CONTRADICTION_BLOCKING_THRESHOLD, DEFAULT_CLAIM_EXTRACTION_BATCH_SIZE, MAX_CLAIM_EXTRACTION_BATCH_SIZE, DEFAULT_CLAIM_EXTRACTION_CONCURRENCY, MAX_CLAIM_EXTRACTION_CONCURRENCY, DEFAULT_SURGEON_CONTEXT_LIMIT, DEFAULT_SURGEON_COST_CAP, DEFAULT_SURGEON_DAILY_COST_CAP, DEFAULT_SURGEON_RETIREMENT_PROTECT_RECALLED_DAYS, DEFAULT_SURGEON_RETIREMENT_PROTECT_MIN_IMPORTANCE, DEFAULT_SURGEON_RETIREMENT_MAX_ACTIONS_PER_RUN, DEFAULT_SURGEON_SKIP_RECENTLY_EVALUATED_DAYS, DEFAULT_SURGEON_DEDUP_MAX_MERGES_PER_RUN, DEFAULT_SURGEON_CONTRADICTIONS_MAX_ACTIONS_PER_RUN;
|
|
3898
|
-
var init_schema2 = __esm({
|
|
3899
|
-
"src/shared/infrastructure/config/schema.ts"() {
|
|
3900
|
-
"use strict";
|
|
3901
|
-
SCORING_SET_KEYS = [
|
|
3902
|
-
"scoring.ftsBoost",
|
|
3903
|
-
"scoring.vectorExponent",
|
|
3904
|
-
"scoring.importanceFloor",
|
|
3905
|
-
"scoring.freshnessWindow.high",
|
|
3906
|
-
"scoring.freshnessWindow.medium",
|
|
3907
|
-
"scoring.freshnessWindow.low",
|
|
3908
|
-
"scoring.recencyHalfLife.permanent",
|
|
3909
|
-
"scoring.recencyHalfLife.temporary",
|
|
3910
|
-
"scoring.freshnessMinImportance",
|
|
3911
|
-
"scoring.contradictionPenalty",
|
|
3912
|
-
"scoring.qualityRange.min",
|
|
3913
|
-
"scoring.qualityRange.max",
|
|
3914
|
-
"scoring.importanceBlend.importance",
|
|
3915
|
-
"scoring.importanceBlend.recall",
|
|
3916
|
-
"scoring.todoPenalty.halfLifeDays",
|
|
3917
|
-
"scoring.todoPenalty.floorHigh",
|
|
3918
|
-
"scoring.todoPenalty.floorLow"
|
|
3919
|
-
];
|
|
3920
|
-
AUTH_METHOD_DEFINITIONS = [
|
|
3921
|
-
{
|
|
3922
|
-
id: "anthropic-oauth",
|
|
3923
|
-
provider: "anthropic",
|
|
3924
|
-
title: "Anthropic - Claude subscription (OAuth)",
|
|
3925
|
-
setupDescription: "Uses your Claude Pro/Team subscription via Claude CLI credentials. No per-token cost. Requires Claude Code CLI.",
|
|
3926
|
-
preferredModels: ["claude-opus-4-6", "claude-sonnet-4-20250514", "claude-haiku-3-5-20241022"]
|
|
3927
|
-
},
|
|
3928
|
-
{
|
|
3929
|
-
id: "anthropic-token",
|
|
3930
|
-
provider: "anthropic",
|
|
3931
|
-
title: "Anthropic - Claude subscription (long-lived token)",
|
|
3932
|
-
setupDescription: "Uses a long-lived token from `claude setup-token`. No per-token cost. Simpler setup.",
|
|
3933
|
-
preferredModels: ["claude-opus-4-6", "claude-sonnet-4-20250514", "claude-haiku-3-5-20241022"]
|
|
3934
|
-
},
|
|
3935
|
-
{
|
|
3936
|
-
id: "anthropic-api-key",
|
|
3937
|
-
provider: "anthropic",
|
|
3938
|
-
title: "Anthropic - API key",
|
|
3939
|
-
setupDescription: "Standard API key from console.anthropic.com. Pay per token.",
|
|
3940
|
-
preferredModels: ["claude-sonnet-4-20250514", "claude-opus-4-6", "claude-haiku-3-5-20241022"]
|
|
3941
|
-
},
|
|
3942
|
-
{
|
|
3943
|
-
id: "openai-subscription",
|
|
3944
|
-
provider: "openai-codex",
|
|
3945
|
-
title: "OpenAI - Subscription (via Codex CLI)",
|
|
3946
|
-
setupDescription: "Uses your ChatGPT Plus subscription via Codex CLI credentials. No per-token cost. Requires Codex CLI.",
|
|
3947
|
-
preferredModels: ["gpt-5.3-codex", "o3-codex"]
|
|
3948
|
-
},
|
|
3949
|
-
{
|
|
3950
|
-
id: "openai-api-key",
|
|
3951
|
-
provider: "openai",
|
|
3952
|
-
title: "OpenAI - API key",
|
|
3953
|
-
setupDescription: "Standard API key from https://platform.openai.com/api-keys. Pay per token.",
|
|
3954
|
-
preferredModels: ["gpt-5.4-mini", "gpt-5.4", "gpt-4.1-mini"]
|
|
3955
|
-
}
|
|
3956
|
-
];
|
|
3957
|
-
AUTH_METHOD_SET = new Set(AUTH_METHOD_DEFINITIONS.map((item) => item.id));
|
|
3958
|
-
MODEL_TASK_KEYS = [
|
|
3959
|
-
"extraction",
|
|
3960
|
-
"claimExtraction",
|
|
3961
|
-
"contradictionJudge",
|
|
3962
|
-
"handoffSummary"
|
|
3963
|
-
];
|
|
3964
|
-
DEFAULT_EMBEDDING_PROVIDER = "openai";
|
|
3965
|
-
DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small";
|
|
3966
|
-
DEFAULT_EMBEDDING_DIMENSIONS = 1024;
|
|
3967
|
-
DEFAULT_FORGETTING_SCORE_THRESHOLD = 0.05;
|
|
3968
|
-
DEFAULT_FORGETTING_MAX_AGE_DAYS = 60;
|
|
3969
|
-
DEFAULT_FORGETTING_LOW_SCORE_TRIGGER_COUNT = 200;
|
|
3970
|
-
DEFAULT_SCORING_FTS_BOOST = 1.3;
|
|
3971
|
-
DEFAULT_SCORING_VECTOR_EXPONENT = 0.7;
|
|
3972
|
-
DEFAULT_SCORING_IMPORTANCE_FLOOR = 0.55;
|
|
3973
|
-
DEFAULT_SCORING_FRESHNESS_HIGH_HOURS = 1;
|
|
3974
|
-
DEFAULT_SCORING_FRESHNESS_MEDIUM_HOURS = 6;
|
|
3975
|
-
DEFAULT_SCORING_FRESHNESS_LOW_HOURS = 24;
|
|
3976
|
-
DEFAULT_SCORING_RECENCY_HALFLIFE_PERMANENT = 365;
|
|
3977
|
-
DEFAULT_SCORING_RECENCY_HALFLIFE_TEMPORARY = 30;
|
|
3978
|
-
DEFAULT_SCORING_FRESHNESS_MIN_IMPORTANCE = 6;
|
|
3979
|
-
DEFAULT_SCORING_CONTRADICTION_PENALTY = 0.8;
|
|
3980
|
-
DEFAULT_SCORING_QUALITY_MIN = 0.7;
|
|
3981
|
-
DEFAULT_SCORING_QUALITY_MAX = 1.3;
|
|
3982
|
-
DEFAULT_SCORING_IMPORTANCE_BLEND = 0.7;
|
|
3983
|
-
DEFAULT_SCORING_RECALL_BLEND = 0.3;
|
|
3984
|
-
DEFAULT_SCORING_TODO_HALFLIFE_DAYS = 7;
|
|
3985
|
-
DEFAULT_SCORING_TODO_FLOOR_HIGH = 0.4;
|
|
3986
|
-
DEFAULT_SCORING_TODO_FLOOR_LOW = 0.1;
|
|
3987
|
-
DEFAULT_CONTRADICTION_ENABLED = true;
|
|
3988
|
-
DEFAULT_TASK_MODEL = "gpt-5.4-mini";
|
|
3989
|
-
DEFAULT_SURGEON_MODEL = "gpt-5.4";
|
|
3990
|
-
DEFAULT_AUTO_SUPERSEDE_CONFIDENCE = 0.85;
|
|
3991
|
-
DEFAULT_CONTRADICTION_BLOCKING_THRESHOLD = 0.85;
|
|
3992
|
-
DEFAULT_CLAIM_EXTRACTION_BATCH_SIZE = 10;
|
|
3993
|
-
MAX_CLAIM_EXTRACTION_BATCH_SIZE = 25;
|
|
3994
|
-
DEFAULT_CLAIM_EXTRACTION_CONCURRENCY = 5;
|
|
3995
|
-
MAX_CLAIM_EXTRACTION_CONCURRENCY = 50;
|
|
3996
|
-
DEFAULT_SURGEON_CONTEXT_LIMIT = null;
|
|
3997
|
-
DEFAULT_SURGEON_COST_CAP = 5;
|
|
3998
|
-
DEFAULT_SURGEON_DAILY_COST_CAP = 15;
|
|
3999
|
-
DEFAULT_SURGEON_RETIREMENT_PROTECT_RECALLED_DAYS = 14;
|
|
4000
|
-
DEFAULT_SURGEON_RETIREMENT_PROTECT_MIN_IMPORTANCE = 8;
|
|
4001
|
-
DEFAULT_SURGEON_RETIREMENT_MAX_ACTIONS_PER_RUN = 100;
|
|
4002
|
-
DEFAULT_SURGEON_SKIP_RECENTLY_EVALUATED_DAYS = 7;
|
|
4003
|
-
DEFAULT_SURGEON_DEDUP_MAX_MERGES_PER_RUN = 100;
|
|
4004
|
-
DEFAULT_SURGEON_CONTRADICTIONS_MAX_ACTIONS_PER_RUN = 100;
|
|
4005
|
-
}
|
|
4006
|
-
});
|
|
4007
|
-
|
|
4008
4450
|
// src/shared/infrastructure/llm/models.ts
|
|
4009
4451
|
import { getModel } from "@mariozechner/pi-ai";
|
|
4010
4452
|
function isAgenrProvider(value) {
|
|
@@ -4489,8 +4931,7 @@ function normalizeForgettingConfig(input) {
|
|
|
4489
4931
|
protect: [],
|
|
4490
4932
|
scoreThreshold: DEFAULT_FORGETTING_SCORE_THRESHOLD,
|
|
4491
4933
|
maxAgeDays: DEFAULT_FORGETTING_MAX_AGE_DAYS,
|
|
4492
|
-
enabled: true
|
|
4493
|
-
lowScoreTriggerCount: DEFAULT_FORGETTING_LOW_SCORE_TRIGGER_COUNT
|
|
4934
|
+
enabled: true
|
|
4494
4935
|
};
|
|
4495
4936
|
if (!input || typeof input !== "object") {
|
|
4496
4937
|
return normalized;
|
|
@@ -4508,8 +4949,33 @@ function normalizeForgettingConfig(input) {
|
|
|
4508
4949
|
if (typeof record.enabled === "boolean") {
|
|
4509
4950
|
normalized.enabled = record.enabled;
|
|
4510
4951
|
}
|
|
4511
|
-
if (typeof record.
|
|
4512
|
-
normalized.
|
|
4952
|
+
if (typeof record.qualityPenaltyWeight === "number" && Number.isFinite(record.qualityPenaltyWeight) && record.qualityPenaltyWeight >= 0) {
|
|
4953
|
+
normalized.qualityPenaltyWeight = record.qualityPenaltyWeight;
|
|
4954
|
+
}
|
|
4955
|
+
return normalized;
|
|
4956
|
+
}
|
|
4957
|
+
function normalizeTtlConfig(input) {
|
|
4958
|
+
const normalized = {
|
|
4959
|
+
enabled: DEFAULT_TEMPORARY_ENTRY_TTL_ENABLED,
|
|
4960
|
+
temporaryMaxDays: DEFAULT_TEMPORARY_ENTRY_TTL_DAYS,
|
|
4961
|
+
temporaryMaxImportance: DEFAULT_TEMPORARY_ENTRY_TTL_MAX_IMPORTANCE,
|
|
4962
|
+
requireZeroRecall: DEFAULT_TEMPORARY_ENTRY_TTL_REQUIRE_ZERO_RECALL
|
|
4963
|
+
};
|
|
4964
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
4965
|
+
return normalized;
|
|
4966
|
+
}
|
|
4967
|
+
const record = input;
|
|
4968
|
+
if (typeof record.enabled === "boolean") {
|
|
4969
|
+
normalized.enabled = record.enabled;
|
|
4970
|
+
}
|
|
4971
|
+
if (typeof record.temporaryMaxDays === "number" && Number.isFinite(record.temporaryMaxDays) && record.temporaryMaxDays > 0) {
|
|
4972
|
+
normalized.temporaryMaxDays = Math.floor(record.temporaryMaxDays);
|
|
4973
|
+
}
|
|
4974
|
+
if (typeof record.temporaryMaxImportance === "number" && Number.isFinite(record.temporaryMaxImportance) && record.temporaryMaxImportance >= 1 && record.temporaryMaxImportance <= 10) {
|
|
4975
|
+
normalized.temporaryMaxImportance = Math.floor(record.temporaryMaxImportance);
|
|
4976
|
+
}
|
|
4977
|
+
if (typeof record.requireZeroRecall === "boolean") {
|
|
4978
|
+
normalized.requireZeroRecall = record.requireZeroRecall;
|
|
4513
4979
|
}
|
|
4514
4980
|
return normalized;
|
|
4515
4981
|
}
|
|
@@ -4849,6 +5315,7 @@ function normalizeConfig(input) {
|
|
|
4849
5315
|
embedding: normalizeEmbeddingConfig(record.embedding),
|
|
4850
5316
|
db: normalizeDbConfig(record.db),
|
|
4851
5317
|
forgetting: normalizeForgettingConfig(record.forgetting),
|
|
5318
|
+
ttl: normalizeTtlConfig(record.ttl),
|
|
4852
5319
|
scoring: normalizeScoringConfig(record.scoring),
|
|
4853
5320
|
contradiction: normalizeContradictionConfig(record.contradiction),
|
|
4854
5321
|
surgeon: normalizeSurgeonConfig(record.surgeon)
|
|
@@ -4894,6 +5361,7 @@ function normalizeConfig(input) {
|
|
|
4894
5361
|
var init_normalize = __esm({
|
|
4895
5362
|
"src/shared/infrastructure/config/normalize.ts"() {
|
|
4896
5363
|
"use strict";
|
|
5364
|
+
init_retirement_thresholds();
|
|
4897
5365
|
init_fs();
|
|
4898
5366
|
init_string();
|
|
4899
5367
|
init_models();
|
|
@@ -4936,6 +5404,12 @@ function mergeConfigPatch(current, patch) {
|
|
|
4936
5404
|
...patch.forgetting ?? {}
|
|
4937
5405
|
};
|
|
4938
5406
|
}
|
|
5407
|
+
if (current?.ttl || patch.ttl) {
|
|
5408
|
+
merged.ttl = {
|
|
5409
|
+
...current?.ttl ?? {},
|
|
5410
|
+
...patch.ttl ?? {}
|
|
5411
|
+
};
|
|
5412
|
+
}
|
|
4939
5413
|
if (current?.scoring || patch.scoring) {
|
|
4940
5414
|
merged.scoring = {
|
|
4941
5415
|
...current?.scoring ?? {},
|
|
@@ -5893,83 +6367,6 @@ var init_openclaw = __esm({
|
|
|
5893
6367
|
}
|
|
5894
6368
|
});
|
|
5895
6369
|
|
|
5896
|
-
// src/shared/utils/source-classification.ts
|
|
5897
|
-
function normalizeSourceToken(sourceFile) {
|
|
5898
|
-
return sourceFile?.trim().toLowerCase() ?? "";
|
|
5899
|
-
}
|
|
5900
|
-
function normalizeSourceClass(value) {
|
|
5901
|
-
const normalized = value?.trim().toLowerCase() ?? "";
|
|
5902
|
-
if (!normalized) {
|
|
5903
|
-
return null;
|
|
5904
|
-
}
|
|
5905
|
-
if (normalized === "agent") {
|
|
5906
|
-
return "tool";
|
|
5907
|
-
}
|
|
5908
|
-
return SOURCE_CLASS_SET.has(normalized) ? normalized : null;
|
|
5909
|
-
}
|
|
5910
|
-
function isToolSourceFile(sourceFile) {
|
|
5911
|
-
return normalizeSourceToken(sourceFile).startsWith(TOOL_SOURCE_PREFIX);
|
|
5912
|
-
}
|
|
5913
|
-
function classifyEntrySource(sourceFile) {
|
|
5914
|
-
const normalized = normalizeSourceToken(sourceFile);
|
|
5915
|
-
if (!normalized) {
|
|
5916
|
-
return "system";
|
|
5917
|
-
}
|
|
5918
|
-
if (isToolSourceFile(normalized)) {
|
|
5919
|
-
return "tool";
|
|
5920
|
-
}
|
|
5921
|
-
if (SYSTEM_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
5922
|
-
return "system";
|
|
5923
|
-
}
|
|
5924
|
-
if (IMPORT_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
5925
|
-
return "import";
|
|
5926
|
-
}
|
|
5927
|
-
if (CLI_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {
|
|
5928
|
-
return "cli";
|
|
5929
|
-
}
|
|
5930
|
-
if (WATCHER_SOURCE_PREFIXES.some((prefix) => normalized.startsWith(prefix)) || normalized.includes("/") || normalized.includes("\\")) {
|
|
5931
|
-
return "watcher";
|
|
5932
|
-
}
|
|
5933
|
-
return "system";
|
|
5934
|
-
}
|
|
5935
|
-
function sourceClassTrustLevel(sourceClass) {
|
|
5936
|
-
return sourceClass === "tool" ? "high" : "low";
|
|
5937
|
-
}
|
|
5938
|
-
var TOOL_SOURCE_PREFIX, SOURCE_CLASSES, SOURCE_CLASS_SET, SYSTEM_SOURCE_PREFIXES, CLI_SOURCE_PREFIXES, WATCHER_SOURCE_PREFIXES, IMPORT_SOURCE_PREFIXES;
|
|
5939
|
-
var init_source_classification = __esm({
|
|
5940
|
-
"src/shared/utils/source-classification.ts"() {
|
|
5941
|
-
"use strict";
|
|
5942
|
-
TOOL_SOURCE_PREFIX = "stdin:tool";
|
|
5943
|
-
SOURCE_CLASSES = [
|
|
5944
|
-
"tool",
|
|
5945
|
-
"watcher",
|
|
5946
|
-
"cli",
|
|
5947
|
-
"import",
|
|
5948
|
-
"system"
|
|
5949
|
-
];
|
|
5950
|
-
SOURCE_CLASS_SET = new Set(SOURCE_CLASSES);
|
|
5951
|
-
SYSTEM_SOURCE_PREFIXES = [
|
|
5952
|
-
"stdin:migration-",
|
|
5953
|
-
"stdin:system-",
|
|
5954
|
-
"migration:",
|
|
5955
|
-
"system:",
|
|
5956
|
-
"_meta:"
|
|
5957
|
-
];
|
|
5958
|
-
CLI_SOURCE_PREFIXES = [
|
|
5959
|
-
"stdin:",
|
|
5960
|
-
"cli:"
|
|
5961
|
-
];
|
|
5962
|
-
WATCHER_SOURCE_PREFIXES = [
|
|
5963
|
-
"file:",
|
|
5964
|
-
"session:",
|
|
5965
|
-
"conversation"
|
|
5966
|
-
];
|
|
5967
|
-
IMPORT_SOURCE_PREFIXES = [
|
|
5968
|
-
"import:"
|
|
5969
|
-
];
|
|
5970
|
-
}
|
|
5971
|
-
});
|
|
5972
|
-
|
|
5973
6370
|
// src/shared/infrastructure/db/stored-entry.ts
|
|
5974
6371
|
function mapRawStoredEntry(row, options) {
|
|
5975
6372
|
const canonicalKey = toStringValue(row.canonical_key).trim();
|
|
@@ -6212,9 +6609,9 @@ async function updateRecallMetadata(db, ids, now, options) {
|
|
|
6212
6609
|
const previous = recallMetadataQueue.get(db) ?? Promise.resolve();
|
|
6213
6610
|
const current = previous.catch(() => void 0).then(async () => {
|
|
6214
6611
|
const nowIso = now.toISOString();
|
|
6215
|
-
const sessionId = options?.sessionId
|
|
6216
|
-
const platform = options?.platform
|
|
6217
|
-
const project = options?.project
|
|
6612
|
+
const sessionId = options?.sessionId?.trim() || null;
|
|
6613
|
+
const platform = options?.platform?.trim() || null;
|
|
6614
|
+
const project = options?.project?.trim() || null;
|
|
6218
6615
|
const runUpdates = async () => {
|
|
6219
6616
|
for (const id of ids) {
|
|
6220
6617
|
const entryRow = await db.execute({
|
|
@@ -6968,7 +7365,7 @@ var init_entry_utils2 = __esm({
|
|
|
6968
7365
|
|
|
6969
7366
|
// src/modules/recall/domain/types.ts
|
|
6970
7367
|
var DEFAULT_VECTOR_CANDIDATE_LIMIT, DEFAULT_LEXICAL_CANDIDATE_LIMIT, DEFAULT_LIMIT, DEFAULT_AROUND_RADIUS_DAYS, DEFAULT_RERANK_WINDOW;
|
|
6971
|
-
var
|
|
7368
|
+
var init_types2 = __esm({
|
|
6972
7369
|
"src/modules/recall/domain/types.ts"() {
|
|
6973
7370
|
"use strict";
|
|
6974
7371
|
DEFAULT_VECTOR_CANDIDATE_LIMIT = 50;
|
|
@@ -7137,7 +7534,7 @@ var init_score = __esm({
|
|
|
7137
7534
|
init_project2();
|
|
7138
7535
|
init_source_classification2();
|
|
7139
7536
|
init_entry_utils2();
|
|
7140
|
-
|
|
7537
|
+
init_types2();
|
|
7141
7538
|
IMPORTANCE_FLOOR = 0.55;
|
|
7142
7539
|
RECENCY_HALFLIFE_PERMANENT_DAYS = 365;
|
|
7143
7540
|
RECENCY_HALFLIFE_TEMPORARY_DAYS = 30;
|
|
@@ -7973,7 +8370,7 @@ var init_preparation = __esm({
|
|
|
7973
8370
|
init_query_normalization();
|
|
7974
8371
|
init_scope();
|
|
7975
8372
|
init_score();
|
|
7976
|
-
|
|
8373
|
+
init_types2();
|
|
7977
8374
|
}
|
|
7978
8375
|
});
|
|
7979
8376
|
|
|
@@ -8360,7 +8757,7 @@ var init_rerank = __esm({
|
|
|
8360
8757
|
init_lexical();
|
|
8361
8758
|
init_rerank_intent();
|
|
8362
8759
|
init_text();
|
|
8363
|
-
|
|
8760
|
+
init_types2();
|
|
8364
8761
|
QUERY_PHRASE_WINDOW = 3;
|
|
8365
8762
|
}
|
|
8366
8763
|
});
|
|
@@ -8464,7 +8861,7 @@ async function finalizeRecallResults(params) {
|
|
|
8464
8861
|
var init_finalize = __esm({
|
|
8465
8862
|
"src/modules/recall/adapters/pipeline/finalize.ts"() {
|
|
8466
8863
|
"use strict";
|
|
8467
|
-
|
|
8864
|
+
init_types2();
|
|
8468
8865
|
}
|
|
8469
8866
|
});
|
|
8470
8867
|
|
|
@@ -9130,7 +9527,7 @@ var init_retrieval = __esm({
|
|
|
9130
9527
|
init_candidate_scoring();
|
|
9131
9528
|
init_entry_filters();
|
|
9132
9529
|
init_lexical();
|
|
9133
|
-
|
|
9530
|
+
init_types2();
|
|
9134
9531
|
init_session_start();
|
|
9135
9532
|
init_candidates();
|
|
9136
9533
|
init_helpers();
|
|
@@ -9202,7 +9599,7 @@ var init_outbound = __esm({
|
|
|
9202
9599
|
init_retrieval();
|
|
9203
9600
|
init_helpers();
|
|
9204
9601
|
init_score();
|
|
9205
|
-
|
|
9602
|
+
init_types2();
|
|
9206
9603
|
init_score();
|
|
9207
9604
|
init_score();
|
|
9208
9605
|
init_session_start();
|
|
@@ -9333,7 +9730,7 @@ var init_recall = __esm({
|
|
|
9333
9730
|
init_rerank();
|
|
9334
9731
|
init_trace();
|
|
9335
9732
|
init_outbound();
|
|
9336
|
-
|
|
9733
|
+
init_types2();
|
|
9337
9734
|
}
|
|
9338
9735
|
});
|
|
9339
9736
|
|
|
@@ -12649,30 +13046,6 @@ var init_jsonl_generic = __esm({
|
|
|
12649
13046
|
}
|
|
12650
13047
|
});
|
|
12651
13048
|
|
|
12652
|
-
// src/shared/domain/types.ts
|
|
12653
|
-
var KNOWLEDGE_TYPES, IMPORTANCE_MIN, IMPORTANCE_MAX, EXPIRY_LEVELS, SCOPE_LEVELS, KNOWLEDGE_PLATFORMS, REJECTED_CONFLICT_ENTRY_ID;
|
|
12654
|
-
var init_types2 = __esm({
|
|
12655
|
-
"src/shared/domain/types.ts"() {
|
|
12656
|
-
"use strict";
|
|
12657
|
-
KNOWLEDGE_TYPES = [
|
|
12658
|
-
"fact",
|
|
12659
|
-
"decision",
|
|
12660
|
-
"preference",
|
|
12661
|
-
"todo",
|
|
12662
|
-
"relationship",
|
|
12663
|
-
"event",
|
|
12664
|
-
"lesson",
|
|
12665
|
-
"reflection"
|
|
12666
|
-
];
|
|
12667
|
-
IMPORTANCE_MIN = 1;
|
|
12668
|
-
IMPORTANCE_MAX = 10;
|
|
12669
|
-
EXPIRY_LEVELS = ["core", "permanent", "temporary"];
|
|
12670
|
-
SCOPE_LEVELS = ["private", "personal", "public"];
|
|
12671
|
-
KNOWLEDGE_PLATFORMS = ["openclaw", "claude-code", "codex", "plaud"];
|
|
12672
|
-
REJECTED_CONFLICT_ENTRY_ID = "rejected";
|
|
12673
|
-
}
|
|
12674
|
-
});
|
|
12675
|
-
|
|
12676
13049
|
// src/shared/utils/expiry.ts
|
|
12677
13050
|
function normalizeExpiry(value) {
|
|
12678
13051
|
if (typeof value !== "string") {
|
|
@@ -12691,7 +13064,7 @@ var EXPIRY_SET, EXPIRY_PRIORITY;
|
|
|
12691
13064
|
var init_expiry = __esm({
|
|
12692
13065
|
"src/shared/utils/expiry.ts"() {
|
|
12693
13066
|
"use strict";
|
|
12694
|
-
|
|
13067
|
+
init_types();
|
|
12695
13068
|
EXPIRY_SET = new Set(EXPIRY_LEVELS);
|
|
12696
13069
|
EXPIRY_PRIORITY = {
|
|
12697
13070
|
temporary: 0,
|
|
@@ -15143,7 +15516,7 @@ var MIN_CONTENT_LENGTH, MAX_CONTENT_LENGTH, MAX_SUBJECT_LENGTH, MIN_IMPORTANCE_F
|
|
|
15143
15516
|
var init_entry_validator = __esm({
|
|
15144
15517
|
"src/modules/ingestion/domain/validation/entry-validator.ts"() {
|
|
15145
15518
|
"use strict";
|
|
15146
|
-
|
|
15519
|
+
init_types();
|
|
15147
15520
|
init_content_patterns();
|
|
15148
15521
|
init_extraction_guards();
|
|
15149
15522
|
MIN_CONTENT_LENGTH = 20;
|
|
@@ -18783,7 +19156,7 @@ var init_write_queue = __esm({
|
|
|
18783
19156
|
var init_types3 = __esm({
|
|
18784
19157
|
"src/types.ts"() {
|
|
18785
19158
|
"use strict";
|
|
18786
|
-
|
|
19159
|
+
init_types();
|
|
18787
19160
|
}
|
|
18788
19161
|
});
|
|
18789
19162
|
|
|
@@ -23754,6 +24127,7 @@ var init_ingestion = __esm({
|
|
|
23754
24127
|
init_lockfile();
|
|
23755
24128
|
init_minhash();
|
|
23756
24129
|
init_schema();
|
|
24130
|
+
init_meta();
|
|
23757
24131
|
init_queries();
|
|
23758
24132
|
init_cache();
|
|
23759
24133
|
init_client2();
|
|
@@ -24277,6 +24651,7 @@ function resolveDbCommandDefaults() {
|
|
|
24277
24651
|
initDb: shared.initDbFn,
|
|
24278
24652
|
closeDb: shared.closeDbFn,
|
|
24279
24653
|
backupDb,
|
|
24654
|
+
checkVectorIntegrity,
|
|
24280
24655
|
getConflictShadowSummary,
|
|
24281
24656
|
resetDb,
|
|
24282
24657
|
walCheckpoint,
|
|
@@ -26159,1170 +26534,1186 @@ function formatPerfLog(fields) {
|
|
|
26159
26534
|
init_inbound();
|
|
26160
26535
|
init_project();
|
|
26161
26536
|
|
|
26162
|
-
// src/edge/openclaw/
|
|
26163
|
-
init_inbound();
|
|
26537
|
+
// src/edge/openclaw/session-state.ts
|
|
26164
26538
|
init_errors();
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26539
|
+
init_logger();
|
|
26540
|
+
|
|
26541
|
+
// src/edge/openclaw/surfaced-memory-ledger.ts
|
|
26542
|
+
var MAX_RECALLED_SESSIONS = 200;
|
|
26543
|
+
var SESSION_SURFACED_MEMORY_INTENTS = {
|
|
26544
|
+
startupPromptVisibleRecall: {
|
|
26545
|
+
writeProfile: {
|
|
26546
|
+
surface: "session-start",
|
|
26547
|
+
source: "startup-prompt-visible",
|
|
26548
|
+
ownership: "before-reset-owned"
|
|
26549
|
+
},
|
|
26550
|
+
read: {
|
|
26551
|
+
sharedSelectors: ["dedupe-relevant", "before-reset-owned"]
|
|
26552
|
+
},
|
|
26553
|
+
lifecycle: {
|
|
26554
|
+
ownership: "before-reset-owned",
|
|
26555
|
+
commandReset: "preserve",
|
|
26556
|
+
beforeResetFeedback: "owned"
|
|
26557
|
+
}
|
|
26558
|
+
},
|
|
26559
|
+
midSessionPluginInjection: {
|
|
26560
|
+
writeProfile: {
|
|
26561
|
+
surface: "mid-session-recall",
|
|
26562
|
+
source: "plugin-injection",
|
|
26563
|
+
ownership: "dedupe-only"
|
|
26564
|
+
},
|
|
26565
|
+
read: {
|
|
26566
|
+
sharedSelectors: ["dedupe-relevant"]
|
|
26567
|
+
},
|
|
26568
|
+
lifecycle: {
|
|
26569
|
+
ownership: "dedupe-only",
|
|
26570
|
+
commandReset: "clear",
|
|
26571
|
+
beforeResetFeedback: "excluded"
|
|
26572
|
+
}
|
|
26573
|
+
},
|
|
26574
|
+
agentToolRecallOutput: {
|
|
26575
|
+
writeProfile: {
|
|
26576
|
+
surface: "agent-tool-recall",
|
|
26577
|
+
source: "agent-tool-output",
|
|
26578
|
+
ownership: "dedupe-only"
|
|
26579
|
+
},
|
|
26580
|
+
read: {
|
|
26581
|
+
sharedSelectors: ["dedupe-relevant"]
|
|
26582
|
+
},
|
|
26583
|
+
lifecycle: {
|
|
26584
|
+
ownership: "dedupe-only",
|
|
26585
|
+
commandReset: "clear",
|
|
26586
|
+
beforeResetFeedback: "excluded"
|
|
26587
|
+
}
|
|
26171
26588
|
}
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26589
|
+
};
|
|
26590
|
+
function buildSessionSurfacedMemoryRecordProfiles() {
|
|
26591
|
+
const profiles = {};
|
|
26592
|
+
for (const [intent, definition] of Object.entries(SESSION_SURFACED_MEMORY_INTENTS)) {
|
|
26593
|
+
profiles[intent] = definition.writeProfile;
|
|
26175
26594
|
}
|
|
26176
|
-
return
|
|
26595
|
+
return profiles;
|
|
26177
26596
|
}
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
26597
|
+
var SESSION_SURFACED_MEMORY_RECORD_PROFILES = buildSessionSurfacedMemoryRecordProfiles();
|
|
26598
|
+
var SESSION_SURFACED_MEMORY_SELECTOR_FILTERS = {
|
|
26599
|
+
"dedupe-relevant": {},
|
|
26600
|
+
"before-reset-owned": { ownership: "before-reset-owned" }
|
|
26601
|
+
};
|
|
26602
|
+
var SESSION_SURFACED_MEMORY_SELECTORS = {
|
|
26603
|
+
dedupeRelevant: "dedupe-relevant",
|
|
26604
|
+
beforeResetOwned: "before-reset-owned",
|
|
26605
|
+
intent(intent) {
|
|
26606
|
+
return { kind: "intent", intent };
|
|
26607
|
+
},
|
|
26608
|
+
profile(profile) {
|
|
26609
|
+
return { kind: "profile", profile };
|
|
26610
|
+
},
|
|
26611
|
+
source(source) {
|
|
26612
|
+
return { kind: "source", source };
|
|
26181
26613
|
}
|
|
26182
|
-
|
|
26183
|
-
|
|
26614
|
+
};
|
|
26615
|
+
function normalizeSessionKey(value) {
|
|
26616
|
+
return value.trim();
|
|
26184
26617
|
}
|
|
26185
|
-
function
|
|
26186
|
-
|
|
26187
|
-
|
|
26188
|
-
|
|
26618
|
+
function trimSurfacedMemoryLedgers(store) {
|
|
26619
|
+
while (store.ledgers.size > MAX_RECALLED_SESSIONS) {
|
|
26620
|
+
const oldestKey = store.ledgers.keys().next().value;
|
|
26621
|
+
if (oldestKey === void 0) {
|
|
26622
|
+
break;
|
|
26623
|
+
}
|
|
26624
|
+
store.ledgers.delete(oldestKey);
|
|
26625
|
+
for (const [alias, canonicalKey] of store.aliases) {
|
|
26626
|
+
if (canonicalKey === oldestKey) {
|
|
26627
|
+
store.aliases.delete(alias);
|
|
26628
|
+
}
|
|
26629
|
+
}
|
|
26189
26630
|
}
|
|
26190
|
-
return Math.floor(parsed);
|
|
26191
26631
|
}
|
|
26192
|
-
function
|
|
26193
|
-
|
|
26194
|
-
|
|
26632
|
+
function touchSurfacedMemoryLedger(store, canonicalKey) {
|
|
26633
|
+
const existing = store.ledgers.get(canonicalKey);
|
|
26634
|
+
if (!existing) {
|
|
26635
|
+
return;
|
|
26195
26636
|
}
|
|
26196
|
-
|
|
26197
|
-
|
|
26198
|
-
project: toProjectName(row.project),
|
|
26199
|
-
count: toCount(row.count),
|
|
26200
|
-
lastUpdated: normalizeDate(row.lastUpdated ?? row.last_updated)
|
|
26201
|
-
};
|
|
26637
|
+
store.ledgers.delete(canonicalKey);
|
|
26638
|
+
store.ledgers.set(canonicalKey, existing);
|
|
26202
26639
|
}
|
|
26203
|
-
function
|
|
26204
|
-
|
|
26205
|
-
|
|
26206
|
-
|
|
26207
|
-
const response = value;
|
|
26208
|
-
if (!Array.isArray(response.projects)) {
|
|
26209
|
-
return null;
|
|
26640
|
+
function resolveCanonicalSurfacedMemoryLedgerKey(store, sessionKey) {
|
|
26641
|
+
const normalizedKey = normalizeSessionKey(sessionKey);
|
|
26642
|
+
if (!normalizedKey) {
|
|
26643
|
+
return "";
|
|
26210
26644
|
}
|
|
26211
|
-
|
|
26212
|
-
const totalFromResponse = Number(response.totalEntries);
|
|
26213
|
-
const totalEntries = Number.isFinite(totalFromResponse) && totalFromResponse >= 0 ? Math.floor(totalFromResponse) : projects.reduce((sum, project) => sum + project.count, 0);
|
|
26214
|
-
return { projects, totalEntries };
|
|
26645
|
+
return store.aliases.get(normalizedKey) ?? normalizedKey;
|
|
26215
26646
|
}
|
|
26216
|
-
|
|
26217
|
-
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
loadMemoryIndex(dbPath, executionOptions?.recallServiceDeps),
|
|
26221
|
-
timeoutMs
|
|
26222
|
-
);
|
|
26223
|
-
if (result === MEMORY_INDEX_TIMEOUT) {
|
|
26224
|
-
return { status: "timeout" };
|
|
26225
|
-
}
|
|
26226
|
-
return result;
|
|
26227
|
-
} catch (error) {
|
|
26228
|
-
return {
|
|
26229
|
-
status: "error",
|
|
26230
|
-
error: toErrorMessage(error)
|
|
26231
|
-
};
|
|
26647
|
+
function getOrCreateSurfacedMemoryLedger(store, sessionKey) {
|
|
26648
|
+
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, sessionKey);
|
|
26649
|
+
if (!canonicalKey) {
|
|
26650
|
+
return null;
|
|
26232
26651
|
}
|
|
26233
|
-
|
|
26234
|
-
|
|
26235
|
-
|
|
26236
|
-
|
|
26237
|
-
{ dbPath },
|
|
26238
|
-
{ runtime: adaptLegacyRecallRuntimeDeps(recallServiceDeps) }
|
|
26239
|
-
)
|
|
26240
|
-
);
|
|
26241
|
-
if (!index) {
|
|
26242
|
-
return { status: "invalid" };
|
|
26652
|
+
const existing = store.ledgers.get(canonicalKey);
|
|
26653
|
+
if (existing) {
|
|
26654
|
+
touchSurfacedMemoryLedger(store, canonicalKey);
|
|
26655
|
+
return existing;
|
|
26243
26656
|
}
|
|
26244
|
-
|
|
26245
|
-
|
|
26246
|
-
|
|
26657
|
+
const ledger = {
|
|
26658
|
+
nextSequence: 1,
|
|
26659
|
+
records: []
|
|
26247
26660
|
};
|
|
26661
|
+
store.ledgers.set(canonicalKey, ledger);
|
|
26662
|
+
trimSurfacedMemoryLedgers(store);
|
|
26663
|
+
return ledger;
|
|
26248
26664
|
}
|
|
26249
|
-
|
|
26250
|
-
|
|
26251
|
-
|
|
26252
|
-
return await new Promise((resolve, reject) => {
|
|
26253
|
-
timer = setTimeout(() => {
|
|
26254
|
-
resolve(MEMORY_INDEX_TIMEOUT);
|
|
26255
|
-
}, timeoutMs);
|
|
26256
|
-
promise.then(resolve).catch(reject);
|
|
26257
|
-
});
|
|
26258
|
-
} finally {
|
|
26259
|
-
if (timer) {
|
|
26260
|
-
clearTimeout(timer);
|
|
26261
|
-
}
|
|
26665
|
+
function matchesSurfacedMemoryLedgerFilter(record, filter) {
|
|
26666
|
+
if (filter.surface && record.surface !== filter.surface) {
|
|
26667
|
+
return false;
|
|
26262
26668
|
}
|
|
26263
|
-
|
|
26264
|
-
|
|
26265
|
-
if (index.projects.length === 0) {
|
|
26266
|
-
return "";
|
|
26669
|
+
if (filter.source && record.source !== filter.source) {
|
|
26670
|
+
return false;
|
|
26267
26671
|
}
|
|
26268
|
-
|
|
26269
|
-
|
|
26270
|
-
const name = project.project ?? "(universal)";
|
|
26271
|
-
const date = project.lastUpdated ? project.lastUpdated.slice(0, 10) : "unknown";
|
|
26272
|
-
lines.push(`- ${name}: ${project.count} entries (last updated ${date})`);
|
|
26672
|
+
if (filter.ownership && record.ownership !== filter.ownership) {
|
|
26673
|
+
return false;
|
|
26273
26674
|
}
|
|
26274
|
-
|
|
26275
|
-
lines.push(`Total: ${index.totalEntries} entries`);
|
|
26276
|
-
return lines.join("\n");
|
|
26277
|
-
}
|
|
26278
|
-
|
|
26279
|
-
// src/edge/openclaw/recall.ts
|
|
26280
|
-
var RECALL_QUERY_MAX_CHARS = 500;
|
|
26281
|
-
var DEFAULT_LIMIT3 = 10;
|
|
26282
|
-
function resolveUpdateMetadata(isBrowse, isSessionStart, requestOverrides) {
|
|
26283
|
-
const metadataMode = requestOverrides?.metadataMode ?? "non-browse";
|
|
26284
|
-
return requestOverrides?.noUpdate !== true && !isBrowse && (metadataMode === "non-browse" || metadataMode === "session-start" && isSessionStart);
|
|
26675
|
+
return true;
|
|
26285
26676
|
}
|
|
26286
|
-
function
|
|
26287
|
-
|
|
26677
|
+
function normalizeSurfacedMemoryLedgerInputs(inputs) {
|
|
26678
|
+
const normalized = [];
|
|
26679
|
+
const seenSignatures = /* @__PURE__ */ new Set();
|
|
26680
|
+
for (const input of inputs) {
|
|
26681
|
+
const entryId = normalizeSessionKey(input.entryId);
|
|
26682
|
+
if (!entryId) {
|
|
26683
|
+
continue;
|
|
26684
|
+
}
|
|
26685
|
+
const signature = [
|
|
26686
|
+
entryId,
|
|
26687
|
+
input.surface,
|
|
26688
|
+
input.source,
|
|
26689
|
+
input.ownership
|
|
26690
|
+
].join("\0");
|
|
26691
|
+
if (seenSignatures.has(signature)) {
|
|
26692
|
+
continue;
|
|
26693
|
+
}
|
|
26694
|
+
seenSignatures.add(signature);
|
|
26695
|
+
normalized.push({
|
|
26696
|
+
entryId,
|
|
26697
|
+
surface: input.surface,
|
|
26698
|
+
source: input.source,
|
|
26699
|
+
ownership: input.ownership
|
|
26700
|
+
});
|
|
26701
|
+
}
|
|
26702
|
+
return normalized;
|
|
26288
26703
|
}
|
|
26289
|
-
function
|
|
26290
|
-
const
|
|
26291
|
-
const
|
|
26292
|
-
|
|
26293
|
-
|
|
26294
|
-
|
|
26295
|
-
|
|
26296
|
-
|
|
26297
|
-
|
|
26298
|
-
|
|
26299
|
-
|
|
26300
|
-
|
|
26301
|
-
)
|
|
26302
|
-
|
|
26303
|
-
|
|
26304
|
-
|
|
26305
|
-
|
|
26306
|
-
|
|
26704
|
+
function normalizeSessionSurfacedMemoryWrites(writes) {
|
|
26705
|
+
const inputs = [];
|
|
26706
|
+
for (const write of writes) {
|
|
26707
|
+
for (const entryId of write.entryIds) {
|
|
26708
|
+
inputs.push({
|
|
26709
|
+
entryId,
|
|
26710
|
+
surface: write.surface,
|
|
26711
|
+
source: write.source,
|
|
26712
|
+
ownership: write.ownership
|
|
26713
|
+
});
|
|
26714
|
+
}
|
|
26715
|
+
}
|
|
26716
|
+
return normalizeSurfacedMemoryLedgerInputs(inputs);
|
|
26717
|
+
}
|
|
26718
|
+
function resolveSessionSurfacedMemoryRecordProfile(profile) {
|
|
26719
|
+
return { ...SESSION_SURFACED_MEMORY_RECORD_PROFILES[profile] };
|
|
26720
|
+
}
|
|
26721
|
+
function resolveSessionSurfacedMemoryIntent(intent) {
|
|
26722
|
+
const definition = SESSION_SURFACED_MEMORY_INTENTS[intent];
|
|
26307
26723
|
return {
|
|
26308
|
-
|
|
26309
|
-
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
|
|
26313
|
-
types: options?.types,
|
|
26314
|
-
until: options?.until,
|
|
26315
|
-
around: options?.around,
|
|
26316
|
-
aroundRadius: options?.aroundRadius,
|
|
26317
|
-
platform: options?.platform,
|
|
26318
|
-
minImportance: options?.minImportance,
|
|
26319
|
-
expiry: options?.expiry,
|
|
26320
|
-
universalOnly,
|
|
26321
|
-
project: resolvedProject,
|
|
26322
|
-
projectStrict: resolvedProjectStrict,
|
|
26323
|
-
projectHints: resolvedProjectHints,
|
|
26324
|
-
budget: isBrowse ? void 0 : options?.budget === null ? void 0 : options?.budget ?? budget
|
|
26724
|
+
intent,
|
|
26725
|
+
writeProfile: { ...definition.writeProfile },
|
|
26726
|
+
readSelector: SESSION_SURFACED_MEMORY_SELECTORS.intent(intent),
|
|
26727
|
+
sharedSelectors: [...definition.read.sharedSelectors],
|
|
26728
|
+
lifecycle: { ...definition.lifecycle }
|
|
26325
26729
|
};
|
|
26326
26730
|
}
|
|
26327
|
-
function
|
|
26731
|
+
function resolveSessionSurfacedMemorySelectorFilter(selector) {
|
|
26732
|
+
if (selector === "dedupe-relevant" || selector === "before-reset-owned") {
|
|
26733
|
+
return { ...SESSION_SURFACED_MEMORY_SELECTOR_FILTERS[selector] };
|
|
26734
|
+
}
|
|
26735
|
+
if (selector.kind === "intent") {
|
|
26736
|
+
return resolveSessionSurfacedMemoryIntent(selector.intent).writeProfile;
|
|
26737
|
+
}
|
|
26738
|
+
if (selector.kind === "profile") {
|
|
26739
|
+
return resolveSessionSurfacedMemoryRecordProfile(selector.profile);
|
|
26740
|
+
}
|
|
26328
26741
|
return {
|
|
26329
|
-
|
|
26330
|
-
decision: result.payload.decision,
|
|
26331
|
-
rollupSummary: result.payload.rollup_summary,
|
|
26332
|
-
surfaceSelection: result.surfaceSelection,
|
|
26333
|
-
results: result.payload.results.map((item) => ({
|
|
26334
|
-
entry: item.entry,
|
|
26335
|
-
score: item.score,
|
|
26336
|
-
category: item.category
|
|
26337
|
-
}))
|
|
26742
|
+
source: selector.source
|
|
26338
26743
|
};
|
|
26339
26744
|
}
|
|
26340
|
-
|
|
26341
|
-
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26745
|
+
function normalizeSessionSurfacedMemoryIntentWrites(writes) {
|
|
26746
|
+
const normalized = [];
|
|
26747
|
+
for (const write of writes) {
|
|
26748
|
+
normalized.push({
|
|
26749
|
+
entryIds: write.entryIds,
|
|
26750
|
+
...resolveSessionSurfacedMemoryIntent(write.intent).writeProfile
|
|
26751
|
+
});
|
|
26752
|
+
}
|
|
26753
|
+
return normalized;
|
|
26349
26754
|
}
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
request.isSessionStart,
|
|
26356
|
-
executionOptions?.requestOverrides
|
|
26357
|
-
);
|
|
26358
|
-
const result = request.isBrowse ? await browseRecall(
|
|
26359
|
-
{
|
|
26360
|
-
dbPath,
|
|
26361
|
-
limit: request.limit,
|
|
26362
|
-
threshold: 0,
|
|
26363
|
-
types: request.types,
|
|
26364
|
-
since: request.since,
|
|
26365
|
-
until: request.until,
|
|
26366
|
-
around: request.around,
|
|
26367
|
-
aroundRadius: request.aroundRadius,
|
|
26368
|
-
platform: request.platform,
|
|
26369
|
-
minImportance: request.minImportance,
|
|
26370
|
-
expiry: request.expiry,
|
|
26371
|
-
project: request.project,
|
|
26372
|
-
projectStrict: request.projectStrict,
|
|
26373
|
-
projectHints: request.projectHints,
|
|
26374
|
-
universalOnly: request.universalOnly,
|
|
26375
|
-
includeRecallCounts: false
|
|
26376
|
-
},
|
|
26377
|
-
{ runtime }
|
|
26378
|
-
) : request.isSessionStart ? await sessionStartRecall(
|
|
26379
|
-
{
|
|
26380
|
-
dbPath,
|
|
26381
|
-
limit: request.limit,
|
|
26382
|
-
budget: request.budget,
|
|
26383
|
-
threshold: 0,
|
|
26384
|
-
types: request.types,
|
|
26385
|
-
since: request.since,
|
|
26386
|
-
until: request.until,
|
|
26387
|
-
around: request.around,
|
|
26388
|
-
aroundRadius: request.aroundRadius,
|
|
26389
|
-
platform: request.platform,
|
|
26390
|
-
minImportance: request.minImportance,
|
|
26391
|
-
expiry: request.expiry,
|
|
26392
|
-
project: request.project,
|
|
26393
|
-
projectStrict: request.projectStrict,
|
|
26394
|
-
projectHints: request.projectHints,
|
|
26395
|
-
universalOnly: request.universalOnly,
|
|
26396
|
-
updateMetadata,
|
|
26397
|
-
includeRecallCounts: false
|
|
26398
|
-
},
|
|
26399
|
-
{ runtime }
|
|
26400
|
-
) : await searchRecall(
|
|
26401
|
-
{
|
|
26402
|
-
dbPath,
|
|
26403
|
-
queryText: request.queryText,
|
|
26404
|
-
queryContext: "default",
|
|
26405
|
-
limit: request.limit,
|
|
26406
|
-
budget: request.budget,
|
|
26407
|
-
threshold: 0,
|
|
26408
|
-
types: request.types,
|
|
26409
|
-
since: request.since,
|
|
26410
|
-
until: request.until,
|
|
26411
|
-
around: request.around,
|
|
26412
|
-
aroundRadius: request.aroundRadius,
|
|
26413
|
-
platform: request.platform,
|
|
26414
|
-
minImportance: request.minImportance,
|
|
26415
|
-
expiry: request.expiry,
|
|
26416
|
-
project: request.project,
|
|
26417
|
-
projectStrict: request.projectStrict,
|
|
26418
|
-
projectHints: request.projectHints,
|
|
26419
|
-
universalOnly: request.universalOnly,
|
|
26420
|
-
updateMetadata,
|
|
26421
|
-
includeRecallCounts: false
|
|
26422
|
-
},
|
|
26423
|
-
{ runtime }
|
|
26424
|
-
);
|
|
26425
|
-
return shapeRecallResult(result);
|
|
26755
|
+
function toSessionSurfacedMemoryWrites(writes) {
|
|
26756
|
+
if (Symbol.iterator in Object(writes) && !("entryIds" in Object(writes))) {
|
|
26757
|
+
return writes;
|
|
26758
|
+
}
|
|
26759
|
+
return [writes];
|
|
26426
26760
|
}
|
|
26427
|
-
|
|
26428
|
-
|
|
26429
|
-
return
|
|
26430
|
-
} catch {
|
|
26431
|
-
return null;
|
|
26761
|
+
function toSessionSurfacedMemoryIntentWrites(writes) {
|
|
26762
|
+
if (Symbol.iterator in Object(writes) && !("entryIds" in Object(writes))) {
|
|
26763
|
+
return writes;
|
|
26432
26764
|
}
|
|
26765
|
+
return [writes];
|
|
26433
26766
|
}
|
|
26434
|
-
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26439
|
-
{ dbPath },
|
|
26440
|
-
{ runtime: resolveRuntimeDeps(executionOptions) }
|
|
26441
|
-
)
|
|
26442
|
-
);
|
|
26443
|
-
} catch {
|
|
26444
|
-
return null;
|
|
26767
|
+
function registerSessionSurfacedMemoryAlias(store, aliasKey, sessionKey) {
|
|
26768
|
+
const normalizedAlias = normalizeSessionKey(aliasKey);
|
|
26769
|
+
const normalizedSessionKey = normalizeSessionKey(sessionKey);
|
|
26770
|
+
if (!normalizedAlias || !normalizedSessionKey) {
|
|
26771
|
+
return;
|
|
26445
26772
|
}
|
|
26446
|
-
const
|
|
26447
|
-
|
|
26773
|
+
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, normalizedSessionKey);
|
|
26774
|
+
store.aliases.set(normalizedAlias, canonicalKey);
|
|
26775
|
+
if (normalizedAlias === canonicalKey) {
|
|
26776
|
+
return;
|
|
26777
|
+
}
|
|
26778
|
+
const aliasLedger = store.ledgers.get(normalizedAlias);
|
|
26779
|
+
if (!aliasLedger) {
|
|
26780
|
+
return;
|
|
26781
|
+
}
|
|
26782
|
+
store.ledgers.delete(normalizedAlias);
|
|
26783
|
+
const targetLedger = getOrCreateSurfacedMemoryLedger(store, canonicalKey);
|
|
26784
|
+
if (!targetLedger) {
|
|
26785
|
+
return;
|
|
26786
|
+
}
|
|
26787
|
+
const existingSignatures = new Set(
|
|
26788
|
+
targetLedger.records.map((record) => [record.entryId, record.surface, record.source, record.ownership].join("\0"))
|
|
26448
26789
|
);
|
|
26449
|
-
const
|
|
26450
|
-
const
|
|
26451
|
-
|
|
26452
|
-
|
|
26453
|
-
|
|
26454
|
-
|
|
26455
|
-
|
|
26456
|
-
|
|
26457
|
-
return {
|
|
26458
|
-
...result,
|
|
26459
|
-
results: filteredResults.slice(0, 30)
|
|
26460
|
-
};
|
|
26461
|
-
}
|
|
26462
|
-
|
|
26463
|
-
// src/edge/openclaw/session-query.ts
|
|
26464
|
-
init_errors();
|
|
26465
|
-
init_guards();
|
|
26466
|
-
import { createReadStream as createReadStream2 } from "fs";
|
|
26467
|
-
import { readdir, stat } from "fs/promises";
|
|
26468
|
-
import path10 from "path";
|
|
26469
|
-
import { createInterface as createInterface2 } from "readline";
|
|
26470
|
-
var EXCHANGE_TEXT_MAX_CHARS = 200;
|
|
26471
|
-
var EXCHANGE_USER_TURN_LIMIT = 5;
|
|
26472
|
-
var RECENT_TURN_MAX_CHARS = 300;
|
|
26473
|
-
var DEFAULT_RECENT_TURN_LIMIT = 7;
|
|
26474
|
-
var SESSION_TRANSCRIPT_ARTIFACT_MARKERS = [".jsonl.reset.", ".jsonl.deleted."];
|
|
26475
|
-
function extractTextFromRoleMessage(message, role, separator) {
|
|
26476
|
-
if (!isRecord(message) || message["role"] !== role) {
|
|
26477
|
-
return "";
|
|
26478
|
-
}
|
|
26479
|
-
const content = message["content"];
|
|
26480
|
-
if (typeof content === "string") {
|
|
26481
|
-
return content.trim();
|
|
26482
|
-
}
|
|
26483
|
-
if (!Array.isArray(content)) {
|
|
26484
|
-
return "";
|
|
26485
|
-
}
|
|
26486
|
-
const textParts = [];
|
|
26487
|
-
for (const part of content) {
|
|
26488
|
-
if (!isRecord(part) || part["type"] !== "text") {
|
|
26790
|
+
for (const record of aliasLedger.records) {
|
|
26791
|
+
const signature = [
|
|
26792
|
+
record.entryId,
|
|
26793
|
+
record.surface,
|
|
26794
|
+
record.source,
|
|
26795
|
+
record.ownership
|
|
26796
|
+
].join("\0");
|
|
26797
|
+
if (existingSignatures.has(signature)) {
|
|
26489
26798
|
continue;
|
|
26490
26799
|
}
|
|
26491
|
-
|
|
26492
|
-
|
|
26800
|
+
existingSignatures.add(signature);
|
|
26801
|
+
targetLedger.records.push({
|
|
26802
|
+
...record,
|
|
26803
|
+
sequence: targetLedger.nextSequence++
|
|
26804
|
+
});
|
|
26805
|
+
}
|
|
26806
|
+
touchSurfacedMemoryLedger(store, canonicalKey);
|
|
26807
|
+
}
|
|
26808
|
+
function appendSessionSurfacedMemoryLedgerEntries(store, sessionKey, inputs) {
|
|
26809
|
+
const ledger = getOrCreateSurfacedMemoryLedger(store, sessionKey);
|
|
26810
|
+
if (!ledger) {
|
|
26811
|
+
return 0;
|
|
26812
|
+
}
|
|
26813
|
+
const normalizedInputs = normalizeSurfacedMemoryLedgerInputs(inputs);
|
|
26814
|
+
const existingSignatures = new Set(
|
|
26815
|
+
ledger.records.map((record) => [record.entryId, record.surface, record.source, record.ownership].join("\0"))
|
|
26816
|
+
);
|
|
26817
|
+
let addedCount = 0;
|
|
26818
|
+
for (const input of normalizedInputs) {
|
|
26819
|
+
const signature = [
|
|
26820
|
+
input.entryId,
|
|
26821
|
+
input.surface,
|
|
26822
|
+
input.source,
|
|
26823
|
+
input.ownership
|
|
26824
|
+
].join("\0");
|
|
26825
|
+
if (existingSignatures.has(signature)) {
|
|
26493
26826
|
continue;
|
|
26494
26827
|
}
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
|
|
26498
|
-
|
|
26828
|
+
existingSignatures.add(signature);
|
|
26829
|
+
ledger.records.push({
|
|
26830
|
+
...input,
|
|
26831
|
+
sequence: ledger.nextSequence++
|
|
26832
|
+
});
|
|
26833
|
+
addedCount += 1;
|
|
26499
26834
|
}
|
|
26500
|
-
|
|
26835
|
+
if (addedCount > 0) {
|
|
26836
|
+
touchSurfacedMemoryLedger(store, resolveCanonicalSurfacedMemoryLedgerKey(store, sessionKey));
|
|
26837
|
+
}
|
|
26838
|
+
return addedCount;
|
|
26501
26839
|
}
|
|
26502
|
-
function
|
|
26503
|
-
|
|
26840
|
+
function replaceSessionSurfacedMemoryLedgerEntries(store, sessionKey, inputs) {
|
|
26841
|
+
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, sessionKey);
|
|
26842
|
+
if (!canonicalKey) {
|
|
26843
|
+
return;
|
|
26844
|
+
}
|
|
26845
|
+
const normalizedInputs = normalizeSurfacedMemoryLedgerInputs(inputs);
|
|
26846
|
+
const ledger = {
|
|
26847
|
+
nextSequence: 1,
|
|
26848
|
+
records: normalizedInputs.map((input, index) => ({
|
|
26849
|
+
...input,
|
|
26850
|
+
sequence: index + 1
|
|
26851
|
+
}))
|
|
26852
|
+
};
|
|
26853
|
+
ledger.nextSequence = ledger.records.length + 1;
|
|
26854
|
+
store.ledgers.delete(canonicalKey);
|
|
26855
|
+
store.ledgers.set(canonicalKey, ledger);
|
|
26856
|
+
trimSurfacedMemoryLedgers(store);
|
|
26504
26857
|
}
|
|
26505
|
-
function
|
|
26506
|
-
|
|
26858
|
+
function recordSessionSurfacedMemory(store, sessionKey, writes, options = {}) {
|
|
26859
|
+
const normalizedInputs = normalizeSessionSurfacedMemoryWrites(
|
|
26860
|
+
toSessionSurfacedMemoryWrites(writes)
|
|
26861
|
+
);
|
|
26862
|
+
if (normalizedInputs.length === 0) {
|
|
26863
|
+
if (options.mode === "replace") {
|
|
26864
|
+
replaceSessionSurfacedMemoryLedgerEntries(store, sessionKey, []);
|
|
26865
|
+
}
|
|
26866
|
+
return 0;
|
|
26867
|
+
}
|
|
26868
|
+
if (options.mode === "replace") {
|
|
26869
|
+
replaceSessionSurfacedMemoryLedgerEntries(store, sessionKey, normalizedInputs);
|
|
26870
|
+
return normalizedInputs.length;
|
|
26871
|
+
}
|
|
26872
|
+
return appendSessionSurfacedMemoryLedgerEntries(store, sessionKey, normalizedInputs);
|
|
26507
26873
|
}
|
|
26508
|
-
function
|
|
26509
|
-
return
|
|
26874
|
+
function recordSessionSurfacedMemoryByIntent(store, sessionKey, writes, options = {}) {
|
|
26875
|
+
return recordSessionSurfacedMemory(
|
|
26876
|
+
store,
|
|
26877
|
+
sessionKey,
|
|
26878
|
+
normalizeSessionSurfacedMemoryIntentWrites(
|
|
26879
|
+
toSessionSurfacedMemoryIntentWrites(writes)
|
|
26880
|
+
),
|
|
26881
|
+
options
|
|
26882
|
+
);
|
|
26510
26883
|
}
|
|
26511
|
-
function
|
|
26512
|
-
|
|
26513
|
-
|
|
26884
|
+
function getSessionSurfacedMemoryLedger(store, sessionKey) {
|
|
26885
|
+
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, sessionKey);
|
|
26886
|
+
if (!canonicalKey) {
|
|
26887
|
+
return [];
|
|
26514
26888
|
}
|
|
26515
|
-
|
|
26516
|
-
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
|
|
26522
|
-
|
|
26523
|
-
|
|
26889
|
+
const ledger = store.ledgers.get(canonicalKey);
|
|
26890
|
+
return ledger ? [...ledger.records] : [];
|
|
26891
|
+
}
|
|
26892
|
+
function getSessionSurfacedMemoryLedgerRecords(store, sessionKey, filter = {}) {
|
|
26893
|
+
return getSessionSurfacedMemoryLedger(store, sessionKey).filter((record) => matchesSurfacedMemoryLedgerFilter(record, filter));
|
|
26894
|
+
}
|
|
26895
|
+
function collectSessionSurfacedMemoryEntryIds(records) {
|
|
26896
|
+
const surfacedEntryIds = [];
|
|
26897
|
+
const seenEntryIds = /* @__PURE__ */ new Set();
|
|
26898
|
+
for (const record of records) {
|
|
26899
|
+
if (seenEntryIds.has(record.entryId)) {
|
|
26900
|
+
continue;
|
|
26524
26901
|
}
|
|
26525
|
-
|
|
26526
|
-
|
|
26527
|
-
return "";
|
|
26902
|
+
seenEntryIds.add(record.entryId);
|
|
26903
|
+
surfacedEntryIds.push(record.entryId);
|
|
26528
26904
|
}
|
|
26905
|
+
return surfacedEntryIds;
|
|
26529
26906
|
}
|
|
26530
|
-
function
|
|
26531
|
-
|
|
26532
|
-
|
|
26533
|
-
|
|
26534
|
-
|
|
26535
|
-
|
|
26536
|
-
|
|
26537
|
-
|
|
26538
|
-
|
|
26539
|
-
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26543
|
-
|
|
26544
|
-
|
|
26545
|
-
|
|
26546
|
-
|
|
26547
|
-
|
|
26548
|
-
|
|
26549
|
-
|
|
26550
|
-
|
|
26551
|
-
|
|
26552
|
-
|
|
26553
|
-
|
|
26554
|
-
|
|
26555
|
-
|
|
26556
|
-
|
|
26557
|
-
|
|
26558
|
-
|
|
26559
|
-
|
|
26560
|
-
|
|
26561
|
-
|
|
26562
|
-
if (
|
|
26563
|
-
|
|
26907
|
+
function removeSessionSurfacedMemoryLedgerEntries(store, sessionKey, filter = {}) {
|
|
26908
|
+
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, sessionKey);
|
|
26909
|
+
if (!canonicalKey) {
|
|
26910
|
+
return 0;
|
|
26911
|
+
}
|
|
26912
|
+
const ledger = store.ledgers.get(canonicalKey);
|
|
26913
|
+
if (!ledger) {
|
|
26914
|
+
return 0;
|
|
26915
|
+
}
|
|
26916
|
+
const keptRecords = ledger.records.filter((record) => !matchesSurfacedMemoryLedgerFilter(record, filter));
|
|
26917
|
+
const removedCount = ledger.records.length - keptRecords.length;
|
|
26918
|
+
if (removedCount === 0) {
|
|
26919
|
+
return 0;
|
|
26920
|
+
}
|
|
26921
|
+
if (keptRecords.length === 0) {
|
|
26922
|
+
clearSessionSurfacedMemoryLedger(store, sessionKey);
|
|
26923
|
+
return removedCount;
|
|
26924
|
+
}
|
|
26925
|
+
ledger.records = keptRecords;
|
|
26926
|
+
touchSurfacedMemoryLedger(store, canonicalKey);
|
|
26927
|
+
return removedCount;
|
|
26928
|
+
}
|
|
26929
|
+
function clearSessionSurfacedMemoryLedger(store, sessionKey) {
|
|
26930
|
+
const normalizedKey = normalizeSessionKey(sessionKey);
|
|
26931
|
+
if (!normalizedKey) {
|
|
26932
|
+
return;
|
|
26933
|
+
}
|
|
26934
|
+
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, normalizedKey);
|
|
26935
|
+
if (canonicalKey) {
|
|
26936
|
+
store.ledgers.delete(canonicalKey);
|
|
26937
|
+
}
|
|
26938
|
+
for (const [alias, targetKey] of store.aliases) {
|
|
26939
|
+
if (alias === normalizedKey || targetKey === canonicalKey) {
|
|
26940
|
+
store.aliases.delete(alias);
|
|
26564
26941
|
}
|
|
26565
|
-
return collected.reverse().map((turn) => `${turn.role === "user" ? "U" : "A"}: ${turn.text}`).join(" | ");
|
|
26566
|
-
} catch {
|
|
26567
|
-
return "";
|
|
26568
26942
|
}
|
|
26569
26943
|
}
|
|
26570
|
-
|
|
26571
|
-
|
|
26572
|
-
|
|
26573
|
-
const entries = await readdir(sessionsDir, { withFileTypes: true });
|
|
26574
|
-
const candidatePaths = [];
|
|
26575
|
-
for (const entry of entries) {
|
|
26576
|
-
if (!entry.isFile()) {
|
|
26577
|
-
continue;
|
|
26578
|
-
}
|
|
26579
|
-
const isPlainJsonl = entry.name.endsWith(".jsonl");
|
|
26580
|
-
const isTranscriptArtifact = SESSION_TRANSCRIPT_ARTIFACT_MARKERS.some((marker) => entry.name.includes(marker));
|
|
26581
|
-
if (!isPlainJsonl && !isTranscriptArtifact) {
|
|
26582
|
-
continue;
|
|
26583
|
-
}
|
|
26584
|
-
if (entry.name.includes(".deleted.") && !entry.name.includes(".jsonl.deleted.")) {
|
|
26585
|
-
continue;
|
|
26586
|
-
}
|
|
26587
|
-
if (normalizedSessionId && entry.name.startsWith(normalizedSessionId)) {
|
|
26588
|
-
continue;
|
|
26589
|
-
}
|
|
26590
|
-
candidatePaths.push(path10.join(sessionsDir, entry.name));
|
|
26591
|
-
}
|
|
26592
|
-
if (candidatePaths.length === 0) {
|
|
26593
|
-
return [];
|
|
26594
|
-
}
|
|
26595
|
-
const statResults = await Promise.all(
|
|
26596
|
-
candidatePaths.map(async (filePath) => {
|
|
26597
|
-
try {
|
|
26598
|
-
const fileStats = await stat(filePath);
|
|
26599
|
-
return { filePath, mtimeMs: fileStats.mtimeMs };
|
|
26600
|
-
} catch {
|
|
26601
|
-
return null;
|
|
26602
|
-
}
|
|
26603
|
-
})
|
|
26604
|
-
);
|
|
26605
|
-
const candidates = statResults.filter(
|
|
26606
|
-
(result) => result !== null
|
|
26607
|
-
);
|
|
26608
|
-
if (candidates.length === 0) {
|
|
26609
|
-
return [];
|
|
26610
|
-
}
|
|
26611
|
-
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
26612
|
-
return candidates;
|
|
26613
|
-
} catch (err) {
|
|
26614
|
-
logger?.debug?.(
|
|
26615
|
-
`listSessionFileCandidates: failed to read sessions dir "${sessionsDir}": ${toErrorMessage(err)}`
|
|
26616
|
-
);
|
|
26617
|
-
return [];
|
|
26944
|
+
var SURFACED_MEMORY_LIFECYCLE_FILTERS = {
|
|
26945
|
+
commandResetCleared: {
|
|
26946
|
+
ownership: "dedupe-only"
|
|
26618
26947
|
}
|
|
26948
|
+
};
|
|
26949
|
+
function applySessionSurfacedMemoryCommandResetPolicy(store, sessionKey) {
|
|
26950
|
+
return removeSessionSurfacedMemoryLedgerEntries(
|
|
26951
|
+
store,
|
|
26952
|
+
sessionKey,
|
|
26953
|
+
SURFACED_MEMORY_LIFECYCLE_FILTERS.commandResetCleared
|
|
26954
|
+
);
|
|
26619
26955
|
}
|
|
26620
|
-
|
|
26621
|
-
|
|
26622
|
-
|
|
26623
|
-
|
|
26624
|
-
|
|
26625
|
-
|
|
26626
|
-
|
|
26627
|
-
|
|
26628
|
-
}
|
|
26629
|
-
const allTurns = [];
|
|
26630
|
-
await new Promise((resolve, reject) => {
|
|
26631
|
-
const rl = createInterface2({
|
|
26632
|
-
input: createReadStream2(filePath, { encoding: "utf8" }),
|
|
26633
|
-
crlfDelay: Infinity
|
|
26634
|
-
});
|
|
26635
|
-
rl.on("line", (line) => {
|
|
26636
|
-
const trimmed = line.trim();
|
|
26637
|
-
if (!trimmed) {
|
|
26638
|
-
return;
|
|
26639
|
-
}
|
|
26640
|
-
let record;
|
|
26641
|
-
try {
|
|
26642
|
-
record = JSON.parse(trimmed);
|
|
26643
|
-
} catch {
|
|
26644
|
-
return;
|
|
26645
|
-
}
|
|
26646
|
-
if (!isRecord(record) || record["type"] !== "message") {
|
|
26647
|
-
return;
|
|
26648
|
-
}
|
|
26649
|
-
const message = record["message"];
|
|
26650
|
-
const messageTimestamp = isRecord(message) && typeof message["timestamp"] === "string" ? message["timestamp"] : void 0;
|
|
26651
|
-
const timestamp2 = typeof record["timestamp"] === "string" ? record["timestamp"] : messageTimestamp;
|
|
26652
|
-
const userText = extractTextFromRoleMessage(message, "user", "\n");
|
|
26653
|
-
if (userText) {
|
|
26654
|
-
allTurns.push({ role: "user", text: userText, timestamp: timestamp2 });
|
|
26655
|
-
return;
|
|
26656
|
-
}
|
|
26657
|
-
const assistantText = extractTextFromRoleMessage(message, "assistant", "\n");
|
|
26658
|
-
if (assistantText) {
|
|
26659
|
-
allTurns.push({ role: "assistant", text: assistantText, timestamp: timestamp2 });
|
|
26660
|
-
}
|
|
26661
|
-
});
|
|
26662
|
-
rl.on("close", resolve);
|
|
26663
|
-
rl.on("error", reject);
|
|
26664
|
-
});
|
|
26665
|
-
const sanitizedTurns = sanitizeTranscriptTurns(allTurns, {
|
|
26666
|
-
maxCharsPerTurn: RECENT_TURN_MAX_CHARS
|
|
26667
|
-
});
|
|
26668
|
-
return sanitizedTurns.slice(-parsedMaxTurns);
|
|
26669
|
-
} catch {
|
|
26670
|
-
return [];
|
|
26671
|
-
}
|
|
26956
|
+
function applySessionSurfacedMemoryBeforeResetPolicy(store, sessionKey) {
|
|
26957
|
+
const beforeResetOwnedEntryIds = querySessionSurfacedMemory(store, sessionKey).select(SESSION_SURFACED_MEMORY_SELECTORS.beforeResetOwned).entryIds();
|
|
26958
|
+
const clearedCount = getSessionSurfacedMemoryLedger(store, sessionKey).length;
|
|
26959
|
+
clearSessionSurfacedMemoryLedger(store, sessionKey);
|
|
26960
|
+
return {
|
|
26961
|
+
beforeResetOwnedEntryIds,
|
|
26962
|
+
clearedCount
|
|
26963
|
+
};
|
|
26672
26964
|
}
|
|
26673
|
-
|
|
26674
|
-
|
|
26675
|
-
|
|
26676
|
-
|
|
26677
|
-
|
|
26678
|
-
|
|
26679
|
-
|
|
26680
|
-
var SESSION_SURFACED_MEMORY_INTENTS = {
|
|
26681
|
-
startupPromptVisibleRecall: {
|
|
26682
|
-
writeProfile: {
|
|
26683
|
-
surface: "session-start",
|
|
26684
|
-
source: "startup-prompt-visible",
|
|
26685
|
-
ownership: "before-reset-owned"
|
|
26686
|
-
},
|
|
26687
|
-
read: {
|
|
26688
|
-
sharedSelectors: ["dedupe-relevant", "before-reset-owned"]
|
|
26689
|
-
},
|
|
26690
|
-
lifecycle: {
|
|
26691
|
-
ownership: "before-reset-owned",
|
|
26692
|
-
commandReset: "preserve",
|
|
26693
|
-
beforeResetFeedback: "owned"
|
|
26965
|
+
function querySessionSurfacedMemory(store, sessionKey) {
|
|
26966
|
+
const records = (filter = {}) => getSessionSurfacedMemoryLedgerRecords(store, sessionKey, filter);
|
|
26967
|
+
const entryIds = (filter = {}) => collectSessionSurfacedMemoryEntryIds(records(filter));
|
|
26968
|
+
const hasEntry = (entryId, filter = {}) => {
|
|
26969
|
+
const normalizedEntryId = normalizeSessionKey(entryId);
|
|
26970
|
+
if (!normalizedEntryId) {
|
|
26971
|
+
return false;
|
|
26694
26972
|
}
|
|
26695
|
-
|
|
26696
|
-
|
|
26697
|
-
|
|
26698
|
-
|
|
26699
|
-
|
|
26700
|
-
|
|
26701
|
-
|
|
26702
|
-
|
|
26703
|
-
|
|
26973
|
+
return records(filter).some((record) => record.entryId === normalizedEntryId);
|
|
26974
|
+
};
|
|
26975
|
+
const select = (selector) => {
|
|
26976
|
+
const filter = resolveSessionSurfacedMemorySelectorFilter(selector);
|
|
26977
|
+
return {
|
|
26978
|
+
records: () => records(filter),
|
|
26979
|
+
entryIds: () => entryIds(filter),
|
|
26980
|
+
hasEntry: (entryId) => hasEntry(entryId, filter)
|
|
26981
|
+
};
|
|
26982
|
+
};
|
|
26983
|
+
return {
|
|
26984
|
+
records,
|
|
26985
|
+
entryIds,
|
|
26986
|
+
hasEntry,
|
|
26987
|
+
select,
|
|
26988
|
+
dedupeEntryIds() {
|
|
26989
|
+
return select(SESSION_SURFACED_MEMORY_SELECTORS.dedupeRelevant).entryIds();
|
|
26704
26990
|
},
|
|
26705
|
-
|
|
26706
|
-
|
|
26707
|
-
commandReset: "clear",
|
|
26708
|
-
beforeResetFeedback: "excluded"
|
|
26709
|
-
}
|
|
26710
|
-
},
|
|
26711
|
-
agentToolRecallOutput: {
|
|
26712
|
-
writeProfile: {
|
|
26713
|
-
surface: "agent-tool-recall",
|
|
26714
|
-
source: "agent-tool-output",
|
|
26715
|
-
ownership: "dedupe-only"
|
|
26991
|
+
hasDedupeEntry(entryId) {
|
|
26992
|
+
return select(SESSION_SURFACED_MEMORY_SELECTORS.dedupeRelevant).hasEntry(entryId);
|
|
26716
26993
|
},
|
|
26717
|
-
|
|
26718
|
-
|
|
26994
|
+
beforeResetOwnedEntryIds() {
|
|
26995
|
+
return select(SESSION_SURFACED_MEMORY_SELECTORS.beforeResetOwned).entryIds();
|
|
26719
26996
|
},
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
|
|
26723
|
-
|
|
26997
|
+
clearDedupeOnly() {
|
|
26998
|
+
return removeSessionSurfacedMemoryLedgerEntries(store, sessionKey, {
|
|
26999
|
+
ownership: "dedupe-only"
|
|
27000
|
+
});
|
|
26724
27001
|
}
|
|
27002
|
+
};
|
|
27003
|
+
}
|
|
27004
|
+
|
|
27005
|
+
// src/edge/openclaw/session-state.ts
|
|
27006
|
+
var SKIP_SESSION_PATTERNS = [":subagent:", ":cron:"];
|
|
27007
|
+
var DEFAULT_MAX_SEEN_SESSIONS = 1e3;
|
|
27008
|
+
var DEFAULT_MID_SESSION_QUERY_SIMILARITY_THRESHOLD = 0.85;
|
|
27009
|
+
var SEEN_SESSION_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
27010
|
+
var CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
|
|
27011
|
+
var sessionStartLog = createLogger("session-start");
|
|
27012
|
+
var SESSION_STATE_GLOBALS_KEY = "__agenrOpenClawSessionState";
|
|
27013
|
+
function getSessionStateGlobals() {
|
|
27014
|
+
const globalState = globalThis;
|
|
27015
|
+
globalState[SESSION_STATE_GLOBALS_KEY] ??= {
|
|
27016
|
+
seenSessions: /* @__PURE__ */ new Map(),
|
|
27017
|
+
lastCleanupMs: 0,
|
|
27018
|
+
surfacedMemoryLedgerStore: {
|
|
27019
|
+
ledgers: /* @__PURE__ */ new Map(),
|
|
27020
|
+
aliases: /* @__PURE__ */ new Map()
|
|
27021
|
+
},
|
|
27022
|
+
sessionSignalState: /* @__PURE__ */ new Map(),
|
|
27023
|
+
handoffSeenSessionIds: /* @__PURE__ */ new Set(),
|
|
27024
|
+
sessionRef: { current: "" }
|
|
27025
|
+
};
|
|
27026
|
+
return globalState[SESSION_STATE_GLOBALS_KEY];
|
|
27027
|
+
}
|
|
27028
|
+
var sessionStateGlobals = getSessionStateGlobals();
|
|
27029
|
+
var seenSessions = sessionStateGlobals.seenSessions;
|
|
27030
|
+
var ledgerStore = sessionStateGlobals.surfacedMemoryLedgerStore;
|
|
27031
|
+
var sessionRef = sessionStateGlobals.sessionRef;
|
|
27032
|
+
var sessionSignalState = sessionStateGlobals.sessionSignalState;
|
|
27033
|
+
var handoffSeenSessionIds = sessionStateGlobals.handoffSeenSessionIds;
|
|
27034
|
+
function registerSessionSurfacedMemoryAlias2(aliasKey, sessionKey) {
|
|
27035
|
+
registerSessionSurfacedMemoryAlias(ledgerStore, aliasKey, sessionKey);
|
|
27036
|
+
}
|
|
27037
|
+
function recordSessionSurfacedMemoryByIntent2(sessionKey, writes, options = {}) {
|
|
27038
|
+
return recordSessionSurfacedMemoryByIntent(ledgerStore, sessionKey, writes, options);
|
|
27039
|
+
}
|
|
27040
|
+
function querySessionSurfacedMemory2(sessionKey) {
|
|
27041
|
+
return querySessionSurfacedMemory(ledgerStore, sessionKey);
|
|
27042
|
+
}
|
|
27043
|
+
function applySessionSurfacedMemoryCommandResetPolicy2(sessionKey) {
|
|
27044
|
+
return applySessionSurfacedMemoryCommandResetPolicy(ledgerStore, sessionKey);
|
|
27045
|
+
}
|
|
27046
|
+
function applySessionSurfacedMemoryBeforeResetPolicy2(sessionKey) {
|
|
27047
|
+
return applySessionSurfacedMemoryBeforeResetPolicy(ledgerStore, sessionKey);
|
|
27048
|
+
}
|
|
27049
|
+
function clearSessionSurfacedMemoryLedger2(sessionKey) {
|
|
27050
|
+
clearSessionSurfacedMemoryLedger(ledgerStore, sessionKey);
|
|
27051
|
+
}
|
|
27052
|
+
function hasHandoffSession(sessionId) {
|
|
27053
|
+
return handoffSeenSessionIds.has(sessionId);
|
|
27054
|
+
}
|
|
27055
|
+
function markHandoffSession(sessionId) {
|
|
27056
|
+
handoffSeenSessionIds.add(sessionId);
|
|
27057
|
+
}
|
|
27058
|
+
function deleteHandoffSession(sessionId) {
|
|
27059
|
+
handoffSeenSessionIds.delete(sessionId);
|
|
27060
|
+
}
|
|
27061
|
+
function resolveMaxSeenSessions() {
|
|
27062
|
+
const raw = process.env.AGENR_OPENCLAW_MAX_SEEN_SESSIONS;
|
|
27063
|
+
if (!raw) {
|
|
27064
|
+
return DEFAULT_MAX_SEEN_SESSIONS;
|
|
26725
27065
|
}
|
|
26726
|
-
|
|
26727
|
-
|
|
26728
|
-
|
|
26729
|
-
for (const [intent, definition] of Object.entries(SESSION_SURFACED_MEMORY_INTENTS)) {
|
|
26730
|
-
profiles[intent] = definition.writeProfile;
|
|
27066
|
+
const parsed = Number.parseInt(raw, 10);
|
|
27067
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
27068
|
+
return parsed;
|
|
26731
27069
|
}
|
|
26732
|
-
return
|
|
27070
|
+
return DEFAULT_MAX_SEEN_SESSIONS;
|
|
26733
27071
|
}
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
26740
|
-
|
|
26741
|
-
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
|
|
26745
|
-
|
|
26746
|
-
|
|
26747
|
-
|
|
26748
|
-
|
|
26749
|
-
|
|
27072
|
+
function canExecuteSql(db) {
|
|
27073
|
+
return typeof db?.execute === "function";
|
|
27074
|
+
}
|
|
27075
|
+
function trimSeenSessionCache() {
|
|
27076
|
+
const maxSeenSessions = resolveMaxSeenSessions();
|
|
27077
|
+
while (seenSessions.size > maxSeenSessions) {
|
|
27078
|
+
const oldestKey = seenSessions.keys().next().value;
|
|
27079
|
+
if (oldestKey === void 0) {
|
|
27080
|
+
break;
|
|
27081
|
+
}
|
|
27082
|
+
seenSessions.delete(oldestKey);
|
|
27083
|
+
}
|
|
27084
|
+
}
|
|
27085
|
+
function touchSeenSessionCache(sessionKey) {
|
|
27086
|
+
seenSessions.delete(sessionKey);
|
|
27087
|
+
seenSessions.set(sessionKey, true);
|
|
27088
|
+
trimSeenSessionCache();
|
|
27089
|
+
}
|
|
27090
|
+
async function hasSeenSessionInDb(db, sessionKey) {
|
|
27091
|
+
const result = await db.execute({
|
|
27092
|
+
sql: "SELECT 1 FROM seen_sessions WHERE dedupe_key = ? AND seen_at >= ? LIMIT 1",
|
|
27093
|
+
args: [sessionKey, Date.now() - SEEN_SESSION_RETENTION_MS]
|
|
27094
|
+
});
|
|
27095
|
+
return result.rows.length > 0;
|
|
27096
|
+
}
|
|
27097
|
+
async function cleanupSeenSessionsInDb(db, nowMs) {
|
|
27098
|
+
const maxSeenSessions = resolveMaxSeenSessions();
|
|
27099
|
+
await db.execute({
|
|
27100
|
+
sql: "DELETE FROM seen_sessions WHERE seen_at < ?",
|
|
27101
|
+
args: [nowMs - SEEN_SESSION_RETENTION_MS]
|
|
27102
|
+
});
|
|
27103
|
+
await db.execute({
|
|
27104
|
+
sql: `
|
|
27105
|
+
DELETE FROM seen_sessions
|
|
27106
|
+
WHERE dedupe_key IN (
|
|
27107
|
+
SELECT dedupe_key
|
|
27108
|
+
FROM seen_sessions
|
|
27109
|
+
ORDER BY seen_at DESC, dedupe_key DESC
|
|
27110
|
+
LIMIT -1 OFFSET ?
|
|
27111
|
+
)
|
|
27112
|
+
`,
|
|
27113
|
+
args: [maxSeenSessions]
|
|
27114
|
+
});
|
|
27115
|
+
}
|
|
27116
|
+
function shouldSkipSession(sessionKey) {
|
|
27117
|
+
return SKIP_SESSION_PATTERNS.some((pattern) => sessionKey.includes(pattern));
|
|
27118
|
+
}
|
|
27119
|
+
async function hasSeenSession(sessionKey, db) {
|
|
27120
|
+
let seen = seenSessions.has(sessionKey);
|
|
27121
|
+
if (seen) {
|
|
27122
|
+
touchSeenSessionCache(sessionKey);
|
|
27123
|
+
sessionStartLog.debug(
|
|
27124
|
+
`hasSeenSession key=${sessionKey.slice(0, 12)} found=${seen} mapSize=${seenSessions.size}`
|
|
27125
|
+
);
|
|
27126
|
+
return true;
|
|
26750
27127
|
}
|
|
26751
|
-
|
|
26752
|
-
|
|
26753
|
-
|
|
26754
|
-
|
|
26755
|
-
|
|
26756
|
-
while (store.ledgers.size > MAX_RECALLED_SESSIONS) {
|
|
26757
|
-
const oldestKey = store.ledgers.keys().next().value;
|
|
26758
|
-
if (oldestKey === void 0) {
|
|
26759
|
-
break;
|
|
26760
|
-
}
|
|
26761
|
-
store.ledgers.delete(oldestKey);
|
|
26762
|
-
for (const [alias, canonicalKey] of store.aliases) {
|
|
26763
|
-
if (canonicalKey === oldestKey) {
|
|
26764
|
-
store.aliases.delete(alias);
|
|
27128
|
+
if (canExecuteSql(db)) {
|
|
27129
|
+
try {
|
|
27130
|
+
seen = await hasSeenSessionInDb(db, sessionKey);
|
|
27131
|
+
if (seen) {
|
|
27132
|
+
touchSeenSessionCache(sessionKey);
|
|
26765
27133
|
}
|
|
27134
|
+
} catch (err) {
|
|
27135
|
+
sessionStartLog.warn(`hasSeenSession db lookup failed: ${toErrorMessage(err)}`);
|
|
26766
27136
|
}
|
|
26767
27137
|
}
|
|
27138
|
+
sessionStartLog.debug(
|
|
27139
|
+
`hasSeenSession key=${sessionKey.slice(0, 12)} found=${seen} mapSize=${seenSessions.size}`
|
|
27140
|
+
);
|
|
27141
|
+
return seen;
|
|
26768
27142
|
}
|
|
26769
|
-
function
|
|
26770
|
-
|
|
26771
|
-
if (!
|
|
27143
|
+
async function markSessionSeen(sessionKey, db) {
|
|
27144
|
+
touchSeenSessionCache(sessionKey);
|
|
27145
|
+
if (!canExecuteSql(db)) {
|
|
26772
27146
|
return;
|
|
26773
27147
|
}
|
|
26774
|
-
|
|
26775
|
-
|
|
26776
|
-
|
|
26777
|
-
|
|
26778
|
-
|
|
26779
|
-
|
|
26780
|
-
|
|
26781
|
-
|
|
26782
|
-
|
|
26783
|
-
}
|
|
26784
|
-
|
|
26785
|
-
|
|
26786
|
-
if (!canonicalKey) {
|
|
26787
|
-
return null;
|
|
26788
|
-
}
|
|
26789
|
-
const existing = store.ledgers.get(canonicalKey);
|
|
26790
|
-
if (existing) {
|
|
26791
|
-
touchSurfacedMemoryLedger(store, canonicalKey);
|
|
26792
|
-
return existing;
|
|
27148
|
+
const nowMs = Date.now();
|
|
27149
|
+
try {
|
|
27150
|
+
await db.execute({
|
|
27151
|
+
sql: "INSERT OR REPLACE INTO seen_sessions (dedupe_key, seen_at) VALUES (?, ?)",
|
|
27152
|
+
args: [sessionKey, nowMs]
|
|
27153
|
+
});
|
|
27154
|
+
if (nowMs - sessionStateGlobals.lastCleanupMs > CLEANUP_INTERVAL_MS) {
|
|
27155
|
+
await cleanupSeenSessionsInDb(db, nowMs);
|
|
27156
|
+
sessionStateGlobals.lastCleanupMs = nowMs;
|
|
27157
|
+
}
|
|
27158
|
+
} catch (err) {
|
|
27159
|
+
sessionStartLog.warn(`markSessionSeen db write failed: ${toErrorMessage(err)}`);
|
|
26793
27160
|
}
|
|
26794
|
-
const ledger = {
|
|
26795
|
-
nextSequence: 1,
|
|
26796
|
-
records: []
|
|
26797
|
-
};
|
|
26798
|
-
store.ledgers.set(canonicalKey, ledger);
|
|
26799
|
-
trimSurfacedMemoryLedgers(store);
|
|
26800
|
-
return ledger;
|
|
26801
27161
|
}
|
|
26802
|
-
function
|
|
26803
|
-
if (
|
|
26804
|
-
return
|
|
26805
|
-
}
|
|
26806
|
-
if (filter.source && record.source !== filter.source) {
|
|
26807
|
-
return false;
|
|
26808
|
-
}
|
|
26809
|
-
if (filter.ownership && record.ownership !== filter.ownership) {
|
|
26810
|
-
return false;
|
|
27162
|
+
function resolveMidSessionLimit(raw, fallback) {
|
|
27163
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
27164
|
+
return fallback;
|
|
26811
27165
|
}
|
|
26812
|
-
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
const normalized = [];
|
|
26816
|
-
const seenSignatures = /* @__PURE__ */ new Set();
|
|
26817
|
-
for (const input of inputs) {
|
|
26818
|
-
const entryId = normalizeSessionKey(input.entryId);
|
|
26819
|
-
if (!entryId) {
|
|
26820
|
-
continue;
|
|
26821
|
-
}
|
|
26822
|
-
const signature = [
|
|
26823
|
-
entryId,
|
|
26824
|
-
input.surface,
|
|
26825
|
-
input.source,
|
|
26826
|
-
input.ownership
|
|
26827
|
-
].join("\0");
|
|
26828
|
-
if (seenSignatures.has(signature)) {
|
|
26829
|
-
continue;
|
|
26830
|
-
}
|
|
26831
|
-
seenSignatures.add(signature);
|
|
26832
|
-
normalized.push({
|
|
26833
|
-
entryId,
|
|
26834
|
-
surface: input.surface,
|
|
26835
|
-
source: input.source,
|
|
26836
|
-
ownership: input.ownership
|
|
26837
|
-
});
|
|
27166
|
+
const normalized = Math.trunc(raw);
|
|
27167
|
+
if (normalized <= 0) {
|
|
27168
|
+
return fallback;
|
|
26838
27169
|
}
|
|
26839
27170
|
return normalized;
|
|
26840
27171
|
}
|
|
26841
|
-
function
|
|
26842
|
-
|
|
26843
|
-
|
|
26844
|
-
for (const entryId of write.entryIds) {
|
|
26845
|
-
inputs.push({
|
|
26846
|
-
entryId,
|
|
26847
|
-
surface: write.surface,
|
|
26848
|
-
source: write.source,
|
|
26849
|
-
ownership: write.ownership
|
|
26850
|
-
});
|
|
26851
|
-
}
|
|
27172
|
+
function resolveMidSessionSimilarityThreshold(raw) {
|
|
27173
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
27174
|
+
return DEFAULT_MID_SESSION_QUERY_SIMILARITY_THRESHOLD;
|
|
26852
27175
|
}
|
|
26853
|
-
|
|
26854
|
-
|
|
26855
|
-
|
|
26856
|
-
return
|
|
27176
|
+
if (raw < 0 || raw > 1) {
|
|
27177
|
+
return DEFAULT_MID_SESSION_QUERY_SIMILARITY_THRESHOLD;
|
|
27178
|
+
}
|
|
27179
|
+
return raw;
|
|
26857
27180
|
}
|
|
26858
|
-
function
|
|
26859
|
-
|
|
26860
|
-
|
|
26861
|
-
|
|
26862
|
-
|
|
26863
|
-
|
|
26864
|
-
|
|
26865
|
-
|
|
26866
|
-
};
|
|
27181
|
+
function clearSessionState() {
|
|
27182
|
+
seenSessions.clear();
|
|
27183
|
+
sessionStateGlobals.lastCleanupMs = 0;
|
|
27184
|
+
sessionSignalState.clear();
|
|
27185
|
+
handoffSeenSessionIds.clear();
|
|
27186
|
+
ledgerStore.ledgers.clear();
|
|
27187
|
+
ledgerStore.aliases.clear();
|
|
27188
|
+
sessionRef.current = "";
|
|
26867
27189
|
}
|
|
26868
|
-
|
|
26869
|
-
|
|
26870
|
-
|
|
26871
|
-
|
|
26872
|
-
|
|
26873
|
-
|
|
27190
|
+
|
|
27191
|
+
// src/edge/openclaw/memory-index.ts
|
|
27192
|
+
init_inbound();
|
|
27193
|
+
init_errors();
|
|
27194
|
+
var MEMORY_INDEX_TIMEOUT_MS = 1e4;
|
|
27195
|
+
var MEMORY_INDEX_TIMEOUT = /* @__PURE__ */ Symbol("memory-index-timeout");
|
|
27196
|
+
function normalizeDate(value) {
|
|
27197
|
+
const raw = typeof value === "string" ? value.trim() : "";
|
|
27198
|
+
if (!raw) {
|
|
27199
|
+
return "";
|
|
26874
27200
|
}
|
|
26875
|
-
|
|
26876
|
-
|
|
27201
|
+
const parsed = new Date(raw);
|
|
27202
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
27203
|
+
return raw;
|
|
26877
27204
|
}
|
|
26878
|
-
return
|
|
26879
|
-
source: selector.source
|
|
26880
|
-
};
|
|
27205
|
+
return parsed.toISOString();
|
|
26881
27206
|
}
|
|
26882
|
-
function
|
|
26883
|
-
|
|
26884
|
-
|
|
26885
|
-
normalized.push({
|
|
26886
|
-
entryIds: write.entryIds,
|
|
26887
|
-
...resolveSessionSurfacedMemoryIntent(write.intent).writeProfile
|
|
26888
|
-
});
|
|
27207
|
+
function toProjectName(value) {
|
|
27208
|
+
if (value === null || value === void 0) {
|
|
27209
|
+
return null;
|
|
26889
27210
|
}
|
|
26890
|
-
|
|
27211
|
+
const normalized = String(value).trim();
|
|
27212
|
+
return normalized.length > 0 ? normalized : null;
|
|
26891
27213
|
}
|
|
26892
|
-
function
|
|
26893
|
-
|
|
26894
|
-
|
|
27214
|
+
function toCount(value) {
|
|
27215
|
+
const parsed = Number(value);
|
|
27216
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
27217
|
+
return 0;
|
|
26895
27218
|
}
|
|
26896
|
-
return
|
|
27219
|
+
return Math.floor(parsed);
|
|
26897
27220
|
}
|
|
26898
|
-
function
|
|
26899
|
-
if (
|
|
26900
|
-
return
|
|
27221
|
+
function normalizeProjectIndexEntry(value) {
|
|
27222
|
+
if (!value || typeof value !== "object") {
|
|
27223
|
+
return null;
|
|
26901
27224
|
}
|
|
26902
|
-
|
|
27225
|
+
const row = value;
|
|
27226
|
+
return {
|
|
27227
|
+
project: toProjectName(row.project),
|
|
27228
|
+
count: toCount(row.count),
|
|
27229
|
+
lastUpdated: normalizeDate(row.lastUpdated ?? row.last_updated)
|
|
27230
|
+
};
|
|
26903
27231
|
}
|
|
26904
|
-
function
|
|
26905
|
-
|
|
26906
|
-
|
|
26907
|
-
if (!normalizedAlias || !normalizedSessionKey) {
|
|
26908
|
-
return;
|
|
26909
|
-
}
|
|
26910
|
-
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, normalizedSessionKey);
|
|
26911
|
-
store.aliases.set(normalizedAlias, canonicalKey);
|
|
26912
|
-
if (normalizedAlias === canonicalKey) {
|
|
26913
|
-
return;
|
|
26914
|
-
}
|
|
26915
|
-
const aliasLedger = store.ledgers.get(normalizedAlias);
|
|
26916
|
-
if (!aliasLedger) {
|
|
26917
|
-
return;
|
|
27232
|
+
function parseMemoryIndex(value) {
|
|
27233
|
+
if (!value || typeof value !== "object") {
|
|
27234
|
+
return null;
|
|
26918
27235
|
}
|
|
26919
|
-
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
return;
|
|
27236
|
+
const response = value;
|
|
27237
|
+
if (!Array.isArray(response.projects)) {
|
|
27238
|
+
return null;
|
|
26923
27239
|
}
|
|
26924
|
-
const
|
|
26925
|
-
|
|
26926
|
-
);
|
|
26927
|
-
|
|
26928
|
-
|
|
26929
|
-
|
|
26930
|
-
|
|
26931
|
-
|
|
26932
|
-
|
|
26933
|
-
|
|
26934
|
-
|
|
26935
|
-
|
|
27240
|
+
const projects = response.projects.map((entry) => normalizeProjectIndexEntry(entry)).filter((entry) => entry !== null);
|
|
27241
|
+
const totalFromResponse = Number(response.totalEntries);
|
|
27242
|
+
const totalEntries = Number.isFinite(totalFromResponse) && totalFromResponse >= 0 ? Math.floor(totalFromResponse) : projects.reduce((sum, project) => sum + project.count, 0);
|
|
27243
|
+
return { projects, totalEntries };
|
|
27244
|
+
}
|
|
27245
|
+
async function runMemoryIndex(dbPath, executionOptions) {
|
|
27246
|
+
try {
|
|
27247
|
+
const timeoutMs = executionOptions?.timeoutMs ?? MEMORY_INDEX_TIMEOUT_MS;
|
|
27248
|
+
const result = await withTimeout(
|
|
27249
|
+
loadMemoryIndex(dbPath, executionOptions?.recallServiceDeps),
|
|
27250
|
+
timeoutMs
|
|
27251
|
+
);
|
|
27252
|
+
if (result === MEMORY_INDEX_TIMEOUT) {
|
|
27253
|
+
return { status: "timeout" };
|
|
26936
27254
|
}
|
|
26937
|
-
|
|
26938
|
-
|
|
26939
|
-
|
|
26940
|
-
|
|
26941
|
-
|
|
27255
|
+
return result;
|
|
27256
|
+
} catch (error) {
|
|
27257
|
+
return {
|
|
27258
|
+
status: "error",
|
|
27259
|
+
error: toErrorMessage(error)
|
|
27260
|
+
};
|
|
26942
27261
|
}
|
|
26943
|
-
touchSurfacedMemoryLedger(store, canonicalKey);
|
|
26944
27262
|
}
|
|
26945
|
-
function
|
|
26946
|
-
const
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
|
|
26951
|
-
const existingSignatures = new Set(
|
|
26952
|
-
ledger.records.map((record) => [record.entryId, record.surface, record.source, record.ownership].join("\0"))
|
|
27263
|
+
async function loadMemoryIndex(dbPath, recallServiceDeps) {
|
|
27264
|
+
const index = parseMemoryIndex(
|
|
27265
|
+
await buildRecallIndex(
|
|
27266
|
+
{ dbPath },
|
|
27267
|
+
{ runtime: adaptLegacyRecallRuntimeDeps(recallServiceDeps) }
|
|
27268
|
+
)
|
|
26953
27269
|
);
|
|
26954
|
-
|
|
26955
|
-
|
|
26956
|
-
|
|
26957
|
-
|
|
26958
|
-
|
|
26959
|
-
|
|
26960
|
-
|
|
26961
|
-
|
|
26962
|
-
|
|
26963
|
-
|
|
26964
|
-
|
|
26965
|
-
|
|
26966
|
-
|
|
26967
|
-
|
|
26968
|
-
|
|
27270
|
+
if (!index) {
|
|
27271
|
+
return { status: "invalid" };
|
|
27272
|
+
}
|
|
27273
|
+
return {
|
|
27274
|
+
status: "ok",
|
|
27275
|
+
index
|
|
27276
|
+
};
|
|
27277
|
+
}
|
|
27278
|
+
async function withTimeout(promise, timeoutMs) {
|
|
27279
|
+
let timer;
|
|
27280
|
+
try {
|
|
27281
|
+
return await new Promise((resolve, reject) => {
|
|
27282
|
+
timer = setTimeout(() => {
|
|
27283
|
+
resolve(MEMORY_INDEX_TIMEOUT);
|
|
27284
|
+
}, timeoutMs);
|
|
27285
|
+
promise.then(resolve).catch(reject);
|
|
26969
27286
|
});
|
|
26970
|
-
|
|
27287
|
+
} finally {
|
|
27288
|
+
if (timer) {
|
|
27289
|
+
clearTimeout(timer);
|
|
27290
|
+
}
|
|
26971
27291
|
}
|
|
26972
|
-
|
|
26973
|
-
|
|
27292
|
+
}
|
|
27293
|
+
function formatMemoryIndex(index) {
|
|
27294
|
+
if (index.projects.length === 0) {
|
|
27295
|
+
return "";
|
|
26974
27296
|
}
|
|
26975
|
-
|
|
27297
|
+
const lines = ["## Memory Index", ""];
|
|
27298
|
+
for (const project of index.projects) {
|
|
27299
|
+
const name = project.project ?? "(universal)";
|
|
27300
|
+
const date = project.lastUpdated ? project.lastUpdated.slice(0, 10) : "unknown";
|
|
27301
|
+
lines.push(`- ${name}: ${project.count} entries (last updated ${date})`);
|
|
27302
|
+
}
|
|
27303
|
+
lines.push("");
|
|
27304
|
+
lines.push(`Total: ${index.totalEntries} entries`);
|
|
27305
|
+
return lines.join("\n");
|
|
26976
27306
|
}
|
|
26977
|
-
|
|
26978
|
-
|
|
26979
|
-
|
|
26980
|
-
|
|
27307
|
+
|
|
27308
|
+
// src/edge/openclaw/recall.ts
|
|
27309
|
+
var RECALL_QUERY_MAX_CHARS = 500;
|
|
27310
|
+
var DEFAULT_LIMIT3 = 10;
|
|
27311
|
+
function resolveUpdateMetadata(isBrowse, isSessionStart, requestOverrides) {
|
|
27312
|
+
const metadataMode = requestOverrides?.metadataMode ?? "non-browse";
|
|
27313
|
+
return requestOverrides?.noUpdate !== true && !isBrowse && (metadataMode === "non-browse" || metadataMode === "session-start" && isSessionStart);
|
|
27314
|
+
}
|
|
27315
|
+
function resolveRuntimeDeps(executionOptions) {
|
|
27316
|
+
return adaptLegacyRecallRuntimeDeps(executionOptions?.recallServiceDeps);
|
|
27317
|
+
}
|
|
27318
|
+
function resolveRecallMetadata(requestOverrides) {
|
|
27319
|
+
if (requestOverrides?.metadata) {
|
|
27320
|
+
return requestOverrides.metadata;
|
|
26981
27321
|
}
|
|
26982
|
-
const
|
|
26983
|
-
|
|
26984
|
-
|
|
26985
|
-
|
|
26986
|
-
|
|
26987
|
-
|
|
27322
|
+
const sessionId = sessionRef.current.trim();
|
|
27323
|
+
if (!sessionId) {
|
|
27324
|
+
return void 0;
|
|
27325
|
+
}
|
|
27326
|
+
return {
|
|
27327
|
+
sessionId,
|
|
27328
|
+
platform: "openclaw"
|
|
27329
|
+
};
|
|
27330
|
+
}
|
|
27331
|
+
function buildRecallRequest(budget, project, query, options) {
|
|
27332
|
+
const isBrowse = options?.context === "browse";
|
|
27333
|
+
const isSessionStart = options?.context === "session-start";
|
|
27334
|
+
const trimmedQuery = query?.trim() ?? "";
|
|
27335
|
+
const queryText = trimmedQuery.length > RECALL_QUERY_MAX_CHARS ? trimmedQuery.slice(0, RECALL_QUERY_MAX_CHARS) : trimmedQuery;
|
|
27336
|
+
const inferredSessionStart = isSessionStart || queryText.length === 0;
|
|
27337
|
+
const universalOnly = options?.nullProjectOnly === true || options?.universalOnly === true;
|
|
27338
|
+
const explicitWildcardProject = (options?.project ?? []).some(
|
|
27339
|
+
(candidate) => candidate.split(",").some((part) => part.trim() === "*")
|
|
27340
|
+
);
|
|
27341
|
+
const normalizedProjects = Array.from(new Set(
|
|
27342
|
+
(options?.project ?? []).map((candidate) => candidate.trim()).filter((candidate) => candidate.length > 0)
|
|
27343
|
+
));
|
|
27344
|
+
const explicitProjectSelection = explicitWildcardProject || normalizedProjects.length > 0;
|
|
27345
|
+
const normalizedProjectHints = parseProjectList(options?.projectHints).filter((projectHint) => projectHint !== "*");
|
|
27346
|
+
const resolvedProject = universalOnly ? void 0 : explicitProjectSelection ? normalizedProjects.length > 0 ? normalizedProjects : void 0 : !project ? void 0 : [project];
|
|
27347
|
+
const resolvedProjectStrict = universalOnly ? void 0 : explicitProjectSelection ? options?.projectStrict ? true : void 0 : project && !universalOnly ? true : void 0;
|
|
27348
|
+
const resolvedProjectHints = universalOnly || explicitProjectSelection || explicitWildcardProject ? void 0 : normalizedProjectHints.length > 0 ? normalizedProjectHints : void 0;
|
|
27349
|
+
return {
|
|
27350
|
+
isBrowse,
|
|
27351
|
+
isSessionStart: !isBrowse && inferredSessionStart,
|
|
27352
|
+
limit: options?.limit ?? DEFAULT_LIMIT3,
|
|
27353
|
+
queryText,
|
|
27354
|
+
since: options?.since,
|
|
27355
|
+
types: options?.types,
|
|
27356
|
+
until: options?.until,
|
|
27357
|
+
around: options?.around,
|
|
27358
|
+
aroundRadius: options?.aroundRadius,
|
|
27359
|
+
platform: options?.platform,
|
|
27360
|
+
minImportance: options?.minImportance,
|
|
27361
|
+
expiry: options?.expiry,
|
|
27362
|
+
universalOnly,
|
|
27363
|
+
project: resolvedProject,
|
|
27364
|
+
projectStrict: resolvedProjectStrict,
|
|
27365
|
+
projectHints: resolvedProjectHints,
|
|
27366
|
+
budget: isBrowse ? void 0 : options?.budget === null ? void 0 : options?.budget ?? budget
|
|
27367
|
+
};
|
|
27368
|
+
}
|
|
27369
|
+
function shapeRecallResult(result) {
|
|
27370
|
+
return {
|
|
27371
|
+
query: result.payload.query,
|
|
27372
|
+
decision: result.payload.decision,
|
|
27373
|
+
rollupSummary: result.payload.rollup_summary,
|
|
27374
|
+
surfaceSelection: result.surfaceSelection,
|
|
27375
|
+
results: result.payload.results.map((item) => ({
|
|
27376
|
+
entry: item.entry,
|
|
27377
|
+
score: item.score,
|
|
27378
|
+
category: item.category
|
|
26988
27379
|
}))
|
|
26989
27380
|
};
|
|
26990
|
-
ledger.nextSequence = ledger.records.length + 1;
|
|
26991
|
-
store.ledgers.delete(canonicalKey);
|
|
26992
|
-
store.ledgers.set(canonicalKey, ledger);
|
|
26993
|
-
trimSurfacedMemoryLedgers(store);
|
|
26994
27381
|
}
|
|
26995
|
-
function
|
|
26996
|
-
|
|
26997
|
-
|
|
27382
|
+
async function runRecallStrict(budget, project, query, options, dbPath, executionOptions) {
|
|
27383
|
+
return executeRecallRequest(
|
|
27384
|
+
budget,
|
|
27385
|
+
project,
|
|
27386
|
+
query,
|
|
27387
|
+
options,
|
|
27388
|
+
dbPath,
|
|
27389
|
+
executionOptions
|
|
26998
27390
|
);
|
|
26999
|
-
if (normalizedInputs.length === 0) {
|
|
27000
|
-
if (options.mode === "replace") {
|
|
27001
|
-
replaceSessionSurfacedMemoryLedgerEntries(store, sessionKey, []);
|
|
27002
|
-
}
|
|
27003
|
-
return 0;
|
|
27004
|
-
}
|
|
27005
|
-
if (options.mode === "replace") {
|
|
27006
|
-
replaceSessionSurfacedMemoryLedgerEntries(store, sessionKey, normalizedInputs);
|
|
27007
|
-
return normalizedInputs.length;
|
|
27008
|
-
}
|
|
27009
|
-
return appendSessionSurfacedMemoryLedgerEntries(store, sessionKey, normalizedInputs);
|
|
27010
27391
|
}
|
|
27011
|
-
function
|
|
27012
|
-
|
|
27013
|
-
|
|
27014
|
-
|
|
27015
|
-
|
|
27016
|
-
|
|
27017
|
-
|
|
27018
|
-
|
|
27392
|
+
async function executeRecallRequest(budget, project, query, options, dbPath, executionOptions) {
|
|
27393
|
+
const request = buildRecallRequest(budget, project, query, options);
|
|
27394
|
+
const runtime = resolveRuntimeDeps(executionOptions);
|
|
27395
|
+
const updateMetadata = resolveUpdateMetadata(
|
|
27396
|
+
request.isBrowse,
|
|
27397
|
+
request.isSessionStart,
|
|
27398
|
+
executionOptions?.requestOverrides
|
|
27399
|
+
);
|
|
27400
|
+
const metadata = resolveRecallMetadata(executionOptions?.requestOverrides);
|
|
27401
|
+
const result = request.isBrowse ? await browseRecall(
|
|
27402
|
+
{
|
|
27403
|
+
dbPath,
|
|
27404
|
+
limit: request.limit,
|
|
27405
|
+
threshold: 0,
|
|
27406
|
+
types: request.types,
|
|
27407
|
+
since: request.since,
|
|
27408
|
+
until: request.until,
|
|
27409
|
+
around: request.around,
|
|
27410
|
+
aroundRadius: request.aroundRadius,
|
|
27411
|
+
platform: request.platform,
|
|
27412
|
+
minImportance: request.minImportance,
|
|
27413
|
+
expiry: request.expiry,
|
|
27414
|
+
project: request.project,
|
|
27415
|
+
projectStrict: request.projectStrict,
|
|
27416
|
+
projectHints: request.projectHints,
|
|
27417
|
+
universalOnly: request.universalOnly,
|
|
27418
|
+
includeRecallCounts: false
|
|
27419
|
+
},
|
|
27420
|
+
{ runtime }
|
|
27421
|
+
) : request.isSessionStart ? await sessionStartRecall(
|
|
27422
|
+
{
|
|
27423
|
+
dbPath,
|
|
27424
|
+
limit: request.limit,
|
|
27425
|
+
budget: request.budget,
|
|
27426
|
+
threshold: 0,
|
|
27427
|
+
types: request.types,
|
|
27428
|
+
since: request.since,
|
|
27429
|
+
until: request.until,
|
|
27430
|
+
around: request.around,
|
|
27431
|
+
aroundRadius: request.aroundRadius,
|
|
27432
|
+
platform: request.platform,
|
|
27433
|
+
minImportance: request.minImportance,
|
|
27434
|
+
expiry: request.expiry,
|
|
27435
|
+
project: request.project,
|
|
27436
|
+
projectStrict: request.projectStrict,
|
|
27437
|
+
projectHints: request.projectHints,
|
|
27438
|
+
universalOnly: request.universalOnly,
|
|
27439
|
+
updateMetadata,
|
|
27440
|
+
metadata,
|
|
27441
|
+
includeRecallCounts: false
|
|
27442
|
+
},
|
|
27443
|
+
{ runtime }
|
|
27444
|
+
) : await searchRecall(
|
|
27445
|
+
{
|
|
27446
|
+
dbPath,
|
|
27447
|
+
queryText: request.queryText,
|
|
27448
|
+
queryContext: "default",
|
|
27449
|
+
limit: request.limit,
|
|
27450
|
+
budget: request.budget,
|
|
27451
|
+
threshold: 0,
|
|
27452
|
+
types: request.types,
|
|
27453
|
+
since: request.since,
|
|
27454
|
+
until: request.until,
|
|
27455
|
+
around: request.around,
|
|
27456
|
+
aroundRadius: request.aroundRadius,
|
|
27457
|
+
platform: request.platform,
|
|
27458
|
+
minImportance: request.minImportance,
|
|
27459
|
+
expiry: request.expiry,
|
|
27460
|
+
project: request.project,
|
|
27461
|
+
projectStrict: request.projectStrict,
|
|
27462
|
+
projectHints: request.projectHints,
|
|
27463
|
+
universalOnly: request.universalOnly,
|
|
27464
|
+
updateMetadata,
|
|
27465
|
+
metadata,
|
|
27466
|
+
includeRecallCounts: false
|
|
27467
|
+
},
|
|
27468
|
+
{ runtime }
|
|
27019
27469
|
);
|
|
27470
|
+
return shapeRecallResult(result);
|
|
27020
27471
|
}
|
|
27021
|
-
function
|
|
27022
|
-
|
|
27023
|
-
|
|
27024
|
-
|
|
27025
|
-
|
|
27026
|
-
const ledger = store.ledgers.get(canonicalKey);
|
|
27027
|
-
return ledger ? [...ledger.records] : [];
|
|
27028
|
-
}
|
|
27029
|
-
function getSessionSurfacedMemoryLedgerRecords(store, sessionKey, filter = {}) {
|
|
27030
|
-
return getSessionSurfacedMemoryLedger(store, sessionKey).filter((record) => matchesSurfacedMemoryLedgerFilter(record, filter));
|
|
27031
|
-
}
|
|
27032
|
-
function collectSessionSurfacedMemoryEntryIds(records) {
|
|
27033
|
-
const surfacedEntryIds = [];
|
|
27034
|
-
const seenEntryIds = /* @__PURE__ */ new Set();
|
|
27035
|
-
for (const record of records) {
|
|
27036
|
-
if (seenEntryIds.has(record.entryId)) {
|
|
27037
|
-
continue;
|
|
27038
|
-
}
|
|
27039
|
-
seenEntryIds.add(record.entryId);
|
|
27040
|
-
surfacedEntryIds.push(record.entryId);
|
|
27041
|
-
}
|
|
27042
|
-
return surfacedEntryIds;
|
|
27043
|
-
}
|
|
27044
|
-
function removeSessionSurfacedMemoryLedgerEntries(store, sessionKey, filter = {}) {
|
|
27045
|
-
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, sessionKey);
|
|
27046
|
-
if (!canonicalKey) {
|
|
27047
|
-
return 0;
|
|
27048
|
-
}
|
|
27049
|
-
const ledger = store.ledgers.get(canonicalKey);
|
|
27050
|
-
if (!ledger) {
|
|
27051
|
-
return 0;
|
|
27052
|
-
}
|
|
27053
|
-
const keptRecords = ledger.records.filter((record) => !matchesSurfacedMemoryLedgerFilter(record, filter));
|
|
27054
|
-
const removedCount = ledger.records.length - keptRecords.length;
|
|
27055
|
-
if (removedCount === 0) {
|
|
27056
|
-
return 0;
|
|
27057
|
-
}
|
|
27058
|
-
if (keptRecords.length === 0) {
|
|
27059
|
-
clearSessionSurfacedMemoryLedger(store, sessionKey);
|
|
27060
|
-
return removedCount;
|
|
27061
|
-
}
|
|
27062
|
-
ledger.records = keptRecords;
|
|
27063
|
-
touchSurfacedMemoryLedger(store, canonicalKey);
|
|
27064
|
-
return removedCount;
|
|
27065
|
-
}
|
|
27066
|
-
function clearSessionSurfacedMemoryLedger(store, sessionKey) {
|
|
27067
|
-
const normalizedKey = normalizeSessionKey(sessionKey);
|
|
27068
|
-
if (!normalizedKey) {
|
|
27069
|
-
return;
|
|
27070
|
-
}
|
|
27071
|
-
const canonicalKey = resolveCanonicalSurfacedMemoryLedgerKey(store, normalizedKey);
|
|
27072
|
-
if (canonicalKey) {
|
|
27073
|
-
store.ledgers.delete(canonicalKey);
|
|
27074
|
-
}
|
|
27075
|
-
for (const [alias, targetKey] of store.aliases) {
|
|
27076
|
-
if (alias === normalizedKey || targetKey === canonicalKey) {
|
|
27077
|
-
store.aliases.delete(alias);
|
|
27078
|
-
}
|
|
27472
|
+
async function runRecall(budget, project, query, options, dbPath, executionOptions) {
|
|
27473
|
+
try {
|
|
27474
|
+
return await executeRecallRequest(budget, project, query, options, dbPath, executionOptions);
|
|
27475
|
+
} catch {
|
|
27476
|
+
return null;
|
|
27079
27477
|
}
|
|
27080
27478
|
}
|
|
27081
|
-
|
|
27082
|
-
|
|
27083
|
-
|
|
27479
|
+
async function fetchCoreEntries(coreProjects, dbPath, executionOptions) {
|
|
27480
|
+
let result;
|
|
27481
|
+
try {
|
|
27482
|
+
result = shapeRecallResult(
|
|
27483
|
+
await loadCoreRecall(
|
|
27484
|
+
{ dbPath },
|
|
27485
|
+
{ runtime: resolveRuntimeDeps(executionOptions) }
|
|
27486
|
+
)
|
|
27487
|
+
);
|
|
27488
|
+
} catch {
|
|
27489
|
+
return null;
|
|
27084
27490
|
}
|
|
27085
|
-
|
|
27086
|
-
|
|
27087
|
-
return removeSessionSurfacedMemoryLedgerEntries(
|
|
27088
|
-
store,
|
|
27089
|
-
sessionKey,
|
|
27090
|
-
SURFACED_MEMORY_LIFECYCLE_FILTERS.commandResetCleared
|
|
27491
|
+
const allowedProjects = new Set(
|
|
27492
|
+
coreProjects.map((project) => project.trim()).filter((project) => project.length > 0)
|
|
27091
27493
|
);
|
|
27092
|
-
|
|
27093
|
-
|
|
27094
|
-
|
|
27095
|
-
|
|
27096
|
-
|
|
27097
|
-
return {
|
|
27098
|
-
beforeResetOwnedEntryIds,
|
|
27099
|
-
clearedCount
|
|
27100
|
-
};
|
|
27101
|
-
}
|
|
27102
|
-
function querySessionSurfacedMemory(store, sessionKey) {
|
|
27103
|
-
const records = (filter = {}) => getSessionSurfacedMemoryLedgerRecords(store, sessionKey, filter);
|
|
27104
|
-
const entryIds = (filter = {}) => collectSessionSurfacedMemoryEntryIds(records(filter));
|
|
27105
|
-
const hasEntry = (entryId, filter = {}) => {
|
|
27106
|
-
const normalizedEntryId = normalizeSessionKey(entryId);
|
|
27107
|
-
if (!normalizedEntryId) {
|
|
27108
|
-
return false;
|
|
27494
|
+
const filteredResults = result.results.filter((item) => {
|
|
27495
|
+
const rawProject = item.entry?.project;
|
|
27496
|
+
const project = typeof rawProject === "string" ? rawProject.trim() : "";
|
|
27497
|
+
if (!project) {
|
|
27498
|
+
return true;
|
|
27109
27499
|
}
|
|
27110
|
-
return
|
|
27111
|
-
};
|
|
27112
|
-
const select = (selector) => {
|
|
27113
|
-
const filter = resolveSessionSurfacedMemorySelectorFilter(selector);
|
|
27114
|
-
return {
|
|
27115
|
-
records: () => records(filter),
|
|
27116
|
-
entryIds: () => entryIds(filter),
|
|
27117
|
-
hasEntry: (entryId) => hasEntry(entryId, filter)
|
|
27118
|
-
};
|
|
27119
|
-
};
|
|
27500
|
+
return allowedProjects.has(project);
|
|
27501
|
+
});
|
|
27120
27502
|
return {
|
|
27121
|
-
|
|
27122
|
-
|
|
27123
|
-
hasEntry,
|
|
27124
|
-
select,
|
|
27125
|
-
dedupeEntryIds() {
|
|
27126
|
-
return select(SESSION_SURFACED_MEMORY_SELECTORS.dedupeRelevant).entryIds();
|
|
27127
|
-
},
|
|
27128
|
-
hasDedupeEntry(entryId) {
|
|
27129
|
-
return select(SESSION_SURFACED_MEMORY_SELECTORS.dedupeRelevant).hasEntry(entryId);
|
|
27130
|
-
},
|
|
27131
|
-
beforeResetOwnedEntryIds() {
|
|
27132
|
-
return select(SESSION_SURFACED_MEMORY_SELECTORS.beforeResetOwned).entryIds();
|
|
27133
|
-
},
|
|
27134
|
-
clearDedupeOnly() {
|
|
27135
|
-
return removeSessionSurfacedMemoryLedgerEntries(store, sessionKey, {
|
|
27136
|
-
ownership: "dedupe-only"
|
|
27137
|
-
});
|
|
27138
|
-
}
|
|
27503
|
+
...result,
|
|
27504
|
+
results: filteredResults.slice(0, 30)
|
|
27139
27505
|
};
|
|
27140
27506
|
}
|
|
27141
27507
|
|
|
27142
|
-
// src/edge/openclaw/session-
|
|
27143
|
-
|
|
27144
|
-
|
|
27145
|
-
|
|
27146
|
-
|
|
27147
|
-
|
|
27148
|
-
|
|
27149
|
-
var
|
|
27150
|
-
|
|
27151
|
-
|
|
27152
|
-
|
|
27153
|
-
|
|
27154
|
-
|
|
27155
|
-
|
|
27156
|
-
|
|
27157
|
-
aliases: /* @__PURE__ */ new Map()
|
|
27158
|
-
},
|
|
27159
|
-
sessionSignalState: /* @__PURE__ */ new Map(),
|
|
27160
|
-
handoffSeenSessionIds: /* @__PURE__ */ new Set(),
|
|
27161
|
-
sessionRef: { current: "" }
|
|
27162
|
-
};
|
|
27163
|
-
return globalState[SESSION_STATE_GLOBALS_KEY];
|
|
27164
|
-
}
|
|
27165
|
-
var sessionStateGlobals = getSessionStateGlobals();
|
|
27166
|
-
var seenSessions = sessionStateGlobals.seenSessions;
|
|
27167
|
-
var ledgerStore = sessionStateGlobals.surfacedMemoryLedgerStore;
|
|
27168
|
-
var sessionRef = sessionStateGlobals.sessionRef;
|
|
27169
|
-
var sessionSignalState = sessionStateGlobals.sessionSignalState;
|
|
27170
|
-
var handoffSeenSessionIds = sessionStateGlobals.handoffSeenSessionIds;
|
|
27171
|
-
function registerSessionSurfacedMemoryAlias2(aliasKey, sessionKey) {
|
|
27172
|
-
registerSessionSurfacedMemoryAlias(ledgerStore, aliasKey, sessionKey);
|
|
27173
|
-
}
|
|
27174
|
-
function recordSessionSurfacedMemoryByIntent2(sessionKey, writes, options = {}) {
|
|
27175
|
-
return recordSessionSurfacedMemoryByIntent(ledgerStore, sessionKey, writes, options);
|
|
27176
|
-
}
|
|
27177
|
-
function querySessionSurfacedMemory2(sessionKey) {
|
|
27178
|
-
return querySessionSurfacedMemory(ledgerStore, sessionKey);
|
|
27179
|
-
}
|
|
27180
|
-
function applySessionSurfacedMemoryCommandResetPolicy2(sessionKey) {
|
|
27181
|
-
return applySessionSurfacedMemoryCommandResetPolicy(ledgerStore, sessionKey);
|
|
27182
|
-
}
|
|
27183
|
-
function applySessionSurfacedMemoryBeforeResetPolicy2(sessionKey) {
|
|
27184
|
-
return applySessionSurfacedMemoryBeforeResetPolicy(ledgerStore, sessionKey);
|
|
27185
|
-
}
|
|
27186
|
-
function clearSessionSurfacedMemoryLedger2(sessionKey) {
|
|
27187
|
-
clearSessionSurfacedMemoryLedger(ledgerStore, sessionKey);
|
|
27188
|
-
}
|
|
27189
|
-
function hasHandoffSession(sessionId) {
|
|
27190
|
-
return handoffSeenSessionIds.has(sessionId);
|
|
27191
|
-
}
|
|
27192
|
-
function markHandoffSession(sessionId) {
|
|
27193
|
-
handoffSeenSessionIds.add(sessionId);
|
|
27194
|
-
}
|
|
27195
|
-
function deleteHandoffSession(sessionId) {
|
|
27196
|
-
handoffSeenSessionIds.delete(sessionId);
|
|
27197
|
-
}
|
|
27198
|
-
function resolveMaxSeenSessions() {
|
|
27199
|
-
const raw = process.env.AGENR_OPENCLAW_MAX_SEEN_SESSIONS;
|
|
27200
|
-
if (!raw) {
|
|
27201
|
-
return DEFAULT_MAX_SEEN_SESSIONS;
|
|
27508
|
+
// src/edge/openclaw/session-query.ts
|
|
27509
|
+
init_errors();
|
|
27510
|
+
init_guards();
|
|
27511
|
+
import { createReadStream as createReadStream2 } from "fs";
|
|
27512
|
+
import { readdir, stat } from "fs/promises";
|
|
27513
|
+
import path10 from "path";
|
|
27514
|
+
import { createInterface as createInterface2 } from "readline";
|
|
27515
|
+
var EXCHANGE_TEXT_MAX_CHARS = 200;
|
|
27516
|
+
var EXCHANGE_USER_TURN_LIMIT = 5;
|
|
27517
|
+
var RECENT_TURN_MAX_CHARS = 300;
|
|
27518
|
+
var DEFAULT_RECENT_TURN_LIMIT = 7;
|
|
27519
|
+
var SESSION_TRANSCRIPT_ARTIFACT_MARKERS = [".jsonl.reset.", ".jsonl.deleted."];
|
|
27520
|
+
function extractTextFromRoleMessage(message, role, separator) {
|
|
27521
|
+
if (!isRecord(message) || message["role"] !== role) {
|
|
27522
|
+
return "";
|
|
27202
27523
|
}
|
|
27203
|
-
const
|
|
27204
|
-
if (
|
|
27205
|
-
return
|
|
27524
|
+
const content = message["content"];
|
|
27525
|
+
if (typeof content === "string") {
|
|
27526
|
+
return content.trim();
|
|
27206
27527
|
}
|
|
27207
|
-
|
|
27208
|
-
|
|
27209
|
-
|
|
27210
|
-
|
|
27211
|
-
|
|
27212
|
-
|
|
27213
|
-
|
|
27214
|
-
|
|
27215
|
-
const
|
|
27216
|
-
if (
|
|
27217
|
-
|
|
27528
|
+
if (!Array.isArray(content)) {
|
|
27529
|
+
return "";
|
|
27530
|
+
}
|
|
27531
|
+
const textParts = [];
|
|
27532
|
+
for (const part of content) {
|
|
27533
|
+
if (!isRecord(part) || part["type"] !== "text") {
|
|
27534
|
+
continue;
|
|
27535
|
+
}
|
|
27536
|
+
const partText = part["text"];
|
|
27537
|
+
if (typeof partText !== "string") {
|
|
27538
|
+
continue;
|
|
27539
|
+
}
|
|
27540
|
+
const trimmed = partText.trim();
|
|
27541
|
+
if (trimmed) {
|
|
27542
|
+
textParts.push(trimmed);
|
|
27218
27543
|
}
|
|
27219
|
-
seenSessions.delete(oldestKey);
|
|
27220
27544
|
}
|
|
27545
|
+
return textParts.join(separator).trim();
|
|
27221
27546
|
}
|
|
27222
|
-
function
|
|
27223
|
-
|
|
27224
|
-
seenSessions.set(sessionKey, true);
|
|
27225
|
-
trimSeenSessionCache();
|
|
27226
|
-
}
|
|
27227
|
-
async function hasSeenSessionInDb(db, sessionKey) {
|
|
27228
|
-
const result = await db.execute({
|
|
27229
|
-
sql: "SELECT 1 FROM seen_sessions WHERE dedupe_key = ? AND seen_at >= ? LIMIT 1",
|
|
27230
|
-
args: [sessionKey, Date.now() - SEEN_SESSION_RETENTION_MS]
|
|
27231
|
-
});
|
|
27232
|
-
return result.rows.length > 0;
|
|
27547
|
+
function extractTextFromUserMessage(message) {
|
|
27548
|
+
return extractTextFromRoleMessage(message, "user", " ");
|
|
27233
27549
|
}
|
|
27234
|
-
|
|
27235
|
-
|
|
27236
|
-
await db.execute({
|
|
27237
|
-
sql: "DELETE FROM seen_sessions WHERE seen_at < ?",
|
|
27238
|
-
args: [nowMs - SEEN_SESSION_RETENTION_MS]
|
|
27239
|
-
});
|
|
27240
|
-
await db.execute({
|
|
27241
|
-
sql: `
|
|
27242
|
-
DELETE FROM seen_sessions
|
|
27243
|
-
WHERE dedupe_key IN (
|
|
27244
|
-
SELECT dedupe_key
|
|
27245
|
-
FROM seen_sessions
|
|
27246
|
-
ORDER BY seen_at DESC, dedupe_key DESC
|
|
27247
|
-
LIMIT -1 OFFSET ?
|
|
27248
|
-
)
|
|
27249
|
-
`,
|
|
27250
|
-
args: [maxSeenSessions]
|
|
27251
|
-
});
|
|
27550
|
+
function extractTextFromAssistantMessage(message) {
|
|
27551
|
+
return extractTextFromRoleMessage(message, "assistant", " ");
|
|
27252
27552
|
}
|
|
27253
|
-
function
|
|
27254
|
-
return
|
|
27553
|
+
function truncateMessageText(text, maxChars = EXCHANGE_TEXT_MAX_CHARS) {
|
|
27554
|
+
return text.length > maxChars ? text.slice(0, maxChars) : text;
|
|
27255
27555
|
}
|
|
27256
|
-
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
touchSeenSessionCache(sessionKey);
|
|
27260
|
-
sessionStartLog.debug(
|
|
27261
|
-
`hasSeenSession key=${sessionKey.slice(0, 12)} found=${seen} mapSize=${seenSessions.size}`
|
|
27262
|
-
);
|
|
27263
|
-
return true;
|
|
27556
|
+
function stripPromptMetadata(raw) {
|
|
27557
|
+
if (!raw) {
|
|
27558
|
+
return "";
|
|
27264
27559
|
}
|
|
27265
|
-
|
|
27266
|
-
|
|
27267
|
-
|
|
27268
|
-
|
|
27269
|
-
|
|
27270
|
-
|
|
27271
|
-
} catch (err) {
|
|
27272
|
-
sessionStartLog.warn(`hasSeenSession db lookup failed: ${toErrorMessage(err)}`);
|
|
27560
|
+
try {
|
|
27561
|
+
const pattern = /\[\w{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2} (?:[A-Z]{2,5}|GMT[+-]\d{1,2})\] /g;
|
|
27562
|
+
let lastMatchEnd = -1;
|
|
27563
|
+
let match = null;
|
|
27564
|
+
while ((match = pattern.exec(raw)) !== null) {
|
|
27565
|
+
lastMatchEnd = match.index + match[0].length;
|
|
27273
27566
|
}
|
|
27567
|
+
if (lastMatchEnd >= 0) {
|
|
27568
|
+
return raw.slice(lastMatchEnd).trim();
|
|
27569
|
+
}
|
|
27570
|
+
return raw.trim();
|
|
27571
|
+
} catch {
|
|
27572
|
+
return "";
|
|
27274
27573
|
}
|
|
27275
|
-
sessionStartLog.debug(
|
|
27276
|
-
`hasSeenSession key=${sessionKey.slice(0, 12)} found=${seen} mapSize=${seenSessions.size}`
|
|
27277
|
-
);
|
|
27278
|
-
return seen;
|
|
27279
27574
|
}
|
|
27280
|
-
|
|
27281
|
-
touchSeenSessionCache(sessionKey);
|
|
27282
|
-
if (!canExecuteSql(db)) {
|
|
27283
|
-
return;
|
|
27284
|
-
}
|
|
27285
|
-
const nowMs = Date.now();
|
|
27575
|
+
function extractLastExchangeText(messages, maxTurns = EXCHANGE_USER_TURN_LIMIT) {
|
|
27286
27576
|
try {
|
|
27287
|
-
|
|
27288
|
-
|
|
27289
|
-
args: [sessionKey, nowMs]
|
|
27290
|
-
});
|
|
27291
|
-
if (nowMs - sessionStateGlobals.lastCleanupMs > CLEANUP_INTERVAL_MS) {
|
|
27292
|
-
await cleanupSeenSessionsInDb(db, nowMs);
|
|
27293
|
-
sessionStateGlobals.lastCleanupMs = nowMs;
|
|
27577
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
27578
|
+
return "";
|
|
27294
27579
|
}
|
|
27295
|
-
|
|
27296
|
-
|
|
27580
|
+
if (maxTurns <= 0) {
|
|
27581
|
+
return "";
|
|
27582
|
+
}
|
|
27583
|
+
const collected = [];
|
|
27584
|
+
let collectedUserTurns = 0;
|
|
27585
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
27586
|
+
const message = messages[index];
|
|
27587
|
+
const userText = sanitizeTranscriptTurnText(extractTextFromUserMessage(message));
|
|
27588
|
+
if (userText) {
|
|
27589
|
+
collected.push({
|
|
27590
|
+
role: "user",
|
|
27591
|
+
text: truncateMessageText(userText.replace(/\s+/g, " ").trim())
|
|
27592
|
+
});
|
|
27593
|
+
collectedUserTurns += 1;
|
|
27594
|
+
if (collectedUserTurns >= maxTurns) {
|
|
27595
|
+
break;
|
|
27596
|
+
}
|
|
27597
|
+
continue;
|
|
27598
|
+
}
|
|
27599
|
+
const assistantText = sanitizeTranscriptTurnText(extractTextFromAssistantMessage(message));
|
|
27600
|
+
if (assistantText) {
|
|
27601
|
+
collected.push({
|
|
27602
|
+
role: "assistant",
|
|
27603
|
+
text: truncateMessageText(assistantText.replace(/\s+/g, " ").trim())
|
|
27604
|
+
});
|
|
27605
|
+
}
|
|
27606
|
+
}
|
|
27607
|
+
if (collected.length === 0) {
|
|
27608
|
+
return "";
|
|
27609
|
+
}
|
|
27610
|
+
return collected.reverse().map((turn) => `${turn.role === "user" ? "U" : "A"}: ${turn.text}`).join(" | ");
|
|
27611
|
+
} catch {
|
|
27612
|
+
return "";
|
|
27297
27613
|
}
|
|
27298
27614
|
}
|
|
27299
|
-
function
|
|
27300
|
-
|
|
27301
|
-
|
|
27302
|
-
|
|
27303
|
-
|
|
27304
|
-
|
|
27305
|
-
|
|
27615
|
+
async function listSessionFileCandidates(sessionsDir, currentSessionId, logger) {
|
|
27616
|
+
try {
|
|
27617
|
+
const normalizedSessionId = currentSessionId?.trim();
|
|
27618
|
+
const entries = await readdir(sessionsDir, { withFileTypes: true });
|
|
27619
|
+
const candidatePaths = [];
|
|
27620
|
+
for (const entry of entries) {
|
|
27621
|
+
if (!entry.isFile()) {
|
|
27622
|
+
continue;
|
|
27623
|
+
}
|
|
27624
|
+
const isPlainJsonl = entry.name.endsWith(".jsonl");
|
|
27625
|
+
const isTranscriptArtifact = SESSION_TRANSCRIPT_ARTIFACT_MARKERS.some((marker) => entry.name.includes(marker));
|
|
27626
|
+
if (!isPlainJsonl && !isTranscriptArtifact) {
|
|
27627
|
+
continue;
|
|
27628
|
+
}
|
|
27629
|
+
if (entry.name.includes(".deleted.") && !entry.name.includes(".jsonl.deleted.")) {
|
|
27630
|
+
continue;
|
|
27631
|
+
}
|
|
27632
|
+
if (normalizedSessionId && entry.name.startsWith(normalizedSessionId)) {
|
|
27633
|
+
continue;
|
|
27634
|
+
}
|
|
27635
|
+
candidatePaths.push(path10.join(sessionsDir, entry.name));
|
|
27636
|
+
}
|
|
27637
|
+
if (candidatePaths.length === 0) {
|
|
27638
|
+
return [];
|
|
27639
|
+
}
|
|
27640
|
+
const statResults = await Promise.all(
|
|
27641
|
+
candidatePaths.map(async (filePath) => {
|
|
27642
|
+
try {
|
|
27643
|
+
const fileStats = await stat(filePath);
|
|
27644
|
+
return { filePath, mtimeMs: fileStats.mtimeMs };
|
|
27645
|
+
} catch {
|
|
27646
|
+
return null;
|
|
27647
|
+
}
|
|
27648
|
+
})
|
|
27649
|
+
);
|
|
27650
|
+
const candidates = statResults.filter(
|
|
27651
|
+
(result) => result !== null
|
|
27652
|
+
);
|
|
27653
|
+
if (candidates.length === 0) {
|
|
27654
|
+
return [];
|
|
27655
|
+
}
|
|
27656
|
+
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
27657
|
+
return candidates;
|
|
27658
|
+
} catch (err) {
|
|
27659
|
+
logger?.debug?.(
|
|
27660
|
+
`listSessionFileCandidates: failed to read sessions dir "${sessionsDir}": ${toErrorMessage(err)}`
|
|
27661
|
+
);
|
|
27662
|
+
return [];
|
|
27306
27663
|
}
|
|
27307
|
-
return normalized;
|
|
27308
27664
|
}
|
|
27309
|
-
function
|
|
27310
|
-
|
|
27311
|
-
|
|
27312
|
-
|
|
27313
|
-
|
|
27314
|
-
|
|
27665
|
+
async function extractRecentTurnObjects(filePath, maxTurns = DEFAULT_RECENT_TURN_LIMIT) {
|
|
27666
|
+
try {
|
|
27667
|
+
if (!filePath) {
|
|
27668
|
+
return [];
|
|
27669
|
+
}
|
|
27670
|
+
const parsedMaxTurns = Number.isFinite(maxTurns) ? Math.max(0, Math.trunc(maxTurns)) : 0;
|
|
27671
|
+
if (parsedMaxTurns === 0) {
|
|
27672
|
+
return [];
|
|
27673
|
+
}
|
|
27674
|
+
const allTurns = [];
|
|
27675
|
+
await new Promise((resolve, reject) => {
|
|
27676
|
+
const rl = createInterface2({
|
|
27677
|
+
input: createReadStream2(filePath, { encoding: "utf8" }),
|
|
27678
|
+
crlfDelay: Infinity
|
|
27679
|
+
});
|
|
27680
|
+
rl.on("line", (line) => {
|
|
27681
|
+
const trimmed = line.trim();
|
|
27682
|
+
if (!trimmed) {
|
|
27683
|
+
return;
|
|
27684
|
+
}
|
|
27685
|
+
let record;
|
|
27686
|
+
try {
|
|
27687
|
+
record = JSON.parse(trimmed);
|
|
27688
|
+
} catch {
|
|
27689
|
+
return;
|
|
27690
|
+
}
|
|
27691
|
+
if (!isRecord(record) || record["type"] !== "message") {
|
|
27692
|
+
return;
|
|
27693
|
+
}
|
|
27694
|
+
const message = record["message"];
|
|
27695
|
+
const messageTimestamp = isRecord(message) && typeof message["timestamp"] === "string" ? message["timestamp"] : void 0;
|
|
27696
|
+
const timestamp2 = typeof record["timestamp"] === "string" ? record["timestamp"] : messageTimestamp;
|
|
27697
|
+
const userText = extractTextFromRoleMessage(message, "user", "\n");
|
|
27698
|
+
if (userText) {
|
|
27699
|
+
allTurns.push({ role: "user", text: userText, timestamp: timestamp2 });
|
|
27700
|
+
return;
|
|
27701
|
+
}
|
|
27702
|
+
const assistantText = extractTextFromRoleMessage(message, "assistant", "\n");
|
|
27703
|
+
if (assistantText) {
|
|
27704
|
+
allTurns.push({ role: "assistant", text: assistantText, timestamp: timestamp2 });
|
|
27705
|
+
}
|
|
27706
|
+
});
|
|
27707
|
+
rl.on("close", resolve);
|
|
27708
|
+
rl.on("error", reject);
|
|
27709
|
+
});
|
|
27710
|
+
const sanitizedTurns = sanitizeTranscriptTurns(allTurns, {
|
|
27711
|
+
maxCharsPerTurn: RECENT_TURN_MAX_CHARS
|
|
27712
|
+
});
|
|
27713
|
+
return sanitizedTurns.slice(-parsedMaxTurns);
|
|
27714
|
+
} catch {
|
|
27715
|
+
return [];
|
|
27315
27716
|
}
|
|
27316
|
-
return raw;
|
|
27317
|
-
}
|
|
27318
|
-
function clearSessionState() {
|
|
27319
|
-
seenSessions.clear();
|
|
27320
|
-
sessionStateGlobals.lastCleanupMs = 0;
|
|
27321
|
-
sessionSignalState.clear();
|
|
27322
|
-
handoffSeenSessionIds.clear();
|
|
27323
|
-
ledgerStore.ledgers.clear();
|
|
27324
|
-
ledgerStore.aliases.clear();
|
|
27325
|
-
sessionRef.current = "";
|
|
27326
27717
|
}
|
|
27327
27718
|
|
|
27328
27719
|
// src/modules/ingestion/application/extract-text.ts
|
|
@@ -29343,6 +29734,16 @@ function resolveWarn(pluginConfig) {
|
|
|
29343
29734
|
log8.warn(message);
|
|
29344
29735
|
};
|
|
29345
29736
|
}
|
|
29737
|
+
function resolveLiveRecallWriteMetadata() {
|
|
29738
|
+
const sessionId = sessionRef.current.trim();
|
|
29739
|
+
if (!sessionId) {
|
|
29740
|
+
return void 0;
|
|
29741
|
+
}
|
|
29742
|
+
return {
|
|
29743
|
+
sessionId,
|
|
29744
|
+
platform: "openclaw"
|
|
29745
|
+
};
|
|
29746
|
+
}
|
|
29346
29747
|
function buildStoreToolReceipt(decisions, fallbackCounts, warningCount) {
|
|
29347
29748
|
const useDecisionCounts = decisions.length > 0;
|
|
29348
29749
|
const countByAction = (action) => decisions.filter((decision) => decision.action === action).length;
|
|
@@ -29436,7 +29837,12 @@ async function runRecallTool(params, defaultProject, dbPath) {
|
|
|
29436
29837
|
const recalledEntryIds = result.results.map((item) => item.entry?.id).filter((entryId) => typeof entryId === "string" && entryId.length > 0);
|
|
29437
29838
|
if (recalledEntryIds.length > 0) {
|
|
29438
29839
|
try {
|
|
29439
|
-
enqueueRecallEventWrite(
|
|
29840
|
+
enqueueRecallEventWrite(
|
|
29841
|
+
recalledEntryIds,
|
|
29842
|
+
/* @__PURE__ */ new Date(),
|
|
29843
|
+
resolveLiveRecallWriteMetadata(),
|
|
29844
|
+
dbPath
|
|
29845
|
+
);
|
|
29440
29846
|
} catch (error) {
|
|
29441
29847
|
log8.warn(`failed to enqueue recall event write: ${toErrorMessage(error)}`);
|
|
29442
29848
|
}
|