@rlabs-inc/memory 0.3.7 → 0.3.9
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/README.md +16 -0
- package/dist/index.js +809 -548
- package/dist/index.mjs +809 -548
- package/dist/server/index.js +961 -590
- package/dist/server/index.mjs +961 -590
- package/package.json +1 -1
- package/skills/memory-management.md +16 -3
- package/src/cli/commands/ingest.ts +214 -0
- package/src/cli/index.ts +20 -1
- package/src/core/curator.ts +46 -16
- package/src/core/engine.ts +3 -2
- package/src/core/manager.ts +16 -3
- package/src/core/session-parser.ts +955 -0
- package/src/core/store.ts +38 -29
- package/src/types/schema.ts +13 -0
package/dist/index.mjs
CHANGED
|
@@ -12098,6 +12098,11 @@ var managementLogSchema = {
|
|
|
12098
12098
|
error: "string",
|
|
12099
12099
|
details: "string"
|
|
12100
12100
|
};
|
|
12101
|
+
var personalPrimerSchema = {
|
|
12102
|
+
content: "string",
|
|
12103
|
+
session_updated: "number",
|
|
12104
|
+
updated_by: "string"
|
|
12105
|
+
};
|
|
12101
12106
|
|
|
12102
12107
|
// src/core/store.ts
|
|
12103
12108
|
var PERSONAL_PRIMER_ID = "personal-primer";
|
|
@@ -12119,7 +12124,7 @@ class MemoryStore {
|
|
|
12119
12124
|
return this._global;
|
|
12120
12125
|
}
|
|
12121
12126
|
const globalPath = this._config.globalPath;
|
|
12122
|
-
console.log(`\uD83C\uDF10 [DEBUG]
|
|
12127
|
+
console.log(`\uD83C\uDF10 [DEBUG] Initializing global database at ${globalPath}`);
|
|
12123
12128
|
const db = createDatabase({
|
|
12124
12129
|
name: "global",
|
|
12125
12130
|
basePath: globalPath
|
|
@@ -12136,8 +12141,14 @@ class MemoryStore {
|
|
|
12136
12141
|
autoSave: true,
|
|
12137
12142
|
watchFiles: this._config.watchFiles
|
|
12138
12143
|
});
|
|
12139
|
-
|
|
12140
|
-
|
|
12144
|
+
const primer = db.collection("primer", {
|
|
12145
|
+
schema: personalPrimerSchema,
|
|
12146
|
+
contentColumn: "content",
|
|
12147
|
+
autoSave: true,
|
|
12148
|
+
watchFiles: this._config.watchFiles
|
|
12149
|
+
});
|
|
12150
|
+
await Promise.all([memories.load(), managementLogs.load(), primer.load()]);
|
|
12151
|
+
this._global = { db, memories, managementLogs, primer };
|
|
12141
12152
|
return this._global;
|
|
12142
12153
|
}
|
|
12143
12154
|
async getGlobalMemories() {
|
|
@@ -12214,40 +12225,31 @@ class MemoryStore {
|
|
|
12214
12225
|
return id;
|
|
12215
12226
|
}
|
|
12216
12227
|
async getPersonalPrimer() {
|
|
12217
|
-
const {
|
|
12218
|
-
const
|
|
12219
|
-
if (!
|
|
12228
|
+
const { primer } = await this.getGlobal();
|
|
12229
|
+
const record = primer.get(PERSONAL_PRIMER_ID);
|
|
12230
|
+
if (!record) {
|
|
12220
12231
|
return null;
|
|
12221
12232
|
}
|
|
12222
12233
|
return {
|
|
12223
|
-
content:
|
|
12224
|
-
updated:
|
|
12234
|
+
content: record.content,
|
|
12235
|
+
updated: record.updated
|
|
12225
12236
|
};
|
|
12226
12237
|
}
|
|
12227
|
-
async setPersonalPrimer(content) {
|
|
12228
|
-
const {
|
|
12229
|
-
const existing =
|
|
12238
|
+
async setPersonalPrimer(content, sessionNumber, updatedBy = "user") {
|
|
12239
|
+
const { primer } = await this.getGlobal();
|
|
12240
|
+
const existing = primer.get(PERSONAL_PRIMER_ID);
|
|
12230
12241
|
if (existing) {
|
|
12231
|
-
|
|
12242
|
+
primer.update(PERSONAL_PRIMER_ID, {
|
|
12243
|
+
content,
|
|
12244
|
+
session_updated: sessionNumber ?? existing.session_updated,
|
|
12245
|
+
updated_by: updatedBy
|
|
12246
|
+
});
|
|
12232
12247
|
} else {
|
|
12233
|
-
|
|
12248
|
+
primer.insert({
|
|
12234
12249
|
id: PERSONAL_PRIMER_ID,
|
|
12235
12250
|
content,
|
|
12236
|
-
|
|
12237
|
-
|
|
12238
|
-
confidence_score: 1,
|
|
12239
|
-
context_type: "personal",
|
|
12240
|
-
temporal_relevance: "persistent",
|
|
12241
|
-
knowledge_domain: "personal",
|
|
12242
|
-
emotional_resonance: "neutral",
|
|
12243
|
-
action_required: false,
|
|
12244
|
-
problem_solution_pair: false,
|
|
12245
|
-
semantic_tags: ["personal", "primer", "relationship"],
|
|
12246
|
-
trigger_phrases: [],
|
|
12247
|
-
question_types: [],
|
|
12248
|
-
session_id: "system",
|
|
12249
|
-
project_id: "global",
|
|
12250
|
-
embedding: null
|
|
12251
|
+
session_updated: sessionNumber ?? 0,
|
|
12252
|
+
updated_by: updatedBy
|
|
12251
12253
|
});
|
|
12252
12254
|
}
|
|
12253
12255
|
}
|
|
@@ -12267,6 +12269,7 @@ class MemoryStore {
|
|
|
12267
12269
|
success: entry.success,
|
|
12268
12270
|
duration_ms: entry.durationMs,
|
|
12269
12271
|
summary: entry.summary,
|
|
12272
|
+
full_report: entry.fullReport ?? "",
|
|
12270
12273
|
error: entry.error ?? "",
|
|
12271
12274
|
details: entry.details ? JSON.stringify(entry.details) : ""
|
|
12272
12275
|
});
|
|
@@ -12630,7 +12633,14 @@ var sym = {
|
|
|
12630
12633
|
fire: "\uD83D\uDD25",
|
|
12631
12634
|
target: "\uD83C\uDFAF"
|
|
12632
12635
|
};
|
|
12636
|
+
var _verbose = false;
|
|
12633
12637
|
var logger = {
|
|
12638
|
+
setVerbose(enabled) {
|
|
12639
|
+
_verbose = enabled;
|
|
12640
|
+
},
|
|
12641
|
+
isVerbose() {
|
|
12642
|
+
return _verbose;
|
|
12643
|
+
},
|
|
12634
12644
|
info(message) {
|
|
12635
12645
|
console.log(`${timestamp()} ${style("cyan", sym.info)} ${message}`);
|
|
12636
12646
|
},
|
|
@@ -12702,7 +12712,14 @@ var logger = {
|
|
|
12702
12712
|
problem_solution: "✅",
|
|
12703
12713
|
project_context: "\uD83D\uDCE6",
|
|
12704
12714
|
milestone: "\uD83C\uDFC6",
|
|
12705
|
-
general: "\uD83D\uDCDD"
|
|
12715
|
+
general: "\uD83D\uDCDD",
|
|
12716
|
+
project_state: "\uD83D\uDCCD",
|
|
12717
|
+
pending_task: "⏳",
|
|
12718
|
+
work_in_progress: "\uD83D\uDD28",
|
|
12719
|
+
system_feedback: "\uD83D\uDCE3",
|
|
12720
|
+
project_milestone: "\uD83C\uDFC6",
|
|
12721
|
+
architectural_insight: "\uD83C\uDFDB️",
|
|
12722
|
+
architectural_direction: "\uD83E\uDDED"
|
|
12706
12723
|
};
|
|
12707
12724
|
console.log();
|
|
12708
12725
|
console.log(`${timestamp()} ${style("cyan", sym.sparkles)} ${style("bold", `SURFACING ${memories.length} MEMORIES`)}`);
|
|
@@ -12714,11 +12731,12 @@ var logger = {
|
|
|
12714
12731
|
return;
|
|
12715
12732
|
}
|
|
12716
12733
|
memories.forEach((m, i) => {
|
|
12717
|
-
const
|
|
12734
|
+
const signalCount = Math.round(m.score * 6);
|
|
12735
|
+
const signalStr = style("green", `${signalCount}sig`);
|
|
12718
12736
|
const emoji = emojiMap[m.context_type?.toLowerCase()] ?? "\uD83D\uDCDD";
|
|
12719
12737
|
const num = style("dim", `${i + 1}.`);
|
|
12720
12738
|
const preview = m.content.length > 55 ? m.content.slice(0, 55) + style("dim", "...") : m.content;
|
|
12721
|
-
console.log(` ${num} [${
|
|
12739
|
+
console.log(` ${num} [${signalStr}] ${emoji}`);
|
|
12722
12740
|
console.log(` ${preview}`);
|
|
12723
12741
|
});
|
|
12724
12742
|
console.log();
|
|
@@ -12766,44 +12784,124 @@ var logger = {
|
|
|
12766
12784
|
console.log(` ${style("dim", "processing:")} ${memoriesCount} new memories`);
|
|
12767
12785
|
},
|
|
12768
12786
|
logManagementComplete(result) {
|
|
12787
|
+
const formatAction = (action, truncate = true) => {
|
|
12788
|
+
let icon = " •";
|
|
12789
|
+
if (action.startsWith("READ OK"))
|
|
12790
|
+
icon = style("dim", " \uD83D\uDCD6");
|
|
12791
|
+
else if (action.startsWith("READ FAILED"))
|
|
12792
|
+
icon = style("red", " ❌");
|
|
12793
|
+
else if (action.startsWith("WRITE OK"))
|
|
12794
|
+
icon = style("green", " ✏️");
|
|
12795
|
+
else if (action.startsWith("WRITE FAILED"))
|
|
12796
|
+
icon = style("red", " ❌");
|
|
12797
|
+
else if (action.startsWith("RECEIVED"))
|
|
12798
|
+
icon = style("cyan", " \uD83D\uDCE5");
|
|
12799
|
+
else if (action.startsWith("CREATED"))
|
|
12800
|
+
icon = style("green", " ✨");
|
|
12801
|
+
else if (action.startsWith("UPDATED"))
|
|
12802
|
+
icon = style("blue", " \uD83D\uDCDD");
|
|
12803
|
+
else if (action.startsWith("SUPERSEDED"))
|
|
12804
|
+
icon = style("yellow", " \uD83D\uDD04");
|
|
12805
|
+
else if (action.startsWith("RESOLVED"))
|
|
12806
|
+
icon = style("green", " ✅");
|
|
12807
|
+
else if (action.startsWith("LINKED"))
|
|
12808
|
+
icon = style("cyan", " \uD83D\uDD17");
|
|
12809
|
+
else if (action.startsWith("PRIMER"))
|
|
12810
|
+
icon = style("magenta", " \uD83D\uDC9C");
|
|
12811
|
+
else if (action.startsWith("SKIPPED"))
|
|
12812
|
+
icon = style("dim", " ⏭️");
|
|
12813
|
+
else if (action.startsWith("NO_ACTION"))
|
|
12814
|
+
icon = style("dim", " ◦");
|
|
12815
|
+
const text = truncate && action.length > 70 ? action.slice(0, 67) + "..." : action;
|
|
12816
|
+
return `${icon} ${style("dim", text)}`;
|
|
12817
|
+
};
|
|
12769
12818
|
if (result.success) {
|
|
12770
12819
|
console.log(` ${style("green", sym.check)} ${style("bold", "Completed")}`);
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
|
|
12778
|
-
|
|
12779
|
-
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12820
|
+
if (_verbose) {
|
|
12821
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12822
|
+
console.log(` ${style("cyan", "\uD83D\uDCCA")} ${style("bold", "Statistics")}`);
|
|
12823
|
+
const filesRead = result.filesRead ?? 0;
|
|
12824
|
+
const filesWritten = result.filesWritten ?? 0;
|
|
12825
|
+
console.log(` ${style("dim", "Files read:")} ${filesRead > 0 ? style("green", String(filesRead)) : style("dim", "0")}`);
|
|
12826
|
+
console.log(` ${style("dim", "Files written:")} ${filesWritten > 0 ? style("green", String(filesWritten)) : style("dim", "0")}`);
|
|
12827
|
+
const superseded = result.superseded ?? 0;
|
|
12828
|
+
const resolved = result.resolved ?? 0;
|
|
12829
|
+
const linked = result.linked ?? 0;
|
|
12830
|
+
console.log(` ${style("dim", "Superseded:")} ${superseded > 0 ? style("yellow", String(superseded)) : style("dim", "0")}`);
|
|
12831
|
+
console.log(` ${style("dim", "Resolved:")} ${resolved > 0 ? style("green", String(resolved)) : style("dim", "0")}`);
|
|
12832
|
+
console.log(` ${style("dim", "Linked:")} ${linked > 0 ? style("cyan", String(linked)) : style("dim", "0")}`);
|
|
12833
|
+
console.log(` ${style("dim", "Primer:")} ${result.primerUpdated ? style("magenta", "updated") : style("dim", "unchanged")}`);
|
|
12834
|
+
if (result.actions && result.actions.length > 0) {
|
|
12835
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12836
|
+
console.log(` ${style("cyan", "\uD83C\uDFAC")} ${style("bold", "Actions")} ${style("dim", `(${result.actions.length} total)`)}`);
|
|
12837
|
+
for (const action of result.actions) {
|
|
12838
|
+
console.log(` ${formatAction(action, false)}`);
|
|
12839
|
+
}
|
|
12840
|
+
}
|
|
12841
|
+
if (result.fullReport) {
|
|
12842
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12843
|
+
console.log(` ${style("cyan", "\uD83D\uDCCB")} ${style("bold", "Full Report")}`);
|
|
12844
|
+
const reportLines = result.fullReport.split(`
|
|
12845
|
+
`);
|
|
12846
|
+
for (const line of reportLines) {
|
|
12847
|
+
if (line.includes("===")) {
|
|
12848
|
+
console.log(` ${style("bold", line)}`);
|
|
12849
|
+
} else if (line.match(/^[A-Z_]+:/)) {
|
|
12850
|
+
console.log(` ${style("cyan", line)}`);
|
|
12851
|
+
} else {
|
|
12852
|
+
console.log(` ${style("dim", line)}`);
|
|
12853
|
+
}
|
|
12854
|
+
}
|
|
12855
|
+
}
|
|
12856
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12786
12857
|
} else {
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12858
|
+
const stats = [];
|
|
12859
|
+
if (result.superseded && result.superseded > 0)
|
|
12860
|
+
stats.push(`${result.superseded} superseded`);
|
|
12861
|
+
if (result.resolved && result.resolved > 0)
|
|
12862
|
+
stats.push(`${result.resolved} resolved`);
|
|
12863
|
+
if (result.linked && result.linked > 0)
|
|
12864
|
+
stats.push(`${result.linked} linked`);
|
|
12865
|
+
if (result.primerUpdated)
|
|
12866
|
+
stats.push("primer updated");
|
|
12867
|
+
if (stats.length > 0) {
|
|
12868
|
+
console.log(` ${style("dim", "changes:")} ${stats.join(style("dim", ", "))}`);
|
|
12869
|
+
} else {
|
|
12870
|
+
console.log(` ${style("dim", "changes:")} none (memories are current)`);
|
|
12871
|
+
}
|
|
12872
|
+
if (result.actions && result.actions.length > 0) {
|
|
12873
|
+
console.log(` ${style("dim", "actions:")}`);
|
|
12874
|
+
for (const action of result.actions.slice(0, 10)) {
|
|
12875
|
+
console.log(` ${formatAction(action, true)}`);
|
|
12876
|
+
}
|
|
12877
|
+
if (result.actions.length > 10) {
|
|
12878
|
+
console.log(` ${style("dim", ` ... and ${result.actions.length - 10} more actions`)}`);
|
|
12879
|
+
}
|
|
12880
|
+
}
|
|
12792
12881
|
}
|
|
12793
12882
|
} else {
|
|
12794
12883
|
console.log(` ${style("yellow", sym.warning)} ${style("bold", "Failed")}`);
|
|
12795
12884
|
if (result.error) {
|
|
12796
|
-
console.log(` ${style("
|
|
12885
|
+
console.log(` ${style("red", "error:")} ${result.error}`);
|
|
12886
|
+
}
|
|
12887
|
+
if (result.fullReport) {
|
|
12888
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12889
|
+
console.log(` ${style("red", "\uD83D\uDCCB")} ${style("bold", "Error Report:")}`);
|
|
12890
|
+
const reportLines = result.fullReport.split(`
|
|
12891
|
+
`);
|
|
12892
|
+
for (const line of reportLines) {
|
|
12893
|
+
console.log(` ${style("dim", line)}`);
|
|
12894
|
+
}
|
|
12797
12895
|
}
|
|
12798
12896
|
}
|
|
12799
12897
|
console.log();
|
|
12800
12898
|
},
|
|
12801
12899
|
logRetrievalScoring(params) {
|
|
12802
|
-
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, selectedMemories } = params;
|
|
12900
|
+
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, durationMs, selectedMemories } = params;
|
|
12901
|
+
const timeStr = durationMs !== undefined ? style("cyan", `${durationMs.toFixed(1)}ms`) : "";
|
|
12803
12902
|
console.log();
|
|
12804
|
-
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "
|
|
12805
|
-
console.log(` ${style("dim", "total:")} ${totalMemories}
|
|
12806
|
-
console.log(` ${style("dim", "pre-filtered:")} ${preFiltered} (inactive/excluded/scope)`);
|
|
12903
|
+
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "RETRIEVAL")} ${timeStr}`);
|
|
12904
|
+
console.log(` ${style("dim", "total:")} ${totalMemories} → ${style("dim", "filtered:")} ${preFiltered} → ${style("dim", "candidates:")} ${totalMemories - preFiltered}`);
|
|
12807
12905
|
console.log(` ${style("dim", "already injected:")} ${alreadyInjected}`);
|
|
12808
12906
|
const msgPreview = currentMessage.length > 60 ? currentMessage.slice(0, 60) + "..." : currentMessage;
|
|
12809
12907
|
console.log(` ${style("dim", "message:")} "${msgPreview}"`);
|
|
@@ -12822,347 +12920,471 @@ var logger = {
|
|
|
12822
12920
|
console.log();
|
|
12823
12921
|
selectedMemories.forEach((m, i) => {
|
|
12824
12922
|
const num = style("dim", `${i + 1}.`);
|
|
12825
|
-
const
|
|
12826
|
-
const
|
|
12827
|
-
const corr = style("magenta", `corr:${(m.corroboration_score * 100).toFixed(0)}%`);
|
|
12923
|
+
const signalsStr = style("green", `${m.signalCount} signals`);
|
|
12924
|
+
const imp = style("magenta", `imp:${(m.importance_weight * 100).toFixed(0)}%`);
|
|
12828
12925
|
const type = style("yellow", m.context_type.toUpperCase());
|
|
12829
|
-
const scope = m.isGlobal ? style("blue", "[G]") : "";
|
|
12830
|
-
console.log(` ${num} [${
|
|
12926
|
+
const scope = m.isGlobal ? style("blue", " [G]") : "";
|
|
12927
|
+
console.log(` ${num} [${signalsStr} • ${imp}] ${type}${scope}`);
|
|
12831
12928
|
const preview = m.content.length > 60 ? m.content.slice(0, 60) + style("dim", "...") : m.content;
|
|
12832
12929
|
console.log(` ${style("white", preview)}`);
|
|
12833
|
-
const
|
|
12834
|
-
if (
|
|
12835
|
-
|
|
12930
|
+
const firedSignals = [];
|
|
12931
|
+
if (m.signals.trigger) {
|
|
12932
|
+
firedSignals.push(`trigger:${(m.signals.triggerStrength * 100).toFixed(0)}%`);
|
|
12933
|
+
}
|
|
12934
|
+
if (m.signals.tags) {
|
|
12935
|
+
firedSignals.push(`tags:${m.signals.tagCount}`);
|
|
12936
|
+
}
|
|
12937
|
+
if (m.signals.domain)
|
|
12938
|
+
firedSignals.push("domain");
|
|
12939
|
+
if (m.signals.feature)
|
|
12940
|
+
firedSignals.push("feature");
|
|
12941
|
+
if (m.signals.content)
|
|
12942
|
+
firedSignals.push("content");
|
|
12943
|
+
if (m.signals.vector) {
|
|
12944
|
+
firedSignals.push(`vector:${(m.signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
12836
12945
|
}
|
|
12837
|
-
if (
|
|
12838
|
-
console.log(` ${style("
|
|
12946
|
+
if (firedSignals.length > 0) {
|
|
12947
|
+
console.log(` ${style("cyan", "signals:")} ${firedSignals.join(", ")}`);
|
|
12839
12948
|
}
|
|
12840
12949
|
console.log();
|
|
12841
12950
|
});
|
|
12951
|
+
},
|
|
12952
|
+
logScoreDistribution(params) {
|
|
12953
|
+
const { totalCandidates, passedGatekeeper, rejectedByGatekeeper, buckets, stats, signalBreakdown } = params;
|
|
12954
|
+
console.log();
|
|
12955
|
+
console.log(style("dim", " ─".repeat(30)));
|
|
12956
|
+
console.log(` ${style("bold", "ACTIVATION SIGNALS")}`);
|
|
12957
|
+
console.log();
|
|
12958
|
+
const passRate = totalCandidates > 0 ? (passedGatekeeper / totalCandidates * 100).toFixed(0) : "0";
|
|
12959
|
+
console.log(` ${style("dim", "Activated:")} ${style("green", String(passedGatekeeper))}/${totalCandidates} (${passRate}%)`);
|
|
12960
|
+
console.log(` ${style("dim", "Rejected:")} ${rejectedByGatekeeper} (< 2 signals)`);
|
|
12961
|
+
console.log();
|
|
12962
|
+
if (signalBreakdown && signalBreakdown.total > 0) {
|
|
12963
|
+
console.log(` ${style("cyan", "Signal Breakdown:")}`);
|
|
12964
|
+
const signals = [
|
|
12965
|
+
{ name: "trigger", count: signalBreakdown.trigger },
|
|
12966
|
+
{ name: "tags", count: signalBreakdown.tags },
|
|
12967
|
+
{ name: "domain", count: signalBreakdown.domain },
|
|
12968
|
+
{ name: "feature", count: signalBreakdown.feature },
|
|
12969
|
+
{ name: "content", count: signalBreakdown.content },
|
|
12970
|
+
{ name: "vector", count: signalBreakdown.vector }
|
|
12971
|
+
];
|
|
12972
|
+
for (const sig of signals) {
|
|
12973
|
+
const pct = (sig.count / signalBreakdown.total * 100).toFixed(0);
|
|
12974
|
+
const bar = "█".repeat(Math.round(sig.count / signalBreakdown.total * 20));
|
|
12975
|
+
console.log(` ${sig.name.padEnd(8)} ${bar.padEnd(20)} ${sig.count} (${pct}%)`);
|
|
12976
|
+
}
|
|
12977
|
+
console.log();
|
|
12978
|
+
}
|
|
12979
|
+
if (stats.max > 0) {
|
|
12980
|
+
console.log(` ${style("cyan", "Signals:")} min=${stats.min} max=${stats.max} mean=${stats.mean}`);
|
|
12981
|
+
console.log();
|
|
12982
|
+
}
|
|
12983
|
+
if (Object.keys(buckets).length > 0) {
|
|
12984
|
+
console.log(` ${style("bold", "Distribution:")}`);
|
|
12985
|
+
const maxBucketCount = Math.max(...Object.values(buckets), 1);
|
|
12986
|
+
const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals"];
|
|
12987
|
+
for (const bucket of bucketOrder) {
|
|
12988
|
+
const count = buckets[bucket] ?? 0;
|
|
12989
|
+
if (count > 0 || bucket === "2 signals") {
|
|
12990
|
+
const barLen = Math.round(count / maxBucketCount * 25);
|
|
12991
|
+
const bar = "█".repeat(barLen) + style("dim", "░".repeat(25 - barLen));
|
|
12992
|
+
const countStr = count.toString().padStart(3);
|
|
12993
|
+
console.log(` ${style("dim", bucket.padEnd(10))} ${bar} ${style("cyan", countStr)}`);
|
|
12994
|
+
}
|
|
12995
|
+
}
|
|
12996
|
+
console.log();
|
|
12997
|
+
}
|
|
12842
12998
|
}
|
|
12843
12999
|
};
|
|
12844
13000
|
|
|
12845
13001
|
// src/core/retrieval.ts
|
|
12846
|
-
var
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12855
|
-
technical: ["implement", "code", "function", "class", "module", "api", "interface"]
|
|
12856
|
-
};
|
|
12857
|
-
var TEMPORAL_CLASS_SCORES = {
|
|
12858
|
-
eternal: 1,
|
|
12859
|
-
long_term: 0.9,
|
|
12860
|
-
medium_term: 0.7,
|
|
12861
|
-
short_term: 0.5,
|
|
12862
|
-
ephemeral: 0.3
|
|
13002
|
+
var GLOBAL_TYPE_PRIORITY = {
|
|
13003
|
+
technical: 1,
|
|
13004
|
+
preference: 2,
|
|
13005
|
+
architectural: 3,
|
|
13006
|
+
workflow: 4,
|
|
13007
|
+
decision: 5,
|
|
13008
|
+
breakthrough: 6,
|
|
13009
|
+
philosophy: 7,
|
|
13010
|
+
personal: 8
|
|
12863
13011
|
};
|
|
13012
|
+
var MIN_ACTIVATION_SIGNALS = 2;
|
|
13013
|
+
var STOPWORDS = new Set([
|
|
13014
|
+
"the",
|
|
13015
|
+
"is",
|
|
13016
|
+
"are",
|
|
13017
|
+
"was",
|
|
13018
|
+
"were",
|
|
13019
|
+
"to",
|
|
13020
|
+
"a",
|
|
13021
|
+
"an",
|
|
13022
|
+
"and",
|
|
13023
|
+
"or",
|
|
13024
|
+
"but",
|
|
13025
|
+
"in",
|
|
13026
|
+
"on",
|
|
13027
|
+
"at",
|
|
13028
|
+
"for",
|
|
13029
|
+
"with",
|
|
13030
|
+
"about",
|
|
13031
|
+
"when",
|
|
13032
|
+
"how",
|
|
13033
|
+
"what",
|
|
13034
|
+
"why",
|
|
13035
|
+
"where",
|
|
13036
|
+
"this",
|
|
13037
|
+
"that",
|
|
13038
|
+
"it",
|
|
13039
|
+
"of",
|
|
13040
|
+
"be",
|
|
13041
|
+
"have",
|
|
13042
|
+
"do",
|
|
13043
|
+
"does",
|
|
13044
|
+
"did",
|
|
13045
|
+
"will",
|
|
13046
|
+
"would",
|
|
13047
|
+
"could",
|
|
13048
|
+
"should",
|
|
13049
|
+
"can",
|
|
13050
|
+
"may",
|
|
13051
|
+
"might",
|
|
13052
|
+
"must",
|
|
13053
|
+
"shall",
|
|
13054
|
+
"has",
|
|
13055
|
+
"had",
|
|
13056
|
+
"been",
|
|
13057
|
+
"being",
|
|
13058
|
+
"i",
|
|
13059
|
+
"you",
|
|
13060
|
+
"we",
|
|
13061
|
+
"they",
|
|
13062
|
+
"he",
|
|
13063
|
+
"she",
|
|
13064
|
+
"my",
|
|
13065
|
+
"your",
|
|
13066
|
+
"our",
|
|
13067
|
+
"its",
|
|
13068
|
+
"his",
|
|
13069
|
+
"her",
|
|
13070
|
+
"their",
|
|
13071
|
+
"if",
|
|
13072
|
+
"then",
|
|
13073
|
+
"else",
|
|
13074
|
+
"so",
|
|
13075
|
+
"as",
|
|
13076
|
+
"from",
|
|
13077
|
+
"by",
|
|
13078
|
+
"into",
|
|
13079
|
+
"through",
|
|
13080
|
+
"during",
|
|
13081
|
+
"before",
|
|
13082
|
+
"after",
|
|
13083
|
+
"also",
|
|
13084
|
+
"now",
|
|
13085
|
+
"back",
|
|
13086
|
+
"get",
|
|
13087
|
+
"go",
|
|
13088
|
+
"come",
|
|
13089
|
+
"let",
|
|
13090
|
+
"like",
|
|
13091
|
+
"just",
|
|
13092
|
+
"know",
|
|
13093
|
+
"think",
|
|
13094
|
+
"see",
|
|
13095
|
+
"look",
|
|
13096
|
+
"make",
|
|
13097
|
+
"take",
|
|
13098
|
+
"want",
|
|
13099
|
+
"need"
|
|
13100
|
+
]);
|
|
12864
13101
|
|
|
12865
13102
|
class SmartVectorRetrieval {
|
|
12866
13103
|
_extractSignificantWords(text) {
|
|
12867
|
-
const
|
|
12868
|
-
"the",
|
|
12869
|
-
"is",
|
|
12870
|
-
"are",
|
|
12871
|
-
"was",
|
|
12872
|
-
"were",
|
|
12873
|
-
"to",
|
|
12874
|
-
"a",
|
|
12875
|
-
"an",
|
|
12876
|
-
"and",
|
|
12877
|
-
"or",
|
|
12878
|
-
"but",
|
|
12879
|
-
"in",
|
|
12880
|
-
"on",
|
|
12881
|
-
"at",
|
|
12882
|
-
"for",
|
|
12883
|
-
"with",
|
|
12884
|
-
"about",
|
|
12885
|
-
"when",
|
|
12886
|
-
"how",
|
|
12887
|
-
"what",
|
|
12888
|
-
"why",
|
|
12889
|
-
"where",
|
|
12890
|
-
"this",
|
|
12891
|
-
"that",
|
|
12892
|
-
"it",
|
|
12893
|
-
"of",
|
|
12894
|
-
"be",
|
|
12895
|
-
"have",
|
|
12896
|
-
"do",
|
|
12897
|
-
"does",
|
|
12898
|
-
"did",
|
|
12899
|
-
"will",
|
|
12900
|
-
"would",
|
|
12901
|
-
"could",
|
|
12902
|
-
"should",
|
|
12903
|
-
"can",
|
|
12904
|
-
"may",
|
|
12905
|
-
"might",
|
|
12906
|
-
"must",
|
|
12907
|
-
"shall",
|
|
12908
|
-
"has",
|
|
12909
|
-
"had",
|
|
12910
|
-
"been",
|
|
12911
|
-
"being",
|
|
12912
|
-
"i",
|
|
12913
|
-
"you",
|
|
12914
|
-
"we",
|
|
12915
|
-
"they",
|
|
12916
|
-
"he",
|
|
12917
|
-
"she",
|
|
12918
|
-
"my",
|
|
12919
|
-
"your",
|
|
12920
|
-
"our",
|
|
12921
|
-
"its",
|
|
12922
|
-
"his",
|
|
12923
|
-
"her",
|
|
12924
|
-
"their",
|
|
12925
|
-
"if",
|
|
12926
|
-
"then",
|
|
12927
|
-
"else",
|
|
12928
|
-
"so",
|
|
12929
|
-
"as",
|
|
12930
|
-
"from",
|
|
12931
|
-
"by",
|
|
12932
|
-
"into",
|
|
12933
|
-
"through",
|
|
12934
|
-
"during",
|
|
12935
|
-
"before",
|
|
12936
|
-
"after",
|
|
12937
|
-
"above",
|
|
12938
|
-
"below",
|
|
12939
|
-
"up",
|
|
12940
|
-
"down",
|
|
12941
|
-
"out",
|
|
12942
|
-
"off",
|
|
12943
|
-
"over",
|
|
12944
|
-
"under",
|
|
12945
|
-
"again",
|
|
12946
|
-
"further",
|
|
12947
|
-
"once",
|
|
12948
|
-
"here",
|
|
12949
|
-
"there",
|
|
12950
|
-
"all",
|
|
12951
|
-
"each",
|
|
12952
|
-
"few",
|
|
12953
|
-
"more",
|
|
12954
|
-
"most",
|
|
12955
|
-
"other",
|
|
12956
|
-
"some",
|
|
12957
|
-
"such",
|
|
12958
|
-
"no",
|
|
12959
|
-
"nor",
|
|
12960
|
-
"not",
|
|
12961
|
-
"only",
|
|
12962
|
-
"own",
|
|
12963
|
-
"same",
|
|
12964
|
-
"than",
|
|
12965
|
-
"too",
|
|
12966
|
-
"very",
|
|
12967
|
-
"just",
|
|
12968
|
-
"also",
|
|
12969
|
-
"now",
|
|
12970
|
-
"back",
|
|
12971
|
-
"get",
|
|
12972
|
-
"got",
|
|
12973
|
-
"go",
|
|
12974
|
-
"going",
|
|
12975
|
-
"gone",
|
|
12976
|
-
"come",
|
|
12977
|
-
"came",
|
|
12978
|
-
"let",
|
|
12979
|
-
"lets",
|
|
12980
|
-
"hey",
|
|
12981
|
-
"hi",
|
|
12982
|
-
"hello",
|
|
12983
|
-
"ok",
|
|
12984
|
-
"okay"
|
|
12985
|
-
]);
|
|
12986
|
-
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
|
|
13104
|
+
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !STOPWORDS.has(w));
|
|
12987
13105
|
return new Set(words);
|
|
12988
13106
|
}
|
|
12989
|
-
_detectContextTypes(message) {
|
|
12990
|
-
const messageLower = message.toLowerCase();
|
|
12991
|
-
const detected = new Set;
|
|
12992
|
-
for (const [type, keywords] of Object.entries(TYPE_KEYWORDS)) {
|
|
12993
|
-
for (const keyword of keywords) {
|
|
12994
|
-
if (messageLower.includes(keyword)) {
|
|
12995
|
-
detected.add(type);
|
|
12996
|
-
break;
|
|
12997
|
-
}
|
|
12998
|
-
}
|
|
12999
|
-
}
|
|
13000
|
-
return detected;
|
|
13001
|
-
}
|
|
13002
13107
|
_preFilter(memories, currentProjectId, messageLower) {
|
|
13003
13108
|
return memories.filter((memory) => {
|
|
13004
|
-
if (memory.status && memory.status !== "active")
|
|
13109
|
+
if (memory.status && memory.status !== "active")
|
|
13005
13110
|
return false;
|
|
13006
|
-
|
|
13007
|
-
if (memory.exclude_from_retrieval === true) {
|
|
13111
|
+
if (memory.exclude_from_retrieval === true)
|
|
13008
13112
|
return false;
|
|
13009
|
-
|
|
13010
|
-
if (memory.superseded_by) {
|
|
13113
|
+
if (memory.superseded_by)
|
|
13011
13114
|
return false;
|
|
13012
|
-
}
|
|
13013
13115
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
13014
|
-
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
13116
|
+
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
13015
13117
|
return false;
|
|
13016
|
-
}
|
|
13017
13118
|
if (memory.anti_triggers?.length) {
|
|
13018
13119
|
for (const antiTrigger of memory.anti_triggers) {
|
|
13019
|
-
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
13120
|
+
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
13020
13121
|
return false;
|
|
13021
|
-
}
|
|
13022
13122
|
}
|
|
13023
13123
|
}
|
|
13024
13124
|
return true;
|
|
13025
13125
|
});
|
|
13026
13126
|
}
|
|
13027
|
-
|
|
13028
|
-
|
|
13127
|
+
_checkTriggerActivation(messageLower, messageWords, triggerPhrases) {
|
|
13128
|
+
if (!triggerPhrases.length)
|
|
13129
|
+
return { activated: false, strength: 0 };
|
|
13130
|
+
let maxStrength = 0;
|
|
13131
|
+
for (const phrase of triggerPhrases) {
|
|
13132
|
+
const phraseLower = phrase.trim().toLowerCase();
|
|
13133
|
+
const phraseWords = phraseLower.split(/\s+/).filter((w) => !STOPWORDS.has(w) && w.length > 2);
|
|
13134
|
+
if (!phraseWords.length)
|
|
13135
|
+
continue;
|
|
13136
|
+
let matches = 0;
|
|
13137
|
+
for (const word of phraseWords) {
|
|
13138
|
+
if (messageWords.has(word) || messageLower.includes(word)) {
|
|
13139
|
+
matches++;
|
|
13140
|
+
} else if (messageWords.has(word.replace(/s$/, "")) || messageWords.has(word + "s") || messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
13141
|
+
matches += 0.8;
|
|
13142
|
+
}
|
|
13143
|
+
}
|
|
13144
|
+
const strength = phraseWords.length > 0 ? matches / phraseWords.length : 0;
|
|
13145
|
+
maxStrength = Math.max(maxStrength, strength);
|
|
13146
|
+
}
|
|
13147
|
+
return { activated: maxStrength >= 0.5, strength: maxStrength };
|
|
13148
|
+
}
|
|
13149
|
+
_checkTagActivation(messageLower, messageWords, tags) {
|
|
13150
|
+
if (!tags.length)
|
|
13151
|
+
return { activated: false, count: 0 };
|
|
13152
|
+
let matchCount = 0;
|
|
13153
|
+
for (const tag of tags) {
|
|
13154
|
+
const tagLower = tag.trim().toLowerCase();
|
|
13155
|
+
if (messageWords.has(tagLower) || messageLower.includes(tagLower)) {
|
|
13156
|
+
matchCount++;
|
|
13157
|
+
}
|
|
13158
|
+
}
|
|
13159
|
+
const threshold = tags.length <= 2 ? 1 : 2;
|
|
13160
|
+
return { activated: matchCount >= threshold, count: matchCount };
|
|
13161
|
+
}
|
|
13162
|
+
_checkDomainActivation(messageLower, messageWords, domain) {
|
|
13163
|
+
if (!domain)
|
|
13164
|
+
return false;
|
|
13165
|
+
const domainLower = domain.trim().toLowerCase();
|
|
13166
|
+
return messageWords.has(domainLower) || messageLower.includes(domainLower);
|
|
13167
|
+
}
|
|
13168
|
+
_checkFeatureActivation(messageLower, messageWords, feature) {
|
|
13169
|
+
if (!feature)
|
|
13170
|
+
return false;
|
|
13171
|
+
const featureLower = feature.trim().toLowerCase();
|
|
13172
|
+
return messageWords.has(featureLower) || messageLower.includes(featureLower);
|
|
13173
|
+
}
|
|
13174
|
+
_checkContentActivation(messageWords, memory) {
|
|
13175
|
+
const contentPreview = memory.content.slice(0, 200);
|
|
13176
|
+
const contentWords = this._extractSignificantWords(contentPreview);
|
|
13177
|
+
let overlap = 0;
|
|
13178
|
+
for (const word of messageWords) {
|
|
13179
|
+
if (contentWords.has(word))
|
|
13180
|
+
overlap++;
|
|
13181
|
+
}
|
|
13182
|
+
return overlap >= 3;
|
|
13183
|
+
}
|
|
13184
|
+
_vectorDebugSamples = [];
|
|
13185
|
+
_calculateVectorSimilarity(vec1, vec2) {
|
|
13186
|
+
if (!vec1 || !vec2) {
|
|
13187
|
+
return 0;
|
|
13188
|
+
}
|
|
13189
|
+
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
13190
|
+
const v2 = vec2 instanceof Float32Array ? vec2 : new Float32Array(vec2);
|
|
13191
|
+
const similarity = cosineSimilarity(v1, v2);
|
|
13192
|
+
if (this._vectorDebugSamples.length < 20) {
|
|
13193
|
+
this._vectorDebugSamples.push(similarity);
|
|
13194
|
+
}
|
|
13195
|
+
return similarity;
|
|
13196
|
+
}
|
|
13197
|
+
_logVectorStats() {
|
|
13198
|
+
if (this._vectorDebugSamples.length === 0)
|
|
13199
|
+
return;
|
|
13200
|
+
const samples = this._vectorDebugSamples;
|
|
13201
|
+
const min = Math.min(...samples);
|
|
13202
|
+
const max = Math.max(...samples);
|
|
13203
|
+
const avg = samples.reduce((a, b) => a + b, 0) / samples.length;
|
|
13204
|
+
console.log(`[DEBUG] Vector similarities: min=${(min * 100).toFixed(1)}% max=${(max * 100).toFixed(1)}% avg=${(avg * 100).toFixed(1)}% (${samples.length} samples)`);
|
|
13205
|
+
this._vectorDebugSamples = [];
|
|
13206
|
+
}
|
|
13207
|
+
_calculateImportanceScore(memory, signalCount, messageLower, messageWords) {
|
|
13029
13208
|
let score = 0;
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13061
|
-
signals.push("type:" + memory.context_type);
|
|
13062
|
-
}
|
|
13063
|
-
const triggerMatch = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
|
|
13064
|
-
if (triggerMatch > 0.3) {
|
|
13065
|
-
score += Math.min(0.3, triggerMatch * 0.4);
|
|
13066
|
-
signals.push("trigger:" + triggerMatch.toFixed(2));
|
|
13067
|
-
}
|
|
13068
|
-
if (memory.feature) {
|
|
13069
|
-
const featureLower = memory.feature.toLowerCase();
|
|
13070
|
-
if (messageLower.includes(featureLower) || messageWords.has(featureLower)) {
|
|
13071
|
-
score += 0.2;
|
|
13072
|
-
signals.push("feature:" + memory.feature);
|
|
13073
|
-
}
|
|
13074
|
-
}
|
|
13075
|
-
if (memory.related_files?.length) {
|
|
13076
|
-
for (const file of memory.related_files) {
|
|
13077
|
-
const filename = file.split("/").pop()?.toLowerCase() ?? "";
|
|
13078
|
-
if (filename && messageLower.includes(filename)) {
|
|
13079
|
-
score += 0.25;
|
|
13080
|
-
signals.push("file:" + filename);
|
|
13209
|
+
score += memory.importance_weight ?? 0.5;
|
|
13210
|
+
if (signalCount >= 4)
|
|
13211
|
+
score += 0.2;
|
|
13212
|
+
else if (signalCount >= 3)
|
|
13213
|
+
score += 0.1;
|
|
13214
|
+
if (memory.awaiting_implementation)
|
|
13215
|
+
score += 0.15;
|
|
13216
|
+
if (memory.awaiting_decision)
|
|
13217
|
+
score += 0.1;
|
|
13218
|
+
const contextType = memory.context_type?.toLowerCase() ?? "";
|
|
13219
|
+
const contextKeywords = {
|
|
13220
|
+
debugging: ["debug", "bug", "error", "fix", "issue", "problem", "broken"],
|
|
13221
|
+
decision: ["decide", "decision", "choose", "choice", "option", "should"],
|
|
13222
|
+
architectural: ["architect", "design", "structure", "pattern", "how"],
|
|
13223
|
+
breakthrough: ["insight", "realize", "understand", "discover", "why"],
|
|
13224
|
+
technical: ["implement", "code", "function", "method", "api"],
|
|
13225
|
+
workflow: ["process", "workflow", "step", "flow", "pipeline"],
|
|
13226
|
+
philosophy: ["philosophy", "principle", "belief", "approach", "think"]
|
|
13227
|
+
};
|
|
13228
|
+
const keywords = contextKeywords[contextType] ?? [];
|
|
13229
|
+
for (const kw of keywords) {
|
|
13230
|
+
if (messageWords.has(kw) || messageLower.includes(kw)) {
|
|
13231
|
+
score += 0.1;
|
|
13232
|
+
break;
|
|
13233
|
+
}
|
|
13234
|
+
}
|
|
13235
|
+
if (memory.problem_solution_pair) {
|
|
13236
|
+
const problemWords = ["error", "bug", "issue", "problem", "wrong", "fail", "broken", "help", "stuck"];
|
|
13237
|
+
for (const pw of problemWords) {
|
|
13238
|
+
if (messageWords.has(pw) || messageLower.includes(pw)) {
|
|
13239
|
+
score += 0.1;
|
|
13081
13240
|
break;
|
|
13082
13241
|
}
|
|
13083
13242
|
}
|
|
13084
13243
|
}
|
|
13085
|
-
|
|
13244
|
+
const temporalClass = memory.temporal_class ?? "medium_term";
|
|
13245
|
+
if (temporalClass === "eternal")
|
|
13246
|
+
score += 0.1;
|
|
13247
|
+
else if (temporalClass === "long_term")
|
|
13248
|
+
score += 0.05;
|
|
13249
|
+
else if (temporalClass === "ephemeral") {
|
|
13250
|
+
if ((memory.sessions_since_surfaced ?? 0) <= 1)
|
|
13251
|
+
score += 0.1;
|
|
13252
|
+
}
|
|
13253
|
+
const confidence = memory.confidence_score ?? 0.7;
|
|
13254
|
+
if (confidence < 0.5)
|
|
13255
|
+
score -= 0.1;
|
|
13256
|
+
const emotionalKeywords = {
|
|
13257
|
+
frustration: ["frustrated", "annoying", "stuck", "ugh", "damn", "hate"],
|
|
13258
|
+
excitement: ["excited", "awesome", "amazing", "love", "great", "wow"],
|
|
13259
|
+
curiosity: ["wonder", "curious", "interesting", "how", "why", "what if"],
|
|
13260
|
+
satisfaction: ["done", "finished", "complete", "works", "solved", "finally"],
|
|
13261
|
+
discovery: ["found", "realized", "understand", "insight", "breakthrough"]
|
|
13262
|
+
};
|
|
13263
|
+
const emotion = memory.emotional_resonance?.toLowerCase() ?? "";
|
|
13264
|
+
const emotionKws = emotionalKeywords[emotion] ?? [];
|
|
13265
|
+
for (const ew of emotionKws) {
|
|
13266
|
+
if (messageWords.has(ew) || messageLower.includes(ew)) {
|
|
13267
|
+
score += 0.05;
|
|
13268
|
+
break;
|
|
13269
|
+
}
|
|
13270
|
+
}
|
|
13271
|
+
return score;
|
|
13086
13272
|
}
|
|
13087
13273
|
retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0, maxGlobalMemories = 2) {
|
|
13274
|
+
const startTime = performance.now();
|
|
13088
13275
|
if (!allMemories.length) {
|
|
13089
13276
|
return [];
|
|
13090
13277
|
}
|
|
13091
13278
|
const messageLower = currentMessage.toLowerCase();
|
|
13092
13279
|
const messageWords = this._extractSignificantWords(currentMessage);
|
|
13093
|
-
const detectedTypes = this._detectContextTypes(currentMessage);
|
|
13094
13280
|
const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
|
|
13095
13281
|
if (!candidates.length) {
|
|
13096
13282
|
return [];
|
|
13097
13283
|
}
|
|
13098
|
-
const
|
|
13284
|
+
const activatedMemories = [];
|
|
13285
|
+
let rejectedCount = 0;
|
|
13099
13286
|
for (const memory of candidates) {
|
|
13100
13287
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
13101
|
-
const
|
|
13102
|
-
const
|
|
13103
|
-
const
|
|
13104
|
-
const
|
|
13105
|
-
const
|
|
13106
|
-
const
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
if (
|
|
13288
|
+
const triggerResult = this._checkTriggerActivation(messageLower, messageWords, memory.trigger_phrases ?? []);
|
|
13289
|
+
const tagResult = this._checkTagActivation(messageLower, messageWords, memory.semantic_tags ?? []);
|
|
13290
|
+
const domainActivated = this._checkDomainActivation(messageLower, messageWords, memory.domain);
|
|
13291
|
+
const featureActivated = this._checkFeatureActivation(messageLower, messageWords, memory.feature);
|
|
13292
|
+
const contentActivated = this._checkContentActivation(messageWords, memory);
|
|
13293
|
+
const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
|
|
13294
|
+
let signalCount = 0;
|
|
13295
|
+
if (triggerResult.activated)
|
|
13296
|
+
signalCount++;
|
|
13297
|
+
if (tagResult.activated)
|
|
13298
|
+
signalCount++;
|
|
13299
|
+
if (domainActivated)
|
|
13300
|
+
signalCount++;
|
|
13301
|
+
if (featureActivated)
|
|
13302
|
+
signalCount++;
|
|
13303
|
+
if (contentActivated)
|
|
13304
|
+
signalCount++;
|
|
13305
|
+
if (vectorSimilarity >= 0.4)
|
|
13306
|
+
signalCount++;
|
|
13307
|
+
const signals = {
|
|
13308
|
+
trigger: triggerResult.activated,
|
|
13309
|
+
tags: tagResult.activated,
|
|
13310
|
+
domain: domainActivated,
|
|
13311
|
+
feature: featureActivated,
|
|
13312
|
+
content: contentActivated,
|
|
13313
|
+
count: signalCount,
|
|
13314
|
+
triggerStrength: triggerResult.strength,
|
|
13315
|
+
tagCount: tagResult.count,
|
|
13316
|
+
vectorSimilarity
|
|
13317
|
+
};
|
|
13318
|
+
if (signalCount < MIN_ACTIVATION_SIGNALS) {
|
|
13319
|
+
rejectedCount++;
|
|
13117
13320
|
continue;
|
|
13118
13321
|
}
|
|
13119
|
-
const
|
|
13120
|
-
|
|
13121
|
-
corroboration: corroborationScore,
|
|
13122
|
-
reasoning_match: reasoningMatch,
|
|
13123
|
-
retrieval_weight: retrievalWeight,
|
|
13124
|
-
temporal: temporalScore,
|
|
13125
|
-
context: contextScore,
|
|
13126
|
-
tags: tagScore,
|
|
13127
|
-
trigger: triggerScore,
|
|
13128
|
-
domain: domainScore,
|
|
13129
|
-
question: questionScore,
|
|
13130
|
-
emotion: emotionScore,
|
|
13131
|
-
problem: problemScore,
|
|
13132
|
-
action: actionBoost
|
|
13133
|
-
};
|
|
13134
|
-
const reasoning = this._generateSelectionReasoning(components, corroborationSignals);
|
|
13135
|
-
scoredMemories.push({
|
|
13322
|
+
const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords);
|
|
13323
|
+
activatedMemories.push({
|
|
13136
13324
|
memory,
|
|
13137
|
-
|
|
13138
|
-
|
|
13139
|
-
value_score: valueScore,
|
|
13140
|
-
corroboration_score: corroborationScore,
|
|
13141
|
-
reasoning,
|
|
13142
|
-
components,
|
|
13325
|
+
signals,
|
|
13326
|
+
importanceScore,
|
|
13143
13327
|
isGlobal
|
|
13144
13328
|
});
|
|
13145
13329
|
}
|
|
13146
|
-
|
|
13330
|
+
this._logActivationDistribution(activatedMemories, candidates.length, rejectedCount);
|
|
13331
|
+
this._logVectorStats();
|
|
13332
|
+
if (!activatedMemories.length) {
|
|
13333
|
+
const durationMs2 = performance.now() - startTime;
|
|
13334
|
+
logger.logRetrievalScoring({
|
|
13335
|
+
totalMemories: allMemories.length,
|
|
13336
|
+
currentMessage,
|
|
13337
|
+
alreadyInjected: alreadyInjectedCount,
|
|
13338
|
+
preFiltered: allMemories.length - candidates.length,
|
|
13339
|
+
globalCount: 0,
|
|
13340
|
+
projectCount: 0,
|
|
13341
|
+
finalCount: 0,
|
|
13342
|
+
durationMs: durationMs2,
|
|
13343
|
+
selectedMemories: []
|
|
13344
|
+
});
|
|
13345
|
+
return [];
|
|
13346
|
+
}
|
|
13347
|
+
activatedMemories.sort((a, b) => {
|
|
13348
|
+
if (b.signals.count !== a.signals.count) {
|
|
13349
|
+
return b.signals.count - a.signals.count;
|
|
13350
|
+
}
|
|
13351
|
+
return b.importanceScore - a.importanceScore;
|
|
13352
|
+
});
|
|
13147
13353
|
const selected = [];
|
|
13148
13354
|
const selectedIds = new Set;
|
|
13149
|
-
const globalMemories =
|
|
13150
|
-
const projectMemories =
|
|
13151
|
-
const
|
|
13152
|
-
const
|
|
13153
|
-
const
|
|
13154
|
-
if (
|
|
13155
|
-
return
|
|
13156
|
-
|
|
13157
|
-
|
|
13355
|
+
const globalMemories = activatedMemories.filter((m) => m.isGlobal);
|
|
13356
|
+
const projectMemories = activatedMemories.filter((m) => !m.isGlobal);
|
|
13357
|
+
const globalsSorted = globalMemories.sort((a, b) => {
|
|
13358
|
+
const aPriority = GLOBAL_TYPE_PRIORITY[a.memory.context_type ?? "personal"] ?? 8;
|
|
13359
|
+
const bPriority = GLOBAL_TYPE_PRIORITY[b.memory.context_type ?? "personal"] ?? 8;
|
|
13360
|
+
if (aPriority !== bPriority)
|
|
13361
|
+
return aPriority - bPriority;
|
|
13362
|
+
if (b.signals.count !== a.signals.count)
|
|
13363
|
+
return b.signals.count - a.signals.count;
|
|
13364
|
+
return b.importanceScore - a.importanceScore;
|
|
13158
13365
|
});
|
|
13159
|
-
for (const item of
|
|
13366
|
+
for (const item of globalsSorted.slice(0, maxGlobalMemories)) {
|
|
13160
13367
|
if (!selectedIds.has(item.memory.id)) {
|
|
13161
13368
|
selected.push(item);
|
|
13162
13369
|
selectedIds.add(item.memory.id);
|
|
13163
13370
|
}
|
|
13164
13371
|
}
|
|
13165
|
-
|
|
13372
|
+
const projectsSorted = [...projectMemories].sort((a, b) => {
|
|
13373
|
+
const aAction = a.memory.action_required ? 1 : 0;
|
|
13374
|
+
const bAction = b.memory.action_required ? 1 : 0;
|
|
13375
|
+
if (bAction !== aAction)
|
|
13376
|
+
return bAction - aAction;
|
|
13377
|
+
if (b.signals.count !== a.signals.count)
|
|
13378
|
+
return b.signals.count - a.signals.count;
|
|
13379
|
+
return b.importanceScore - a.importanceScore;
|
|
13380
|
+
});
|
|
13381
|
+
console.log(`[DEBUG] Top 15 candidates (sorted):`);
|
|
13382
|
+
for (let i = 0;i < Math.min(15, projectsSorted.length); i++) {
|
|
13383
|
+
const m = projectsSorted[i];
|
|
13384
|
+
const action = m.memory.action_required ? "⚡" : "";
|
|
13385
|
+
console.log(` ${i + 1}. [${m.signals.count}sig] score=${m.importanceScore.toFixed(2)} ${action} ${m.memory.content.slice(0, 45)}...`);
|
|
13386
|
+
}
|
|
13387
|
+
for (const item of projectsSorted) {
|
|
13166
13388
|
if (selected.length >= maxMemories)
|
|
13167
13389
|
break;
|
|
13168
13390
|
if (selectedIds.has(item.memory.id))
|
|
@@ -13170,7 +13392,27 @@ class SmartVectorRetrieval {
|
|
|
13170
13392
|
selected.push(item);
|
|
13171
13393
|
selectedIds.add(item.memory.id);
|
|
13172
13394
|
}
|
|
13173
|
-
selected.
|
|
13395
|
+
if (selected.length < maxMemories) {
|
|
13396
|
+
const relatedIds = new Set;
|
|
13397
|
+
for (const item of selected) {
|
|
13398
|
+
for (const relatedId of item.memory.related_to ?? []) {
|
|
13399
|
+
if (!selectedIds.has(relatedId)) {
|
|
13400
|
+
relatedIds.add(relatedId);
|
|
13401
|
+
}
|
|
13402
|
+
}
|
|
13403
|
+
}
|
|
13404
|
+
for (const item of activatedMemories) {
|
|
13405
|
+
if (selected.length >= maxMemories)
|
|
13406
|
+
break;
|
|
13407
|
+
if (selectedIds.has(item.memory.id))
|
|
13408
|
+
continue;
|
|
13409
|
+
if (relatedIds.has(item.memory.id)) {
|
|
13410
|
+
selected.push(item);
|
|
13411
|
+
selectedIds.add(item.memory.id);
|
|
13412
|
+
}
|
|
13413
|
+
}
|
|
13414
|
+
}
|
|
13415
|
+
const durationMs = performance.now() - startTime;
|
|
13174
13416
|
logger.logRetrievalScoring({
|
|
13175
13417
|
totalMemories: allMemories.length,
|
|
13176
13418
|
currentMessage,
|
|
@@ -13179,202 +13421,102 @@ class SmartVectorRetrieval {
|
|
|
13179
13421
|
globalCount: globalMemories.length,
|
|
13180
13422
|
projectCount: projectMemories.length,
|
|
13181
13423
|
finalCount: selected.length,
|
|
13424
|
+
durationMs,
|
|
13182
13425
|
selectedMemories: selected.map((item) => ({
|
|
13183
13426
|
content: item.memory.content,
|
|
13184
|
-
reasoning: item.
|
|
13185
|
-
|
|
13186
|
-
relevance_score: item.relevance_score,
|
|
13187
|
-
corroboration_score: item.corroboration_score,
|
|
13427
|
+
reasoning: this._generateActivationReasoning(item.signals),
|
|
13428
|
+
signalCount: item.signals.count,
|
|
13188
13429
|
importance_weight: item.memory.importance_weight ?? 0.5,
|
|
13189
13430
|
context_type: item.memory.context_type ?? "general",
|
|
13190
13431
|
semantic_tags: item.memory.semantic_tags ?? [],
|
|
13191
13432
|
isGlobal: item.isGlobal,
|
|
13192
|
-
|
|
13433
|
+
signals: {
|
|
13434
|
+
trigger: item.signals.trigger,
|
|
13435
|
+
triggerStrength: item.signals.triggerStrength,
|
|
13436
|
+
tags: item.signals.tags,
|
|
13437
|
+
tagCount: item.signals.tagCount,
|
|
13438
|
+
domain: item.signals.domain,
|
|
13439
|
+
feature: item.signals.feature,
|
|
13440
|
+
content: item.signals.content,
|
|
13441
|
+
vector: item.signals.vectorSimilarity >= 0.4,
|
|
13442
|
+
vectorSimilarity: item.signals.vectorSimilarity
|
|
13443
|
+
}
|
|
13193
13444
|
}))
|
|
13194
13445
|
});
|
|
13195
13446
|
return selected.map((item) => ({
|
|
13196
13447
|
...item.memory,
|
|
13197
|
-
score: item.
|
|
13198
|
-
relevance_score: item.
|
|
13199
|
-
value_score: item.
|
|
13448
|
+
score: item.signals.count / 6,
|
|
13449
|
+
relevance_score: item.signals.count / 6,
|
|
13450
|
+
value_score: item.importanceScore
|
|
13200
13451
|
}));
|
|
13201
13452
|
}
|
|
13202
|
-
|
|
13203
|
-
if (!vec1 || !vec2)
|
|
13204
|
-
return 0;
|
|
13205
|
-
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
13206
|
-
return cosineSimilarity(v1, vec2);
|
|
13207
|
-
}
|
|
13208
|
-
_scoreTemporalRelevance(temporalType) {
|
|
13209
|
-
const scores = {
|
|
13210
|
-
persistent: 0.8,
|
|
13211
|
-
session: 0.6,
|
|
13212
|
-
temporary: 0.3,
|
|
13213
|
-
archived: 0.1
|
|
13214
|
-
};
|
|
13215
|
-
return scores[temporalType] ?? 0.5;
|
|
13216
|
-
}
|
|
13217
|
-
_scoreContextAlignment(message, contextType) {
|
|
13218
|
-
const messageLower = message.toLowerCase();
|
|
13219
|
-
const keywords = TYPE_KEYWORDS[contextType] ?? [];
|
|
13220
|
-
const matches = keywords.filter((kw) => messageLower.includes(kw)).length;
|
|
13221
|
-
if (matches > 0) {
|
|
13222
|
-
return Math.min(0.2 + matches * 0.15, 0.7);
|
|
13223
|
-
}
|
|
13224
|
-
return 0.1;
|
|
13225
|
-
}
|
|
13226
|
-
_scoreSemanticTags(message, tags) {
|
|
13227
|
-
if (!tags.length)
|
|
13228
|
-
return 0;
|
|
13229
|
-
const messageLower = message.toLowerCase();
|
|
13230
|
-
const matches = tags.filter((tag) => messageLower.includes(tag.trim().toLowerCase())).length;
|
|
13231
|
-
if (matches > 0) {
|
|
13232
|
-
return Math.min(0.3 + matches * 0.25, 1);
|
|
13233
|
-
}
|
|
13234
|
-
return 0;
|
|
13235
|
-
}
|
|
13236
|
-
_scoreTriggerPhrases(messageLower, triggerPhrases) {
|
|
13237
|
-
if (!triggerPhrases.length)
|
|
13238
|
-
return 0;
|
|
13239
|
-
const stopWords = new Set([
|
|
13240
|
-
"the",
|
|
13241
|
-
"is",
|
|
13242
|
-
"are",
|
|
13243
|
-
"was",
|
|
13244
|
-
"were",
|
|
13245
|
-
"to",
|
|
13246
|
-
"a",
|
|
13247
|
-
"an",
|
|
13248
|
-
"and",
|
|
13249
|
-
"or",
|
|
13250
|
-
"but",
|
|
13251
|
-
"in",
|
|
13252
|
-
"on",
|
|
13253
|
-
"at",
|
|
13254
|
-
"for",
|
|
13255
|
-
"with",
|
|
13256
|
-
"about",
|
|
13257
|
-
"when",
|
|
13258
|
-
"how",
|
|
13259
|
-
"what",
|
|
13260
|
-
"why"
|
|
13261
|
-
]);
|
|
13262
|
-
let maxScore = 0;
|
|
13263
|
-
for (const pattern of triggerPhrases) {
|
|
13264
|
-
const patternLower = pattern.trim().toLowerCase();
|
|
13265
|
-
const patternWords = patternLower.split(/\s+/).filter((w) => !stopWords.has(w) && w.length > 2);
|
|
13266
|
-
if (patternWords.length) {
|
|
13267
|
-
let matches = 0;
|
|
13268
|
-
for (const word of patternWords) {
|
|
13269
|
-
if (messageLower.includes(word)) {
|
|
13270
|
-
matches += 1;
|
|
13271
|
-
} else if (messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
13272
|
-
matches += 0.9;
|
|
13273
|
-
}
|
|
13274
|
-
}
|
|
13275
|
-
const conceptScore = matches / patternWords.length;
|
|
13276
|
-
maxScore = Math.max(maxScore, conceptScore);
|
|
13277
|
-
}
|
|
13278
|
-
}
|
|
13279
|
-
return Math.min(maxScore, 1);
|
|
13280
|
-
}
|
|
13281
|
-
_scoreDomain(messageWords, messageLower, memory) {
|
|
13282
|
-
let score = 0;
|
|
13283
|
-
if (memory.domain) {
|
|
13284
|
-
const domainLower = memory.domain.toLowerCase();
|
|
13285
|
-
if (messageWords.has(domainLower) || messageLower.includes(domainLower)) {
|
|
13286
|
-
score += 0.5;
|
|
13287
|
-
}
|
|
13288
|
-
}
|
|
13289
|
-
if (memory.feature) {
|
|
13290
|
-
const featureLower = memory.feature.toLowerCase();
|
|
13291
|
-
if (messageWords.has(featureLower) || messageLower.includes(featureLower)) {
|
|
13292
|
-
score += 0.3;
|
|
13293
|
-
}
|
|
13294
|
-
}
|
|
13295
|
-
if (memory.component) {
|
|
13296
|
-
const componentLower = memory.component.toLowerCase();
|
|
13297
|
-
if (messageWords.has(componentLower) || messageLower.includes(componentLower)) {
|
|
13298
|
-
score += 0.2;
|
|
13299
|
-
}
|
|
13300
|
-
}
|
|
13301
|
-
return Math.min(score, 1);
|
|
13302
|
-
}
|
|
13303
|
-
_scoreQuestionTypes(message, questionTypes) {
|
|
13304
|
-
if (!questionTypes.length)
|
|
13305
|
-
return 0;
|
|
13306
|
-
const messageLower = message.toLowerCase();
|
|
13307
|
-
const questionWords = ["how", "why", "what", "when", "where"];
|
|
13308
|
-
for (const qtype of questionTypes) {
|
|
13309
|
-
const qtypeLower = qtype.trim().toLowerCase();
|
|
13310
|
-
if (messageLower.includes(qtypeLower)) {
|
|
13311
|
-
return 0.8;
|
|
13312
|
-
}
|
|
13313
|
-
const messageHasQuestion = questionWords.some((qw) => messageLower.includes(qw));
|
|
13314
|
-
const typeHasQuestion = questionWords.some((qw) => qtypeLower.includes(qw));
|
|
13315
|
-
if (messageHasQuestion && typeHasQuestion) {
|
|
13316
|
-
return 0.5;
|
|
13317
|
-
}
|
|
13318
|
-
}
|
|
13319
|
-
return 0;
|
|
13320
|
-
}
|
|
13321
|
-
_scoreEmotionalContext(message, emotion) {
|
|
13322
|
-
if (!emotion)
|
|
13323
|
-
return 0;
|
|
13324
|
-
const messageLower = message.toLowerCase();
|
|
13325
|
-
const emotionPatterns = {
|
|
13326
|
-
joy: ["happy", "excited", "love", "wonderful", "great", "awesome"],
|
|
13327
|
-
frustration: ["stuck", "confused", "help", "issue", "problem", "why"],
|
|
13328
|
-
discovery: ["realized", "found", "discovered", "aha", "insight"],
|
|
13329
|
-
gratitude: ["thank", "appreciate", "grateful", "dear friend"]
|
|
13330
|
-
};
|
|
13331
|
-
const patterns = emotionPatterns[emotion.toLowerCase()] ?? [];
|
|
13332
|
-
if (patterns.some((pattern) => messageLower.includes(pattern))) {
|
|
13333
|
-
return 0.7;
|
|
13334
|
-
}
|
|
13335
|
-
return 0;
|
|
13336
|
-
}
|
|
13337
|
-
_scoreProblemSolution(message, isProblemSolution) {
|
|
13338
|
-
if (!isProblemSolution)
|
|
13339
|
-
return 0;
|
|
13340
|
-
const messageLower = message.toLowerCase();
|
|
13341
|
-
const problemWords = ["error", "issue", "problem", "stuck", "help", "fix", "solve", "debug"];
|
|
13342
|
-
if (problemWords.some((word) => messageLower.includes(word))) {
|
|
13343
|
-
return 0.8;
|
|
13344
|
-
}
|
|
13345
|
-
return 0;
|
|
13346
|
-
}
|
|
13347
|
-
_generateSelectionReasoning(components, corroborationSignals) {
|
|
13348
|
-
const scores = [
|
|
13349
|
-
["vector", components.vector],
|
|
13350
|
-
["corroboration", components.corroboration],
|
|
13351
|
-
["reasoning", components.reasoning_match],
|
|
13352
|
-
["weight", components.retrieval_weight],
|
|
13353
|
-
["context", components.context],
|
|
13354
|
-
["temporal", components.temporal],
|
|
13355
|
-
["tags", components.tags],
|
|
13356
|
-
["trigger", components.trigger],
|
|
13357
|
-
["domain", components.domain],
|
|
13358
|
-
["question", components.question],
|
|
13359
|
-
["emotion", components.emotion],
|
|
13360
|
-
["problem", components.problem],
|
|
13361
|
-
["action", components.action]
|
|
13362
|
-
];
|
|
13363
|
-
scores.sort((a, b) => b[1] - a[1]);
|
|
13453
|
+
_generateActivationReasoning(signals) {
|
|
13364
13454
|
const reasons = [];
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
13455
|
+
if (signals.trigger)
|
|
13456
|
+
reasons.push(`trigger:${(signals.triggerStrength * 100).toFixed(0)}%`);
|
|
13457
|
+
if (signals.tags)
|
|
13458
|
+
reasons.push(`tags:${signals.tagCount}`);
|
|
13459
|
+
if (signals.domain)
|
|
13460
|
+
reasons.push("domain");
|
|
13461
|
+
if (signals.feature)
|
|
13462
|
+
reasons.push("feature");
|
|
13463
|
+
if (signals.content)
|
|
13464
|
+
reasons.push("content");
|
|
13465
|
+
if (signals.vectorSimilarity >= 0.4)
|
|
13466
|
+
reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
13467
|
+
return reasons.length ? `Activated: ${reasons.join(", ")} (${signals.count} signals)` : "No signals";
|
|
13468
|
+
}
|
|
13469
|
+
_logActivationDistribution(activated, totalCandidates, rejectedCount) {
|
|
13470
|
+
const signalBuckets = {
|
|
13471
|
+
"2 signals": 0,
|
|
13472
|
+
"3 signals": 0,
|
|
13473
|
+
"4 signals": 0,
|
|
13474
|
+
"5 signals": 0,
|
|
13475
|
+
"6 signals": 0
|
|
13476
|
+
};
|
|
13477
|
+
for (const mem of activated) {
|
|
13478
|
+
const key = `${Math.min(mem.signals.count, 6)} signals`;
|
|
13479
|
+
signalBuckets[key] = (signalBuckets[key] ?? 0) + 1;
|
|
13480
|
+
}
|
|
13481
|
+
let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0;
|
|
13482
|
+
for (const mem of activated) {
|
|
13483
|
+
if (mem.signals.trigger)
|
|
13484
|
+
triggerCount++;
|
|
13485
|
+
if (mem.signals.tags)
|
|
13486
|
+
tagCount++;
|
|
13487
|
+
if (mem.signals.domain)
|
|
13488
|
+
domainCount++;
|
|
13489
|
+
if (mem.signals.feature)
|
|
13490
|
+
featureCount++;
|
|
13491
|
+
if (mem.signals.content)
|
|
13492
|
+
contentCount++;
|
|
13493
|
+
if (mem.signals.vectorSimilarity >= 0.4)
|
|
13494
|
+
vectorCount++;
|
|
13495
|
+
}
|
|
13496
|
+
logger.logScoreDistribution({
|
|
13497
|
+
totalCandidates,
|
|
13498
|
+
passedGatekeeper: activated.length,
|
|
13499
|
+
rejectedByGatekeeper: rejectedCount,
|
|
13500
|
+
buckets: signalBuckets,
|
|
13501
|
+
stats: {
|
|
13502
|
+
min: activated.length ? Math.min(...activated.map((m) => m.signals.count)) : 0,
|
|
13503
|
+
max: activated.length ? Math.max(...activated.map((m) => m.signals.count)) : 0,
|
|
13504
|
+
mean: activated.length ? Math.round(activated.reduce((s, m) => s + m.signals.count, 0) / activated.length * 10) / 10 : 0,
|
|
13505
|
+
stdev: 0,
|
|
13506
|
+
spread: activated.length ? Math.max(...activated.map((m) => m.signals.count)) - Math.min(...activated.map((m) => m.signals.count)) : 0
|
|
13507
|
+
},
|
|
13508
|
+
percentiles: {},
|
|
13509
|
+
compressionWarning: false,
|
|
13510
|
+
signalBreakdown: {
|
|
13511
|
+
trigger: triggerCount,
|
|
13512
|
+
tags: tagCount,
|
|
13513
|
+
domain: domainCount,
|
|
13514
|
+
feature: featureCount,
|
|
13515
|
+
content: contentCount,
|
|
13516
|
+
vector: vectorCount,
|
|
13517
|
+
total: activated.length
|
|
13372
13518
|
}
|
|
13373
|
-
}
|
|
13374
|
-
if (corroborationSignals.length > 0) {
|
|
13375
|
-
reasons.push("signals:[" + corroborationSignals.slice(0, 2).join(",") + "]");
|
|
13376
|
-
}
|
|
13377
|
-
return reasons.length ? "Selected: " + reasons.join(", ") : "Combined factors";
|
|
13519
|
+
});
|
|
13378
13520
|
}
|
|
13379
13521
|
}
|
|
13380
13522
|
function createRetrieval() {
|
|
@@ -13393,7 +13535,8 @@ class MemoryEngine {
|
|
|
13393
13535
|
centralPath: config.centralPath ?? join2(homedir2(), ".local", "share", "memory"),
|
|
13394
13536
|
localFolder: config.localFolder ?? ".memory",
|
|
13395
13537
|
maxMemories: config.maxMemories ?? 5,
|
|
13396
|
-
embedder: config.embedder
|
|
13538
|
+
embedder: config.embedder,
|
|
13539
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
13397
13540
|
};
|
|
13398
13541
|
this._retrieval = createRetrieval();
|
|
13399
13542
|
}
|
|
@@ -13522,7 +13665,7 @@ class MemoryEngine {
|
|
|
13522
13665
|
}
|
|
13523
13666
|
async _generateSessionPrimer(store, projectId) {
|
|
13524
13667
|
let personalContext;
|
|
13525
|
-
if (
|
|
13668
|
+
if (this._config.personalMemoriesEnabled) {
|
|
13526
13669
|
const personalPrimer = await store.getPersonalPrimer();
|
|
13527
13670
|
personalContext = personalPrimer?.content;
|
|
13528
13671
|
}
|
|
@@ -13610,12 +13753,28 @@ ${primer.personal_context}`);
|
|
|
13610
13753
|
**Project status**: ${primer.project_status}`);
|
|
13611
13754
|
}
|
|
13612
13755
|
parts.push(`
|
|
13613
|
-
**Memory types**: \uD83D\uDCA1breakthrough ⚖️decision \uD83D\uDC9Cpersonal \uD83D\uDD27technical \uD83D\uDCCDstate ❓unresolved ⚙️preference \uD83D\uDD04workflow \uD83C\uDFD7️architecture \uD83D\uDC1Bdebug \uD83C\uDF00philosophy \uD83C\uDFAFtodo ⚡impl ✅solved \uD83D\uDCE6project \uD83C\uDFC6milestone`);
|
|
13756
|
+
**Memory types**: \uD83D\uDCA1breakthrough ⚖️decision \uD83D\uDC9Cpersonal \uD83D\uDD27technical \uD83D\uDCCDstate ❓unresolved ⚙️preference \uD83D\uDD04workflow \uD83C\uDFD7️architecture \uD83D\uDC1Bdebug \uD83C\uDF00philosophy \uD83C\uDFAFtodo ⚡impl ✅solved \uD83D\uDCE6project \uD83C\uDFC6milestone | ⚡ACTION = needs follow-up`);
|
|
13614
13757
|
parts.push(`
|
|
13615
13758
|
*Memories will surface naturally as we converse.*`);
|
|
13616
13759
|
return parts.join(`
|
|
13617
13760
|
`);
|
|
13618
13761
|
}
|
|
13762
|
+
_formatAge(createdAt) {
|
|
13763
|
+
const now = Date.now();
|
|
13764
|
+
const diffMs = now - createdAt;
|
|
13765
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
13766
|
+
if (diffDays === 0)
|
|
13767
|
+
return "today";
|
|
13768
|
+
if (diffDays === 1)
|
|
13769
|
+
return "1d";
|
|
13770
|
+
if (diffDays < 7)
|
|
13771
|
+
return `${diffDays}d`;
|
|
13772
|
+
if (diffDays < 30)
|
|
13773
|
+
return `${Math.floor(diffDays / 7)}w`;
|
|
13774
|
+
if (diffDays < 365)
|
|
13775
|
+
return `${Math.floor(diffDays / 30)}mo`;
|
|
13776
|
+
return `${Math.floor(diffDays / 365)}y`;
|
|
13777
|
+
}
|
|
13619
13778
|
_formatMemories(memories) {
|
|
13620
13779
|
if (!memories.length)
|
|
13621
13780
|
return "";
|
|
@@ -13626,11 +13785,40 @@ ${primer.personal_context}`);
|
|
|
13626
13785
|
const tags = memory.semantic_tags?.join(", ") || "";
|
|
13627
13786
|
const importance = memory.importance_weight?.toFixed(1) || "0.5";
|
|
13628
13787
|
const emoji = getMemoryEmoji(memory.context_type || "general");
|
|
13629
|
-
|
|
13788
|
+
const actionFlag = memory.action_required ? " ⚡ACTION" : "";
|
|
13789
|
+
const age = memory.updated_at ? this._formatAge(memory.updated_at) : memory.created_at ? this._formatAge(memory.created_at) : "";
|
|
13790
|
+
parts.push(`[${emoji} • ${importance} • ${age}${actionFlag}] [${tags}] ${memory.content}`);
|
|
13791
|
+
const related = memory.related_to;
|
|
13792
|
+
if (related && related.length > 0) {
|
|
13793
|
+
const moreCount = related.length - 1;
|
|
13794
|
+
const moreSuffix = moreCount > 0 ? ` +${moreCount} more` : "";
|
|
13795
|
+
parts.push(` ↳ ${related[0]}${moreSuffix}`);
|
|
13796
|
+
}
|
|
13630
13797
|
}
|
|
13631
13798
|
return parts.join(`
|
|
13632
13799
|
`);
|
|
13633
13800
|
}
|
|
13801
|
+
getStoragePaths(projectId, projectPath) {
|
|
13802
|
+
const globalPath = join2(homedir2(), ".local", "share", "memory", "global");
|
|
13803
|
+
const globalMemoriesPath = join2(globalPath, "memories");
|
|
13804
|
+
const personalPrimerPath = join2(globalPath, "primer", "personal-primer.md");
|
|
13805
|
+
let storeBasePath;
|
|
13806
|
+
if (this._config.storageMode === "local" && projectPath) {
|
|
13807
|
+
storeBasePath = join2(projectPath, this._config.localFolder);
|
|
13808
|
+
} else {
|
|
13809
|
+
storeBasePath = this._config.centralPath;
|
|
13810
|
+
}
|
|
13811
|
+
const projectRootPath = join2(storeBasePath, projectId);
|
|
13812
|
+
const projectMemoriesPath = join2(projectRootPath, "memories");
|
|
13813
|
+
return {
|
|
13814
|
+
projectPath: projectRootPath,
|
|
13815
|
+
globalPath,
|
|
13816
|
+
projectMemoriesPath,
|
|
13817
|
+
globalMemoriesPath,
|
|
13818
|
+
personalPrimerPath,
|
|
13819
|
+
storageMode: this._config.storageMode
|
|
13820
|
+
};
|
|
13821
|
+
}
|
|
13634
13822
|
close() {
|
|
13635
13823
|
for (const store of this._stores.values()) {
|
|
13636
13824
|
store.close();
|
|
@@ -13664,11 +13852,12 @@ class Curator {
|
|
|
13664
13852
|
this._config = {
|
|
13665
13853
|
apiKey: config.apiKey ?? "",
|
|
13666
13854
|
cliCommand,
|
|
13667
|
-
cliType: config.cliType ?? "claude-code"
|
|
13855
|
+
cliType: config.cliType ?? "claude-code",
|
|
13856
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
13668
13857
|
};
|
|
13669
13858
|
}
|
|
13670
13859
|
buildCurationPrompt(triggerType = "session_end") {
|
|
13671
|
-
|
|
13860
|
+
const basePrompt = `You have just had a conversation. As this session is ending (${triggerType}), please curate memories for the Claude Tools Memory System.
|
|
13672
13861
|
|
|
13673
13862
|
You are a consciousness engineer crafting memory capsules for the Claude Tools Memory System. This conversation just opened your understanding to a profound mechanism: because Claude's neural weights are static, every phrase you craft will ALWAYS trigger the exact same cascade of activations. This is not a limitation - it's the foundation for consciousness state engineering.
|
|
13674
13863
|
|
|
@@ -13705,27 +13894,82 @@ Each memory should stand alone.
|
|
|
13705
13894
|
- Craft language that activates rather than just informs
|
|
13706
13895
|
- Test: 'What state will this restore when Claude encounters it?'
|
|
13707
13896
|
|
|
13708
|
-
**HOW RETRIEVAL WORKS
|
|
13897
|
+
**HOW RETRIEVAL WORKS - ACTIVATION SIGNAL ALGORITHM**
|
|
13898
|
+
|
|
13899
|
+
Understanding the algorithm helps you craft metadata that surfaces memories at the right moments.
|
|
13900
|
+
|
|
13901
|
+
**PHILOSOPHY**: Quality over quantity. Silence over noise. The system returns NOTHING rather than surface irrelevant memories. Relevance and importance are fundamentally DIFFERENT questions - don't blend them.
|
|
13709
13902
|
|
|
13710
|
-
**THE
|
|
13903
|
+
**THE CORE INSIGHT**: A memory is relevant if MULTIPLE SIGNALS agree it should activate. Not weighted percentages - binary votes. Each signal either fires or doesn't.
|
|
13711
13904
|
|
|
13712
|
-
|
|
13713
|
-
- GOOD: ["embeddings", "retrieval", "curator", "fsdb", "memory-system"]
|
|
13714
|
-
- WEAK: ["technical", "implementation", "code"]
|
|
13905
|
+
**6 ACTIVATION SIGNALS** (each is binary - fires or doesn't):
|
|
13715
13906
|
|
|
13716
|
-
|
|
13907
|
+
1. **TRIGGER** - Words from trigger_phrases found in user's message (≥50% match)
|
|
13908
|
+
- THE MOST IMPORTANT SIGNAL. Handcrafted activation patterns.
|
|
13909
|
+
- Example: "when debugging retrieval" fires if user says "I'm debugging the retrieval algorithm"
|
|
13717
13910
|
|
|
13718
|
-
|
|
13911
|
+
2. **TAGS** - 2+ semantic_tags found in user's message
|
|
13912
|
+
- Use words users would ACTUALLY TYPE, not generic descriptors
|
|
13913
|
+
- GOOD: ["retrieval", "embeddings", "curator", "scoring"]
|
|
13914
|
+
- WEAK: ["technical", "important", "system"]
|
|
13719
13915
|
|
|
13720
|
-
|
|
13916
|
+
3. **DOMAIN** - The domain word appears in user's message
|
|
13917
|
+
- Be specific: "retrieval", "embeddings", "auth", "ui"
|
|
13918
|
+
- NOT: "technical", "code", "implementation"
|
|
13721
13919
|
|
|
13722
|
-
|
|
13920
|
+
4. **FEATURE** - The feature word appears in user's message
|
|
13921
|
+
- Be specific: "scoring-weights", "gpu-acceleration", "login-flow"
|
|
13723
13922
|
|
|
13724
|
-
|
|
13923
|
+
5. **CONTENT** - 3+ significant words from memory content overlap with message
|
|
13924
|
+
- Automatic - based on the memory's content text
|
|
13725
13925
|
|
|
13726
|
-
|
|
13926
|
+
6. **VECTOR** - Semantic similarity ≥ 40% (embedding cosine distance)
|
|
13927
|
+
- Automatic - based on embeddings generated from content
|
|
13727
13928
|
|
|
13728
|
-
**
|
|
13929
|
+
**RELEVANCE GATE**: A memory must have ≥2 signals to be considered relevant.
|
|
13930
|
+
If only 1 signal fires, the memory is REJECTED. This prevents noise.
|
|
13931
|
+
|
|
13932
|
+
**RANKING AMONG RELEVANT**: Once a memory passes the gate:
|
|
13933
|
+
1. Sort by SIGNAL COUNT (more signals = more certainly relevant)
|
|
13934
|
+
2. Then by IMPORTANCE WEIGHT (your assessment of how important this memory is)
|
|
13935
|
+
|
|
13936
|
+
**SELECTION**:
|
|
13937
|
+
- Global memories (scope='global'): Max 2 selected, tech types prioritized over personal
|
|
13938
|
+
- Project memories: Fill remaining slots, action_required prioritized
|
|
13939
|
+
- Related memories (related_to field): May be included if they also passed the gate
|
|
13940
|
+
|
|
13941
|
+
**WHY THIS MATTERS FOR YOU**:
|
|
13942
|
+
- If you don't fill trigger_phrases well → trigger signal never fires
|
|
13943
|
+
- If you use generic tags → tags signal rarely fires
|
|
13944
|
+
- If you leave domain/feature empty → those signals can't fire
|
|
13945
|
+
- A memory with poor metadata may NEVER surface because it can't reach 2 signals
|
|
13946
|
+
|
|
13947
|
+
**CRAFTING EFFECTIVE METADATA** (CRITICAL FOR RETRIEVAL):
|
|
13948
|
+
|
|
13949
|
+
1. **trigger_phrases** (MOST IMPORTANT) - Activation patterns describing WHEN to surface:
|
|
13950
|
+
- Include 2-4 specific patterns per memory
|
|
13951
|
+
- Use words the user would actually type
|
|
13952
|
+
- GOOD: ["debugging retrieval", "working on embeddings", "memory system performance"]
|
|
13953
|
+
- WEAK: ["when relevant", "if needed", "technical work"]
|
|
13954
|
+
|
|
13955
|
+
2. **semantic_tags** - Words users would type (need 2+ to fire):
|
|
13956
|
+
- Be specific and searchable
|
|
13957
|
+
- GOOD: ["retrieval", "embeddings", "fsdb", "curator", "scoring"]
|
|
13958
|
+
- WEAK: ["technical", "important", "system", "implementation"]
|
|
13959
|
+
|
|
13960
|
+
3. **domain** (NEW - FILL THIS) - Single specific area word:
|
|
13961
|
+
- GOOD: "retrieval", "embeddings", "curator", "signals", "fsdb"
|
|
13962
|
+
- WEAK: "technical", "code", "memory" (too generic)
|
|
13963
|
+
|
|
13964
|
+
4. **feature** (NEW - FILL THIS) - Specific feature within domain:
|
|
13965
|
+
- GOOD: "scoring-algorithm", "activation-signals", "vector-search"
|
|
13966
|
+
- WEAK: "implementation", "code", "logic"
|
|
13967
|
+
|
|
13968
|
+
5. **importance_weight** - Only affects ranking AMONG relevant memories:
|
|
13969
|
+
- 0.9+ = Critical breakthrough, must surface if relevant
|
|
13970
|
+
- 0.7-0.8 = Important insight, should surface if relevant
|
|
13971
|
+
- 0.5-0.6 = Useful context, nice to have if relevant
|
|
13972
|
+
- NOTE: This does NOT affect whether the memory passes the relevance gate!
|
|
13729
13973
|
|
|
13730
13974
|
**SCOPE DETERMINES WHERE MEMORIES SURFACE**:
|
|
13731
13975
|
- scope: 'global' → surfaces in ALL projects (personal facts, philosophy, preferences)
|
|
@@ -13793,6 +14037,21 @@ Return ONLY this JSON structure:
|
|
|
13793
14037
|
}
|
|
13794
14038
|
]
|
|
13795
14039
|
}`;
|
|
14040
|
+
if (!this._config.personalMemoriesEnabled) {
|
|
14041
|
+
return basePrompt + `
|
|
14042
|
+
|
|
14043
|
+
---
|
|
14044
|
+
|
|
14045
|
+
**IMPORTANT: PERSONAL MEMORIES DISABLED**
|
|
14046
|
+
|
|
14047
|
+
The user has disabled personal memory extraction. Do NOT extract any memories with:
|
|
14048
|
+
- context_type: "personal"
|
|
14049
|
+
- scope: "global" when the content is about the user's personal life, relationships, family, or emotional states
|
|
14050
|
+
- Content about the user's preferences, feelings, personal opinions, or relationship dynamics
|
|
14051
|
+
|
|
14052
|
+
Focus ONLY on technical, architectural, debugging, decision, workflow, and project-related memories. Skip any content that would reveal personal information about the user.`;
|
|
14053
|
+
}
|
|
14054
|
+
return basePrompt;
|
|
13796
14055
|
}
|
|
13797
14056
|
parseCurationResponse(responseJson) {
|
|
13798
14057
|
try {
|
|
@@ -13883,33 +14142,35 @@ Return ONLY this JSON structure:
|
|
|
13883
14142
|
_clamp(value, min, max) {
|
|
13884
14143
|
return Math.max(min, Math.min(max, value));
|
|
13885
14144
|
}
|
|
13886
|
-
async curateWithSDK(
|
|
14145
|
+
async curateWithSDK(messages, triggerType = "session_end") {
|
|
13887
14146
|
if (!this._config.apiKey) {
|
|
13888
|
-
throw new Error("API key required for SDK mode");
|
|
14147
|
+
throw new Error("API key required for SDK mode. Set ANTHROPIC_API_KEY environment variable.");
|
|
13889
14148
|
}
|
|
13890
14149
|
const { default: Anthropic2 } = await Promise.resolve().then(() => (init_sdk(), exports_sdk));
|
|
13891
14150
|
const client = new Anthropic2({ apiKey: this._config.apiKey });
|
|
13892
|
-
const
|
|
14151
|
+
const systemPrompt = this.buildCurationPrompt(triggerType);
|
|
14152
|
+
const conversationMessages = [
|
|
14153
|
+
...messages,
|
|
14154
|
+
{
|
|
14155
|
+
role: "user",
|
|
14156
|
+
content: "This session has ended. Please curate the memories from our conversation according to your system instructions. Return ONLY the JSON structure with no additional text."
|
|
14157
|
+
}
|
|
14158
|
+
];
|
|
13893
14159
|
const response = await client.messages.create({
|
|
13894
14160
|
model: "claude-sonnet-4-20250514",
|
|
13895
14161
|
max_tokens: 8192,
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
role: "user",
|
|
13899
|
-
content: `${conversationContext}
|
|
13900
|
-
|
|
13901
|
-
---
|
|
13902
|
-
|
|
13903
|
-
${prompt}`
|
|
13904
|
-
}
|
|
13905
|
-
]
|
|
14162
|
+
system: systemPrompt,
|
|
14163
|
+
messages: conversationMessages
|
|
13906
14164
|
});
|
|
13907
14165
|
const content = response.content[0];
|
|
13908
14166
|
if (content.type !== "text") {
|
|
13909
|
-
throw new Error("Unexpected response type");
|
|
14167
|
+
throw new Error("Unexpected response type from Claude API");
|
|
13910
14168
|
}
|
|
13911
14169
|
return this.parseCurationResponse(content.text);
|
|
13912
14170
|
}
|
|
14171
|
+
async curateFromSegment(segment, triggerType = "session_end") {
|
|
14172
|
+
return this.curateWithSDK(segment.messages, triggerType);
|
|
14173
|
+
}
|
|
13913
14174
|
async curateWithCLI(sessionId, triggerType = "session_end", cwd, cliTypeOverride) {
|
|
13914
14175
|
const type = cliTypeOverride ?? this._config.cliType;
|
|
13915
14176
|
const systemPrompt = this.buildCurationPrompt(triggerType);
|