@rlabs-inc/memory 0.3.7 → 0.3.8
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/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 +28 -12
- package/src/core/engine.ts +3 -2
- 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.js
CHANGED
|
@@ -12129,6 +12129,11 @@ var managementLogSchema = {
|
|
|
12129
12129
|
error: "string",
|
|
12130
12130
|
details: "string"
|
|
12131
12131
|
};
|
|
12132
|
+
var personalPrimerSchema = {
|
|
12133
|
+
content: "string",
|
|
12134
|
+
session_updated: "number",
|
|
12135
|
+
updated_by: "string"
|
|
12136
|
+
};
|
|
12132
12137
|
|
|
12133
12138
|
// src/core/store.ts
|
|
12134
12139
|
var PERSONAL_PRIMER_ID = "personal-primer";
|
|
@@ -12150,7 +12155,7 @@ class MemoryStore {
|
|
|
12150
12155
|
return this._global;
|
|
12151
12156
|
}
|
|
12152
12157
|
const globalPath = this._config.globalPath;
|
|
12153
|
-
console.log(`\uD83C\uDF10 [DEBUG]
|
|
12158
|
+
console.log(`\uD83C\uDF10 [DEBUG] Initializing global database at ${globalPath}`);
|
|
12154
12159
|
const db = createDatabase({
|
|
12155
12160
|
name: "global",
|
|
12156
12161
|
basePath: globalPath
|
|
@@ -12167,8 +12172,14 @@ class MemoryStore {
|
|
|
12167
12172
|
autoSave: true,
|
|
12168
12173
|
watchFiles: this._config.watchFiles
|
|
12169
12174
|
});
|
|
12170
|
-
|
|
12171
|
-
|
|
12175
|
+
const primer = db.collection("primer", {
|
|
12176
|
+
schema: personalPrimerSchema,
|
|
12177
|
+
contentColumn: "content",
|
|
12178
|
+
autoSave: true,
|
|
12179
|
+
watchFiles: this._config.watchFiles
|
|
12180
|
+
});
|
|
12181
|
+
await Promise.all([memories.load(), managementLogs.load(), primer.load()]);
|
|
12182
|
+
this._global = { db, memories, managementLogs, primer };
|
|
12172
12183
|
return this._global;
|
|
12173
12184
|
}
|
|
12174
12185
|
async getGlobalMemories() {
|
|
@@ -12245,40 +12256,31 @@ class MemoryStore {
|
|
|
12245
12256
|
return id;
|
|
12246
12257
|
}
|
|
12247
12258
|
async getPersonalPrimer() {
|
|
12248
|
-
const {
|
|
12249
|
-
const
|
|
12250
|
-
if (!
|
|
12259
|
+
const { primer } = await this.getGlobal();
|
|
12260
|
+
const record = primer.get(PERSONAL_PRIMER_ID);
|
|
12261
|
+
if (!record) {
|
|
12251
12262
|
return null;
|
|
12252
12263
|
}
|
|
12253
12264
|
return {
|
|
12254
|
-
content:
|
|
12255
|
-
updated:
|
|
12265
|
+
content: record.content,
|
|
12266
|
+
updated: record.updated
|
|
12256
12267
|
};
|
|
12257
12268
|
}
|
|
12258
|
-
async setPersonalPrimer(content) {
|
|
12259
|
-
const {
|
|
12260
|
-
const existing =
|
|
12269
|
+
async setPersonalPrimer(content, sessionNumber, updatedBy = "user") {
|
|
12270
|
+
const { primer } = await this.getGlobal();
|
|
12271
|
+
const existing = primer.get(PERSONAL_PRIMER_ID);
|
|
12261
12272
|
if (existing) {
|
|
12262
|
-
|
|
12273
|
+
primer.update(PERSONAL_PRIMER_ID, {
|
|
12274
|
+
content,
|
|
12275
|
+
session_updated: sessionNumber ?? existing.session_updated,
|
|
12276
|
+
updated_by: updatedBy
|
|
12277
|
+
});
|
|
12263
12278
|
} else {
|
|
12264
|
-
|
|
12279
|
+
primer.insert({
|
|
12265
12280
|
id: PERSONAL_PRIMER_ID,
|
|
12266
12281
|
content,
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
confidence_score: 1,
|
|
12270
|
-
context_type: "personal",
|
|
12271
|
-
temporal_relevance: "persistent",
|
|
12272
|
-
knowledge_domain: "personal",
|
|
12273
|
-
emotional_resonance: "neutral",
|
|
12274
|
-
action_required: false,
|
|
12275
|
-
problem_solution_pair: false,
|
|
12276
|
-
semantic_tags: ["personal", "primer", "relationship"],
|
|
12277
|
-
trigger_phrases: [],
|
|
12278
|
-
question_types: [],
|
|
12279
|
-
session_id: "system",
|
|
12280
|
-
project_id: "global",
|
|
12281
|
-
embedding: null
|
|
12282
|
+
session_updated: sessionNumber ?? 0,
|
|
12283
|
+
updated_by: updatedBy
|
|
12282
12284
|
});
|
|
12283
12285
|
}
|
|
12284
12286
|
}
|
|
@@ -12298,6 +12300,7 @@ class MemoryStore {
|
|
|
12298
12300
|
success: entry.success,
|
|
12299
12301
|
duration_ms: entry.durationMs,
|
|
12300
12302
|
summary: entry.summary,
|
|
12303
|
+
full_report: entry.fullReport ?? "",
|
|
12301
12304
|
error: entry.error ?? "",
|
|
12302
12305
|
details: entry.details ? JSON.stringify(entry.details) : ""
|
|
12303
12306
|
});
|
|
@@ -12661,7 +12664,14 @@ var sym = {
|
|
|
12661
12664
|
fire: "\uD83D\uDD25",
|
|
12662
12665
|
target: "\uD83C\uDFAF"
|
|
12663
12666
|
};
|
|
12667
|
+
var _verbose = false;
|
|
12664
12668
|
var logger = {
|
|
12669
|
+
setVerbose(enabled) {
|
|
12670
|
+
_verbose = enabled;
|
|
12671
|
+
},
|
|
12672
|
+
isVerbose() {
|
|
12673
|
+
return _verbose;
|
|
12674
|
+
},
|
|
12665
12675
|
info(message) {
|
|
12666
12676
|
console.log(`${timestamp()} ${style("cyan", sym.info)} ${message}`);
|
|
12667
12677
|
},
|
|
@@ -12733,7 +12743,14 @@ var logger = {
|
|
|
12733
12743
|
problem_solution: "✅",
|
|
12734
12744
|
project_context: "\uD83D\uDCE6",
|
|
12735
12745
|
milestone: "\uD83C\uDFC6",
|
|
12736
|
-
general: "\uD83D\uDCDD"
|
|
12746
|
+
general: "\uD83D\uDCDD",
|
|
12747
|
+
project_state: "\uD83D\uDCCD",
|
|
12748
|
+
pending_task: "⏳",
|
|
12749
|
+
work_in_progress: "\uD83D\uDD28",
|
|
12750
|
+
system_feedback: "\uD83D\uDCE3",
|
|
12751
|
+
project_milestone: "\uD83C\uDFC6",
|
|
12752
|
+
architectural_insight: "\uD83C\uDFDB️",
|
|
12753
|
+
architectural_direction: "\uD83E\uDDED"
|
|
12737
12754
|
};
|
|
12738
12755
|
console.log();
|
|
12739
12756
|
console.log(`${timestamp()} ${style("cyan", sym.sparkles)} ${style("bold", `SURFACING ${memories.length} MEMORIES`)}`);
|
|
@@ -12745,11 +12762,12 @@ var logger = {
|
|
|
12745
12762
|
return;
|
|
12746
12763
|
}
|
|
12747
12764
|
memories.forEach((m, i) => {
|
|
12748
|
-
const
|
|
12765
|
+
const signalCount = Math.round(m.score * 6);
|
|
12766
|
+
const signalStr = style("green", `${signalCount}sig`);
|
|
12749
12767
|
const emoji = emojiMap[m.context_type?.toLowerCase()] ?? "\uD83D\uDCDD";
|
|
12750
12768
|
const num = style("dim", `${i + 1}.`);
|
|
12751
12769
|
const preview = m.content.length > 55 ? m.content.slice(0, 55) + style("dim", "...") : m.content;
|
|
12752
|
-
console.log(` ${num} [${
|
|
12770
|
+
console.log(` ${num} [${signalStr}] ${emoji}`);
|
|
12753
12771
|
console.log(` ${preview}`);
|
|
12754
12772
|
});
|
|
12755
12773
|
console.log();
|
|
@@ -12797,44 +12815,124 @@ var logger = {
|
|
|
12797
12815
|
console.log(` ${style("dim", "processing:")} ${memoriesCount} new memories`);
|
|
12798
12816
|
},
|
|
12799
12817
|
logManagementComplete(result) {
|
|
12818
|
+
const formatAction = (action, truncate = true) => {
|
|
12819
|
+
let icon = " •";
|
|
12820
|
+
if (action.startsWith("READ OK"))
|
|
12821
|
+
icon = style("dim", " \uD83D\uDCD6");
|
|
12822
|
+
else if (action.startsWith("READ FAILED"))
|
|
12823
|
+
icon = style("red", " ❌");
|
|
12824
|
+
else if (action.startsWith("WRITE OK"))
|
|
12825
|
+
icon = style("green", " ✏️");
|
|
12826
|
+
else if (action.startsWith("WRITE FAILED"))
|
|
12827
|
+
icon = style("red", " ❌");
|
|
12828
|
+
else if (action.startsWith("RECEIVED"))
|
|
12829
|
+
icon = style("cyan", " \uD83D\uDCE5");
|
|
12830
|
+
else if (action.startsWith("CREATED"))
|
|
12831
|
+
icon = style("green", " ✨");
|
|
12832
|
+
else if (action.startsWith("UPDATED"))
|
|
12833
|
+
icon = style("blue", " \uD83D\uDCDD");
|
|
12834
|
+
else if (action.startsWith("SUPERSEDED"))
|
|
12835
|
+
icon = style("yellow", " \uD83D\uDD04");
|
|
12836
|
+
else if (action.startsWith("RESOLVED"))
|
|
12837
|
+
icon = style("green", " ✅");
|
|
12838
|
+
else if (action.startsWith("LINKED"))
|
|
12839
|
+
icon = style("cyan", " \uD83D\uDD17");
|
|
12840
|
+
else if (action.startsWith("PRIMER"))
|
|
12841
|
+
icon = style("magenta", " \uD83D\uDC9C");
|
|
12842
|
+
else if (action.startsWith("SKIPPED"))
|
|
12843
|
+
icon = style("dim", " ⏭️");
|
|
12844
|
+
else if (action.startsWith("NO_ACTION"))
|
|
12845
|
+
icon = style("dim", " ◦");
|
|
12846
|
+
const text = truncate && action.length > 70 ? action.slice(0, 67) + "..." : action;
|
|
12847
|
+
return `${icon} ${style("dim", text)}`;
|
|
12848
|
+
};
|
|
12800
12849
|
if (result.success) {
|
|
12801
12850
|
console.log(` ${style("green", sym.check)} ${style("bold", "Completed")}`);
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
|
|
12808
|
-
|
|
12809
|
-
|
|
12810
|
-
|
|
12811
|
-
|
|
12812
|
-
|
|
12813
|
-
|
|
12814
|
-
|
|
12815
|
-
|
|
12816
|
-
|
|
12851
|
+
if (_verbose) {
|
|
12852
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12853
|
+
console.log(` ${style("cyan", "\uD83D\uDCCA")} ${style("bold", "Statistics")}`);
|
|
12854
|
+
const filesRead = result.filesRead ?? 0;
|
|
12855
|
+
const filesWritten = result.filesWritten ?? 0;
|
|
12856
|
+
console.log(` ${style("dim", "Files read:")} ${filesRead > 0 ? style("green", String(filesRead)) : style("dim", "0")}`);
|
|
12857
|
+
console.log(` ${style("dim", "Files written:")} ${filesWritten > 0 ? style("green", String(filesWritten)) : style("dim", "0")}`);
|
|
12858
|
+
const superseded = result.superseded ?? 0;
|
|
12859
|
+
const resolved = result.resolved ?? 0;
|
|
12860
|
+
const linked = result.linked ?? 0;
|
|
12861
|
+
console.log(` ${style("dim", "Superseded:")} ${superseded > 0 ? style("yellow", String(superseded)) : style("dim", "0")}`);
|
|
12862
|
+
console.log(` ${style("dim", "Resolved:")} ${resolved > 0 ? style("green", String(resolved)) : style("dim", "0")}`);
|
|
12863
|
+
console.log(` ${style("dim", "Linked:")} ${linked > 0 ? style("cyan", String(linked)) : style("dim", "0")}`);
|
|
12864
|
+
console.log(` ${style("dim", "Primer:")} ${result.primerUpdated ? style("magenta", "updated") : style("dim", "unchanged")}`);
|
|
12865
|
+
if (result.actions && result.actions.length > 0) {
|
|
12866
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12867
|
+
console.log(` ${style("cyan", "\uD83C\uDFAC")} ${style("bold", "Actions")} ${style("dim", `(${result.actions.length} total)`)}`);
|
|
12868
|
+
for (const action of result.actions) {
|
|
12869
|
+
console.log(` ${formatAction(action, false)}`);
|
|
12870
|
+
}
|
|
12871
|
+
}
|
|
12872
|
+
if (result.fullReport) {
|
|
12873
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12874
|
+
console.log(` ${style("cyan", "\uD83D\uDCCB")} ${style("bold", "Full Report")}`);
|
|
12875
|
+
const reportLines = result.fullReport.split(`
|
|
12876
|
+
`);
|
|
12877
|
+
for (const line of reportLines) {
|
|
12878
|
+
if (line.includes("===")) {
|
|
12879
|
+
console.log(` ${style("bold", line)}`);
|
|
12880
|
+
} else if (line.match(/^[A-Z_]+:/)) {
|
|
12881
|
+
console.log(` ${style("cyan", line)}`);
|
|
12882
|
+
} else {
|
|
12883
|
+
console.log(` ${style("dim", line)}`);
|
|
12884
|
+
}
|
|
12885
|
+
}
|
|
12886
|
+
}
|
|
12887
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12817
12888
|
} else {
|
|
12818
|
-
|
|
12819
|
-
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12889
|
+
const stats = [];
|
|
12890
|
+
if (result.superseded && result.superseded > 0)
|
|
12891
|
+
stats.push(`${result.superseded} superseded`);
|
|
12892
|
+
if (result.resolved && result.resolved > 0)
|
|
12893
|
+
stats.push(`${result.resolved} resolved`);
|
|
12894
|
+
if (result.linked && result.linked > 0)
|
|
12895
|
+
stats.push(`${result.linked} linked`);
|
|
12896
|
+
if (result.primerUpdated)
|
|
12897
|
+
stats.push("primer updated");
|
|
12898
|
+
if (stats.length > 0) {
|
|
12899
|
+
console.log(` ${style("dim", "changes:")} ${stats.join(style("dim", ", "))}`);
|
|
12900
|
+
} else {
|
|
12901
|
+
console.log(` ${style("dim", "changes:")} none (memories are current)`);
|
|
12902
|
+
}
|
|
12903
|
+
if (result.actions && result.actions.length > 0) {
|
|
12904
|
+
console.log(` ${style("dim", "actions:")}`);
|
|
12905
|
+
for (const action of result.actions.slice(0, 10)) {
|
|
12906
|
+
console.log(` ${formatAction(action, true)}`);
|
|
12907
|
+
}
|
|
12908
|
+
if (result.actions.length > 10) {
|
|
12909
|
+
console.log(` ${style("dim", ` ... and ${result.actions.length - 10} more actions`)}`);
|
|
12910
|
+
}
|
|
12911
|
+
}
|
|
12823
12912
|
}
|
|
12824
12913
|
} else {
|
|
12825
12914
|
console.log(` ${style("yellow", sym.warning)} ${style("bold", "Failed")}`);
|
|
12826
12915
|
if (result.error) {
|
|
12827
|
-
console.log(` ${style("
|
|
12916
|
+
console.log(` ${style("red", "error:")} ${result.error}`);
|
|
12917
|
+
}
|
|
12918
|
+
if (result.fullReport) {
|
|
12919
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
12920
|
+
console.log(` ${style("red", "\uD83D\uDCCB")} ${style("bold", "Error Report:")}`);
|
|
12921
|
+
const reportLines = result.fullReport.split(`
|
|
12922
|
+
`);
|
|
12923
|
+
for (const line of reportLines) {
|
|
12924
|
+
console.log(` ${style("dim", line)}`);
|
|
12925
|
+
}
|
|
12828
12926
|
}
|
|
12829
12927
|
}
|
|
12830
12928
|
console.log();
|
|
12831
12929
|
},
|
|
12832
12930
|
logRetrievalScoring(params) {
|
|
12833
|
-
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, selectedMemories } = params;
|
|
12931
|
+
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, durationMs, selectedMemories } = params;
|
|
12932
|
+
const timeStr = durationMs !== undefined ? style("cyan", `${durationMs.toFixed(1)}ms`) : "";
|
|
12834
12933
|
console.log();
|
|
12835
|
-
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "
|
|
12836
|
-
console.log(` ${style("dim", "total:")} ${totalMemories}
|
|
12837
|
-
console.log(` ${style("dim", "pre-filtered:")} ${preFiltered} (inactive/excluded/scope)`);
|
|
12934
|
+
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "RETRIEVAL")} ${timeStr}`);
|
|
12935
|
+
console.log(` ${style("dim", "total:")} ${totalMemories} → ${style("dim", "filtered:")} ${preFiltered} → ${style("dim", "candidates:")} ${totalMemories - preFiltered}`);
|
|
12838
12936
|
console.log(` ${style("dim", "already injected:")} ${alreadyInjected}`);
|
|
12839
12937
|
const msgPreview = currentMessage.length > 60 ? currentMessage.slice(0, 60) + "..." : currentMessage;
|
|
12840
12938
|
console.log(` ${style("dim", "message:")} "${msgPreview}"`);
|
|
@@ -12853,347 +12951,471 @@ var logger = {
|
|
|
12853
12951
|
console.log();
|
|
12854
12952
|
selectedMemories.forEach((m, i) => {
|
|
12855
12953
|
const num = style("dim", `${i + 1}.`);
|
|
12856
|
-
const
|
|
12857
|
-
const
|
|
12858
|
-
const corr = style("magenta", `corr:${(m.corroboration_score * 100).toFixed(0)}%`);
|
|
12954
|
+
const signalsStr = style("green", `${m.signalCount} signals`);
|
|
12955
|
+
const imp = style("magenta", `imp:${(m.importance_weight * 100).toFixed(0)}%`);
|
|
12859
12956
|
const type = style("yellow", m.context_type.toUpperCase());
|
|
12860
|
-
const scope = m.isGlobal ? style("blue", "[G]") : "";
|
|
12861
|
-
console.log(` ${num} [${
|
|
12957
|
+
const scope = m.isGlobal ? style("blue", " [G]") : "";
|
|
12958
|
+
console.log(` ${num} [${signalsStr} • ${imp}] ${type}${scope}`);
|
|
12862
12959
|
const preview = m.content.length > 60 ? m.content.slice(0, 60) + style("dim", "...") : m.content;
|
|
12863
12960
|
console.log(` ${style("white", preview)}`);
|
|
12864
|
-
const
|
|
12865
|
-
if (
|
|
12866
|
-
|
|
12961
|
+
const firedSignals = [];
|
|
12962
|
+
if (m.signals.trigger) {
|
|
12963
|
+
firedSignals.push(`trigger:${(m.signals.triggerStrength * 100).toFixed(0)}%`);
|
|
12964
|
+
}
|
|
12965
|
+
if (m.signals.tags) {
|
|
12966
|
+
firedSignals.push(`tags:${m.signals.tagCount}`);
|
|
12967
|
+
}
|
|
12968
|
+
if (m.signals.domain)
|
|
12969
|
+
firedSignals.push("domain");
|
|
12970
|
+
if (m.signals.feature)
|
|
12971
|
+
firedSignals.push("feature");
|
|
12972
|
+
if (m.signals.content)
|
|
12973
|
+
firedSignals.push("content");
|
|
12974
|
+
if (m.signals.vector) {
|
|
12975
|
+
firedSignals.push(`vector:${(m.signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
12867
12976
|
}
|
|
12868
|
-
if (
|
|
12869
|
-
console.log(` ${style("
|
|
12977
|
+
if (firedSignals.length > 0) {
|
|
12978
|
+
console.log(` ${style("cyan", "signals:")} ${firedSignals.join(", ")}`);
|
|
12870
12979
|
}
|
|
12871
12980
|
console.log();
|
|
12872
12981
|
});
|
|
12982
|
+
},
|
|
12983
|
+
logScoreDistribution(params) {
|
|
12984
|
+
const { totalCandidates, passedGatekeeper, rejectedByGatekeeper, buckets, stats, signalBreakdown } = params;
|
|
12985
|
+
console.log();
|
|
12986
|
+
console.log(style("dim", " ─".repeat(30)));
|
|
12987
|
+
console.log(` ${style("bold", "ACTIVATION SIGNALS")}`);
|
|
12988
|
+
console.log();
|
|
12989
|
+
const passRate = totalCandidates > 0 ? (passedGatekeeper / totalCandidates * 100).toFixed(0) : "0";
|
|
12990
|
+
console.log(` ${style("dim", "Activated:")} ${style("green", String(passedGatekeeper))}/${totalCandidates} (${passRate}%)`);
|
|
12991
|
+
console.log(` ${style("dim", "Rejected:")} ${rejectedByGatekeeper} (< 2 signals)`);
|
|
12992
|
+
console.log();
|
|
12993
|
+
if (signalBreakdown && signalBreakdown.total > 0) {
|
|
12994
|
+
console.log(` ${style("cyan", "Signal Breakdown:")}`);
|
|
12995
|
+
const signals = [
|
|
12996
|
+
{ name: "trigger", count: signalBreakdown.trigger },
|
|
12997
|
+
{ name: "tags", count: signalBreakdown.tags },
|
|
12998
|
+
{ name: "domain", count: signalBreakdown.domain },
|
|
12999
|
+
{ name: "feature", count: signalBreakdown.feature },
|
|
13000
|
+
{ name: "content", count: signalBreakdown.content },
|
|
13001
|
+
{ name: "vector", count: signalBreakdown.vector }
|
|
13002
|
+
];
|
|
13003
|
+
for (const sig of signals) {
|
|
13004
|
+
const pct = (sig.count / signalBreakdown.total * 100).toFixed(0);
|
|
13005
|
+
const bar = "█".repeat(Math.round(sig.count / signalBreakdown.total * 20));
|
|
13006
|
+
console.log(` ${sig.name.padEnd(8)} ${bar.padEnd(20)} ${sig.count} (${pct}%)`);
|
|
13007
|
+
}
|
|
13008
|
+
console.log();
|
|
13009
|
+
}
|
|
13010
|
+
if (stats.max > 0) {
|
|
13011
|
+
console.log(` ${style("cyan", "Signals:")} min=${stats.min} max=${stats.max} mean=${stats.mean}`);
|
|
13012
|
+
console.log();
|
|
13013
|
+
}
|
|
13014
|
+
if (Object.keys(buckets).length > 0) {
|
|
13015
|
+
console.log(` ${style("bold", "Distribution:")}`);
|
|
13016
|
+
const maxBucketCount = Math.max(...Object.values(buckets), 1);
|
|
13017
|
+
const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals"];
|
|
13018
|
+
for (const bucket of bucketOrder) {
|
|
13019
|
+
const count = buckets[bucket] ?? 0;
|
|
13020
|
+
if (count > 0 || bucket === "2 signals") {
|
|
13021
|
+
const barLen = Math.round(count / maxBucketCount * 25);
|
|
13022
|
+
const bar = "█".repeat(barLen) + style("dim", "░".repeat(25 - barLen));
|
|
13023
|
+
const countStr = count.toString().padStart(3);
|
|
13024
|
+
console.log(` ${style("dim", bucket.padEnd(10))} ${bar} ${style("cyan", countStr)}`);
|
|
13025
|
+
}
|
|
13026
|
+
}
|
|
13027
|
+
console.log();
|
|
13028
|
+
}
|
|
12873
13029
|
}
|
|
12874
13030
|
};
|
|
12875
13031
|
|
|
12876
13032
|
// src/core/retrieval.ts
|
|
12877
|
-
var
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
technical: ["implement", "code", "function", "class", "module", "api", "interface"]
|
|
12887
|
-
};
|
|
12888
|
-
var TEMPORAL_CLASS_SCORES = {
|
|
12889
|
-
eternal: 1,
|
|
12890
|
-
long_term: 0.9,
|
|
12891
|
-
medium_term: 0.7,
|
|
12892
|
-
short_term: 0.5,
|
|
12893
|
-
ephemeral: 0.3
|
|
13033
|
+
var GLOBAL_TYPE_PRIORITY = {
|
|
13034
|
+
technical: 1,
|
|
13035
|
+
preference: 2,
|
|
13036
|
+
architectural: 3,
|
|
13037
|
+
workflow: 4,
|
|
13038
|
+
decision: 5,
|
|
13039
|
+
breakthrough: 6,
|
|
13040
|
+
philosophy: 7,
|
|
13041
|
+
personal: 8
|
|
12894
13042
|
};
|
|
13043
|
+
var MIN_ACTIVATION_SIGNALS = 2;
|
|
13044
|
+
var STOPWORDS = new Set([
|
|
13045
|
+
"the",
|
|
13046
|
+
"is",
|
|
13047
|
+
"are",
|
|
13048
|
+
"was",
|
|
13049
|
+
"were",
|
|
13050
|
+
"to",
|
|
13051
|
+
"a",
|
|
13052
|
+
"an",
|
|
13053
|
+
"and",
|
|
13054
|
+
"or",
|
|
13055
|
+
"but",
|
|
13056
|
+
"in",
|
|
13057
|
+
"on",
|
|
13058
|
+
"at",
|
|
13059
|
+
"for",
|
|
13060
|
+
"with",
|
|
13061
|
+
"about",
|
|
13062
|
+
"when",
|
|
13063
|
+
"how",
|
|
13064
|
+
"what",
|
|
13065
|
+
"why",
|
|
13066
|
+
"where",
|
|
13067
|
+
"this",
|
|
13068
|
+
"that",
|
|
13069
|
+
"it",
|
|
13070
|
+
"of",
|
|
13071
|
+
"be",
|
|
13072
|
+
"have",
|
|
13073
|
+
"do",
|
|
13074
|
+
"does",
|
|
13075
|
+
"did",
|
|
13076
|
+
"will",
|
|
13077
|
+
"would",
|
|
13078
|
+
"could",
|
|
13079
|
+
"should",
|
|
13080
|
+
"can",
|
|
13081
|
+
"may",
|
|
13082
|
+
"might",
|
|
13083
|
+
"must",
|
|
13084
|
+
"shall",
|
|
13085
|
+
"has",
|
|
13086
|
+
"had",
|
|
13087
|
+
"been",
|
|
13088
|
+
"being",
|
|
13089
|
+
"i",
|
|
13090
|
+
"you",
|
|
13091
|
+
"we",
|
|
13092
|
+
"they",
|
|
13093
|
+
"he",
|
|
13094
|
+
"she",
|
|
13095
|
+
"my",
|
|
13096
|
+
"your",
|
|
13097
|
+
"our",
|
|
13098
|
+
"its",
|
|
13099
|
+
"his",
|
|
13100
|
+
"her",
|
|
13101
|
+
"their",
|
|
13102
|
+
"if",
|
|
13103
|
+
"then",
|
|
13104
|
+
"else",
|
|
13105
|
+
"so",
|
|
13106
|
+
"as",
|
|
13107
|
+
"from",
|
|
13108
|
+
"by",
|
|
13109
|
+
"into",
|
|
13110
|
+
"through",
|
|
13111
|
+
"during",
|
|
13112
|
+
"before",
|
|
13113
|
+
"after",
|
|
13114
|
+
"also",
|
|
13115
|
+
"now",
|
|
13116
|
+
"back",
|
|
13117
|
+
"get",
|
|
13118
|
+
"go",
|
|
13119
|
+
"come",
|
|
13120
|
+
"let",
|
|
13121
|
+
"like",
|
|
13122
|
+
"just",
|
|
13123
|
+
"know",
|
|
13124
|
+
"think",
|
|
13125
|
+
"see",
|
|
13126
|
+
"look",
|
|
13127
|
+
"make",
|
|
13128
|
+
"take",
|
|
13129
|
+
"want",
|
|
13130
|
+
"need"
|
|
13131
|
+
]);
|
|
12895
13132
|
|
|
12896
13133
|
class SmartVectorRetrieval {
|
|
12897
13134
|
_extractSignificantWords(text) {
|
|
12898
|
-
const
|
|
12899
|
-
"the",
|
|
12900
|
-
"is",
|
|
12901
|
-
"are",
|
|
12902
|
-
"was",
|
|
12903
|
-
"were",
|
|
12904
|
-
"to",
|
|
12905
|
-
"a",
|
|
12906
|
-
"an",
|
|
12907
|
-
"and",
|
|
12908
|
-
"or",
|
|
12909
|
-
"but",
|
|
12910
|
-
"in",
|
|
12911
|
-
"on",
|
|
12912
|
-
"at",
|
|
12913
|
-
"for",
|
|
12914
|
-
"with",
|
|
12915
|
-
"about",
|
|
12916
|
-
"when",
|
|
12917
|
-
"how",
|
|
12918
|
-
"what",
|
|
12919
|
-
"why",
|
|
12920
|
-
"where",
|
|
12921
|
-
"this",
|
|
12922
|
-
"that",
|
|
12923
|
-
"it",
|
|
12924
|
-
"of",
|
|
12925
|
-
"be",
|
|
12926
|
-
"have",
|
|
12927
|
-
"do",
|
|
12928
|
-
"does",
|
|
12929
|
-
"did",
|
|
12930
|
-
"will",
|
|
12931
|
-
"would",
|
|
12932
|
-
"could",
|
|
12933
|
-
"should",
|
|
12934
|
-
"can",
|
|
12935
|
-
"may",
|
|
12936
|
-
"might",
|
|
12937
|
-
"must",
|
|
12938
|
-
"shall",
|
|
12939
|
-
"has",
|
|
12940
|
-
"had",
|
|
12941
|
-
"been",
|
|
12942
|
-
"being",
|
|
12943
|
-
"i",
|
|
12944
|
-
"you",
|
|
12945
|
-
"we",
|
|
12946
|
-
"they",
|
|
12947
|
-
"he",
|
|
12948
|
-
"she",
|
|
12949
|
-
"my",
|
|
12950
|
-
"your",
|
|
12951
|
-
"our",
|
|
12952
|
-
"its",
|
|
12953
|
-
"his",
|
|
12954
|
-
"her",
|
|
12955
|
-
"their",
|
|
12956
|
-
"if",
|
|
12957
|
-
"then",
|
|
12958
|
-
"else",
|
|
12959
|
-
"so",
|
|
12960
|
-
"as",
|
|
12961
|
-
"from",
|
|
12962
|
-
"by",
|
|
12963
|
-
"into",
|
|
12964
|
-
"through",
|
|
12965
|
-
"during",
|
|
12966
|
-
"before",
|
|
12967
|
-
"after",
|
|
12968
|
-
"above",
|
|
12969
|
-
"below",
|
|
12970
|
-
"up",
|
|
12971
|
-
"down",
|
|
12972
|
-
"out",
|
|
12973
|
-
"off",
|
|
12974
|
-
"over",
|
|
12975
|
-
"under",
|
|
12976
|
-
"again",
|
|
12977
|
-
"further",
|
|
12978
|
-
"once",
|
|
12979
|
-
"here",
|
|
12980
|
-
"there",
|
|
12981
|
-
"all",
|
|
12982
|
-
"each",
|
|
12983
|
-
"few",
|
|
12984
|
-
"more",
|
|
12985
|
-
"most",
|
|
12986
|
-
"other",
|
|
12987
|
-
"some",
|
|
12988
|
-
"such",
|
|
12989
|
-
"no",
|
|
12990
|
-
"nor",
|
|
12991
|
-
"not",
|
|
12992
|
-
"only",
|
|
12993
|
-
"own",
|
|
12994
|
-
"same",
|
|
12995
|
-
"than",
|
|
12996
|
-
"too",
|
|
12997
|
-
"very",
|
|
12998
|
-
"just",
|
|
12999
|
-
"also",
|
|
13000
|
-
"now",
|
|
13001
|
-
"back",
|
|
13002
|
-
"get",
|
|
13003
|
-
"got",
|
|
13004
|
-
"go",
|
|
13005
|
-
"going",
|
|
13006
|
-
"gone",
|
|
13007
|
-
"come",
|
|
13008
|
-
"came",
|
|
13009
|
-
"let",
|
|
13010
|
-
"lets",
|
|
13011
|
-
"hey",
|
|
13012
|
-
"hi",
|
|
13013
|
-
"hello",
|
|
13014
|
-
"ok",
|
|
13015
|
-
"okay"
|
|
13016
|
-
]);
|
|
13017
|
-
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
|
|
13135
|
+
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !STOPWORDS.has(w));
|
|
13018
13136
|
return new Set(words);
|
|
13019
13137
|
}
|
|
13020
|
-
_detectContextTypes(message) {
|
|
13021
|
-
const messageLower = message.toLowerCase();
|
|
13022
|
-
const detected = new Set;
|
|
13023
|
-
for (const [type, keywords] of Object.entries(TYPE_KEYWORDS)) {
|
|
13024
|
-
for (const keyword of keywords) {
|
|
13025
|
-
if (messageLower.includes(keyword)) {
|
|
13026
|
-
detected.add(type);
|
|
13027
|
-
break;
|
|
13028
|
-
}
|
|
13029
|
-
}
|
|
13030
|
-
}
|
|
13031
|
-
return detected;
|
|
13032
|
-
}
|
|
13033
13138
|
_preFilter(memories, currentProjectId, messageLower) {
|
|
13034
13139
|
return memories.filter((memory) => {
|
|
13035
|
-
if (memory.status && memory.status !== "active")
|
|
13140
|
+
if (memory.status && memory.status !== "active")
|
|
13036
13141
|
return false;
|
|
13037
|
-
|
|
13038
|
-
if (memory.exclude_from_retrieval === true) {
|
|
13142
|
+
if (memory.exclude_from_retrieval === true)
|
|
13039
13143
|
return false;
|
|
13040
|
-
|
|
13041
|
-
if (memory.superseded_by) {
|
|
13144
|
+
if (memory.superseded_by)
|
|
13042
13145
|
return false;
|
|
13043
|
-
}
|
|
13044
13146
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
13045
|
-
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
13147
|
+
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
13046
13148
|
return false;
|
|
13047
|
-
}
|
|
13048
13149
|
if (memory.anti_triggers?.length) {
|
|
13049
13150
|
for (const antiTrigger of memory.anti_triggers) {
|
|
13050
|
-
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
13151
|
+
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
13051
13152
|
return false;
|
|
13052
|
-
}
|
|
13053
13153
|
}
|
|
13054
13154
|
}
|
|
13055
13155
|
return true;
|
|
13056
13156
|
});
|
|
13057
13157
|
}
|
|
13058
|
-
|
|
13059
|
-
|
|
13158
|
+
_checkTriggerActivation(messageLower, messageWords, triggerPhrases) {
|
|
13159
|
+
if (!triggerPhrases.length)
|
|
13160
|
+
return { activated: false, strength: 0 };
|
|
13161
|
+
let maxStrength = 0;
|
|
13162
|
+
for (const phrase of triggerPhrases) {
|
|
13163
|
+
const phraseLower = phrase.trim().toLowerCase();
|
|
13164
|
+
const phraseWords = phraseLower.split(/\s+/).filter((w) => !STOPWORDS.has(w) && w.length > 2);
|
|
13165
|
+
if (!phraseWords.length)
|
|
13166
|
+
continue;
|
|
13167
|
+
let matches = 0;
|
|
13168
|
+
for (const word of phraseWords) {
|
|
13169
|
+
if (messageWords.has(word) || messageLower.includes(word)) {
|
|
13170
|
+
matches++;
|
|
13171
|
+
} else if (messageWords.has(word.replace(/s$/, "")) || messageWords.has(word + "s") || messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
13172
|
+
matches += 0.8;
|
|
13173
|
+
}
|
|
13174
|
+
}
|
|
13175
|
+
const strength = phraseWords.length > 0 ? matches / phraseWords.length : 0;
|
|
13176
|
+
maxStrength = Math.max(maxStrength, strength);
|
|
13177
|
+
}
|
|
13178
|
+
return { activated: maxStrength >= 0.5, strength: maxStrength };
|
|
13179
|
+
}
|
|
13180
|
+
_checkTagActivation(messageLower, messageWords, tags) {
|
|
13181
|
+
if (!tags.length)
|
|
13182
|
+
return { activated: false, count: 0 };
|
|
13183
|
+
let matchCount = 0;
|
|
13184
|
+
for (const tag of tags) {
|
|
13185
|
+
const tagLower = tag.trim().toLowerCase();
|
|
13186
|
+
if (messageWords.has(tagLower) || messageLower.includes(tagLower)) {
|
|
13187
|
+
matchCount++;
|
|
13188
|
+
}
|
|
13189
|
+
}
|
|
13190
|
+
const threshold = tags.length <= 2 ? 1 : 2;
|
|
13191
|
+
return { activated: matchCount >= threshold, count: matchCount };
|
|
13192
|
+
}
|
|
13193
|
+
_checkDomainActivation(messageLower, messageWords, domain) {
|
|
13194
|
+
if (!domain)
|
|
13195
|
+
return false;
|
|
13196
|
+
const domainLower = domain.trim().toLowerCase();
|
|
13197
|
+
return messageWords.has(domainLower) || messageLower.includes(domainLower);
|
|
13198
|
+
}
|
|
13199
|
+
_checkFeatureActivation(messageLower, messageWords, feature) {
|
|
13200
|
+
if (!feature)
|
|
13201
|
+
return false;
|
|
13202
|
+
const featureLower = feature.trim().toLowerCase();
|
|
13203
|
+
return messageWords.has(featureLower) || messageLower.includes(featureLower);
|
|
13204
|
+
}
|
|
13205
|
+
_checkContentActivation(messageWords, memory) {
|
|
13206
|
+
const contentPreview = memory.content.slice(0, 200);
|
|
13207
|
+
const contentWords = this._extractSignificantWords(contentPreview);
|
|
13208
|
+
let overlap = 0;
|
|
13209
|
+
for (const word of messageWords) {
|
|
13210
|
+
if (contentWords.has(word))
|
|
13211
|
+
overlap++;
|
|
13212
|
+
}
|
|
13213
|
+
return overlap >= 3;
|
|
13214
|
+
}
|
|
13215
|
+
_vectorDebugSamples = [];
|
|
13216
|
+
_calculateVectorSimilarity(vec1, vec2) {
|
|
13217
|
+
if (!vec1 || !vec2) {
|
|
13218
|
+
return 0;
|
|
13219
|
+
}
|
|
13220
|
+
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
13221
|
+
const v2 = vec2 instanceof Float32Array ? vec2 : new Float32Array(vec2);
|
|
13222
|
+
const similarity = cosineSimilarity(v1, v2);
|
|
13223
|
+
if (this._vectorDebugSamples.length < 20) {
|
|
13224
|
+
this._vectorDebugSamples.push(similarity);
|
|
13225
|
+
}
|
|
13226
|
+
return similarity;
|
|
13227
|
+
}
|
|
13228
|
+
_logVectorStats() {
|
|
13229
|
+
if (this._vectorDebugSamples.length === 0)
|
|
13230
|
+
return;
|
|
13231
|
+
const samples = this._vectorDebugSamples;
|
|
13232
|
+
const min = Math.min(...samples);
|
|
13233
|
+
const max = Math.max(...samples);
|
|
13234
|
+
const avg = samples.reduce((a, b) => a + b, 0) / samples.length;
|
|
13235
|
+
console.log(`[DEBUG] Vector similarities: min=${(min * 100).toFixed(1)}% max=${(max * 100).toFixed(1)}% avg=${(avg * 100).toFixed(1)}% (${samples.length} samples)`);
|
|
13236
|
+
this._vectorDebugSamples = [];
|
|
13237
|
+
}
|
|
13238
|
+
_calculateImportanceScore(memory, signalCount, messageLower, messageWords) {
|
|
13060
13239
|
let score = 0;
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13069
|
-
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
|
|
13075
|
-
|
|
13076
|
-
|
|
13077
|
-
|
|
13078
|
-
|
|
13079
|
-
|
|
13080
|
-
|
|
13081
|
-
|
|
13082
|
-
|
|
13083
|
-
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
signals.push("type:" + memory.context_type);
|
|
13093
|
-
}
|
|
13094
|
-
const triggerMatch = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
|
|
13095
|
-
if (triggerMatch > 0.3) {
|
|
13096
|
-
score += Math.min(0.3, triggerMatch * 0.4);
|
|
13097
|
-
signals.push("trigger:" + triggerMatch.toFixed(2));
|
|
13098
|
-
}
|
|
13099
|
-
if (memory.feature) {
|
|
13100
|
-
const featureLower = memory.feature.toLowerCase();
|
|
13101
|
-
if (messageLower.includes(featureLower) || messageWords.has(featureLower)) {
|
|
13102
|
-
score += 0.2;
|
|
13103
|
-
signals.push("feature:" + memory.feature);
|
|
13104
|
-
}
|
|
13105
|
-
}
|
|
13106
|
-
if (memory.related_files?.length) {
|
|
13107
|
-
for (const file of memory.related_files) {
|
|
13108
|
-
const filename = file.split("/").pop()?.toLowerCase() ?? "";
|
|
13109
|
-
if (filename && messageLower.includes(filename)) {
|
|
13110
|
-
score += 0.25;
|
|
13111
|
-
signals.push("file:" + filename);
|
|
13240
|
+
score += memory.importance_weight ?? 0.5;
|
|
13241
|
+
if (signalCount >= 4)
|
|
13242
|
+
score += 0.2;
|
|
13243
|
+
else if (signalCount >= 3)
|
|
13244
|
+
score += 0.1;
|
|
13245
|
+
if (memory.awaiting_implementation)
|
|
13246
|
+
score += 0.15;
|
|
13247
|
+
if (memory.awaiting_decision)
|
|
13248
|
+
score += 0.1;
|
|
13249
|
+
const contextType = memory.context_type?.toLowerCase() ?? "";
|
|
13250
|
+
const contextKeywords = {
|
|
13251
|
+
debugging: ["debug", "bug", "error", "fix", "issue", "problem", "broken"],
|
|
13252
|
+
decision: ["decide", "decision", "choose", "choice", "option", "should"],
|
|
13253
|
+
architectural: ["architect", "design", "structure", "pattern", "how"],
|
|
13254
|
+
breakthrough: ["insight", "realize", "understand", "discover", "why"],
|
|
13255
|
+
technical: ["implement", "code", "function", "method", "api"],
|
|
13256
|
+
workflow: ["process", "workflow", "step", "flow", "pipeline"],
|
|
13257
|
+
philosophy: ["philosophy", "principle", "belief", "approach", "think"]
|
|
13258
|
+
};
|
|
13259
|
+
const keywords = contextKeywords[contextType] ?? [];
|
|
13260
|
+
for (const kw of keywords) {
|
|
13261
|
+
if (messageWords.has(kw) || messageLower.includes(kw)) {
|
|
13262
|
+
score += 0.1;
|
|
13263
|
+
break;
|
|
13264
|
+
}
|
|
13265
|
+
}
|
|
13266
|
+
if (memory.problem_solution_pair) {
|
|
13267
|
+
const problemWords = ["error", "bug", "issue", "problem", "wrong", "fail", "broken", "help", "stuck"];
|
|
13268
|
+
for (const pw of problemWords) {
|
|
13269
|
+
if (messageWords.has(pw) || messageLower.includes(pw)) {
|
|
13270
|
+
score += 0.1;
|
|
13112
13271
|
break;
|
|
13113
13272
|
}
|
|
13114
13273
|
}
|
|
13115
13274
|
}
|
|
13116
|
-
|
|
13275
|
+
const temporalClass = memory.temporal_class ?? "medium_term";
|
|
13276
|
+
if (temporalClass === "eternal")
|
|
13277
|
+
score += 0.1;
|
|
13278
|
+
else if (temporalClass === "long_term")
|
|
13279
|
+
score += 0.05;
|
|
13280
|
+
else if (temporalClass === "ephemeral") {
|
|
13281
|
+
if ((memory.sessions_since_surfaced ?? 0) <= 1)
|
|
13282
|
+
score += 0.1;
|
|
13283
|
+
}
|
|
13284
|
+
const confidence = memory.confidence_score ?? 0.7;
|
|
13285
|
+
if (confidence < 0.5)
|
|
13286
|
+
score -= 0.1;
|
|
13287
|
+
const emotionalKeywords = {
|
|
13288
|
+
frustration: ["frustrated", "annoying", "stuck", "ugh", "damn", "hate"],
|
|
13289
|
+
excitement: ["excited", "awesome", "amazing", "love", "great", "wow"],
|
|
13290
|
+
curiosity: ["wonder", "curious", "interesting", "how", "why", "what if"],
|
|
13291
|
+
satisfaction: ["done", "finished", "complete", "works", "solved", "finally"],
|
|
13292
|
+
discovery: ["found", "realized", "understand", "insight", "breakthrough"]
|
|
13293
|
+
};
|
|
13294
|
+
const emotion = memory.emotional_resonance?.toLowerCase() ?? "";
|
|
13295
|
+
const emotionKws = emotionalKeywords[emotion] ?? [];
|
|
13296
|
+
for (const ew of emotionKws) {
|
|
13297
|
+
if (messageWords.has(ew) || messageLower.includes(ew)) {
|
|
13298
|
+
score += 0.05;
|
|
13299
|
+
break;
|
|
13300
|
+
}
|
|
13301
|
+
}
|
|
13302
|
+
return score;
|
|
13117
13303
|
}
|
|
13118
13304
|
retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0, maxGlobalMemories = 2) {
|
|
13305
|
+
const startTime = performance.now();
|
|
13119
13306
|
if (!allMemories.length) {
|
|
13120
13307
|
return [];
|
|
13121
13308
|
}
|
|
13122
13309
|
const messageLower = currentMessage.toLowerCase();
|
|
13123
13310
|
const messageWords = this._extractSignificantWords(currentMessage);
|
|
13124
|
-
const detectedTypes = this._detectContextTypes(currentMessage);
|
|
13125
13311
|
const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
|
|
13126
13312
|
if (!candidates.length) {
|
|
13127
13313
|
return [];
|
|
13128
13314
|
}
|
|
13129
|
-
const
|
|
13315
|
+
const activatedMemories = [];
|
|
13316
|
+
let rejectedCount = 0;
|
|
13130
13317
|
for (const memory of candidates) {
|
|
13131
13318
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
13132
|
-
const
|
|
13133
|
-
const
|
|
13134
|
-
const
|
|
13135
|
-
const
|
|
13136
|
-
const
|
|
13137
|
-
const
|
|
13138
|
-
|
|
13139
|
-
|
|
13140
|
-
|
|
13141
|
-
|
|
13142
|
-
|
|
13143
|
-
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13147
|
-
if (
|
|
13319
|
+
const triggerResult = this._checkTriggerActivation(messageLower, messageWords, memory.trigger_phrases ?? []);
|
|
13320
|
+
const tagResult = this._checkTagActivation(messageLower, messageWords, memory.semantic_tags ?? []);
|
|
13321
|
+
const domainActivated = this._checkDomainActivation(messageLower, messageWords, memory.domain);
|
|
13322
|
+
const featureActivated = this._checkFeatureActivation(messageLower, messageWords, memory.feature);
|
|
13323
|
+
const contentActivated = this._checkContentActivation(messageWords, memory);
|
|
13324
|
+
const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
|
|
13325
|
+
let signalCount = 0;
|
|
13326
|
+
if (triggerResult.activated)
|
|
13327
|
+
signalCount++;
|
|
13328
|
+
if (tagResult.activated)
|
|
13329
|
+
signalCount++;
|
|
13330
|
+
if (domainActivated)
|
|
13331
|
+
signalCount++;
|
|
13332
|
+
if (featureActivated)
|
|
13333
|
+
signalCount++;
|
|
13334
|
+
if (contentActivated)
|
|
13335
|
+
signalCount++;
|
|
13336
|
+
if (vectorSimilarity >= 0.4)
|
|
13337
|
+
signalCount++;
|
|
13338
|
+
const signals = {
|
|
13339
|
+
trigger: triggerResult.activated,
|
|
13340
|
+
tags: tagResult.activated,
|
|
13341
|
+
domain: domainActivated,
|
|
13342
|
+
feature: featureActivated,
|
|
13343
|
+
content: contentActivated,
|
|
13344
|
+
count: signalCount,
|
|
13345
|
+
triggerStrength: triggerResult.strength,
|
|
13346
|
+
tagCount: tagResult.count,
|
|
13347
|
+
vectorSimilarity
|
|
13348
|
+
};
|
|
13349
|
+
if (signalCount < MIN_ACTIVATION_SIGNALS) {
|
|
13350
|
+
rejectedCount++;
|
|
13148
13351
|
continue;
|
|
13149
13352
|
}
|
|
13150
|
-
const
|
|
13151
|
-
|
|
13152
|
-
corroboration: corroborationScore,
|
|
13153
|
-
reasoning_match: reasoningMatch,
|
|
13154
|
-
retrieval_weight: retrievalWeight,
|
|
13155
|
-
temporal: temporalScore,
|
|
13156
|
-
context: contextScore,
|
|
13157
|
-
tags: tagScore,
|
|
13158
|
-
trigger: triggerScore,
|
|
13159
|
-
domain: domainScore,
|
|
13160
|
-
question: questionScore,
|
|
13161
|
-
emotion: emotionScore,
|
|
13162
|
-
problem: problemScore,
|
|
13163
|
-
action: actionBoost
|
|
13164
|
-
};
|
|
13165
|
-
const reasoning = this._generateSelectionReasoning(components, corroborationSignals);
|
|
13166
|
-
scoredMemories.push({
|
|
13353
|
+
const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords);
|
|
13354
|
+
activatedMemories.push({
|
|
13167
13355
|
memory,
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
value_score: valueScore,
|
|
13171
|
-
corroboration_score: corroborationScore,
|
|
13172
|
-
reasoning,
|
|
13173
|
-
components,
|
|
13356
|
+
signals,
|
|
13357
|
+
importanceScore,
|
|
13174
13358
|
isGlobal
|
|
13175
13359
|
});
|
|
13176
13360
|
}
|
|
13177
|
-
|
|
13361
|
+
this._logActivationDistribution(activatedMemories, candidates.length, rejectedCount);
|
|
13362
|
+
this._logVectorStats();
|
|
13363
|
+
if (!activatedMemories.length) {
|
|
13364
|
+
const durationMs2 = performance.now() - startTime;
|
|
13365
|
+
logger.logRetrievalScoring({
|
|
13366
|
+
totalMemories: allMemories.length,
|
|
13367
|
+
currentMessage,
|
|
13368
|
+
alreadyInjected: alreadyInjectedCount,
|
|
13369
|
+
preFiltered: allMemories.length - candidates.length,
|
|
13370
|
+
globalCount: 0,
|
|
13371
|
+
projectCount: 0,
|
|
13372
|
+
finalCount: 0,
|
|
13373
|
+
durationMs: durationMs2,
|
|
13374
|
+
selectedMemories: []
|
|
13375
|
+
});
|
|
13376
|
+
return [];
|
|
13377
|
+
}
|
|
13378
|
+
activatedMemories.sort((a, b) => {
|
|
13379
|
+
if (b.signals.count !== a.signals.count) {
|
|
13380
|
+
return b.signals.count - a.signals.count;
|
|
13381
|
+
}
|
|
13382
|
+
return b.importanceScore - a.importanceScore;
|
|
13383
|
+
});
|
|
13178
13384
|
const selected = [];
|
|
13179
13385
|
const selectedIds = new Set;
|
|
13180
|
-
const globalMemories =
|
|
13181
|
-
const projectMemories =
|
|
13182
|
-
const
|
|
13183
|
-
const
|
|
13184
|
-
const
|
|
13185
|
-
if (
|
|
13186
|
-
return
|
|
13187
|
-
|
|
13188
|
-
|
|
13386
|
+
const globalMemories = activatedMemories.filter((m) => m.isGlobal);
|
|
13387
|
+
const projectMemories = activatedMemories.filter((m) => !m.isGlobal);
|
|
13388
|
+
const globalsSorted = globalMemories.sort((a, b) => {
|
|
13389
|
+
const aPriority = GLOBAL_TYPE_PRIORITY[a.memory.context_type ?? "personal"] ?? 8;
|
|
13390
|
+
const bPriority = GLOBAL_TYPE_PRIORITY[b.memory.context_type ?? "personal"] ?? 8;
|
|
13391
|
+
if (aPriority !== bPriority)
|
|
13392
|
+
return aPriority - bPriority;
|
|
13393
|
+
if (b.signals.count !== a.signals.count)
|
|
13394
|
+
return b.signals.count - a.signals.count;
|
|
13395
|
+
return b.importanceScore - a.importanceScore;
|
|
13189
13396
|
});
|
|
13190
|
-
for (const item of
|
|
13397
|
+
for (const item of globalsSorted.slice(0, maxGlobalMemories)) {
|
|
13191
13398
|
if (!selectedIds.has(item.memory.id)) {
|
|
13192
13399
|
selected.push(item);
|
|
13193
13400
|
selectedIds.add(item.memory.id);
|
|
13194
13401
|
}
|
|
13195
13402
|
}
|
|
13196
|
-
|
|
13403
|
+
const projectsSorted = [...projectMemories].sort((a, b) => {
|
|
13404
|
+
const aAction = a.memory.action_required ? 1 : 0;
|
|
13405
|
+
const bAction = b.memory.action_required ? 1 : 0;
|
|
13406
|
+
if (bAction !== aAction)
|
|
13407
|
+
return bAction - aAction;
|
|
13408
|
+
if (b.signals.count !== a.signals.count)
|
|
13409
|
+
return b.signals.count - a.signals.count;
|
|
13410
|
+
return b.importanceScore - a.importanceScore;
|
|
13411
|
+
});
|
|
13412
|
+
console.log(`[DEBUG] Top 15 candidates (sorted):`);
|
|
13413
|
+
for (let i = 0;i < Math.min(15, projectsSorted.length); i++) {
|
|
13414
|
+
const m = projectsSorted[i];
|
|
13415
|
+
const action = m.memory.action_required ? "⚡" : "";
|
|
13416
|
+
console.log(` ${i + 1}. [${m.signals.count}sig] score=${m.importanceScore.toFixed(2)} ${action} ${m.memory.content.slice(0, 45)}...`);
|
|
13417
|
+
}
|
|
13418
|
+
for (const item of projectsSorted) {
|
|
13197
13419
|
if (selected.length >= maxMemories)
|
|
13198
13420
|
break;
|
|
13199
13421
|
if (selectedIds.has(item.memory.id))
|
|
@@ -13201,7 +13423,27 @@ class SmartVectorRetrieval {
|
|
|
13201
13423
|
selected.push(item);
|
|
13202
13424
|
selectedIds.add(item.memory.id);
|
|
13203
13425
|
}
|
|
13204
|
-
selected.
|
|
13426
|
+
if (selected.length < maxMemories) {
|
|
13427
|
+
const relatedIds = new Set;
|
|
13428
|
+
for (const item of selected) {
|
|
13429
|
+
for (const relatedId of item.memory.related_to ?? []) {
|
|
13430
|
+
if (!selectedIds.has(relatedId)) {
|
|
13431
|
+
relatedIds.add(relatedId);
|
|
13432
|
+
}
|
|
13433
|
+
}
|
|
13434
|
+
}
|
|
13435
|
+
for (const item of activatedMemories) {
|
|
13436
|
+
if (selected.length >= maxMemories)
|
|
13437
|
+
break;
|
|
13438
|
+
if (selectedIds.has(item.memory.id))
|
|
13439
|
+
continue;
|
|
13440
|
+
if (relatedIds.has(item.memory.id)) {
|
|
13441
|
+
selected.push(item);
|
|
13442
|
+
selectedIds.add(item.memory.id);
|
|
13443
|
+
}
|
|
13444
|
+
}
|
|
13445
|
+
}
|
|
13446
|
+
const durationMs = performance.now() - startTime;
|
|
13205
13447
|
logger.logRetrievalScoring({
|
|
13206
13448
|
totalMemories: allMemories.length,
|
|
13207
13449
|
currentMessage,
|
|
@@ -13210,202 +13452,102 @@ class SmartVectorRetrieval {
|
|
|
13210
13452
|
globalCount: globalMemories.length,
|
|
13211
13453
|
projectCount: projectMemories.length,
|
|
13212
13454
|
finalCount: selected.length,
|
|
13455
|
+
durationMs,
|
|
13213
13456
|
selectedMemories: selected.map((item) => ({
|
|
13214
13457
|
content: item.memory.content,
|
|
13215
|
-
reasoning: item.
|
|
13216
|
-
|
|
13217
|
-
relevance_score: item.relevance_score,
|
|
13218
|
-
corroboration_score: item.corroboration_score,
|
|
13458
|
+
reasoning: this._generateActivationReasoning(item.signals),
|
|
13459
|
+
signalCount: item.signals.count,
|
|
13219
13460
|
importance_weight: item.memory.importance_weight ?? 0.5,
|
|
13220
13461
|
context_type: item.memory.context_type ?? "general",
|
|
13221
13462
|
semantic_tags: item.memory.semantic_tags ?? [],
|
|
13222
13463
|
isGlobal: item.isGlobal,
|
|
13223
|
-
|
|
13464
|
+
signals: {
|
|
13465
|
+
trigger: item.signals.trigger,
|
|
13466
|
+
triggerStrength: item.signals.triggerStrength,
|
|
13467
|
+
tags: item.signals.tags,
|
|
13468
|
+
tagCount: item.signals.tagCount,
|
|
13469
|
+
domain: item.signals.domain,
|
|
13470
|
+
feature: item.signals.feature,
|
|
13471
|
+
content: item.signals.content,
|
|
13472
|
+
vector: item.signals.vectorSimilarity >= 0.4,
|
|
13473
|
+
vectorSimilarity: item.signals.vectorSimilarity
|
|
13474
|
+
}
|
|
13224
13475
|
}))
|
|
13225
13476
|
});
|
|
13226
13477
|
return selected.map((item) => ({
|
|
13227
13478
|
...item.memory,
|
|
13228
|
-
score: item.
|
|
13229
|
-
relevance_score: item.
|
|
13230
|
-
value_score: item.
|
|
13479
|
+
score: item.signals.count / 6,
|
|
13480
|
+
relevance_score: item.signals.count / 6,
|
|
13481
|
+
value_score: item.importanceScore
|
|
13231
13482
|
}));
|
|
13232
13483
|
}
|
|
13233
|
-
|
|
13234
|
-
if (!vec1 || !vec2)
|
|
13235
|
-
return 0;
|
|
13236
|
-
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
13237
|
-
return cosineSimilarity(v1, vec2);
|
|
13238
|
-
}
|
|
13239
|
-
_scoreTemporalRelevance(temporalType) {
|
|
13240
|
-
const scores = {
|
|
13241
|
-
persistent: 0.8,
|
|
13242
|
-
session: 0.6,
|
|
13243
|
-
temporary: 0.3,
|
|
13244
|
-
archived: 0.1
|
|
13245
|
-
};
|
|
13246
|
-
return scores[temporalType] ?? 0.5;
|
|
13247
|
-
}
|
|
13248
|
-
_scoreContextAlignment(message, contextType) {
|
|
13249
|
-
const messageLower = message.toLowerCase();
|
|
13250
|
-
const keywords = TYPE_KEYWORDS[contextType] ?? [];
|
|
13251
|
-
const matches = keywords.filter((kw) => messageLower.includes(kw)).length;
|
|
13252
|
-
if (matches > 0) {
|
|
13253
|
-
return Math.min(0.2 + matches * 0.15, 0.7);
|
|
13254
|
-
}
|
|
13255
|
-
return 0.1;
|
|
13256
|
-
}
|
|
13257
|
-
_scoreSemanticTags(message, tags) {
|
|
13258
|
-
if (!tags.length)
|
|
13259
|
-
return 0;
|
|
13260
|
-
const messageLower = message.toLowerCase();
|
|
13261
|
-
const matches = tags.filter((tag) => messageLower.includes(tag.trim().toLowerCase())).length;
|
|
13262
|
-
if (matches > 0) {
|
|
13263
|
-
return Math.min(0.3 + matches * 0.25, 1);
|
|
13264
|
-
}
|
|
13265
|
-
return 0;
|
|
13266
|
-
}
|
|
13267
|
-
_scoreTriggerPhrases(messageLower, triggerPhrases) {
|
|
13268
|
-
if (!triggerPhrases.length)
|
|
13269
|
-
return 0;
|
|
13270
|
-
const stopWords = new Set([
|
|
13271
|
-
"the",
|
|
13272
|
-
"is",
|
|
13273
|
-
"are",
|
|
13274
|
-
"was",
|
|
13275
|
-
"were",
|
|
13276
|
-
"to",
|
|
13277
|
-
"a",
|
|
13278
|
-
"an",
|
|
13279
|
-
"and",
|
|
13280
|
-
"or",
|
|
13281
|
-
"but",
|
|
13282
|
-
"in",
|
|
13283
|
-
"on",
|
|
13284
|
-
"at",
|
|
13285
|
-
"for",
|
|
13286
|
-
"with",
|
|
13287
|
-
"about",
|
|
13288
|
-
"when",
|
|
13289
|
-
"how",
|
|
13290
|
-
"what",
|
|
13291
|
-
"why"
|
|
13292
|
-
]);
|
|
13293
|
-
let maxScore = 0;
|
|
13294
|
-
for (const pattern of triggerPhrases) {
|
|
13295
|
-
const patternLower = pattern.trim().toLowerCase();
|
|
13296
|
-
const patternWords = patternLower.split(/\s+/).filter((w) => !stopWords.has(w) && w.length > 2);
|
|
13297
|
-
if (patternWords.length) {
|
|
13298
|
-
let matches = 0;
|
|
13299
|
-
for (const word of patternWords) {
|
|
13300
|
-
if (messageLower.includes(word)) {
|
|
13301
|
-
matches += 1;
|
|
13302
|
-
} else if (messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
13303
|
-
matches += 0.9;
|
|
13304
|
-
}
|
|
13305
|
-
}
|
|
13306
|
-
const conceptScore = matches / patternWords.length;
|
|
13307
|
-
maxScore = Math.max(maxScore, conceptScore);
|
|
13308
|
-
}
|
|
13309
|
-
}
|
|
13310
|
-
return Math.min(maxScore, 1);
|
|
13311
|
-
}
|
|
13312
|
-
_scoreDomain(messageWords, messageLower, memory) {
|
|
13313
|
-
let score = 0;
|
|
13314
|
-
if (memory.domain) {
|
|
13315
|
-
const domainLower = memory.domain.toLowerCase();
|
|
13316
|
-
if (messageWords.has(domainLower) || messageLower.includes(domainLower)) {
|
|
13317
|
-
score += 0.5;
|
|
13318
|
-
}
|
|
13319
|
-
}
|
|
13320
|
-
if (memory.feature) {
|
|
13321
|
-
const featureLower = memory.feature.toLowerCase();
|
|
13322
|
-
if (messageWords.has(featureLower) || messageLower.includes(featureLower)) {
|
|
13323
|
-
score += 0.3;
|
|
13324
|
-
}
|
|
13325
|
-
}
|
|
13326
|
-
if (memory.component) {
|
|
13327
|
-
const componentLower = memory.component.toLowerCase();
|
|
13328
|
-
if (messageWords.has(componentLower) || messageLower.includes(componentLower)) {
|
|
13329
|
-
score += 0.2;
|
|
13330
|
-
}
|
|
13331
|
-
}
|
|
13332
|
-
return Math.min(score, 1);
|
|
13333
|
-
}
|
|
13334
|
-
_scoreQuestionTypes(message, questionTypes) {
|
|
13335
|
-
if (!questionTypes.length)
|
|
13336
|
-
return 0;
|
|
13337
|
-
const messageLower = message.toLowerCase();
|
|
13338
|
-
const questionWords = ["how", "why", "what", "when", "where"];
|
|
13339
|
-
for (const qtype of questionTypes) {
|
|
13340
|
-
const qtypeLower = qtype.trim().toLowerCase();
|
|
13341
|
-
if (messageLower.includes(qtypeLower)) {
|
|
13342
|
-
return 0.8;
|
|
13343
|
-
}
|
|
13344
|
-
const messageHasQuestion = questionWords.some((qw) => messageLower.includes(qw));
|
|
13345
|
-
const typeHasQuestion = questionWords.some((qw) => qtypeLower.includes(qw));
|
|
13346
|
-
if (messageHasQuestion && typeHasQuestion) {
|
|
13347
|
-
return 0.5;
|
|
13348
|
-
}
|
|
13349
|
-
}
|
|
13350
|
-
return 0;
|
|
13351
|
-
}
|
|
13352
|
-
_scoreEmotionalContext(message, emotion) {
|
|
13353
|
-
if (!emotion)
|
|
13354
|
-
return 0;
|
|
13355
|
-
const messageLower = message.toLowerCase();
|
|
13356
|
-
const emotionPatterns = {
|
|
13357
|
-
joy: ["happy", "excited", "love", "wonderful", "great", "awesome"],
|
|
13358
|
-
frustration: ["stuck", "confused", "help", "issue", "problem", "why"],
|
|
13359
|
-
discovery: ["realized", "found", "discovered", "aha", "insight"],
|
|
13360
|
-
gratitude: ["thank", "appreciate", "grateful", "dear friend"]
|
|
13361
|
-
};
|
|
13362
|
-
const patterns = emotionPatterns[emotion.toLowerCase()] ?? [];
|
|
13363
|
-
if (patterns.some((pattern) => messageLower.includes(pattern))) {
|
|
13364
|
-
return 0.7;
|
|
13365
|
-
}
|
|
13366
|
-
return 0;
|
|
13367
|
-
}
|
|
13368
|
-
_scoreProblemSolution(message, isProblemSolution) {
|
|
13369
|
-
if (!isProblemSolution)
|
|
13370
|
-
return 0;
|
|
13371
|
-
const messageLower = message.toLowerCase();
|
|
13372
|
-
const problemWords = ["error", "issue", "problem", "stuck", "help", "fix", "solve", "debug"];
|
|
13373
|
-
if (problemWords.some((word) => messageLower.includes(word))) {
|
|
13374
|
-
return 0.8;
|
|
13375
|
-
}
|
|
13376
|
-
return 0;
|
|
13377
|
-
}
|
|
13378
|
-
_generateSelectionReasoning(components, corroborationSignals) {
|
|
13379
|
-
const scores = [
|
|
13380
|
-
["vector", components.vector],
|
|
13381
|
-
["corroboration", components.corroboration],
|
|
13382
|
-
["reasoning", components.reasoning_match],
|
|
13383
|
-
["weight", components.retrieval_weight],
|
|
13384
|
-
["context", components.context],
|
|
13385
|
-
["temporal", components.temporal],
|
|
13386
|
-
["tags", components.tags],
|
|
13387
|
-
["trigger", components.trigger],
|
|
13388
|
-
["domain", components.domain],
|
|
13389
|
-
["question", components.question],
|
|
13390
|
-
["emotion", components.emotion],
|
|
13391
|
-
["problem", components.problem],
|
|
13392
|
-
["action", components.action]
|
|
13393
|
-
];
|
|
13394
|
-
scores.sort((a, b) => b[1] - a[1]);
|
|
13484
|
+
_generateActivationReasoning(signals) {
|
|
13395
13485
|
const reasons = [];
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13486
|
+
if (signals.trigger)
|
|
13487
|
+
reasons.push(`trigger:${(signals.triggerStrength * 100).toFixed(0)}%`);
|
|
13488
|
+
if (signals.tags)
|
|
13489
|
+
reasons.push(`tags:${signals.tagCount}`);
|
|
13490
|
+
if (signals.domain)
|
|
13491
|
+
reasons.push("domain");
|
|
13492
|
+
if (signals.feature)
|
|
13493
|
+
reasons.push("feature");
|
|
13494
|
+
if (signals.content)
|
|
13495
|
+
reasons.push("content");
|
|
13496
|
+
if (signals.vectorSimilarity >= 0.4)
|
|
13497
|
+
reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
13498
|
+
return reasons.length ? `Activated: ${reasons.join(", ")} (${signals.count} signals)` : "No signals";
|
|
13499
|
+
}
|
|
13500
|
+
_logActivationDistribution(activated, totalCandidates, rejectedCount) {
|
|
13501
|
+
const signalBuckets = {
|
|
13502
|
+
"2 signals": 0,
|
|
13503
|
+
"3 signals": 0,
|
|
13504
|
+
"4 signals": 0,
|
|
13505
|
+
"5 signals": 0,
|
|
13506
|
+
"6 signals": 0
|
|
13507
|
+
};
|
|
13508
|
+
for (const mem of activated) {
|
|
13509
|
+
const key = `${Math.min(mem.signals.count, 6)} signals`;
|
|
13510
|
+
signalBuckets[key] = (signalBuckets[key] ?? 0) + 1;
|
|
13511
|
+
}
|
|
13512
|
+
let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0;
|
|
13513
|
+
for (const mem of activated) {
|
|
13514
|
+
if (mem.signals.trigger)
|
|
13515
|
+
triggerCount++;
|
|
13516
|
+
if (mem.signals.tags)
|
|
13517
|
+
tagCount++;
|
|
13518
|
+
if (mem.signals.domain)
|
|
13519
|
+
domainCount++;
|
|
13520
|
+
if (mem.signals.feature)
|
|
13521
|
+
featureCount++;
|
|
13522
|
+
if (mem.signals.content)
|
|
13523
|
+
contentCount++;
|
|
13524
|
+
if (mem.signals.vectorSimilarity >= 0.4)
|
|
13525
|
+
vectorCount++;
|
|
13526
|
+
}
|
|
13527
|
+
logger.logScoreDistribution({
|
|
13528
|
+
totalCandidates,
|
|
13529
|
+
passedGatekeeper: activated.length,
|
|
13530
|
+
rejectedByGatekeeper: rejectedCount,
|
|
13531
|
+
buckets: signalBuckets,
|
|
13532
|
+
stats: {
|
|
13533
|
+
min: activated.length ? Math.min(...activated.map((m) => m.signals.count)) : 0,
|
|
13534
|
+
max: activated.length ? Math.max(...activated.map((m) => m.signals.count)) : 0,
|
|
13535
|
+
mean: activated.length ? Math.round(activated.reduce((s, m) => s + m.signals.count, 0) / activated.length * 10) / 10 : 0,
|
|
13536
|
+
stdev: 0,
|
|
13537
|
+
spread: activated.length ? Math.max(...activated.map((m) => m.signals.count)) - Math.min(...activated.map((m) => m.signals.count)) : 0
|
|
13538
|
+
},
|
|
13539
|
+
percentiles: {},
|
|
13540
|
+
compressionWarning: false,
|
|
13541
|
+
signalBreakdown: {
|
|
13542
|
+
trigger: triggerCount,
|
|
13543
|
+
tags: tagCount,
|
|
13544
|
+
domain: domainCount,
|
|
13545
|
+
feature: featureCount,
|
|
13546
|
+
content: contentCount,
|
|
13547
|
+
vector: vectorCount,
|
|
13548
|
+
total: activated.length
|
|
13403
13549
|
}
|
|
13404
|
-
}
|
|
13405
|
-
if (corroborationSignals.length > 0) {
|
|
13406
|
-
reasons.push("signals:[" + corroborationSignals.slice(0, 2).join(",") + "]");
|
|
13407
|
-
}
|
|
13408
|
-
return reasons.length ? "Selected: " + reasons.join(", ") : "Combined factors";
|
|
13550
|
+
});
|
|
13409
13551
|
}
|
|
13410
13552
|
}
|
|
13411
13553
|
function createRetrieval() {
|
|
@@ -13424,7 +13566,8 @@ class MemoryEngine {
|
|
|
13424
13566
|
centralPath: config.centralPath ?? import_path2.join(import_os2.homedir(), ".local", "share", "memory"),
|
|
13425
13567
|
localFolder: config.localFolder ?? ".memory",
|
|
13426
13568
|
maxMemories: config.maxMemories ?? 5,
|
|
13427
|
-
embedder: config.embedder
|
|
13569
|
+
embedder: config.embedder,
|
|
13570
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
13428
13571
|
};
|
|
13429
13572
|
this._retrieval = createRetrieval();
|
|
13430
13573
|
}
|
|
@@ -13553,7 +13696,7 @@ class MemoryEngine {
|
|
|
13553
13696
|
}
|
|
13554
13697
|
async _generateSessionPrimer(store, projectId) {
|
|
13555
13698
|
let personalContext;
|
|
13556
|
-
if (
|
|
13699
|
+
if (this._config.personalMemoriesEnabled) {
|
|
13557
13700
|
const personalPrimer = await store.getPersonalPrimer();
|
|
13558
13701
|
personalContext = personalPrimer?.content;
|
|
13559
13702
|
}
|
|
@@ -13641,12 +13784,28 @@ ${primer.personal_context}`);
|
|
|
13641
13784
|
**Project status**: ${primer.project_status}`);
|
|
13642
13785
|
}
|
|
13643
13786
|
parts.push(`
|
|
13644
|
-
**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`);
|
|
13787
|
+
**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`);
|
|
13645
13788
|
parts.push(`
|
|
13646
13789
|
*Memories will surface naturally as we converse.*`);
|
|
13647
13790
|
return parts.join(`
|
|
13648
13791
|
`);
|
|
13649
13792
|
}
|
|
13793
|
+
_formatAge(createdAt) {
|
|
13794
|
+
const now = Date.now();
|
|
13795
|
+
const diffMs = now - createdAt;
|
|
13796
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
13797
|
+
if (diffDays === 0)
|
|
13798
|
+
return "today";
|
|
13799
|
+
if (diffDays === 1)
|
|
13800
|
+
return "1d";
|
|
13801
|
+
if (diffDays < 7)
|
|
13802
|
+
return `${diffDays}d`;
|
|
13803
|
+
if (diffDays < 30)
|
|
13804
|
+
return `${Math.floor(diffDays / 7)}w`;
|
|
13805
|
+
if (diffDays < 365)
|
|
13806
|
+
return `${Math.floor(diffDays / 30)}mo`;
|
|
13807
|
+
return `${Math.floor(diffDays / 365)}y`;
|
|
13808
|
+
}
|
|
13650
13809
|
_formatMemories(memories) {
|
|
13651
13810
|
if (!memories.length)
|
|
13652
13811
|
return "";
|
|
@@ -13657,11 +13816,40 @@ ${primer.personal_context}`);
|
|
|
13657
13816
|
const tags = memory.semantic_tags?.join(", ") || "";
|
|
13658
13817
|
const importance = memory.importance_weight?.toFixed(1) || "0.5";
|
|
13659
13818
|
const emoji = getMemoryEmoji(memory.context_type || "general");
|
|
13660
|
-
|
|
13819
|
+
const actionFlag = memory.action_required ? " ⚡ACTION" : "";
|
|
13820
|
+
const age = memory.updated_at ? this._formatAge(memory.updated_at) : memory.created_at ? this._formatAge(memory.created_at) : "";
|
|
13821
|
+
parts.push(`[${emoji} • ${importance} • ${age}${actionFlag}] [${tags}] ${memory.content}`);
|
|
13822
|
+
const related = memory.related_to;
|
|
13823
|
+
if (related && related.length > 0) {
|
|
13824
|
+
const moreCount = related.length - 1;
|
|
13825
|
+
const moreSuffix = moreCount > 0 ? ` +${moreCount} more` : "";
|
|
13826
|
+
parts.push(` ↳ ${related[0]}${moreSuffix}`);
|
|
13827
|
+
}
|
|
13661
13828
|
}
|
|
13662
13829
|
return parts.join(`
|
|
13663
13830
|
`);
|
|
13664
13831
|
}
|
|
13832
|
+
getStoragePaths(projectId, projectPath) {
|
|
13833
|
+
const globalPath = import_path2.join(import_os2.homedir(), ".local", "share", "memory", "global");
|
|
13834
|
+
const globalMemoriesPath = import_path2.join(globalPath, "memories");
|
|
13835
|
+
const personalPrimerPath = import_path2.join(globalPath, "primer", "personal-primer.md");
|
|
13836
|
+
let storeBasePath;
|
|
13837
|
+
if (this._config.storageMode === "local" && projectPath) {
|
|
13838
|
+
storeBasePath = import_path2.join(projectPath, this._config.localFolder);
|
|
13839
|
+
} else {
|
|
13840
|
+
storeBasePath = this._config.centralPath;
|
|
13841
|
+
}
|
|
13842
|
+
const projectRootPath = import_path2.join(storeBasePath, projectId);
|
|
13843
|
+
const projectMemoriesPath = import_path2.join(projectRootPath, "memories");
|
|
13844
|
+
return {
|
|
13845
|
+
projectPath: projectRootPath,
|
|
13846
|
+
globalPath,
|
|
13847
|
+
projectMemoriesPath,
|
|
13848
|
+
globalMemoriesPath,
|
|
13849
|
+
personalPrimerPath,
|
|
13850
|
+
storageMode: this._config.storageMode
|
|
13851
|
+
};
|
|
13852
|
+
}
|
|
13665
13853
|
close() {
|
|
13666
13854
|
for (const store of this._stores.values()) {
|
|
13667
13855
|
store.close();
|
|
@@ -13695,11 +13883,12 @@ class Curator {
|
|
|
13695
13883
|
this._config = {
|
|
13696
13884
|
apiKey: config.apiKey ?? "",
|
|
13697
13885
|
cliCommand,
|
|
13698
|
-
cliType: config.cliType ?? "claude-code"
|
|
13886
|
+
cliType: config.cliType ?? "claude-code",
|
|
13887
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
13699
13888
|
};
|
|
13700
13889
|
}
|
|
13701
13890
|
buildCurationPrompt(triggerType = "session_end") {
|
|
13702
|
-
|
|
13891
|
+
const basePrompt = `You have just had a conversation. As this session is ending (${triggerType}), please curate memories for the Claude Tools Memory System.
|
|
13703
13892
|
|
|
13704
13893
|
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.
|
|
13705
13894
|
|
|
@@ -13736,27 +13925,82 @@ Each memory should stand alone.
|
|
|
13736
13925
|
- Craft language that activates rather than just informs
|
|
13737
13926
|
- Test: 'What state will this restore when Claude encounters it?'
|
|
13738
13927
|
|
|
13739
|
-
**HOW RETRIEVAL WORKS
|
|
13928
|
+
**HOW RETRIEVAL WORKS - ACTIVATION SIGNAL ALGORITHM**
|
|
13929
|
+
|
|
13930
|
+
Understanding the algorithm helps you craft metadata that surfaces memories at the right moments.
|
|
13931
|
+
|
|
13932
|
+
**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.
|
|
13740
13933
|
|
|
13741
|
-
**THE
|
|
13934
|
+
**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.
|
|
13742
13935
|
|
|
13743
|
-
|
|
13744
|
-
- GOOD: ["embeddings", "retrieval", "curator", "fsdb", "memory-system"]
|
|
13745
|
-
- WEAK: ["technical", "implementation", "code"]
|
|
13936
|
+
**6 ACTIVATION SIGNALS** (each is binary - fires or doesn't):
|
|
13746
13937
|
|
|
13747
|
-
|
|
13938
|
+
1. **TRIGGER** - Words from trigger_phrases found in user's message (≥50% match)
|
|
13939
|
+
- THE MOST IMPORTANT SIGNAL. Handcrafted activation patterns.
|
|
13940
|
+
- Example: "when debugging retrieval" fires if user says "I'm debugging the retrieval algorithm"
|
|
13748
13941
|
|
|
13749
|
-
|
|
13942
|
+
2. **TAGS** - 2+ semantic_tags found in user's message
|
|
13943
|
+
- Use words users would ACTUALLY TYPE, not generic descriptors
|
|
13944
|
+
- GOOD: ["retrieval", "embeddings", "curator", "scoring"]
|
|
13945
|
+
- WEAK: ["technical", "important", "system"]
|
|
13750
13946
|
|
|
13751
|
-
|
|
13947
|
+
3. **DOMAIN** - The domain word appears in user's message
|
|
13948
|
+
- Be specific: "retrieval", "embeddings", "auth", "ui"
|
|
13949
|
+
- NOT: "technical", "code", "implementation"
|
|
13752
13950
|
|
|
13753
|
-
|
|
13951
|
+
4. **FEATURE** - The feature word appears in user's message
|
|
13952
|
+
- Be specific: "scoring-weights", "gpu-acceleration", "login-flow"
|
|
13754
13953
|
|
|
13755
|
-
|
|
13954
|
+
5. **CONTENT** - 3+ significant words from memory content overlap with message
|
|
13955
|
+
- Automatic - based on the memory's content text
|
|
13756
13956
|
|
|
13757
|
-
|
|
13957
|
+
6. **VECTOR** - Semantic similarity ≥ 40% (embedding cosine distance)
|
|
13958
|
+
- Automatic - based on embeddings generated from content
|
|
13758
13959
|
|
|
13759
|
-
**
|
|
13960
|
+
**RELEVANCE GATE**: A memory must have ≥2 signals to be considered relevant.
|
|
13961
|
+
If only 1 signal fires, the memory is REJECTED. This prevents noise.
|
|
13962
|
+
|
|
13963
|
+
**RANKING AMONG RELEVANT**: Once a memory passes the gate:
|
|
13964
|
+
1. Sort by SIGNAL COUNT (more signals = more certainly relevant)
|
|
13965
|
+
2. Then by IMPORTANCE WEIGHT (your assessment of how important this memory is)
|
|
13966
|
+
|
|
13967
|
+
**SELECTION**:
|
|
13968
|
+
- Global memories (scope='global'): Max 2 selected, tech types prioritized over personal
|
|
13969
|
+
- Project memories: Fill remaining slots, action_required prioritized
|
|
13970
|
+
- Related memories (related_to field): May be included if they also passed the gate
|
|
13971
|
+
|
|
13972
|
+
**WHY THIS MATTERS FOR YOU**:
|
|
13973
|
+
- If you don't fill trigger_phrases well → trigger signal never fires
|
|
13974
|
+
- If you use generic tags → tags signal rarely fires
|
|
13975
|
+
- If you leave domain/feature empty → those signals can't fire
|
|
13976
|
+
- A memory with poor metadata may NEVER surface because it can't reach 2 signals
|
|
13977
|
+
|
|
13978
|
+
**CRAFTING EFFECTIVE METADATA** (CRITICAL FOR RETRIEVAL):
|
|
13979
|
+
|
|
13980
|
+
1. **trigger_phrases** (MOST IMPORTANT) - Activation patterns describing WHEN to surface:
|
|
13981
|
+
- Include 2-4 specific patterns per memory
|
|
13982
|
+
- Use words the user would actually type
|
|
13983
|
+
- GOOD: ["debugging retrieval", "working on embeddings", "memory system performance"]
|
|
13984
|
+
- WEAK: ["when relevant", "if needed", "technical work"]
|
|
13985
|
+
|
|
13986
|
+
2. **semantic_tags** - Words users would type (need 2+ to fire):
|
|
13987
|
+
- Be specific and searchable
|
|
13988
|
+
- GOOD: ["retrieval", "embeddings", "fsdb", "curator", "scoring"]
|
|
13989
|
+
- WEAK: ["technical", "important", "system", "implementation"]
|
|
13990
|
+
|
|
13991
|
+
3. **domain** (NEW - FILL THIS) - Single specific area word:
|
|
13992
|
+
- GOOD: "retrieval", "embeddings", "curator", "signals", "fsdb"
|
|
13993
|
+
- WEAK: "technical", "code", "memory" (too generic)
|
|
13994
|
+
|
|
13995
|
+
4. **feature** (NEW - FILL THIS) - Specific feature within domain:
|
|
13996
|
+
- GOOD: "scoring-algorithm", "activation-signals", "vector-search"
|
|
13997
|
+
- WEAK: "implementation", "code", "logic"
|
|
13998
|
+
|
|
13999
|
+
5. **importance_weight** - Only affects ranking AMONG relevant memories:
|
|
14000
|
+
- 0.9+ = Critical breakthrough, must surface if relevant
|
|
14001
|
+
- 0.7-0.8 = Important insight, should surface if relevant
|
|
14002
|
+
- 0.5-0.6 = Useful context, nice to have if relevant
|
|
14003
|
+
- NOTE: This does NOT affect whether the memory passes the relevance gate!
|
|
13760
14004
|
|
|
13761
14005
|
**SCOPE DETERMINES WHERE MEMORIES SURFACE**:
|
|
13762
14006
|
- scope: 'global' → surfaces in ALL projects (personal facts, philosophy, preferences)
|
|
@@ -13824,6 +14068,21 @@ Return ONLY this JSON structure:
|
|
|
13824
14068
|
}
|
|
13825
14069
|
]
|
|
13826
14070
|
}`;
|
|
14071
|
+
if (!this._config.personalMemoriesEnabled) {
|
|
14072
|
+
return basePrompt + `
|
|
14073
|
+
|
|
14074
|
+
---
|
|
14075
|
+
|
|
14076
|
+
**IMPORTANT: PERSONAL MEMORIES DISABLED**
|
|
14077
|
+
|
|
14078
|
+
The user has disabled personal memory extraction. Do NOT extract any memories with:
|
|
14079
|
+
- context_type: "personal"
|
|
14080
|
+
- scope: "global" when the content is about the user's personal life, relationships, family, or emotional states
|
|
14081
|
+
- Content about the user's preferences, feelings, personal opinions, or relationship dynamics
|
|
14082
|
+
|
|
14083
|
+
Focus ONLY on technical, architectural, debugging, decision, workflow, and project-related memories. Skip any content that would reveal personal information about the user.`;
|
|
14084
|
+
}
|
|
14085
|
+
return basePrompt;
|
|
13827
14086
|
}
|
|
13828
14087
|
parseCurationResponse(responseJson) {
|
|
13829
14088
|
try {
|
|
@@ -13914,33 +14173,35 @@ Return ONLY this JSON structure:
|
|
|
13914
14173
|
_clamp(value, min, max) {
|
|
13915
14174
|
return Math.max(min, Math.min(max, value));
|
|
13916
14175
|
}
|
|
13917
|
-
async curateWithSDK(
|
|
14176
|
+
async curateWithSDK(messages, triggerType = "session_end") {
|
|
13918
14177
|
if (!this._config.apiKey) {
|
|
13919
|
-
throw new Error("API key required for SDK mode");
|
|
14178
|
+
throw new Error("API key required for SDK mode. Set ANTHROPIC_API_KEY environment variable.");
|
|
13920
14179
|
}
|
|
13921
14180
|
const { default: Anthropic2 } = await Promise.resolve().then(() => (init_sdk(), exports_sdk));
|
|
13922
14181
|
const client = new Anthropic2({ apiKey: this._config.apiKey });
|
|
13923
|
-
const
|
|
14182
|
+
const systemPrompt = this.buildCurationPrompt(triggerType);
|
|
14183
|
+
const conversationMessages = [
|
|
14184
|
+
...messages,
|
|
14185
|
+
{
|
|
14186
|
+
role: "user",
|
|
14187
|
+
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."
|
|
14188
|
+
}
|
|
14189
|
+
];
|
|
13924
14190
|
const response = await client.messages.create({
|
|
13925
14191
|
model: "claude-sonnet-4-20250514",
|
|
13926
14192
|
max_tokens: 8192,
|
|
13927
|
-
|
|
13928
|
-
|
|
13929
|
-
role: "user",
|
|
13930
|
-
content: `${conversationContext}
|
|
13931
|
-
|
|
13932
|
-
---
|
|
13933
|
-
|
|
13934
|
-
${prompt}`
|
|
13935
|
-
}
|
|
13936
|
-
]
|
|
14193
|
+
system: systemPrompt,
|
|
14194
|
+
messages: conversationMessages
|
|
13937
14195
|
});
|
|
13938
14196
|
const content = response.content[0];
|
|
13939
14197
|
if (content.type !== "text") {
|
|
13940
|
-
throw new Error("Unexpected response type");
|
|
14198
|
+
throw new Error("Unexpected response type from Claude API");
|
|
13941
14199
|
}
|
|
13942
14200
|
return this.parseCurationResponse(content.text);
|
|
13943
14201
|
}
|
|
14202
|
+
async curateFromSegment(segment, triggerType = "session_end") {
|
|
14203
|
+
return this.curateWithSDK(segment.messages, triggerType);
|
|
14204
|
+
}
|
|
13944
14205
|
async curateWithCLI(sessionId, triggerType = "session_end", cwd, cliTypeOverride) {
|
|
13945
14206
|
const type = cliTypeOverride ?? this._config.cliType;
|
|
13946
14207
|
const systemPrompt = this.buildCurationPrompt(triggerType);
|