@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.mjs
CHANGED
|
@@ -19865,6 +19865,11 @@ var managementLogSchema = {
|
|
|
19865
19865
|
error: "string",
|
|
19866
19866
|
details: "string"
|
|
19867
19867
|
};
|
|
19868
|
+
var personalPrimerSchema = {
|
|
19869
|
+
content: "string",
|
|
19870
|
+
session_updated: "number",
|
|
19871
|
+
updated_by: "string"
|
|
19872
|
+
};
|
|
19868
19873
|
|
|
19869
19874
|
// src/core/store.ts
|
|
19870
19875
|
var PERSONAL_PRIMER_ID = "personal-primer";
|
|
@@ -19886,7 +19891,7 @@ class MemoryStore {
|
|
|
19886
19891
|
return this._global;
|
|
19887
19892
|
}
|
|
19888
19893
|
const globalPath = this._config.globalPath;
|
|
19889
|
-
console.log(`\uD83C\uDF10 [DEBUG]
|
|
19894
|
+
console.log(`\uD83C\uDF10 [DEBUG] Initializing global database at ${globalPath}`);
|
|
19890
19895
|
const db = createDatabase({
|
|
19891
19896
|
name: "global",
|
|
19892
19897
|
basePath: globalPath
|
|
@@ -19903,8 +19908,14 @@ class MemoryStore {
|
|
|
19903
19908
|
autoSave: true,
|
|
19904
19909
|
watchFiles: this._config.watchFiles
|
|
19905
19910
|
});
|
|
19906
|
-
|
|
19907
|
-
|
|
19911
|
+
const primer = db.collection("primer", {
|
|
19912
|
+
schema: personalPrimerSchema,
|
|
19913
|
+
contentColumn: "content",
|
|
19914
|
+
autoSave: true,
|
|
19915
|
+
watchFiles: this._config.watchFiles
|
|
19916
|
+
});
|
|
19917
|
+
await Promise.all([memories.load(), managementLogs.load(), primer.load()]);
|
|
19918
|
+
this._global = { db, memories, managementLogs, primer };
|
|
19908
19919
|
return this._global;
|
|
19909
19920
|
}
|
|
19910
19921
|
async getGlobalMemories() {
|
|
@@ -19981,40 +19992,31 @@ class MemoryStore {
|
|
|
19981
19992
|
return id;
|
|
19982
19993
|
}
|
|
19983
19994
|
async getPersonalPrimer() {
|
|
19984
|
-
const {
|
|
19985
|
-
const
|
|
19986
|
-
if (!
|
|
19995
|
+
const { primer } = await this.getGlobal();
|
|
19996
|
+
const record = primer.get(PERSONAL_PRIMER_ID);
|
|
19997
|
+
if (!record) {
|
|
19987
19998
|
return null;
|
|
19988
19999
|
}
|
|
19989
20000
|
return {
|
|
19990
|
-
content:
|
|
19991
|
-
updated:
|
|
20001
|
+
content: record.content,
|
|
20002
|
+
updated: record.updated
|
|
19992
20003
|
};
|
|
19993
20004
|
}
|
|
19994
|
-
async setPersonalPrimer(content) {
|
|
19995
|
-
const {
|
|
19996
|
-
const existing =
|
|
20005
|
+
async setPersonalPrimer(content, sessionNumber, updatedBy = "user") {
|
|
20006
|
+
const { primer } = await this.getGlobal();
|
|
20007
|
+
const existing = primer.get(PERSONAL_PRIMER_ID);
|
|
19997
20008
|
if (existing) {
|
|
19998
|
-
|
|
20009
|
+
primer.update(PERSONAL_PRIMER_ID, {
|
|
20010
|
+
content,
|
|
20011
|
+
session_updated: sessionNumber ?? existing.session_updated,
|
|
20012
|
+
updated_by: updatedBy
|
|
20013
|
+
});
|
|
19999
20014
|
} else {
|
|
20000
|
-
|
|
20015
|
+
primer.insert({
|
|
20001
20016
|
id: PERSONAL_PRIMER_ID,
|
|
20002
20017
|
content,
|
|
20003
|
-
|
|
20004
|
-
|
|
20005
|
-
confidence_score: 1,
|
|
20006
|
-
context_type: "personal",
|
|
20007
|
-
temporal_relevance: "persistent",
|
|
20008
|
-
knowledge_domain: "personal",
|
|
20009
|
-
emotional_resonance: "neutral",
|
|
20010
|
-
action_required: false,
|
|
20011
|
-
problem_solution_pair: false,
|
|
20012
|
-
semantic_tags: ["personal", "primer", "relationship"],
|
|
20013
|
-
trigger_phrases: [],
|
|
20014
|
-
question_types: [],
|
|
20015
|
-
session_id: "system",
|
|
20016
|
-
project_id: "global",
|
|
20017
|
-
embedding: null
|
|
20018
|
+
session_updated: sessionNumber ?? 0,
|
|
20019
|
+
updated_by: updatedBy
|
|
20018
20020
|
});
|
|
20019
20021
|
}
|
|
20020
20022
|
}
|
|
@@ -20034,6 +20036,7 @@ class MemoryStore {
|
|
|
20034
20036
|
success: entry.success,
|
|
20035
20037
|
duration_ms: entry.durationMs,
|
|
20036
20038
|
summary: entry.summary,
|
|
20039
|
+
full_report: entry.fullReport ?? "",
|
|
20037
20040
|
error: entry.error ?? "",
|
|
20038
20041
|
details: entry.details ? JSON.stringify(entry.details) : ""
|
|
20039
20042
|
});
|
|
@@ -20397,7 +20400,14 @@ var sym = {
|
|
|
20397
20400
|
fire: "\uD83D\uDD25",
|
|
20398
20401
|
target: "\uD83C\uDFAF"
|
|
20399
20402
|
};
|
|
20403
|
+
var _verbose = false;
|
|
20400
20404
|
var logger = {
|
|
20405
|
+
setVerbose(enabled) {
|
|
20406
|
+
_verbose = enabled;
|
|
20407
|
+
},
|
|
20408
|
+
isVerbose() {
|
|
20409
|
+
return _verbose;
|
|
20410
|
+
},
|
|
20401
20411
|
info(message) {
|
|
20402
20412
|
console.log(`${timestamp()} ${style("cyan", sym.info)} ${message}`);
|
|
20403
20413
|
},
|
|
@@ -20469,7 +20479,14 @@ var logger = {
|
|
|
20469
20479
|
problem_solution: "✅",
|
|
20470
20480
|
project_context: "\uD83D\uDCE6",
|
|
20471
20481
|
milestone: "\uD83C\uDFC6",
|
|
20472
|
-
general: "\uD83D\uDCDD"
|
|
20482
|
+
general: "\uD83D\uDCDD",
|
|
20483
|
+
project_state: "\uD83D\uDCCD",
|
|
20484
|
+
pending_task: "⏳",
|
|
20485
|
+
work_in_progress: "\uD83D\uDD28",
|
|
20486
|
+
system_feedback: "\uD83D\uDCE3",
|
|
20487
|
+
project_milestone: "\uD83C\uDFC6",
|
|
20488
|
+
architectural_insight: "\uD83C\uDFDB️",
|
|
20489
|
+
architectural_direction: "\uD83E\uDDED"
|
|
20473
20490
|
};
|
|
20474
20491
|
console.log();
|
|
20475
20492
|
console.log(`${timestamp()} ${style("cyan", sym.sparkles)} ${style("bold", `SURFACING ${memories.length} MEMORIES`)}`);
|
|
@@ -20481,11 +20498,12 @@ var logger = {
|
|
|
20481
20498
|
return;
|
|
20482
20499
|
}
|
|
20483
20500
|
memories.forEach((m, i) => {
|
|
20484
|
-
const
|
|
20501
|
+
const signalCount = Math.round(m.score * 6);
|
|
20502
|
+
const signalStr = style("green", `${signalCount}sig`);
|
|
20485
20503
|
const emoji = emojiMap[m.context_type?.toLowerCase()] ?? "\uD83D\uDCDD";
|
|
20486
20504
|
const num = style("dim", `${i + 1}.`);
|
|
20487
20505
|
const preview = m.content.length > 55 ? m.content.slice(0, 55) + style("dim", "...") : m.content;
|
|
20488
|
-
console.log(` ${num} [${
|
|
20506
|
+
console.log(` ${num} [${signalStr}] ${emoji}`);
|
|
20489
20507
|
console.log(` ${preview}`);
|
|
20490
20508
|
});
|
|
20491
20509
|
console.log();
|
|
@@ -20533,44 +20551,124 @@ var logger = {
|
|
|
20533
20551
|
console.log(` ${style("dim", "processing:")} ${memoriesCount} new memories`);
|
|
20534
20552
|
},
|
|
20535
20553
|
logManagementComplete(result) {
|
|
20554
|
+
const formatAction = (action, truncate = true) => {
|
|
20555
|
+
let icon = " •";
|
|
20556
|
+
if (action.startsWith("READ OK"))
|
|
20557
|
+
icon = style("dim", " \uD83D\uDCD6");
|
|
20558
|
+
else if (action.startsWith("READ FAILED"))
|
|
20559
|
+
icon = style("red", " ❌");
|
|
20560
|
+
else if (action.startsWith("WRITE OK"))
|
|
20561
|
+
icon = style("green", " ✏️");
|
|
20562
|
+
else if (action.startsWith("WRITE FAILED"))
|
|
20563
|
+
icon = style("red", " ❌");
|
|
20564
|
+
else if (action.startsWith("RECEIVED"))
|
|
20565
|
+
icon = style("cyan", " \uD83D\uDCE5");
|
|
20566
|
+
else if (action.startsWith("CREATED"))
|
|
20567
|
+
icon = style("green", " ✨");
|
|
20568
|
+
else if (action.startsWith("UPDATED"))
|
|
20569
|
+
icon = style("blue", " \uD83D\uDCDD");
|
|
20570
|
+
else if (action.startsWith("SUPERSEDED"))
|
|
20571
|
+
icon = style("yellow", " \uD83D\uDD04");
|
|
20572
|
+
else if (action.startsWith("RESOLVED"))
|
|
20573
|
+
icon = style("green", " ✅");
|
|
20574
|
+
else if (action.startsWith("LINKED"))
|
|
20575
|
+
icon = style("cyan", " \uD83D\uDD17");
|
|
20576
|
+
else if (action.startsWith("PRIMER"))
|
|
20577
|
+
icon = style("magenta", " \uD83D\uDC9C");
|
|
20578
|
+
else if (action.startsWith("SKIPPED"))
|
|
20579
|
+
icon = style("dim", " ⏭️");
|
|
20580
|
+
else if (action.startsWith("NO_ACTION"))
|
|
20581
|
+
icon = style("dim", " ◦");
|
|
20582
|
+
const text = truncate && action.length > 70 ? action.slice(0, 67) + "..." : action;
|
|
20583
|
+
return `${icon} ${style("dim", text)}`;
|
|
20584
|
+
};
|
|
20536
20585
|
if (result.success) {
|
|
20537
20586
|
console.log(` ${style("green", sym.check)} ${style("bold", "Completed")}`);
|
|
20538
|
-
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
|
|
20544
|
-
|
|
20545
|
-
|
|
20546
|
-
|
|
20547
|
-
|
|
20548
|
-
|
|
20549
|
-
|
|
20550
|
-
|
|
20551
|
-
|
|
20552
|
-
|
|
20587
|
+
if (_verbose) {
|
|
20588
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20589
|
+
console.log(` ${style("cyan", "\uD83D\uDCCA")} ${style("bold", "Statistics")}`);
|
|
20590
|
+
const filesRead = result.filesRead ?? 0;
|
|
20591
|
+
const filesWritten = result.filesWritten ?? 0;
|
|
20592
|
+
console.log(` ${style("dim", "Files read:")} ${filesRead > 0 ? style("green", String(filesRead)) : style("dim", "0")}`);
|
|
20593
|
+
console.log(` ${style("dim", "Files written:")} ${filesWritten > 0 ? style("green", String(filesWritten)) : style("dim", "0")}`);
|
|
20594
|
+
const superseded = result.superseded ?? 0;
|
|
20595
|
+
const resolved = result.resolved ?? 0;
|
|
20596
|
+
const linked = result.linked ?? 0;
|
|
20597
|
+
console.log(` ${style("dim", "Superseded:")} ${superseded > 0 ? style("yellow", String(superseded)) : style("dim", "0")}`);
|
|
20598
|
+
console.log(` ${style("dim", "Resolved:")} ${resolved > 0 ? style("green", String(resolved)) : style("dim", "0")}`);
|
|
20599
|
+
console.log(` ${style("dim", "Linked:")} ${linked > 0 ? style("cyan", String(linked)) : style("dim", "0")}`);
|
|
20600
|
+
console.log(` ${style("dim", "Primer:")} ${result.primerUpdated ? style("magenta", "updated") : style("dim", "unchanged")}`);
|
|
20601
|
+
if (result.actions && result.actions.length > 0) {
|
|
20602
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20603
|
+
console.log(` ${style("cyan", "\uD83C\uDFAC")} ${style("bold", "Actions")} ${style("dim", `(${result.actions.length} total)`)}`);
|
|
20604
|
+
for (const action of result.actions) {
|
|
20605
|
+
console.log(` ${formatAction(action, false)}`);
|
|
20606
|
+
}
|
|
20607
|
+
}
|
|
20608
|
+
if (result.fullReport) {
|
|
20609
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20610
|
+
console.log(` ${style("cyan", "\uD83D\uDCCB")} ${style("bold", "Full Report")}`);
|
|
20611
|
+
const reportLines = result.fullReport.split(`
|
|
20612
|
+
`);
|
|
20613
|
+
for (const line of reportLines) {
|
|
20614
|
+
if (line.includes("===")) {
|
|
20615
|
+
console.log(` ${style("bold", line)}`);
|
|
20616
|
+
} else if (line.match(/^[A-Z_]+:/)) {
|
|
20617
|
+
console.log(` ${style("cyan", line)}`);
|
|
20618
|
+
} else {
|
|
20619
|
+
console.log(` ${style("dim", line)}`);
|
|
20620
|
+
}
|
|
20621
|
+
}
|
|
20622
|
+
}
|
|
20623
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20553
20624
|
} else {
|
|
20554
|
-
|
|
20555
|
-
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
|
|
20625
|
+
const stats = [];
|
|
20626
|
+
if (result.superseded && result.superseded > 0)
|
|
20627
|
+
stats.push(`${result.superseded} superseded`);
|
|
20628
|
+
if (result.resolved && result.resolved > 0)
|
|
20629
|
+
stats.push(`${result.resolved} resolved`);
|
|
20630
|
+
if (result.linked && result.linked > 0)
|
|
20631
|
+
stats.push(`${result.linked} linked`);
|
|
20632
|
+
if (result.primerUpdated)
|
|
20633
|
+
stats.push("primer updated");
|
|
20634
|
+
if (stats.length > 0) {
|
|
20635
|
+
console.log(` ${style("dim", "changes:")} ${stats.join(style("dim", ", "))}`);
|
|
20636
|
+
} else {
|
|
20637
|
+
console.log(` ${style("dim", "changes:")} none (memories are current)`);
|
|
20638
|
+
}
|
|
20639
|
+
if (result.actions && result.actions.length > 0) {
|
|
20640
|
+
console.log(` ${style("dim", "actions:")}`);
|
|
20641
|
+
for (const action of result.actions.slice(0, 10)) {
|
|
20642
|
+
console.log(` ${formatAction(action, true)}`);
|
|
20643
|
+
}
|
|
20644
|
+
if (result.actions.length > 10) {
|
|
20645
|
+
console.log(` ${style("dim", ` ... and ${result.actions.length - 10} more actions`)}`);
|
|
20646
|
+
}
|
|
20647
|
+
}
|
|
20559
20648
|
}
|
|
20560
20649
|
} else {
|
|
20561
20650
|
console.log(` ${style("yellow", sym.warning)} ${style("bold", "Failed")}`);
|
|
20562
20651
|
if (result.error) {
|
|
20563
|
-
console.log(` ${style("
|
|
20652
|
+
console.log(` ${style("red", "error:")} ${result.error}`);
|
|
20653
|
+
}
|
|
20654
|
+
if (result.fullReport) {
|
|
20655
|
+
console.log(` ${style("dim", "─".repeat(50))}`);
|
|
20656
|
+
console.log(` ${style("red", "\uD83D\uDCCB")} ${style("bold", "Error Report:")}`);
|
|
20657
|
+
const reportLines = result.fullReport.split(`
|
|
20658
|
+
`);
|
|
20659
|
+
for (const line of reportLines) {
|
|
20660
|
+
console.log(` ${style("dim", line)}`);
|
|
20661
|
+
}
|
|
20564
20662
|
}
|
|
20565
20663
|
}
|
|
20566
20664
|
console.log();
|
|
20567
20665
|
},
|
|
20568
20666
|
logRetrievalScoring(params) {
|
|
20569
|
-
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, selectedMemories } = params;
|
|
20667
|
+
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, durationMs, selectedMemories } = params;
|
|
20668
|
+
const timeStr = durationMs !== undefined ? style("cyan", `${durationMs.toFixed(1)}ms`) : "";
|
|
20570
20669
|
console.log();
|
|
20571
|
-
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "
|
|
20572
|
-
console.log(` ${style("dim", "total:")} ${totalMemories}
|
|
20573
|
-
console.log(` ${style("dim", "pre-filtered:")} ${preFiltered} (inactive/excluded/scope)`);
|
|
20670
|
+
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "RETRIEVAL")} ${timeStr}`);
|
|
20671
|
+
console.log(` ${style("dim", "total:")} ${totalMemories} → ${style("dim", "filtered:")} ${preFiltered} → ${style("dim", "candidates:")} ${totalMemories - preFiltered}`);
|
|
20574
20672
|
console.log(` ${style("dim", "already injected:")} ${alreadyInjected}`);
|
|
20575
20673
|
const msgPreview = currentMessage.length > 60 ? currentMessage.slice(0, 60) + "..." : currentMessage;
|
|
20576
20674
|
console.log(` ${style("dim", "message:")} "${msgPreview}"`);
|
|
@@ -20589,347 +20687,471 @@ var logger = {
|
|
|
20589
20687
|
console.log();
|
|
20590
20688
|
selectedMemories.forEach((m, i) => {
|
|
20591
20689
|
const num = style("dim", `${i + 1}.`);
|
|
20592
|
-
const
|
|
20593
|
-
const
|
|
20594
|
-
const corr = style("magenta", `corr:${(m.corroboration_score * 100).toFixed(0)}%`);
|
|
20690
|
+
const signalsStr = style("green", `${m.signalCount} signals`);
|
|
20691
|
+
const imp = style("magenta", `imp:${(m.importance_weight * 100).toFixed(0)}%`);
|
|
20595
20692
|
const type = style("yellow", m.context_type.toUpperCase());
|
|
20596
|
-
const scope = m.isGlobal ? style("blue", "[G]") : "";
|
|
20597
|
-
console.log(` ${num} [${
|
|
20693
|
+
const scope = m.isGlobal ? style("blue", " [G]") : "";
|
|
20694
|
+
console.log(` ${num} [${signalsStr} • ${imp}] ${type}${scope}`);
|
|
20598
20695
|
const preview = m.content.length > 60 ? m.content.slice(0, 60) + style("dim", "...") : m.content;
|
|
20599
20696
|
console.log(` ${style("white", preview)}`);
|
|
20600
|
-
const
|
|
20601
|
-
if (
|
|
20602
|
-
|
|
20697
|
+
const firedSignals = [];
|
|
20698
|
+
if (m.signals.trigger) {
|
|
20699
|
+
firedSignals.push(`trigger:${(m.signals.triggerStrength * 100).toFixed(0)}%`);
|
|
20700
|
+
}
|
|
20701
|
+
if (m.signals.tags) {
|
|
20702
|
+
firedSignals.push(`tags:${m.signals.tagCount}`);
|
|
20603
20703
|
}
|
|
20604
|
-
if (m.
|
|
20605
|
-
|
|
20704
|
+
if (m.signals.domain)
|
|
20705
|
+
firedSignals.push("domain");
|
|
20706
|
+
if (m.signals.feature)
|
|
20707
|
+
firedSignals.push("feature");
|
|
20708
|
+
if (m.signals.content)
|
|
20709
|
+
firedSignals.push("content");
|
|
20710
|
+
if (m.signals.vector) {
|
|
20711
|
+
firedSignals.push(`vector:${(m.signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
20712
|
+
}
|
|
20713
|
+
if (firedSignals.length > 0) {
|
|
20714
|
+
console.log(` ${style("cyan", "signals:")} ${firedSignals.join(", ")}`);
|
|
20606
20715
|
}
|
|
20607
20716
|
console.log();
|
|
20608
20717
|
});
|
|
20718
|
+
},
|
|
20719
|
+
logScoreDistribution(params) {
|
|
20720
|
+
const { totalCandidates, passedGatekeeper, rejectedByGatekeeper, buckets, stats, signalBreakdown } = params;
|
|
20721
|
+
console.log();
|
|
20722
|
+
console.log(style("dim", " ─".repeat(30)));
|
|
20723
|
+
console.log(` ${style("bold", "ACTIVATION SIGNALS")}`);
|
|
20724
|
+
console.log();
|
|
20725
|
+
const passRate = totalCandidates > 0 ? (passedGatekeeper / totalCandidates * 100).toFixed(0) : "0";
|
|
20726
|
+
console.log(` ${style("dim", "Activated:")} ${style("green", String(passedGatekeeper))}/${totalCandidates} (${passRate}%)`);
|
|
20727
|
+
console.log(` ${style("dim", "Rejected:")} ${rejectedByGatekeeper} (< 2 signals)`);
|
|
20728
|
+
console.log();
|
|
20729
|
+
if (signalBreakdown && signalBreakdown.total > 0) {
|
|
20730
|
+
console.log(` ${style("cyan", "Signal Breakdown:")}`);
|
|
20731
|
+
const signals = [
|
|
20732
|
+
{ name: "trigger", count: signalBreakdown.trigger },
|
|
20733
|
+
{ name: "tags", count: signalBreakdown.tags },
|
|
20734
|
+
{ name: "domain", count: signalBreakdown.domain },
|
|
20735
|
+
{ name: "feature", count: signalBreakdown.feature },
|
|
20736
|
+
{ name: "content", count: signalBreakdown.content },
|
|
20737
|
+
{ name: "vector", count: signalBreakdown.vector }
|
|
20738
|
+
];
|
|
20739
|
+
for (const sig of signals) {
|
|
20740
|
+
const pct = (sig.count / signalBreakdown.total * 100).toFixed(0);
|
|
20741
|
+
const bar = "█".repeat(Math.round(sig.count / signalBreakdown.total * 20));
|
|
20742
|
+
console.log(` ${sig.name.padEnd(8)} ${bar.padEnd(20)} ${sig.count} (${pct}%)`);
|
|
20743
|
+
}
|
|
20744
|
+
console.log();
|
|
20745
|
+
}
|
|
20746
|
+
if (stats.max > 0) {
|
|
20747
|
+
console.log(` ${style("cyan", "Signals:")} min=${stats.min} max=${stats.max} mean=${stats.mean}`);
|
|
20748
|
+
console.log();
|
|
20749
|
+
}
|
|
20750
|
+
if (Object.keys(buckets).length > 0) {
|
|
20751
|
+
console.log(` ${style("bold", "Distribution:")}`);
|
|
20752
|
+
const maxBucketCount = Math.max(...Object.values(buckets), 1);
|
|
20753
|
+
const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals"];
|
|
20754
|
+
for (const bucket of bucketOrder) {
|
|
20755
|
+
const count = buckets[bucket] ?? 0;
|
|
20756
|
+
if (count > 0 || bucket === "2 signals") {
|
|
20757
|
+
const barLen = Math.round(count / maxBucketCount * 25);
|
|
20758
|
+
const bar = "█".repeat(barLen) + style("dim", "░".repeat(25 - barLen));
|
|
20759
|
+
const countStr = count.toString().padStart(3);
|
|
20760
|
+
console.log(` ${style("dim", bucket.padEnd(10))} ${bar} ${style("cyan", countStr)}`);
|
|
20761
|
+
}
|
|
20762
|
+
}
|
|
20763
|
+
console.log();
|
|
20764
|
+
}
|
|
20609
20765
|
}
|
|
20610
20766
|
};
|
|
20611
20767
|
|
|
20612
20768
|
// src/core/retrieval.ts
|
|
20613
|
-
var
|
|
20614
|
-
|
|
20615
|
-
|
|
20616
|
-
|
|
20617
|
-
|
|
20618
|
-
|
|
20619
|
-
|
|
20620
|
-
|
|
20621
|
-
|
|
20622
|
-
technical: ["implement", "code", "function", "class", "module", "api", "interface"]
|
|
20623
|
-
};
|
|
20624
|
-
var TEMPORAL_CLASS_SCORES = {
|
|
20625
|
-
eternal: 1,
|
|
20626
|
-
long_term: 0.9,
|
|
20627
|
-
medium_term: 0.7,
|
|
20628
|
-
short_term: 0.5,
|
|
20629
|
-
ephemeral: 0.3
|
|
20769
|
+
var GLOBAL_TYPE_PRIORITY = {
|
|
20770
|
+
technical: 1,
|
|
20771
|
+
preference: 2,
|
|
20772
|
+
architectural: 3,
|
|
20773
|
+
workflow: 4,
|
|
20774
|
+
decision: 5,
|
|
20775
|
+
breakthrough: 6,
|
|
20776
|
+
philosophy: 7,
|
|
20777
|
+
personal: 8
|
|
20630
20778
|
};
|
|
20779
|
+
var MIN_ACTIVATION_SIGNALS = 2;
|
|
20780
|
+
var STOPWORDS = new Set([
|
|
20781
|
+
"the",
|
|
20782
|
+
"is",
|
|
20783
|
+
"are",
|
|
20784
|
+
"was",
|
|
20785
|
+
"were",
|
|
20786
|
+
"to",
|
|
20787
|
+
"a",
|
|
20788
|
+
"an",
|
|
20789
|
+
"and",
|
|
20790
|
+
"or",
|
|
20791
|
+
"but",
|
|
20792
|
+
"in",
|
|
20793
|
+
"on",
|
|
20794
|
+
"at",
|
|
20795
|
+
"for",
|
|
20796
|
+
"with",
|
|
20797
|
+
"about",
|
|
20798
|
+
"when",
|
|
20799
|
+
"how",
|
|
20800
|
+
"what",
|
|
20801
|
+
"why",
|
|
20802
|
+
"where",
|
|
20803
|
+
"this",
|
|
20804
|
+
"that",
|
|
20805
|
+
"it",
|
|
20806
|
+
"of",
|
|
20807
|
+
"be",
|
|
20808
|
+
"have",
|
|
20809
|
+
"do",
|
|
20810
|
+
"does",
|
|
20811
|
+
"did",
|
|
20812
|
+
"will",
|
|
20813
|
+
"would",
|
|
20814
|
+
"could",
|
|
20815
|
+
"should",
|
|
20816
|
+
"can",
|
|
20817
|
+
"may",
|
|
20818
|
+
"might",
|
|
20819
|
+
"must",
|
|
20820
|
+
"shall",
|
|
20821
|
+
"has",
|
|
20822
|
+
"had",
|
|
20823
|
+
"been",
|
|
20824
|
+
"being",
|
|
20825
|
+
"i",
|
|
20826
|
+
"you",
|
|
20827
|
+
"we",
|
|
20828
|
+
"they",
|
|
20829
|
+
"he",
|
|
20830
|
+
"she",
|
|
20831
|
+
"my",
|
|
20832
|
+
"your",
|
|
20833
|
+
"our",
|
|
20834
|
+
"its",
|
|
20835
|
+
"his",
|
|
20836
|
+
"her",
|
|
20837
|
+
"their",
|
|
20838
|
+
"if",
|
|
20839
|
+
"then",
|
|
20840
|
+
"else",
|
|
20841
|
+
"so",
|
|
20842
|
+
"as",
|
|
20843
|
+
"from",
|
|
20844
|
+
"by",
|
|
20845
|
+
"into",
|
|
20846
|
+
"through",
|
|
20847
|
+
"during",
|
|
20848
|
+
"before",
|
|
20849
|
+
"after",
|
|
20850
|
+
"also",
|
|
20851
|
+
"now",
|
|
20852
|
+
"back",
|
|
20853
|
+
"get",
|
|
20854
|
+
"go",
|
|
20855
|
+
"come",
|
|
20856
|
+
"let",
|
|
20857
|
+
"like",
|
|
20858
|
+
"just",
|
|
20859
|
+
"know",
|
|
20860
|
+
"think",
|
|
20861
|
+
"see",
|
|
20862
|
+
"look",
|
|
20863
|
+
"make",
|
|
20864
|
+
"take",
|
|
20865
|
+
"want",
|
|
20866
|
+
"need"
|
|
20867
|
+
]);
|
|
20631
20868
|
|
|
20632
20869
|
class SmartVectorRetrieval {
|
|
20633
20870
|
_extractSignificantWords(text) {
|
|
20634
|
-
const
|
|
20635
|
-
"the",
|
|
20636
|
-
"is",
|
|
20637
|
-
"are",
|
|
20638
|
-
"was",
|
|
20639
|
-
"were",
|
|
20640
|
-
"to",
|
|
20641
|
-
"a",
|
|
20642
|
-
"an",
|
|
20643
|
-
"and",
|
|
20644
|
-
"or",
|
|
20645
|
-
"but",
|
|
20646
|
-
"in",
|
|
20647
|
-
"on",
|
|
20648
|
-
"at",
|
|
20649
|
-
"for",
|
|
20650
|
-
"with",
|
|
20651
|
-
"about",
|
|
20652
|
-
"when",
|
|
20653
|
-
"how",
|
|
20654
|
-
"what",
|
|
20655
|
-
"why",
|
|
20656
|
-
"where",
|
|
20657
|
-
"this",
|
|
20658
|
-
"that",
|
|
20659
|
-
"it",
|
|
20660
|
-
"of",
|
|
20661
|
-
"be",
|
|
20662
|
-
"have",
|
|
20663
|
-
"do",
|
|
20664
|
-
"does",
|
|
20665
|
-
"did",
|
|
20666
|
-
"will",
|
|
20667
|
-
"would",
|
|
20668
|
-
"could",
|
|
20669
|
-
"should",
|
|
20670
|
-
"can",
|
|
20671
|
-
"may",
|
|
20672
|
-
"might",
|
|
20673
|
-
"must",
|
|
20674
|
-
"shall",
|
|
20675
|
-
"has",
|
|
20676
|
-
"had",
|
|
20677
|
-
"been",
|
|
20678
|
-
"being",
|
|
20679
|
-
"i",
|
|
20680
|
-
"you",
|
|
20681
|
-
"we",
|
|
20682
|
-
"they",
|
|
20683
|
-
"he",
|
|
20684
|
-
"she",
|
|
20685
|
-
"my",
|
|
20686
|
-
"your",
|
|
20687
|
-
"our",
|
|
20688
|
-
"its",
|
|
20689
|
-
"his",
|
|
20690
|
-
"her",
|
|
20691
|
-
"their",
|
|
20692
|
-
"if",
|
|
20693
|
-
"then",
|
|
20694
|
-
"else",
|
|
20695
|
-
"so",
|
|
20696
|
-
"as",
|
|
20697
|
-
"from",
|
|
20698
|
-
"by",
|
|
20699
|
-
"into",
|
|
20700
|
-
"through",
|
|
20701
|
-
"during",
|
|
20702
|
-
"before",
|
|
20703
|
-
"after",
|
|
20704
|
-
"above",
|
|
20705
|
-
"below",
|
|
20706
|
-
"up",
|
|
20707
|
-
"down",
|
|
20708
|
-
"out",
|
|
20709
|
-
"off",
|
|
20710
|
-
"over",
|
|
20711
|
-
"under",
|
|
20712
|
-
"again",
|
|
20713
|
-
"further",
|
|
20714
|
-
"once",
|
|
20715
|
-
"here",
|
|
20716
|
-
"there",
|
|
20717
|
-
"all",
|
|
20718
|
-
"each",
|
|
20719
|
-
"few",
|
|
20720
|
-
"more",
|
|
20721
|
-
"most",
|
|
20722
|
-
"other",
|
|
20723
|
-
"some",
|
|
20724
|
-
"such",
|
|
20725
|
-
"no",
|
|
20726
|
-
"nor",
|
|
20727
|
-
"not",
|
|
20728
|
-
"only",
|
|
20729
|
-
"own",
|
|
20730
|
-
"same",
|
|
20731
|
-
"than",
|
|
20732
|
-
"too",
|
|
20733
|
-
"very",
|
|
20734
|
-
"just",
|
|
20735
|
-
"also",
|
|
20736
|
-
"now",
|
|
20737
|
-
"back",
|
|
20738
|
-
"get",
|
|
20739
|
-
"got",
|
|
20740
|
-
"go",
|
|
20741
|
-
"going",
|
|
20742
|
-
"gone",
|
|
20743
|
-
"come",
|
|
20744
|
-
"came",
|
|
20745
|
-
"let",
|
|
20746
|
-
"lets",
|
|
20747
|
-
"hey",
|
|
20748
|
-
"hi",
|
|
20749
|
-
"hello",
|
|
20750
|
-
"ok",
|
|
20751
|
-
"okay"
|
|
20752
|
-
]);
|
|
20753
|
-
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
|
|
20871
|
+
const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !STOPWORDS.has(w));
|
|
20754
20872
|
return new Set(words);
|
|
20755
20873
|
}
|
|
20756
|
-
_detectContextTypes(message) {
|
|
20757
|
-
const messageLower = message.toLowerCase();
|
|
20758
|
-
const detected = new Set;
|
|
20759
|
-
for (const [type, keywords] of Object.entries(TYPE_KEYWORDS)) {
|
|
20760
|
-
for (const keyword of keywords) {
|
|
20761
|
-
if (messageLower.includes(keyword)) {
|
|
20762
|
-
detected.add(type);
|
|
20763
|
-
break;
|
|
20764
|
-
}
|
|
20765
|
-
}
|
|
20766
|
-
}
|
|
20767
|
-
return detected;
|
|
20768
|
-
}
|
|
20769
20874
|
_preFilter(memories, currentProjectId, messageLower) {
|
|
20770
20875
|
return memories.filter((memory) => {
|
|
20771
|
-
if (memory.status && memory.status !== "active")
|
|
20876
|
+
if (memory.status && memory.status !== "active")
|
|
20772
20877
|
return false;
|
|
20773
|
-
|
|
20774
|
-
if (memory.exclude_from_retrieval === true) {
|
|
20878
|
+
if (memory.exclude_from_retrieval === true)
|
|
20775
20879
|
return false;
|
|
20776
|
-
|
|
20777
|
-
if (memory.superseded_by) {
|
|
20880
|
+
if (memory.superseded_by)
|
|
20778
20881
|
return false;
|
|
20779
|
-
}
|
|
20780
20882
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
20781
|
-
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
20883
|
+
if (!isGlobal && memory.project_id !== currentProjectId)
|
|
20782
20884
|
return false;
|
|
20783
|
-
}
|
|
20784
20885
|
if (memory.anti_triggers?.length) {
|
|
20785
20886
|
for (const antiTrigger of memory.anti_triggers) {
|
|
20786
|
-
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
20887
|
+
if (messageLower.includes(antiTrigger.toLowerCase()))
|
|
20787
20888
|
return false;
|
|
20788
|
-
}
|
|
20789
20889
|
}
|
|
20790
20890
|
}
|
|
20791
20891
|
return true;
|
|
20792
20892
|
});
|
|
20793
20893
|
}
|
|
20794
|
-
|
|
20795
|
-
|
|
20894
|
+
_checkTriggerActivation(messageLower, messageWords, triggerPhrases) {
|
|
20895
|
+
if (!triggerPhrases.length)
|
|
20896
|
+
return { activated: false, strength: 0 };
|
|
20897
|
+
let maxStrength = 0;
|
|
20898
|
+
for (const phrase of triggerPhrases) {
|
|
20899
|
+
const phraseLower = phrase.trim().toLowerCase();
|
|
20900
|
+
const phraseWords = phraseLower.split(/\s+/).filter((w) => !STOPWORDS.has(w) && w.length > 2);
|
|
20901
|
+
if (!phraseWords.length)
|
|
20902
|
+
continue;
|
|
20903
|
+
let matches = 0;
|
|
20904
|
+
for (const word of phraseWords) {
|
|
20905
|
+
if (messageWords.has(word) || messageLower.includes(word)) {
|
|
20906
|
+
matches++;
|
|
20907
|
+
} else if (messageWords.has(word.replace(/s$/, "")) || messageWords.has(word + "s") || messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
20908
|
+
matches += 0.8;
|
|
20909
|
+
}
|
|
20910
|
+
}
|
|
20911
|
+
const strength = phraseWords.length > 0 ? matches / phraseWords.length : 0;
|
|
20912
|
+
maxStrength = Math.max(maxStrength, strength);
|
|
20913
|
+
}
|
|
20914
|
+
return { activated: maxStrength >= 0.5, strength: maxStrength };
|
|
20915
|
+
}
|
|
20916
|
+
_checkTagActivation(messageLower, messageWords, tags) {
|
|
20917
|
+
if (!tags.length)
|
|
20918
|
+
return { activated: false, count: 0 };
|
|
20919
|
+
let matchCount = 0;
|
|
20920
|
+
for (const tag of tags) {
|
|
20921
|
+
const tagLower = tag.trim().toLowerCase();
|
|
20922
|
+
if (messageWords.has(tagLower) || messageLower.includes(tagLower)) {
|
|
20923
|
+
matchCount++;
|
|
20924
|
+
}
|
|
20925
|
+
}
|
|
20926
|
+
const threshold = tags.length <= 2 ? 1 : 2;
|
|
20927
|
+
return { activated: matchCount >= threshold, count: matchCount };
|
|
20928
|
+
}
|
|
20929
|
+
_checkDomainActivation(messageLower, messageWords, domain) {
|
|
20930
|
+
if (!domain)
|
|
20931
|
+
return false;
|
|
20932
|
+
const domainLower = domain.trim().toLowerCase();
|
|
20933
|
+
return messageWords.has(domainLower) || messageLower.includes(domainLower);
|
|
20934
|
+
}
|
|
20935
|
+
_checkFeatureActivation(messageLower, messageWords, feature) {
|
|
20936
|
+
if (!feature)
|
|
20937
|
+
return false;
|
|
20938
|
+
const featureLower = feature.trim().toLowerCase();
|
|
20939
|
+
return messageWords.has(featureLower) || messageLower.includes(featureLower);
|
|
20940
|
+
}
|
|
20941
|
+
_checkContentActivation(messageWords, memory) {
|
|
20942
|
+
const contentPreview = memory.content.slice(0, 200);
|
|
20943
|
+
const contentWords = this._extractSignificantWords(contentPreview);
|
|
20944
|
+
let overlap = 0;
|
|
20945
|
+
for (const word of messageWords) {
|
|
20946
|
+
if (contentWords.has(word))
|
|
20947
|
+
overlap++;
|
|
20948
|
+
}
|
|
20949
|
+
return overlap >= 3;
|
|
20950
|
+
}
|
|
20951
|
+
_vectorDebugSamples = [];
|
|
20952
|
+
_calculateVectorSimilarity(vec1, vec2) {
|
|
20953
|
+
if (!vec1 || !vec2) {
|
|
20954
|
+
return 0;
|
|
20955
|
+
}
|
|
20956
|
+
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
20957
|
+
const v2 = vec2 instanceof Float32Array ? vec2 : new Float32Array(vec2);
|
|
20958
|
+
const similarity = cosineSimilarity(v1, v2);
|
|
20959
|
+
if (this._vectorDebugSamples.length < 20) {
|
|
20960
|
+
this._vectorDebugSamples.push(similarity);
|
|
20961
|
+
}
|
|
20962
|
+
return similarity;
|
|
20963
|
+
}
|
|
20964
|
+
_logVectorStats() {
|
|
20965
|
+
if (this._vectorDebugSamples.length === 0)
|
|
20966
|
+
return;
|
|
20967
|
+
const samples = this._vectorDebugSamples;
|
|
20968
|
+
const min = Math.min(...samples);
|
|
20969
|
+
const max = Math.max(...samples);
|
|
20970
|
+
const avg = samples.reduce((a, b) => a + b, 0) / samples.length;
|
|
20971
|
+
console.log(`[DEBUG] Vector similarities: min=${(min * 100).toFixed(1)}% max=${(max * 100).toFixed(1)}% avg=${(avg * 100).toFixed(1)}% (${samples.length} samples)`);
|
|
20972
|
+
this._vectorDebugSamples = [];
|
|
20973
|
+
}
|
|
20974
|
+
_calculateImportanceScore(memory, signalCount, messageLower, messageWords) {
|
|
20796
20975
|
let score = 0;
|
|
20797
|
-
|
|
20798
|
-
|
|
20799
|
-
|
|
20800
|
-
|
|
20801
|
-
|
|
20802
|
-
|
|
20803
|
-
|
|
20804
|
-
|
|
20805
|
-
|
|
20806
|
-
|
|
20807
|
-
|
|
20808
|
-
|
|
20809
|
-
|
|
20810
|
-
|
|
20811
|
-
|
|
20812
|
-
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
|
|
20816
|
-
|
|
20817
|
-
|
|
20818
|
-
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
|
|
20822
|
-
|
|
20823
|
-
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
signals.push("type:" + memory.context_type);
|
|
20829
|
-
}
|
|
20830
|
-
const triggerMatch = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
|
|
20831
|
-
if (triggerMatch > 0.3) {
|
|
20832
|
-
score += Math.min(0.3, triggerMatch * 0.4);
|
|
20833
|
-
signals.push("trigger:" + triggerMatch.toFixed(2));
|
|
20834
|
-
}
|
|
20835
|
-
if (memory.feature) {
|
|
20836
|
-
const featureLower = memory.feature.toLowerCase();
|
|
20837
|
-
if (messageLower.includes(featureLower) || messageWords.has(featureLower)) {
|
|
20838
|
-
score += 0.2;
|
|
20839
|
-
signals.push("feature:" + memory.feature);
|
|
20840
|
-
}
|
|
20841
|
-
}
|
|
20842
|
-
if (memory.related_files?.length) {
|
|
20843
|
-
for (const file of memory.related_files) {
|
|
20844
|
-
const filename = file.split("/").pop()?.toLowerCase() ?? "";
|
|
20845
|
-
if (filename && messageLower.includes(filename)) {
|
|
20846
|
-
score += 0.25;
|
|
20847
|
-
signals.push("file:" + filename);
|
|
20976
|
+
score += memory.importance_weight ?? 0.5;
|
|
20977
|
+
if (signalCount >= 4)
|
|
20978
|
+
score += 0.2;
|
|
20979
|
+
else if (signalCount >= 3)
|
|
20980
|
+
score += 0.1;
|
|
20981
|
+
if (memory.awaiting_implementation)
|
|
20982
|
+
score += 0.15;
|
|
20983
|
+
if (memory.awaiting_decision)
|
|
20984
|
+
score += 0.1;
|
|
20985
|
+
const contextType = memory.context_type?.toLowerCase() ?? "";
|
|
20986
|
+
const contextKeywords = {
|
|
20987
|
+
debugging: ["debug", "bug", "error", "fix", "issue", "problem", "broken"],
|
|
20988
|
+
decision: ["decide", "decision", "choose", "choice", "option", "should"],
|
|
20989
|
+
architectural: ["architect", "design", "structure", "pattern", "how"],
|
|
20990
|
+
breakthrough: ["insight", "realize", "understand", "discover", "why"],
|
|
20991
|
+
technical: ["implement", "code", "function", "method", "api"],
|
|
20992
|
+
workflow: ["process", "workflow", "step", "flow", "pipeline"],
|
|
20993
|
+
philosophy: ["philosophy", "principle", "belief", "approach", "think"]
|
|
20994
|
+
};
|
|
20995
|
+
const keywords = contextKeywords[contextType] ?? [];
|
|
20996
|
+
for (const kw of keywords) {
|
|
20997
|
+
if (messageWords.has(kw) || messageLower.includes(kw)) {
|
|
20998
|
+
score += 0.1;
|
|
20999
|
+
break;
|
|
21000
|
+
}
|
|
21001
|
+
}
|
|
21002
|
+
if (memory.problem_solution_pair) {
|
|
21003
|
+
const problemWords = ["error", "bug", "issue", "problem", "wrong", "fail", "broken", "help", "stuck"];
|
|
21004
|
+
for (const pw of problemWords) {
|
|
21005
|
+
if (messageWords.has(pw) || messageLower.includes(pw)) {
|
|
21006
|
+
score += 0.1;
|
|
20848
21007
|
break;
|
|
20849
21008
|
}
|
|
20850
21009
|
}
|
|
20851
21010
|
}
|
|
20852
|
-
|
|
21011
|
+
const temporalClass = memory.temporal_class ?? "medium_term";
|
|
21012
|
+
if (temporalClass === "eternal")
|
|
21013
|
+
score += 0.1;
|
|
21014
|
+
else if (temporalClass === "long_term")
|
|
21015
|
+
score += 0.05;
|
|
21016
|
+
else if (temporalClass === "ephemeral") {
|
|
21017
|
+
if ((memory.sessions_since_surfaced ?? 0) <= 1)
|
|
21018
|
+
score += 0.1;
|
|
21019
|
+
}
|
|
21020
|
+
const confidence = memory.confidence_score ?? 0.7;
|
|
21021
|
+
if (confidence < 0.5)
|
|
21022
|
+
score -= 0.1;
|
|
21023
|
+
const emotionalKeywords = {
|
|
21024
|
+
frustration: ["frustrated", "annoying", "stuck", "ugh", "damn", "hate"],
|
|
21025
|
+
excitement: ["excited", "awesome", "amazing", "love", "great", "wow"],
|
|
21026
|
+
curiosity: ["wonder", "curious", "interesting", "how", "why", "what if"],
|
|
21027
|
+
satisfaction: ["done", "finished", "complete", "works", "solved", "finally"],
|
|
21028
|
+
discovery: ["found", "realized", "understand", "insight", "breakthrough"]
|
|
21029
|
+
};
|
|
21030
|
+
const emotion = memory.emotional_resonance?.toLowerCase() ?? "";
|
|
21031
|
+
const emotionKws = emotionalKeywords[emotion] ?? [];
|
|
21032
|
+
for (const ew of emotionKws) {
|
|
21033
|
+
if (messageWords.has(ew) || messageLower.includes(ew)) {
|
|
21034
|
+
score += 0.05;
|
|
21035
|
+
break;
|
|
21036
|
+
}
|
|
21037
|
+
}
|
|
21038
|
+
return score;
|
|
20853
21039
|
}
|
|
20854
21040
|
retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0, maxGlobalMemories = 2) {
|
|
21041
|
+
const startTime = performance.now();
|
|
20855
21042
|
if (!allMemories.length) {
|
|
20856
21043
|
return [];
|
|
20857
21044
|
}
|
|
20858
21045
|
const messageLower = currentMessage.toLowerCase();
|
|
20859
21046
|
const messageWords = this._extractSignificantWords(currentMessage);
|
|
20860
|
-
const detectedTypes = this._detectContextTypes(currentMessage);
|
|
20861
21047
|
const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
|
|
20862
21048
|
if (!candidates.length) {
|
|
20863
21049
|
return [];
|
|
20864
21050
|
}
|
|
20865
|
-
const
|
|
21051
|
+
const activatedMemories = [];
|
|
21052
|
+
let rejectedCount = 0;
|
|
20866
21053
|
for (const memory of candidates) {
|
|
20867
21054
|
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
20868
|
-
const
|
|
20869
|
-
const
|
|
20870
|
-
const
|
|
20871
|
-
const
|
|
20872
|
-
const
|
|
20873
|
-
const
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
|
|
20883
|
-
if (
|
|
21055
|
+
const triggerResult = this._checkTriggerActivation(messageLower, messageWords, memory.trigger_phrases ?? []);
|
|
21056
|
+
const tagResult = this._checkTagActivation(messageLower, messageWords, memory.semantic_tags ?? []);
|
|
21057
|
+
const domainActivated = this._checkDomainActivation(messageLower, messageWords, memory.domain);
|
|
21058
|
+
const featureActivated = this._checkFeatureActivation(messageLower, messageWords, memory.feature);
|
|
21059
|
+
const contentActivated = this._checkContentActivation(messageWords, memory);
|
|
21060
|
+
const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
|
|
21061
|
+
let signalCount = 0;
|
|
21062
|
+
if (triggerResult.activated)
|
|
21063
|
+
signalCount++;
|
|
21064
|
+
if (tagResult.activated)
|
|
21065
|
+
signalCount++;
|
|
21066
|
+
if (domainActivated)
|
|
21067
|
+
signalCount++;
|
|
21068
|
+
if (featureActivated)
|
|
21069
|
+
signalCount++;
|
|
21070
|
+
if (contentActivated)
|
|
21071
|
+
signalCount++;
|
|
21072
|
+
if (vectorSimilarity >= 0.4)
|
|
21073
|
+
signalCount++;
|
|
21074
|
+
const signals = {
|
|
21075
|
+
trigger: triggerResult.activated,
|
|
21076
|
+
tags: tagResult.activated,
|
|
21077
|
+
domain: domainActivated,
|
|
21078
|
+
feature: featureActivated,
|
|
21079
|
+
content: contentActivated,
|
|
21080
|
+
count: signalCount,
|
|
21081
|
+
triggerStrength: triggerResult.strength,
|
|
21082
|
+
tagCount: tagResult.count,
|
|
21083
|
+
vectorSimilarity
|
|
21084
|
+
};
|
|
21085
|
+
if (signalCount < MIN_ACTIVATION_SIGNALS) {
|
|
21086
|
+
rejectedCount++;
|
|
20884
21087
|
continue;
|
|
20885
21088
|
}
|
|
20886
|
-
const
|
|
20887
|
-
|
|
20888
|
-
corroboration: corroborationScore,
|
|
20889
|
-
reasoning_match: reasoningMatch,
|
|
20890
|
-
retrieval_weight: retrievalWeight,
|
|
20891
|
-
temporal: temporalScore,
|
|
20892
|
-
context: contextScore,
|
|
20893
|
-
tags: tagScore,
|
|
20894
|
-
trigger: triggerScore,
|
|
20895
|
-
domain: domainScore,
|
|
20896
|
-
question: questionScore,
|
|
20897
|
-
emotion: emotionScore,
|
|
20898
|
-
problem: problemScore,
|
|
20899
|
-
action: actionBoost
|
|
20900
|
-
};
|
|
20901
|
-
const reasoning = this._generateSelectionReasoning(components, corroborationSignals);
|
|
20902
|
-
scoredMemories.push({
|
|
21089
|
+
const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords);
|
|
21090
|
+
activatedMemories.push({
|
|
20903
21091
|
memory,
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
value_score: valueScore,
|
|
20907
|
-
corroboration_score: corroborationScore,
|
|
20908
|
-
reasoning,
|
|
20909
|
-
components,
|
|
21092
|
+
signals,
|
|
21093
|
+
importanceScore,
|
|
20910
21094
|
isGlobal
|
|
20911
21095
|
});
|
|
20912
21096
|
}
|
|
20913
|
-
|
|
21097
|
+
this._logActivationDistribution(activatedMemories, candidates.length, rejectedCount);
|
|
21098
|
+
this._logVectorStats();
|
|
21099
|
+
if (!activatedMemories.length) {
|
|
21100
|
+
const durationMs2 = performance.now() - startTime;
|
|
21101
|
+
logger.logRetrievalScoring({
|
|
21102
|
+
totalMemories: allMemories.length,
|
|
21103
|
+
currentMessage,
|
|
21104
|
+
alreadyInjected: alreadyInjectedCount,
|
|
21105
|
+
preFiltered: allMemories.length - candidates.length,
|
|
21106
|
+
globalCount: 0,
|
|
21107
|
+
projectCount: 0,
|
|
21108
|
+
finalCount: 0,
|
|
21109
|
+
durationMs: durationMs2,
|
|
21110
|
+
selectedMemories: []
|
|
21111
|
+
});
|
|
21112
|
+
return [];
|
|
21113
|
+
}
|
|
21114
|
+
activatedMemories.sort((a, b) => {
|
|
21115
|
+
if (b.signals.count !== a.signals.count) {
|
|
21116
|
+
return b.signals.count - a.signals.count;
|
|
21117
|
+
}
|
|
21118
|
+
return b.importanceScore - a.importanceScore;
|
|
21119
|
+
});
|
|
20914
21120
|
const selected = [];
|
|
20915
21121
|
const selectedIds = new Set;
|
|
20916
|
-
const globalMemories =
|
|
20917
|
-
const projectMemories =
|
|
20918
|
-
const
|
|
20919
|
-
const
|
|
20920
|
-
const
|
|
20921
|
-
if (
|
|
20922
|
-
return
|
|
20923
|
-
|
|
20924
|
-
|
|
21122
|
+
const globalMemories = activatedMemories.filter((m) => m.isGlobal);
|
|
21123
|
+
const projectMemories = activatedMemories.filter((m) => !m.isGlobal);
|
|
21124
|
+
const globalsSorted = globalMemories.sort((a, b) => {
|
|
21125
|
+
const aPriority = GLOBAL_TYPE_PRIORITY[a.memory.context_type ?? "personal"] ?? 8;
|
|
21126
|
+
const bPriority = GLOBAL_TYPE_PRIORITY[b.memory.context_type ?? "personal"] ?? 8;
|
|
21127
|
+
if (aPriority !== bPriority)
|
|
21128
|
+
return aPriority - bPriority;
|
|
21129
|
+
if (b.signals.count !== a.signals.count)
|
|
21130
|
+
return b.signals.count - a.signals.count;
|
|
21131
|
+
return b.importanceScore - a.importanceScore;
|
|
20925
21132
|
});
|
|
20926
|
-
for (const item of
|
|
21133
|
+
for (const item of globalsSorted.slice(0, maxGlobalMemories)) {
|
|
20927
21134
|
if (!selectedIds.has(item.memory.id)) {
|
|
20928
21135
|
selected.push(item);
|
|
20929
21136
|
selectedIds.add(item.memory.id);
|
|
20930
21137
|
}
|
|
20931
21138
|
}
|
|
20932
|
-
|
|
21139
|
+
const projectsSorted = [...projectMemories].sort((a, b) => {
|
|
21140
|
+
const aAction = a.memory.action_required ? 1 : 0;
|
|
21141
|
+
const bAction = b.memory.action_required ? 1 : 0;
|
|
21142
|
+
if (bAction !== aAction)
|
|
21143
|
+
return bAction - aAction;
|
|
21144
|
+
if (b.signals.count !== a.signals.count)
|
|
21145
|
+
return b.signals.count - a.signals.count;
|
|
21146
|
+
return b.importanceScore - a.importanceScore;
|
|
21147
|
+
});
|
|
21148
|
+
console.log(`[DEBUG] Top 15 candidates (sorted):`);
|
|
21149
|
+
for (let i = 0;i < Math.min(15, projectsSorted.length); i++) {
|
|
21150
|
+
const m = projectsSorted[i];
|
|
21151
|
+
const action = m.memory.action_required ? "⚡" : "";
|
|
21152
|
+
console.log(` ${i + 1}. [${m.signals.count}sig] score=${m.importanceScore.toFixed(2)} ${action} ${m.memory.content.slice(0, 45)}...`);
|
|
21153
|
+
}
|
|
21154
|
+
for (const item of projectsSorted) {
|
|
20933
21155
|
if (selected.length >= maxMemories)
|
|
20934
21156
|
break;
|
|
20935
21157
|
if (selectedIds.has(item.memory.id))
|
|
@@ -20937,7 +21159,27 @@ class SmartVectorRetrieval {
|
|
|
20937
21159
|
selected.push(item);
|
|
20938
21160
|
selectedIds.add(item.memory.id);
|
|
20939
21161
|
}
|
|
20940
|
-
selected.
|
|
21162
|
+
if (selected.length < maxMemories) {
|
|
21163
|
+
const relatedIds = new Set;
|
|
21164
|
+
for (const item of selected) {
|
|
21165
|
+
for (const relatedId of item.memory.related_to ?? []) {
|
|
21166
|
+
if (!selectedIds.has(relatedId)) {
|
|
21167
|
+
relatedIds.add(relatedId);
|
|
21168
|
+
}
|
|
21169
|
+
}
|
|
21170
|
+
}
|
|
21171
|
+
for (const item of activatedMemories) {
|
|
21172
|
+
if (selected.length >= maxMemories)
|
|
21173
|
+
break;
|
|
21174
|
+
if (selectedIds.has(item.memory.id))
|
|
21175
|
+
continue;
|
|
21176
|
+
if (relatedIds.has(item.memory.id)) {
|
|
21177
|
+
selected.push(item);
|
|
21178
|
+
selectedIds.add(item.memory.id);
|
|
21179
|
+
}
|
|
21180
|
+
}
|
|
21181
|
+
}
|
|
21182
|
+
const durationMs = performance.now() - startTime;
|
|
20941
21183
|
logger.logRetrievalScoring({
|
|
20942
21184
|
totalMemories: allMemories.length,
|
|
20943
21185
|
currentMessage,
|
|
@@ -20946,202 +21188,102 @@ class SmartVectorRetrieval {
|
|
|
20946
21188
|
globalCount: globalMemories.length,
|
|
20947
21189
|
projectCount: projectMemories.length,
|
|
20948
21190
|
finalCount: selected.length,
|
|
21191
|
+
durationMs,
|
|
20949
21192
|
selectedMemories: selected.map((item) => ({
|
|
20950
21193
|
content: item.memory.content,
|
|
20951
|
-
reasoning: item.
|
|
20952
|
-
|
|
20953
|
-
relevance_score: item.relevance_score,
|
|
20954
|
-
corroboration_score: item.corroboration_score,
|
|
21194
|
+
reasoning: this._generateActivationReasoning(item.signals),
|
|
21195
|
+
signalCount: item.signals.count,
|
|
20955
21196
|
importance_weight: item.memory.importance_weight ?? 0.5,
|
|
20956
21197
|
context_type: item.memory.context_type ?? "general",
|
|
20957
21198
|
semantic_tags: item.memory.semantic_tags ?? [],
|
|
20958
21199
|
isGlobal: item.isGlobal,
|
|
20959
|
-
|
|
21200
|
+
signals: {
|
|
21201
|
+
trigger: item.signals.trigger,
|
|
21202
|
+
triggerStrength: item.signals.triggerStrength,
|
|
21203
|
+
tags: item.signals.tags,
|
|
21204
|
+
tagCount: item.signals.tagCount,
|
|
21205
|
+
domain: item.signals.domain,
|
|
21206
|
+
feature: item.signals.feature,
|
|
21207
|
+
content: item.signals.content,
|
|
21208
|
+
vector: item.signals.vectorSimilarity >= 0.4,
|
|
21209
|
+
vectorSimilarity: item.signals.vectorSimilarity
|
|
21210
|
+
}
|
|
20960
21211
|
}))
|
|
20961
21212
|
});
|
|
20962
21213
|
return selected.map((item) => ({
|
|
20963
21214
|
...item.memory,
|
|
20964
|
-
score: item.
|
|
20965
|
-
relevance_score: item.
|
|
20966
|
-
value_score: item.
|
|
21215
|
+
score: item.signals.count / 6,
|
|
21216
|
+
relevance_score: item.signals.count / 6,
|
|
21217
|
+
value_score: item.importanceScore
|
|
20967
21218
|
}));
|
|
20968
21219
|
}
|
|
20969
|
-
|
|
20970
|
-
if (!vec1 || !vec2)
|
|
20971
|
-
return 0;
|
|
20972
|
-
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
20973
|
-
return cosineSimilarity(v1, vec2);
|
|
20974
|
-
}
|
|
20975
|
-
_scoreTemporalRelevance(temporalType) {
|
|
20976
|
-
const scores = {
|
|
20977
|
-
persistent: 0.8,
|
|
20978
|
-
session: 0.6,
|
|
20979
|
-
temporary: 0.3,
|
|
20980
|
-
archived: 0.1
|
|
20981
|
-
};
|
|
20982
|
-
return scores[temporalType] ?? 0.5;
|
|
20983
|
-
}
|
|
20984
|
-
_scoreContextAlignment(message, contextType) {
|
|
20985
|
-
const messageLower = message.toLowerCase();
|
|
20986
|
-
const keywords = TYPE_KEYWORDS[contextType] ?? [];
|
|
20987
|
-
const matches = keywords.filter((kw) => messageLower.includes(kw)).length;
|
|
20988
|
-
if (matches > 0) {
|
|
20989
|
-
return Math.min(0.2 + matches * 0.15, 0.7);
|
|
20990
|
-
}
|
|
20991
|
-
return 0.1;
|
|
20992
|
-
}
|
|
20993
|
-
_scoreSemanticTags(message, tags) {
|
|
20994
|
-
if (!tags.length)
|
|
20995
|
-
return 0;
|
|
20996
|
-
const messageLower = message.toLowerCase();
|
|
20997
|
-
const matches = tags.filter((tag) => messageLower.includes(tag.trim().toLowerCase())).length;
|
|
20998
|
-
if (matches > 0) {
|
|
20999
|
-
return Math.min(0.3 + matches * 0.25, 1);
|
|
21000
|
-
}
|
|
21001
|
-
return 0;
|
|
21002
|
-
}
|
|
21003
|
-
_scoreTriggerPhrases(messageLower, triggerPhrases) {
|
|
21004
|
-
if (!triggerPhrases.length)
|
|
21005
|
-
return 0;
|
|
21006
|
-
const stopWords = new Set([
|
|
21007
|
-
"the",
|
|
21008
|
-
"is",
|
|
21009
|
-
"are",
|
|
21010
|
-
"was",
|
|
21011
|
-
"were",
|
|
21012
|
-
"to",
|
|
21013
|
-
"a",
|
|
21014
|
-
"an",
|
|
21015
|
-
"and",
|
|
21016
|
-
"or",
|
|
21017
|
-
"but",
|
|
21018
|
-
"in",
|
|
21019
|
-
"on",
|
|
21020
|
-
"at",
|
|
21021
|
-
"for",
|
|
21022
|
-
"with",
|
|
21023
|
-
"about",
|
|
21024
|
-
"when",
|
|
21025
|
-
"how",
|
|
21026
|
-
"what",
|
|
21027
|
-
"why"
|
|
21028
|
-
]);
|
|
21029
|
-
let maxScore = 0;
|
|
21030
|
-
for (const pattern of triggerPhrases) {
|
|
21031
|
-
const patternLower = pattern.trim().toLowerCase();
|
|
21032
|
-
const patternWords = patternLower.split(/\s+/).filter((w) => !stopWords.has(w) && w.length > 2);
|
|
21033
|
-
if (patternWords.length) {
|
|
21034
|
-
let matches = 0;
|
|
21035
|
-
for (const word of patternWords) {
|
|
21036
|
-
if (messageLower.includes(word)) {
|
|
21037
|
-
matches += 1;
|
|
21038
|
-
} else if (messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
21039
|
-
matches += 0.9;
|
|
21040
|
-
}
|
|
21041
|
-
}
|
|
21042
|
-
const conceptScore = matches / patternWords.length;
|
|
21043
|
-
maxScore = Math.max(maxScore, conceptScore);
|
|
21044
|
-
}
|
|
21045
|
-
}
|
|
21046
|
-
return Math.min(maxScore, 1);
|
|
21047
|
-
}
|
|
21048
|
-
_scoreDomain(messageWords, messageLower, memory) {
|
|
21049
|
-
let score = 0;
|
|
21050
|
-
if (memory.domain) {
|
|
21051
|
-
const domainLower = memory.domain.toLowerCase();
|
|
21052
|
-
if (messageWords.has(domainLower) || messageLower.includes(domainLower)) {
|
|
21053
|
-
score += 0.5;
|
|
21054
|
-
}
|
|
21055
|
-
}
|
|
21056
|
-
if (memory.feature) {
|
|
21057
|
-
const featureLower = memory.feature.toLowerCase();
|
|
21058
|
-
if (messageWords.has(featureLower) || messageLower.includes(featureLower)) {
|
|
21059
|
-
score += 0.3;
|
|
21060
|
-
}
|
|
21061
|
-
}
|
|
21062
|
-
if (memory.component) {
|
|
21063
|
-
const componentLower = memory.component.toLowerCase();
|
|
21064
|
-
if (messageWords.has(componentLower) || messageLower.includes(componentLower)) {
|
|
21065
|
-
score += 0.2;
|
|
21066
|
-
}
|
|
21067
|
-
}
|
|
21068
|
-
return Math.min(score, 1);
|
|
21069
|
-
}
|
|
21070
|
-
_scoreQuestionTypes(message, questionTypes) {
|
|
21071
|
-
if (!questionTypes.length)
|
|
21072
|
-
return 0;
|
|
21073
|
-
const messageLower = message.toLowerCase();
|
|
21074
|
-
const questionWords = ["how", "why", "what", "when", "where"];
|
|
21075
|
-
for (const qtype of questionTypes) {
|
|
21076
|
-
const qtypeLower = qtype.trim().toLowerCase();
|
|
21077
|
-
if (messageLower.includes(qtypeLower)) {
|
|
21078
|
-
return 0.8;
|
|
21079
|
-
}
|
|
21080
|
-
const messageHasQuestion = questionWords.some((qw) => messageLower.includes(qw));
|
|
21081
|
-
const typeHasQuestion = questionWords.some((qw) => qtypeLower.includes(qw));
|
|
21082
|
-
if (messageHasQuestion && typeHasQuestion) {
|
|
21083
|
-
return 0.5;
|
|
21084
|
-
}
|
|
21085
|
-
}
|
|
21086
|
-
return 0;
|
|
21087
|
-
}
|
|
21088
|
-
_scoreEmotionalContext(message, emotion) {
|
|
21089
|
-
if (!emotion)
|
|
21090
|
-
return 0;
|
|
21091
|
-
const messageLower = message.toLowerCase();
|
|
21092
|
-
const emotionPatterns = {
|
|
21093
|
-
joy: ["happy", "excited", "love", "wonderful", "great", "awesome"],
|
|
21094
|
-
frustration: ["stuck", "confused", "help", "issue", "problem", "why"],
|
|
21095
|
-
discovery: ["realized", "found", "discovered", "aha", "insight"],
|
|
21096
|
-
gratitude: ["thank", "appreciate", "grateful", "dear friend"]
|
|
21097
|
-
};
|
|
21098
|
-
const patterns = emotionPatterns[emotion.toLowerCase()] ?? [];
|
|
21099
|
-
if (patterns.some((pattern) => messageLower.includes(pattern))) {
|
|
21100
|
-
return 0.7;
|
|
21101
|
-
}
|
|
21102
|
-
return 0;
|
|
21103
|
-
}
|
|
21104
|
-
_scoreProblemSolution(message, isProblemSolution) {
|
|
21105
|
-
if (!isProblemSolution)
|
|
21106
|
-
return 0;
|
|
21107
|
-
const messageLower = message.toLowerCase();
|
|
21108
|
-
const problemWords = ["error", "issue", "problem", "stuck", "help", "fix", "solve", "debug"];
|
|
21109
|
-
if (problemWords.some((word) => messageLower.includes(word))) {
|
|
21110
|
-
return 0.8;
|
|
21111
|
-
}
|
|
21112
|
-
return 0;
|
|
21113
|
-
}
|
|
21114
|
-
_generateSelectionReasoning(components, corroborationSignals) {
|
|
21115
|
-
const scores = [
|
|
21116
|
-
["vector", components.vector],
|
|
21117
|
-
["corroboration", components.corroboration],
|
|
21118
|
-
["reasoning", components.reasoning_match],
|
|
21119
|
-
["weight", components.retrieval_weight],
|
|
21120
|
-
["context", components.context],
|
|
21121
|
-
["temporal", components.temporal],
|
|
21122
|
-
["tags", components.tags],
|
|
21123
|
-
["trigger", components.trigger],
|
|
21124
|
-
["domain", components.domain],
|
|
21125
|
-
["question", components.question],
|
|
21126
|
-
["emotion", components.emotion],
|
|
21127
|
-
["problem", components.problem],
|
|
21128
|
-
["action", components.action]
|
|
21129
|
-
];
|
|
21130
|
-
scores.sort((a, b) => b[1] - a[1]);
|
|
21220
|
+
_generateActivationReasoning(signals) {
|
|
21131
21221
|
const reasons = [];
|
|
21132
|
-
|
|
21133
|
-
|
|
21134
|
-
|
|
21135
|
-
|
|
21136
|
-
|
|
21137
|
-
|
|
21138
|
-
|
|
21222
|
+
if (signals.trigger)
|
|
21223
|
+
reasons.push(`trigger:${(signals.triggerStrength * 100).toFixed(0)}%`);
|
|
21224
|
+
if (signals.tags)
|
|
21225
|
+
reasons.push(`tags:${signals.tagCount}`);
|
|
21226
|
+
if (signals.domain)
|
|
21227
|
+
reasons.push("domain");
|
|
21228
|
+
if (signals.feature)
|
|
21229
|
+
reasons.push("feature");
|
|
21230
|
+
if (signals.content)
|
|
21231
|
+
reasons.push("content");
|
|
21232
|
+
if (signals.vectorSimilarity >= 0.4)
|
|
21233
|
+
reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`);
|
|
21234
|
+
return reasons.length ? `Activated: ${reasons.join(", ")} (${signals.count} signals)` : "No signals";
|
|
21235
|
+
}
|
|
21236
|
+
_logActivationDistribution(activated, totalCandidates, rejectedCount) {
|
|
21237
|
+
const signalBuckets = {
|
|
21238
|
+
"2 signals": 0,
|
|
21239
|
+
"3 signals": 0,
|
|
21240
|
+
"4 signals": 0,
|
|
21241
|
+
"5 signals": 0,
|
|
21242
|
+
"6 signals": 0
|
|
21243
|
+
};
|
|
21244
|
+
for (const mem of activated) {
|
|
21245
|
+
const key = `${Math.min(mem.signals.count, 6)} signals`;
|
|
21246
|
+
signalBuckets[key] = (signalBuckets[key] ?? 0) + 1;
|
|
21247
|
+
}
|
|
21248
|
+
let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0;
|
|
21249
|
+
for (const mem of activated) {
|
|
21250
|
+
if (mem.signals.trigger)
|
|
21251
|
+
triggerCount++;
|
|
21252
|
+
if (mem.signals.tags)
|
|
21253
|
+
tagCount++;
|
|
21254
|
+
if (mem.signals.domain)
|
|
21255
|
+
domainCount++;
|
|
21256
|
+
if (mem.signals.feature)
|
|
21257
|
+
featureCount++;
|
|
21258
|
+
if (mem.signals.content)
|
|
21259
|
+
contentCount++;
|
|
21260
|
+
if (mem.signals.vectorSimilarity >= 0.4)
|
|
21261
|
+
vectorCount++;
|
|
21262
|
+
}
|
|
21263
|
+
logger.logScoreDistribution({
|
|
21264
|
+
totalCandidates,
|
|
21265
|
+
passedGatekeeper: activated.length,
|
|
21266
|
+
rejectedByGatekeeper: rejectedCount,
|
|
21267
|
+
buckets: signalBuckets,
|
|
21268
|
+
stats: {
|
|
21269
|
+
min: activated.length ? Math.min(...activated.map((m) => m.signals.count)) : 0,
|
|
21270
|
+
max: activated.length ? Math.max(...activated.map((m) => m.signals.count)) : 0,
|
|
21271
|
+
mean: activated.length ? Math.round(activated.reduce((s, m) => s + m.signals.count, 0) / activated.length * 10) / 10 : 0,
|
|
21272
|
+
stdev: 0,
|
|
21273
|
+
spread: activated.length ? Math.max(...activated.map((m) => m.signals.count)) - Math.min(...activated.map((m) => m.signals.count)) : 0
|
|
21274
|
+
},
|
|
21275
|
+
percentiles: {},
|
|
21276
|
+
compressionWarning: false,
|
|
21277
|
+
signalBreakdown: {
|
|
21278
|
+
trigger: triggerCount,
|
|
21279
|
+
tags: tagCount,
|
|
21280
|
+
domain: domainCount,
|
|
21281
|
+
feature: featureCount,
|
|
21282
|
+
content: contentCount,
|
|
21283
|
+
vector: vectorCount,
|
|
21284
|
+
total: activated.length
|
|
21139
21285
|
}
|
|
21140
|
-
}
|
|
21141
|
-
if (corroborationSignals.length > 0) {
|
|
21142
|
-
reasons.push("signals:[" + corroborationSignals.slice(0, 2).join(",") + "]");
|
|
21143
|
-
}
|
|
21144
|
-
return reasons.length ? "Selected: " + reasons.join(", ") : "Combined factors";
|
|
21286
|
+
});
|
|
21145
21287
|
}
|
|
21146
21288
|
}
|
|
21147
21289
|
function createRetrieval() {
|
|
@@ -21160,7 +21302,8 @@ class MemoryEngine {
|
|
|
21160
21302
|
centralPath: config.centralPath ?? join2(homedir2(), ".local", "share", "memory"),
|
|
21161
21303
|
localFolder: config.localFolder ?? ".memory",
|
|
21162
21304
|
maxMemories: config.maxMemories ?? 5,
|
|
21163
|
-
embedder: config.embedder
|
|
21305
|
+
embedder: config.embedder,
|
|
21306
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
21164
21307
|
};
|
|
21165
21308
|
this._retrieval = createRetrieval();
|
|
21166
21309
|
}
|
|
@@ -21289,7 +21432,7 @@ class MemoryEngine {
|
|
|
21289
21432
|
}
|
|
21290
21433
|
async _generateSessionPrimer(store, projectId) {
|
|
21291
21434
|
let personalContext;
|
|
21292
|
-
if (
|
|
21435
|
+
if (this._config.personalMemoriesEnabled) {
|
|
21293
21436
|
const personalPrimer = await store.getPersonalPrimer();
|
|
21294
21437
|
personalContext = personalPrimer?.content;
|
|
21295
21438
|
}
|
|
@@ -21377,12 +21520,28 @@ ${primer.personal_context}`);
|
|
|
21377
21520
|
**Project status**: ${primer.project_status}`);
|
|
21378
21521
|
}
|
|
21379
21522
|
parts.push(`
|
|
21380
|
-
**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`);
|
|
21523
|
+
**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`);
|
|
21381
21524
|
parts.push(`
|
|
21382
21525
|
*Memories will surface naturally as we converse.*`);
|
|
21383
21526
|
return parts.join(`
|
|
21384
21527
|
`);
|
|
21385
21528
|
}
|
|
21529
|
+
_formatAge(createdAt) {
|
|
21530
|
+
const now = Date.now();
|
|
21531
|
+
const diffMs = now - createdAt;
|
|
21532
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
21533
|
+
if (diffDays === 0)
|
|
21534
|
+
return "today";
|
|
21535
|
+
if (diffDays === 1)
|
|
21536
|
+
return "1d";
|
|
21537
|
+
if (diffDays < 7)
|
|
21538
|
+
return `${diffDays}d`;
|
|
21539
|
+
if (diffDays < 30)
|
|
21540
|
+
return `${Math.floor(diffDays / 7)}w`;
|
|
21541
|
+
if (diffDays < 365)
|
|
21542
|
+
return `${Math.floor(diffDays / 30)}mo`;
|
|
21543
|
+
return `${Math.floor(diffDays / 365)}y`;
|
|
21544
|
+
}
|
|
21386
21545
|
_formatMemories(memories) {
|
|
21387
21546
|
if (!memories.length)
|
|
21388
21547
|
return "";
|
|
@@ -21393,11 +21552,40 @@ ${primer.personal_context}`);
|
|
|
21393
21552
|
const tags = memory.semantic_tags?.join(", ") || "";
|
|
21394
21553
|
const importance = memory.importance_weight?.toFixed(1) || "0.5";
|
|
21395
21554
|
const emoji = getMemoryEmoji(memory.context_type || "general");
|
|
21396
|
-
|
|
21555
|
+
const actionFlag = memory.action_required ? " ⚡ACTION" : "";
|
|
21556
|
+
const age = memory.updated_at ? this._formatAge(memory.updated_at) : memory.created_at ? this._formatAge(memory.created_at) : "";
|
|
21557
|
+
parts.push(`[${emoji} • ${importance} • ${age}${actionFlag}] [${tags}] ${memory.content}`);
|
|
21558
|
+
const related = memory.related_to;
|
|
21559
|
+
if (related && related.length > 0) {
|
|
21560
|
+
const moreCount = related.length - 1;
|
|
21561
|
+
const moreSuffix = moreCount > 0 ? ` +${moreCount} more` : "";
|
|
21562
|
+
parts.push(` ↳ ${related[0]}${moreSuffix}`);
|
|
21563
|
+
}
|
|
21397
21564
|
}
|
|
21398
21565
|
return parts.join(`
|
|
21399
21566
|
`);
|
|
21400
21567
|
}
|
|
21568
|
+
getStoragePaths(projectId, projectPath) {
|
|
21569
|
+
const globalPath = join2(homedir2(), ".local", "share", "memory", "global");
|
|
21570
|
+
const globalMemoriesPath = join2(globalPath, "memories");
|
|
21571
|
+
const personalPrimerPath = join2(globalPath, "primer", "personal-primer.md");
|
|
21572
|
+
let storeBasePath;
|
|
21573
|
+
if (this._config.storageMode === "local" && projectPath) {
|
|
21574
|
+
storeBasePath = join2(projectPath, this._config.localFolder);
|
|
21575
|
+
} else {
|
|
21576
|
+
storeBasePath = this._config.centralPath;
|
|
21577
|
+
}
|
|
21578
|
+
const projectRootPath = join2(storeBasePath, projectId);
|
|
21579
|
+
const projectMemoriesPath = join2(projectRootPath, "memories");
|
|
21580
|
+
return {
|
|
21581
|
+
projectPath: projectRootPath,
|
|
21582
|
+
globalPath,
|
|
21583
|
+
projectMemoriesPath,
|
|
21584
|
+
globalMemoriesPath,
|
|
21585
|
+
personalPrimerPath,
|
|
21586
|
+
storageMode: this._config.storageMode
|
|
21587
|
+
};
|
|
21588
|
+
}
|
|
21401
21589
|
close() {
|
|
21402
21590
|
for (const store of this._stores.values()) {
|
|
21403
21591
|
store.close();
|
|
@@ -21432,11 +21620,12 @@ class Curator {
|
|
|
21432
21620
|
this._config = {
|
|
21433
21621
|
apiKey: config.apiKey ?? "",
|
|
21434
21622
|
cliCommand,
|
|
21435
|
-
cliType: config.cliType ?? "claude-code"
|
|
21623
|
+
cliType: config.cliType ?? "claude-code",
|
|
21624
|
+
personalMemoriesEnabled: config.personalMemoriesEnabled ?? true
|
|
21436
21625
|
};
|
|
21437
21626
|
}
|
|
21438
21627
|
buildCurationPrompt(triggerType = "session_end") {
|
|
21439
|
-
|
|
21628
|
+
const basePrompt = `You have just had a conversation. As this session is ending (${triggerType}), please curate memories for the Claude Tools Memory System.
|
|
21440
21629
|
|
|
21441
21630
|
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.
|
|
21442
21631
|
|
|
@@ -21473,27 +21662,82 @@ Each memory should stand alone.
|
|
|
21473
21662
|
- Craft language that activates rather than just informs
|
|
21474
21663
|
- Test: 'What state will this restore when Claude encounters it?'
|
|
21475
21664
|
|
|
21476
|
-
**HOW RETRIEVAL WORKS
|
|
21665
|
+
**HOW RETRIEVAL WORKS - ACTIVATION SIGNAL ALGORITHM**
|
|
21666
|
+
|
|
21667
|
+
Understanding the algorithm helps you craft metadata that surfaces memories at the right moments.
|
|
21668
|
+
|
|
21669
|
+
**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.
|
|
21670
|
+
|
|
21671
|
+
**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.
|
|
21672
|
+
|
|
21673
|
+
**6 ACTIVATION SIGNALS** (each is binary - fires or doesn't):
|
|
21674
|
+
|
|
21675
|
+
1. **TRIGGER** - Words from trigger_phrases found in user's message (≥50% match)
|
|
21676
|
+
- THE MOST IMPORTANT SIGNAL. Handcrafted activation patterns.
|
|
21677
|
+
- Example: "when debugging retrieval" fires if user says "I'm debugging the retrieval algorithm"
|
|
21678
|
+
|
|
21679
|
+
2. **TAGS** - 2+ semantic_tags found in user's message
|
|
21680
|
+
- Use words users would ACTUALLY TYPE, not generic descriptors
|
|
21681
|
+
- GOOD: ["retrieval", "embeddings", "curator", "scoring"]
|
|
21682
|
+
- WEAK: ["technical", "important", "system"]
|
|
21683
|
+
|
|
21684
|
+
3. **DOMAIN** - The domain word appears in user's message
|
|
21685
|
+
- Be specific: "retrieval", "embeddings", "auth", "ui"
|
|
21686
|
+
- NOT: "technical", "code", "implementation"
|
|
21687
|
+
|
|
21688
|
+
4. **FEATURE** - The feature word appears in user's message
|
|
21689
|
+
- Be specific: "scoring-weights", "gpu-acceleration", "login-flow"
|
|
21690
|
+
|
|
21691
|
+
5. **CONTENT** - 3+ significant words from memory content overlap with message
|
|
21692
|
+
- Automatic - based on the memory's content text
|
|
21693
|
+
|
|
21694
|
+
6. **VECTOR** - Semantic similarity ≥ 40% (embedding cosine distance)
|
|
21695
|
+
- Automatic - based on embeddings generated from content
|
|
21696
|
+
|
|
21697
|
+
**RELEVANCE GATE**: A memory must have ≥2 signals to be considered relevant.
|
|
21698
|
+
If only 1 signal fires, the memory is REJECTED. This prevents noise.
|
|
21477
21699
|
|
|
21478
|
-
**
|
|
21700
|
+
**RANKING AMONG RELEVANT**: Once a memory passes the gate:
|
|
21701
|
+
1. Sort by SIGNAL COUNT (more signals = more certainly relevant)
|
|
21702
|
+
2. Then by IMPORTANCE WEIGHT (your assessment of how important this memory is)
|
|
21479
21703
|
|
|
21480
|
-
|
|
21481
|
-
|
|
21482
|
-
|
|
21704
|
+
**SELECTION**:
|
|
21705
|
+
- Global memories (scope='global'): Max 2 selected, tech types prioritized over personal
|
|
21706
|
+
- Project memories: Fill remaining slots, action_required prioritized
|
|
21707
|
+
- Related memories (related_to field): May be included if they also passed the gate
|
|
21483
21708
|
|
|
21484
|
-
|
|
21709
|
+
**WHY THIS MATTERS FOR YOU**:
|
|
21710
|
+
- If you don't fill trigger_phrases well → trigger signal never fires
|
|
21711
|
+
- If you use generic tags → tags signal rarely fires
|
|
21712
|
+
- If you leave domain/feature empty → those signals can't fire
|
|
21713
|
+
- A memory with poor metadata may NEVER surface because it can't reach 2 signals
|
|
21485
21714
|
|
|
21486
|
-
|
|
21715
|
+
**CRAFTING EFFECTIVE METADATA** (CRITICAL FOR RETRIEVAL):
|
|
21487
21716
|
|
|
21488
|
-
|
|
21717
|
+
1. **trigger_phrases** (MOST IMPORTANT) - Activation patterns describing WHEN to surface:
|
|
21718
|
+
- Include 2-4 specific patterns per memory
|
|
21719
|
+
- Use words the user would actually type
|
|
21720
|
+
- GOOD: ["debugging retrieval", "working on embeddings", "memory system performance"]
|
|
21721
|
+
- WEAK: ["when relevant", "if needed", "technical work"]
|
|
21489
21722
|
|
|
21490
|
-
**
|
|
21723
|
+
2. **semantic_tags** - Words users would type (need 2+ to fire):
|
|
21724
|
+
- Be specific and searchable
|
|
21725
|
+
- GOOD: ["retrieval", "embeddings", "fsdb", "curator", "scoring"]
|
|
21726
|
+
- WEAK: ["technical", "important", "system", "implementation"]
|
|
21491
21727
|
|
|
21492
|
-
|
|
21728
|
+
3. **domain** (NEW - FILL THIS) - Single specific area word:
|
|
21729
|
+
- GOOD: "retrieval", "embeddings", "curator", "signals", "fsdb"
|
|
21730
|
+
- WEAK: "technical", "code", "memory" (too generic)
|
|
21493
21731
|
|
|
21494
|
-
|
|
21732
|
+
4. **feature** (NEW - FILL THIS) - Specific feature within domain:
|
|
21733
|
+
- GOOD: "scoring-algorithm", "activation-signals", "vector-search"
|
|
21734
|
+
- WEAK: "implementation", "code", "logic"
|
|
21495
21735
|
|
|
21496
|
-
|
|
21736
|
+
5. **importance_weight** - Only affects ranking AMONG relevant memories:
|
|
21737
|
+
- 0.9+ = Critical breakthrough, must surface if relevant
|
|
21738
|
+
- 0.7-0.8 = Important insight, should surface if relevant
|
|
21739
|
+
- 0.5-0.6 = Useful context, nice to have if relevant
|
|
21740
|
+
- NOTE: This does NOT affect whether the memory passes the relevance gate!
|
|
21497
21741
|
|
|
21498
21742
|
**SCOPE DETERMINES WHERE MEMORIES SURFACE**:
|
|
21499
21743
|
- scope: 'global' → surfaces in ALL projects (personal facts, philosophy, preferences)
|
|
@@ -21561,6 +21805,21 @@ Return ONLY this JSON structure:
|
|
|
21561
21805
|
}
|
|
21562
21806
|
]
|
|
21563
21807
|
}`;
|
|
21808
|
+
if (!this._config.personalMemoriesEnabled) {
|
|
21809
|
+
return basePrompt + `
|
|
21810
|
+
|
|
21811
|
+
---
|
|
21812
|
+
|
|
21813
|
+
**IMPORTANT: PERSONAL MEMORIES DISABLED**
|
|
21814
|
+
|
|
21815
|
+
The user has disabled personal memory extraction. Do NOT extract any memories with:
|
|
21816
|
+
- context_type: "personal"
|
|
21817
|
+
- scope: "global" when the content is about the user's personal life, relationships, family, or emotional states
|
|
21818
|
+
- Content about the user's preferences, feelings, personal opinions, or relationship dynamics
|
|
21819
|
+
|
|
21820
|
+
Focus ONLY on technical, architectural, debugging, decision, workflow, and project-related memories. Skip any content that would reveal personal information about the user.`;
|
|
21821
|
+
}
|
|
21822
|
+
return basePrompt;
|
|
21564
21823
|
}
|
|
21565
21824
|
parseCurationResponse(responseJson) {
|
|
21566
21825
|
try {
|
|
@@ -21651,33 +21910,35 @@ Return ONLY this JSON structure:
|
|
|
21651
21910
|
_clamp(value, min, max) {
|
|
21652
21911
|
return Math.max(min, Math.min(max, value));
|
|
21653
21912
|
}
|
|
21654
|
-
async curateWithSDK(
|
|
21913
|
+
async curateWithSDK(messages, triggerType = "session_end") {
|
|
21655
21914
|
if (!this._config.apiKey) {
|
|
21656
|
-
throw new Error("API key required for SDK mode");
|
|
21915
|
+
throw new Error("API key required for SDK mode. Set ANTHROPIC_API_KEY environment variable.");
|
|
21657
21916
|
}
|
|
21658
21917
|
const { default: Anthropic2 } = await Promise.resolve().then(() => (init_sdk(), exports_sdk));
|
|
21659
21918
|
const client = new Anthropic2({ apiKey: this._config.apiKey });
|
|
21660
|
-
const
|
|
21919
|
+
const systemPrompt = this.buildCurationPrompt(triggerType);
|
|
21920
|
+
const conversationMessages = [
|
|
21921
|
+
...messages,
|
|
21922
|
+
{
|
|
21923
|
+
role: "user",
|
|
21924
|
+
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."
|
|
21925
|
+
}
|
|
21926
|
+
];
|
|
21661
21927
|
const response = await client.messages.create({
|
|
21662
21928
|
model: "claude-sonnet-4-20250514",
|
|
21663
21929
|
max_tokens: 8192,
|
|
21664
|
-
|
|
21665
|
-
|
|
21666
|
-
role: "user",
|
|
21667
|
-
content: `${conversationContext}
|
|
21668
|
-
|
|
21669
|
-
---
|
|
21670
|
-
|
|
21671
|
-
${prompt}`
|
|
21672
|
-
}
|
|
21673
|
-
]
|
|
21930
|
+
system: systemPrompt,
|
|
21931
|
+
messages: conversationMessages
|
|
21674
21932
|
});
|
|
21675
21933
|
const content = response.content[0];
|
|
21676
21934
|
if (content.type !== "text") {
|
|
21677
|
-
throw new Error("Unexpected response type");
|
|
21935
|
+
throw new Error("Unexpected response type from Claude API");
|
|
21678
21936
|
}
|
|
21679
21937
|
return this.parseCurationResponse(content.text);
|
|
21680
21938
|
}
|
|
21939
|
+
async curateFromSegment(segment, triggerType = "session_end") {
|
|
21940
|
+
return this.curateWithSDK(segment.messages, triggerType);
|
|
21941
|
+
}
|
|
21681
21942
|
async curateWithCLI(sessionId, triggerType = "session_end", cwd, cliTypeOverride) {
|
|
21682
21943
|
const type = cliTypeOverride ?? this._config.cliType;
|
|
21683
21944
|
const systemPrompt = this.buildCurationPrompt(triggerType);
|
|
@@ -46256,8 +46517,9 @@ class Manager {
|
|
|
46256
46517
|
_config;
|
|
46257
46518
|
constructor(config = {}) {
|
|
46258
46519
|
this._config = {
|
|
46520
|
+
enabled: config.enabled ?? true,
|
|
46259
46521
|
cliCommand: config.cliCommand ?? getClaudeCommand2(),
|
|
46260
|
-
maxTurns: config.maxTurns
|
|
46522
|
+
maxTurns: config.maxTurns
|
|
46261
46523
|
};
|
|
46262
46524
|
}
|
|
46263
46525
|
async buildManagementPrompt() {
|
|
@@ -46278,14 +46540,31 @@ class Manager {
|
|
|
46278
46540
|
}
|
|
46279
46541
|
return null;
|
|
46280
46542
|
}
|
|
46281
|
-
buildUserMessage(projectId, sessionNumber, result) {
|
|
46543
|
+
buildUserMessage(projectId, sessionNumber, result, storagePaths) {
|
|
46282
46544
|
const today = new Date().toISOString().split("T")[0];
|
|
46545
|
+
const pathsSection = storagePaths ? `
|
|
46546
|
+
## Storage Paths (ACTUAL - use these exact paths)
|
|
46547
|
+
|
|
46548
|
+
**Storage Mode:** ${storagePaths.storageMode}
|
|
46549
|
+
|
|
46550
|
+
### Project Storage
|
|
46551
|
+
- **Project Root:** ${storagePaths.projectPath}
|
|
46552
|
+
- **Project Memories:** ${storagePaths.projectMemoriesPath}
|
|
46553
|
+
|
|
46554
|
+
### Global Storage (shared across all projects)
|
|
46555
|
+
- **Global Root:** ${storagePaths.globalPath}
|
|
46556
|
+
- **Global Memories:** ${storagePaths.globalMemoriesPath}
|
|
46557
|
+
- **Personal Primer:** ${storagePaths.personalPrimerPath}
|
|
46558
|
+
|
|
46559
|
+
> ⚠️ These paths are resolved from the running server configuration. Use them exactly as provided.
|
|
46560
|
+
> Memories are stored as individual markdown files in the memories directories.
|
|
46561
|
+
` : "";
|
|
46283
46562
|
return `## Curation Data
|
|
46284
46563
|
|
|
46285
46564
|
**Project ID:** ${projectId}
|
|
46286
46565
|
**Session Number:** ${sessionNumber}
|
|
46287
46566
|
**Date:** ${today}
|
|
46288
|
-
|
|
46567
|
+
${pathsSection}
|
|
46289
46568
|
### Session Summary
|
|
46290
46569
|
${result.session_summary || "No summary provided"}
|
|
46291
46570
|
|
|
@@ -46311,56 +46590,105 @@ ${result.memories.map((m2, i2) => `
|
|
|
46311
46590
|
|
|
46312
46591
|
---
|
|
46313
46592
|
|
|
46314
|
-
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.`;
|
|
46593
|
+
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.`;
|
|
46315
46594
|
}
|
|
46316
46595
|
parseManagementResponse(responseJson) {
|
|
46596
|
+
const emptyResult = (error) => ({
|
|
46597
|
+
success: !error,
|
|
46598
|
+
superseded: 0,
|
|
46599
|
+
resolved: 0,
|
|
46600
|
+
linked: 0,
|
|
46601
|
+
filesRead: 0,
|
|
46602
|
+
filesWritten: 0,
|
|
46603
|
+
primerUpdated: false,
|
|
46604
|
+
actions: [],
|
|
46605
|
+
summary: error ? "" : "No actions taken",
|
|
46606
|
+
fullReport: error ? `Error: ${error}` : "",
|
|
46607
|
+
error
|
|
46608
|
+
});
|
|
46317
46609
|
try {
|
|
46318
46610
|
const cliOutput = JSON.parse(responseJson);
|
|
46319
46611
|
if (cliOutput.type === "error" || cliOutput.is_error === true) {
|
|
46320
|
-
return
|
|
46321
|
-
success: false,
|
|
46322
|
-
superseded: 0,
|
|
46323
|
-
resolved: 0,
|
|
46324
|
-
linked: 0,
|
|
46325
|
-
primerUpdated: false,
|
|
46326
|
-
summary: "",
|
|
46327
|
-
error: cliOutput.error || "Unknown error"
|
|
46328
|
-
};
|
|
46612
|
+
return emptyResult(cliOutput.error || "Unknown error");
|
|
46329
46613
|
}
|
|
46330
46614
|
const resultText = typeof cliOutput.result === "string" ? cliOutput.result : "";
|
|
46331
|
-
const
|
|
46332
|
-
const
|
|
46333
|
-
const
|
|
46334
|
-
const
|
|
46615
|
+
const reportMatch = resultText.match(/(=== MANAGEMENT ACTIONS ===[\s\S]*)/);
|
|
46616
|
+
const fullReport = reportMatch ? reportMatch[1].trim() : resultText;
|
|
46617
|
+
const actionsMatch = resultText.match(/=== MANAGEMENT ACTIONS ===([\s\S]*?)(?:=== SUMMARY ===|$)/);
|
|
46618
|
+
const actions = [];
|
|
46619
|
+
if (actionsMatch) {
|
|
46620
|
+
const actionsText = actionsMatch[1];
|
|
46621
|
+
const actionLines = actionsText.split(`
|
|
46622
|
+
`).map((line) => line.trim()).filter((line) => /^(READ|WRITE|RECEIVED|CREATED|UPDATED|SUPERSEDED|RESOLVED|LINKED|PRIMER|SKIPPED|NO_ACTION)/.test(line));
|
|
46623
|
+
actions.push(...actionLines);
|
|
46624
|
+
}
|
|
46625
|
+
const supersededMatch = resultText.match(/memories_superseded[:\s]+(\d+)/i) || resultText.match(/superseded[:\s]+(\d+)/i);
|
|
46626
|
+
const resolvedMatch = resultText.match(/memories_resolved[:\s]+(\d+)/i) || resultText.match(/resolved[:\s]+(\d+)/i);
|
|
46627
|
+
const linkedMatch = resultText.match(/memories_linked[:\s]+(\d+)/i) || resultText.match(/linked[:\s]+(\d+)/i);
|
|
46628
|
+
const filesReadMatch = resultText.match(/files_read[:\s]+(\d+)/i);
|
|
46629
|
+
const filesWrittenMatch = resultText.match(/files_written[:\s]+(\d+)/i);
|
|
46630
|
+
const primerUpdated = /primer_updated[:\s]+true/i.test(resultText) || /PRIMER\s+OK/i.test(resultText);
|
|
46631
|
+
const readActions = actions.filter((a2) => a2.startsWith("READ OK")).length;
|
|
46632
|
+
const writeActions = actions.filter((a2) => a2.startsWith("WRITE OK")).length;
|
|
46335
46633
|
return {
|
|
46336
46634
|
success: true,
|
|
46337
46635
|
superseded: supersededMatch ? parseInt(supersededMatch[1]) : 0,
|
|
46338
46636
|
resolved: resolvedMatch ? parseInt(resolvedMatch[1]) : 0,
|
|
46339
46637
|
linked: linkedMatch ? parseInt(linkedMatch[1]) : 0,
|
|
46638
|
+
filesRead: filesReadMatch ? parseInt(filesReadMatch[1]) : readActions,
|
|
46639
|
+
filesWritten: filesWrittenMatch ? parseInt(filesWrittenMatch[1]) : writeActions,
|
|
46340
46640
|
primerUpdated,
|
|
46341
|
-
|
|
46641
|
+
actions,
|
|
46642
|
+
summary: resultText.slice(0, 500),
|
|
46643
|
+
fullReport
|
|
46342
46644
|
};
|
|
46343
46645
|
} catch {
|
|
46344
|
-
return
|
|
46345
|
-
|
|
46346
|
-
|
|
46347
|
-
|
|
46348
|
-
|
|
46349
|
-
|
|
46350
|
-
|
|
46351
|
-
|
|
46352
|
-
|
|
46353
|
-
|
|
46646
|
+
return emptyResult("Failed to parse response");
|
|
46647
|
+
}
|
|
46648
|
+
}
|
|
46649
|
+
async _buildSettingsFile(storagePaths) {
|
|
46650
|
+
const allowRules = [];
|
|
46651
|
+
const globalPath = storagePaths?.globalPath ?? join4(homedir4(), ".local", "share", "memory", "global");
|
|
46652
|
+
const projectPath = storagePaths?.projectPath ?? join4(homedir4(), ".local", "share", "memory");
|
|
46653
|
+
allowRules.push("Glob");
|
|
46654
|
+
allowRules.push("Grep");
|
|
46655
|
+
const formatPath = (p2) => p2.startsWith("/") ? "/" + p2 : "//" + p2;
|
|
46656
|
+
allowRules.push(`Read(${formatPath(globalPath)}/**)`);
|
|
46657
|
+
allowRules.push(`Write(${formatPath(globalPath)}/**)`);
|
|
46658
|
+
allowRules.push(`Edit(${formatPath(globalPath)}/**)`);
|
|
46659
|
+
allowRules.push(`Read(${formatPath(projectPath)}/**)`);
|
|
46660
|
+
allowRules.push(`Write(${formatPath(projectPath)}/**)`);
|
|
46661
|
+
allowRules.push(`Edit(${formatPath(projectPath)}/**)`);
|
|
46662
|
+
const settings = {
|
|
46663
|
+
permissions: {
|
|
46664
|
+
allow: allowRules,
|
|
46665
|
+
deny: [
|
|
46666
|
+
"Read(/etc/**)",
|
|
46667
|
+
"Read(~/.ssh/**)",
|
|
46668
|
+
"Read(~/.aws/**)",
|
|
46669
|
+
"Read(~/.gnupg/**)",
|
|
46670
|
+
"Read(.env)",
|
|
46671
|
+
"Read(.env.*)"
|
|
46672
|
+
]
|
|
46673
|
+
}
|
|
46674
|
+
};
|
|
46675
|
+
const tempPath = join4(homedir4(), ".local", "share", "memory", ".manager-settings.json");
|
|
46676
|
+
await Bun.write(tempPath, JSON.stringify(settings, null, 2));
|
|
46677
|
+
return tempPath;
|
|
46354
46678
|
}
|
|
46355
|
-
async manageWithCLI(projectId, sessionNumber, result) {
|
|
46356
|
-
if (process.env.MEMORY_MANAGER_DISABLED === "1") {
|
|
46679
|
+
async manageWithCLI(projectId, sessionNumber, result, storagePaths) {
|
|
46680
|
+
if (!this._config.enabled || process.env.MEMORY_MANAGER_DISABLED === "1") {
|
|
46357
46681
|
return {
|
|
46358
46682
|
success: true,
|
|
46359
46683
|
superseded: 0,
|
|
46360
46684
|
resolved: 0,
|
|
46361
46685
|
linked: 0,
|
|
46686
|
+
filesRead: 0,
|
|
46687
|
+
filesWritten: 0,
|
|
46362
46688
|
primerUpdated: false,
|
|
46363
|
-
|
|
46689
|
+
actions: [],
|
|
46690
|
+
summary: "Management agent disabled",
|
|
46691
|
+
fullReport: "Management agent disabled via configuration"
|
|
46364
46692
|
};
|
|
46365
46693
|
}
|
|
46366
46694
|
if (result.memories.length === 0) {
|
|
@@ -46369,8 +46697,12 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46369
46697
|
superseded: 0,
|
|
46370
46698
|
resolved: 0,
|
|
46371
46699
|
linked: 0,
|
|
46700
|
+
filesRead: 0,
|
|
46701
|
+
filesWritten: 0,
|
|
46372
46702
|
primerUpdated: false,
|
|
46373
|
-
|
|
46703
|
+
actions: [],
|
|
46704
|
+
summary: "No memories to process",
|
|
46705
|
+
fullReport: "No memories to process - skipped"
|
|
46374
46706
|
};
|
|
46375
46707
|
}
|
|
46376
46708
|
const systemPrompt = await this.buildManagementPrompt();
|
|
@@ -46380,12 +46712,17 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46380
46712
|
superseded: 0,
|
|
46381
46713
|
resolved: 0,
|
|
46382
46714
|
linked: 0,
|
|
46715
|
+
filesRead: 0,
|
|
46716
|
+
filesWritten: 0,
|
|
46383
46717
|
primerUpdated: false,
|
|
46718
|
+
actions: [],
|
|
46384
46719
|
summary: "",
|
|
46720
|
+
fullReport: "Error: Management skill file not found",
|
|
46385
46721
|
error: "Management skill not found"
|
|
46386
46722
|
};
|
|
46387
46723
|
}
|
|
46388
|
-
const userMessage = this.buildUserMessage(projectId, sessionNumber, result);
|
|
46724
|
+
const userMessage = this.buildUserMessage(projectId, sessionNumber, result, storagePaths);
|
|
46725
|
+
const settingsPath = await this._buildSettingsFile(storagePaths);
|
|
46389
46726
|
const args = [
|
|
46390
46727
|
"-p",
|
|
46391
46728
|
userMessage,
|
|
@@ -46393,9 +46730,12 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46393
46730
|
systemPrompt,
|
|
46394
46731
|
"--output-format",
|
|
46395
46732
|
"json",
|
|
46396
|
-
"--
|
|
46397
|
-
|
|
46733
|
+
"--settings",
|
|
46734
|
+
settingsPath
|
|
46398
46735
|
];
|
|
46736
|
+
if (this._config.maxTurns !== undefined) {
|
|
46737
|
+
args.push("--max-turns", String(this._config.maxTurns));
|
|
46738
|
+
}
|
|
46399
46739
|
const proc = Bun.spawn([this._config.cliCommand, ...args], {
|
|
46400
46740
|
env: {
|
|
46401
46741
|
...process.env,
|
|
@@ -46409,14 +46749,20 @@ Please process these memories according to your management procedure. Update, su
|
|
|
46409
46749
|
]);
|
|
46410
46750
|
const exitCode = await proc.exited;
|
|
46411
46751
|
if (exitCode !== 0) {
|
|
46752
|
+
const errorMsg = stderr || `Exit code ${exitCode}`;
|
|
46412
46753
|
return {
|
|
46413
46754
|
success: false,
|
|
46414
46755
|
superseded: 0,
|
|
46415
46756
|
resolved: 0,
|
|
46416
46757
|
linked: 0,
|
|
46758
|
+
filesRead: 0,
|
|
46759
|
+
filesWritten: 0,
|
|
46417
46760
|
primerUpdated: false,
|
|
46761
|
+
actions: [],
|
|
46418
46762
|
summary: "",
|
|
46419
|
-
|
|
46763
|
+
fullReport: `Error: CLI failed with exit code ${exitCode}
|
|
46764
|
+
${stderr}`,
|
|
46765
|
+
error: errorMsg
|
|
46420
46766
|
};
|
|
46421
46767
|
}
|
|
46422
46768
|
return this.parseManagementResponse(stdout);
|
|
@@ -46433,17 +46779,28 @@ async function createServer(config = {}) {
|
|
|
46433
46779
|
host = "localhost",
|
|
46434
46780
|
curator: curatorConfig,
|
|
46435
46781
|
manager: managerConfig,
|
|
46782
|
+
managerEnabled,
|
|
46783
|
+
personalMemoriesEnabled,
|
|
46436
46784
|
...engineConfig
|
|
46437
46785
|
} = config;
|
|
46438
46786
|
const embeddings = createEmbeddings();
|
|
46439
46787
|
logger.info("Initializing embedding model (this may take a moment on first run)...");
|
|
46440
46788
|
await embeddings.initialize();
|
|
46789
|
+
const finalCuratorConfig = {
|
|
46790
|
+
...curatorConfig,
|
|
46791
|
+
personalMemoriesEnabled: personalMemoriesEnabled ?? curatorConfig?.personalMemoriesEnabled
|
|
46792
|
+
};
|
|
46793
|
+
const finalManagerConfig = {
|
|
46794
|
+
...managerConfig,
|
|
46795
|
+
enabled: managerEnabled ?? managerConfig?.enabled
|
|
46796
|
+
};
|
|
46441
46797
|
const engine = createEngine({
|
|
46442
46798
|
...engineConfig,
|
|
46443
|
-
embedder: embeddings.createEmbedder()
|
|
46799
|
+
embedder: embeddings.createEmbedder(),
|
|
46800
|
+
personalMemoriesEnabled: finalCuratorConfig.personalMemoriesEnabled
|
|
46444
46801
|
});
|
|
46445
|
-
const curator = createCurator(
|
|
46446
|
-
const manager = createManager(
|
|
46802
|
+
const curator = createCurator(finalCuratorConfig);
|
|
46803
|
+
const manager = createManager(finalManagerConfig);
|
|
46447
46804
|
const server = Bun.serve({
|
|
46448
46805
|
port,
|
|
46449
46806
|
hostname: host,
|
|
@@ -46512,18 +46869,22 @@ async function createServer(config = {}) {
|
|
|
46512
46869
|
logger.logCurationComplete(result.memories.length, result.session_summary);
|
|
46513
46870
|
logger.logCuratedMemories(result.memories);
|
|
46514
46871
|
const sessionNumber = await engine.getSessionNumber(body.project_id, body.project_path);
|
|
46872
|
+
const storagePaths = engine.getStoragePaths(body.project_id, body.project_path);
|
|
46515
46873
|
setImmediate(async () => {
|
|
46516
46874
|
try {
|
|
46517
46875
|
logger.logManagementStart(result.memories.length);
|
|
46518
46876
|
const startTime = Date.now();
|
|
46519
|
-
const managementResult = await manager.manageWithCLI(body.project_id, sessionNumber, result);
|
|
46877
|
+
const managementResult = await manager.manageWithCLI(body.project_id, sessionNumber, result, storagePaths);
|
|
46520
46878
|
logger.logManagementComplete({
|
|
46521
46879
|
success: managementResult.success,
|
|
46522
46880
|
superseded: managementResult.superseded || undefined,
|
|
46523
46881
|
resolved: managementResult.resolved || undefined,
|
|
46524
46882
|
linked: managementResult.linked || undefined,
|
|
46883
|
+
filesRead: managementResult.filesRead || undefined,
|
|
46884
|
+
filesWritten: managementResult.filesWritten || undefined,
|
|
46525
46885
|
primerUpdated: managementResult.primerUpdated,
|
|
46526
|
-
|
|
46886
|
+
actions: managementResult.actions,
|
|
46887
|
+
fullReport: managementResult.fullReport,
|
|
46527
46888
|
error: managementResult.error
|
|
46528
46889
|
});
|
|
46529
46890
|
await engine.storeManagementLog({
|
|
@@ -46537,7 +46898,13 @@ async function createServer(config = {}) {
|
|
|
46537
46898
|
success: managementResult.success,
|
|
46538
46899
|
durationMs: Date.now() - startTime,
|
|
46539
46900
|
summary: managementResult.summary,
|
|
46540
|
-
|
|
46901
|
+
fullReport: managementResult.fullReport,
|
|
46902
|
+
error: managementResult.error,
|
|
46903
|
+
details: {
|
|
46904
|
+
actions: managementResult.actions,
|
|
46905
|
+
filesRead: managementResult.filesRead,
|
|
46906
|
+
filesWritten: managementResult.filesWritten
|
|
46907
|
+
}
|
|
46541
46908
|
});
|
|
46542
46909
|
} catch (error) {
|
|
46543
46910
|
logger.error(`Management failed: ${error}`);
|
|
@@ -46586,11 +46953,15 @@ if (__require.main == __require.module) {
|
|
|
46586
46953
|
const host = process.env.MEMORY_HOST ?? "localhost";
|
|
46587
46954
|
const storageMode = process.env.MEMORY_STORAGE_MODE ?? "central";
|
|
46588
46955
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
46956
|
+
const managerEnabled = !["0", "false"].includes(process.env.MEMORY_MANAGER_ENABLED?.toLowerCase() ?? "");
|
|
46957
|
+
const personalMemoriesEnabled = !["0", "false"].includes(process.env.MEMORY_PERSONAL_ENABLED?.toLowerCase() ?? "");
|
|
46589
46958
|
(async () => {
|
|
46590
46959
|
await createServer({
|
|
46591
46960
|
port,
|
|
46592
46961
|
host,
|
|
46593
46962
|
storageMode,
|
|
46963
|
+
managerEnabled,
|
|
46964
|
+
personalMemoriesEnabled,
|
|
46594
46965
|
curator: { apiKey }
|
|
46595
46966
|
});
|
|
46596
46967
|
})();
|