dotmem 0.0.4 → 0.0.7
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/AGENTS.md +3 -3
- package/LICENSE.md +10 -0
- package/README.md +1 -1
- package/SECURITY.md +2 -4
- package/cli.cjs +934 -33
- package/cli.js +958 -57
- package/global.cjs +1319 -30
- package/global.js +1344 -55
- package/index.cjs +1326 -37
- package/index.js +1350 -61
- package/llms.txt +295 -295
- package/mcp.cjs +1326 -37
- package/mcp.js +1350 -61
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -5227,13 +5227,13 @@ var init_mem_repository = __esm({
|
|
|
5227
5227
|
});
|
|
5228
5228
|
}
|
|
5229
5229
|
async loadSources(sourceIds) {
|
|
5230
|
-
const { readFile:
|
|
5230
|
+
const { readFile: readFile5 } = await nodeFs4();
|
|
5231
5231
|
const ids = sourceIds ?? await this.discover();
|
|
5232
5232
|
const sources = [];
|
|
5233
5233
|
for (const sourceId of ids) {
|
|
5234
5234
|
const filePath = memFilePath(this.directoryPath, sourceId);
|
|
5235
5235
|
try {
|
|
5236
|
-
const decoded = await parseMemFile(await
|
|
5236
|
+
const decoded = await parseMemFile(await readFile5(filePath), sourceId);
|
|
5237
5237
|
sources.push({
|
|
5238
5238
|
id: sourceId,
|
|
5239
5239
|
filePath,
|
|
@@ -5317,10 +5317,10 @@ var init_mem_repository = __esm({
|
|
|
5317
5317
|
* valid package, so the backup is always a version this library can read.
|
|
5318
5318
|
*/
|
|
5319
5319
|
async keepLastValidVersion(filePath) {
|
|
5320
|
-
const { readFile:
|
|
5320
|
+
const { readFile: readFile5, copyFile, rename, rm } = await nodeFs4();
|
|
5321
5321
|
let current;
|
|
5322
5322
|
try {
|
|
5323
|
-
current = await
|
|
5323
|
+
current = await readFile5(filePath);
|
|
5324
5324
|
} catch {
|
|
5325
5325
|
return;
|
|
5326
5326
|
}
|
|
@@ -5353,7 +5353,7 @@ var init_mem_repository = __esm({
|
|
|
5353
5353
|
}
|
|
5354
5354
|
}
|
|
5355
5355
|
async writeSource(filePath, state3, preserved, preservedPackage) {
|
|
5356
|
-
const { mkdir, rename, rm, open: open2, readFile:
|
|
5356
|
+
const { mkdir, rename, rm, open: open2, readFile: readFile5 } = await nodeFs4();
|
|
5357
5357
|
if (!Number.isInteger(state3.version) || state3.version < 0) throw new Error("Invalid writable .mem knowledge version");
|
|
5358
5358
|
for (const claim2 of state3.claims) validateClaim(claim2);
|
|
5359
5359
|
await mkdir(parentPath(filePath), { recursive: true });
|
|
@@ -5375,7 +5375,7 @@ var init_mem_repository = __esm({
|
|
|
5375
5375
|
} finally {
|
|
5376
5376
|
await handle.close();
|
|
5377
5377
|
}
|
|
5378
|
-
await decodeMemArtifact(await
|
|
5378
|
+
await decodeMemArtifact(await readFile5(temporary));
|
|
5379
5379
|
await this.keepLastValidVersion(filePath);
|
|
5380
5380
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
5381
5381
|
try {
|
|
@@ -5403,8 +5403,8 @@ var init_mem_repository = __esm({
|
|
|
5403
5403
|
// core/knowledge/project-anchor.ts
|
|
5404
5404
|
async function projectAnchorExists(projectId) {
|
|
5405
5405
|
try {
|
|
5406
|
-
const { readFile:
|
|
5407
|
-
await
|
|
5406
|
+
const { readFile: readFile5 } = await import('fs/promises');
|
|
5407
|
+
await readFile5(resolveUserProjectMemoryPath(projectId));
|
|
5408
5408
|
return true;
|
|
5409
5409
|
} catch {
|
|
5410
5410
|
return false;
|
|
@@ -5469,8 +5469,8 @@ var init_user_identity = __esm({
|
|
|
5469
5469
|
"core/knowledge/user-identity.ts"() {
|
|
5470
5470
|
USER_IDENTITY_KEY = "__dotmem_user_identity__";
|
|
5471
5471
|
identityStore = null;
|
|
5472
|
-
configureUserIdentityStore = /* @__PURE__ */ __name((
|
|
5473
|
-
identityStore =
|
|
5472
|
+
configureUserIdentityStore = /* @__PURE__ */ __name((store7) => {
|
|
5473
|
+
identityStore = store7;
|
|
5474
5474
|
}, "configureUserIdentityStore");
|
|
5475
5475
|
requireStore = /* @__PURE__ */ __name(() => {
|
|
5476
5476
|
if (!identityStore) {
|
|
@@ -6085,9 +6085,155 @@ var init_staging = __esm({
|
|
|
6085
6085
|
});
|
|
6086
6086
|
|
|
6087
6087
|
// core/knowledge/loops.ts
|
|
6088
|
+
function joinPath3(base, child) {
|
|
6089
|
+
const separator3 = process.platform === "win32" ? "\\" : "/";
|
|
6090
|
+
return `${base.replace(/[\\/]+/g, separator3)}${separator3}${child}`;
|
|
6091
|
+
}
|
|
6092
|
+
function resolveLoopsFilePath() {
|
|
6093
|
+
return joinPath3(resolveUserDotMemPath(), LOOPS_FILE_NAME);
|
|
6094
|
+
}
|
|
6095
|
+
async function readFile(path) {
|
|
6096
|
+
try {
|
|
6097
|
+
const fs = await import('fs/promises');
|
|
6098
|
+
return await fs.readFile(path, "utf8");
|
|
6099
|
+
} catch {
|
|
6100
|
+
return null;
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
6103
|
+
async function writeFile(path, content) {
|
|
6104
|
+
const fs = await import('fs/promises');
|
|
6105
|
+
const dir = path.replace(/[\\/][^\\/]+$/, "");
|
|
6106
|
+
await fs.mkdir(dir, { recursive: true });
|
|
6107
|
+
const temporary = `${path}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
6108
|
+
try {
|
|
6109
|
+
await fs.writeFile(temporary, content, "utf8");
|
|
6110
|
+
await fs.rename(temporary, path);
|
|
6111
|
+
} catch {
|
|
6112
|
+
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
6113
|
+
throw new Error(`Failed to write loops file at ${path}`);
|
|
6114
|
+
}
|
|
6115
|
+
}
|
|
6116
|
+
function emptyDocument() {
|
|
6117
|
+
return { version: LOOPS_FORMAT_VERSION, loops: {} };
|
|
6118
|
+
}
|
|
6119
|
+
function validateDocument(value) {
|
|
6120
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6121
|
+
throw new Error("Loops document must be an object");
|
|
6122
|
+
}
|
|
6123
|
+
const document = value;
|
|
6124
|
+
if (document.version !== LOOPS_FORMAT_VERSION) {
|
|
6125
|
+
throw new Error(`Unsupported loops document version: ${String(document.version)}`);
|
|
6126
|
+
}
|
|
6127
|
+
if (!document.loops || typeof document.loops !== "object" || Array.isArray(document.loops)) {
|
|
6128
|
+
throw new Error("Loops document loops must be an object");
|
|
6129
|
+
}
|
|
6130
|
+
const loops = {};
|
|
6131
|
+
for (const [id, loopValue] of Object.entries(document.loops)) {
|
|
6132
|
+
const loop = validateLoop(loopValue, id);
|
|
6133
|
+
loops[loop.id] = loop;
|
|
6134
|
+
}
|
|
6135
|
+
return { version: LOOPS_FORMAT_VERSION, loops };
|
|
6136
|
+
}
|
|
6137
|
+
function validateLoop(value, id) {
|
|
6138
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6139
|
+
throw new Error(`Loop ${id} must be an object`);
|
|
6140
|
+
}
|
|
6141
|
+
const loop = value;
|
|
6142
|
+
if (typeof loop.id !== "string" || !loop.id) {
|
|
6143
|
+
throw new Error(`Loop ${id} requires an id`);
|
|
6144
|
+
}
|
|
6145
|
+
if (typeof loop.text !== "string" || !loop.text) {
|
|
6146
|
+
throw new Error(`Loop ${id} requires non-empty text`);
|
|
6147
|
+
}
|
|
6148
|
+
if (typeof loop.waitor !== "string" || !loop.waitor) {
|
|
6149
|
+
throw new Error(`Loop ${id} requires a waitor`);
|
|
6150
|
+
}
|
|
6151
|
+
if (typeof loop.openedAt !== "string" || !loop.openedAt) {
|
|
6152
|
+
throw new Error(`Loop ${id} requires an openedAt timestamp`);
|
|
6153
|
+
}
|
|
6154
|
+
const result = {
|
|
6155
|
+
id: loop.id,
|
|
6156
|
+
text: loop.text,
|
|
6157
|
+
waitor: loop.waitor,
|
|
6158
|
+
openedAt: loop.openedAt
|
|
6159
|
+
};
|
|
6160
|
+
if (typeof loop.closedAt === "string") result.closedAt = loop.closedAt;
|
|
6161
|
+
if (typeof loop.closedBy === "string") result.closedBy = loop.closedBy;
|
|
6162
|
+
if (typeof loop.closeNote === "string") result.closeNote = loop.closeNote;
|
|
6163
|
+
return result;
|
|
6164
|
+
}
|
|
6165
|
+
async function loadLoops() {
|
|
6166
|
+
const raw = await readFile(resolveLoopsFilePath());
|
|
6167
|
+
if (!raw) return emptyDocument();
|
|
6168
|
+
try {
|
|
6169
|
+
return validateDocument(JSON.parse(raw));
|
|
6170
|
+
} catch {
|
|
6171
|
+
return emptyDocument();
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
6174
|
+
async function saveLoops(document) {
|
|
6175
|
+
await writeFile(resolveLoopsFilePath(), JSON.stringify(document, null, 2) + "\n");
|
|
6176
|
+
}
|
|
6177
|
+
async function addLoop(input2) {
|
|
6178
|
+
const document = await loadLoops();
|
|
6179
|
+
if (document.loops[input2.id]) {
|
|
6180
|
+
throw new Error(`Loop ${input2.id} already exists`);
|
|
6181
|
+
}
|
|
6182
|
+
const loop = {
|
|
6183
|
+
id: input2.id,
|
|
6184
|
+
text: input2.text,
|
|
6185
|
+
waitor: input2.waitor,
|
|
6186
|
+
openedAt: input2.openedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
6187
|
+
};
|
|
6188
|
+
document.loops[loop.id] = loop;
|
|
6189
|
+
await saveLoops(document);
|
|
6190
|
+
return loop;
|
|
6191
|
+
}
|
|
6192
|
+
async function listLoops(openOnly = true) {
|
|
6193
|
+
const document = await loadLoops();
|
|
6194
|
+
const all = Object.values(document.loops);
|
|
6195
|
+
if (openOnly) {
|
|
6196
|
+
return all.filter((loop) => loop.closedAt === void 0);
|
|
6197
|
+
}
|
|
6198
|
+
return all;
|
|
6199
|
+
}
|
|
6200
|
+
async function closeLoop(id, input2) {
|
|
6201
|
+
const document = await loadLoops();
|
|
6202
|
+
const existing = document.loops[id];
|
|
6203
|
+
if (!existing) {
|
|
6204
|
+
throw new Error(`Loop ${id} not found`);
|
|
6205
|
+
}
|
|
6206
|
+
if (existing.closedAt) {
|
|
6207
|
+
throw new Error(`Loop ${id} is already closed`);
|
|
6208
|
+
}
|
|
6209
|
+
const closed = {
|
|
6210
|
+
...existing,
|
|
6211
|
+
closedAt: input2.closedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6212
|
+
closedBy: input2.by,
|
|
6213
|
+
...input2.note ? { closeNote: input2.note } : {}
|
|
6214
|
+
};
|
|
6215
|
+
document.loops[id] = closed;
|
|
6216
|
+
await saveLoops(document);
|
|
6217
|
+
return closed;
|
|
6218
|
+
}
|
|
6219
|
+
var LOOPS_FILE_NAME, LOOPS_FORMAT_VERSION;
|
|
6088
6220
|
var init_loops = __esm({
|
|
6089
6221
|
"core/knowledge/loops.ts"() {
|
|
6090
6222
|
init_user_paths();
|
|
6223
|
+
LOOPS_FILE_NAME = "loops.json";
|
|
6224
|
+
LOOPS_FORMAT_VERSION = 1;
|
|
6225
|
+
__name(joinPath3, "joinPath");
|
|
6226
|
+
__name(resolveLoopsFilePath, "resolveLoopsFilePath");
|
|
6227
|
+
__name(readFile, "readFile");
|
|
6228
|
+
__name(writeFile, "writeFile");
|
|
6229
|
+
__name(emptyDocument, "emptyDocument");
|
|
6230
|
+
__name(validateDocument, "validateDocument");
|
|
6231
|
+
__name(validateLoop, "validateLoop");
|
|
6232
|
+
__name(loadLoops, "loadLoops");
|
|
6233
|
+
__name(saveLoops, "saveLoops");
|
|
6234
|
+
__name(addLoop, "addLoop");
|
|
6235
|
+
__name(listLoops, "listLoops");
|
|
6236
|
+
__name(closeLoop, "closeLoop");
|
|
6091
6237
|
}
|
|
6092
6238
|
});
|
|
6093
6239
|
|
|
@@ -6165,14 +6311,14 @@ var init_violations = __esm({
|
|
|
6165
6311
|
});
|
|
6166
6312
|
|
|
6167
6313
|
// core/knowledge/session.ts
|
|
6168
|
-
function
|
|
6314
|
+
function joinPath4(base, child) {
|
|
6169
6315
|
const separator3 = process.platform === "win32" ? "\\" : "/";
|
|
6170
6316
|
return `${base.replace(/[\\/]+/g, separator3)}${separator3}${child}`;
|
|
6171
6317
|
}
|
|
6172
6318
|
function resolveSessionFilePath() {
|
|
6173
|
-
return
|
|
6319
|
+
return joinPath4(resolveUserDotMemPath(), SESSION_FILE_NAME);
|
|
6174
6320
|
}
|
|
6175
|
-
async function
|
|
6321
|
+
async function readFile2(path) {
|
|
6176
6322
|
try {
|
|
6177
6323
|
const fs = await import('fs/promises');
|
|
6178
6324
|
return await fs.readFile(path, "utf8");
|
|
@@ -6181,7 +6327,7 @@ async function readFile(path) {
|
|
|
6181
6327
|
}
|
|
6182
6328
|
}
|
|
6183
6329
|
async function loadSession() {
|
|
6184
|
-
const raw = await
|
|
6330
|
+
const raw = await readFile2(resolveSessionFilePath());
|
|
6185
6331
|
if (!raw) return { ...DEFAULT_SESSION };
|
|
6186
6332
|
try {
|
|
6187
6333
|
const parsed = JSON.parse(raw);
|
|
@@ -6203,9 +6349,9 @@ var init_session = __esm({
|
|
|
6203
6349
|
"core/knowledge/session.ts"() {
|
|
6204
6350
|
init_user_paths();
|
|
6205
6351
|
SESSION_FILE_NAME = "session.json";
|
|
6206
|
-
__name(
|
|
6352
|
+
__name(joinPath4, "joinPath");
|
|
6207
6353
|
__name(resolveSessionFilePath, "resolveSessionFilePath");
|
|
6208
|
-
__name(
|
|
6354
|
+
__name(readFile2, "readFile");
|
|
6209
6355
|
DEFAULT_SESSION = {
|
|
6210
6356
|
doing: "",
|
|
6211
6357
|
changedFiles: [],
|
|
@@ -6218,16 +6364,368 @@ var init_session = __esm({
|
|
|
6218
6364
|
});
|
|
6219
6365
|
|
|
6220
6366
|
// core/knowledge/exemplars.ts
|
|
6367
|
+
function joinPath5(base, child) {
|
|
6368
|
+
const separator3 = process.platform === "win32" ? "\\" : "/";
|
|
6369
|
+
return `${base.replace(/[\\/]+/g, separator3)}${separator3}${child}`;
|
|
6370
|
+
}
|
|
6371
|
+
function resolveExemplarsPath() {
|
|
6372
|
+
return joinPath5(resolveUserDotMemPath(), EXEMPLARS_FILE_NAME);
|
|
6373
|
+
}
|
|
6374
|
+
async function readFile3(path) {
|
|
6375
|
+
try {
|
|
6376
|
+
const fs = await import('fs/promises');
|
|
6377
|
+
return await fs.readFile(path, "utf8");
|
|
6378
|
+
} catch {
|
|
6379
|
+
return null;
|
|
6380
|
+
}
|
|
6381
|
+
}
|
|
6382
|
+
async function writeFile2(path, content) {
|
|
6383
|
+
const fs = await import('fs/promises');
|
|
6384
|
+
const dir = path.replace(/[\\/][^\\/]+$/, "");
|
|
6385
|
+
await fs.mkdir(dir, { recursive: true });
|
|
6386
|
+
const temporary = `${path}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
6387
|
+
try {
|
|
6388
|
+
await fs.writeFile(temporary, content, "utf8");
|
|
6389
|
+
await fs.rename(temporary, path);
|
|
6390
|
+
} catch {
|
|
6391
|
+
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
6392
|
+
throw new Error(`Failed to write exemplars file at ${path}`);
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
function emptyStore() {
|
|
6396
|
+
return { version: EXEMPLARS_FORMAT_VERSION, exemplars: {} };
|
|
6397
|
+
}
|
|
6398
|
+
function validateExemplar(value) {
|
|
6399
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6400
|
+
throw new Error("Exemplar must be an object");
|
|
6401
|
+
}
|
|
6402
|
+
const e = value;
|
|
6403
|
+
if (typeof e.id !== "string" || !e.id) throw new Error("Exemplar requires non-empty id");
|
|
6404
|
+
if (typeof e.pattern !== "string" || !e.pattern) {
|
|
6405
|
+
throw new Error(`Exemplar ${e.id} requires non-empty pattern`);
|
|
6406
|
+
}
|
|
6407
|
+
const source = e.source ?? {};
|
|
6408
|
+
if (typeof source.conversationId !== "string" || !source.conversationId) {
|
|
6409
|
+
throw new Error(`Exemplar ${e.id} requires source.conversationId`);
|
|
6410
|
+
}
|
|
6411
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6412
|
+
return {
|
|
6413
|
+
id: e.id,
|
|
6414
|
+
type: "interaction-exemplar",
|
|
6415
|
+
source: {
|
|
6416
|
+
conversationId: source.conversationId,
|
|
6417
|
+
messageIds: Array.isArray(source.messageIds) ? source.messageIds.map(String) : [],
|
|
6418
|
+
...typeof source.timestamp === "string" ? { timestamp: source.timestamp } : {}
|
|
6419
|
+
},
|
|
6420
|
+
pattern: e.pattern,
|
|
6421
|
+
originalContent: typeof e.originalContent === "string" ? e.originalContent : null,
|
|
6422
|
+
sourceReference: typeof e.sourceReference === "string" ? e.sourceReference : null,
|
|
6423
|
+
retrievalHints: Array.isArray(e.retrievalHints) ? e.retrievalHints.map(String) : [],
|
|
6424
|
+
significance: typeof e.significance === "number" && e.significance >= 0 ? e.significance : 0,
|
|
6425
|
+
confidence: typeof e.confidence === "number" && e.confidence >= 0 && e.confidence <= 1 ? e.confidence : 0,
|
|
6426
|
+
provenance: ["human-stated", "observed", "inferred"].includes(
|
|
6427
|
+
e.provenance
|
|
6428
|
+
) ? e.provenance : "observed",
|
|
6429
|
+
createdAt: typeof e.createdAt === "string" ? e.createdAt : now2,
|
|
6430
|
+
revision: typeof e.revision === "number" && e.revision > 0 ? e.revision : 1
|
|
6431
|
+
};
|
|
6432
|
+
}
|
|
6433
|
+
function validateStore(value) {
|
|
6434
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6435
|
+
throw new Error("Exemplars document must be an object");
|
|
6436
|
+
}
|
|
6437
|
+
const doc = value;
|
|
6438
|
+
if (doc.version !== EXEMPLARS_FORMAT_VERSION) {
|
|
6439
|
+
throw new Error(`Unsupported exemplars version: ${String(doc.version)}`);
|
|
6440
|
+
}
|
|
6441
|
+
const exemplars = {};
|
|
6442
|
+
if (doc.exemplars && typeof doc.exemplars === "object" && !Array.isArray(doc.exemplars)) {
|
|
6443
|
+
for (const [_, exemplarValue] of Object.entries(doc.exemplars)) {
|
|
6444
|
+
const exemplar = validateExemplar(exemplarValue);
|
|
6445
|
+
exemplars[exemplar.id] = exemplar;
|
|
6446
|
+
}
|
|
6447
|
+
}
|
|
6448
|
+
return { version: EXEMPLARS_FORMAT_VERSION, exemplars };
|
|
6449
|
+
}
|
|
6450
|
+
async function loadExemplars() {
|
|
6451
|
+
const raw = await readFile3(resolveExemplarsPath());
|
|
6452
|
+
if (raw === null) return emptyStore();
|
|
6453
|
+
try {
|
|
6454
|
+
return validateStore(JSON.parse(raw));
|
|
6455
|
+
} catch {
|
|
6456
|
+
return emptyStore();
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
async function saveExemplars(store7) {
|
|
6460
|
+
const validated = validateStore(store7);
|
|
6461
|
+
await writeFile2(resolveExemplarsPath(), JSON.stringify(validated, null, 2));
|
|
6462
|
+
}
|
|
6463
|
+
async function addExemplar(exemplar) {
|
|
6464
|
+
const store7 = await loadExemplars();
|
|
6465
|
+
if (store7.exemplars[exemplar.id]) {
|
|
6466
|
+
throw new Error(`Exemplar ${exemplar.id} already exists`);
|
|
6467
|
+
}
|
|
6468
|
+
if (exemplar.sourceReference) {
|
|
6469
|
+
const existingRef = Object.values(store7.exemplars).find(
|
|
6470
|
+
(e) => e.sourceReference === exemplar.sourceReference
|
|
6471
|
+
);
|
|
6472
|
+
if (existingRef) {
|
|
6473
|
+
throw new Error(`Exemplar with source reference ${exemplar.sourceReference} already exists (id: ${existingRef.id})`);
|
|
6474
|
+
}
|
|
6475
|
+
}
|
|
6476
|
+
const full = {
|
|
6477
|
+
...exemplar,
|
|
6478
|
+
type: "interaction-exemplar",
|
|
6479
|
+
revision: 1
|
|
6480
|
+
};
|
|
6481
|
+
store7.exemplars[exemplar.id] = full;
|
|
6482
|
+
await saveExemplars(store7);
|
|
6483
|
+
return full;
|
|
6484
|
+
}
|
|
6485
|
+
async function updateExemplar(id, updates) {
|
|
6486
|
+
const store7 = await loadExemplars();
|
|
6487
|
+
const existing = store7.exemplars[id];
|
|
6488
|
+
if (!existing) {
|
|
6489
|
+
throw new Error(`Exemplar ${id} not found`);
|
|
6490
|
+
}
|
|
6491
|
+
const updated = {
|
|
6492
|
+
...existing,
|
|
6493
|
+
...updates,
|
|
6494
|
+
id: existing.id,
|
|
6495
|
+
type: "interaction-exemplar",
|
|
6496
|
+
createdAt: existing.createdAt,
|
|
6497
|
+
revision: (updates.revision ?? existing.revision) + 1
|
|
6498
|
+
};
|
|
6499
|
+
store7.exemplars[id] = updated;
|
|
6500
|
+
await saveExemplars(store7);
|
|
6501
|
+
return updated;
|
|
6502
|
+
}
|
|
6503
|
+
var EXEMPLARS_FILE_NAME, EXEMPLARS_FORMAT_VERSION;
|
|
6221
6504
|
var init_exemplars = __esm({
|
|
6222
6505
|
"core/knowledge/exemplars.ts"() {
|
|
6223
6506
|
init_user_paths();
|
|
6507
|
+
EXEMPLARS_FILE_NAME = "exemplars.json";
|
|
6508
|
+
EXEMPLARS_FORMAT_VERSION = 1;
|
|
6509
|
+
__name(joinPath5, "joinPath");
|
|
6510
|
+
__name(resolveExemplarsPath, "resolveExemplarsPath");
|
|
6511
|
+
__name(readFile3, "readFile");
|
|
6512
|
+
__name(writeFile2, "writeFile");
|
|
6513
|
+
__name(emptyStore, "emptyStore");
|
|
6514
|
+
__name(validateExemplar, "validateExemplar");
|
|
6515
|
+
__name(validateStore, "validateStore");
|
|
6516
|
+
__name(loadExemplars, "loadExemplars");
|
|
6517
|
+
__name(saveExemplars, "saveExemplars");
|
|
6518
|
+
__name(addExemplar, "addExemplar");
|
|
6519
|
+
__name(updateExemplar, "updateExemplar");
|
|
6224
6520
|
}
|
|
6225
6521
|
});
|
|
6226
6522
|
|
|
6227
6523
|
// core/knowledge/personality-anchor.ts
|
|
6524
|
+
function joinPath6(base, child) {
|
|
6525
|
+
const separator3 = process.platform === "win32" ? "\\" : "/";
|
|
6526
|
+
return `${base.replace(/[\\/]+/g, separator3)}${separator3}${child}`;
|
|
6527
|
+
}
|
|
6528
|
+
function resolvePersonalityAnchorPath() {
|
|
6529
|
+
return joinPath6(resolveUserDotMemPath(), PERSONALITY_ANCHOR_FILE_NAME);
|
|
6530
|
+
}
|
|
6531
|
+
async function readFile4(path) {
|
|
6532
|
+
try {
|
|
6533
|
+
const fs = await import('fs/promises');
|
|
6534
|
+
return await fs.readFile(path, "utf8");
|
|
6535
|
+
} catch {
|
|
6536
|
+
return null;
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
async function writeFile3(path, content) {
|
|
6540
|
+
const fs = await import('fs/promises');
|
|
6541
|
+
const dir = path.replace(/[\\/][^\\/]+$/, "");
|
|
6542
|
+
await fs.mkdir(dir, { recursive: true });
|
|
6543
|
+
const temporary = `${path}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
6544
|
+
try {
|
|
6545
|
+
await fs.writeFile(temporary, content, "utf8");
|
|
6546
|
+
await fs.rename(temporary, path);
|
|
6547
|
+
} catch {
|
|
6548
|
+
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
6549
|
+
throw new Error(`Failed to write personality anchor at ${path}`);
|
|
6550
|
+
}
|
|
6551
|
+
}
|
|
6552
|
+
function emptyAnchor() {
|
|
6553
|
+
const epoch = "1970-01-01T00:00:00.000Z";
|
|
6554
|
+
return {
|
|
6555
|
+
version: PERSONALITY_ANCHOR_FORMAT_VERSION,
|
|
6556
|
+
identity: {
|
|
6557
|
+
communicationStyle: [],
|
|
6558
|
+
durablePreferences: [],
|
|
6559
|
+
interactionBoundaries: []
|
|
6560
|
+
},
|
|
6561
|
+
metadata: {
|
|
6562
|
+
createdAt: epoch,
|
|
6563
|
+
updatedAt: epoch,
|
|
6564
|
+
revision: 1,
|
|
6565
|
+
sourceReferences: []
|
|
6566
|
+
}
|
|
6567
|
+
};
|
|
6568
|
+
}
|
|
6569
|
+
function validateCommunicationStyle(value) {
|
|
6570
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6571
|
+
throw new Error("communicationStyle entry must be an object");
|
|
6572
|
+
}
|
|
6573
|
+
const style = value;
|
|
6574
|
+
if (typeof style.description !== "string" || !style.description) {
|
|
6575
|
+
throw new Error("communicationStyle entry requires non-empty description");
|
|
6576
|
+
}
|
|
6577
|
+
const confidence = style.confidence ?? "inferred";
|
|
6578
|
+
if (!["inferred", "observed", "human-stated"].includes(confidence)) {
|
|
6579
|
+
throw new Error(`Invalid confidence value: ${String(style.confidence)}`);
|
|
6580
|
+
}
|
|
6581
|
+
const evidence = Array.isArray(style.evidence) ? style.evidence.map(String) : [];
|
|
6582
|
+
return {
|
|
6583
|
+
description: style.description,
|
|
6584
|
+
confidence,
|
|
6585
|
+
evidence,
|
|
6586
|
+
lastObserved: typeof style.lastObserved === "string" ? style.lastObserved : (/* @__PURE__ */ new Date()).toISOString()
|
|
6587
|
+
};
|
|
6588
|
+
}
|
|
6589
|
+
function validateDurablePreference(value) {
|
|
6590
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6591
|
+
throw new Error("durablePreference entry must be an object");
|
|
6592
|
+
}
|
|
6593
|
+
const pref = value;
|
|
6594
|
+
if (typeof pref.id !== "string" || !pref.id) {
|
|
6595
|
+
throw new Error("durablePreference entry requires non-empty id");
|
|
6596
|
+
}
|
|
6597
|
+
if (typeof pref.statement !== "string" || !pref.statement) {
|
|
6598
|
+
throw new Error(`durablePreference ${pref.id} requires non-empty statement`);
|
|
6599
|
+
}
|
|
6600
|
+
const confidence = pref.confidence ?? "inferred";
|
|
6601
|
+
if (!["inferred", "observed", "human-stated"].includes(confidence)) {
|
|
6602
|
+
throw new Error(`Invalid confidence value: ${String(pref.confidence)}`);
|
|
6603
|
+
}
|
|
6604
|
+
const evidence = Array.isArray(pref.evidence) ? pref.evidence.map(String) : [];
|
|
6605
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6606
|
+
return {
|
|
6607
|
+
id: pref.id,
|
|
6608
|
+
statement: pref.statement,
|
|
6609
|
+
confidence,
|
|
6610
|
+
evidence,
|
|
6611
|
+
firstObserved: typeof pref.firstObserved === "string" ? pref.firstObserved : now2,
|
|
6612
|
+
lastConfirmed: typeof pref.lastConfirmed === "string" ? pref.lastConfirmed : now2,
|
|
6613
|
+
...typeof pref.supersededBy === "string" ? { supersededBy: pref.supersededBy } : {}
|
|
6614
|
+
};
|
|
6615
|
+
}
|
|
6616
|
+
function validateInteractionBoundary(value) {
|
|
6617
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6618
|
+
throw new Error("interactionBoundary entry must be an object");
|
|
6619
|
+
}
|
|
6620
|
+
const boundary = value;
|
|
6621
|
+
if (typeof boundary.id !== "string" || !boundary.id) {
|
|
6622
|
+
throw new Error("interactionBoundary entry requires non-empty id");
|
|
6623
|
+
}
|
|
6624
|
+
if (typeof boundary.rule !== "string" || !boundary.rule) {
|
|
6625
|
+
throw new Error(`interactionBoundary ${boundary.id} requires non-empty rule`);
|
|
6626
|
+
}
|
|
6627
|
+
const confidence = boundary.confidence ?? "inferred";
|
|
6628
|
+
if (!["inferred", "observed", "human-stated"].includes(confidence)) {
|
|
6629
|
+
throw new Error(`Invalid confidence value: ${String(boundary.confidence)}`);
|
|
6630
|
+
}
|
|
6631
|
+
const evidence = Array.isArray(boundary.evidence) ? boundary.evidence.map(String) : [];
|
|
6632
|
+
const source = boundary.source ?? "conversation";
|
|
6633
|
+
if (!["profile", "memory", "conversation"].includes(source)) {
|
|
6634
|
+
throw new Error(`Invalid boundary source: ${String(boundary.source)}`);
|
|
6635
|
+
}
|
|
6636
|
+
return {
|
|
6637
|
+
id: boundary.id,
|
|
6638
|
+
rule: boundary.rule,
|
|
6639
|
+
confidence,
|
|
6640
|
+
evidence,
|
|
6641
|
+
source,
|
|
6642
|
+
lastConfirmed: typeof boundary.lastConfirmed === "string" ? boundary.lastConfirmed : (/* @__PURE__ */ new Date()).toISOString()
|
|
6643
|
+
};
|
|
6644
|
+
}
|
|
6645
|
+
function validateAnchor(value) {
|
|
6646
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6647
|
+
throw new Error("Personality anchor document must be an object");
|
|
6648
|
+
}
|
|
6649
|
+
const doc = value;
|
|
6650
|
+
if (doc.version !== PERSONALITY_ANCHOR_FORMAT_VERSION) {
|
|
6651
|
+
throw new Error(`Unsupported personality anchor version: ${String(doc.version)}`);
|
|
6652
|
+
}
|
|
6653
|
+
const identity = doc.identity ?? {};
|
|
6654
|
+
const metadata = doc.metadata ?? {};
|
|
6655
|
+
const communicationStyle = Array.isArray(identity.communicationStyle) ? identity.communicationStyle.map(validateCommunicationStyle) : [];
|
|
6656
|
+
const durablePreferences = Array.isArray(identity.durablePreferences) ? identity.durablePreferences.map(validateDurablePreference) : [];
|
|
6657
|
+
const interactionBoundaries = Array.isArray(identity.interactionBoundaries) ? identity.interactionBoundaries.map(validateInteractionBoundary) : [];
|
|
6658
|
+
const sourceRefs = Array.isArray(metadata.sourceReferences) ? metadata.sourceReferences.map(String) : [];
|
|
6659
|
+
return {
|
|
6660
|
+
version: PERSONALITY_ANCHOR_FORMAT_VERSION,
|
|
6661
|
+
identity: {
|
|
6662
|
+
communicationStyle,
|
|
6663
|
+
durablePreferences,
|
|
6664
|
+
interactionBoundaries
|
|
6665
|
+
},
|
|
6666
|
+
metadata: {
|
|
6667
|
+
createdAt: typeof metadata.createdAt === "string" ? metadata.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
6668
|
+
updatedAt: typeof metadata.updatedAt === "string" ? metadata.updatedAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
6669
|
+
revision: typeof metadata.revision === "number" && metadata.revision > 0 ? metadata.revision : 1,
|
|
6670
|
+
sourceReferences: sourceRefs
|
|
6671
|
+
}
|
|
6672
|
+
};
|
|
6673
|
+
}
|
|
6674
|
+
async function loadPersonalityAnchor() {
|
|
6675
|
+
const raw = await readFile4(resolvePersonalityAnchorPath());
|
|
6676
|
+
if (raw === null) return emptyAnchor();
|
|
6677
|
+
try {
|
|
6678
|
+
const parsed = JSON.parse(raw);
|
|
6679
|
+
return validateAnchor(parsed);
|
|
6680
|
+
} catch {
|
|
6681
|
+
return emptyAnchor();
|
|
6682
|
+
}
|
|
6683
|
+
}
|
|
6684
|
+
async function savePersonalityAnchor(anchor2) {
|
|
6685
|
+
const validated = validateAnchor(anchor2);
|
|
6686
|
+
const dirPath = resolveUserDotMemPath();
|
|
6687
|
+
const fs = await import('fs/promises');
|
|
6688
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
6689
|
+
await writeFile3(resolvePersonalityAnchorPath(), JSON.stringify(validated, null, 2));
|
|
6690
|
+
}
|
|
6691
|
+
async function updatePersonalityAnchor(updates) {
|
|
6692
|
+
const existing = await loadPersonalityAnchor();
|
|
6693
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6694
|
+
const updated = {
|
|
6695
|
+
...existing,
|
|
6696
|
+
identity: {
|
|
6697
|
+
communicationStyle: updates.communicationStyle ?? existing.identity.communicationStyle,
|
|
6698
|
+
durablePreferences: updates.durablePreferences ?? existing.identity.durablePreferences,
|
|
6699
|
+
interactionBoundaries: updates.interactionBoundaries ?? existing.identity.interactionBoundaries
|
|
6700
|
+
},
|
|
6701
|
+
metadata: {
|
|
6702
|
+
...existing.metadata,
|
|
6703
|
+
updatedAt: now2,
|
|
6704
|
+
revision: existing.metadata.revision + 1,
|
|
6705
|
+
sourceReferences: updates.sourceReferences ? [.../* @__PURE__ */ new Set([...existing.metadata.sourceReferences, ...updates.sourceReferences])] : existing.metadata.sourceReferences
|
|
6706
|
+
}
|
|
6707
|
+
};
|
|
6708
|
+
await savePersonalityAnchor(updated);
|
|
6709
|
+
return updated;
|
|
6710
|
+
}
|
|
6711
|
+
var PERSONALITY_ANCHOR_FILE_NAME, PERSONALITY_ANCHOR_FORMAT_VERSION;
|
|
6228
6712
|
var init_personality_anchor = __esm({
|
|
6229
6713
|
"core/knowledge/personality-anchor.ts"() {
|
|
6230
6714
|
init_user_paths();
|
|
6715
|
+
PERSONALITY_ANCHOR_FILE_NAME = "personality-anchor.json";
|
|
6716
|
+
PERSONALITY_ANCHOR_FORMAT_VERSION = 1;
|
|
6717
|
+
__name(joinPath6, "joinPath");
|
|
6718
|
+
__name(resolvePersonalityAnchorPath, "resolvePersonalityAnchorPath");
|
|
6719
|
+
__name(readFile4, "readFile");
|
|
6720
|
+
__name(writeFile3, "writeFile");
|
|
6721
|
+
__name(emptyAnchor, "emptyAnchor");
|
|
6722
|
+
__name(validateCommunicationStyle, "validateCommunicationStyle");
|
|
6723
|
+
__name(validateDurablePreference, "validateDurablePreference");
|
|
6724
|
+
__name(validateInteractionBoundary, "validateInteractionBoundary");
|
|
6725
|
+
__name(validateAnchor, "validateAnchor");
|
|
6726
|
+
__name(loadPersonalityAnchor, "loadPersonalityAnchor");
|
|
6727
|
+
__name(savePersonalityAnchor, "savePersonalityAnchor");
|
|
6728
|
+
__name(updatePersonalityAnchor, "updatePersonalityAnchor");
|
|
6231
6729
|
}
|
|
6232
6730
|
});
|
|
6233
6731
|
|
|
@@ -7390,27 +7888,27 @@ var auditPolicyAction = /* @__PURE__ */ __name((action, key) => {
|
|
|
7390
7888
|
}
|
|
7391
7889
|
}, "auditPolicyAction");
|
|
7392
7890
|
var resolveOverrides = /* @__PURE__ */ __name(async (namespace2, now2 = () => /* @__PURE__ */ new Date()) => {
|
|
7393
|
-
const
|
|
7891
|
+
const store7 = await getPolicyStore();
|
|
7394
7892
|
const namespaces = namespace2 ? [namespace2] : POLICY_NAMESPACES;
|
|
7395
|
-
const documents = await Promise.all(namespaces.map((entry) =>
|
|
7893
|
+
const documents = await Promise.all(namespaces.map((entry) => store7.load(entry)));
|
|
7396
7894
|
return documents.flatMap((document) => document.overrides.map(
|
|
7397
7895
|
(override) => resolveOverrideState(override, document.records.find((record2) => record2.id === override.targetPolicyId), now2)
|
|
7398
7896
|
)).sort((left, right) => left.override.id.localeCompare(right.override.id));
|
|
7399
7897
|
}, "resolveOverrides");
|
|
7400
7898
|
var policy = {
|
|
7401
7899
|
async assign(input2, namespace2 = "operator", basedOnRevision) {
|
|
7402
|
-
const
|
|
7403
|
-
const record2 = await
|
|
7900
|
+
const store7 = await getPolicyStore();
|
|
7901
|
+
const record2 = await store7.assign(input2, namespace2, basedOnRevision);
|
|
7404
7902
|
auditPolicyAction("policy.assign", record2.id);
|
|
7405
7903
|
return record2;
|
|
7406
7904
|
},
|
|
7407
7905
|
async list() {
|
|
7408
|
-
const
|
|
7409
|
-
return
|
|
7906
|
+
const store7 = await getPolicyStore();
|
|
7907
|
+
return store7.list();
|
|
7410
7908
|
},
|
|
7411
7909
|
async remove(id, namespace2 = "operator", basedOnRevision) {
|
|
7412
|
-
const
|
|
7413
|
-
await
|
|
7910
|
+
const store7 = await getPolicyStore();
|
|
7911
|
+
await store7.remove(id, namespace2, basedOnRevision);
|
|
7414
7912
|
auditPolicyAction("policy.remove", id);
|
|
7415
7913
|
},
|
|
7416
7914
|
/**
|
|
@@ -7424,16 +7922,16 @@ var policy = {
|
|
|
7424
7922
|
* the durable audit trail with a terminal CLEAR entry.
|
|
7425
7923
|
*/
|
|
7426
7924
|
async clear(authority) {
|
|
7427
|
-
const
|
|
7428
|
-
await
|
|
7925
|
+
const store7 = await getPolicyStore();
|
|
7926
|
+
await store7.clear(authority);
|
|
7429
7927
|
auditPolicyAction("policy.clear", "policy");
|
|
7430
7928
|
},
|
|
7431
7929
|
async evaluate(event, payload) {
|
|
7432
|
-
const
|
|
7930
|
+
const store7 = await getPolicyStore();
|
|
7433
7931
|
const [state3, policies, overrides] = await Promise.all([
|
|
7434
7932
|
loadCombinedKnowledgeState(),
|
|
7435
|
-
|
|
7436
|
-
|
|
7933
|
+
store7.list(),
|
|
7934
|
+
store7.listOverrides()
|
|
7437
7935
|
]);
|
|
7438
7936
|
const claims = activeClaims(state3);
|
|
7439
7937
|
return evaluatePolicies({ event, claims, policies, overrides, payload });
|
|
@@ -7455,14 +7953,14 @@ var policy = {
|
|
|
7455
7953
|
*/
|
|
7456
7954
|
override: {
|
|
7457
7955
|
async grant(input2, namespace2 = "operator", basedOnRevision) {
|
|
7458
|
-
const
|
|
7459
|
-
const override = await
|
|
7956
|
+
const store7 = await getPolicyStore();
|
|
7957
|
+
const override = await store7.grantOverride(input2, namespace2, basedOnRevision);
|
|
7460
7958
|
auditPolicyAction("policy.override", override.id);
|
|
7461
7959
|
return override;
|
|
7462
7960
|
},
|
|
7463
7961
|
async revoke(input2, namespace2 = "operator", basedOnRevision) {
|
|
7464
|
-
const
|
|
7465
|
-
const override = await
|
|
7962
|
+
const store7 = await getPolicyStore();
|
|
7963
|
+
const override = await store7.revokeOverride(input2, namespace2, basedOnRevision);
|
|
7466
7964
|
auditPolicyAction("policy.revoke", override.id);
|
|
7467
7965
|
return override;
|
|
7468
7966
|
},
|
|
@@ -7470,8 +7968,8 @@ var policy = {
|
|
|
7470
7968
|
return resolveOverrides(namespace2);
|
|
7471
7969
|
},
|
|
7472
7970
|
async effective(event, namespace2) {
|
|
7473
|
-
const
|
|
7474
|
-
const states = await
|
|
7971
|
+
const store7 = await getPolicyStore();
|
|
7972
|
+
const states = await store7.effectiveState(namespace2);
|
|
7475
7973
|
if (event === void 0) return states;
|
|
7476
7974
|
const canonical = event.trim();
|
|
7477
7975
|
return states.filter((state3) => state3.event === canonical);
|
|
@@ -7484,8 +7982,8 @@ var policy = {
|
|
|
7484
7982
|
* additional best-effort sink, and this is the durable record.
|
|
7485
7983
|
*/
|
|
7486
7984
|
async audit(namespace2) {
|
|
7487
|
-
const
|
|
7488
|
-
return
|
|
7985
|
+
const store7 = await getPolicyStore();
|
|
7986
|
+
return store7.auditTrail(namespace2);
|
|
7489
7987
|
},
|
|
7490
7988
|
contract() {
|
|
7491
7989
|
return MEMORY_PROJECTION_CONTRACT;
|
|
@@ -8459,6 +8957,778 @@ var injectJoySeed = /* @__PURE__ */ __name(async (opts) => {
|
|
|
8459
8957
|
return { id: seedId, action: "stored" };
|
|
8460
8958
|
}, "injectJoySeed");
|
|
8461
8959
|
|
|
8960
|
+
// core/memory/capture-engine.ts
|
|
8961
|
+
init_exemplars();
|
|
8962
|
+
init_loops();
|
|
8963
|
+
init_personality_anchor();
|
|
8964
|
+
var CAPTURE_STATE_KEY = "__dotmem_capture_state__";
|
|
8965
|
+
var DEFAULT_CONFIG = {
|
|
8966
|
+
minSignificance: 0.3,
|
|
8967
|
+
maxItemsPerConversation: 50,
|
|
8968
|
+
dedupSimilarityThreshold: 0.75,
|
|
8969
|
+
preserveConflicts: true,
|
|
8970
|
+
tempContextTtlMs: 24 * 60 * 60 * 1e3,
|
|
8971
|
+
autoCreateExemplars: true,
|
|
8972
|
+
autoCreateLoops: true,
|
|
8973
|
+
autoCreateClaims: true
|
|
8974
|
+
};
|
|
8975
|
+
var CATEGORY_KEYWORDS = {
|
|
8976
|
+
fact: [
|
|
8977
|
+
"is",
|
|
8978
|
+
"are",
|
|
8979
|
+
"was",
|
|
8980
|
+
"were",
|
|
8981
|
+
"uses",
|
|
8982
|
+
"built with",
|
|
8983
|
+
"depends on",
|
|
8984
|
+
"located at",
|
|
8985
|
+
"named",
|
|
8986
|
+
"called",
|
|
8987
|
+
"version",
|
|
8988
|
+
"language",
|
|
8989
|
+
"framework",
|
|
8990
|
+
"test",
|
|
8991
|
+
"testing"
|
|
8992
|
+
],
|
|
8993
|
+
decision: [
|
|
8994
|
+
"decided",
|
|
8995
|
+
"decision",
|
|
8996
|
+
"agreed",
|
|
8997
|
+
"chose",
|
|
8998
|
+
"selected",
|
|
8999
|
+
"adopt",
|
|
9000
|
+
"going with",
|
|
9001
|
+
"will use",
|
|
9002
|
+
"commit to",
|
|
9003
|
+
"resolve",
|
|
9004
|
+
"settled"
|
|
9005
|
+
],
|
|
9006
|
+
experience: [
|
|
9007
|
+
"remember when",
|
|
9008
|
+
"we talked about",
|
|
9009
|
+
"discussed",
|
|
9010
|
+
"experienced",
|
|
9011
|
+
"encountered",
|
|
9012
|
+
"faced",
|
|
9013
|
+
"went through",
|
|
9014
|
+
"session",
|
|
9015
|
+
"conversation"
|
|
9016
|
+
],
|
|
9017
|
+
question: [
|
|
9018
|
+
"?",
|
|
9019
|
+
"how do",
|
|
9020
|
+
"what is",
|
|
9021
|
+
"why",
|
|
9022
|
+
"when",
|
|
9023
|
+
"where",
|
|
9024
|
+
"which",
|
|
9025
|
+
"can you",
|
|
9026
|
+
"should we",
|
|
9027
|
+
"is it possible"
|
|
9028
|
+
],
|
|
9029
|
+
open_loop: [
|
|
9030
|
+
"still need to",
|
|
9031
|
+
"pending",
|
|
9032
|
+
"waiting for",
|
|
9033
|
+
"todo",
|
|
9034
|
+
"to do",
|
|
9035
|
+
"open",
|
|
9036
|
+
"unresolved",
|
|
9037
|
+
"outstanding",
|
|
9038
|
+
"follow up",
|
|
9039
|
+
"blocked"
|
|
9040
|
+
],
|
|
9041
|
+
preference: [
|
|
9042
|
+
"prefer",
|
|
9043
|
+
"prefers",
|
|
9044
|
+
"like",
|
|
9045
|
+
"likes",
|
|
9046
|
+
"favorite",
|
|
9047
|
+
"always",
|
|
9048
|
+
"never",
|
|
9049
|
+
"don't like",
|
|
9050
|
+
"dislike",
|
|
9051
|
+
"style",
|
|
9052
|
+
"habit"
|
|
9053
|
+
],
|
|
9054
|
+
project_context: [
|
|
9055
|
+
"project",
|
|
9056
|
+
"repository",
|
|
9057
|
+
"codebase",
|
|
9058
|
+
"architecture",
|
|
9059
|
+
"stack",
|
|
9060
|
+
"deploy",
|
|
9061
|
+
"ci",
|
|
9062
|
+
"pipeline",
|
|
9063
|
+
"package"
|
|
9064
|
+
],
|
|
9065
|
+
temporary_context: [
|
|
9066
|
+
"for now",
|
|
9067
|
+
"just for",
|
|
9068
|
+
"temporary",
|
|
9069
|
+
"this session",
|
|
9070
|
+
"currently",
|
|
9071
|
+
"right now",
|
|
9072
|
+
"at the moment",
|
|
9073
|
+
"for this task"
|
|
9074
|
+
],
|
|
9075
|
+
inference: [
|
|
9076
|
+
"probably",
|
|
9077
|
+
"likely",
|
|
9078
|
+
"maybe",
|
|
9079
|
+
"perhaps",
|
|
9080
|
+
"seems",
|
|
9081
|
+
"appears",
|
|
9082
|
+
"suggests",
|
|
9083
|
+
"indicates",
|
|
9084
|
+
"implies",
|
|
9085
|
+
"might",
|
|
9086
|
+
"could be"
|
|
9087
|
+
]
|
|
9088
|
+
};
|
|
9089
|
+
var REMEMBER_PATTERNS = [
|
|
9090
|
+
/remember this/i,
|
|
9091
|
+
/remember that/i,
|
|
9092
|
+
/keep this in mind/i,
|
|
9093
|
+
/don't forget/i,
|
|
9094
|
+
/do not forget/i,
|
|
9095
|
+
/store this/i,
|
|
9096
|
+
/save this/i
|
|
9097
|
+
];
|
|
9098
|
+
var DO_NOT_REMEMBER_PATTERNS = [
|
|
9099
|
+
/don't remember/i,
|
|
9100
|
+
/do not remember/i,
|
|
9101
|
+
/forget this/i,
|
|
9102
|
+
/don't store/i,
|
|
9103
|
+
/do not store/i,
|
|
9104
|
+
/not relevant/i,
|
|
9105
|
+
/ignore this/i,
|
|
9106
|
+
/temporary only/i,
|
|
9107
|
+
/this session only/i
|
|
9108
|
+
];
|
|
9109
|
+
function generateId() {
|
|
9110
|
+
return `capture-${memorio.hlc.serialize(memorio.hlc.tick())}-${Math.random().toString(36).slice(2, 10)}`;
|
|
9111
|
+
}
|
|
9112
|
+
__name(generateId, "generateId");
|
|
9113
|
+
function nowIso() {
|
|
9114
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9115
|
+
}
|
|
9116
|
+
__name(nowIso, "nowIso");
|
|
9117
|
+
function normalizeText(text) {
|
|
9118
|
+
return text.normalize("NFKC").toLocaleLowerCase("en-US").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
9119
|
+
}
|
|
9120
|
+
__name(normalizeText, "normalizeText");
|
|
9121
|
+
function tokenize(text) {
|
|
9122
|
+
return new Set(normalizeText(text).split(" ").filter((t) => t.length > 2));
|
|
9123
|
+
}
|
|
9124
|
+
__name(tokenize, "tokenize");
|
|
9125
|
+
function jaccardSimilarity(a, b) {
|
|
9126
|
+
const ta = tokenize(a);
|
|
9127
|
+
const tb = tokenize(b);
|
|
9128
|
+
if (ta.size === 0 && tb.size === 0) return 1;
|
|
9129
|
+
if (ta.size === 0 || tb.size === 0) return 0;
|
|
9130
|
+
const intersection2 = new Set([...ta].filter((x2) => tb.has(x2)));
|
|
9131
|
+
const union2 = /* @__PURE__ */ new Set([...ta, ...tb]);
|
|
9132
|
+
return intersection2.size / union2.size;
|
|
9133
|
+
}
|
|
9134
|
+
__name(jaccardSimilarity, "jaccardSimilarity");
|
|
9135
|
+
function containsPhrase2(text, phrase) {
|
|
9136
|
+
const t = normalizeText(text);
|
|
9137
|
+
const p = normalizeText(phrase);
|
|
9138
|
+
return t.includes(p);
|
|
9139
|
+
}
|
|
9140
|
+
__name(containsPhrase2, "containsPhrase");
|
|
9141
|
+
function detectExplicitInstructions(text, source) {
|
|
9142
|
+
const instructions = [];
|
|
9143
|
+
for (const pattern of REMEMBER_PATTERNS) {
|
|
9144
|
+
if (pattern.test(text)) {
|
|
9145
|
+
instructions.push({
|
|
9146
|
+
type: "remember",
|
|
9147
|
+
content: text,
|
|
9148
|
+
source,
|
|
9149
|
+
timestamp: nowIso()
|
|
9150
|
+
});
|
|
9151
|
+
break;
|
|
9152
|
+
}
|
|
9153
|
+
}
|
|
9154
|
+
for (const pattern of DO_NOT_REMEMBER_PATTERNS) {
|
|
9155
|
+
if (pattern.test(text)) {
|
|
9156
|
+
instructions.push({
|
|
9157
|
+
type: "do-not-remember",
|
|
9158
|
+
content: text,
|
|
9159
|
+
source,
|
|
9160
|
+
timestamp: nowIso()
|
|
9161
|
+
});
|
|
9162
|
+
break;
|
|
9163
|
+
}
|
|
9164
|
+
}
|
|
9165
|
+
return instructions;
|
|
9166
|
+
}
|
|
9167
|
+
__name(detectExplicitInstructions, "detectExplicitInstructions");
|
|
9168
|
+
function classifyCategory(text, explicitInstructions) {
|
|
9169
|
+
const lower = text.toLocaleLowerCase("en-US");
|
|
9170
|
+
if (explicitInstructions.some((i2) => i2.type === "do-not-remember")) {
|
|
9171
|
+
return "temporary_context";
|
|
9172
|
+
}
|
|
9173
|
+
if (explicitInstructions.some((i2) => i2.type === "remember")) {
|
|
9174
|
+
if (/prefer|prefers|like|likes|favorite|always|never/i.test(lower)) {
|
|
9175
|
+
return "preference";
|
|
9176
|
+
}
|
|
9177
|
+
if (/decided|decision|agreed|chose|adopt/i.test(lower)) {
|
|
9178
|
+
return "decision";
|
|
9179
|
+
}
|
|
9180
|
+
return "fact";
|
|
9181
|
+
}
|
|
9182
|
+
const scores = {};
|
|
9183
|
+
for (const cat of Object.keys(CATEGORY_KEYWORDS)) {
|
|
9184
|
+
scores[cat] = CATEGORY_KEYWORDS[cat].filter((kw) => containsPhrase2(lower, kw)).length;
|
|
9185
|
+
}
|
|
9186
|
+
if (/probably|likely|maybe|perhaps|seems|appears|might|could be/i.test(lower)) {
|
|
9187
|
+
scores.inference += 3;
|
|
9188
|
+
}
|
|
9189
|
+
if (text.includes("?")) {
|
|
9190
|
+
scores.question += 3;
|
|
9191
|
+
}
|
|
9192
|
+
if (/decided|decision|agreed|chose|adopt|will use|going with|resolve|settled/i.test(lower)) {
|
|
9193
|
+
scores.decision += 3;
|
|
9194
|
+
}
|
|
9195
|
+
if (/prefer|prefers|like|likes|favorite|always|never|don.t like|dislike/i.test(lower)) {
|
|
9196
|
+
scores.preference += 3;
|
|
9197
|
+
}
|
|
9198
|
+
if (/still need to|pending|waiting for|todo|to do|unresolved|outstanding|follow.?up|blocked/i.test(lower)) {
|
|
9199
|
+
scores.open_loop += 3;
|
|
9200
|
+
}
|
|
9201
|
+
if (/remember when|we talked about|discussed|experienced|encountered|session|conversation/i.test(lower)) {
|
|
9202
|
+
scores.experience += 2;
|
|
9203
|
+
}
|
|
9204
|
+
if (/repository|architecture|deploy|ci|pipeline/i.test(lower)) {
|
|
9205
|
+
scores.project_context += 2;
|
|
9206
|
+
}
|
|
9207
|
+
if (/just for|this session|for now|temporary/i.test(lower)) {
|
|
9208
|
+
scores.temporary_context += 3;
|
|
9209
|
+
}
|
|
9210
|
+
if (scores.fact > 0 && scores.project_context > 0 && scores.fact === scores.project_context) {
|
|
9211
|
+
scores.fact += 1;
|
|
9212
|
+
}
|
|
9213
|
+
let bestCategory = "fact";
|
|
9214
|
+
let bestScore = -1;
|
|
9215
|
+
for (const [cat, score] of Object.entries(scores)) {
|
|
9216
|
+
if (score > bestScore) {
|
|
9217
|
+
bestScore = score;
|
|
9218
|
+
bestCategory = cat;
|
|
9219
|
+
}
|
|
9220
|
+
}
|
|
9221
|
+
if (bestScore === 0) return "fact";
|
|
9222
|
+
return bestCategory;
|
|
9223
|
+
}
|
|
9224
|
+
__name(classifyCategory, "classifyCategory");
|
|
9225
|
+
function determineEpistemicType(category, text, explicitInstructions) {
|
|
9226
|
+
if (explicitInstructions.some((i2) => i2.type === "remember")) {
|
|
9227
|
+
return "human-stated";
|
|
9228
|
+
}
|
|
9229
|
+
if (category === "inference" || /probably|likely|maybe|perhaps|seems|appears/i.test(text)) {
|
|
9230
|
+
return "inferred";
|
|
9231
|
+
}
|
|
9232
|
+
if (category === "question") return "observed";
|
|
9233
|
+
return "observed";
|
|
9234
|
+
}
|
|
9235
|
+
__name(determineEpistemicType, "determineEpistemicType");
|
|
9236
|
+
function determineConfidence(category, epistemicType, _text) {
|
|
9237
|
+
if (epistemicType === "human-stated") return "high";
|
|
9238
|
+
if (epistemicType === "inferred") return "low";
|
|
9239
|
+
if (category === "decision" || category === "preference") return "medium";
|
|
9240
|
+
if (category === "question") return "medium";
|
|
9241
|
+
if (category === "fact") return "medium";
|
|
9242
|
+
return "low";
|
|
9243
|
+
}
|
|
9244
|
+
__name(determineConfidence, "determineConfidence");
|
|
9245
|
+
function extractEntities(text) {
|
|
9246
|
+
const entities = [];
|
|
9247
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9248
|
+
const capitalizedMatches = text.match(/\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\b/g) || [];
|
|
9249
|
+
for (const match of capitalizedMatches) {
|
|
9250
|
+
if (seen.has(match)) continue;
|
|
9251
|
+
seen.add(match);
|
|
9252
|
+
entities.push({
|
|
9253
|
+
id: `entity-${normalizeText(match)}`,
|
|
9254
|
+
type: "concept",
|
|
9255
|
+
name: match,
|
|
9256
|
+
aliases: [],
|
|
9257
|
+
relationships: []
|
|
9258
|
+
});
|
|
9259
|
+
}
|
|
9260
|
+
const quotedMatches = text.match(/["'`]([^"`']{2,})["'`]/g) || [];
|
|
9261
|
+
for (const match of quotedMatches) {
|
|
9262
|
+
const clean = match.replace(/["'`]/g, "");
|
|
9263
|
+
if (seen.has(clean)) continue;
|
|
9264
|
+
seen.add(clean);
|
|
9265
|
+
entities.push({
|
|
9266
|
+
id: `entity-${normalizeText(clean)}`,
|
|
9267
|
+
type: "concept",
|
|
9268
|
+
name: clean,
|
|
9269
|
+
aliases: [],
|
|
9270
|
+
relationships: []
|
|
9271
|
+
});
|
|
9272
|
+
}
|
|
9273
|
+
return entities;
|
|
9274
|
+
}
|
|
9275
|
+
__name(extractEntities, "extractEntities");
|
|
9276
|
+
function extractRelationships(_text, entities) {
|
|
9277
|
+
const relationships = [];
|
|
9278
|
+
if (entities.length < 2) return relationships;
|
|
9279
|
+
for (let i2 = 0; i2 < entities.length - 1; i2++) {
|
|
9280
|
+
for (let j = i2 + 1; j < entities.length; j++) {
|
|
9281
|
+
relationships.push({
|
|
9282
|
+
from: entities[i2].id,
|
|
9283
|
+
to: entities[j].id,
|
|
9284
|
+
type: "related-to",
|
|
9285
|
+
confidence: 0.5
|
|
9286
|
+
});
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
return relationships;
|
|
9290
|
+
}
|
|
9291
|
+
__name(extractRelationships, "extractRelationships");
|
|
9292
|
+
async function findExistingSimilarItems(statement, category, existingItems, threshold) {
|
|
9293
|
+
const matches2 = [];
|
|
9294
|
+
for (const existing of existingItems) {
|
|
9295
|
+
if (existing.category !== category) continue;
|
|
9296
|
+
const similarity = jaccardSimilarity(statement, existing.statement);
|
|
9297
|
+
if (similarity >= threshold) {
|
|
9298
|
+
matches2.push({
|
|
9299
|
+
existingId: existing.id,
|
|
9300
|
+
existingStatement: existing.statement,
|
|
9301
|
+
similarity,
|
|
9302
|
+
matchType: similarity >= 0.95 ? "exact" : "semantic",
|
|
9303
|
+
category: existing.category
|
|
9304
|
+
});
|
|
9305
|
+
}
|
|
9306
|
+
}
|
|
9307
|
+
return matches2;
|
|
9308
|
+
}
|
|
9309
|
+
__name(findExistingSimilarItems, "findExistingSimilarItems");
|
|
9310
|
+
async function findExistingMemoryEntries(statement, category) {
|
|
9311
|
+
const matches2 = [];
|
|
9312
|
+
try {
|
|
9313
|
+
const all = await entries._loadAll();
|
|
9314
|
+
for (const entry of all) {
|
|
9315
|
+
if (entry.status !== "active") continue;
|
|
9316
|
+
if (entry.type !== category && entry.type !== "fact") continue;
|
|
9317
|
+
const entryText = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value);
|
|
9318
|
+
const similarity = jaccardSimilarity(statement, entryText);
|
|
9319
|
+
if (similarity >= DEFAULT_CONFIG.dedupSimilarityThreshold) {
|
|
9320
|
+
matches2.push({
|
|
9321
|
+
existingId: entry.id,
|
|
9322
|
+
existingStatement: entryText,
|
|
9323
|
+
similarity,
|
|
9324
|
+
matchType: similarity >= 0.95 ? "exact" : "semantic",
|
|
9325
|
+
category
|
|
9326
|
+
});
|
|
9327
|
+
}
|
|
9328
|
+
}
|
|
9329
|
+
} catch {
|
|
9330
|
+
}
|
|
9331
|
+
return matches2;
|
|
9332
|
+
}
|
|
9333
|
+
__name(findExistingMemoryEntries, "findExistingMemoryEntries");
|
|
9334
|
+
function shouldStoreInCoreMemory(category, epistemicType) {
|
|
9335
|
+
return category === "preference" && epistemicType === "human-stated";
|
|
9336
|
+
}
|
|
9337
|
+
__name(shouldStoreInCoreMemory, "shouldStoreInCoreMemory");
|
|
9338
|
+
async function routeToStorage(item, adapters, config2) {
|
|
9339
|
+
const { category, epistemicType, statement, entities, provenance, confidence } = item;
|
|
9340
|
+
const memKey = `capture:${item.id}`;
|
|
9341
|
+
await entries.remember(memKey, {
|
|
9342
|
+
id: item.id,
|
|
9343
|
+
category,
|
|
9344
|
+
statement,
|
|
9345
|
+
entities,
|
|
9346
|
+
relationships: item.relationships,
|
|
9347
|
+
provenance,
|
|
9348
|
+
confidence,
|
|
9349
|
+
epistemicType,
|
|
9350
|
+
capturedAt: provenance.capturedAt
|
|
9351
|
+
}, {
|
|
9352
|
+
id: item.id,
|
|
9353
|
+
type: category === "preference" ? "preference" : category === "decision" ? "decision" : category === "fact" ? "fact" : "context",
|
|
9354
|
+
confidence: confidence === "high" ? 1 : confidence === "medium" ? 0.7 : 0.4,
|
|
9355
|
+
scope: category === "temporary_context" ? "session" : "local",
|
|
9356
|
+
tags: [category, provenance.epistemicType, provenance.source.conversationId],
|
|
9357
|
+
source: `capture:${provenance.source.conversationId}`
|
|
9358
|
+
});
|
|
9359
|
+
if (shouldStoreInCoreMemory(category, epistemicType)) {
|
|
9360
|
+
try {
|
|
9361
|
+
const anchor2 = await loadPersonalityAnchor();
|
|
9362
|
+
const existingPref = anchor2.identity.durablePreferences.find((p) => p.statement === statement);
|
|
9363
|
+
if (!existingPref) {
|
|
9364
|
+
await updatePersonalityAnchor({
|
|
9365
|
+
durablePreferences: [
|
|
9366
|
+
...anchor2.identity.durablePreferences,
|
|
9367
|
+
{
|
|
9368
|
+
id: item.id,
|
|
9369
|
+
statement,
|
|
9370
|
+
confidence: epistemicType,
|
|
9371
|
+
evidence: [provenance.source.conversationId],
|
|
9372
|
+
firstObserved: provenance.capturedAt,
|
|
9373
|
+
lastConfirmed: provenance.capturedAt
|
|
9374
|
+
}
|
|
9375
|
+
]
|
|
9376
|
+
});
|
|
9377
|
+
}
|
|
9378
|
+
} catch {
|
|
9379
|
+
}
|
|
9380
|
+
}
|
|
9381
|
+
if (category === "open_loop" && config2.autoCreateLoops) {
|
|
9382
|
+
try {
|
|
9383
|
+
const existingLoops = await listLoops(true);
|
|
9384
|
+
const alreadyExists = existingLoops.some((l) => l.text === statement);
|
|
9385
|
+
if (!alreadyExists) {
|
|
9386
|
+
await addLoop({
|
|
9387
|
+
id: item.id,
|
|
9388
|
+
text: statement,
|
|
9389
|
+
waitor: "joy",
|
|
9390
|
+
openedAt: provenance.capturedAt
|
|
9391
|
+
});
|
|
9392
|
+
}
|
|
9393
|
+
} catch {
|
|
9394
|
+
}
|
|
9395
|
+
}
|
|
9396
|
+
if (category === "experience" && config2.autoCreateExemplars) {
|
|
9397
|
+
try {
|
|
9398
|
+
await addExemplar({
|
|
9399
|
+
id: item.id,
|
|
9400
|
+
source: {
|
|
9401
|
+
conversationId: provenance.source.conversationId,
|
|
9402
|
+
messageIds: provenance.source.messageIds
|
|
9403
|
+
},
|
|
9404
|
+
pattern: statement,
|
|
9405
|
+
originalContent: statement,
|
|
9406
|
+
sourceReference: `capture:${item.id}`,
|
|
9407
|
+
retrievalHints: [statement, ...entities.map((e) => e.name)],
|
|
9408
|
+
significance: confidence === "high" ? 0.8 : confidence === "medium" ? 0.5 : 0.3,
|
|
9409
|
+
confidence: confidence === "high" ? 1 : confidence === "medium" ? 0.7 : 0.4,
|
|
9410
|
+
provenance: epistemicType,
|
|
9411
|
+
createdAt: provenance.capturedAt
|
|
9412
|
+
});
|
|
9413
|
+
} catch {
|
|
9414
|
+
}
|
|
9415
|
+
}
|
|
9416
|
+
if (config2.autoCreateClaims && (category === "fact" || category === "decision")) {
|
|
9417
|
+
try {
|
|
9418
|
+
await acquire.acquire({
|
|
9419
|
+
id: `claim-${item.id}`,
|
|
9420
|
+
knowledge: { statement, category },
|
|
9421
|
+
applicability: { area: "project" },
|
|
9422
|
+
evidence: [{
|
|
9423
|
+
id: `ev-${item.id}`,
|
|
9424
|
+
source: provenance.source.conversationId,
|
|
9425
|
+
observedAt: provenance.capturedAt,
|
|
9426
|
+
supports: statement
|
|
9427
|
+
}],
|
|
9428
|
+
epistemicType,
|
|
9429
|
+
provenance: { source: "conversation-capture", detail: `category:${category}` },
|
|
9430
|
+
acquiredAt: provenance.capturedAt,
|
|
9431
|
+
scope: "project"
|
|
9432
|
+
});
|
|
9433
|
+
} catch {
|
|
9434
|
+
}
|
|
9435
|
+
}
|
|
9436
|
+
memorio.dispatch.set("dotmem:capture:item-captured", {
|
|
9437
|
+
detail: { id: item.id, category, statement }
|
|
9438
|
+
});
|
|
9439
|
+
}
|
|
9440
|
+
__name(routeToStorage, "routeToStorage");
|
|
9441
|
+
var CaptureEngine = class {
|
|
9442
|
+
static {
|
|
9443
|
+
__name(this, "CaptureEngine");
|
|
9444
|
+
}
|
|
9445
|
+
config;
|
|
9446
|
+
adapters;
|
|
9447
|
+
sessionContexts = /* @__PURE__ */ new Map();
|
|
9448
|
+
constructor(config2 = {}, adapters) {
|
|
9449
|
+
this.config = { ...DEFAULT_CONFIG, ...config2 };
|
|
9450
|
+
this.adapters = {
|
|
9451
|
+
remember: entries.remember.bind(entries),
|
|
9452
|
+
recall: entries.recall.bind(entries),
|
|
9453
|
+
update: entries.update.bind(entries),
|
|
9454
|
+
addExemplar: addExemplar.bind(null),
|
|
9455
|
+
updateExemplar: updateExemplar.bind(null),
|
|
9456
|
+
addLoop: addLoop.bind(null),
|
|
9457
|
+
closeLoop: closeLoop.bind(null),
|
|
9458
|
+
updatePersonalityAnchor: updatePersonalityAnchor.bind(null),
|
|
9459
|
+
...adapters
|
|
9460
|
+
};
|
|
9461
|
+
}
|
|
9462
|
+
/**
|
|
9463
|
+
* Capture information from a conversation turn.
|
|
9464
|
+
* Processes a single message or turn of conversation.
|
|
9465
|
+
*/
|
|
9466
|
+
async captureTurn(text, options) {
|
|
9467
|
+
const source = {
|
|
9468
|
+
conversationId: options.conversationId,
|
|
9469
|
+
messageIds: options.messageIds ?? [],
|
|
9470
|
+
timestamp: nowIso(),
|
|
9471
|
+
turnIndex: 0
|
|
9472
|
+
};
|
|
9473
|
+
const instructions = detectExplicitInstructions(text, source);
|
|
9474
|
+
const hasDoNotRemember = instructions.some((i2) => i2.type === "do-not-remember");
|
|
9475
|
+
const hasExplicitRemember = instructions.some((i2) => i2.type === "remember");
|
|
9476
|
+
let created = 0, linked = 0, ignored = 0;
|
|
9477
|
+
if (hasDoNotRemember && !hasExplicitRemember) {
|
|
9478
|
+
const item2 = {
|
|
9479
|
+
id: generateId(),
|
|
9480
|
+
category: "temporary_context",
|
|
9481
|
+
statement: text,
|
|
9482
|
+
content: { text },
|
|
9483
|
+
entities: [],
|
|
9484
|
+
relationships: [],
|
|
9485
|
+
provenance: {
|
|
9486
|
+
source,
|
|
9487
|
+
classification: "temporary_context",
|
|
9488
|
+
confidence: "low",
|
|
9489
|
+
epistemicType: "observed",
|
|
9490
|
+
capturedAt: nowIso(),
|
|
9491
|
+
method: "automatic"
|
|
9492
|
+
},
|
|
9493
|
+
confidence: "low",
|
|
9494
|
+
epistemicType: "observed"
|
|
9495
|
+
};
|
|
9496
|
+
await this.storeSessionContext(options.conversationId, [item2], instructions);
|
|
9497
|
+
ignored++;
|
|
9498
|
+
return { items: [item2], instructions, created: 0, linked: 0, ignored: 1, dedupMatches: [] };
|
|
9499
|
+
}
|
|
9500
|
+
const category = classifyCategory(text, instructions);
|
|
9501
|
+
const epistemicType = determineEpistemicType(category, text, instructions);
|
|
9502
|
+
const confidence = determineConfidence(category, epistemicType);
|
|
9503
|
+
const entities = extractEntities(text);
|
|
9504
|
+
const relationships = extractRelationships(text, entities);
|
|
9505
|
+
const provenance = {
|
|
9506
|
+
source,
|
|
9507
|
+
classification: category,
|
|
9508
|
+
confidence,
|
|
9509
|
+
epistemicType,
|
|
9510
|
+
capturedAt: nowIso(),
|
|
9511
|
+
method: hasExplicitRemember ? "explicit" : "automatic"
|
|
9512
|
+
};
|
|
9513
|
+
const item = {
|
|
9514
|
+
id: generateId(),
|
|
9515
|
+
category,
|
|
9516
|
+
statement: text,
|
|
9517
|
+
content: { text },
|
|
9518
|
+
entities,
|
|
9519
|
+
relationships,
|
|
9520
|
+
provenance,
|
|
9521
|
+
confidence,
|
|
9522
|
+
epistemicType
|
|
9523
|
+
};
|
|
9524
|
+
const sessionCtx = this.sessionContexts.get(options.conversationId);
|
|
9525
|
+
const existingItems = sessionCtx ? sessionCtx.items : [];
|
|
9526
|
+
const dedupMatches = await findExistingSimilarItems(
|
|
9527
|
+
text,
|
|
9528
|
+
category,
|
|
9529
|
+
existingItems,
|
|
9530
|
+
this.config.dedupSimilarityThreshold
|
|
9531
|
+
);
|
|
9532
|
+
const memoryMatches = await findExistingMemoryEntries(text, category);
|
|
9533
|
+
const allMatches = [...dedupMatches, ...memoryMatches];
|
|
9534
|
+
let decision = "create";
|
|
9535
|
+
if (allMatches.length > 0) {
|
|
9536
|
+
const exactMatch = allMatches.find((m) => m.matchType === "exact");
|
|
9537
|
+
if (exactMatch) {
|
|
9538
|
+
decision = "link";
|
|
9539
|
+
linked++;
|
|
9540
|
+
} else {
|
|
9541
|
+
decision = "create";
|
|
9542
|
+
created++;
|
|
9543
|
+
}
|
|
9544
|
+
} else {
|
|
9545
|
+
created++;
|
|
9546
|
+
}
|
|
9547
|
+
const finalItem = { ...item };
|
|
9548
|
+
const shouldStore = decision === "create" || decision === "link";
|
|
9549
|
+
if (shouldStore) {
|
|
9550
|
+
await routeToStorage(finalItem, this.adapters, this.config);
|
|
9551
|
+
}
|
|
9552
|
+
await this.storeSessionContext(options.conversationId, [finalItem], instructions);
|
|
9553
|
+
return { items: [finalItem], instructions, created, linked, ignored, dedupMatches: allMatches };
|
|
9554
|
+
}
|
|
9555
|
+
/**
|
|
9556
|
+
* Capture information from a full conversation (array of messages).
|
|
9557
|
+
* Processes each message and returns aggregated results.
|
|
9558
|
+
*/
|
|
9559
|
+
async captureConversation(messages, options) {
|
|
9560
|
+
const allItems = [];
|
|
9561
|
+
const allInstructions = [];
|
|
9562
|
+
let created = 0, updated = 0, linked = 0, ignored = 0;
|
|
9563
|
+
const allDedupMatches = [];
|
|
9564
|
+
for (const [index, msg] of messages.entries()) {
|
|
9565
|
+
const source = {
|
|
9566
|
+
conversationId: options.conversationId,
|
|
9567
|
+
messageIds: [msg.id ?? `msg-${index}`],
|
|
9568
|
+
timestamp: msg.timestamp ?? nowIso(),
|
|
9569
|
+
turnIndex: index
|
|
9570
|
+
};
|
|
9571
|
+
const instructions = detectExplicitInstructions(msg.content, source);
|
|
9572
|
+
allInstructions.push(...instructions);
|
|
9573
|
+
const hasDoNotRemember = instructions.some((i2) => i2.type === "do-not-remember");
|
|
9574
|
+
const hasExplicitRemember = instructions.some((i2) => i2.type === "remember");
|
|
9575
|
+
if (hasDoNotRemember && !hasExplicitRemember) {
|
|
9576
|
+
const item2 = {
|
|
9577
|
+
id: generateId(),
|
|
9578
|
+
category: "temporary_context",
|
|
9579
|
+
statement: msg.content,
|
|
9580
|
+
content: { text: msg.content, role: msg.role },
|
|
9581
|
+
entities: [],
|
|
9582
|
+
relationships: [],
|
|
9583
|
+
provenance: {
|
|
9584
|
+
source,
|
|
9585
|
+
classification: "temporary_context",
|
|
9586
|
+
confidence: "low",
|
|
9587
|
+
epistemicType: "observed",
|
|
9588
|
+
capturedAt: nowIso(),
|
|
9589
|
+
method: "automatic"
|
|
9590
|
+
},
|
|
9591
|
+
confidence: "low",
|
|
9592
|
+
epistemicType: "observed"
|
|
9593
|
+
};
|
|
9594
|
+
allItems.push(item2);
|
|
9595
|
+
ignored++;
|
|
9596
|
+
continue;
|
|
9597
|
+
}
|
|
9598
|
+
const category = classifyCategory(msg.content, instructions);
|
|
9599
|
+
const epistemicType = determineEpistemicType(category, msg.content, instructions);
|
|
9600
|
+
const confidence = determineConfidence(category, epistemicType, msg.content);
|
|
9601
|
+
const entities = extractEntities(msg.content);
|
|
9602
|
+
const relationships = extractRelationships(msg.content, entities);
|
|
9603
|
+
const provenance = {
|
|
9604
|
+
source,
|
|
9605
|
+
classification: category,
|
|
9606
|
+
confidence,
|
|
9607
|
+
epistemicType,
|
|
9608
|
+
capturedAt: nowIso(),
|
|
9609
|
+
method: hasExplicitRemember ? "explicit" : "automatic"
|
|
9610
|
+
};
|
|
9611
|
+
const item = {
|
|
9612
|
+
id: generateId(),
|
|
9613
|
+
category,
|
|
9614
|
+
statement: msg.content,
|
|
9615
|
+
content: { text: msg.content, role: msg.role },
|
|
9616
|
+
entities,
|
|
9617
|
+
relationships,
|
|
9618
|
+
provenance,
|
|
9619
|
+
confidence,
|
|
9620
|
+
epistemicType
|
|
9621
|
+
};
|
|
9622
|
+
const dedupMatches = await findExistingSimilarItems(
|
|
9623
|
+
msg.content,
|
|
9624
|
+
category,
|
|
9625
|
+
allItems,
|
|
9626
|
+
this.config.dedupSimilarityThreshold
|
|
9627
|
+
);
|
|
9628
|
+
const memoryMatches = await findExistingMemoryEntries(msg.content, category);
|
|
9629
|
+
const allMatches = [...dedupMatches, ...memoryMatches];
|
|
9630
|
+
allDedupMatches.push(...allMatches);
|
|
9631
|
+
let decision = "create";
|
|
9632
|
+
if (allMatches.length > 0) {
|
|
9633
|
+
const exactMatch = allMatches.find((m) => m.matchType === "exact");
|
|
9634
|
+
if (exactMatch) {
|
|
9635
|
+
decision = "link";
|
|
9636
|
+
linked++;
|
|
9637
|
+
} else {
|
|
9638
|
+
decision = "create";
|
|
9639
|
+
created++;
|
|
9640
|
+
}
|
|
9641
|
+
} else {
|
|
9642
|
+
created++;
|
|
9643
|
+
}
|
|
9644
|
+
const shouldStore = decision === "create" || decision === "link";
|
|
9645
|
+
if (shouldStore) {
|
|
9646
|
+
await routeToStorage(item, this.adapters, this.config);
|
|
9647
|
+
}
|
|
9648
|
+
if (!shouldStore) ignored++;
|
|
9649
|
+
allItems.push(item);
|
|
9650
|
+
}
|
|
9651
|
+
await this.storeSessionContext(options.conversationId, allItems, allInstructions);
|
|
9652
|
+
return {
|
|
9653
|
+
items: allItems,
|
|
9654
|
+
created,
|
|
9655
|
+
updated,
|
|
9656
|
+
linked,
|
|
9657
|
+
ignored,
|
|
9658
|
+
dedupMatches: allDedupMatches,
|
|
9659
|
+
provenance: allItems.map((i2) => i2.provenance)
|
|
9660
|
+
};
|
|
9661
|
+
}
|
|
9662
|
+
/**
|
|
9663
|
+
* Retrieve captured items for a conversation.
|
|
9664
|
+
*/
|
|
9665
|
+
async getCapturedItems(conversationId) {
|
|
9666
|
+
const ctx = this.sessionContexts.get(conversationId);
|
|
9667
|
+
return ctx ? ctx.items : [];
|
|
9668
|
+
}
|
|
9669
|
+
/**
|
|
9670
|
+
* Get all captured items across all conversations.
|
|
9671
|
+
*/
|
|
9672
|
+
async getAllCapturedItems() {
|
|
9673
|
+
const all = [];
|
|
9674
|
+
for (const ctx of this.sessionContexts.values()) {
|
|
9675
|
+
all.push(...ctx.items);
|
|
9676
|
+
}
|
|
9677
|
+
return all;
|
|
9678
|
+
}
|
|
9679
|
+
/**
|
|
9680
|
+
* Clear session context (for testing / cleanup).
|
|
9681
|
+
*/
|
|
9682
|
+
clearSession(conversationId) {
|
|
9683
|
+
this.sessionContexts.delete(conversationId);
|
|
9684
|
+
}
|
|
9685
|
+
/**
|
|
9686
|
+
* Get capture engine configuration.
|
|
9687
|
+
*/
|
|
9688
|
+
getConfig() {
|
|
9689
|
+
return { ...this.config };
|
|
9690
|
+
}
|
|
9691
|
+
// ---------------------------------------------------------------------------
|
|
9692
|
+
// Private
|
|
9693
|
+
// ---------------------------------------------------------------------------
|
|
9694
|
+
async storeSessionContext(conversationId, items, instructions) {
|
|
9695
|
+
let ctx = this.sessionContexts.get(conversationId);
|
|
9696
|
+
if (!ctx) {
|
|
9697
|
+
ctx = {
|
|
9698
|
+
conversationId,
|
|
9699
|
+
startedAt: nowIso(),
|
|
9700
|
+
items: [],
|
|
9701
|
+
explicitInstructions: []
|
|
9702
|
+
};
|
|
9703
|
+
this.sessionContexts.set(conversationId, ctx);
|
|
9704
|
+
}
|
|
9705
|
+
ctx.items.push(...items);
|
|
9706
|
+
ctx.explicitInstructions.push(...instructions);
|
|
9707
|
+
try {
|
|
9708
|
+
const stateKey = `${CAPTURE_STATE_KEY}:${conversationId}`;
|
|
9709
|
+
const existing = store.get(stateKey);
|
|
9710
|
+
let prior = [];
|
|
9711
|
+
if (existing) {
|
|
9712
|
+
try {
|
|
9713
|
+
prior = JSON.parse(existing);
|
|
9714
|
+
} catch {
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
9717
|
+
store.set(stateKey, JSON.stringify([...prior, ...items]));
|
|
9718
|
+
} catch {
|
|
9719
|
+
}
|
|
9720
|
+
}
|
|
9721
|
+
};
|
|
9722
|
+
var captureEngine = new CaptureEngine();
|
|
9723
|
+
async function captureConversation(messages, options) {
|
|
9724
|
+
return captureEngine.captureConversation(messages, options);
|
|
9725
|
+
}
|
|
9726
|
+
__name(captureConversation, "captureConversation");
|
|
9727
|
+
async function captureTurn(text, options) {
|
|
9728
|
+
return captureEngine.captureTurn(text, options);
|
|
9729
|
+
}
|
|
9730
|
+
__name(captureTurn, "captureTurn");
|
|
9731
|
+
|
|
8462
9732
|
// core/memory/index.ts
|
|
8463
9733
|
init_detection_events();
|
|
8464
9734
|
init_detection_journal();
|
|
@@ -8646,6 +9916,25 @@ var memory = {
|
|
|
8646
9916
|
async getSeedId() {
|
|
8647
9917
|
return SEED_ID;
|
|
8648
9918
|
}
|
|
9919
|
+
},
|
|
9920
|
+
/** Automatic conversational capture engine (Phase 2 — Workstream B). */
|
|
9921
|
+
capture: {
|
|
9922
|
+
engine: captureEngine,
|
|
9923
|
+
async conversation(messages, options) {
|
|
9924
|
+
return captureConversation(messages, options);
|
|
9925
|
+
},
|
|
9926
|
+
async turn(text, options) {
|
|
9927
|
+
return captureTurn(text, options);
|
|
9928
|
+
},
|
|
9929
|
+
async getItems(conversationId) {
|
|
9930
|
+
return captureEngine.getCapturedItems(conversationId);
|
|
9931
|
+
},
|
|
9932
|
+
async getAllItems() {
|
|
9933
|
+
return captureEngine.getAllCapturedItems();
|
|
9934
|
+
},
|
|
9935
|
+
clearSession(conversationId) {
|
|
9936
|
+
captureEngine.clearSession(conversationId);
|
|
9937
|
+
}
|
|
8649
9938
|
}
|
|
8650
9939
|
};
|
|
8651
9940
|
|