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/cli.cjs
CHANGED
|
@@ -6271,8 +6271,8 @@ var init_user_identity = __esm({
|
|
|
6271
6271
|
"core/knowledge/user-identity.ts"() {
|
|
6272
6272
|
USER_IDENTITY_KEY = "__dotmem_user_identity__";
|
|
6273
6273
|
identityStore = null;
|
|
6274
|
-
configureUserIdentityStore = /* @__PURE__ */ __name((
|
|
6275
|
-
identityStore =
|
|
6274
|
+
configureUserIdentityStore = /* @__PURE__ */ __name((store6) => {
|
|
6275
|
+
identityStore = store6;
|
|
6276
6276
|
}, "configureUserIdentityStore");
|
|
6277
6277
|
requireStore = /* @__PURE__ */ __name(() => {
|
|
6278
6278
|
if (!identityStore) {
|
|
@@ -8697,6 +8697,19 @@ async function readFile6(path3) {
|
|
|
8697
8697
|
return null;
|
|
8698
8698
|
}
|
|
8699
8699
|
}
|
|
8700
|
+
async function writeFile6(path3, content) {
|
|
8701
|
+
const fs3 = await import('fs/promises');
|
|
8702
|
+
const dir = path3.replace(/[\\/][^\\/]+$/, "");
|
|
8703
|
+
await fs3.mkdir(dir, { recursive: true });
|
|
8704
|
+
const temporary = `${path3}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
8705
|
+
try {
|
|
8706
|
+
await fs3.writeFile(temporary, content, "utf8");
|
|
8707
|
+
await fs3.rename(temporary, path3);
|
|
8708
|
+
} catch {
|
|
8709
|
+
await fs3.rm(temporary, { force: true }).catch(() => void 0);
|
|
8710
|
+
throw new Error(`Failed to write exemplars file at ${path3}`);
|
|
8711
|
+
}
|
|
8712
|
+
}
|
|
8700
8713
|
function emptyStore() {
|
|
8701
8714
|
return { version: EXEMPLARS_FORMAT_VERSION, exemplars: {} };
|
|
8702
8715
|
}
|
|
@@ -8761,9 +8774,53 @@ async function loadExemplars() {
|
|
|
8761
8774
|
return emptyStore();
|
|
8762
8775
|
}
|
|
8763
8776
|
}
|
|
8777
|
+
async function saveExemplars(store6) {
|
|
8778
|
+
const validated = validateStore(store6);
|
|
8779
|
+
await writeFile6(resolveExemplarsPath(), JSON.stringify(validated, null, 2));
|
|
8780
|
+
}
|
|
8781
|
+
async function addExemplar(exemplar) {
|
|
8782
|
+
const store6 = await loadExemplars();
|
|
8783
|
+
if (store6.exemplars[exemplar.id]) {
|
|
8784
|
+
throw new Error(`Exemplar ${exemplar.id} already exists`);
|
|
8785
|
+
}
|
|
8786
|
+
if (exemplar.sourceReference) {
|
|
8787
|
+
const existingRef = Object.values(store6.exemplars).find(
|
|
8788
|
+
(e) => e.sourceReference === exemplar.sourceReference
|
|
8789
|
+
);
|
|
8790
|
+
if (existingRef) {
|
|
8791
|
+
throw new Error(`Exemplar with source reference ${exemplar.sourceReference} already exists (id: ${existingRef.id})`);
|
|
8792
|
+
}
|
|
8793
|
+
}
|
|
8794
|
+
const full = {
|
|
8795
|
+
...exemplar,
|
|
8796
|
+
type: "interaction-exemplar",
|
|
8797
|
+
revision: 1
|
|
8798
|
+
};
|
|
8799
|
+
store6.exemplars[exemplar.id] = full;
|
|
8800
|
+
await saveExemplars(store6);
|
|
8801
|
+
return full;
|
|
8802
|
+
}
|
|
8803
|
+
async function updateExemplar(id, updates) {
|
|
8804
|
+
const store6 = await loadExemplars();
|
|
8805
|
+
const existing = store6.exemplars[id];
|
|
8806
|
+
if (!existing) {
|
|
8807
|
+
throw new Error(`Exemplar ${id} not found`);
|
|
8808
|
+
}
|
|
8809
|
+
const updated = {
|
|
8810
|
+
...existing,
|
|
8811
|
+
...updates,
|
|
8812
|
+
id: existing.id,
|
|
8813
|
+
type: "interaction-exemplar",
|
|
8814
|
+
createdAt: existing.createdAt,
|
|
8815
|
+
revision: (updates.revision ?? existing.revision) + 1
|
|
8816
|
+
};
|
|
8817
|
+
store6.exemplars[id] = updated;
|
|
8818
|
+
await saveExemplars(store6);
|
|
8819
|
+
return updated;
|
|
8820
|
+
}
|
|
8764
8821
|
async function searchExemplars(options = {}) {
|
|
8765
|
-
const
|
|
8766
|
-
const all = Object.values(
|
|
8822
|
+
const store6 = await loadExemplars();
|
|
8823
|
+
const all = Object.values(store6.exemplars);
|
|
8767
8824
|
const filtered = all.filter((exemplar) => {
|
|
8768
8825
|
if (options.minSignificance !== void 0 && exemplar.significance < options.minSignificance) {
|
|
8769
8826
|
return false;
|
|
@@ -8823,10 +8880,14 @@ var init_exemplars = __esm({
|
|
|
8823
8880
|
__name(joinPath9, "joinPath");
|
|
8824
8881
|
__name(resolveExemplarsPath, "resolveExemplarsPath");
|
|
8825
8882
|
__name(readFile6, "readFile");
|
|
8883
|
+
__name(writeFile6, "writeFile");
|
|
8826
8884
|
__name(emptyStore, "emptyStore");
|
|
8827
8885
|
__name(validateExemplar, "validateExemplar");
|
|
8828
8886
|
__name(validateStore, "validateStore");
|
|
8829
8887
|
__name(loadExemplars, "loadExemplars");
|
|
8888
|
+
__name(saveExemplars, "saveExemplars");
|
|
8889
|
+
__name(addExemplar, "addExemplar");
|
|
8890
|
+
__name(updateExemplar, "updateExemplar");
|
|
8830
8891
|
__name(searchExemplars, "searchExemplars");
|
|
8831
8892
|
__name(projectExemplar, "projectExemplar");
|
|
8832
8893
|
__name(projectExemplarContext, "projectExemplarContext");
|
|
@@ -8849,6 +8910,19 @@ async function readFile7(path3) {
|
|
|
8849
8910
|
return null;
|
|
8850
8911
|
}
|
|
8851
8912
|
}
|
|
8913
|
+
async function writeFile7(path3, content) {
|
|
8914
|
+
const fs3 = await import('fs/promises');
|
|
8915
|
+
const dir = path3.replace(/[\\/][^\\/]+$/, "");
|
|
8916
|
+
await fs3.mkdir(dir, { recursive: true });
|
|
8917
|
+
const temporary = `${path3}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
8918
|
+
try {
|
|
8919
|
+
await fs3.writeFile(temporary, content, "utf8");
|
|
8920
|
+
await fs3.rename(temporary, path3);
|
|
8921
|
+
} catch {
|
|
8922
|
+
await fs3.rm(temporary, { force: true }).catch(() => void 0);
|
|
8923
|
+
throw new Error(`Failed to write personality anchor at ${path3}`);
|
|
8924
|
+
}
|
|
8925
|
+
}
|
|
8852
8926
|
function emptyAnchor() {
|
|
8853
8927
|
const epoch = "1970-01-01T00:00:00.000Z";
|
|
8854
8928
|
return {
|
|
@@ -8981,6 +9055,33 @@ async function loadPersonalityAnchor() {
|
|
|
8981
9055
|
return emptyAnchor();
|
|
8982
9056
|
}
|
|
8983
9057
|
}
|
|
9058
|
+
async function savePersonalityAnchor(anchor) {
|
|
9059
|
+
const validated = validateAnchor(anchor);
|
|
9060
|
+
const dirPath = resolveUserDotMemPath();
|
|
9061
|
+
const fs3 = await import('fs/promises');
|
|
9062
|
+
await fs3.mkdir(dirPath, { recursive: true });
|
|
9063
|
+
await writeFile7(resolvePersonalityAnchorPath(), JSON.stringify(validated, null, 2));
|
|
9064
|
+
}
|
|
9065
|
+
async function updatePersonalityAnchor(updates) {
|
|
9066
|
+
const existing = await loadPersonalityAnchor();
|
|
9067
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
9068
|
+
const updated = {
|
|
9069
|
+
...existing,
|
|
9070
|
+
identity: {
|
|
9071
|
+
communicationStyle: updates.communicationStyle ?? existing.identity.communicationStyle,
|
|
9072
|
+
durablePreferences: updates.durablePreferences ?? existing.identity.durablePreferences,
|
|
9073
|
+
interactionBoundaries: updates.interactionBoundaries ?? existing.identity.interactionBoundaries
|
|
9074
|
+
},
|
|
9075
|
+
metadata: {
|
|
9076
|
+
...existing.metadata,
|
|
9077
|
+
updatedAt: now2,
|
|
9078
|
+
revision: existing.metadata.revision + 1,
|
|
9079
|
+
sourceReferences: updates.sourceReferences ? [.../* @__PURE__ */ new Set([...existing.metadata.sourceReferences, ...updates.sourceReferences])] : existing.metadata.sourceReferences
|
|
9080
|
+
}
|
|
9081
|
+
};
|
|
9082
|
+
await savePersonalityAnchor(updated);
|
|
9083
|
+
return updated;
|
|
9084
|
+
}
|
|
8984
9085
|
function projectPersonalityAnchor(anchor, options) {
|
|
8985
9086
|
const maxChars = options?.maxCharacters ?? 4e3;
|
|
8986
9087
|
const hasContent = anchor.identity.communicationStyle.length > 0 || anchor.identity.durablePreferences.length > 0 || anchor.identity.interactionBoundaries.length > 0;
|
|
@@ -9035,12 +9136,15 @@ var init_personality_anchor = __esm({
|
|
|
9035
9136
|
__name(joinPath10, "joinPath");
|
|
9036
9137
|
__name(resolvePersonalityAnchorPath, "resolvePersonalityAnchorPath");
|
|
9037
9138
|
__name(readFile7, "readFile");
|
|
9139
|
+
__name(writeFile7, "writeFile");
|
|
9038
9140
|
__name(emptyAnchor, "emptyAnchor");
|
|
9039
9141
|
__name(validateCommunicationStyle, "validateCommunicationStyle");
|
|
9040
9142
|
__name(validateDurablePreference, "validateDurablePreference");
|
|
9041
9143
|
__name(validateInteractionBoundary, "validateInteractionBoundary");
|
|
9042
9144
|
__name(validateAnchor, "validateAnchor");
|
|
9043
9145
|
__name(loadPersonalityAnchor, "loadPersonalityAnchor");
|
|
9146
|
+
__name(savePersonalityAnchor, "savePersonalityAnchor");
|
|
9147
|
+
__name(updatePersonalityAnchor, "updatePersonalityAnchor");
|
|
9044
9148
|
__name(projectPersonalityAnchor, "projectPersonalityAnchor");
|
|
9045
9149
|
__name(calculateAnchorBudget, "calculateAnchorBudget");
|
|
9046
9150
|
}
|
|
@@ -9732,27 +9836,27 @@ var init_policy2 = __esm({
|
|
|
9732
9836
|
}
|
|
9733
9837
|
}, "auditPolicyAction");
|
|
9734
9838
|
resolveOverrides = /* @__PURE__ */ __name(async (namespace2, now2 = () => /* @__PURE__ */ new Date()) => {
|
|
9735
|
-
const
|
|
9839
|
+
const store6 = await getPolicyStore();
|
|
9736
9840
|
const namespaces = namespace2 ? [namespace2] : exports.POLICY_NAMESPACES;
|
|
9737
|
-
const documents = await Promise.all(namespaces.map((entry) =>
|
|
9841
|
+
const documents = await Promise.all(namespaces.map((entry) => store6.load(entry)));
|
|
9738
9842
|
return documents.flatMap((document) => document.overrides.map(
|
|
9739
9843
|
(override) => exports.resolveOverrideState(override, document.records.find((record) => record.id === override.targetPolicyId), now2)
|
|
9740
9844
|
)).sort((left, right) => left.override.id.localeCompare(right.override.id));
|
|
9741
9845
|
}, "resolveOverrides");
|
|
9742
9846
|
policy = {
|
|
9743
9847
|
async assign(input, namespace2 = "operator", basedOnRevision) {
|
|
9744
|
-
const
|
|
9745
|
-
const record = await
|
|
9848
|
+
const store6 = await getPolicyStore();
|
|
9849
|
+
const record = await store6.assign(input, namespace2, basedOnRevision);
|
|
9746
9850
|
auditPolicyAction("policy.assign", record.id);
|
|
9747
9851
|
return record;
|
|
9748
9852
|
},
|
|
9749
9853
|
async list() {
|
|
9750
|
-
const
|
|
9751
|
-
return
|
|
9854
|
+
const store6 = await getPolicyStore();
|
|
9855
|
+
return store6.list();
|
|
9752
9856
|
},
|
|
9753
9857
|
async remove(id, namespace2 = "operator", basedOnRevision) {
|
|
9754
|
-
const
|
|
9755
|
-
await
|
|
9858
|
+
const store6 = await getPolicyStore();
|
|
9859
|
+
await store6.remove(id, namespace2, basedOnRevision);
|
|
9756
9860
|
auditPolicyAction("policy.remove", id);
|
|
9757
9861
|
},
|
|
9758
9862
|
/**
|
|
@@ -9766,16 +9870,16 @@ var init_policy2 = __esm({
|
|
|
9766
9870
|
* the durable audit trail with a terminal CLEAR entry.
|
|
9767
9871
|
*/
|
|
9768
9872
|
async clear(authority) {
|
|
9769
|
-
const
|
|
9770
|
-
await
|
|
9873
|
+
const store6 = await getPolicyStore();
|
|
9874
|
+
await store6.clear(authority);
|
|
9771
9875
|
auditPolicyAction("policy.clear", "policy");
|
|
9772
9876
|
},
|
|
9773
9877
|
async evaluate(event, payload) {
|
|
9774
|
-
const
|
|
9878
|
+
const store6 = await getPolicyStore();
|
|
9775
9879
|
const [state2, policies, overrides] = await Promise.all([
|
|
9776
9880
|
loadCombinedKnowledgeState(),
|
|
9777
|
-
|
|
9778
|
-
|
|
9881
|
+
store6.list(),
|
|
9882
|
+
store6.listOverrides()
|
|
9779
9883
|
]);
|
|
9780
9884
|
const claims = activeClaims(state2);
|
|
9781
9885
|
return exports.evaluatePolicies({ event, claims, policies, overrides, payload });
|
|
@@ -9797,14 +9901,14 @@ var init_policy2 = __esm({
|
|
|
9797
9901
|
*/
|
|
9798
9902
|
override: {
|
|
9799
9903
|
async grant(input, namespace2 = "operator", basedOnRevision) {
|
|
9800
|
-
const
|
|
9801
|
-
const override = await
|
|
9904
|
+
const store6 = await getPolicyStore();
|
|
9905
|
+
const override = await store6.grantOverride(input, namespace2, basedOnRevision);
|
|
9802
9906
|
auditPolicyAction("policy.override", override.id);
|
|
9803
9907
|
return override;
|
|
9804
9908
|
},
|
|
9805
9909
|
async revoke(input, namespace2 = "operator", basedOnRevision) {
|
|
9806
|
-
const
|
|
9807
|
-
const override = await
|
|
9910
|
+
const store6 = await getPolicyStore();
|
|
9911
|
+
const override = await store6.revokeOverride(input, namespace2, basedOnRevision);
|
|
9808
9912
|
auditPolicyAction("policy.revoke", override.id);
|
|
9809
9913
|
return override;
|
|
9810
9914
|
},
|
|
@@ -9812,8 +9916,8 @@ var init_policy2 = __esm({
|
|
|
9812
9916
|
return resolveOverrides(namespace2);
|
|
9813
9917
|
},
|
|
9814
9918
|
async effective(event, namespace2) {
|
|
9815
|
-
const
|
|
9816
|
-
const states = await
|
|
9919
|
+
const store6 = await getPolicyStore();
|
|
9920
|
+
const states = await store6.effectiveState(namespace2);
|
|
9817
9921
|
if (event === void 0) return states;
|
|
9818
9922
|
const canonical = event.trim();
|
|
9819
9923
|
return states.filter((state2) => state2.event === canonical);
|
|
@@ -9826,8 +9930,8 @@ var init_policy2 = __esm({
|
|
|
9826
9930
|
* additional best-effort sink, and this is the durable record.
|
|
9827
9931
|
*/
|
|
9828
9932
|
async audit(namespace2) {
|
|
9829
|
-
const
|
|
9830
|
-
return
|
|
9933
|
+
const store6 = await getPolicyStore();
|
|
9934
|
+
return store6.auditTrail(namespace2);
|
|
9831
9935
|
},
|
|
9832
9936
|
contract() {
|
|
9833
9937
|
return exports.MEMORY_PROJECTION_CONTRACT;
|
|
@@ -11241,6 +11345,783 @@ var init_seed = __esm({
|
|
|
11241
11345
|
}, "injectJoySeed");
|
|
11242
11346
|
}
|
|
11243
11347
|
});
|
|
11348
|
+
function generateId() {
|
|
11349
|
+
return `capture-${memorio.hlc.serialize(memorio.hlc.tick())}-${Math.random().toString(36).slice(2, 10)}`;
|
|
11350
|
+
}
|
|
11351
|
+
function nowIso() {
|
|
11352
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
11353
|
+
}
|
|
11354
|
+
function normalizeText(text) {
|
|
11355
|
+
return text.normalize("NFKC").toLocaleLowerCase("en-US").replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim();
|
|
11356
|
+
}
|
|
11357
|
+
function tokenize(text) {
|
|
11358
|
+
return new Set(normalizeText(text).split(" ").filter((t) => t.length > 2));
|
|
11359
|
+
}
|
|
11360
|
+
function jaccardSimilarity(a, b) {
|
|
11361
|
+
const ta = tokenize(a);
|
|
11362
|
+
const tb = tokenize(b);
|
|
11363
|
+
if (ta.size === 0 && tb.size === 0) return 1;
|
|
11364
|
+
if (ta.size === 0 || tb.size === 0) return 0;
|
|
11365
|
+
const intersection = new Set([...ta].filter((x2) => tb.has(x2)));
|
|
11366
|
+
const union = /* @__PURE__ */ new Set([...ta, ...tb]);
|
|
11367
|
+
return intersection.size / union.size;
|
|
11368
|
+
}
|
|
11369
|
+
function containsPhrase2(text, phrase) {
|
|
11370
|
+
const t = normalizeText(text);
|
|
11371
|
+
const p = normalizeText(phrase);
|
|
11372
|
+
return t.includes(p);
|
|
11373
|
+
}
|
|
11374
|
+
function detectExplicitInstructions(text, source) {
|
|
11375
|
+
const instructions = [];
|
|
11376
|
+
for (const pattern of REMEMBER_PATTERNS) {
|
|
11377
|
+
if (pattern.test(text)) {
|
|
11378
|
+
instructions.push({
|
|
11379
|
+
type: "remember",
|
|
11380
|
+
content: text,
|
|
11381
|
+
source,
|
|
11382
|
+
timestamp: nowIso()
|
|
11383
|
+
});
|
|
11384
|
+
break;
|
|
11385
|
+
}
|
|
11386
|
+
}
|
|
11387
|
+
for (const pattern of DO_NOT_REMEMBER_PATTERNS) {
|
|
11388
|
+
if (pattern.test(text)) {
|
|
11389
|
+
instructions.push({
|
|
11390
|
+
type: "do-not-remember",
|
|
11391
|
+
content: text,
|
|
11392
|
+
source,
|
|
11393
|
+
timestamp: nowIso()
|
|
11394
|
+
});
|
|
11395
|
+
break;
|
|
11396
|
+
}
|
|
11397
|
+
}
|
|
11398
|
+
return instructions;
|
|
11399
|
+
}
|
|
11400
|
+
function classifyCategory(text, explicitInstructions) {
|
|
11401
|
+
const lower = text.toLocaleLowerCase("en-US");
|
|
11402
|
+
if (explicitInstructions.some((i2) => i2.type === "do-not-remember")) {
|
|
11403
|
+
return "temporary_context";
|
|
11404
|
+
}
|
|
11405
|
+
if (explicitInstructions.some((i2) => i2.type === "remember")) {
|
|
11406
|
+
if (/prefer|prefers|like|likes|favorite|always|never/i.test(lower)) {
|
|
11407
|
+
return "preference";
|
|
11408
|
+
}
|
|
11409
|
+
if (/decided|decision|agreed|chose|adopt/i.test(lower)) {
|
|
11410
|
+
return "decision";
|
|
11411
|
+
}
|
|
11412
|
+
return "fact";
|
|
11413
|
+
}
|
|
11414
|
+
const scores = {};
|
|
11415
|
+
for (const cat of Object.keys(CATEGORY_KEYWORDS)) {
|
|
11416
|
+
scores[cat] = CATEGORY_KEYWORDS[cat].filter((kw) => containsPhrase2(lower, kw)).length;
|
|
11417
|
+
}
|
|
11418
|
+
if (/probably|likely|maybe|perhaps|seems|appears|might|could be/i.test(lower)) {
|
|
11419
|
+
scores.inference += 3;
|
|
11420
|
+
}
|
|
11421
|
+
if (text.includes("?")) {
|
|
11422
|
+
scores.question += 3;
|
|
11423
|
+
}
|
|
11424
|
+
if (/decided|decision|agreed|chose|adopt|will use|going with|resolve|settled/i.test(lower)) {
|
|
11425
|
+
scores.decision += 3;
|
|
11426
|
+
}
|
|
11427
|
+
if (/prefer|prefers|like|likes|favorite|always|never|don.t like|dislike/i.test(lower)) {
|
|
11428
|
+
scores.preference += 3;
|
|
11429
|
+
}
|
|
11430
|
+
if (/still need to|pending|waiting for|todo|to do|unresolved|outstanding|follow.?up|blocked/i.test(lower)) {
|
|
11431
|
+
scores.open_loop += 3;
|
|
11432
|
+
}
|
|
11433
|
+
if (/remember when|we talked about|discussed|experienced|encountered|session|conversation/i.test(lower)) {
|
|
11434
|
+
scores.experience += 2;
|
|
11435
|
+
}
|
|
11436
|
+
if (/repository|architecture|deploy|ci|pipeline/i.test(lower)) {
|
|
11437
|
+
scores.project_context += 2;
|
|
11438
|
+
}
|
|
11439
|
+
if (/just for|this session|for now|temporary/i.test(lower)) {
|
|
11440
|
+
scores.temporary_context += 3;
|
|
11441
|
+
}
|
|
11442
|
+
if (scores.fact > 0 && scores.project_context > 0 && scores.fact === scores.project_context) {
|
|
11443
|
+
scores.fact += 1;
|
|
11444
|
+
}
|
|
11445
|
+
let bestCategory = "fact";
|
|
11446
|
+
let bestScore = -1;
|
|
11447
|
+
for (const [cat, score] of Object.entries(scores)) {
|
|
11448
|
+
if (score > bestScore) {
|
|
11449
|
+
bestScore = score;
|
|
11450
|
+
bestCategory = cat;
|
|
11451
|
+
}
|
|
11452
|
+
}
|
|
11453
|
+
if (bestScore === 0) return "fact";
|
|
11454
|
+
return bestCategory;
|
|
11455
|
+
}
|
|
11456
|
+
function determineEpistemicType(category, text, explicitInstructions) {
|
|
11457
|
+
if (explicitInstructions.some((i2) => i2.type === "remember")) {
|
|
11458
|
+
return "human-stated";
|
|
11459
|
+
}
|
|
11460
|
+
if (category === "inference" || /probably|likely|maybe|perhaps|seems|appears/i.test(text)) {
|
|
11461
|
+
return "inferred";
|
|
11462
|
+
}
|
|
11463
|
+
if (category === "question") return "observed";
|
|
11464
|
+
return "observed";
|
|
11465
|
+
}
|
|
11466
|
+
function determineConfidence(category, epistemicType, _text) {
|
|
11467
|
+
if (epistemicType === "human-stated") return "high";
|
|
11468
|
+
if (epistemicType === "inferred") return "low";
|
|
11469
|
+
if (category === "decision" || category === "preference") return "medium";
|
|
11470
|
+
if (category === "question") return "medium";
|
|
11471
|
+
if (category === "fact") return "medium";
|
|
11472
|
+
return "low";
|
|
11473
|
+
}
|
|
11474
|
+
function extractEntities(text) {
|
|
11475
|
+
const entities = [];
|
|
11476
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11477
|
+
const capitalizedMatches = text.match(/\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\b/g) || [];
|
|
11478
|
+
for (const match of capitalizedMatches) {
|
|
11479
|
+
if (seen.has(match)) continue;
|
|
11480
|
+
seen.add(match);
|
|
11481
|
+
entities.push({
|
|
11482
|
+
id: `entity-${normalizeText(match)}`,
|
|
11483
|
+
type: "concept",
|
|
11484
|
+
name: match,
|
|
11485
|
+
aliases: [],
|
|
11486
|
+
relationships: []
|
|
11487
|
+
});
|
|
11488
|
+
}
|
|
11489
|
+
const quotedMatches = text.match(/["'`]([^"`']{2,})["'`]/g) || [];
|
|
11490
|
+
for (const match of quotedMatches) {
|
|
11491
|
+
const clean = match.replace(/["'`]/g, "");
|
|
11492
|
+
if (seen.has(clean)) continue;
|
|
11493
|
+
seen.add(clean);
|
|
11494
|
+
entities.push({
|
|
11495
|
+
id: `entity-${normalizeText(clean)}`,
|
|
11496
|
+
type: "concept",
|
|
11497
|
+
name: clean,
|
|
11498
|
+
aliases: [],
|
|
11499
|
+
relationships: []
|
|
11500
|
+
});
|
|
11501
|
+
}
|
|
11502
|
+
return entities;
|
|
11503
|
+
}
|
|
11504
|
+
function extractRelationships(_text, entities) {
|
|
11505
|
+
const relationships = [];
|
|
11506
|
+
if (entities.length < 2) return relationships;
|
|
11507
|
+
for (let i2 = 0; i2 < entities.length - 1; i2++) {
|
|
11508
|
+
for (let j = i2 + 1; j < entities.length; j++) {
|
|
11509
|
+
relationships.push({
|
|
11510
|
+
from: entities[i2].id,
|
|
11511
|
+
to: entities[j].id,
|
|
11512
|
+
type: "related-to",
|
|
11513
|
+
confidence: 0.5
|
|
11514
|
+
});
|
|
11515
|
+
}
|
|
11516
|
+
}
|
|
11517
|
+
return relationships;
|
|
11518
|
+
}
|
|
11519
|
+
async function findExistingSimilarItems(statement, category, existingItems, threshold) {
|
|
11520
|
+
const matches2 = [];
|
|
11521
|
+
for (const existing of existingItems) {
|
|
11522
|
+
if (existing.category !== category) continue;
|
|
11523
|
+
const similarity = jaccardSimilarity(statement, existing.statement);
|
|
11524
|
+
if (similarity >= threshold) {
|
|
11525
|
+
matches2.push({
|
|
11526
|
+
existingId: existing.id,
|
|
11527
|
+
existingStatement: existing.statement,
|
|
11528
|
+
similarity,
|
|
11529
|
+
matchType: similarity >= 0.95 ? "exact" : "semantic",
|
|
11530
|
+
category: existing.category
|
|
11531
|
+
});
|
|
11532
|
+
}
|
|
11533
|
+
}
|
|
11534
|
+
return matches2;
|
|
11535
|
+
}
|
|
11536
|
+
async function findExistingMemoryEntries(statement, category) {
|
|
11537
|
+
const matches2 = [];
|
|
11538
|
+
try {
|
|
11539
|
+
const all = await entries._loadAll();
|
|
11540
|
+
for (const entry of all) {
|
|
11541
|
+
if (entry.status !== "active") continue;
|
|
11542
|
+
if (entry.type !== category && entry.type !== "fact") continue;
|
|
11543
|
+
const entryText = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value);
|
|
11544
|
+
const similarity = jaccardSimilarity(statement, entryText);
|
|
11545
|
+
if (similarity >= DEFAULT_CONFIG.dedupSimilarityThreshold) {
|
|
11546
|
+
matches2.push({
|
|
11547
|
+
existingId: entry.id,
|
|
11548
|
+
existingStatement: entryText,
|
|
11549
|
+
similarity,
|
|
11550
|
+
matchType: similarity >= 0.95 ? "exact" : "semantic",
|
|
11551
|
+
category
|
|
11552
|
+
});
|
|
11553
|
+
}
|
|
11554
|
+
}
|
|
11555
|
+
} catch {
|
|
11556
|
+
}
|
|
11557
|
+
return matches2;
|
|
11558
|
+
}
|
|
11559
|
+
function shouldStoreInCoreMemory(category, epistemicType) {
|
|
11560
|
+
return category === "preference" && epistemicType === "human-stated";
|
|
11561
|
+
}
|
|
11562
|
+
async function routeToStorage(item, adapters, config) {
|
|
11563
|
+
const { category, epistemicType, statement, entities, provenance, confidence } = item;
|
|
11564
|
+
const memKey = `capture:${item.id}`;
|
|
11565
|
+
await entries.remember(memKey, {
|
|
11566
|
+
id: item.id,
|
|
11567
|
+
category,
|
|
11568
|
+
statement,
|
|
11569
|
+
entities,
|
|
11570
|
+
relationships: item.relationships,
|
|
11571
|
+
provenance,
|
|
11572
|
+
confidence,
|
|
11573
|
+
epistemicType,
|
|
11574
|
+
capturedAt: provenance.capturedAt
|
|
11575
|
+
}, {
|
|
11576
|
+
id: item.id,
|
|
11577
|
+
type: category === "preference" ? "preference" : category === "decision" ? "decision" : category === "fact" ? "fact" : "context",
|
|
11578
|
+
confidence: confidence === "high" ? 1 : confidence === "medium" ? 0.7 : 0.4,
|
|
11579
|
+
scope: category === "temporary_context" ? "session" : "local",
|
|
11580
|
+
tags: [category, provenance.epistemicType, provenance.source.conversationId],
|
|
11581
|
+
source: `capture:${provenance.source.conversationId}`
|
|
11582
|
+
});
|
|
11583
|
+
if (shouldStoreInCoreMemory(category, epistemicType)) {
|
|
11584
|
+
try {
|
|
11585
|
+
const anchor = await loadPersonalityAnchor();
|
|
11586
|
+
const existingPref = anchor.identity.durablePreferences.find((p) => p.statement === statement);
|
|
11587
|
+
if (!existingPref) {
|
|
11588
|
+
await updatePersonalityAnchor({
|
|
11589
|
+
durablePreferences: [
|
|
11590
|
+
...anchor.identity.durablePreferences,
|
|
11591
|
+
{
|
|
11592
|
+
id: item.id,
|
|
11593
|
+
statement,
|
|
11594
|
+
confidence: epistemicType,
|
|
11595
|
+
evidence: [provenance.source.conversationId],
|
|
11596
|
+
firstObserved: provenance.capturedAt,
|
|
11597
|
+
lastConfirmed: provenance.capturedAt
|
|
11598
|
+
}
|
|
11599
|
+
]
|
|
11600
|
+
});
|
|
11601
|
+
}
|
|
11602
|
+
} catch {
|
|
11603
|
+
}
|
|
11604
|
+
}
|
|
11605
|
+
if (category === "open_loop" && config.autoCreateLoops) {
|
|
11606
|
+
try {
|
|
11607
|
+
const existingLoops = await listLoops(true);
|
|
11608
|
+
const alreadyExists = existingLoops.some((l) => l.text === statement);
|
|
11609
|
+
if (!alreadyExists) {
|
|
11610
|
+
await addLoop({
|
|
11611
|
+
id: item.id,
|
|
11612
|
+
text: statement,
|
|
11613
|
+
waitor: "joy",
|
|
11614
|
+
openedAt: provenance.capturedAt
|
|
11615
|
+
});
|
|
11616
|
+
}
|
|
11617
|
+
} catch {
|
|
11618
|
+
}
|
|
11619
|
+
}
|
|
11620
|
+
if (category === "experience" && config.autoCreateExemplars) {
|
|
11621
|
+
try {
|
|
11622
|
+
await addExemplar({
|
|
11623
|
+
id: item.id,
|
|
11624
|
+
source: {
|
|
11625
|
+
conversationId: provenance.source.conversationId,
|
|
11626
|
+
messageIds: provenance.source.messageIds
|
|
11627
|
+
},
|
|
11628
|
+
pattern: statement,
|
|
11629
|
+
originalContent: statement,
|
|
11630
|
+
sourceReference: `capture:${item.id}`,
|
|
11631
|
+
retrievalHints: [statement, ...entities.map((e) => e.name)],
|
|
11632
|
+
significance: confidence === "high" ? 0.8 : confidence === "medium" ? 0.5 : 0.3,
|
|
11633
|
+
confidence: confidence === "high" ? 1 : confidence === "medium" ? 0.7 : 0.4,
|
|
11634
|
+
provenance: epistemicType,
|
|
11635
|
+
createdAt: provenance.capturedAt
|
|
11636
|
+
});
|
|
11637
|
+
} catch {
|
|
11638
|
+
}
|
|
11639
|
+
}
|
|
11640
|
+
if (config.autoCreateClaims && (category === "fact" || category === "decision")) {
|
|
11641
|
+
try {
|
|
11642
|
+
await acquire.acquire({
|
|
11643
|
+
id: `claim-${item.id}`,
|
|
11644
|
+
knowledge: { statement, category },
|
|
11645
|
+
applicability: { area: "project" },
|
|
11646
|
+
evidence: [{
|
|
11647
|
+
id: `ev-${item.id}`,
|
|
11648
|
+
source: provenance.source.conversationId,
|
|
11649
|
+
observedAt: provenance.capturedAt,
|
|
11650
|
+
supports: statement
|
|
11651
|
+
}],
|
|
11652
|
+
epistemicType,
|
|
11653
|
+
provenance: { source: "conversation-capture", detail: `category:${category}` },
|
|
11654
|
+
acquiredAt: provenance.capturedAt,
|
|
11655
|
+
scope: "project"
|
|
11656
|
+
});
|
|
11657
|
+
} catch {
|
|
11658
|
+
}
|
|
11659
|
+
}
|
|
11660
|
+
memorio.dispatch.set("dotmem:capture:item-captured", {
|
|
11661
|
+
detail: { id: item.id, category, statement }
|
|
11662
|
+
});
|
|
11663
|
+
}
|
|
11664
|
+
async function captureConversation(messages, options) {
|
|
11665
|
+
return captureEngine.captureConversation(messages, options);
|
|
11666
|
+
}
|
|
11667
|
+
async function captureTurn(text, options) {
|
|
11668
|
+
return captureEngine.captureTurn(text, options);
|
|
11669
|
+
}
|
|
11670
|
+
var CAPTURE_STATE_KEY, DEFAULT_CONFIG, CATEGORY_KEYWORDS, REMEMBER_PATTERNS, DO_NOT_REMEMBER_PATTERNS, CaptureEngine, captureEngine;
|
|
11671
|
+
var init_capture_engine = __esm({
|
|
11672
|
+
"core/memory/capture-engine.ts"() {
|
|
11673
|
+
init_exemplars();
|
|
11674
|
+
init_loops();
|
|
11675
|
+
init_personality_anchor();
|
|
11676
|
+
init_entries();
|
|
11677
|
+
init_acquire();
|
|
11678
|
+
CAPTURE_STATE_KEY = "__dotmem_capture_state__";
|
|
11679
|
+
DEFAULT_CONFIG = {
|
|
11680
|
+
minSignificance: 0.3,
|
|
11681
|
+
maxItemsPerConversation: 50,
|
|
11682
|
+
dedupSimilarityThreshold: 0.75,
|
|
11683
|
+
preserveConflicts: true,
|
|
11684
|
+
tempContextTtlMs: 24 * 60 * 60 * 1e3,
|
|
11685
|
+
autoCreateExemplars: true,
|
|
11686
|
+
autoCreateLoops: true,
|
|
11687
|
+
autoCreateClaims: true
|
|
11688
|
+
};
|
|
11689
|
+
CATEGORY_KEYWORDS = {
|
|
11690
|
+
fact: [
|
|
11691
|
+
"is",
|
|
11692
|
+
"are",
|
|
11693
|
+
"was",
|
|
11694
|
+
"were",
|
|
11695
|
+
"uses",
|
|
11696
|
+
"built with",
|
|
11697
|
+
"depends on",
|
|
11698
|
+
"located at",
|
|
11699
|
+
"named",
|
|
11700
|
+
"called",
|
|
11701
|
+
"version",
|
|
11702
|
+
"language",
|
|
11703
|
+
"framework",
|
|
11704
|
+
"test",
|
|
11705
|
+
"testing"
|
|
11706
|
+
],
|
|
11707
|
+
decision: [
|
|
11708
|
+
"decided",
|
|
11709
|
+
"decision",
|
|
11710
|
+
"agreed",
|
|
11711
|
+
"chose",
|
|
11712
|
+
"selected",
|
|
11713
|
+
"adopt",
|
|
11714
|
+
"going with",
|
|
11715
|
+
"will use",
|
|
11716
|
+
"commit to",
|
|
11717
|
+
"resolve",
|
|
11718
|
+
"settled"
|
|
11719
|
+
],
|
|
11720
|
+
experience: [
|
|
11721
|
+
"remember when",
|
|
11722
|
+
"we talked about",
|
|
11723
|
+
"discussed",
|
|
11724
|
+
"experienced",
|
|
11725
|
+
"encountered",
|
|
11726
|
+
"faced",
|
|
11727
|
+
"went through",
|
|
11728
|
+
"session",
|
|
11729
|
+
"conversation"
|
|
11730
|
+
],
|
|
11731
|
+
question: [
|
|
11732
|
+
"?",
|
|
11733
|
+
"how do",
|
|
11734
|
+
"what is",
|
|
11735
|
+
"why",
|
|
11736
|
+
"when",
|
|
11737
|
+
"where",
|
|
11738
|
+
"which",
|
|
11739
|
+
"can you",
|
|
11740
|
+
"should we",
|
|
11741
|
+
"is it possible"
|
|
11742
|
+
],
|
|
11743
|
+
open_loop: [
|
|
11744
|
+
"still need to",
|
|
11745
|
+
"pending",
|
|
11746
|
+
"waiting for",
|
|
11747
|
+
"todo",
|
|
11748
|
+
"to do",
|
|
11749
|
+
"open",
|
|
11750
|
+
"unresolved",
|
|
11751
|
+
"outstanding",
|
|
11752
|
+
"follow up",
|
|
11753
|
+
"blocked"
|
|
11754
|
+
],
|
|
11755
|
+
preference: [
|
|
11756
|
+
"prefer",
|
|
11757
|
+
"prefers",
|
|
11758
|
+
"like",
|
|
11759
|
+
"likes",
|
|
11760
|
+
"favorite",
|
|
11761
|
+
"always",
|
|
11762
|
+
"never",
|
|
11763
|
+
"don't like",
|
|
11764
|
+
"dislike",
|
|
11765
|
+
"style",
|
|
11766
|
+
"habit"
|
|
11767
|
+
],
|
|
11768
|
+
project_context: [
|
|
11769
|
+
"project",
|
|
11770
|
+
"repository",
|
|
11771
|
+
"codebase",
|
|
11772
|
+
"architecture",
|
|
11773
|
+
"stack",
|
|
11774
|
+
"deploy",
|
|
11775
|
+
"ci",
|
|
11776
|
+
"pipeline",
|
|
11777
|
+
"package"
|
|
11778
|
+
],
|
|
11779
|
+
temporary_context: [
|
|
11780
|
+
"for now",
|
|
11781
|
+
"just for",
|
|
11782
|
+
"temporary",
|
|
11783
|
+
"this session",
|
|
11784
|
+
"currently",
|
|
11785
|
+
"right now",
|
|
11786
|
+
"at the moment",
|
|
11787
|
+
"for this task"
|
|
11788
|
+
],
|
|
11789
|
+
inference: [
|
|
11790
|
+
"probably",
|
|
11791
|
+
"likely",
|
|
11792
|
+
"maybe",
|
|
11793
|
+
"perhaps",
|
|
11794
|
+
"seems",
|
|
11795
|
+
"appears",
|
|
11796
|
+
"suggests",
|
|
11797
|
+
"indicates",
|
|
11798
|
+
"implies",
|
|
11799
|
+
"might",
|
|
11800
|
+
"could be"
|
|
11801
|
+
]
|
|
11802
|
+
};
|
|
11803
|
+
REMEMBER_PATTERNS = [
|
|
11804
|
+
/remember this/i,
|
|
11805
|
+
/remember that/i,
|
|
11806
|
+
/keep this in mind/i,
|
|
11807
|
+
/don't forget/i,
|
|
11808
|
+
/do not forget/i,
|
|
11809
|
+
/store this/i,
|
|
11810
|
+
/save this/i
|
|
11811
|
+
];
|
|
11812
|
+
DO_NOT_REMEMBER_PATTERNS = [
|
|
11813
|
+
/don't remember/i,
|
|
11814
|
+
/do not remember/i,
|
|
11815
|
+
/forget this/i,
|
|
11816
|
+
/don't store/i,
|
|
11817
|
+
/do not store/i,
|
|
11818
|
+
/not relevant/i,
|
|
11819
|
+
/ignore this/i,
|
|
11820
|
+
/temporary only/i,
|
|
11821
|
+
/this session only/i
|
|
11822
|
+
];
|
|
11823
|
+
__name(generateId, "generateId");
|
|
11824
|
+
__name(nowIso, "nowIso");
|
|
11825
|
+
__name(normalizeText, "normalizeText");
|
|
11826
|
+
__name(tokenize, "tokenize");
|
|
11827
|
+
__name(jaccardSimilarity, "jaccardSimilarity");
|
|
11828
|
+
__name(containsPhrase2, "containsPhrase");
|
|
11829
|
+
__name(detectExplicitInstructions, "detectExplicitInstructions");
|
|
11830
|
+
__name(classifyCategory, "classifyCategory");
|
|
11831
|
+
__name(determineEpistemicType, "determineEpistemicType");
|
|
11832
|
+
__name(determineConfidence, "determineConfidence");
|
|
11833
|
+
__name(extractEntities, "extractEntities");
|
|
11834
|
+
__name(extractRelationships, "extractRelationships");
|
|
11835
|
+
__name(findExistingSimilarItems, "findExistingSimilarItems");
|
|
11836
|
+
__name(findExistingMemoryEntries, "findExistingMemoryEntries");
|
|
11837
|
+
__name(shouldStoreInCoreMemory, "shouldStoreInCoreMemory");
|
|
11838
|
+
__name(routeToStorage, "routeToStorage");
|
|
11839
|
+
CaptureEngine = class {
|
|
11840
|
+
static {
|
|
11841
|
+
__name(this, "CaptureEngine");
|
|
11842
|
+
}
|
|
11843
|
+
config;
|
|
11844
|
+
adapters;
|
|
11845
|
+
sessionContexts = /* @__PURE__ */ new Map();
|
|
11846
|
+
constructor(config = {}, adapters) {
|
|
11847
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
11848
|
+
this.adapters = {
|
|
11849
|
+
remember: entries.remember.bind(entries),
|
|
11850
|
+
recall: entries.recall.bind(entries),
|
|
11851
|
+
update: entries.update.bind(entries),
|
|
11852
|
+
addExemplar: addExemplar.bind(null),
|
|
11853
|
+
updateExemplar: updateExemplar.bind(null),
|
|
11854
|
+
addLoop: addLoop.bind(null),
|
|
11855
|
+
closeLoop: closeLoop.bind(null),
|
|
11856
|
+
updatePersonalityAnchor: updatePersonalityAnchor.bind(null),
|
|
11857
|
+
...adapters
|
|
11858
|
+
};
|
|
11859
|
+
}
|
|
11860
|
+
/**
|
|
11861
|
+
* Capture information from a conversation turn.
|
|
11862
|
+
* Processes a single message or turn of conversation.
|
|
11863
|
+
*/
|
|
11864
|
+
async captureTurn(text, options) {
|
|
11865
|
+
const source = {
|
|
11866
|
+
conversationId: options.conversationId,
|
|
11867
|
+
messageIds: options.messageIds ?? [],
|
|
11868
|
+
timestamp: nowIso(),
|
|
11869
|
+
turnIndex: 0
|
|
11870
|
+
};
|
|
11871
|
+
const instructions = detectExplicitInstructions(text, source);
|
|
11872
|
+
const hasDoNotRemember = instructions.some((i2) => i2.type === "do-not-remember");
|
|
11873
|
+
const hasExplicitRemember = instructions.some((i2) => i2.type === "remember");
|
|
11874
|
+
let created = 0, linked = 0, ignored = 0;
|
|
11875
|
+
if (hasDoNotRemember && !hasExplicitRemember) {
|
|
11876
|
+
const item2 = {
|
|
11877
|
+
id: generateId(),
|
|
11878
|
+
category: "temporary_context",
|
|
11879
|
+
statement: text,
|
|
11880
|
+
content: { text },
|
|
11881
|
+
entities: [],
|
|
11882
|
+
relationships: [],
|
|
11883
|
+
provenance: {
|
|
11884
|
+
source,
|
|
11885
|
+
classification: "temporary_context",
|
|
11886
|
+
confidence: "low",
|
|
11887
|
+
epistemicType: "observed",
|
|
11888
|
+
capturedAt: nowIso(),
|
|
11889
|
+
method: "automatic"
|
|
11890
|
+
},
|
|
11891
|
+
confidence: "low",
|
|
11892
|
+
epistemicType: "observed"
|
|
11893
|
+
};
|
|
11894
|
+
await this.storeSessionContext(options.conversationId, [item2], instructions);
|
|
11895
|
+
ignored++;
|
|
11896
|
+
return { items: [item2], instructions, created: 0, linked: 0, ignored: 1, dedupMatches: [] };
|
|
11897
|
+
}
|
|
11898
|
+
const category = classifyCategory(text, instructions);
|
|
11899
|
+
const epistemicType = determineEpistemicType(category, text, instructions);
|
|
11900
|
+
const confidence = determineConfidence(category, epistemicType);
|
|
11901
|
+
const entities = extractEntities(text);
|
|
11902
|
+
const relationships = extractRelationships(text, entities);
|
|
11903
|
+
const provenance = {
|
|
11904
|
+
source,
|
|
11905
|
+
classification: category,
|
|
11906
|
+
confidence,
|
|
11907
|
+
epistemicType,
|
|
11908
|
+
capturedAt: nowIso(),
|
|
11909
|
+
method: hasExplicitRemember ? "explicit" : "automatic"
|
|
11910
|
+
};
|
|
11911
|
+
const item = {
|
|
11912
|
+
id: generateId(),
|
|
11913
|
+
category,
|
|
11914
|
+
statement: text,
|
|
11915
|
+
content: { text },
|
|
11916
|
+
entities,
|
|
11917
|
+
relationships,
|
|
11918
|
+
provenance,
|
|
11919
|
+
confidence,
|
|
11920
|
+
epistemicType
|
|
11921
|
+
};
|
|
11922
|
+
const sessionCtx = this.sessionContexts.get(options.conversationId);
|
|
11923
|
+
const existingItems = sessionCtx ? sessionCtx.items : [];
|
|
11924
|
+
const dedupMatches = await findExistingSimilarItems(
|
|
11925
|
+
text,
|
|
11926
|
+
category,
|
|
11927
|
+
existingItems,
|
|
11928
|
+
this.config.dedupSimilarityThreshold
|
|
11929
|
+
);
|
|
11930
|
+
const memoryMatches = await findExistingMemoryEntries(text, category);
|
|
11931
|
+
const allMatches = [...dedupMatches, ...memoryMatches];
|
|
11932
|
+
let decision = "create";
|
|
11933
|
+
if (allMatches.length > 0) {
|
|
11934
|
+
const exactMatch = allMatches.find((m) => m.matchType === "exact");
|
|
11935
|
+
if (exactMatch) {
|
|
11936
|
+
decision = "link";
|
|
11937
|
+
linked++;
|
|
11938
|
+
} else {
|
|
11939
|
+
decision = "create";
|
|
11940
|
+
created++;
|
|
11941
|
+
}
|
|
11942
|
+
} else {
|
|
11943
|
+
created++;
|
|
11944
|
+
}
|
|
11945
|
+
const finalItem = { ...item };
|
|
11946
|
+
const shouldStore = decision === "create" || decision === "link";
|
|
11947
|
+
if (shouldStore) {
|
|
11948
|
+
await routeToStorage(finalItem, this.adapters, this.config);
|
|
11949
|
+
}
|
|
11950
|
+
await this.storeSessionContext(options.conversationId, [finalItem], instructions);
|
|
11951
|
+
return { items: [finalItem], instructions, created, linked, ignored, dedupMatches: allMatches };
|
|
11952
|
+
}
|
|
11953
|
+
/**
|
|
11954
|
+
* Capture information from a full conversation (array of messages).
|
|
11955
|
+
* Processes each message and returns aggregated results.
|
|
11956
|
+
*/
|
|
11957
|
+
async captureConversation(messages, options) {
|
|
11958
|
+
const allItems = [];
|
|
11959
|
+
const allInstructions = [];
|
|
11960
|
+
let created = 0, updated = 0, linked = 0, ignored = 0;
|
|
11961
|
+
const allDedupMatches = [];
|
|
11962
|
+
for (const [index, msg] of messages.entries()) {
|
|
11963
|
+
const source = {
|
|
11964
|
+
conversationId: options.conversationId,
|
|
11965
|
+
messageIds: [msg.id ?? `msg-${index}`],
|
|
11966
|
+
timestamp: msg.timestamp ?? nowIso(),
|
|
11967
|
+
turnIndex: index
|
|
11968
|
+
};
|
|
11969
|
+
const instructions = detectExplicitInstructions(msg.content, source);
|
|
11970
|
+
allInstructions.push(...instructions);
|
|
11971
|
+
const hasDoNotRemember = instructions.some((i2) => i2.type === "do-not-remember");
|
|
11972
|
+
const hasExplicitRemember = instructions.some((i2) => i2.type === "remember");
|
|
11973
|
+
if (hasDoNotRemember && !hasExplicitRemember) {
|
|
11974
|
+
const item2 = {
|
|
11975
|
+
id: generateId(),
|
|
11976
|
+
category: "temporary_context",
|
|
11977
|
+
statement: msg.content,
|
|
11978
|
+
content: { text: msg.content, role: msg.role },
|
|
11979
|
+
entities: [],
|
|
11980
|
+
relationships: [],
|
|
11981
|
+
provenance: {
|
|
11982
|
+
source,
|
|
11983
|
+
classification: "temporary_context",
|
|
11984
|
+
confidence: "low",
|
|
11985
|
+
epistemicType: "observed",
|
|
11986
|
+
capturedAt: nowIso(),
|
|
11987
|
+
method: "automatic"
|
|
11988
|
+
},
|
|
11989
|
+
confidence: "low",
|
|
11990
|
+
epistemicType: "observed"
|
|
11991
|
+
};
|
|
11992
|
+
allItems.push(item2);
|
|
11993
|
+
ignored++;
|
|
11994
|
+
continue;
|
|
11995
|
+
}
|
|
11996
|
+
const category = classifyCategory(msg.content, instructions);
|
|
11997
|
+
const epistemicType = determineEpistemicType(category, msg.content, instructions);
|
|
11998
|
+
const confidence = determineConfidence(category, epistemicType, msg.content);
|
|
11999
|
+
const entities = extractEntities(msg.content);
|
|
12000
|
+
const relationships = extractRelationships(msg.content, entities);
|
|
12001
|
+
const provenance = {
|
|
12002
|
+
source,
|
|
12003
|
+
classification: category,
|
|
12004
|
+
confidence,
|
|
12005
|
+
epistemicType,
|
|
12006
|
+
capturedAt: nowIso(),
|
|
12007
|
+
method: hasExplicitRemember ? "explicit" : "automatic"
|
|
12008
|
+
};
|
|
12009
|
+
const item = {
|
|
12010
|
+
id: generateId(),
|
|
12011
|
+
category,
|
|
12012
|
+
statement: msg.content,
|
|
12013
|
+
content: { text: msg.content, role: msg.role },
|
|
12014
|
+
entities,
|
|
12015
|
+
relationships,
|
|
12016
|
+
provenance,
|
|
12017
|
+
confidence,
|
|
12018
|
+
epistemicType
|
|
12019
|
+
};
|
|
12020
|
+
const dedupMatches = await findExistingSimilarItems(
|
|
12021
|
+
msg.content,
|
|
12022
|
+
category,
|
|
12023
|
+
allItems,
|
|
12024
|
+
this.config.dedupSimilarityThreshold
|
|
12025
|
+
);
|
|
12026
|
+
const memoryMatches = await findExistingMemoryEntries(msg.content, category);
|
|
12027
|
+
const allMatches = [...dedupMatches, ...memoryMatches];
|
|
12028
|
+
allDedupMatches.push(...allMatches);
|
|
12029
|
+
let decision = "create";
|
|
12030
|
+
if (allMatches.length > 0) {
|
|
12031
|
+
const exactMatch = allMatches.find((m) => m.matchType === "exact");
|
|
12032
|
+
if (exactMatch) {
|
|
12033
|
+
decision = "link";
|
|
12034
|
+
linked++;
|
|
12035
|
+
} else {
|
|
12036
|
+
decision = "create";
|
|
12037
|
+
created++;
|
|
12038
|
+
}
|
|
12039
|
+
} else {
|
|
12040
|
+
created++;
|
|
12041
|
+
}
|
|
12042
|
+
const shouldStore = decision === "create" || decision === "link";
|
|
12043
|
+
if (shouldStore) {
|
|
12044
|
+
await routeToStorage(item, this.adapters, this.config);
|
|
12045
|
+
}
|
|
12046
|
+
if (!shouldStore) ignored++;
|
|
12047
|
+
allItems.push(item);
|
|
12048
|
+
}
|
|
12049
|
+
await this.storeSessionContext(options.conversationId, allItems, allInstructions);
|
|
12050
|
+
return {
|
|
12051
|
+
items: allItems,
|
|
12052
|
+
created,
|
|
12053
|
+
updated,
|
|
12054
|
+
linked,
|
|
12055
|
+
ignored,
|
|
12056
|
+
dedupMatches: allDedupMatches,
|
|
12057
|
+
provenance: allItems.map((i2) => i2.provenance)
|
|
12058
|
+
};
|
|
12059
|
+
}
|
|
12060
|
+
/**
|
|
12061
|
+
* Retrieve captured items for a conversation.
|
|
12062
|
+
*/
|
|
12063
|
+
async getCapturedItems(conversationId) {
|
|
12064
|
+
const ctx = this.sessionContexts.get(conversationId);
|
|
12065
|
+
return ctx ? ctx.items : [];
|
|
12066
|
+
}
|
|
12067
|
+
/**
|
|
12068
|
+
* Get all captured items across all conversations.
|
|
12069
|
+
*/
|
|
12070
|
+
async getAllCapturedItems() {
|
|
12071
|
+
const all = [];
|
|
12072
|
+
for (const ctx of this.sessionContexts.values()) {
|
|
12073
|
+
all.push(...ctx.items);
|
|
12074
|
+
}
|
|
12075
|
+
return all;
|
|
12076
|
+
}
|
|
12077
|
+
/**
|
|
12078
|
+
* Clear session context (for testing / cleanup).
|
|
12079
|
+
*/
|
|
12080
|
+
clearSession(conversationId) {
|
|
12081
|
+
this.sessionContexts.delete(conversationId);
|
|
12082
|
+
}
|
|
12083
|
+
/**
|
|
12084
|
+
* Get capture engine configuration.
|
|
12085
|
+
*/
|
|
12086
|
+
getConfig() {
|
|
12087
|
+
return { ...this.config };
|
|
12088
|
+
}
|
|
12089
|
+
// ---------------------------------------------------------------------------
|
|
12090
|
+
// Private
|
|
12091
|
+
// ---------------------------------------------------------------------------
|
|
12092
|
+
async storeSessionContext(conversationId, items, instructions) {
|
|
12093
|
+
let ctx = this.sessionContexts.get(conversationId);
|
|
12094
|
+
if (!ctx) {
|
|
12095
|
+
ctx = {
|
|
12096
|
+
conversationId,
|
|
12097
|
+
startedAt: nowIso(),
|
|
12098
|
+
items: [],
|
|
12099
|
+
explicitInstructions: []
|
|
12100
|
+
};
|
|
12101
|
+
this.sessionContexts.set(conversationId, ctx);
|
|
12102
|
+
}
|
|
12103
|
+
ctx.items.push(...items);
|
|
12104
|
+
ctx.explicitInstructions.push(...instructions);
|
|
12105
|
+
try {
|
|
12106
|
+
const stateKey = `${CAPTURE_STATE_KEY}:${conversationId}`;
|
|
12107
|
+
const existing = store.get(stateKey);
|
|
12108
|
+
let prior = [];
|
|
12109
|
+
if (existing) {
|
|
12110
|
+
try {
|
|
12111
|
+
prior = JSON.parse(existing);
|
|
12112
|
+
} catch {
|
|
12113
|
+
}
|
|
12114
|
+
}
|
|
12115
|
+
store.set(stateKey, JSON.stringify([...prior, ...items]));
|
|
12116
|
+
} catch {
|
|
12117
|
+
}
|
|
12118
|
+
}
|
|
12119
|
+
};
|
|
12120
|
+
captureEngine = new CaptureEngine();
|
|
12121
|
+
__name(captureConversation, "captureConversation");
|
|
12122
|
+
__name(captureTurn, "captureTurn");
|
|
12123
|
+
}
|
|
12124
|
+
});
|
|
11244
12125
|
|
|
11245
12126
|
// core/memory/index.ts
|
|
11246
12127
|
var memory_exports = {};
|
|
@@ -11268,6 +12149,7 @@ var init_memory = __esm({
|
|
|
11268
12149
|
init_profile2();
|
|
11269
12150
|
init_note();
|
|
11270
12151
|
init_seed();
|
|
12152
|
+
init_capture_engine();
|
|
11271
12153
|
init_security();
|
|
11272
12154
|
init_detection_events();
|
|
11273
12155
|
init_detection_journal();
|
|
@@ -11456,6 +12338,25 @@ var init_memory = __esm({
|
|
|
11456
12338
|
async getSeedId() {
|
|
11457
12339
|
return exports.SEED_ID;
|
|
11458
12340
|
}
|
|
12341
|
+
},
|
|
12342
|
+
/** Automatic conversational capture engine (Phase 2 — Workstream B). */
|
|
12343
|
+
capture: {
|
|
12344
|
+
engine: captureEngine,
|
|
12345
|
+
async conversation(messages, options) {
|
|
12346
|
+
return captureConversation(messages, options);
|
|
12347
|
+
},
|
|
12348
|
+
async turn(text, options) {
|
|
12349
|
+
return captureTurn(text, options);
|
|
12350
|
+
},
|
|
12351
|
+
async getItems(conversationId) {
|
|
12352
|
+
return captureEngine.getCapturedItems(conversationId);
|
|
12353
|
+
},
|
|
12354
|
+
async getAllItems() {
|
|
12355
|
+
return captureEngine.getAllCapturedItems();
|
|
12356
|
+
},
|
|
12357
|
+
clearSession(conversationId) {
|
|
12358
|
+
captureEngine.clearSession(conversationId);
|
|
12359
|
+
}
|
|
11459
12360
|
}
|
|
11460
12361
|
};
|
|
11461
12362
|
}
|
|
@@ -13333,7 +14234,7 @@ Storage and session:
|
|
|
13333
14234
|
Utilities:
|
|
13334
14235
|
codenames print reserved codenames
|
|
13335
14236
|
render <view> render a markdown view from the knowledge store
|
|
13336
|
-
spark --verify verify the dotmem-scintilla block in
|
|
14237
|
+
spark --verify verify the dotmem-scintilla block in specifications/AGENTS.md
|
|
13337
14238
|
note <subcommand> store and retrieve markdown files and text blocks (add|list|show|export|forget)
|
|
13338
14239
|
seed <file|-> development-only: inject the Joy initial memory seed (JHF, requires \`--dev\`)
|
|
13339
14240
|
|
|
@@ -13370,13 +14271,13 @@ Examples:
|
|
|
13370
14271
|
npx dotmem --version
|
|
13371
14272
|
|
|
13372
14273
|
Learn more:
|
|
13373
|
-
|
|
13374
|
-
|
|
14274
|
+
specifications/agents.md AI working rules and the complete memory.* surface
|
|
14275
|
+
specifications/memory.md the memory reference
|
|
13375
14276
|
npx dotmem policy contract acquire --json
|
|
13376
14277
|
the acquisition contract`;
|
|
13377
14278
|
}
|
|
13378
14279
|
__name(topLevelUsage, "topLevelUsage");
|
|
13379
|
-
function
|
|
14280
|
+
function dispatch4(command, args) {
|
|
13380
14281
|
if (!command) {
|
|
13381
14282
|
void runContextCli("current project");
|
|
13382
14283
|
return;
|
|
@@ -13745,7 +14646,7 @@ ids by keeping the newest copy (by acquiredAt) and retracting the shadowed one.
|
|
|
13745
14646
|
console.info(topLevelUsage());
|
|
13746
14647
|
process2__default.default.exit(1);
|
|
13747
14648
|
}
|
|
13748
|
-
__name(
|
|
14649
|
+
__name(dispatch4, "dispatch");
|
|
13749
14650
|
async function runCodenamesCli(args) {
|
|
13750
14651
|
const json = args.includes("--json");
|
|
13751
14652
|
if (json) {
|
|
@@ -14711,10 +15612,10 @@ async function runDoctorCli(args) {
|
|
|
14711
15612
|
const policyStale = [];
|
|
14712
15613
|
try {
|
|
14713
15614
|
const { PolicyStore: PolicyStore2 } = await Promise.resolve().then(() => (init_policy_store(), policy_store_exports));
|
|
14714
|
-
const
|
|
15615
|
+
const store6 = new PolicyStore2();
|
|
14715
15616
|
for (const ns of ["operator", "security"]) {
|
|
14716
15617
|
try {
|
|
14717
|
-
const doc = await
|
|
15618
|
+
const doc = await store6.load(ns);
|
|
14718
15619
|
for (const record of doc.records) {
|
|
14719
15620
|
if (record.condition && record.condition.kind === "payload-language") {
|
|
14720
15621
|
policyStale.push({ namespace: ns, id: record.id, event: record.event, condition: JSON.stringify(record.condition) });
|
|
@@ -15570,7 +16471,7 @@ exports.detectAiEnvironment = detectAiEnvironment;
|
|
|
15570
16471
|
exports.detectAuthorityRoleDrift = detectAuthorityRoleDrift;
|
|
15571
16472
|
exports.detectIdentityFusion = detectIdentityFusion;
|
|
15572
16473
|
exports.detectUnauthorizedTransitions = detectUnauthorizedTransitions;
|
|
15573
|
-
exports.dispatch =
|
|
16474
|
+
exports.dispatch = dispatch4;
|
|
15574
16475
|
exports.draftCorrectionExperience = draftCorrectionExperience;
|
|
15575
16476
|
exports.encodeLogicSeed = encodeLogicSeed;
|
|
15576
16477
|
exports.ensureProjectAnchor = ensureProjectAnchor;
|