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/global.cjs
CHANGED
|
@@ -5061,13 +5061,13 @@ var init_mem_repository = __esm({
|
|
|
5061
5061
|
});
|
|
5062
5062
|
}
|
|
5063
5063
|
async loadSources(sourceIds) {
|
|
5064
|
-
const { readFile } = await nodeFs4();
|
|
5064
|
+
const { readFile: readFile4 } = await nodeFs4();
|
|
5065
5065
|
const ids = sourceIds ?? await this.discover();
|
|
5066
5066
|
const sources = [];
|
|
5067
5067
|
for (const sourceId of ids) {
|
|
5068
5068
|
const filePath = memFilePath(this.directoryPath, sourceId);
|
|
5069
5069
|
try {
|
|
5070
|
-
const decoded = await parseMemFile(await
|
|
5070
|
+
const decoded = await parseMemFile(await readFile4(filePath), sourceId);
|
|
5071
5071
|
sources.push({
|
|
5072
5072
|
id: sourceId,
|
|
5073
5073
|
filePath,
|
|
@@ -5151,10 +5151,10 @@ var init_mem_repository = __esm({
|
|
|
5151
5151
|
* valid package, so the backup is always a version this library can read.
|
|
5152
5152
|
*/
|
|
5153
5153
|
async keepLastValidVersion(filePath) {
|
|
5154
|
-
const { readFile, copyFile, rename, rm } = await nodeFs4();
|
|
5154
|
+
const { readFile: readFile4, copyFile, rename, rm } = await nodeFs4();
|
|
5155
5155
|
let current;
|
|
5156
5156
|
try {
|
|
5157
|
-
current = await
|
|
5157
|
+
current = await readFile4(filePath);
|
|
5158
5158
|
} catch {
|
|
5159
5159
|
return;
|
|
5160
5160
|
}
|
|
@@ -5187,7 +5187,7 @@ var init_mem_repository = __esm({
|
|
|
5187
5187
|
}
|
|
5188
5188
|
}
|
|
5189
5189
|
async writeSource(filePath, state3, preserved, preservedPackage) {
|
|
5190
|
-
const { mkdir, rename, rm, open, readFile } = await nodeFs4();
|
|
5190
|
+
const { mkdir, rename, rm, open, readFile: readFile4 } = await nodeFs4();
|
|
5191
5191
|
if (!Number.isInteger(state3.version) || state3.version < 0) throw new Error("Invalid writable .mem knowledge version");
|
|
5192
5192
|
for (const claim of state3.claims) validateClaim(claim);
|
|
5193
5193
|
await mkdir(parentPath(filePath), { recursive: true });
|
|
@@ -5209,7 +5209,7 @@ var init_mem_repository = __esm({
|
|
|
5209
5209
|
} finally {
|
|
5210
5210
|
await handle.close();
|
|
5211
5211
|
}
|
|
5212
|
-
await decodeMemArtifact(await
|
|
5212
|
+
await decodeMemArtifact(await readFile4(temporary));
|
|
5213
5213
|
await this.keepLastValidVersion(filePath);
|
|
5214
5214
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
5215
5215
|
try {
|
|
@@ -5237,8 +5237,8 @@ var init_mem_repository = __esm({
|
|
|
5237
5237
|
// core/knowledge/project-anchor.ts
|
|
5238
5238
|
async function projectAnchorExists(projectId) {
|
|
5239
5239
|
try {
|
|
5240
|
-
const { readFile } = await import('fs/promises');
|
|
5241
|
-
await
|
|
5240
|
+
const { readFile: readFile4 } = await import('fs/promises');
|
|
5241
|
+
await readFile4(resolveUserProjectMemoryPath(projectId));
|
|
5242
5242
|
return true;
|
|
5243
5243
|
} catch {
|
|
5244
5244
|
return false;
|
|
@@ -5274,7 +5274,7 @@ var init_project_anchor = __esm({
|
|
|
5274
5274
|
var configureUserIdentityStore;
|
|
5275
5275
|
var init_user_identity = __esm({
|
|
5276
5276
|
"core/knowledge/user-identity.ts"() {
|
|
5277
|
-
configureUserIdentityStore = /* @__PURE__ */ __name((
|
|
5277
|
+
configureUserIdentityStore = /* @__PURE__ */ __name((store7) => {
|
|
5278
5278
|
}, "configureUserIdentityStore");
|
|
5279
5279
|
}
|
|
5280
5280
|
});
|
|
@@ -5878,9 +5878,155 @@ var init_staging = __esm({
|
|
|
5878
5878
|
});
|
|
5879
5879
|
|
|
5880
5880
|
// core/knowledge/loops.ts
|
|
5881
|
+
function joinPath3(base, child) {
|
|
5882
|
+
const separator3 = process.platform === "win32" ? "\\" : "/";
|
|
5883
|
+
return `${base.replace(/[\\/]+/g, separator3)}${separator3}${child}`;
|
|
5884
|
+
}
|
|
5885
|
+
function resolveLoopsFilePath() {
|
|
5886
|
+
return joinPath3(resolveUserDotMemPath(), LOOPS_FILE_NAME);
|
|
5887
|
+
}
|
|
5888
|
+
async function readFile(path) {
|
|
5889
|
+
try {
|
|
5890
|
+
const fs = await import('fs/promises');
|
|
5891
|
+
return await fs.readFile(path, "utf8");
|
|
5892
|
+
} catch {
|
|
5893
|
+
return null;
|
|
5894
|
+
}
|
|
5895
|
+
}
|
|
5896
|
+
async function writeFile(path, content) {
|
|
5897
|
+
const fs = await import('fs/promises');
|
|
5898
|
+
const dir = path.replace(/[\\/][^\\/]+$/, "");
|
|
5899
|
+
await fs.mkdir(dir, { recursive: true });
|
|
5900
|
+
const temporary = `${path}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
5901
|
+
try {
|
|
5902
|
+
await fs.writeFile(temporary, content, "utf8");
|
|
5903
|
+
await fs.rename(temporary, path);
|
|
5904
|
+
} catch {
|
|
5905
|
+
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
5906
|
+
throw new Error(`Failed to write loops file at ${path}`);
|
|
5907
|
+
}
|
|
5908
|
+
}
|
|
5909
|
+
function emptyDocument() {
|
|
5910
|
+
return { version: LOOPS_FORMAT_VERSION, loops: {} };
|
|
5911
|
+
}
|
|
5912
|
+
function validateDocument(value) {
|
|
5913
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5914
|
+
throw new Error("Loops document must be an object");
|
|
5915
|
+
}
|
|
5916
|
+
const document = value;
|
|
5917
|
+
if (document.version !== LOOPS_FORMAT_VERSION) {
|
|
5918
|
+
throw new Error(`Unsupported loops document version: ${String(document.version)}`);
|
|
5919
|
+
}
|
|
5920
|
+
if (!document.loops || typeof document.loops !== "object" || Array.isArray(document.loops)) {
|
|
5921
|
+
throw new Error("Loops document loops must be an object");
|
|
5922
|
+
}
|
|
5923
|
+
const loops = {};
|
|
5924
|
+
for (const [id, loopValue] of Object.entries(document.loops)) {
|
|
5925
|
+
const loop = validateLoop(loopValue, id);
|
|
5926
|
+
loops[loop.id] = loop;
|
|
5927
|
+
}
|
|
5928
|
+
return { version: LOOPS_FORMAT_VERSION, loops };
|
|
5929
|
+
}
|
|
5930
|
+
function validateLoop(value, id) {
|
|
5931
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5932
|
+
throw new Error(`Loop ${id} must be an object`);
|
|
5933
|
+
}
|
|
5934
|
+
const loop = value;
|
|
5935
|
+
if (typeof loop.id !== "string" || !loop.id) {
|
|
5936
|
+
throw new Error(`Loop ${id} requires an id`);
|
|
5937
|
+
}
|
|
5938
|
+
if (typeof loop.text !== "string" || !loop.text) {
|
|
5939
|
+
throw new Error(`Loop ${id} requires non-empty text`);
|
|
5940
|
+
}
|
|
5941
|
+
if (typeof loop.waitor !== "string" || !loop.waitor) {
|
|
5942
|
+
throw new Error(`Loop ${id} requires a waitor`);
|
|
5943
|
+
}
|
|
5944
|
+
if (typeof loop.openedAt !== "string" || !loop.openedAt) {
|
|
5945
|
+
throw new Error(`Loop ${id} requires an openedAt timestamp`);
|
|
5946
|
+
}
|
|
5947
|
+
const result = {
|
|
5948
|
+
id: loop.id,
|
|
5949
|
+
text: loop.text,
|
|
5950
|
+
waitor: loop.waitor,
|
|
5951
|
+
openedAt: loop.openedAt
|
|
5952
|
+
};
|
|
5953
|
+
if (typeof loop.closedAt === "string") result.closedAt = loop.closedAt;
|
|
5954
|
+
if (typeof loop.closedBy === "string") result.closedBy = loop.closedBy;
|
|
5955
|
+
if (typeof loop.closeNote === "string") result.closeNote = loop.closeNote;
|
|
5956
|
+
return result;
|
|
5957
|
+
}
|
|
5958
|
+
async function loadLoops() {
|
|
5959
|
+
const raw = await readFile(resolveLoopsFilePath());
|
|
5960
|
+
if (!raw) return emptyDocument();
|
|
5961
|
+
try {
|
|
5962
|
+
return validateDocument(JSON.parse(raw));
|
|
5963
|
+
} catch {
|
|
5964
|
+
return emptyDocument();
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
async function saveLoops(document) {
|
|
5968
|
+
await writeFile(resolveLoopsFilePath(), JSON.stringify(document, null, 2) + "\n");
|
|
5969
|
+
}
|
|
5970
|
+
async function addLoop(input) {
|
|
5971
|
+
const document = await loadLoops();
|
|
5972
|
+
if (document.loops[input.id]) {
|
|
5973
|
+
throw new Error(`Loop ${input.id} already exists`);
|
|
5974
|
+
}
|
|
5975
|
+
const loop = {
|
|
5976
|
+
id: input.id,
|
|
5977
|
+
text: input.text,
|
|
5978
|
+
waitor: input.waitor,
|
|
5979
|
+
openedAt: input.openedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
5980
|
+
};
|
|
5981
|
+
document.loops[loop.id] = loop;
|
|
5982
|
+
await saveLoops(document);
|
|
5983
|
+
return loop;
|
|
5984
|
+
}
|
|
5985
|
+
async function listLoops(openOnly = true) {
|
|
5986
|
+
const document = await loadLoops();
|
|
5987
|
+
const all = Object.values(document.loops);
|
|
5988
|
+
if (openOnly) {
|
|
5989
|
+
return all.filter((loop) => loop.closedAt === void 0);
|
|
5990
|
+
}
|
|
5991
|
+
return all;
|
|
5992
|
+
}
|
|
5993
|
+
async function closeLoop(id, input) {
|
|
5994
|
+
const document = await loadLoops();
|
|
5995
|
+
const existing = document.loops[id];
|
|
5996
|
+
if (!existing) {
|
|
5997
|
+
throw new Error(`Loop ${id} not found`);
|
|
5998
|
+
}
|
|
5999
|
+
if (existing.closedAt) {
|
|
6000
|
+
throw new Error(`Loop ${id} is already closed`);
|
|
6001
|
+
}
|
|
6002
|
+
const closed = {
|
|
6003
|
+
...existing,
|
|
6004
|
+
closedAt: input.closedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6005
|
+
closedBy: input.by,
|
|
6006
|
+
...input.note ? { closeNote: input.note } : {}
|
|
6007
|
+
};
|
|
6008
|
+
document.loops[id] = closed;
|
|
6009
|
+
await saveLoops(document);
|
|
6010
|
+
return closed;
|
|
6011
|
+
}
|
|
6012
|
+
var LOOPS_FILE_NAME, LOOPS_FORMAT_VERSION;
|
|
5881
6013
|
var init_loops = __esm({
|
|
5882
6014
|
"core/knowledge/loops.ts"() {
|
|
5883
6015
|
init_user_paths();
|
|
6016
|
+
LOOPS_FILE_NAME = "loops.json";
|
|
6017
|
+
LOOPS_FORMAT_VERSION = 1;
|
|
6018
|
+
__name(joinPath3, "joinPath");
|
|
6019
|
+
__name(resolveLoopsFilePath, "resolveLoopsFilePath");
|
|
6020
|
+
__name(readFile, "readFile");
|
|
6021
|
+
__name(writeFile, "writeFile");
|
|
6022
|
+
__name(emptyDocument, "emptyDocument");
|
|
6023
|
+
__name(validateDocument, "validateDocument");
|
|
6024
|
+
__name(validateLoop, "validateLoop");
|
|
6025
|
+
__name(loadLoops, "loadLoops");
|
|
6026
|
+
__name(saveLoops, "saveLoops");
|
|
6027
|
+
__name(addLoop, "addLoop");
|
|
6028
|
+
__name(listLoops, "listLoops");
|
|
6029
|
+
__name(closeLoop, "closeLoop");
|
|
5884
6030
|
}
|
|
5885
6031
|
});
|
|
5886
6032
|
|
|
@@ -5965,16 +6111,368 @@ var init_session = __esm({
|
|
|
5965
6111
|
});
|
|
5966
6112
|
|
|
5967
6113
|
// core/knowledge/exemplars.ts
|
|
6114
|
+
function joinPath4(base, child) {
|
|
6115
|
+
const separator3 = process.platform === "win32" ? "\\" : "/";
|
|
6116
|
+
return `${base.replace(/[\\/]+/g, separator3)}${separator3}${child}`;
|
|
6117
|
+
}
|
|
6118
|
+
function resolveExemplarsPath() {
|
|
6119
|
+
return joinPath4(resolveUserDotMemPath(), EXEMPLARS_FILE_NAME);
|
|
6120
|
+
}
|
|
6121
|
+
async function readFile2(path) {
|
|
6122
|
+
try {
|
|
6123
|
+
const fs = await import('fs/promises');
|
|
6124
|
+
return await fs.readFile(path, "utf8");
|
|
6125
|
+
} catch {
|
|
6126
|
+
return null;
|
|
6127
|
+
}
|
|
6128
|
+
}
|
|
6129
|
+
async function writeFile2(path, content) {
|
|
6130
|
+
const fs = await import('fs/promises');
|
|
6131
|
+
const dir = path.replace(/[\\/][^\\/]+$/, "");
|
|
6132
|
+
await fs.mkdir(dir, { recursive: true });
|
|
6133
|
+
const temporary = `${path}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
6134
|
+
try {
|
|
6135
|
+
await fs.writeFile(temporary, content, "utf8");
|
|
6136
|
+
await fs.rename(temporary, path);
|
|
6137
|
+
} catch {
|
|
6138
|
+
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
6139
|
+
throw new Error(`Failed to write exemplars file at ${path}`);
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
6142
|
+
function emptyStore() {
|
|
6143
|
+
return { version: EXEMPLARS_FORMAT_VERSION, exemplars: {} };
|
|
6144
|
+
}
|
|
6145
|
+
function validateExemplar(value) {
|
|
6146
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6147
|
+
throw new Error("Exemplar must be an object");
|
|
6148
|
+
}
|
|
6149
|
+
const e = value;
|
|
6150
|
+
if (typeof e.id !== "string" || !e.id) throw new Error("Exemplar requires non-empty id");
|
|
6151
|
+
if (typeof e.pattern !== "string" || !e.pattern) {
|
|
6152
|
+
throw new Error(`Exemplar ${e.id} requires non-empty pattern`);
|
|
6153
|
+
}
|
|
6154
|
+
const source = e.source ?? {};
|
|
6155
|
+
if (typeof source.conversationId !== "string" || !source.conversationId) {
|
|
6156
|
+
throw new Error(`Exemplar ${e.id} requires source.conversationId`);
|
|
6157
|
+
}
|
|
6158
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6159
|
+
return {
|
|
6160
|
+
id: e.id,
|
|
6161
|
+
type: "interaction-exemplar",
|
|
6162
|
+
source: {
|
|
6163
|
+
conversationId: source.conversationId,
|
|
6164
|
+
messageIds: Array.isArray(source.messageIds) ? source.messageIds.map(String) : [],
|
|
6165
|
+
...typeof source.timestamp === "string" ? { timestamp: source.timestamp } : {}
|
|
6166
|
+
},
|
|
6167
|
+
pattern: e.pattern,
|
|
6168
|
+
originalContent: typeof e.originalContent === "string" ? e.originalContent : null,
|
|
6169
|
+
sourceReference: typeof e.sourceReference === "string" ? e.sourceReference : null,
|
|
6170
|
+
retrievalHints: Array.isArray(e.retrievalHints) ? e.retrievalHints.map(String) : [],
|
|
6171
|
+
significance: typeof e.significance === "number" && e.significance >= 0 ? e.significance : 0,
|
|
6172
|
+
confidence: typeof e.confidence === "number" && e.confidence >= 0 && e.confidence <= 1 ? e.confidence : 0,
|
|
6173
|
+
provenance: ["human-stated", "observed", "inferred"].includes(
|
|
6174
|
+
e.provenance
|
|
6175
|
+
) ? e.provenance : "observed",
|
|
6176
|
+
createdAt: typeof e.createdAt === "string" ? e.createdAt : now2,
|
|
6177
|
+
revision: typeof e.revision === "number" && e.revision > 0 ? e.revision : 1
|
|
6178
|
+
};
|
|
6179
|
+
}
|
|
6180
|
+
function validateStore(value) {
|
|
6181
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6182
|
+
throw new Error("Exemplars document must be an object");
|
|
6183
|
+
}
|
|
6184
|
+
const doc = value;
|
|
6185
|
+
if (doc.version !== EXEMPLARS_FORMAT_VERSION) {
|
|
6186
|
+
throw new Error(`Unsupported exemplars version: ${String(doc.version)}`);
|
|
6187
|
+
}
|
|
6188
|
+
const exemplars = {};
|
|
6189
|
+
if (doc.exemplars && typeof doc.exemplars === "object" && !Array.isArray(doc.exemplars)) {
|
|
6190
|
+
for (const [_, exemplarValue] of Object.entries(doc.exemplars)) {
|
|
6191
|
+
const exemplar = validateExemplar(exemplarValue);
|
|
6192
|
+
exemplars[exemplar.id] = exemplar;
|
|
6193
|
+
}
|
|
6194
|
+
}
|
|
6195
|
+
return { version: EXEMPLARS_FORMAT_VERSION, exemplars };
|
|
6196
|
+
}
|
|
6197
|
+
async function loadExemplars() {
|
|
6198
|
+
const raw = await readFile2(resolveExemplarsPath());
|
|
6199
|
+
if (raw === null) return emptyStore();
|
|
6200
|
+
try {
|
|
6201
|
+
return validateStore(JSON.parse(raw));
|
|
6202
|
+
} catch {
|
|
6203
|
+
return emptyStore();
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6206
|
+
async function saveExemplars(store7) {
|
|
6207
|
+
const validated = validateStore(store7);
|
|
6208
|
+
await writeFile2(resolveExemplarsPath(), JSON.stringify(validated, null, 2));
|
|
6209
|
+
}
|
|
6210
|
+
async function addExemplar(exemplar) {
|
|
6211
|
+
const store7 = await loadExemplars();
|
|
6212
|
+
if (store7.exemplars[exemplar.id]) {
|
|
6213
|
+
throw new Error(`Exemplar ${exemplar.id} already exists`);
|
|
6214
|
+
}
|
|
6215
|
+
if (exemplar.sourceReference) {
|
|
6216
|
+
const existingRef = Object.values(store7.exemplars).find(
|
|
6217
|
+
(e) => e.sourceReference === exemplar.sourceReference
|
|
6218
|
+
);
|
|
6219
|
+
if (existingRef) {
|
|
6220
|
+
throw new Error(`Exemplar with source reference ${exemplar.sourceReference} already exists (id: ${existingRef.id})`);
|
|
6221
|
+
}
|
|
6222
|
+
}
|
|
6223
|
+
const full = {
|
|
6224
|
+
...exemplar,
|
|
6225
|
+
type: "interaction-exemplar",
|
|
6226
|
+
revision: 1
|
|
6227
|
+
};
|
|
6228
|
+
store7.exemplars[exemplar.id] = full;
|
|
6229
|
+
await saveExemplars(store7);
|
|
6230
|
+
return full;
|
|
6231
|
+
}
|
|
6232
|
+
async function updateExemplar(id, updates) {
|
|
6233
|
+
const store7 = await loadExemplars();
|
|
6234
|
+
const existing = store7.exemplars[id];
|
|
6235
|
+
if (!existing) {
|
|
6236
|
+
throw new Error(`Exemplar ${id} not found`);
|
|
6237
|
+
}
|
|
6238
|
+
const updated = {
|
|
6239
|
+
...existing,
|
|
6240
|
+
...updates,
|
|
6241
|
+
id: existing.id,
|
|
6242
|
+
type: "interaction-exemplar",
|
|
6243
|
+
createdAt: existing.createdAt,
|
|
6244
|
+
revision: (updates.revision ?? existing.revision) + 1
|
|
6245
|
+
};
|
|
6246
|
+
store7.exemplars[id] = updated;
|
|
6247
|
+
await saveExemplars(store7);
|
|
6248
|
+
return updated;
|
|
6249
|
+
}
|
|
6250
|
+
var EXEMPLARS_FILE_NAME, EXEMPLARS_FORMAT_VERSION;
|
|
5968
6251
|
var init_exemplars = __esm({
|
|
5969
6252
|
"core/knowledge/exemplars.ts"() {
|
|
5970
6253
|
init_user_paths();
|
|
6254
|
+
EXEMPLARS_FILE_NAME = "exemplars.json";
|
|
6255
|
+
EXEMPLARS_FORMAT_VERSION = 1;
|
|
6256
|
+
__name(joinPath4, "joinPath");
|
|
6257
|
+
__name(resolveExemplarsPath, "resolveExemplarsPath");
|
|
6258
|
+
__name(readFile2, "readFile");
|
|
6259
|
+
__name(writeFile2, "writeFile");
|
|
6260
|
+
__name(emptyStore, "emptyStore");
|
|
6261
|
+
__name(validateExemplar, "validateExemplar");
|
|
6262
|
+
__name(validateStore, "validateStore");
|
|
6263
|
+
__name(loadExemplars, "loadExemplars");
|
|
6264
|
+
__name(saveExemplars, "saveExemplars");
|
|
6265
|
+
__name(addExemplar, "addExemplar");
|
|
6266
|
+
__name(updateExemplar, "updateExemplar");
|
|
5971
6267
|
}
|
|
5972
6268
|
});
|
|
5973
6269
|
|
|
5974
6270
|
// core/knowledge/personality-anchor.ts
|
|
6271
|
+
function joinPath5(base, child) {
|
|
6272
|
+
const separator3 = process.platform === "win32" ? "\\" : "/";
|
|
6273
|
+
return `${base.replace(/[\\/]+/g, separator3)}${separator3}${child}`;
|
|
6274
|
+
}
|
|
6275
|
+
function resolvePersonalityAnchorPath() {
|
|
6276
|
+
return joinPath5(resolveUserDotMemPath(), PERSONALITY_ANCHOR_FILE_NAME);
|
|
6277
|
+
}
|
|
6278
|
+
async function readFile3(path) {
|
|
6279
|
+
try {
|
|
6280
|
+
const fs = await import('fs/promises');
|
|
6281
|
+
return await fs.readFile(path, "utf8");
|
|
6282
|
+
} catch {
|
|
6283
|
+
return null;
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
async function writeFile3(path, content) {
|
|
6287
|
+
const fs = await import('fs/promises');
|
|
6288
|
+
const dir = path.replace(/[\\/][^\\/]+$/, "");
|
|
6289
|
+
await fs.mkdir(dir, { recursive: true });
|
|
6290
|
+
const temporary = `${path}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
6291
|
+
try {
|
|
6292
|
+
await fs.writeFile(temporary, content, "utf8");
|
|
6293
|
+
await fs.rename(temporary, path);
|
|
6294
|
+
} catch {
|
|
6295
|
+
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
6296
|
+
throw new Error(`Failed to write personality anchor at ${path}`);
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
function emptyAnchor() {
|
|
6300
|
+
const epoch = "1970-01-01T00:00:00.000Z";
|
|
6301
|
+
return {
|
|
6302
|
+
version: PERSONALITY_ANCHOR_FORMAT_VERSION,
|
|
6303
|
+
identity: {
|
|
6304
|
+
communicationStyle: [],
|
|
6305
|
+
durablePreferences: [],
|
|
6306
|
+
interactionBoundaries: []
|
|
6307
|
+
},
|
|
6308
|
+
metadata: {
|
|
6309
|
+
createdAt: epoch,
|
|
6310
|
+
updatedAt: epoch,
|
|
6311
|
+
revision: 1,
|
|
6312
|
+
sourceReferences: []
|
|
6313
|
+
}
|
|
6314
|
+
};
|
|
6315
|
+
}
|
|
6316
|
+
function validateCommunicationStyle(value) {
|
|
6317
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6318
|
+
throw new Error("communicationStyle entry must be an object");
|
|
6319
|
+
}
|
|
6320
|
+
const style = value;
|
|
6321
|
+
if (typeof style.description !== "string" || !style.description) {
|
|
6322
|
+
throw new Error("communicationStyle entry requires non-empty description");
|
|
6323
|
+
}
|
|
6324
|
+
const confidence = style.confidence ?? "inferred";
|
|
6325
|
+
if (!["inferred", "observed", "human-stated"].includes(confidence)) {
|
|
6326
|
+
throw new Error(`Invalid confidence value: ${String(style.confidence)}`);
|
|
6327
|
+
}
|
|
6328
|
+
const evidence = Array.isArray(style.evidence) ? style.evidence.map(String) : [];
|
|
6329
|
+
return {
|
|
6330
|
+
description: style.description,
|
|
6331
|
+
confidence,
|
|
6332
|
+
evidence,
|
|
6333
|
+
lastObserved: typeof style.lastObserved === "string" ? style.lastObserved : (/* @__PURE__ */ new Date()).toISOString()
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
function validateDurablePreference(value) {
|
|
6337
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6338
|
+
throw new Error("durablePreference entry must be an object");
|
|
6339
|
+
}
|
|
6340
|
+
const pref = value;
|
|
6341
|
+
if (typeof pref.id !== "string" || !pref.id) {
|
|
6342
|
+
throw new Error("durablePreference entry requires non-empty id");
|
|
6343
|
+
}
|
|
6344
|
+
if (typeof pref.statement !== "string" || !pref.statement) {
|
|
6345
|
+
throw new Error(`durablePreference ${pref.id} requires non-empty statement`);
|
|
6346
|
+
}
|
|
6347
|
+
const confidence = pref.confidence ?? "inferred";
|
|
6348
|
+
if (!["inferred", "observed", "human-stated"].includes(confidence)) {
|
|
6349
|
+
throw new Error(`Invalid confidence value: ${String(pref.confidence)}`);
|
|
6350
|
+
}
|
|
6351
|
+
const evidence = Array.isArray(pref.evidence) ? pref.evidence.map(String) : [];
|
|
6352
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6353
|
+
return {
|
|
6354
|
+
id: pref.id,
|
|
6355
|
+
statement: pref.statement,
|
|
6356
|
+
confidence,
|
|
6357
|
+
evidence,
|
|
6358
|
+
firstObserved: typeof pref.firstObserved === "string" ? pref.firstObserved : now2,
|
|
6359
|
+
lastConfirmed: typeof pref.lastConfirmed === "string" ? pref.lastConfirmed : now2,
|
|
6360
|
+
...typeof pref.supersededBy === "string" ? { supersededBy: pref.supersededBy } : {}
|
|
6361
|
+
};
|
|
6362
|
+
}
|
|
6363
|
+
function validateInteractionBoundary(value) {
|
|
6364
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6365
|
+
throw new Error("interactionBoundary entry must be an object");
|
|
6366
|
+
}
|
|
6367
|
+
const boundary = value;
|
|
6368
|
+
if (typeof boundary.id !== "string" || !boundary.id) {
|
|
6369
|
+
throw new Error("interactionBoundary entry requires non-empty id");
|
|
6370
|
+
}
|
|
6371
|
+
if (typeof boundary.rule !== "string" || !boundary.rule) {
|
|
6372
|
+
throw new Error(`interactionBoundary ${boundary.id} requires non-empty rule`);
|
|
6373
|
+
}
|
|
6374
|
+
const confidence = boundary.confidence ?? "inferred";
|
|
6375
|
+
if (!["inferred", "observed", "human-stated"].includes(confidence)) {
|
|
6376
|
+
throw new Error(`Invalid confidence value: ${String(boundary.confidence)}`);
|
|
6377
|
+
}
|
|
6378
|
+
const evidence = Array.isArray(boundary.evidence) ? boundary.evidence.map(String) : [];
|
|
6379
|
+
const source = boundary.source ?? "conversation";
|
|
6380
|
+
if (!["profile", "memory", "conversation"].includes(source)) {
|
|
6381
|
+
throw new Error(`Invalid boundary source: ${String(boundary.source)}`);
|
|
6382
|
+
}
|
|
6383
|
+
return {
|
|
6384
|
+
id: boundary.id,
|
|
6385
|
+
rule: boundary.rule,
|
|
6386
|
+
confidence,
|
|
6387
|
+
evidence,
|
|
6388
|
+
source,
|
|
6389
|
+
lastConfirmed: typeof boundary.lastConfirmed === "string" ? boundary.lastConfirmed : (/* @__PURE__ */ new Date()).toISOString()
|
|
6390
|
+
};
|
|
6391
|
+
}
|
|
6392
|
+
function validateAnchor(value) {
|
|
6393
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6394
|
+
throw new Error("Personality anchor document must be an object");
|
|
6395
|
+
}
|
|
6396
|
+
const doc = value;
|
|
6397
|
+
if (doc.version !== PERSONALITY_ANCHOR_FORMAT_VERSION) {
|
|
6398
|
+
throw new Error(`Unsupported personality anchor version: ${String(doc.version)}`);
|
|
6399
|
+
}
|
|
6400
|
+
const identity = doc.identity ?? {};
|
|
6401
|
+
const metadata = doc.metadata ?? {};
|
|
6402
|
+
const communicationStyle = Array.isArray(identity.communicationStyle) ? identity.communicationStyle.map(validateCommunicationStyle) : [];
|
|
6403
|
+
const durablePreferences = Array.isArray(identity.durablePreferences) ? identity.durablePreferences.map(validateDurablePreference) : [];
|
|
6404
|
+
const interactionBoundaries = Array.isArray(identity.interactionBoundaries) ? identity.interactionBoundaries.map(validateInteractionBoundary) : [];
|
|
6405
|
+
const sourceRefs = Array.isArray(metadata.sourceReferences) ? metadata.sourceReferences.map(String) : [];
|
|
6406
|
+
return {
|
|
6407
|
+
version: PERSONALITY_ANCHOR_FORMAT_VERSION,
|
|
6408
|
+
identity: {
|
|
6409
|
+
communicationStyle,
|
|
6410
|
+
durablePreferences,
|
|
6411
|
+
interactionBoundaries
|
|
6412
|
+
},
|
|
6413
|
+
metadata: {
|
|
6414
|
+
createdAt: typeof metadata.createdAt === "string" ? metadata.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
6415
|
+
updatedAt: typeof metadata.updatedAt === "string" ? metadata.updatedAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
6416
|
+
revision: typeof metadata.revision === "number" && metadata.revision > 0 ? metadata.revision : 1,
|
|
6417
|
+
sourceReferences: sourceRefs
|
|
6418
|
+
}
|
|
6419
|
+
};
|
|
6420
|
+
}
|
|
6421
|
+
async function loadPersonalityAnchor() {
|
|
6422
|
+
const raw = await readFile3(resolvePersonalityAnchorPath());
|
|
6423
|
+
if (raw === null) return emptyAnchor();
|
|
6424
|
+
try {
|
|
6425
|
+
const parsed = JSON.parse(raw);
|
|
6426
|
+
return validateAnchor(parsed);
|
|
6427
|
+
} catch {
|
|
6428
|
+
return emptyAnchor();
|
|
6429
|
+
}
|
|
6430
|
+
}
|
|
6431
|
+
async function savePersonalityAnchor(anchor) {
|
|
6432
|
+
const validated = validateAnchor(anchor);
|
|
6433
|
+
const dirPath = resolveUserDotMemPath();
|
|
6434
|
+
const fs = await import('fs/promises');
|
|
6435
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
6436
|
+
await writeFile3(resolvePersonalityAnchorPath(), JSON.stringify(validated, null, 2));
|
|
6437
|
+
}
|
|
6438
|
+
async function updatePersonalityAnchor(updates) {
|
|
6439
|
+
const existing = await loadPersonalityAnchor();
|
|
6440
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
6441
|
+
const updated = {
|
|
6442
|
+
...existing,
|
|
6443
|
+
identity: {
|
|
6444
|
+
communicationStyle: updates.communicationStyle ?? existing.identity.communicationStyle,
|
|
6445
|
+
durablePreferences: updates.durablePreferences ?? existing.identity.durablePreferences,
|
|
6446
|
+
interactionBoundaries: updates.interactionBoundaries ?? existing.identity.interactionBoundaries
|
|
6447
|
+
},
|
|
6448
|
+
metadata: {
|
|
6449
|
+
...existing.metadata,
|
|
6450
|
+
updatedAt: now2,
|
|
6451
|
+
revision: existing.metadata.revision + 1,
|
|
6452
|
+
sourceReferences: updates.sourceReferences ? [.../* @__PURE__ */ new Set([...existing.metadata.sourceReferences, ...updates.sourceReferences])] : existing.metadata.sourceReferences
|
|
6453
|
+
}
|
|
6454
|
+
};
|
|
6455
|
+
await savePersonalityAnchor(updated);
|
|
6456
|
+
return updated;
|
|
6457
|
+
}
|
|
6458
|
+
var PERSONALITY_ANCHOR_FILE_NAME, PERSONALITY_ANCHOR_FORMAT_VERSION;
|
|
5975
6459
|
var init_personality_anchor = __esm({
|
|
5976
6460
|
"core/knowledge/personality-anchor.ts"() {
|
|
5977
6461
|
init_user_paths();
|
|
6462
|
+
PERSONALITY_ANCHOR_FILE_NAME = "personality-anchor.json";
|
|
6463
|
+
PERSONALITY_ANCHOR_FORMAT_VERSION = 1;
|
|
6464
|
+
__name(joinPath5, "joinPath");
|
|
6465
|
+
__name(resolvePersonalityAnchorPath, "resolvePersonalityAnchorPath");
|
|
6466
|
+
__name(readFile3, "readFile");
|
|
6467
|
+
__name(writeFile3, "writeFile");
|
|
6468
|
+
__name(emptyAnchor, "emptyAnchor");
|
|
6469
|
+
__name(validateCommunicationStyle, "validateCommunicationStyle");
|
|
6470
|
+
__name(validateDurablePreference, "validateDurablePreference");
|
|
6471
|
+
__name(validateInteractionBoundary, "validateInteractionBoundary");
|
|
6472
|
+
__name(validateAnchor, "validateAnchor");
|
|
6473
|
+
__name(loadPersonalityAnchor, "loadPersonalityAnchor");
|
|
6474
|
+
__name(savePersonalityAnchor, "savePersonalityAnchor");
|
|
6475
|
+
__name(updatePersonalityAnchor, "updatePersonalityAnchor");
|
|
5978
6476
|
}
|
|
5979
6477
|
});
|
|
5980
6478
|
|
|
@@ -7067,27 +7565,27 @@ var auditPolicyAction = /* @__PURE__ */ __name((action, key) => {
|
|
|
7067
7565
|
}
|
|
7068
7566
|
}, "auditPolicyAction");
|
|
7069
7567
|
var resolveOverrides = /* @__PURE__ */ __name(async (namespace2, now2 = () => /* @__PURE__ */ new Date()) => {
|
|
7070
|
-
const
|
|
7568
|
+
const store7 = await getPolicyStore();
|
|
7071
7569
|
const namespaces = namespace2 ? [namespace2] : POLICY_NAMESPACES;
|
|
7072
|
-
const documents = await Promise.all(namespaces.map((entry) =>
|
|
7570
|
+
const documents = await Promise.all(namespaces.map((entry) => store7.load(entry)));
|
|
7073
7571
|
return documents.flatMap((document) => document.overrides.map(
|
|
7074
7572
|
(override) => resolveOverrideState(override, document.records.find((record) => record.id === override.targetPolicyId), now2)
|
|
7075
7573
|
)).sort((left, right) => left.override.id.localeCompare(right.override.id));
|
|
7076
7574
|
}, "resolveOverrides");
|
|
7077
7575
|
var policy = {
|
|
7078
7576
|
async assign(input, namespace2 = "operator", basedOnRevision) {
|
|
7079
|
-
const
|
|
7080
|
-
const record = await
|
|
7577
|
+
const store7 = await getPolicyStore();
|
|
7578
|
+
const record = await store7.assign(input, namespace2, basedOnRevision);
|
|
7081
7579
|
auditPolicyAction("policy.assign", record.id);
|
|
7082
7580
|
return record;
|
|
7083
7581
|
},
|
|
7084
7582
|
async list() {
|
|
7085
|
-
const
|
|
7086
|
-
return
|
|
7583
|
+
const store7 = await getPolicyStore();
|
|
7584
|
+
return store7.list();
|
|
7087
7585
|
},
|
|
7088
7586
|
async remove(id, namespace2 = "operator", basedOnRevision) {
|
|
7089
|
-
const
|
|
7090
|
-
await
|
|
7587
|
+
const store7 = await getPolicyStore();
|
|
7588
|
+
await store7.remove(id, namespace2, basedOnRevision);
|
|
7091
7589
|
auditPolicyAction("policy.remove", id);
|
|
7092
7590
|
},
|
|
7093
7591
|
/**
|
|
@@ -7101,16 +7599,16 @@ var policy = {
|
|
|
7101
7599
|
* the durable audit trail with a terminal CLEAR entry.
|
|
7102
7600
|
*/
|
|
7103
7601
|
async clear(authority) {
|
|
7104
|
-
const
|
|
7105
|
-
await
|
|
7602
|
+
const store7 = await getPolicyStore();
|
|
7603
|
+
await store7.clear(authority);
|
|
7106
7604
|
auditPolicyAction("policy.clear", "policy");
|
|
7107
7605
|
},
|
|
7108
7606
|
async evaluate(event, payload) {
|
|
7109
|
-
const
|
|
7607
|
+
const store7 = await getPolicyStore();
|
|
7110
7608
|
const [state3, policies, overrides] = await Promise.all([
|
|
7111
7609
|
loadCombinedKnowledgeState(),
|
|
7112
|
-
|
|
7113
|
-
|
|
7610
|
+
store7.list(),
|
|
7611
|
+
store7.listOverrides()
|
|
7114
7612
|
]);
|
|
7115
7613
|
const claims = activeClaims(state3);
|
|
7116
7614
|
return evaluatePolicies({ event, claims, policies, overrides, payload });
|
|
@@ -7132,14 +7630,14 @@ var policy = {
|
|
|
7132
7630
|
*/
|
|
7133
7631
|
override: {
|
|
7134
7632
|
async grant(input, namespace2 = "operator", basedOnRevision) {
|
|
7135
|
-
const
|
|
7136
|
-
const override = await
|
|
7633
|
+
const store7 = await getPolicyStore();
|
|
7634
|
+
const override = await store7.grantOverride(input, namespace2, basedOnRevision);
|
|
7137
7635
|
auditPolicyAction("policy.override", override.id);
|
|
7138
7636
|
return override;
|
|
7139
7637
|
},
|
|
7140
7638
|
async revoke(input, namespace2 = "operator", basedOnRevision) {
|
|
7141
|
-
const
|
|
7142
|
-
const override = await
|
|
7639
|
+
const store7 = await getPolicyStore();
|
|
7640
|
+
const override = await store7.revokeOverride(input, namespace2, basedOnRevision);
|
|
7143
7641
|
auditPolicyAction("policy.revoke", override.id);
|
|
7144
7642
|
return override;
|
|
7145
7643
|
},
|
|
@@ -7147,8 +7645,8 @@ var policy = {
|
|
|
7147
7645
|
return resolveOverrides(namespace2);
|
|
7148
7646
|
},
|
|
7149
7647
|
async effective(event, namespace2) {
|
|
7150
|
-
const
|
|
7151
|
-
const states = await
|
|
7648
|
+
const store7 = await getPolicyStore();
|
|
7649
|
+
const states = await store7.effectiveState(namespace2);
|
|
7152
7650
|
if (event === void 0) return states;
|
|
7153
7651
|
const canonical = event.trim();
|
|
7154
7652
|
return states.filter((state3) => state3.event === canonical);
|
|
@@ -7161,8 +7659,8 @@ var policy = {
|
|
|
7161
7659
|
* additional best-effort sink, and this is the durable record.
|
|
7162
7660
|
*/
|
|
7163
7661
|
async audit(namespace2) {
|
|
7164
|
-
const
|
|
7165
|
-
return
|
|
7662
|
+
const store7 = await getPolicyStore();
|
|
7663
|
+
return store7.auditTrail(namespace2);
|
|
7166
7664
|
},
|
|
7167
7665
|
contract() {
|
|
7168
7666
|
return MEMORY_PROJECTION_CONTRACT;
|
|
@@ -8136,6 +8634,778 @@ var injectJoySeed = /* @__PURE__ */ __name(async (opts) => {
|
|
|
8136
8634
|
return { id: seedId, action: "stored" };
|
|
8137
8635
|
}, "injectJoySeed");
|
|
8138
8636
|
|
|
8637
|
+
// core/memory/capture-engine.ts
|
|
8638
|
+
init_exemplars();
|
|
8639
|
+
init_loops();
|
|
8640
|
+
init_personality_anchor();
|
|
8641
|
+
var CAPTURE_STATE_KEY = "__dotmem_capture_state__";
|
|
8642
|
+
var DEFAULT_CONFIG = {
|
|
8643
|
+
minSignificance: 0.3,
|
|
8644
|
+
maxItemsPerConversation: 50,
|
|
8645
|
+
dedupSimilarityThreshold: 0.75,
|
|
8646
|
+
preserveConflicts: true,
|
|
8647
|
+
tempContextTtlMs: 24 * 60 * 60 * 1e3,
|
|
8648
|
+
autoCreateExemplars: true,
|
|
8649
|
+
autoCreateLoops: true,
|
|
8650
|
+
autoCreateClaims: true
|
|
8651
|
+
};
|
|
8652
|
+
var CATEGORY_KEYWORDS = {
|
|
8653
|
+
fact: [
|
|
8654
|
+
"is",
|
|
8655
|
+
"are",
|
|
8656
|
+
"was",
|
|
8657
|
+
"were",
|
|
8658
|
+
"uses",
|
|
8659
|
+
"built with",
|
|
8660
|
+
"depends on",
|
|
8661
|
+
"located at",
|
|
8662
|
+
"named",
|
|
8663
|
+
"called",
|
|
8664
|
+
"version",
|
|
8665
|
+
"language",
|
|
8666
|
+
"framework",
|
|
8667
|
+
"test",
|
|
8668
|
+
"testing"
|
|
8669
|
+
],
|
|
8670
|
+
decision: [
|
|
8671
|
+
"decided",
|
|
8672
|
+
"decision",
|
|
8673
|
+
"agreed",
|
|
8674
|
+
"chose",
|
|
8675
|
+
"selected",
|
|
8676
|
+
"adopt",
|
|
8677
|
+
"going with",
|
|
8678
|
+
"will use",
|
|
8679
|
+
"commit to",
|
|
8680
|
+
"resolve",
|
|
8681
|
+
"settled"
|
|
8682
|
+
],
|
|
8683
|
+
experience: [
|
|
8684
|
+
"remember when",
|
|
8685
|
+
"we talked about",
|
|
8686
|
+
"discussed",
|
|
8687
|
+
"experienced",
|
|
8688
|
+
"encountered",
|
|
8689
|
+
"faced",
|
|
8690
|
+
"went through",
|
|
8691
|
+
"session",
|
|
8692
|
+
"conversation"
|
|
8693
|
+
],
|
|
8694
|
+
question: [
|
|
8695
|
+
"?",
|
|
8696
|
+
"how do",
|
|
8697
|
+
"what is",
|
|
8698
|
+
"why",
|
|
8699
|
+
"when",
|
|
8700
|
+
"where",
|
|
8701
|
+
"which",
|
|
8702
|
+
"can you",
|
|
8703
|
+
"should we",
|
|
8704
|
+
"is it possible"
|
|
8705
|
+
],
|
|
8706
|
+
open_loop: [
|
|
8707
|
+
"still need to",
|
|
8708
|
+
"pending",
|
|
8709
|
+
"waiting for",
|
|
8710
|
+
"todo",
|
|
8711
|
+
"to do",
|
|
8712
|
+
"open",
|
|
8713
|
+
"unresolved",
|
|
8714
|
+
"outstanding",
|
|
8715
|
+
"follow up",
|
|
8716
|
+
"blocked"
|
|
8717
|
+
],
|
|
8718
|
+
preference: [
|
|
8719
|
+
"prefer",
|
|
8720
|
+
"prefers",
|
|
8721
|
+
"like",
|
|
8722
|
+
"likes",
|
|
8723
|
+
"favorite",
|
|
8724
|
+
"always",
|
|
8725
|
+
"never",
|
|
8726
|
+
"don't like",
|
|
8727
|
+
"dislike",
|
|
8728
|
+
"style",
|
|
8729
|
+
"habit"
|
|
8730
|
+
],
|
|
8731
|
+
project_context: [
|
|
8732
|
+
"project",
|
|
8733
|
+
"repository",
|
|
8734
|
+
"codebase",
|
|
8735
|
+
"architecture",
|
|
8736
|
+
"stack",
|
|
8737
|
+
"deploy",
|
|
8738
|
+
"ci",
|
|
8739
|
+
"pipeline",
|
|
8740
|
+
"package"
|
|
8741
|
+
],
|
|
8742
|
+
temporary_context: [
|
|
8743
|
+
"for now",
|
|
8744
|
+
"just for",
|
|
8745
|
+
"temporary",
|
|
8746
|
+
"this session",
|
|
8747
|
+
"currently",
|
|
8748
|
+
"right now",
|
|
8749
|
+
"at the moment",
|
|
8750
|
+
"for this task"
|
|
8751
|
+
],
|
|
8752
|
+
inference: [
|
|
8753
|
+
"probably",
|
|
8754
|
+
"likely",
|
|
8755
|
+
"maybe",
|
|
8756
|
+
"perhaps",
|
|
8757
|
+
"seems",
|
|
8758
|
+
"appears",
|
|
8759
|
+
"suggests",
|
|
8760
|
+
"indicates",
|
|
8761
|
+
"implies",
|
|
8762
|
+
"might",
|
|
8763
|
+
"could be"
|
|
8764
|
+
]
|
|
8765
|
+
};
|
|
8766
|
+
var REMEMBER_PATTERNS = [
|
|
8767
|
+
/remember this/i,
|
|
8768
|
+
/remember that/i,
|
|
8769
|
+
/keep this in mind/i,
|
|
8770
|
+
/don't forget/i,
|
|
8771
|
+
/do not forget/i,
|
|
8772
|
+
/store this/i,
|
|
8773
|
+
/save this/i
|
|
8774
|
+
];
|
|
8775
|
+
var DO_NOT_REMEMBER_PATTERNS = [
|
|
8776
|
+
/don't remember/i,
|
|
8777
|
+
/do not remember/i,
|
|
8778
|
+
/forget this/i,
|
|
8779
|
+
/don't store/i,
|
|
8780
|
+
/do not store/i,
|
|
8781
|
+
/not relevant/i,
|
|
8782
|
+
/ignore this/i,
|
|
8783
|
+
/temporary only/i,
|
|
8784
|
+
/this session only/i
|
|
8785
|
+
];
|
|
8786
|
+
function generateId() {
|
|
8787
|
+
return `capture-${memorio.hlc.serialize(memorio.hlc.tick())}-${Math.random().toString(36).slice(2, 10)}`;
|
|
8788
|
+
}
|
|
8789
|
+
__name(generateId, "generateId");
|
|
8790
|
+
function nowIso() {
|
|
8791
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
8792
|
+
}
|
|
8793
|
+
__name(nowIso, "nowIso");
|
|
8794
|
+
function normalizeText(text) {
|
|
8795
|
+
return text.normalize("NFKC").toLocaleLowerCase("en-US").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
8796
|
+
}
|
|
8797
|
+
__name(normalizeText, "normalizeText");
|
|
8798
|
+
function tokenize(text) {
|
|
8799
|
+
return new Set(normalizeText(text).split(" ").filter((t) => t.length > 2));
|
|
8800
|
+
}
|
|
8801
|
+
__name(tokenize, "tokenize");
|
|
8802
|
+
function jaccardSimilarity(a, b) {
|
|
8803
|
+
const ta = tokenize(a);
|
|
8804
|
+
const tb = tokenize(b);
|
|
8805
|
+
if (ta.size === 0 && tb.size === 0) return 1;
|
|
8806
|
+
if (ta.size === 0 || tb.size === 0) return 0;
|
|
8807
|
+
const intersection = new Set([...ta].filter((x2) => tb.has(x2)));
|
|
8808
|
+
const union = /* @__PURE__ */ new Set([...ta, ...tb]);
|
|
8809
|
+
return intersection.size / union.size;
|
|
8810
|
+
}
|
|
8811
|
+
__name(jaccardSimilarity, "jaccardSimilarity");
|
|
8812
|
+
function containsPhrase2(text, phrase) {
|
|
8813
|
+
const t = normalizeText(text);
|
|
8814
|
+
const p = normalizeText(phrase);
|
|
8815
|
+
return t.includes(p);
|
|
8816
|
+
}
|
|
8817
|
+
__name(containsPhrase2, "containsPhrase");
|
|
8818
|
+
function detectExplicitInstructions(text, source) {
|
|
8819
|
+
const instructions = [];
|
|
8820
|
+
for (const pattern of REMEMBER_PATTERNS) {
|
|
8821
|
+
if (pattern.test(text)) {
|
|
8822
|
+
instructions.push({
|
|
8823
|
+
type: "remember",
|
|
8824
|
+
content: text,
|
|
8825
|
+
source,
|
|
8826
|
+
timestamp: nowIso()
|
|
8827
|
+
});
|
|
8828
|
+
break;
|
|
8829
|
+
}
|
|
8830
|
+
}
|
|
8831
|
+
for (const pattern of DO_NOT_REMEMBER_PATTERNS) {
|
|
8832
|
+
if (pattern.test(text)) {
|
|
8833
|
+
instructions.push({
|
|
8834
|
+
type: "do-not-remember",
|
|
8835
|
+
content: text,
|
|
8836
|
+
source,
|
|
8837
|
+
timestamp: nowIso()
|
|
8838
|
+
});
|
|
8839
|
+
break;
|
|
8840
|
+
}
|
|
8841
|
+
}
|
|
8842
|
+
return instructions;
|
|
8843
|
+
}
|
|
8844
|
+
__name(detectExplicitInstructions, "detectExplicitInstructions");
|
|
8845
|
+
function classifyCategory(text, explicitInstructions) {
|
|
8846
|
+
const lower = text.toLocaleLowerCase("en-US");
|
|
8847
|
+
if (explicitInstructions.some((i2) => i2.type === "do-not-remember")) {
|
|
8848
|
+
return "temporary_context";
|
|
8849
|
+
}
|
|
8850
|
+
if (explicitInstructions.some((i2) => i2.type === "remember")) {
|
|
8851
|
+
if (/prefer|prefers|like|likes|favorite|always|never/i.test(lower)) {
|
|
8852
|
+
return "preference";
|
|
8853
|
+
}
|
|
8854
|
+
if (/decided|decision|agreed|chose|adopt/i.test(lower)) {
|
|
8855
|
+
return "decision";
|
|
8856
|
+
}
|
|
8857
|
+
return "fact";
|
|
8858
|
+
}
|
|
8859
|
+
const scores = {};
|
|
8860
|
+
for (const cat of Object.keys(CATEGORY_KEYWORDS)) {
|
|
8861
|
+
scores[cat] = CATEGORY_KEYWORDS[cat].filter((kw) => containsPhrase2(lower, kw)).length;
|
|
8862
|
+
}
|
|
8863
|
+
if (/probably|likely|maybe|perhaps|seems|appears|might|could be/i.test(lower)) {
|
|
8864
|
+
scores.inference += 3;
|
|
8865
|
+
}
|
|
8866
|
+
if (text.includes("?")) {
|
|
8867
|
+
scores.question += 3;
|
|
8868
|
+
}
|
|
8869
|
+
if (/decided|decision|agreed|chose|adopt|will use|going with|resolve|settled/i.test(lower)) {
|
|
8870
|
+
scores.decision += 3;
|
|
8871
|
+
}
|
|
8872
|
+
if (/prefer|prefers|like|likes|favorite|always|never|don.t like|dislike/i.test(lower)) {
|
|
8873
|
+
scores.preference += 3;
|
|
8874
|
+
}
|
|
8875
|
+
if (/still need to|pending|waiting for|todo|to do|unresolved|outstanding|follow.?up|blocked/i.test(lower)) {
|
|
8876
|
+
scores.open_loop += 3;
|
|
8877
|
+
}
|
|
8878
|
+
if (/remember when|we talked about|discussed|experienced|encountered|session|conversation/i.test(lower)) {
|
|
8879
|
+
scores.experience += 2;
|
|
8880
|
+
}
|
|
8881
|
+
if (/repository|architecture|deploy|ci|pipeline/i.test(lower)) {
|
|
8882
|
+
scores.project_context += 2;
|
|
8883
|
+
}
|
|
8884
|
+
if (/just for|this session|for now|temporary/i.test(lower)) {
|
|
8885
|
+
scores.temporary_context += 3;
|
|
8886
|
+
}
|
|
8887
|
+
if (scores.fact > 0 && scores.project_context > 0 && scores.fact === scores.project_context) {
|
|
8888
|
+
scores.fact += 1;
|
|
8889
|
+
}
|
|
8890
|
+
let bestCategory = "fact";
|
|
8891
|
+
let bestScore = -1;
|
|
8892
|
+
for (const [cat, score] of Object.entries(scores)) {
|
|
8893
|
+
if (score > bestScore) {
|
|
8894
|
+
bestScore = score;
|
|
8895
|
+
bestCategory = cat;
|
|
8896
|
+
}
|
|
8897
|
+
}
|
|
8898
|
+
if (bestScore === 0) return "fact";
|
|
8899
|
+
return bestCategory;
|
|
8900
|
+
}
|
|
8901
|
+
__name(classifyCategory, "classifyCategory");
|
|
8902
|
+
function determineEpistemicType(category, text, explicitInstructions) {
|
|
8903
|
+
if (explicitInstructions.some((i2) => i2.type === "remember")) {
|
|
8904
|
+
return "human-stated";
|
|
8905
|
+
}
|
|
8906
|
+
if (category === "inference" || /probably|likely|maybe|perhaps|seems|appears/i.test(text)) {
|
|
8907
|
+
return "inferred";
|
|
8908
|
+
}
|
|
8909
|
+
if (category === "question") return "observed";
|
|
8910
|
+
return "observed";
|
|
8911
|
+
}
|
|
8912
|
+
__name(determineEpistemicType, "determineEpistemicType");
|
|
8913
|
+
function determineConfidence(category, epistemicType, _text) {
|
|
8914
|
+
if (epistemicType === "human-stated") return "high";
|
|
8915
|
+
if (epistemicType === "inferred") return "low";
|
|
8916
|
+
if (category === "decision" || category === "preference") return "medium";
|
|
8917
|
+
if (category === "question") return "medium";
|
|
8918
|
+
if (category === "fact") return "medium";
|
|
8919
|
+
return "low";
|
|
8920
|
+
}
|
|
8921
|
+
__name(determineConfidence, "determineConfidence");
|
|
8922
|
+
function extractEntities(text) {
|
|
8923
|
+
const entities = [];
|
|
8924
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8925
|
+
const capitalizedMatches = text.match(/\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\b/g) || [];
|
|
8926
|
+
for (const match of capitalizedMatches) {
|
|
8927
|
+
if (seen.has(match)) continue;
|
|
8928
|
+
seen.add(match);
|
|
8929
|
+
entities.push({
|
|
8930
|
+
id: `entity-${normalizeText(match)}`,
|
|
8931
|
+
type: "concept",
|
|
8932
|
+
name: match,
|
|
8933
|
+
aliases: [],
|
|
8934
|
+
relationships: []
|
|
8935
|
+
});
|
|
8936
|
+
}
|
|
8937
|
+
const quotedMatches = text.match(/["'`]([^"`']{2,})["'`]/g) || [];
|
|
8938
|
+
for (const match of quotedMatches) {
|
|
8939
|
+
const clean = match.replace(/["'`]/g, "");
|
|
8940
|
+
if (seen.has(clean)) continue;
|
|
8941
|
+
seen.add(clean);
|
|
8942
|
+
entities.push({
|
|
8943
|
+
id: `entity-${normalizeText(clean)}`,
|
|
8944
|
+
type: "concept",
|
|
8945
|
+
name: clean,
|
|
8946
|
+
aliases: [],
|
|
8947
|
+
relationships: []
|
|
8948
|
+
});
|
|
8949
|
+
}
|
|
8950
|
+
return entities;
|
|
8951
|
+
}
|
|
8952
|
+
__name(extractEntities, "extractEntities");
|
|
8953
|
+
function extractRelationships(_text, entities) {
|
|
8954
|
+
const relationships = [];
|
|
8955
|
+
if (entities.length < 2) return relationships;
|
|
8956
|
+
for (let i2 = 0; i2 < entities.length - 1; i2++) {
|
|
8957
|
+
for (let j = i2 + 1; j < entities.length; j++) {
|
|
8958
|
+
relationships.push({
|
|
8959
|
+
from: entities[i2].id,
|
|
8960
|
+
to: entities[j].id,
|
|
8961
|
+
type: "related-to",
|
|
8962
|
+
confidence: 0.5
|
|
8963
|
+
});
|
|
8964
|
+
}
|
|
8965
|
+
}
|
|
8966
|
+
return relationships;
|
|
8967
|
+
}
|
|
8968
|
+
__name(extractRelationships, "extractRelationships");
|
|
8969
|
+
async function findExistingSimilarItems(statement, category, existingItems, threshold) {
|
|
8970
|
+
const matches2 = [];
|
|
8971
|
+
for (const existing of existingItems) {
|
|
8972
|
+
if (existing.category !== category) continue;
|
|
8973
|
+
const similarity = jaccardSimilarity(statement, existing.statement);
|
|
8974
|
+
if (similarity >= threshold) {
|
|
8975
|
+
matches2.push({
|
|
8976
|
+
existingId: existing.id,
|
|
8977
|
+
existingStatement: existing.statement,
|
|
8978
|
+
similarity,
|
|
8979
|
+
matchType: similarity >= 0.95 ? "exact" : "semantic",
|
|
8980
|
+
category: existing.category
|
|
8981
|
+
});
|
|
8982
|
+
}
|
|
8983
|
+
}
|
|
8984
|
+
return matches2;
|
|
8985
|
+
}
|
|
8986
|
+
__name(findExistingSimilarItems, "findExistingSimilarItems");
|
|
8987
|
+
async function findExistingMemoryEntries(statement, category) {
|
|
8988
|
+
const matches2 = [];
|
|
8989
|
+
try {
|
|
8990
|
+
const all = await entries._loadAll();
|
|
8991
|
+
for (const entry of all) {
|
|
8992
|
+
if (entry.status !== "active") continue;
|
|
8993
|
+
if (entry.type !== category && entry.type !== "fact") continue;
|
|
8994
|
+
const entryText = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value);
|
|
8995
|
+
const similarity = jaccardSimilarity(statement, entryText);
|
|
8996
|
+
if (similarity >= DEFAULT_CONFIG.dedupSimilarityThreshold) {
|
|
8997
|
+
matches2.push({
|
|
8998
|
+
existingId: entry.id,
|
|
8999
|
+
existingStatement: entryText,
|
|
9000
|
+
similarity,
|
|
9001
|
+
matchType: similarity >= 0.95 ? "exact" : "semantic",
|
|
9002
|
+
category
|
|
9003
|
+
});
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
9006
|
+
} catch {
|
|
9007
|
+
}
|
|
9008
|
+
return matches2;
|
|
9009
|
+
}
|
|
9010
|
+
__name(findExistingMemoryEntries, "findExistingMemoryEntries");
|
|
9011
|
+
function shouldStoreInCoreMemory(category, epistemicType) {
|
|
9012
|
+
return category === "preference" && epistemicType === "human-stated";
|
|
9013
|
+
}
|
|
9014
|
+
__name(shouldStoreInCoreMemory, "shouldStoreInCoreMemory");
|
|
9015
|
+
async function routeToStorage(item, adapters, config) {
|
|
9016
|
+
const { category, epistemicType, statement, entities, provenance, confidence } = item;
|
|
9017
|
+
const memKey = `capture:${item.id}`;
|
|
9018
|
+
await entries.remember(memKey, {
|
|
9019
|
+
id: item.id,
|
|
9020
|
+
category,
|
|
9021
|
+
statement,
|
|
9022
|
+
entities,
|
|
9023
|
+
relationships: item.relationships,
|
|
9024
|
+
provenance,
|
|
9025
|
+
confidence,
|
|
9026
|
+
epistemicType,
|
|
9027
|
+
capturedAt: provenance.capturedAt
|
|
9028
|
+
}, {
|
|
9029
|
+
id: item.id,
|
|
9030
|
+
type: category === "preference" ? "preference" : category === "decision" ? "decision" : category === "fact" ? "fact" : "context",
|
|
9031
|
+
confidence: confidence === "high" ? 1 : confidence === "medium" ? 0.7 : 0.4,
|
|
9032
|
+
scope: category === "temporary_context" ? "session" : "local",
|
|
9033
|
+
tags: [category, provenance.epistemicType, provenance.source.conversationId],
|
|
9034
|
+
source: `capture:${provenance.source.conversationId}`
|
|
9035
|
+
});
|
|
9036
|
+
if (shouldStoreInCoreMemory(category, epistemicType)) {
|
|
9037
|
+
try {
|
|
9038
|
+
const anchor = await loadPersonalityAnchor();
|
|
9039
|
+
const existingPref = anchor.identity.durablePreferences.find((p) => p.statement === statement);
|
|
9040
|
+
if (!existingPref) {
|
|
9041
|
+
await updatePersonalityAnchor({
|
|
9042
|
+
durablePreferences: [
|
|
9043
|
+
...anchor.identity.durablePreferences,
|
|
9044
|
+
{
|
|
9045
|
+
id: item.id,
|
|
9046
|
+
statement,
|
|
9047
|
+
confidence: epistemicType,
|
|
9048
|
+
evidence: [provenance.source.conversationId],
|
|
9049
|
+
firstObserved: provenance.capturedAt,
|
|
9050
|
+
lastConfirmed: provenance.capturedAt
|
|
9051
|
+
}
|
|
9052
|
+
]
|
|
9053
|
+
});
|
|
9054
|
+
}
|
|
9055
|
+
} catch {
|
|
9056
|
+
}
|
|
9057
|
+
}
|
|
9058
|
+
if (category === "open_loop" && config.autoCreateLoops) {
|
|
9059
|
+
try {
|
|
9060
|
+
const existingLoops = await listLoops(true);
|
|
9061
|
+
const alreadyExists = existingLoops.some((l) => l.text === statement);
|
|
9062
|
+
if (!alreadyExists) {
|
|
9063
|
+
await addLoop({
|
|
9064
|
+
id: item.id,
|
|
9065
|
+
text: statement,
|
|
9066
|
+
waitor: "joy",
|
|
9067
|
+
openedAt: provenance.capturedAt
|
|
9068
|
+
});
|
|
9069
|
+
}
|
|
9070
|
+
} catch {
|
|
9071
|
+
}
|
|
9072
|
+
}
|
|
9073
|
+
if (category === "experience" && config.autoCreateExemplars) {
|
|
9074
|
+
try {
|
|
9075
|
+
await addExemplar({
|
|
9076
|
+
id: item.id,
|
|
9077
|
+
source: {
|
|
9078
|
+
conversationId: provenance.source.conversationId,
|
|
9079
|
+
messageIds: provenance.source.messageIds
|
|
9080
|
+
},
|
|
9081
|
+
pattern: statement,
|
|
9082
|
+
originalContent: statement,
|
|
9083
|
+
sourceReference: `capture:${item.id}`,
|
|
9084
|
+
retrievalHints: [statement, ...entities.map((e) => e.name)],
|
|
9085
|
+
significance: confidence === "high" ? 0.8 : confidence === "medium" ? 0.5 : 0.3,
|
|
9086
|
+
confidence: confidence === "high" ? 1 : confidence === "medium" ? 0.7 : 0.4,
|
|
9087
|
+
provenance: epistemicType,
|
|
9088
|
+
createdAt: provenance.capturedAt
|
|
9089
|
+
});
|
|
9090
|
+
} catch {
|
|
9091
|
+
}
|
|
9092
|
+
}
|
|
9093
|
+
if (config.autoCreateClaims && (category === "fact" || category === "decision")) {
|
|
9094
|
+
try {
|
|
9095
|
+
await acquire.acquire({
|
|
9096
|
+
id: `claim-${item.id}`,
|
|
9097
|
+
knowledge: { statement, category },
|
|
9098
|
+
applicability: { area: "project" },
|
|
9099
|
+
evidence: [{
|
|
9100
|
+
id: `ev-${item.id}`,
|
|
9101
|
+
source: provenance.source.conversationId,
|
|
9102
|
+
observedAt: provenance.capturedAt,
|
|
9103
|
+
supports: statement
|
|
9104
|
+
}],
|
|
9105
|
+
epistemicType,
|
|
9106
|
+
provenance: { source: "conversation-capture", detail: `category:${category}` },
|
|
9107
|
+
acquiredAt: provenance.capturedAt,
|
|
9108
|
+
scope: "project"
|
|
9109
|
+
});
|
|
9110
|
+
} catch {
|
|
9111
|
+
}
|
|
9112
|
+
}
|
|
9113
|
+
memorio.dispatch.set("dotmem:capture:item-captured", {
|
|
9114
|
+
detail: { id: item.id, category, statement }
|
|
9115
|
+
});
|
|
9116
|
+
}
|
|
9117
|
+
__name(routeToStorage, "routeToStorage");
|
|
9118
|
+
var CaptureEngine = class {
|
|
9119
|
+
static {
|
|
9120
|
+
__name(this, "CaptureEngine");
|
|
9121
|
+
}
|
|
9122
|
+
config;
|
|
9123
|
+
adapters;
|
|
9124
|
+
sessionContexts = /* @__PURE__ */ new Map();
|
|
9125
|
+
constructor(config = {}, adapters) {
|
|
9126
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
9127
|
+
this.adapters = {
|
|
9128
|
+
remember: entries.remember.bind(entries),
|
|
9129
|
+
recall: entries.recall.bind(entries),
|
|
9130
|
+
update: entries.update.bind(entries),
|
|
9131
|
+
addExemplar: addExemplar.bind(null),
|
|
9132
|
+
updateExemplar: updateExemplar.bind(null),
|
|
9133
|
+
addLoop: addLoop.bind(null),
|
|
9134
|
+
closeLoop: closeLoop.bind(null),
|
|
9135
|
+
updatePersonalityAnchor: updatePersonalityAnchor.bind(null),
|
|
9136
|
+
...adapters
|
|
9137
|
+
};
|
|
9138
|
+
}
|
|
9139
|
+
/**
|
|
9140
|
+
* Capture information from a conversation turn.
|
|
9141
|
+
* Processes a single message or turn of conversation.
|
|
9142
|
+
*/
|
|
9143
|
+
async captureTurn(text, options) {
|
|
9144
|
+
const source = {
|
|
9145
|
+
conversationId: options.conversationId,
|
|
9146
|
+
messageIds: options.messageIds ?? [],
|
|
9147
|
+
timestamp: nowIso(),
|
|
9148
|
+
turnIndex: 0
|
|
9149
|
+
};
|
|
9150
|
+
const instructions = detectExplicitInstructions(text, source);
|
|
9151
|
+
const hasDoNotRemember = instructions.some((i2) => i2.type === "do-not-remember");
|
|
9152
|
+
const hasExplicitRemember = instructions.some((i2) => i2.type === "remember");
|
|
9153
|
+
let created = 0, linked = 0, ignored = 0;
|
|
9154
|
+
if (hasDoNotRemember && !hasExplicitRemember) {
|
|
9155
|
+
const item2 = {
|
|
9156
|
+
id: generateId(),
|
|
9157
|
+
category: "temporary_context",
|
|
9158
|
+
statement: text,
|
|
9159
|
+
content: { text },
|
|
9160
|
+
entities: [],
|
|
9161
|
+
relationships: [],
|
|
9162
|
+
provenance: {
|
|
9163
|
+
source,
|
|
9164
|
+
classification: "temporary_context",
|
|
9165
|
+
confidence: "low",
|
|
9166
|
+
epistemicType: "observed",
|
|
9167
|
+
capturedAt: nowIso(),
|
|
9168
|
+
method: "automatic"
|
|
9169
|
+
},
|
|
9170
|
+
confidence: "low",
|
|
9171
|
+
epistemicType: "observed"
|
|
9172
|
+
};
|
|
9173
|
+
await this.storeSessionContext(options.conversationId, [item2], instructions);
|
|
9174
|
+
ignored++;
|
|
9175
|
+
return { items: [item2], instructions, created: 0, linked: 0, ignored: 1, dedupMatches: [] };
|
|
9176
|
+
}
|
|
9177
|
+
const category = classifyCategory(text, instructions);
|
|
9178
|
+
const epistemicType = determineEpistemicType(category, text, instructions);
|
|
9179
|
+
const confidence = determineConfidence(category, epistemicType);
|
|
9180
|
+
const entities = extractEntities(text);
|
|
9181
|
+
const relationships = extractRelationships(text, entities);
|
|
9182
|
+
const provenance = {
|
|
9183
|
+
source,
|
|
9184
|
+
classification: category,
|
|
9185
|
+
confidence,
|
|
9186
|
+
epistemicType,
|
|
9187
|
+
capturedAt: nowIso(),
|
|
9188
|
+
method: hasExplicitRemember ? "explicit" : "automatic"
|
|
9189
|
+
};
|
|
9190
|
+
const item = {
|
|
9191
|
+
id: generateId(),
|
|
9192
|
+
category,
|
|
9193
|
+
statement: text,
|
|
9194
|
+
content: { text },
|
|
9195
|
+
entities,
|
|
9196
|
+
relationships,
|
|
9197
|
+
provenance,
|
|
9198
|
+
confidence,
|
|
9199
|
+
epistemicType
|
|
9200
|
+
};
|
|
9201
|
+
const sessionCtx = this.sessionContexts.get(options.conversationId);
|
|
9202
|
+
const existingItems = sessionCtx ? sessionCtx.items : [];
|
|
9203
|
+
const dedupMatches = await findExistingSimilarItems(
|
|
9204
|
+
text,
|
|
9205
|
+
category,
|
|
9206
|
+
existingItems,
|
|
9207
|
+
this.config.dedupSimilarityThreshold
|
|
9208
|
+
);
|
|
9209
|
+
const memoryMatches = await findExistingMemoryEntries(text, category);
|
|
9210
|
+
const allMatches = [...dedupMatches, ...memoryMatches];
|
|
9211
|
+
let decision = "create";
|
|
9212
|
+
if (allMatches.length > 0) {
|
|
9213
|
+
const exactMatch = allMatches.find((m) => m.matchType === "exact");
|
|
9214
|
+
if (exactMatch) {
|
|
9215
|
+
decision = "link";
|
|
9216
|
+
linked++;
|
|
9217
|
+
} else {
|
|
9218
|
+
decision = "create";
|
|
9219
|
+
created++;
|
|
9220
|
+
}
|
|
9221
|
+
} else {
|
|
9222
|
+
created++;
|
|
9223
|
+
}
|
|
9224
|
+
const finalItem = { ...item };
|
|
9225
|
+
const shouldStore = decision === "create" || decision === "link";
|
|
9226
|
+
if (shouldStore) {
|
|
9227
|
+
await routeToStorage(finalItem, this.adapters, this.config);
|
|
9228
|
+
}
|
|
9229
|
+
await this.storeSessionContext(options.conversationId, [finalItem], instructions);
|
|
9230
|
+
return { items: [finalItem], instructions, created, linked, ignored, dedupMatches: allMatches };
|
|
9231
|
+
}
|
|
9232
|
+
/**
|
|
9233
|
+
* Capture information from a full conversation (array of messages).
|
|
9234
|
+
* Processes each message and returns aggregated results.
|
|
9235
|
+
*/
|
|
9236
|
+
async captureConversation(messages, options) {
|
|
9237
|
+
const allItems = [];
|
|
9238
|
+
const allInstructions = [];
|
|
9239
|
+
let created = 0, updated = 0, linked = 0, ignored = 0;
|
|
9240
|
+
const allDedupMatches = [];
|
|
9241
|
+
for (const [index, msg] of messages.entries()) {
|
|
9242
|
+
const source = {
|
|
9243
|
+
conversationId: options.conversationId,
|
|
9244
|
+
messageIds: [msg.id ?? `msg-${index}`],
|
|
9245
|
+
timestamp: msg.timestamp ?? nowIso(),
|
|
9246
|
+
turnIndex: index
|
|
9247
|
+
};
|
|
9248
|
+
const instructions = detectExplicitInstructions(msg.content, source);
|
|
9249
|
+
allInstructions.push(...instructions);
|
|
9250
|
+
const hasDoNotRemember = instructions.some((i2) => i2.type === "do-not-remember");
|
|
9251
|
+
const hasExplicitRemember = instructions.some((i2) => i2.type === "remember");
|
|
9252
|
+
if (hasDoNotRemember && !hasExplicitRemember) {
|
|
9253
|
+
const item2 = {
|
|
9254
|
+
id: generateId(),
|
|
9255
|
+
category: "temporary_context",
|
|
9256
|
+
statement: msg.content,
|
|
9257
|
+
content: { text: msg.content, role: msg.role },
|
|
9258
|
+
entities: [],
|
|
9259
|
+
relationships: [],
|
|
9260
|
+
provenance: {
|
|
9261
|
+
source,
|
|
9262
|
+
classification: "temporary_context",
|
|
9263
|
+
confidence: "low",
|
|
9264
|
+
epistemicType: "observed",
|
|
9265
|
+
capturedAt: nowIso(),
|
|
9266
|
+
method: "automatic"
|
|
9267
|
+
},
|
|
9268
|
+
confidence: "low",
|
|
9269
|
+
epistemicType: "observed"
|
|
9270
|
+
};
|
|
9271
|
+
allItems.push(item2);
|
|
9272
|
+
ignored++;
|
|
9273
|
+
continue;
|
|
9274
|
+
}
|
|
9275
|
+
const category = classifyCategory(msg.content, instructions);
|
|
9276
|
+
const epistemicType = determineEpistemicType(category, msg.content, instructions);
|
|
9277
|
+
const confidence = determineConfidence(category, epistemicType, msg.content);
|
|
9278
|
+
const entities = extractEntities(msg.content);
|
|
9279
|
+
const relationships = extractRelationships(msg.content, entities);
|
|
9280
|
+
const provenance = {
|
|
9281
|
+
source,
|
|
9282
|
+
classification: category,
|
|
9283
|
+
confidence,
|
|
9284
|
+
epistemicType,
|
|
9285
|
+
capturedAt: nowIso(),
|
|
9286
|
+
method: hasExplicitRemember ? "explicit" : "automatic"
|
|
9287
|
+
};
|
|
9288
|
+
const item = {
|
|
9289
|
+
id: generateId(),
|
|
9290
|
+
category,
|
|
9291
|
+
statement: msg.content,
|
|
9292
|
+
content: { text: msg.content, role: msg.role },
|
|
9293
|
+
entities,
|
|
9294
|
+
relationships,
|
|
9295
|
+
provenance,
|
|
9296
|
+
confidence,
|
|
9297
|
+
epistemicType
|
|
9298
|
+
};
|
|
9299
|
+
const dedupMatches = await findExistingSimilarItems(
|
|
9300
|
+
msg.content,
|
|
9301
|
+
category,
|
|
9302
|
+
allItems,
|
|
9303
|
+
this.config.dedupSimilarityThreshold
|
|
9304
|
+
);
|
|
9305
|
+
const memoryMatches = await findExistingMemoryEntries(msg.content, category);
|
|
9306
|
+
const allMatches = [...dedupMatches, ...memoryMatches];
|
|
9307
|
+
allDedupMatches.push(...allMatches);
|
|
9308
|
+
let decision = "create";
|
|
9309
|
+
if (allMatches.length > 0) {
|
|
9310
|
+
const exactMatch = allMatches.find((m) => m.matchType === "exact");
|
|
9311
|
+
if (exactMatch) {
|
|
9312
|
+
decision = "link";
|
|
9313
|
+
linked++;
|
|
9314
|
+
} else {
|
|
9315
|
+
decision = "create";
|
|
9316
|
+
created++;
|
|
9317
|
+
}
|
|
9318
|
+
} else {
|
|
9319
|
+
created++;
|
|
9320
|
+
}
|
|
9321
|
+
const shouldStore = decision === "create" || decision === "link";
|
|
9322
|
+
if (shouldStore) {
|
|
9323
|
+
await routeToStorage(item, this.adapters, this.config);
|
|
9324
|
+
}
|
|
9325
|
+
if (!shouldStore) ignored++;
|
|
9326
|
+
allItems.push(item);
|
|
9327
|
+
}
|
|
9328
|
+
await this.storeSessionContext(options.conversationId, allItems, allInstructions);
|
|
9329
|
+
return {
|
|
9330
|
+
items: allItems,
|
|
9331
|
+
created,
|
|
9332
|
+
updated,
|
|
9333
|
+
linked,
|
|
9334
|
+
ignored,
|
|
9335
|
+
dedupMatches: allDedupMatches,
|
|
9336
|
+
provenance: allItems.map((i2) => i2.provenance)
|
|
9337
|
+
};
|
|
9338
|
+
}
|
|
9339
|
+
/**
|
|
9340
|
+
* Retrieve captured items for a conversation.
|
|
9341
|
+
*/
|
|
9342
|
+
async getCapturedItems(conversationId) {
|
|
9343
|
+
const ctx = this.sessionContexts.get(conversationId);
|
|
9344
|
+
return ctx ? ctx.items : [];
|
|
9345
|
+
}
|
|
9346
|
+
/**
|
|
9347
|
+
* Get all captured items across all conversations.
|
|
9348
|
+
*/
|
|
9349
|
+
async getAllCapturedItems() {
|
|
9350
|
+
const all = [];
|
|
9351
|
+
for (const ctx of this.sessionContexts.values()) {
|
|
9352
|
+
all.push(...ctx.items);
|
|
9353
|
+
}
|
|
9354
|
+
return all;
|
|
9355
|
+
}
|
|
9356
|
+
/**
|
|
9357
|
+
* Clear session context (for testing / cleanup).
|
|
9358
|
+
*/
|
|
9359
|
+
clearSession(conversationId) {
|
|
9360
|
+
this.sessionContexts.delete(conversationId);
|
|
9361
|
+
}
|
|
9362
|
+
/**
|
|
9363
|
+
* Get capture engine configuration.
|
|
9364
|
+
*/
|
|
9365
|
+
getConfig() {
|
|
9366
|
+
return { ...this.config };
|
|
9367
|
+
}
|
|
9368
|
+
// ---------------------------------------------------------------------------
|
|
9369
|
+
// Private
|
|
9370
|
+
// ---------------------------------------------------------------------------
|
|
9371
|
+
async storeSessionContext(conversationId, items, instructions) {
|
|
9372
|
+
let ctx = this.sessionContexts.get(conversationId);
|
|
9373
|
+
if (!ctx) {
|
|
9374
|
+
ctx = {
|
|
9375
|
+
conversationId,
|
|
9376
|
+
startedAt: nowIso(),
|
|
9377
|
+
items: [],
|
|
9378
|
+
explicitInstructions: []
|
|
9379
|
+
};
|
|
9380
|
+
this.sessionContexts.set(conversationId, ctx);
|
|
9381
|
+
}
|
|
9382
|
+
ctx.items.push(...items);
|
|
9383
|
+
ctx.explicitInstructions.push(...instructions);
|
|
9384
|
+
try {
|
|
9385
|
+
const stateKey = `${CAPTURE_STATE_KEY}:${conversationId}`;
|
|
9386
|
+
const existing = store.get(stateKey);
|
|
9387
|
+
let prior = [];
|
|
9388
|
+
if (existing) {
|
|
9389
|
+
try {
|
|
9390
|
+
prior = JSON.parse(existing);
|
|
9391
|
+
} catch {
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
store.set(stateKey, JSON.stringify([...prior, ...items]));
|
|
9395
|
+
} catch {
|
|
9396
|
+
}
|
|
9397
|
+
}
|
|
9398
|
+
};
|
|
9399
|
+
var captureEngine = new CaptureEngine();
|
|
9400
|
+
async function captureConversation(messages, options) {
|
|
9401
|
+
return captureEngine.captureConversation(messages, options);
|
|
9402
|
+
}
|
|
9403
|
+
__name(captureConversation, "captureConversation");
|
|
9404
|
+
async function captureTurn(text, options) {
|
|
9405
|
+
return captureEngine.captureTurn(text, options);
|
|
9406
|
+
}
|
|
9407
|
+
__name(captureTurn, "captureTurn");
|
|
9408
|
+
|
|
8139
9409
|
// core/memory/index.ts
|
|
8140
9410
|
init_detection_events();
|
|
8141
9411
|
init_detection_journal();
|
|
@@ -8323,6 +9593,25 @@ var memory = {
|
|
|
8323
9593
|
async getSeedId() {
|
|
8324
9594
|
return SEED_ID;
|
|
8325
9595
|
}
|
|
9596
|
+
},
|
|
9597
|
+
/** Automatic conversational capture engine (Phase 2 — Workstream B). */
|
|
9598
|
+
capture: {
|
|
9599
|
+
engine: captureEngine,
|
|
9600
|
+
async conversation(messages, options) {
|
|
9601
|
+
return captureConversation(messages, options);
|
|
9602
|
+
},
|
|
9603
|
+
async turn(text, options) {
|
|
9604
|
+
return captureTurn(text, options);
|
|
9605
|
+
},
|
|
9606
|
+
async getItems(conversationId) {
|
|
9607
|
+
return captureEngine.getCapturedItems(conversationId);
|
|
9608
|
+
},
|
|
9609
|
+
async getAllItems() {
|
|
9610
|
+
return captureEngine.getAllCapturedItems();
|
|
9611
|
+
},
|
|
9612
|
+
clearSession(conversationId) {
|
|
9613
|
+
captureEngine.clearSession(conversationId);
|
|
9614
|
+
}
|
|
8326
9615
|
}
|
|
8327
9616
|
};
|
|
8328
9617
|
|