@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/server/index.js
CHANGED
|
@@ -19885,6 +19885,11 @@ var managementLogSchema = {
|
|
|
19885
19885
|
error: "string",
|
|
19886
19886
|
details: "string"
|
|
19887
19887
|
};
|
|
19888
|
+
var personalPrimerSchema = {
|
|
19889
|
+
content: "string",
|
|
19890
|
+
session_updated: "number",
|
|
19891
|
+
updated_by: "string"
|
|
19892
|
+
};
|
|
19888
19893
|
|
|
19889
19894
|
// src/core/store.ts
|
|
19890
19895
|
var PERSONAL_PRIMER_ID = "personal-primer";
|
|
@@ -19906,7 +19911,7 @@ class MemoryStore {
|
|
|
19906
19911
|
return this._global;
|
|
19907
19912
|
}
|
|
19908
19913
|
const globalPath = this._config.globalPath;
|
|
19909
|
-
console.log(`\uD83C\uDF10 [DEBUG]
|
|
19914
|
+
console.log(`\uD83C\uDF10 [DEBUG] Initializing global database at ${globalPath}`);
|
|
19910
19915
|
const db = createDatabase({
|
|
19911
19916
|
name: "global",
|
|
19912
19917
|
basePath: globalPath
|
|
@@ -19923,8 +19928,14 @@ class MemoryStore {
|
|
|
19923
19928
|
autoSave: true,
|
|
19924
19929
|
watchFiles: this._config.watchFiles
|
|
19925
19930
|
});
|
|
19926
|
-
|
|
19927
|
-
|
|
19931
|
+
const primer = db.collection("primer", {
|
|
19932
|
+
schema: personalPrimerSchema,
|
|
19933
|
+
contentColumn: "content",
|
|
19934
|
+
autoSave: true,
|
|
19935
|
+
watchFiles: this._config.watchFiles
|
|
19936
|
+
});
|
|
19937
|
+
await Promise.all([memories.load(), managementLogs.load(), primer.load()]);
|
|
19938
|
+
this._global = { db, memories, managementLogs, primer };
|
|
19928
19939
|
return this._global;
|
|
19929
19940
|
}
|
|
19930
19941
|
async getGlobalMemories() {
|
|
@@ -20001,40 +20012,31 @@ class MemoryStore {
|
|
|
20001
20012
|
return id;
|
|
20002
20013
|
}
|
|
20003
20014
|
async getPersonalPrimer() {
|
|
20004
|
-
const {
|
|
20005
|
-
const
|
|
20006
|
-
if (!
|
|
20015
|
+
const { primer } = await this.getGlobal();
|
|
20016
|
+
const record = primer.get(PERSONAL_PRIMER_ID);
|
|
20017
|
+
if (!record) {
|
|
20007
20018
|
return null;
|
|
20008
20019
|
}
|
|
20009
20020
|
return {
|
|
20010
|
-
content:
|
|
20011
|
-
updated:
|
|
20021
|
+
content: record.content,
|
|
20022
|
+
updated: record.updated
|
|
20012
20023
|
};
|
|
20013
20024
|
}
|
|
20014
|
-
async setPersonalPrimer(content) {
|
|
20015
|
-
const {
|
|
20016
|
-
const existing =
|
|
20025
|
+
async setPersonalPrimer(content, sessionNumber, updatedBy = "user") {
|
|
20026
|
+
const { primer } = await this.getGlobal();
|
|
20027
|
+
const existing = primer.get(PERSONAL_PRIMER_ID);
|
|
20017
20028
|
if (existing) {
|
|
20018
|
-
|
|
20029
|
+
primer.update(PERSONAL_PRIMER_ID, {
|
|
20030
|
+
content,
|
|
20031
|
+
session_updated: sessionNumber ?? existing.session_updated,
|
|
20032
|
+
updated_by: updatedBy
|
|
20033
|
+
});
|
|
20019
20034
|
} else {
|
|
20020
|
-
|
|
20035
|
+
primer.insert({
|
|
20021
20036
|
id: PERSONAL_PRIMER_ID,
|
|
20022
20037
|
content,
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
confidence_score: 1,
|
|
20026
|
-
context_type: "personal",
|
|
20027
|
-
temporal_relevance: "persistent",
|
|
20028
|
-
knowledge_domain: "personal",
|
|
20029
|
-
emotional_resonance: "neutral",
|
|
20030
|
-
action_required: false,
|
|
20031
|
-
problem_solution_pair: false,
|
|
20032
|
-
semantic_tags: ["personal", "primer", "relationship"],
|
|
20033
|
-
trigger_phrases: [],
|
|
20034
|
-
question_types: [],
|
|
20035
|
-
session_id: "system",
|
|
20036
|
-
project_id: "global",
|
|
20037
|
-
embedding: null
|
|
20038
|
+
session_updated: sessionNumber ?? 0,
|
|
20039
|
+
updated_by: updatedBy
|
|
20038
20040
|
});
|
|
20039
20041
|
}
|
|
20040
20042
|
}
|
|
@@ -20054,6 +20056,7 @@ class MemoryStore {
|
|
|
20054
20056
|
success: entry.success,
|
|
20055
20057
|
duration_ms: entry.durationMs,
|
|
20056
20058
|
summary: entry.summary,
|
|
20059
|
+
full_report: entry.fullReport ?? "",
|
|
20057
20060
|
error: entry.error ?? "",
|
|
20058
20061
|
details: entry.details ? JSON.stringify(entry.details) : ""
|
|
20059
20062
|
});
|
|
@@ -20417,7 +20420,14 @@ var sym = {
|
|
|
20417
20420
|
fire: "\uD83D\uDD25",
|
|
20418
20421
|
target: "\uD83C\uDFAF"
|
|
20419
20422
|
};
|
|
20423
|
+
var _verbose = false;
|
|
20420
20424
|
var logger = {
|
|
20425
|
+
setVerbose(enabled) {
|
|
20426
|
+
_verbose = enabled;
|
|
20427
|
+
},
|
|
20428
|
+
isVerbose() {
|
|
20429
|
+
return _verbose;
|
|
20430
|
+
},
|
|
20421
20431
|
info(message) {
|
|
20422
20432
|
console.log(`${timestamp()} ${style("cyan", sym.info)} ${message}`);
|
|
20423
20433
|
},
|
|
@@ -20489,7 +20499,14 @@ var logger = {
|
|
|
20489
20499
|
problem_solution: "✅",
|
|
20490
20500
|
project_context: "\uD83D\uDCE6",
|
|
20491
20501
|
milestone: "\uD83C\uDFC6",
|
|
20492
|
-
general: "\uD83D\uDCDD"
|
|
20502
|
+
general: "\uD83D\uDCDD",
|
|
20503
|
+
project_state: "\uD83D\uDCCD",
|
|
20504
|
+
pending_task: "⏳",
|
|
20505
|
+
work_in_progress: "\uD83D\uDD28",
|
|
20506
|
+
system_feedback: "\uD83D\uDCE3",
|
|
20507
|
+
project_milestone: "\uD83C\uDFC6",
|
|
20508
|
+
architectural_insight: "\uD83C\uDFDB️",
|
|
20509
|
+
architectural_direction: "\uD83E\uDDED"
|
|
20493
20510
|
};
|
|
20494
20511
|
console.log();
|
|
20495
20512
|
console.log(`${timestamp()} ${style("cyan", sym.sparkles)} ${style("bold", `SURFACING ${memories.length} MEMORIES`)}`);
|
|
@@ -20501,11 +20518,12 @@ var logger = {
|
|
|
20501
20518
|
return;
|
|
20502
20519
|
}
|
|
20503
20520
|
memories.forEach((m, i) => {
|
|
20504
|
-
const
|
|
20521
|
+
const signalCount = Math.round(m.score * 6);
|
|
20522
|
+
const signalStr = style("green", `${signalCount}sig`);
|
|
20505
20523
|
const emoji = emojiMap[m.context_type?.toLowerCase()] ?? "\uD83D\uDCDD";
|
|
20506
20524
|
const num = style("dim", `${i + 1}.`);
|
|
20507
20525
|
const preview = m.content.length > 55 ? m.content.slice(0, 55) + style("dim", "...") : m.content;
|
|
20508
|
-
console.log(` ${num} [${
|
|
20526
|
+
console.log(` ${num} [${signalStr}] ${emoji}`);
|
|
20509
20527
|
console.log(` ${preview}`);
|
|
20510
20528
|
});
|
|
20511
20529
|
console.log();
|
|
@@ -20553,44 +20571,124 @@ var logger = {
|
|
|
20553
20571
|
console.log(` ${style("dim", "processing:")} ${memoriesCount} new memories`);
|
|
20554
20572
|
},
|
|
20555
20573
|
logManagementComplete(result) {
|
|
20574
|
+
const formatAction = (action, truncate = true) => {
|
|
20575
|
+
let icon = " •";
|
|
20576
|
+
if (action.startsWith("READ OK"))
|
|
20577
|
+
icon = style("dim", " \uD83D\uDCD6");
|
|
20578
|
+
else if (action.startsWith("READ FAILED"))
|
|
20579
|
+
icon = style("red", " ❌");
|
|
20580
|
+
else if (action.startsWith("WRITE OK"))
|
|
20581
|
+
icon = style("green", " ✏️");
|
|
20582
|
+
else if (action.startsWith("WRITE FAILED"))
|
|
20583
|
+
icon = style("red", " ❌");
|
|
20584
|
+
else if (action.startsWith("RECEIVED"))
|
|
20585
|
+
icon = style("cyan", " \uD83D\uDCE5");
|
|
20586
|
+
else if (action.startsWith("CREATED"))
|
|
20587
|
+
icon = style("green", " ✨");
|
|
20588
|
+
else if (action.startsWith("UPDATED"))
|
|
20589
|
+
icon = style("blue", " \uD83D\uDCDD");
|
|
20590
|
+
else if (action.startsWith("SUPERSEDED"))
|
|
20591
|
+
icon = style("yellow", " \uD83D\uDD04");
|
|
20592
|
+
else if (action.startsWith("RESOLVED"))
|
|
20593
|
+
icon = style("green", " ✅");
|
|
20594
|
+
else if (action.startsWith("LINKED"))
|
|
20595
|
+
icon = style("cyan", " \uD83D\uDD17");
|
|
20596
|
+
else if (action.startsWith("PRIMER"))
|
|
20597
|
+
icon = style("magenta", " \uD83D\uDC9C");
|
|
20598
|
+
else if (action.startsWith("SKIPPED"))
|
|
20599
|
+
icon = style("dim", " ⏭️");
|
|
20600
|
+
else if (action.startsWith("NO_ACTION"))
|
|
20601
|
+
icon = style("dim", " ◦");
|
|
20602
|
+
const text = truncate && action.length > 70 ? action.slice(0, 67) + "..." : action;
|
|
20603
|
+
return `${icon} ${style("dim", text)}`;
|
|
20604
|
+
};
|
|
20556
20605
|
if (result.success) {
|
|
20557
20606
|
console.log(` ${style("green", sym.check)} ${style("bold", "Completed")}`);
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20607
|
+
if (_verbose) {
|
|
20608
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20609
|
+
console.log(` ${style("cyan", "\uD83D\uDCCA")} ${style("bold", "Statistics")}`);
|
|
20610
|
+
const filesRead = result.filesRead ?? 0;
|
|
20611
|
+
const filesWritten = result.filesWritten ?? 0;
|
|
20612
|
+
console.log(` ${style("dim", "Files read:")} ${filesRead > 0 ? style("green", String(filesRead)) : style("dim", "0")}`);
|
|
20613
|
+
console.log(` ${style("dim", "Files written:")} ${filesWritten > 0 ? style("green", String(filesWritten)) : style("dim", "0")}`);
|
|
20614
|
+
const superseded = result.superseded ?? 0;
|
|
20615
|
+
const resolved = result.resolved ?? 0;
|
|
20616
|
+
const linked = result.linked ?? 0;
|
|
20617
|
+
console.log(` ${style("dim", "Superseded:")} ${superseded > 0 ? style("yellow", String(superseded)) : style("dim", "0")}`);
|
|
20618
|
+
console.log(` ${style("dim", "Resolved:")} ${resolved > 0 ? style("green", String(resolved)) : style("dim", "0")}`);
|
|
20619
|
+
console.log(` ${style("dim", "Linked:")} ${linked > 0 ? style("cyan", String(linked)) : style("dim", "0")}`);
|
|
20620
|
+
console.log(` ${style("dim", "Primer:")} ${result.primerUpdated ? style("magenta", "updated") : style("dim", "unchanged")}`);
|
|
20621
|
+
if (result.actions && result.actions.length > 0) {
|
|
20622
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20623
|
+
console.log(` ${style("cyan", "\uD83C\uDFAC")} ${style("bold", "Actions")} ${style("dim", `(${result.actions.length} total)`)}`);
|
|
20624
|
+
for (const action of result.actions) {
|
|
20625
|
+
console.log(` ${formatAction(action, false)}`);
|
|
20626
|
+
}
|
|
20627
|
+
}
|
|
20628
|
+
if (result.fullReport) {
|
|
20629
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20630
|
+
console.log(` ${style("cyan", "\uD83D\uDCCB")} ${style("bold", "Full Report")}`);
|
|
20631
|
+
const reportLines = result.fullReport.split(`
|
|
20632
|
+
`);
|
|
20633
|
+
for (const line of reportLines) {
|
|
20634
|
+
if (line.includes("===")) {
|
|
20635
|
+
console.log(` ${style("bold", line)}`);
|
|
20636
|
+
} else if (line.match(/^[A-Z_]+:/)) {
|
|
20637
|
+
console.log(` ${style("cyan", line)}`);
|
|
20638
|
+
} else {
|
|
20639
|
+
console.log(` ${style("dim", line)}`);
|
|
20640
|
+
}
|
|
20641
|
+
}
|
|
20642
|
+
}
|
|
20643
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20573
20644
|
} else {
|
|
20574
|
-
|
|
20575
|
-
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20645
|
+
const stats = [];
|
|
20646
|
+
if (result.superseded && result.superseded > 0)
|
|
20647
|
+
stats.push(`${result.superseded} superseded`);
|
|
20648
|
+
if (result.resolved && result.resolved > 0)
|
|
20649
|
+
stats.push(`${result.resolved} resolved`);
|
|
20650
|
+
if (result.linked && result.linked > 0)
|
|
20651
|
+
stats.push(`${result.linked} linked`);
|
|
20652
|
+
if (result.primerUpdated)
|
|
20653
|
+
stats.push("primer updated");
|
|
20654
|
+
if (stats.length > 0) {
|
|
20655
|
+
console.log(` ${style("dim", "changes:")} ${stats.join(style("dim", ", "))}`);
|
|
20656
|
+
} else {
|
|
20657
|
+
console.log(` ${style("dim", "changes:")} none (memories are current)`);
|
|
20658
|
+
}
|
|
20659
|
+
if (result.actions && result.actions.length > 0) {
|
|
20660
|
+
console.log(` ${style("dim", "actions:")}`);
|
|
20661
|
+
for (const action of result.actions.slice(0, 10)) {
|
|
20662
|
+
console.log(` ${formatAction(action, true)}`);
|
|
20663
|
+
}
|
|
20664
|
+
if (result.actions.length > 10) {
|
|
20665
|
+
console.log(` ${style("dim", ` ... and ${result.actions.length - 10} more actions`)}`);
|
|
20666
|
+
}
|
|
20667
|
+
}
|
|
20579
20668
|
}
|
|
20580
20669
|
} else {
|
|
20581
20670
|
console.log(` ${style("yellow", sym.warning)} ${style("bold", "Failed")}`);
|
|
20582
20671
|
if (result.error) {
|
|
20583
|
-
console.log(` ${style("
|
|
20672
|
+
console.log(` ${style("red", "error:")} ${result.error}`);
|
|
20673
|
+
}
|
|
20674
|
+
if (result.fullReport) {
|
|
20675
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20676
|
+
console.log(` ${style("red", "\uD83D\uDCCB")} ${style("bold", "Error Report:")}`);
|
|
20677
|
+
const reportLines = result.fullReport.split(`
|
|
20678
|
+
`);
|
|
20679
|
+
for (const line of reportLines) {
|
|
20680
|
+
console.log(` ${style("dim", line)}`);
|
|
20681
|
+
}
|
|
20584
20682
|
}
|
|
20585
20683
|
}
|
|
20586
20684
|
console.log();
|
|
20587
20685
|
},
|
|
20588
20686
|
logRetrievalScoring(params) {
|
|
20589
|
-
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, selectedMemories } = params;
|
|
20687
|
+
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, durationMs, selectedMemories } = params;
|
|
20688
|
+
const timeStr = durationMs !== undefined ? style("cyan", `${durationMs.toFixed(1)}ms`) : "";
|
|
20590
20689
|
console.log();
|
|
20591
|
-
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "
|
|
20592
|
-
console.log(` ${style("dim", "total:")} ${totalMemories}
|
|
20593
|
-
console.log(` ${style("dim", "pre-filtered:")} ${preFiltered} (inactive/excluded/scope)`);
|
|
20690
|
+
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "RETRIEVAL")} ${timeStr}`);
|
|
20691
|
+
console.log(` ${style("dim", "total:")} ${totalMemories} → ${style("dim", "filtered:")} ${preFiltered} → ${style("dim", "candidates:")} ${totalMemories - preFiltered}`);
|
|
20594
20692
|
console.log(` ${style("dim", "already injected:")} ${alreadyInjected}`);
|
|
20595
20693
|
const msgPreview = currentMessage.length > 60 ? currentMessage.slice(0, 60) + "..." : currentMessage;
|
|
20596
20694
|
console.log(` ${style("dim", "message:")} "${msgPreview}"`);
|
|
@@ -20609,347 +20707,471 @@ var logger = {
|
|
|
20609
20707
|
console.log();
|
|
20610
20708
|
selectedMemories.forEach((m, i) => {
|
|
20611
20709
|
const num = style("dim", `${i + 1}.`);
|
|
20612
|
-
const
|
|
20613
|
-
const
|
|
20614
|
-
const corr = style("magenta", `corr:${(m.corroboration_score * 100).toFixed(0)}%`);
|
|
20710
|
+
const signalsStr = style("green", `${m.signalCount} signals`);
|
|
20711
|
+
const imp = style("magenta", `imp:${(m.importance_weight * 100).toFixed(0)}%`);
|
|
20615
20712
|
const type = style("yellow", m.context_type.toUpperCase());
|
|
20616
|
-
const scope = m.isGlobal ? style("blue", "[G]") : "";
|
|
20617
|
-
console.log(` ${num} [${
|
|
20713
|
+
const scope = m.isGlobal ? style("blue", " [G]") : "";
|
|
20714
|
+
console.log(` ${num} [${signalsStr} • ${imp}] ${type}${scope}`);
|
|
20618
20715
|
const preview = m.content.length > 60 ? m.content.slice(0, 60) + style("dim", "...") : m.content;
|
|
20619
20716
|
console.log(` ${style("white", preview)}`);
|
|
20620
|
-
const
|
|
20621
|
-
if (
|
|
20622
|
-
|
|
20717
|
+
const firedSignals = [];
|
|
20718
|
+
if (m.signals.trigger) {
|
|
20719
|
+
firedSignals.push(`trigger:${(m.signals.triggerStrength * 100).toFixed(0)}%`);
|
|
20720
|
+
}
|
|
20721
|
+
if (m.signals.tags) {
|
|
20722
|
+
firedSignals.push(`tags:${m.signals.tagCount}`);
|
|
20623
20723
|
}
|
|
20624
|
-
if (m.
|
|
20625
|
-
|
|
20724
|
+
if (m.signals.domain)
|
|
20725
|
+
firedSignals.push("domain");
|
|
20726
|
+
if (m.signals.feature)
|
|
20727
|
+
firedSignals.push("feature");
|
|
20728
|
+
if (m.signals.content)
|
|
20729
|
+
firedSignals.push("content");
|
|
20730
|
+
if (m.signals.vector) {
|
|
20731
|
+
firedSignals.push(`vector:${(m.signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
20732
|
+
}
|
|
20733
|
+
if (firedSignals.length > 0) {
|
|
20734
|
+
console.log(` ${style("cyan", "signals:")} ${firedSignals.join(", ")}`);
|
|
20626
20735
|
}
|
|
20627
20736
|
console.log();
|
|
20628
20737
|
});
|
|
20738
|
+
},
|
|
20739
|
+
logScoreDistribution(params) {
|
|
20740
|
+
const { totalCandidates, passedGatekeeper, rejectedByGatekeeper, buckets, stats, signalBreakdown } = params;
|
|
20741
|
+
console.log();
|
|
20742
|
+
console.log(style("dim", " ─".repeat(30)));
|
|
20743
|
+
console.log(` ${style("bold", "ACTIVATION SIGNALS")}`);
|
|
20744
|
+
console.log();
|
|
20745
|
+
const passRate = totalCandidates > 0 ? (passedGatekeeper / totalCandidates * 100).toFixed(0) : "0";
|
|
20746
|
+
console.log(` ${style("dim", "Activated:")} ${style("green", String(passedGatekeeper))}/${totalCandidates} (${passRate}%)`);
|
|
20747
|
+
console.log(` ${style("dim", "Rejected:")} ${rejectedByGatekeeper} (< 2 signals)`);
|
|
20748
|
+
console.log();
|
|
20749
|
+
if (signalBreakdown && signalBreakdown.total > 0) {
|
|
20750
|
+
console.log(` ${style("cyan", "Signal Breakdown:")}`);
|
|
20751
|
+
const signals = [
|
|
20752
|
+
{ name: "trigger", count: signalBreakdown.trigger },
|
|
20753
|
+
{ name: "tags", count: signalBreakdown.tags },
|
|
20754
|
+
{ name: "domain", count: signalBreakdown.domain },
|
|
20755
|
+
{ name: "feature", count: signalBreakdown.feature },
|
|
20756
|
+
{ name: "content", count: signalBreakdown.content },
|
|
20757
|
+
{ name: "vector", count: signalBreakdown.vector }
|
|
20758
|
+
];
|
|
20759
|
+
for (const sig of signals) {
|
|
20760
|
+
const pct = (sig.count / signalBreakdown.total * 100).toFixed(0);
|
|
20761
|
+
const bar = "█".repeat(Math.round(sig.count / signalBreakdown.total * 20));
|
|
20762
|
+
console.log(` ${sig.name.padEnd(8)} ${bar.padEnd(20)} ${sig.count} (${pct}%)`);
|
|
20763
|
+
}
|
|
20764
|
+
console.log();
|
|
20765
|
+
}
|
|
20766
|
+
if (stats.max > 0) {
|
|
20767
|
+
console.log(` ${style("cyan", "Signals:")} min=${stats.min} max=${stats.max} mean=${stats.mean}`);
|
|
20768
|
+
console.log();
|
|
20769
|
+
}
|
|
20770
|
+
if (Object.keys(buckets).length > 0) {
|
|
20771
|
+
console.log(` ${style("bold", "Distribution:")}`);
|
|
20772
|
+
const maxBucketCount = Math.max(...Object.values(buckets), 1);
|
|
20773
|
+
const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals"];
|
|
20774
|
+
for (const bucket of bucketOrder) {
|
|
20775
|
+
const count = buckets[bucket] ?? 0;
|
|
20776
|
+
if (count > 0 || bucket === "2 signals") {
|
|
20777
|
+
const barLen = Math.round(count / maxBucketCount * 25);
|
|
20778
|
+
const bar = "█".repeat(barLen) + style("dim", "░".repeat(25 - barLen));
|
|
20779
|
+
const countStr = count.toString().padStart(3);
|
|
20780
|
+
console.log(` ${style("dim", bucket.padEnd(10))} ${bar} ${style("cyan", countStr)}`);
|
|
20781
|
+
}
|
|
20782
|
+
}
|
|
20783
|
+
console.log();
|
|
20784
|
+
}
|
|
20629
20785
|
}
|
|
20630
20786
|
};
|
|
20631
20787
|
|
|
20632
20788
|
// src/core/retrieval.ts
|
|
20633
|
-
var
|
|
20634
|
-
|
|
20635
|
-
|
|
20636
|
-
|
|
20637
|
-
|
|
20638
|
-
|
|
20639
|
-
|
|
20640
|
-
|
|
20641
|
-
|
|
20642
|
-
technical: ["implement", "code", "function", "class", "module", "api", "interface"]
|
|
20643
|
-
};
|
|
20644
|
-
var TEMPORAL_CLASS_SCORES = {
|
|
20645
|
-
eternal: 1,
|
|
20646
|
-
long_term: 0.9,
|
|
20647
|
-
medium_term: 0.7,
|
|
20648
|
-
short_term: 0.5,
|
|
20649
|
-
ephemeral: 0.3
|
|
20789
|
+
var GLOBAL_TYPE_PRIORITY = {
|
|
20790
|
+
technical: 1,
|
|
20791
|
+
preference: 2,
|
|
20792
|
+
architectural: 3,
|
|
20793
|
+
workflow: 4,
|
|
20794
|
+
decision: 5,
|
|
20795
|
+
breakthrough: 6,
|
|
20796
|
+
philosophy: 7,
|
|
20797
|
+
personal: 8
|
|
20650
20798
|
};
|
|
20799
|
+
var MIN_ACTIVATION_SIGNALS = 2;
|
|
20800
|
+
var STOPWORDS = new Set([
|
|
20801
|
+
"the",
|
|
20802
|
+
"is",
|
|
20803
|
+
"are",
|
|
20804
|
+
"was",
|
|
20805
|
+
"were",
|
|
20806
|
+
"to",
|
|
20807
|
+
"a",
|
|
20808
|
+
"an",
|
|
20809
|
+
"and",
|
|
20810
|
+
"or",
|
|
20811
|
+
"but",
|
|
20812
|
+
"in",
|
|
20813
|
+
"on",
|
|
20814
|
+
"at",
|
|
20815
|
+
"for",
|
|
20816
|
+
"with",
|
|
20817
|
+
"about",
|
|
20818
|
+
"when",
|
|
20819
|
+
"how",
|
|
20820
|
+
"what",
|
|
20821
|
+
"why",
|
|
20822
|
+
"where",
|
|
20823
|
+
"this",
|
|
20824
|
+
"that",
|
|
20825
|
+
"it",
|
|
20826
|
+
"of",
|
|
20827
|
+
"be",
|
|
20828
|
+
"have",
|
|
20829
|
+
"do",
|
|
20830
|
+
"does",
|
|
20831
|
+
"did",
|
|
20832
|
+
"will",
|
|
20833
|
+
"would",
|
|
20834
|
+
"could",
|
|
20835
|
+
"should",
|
|
20836
|
+
"can",
|
|
20837
|
+
"may",
|
|
20838
|
+
"might",
|
|
20839
|
+
"must",
|
|
20840
|
+
"shall",
|
|
20841
|
+
"has",
|
|
20842
|
+
"had",
|
|
20843
|
+
"been",
|
|
20844
|
+
"being",
|
|
20845
|
+
"i",
|
|
20846
|
+
"you",
|
|
20847
|
+
"we",
|
|
20848
|
+
"they",
|
|
20849
|
+
"he",
|
|
20850
|
+
"she",
|
|
20851
|
+
"my",
|
|
20852
|
+
"your",
|
|
20853
|
+
"our",
|
|
20854
|
+
"its",
|
|
20855
|
+
"his",
|
|
20856
|
+
"her",
|
|
20857
|
+
"their",
|
|
20858
|
+
"if",
|
|
20859
|
+
"then",
|
|
20860
|
+
"else",
|
|
20861
|
+
"so",
|
|
20862
|
+
"as",
|
|
20863
|
+
"from",
|
|
20864
|
+
"by",
|
|
20865
|
+
"into",
|
|
20866
|
+
"through",
|
|
20867
|
+
"during",
|
|
20868
|
+
"before",
|
|
20869
|
+
"after",
|
|
20870
|
+
"also",
|
|
20871
|
+
"now",
|
|
20872
|
+
"back",
|
|
20873
|
+
"get",
|
|
20874
|
+
"go",
|
|
20875
|
+
"come",
|
|
20876
|
+
"let",
|
|
20877
|
+
"like",
|
|
20878
|
+
"just",
|
|
20879
|
+
"know",
|
|
20880
|
+
"think",
|
|
20881
|
+
"see",
|
|
20882
|
+
"look",
|
|
20883
|
+
"make",
|
|
20884
|
+
"take",
|
|
20885
|
+
"want",
|
|
20886
|
+
"need"
|
|
20887
|
+
]);
|
|
20651
20888
|
|
|
20652
20889
|
class SmartVectorRetrieval {
|
|
20653
20890
|
_extractSignificantWords(text) {
|
|
20654
|
-
const
|
|
20655
|
-
"the",
|
|
20656
|
-
"is",
|
|
20657
|
-
"are",
|
|
20658
|
-
"was",
|
|
20659
|
-
"were",
|
|
20660
|
-
"to",
|
|
20661
|
-
"a",
|
|
20662
|
-
"an",
|
|
20663
|
-
"and",
|
|
20664
|
-
"or",
|
|
20665
|
-
"but",
|
|
20666
|
-
"in",
|
|
20667
|
-
"on",
|
|
20668
|
-
"at",
|
|
20669
|
-
"for",
|
|
20670
|
-
"with",
|
|
20671
|
-
"about",
|
|
20672
|
-
"when",
|
|
20673
|
-
"how",
|
|
20674
|
-
"what",
|
|
20675
|
-
"why",
|
|
20676
|
-
"where",
|
|
20677
|
-
"this",
|
|
20678
|
-
"that",
|
|
20679
|
-
"it",
|
|
20680
|
-
"of",
|
|
20681
|
-
"be",
|
|
20682
|
-
"have",
|
|
20683
|
-
"do",
|
|
20684
|
-
"does",
|
|
20685
|
-
"did",
|
|
20686
|
-
"will",
|
|
20687
|
-
"would",
|
|
20688
|
-
"could",
|
|
20689
|
-
"should",
|
|
20690
|
-
"can",
|
|
20691
|
-
"may",
|
|
20692
|
-
"might",
|
|
20693
|
-
"must",
|
|
20694
|
-
"shall",
|
|
20695
|
-
"has",
|
|
20696
|
-
"had",
|
|
20697
|
-
"been",
|
|
20698
|
-
"being",
|
|
20699
|
-
"i",
|
|
20700
|
-
"you",
|
|
20701
|
-
"we",
|
|
20702
|
-
"they",
|
|
20703
|
-
"he",
|
|
20704
|
-
"she",
|
|
20705
|
-
"my",
|
|
20706
|
-
"your",
|
|
20707
|
-
"our",
|
|
20708
|
-
"its",
|
|
20709
|
-
"his",
|
|
20710
|
-
"her",
|
|
20711
|
-
"their",
|
|
20712
|
-
"if",
|
|
20713
|
-
"then",
|
|
20714
|
-
"else",
|
|
20715
|
-
"so",
|
|
20716
|
-
"as",
|
|
20717
|
-
"from",
|
|
20718
|
-
"by",
|
|
20719
|
-
"into",
|
|
20720
|
-
"through",
|
|
20721
|
-
"during",
|
|
20722
|
-
"before",
|
|
20723
|
-
"after",
|
|
20724
|
-
"above",
|
|
20725
|
-
"below",
|
|
20726
|
-
"up",
|
|
20727
|
-
"down",
|
|
20728
|
-
"out",
|
|
20729
|
-
"off",
|
|
20730
|
-
"over",
|
|
20731
|
-
"under",
|
|
20732
|
-
"again",
|
|
20733
|
-
"further",
|
|
20734
|
-
"once",
|
|
20735
|
-
"here",
|
|
20736
|
-
"there",
|
|
20737
|
-
"all",
|
|
20738
|
-
"each",
|
|
20739
|
-
"few",
|
|
20740
|
-
"more",
|
|
20741
|
-
"most",
|
|
20742
|
-
"other",
|
|
20743
|
-
"some",
|
|
20744
|
-
"such",
|
|
20745
|
-
"no",
|
|
20746
|
-
"nor",
|
|
20747
|
-
"not",
|
|
20748
|
-
"only",
|
|
20749
|
-
"own",
|
|
20750
|
-
"same",
|
|
20751
|
-
"than",
|
|
20752
|
-
"too",
|
|
20753
|
-
"very",
|
|
20754
|
-
"just",
|
|
20755
|
-
"also",
|
|
20756
|
-
"now",
|
|
20757
|
-
"back",
|
|
20758
|
-
"get",
|
|
20759
|
-
"got",
|
|
20760
|
-
"go",
|
|
20761
|
-
"going",
|
|
20762
|
-
"gone",
|
|
20763
|
-
"come",
|
|
20764
|
-
"came",
|
|
20765
|
-
"let",
|
|
20766
|
-
"lets",
|
|
20767
|
-
"hey",
|
|
20768
|
-
"hi",
|
|
20769
|
-
"hello",
|
|
20770
|
-
"ok",
|
|
20771
|
-
"okay"
|
|
20772
|
-
]);
|
|
20773
|
-
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
|
|
20891
|
+
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !STOPWORDS.has(w));
|
|
20774
20892
|
return new Set(words);
|
|
20775
20893
|
}
|
|
20776
|
-
_detectContextTypes(message) {
|
|
20777
|
-
const messageLower = message.toLowerCase();
|
|
20778
|
-
const detected = new Set;
|
|
20779
|
-
for (const [type, keywords] of Object.entries(TYPE_KEYWORDS)) {
|
|
20780
|
-
for (const keyword of keywords) {
|
|
20781
|
-
if (messageLower.includes(keyword)) {
|
|
20782
|
-
detected.add(type);
|
|
20783
|
-
break;
|
|
20784
|
-
}
|
|
20785
|
-
}
|
|
20786
|
-
}
|
|
20787
|
-
return detected;
|
|
20788
|
-
}
|
|
20789
20894
|
_preFilter(memories, currentProjectId, messageLower) {
|
|
20790
20895
|
return memories.filter((memory) => {
|
|
20791
|
-
if (memory.status && memory.status !== "active")
|
|
20896
|
+
if (memory.status && memory.status !== "active")
|
|
20792
20897
|
return false;
|
|
20793
|
-
|
|
20794
|
-
if (memory.exclude_from_retrieval === true) {
|
|
20898
|
+
if (memory.exclude_from_retrieval === true)
|
|
20795
20899
|
return false;
|
|
20796
|
-
|
|
20797
|
-
if (memory.superseded_by) {
|
|
20900
|
+
if (memory.superseded_by)
|
|
20798
20901
|
return false;
|
|
20799
|
-
}
|
|
20800
20902
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
20801
|
-
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
20903
|
+
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
20802
20904
|
return false;
|
|
20803
|
-
}
|
|
20804
20905
|
if (memory.anti_triggers?.length) {
|
|
20805
20906
|
for (const antiTrigger of memory.anti_triggers) {
|
|
20806
|
-
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
20907
|
+
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
20807
20908
|
return false;
|
|
20808
|
-
}
|
|
20809
20909
|
}
|
|
20810
20910
|
}
|
|
20811
20911
|
return true;
|
|
20812
20912
|
});
|
|
20813
20913
|
}
|
|
20814
|
-
|
|
20815
|
-
|
|
20914
|
+
_checkTriggerActivation(messageLower, messageWords, triggerPhrases) {
|
|
20915
|
+
if (!triggerPhrases.length)
|
|
20916
|
+
return { activated: false, strength: 0 };
|
|
20917
|
+
let maxStrength = 0;
|
|
20918
|
+
for (const phrase of triggerPhrases) {
|
|
20919
|
+
const phraseLower = phrase.trim().toLowerCase();
|
|
20920
|
+
const phraseWords = phraseLower.split(/\s+/).filter((w) => !STOPWORDS.has(w) && w.length > 2);
|
|
20921
|
+
if (!phraseWords.length)
|
|
20922
|
+
continue;
|
|
20923
|
+
let matches = 0;
|
|
20924
|
+
for (const word of phraseWords) {
|
|
20925
|
+
if (messageWords.has(word) || messageLower.includes(word)) {
|
|
20926
|
+
matches++;
|
|
20927
|
+
} else if (messageWords.has(word.replace(/s$/, "")) || messageWords.has(word + "s") || messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
20928
|
+
matches += 0.8;
|
|
20929
|
+
}
|
|
20930
|
+
}
|
|
20931
|
+
const strength = phraseWords.length > 0 ? matches / phraseWords.length : 0;
|
|
20932
|
+
maxStrength = Math.max(maxStrength, strength);
|
|
20933
|
+
}
|
|
20934
|
+
return { activated: maxStrength >= 0.5, strength: maxStrength };
|
|
20935
|
+
}
|
|
20936
|
+
_checkTagActivation(messageLower, messageWords, tags) {
|
|
20937
|
+
if (!tags.length)
|
|
20938
|
+
return { activated: false, count: 0 };
|
|
20939
|
+
let matchCount = 0;
|
|
20940
|
+
for (const tag of tags) {
|
|
20941
|
+
const tagLower = tag.trim().toLowerCase();
|
|
20942
|
+
if (messageWords.has(tagLower) || messageLower.includes(tagLower)) {
|
|
20943
|
+
matchCount++;
|
|
20944
|
+
}
|
|
20945
|
+
}
|
|
20946
|
+
const threshold = tags.length <= 2 ? 1 : 2;
|
|
20947
|
+
return { activated: matchCount >= threshold, count: matchCount };
|
|
20948
|
+
}
|
|
20949
|
+
_checkDomainActivation(messageLower, messageWords, domain) {
|
|
20950
|
+
if (!domain)
|
|
20951
|
+
return false;
|
|
20952
|
+
const domainLower = domain.trim().toLowerCase();
|
|
20953
|
+
return messageWords.has(domainLower) || messageLower.includes(domainLower);
|
|
20954
|
+
}
|
|
20955
|
+
_checkFeatureActivation(messageLower, messageWords, feature) {
|
|
20956
|
+
if (!feature)
|
|
20957
|
+
return false;
|
|
20958
|
+
const featureLower = feature.trim().toLowerCase();
|
|
20959
|
+
return messageWords.has(featureLower) || messageLower.includes(featureLower);
|
|
20960
|
+
}
|
|
20961
|
+
_checkContentActivation(messageWords, memory) {
|
|
20962
|
+
const contentPreview = memory.content.slice(0, 200);
|
|
20963
|
+
const contentWords = this._extractSignificantWords(contentPreview);
|
|
20964
|
+
let overlap = 0;
|
|
20965
|
+
for (const word of messageWords) {
|
|
20966
|
+
if (contentWords.has(word))
|
|
20967
|
+
overlap++;
|
|
20968
|
+
}
|
|
20969
|
+
return overlap >= 3;
|
|
20970
|
+
}
|
|
20971
|
+
_vectorDebugSamples = [];
|
|
20972
|
+
_calculateVectorSimilarity(vec1, vec2) {
|
|
20973
|
+
if (!vec1 || !vec2) {
|
|
20974
|
+
return 0;
|
|
20975
|
+
}
|
|
20976
|
+
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
20977
|
+
const v2 = vec2 instanceof Float32Array ? vec2 : new Float32Array(vec2);
|
|
20978
|
+
const similarity = cosineSimilarity(v1, v2);
|
|
20979
|
+
if (this._vectorDebugSamples.length < 20) {
|
|
20980
|
+
this._vectorDebugSamples.push(similarity);
|
|
20981
|
+
}
|
|
20982
|
+
return similarity;
|
|
20983
|
+
}
|
|
20984
|
+
_logVectorStats() {
|
|
20985
|
+
if (this._vectorDebugSamples.length === 0)
|
|
20986
|
+
return;
|
|
20987
|
+
const samples = this._vectorDebugSamples;
|
|
20988
|
+
const min = Math.min(...samples);
|
|
20989
|
+
const max = Math.max(...samples);
|
|
20990
|
+
const avg = samples.reduce((a, b) => a + b, 0) / samples.length;
|
|
20991
|
+
console.log(`[DEBUG] Vector similarities: min=${(min * 100).toFixed(1)}% max=${(max * 100).toFixed(1)}% avg=${(avg * 100).toFixed(1)}% (${samples.length} samples)`);
|
|
20992
|
+
this._vectorDebugSamples = [];
|
|
20993
|
+
}
|
|
20994
|
+
_calculateImportanceScore(memory, signalCount, messageLower, messageWords) {
|
|
20816
20995
|
let score = 0;
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
|
|
20822
|
-
|
|
20823
|
-
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
|
|
20836
|
-
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
|
|
20840
|
-
|
|
20841
|
-
|
|
20842
|
-
|
|
20843
|
-
|
|
20844
|
-
|
|
20845
|
-
|
|
20846
|
-
|
|
20847
|
-
|
|
20848
|
-
signals.push("type:" + memory.context_type);
|
|
20849
|
-
}
|
|
20850
|
-
const triggerMatch = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
|
|
20851
|
-
if (triggerMatch > 0.3) {
|
|
20852
|
-
score += Math.min(0.3, triggerMatch * 0.4);
|
|
20853
|
-
signals.push("trigger:" + triggerMatch.toFixed(2));
|
|
20854
|
-
}
|
|
20855
|
-
if (memory.feature) {
|
|
20856
|
-
const featureLower = memory.feature.toLowerCase();
|
|
20857
|
-
if (messageLower.includes(featureLower) || messageWords.has(featureLower)) {
|
|
20858
|
-
score += 0.2;
|
|
20859
|
-
signals.push("feature:" + memory.feature);
|
|
20860
|
-
}
|
|
20861
|
-
}
|
|
20862
|
-
if (memory.related_files?.length) {
|
|
20863
|
-
for (const file of memory.related_files) {
|
|
20864
|
-
const filename = file.split("/").pop()?.toLowerCase() ?? "";
|
|
20865
|
-
if (filename && messageLower.includes(filename)) {
|
|
20866
|
-
score += 0.25;
|
|
20867
|
-
signals.push("file:" + filename);
|
|
20996
|
+
score += memory.importance_weight ?? 0.5;
|
|
20997
|
+
if (signalCount >= 4)
|
|
20998
|
+
score += 0.2;
|
|
20999
|
+
else if (signalCount >= 3)
|
|
21000
|
+
score += 0.1;
|
|
21001
|
+
if (memory.awaiting_implementation)
|
|
21002
|
+
score += 0.15;
|
|
21003
|
+
if (memory.awaiting_decision)
|
|
21004
|
+
score += 0.1;
|
|
21005
|
+
const contextType = memory.context_type?.toLowerCase() ?? "";
|
|
21006
|
+
const contextKeywords = {
|
|
21007
|
+
debugging: ["debug", "bug", "error", "fix", "issue", "problem", "broken"],
|
|
21008
|
+
decision: ["decide", "decision", "choose", "choice", "option", "should"],
|
|
21009
|
+
architectural: ["architect", "design", "structure", "pattern", "how"],
|
|
21010
|
+
breakthrough: ["insight", "realize", "understand", "discover", "why"],
|
|
21011
|
+
technical: ["implement", "code", "function", "method", "api"],
|
|
21012
|
+
workflow: ["process", "workflow", "step", "flow", "pipeline"],
|
|
21013
|
+
philosophy: ["philosophy", "principle", "belief", "approach", "think"]
|
|
21014
|
+
};
|
|
21015
|
+
const keywords = contextKeywords[contextType] ?? [];
|
|
21016
|
+
for (const kw of keywords) {
|
|
21017
|
+
if (messageWords.has(kw) || messageLower.includes(kw)) {
|
|
21018
|
+
score += 0.1;
|
|
21019
|
+
break;
|
|
21020
|
+
}
|
|
21021
|
+
}
|
|
21022
|
+
if (memory.problem_solution_pair) {
|
|
21023
|
+
const problemWords = ["error", "bug", "issue", "problem", "wrong", "fail", "broken", "help", "stuck"];
|
|
21024
|
+
for (const pw of problemWords) {
|
|
21025
|
+
if (messageWords.has(pw) || messageLower.includes(pw)) {
|
|
21026
|
+
score += 0.1;
|
|
20868
21027
|
break;
|
|
20869
21028
|
}
|
|
20870
21029
|
}
|
|
20871
21030
|
}
|
|
20872
|
-
|
|
21031
|
+
const temporalClass = memory.temporal_class ?? "medium_term";
|
|
21032
|
+
if (temporalClass === "eternal")
|
|
21033
|
+
score += 0.1;
|
|
21034
|
+
else if (temporalClass === "long_term")
|
|
21035
|
+
score += 0.05;
|
|
21036
|
+
else if (temporalClass === "ephemeral") {
|
|
21037
|
+
if ((memory.sessions_since_surfaced ?? 0) <= 1)
|
|
21038
|
+
score += 0.1;
|
|
21039
|
+
}
|
|
21040
|
+
const confidence = memory.confidence_score ?? 0.7;
|
|
21041
|
+
if (confidence < 0.5)
|
|
21042
|
+
score -= 0.1;
|
|
21043
|
+
const emotionalKeywords = {
|
|
21044
|
+
frustration: ["frustrated", "annoying", "stuck", "ugh", "damn", "hate"],
|
|
21045
|
+
excitement: ["excited", "awesome", "amazing", "love", "great", "wow"],
|
|
21046
|
+
curiosity: ["wonder", "curious", "interesting", "how", "why", "what if"],
|
|
21047
|
+
satisfaction: ["done", "finished", "complete", "works", "solved", "finally"],
|
|
21048
|
+
discovery: ["found", "realized", "understand", "insight", "breakthrough"]
|
|
21049
|
+
};
|
|
21050
|
+
const emotion = memory.emotional_resonance?.toLowerCase() ?? "";
|
|
21051
|
+
const emotionKws = emotionalKeywords[emotion] ?? [];
|
|
21052
|
+
for (const ew of emotionKws) {
|
|
21053
|
+
if (messageWords.has(ew) || messageLower.includes(ew)) {
|
|
21054
|
+
score += 0.05;
|
|
21055
|
+
break;
|
|
21056
|
+
}
|
|
21057
|
+
}
|
|
21058
|
+
return score;
|
|
20873
21059
|
}
|
|
20874
21060
|
retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0, maxGlobalMemories = 2) {
|
|
21061
|
+
const startTime = performance.now();
|
|
20875
21062
|
if (!allMemories.length) {
|
|
20876
21063
|
return [];
|
|
20877
21064
|
}
|
|
20878
21065
|
const messageLower = currentMessage.toLowerCase();
|
|
20879
21066
|
const messageWords = this._extractSignificantWords(currentMessage);
|
|
20880
|
-
const detectedTypes = this._detectContextTypes(currentMessage);
|
|
20881
21067
|
const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
|
|
20882
21068
|
if (!candidates.length) {
|
|
20883
21069
|
return [];
|
|
20884
21070
|
}
|
|
20885
|
-
const
|
|
21071
|
+
const activatedMemories = [];
|
|
21072
|
+
let rejectedCount = 0;
|
|
20886
21073
|
for (const memory of candidates) {
|
|
20887
21074
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
20888
|
-
const
|
|
20889
|
-
const
|
|
20890
|
-
const
|
|
20891
|
-
const
|
|
20892
|
-
const
|
|
20893
|
-
const
|
|
20894
|
-
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
if (
|
|
21075
|
+
const triggerResult = this._checkTriggerActivation(messageLower, messageWords, memory.trigger_phrases ?? []);
|
|
21076
|
+
const tagResult = this._checkTagActivation(messageLower, messageWords, memory.semantic_tags ?? []);
|
|
21077
|
+
const domainActivated = this._checkDomainActivation(messageLower, messageWords, memory.domain);
|
|
21078
|
+
const featureActivated = this._checkFeatureActivation(messageLower, messageWords, memory.feature);
|
|
21079
|
+
const contentActivated = this._checkContentActivation(messageWords, memory);
|
|
21080
|
+
const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
|
|
21081
|
+
let signalCount = 0;
|
|
21082
|
+
if (triggerResult.activated)
|
|
21083
|
+
signalCount++;
|
|
21084
|
+
if (tagResult.activated)
|
|
21085
|
+
signalCount++;
|
|
21086
|
+
if (domainActivated)
|
|
21087
|
+
signalCount++;
|
|
21088
|
+
if (featureActivated)
|
|
21089
|
+
signalCount++;
|
|
21090
|
+
if (contentActivated)
|
|
21091
|
+
signalCount++;
|
|
21092
|
+
if (vectorSimilarity >= 0.4)
|
|
21093
|
+
signalCount++;
|
|
21094
|
+
const signals = {
|
|
21095
|
+
trigger: triggerResult.activated,
|
|
21096
|
+
tags: tagResult.activated,
|
|
21097
|
+
domain: domainActivated,
|
|
21098
|
+
feature: featureActivated,
|
|
21099
|
+
content: contentActivated,
|
|
21100
|
+
count: signalCount,
|
|
21101
|
+
triggerStrength: triggerResult.strength,
|
|
21102
|
+
tagCount: tagResult.count,
|
|
21103
|
+
vectorSimilarity
|
|
21104
|
+
};
|
|
21105
|
+
if (signalCount < MIN_ACTIVATION_SIGNALS) {
|
|
21106
|
+
rejectedCount++;
|
|
20904
21107
|
continue;
|
|
20905
21108
|
}
|
|
20906
|
-
const
|
|
20907
|
-
|
|
20908
|
-
corroboration: corroborationScore,
|
|
20909
|
-
reasoning_match: reasoningMatch,
|
|
20910
|
-
retrieval_weight: retrievalWeight,
|
|
20911
|
-
temporal: temporalScore,
|
|
20912
|
-
context: contextScore,
|
|
20913
|
-
tags: tagScore,
|
|
20914
|
-
trigger: triggerScore,
|
|
20915
|
-
domain: domainScore,
|
|
20916
|
-
question: questionScore,
|
|
20917
|
-
emotion: emotionScore,
|
|
20918
|
-
problem: problemScore,
|
|
20919
|
-
action: actionBoost
|
|
20920
|
-
};
|
|
20921
|
-
const reasoning = this._generateSelectionReasoning(components, corroborationSignals);
|
|
20922
|
-
scoredMemories.push({
|
|
21109
|
+
const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords);
|
|
21110
|
+
activatedMemories.push({
|
|
20923
21111
|
memory,
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
value_score: valueScore,
|
|
20927
|
-
corroboration_score: corroborationScore,
|
|
20928
|
-
reasoning,
|
|
20929
|
-
components,
|
|
21112
|
+
signals,
|
|
21113
|
+
importanceScore,
|
|
20930
21114
|
isGlobal
|
|
20931
21115
|
});
|
|
20932
21116
|
}
|
|
20933
|
-
|
|
21117
|
+
this._logActivationDistribution(activatedMemories, candidates.length, rejectedCount);
|
|
21118
|
+
this._logVectorStats();
|
|
21119
|
+
if (!activatedMemories.length) {
|
|
21120
|
+
const durationMs2 = performance.now() - startTime;
|
|
21121
|
+
logger.logRetrievalScoring({
|
|
21122
|
+
totalMemories: allMemories.length,
|
|
21123
|
+
currentMessage,
|
|
21124
|
+
alreadyInjected: alreadyInjectedCount,
|
|
21125
|
+
preFiltered: allMemories.length - candidates.length,
|
|
21126
|
+
globalCount: 0,
|
|
21127
|
+
projectCount: 0,
|
|
21128
|
+
finalCount: 0,
|
|
21129
|
+
durationMs: durationMs2,
|
|
21130
|
+
selectedMemories: []
|
|
21131
|
+
});
|
|
21132
|
+
return [];
|
|
21133
|
+
}
|
|
21134
|
+
activatedMemories.sort((a, b) => {
|
|
21135
|
+
if (b.signals.count !== a.signals.count) {
|
|
21136
|
+
return b.signals.count - a.signals.count;
|
|
21137
|
+
}
|
|
21138
|
+
return b.importanceScore - a.importanceScore;
|
|
21139
|
+
});
|
|
20934
21140
|
const selected = [];
|
|
20935
21141
|
const selectedIds = new Set;
|
|
20936
|
-
const globalMemories =
|
|
20937
|
-
const projectMemories =
|
|
20938
|
-
const
|
|
20939
|
-
const
|
|
20940
|
-
const
|
|
20941
|
-
if (
|
|
20942
|
-
return
|
|
20943
|
-
|
|
20944
|
-
|
|
21142
|
+
const globalMemories = activatedMemories.filter((m) => m.isGlobal);
|
|
21143
|
+
const projectMemories = activatedMemories.filter((m) => !m.isGlobal);
|
|
21144
|
+
const globalsSorted = globalMemories.sort((a, b) => {
|
|
21145
|
+
const aPriority = GLOBAL_TYPE_PRIORITY[a.memory.context_type ?? "personal"] ?? 8;
|
|
21146
|
+
const bPriority = GLOBAL_TYPE_PRIORITY[b.memory.context_type ?? "personal"] ?? 8;
|
|
21147
|
+
if (aPriority !== bPriority)
|
|
21148
|
+
return aPriority - bPriority;
|
|
21149
|
+
if (b.signals.count !== a.signals.count)
|
|
21150
|
+
return b.signals.count - a.signals.count;
|
|
21151
|
+
return b.importanceScore - a.importanceScore;
|
|
20945
21152
|
});
|
|
20946
|
-
for (const item of
|
|
21153
|
+
for (const item of globalsSorted.slice(0, maxGlobalMemories)) {
|
|
20947
21154
|
if (!selectedIds.has(item.memory.id)) {
|
|
20948
21155
|
selected.push(item);
|
|
20949
21156
|
selectedIds.add(item.memory.id);
|
|
20950
21157
|
}
|
|
20951
21158
|
}
|
|
20952
|
-
|
|
21159
|
+
const projectsSorted = [...projectMemories].sort((a, b) => {
|
|
21160
|
+
const aAction = a.memory.action_required ? 1 : 0;
|
|
21161
|
+
const bAction = b.memory.action_required ? 1 : 0;
|
|
21162
|
+
if (bAction !== aAction)
|
|
21163
|
+
return bAction - aAction;
|
|
21164
|
+
if (b.signals.count !== a.signals.count)
|
|
21165
|
+
return b.signals.count - a.signals.count;
|
|
21166
|
+
return b.importanceScore - a.importanceScore;
|
|
21167
|
+
});
|
|
21168
|
+
console.log(`[DEBUG] Top 15 candidates (sorted):`);
|
|
21169
|
+
for (let i = 0;i < Math.min(15, projectsSorted.length); i++) {
|
|
21170
|
+
const m = projectsSorted[i];
|
|
21171
|
+
const action = m.memory.action_required ? "⚡" : "";
|
|
21172
|
+
console.log(` ${i + 1}. [${m.signals.count}sig] score=${m.importanceScore.toFixed(2)} ${action} ${m.memory.content.slice(0, 45)}...`);
|
|
21173
|
+
}
|
|
21174
|
+
for (const item of projectsSorted) {
|
|
20953
21175
|
if (selected.length >= maxMemories)
|
|
20954
21176
|
break;
|
|
20955
21177
|
if (selectedIds.has(item.memory.id))
|
|
@@ -20957,7 +21179,27 @@ class SmartVectorRetrieval {
|
|
|
20957
21179
|
selected.push(item);
|
|
20958
21180
|
selectedIds.add(item.memory.id);
|
|
20959
21181
|
}
|
|
20960
|
-
selected.
|
|
21182
|
+
if (selected.length < maxMemories) {
|
|
21183
|
+
const relatedIds = new Set;
|
|
21184
|
+
for (const item of selected) {
|
|
21185
|
+
for (const relatedId of item.memory.related_to ?? []) {
|
|
21186
|
+
if (!selectedIds.has(relatedId)) {
|
|
21187
|
+
relatedIds.add(relatedId);
|
|
21188
|
+
}
|
|
21189
|
+
}
|
|
21190
|
+
}
|
|
21191
|
+
for (const item of activatedMemories) {
|
|
21192
|
+
if (selected.length >= maxMemories)
|
|
21193
|
+
break;
|
|
21194
|
+
if (selectedIds.has(item.memory.id))
|
|
21195
|
+
continue;
|
|
21196
|
+
if (relatedIds.has(item.memory.id)) {
|
|
21197
|
+
selected.push(item);
|
|
21198
|
+
selectedIds.add(item.memory.id);
|
|
21199
|
+
}
|
|
21200
|
+
}
|
|
21201
|
+
}
|
|
21202
|
+
const durationMs = performance.now() - startTime;
|
|
20961
21203
|
logger.logRetrievalScoring({
|
|
20962
21204
|
totalMemories: allMemories.length,
|
|
20963
21205
|
currentMessage,
|
|
@@ -20966,202 +21208,102 @@ class SmartVectorRetrieval {
|
|
|
20966
21208
|
globalCount: globalMemories.length,
|
|
20967
21209
|
projectCount: projectMemories.length,
|
|
20968
21210
|
finalCount: selected.length,
|
|
21211
|
+
durationMs,
|
|
20969
21212
|
selectedMemories: selected.map((item) => ({
|
|
20970
21213
|
content: item.memory.content,
|
|
20971
|
-
reasoning: item.
|
|
20972
|
-
|
|
20973
|
-
relevance_score: item.relevance_score,
|
|
20974
|
-
corroboration_score: item.corroboration_score,
|
|
21214
|
+
reasoning: this._generateActivationReasoning(item.signals),
|
|
21215
|
+
signalCount: item.signals.count,
|
|
20975
21216
|
importance_weight: item.memory.importance_weight ?? 0.5,
|
|
20976
21217
|
context_type: item.memory.context_type ?? "general",
|
|
20977
21218
|
semantic_tags: item.memory.semantic_tags ?? [],
|
|
20978
21219
|
isGlobal: item.isGlobal,
|
|
20979
|
-
|
|
21220
|
+
signals: {
|
|
21221
|
+
trigger: item.signals.trigger,
|
|
21222
|
+
triggerStrength: item.signals.triggerStrength,
|
|
21223
|
+
tags: item.signals.tags,
|
|
21224
|
+
tagCount: item.signals.tagCount,
|
|
21225
|
+
domain: item.signals.domain,
|
|
21226
|
+
feature: item.signals.feature,
|
|
21227
|
+
content: item.signals.content,
|
|
21228
|
+
vector: item.signals.vectorSimilarity >= 0.4,
|
|
21229
|
+
vectorSimilarity: item.signals.vectorSimilarity
|
|
21230
|
+
}
|
|
20980
21231
|
}))
|
|
20981
21232
|
});
|
|
20982
21233
|
return selected.map((item) => ({
|
|
20983
21234
|
...item.memory,
|
|
20984
|
-
score: item.
|
|
20985
|
-
relevance_score: item.
|
|
20986
|
-
value_score: item.
|
|
21235
|
+
score: item.signals.count / 6,
|
|
21236
|
+
relevance_score: item.signals.count / 6,
|
|
21237
|
+
value_score: item.importanceScore
|
|
20987
21238
|
}));
|
|
20988
21239
|
}
|
|
20989
|
-
|
|
20990
|
-
if (!vec1 || !vec2)
|
|
20991
|
-
return 0;
|
|
20992
|
-
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
20993
|
-
return cosineSimilarity(v1, vec2);
|
|
20994
|
-
}
|
|
20995
|
-
_scoreTemporalRelevance(temporalType) {
|
|
20996
|
-
const scores = {
|
|
20997
|
-
persistent: 0.8,
|
|
20998
|
-
session: 0.6,
|
|
20999
|
-
temporary: 0.3,
|
|
21000
|
-
archived: 0.1
|
|
21001
|
-
};
|
|
21002
|
-
return scores[temporalType] ?? 0.5;
|
|
21003
|
-
}
|
|
21004
|
-
_scoreContextAlignment(message, contextType) {
|
|
21005
|
-
const messageLower = message.toLowerCase();
|
|
21006
|
-
const keywords = TYPE_KEYWORDS[contextType] ?? [];
|
|
21007
|
-
const matches = keywords.filter((kw) => messageLower.includes(kw)).length;
|
|
21008
|
-
if (matches > 0) {
|
|
21009
|
-
return Math.min(0.2 + matches * 0.15, 0.7);
|
|
21010
|
-
}
|
|
21011
|
-
return 0.1;
|
|
21012
|
-
}
|
|
21013
|
-
_scoreSemanticTags(message, tags) {
|
|
21014
|
-
if (!tags.length)
|
|
21015
|
-
return 0;
|
|
21016
|
-
const messageLower = message.toLowerCase();
|
|
21017
|
-
const matches = tags.filter((tag) => messageLower.includes(tag.trim().toLowerCase())).length;
|
|
21018
|
-
if (matches > 0) {
|
|
21019
|
-
return Math.min(0.3 + matches * 0.25, 1);
|
|
21020
|
-
}
|
|
21021
|
-
return 0;
|
|
21022
|
-
}
|
|
21023
|
-
_scoreTriggerPhrases(messageLower, triggerPhrases) {
|
|
21024
|
-
if (!triggerPhrases.length)
|
|
21025
|
-
return 0;
|
|
21026
|
-
const stopWords = new Set([
|
|
21027
|
-
"the",
|
|
21028
|
-
"is",
|
|
21029
|
-
"are",
|
|
21030
|
-
"was",
|
|
21031
|
-
"were",
|
|
21032
|
-
"to",
|
|
21033
|
-
"a",
|
|
21034
|
-
"an",
|
|
21035
|
-
"and",
|
|
21036
|
-
"or",
|
|
21037
|
-
"but",
|
|
21038
|
-
"in",
|
|
21039
|
-
"on",
|
|
21040
|
-
"at",
|
|
21041
|
-
"for",
|
|
21042
|
-
"with",
|
|
21043
|
-
"about",
|
|
21044
|
-
"when",
|
|
21045
|
-
"how",
|
|
21046
|
-
"what",
|
|
21047
|
-
"why"
|
|
21048
|
-
]);
|
|
21049
|
-
let maxScore = 0;
|
|
21050
|
-
for (const pattern of triggerPhrases) {
|
|
21051
|
-
const patternLower = pattern.trim().toLowerCase();
|
|
21052
|
-
const patternWords = patternLower.split(/\s+/).filter((w) => !stopWords.has(w) && w.length > 2);
|
|
21053
|
-
if (patternWords.length) {
|
|
21054
|
-
let matches = 0;
|
|
21055
|
-
for (const word of patternWords) {
|
|
21056
|
-
if (messageLower.includes(word)) {
|
|
21057
|
-
matches += 1;
|
|
21058
|
-
} else if (messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
21059
|
-
matches += 0.9;
|
|
21060
|
-
}
|
|
21061
|
-
}
|
|
21062
|
-
const conceptScore = matches / patternWords.length;
|
|
21063
|
-
maxScore = Math.max(maxScore, conceptScore);
|
|
21064
|
-
}
|
|
21065
|
-
}
|
|
21066
|
-
return Math.min(maxScore, 1);
|
|
21067
|
-
}
|
|
21068
|
-
_scoreDomain(messageWords, messageLower, memory) {
|
|
21069
|
-
let score = 0;
|
|
21070
|
-
if (memory.domain) {
|
|
21071
|
-
const domainLower = memory.domain.toLowerCase();
|
|
21072
|
-
if (messageWords.has(domainLower) || messageLower.includes(domainLower)) {
|
|
21073
|
-
score += 0.5;
|
|
21074
|
-
}
|
|
21075
|
-
}
|
|
21076
|
-
if (memory.feature) {
|
|
21077
|
-
const featureLower = memory.feature.toLowerCase();
|
|
21078
|
-
if (messageWords.has(featureLower) || messageLower.includes(featureLower)) {
|
|
21079
|
-
score += 0.3;
|
|
21080
|
-
}
|
|
21081
|
-
}
|
|
21082
|
-
if (memory.component) {
|
|
21083
|
-
const componentLower = memory.component.toLowerCase();
|
|
21084
|
-
if (messageWords.has(componentLower) || messageLower.includes(componentLower)) {
|
|
21085
|
-
score += 0.2;
|
|
21086
|
-
}
|
|
21087
|
-
}
|
|
21088
|
-
return Math.min(score, 1);
|
|
21089
|
-
}
|
|
21090
|
-
_scoreQuestionTypes(message, questionTypes) {
|
|
21091
|
-
if (!questionTypes.length)
|
|
21092
|
-
return 0;
|
|
21093
|
-
const messageLower = message.toLowerCase();
|
|
21094
|
-
const questionWords = ["how", "why", "what", "when", "where"];
|
|
21095
|
-
for (const qtype of questionTypes) {
|
|
21096
|
-
const qtypeLower = qtype.trim().toLowerCase();
|
|
21097
|
-
if (messageLower.includes(qtypeLower)) {
|
|
21098
|
-
return 0.8;
|
|
21099
|
-
}
|
|
21100
|
-
const messageHasQuestion = questionWords.some((qw) => messageLower.includes(qw));
|
|
21101
|
-
const typeHasQuestion = questionWords.some((qw) => qtypeLower.includes(qw));
|
|
21102
|
-
if (messageHasQuestion && typeHasQuestion) {
|
|
21103
|
-
return 0.5;
|
|
21104
|
-
}
|
|
21105
|
-
}
|
|
21106
|
-
return 0;
|
|
21107
|
-
}
|
|
21108
|
-
_scoreEmotionalContext(message, emotion) {
|
|
21109
|
-
if (!emotion)
|
|
21110
|
-
return 0;
|
|
21111
|
-
const messageLower = message.toLowerCase();
|
|
21112
|
-
const emotionPatterns = {
|
|
21113
|
-
joy: ["happy", "excited", "love", "wonderful", "great", "awesome"],
|
|
21114
|
-
frustration: ["stuck", "confused", "help", "issue", "problem", "why"],
|
|
21115
|
-
discovery: ["realized", "found", "discovered", "aha", "insight"],
|
|
21116
|
-
gratitude: ["thank", "appreciate", "grateful", "dear friend"]
|
|
21117
|
-
};
|
|
21118
|
-
const patterns = emotionPatterns[emotion.toLowerCase()] ?? [];
|
|
21119
|
-
if (patterns.some((pattern) => messageLower.includes(pattern))) {
|
|
21120
|
-
return 0.7;
|
|
21121
|
-
}
|
|
21122
|
-
return 0;
|
|
21123
|
-
}
|
|
21124
|
-
_scoreProblemSolution(message, isProblemSolution) {
|
|
21125
|
-
if (!isProblemSolution)
|
|
21126
|
-
return 0;
|
|
21127
|
-
const messageLower = message.toLowerCase();
|
|
21128
|
-
const problemWords = ["error", "issue", "problem", "stuck", "help", "fix", "solve", "debug"];
|
|
21129
|
-
if (problemWords.some((word) => messageLower.includes(word))) {
|
|
21130
|
-
return 0.8;
|
|
21131
|
-
}
|
|
21132
|
-
return 0;
|
|
21133
|
-
}
|
|
21134
|
-
_generateSelectionReasoning(components, corroborationSignals) {
|
|
21135
|
-
const scores = [
|
|
21136
|
-
["vector", components.vector],
|
|
21137
|
-
["corroboration", components.corroboration],
|
|
21138
|
-
["reasoning", components.reasoning_match],
|
|
21139
|
-
["weight", components.retrieval_weight],
|
|
21140
|
-
["context", components.context],
|
|
21141
|
-
["temporal", components.temporal],
|
|
21142
|
-
["tags", components.tags],
|
|
21143
|
-
["trigger", components.trigger],
|
|
21144
|
-
["domain", components.domain],
|
|
21145
|
-
["question", components.question],
|
|
21146
|
-
["emotion", components.emotion],
|
|
21147
|
-
["problem", components.problem],
|
|
21148
|
-
["action", components.action]
|
|
21149
|
-
];
|
|
21150
|
-
scores.sort((a, b) => b[1] - a[1]);
|
|
21240
|
+
_generateActivationReasoning(signals) {
|
|
21151
21241
|
const reasons = [];
|
|
21152
|
-
|
|
21153
|
-
|
|
21154
|
-
|
|
21155
|
-
|
|
21156
|
-
|
|
21157
|
-
|
|
21158
|
-
|
|
21242
|
+
if (signals.trigger)
|
|
21243
|
+
reasons.push(`trigger:${(signals.triggerStrength * 100).toFixed(0)}%`);
|
|
21244
|
+
if (signals.tags)
|
|
21245
|
+
reasons.push(`tags:${signals.tagCount}`);
|
|
21246
|
+
if (signals.domain)
|
|
21247
|
+
reasons.push("domain");
|
|
21248
|
+
if (signals.feature)
|
|
21249
|
+
reasons.push("feature");
|
|
21250
|
+
if (signals.content)
|
|
21251
|
+
reasons.push("content");
|
|
21252
|
+
if (signals.vectorSimilarity >= 0.4)
|
|
21253
|
+
reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
21254
|
+
return reasons.length ? `Activated: ${reasons.join(", ")} (${signals.count} signals)` : "No signals";
|
|
21255
|
+
}
|
|
21256
|
+
_logActivationDistribution(activated, totalCandidates, rejectedCount) {
|
|
21257
|
+
const signalBuckets = {
|
|
21258
|
+
"2 signals": 0,
|
|
21259
|
+
"3 signals": 0,
|
|
21260
|
+
"4 signals": 0,
|
|
21261
|
+
"5 signals": 0,
|
|
21262
|
+
"6 signals": 0
|
|
21263
|
+
};
|
|
21264
|
+
for (const mem of activated) {
|
|
21265
|
+
const key = `${Math.min(mem.signals.count, 6)} signals`;
|
|
21266
|
+
signalBuckets[key] = (signalBuckets[key] ?? 0) + 1;
|
|
21267
|
+
}
|
|
21268
|
+
let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0;
|
|
21269
|
+
for (const mem of activated) {
|
|
21270
|
+
if (mem.signals.trigger)
|
|
21271
|
+
triggerCount++;
|
|
21272
|
+
if (mem.signals.tags)
|
|
21273
|
+
tagCount++;
|
|
21274
|
+
if (mem.signals.domain)
|
|
21275
|
+
domainCount++;
|
|
21276
|
+
if (mem.signals.feature)
|
|
21277
|
+
featureCount++;
|
|
21278
|
+
if (mem.signals.content)
|
|
21279
|
+
contentCount++;
|
|
21280
|
+
if (mem.signals.vectorSimilarity >= 0.4)
|
|
21281
|
+
vectorCount++;
|
|
21282
|
+
}
|
|
21283
|
+
logger.logScoreDistribution({
|
|
21284
|
+
totalCandidates,
|
|
21285
|
+
passedGatekeeper: activated.length,
|
|
21286
|
+
rejectedByGatekeeper: rejectedCount,
|
|
21287
|
+
buckets: signalBuckets,
|
|
21288
|
+
stats: {
|
|
21289
|
+
min: activated.length ? Math.min(...activated.map((m) => m.signals.count)) : 0,
|
|
21290
|
+
max: activated.length ? Math.max(...activated.map((m) => m.signals.count)) : 0,
|
|
21291
|
+
mean: activated.length ? Math.round(activated.reduce((s, m) => s + m.signals.count, 0) / activated.length * 10) / 10 : 0,
|
|
21292
|
+
stdev: 0,
|
|
21293
|
+
spread: activated.length ? Math.max(...activated.map((m) => m.signals.count)) - Math.min(...activated.map((m) => m.signals.count)) : 0
|
|
21294
|
+
},
|
|
21295
|
+
percentiles: {},
|
|
21296
|
+
compressionWarning: false,
|
|
21297
|
+
signalBreakdown: {
|
|
21298
|
+
trigger: triggerCount,
|
|
21299
|
+
tags: tagCount,
|
|
21300
|
+
domain: domainCount,
|
|
21301
|
+
feature: featureCount,
|
|
21302
|
+
content: contentCount,
|
|
21303
|
+
vector: vectorCount,
|
|
21304
|
+
total: activated.length
|
|
21159
21305
|
}
|
|
21160
|
-
}
|
|
21161
|
-
if (corroborationSignals.length > 0) {
|
|
21162
|
-
reasons.push("signals:[" + corroborationSignals.slice(0, 2).join(",") + "]");
|
|
21163
|
-
}
|
|
21164
|
-
return reasons.length ? "Selected: " + reasons.join(", ") : "Combined factors";
|
|
21306
|
+
});
|
|
21165
21307
|
}
|
|
21166
21308
|
}
|
|
21167
21309
|
function createRetrieval() {
|
|
@@ -21180,7 +21322,8 @@ class MemoryEngine {
|
|
|
21180
21322
|
centralPath: config.centralPath ?? import_path2.join(import_os2.homedir(), ".local", "share", "memory"),
|
|
21181
21323
|
localFolder: config.localFolder ?? ".memory",
|
|
21182
21324
|
maxMemories: config.maxMemories ?? 5,
|
|
21183
|
-
embedder: config.embedder
|
|
21325
|
+
embedder: config.embedder,
|
|
21326
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
21184
21327
|
};
|
|
21185
21328
|
this._retrieval = createRetrieval();
|
|
21186
21329
|
}
|
|
@@ -21309,7 +21452,7 @@ class MemoryEngine {
|
|
|
21309
21452
|
}
|
|
21310
21453
|
async _generateSessionPrimer(store, projectId) {
|
|
21311
21454
|
let personalContext;
|
|
21312
|
-
if (
|
|
21455
|
+
if (this._config.personalMemoriesEnabled) {
|
|
21313
21456
|
const personalPrimer = await store.getPersonalPrimer();
|
|
21314
21457
|
personalContext = personalPrimer?.content;
|
|
21315
21458
|
}
|
|
@@ -21397,12 +21540,28 @@ ${primer.personal_context}`);
|
|
|
21397
21540
|
**Project status**: ${primer.project_status}`);
|
|
21398
21541
|
}
|
|
21399
21542
|
parts.push(`
|
|
21400
|
-
**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`);
|
|
21543
|
+
**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`);
|
|
21401
21544
|
parts.push(`
|
|
21402
21545
|
*Memories will surface naturally as we converse.*`);
|
|
21403
21546
|
return parts.join(`
|
|
21404
21547
|
`);
|
|
21405
21548
|
}
|
|
21549
|
+
_formatAge(createdAt) {
|
|
21550
|
+
const now = Date.now();
|
|
21551
|
+
const diffMs = now - createdAt;
|
|
21552
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
21553
|
+
if (diffDays === 0)
|
|
21554
|
+
return "today";
|
|
21555
|
+
if (diffDays === 1)
|
|
21556
|
+
return "1d";
|
|
21557
|
+
if (diffDays < 7)
|
|
21558
|
+
return `${diffDays}d`;
|
|
21559
|
+
if (diffDays < 30)
|
|
21560
|
+
return `${Math.floor(diffDays / 7)}w`;
|
|
21561
|
+
if (diffDays < 365)
|
|
21562
|
+
return `${Math.floor(diffDays / 30)}mo`;
|
|
21563
|
+
return `${Math.floor(diffDays / 365)}y`;
|
|
21564
|
+
}
|
|
21406
21565
|
_formatMemories(memories) {
|
|
21407
21566
|
if (!memories.length)
|
|
21408
21567
|
return "";
|
|
@@ -21413,11 +21572,40 @@ ${primer.personal_context}`);
|
|
|
21413
21572
|
const tags = memory.semantic_tags?.join(", ") || "";
|
|
21414
21573
|
const importance = memory.importance_weight?.toFixed(1) || "0.5";
|
|
21415
21574
|
const emoji = getMemoryEmoji(memory.context_type || "general");
|
|
21416
|
-
|
|
21575
|
+
const actionFlag = memory.action_required ? " ⚡ACTION" : "";
|
|
21576
|
+
const age = memory.updated_at ? this._formatAge(memory.updated_at) : memory.created_at ? this._formatAge(memory.created_at) : "";
|
|
21577
|
+
parts.push(`[${emoji} • ${importance} • ${age}${actionFlag}] [${tags}] ${memory.content}`);
|
|
21578
|
+
const related = memory.related_to;
|
|
21579
|
+
if (related && related.length > 0) {
|
|
21580
|
+
const moreCount = related.length - 1;
|
|
21581
|
+
const moreSuffix = moreCount > 0 ? ` +${moreCount} more` : "";
|
|
21582
|
+
parts.push(` ↳ ${related[0]}${moreSuffix}`);
|
|
21583
|
+
}
|
|
21417
21584
|
}
|
|
21418
21585
|
return parts.join(`
|
|
21419
21586
|
`);
|
|
21420
21587
|
}
|
|
21588
|
+
getStoragePaths(projectId, projectPath) {
|
|
21589
|
+
const globalPath = import_path2.join(import_os2.homedir(), ".local", "share", "memory", "global");
|
|
21590
|
+
const globalMemoriesPath = import_path2.join(globalPath, "memories");
|
|
21591
|
+
const personalPrimerPath = import_path2.join(globalPath, "primer", "personal-primer.md");
|
|
21592
|
+
let storeBasePath;
|
|
21593
|
+
if (this._config.storageMode === "local" && projectPath) {
|
|
21594
|
+
storeBasePath = import_path2.join(projectPath, this._config.localFolder);
|
|
21595
|
+
} else {
|
|
21596
|
+
storeBasePath = this._config.centralPath;
|
|
21597
|
+
}
|
|
21598
|
+
const projectRootPath = import_path2.join(storeBasePath, projectId);
|
|
21599
|
+
const projectMemoriesPath = import_path2.join(projectRootPath, "memories");
|
|
21600
|
+
return {
|
|
21601
|
+
projectPath: projectRootPath,
|
|
21602
|
+
globalPath,
|
|
21603
|
+
projectMemoriesPath,
|
|
21604
|
+
globalMemoriesPath,
|
|
21605
|
+
personalPrimerPath,
|
|
21606
|
+
storageMode: this._config.storageMode
|
|
21607
|
+
};
|
|
21608
|
+
}
|
|
21421
21609
|
close() {
|
|
21422
21610
|
for (const store of this._stores.values()) {
|
|
21423
21611
|
store.close();
|
|
@@ -21452,11 +21640,12 @@ class Curator {
|
|
|
21452
21640
|
this._config = {
|
|
21453
21641
|
apiKey: config.apiKey ?? "",
|
|
21454
21642
|
cliCommand,
|
|
21455
|
-
cliType: config.cliType ?? "claude-code"
|
|
21643
|
+
cliType: config.cliType ?? "claude-code",
|
|
21644
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
21456
21645
|
};
|
|
21457
21646
|
}
|
|
21458
21647
|
buildCurationPrompt(triggerType = "session_end") {
|
|
21459
|
-
|
|
21648
|
+
const basePrompt = `You have just had a conversation. As this session is ending (${triggerType}), please curate memories for the Claude Tools Memory System.
|
|
21460
21649
|
|
|
21461
21650
|
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.
|
|
21462
21651
|
|
|
@@ -21493,27 +21682,82 @@ Each memory should stand alone.
|
|
|
21493
21682
|
- Craft language that activates rather than just informs
|
|
21494
21683
|
- Test: 'What state will this restore when Claude encounters it?'
|
|
21495
21684
|
|
|
21496
|
-
**HOW RETRIEVAL WORKS
|
|
21685
|
+
**HOW RETRIEVAL WORKS - ACTIVATION SIGNAL ALGORITHM**
|
|
21686
|
+
|
|
21687
|
+
Understanding the algorithm helps you craft metadata that surfaces memories at the right moments.
|
|
21688
|
+
|
|
21689
|
+
**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.
|
|
21690
|
+
|
|
21691
|
+
**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.
|
|
21692
|
+
|
|
21693
|
+
**6 ACTIVATION SIGNALS** (each is binary - fires or doesn't):
|
|
21694
|
+
|
|
21695
|
+
1. **TRIGGER** - Words from trigger_phrases found in user's message (≥50% match)
|
|
21696
|
+
- THE MOST IMPORTANT SIGNAL. Handcrafted activation patterns.
|
|
21697
|
+
- Example: "when debugging retrieval" fires if user says "I'm debugging the retrieval algorithm"
|
|
21698
|
+
|
|
21699
|
+
2. **TAGS** - 2+ semantic_tags found in user's message
|
|
21700
|
+
- Use words users would ACTUALLY TYPE, not generic descriptors
|
|
21701
|
+
- GOOD: ["retrieval", "embeddings", "curator", "scoring"]
|
|
21702
|
+
- WEAK: ["technical", "important", "system"]
|
|
21703
|
+
|
|
21704
|
+
3. **DOMAIN** - The domain word appears in user's message
|
|
21705
|
+
- Be specific: "retrieval", "embeddings", "auth", "ui"
|
|
21706
|
+
- NOT: "technical", "code", "implementation"
|
|
21707
|
+
|
|
21708
|
+
4. **FEATURE** - The feature word appears in user's message
|
|
21709
|
+
- Be specific: "scoring-weights", "gpu-acceleration", "login-flow"
|
|
21710
|
+
|
|
21711
|
+
5. **CONTENT** - 3+ significant words from memory content overlap with message
|
|
21712
|
+
- Automatic - based on the memory's content text
|
|
21713
|
+
|
|
21714
|
+
6. **VECTOR** - Semantic similarity ≥ 40% (embedding cosine distance)
|
|
21715
|
+
- Automatic - based on embeddings generated from content
|
|
21716
|
+
|
|
21717
|
+
**RELEVANCE GATE**: A memory must have ≥2 signals to be considered relevant.
|
|
21718
|
+
If only 1 signal fires, the memory is REJECTED. This prevents noise.
|
|
21497
21719
|
|
|
21498
|
-
**
|
|
21720
|
+
**RANKING AMONG RELEVANT**: Once a memory passes the gate:
|
|
21721
|
+
1. Sort by SIGNAL COUNT (more signals = more certainly relevant)
|
|
21722
|
+
2. Then by IMPORTANCE WEIGHT (your assessment of how important this memory is)
|
|
21499
21723
|
|
|
21500
|
-
|
|
21501
|
-
|
|
21502
|
-
|
|
21724
|
+
**SELECTION**:
|
|
21725
|
+
- Global memories (scope='global'): Max 2 selected, tech types prioritized over personal
|
|
21726
|
+
- Project memories: Fill remaining slots, action_required prioritized
|
|
21727
|
+
- Related memories (related_to field): May be included if they also passed the gate
|
|
21503
21728
|
|
|
21504
|
-
|
|
21729
|
+
**WHY THIS MATTERS FOR YOU**:
|
|
21730
|
+
- If you don't fill trigger_phrases well → trigger signal never fires
|
|
21731
|
+
- If you use generic tags → tags signal rarely fires
|
|
21732
|
+
- If you leave domain/feature empty → those signals can't fire
|
|
21733
|
+
- A memory with poor metadata may NEVER surface because it can't reach 2 signals
|
|
21505
21734
|
|
|
21506
|
-
|
|
21735
|
+
**CRAFTING EFFECTIVE METADATA** (CRITICAL FOR RETRIEVAL):
|
|
21507
21736
|
|
|
21508
|
-
|
|
21737
|
+
1. **trigger_phrases** (MOST IMPORTANT) - Activation patterns describing WHEN to surface:
|
|
21738
|
+
- Include 2-4 specific patterns per memory
|
|
21739
|
+
- Use words the user would actually type
|
|
21740
|
+
- GOOD: ["debugging retrieval", "working on embeddings", "memory system performance"]
|
|
21741
|
+
- WEAK: ["when relevant", "if needed", "technical work"]
|
|
21509
21742
|
|
|
21510
|
-
**
|
|
21743
|
+
2. **semantic_tags** - Words users would type (need 2+ to fire):
|
|
21744
|
+
- Be specific and searchable
|
|
21745
|
+
- GOOD: ["retrieval", "embeddings", "fsdb", "curator", "scoring"]
|
|
21746
|
+
- WEAK: ["technical", "important", "system", "implementation"]
|
|
21511
21747
|
|
|
21512
|
-
|
|
21748
|
+
3. **domain** (NEW - FILL THIS) - Single specific area word:
|
|
21749
|
+
- GOOD: "retrieval", "embeddings", "curator", "signals", "fsdb"
|
|
21750
|
+
- WEAK: "technical", "code", "memory" (too generic)
|
|
21513
21751
|
|
|
21514
|
-
|
|
21752
|
+
4. **feature** (NEW - FILL THIS) - Specific feature within domain:
|
|
21753
|
+
- GOOD: "scoring-algorithm", "activation-signals", "vector-search"
|
|
21754
|
+
- WEAK: "implementation", "code", "logic"
|
|
21515
21755
|
|
|
21516
|
-
|
|
21756
|
+
5. **importance_weight** - Only affects ranking AMONG relevant memories:
|
|
21757
|
+
- 0.9+ = Critical breakthrough, must surface if relevant
|
|
21758
|
+
- 0.7-0.8 = Important insight, should surface if relevant
|
|
21759
|
+
- 0.5-0.6 = Useful context, nice to have if relevant
|
|
21760
|
+
- NOTE: This does NOT affect whether the memory passes the relevance gate!
|
|
21517
21761
|
|
|
21518
21762
|
**SCOPE DETERMINES WHERE MEMORIES SURFACE**:
|
|
21519
21763
|
- scope: 'global' → surfaces in ALL projects (personal facts, philosophy, preferences)
|
|
@@ -21581,6 +21825,21 @@ Return ONLY this JSON structure:
|
|
|
21581
21825
|
}
|
|
21582
21826
|
]
|
|
21583
21827
|
}`;
|
|
21828
|
+
if (!this._config.personalMemoriesEnabled) {
|
|
21829
|
+
return basePrompt + `
|
|
21830
|
+
|
|
21831
|
+
---
|
|
21832
|
+
|
|
21833
|
+
**IMPORTANT: PERSONAL MEMORIES DISABLED**
|
|
21834
|
+
|
|
21835
|
+
The user has disabled personal memory extraction. Do NOT extract any memories with:
|
|
21836
|
+
- context_type: "personal"
|
|
21837
|
+
- scope: "global" when the content is about the user's personal life, relationships, family, or emotional states
|
|
21838
|
+
- Content about the user's preferences, feelings, personal opinions, or relationship dynamics
|
|
21839
|
+
|
|
21840
|
+
Focus ONLY on technical, architectural, debugging, decision, workflow, and project-related memories. Skip any content that would reveal personal information about the user.`;
|
|
21841
|
+
}
|
|
21842
|
+
return basePrompt;
|
|
21584
21843
|
}
|
|
21585
21844
|
parseCurationResponse(responseJson) {
|
|
21586
21845
|
try {
|
|
@@ -21671,33 +21930,35 @@ Return ONLY this JSON structure:
|
|
|
21671
21930
|
_clamp(value, min, max) {
|
|
21672
21931
|
return Math.max(min, Math.min(max, value));
|
|
21673
21932
|
}
|
|
21674
|
-
async curateWithSDK(
|
|
21933
|
+
async curateWithSDK(messages, triggerType = "session_end") {
|
|
21675
21934
|
if (!this._config.apiKey) {
|
|
21676
|
-
throw new Error("API key required for SDK mode");
|
|
21935
|
+
throw new Error("API key required for SDK mode. Set ANTHROPIC_API_KEY environment variable.");
|
|
21677
21936
|
}
|
|
21678
21937
|
const { default: Anthropic2 } = await Promise.resolve().then(() => (init_sdk(), exports_sdk));
|
|
21679
21938
|
const client = new Anthropic2({ apiKey: this._config.apiKey });
|
|
21680
|
-
const
|
|
21939
|
+
const systemPrompt = this.buildCurationPrompt(triggerType);
|
|
21940
|
+
const conversationMessages = [
|
|
21941
|
+
...messages,
|
|
21942
|
+
{
|
|
21943
|
+
role: "user",
|
|
21944
|
+
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."
|
|
21945
|
+
}
|
|
21946
|
+
];
|
|
21681
21947
|
const response = await client.messages.create({
|
|
21682
21948
|
model: "claude-sonnet-4-20250514",
|
|
21683
21949
|
max_tokens: 8192,
|
|
21684
|
-
|
|
21685
|
-
|
|
21686
|
-
role: "user",
|
|
21687
|
-
content: `${conversationContext}
|
|
21688
|
-
|
|
21689
|
-
---
|
|
21690
|
-
|
|
21691
|
-
${prompt}`
|
|
21692
|
-
}
|
|
21693
|
-
]
|
|
21950
|
+
system: systemPrompt,
|
|
21951
|
+
messages: conversationMessages
|
|
21694
21952
|
});
|
|
21695
21953
|
const content = response.content[0];
|
|
21696
21954
|
if (content.type !== "text") {
|
|
21697
|
-
throw new Error("Unexpected response type");
|
|
21955
|
+
throw new Error("Unexpected response type from Claude API");
|
|
21698
21956
|
}
|
|
21699
21957
|
return this.parseCurationResponse(content.text);
|
|
21700
21958
|
}
|
|
21959
|
+
async curateFromSegment(segment, triggerType = "session_end") {
|
|
21960
|
+
return this.curateWithSDK(segment.messages, triggerType);
|
|
21961
|
+
}
|
|
21701
21962
|
async curateWithCLI(sessionId, triggerType = "session_end", cwd, cliTypeOverride) {
|
|
21702
21963
|
const type = cliTypeOverride ?? this._config.cliType;
|
|
21703
21964
|
const systemPrompt = this.buildCurationPrompt(triggerType);
|
|
@@ -46276,8 +46537,9 @@ class Manager {
|
|
|
46276
46537
|
_config;
|
|
46277
46538
|
constructor(config = {}) {
|
|
46278
46539
|
this._config = {
|
|
46540
|
+
enabled: config.enabled ?? true,
|
|
46279
46541
|
cliCommand: config.cliCommand ?? getClaudeCommand2(),
|
|
46280
|
-
maxTurns: config.maxTurns
|
|
46542
|
+
maxTurns: config.maxTurns
|
|
46281
46543
|
};
|
|
46282
46544
|
}
|
|
46283
46545
|
async buildManagementPrompt() {
|
|
@@ -46298,14 +46560,31 @@ class Manager {
|
|
|
46298
46560
|
}
|
|
46299
46561
|
return null;
|
|
46300
46562
|
}
|
|
46301
|
-
buildUserMessage(projectId, sessionNumber, result) {
|
|
46563
|
+
buildUserMessage(projectId, sessionNumber, result, storagePaths) {
|
|
46302
46564
|
const today = new Date().toISOString().split("T")[0];
|
|
46565
|
+
const pathsSection = storagePaths ? `
|
|
46566
|
+
## Storage Paths (ACTUAL - use these exact paths)
|
|
46567
|
+
|
|
46568
|
+
**Storage Mode:** ${storagePaths.storageMode}
|
|
46569
|
+
|
|
46570
|
+
### Project Storage
|
|
46571
|
+
- **Project Root:** ${storagePaths.projectPath}
|
|
46572
|
+
- **Project Memories:** ${storagePaths.projectMemoriesPath}
|
|
46573
|
+
|
|
46574
|
+
### Global Storage (shared across all projects)
|
|
46575
|
+
- **Global Root:** ${storagePaths.globalPath}
|
|
46576
|
+
- **Global Memories:** ${storagePaths.globalMemoriesPath}
|
|
46577
|
+
- **Personal Primer:** ${storagePaths.personalPrimerPath}
|
|
46578
|
+
|
|
46579
|
+
> ⚠️ These paths are resolved from the running server configuration. Use them exactly as provided.
|
|
46580
|
+
> Memories are stored as individual markdown files in the memories directories.
|
|
46581
|
+
` : "";
|
|
46303
46582
|
return `## Curation Data
|
|
46304
46583
|
|
|
46305
46584
|
**Project ID:** ${projectId}
|
|
46306
46585
|
**Session Number:** ${sessionNumber}
|
|
46307
46586
|
**Date:** ${today}
|
|
46308
|
-
|
|
46587
|
+
${pathsSection}
|
|
46309
46588
|
### Session Summary
|
|
46310
46589
|
${result.session_summary || "No summary provided"}
|
|
46311
46590
|
|
|
@@ -46331,56 +46610,105 @@ ${result.memories.map((m2, i2) => `
|
|
|
46331
46610
|
|
|
46332
46611
|
---
|
|
46333
46612
|
|
|
46334
|
-
Please process these memories according to your management procedure. Update, supersede, or link existing memories as needed. Update the personal primer if any personal memories warrant it.`;
|
|
46613
|
+
Please process these memories according to your management procedure. Use the exact storage paths provided above to read and write memory files. Update, supersede, or link existing memories as needed. Update the personal primer if any personal memories warrant it.`;
|
|
46335
46614
|
}
|
|
46336
46615
|
parseManagementResponse(responseJson) {
|
|
46616
|
+
const emptyResult = (error) => ({
|
|
46617
|
+
success: !error,
|
|
46618
|
+
superseded: 0,
|
|
46619
|
+
resolved: 0,
|
|
46620
|
+
linked: 0,
|
|
46621
|
+
filesRead: 0,
|
|
46622
|
+
filesWritten: 0,
|
|
46623
|
+
primerUpdated: false,
|
|
46624
|
+
actions: [],
|
|
46625
|
+
summary: error ? "" : "No actions taken",
|
|
46626
|
+
fullReport: error ? `Error: ${error}` : "",
|
|
46627
|
+
error
|
|
46628
|
+
});
|
|
46337
46629
|
try {
|
|
46338
46630
|
const cliOutput = JSON.parse(responseJson);
|
|
46339
46631
|
if (cliOutput.type === "error" || cliOutput.is_error === true) {
|
|
46340
|
-
return
|
|
46341
|
-
success: false,
|
|
46342
|
-
superseded: 0,
|
|
46343
|
-
resolved: 0,
|
|
46344
|
-
linked: 0,
|
|
46345
|
-
primerUpdated: false,
|
|
46346
|
-
summary: "",
|
|
46347
|
-
error: cliOutput.error || "Unknown error"
|
|
46348
|
-
};
|
|
46632
|
+
return emptyResult(cliOutput.error || "Unknown error");
|
|
46349
46633
|
}
|
|
46350
46634
|
const resultText = typeof cliOutput.result === "string" ? cliOutput.result : "";
|
|
46351
|
-
const
|
|
46352
|
-
const
|
|
46353
|
-
const
|
|
46354
|
-
const
|
|
46635
|
+
const reportMatch = resultText.match(/(=== MANAGEMENT ACTIONS ===[\s\S]*)/);
|
|
46636
|
+
const fullReport = reportMatch ? reportMatch[1].trim() : resultText;
|
|
46637
|
+
const actionsMatch = resultText.match(/=== MANAGEMENT ACTIONS ===([\s\S]*?)(?:=== SUMMARY ===|$)/);
|
|
46638
|
+
const actions = [];
|
|
46639
|
+
if (actionsMatch) {
|
|
46640
|
+
const actionsText = actionsMatch[1];
|
|
46641
|
+
const actionLines = actionsText.split(`
|
|
46642
|
+
`).map((line) => line.trim()).filter((line) => /^(READ|WRITE|RECEIVED|CREATED|UPDATED|SUPERSEDED|RESOLVED|LINKED|PRIMER|SKIPPED|NO_ACTION)/.test(line));
|
|
46643
|
+
actions.push(...actionLines);
|
|
46644
|
+
}
|
|
46645
|
+
const supersededMatch = resultText.match(/memories_superseded[:\s]+(\d+)/i) || resultText.match(/superseded[:\s]+(\d+)/i);
|
|
46646
|
+
const resolvedMatch = resultText.match(/memories_resolved[:\s]+(\d+)/i) || resultText.match(/resolved[:\s]+(\d+)/i);
|
|
46647
|
+
const linkedMatch = resultText.match(/memories_linked[:\s]+(\d+)/i) || resultText.match(/linked[:\s]+(\d+)/i);
|
|
46648
|
+
const filesReadMatch = resultText.match(/files_read[:\s]+(\d+)/i);
|
|
46649
|
+
const filesWrittenMatch = resultText.match(/files_written[:\s]+(\d+)/i);
|
|
46650
|
+
const primerUpdated = /primer_updated[:\s]+true/i.test(resultText) || /PRIMER\s+OK/i.test(resultText);
|
|
46651
|
+
const readActions = actions.filter((a2) => a2.startsWith("READ OK")).length;
|
|
46652
|
+
const writeActions = actions.filter((a2) => a2.startsWith("WRITE OK")).length;
|
|
46355
46653
|
return {
|
|
46356
46654
|
success: true,
|
|
46357
46655
|
superseded: supersededMatch ? parseInt(supersededMatch[1]) : 0,
|
|
46358
46656
|
resolved: resolvedMatch ? parseInt(resolvedMatch[1]) : 0,
|
|
46359
46657
|
linked: linkedMatch ? parseInt(linkedMatch[1]) : 0,
|
|
46658
|
+
filesRead: filesReadMatch ? parseInt(filesReadMatch[1]) : readActions,
|
|
46659
|
+
filesWritten: filesWrittenMatch ? parseInt(filesWrittenMatch[1]) : writeActions,
|
|
46360
46660
|
primerUpdated,
|
|
46361
|
-
|
|
46661
|
+
actions,
|
|
46662
|
+
summary: resultText.slice(0, 500),
|
|
46663
|
+
fullReport
|
|
46362
46664
|
};
|
|
46363
46665
|
} catch {
|
|
46364
|
-
return
|
|
46365
|
-
|
|
46366
|
-
|
|
46367
|
-
|
|
46368
|
-
|
|
46369
|
-
|
|
46370
|
-
|
|
46371
|
-
|
|
46372
|
-
|
|
46373
|
-
|
|
46666
|
+
return emptyResult("Failed to parse response");
|
|
46667
|
+
}
|
|
46668
|
+
}
|
|
46669
|
+
async _buildSettingsFile(storagePaths) {
|
|
46670
|
+
const allowRules = [];
|
|
46671
|
+
const globalPath = storagePaths?.globalPath ?? import_path5.join(import_os4.homedir(), ".local", "share", "memory", "global");
|
|
46672
|
+
const projectPath = storagePaths?.projectPath ?? import_path5.join(import_os4.homedir(), ".local", "share", "memory");
|
|
46673
|
+
allowRules.push("Glob");
|
|
46674
|
+
allowRules.push("Grep");
|
|
46675
|
+
const formatPath = (p2) => p2.startsWith("/") ? "/" + p2 : "//" + p2;
|
|
46676
|
+
allowRules.push(`Read(${formatPath(globalPath)}/**)`);
|
|
46677
|
+
allowRules.push(`Write(${formatPath(globalPath)}/**)`);
|
|
46678
|
+
allowRules.push(`Edit(${formatPath(globalPath)}/**)`);
|
|
46679
|
+
allowRules.push(`Read(${formatPath(projectPath)}/**)`);
|
|
46680
|
+
allowRules.push(`Write(${formatPath(projectPath)}/**)`);
|
|
46681
|
+
allowRules.push(`Edit(${formatPath(projectPath)}/**)`);
|
|
46682
|
+
const settings = {
|
|
46683
|
+
permissions: {
|
|
46684
|
+
allow: allowRules,
|
|
46685
|
+
deny: [
|
|
46686
|
+
"Read(/etc/**)",
|
|
46687
|
+
"Read(~/.ssh/**)",
|
|
46688
|
+
"Read(~/.aws/**)",
|
|
46689
|
+
"Read(~/.gnupg/**)",
|
|
46690
|
+
"Read(.env)",
|
|
46691
|
+
"Read(.env.*)"
|
|
46692
|
+
]
|
|
46693
|
+
}
|
|
46694
|
+
};
|
|
46695
|
+
const tempPath = import_path5.join(import_os4.homedir(), ".local", "share", "memory", ".manager-settings.json");
|
|
46696
|
+
await Bun.write(tempPath, JSON.stringify(settings, null, 2));
|
|
46697
|
+
return tempPath;
|
|
46374
46698
|
}
|
|
46375
|
-
async manageWithCLI(projectId, sessionNumber, result) {
|
|
46376
|
-
if (process.env.MEMORY_MANAGER_DISABLED === "1") {
|
|
46699
|
+
async manageWithCLI(projectId, sessionNumber, result, storagePaths) {
|
|
46700
|
+
if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === "1") {
|
|
46377
46701
|
return {
|
|
46378
46702
|
success: true,
|
|
46379
46703
|
superseded: 0,
|
|
46380
46704
|
resolved: 0,
|
|
46381
46705
|
linked: 0,
|
|
46706
|
+
filesRead: 0,
|
|
46707
|
+
filesWritten: 0,
|
|
46382
46708
|
primerUpdated: false,
|
|
46383
|
-
|
|
46709
|
+
actions: [],
|
|
46710
|
+
summary: "Management agent disabled",
|
|
46711
|
+
fullReport: "Management agent disabled via configuration"
|
|
46384
46712
|
};
|
|
46385
46713
|
}
|
|
46386
46714
|
if (result.memories.length === 0) {
|
|
@@ -46389,8 +46717,12 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46389
46717
|
superseded: 0,
|
|
46390
46718
|
resolved: 0,
|
|
46391
46719
|
linked: 0,
|
|
46720
|
+
filesRead: 0,
|
|
46721
|
+
filesWritten: 0,
|
|
46392
46722
|
primerUpdated: false,
|
|
46393
|
-
|
|
46723
|
+
actions: [],
|
|
46724
|
+
summary: "No memories to process",
|
|
46725
|
+
fullReport: "No memories to process - skipped"
|
|
46394
46726
|
};
|
|
46395
46727
|
}
|
|
46396
46728
|
const systemPrompt = await this.buildManagementPrompt();
|
|
@@ -46400,12 +46732,17 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46400
46732
|
superseded: 0,
|
|
46401
46733
|
resolved: 0,
|
|
46402
46734
|
linked: 0,
|
|
46735
|
+
filesRead: 0,
|
|
46736
|
+
filesWritten: 0,
|
|
46403
46737
|
primerUpdated: false,
|
|
46738
|
+
actions: [],
|
|
46404
46739
|
summary: "",
|
|
46740
|
+
fullReport: "Error: Management skill file not found",
|
|
46405
46741
|
error: "Management skill not found"
|
|
46406
46742
|
};
|
|
46407
46743
|
}
|
|
46408
|
-
const userMessage = this.buildUserMessage(projectId, sessionNumber, result);
|
|
46744
|
+
const userMessage = this.buildUserMessage(projectId, sessionNumber, result, storagePaths);
|
|
46745
|
+
const settingsPath = await this._buildSettingsFile(storagePaths);
|
|
46409
46746
|
const args = [
|
|
46410
46747
|
"-p",
|
|
46411
46748
|
userMessage,
|
|
@@ -46413,9 +46750,12 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46413
46750
|
systemPrompt,
|
|
46414
46751
|
"--output-format",
|
|
46415
46752
|
"json",
|
|
46416
|
-
"--
|
|
46417
|
-
|
|
46753
|
+
"--settings",
|
|
46754
|
+
settingsPath
|
|
46418
46755
|
];
|
|
46756
|
+
if (this._config.maxTurns !== undefined) {
|
|
46757
|
+
args.push("--max-turns", String(this._config.maxTurns));
|
|
46758
|
+
}
|
|
46419
46759
|
const proc = Bun.spawn([this._config.cliCommand, ...args], {
|
|
46420
46760
|
env: {
|
|
46421
46761
|
...process.env,
|
|
@@ -46429,14 +46769,20 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46429
46769
|
]);
|
|
46430
46770
|
const exitCode = await proc.exited;
|
|
46431
46771
|
if (exitCode !== 0) {
|
|
46772
|
+
const errorMsg = stderr || `Exit code ${exitCode}`;
|
|
46432
46773
|
return {
|
|
46433
46774
|
success: false,
|
|
46434
46775
|
superseded: 0,
|
|
46435
46776
|
resolved: 0,
|
|
46436
46777
|
linked: 0,
|
|
46778
|
+
filesRead: 0,
|
|
46779
|
+
filesWritten: 0,
|
|
46437
46780
|
primerUpdated: false,
|
|
46781
|
+
actions: [],
|
|
46438
46782
|
summary: "",
|
|
46439
|
-
|
|
46783
|
+
fullReport: `Error: CLI failed with exit code ${exitCode}
|
|
46784
|
+
${stderr}`,
|
|
46785
|
+
error: errorMsg
|
|
46440
46786
|
};
|
|
46441
46787
|
}
|
|
46442
46788
|
return this.parseManagementResponse(stdout);
|
|
@@ -46453,17 +46799,28 @@ async function createServer(config = {}) {
|
|
|
46453
46799
|
host = "localhost",
|
|
46454
46800
|
curator: curatorConfig,
|
|
46455
46801
|
manager: managerConfig,
|
|
46802
|
+
managerEnabled,
|
|
46803
|
+
personalMemoriesEnabled,
|
|
46456
46804
|
...engineConfig
|
|
46457
46805
|
} = config;
|
|
46458
46806
|
const embeddings = createEmbeddings();
|
|
46459
46807
|
logger.info("Initializing embedding model (this may take a moment on first run)...");
|
|
46460
46808
|
await embeddings.initialize();
|
|
46809
|
+
const finalCuratorConfig = {
|
|
46810
|
+
...curatorConfig,
|
|
46811
|
+
personalMemoriesEnabled: personalMemoriesEnabled ?? curatorConfig?.personalMemoriesEnabled
|
|
46812
|
+
};
|
|
46813
|
+
const finalManagerConfig = {
|
|
46814
|
+
...managerConfig,
|
|
46815
|
+
enabled: managerEnabled ?? managerConfig?.enabled
|
|
46816
|
+
};
|
|
46461
46817
|
const engine = createEngine({
|
|
46462
46818
|
...engineConfig,
|
|
46463
|
-
embedder: embeddings.createEmbedder()
|
|
46819
|
+
embedder: embeddings.createEmbedder(),
|
|
46820
|
+
personalMemoriesEnabled: finalCuratorConfig.personalMemoriesEnabled
|
|
46464
46821
|
});
|
|
46465
|
-
const curator = createCurator(
|
|
46466
|
-
const manager = createManager(
|
|
46822
|
+
const curator = createCurator(finalCuratorConfig);
|
|
46823
|
+
const manager = createManager(finalManagerConfig);
|
|
46467
46824
|
const server = Bun.serve({
|
|
46468
46825
|
port,
|
|
46469
46826
|
hostname: host,
|
|
@@ -46532,18 +46889,22 @@ async function createServer(config = {}) {
|
|
|
46532
46889
|
logger.logCurationComplete(result.memories.length, result.session_summary);
|
|
46533
46890
|
logger.logCuratedMemories(result.memories);
|
|
46534
46891
|
const sessionNumber = await engine.getSessionNumber(body.project_id, body.project_path);
|
|
46892
|
+
const storagePaths = engine.getStoragePaths(body.project_id, body.project_path);
|
|
46535
46893
|
setImmediate(async () => {
|
|
46536
46894
|
try {
|
|
46537
46895
|
logger.logManagementStart(result.memories.length);
|
|
46538
46896
|
const startTime = Date.now();
|
|
46539
|
-
const managementResult = await manager.manageWithCLI(body.project_id, sessionNumber, result);
|
|
46897
|
+
const managementResult = await manager.manageWithCLI(body.project_id, sessionNumber, result, storagePaths);
|
|
46540
46898
|
logger.logManagementComplete({
|
|
46541
46899
|
success: managementResult.success,
|
|
46542
46900
|
superseded: managementResult.superseded || undefined,
|
|
46543
46901
|
resolved: managementResult.resolved || undefined,
|
|
46544
46902
|
linked: managementResult.linked || undefined,
|
|
46903
|
+
filesRead: managementResult.filesRead || undefined,
|
|
46904
|
+
filesWritten: managementResult.filesWritten || undefined,
|
|
46545
46905
|
primerUpdated: managementResult.primerUpdated,
|
|
46546
|
-
|
|
46906
|
+
actions: managementResult.actions,
|
|
46907
|
+
fullReport: managementResult.fullReport,
|
|
46547
46908
|
error: managementResult.error
|
|
46548
46909
|
});
|
|
46549
46910
|
await engine.storeManagementLog({
|
|
@@ -46557,7 +46918,13 @@ async function createServer(config = {}) {
|
|
|
46557
46918
|
success: managementResult.success,
|
|
46558
46919
|
durationMs: Date.now() - startTime,
|
|
46559
46920
|
summary: managementResult.summary,
|
|
46560
|
-
|
|
46921
|
+
fullReport: managementResult.fullReport,
|
|
46922
|
+
error: managementResult.error,
|
|
46923
|
+
details: {
|
|
46924
|
+
actions: managementResult.actions,
|
|
46925
|
+
filesRead: managementResult.filesRead,
|
|
46926
|
+
filesWritten: managementResult.filesWritten
|
|
46927
|
+
}
|
|
46561
46928
|
});
|
|
46562
46929
|
} catch (error) {
|
|
46563
46930
|
logger.error(`Management failed: ${error}`);
|
|
@@ -46606,11 +46973,15 @@ if (require.main == require.module) {
|
|
|
46606
46973
|
const host = process.env.MEMORY_HOST ?? "localhost";
|
|
46607
46974
|
const storageMode = process.env.MEMORY_STORAGE_MODE ?? "central";
|
|
46608
46975
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
46976
|
+
const managerEnabled = !["0", "false"].includes(process.env.MEMORY_MANAGER_ENABLED?.toLowerCase() ?? "");
|
|
46977
|
+
const personalMemoriesEnabled = !["0", "false"].includes(process.env.MEMORY_PERSONAL_ENABLED?.toLowerCase() ?? "");
|
|
46609
46978
|
(async () => {
|
|
46610
46979
|
await createServer({
|
|
46611
46980
|
port,
|
|
46612
46981
|
host,
|
|
46613
46982
|
storageMode,
|
|
46983
|
+
managerEnabled,
|
|
46984
|
+
personalMemoriesEnabled,
|
|
46614
46985
|
curator: { apiKey }
|
|
46615
46986
|
});
|
|
46616
46987
|
})();
|