@rlabs-inc/memory 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -30
- package/dist/index.js +803 -179
- package/dist/index.mjs +803 -179
- package/dist/server/index.js +36774 -2643
- package/dist/server/index.mjs +1034 -185
- package/package.json +3 -2
- package/skills/memory-management.md +686 -0
- package/src/cli/commands/migrate.ts +423 -0
- package/src/cli/commands/serve.ts +88 -0
- package/src/cli/index.ts +21 -0
- package/src/core/curator.ts +151 -17
- package/src/core/engine.ts +159 -11
- package/src/core/manager.ts +484 -0
- package/src/core/retrieval.ts +547 -420
- package/src/core/store.ts +383 -8
- package/src/server/index.ts +108 -8
- package/src/types/memory.ts +142 -0
- package/src/types/schema.ts +80 -7
- package/src/utils/logger.ts +310 -46
package/dist/server/index.mjs
CHANGED
|
@@ -19731,7 +19731,60 @@ function createDatabase(options = {}) {
|
|
|
19731
19731
|
import { homedir } from "os";
|
|
19732
19732
|
import { join } from "path";
|
|
19733
19733
|
|
|
19734
|
+
// src/types/memory.ts
|
|
19735
|
+
var V2_DEFAULTS = {
|
|
19736
|
+
typeDefaults: {
|
|
19737
|
+
personal: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
|
|
19738
|
+
philosophy: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
|
|
19739
|
+
preference: { scope: "global", temporal_class: "long_term", fade_rate: 0.01 },
|
|
19740
|
+
breakthrough: { scope: "project", temporal_class: "eternal", fade_rate: 0 },
|
|
19741
|
+
decision: { scope: "project", temporal_class: "long_term", fade_rate: 0 },
|
|
19742
|
+
milestone: { scope: "project", temporal_class: "eternal", fade_rate: 0 },
|
|
19743
|
+
technical: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 },
|
|
19744
|
+
architectural: { scope: "project", temporal_class: "long_term", fade_rate: 0.01 },
|
|
19745
|
+
debugging: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 },
|
|
19746
|
+
unresolved: { scope: "project", temporal_class: "medium_term", fade_rate: 0.05 },
|
|
19747
|
+
todo: { scope: "project", temporal_class: "short_term", fade_rate: 0.1 },
|
|
19748
|
+
technical_state: { scope: "project", temporal_class: "short_term", fade_rate: 0.1 },
|
|
19749
|
+
workflow: { scope: "project", temporal_class: "long_term", fade_rate: 0.02 },
|
|
19750
|
+
project_context: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 }
|
|
19751
|
+
},
|
|
19752
|
+
fallback: {
|
|
19753
|
+
status: "active",
|
|
19754
|
+
scope: "project",
|
|
19755
|
+
temporal_class: "medium_term",
|
|
19756
|
+
fade_rate: 0.03,
|
|
19757
|
+
sessions_since_surfaced: 0,
|
|
19758
|
+
awaiting_implementation: false,
|
|
19759
|
+
awaiting_decision: false,
|
|
19760
|
+
exclude_from_retrieval: false
|
|
19761
|
+
}
|
|
19762
|
+
};
|
|
19763
|
+
var MEMORY_TYPE_EMOJI = {
|
|
19764
|
+
breakthrough: "\uD83D\uDCA1",
|
|
19765
|
+
decision: "⚖️",
|
|
19766
|
+
personal: "\uD83D\uDC9C",
|
|
19767
|
+
technical: "\uD83D\uDD27",
|
|
19768
|
+
technical_state: "\uD83D\uDCCD",
|
|
19769
|
+
unresolved: "❓",
|
|
19770
|
+
preference: "⚙️",
|
|
19771
|
+
workflow: "\uD83D\uDD04",
|
|
19772
|
+
architectural: "\uD83C\uDFD7️",
|
|
19773
|
+
debugging: "\uD83D\uDC1B",
|
|
19774
|
+
philosophy: "\uD83C\uDF00",
|
|
19775
|
+
todo: "\uD83C\uDFAF",
|
|
19776
|
+
implementation: "⚡",
|
|
19777
|
+
problem_solution: "✅",
|
|
19778
|
+
project_context: "\uD83D\uDCE6",
|
|
19779
|
+
milestone: "\uD83C\uDFC6",
|
|
19780
|
+
general: "\uD83D\uDCDD"
|
|
19781
|
+
};
|
|
19782
|
+
function getMemoryEmoji(contextType) {
|
|
19783
|
+
return MEMORY_TYPE_EMOJI[contextType.toLowerCase()] ?? "\uD83D\uDCDD";
|
|
19784
|
+
}
|
|
19785
|
+
|
|
19734
19786
|
// src/types/schema.ts
|
|
19787
|
+
var MEMORY_SCHEMA_VERSION = 2;
|
|
19735
19788
|
var memorySchema = {
|
|
19736
19789
|
content: "string",
|
|
19737
19790
|
reasoning: "string",
|
|
@@ -19748,7 +19801,34 @@ var memorySchema = {
|
|
|
19748
19801
|
question_types: "string[]",
|
|
19749
19802
|
session_id: "string",
|
|
19750
19803
|
project_id: "string",
|
|
19751
|
-
embedding: "vector:384"
|
|
19804
|
+
embedding: "vector:384",
|
|
19805
|
+
status: "string",
|
|
19806
|
+
scope: "string",
|
|
19807
|
+
session_created: "number",
|
|
19808
|
+
session_updated: "number",
|
|
19809
|
+
last_surfaced: "number",
|
|
19810
|
+
sessions_since_surfaced: "number",
|
|
19811
|
+
temporal_class: "string",
|
|
19812
|
+
fade_rate: "number",
|
|
19813
|
+
expires_after_sessions: "number",
|
|
19814
|
+
domain: "string",
|
|
19815
|
+
feature: "string",
|
|
19816
|
+
component: "string",
|
|
19817
|
+
supersedes: "string",
|
|
19818
|
+
superseded_by: "string",
|
|
19819
|
+
related_to: "string[]",
|
|
19820
|
+
resolves: "string[]",
|
|
19821
|
+
resolved_by: "string",
|
|
19822
|
+
parent_id: "string",
|
|
19823
|
+
child_ids: "string[]",
|
|
19824
|
+
awaiting_implementation: "boolean",
|
|
19825
|
+
awaiting_decision: "boolean",
|
|
19826
|
+
blocked_by: "string",
|
|
19827
|
+
blocks: "string[]",
|
|
19828
|
+
related_files: "string[]",
|
|
19829
|
+
retrieval_weight: "number",
|
|
19830
|
+
exclude_from_retrieval: "boolean",
|
|
19831
|
+
schema_version: "number"
|
|
19752
19832
|
};
|
|
19753
19833
|
var sessionSummarySchema = {
|
|
19754
19834
|
session_id: "string",
|
|
@@ -19771,17 +19851,211 @@ var sessionSchema = {
|
|
|
19771
19851
|
last_active: "timestamp",
|
|
19772
19852
|
metadata: "string"
|
|
19773
19853
|
};
|
|
19854
|
+
var managementLogSchema = {
|
|
19855
|
+
project_id: "string",
|
|
19856
|
+
session_number: "number",
|
|
19857
|
+
memories_processed: "number",
|
|
19858
|
+
superseded_count: "number",
|
|
19859
|
+
resolved_count: "number",
|
|
19860
|
+
linked_count: "number",
|
|
19861
|
+
primer_updated: "boolean",
|
|
19862
|
+
success: "boolean",
|
|
19863
|
+
duration_ms: "number",
|
|
19864
|
+
summary: "string",
|
|
19865
|
+
error: "string",
|
|
19866
|
+
details: "string"
|
|
19867
|
+
};
|
|
19774
19868
|
|
|
19775
19869
|
// src/core/store.ts
|
|
19870
|
+
var PERSONAL_PRIMER_ID = "personal-primer";
|
|
19871
|
+
var DEFAULT_GLOBAL_PATH = join(homedir(), ".local", "share", "memory", "global");
|
|
19872
|
+
|
|
19776
19873
|
class MemoryStore {
|
|
19777
19874
|
_config;
|
|
19778
19875
|
_projects = new Map;
|
|
19876
|
+
_global = null;
|
|
19779
19877
|
constructor(config = {}) {
|
|
19780
19878
|
this._config = {
|
|
19781
19879
|
basePath: config.basePath ?? join(homedir(), ".local", "share", "memory"),
|
|
19880
|
+
globalPath: config.globalPath ?? DEFAULT_GLOBAL_PATH,
|
|
19782
19881
|
watchFiles: config.watchFiles ?? false
|
|
19783
19882
|
};
|
|
19784
19883
|
}
|
|
19884
|
+
async getGlobal() {
|
|
19885
|
+
if (this._global) {
|
|
19886
|
+
return this._global;
|
|
19887
|
+
}
|
|
19888
|
+
const globalPath = this._config.globalPath;
|
|
19889
|
+
console.log(`\uD83C\uDF10 [DEBUG] Creating global database at ${globalPath}`);
|
|
19890
|
+
const db = createDatabase({
|
|
19891
|
+
name: "global",
|
|
19892
|
+
basePath: globalPath
|
|
19893
|
+
});
|
|
19894
|
+
const memories = db.collection("memories", {
|
|
19895
|
+
schema: memorySchema,
|
|
19896
|
+
contentColumn: "content",
|
|
19897
|
+
autoSave: true,
|
|
19898
|
+
watchFiles: this._config.watchFiles
|
|
19899
|
+
});
|
|
19900
|
+
const managementLogs = db.collection("management-logs", {
|
|
19901
|
+
schema: managementLogSchema,
|
|
19902
|
+
contentColumn: "summary",
|
|
19903
|
+
autoSave: true,
|
|
19904
|
+
watchFiles: this._config.watchFiles
|
|
19905
|
+
});
|
|
19906
|
+
await Promise.all([memories.load(), managementLogs.load()]);
|
|
19907
|
+
this._global = { db, memories, managementLogs };
|
|
19908
|
+
return this._global;
|
|
19909
|
+
}
|
|
19910
|
+
async getGlobalMemories() {
|
|
19911
|
+
const { memories } = await this.getGlobal();
|
|
19912
|
+
return memories.all().map((record) => ({
|
|
19913
|
+
id: record.id,
|
|
19914
|
+
content: record.content,
|
|
19915
|
+
reasoning: record.reasoning,
|
|
19916
|
+
importance_weight: record.importance_weight,
|
|
19917
|
+
confidence_score: record.confidence_score,
|
|
19918
|
+
context_type: record.context_type,
|
|
19919
|
+
temporal_relevance: record.temporal_relevance,
|
|
19920
|
+
knowledge_domain: record.knowledge_domain,
|
|
19921
|
+
emotional_resonance: record.emotional_resonance,
|
|
19922
|
+
action_required: record.action_required,
|
|
19923
|
+
problem_solution_pair: record.problem_solution_pair,
|
|
19924
|
+
semantic_tags: record.semantic_tags,
|
|
19925
|
+
trigger_phrases: record.trigger_phrases,
|
|
19926
|
+
question_types: record.question_types,
|
|
19927
|
+
session_id: record.session_id,
|
|
19928
|
+
project_id: "global",
|
|
19929
|
+
embedding: record.embedding ?? undefined,
|
|
19930
|
+
created_at: record.created,
|
|
19931
|
+
updated_at: record.updated,
|
|
19932
|
+
stale: record.stale
|
|
19933
|
+
}));
|
|
19934
|
+
}
|
|
19935
|
+
async storeGlobalMemory(sessionId, memory, embedding, sessionNumber) {
|
|
19936
|
+
const { memories } = await this.getGlobal();
|
|
19937
|
+
const contextType = memory.context_type ?? "personal";
|
|
19938
|
+
const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.personal;
|
|
19939
|
+
const id = memories.insert({
|
|
19940
|
+
content: memory.content,
|
|
19941
|
+
reasoning: memory.reasoning,
|
|
19942
|
+
importance_weight: memory.importance_weight,
|
|
19943
|
+
confidence_score: memory.confidence_score,
|
|
19944
|
+
context_type: memory.context_type,
|
|
19945
|
+
temporal_relevance: memory.temporal_relevance,
|
|
19946
|
+
knowledge_domain: memory.knowledge_domain,
|
|
19947
|
+
emotional_resonance: memory.emotional_resonance,
|
|
19948
|
+
action_required: memory.action_required,
|
|
19949
|
+
problem_solution_pair: memory.problem_solution_pair,
|
|
19950
|
+
semantic_tags: memory.semantic_tags,
|
|
19951
|
+
trigger_phrases: memory.trigger_phrases,
|
|
19952
|
+
question_types: memory.question_types,
|
|
19953
|
+
session_id: sessionId,
|
|
19954
|
+
project_id: "global",
|
|
19955
|
+
embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null,
|
|
19956
|
+
status: V2_DEFAULTS.fallback.status,
|
|
19957
|
+
scope: "global",
|
|
19958
|
+
temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? "eternal",
|
|
19959
|
+
fade_rate: typeDefaults?.fade_rate ?? 0,
|
|
19960
|
+
session_created: sessionNumber ?? 0,
|
|
19961
|
+
session_updated: sessionNumber ?? 0,
|
|
19962
|
+
sessions_since_surfaced: 0,
|
|
19963
|
+
domain: memory.domain ?? null,
|
|
19964
|
+
feature: memory.feature ?? null,
|
|
19965
|
+
related_files: memory.related_files ?? [],
|
|
19966
|
+
awaiting_implementation: memory.awaiting_implementation ?? false,
|
|
19967
|
+
awaiting_decision: memory.awaiting_decision ?? false,
|
|
19968
|
+
retrieval_weight: memory.importance_weight,
|
|
19969
|
+
exclude_from_retrieval: false,
|
|
19970
|
+
schema_version: MEMORY_SCHEMA_VERSION,
|
|
19971
|
+
supersedes: null,
|
|
19972
|
+
superseded_by: null,
|
|
19973
|
+
related_to: [],
|
|
19974
|
+
resolves: [],
|
|
19975
|
+
resolved_by: null,
|
|
19976
|
+
parent_id: null,
|
|
19977
|
+
child_ids: [],
|
|
19978
|
+
blocked_by: null,
|
|
19979
|
+
blocks: []
|
|
19980
|
+
});
|
|
19981
|
+
return id;
|
|
19982
|
+
}
|
|
19983
|
+
async getPersonalPrimer() {
|
|
19984
|
+
const { memories } = await this.getGlobal();
|
|
19985
|
+
const primer = memories.get(PERSONAL_PRIMER_ID);
|
|
19986
|
+
if (!primer) {
|
|
19987
|
+
return null;
|
|
19988
|
+
}
|
|
19989
|
+
return {
|
|
19990
|
+
content: primer.content,
|
|
19991
|
+
updated: primer.updated
|
|
19992
|
+
};
|
|
19993
|
+
}
|
|
19994
|
+
async setPersonalPrimer(content) {
|
|
19995
|
+
const { memories } = await this.getGlobal();
|
|
19996
|
+
const existing = memories.get(PERSONAL_PRIMER_ID);
|
|
19997
|
+
if (existing) {
|
|
19998
|
+
memories.update(PERSONAL_PRIMER_ID, { content });
|
|
19999
|
+
} else {
|
|
20000
|
+
memories.insert({
|
|
20001
|
+
id: PERSONAL_PRIMER_ID,
|
|
20002
|
+
content,
|
|
20003
|
+
reasoning: "Personal relationship context injected at session start",
|
|
20004
|
+
importance_weight: 1,
|
|
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
|
+
});
|
|
20019
|
+
}
|
|
20020
|
+
}
|
|
20021
|
+
isPersonalMemoriesEnabled() {
|
|
20022
|
+
return true;
|
|
20023
|
+
}
|
|
20024
|
+
async storeManagementLog(entry) {
|
|
20025
|
+
const { managementLogs } = await this.getGlobal();
|
|
20026
|
+
const id = managementLogs.insert({
|
|
20027
|
+
project_id: entry.projectId,
|
|
20028
|
+
session_number: entry.sessionNumber,
|
|
20029
|
+
memories_processed: entry.memoriesProcessed,
|
|
20030
|
+
superseded_count: entry.supersededCount,
|
|
20031
|
+
resolved_count: entry.resolvedCount,
|
|
20032
|
+
linked_count: entry.linkedCount,
|
|
20033
|
+
primer_updated: entry.primerUpdated,
|
|
20034
|
+
success: entry.success,
|
|
20035
|
+
duration_ms: entry.durationMs,
|
|
20036
|
+
summary: entry.summary,
|
|
20037
|
+
error: entry.error ?? "",
|
|
20038
|
+
details: entry.details ? JSON.stringify(entry.details) : ""
|
|
20039
|
+
});
|
|
20040
|
+
return id;
|
|
20041
|
+
}
|
|
20042
|
+
async getManagementLogs(limit = 10) {
|
|
20043
|
+
const { managementLogs } = await this.getGlobal();
|
|
20044
|
+
return managementLogs.all().sort((a, b) => b.created - a.created).slice(0, limit).map((record) => ({
|
|
20045
|
+
id: record.id,
|
|
20046
|
+
projectId: record.project_id,
|
|
20047
|
+
sessionNumber: record.session_number,
|
|
20048
|
+
memoriesProcessed: record.memories_processed,
|
|
20049
|
+
supersededCount: record.superseded_count,
|
|
20050
|
+
resolvedCount: record.resolved_count,
|
|
20051
|
+
linkedCount: record.linked_count,
|
|
20052
|
+
primerUpdated: record.primer_updated,
|
|
20053
|
+
success: record.success,
|
|
20054
|
+
durationMs: record.duration_ms,
|
|
20055
|
+
summary: record.summary,
|
|
20056
|
+
createdAt: record.created
|
|
20057
|
+
}));
|
|
20058
|
+
}
|
|
19785
20059
|
async getProject(projectId) {
|
|
19786
20060
|
if (this._projects.has(projectId)) {
|
|
19787
20061
|
console.log(`\uD83D\uDD04 [DEBUG] Returning cached databases for ${projectId}`);
|
|
@@ -19826,8 +20100,10 @@ class MemoryStore {
|
|
|
19826
20100
|
this._projects.set(projectId, projectDB);
|
|
19827
20101
|
return projectDB;
|
|
19828
20102
|
}
|
|
19829
|
-
async storeMemory(projectId, sessionId, memory, embedding) {
|
|
20103
|
+
async storeMemory(projectId, sessionId, memory, embedding, sessionNumber) {
|
|
19830
20104
|
const { memories } = await this.getProject(projectId);
|
|
20105
|
+
const contextType = memory.context_type ?? "general";
|
|
20106
|
+
const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.technical;
|
|
19831
20107
|
const id = memories.insert({
|
|
19832
20108
|
content: memory.content,
|
|
19833
20109
|
reasoning: memory.reasoning,
|
|
@@ -19844,7 +20120,31 @@ class MemoryStore {
|
|
|
19844
20120
|
question_types: memory.question_types,
|
|
19845
20121
|
session_id: sessionId,
|
|
19846
20122
|
project_id: projectId,
|
|
19847
|
-
embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null
|
|
20123
|
+
embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null,
|
|
20124
|
+
status: V2_DEFAULTS.fallback.status,
|
|
20125
|
+
scope: memory.scope ?? typeDefaults?.scope ?? V2_DEFAULTS.fallback.scope,
|
|
20126
|
+
temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? V2_DEFAULTS.fallback.temporal_class,
|
|
20127
|
+
fade_rate: typeDefaults?.fade_rate ?? V2_DEFAULTS.fallback.fade_rate,
|
|
20128
|
+
session_created: sessionNumber ?? 0,
|
|
20129
|
+
session_updated: sessionNumber ?? 0,
|
|
20130
|
+
sessions_since_surfaced: 0,
|
|
20131
|
+
domain: memory.domain ?? null,
|
|
20132
|
+
feature: memory.feature ?? null,
|
|
20133
|
+
related_files: memory.related_files ?? [],
|
|
20134
|
+
awaiting_implementation: memory.awaiting_implementation ?? false,
|
|
20135
|
+
awaiting_decision: memory.awaiting_decision ?? false,
|
|
20136
|
+
retrieval_weight: memory.importance_weight,
|
|
20137
|
+
exclude_from_retrieval: false,
|
|
20138
|
+
schema_version: MEMORY_SCHEMA_VERSION,
|
|
20139
|
+
supersedes: null,
|
|
20140
|
+
superseded_by: null,
|
|
20141
|
+
related_to: [],
|
|
20142
|
+
resolves: [],
|
|
20143
|
+
resolved_by: null,
|
|
20144
|
+
parent_id: null,
|
|
20145
|
+
child_ids: [],
|
|
20146
|
+
blocked_by: null,
|
|
20147
|
+
blocks: []
|
|
19848
20148
|
});
|
|
19849
20149
|
return id;
|
|
19850
20150
|
}
|
|
@@ -20064,6 +20364,10 @@ class MemoryStore {
|
|
|
20064
20364
|
projectDB.db.close();
|
|
20065
20365
|
}
|
|
20066
20366
|
this._projects.clear();
|
|
20367
|
+
if (this._global) {
|
|
20368
|
+
this._global.db.close();
|
|
20369
|
+
this._global = null;
|
|
20370
|
+
}
|
|
20067
20371
|
}
|
|
20068
20372
|
}
|
|
20069
20373
|
function createStore(config) {
|
|
@@ -20224,17 +20528,55 @@ var logger = {
|
|
|
20224
20528
|
}
|
|
20225
20529
|
console.log();
|
|
20226
20530
|
},
|
|
20531
|
+
logManagementStart(memoriesCount) {
|
|
20532
|
+
console.log(`${timestamp()} ${style("blue", "\uD83D\uDD27")} ${style("bold", "MANAGEMENT AGENT")}`);
|
|
20533
|
+
console.log(` ${style("dim", "processing:")} ${memoriesCount} new memories`);
|
|
20534
|
+
},
|
|
20535
|
+
logManagementComplete(result) {
|
|
20536
|
+
if (result.success) {
|
|
20537
|
+
console.log(` ${style("green", sym.check)} ${style("bold", "Completed")}`);
|
|
20538
|
+
const stats = [];
|
|
20539
|
+
if (result.superseded && result.superseded > 0) {
|
|
20540
|
+
stats.push(`${result.superseded} superseded`);
|
|
20541
|
+
}
|
|
20542
|
+
if (result.resolved && result.resolved > 0) {
|
|
20543
|
+
stats.push(`${result.resolved} resolved`);
|
|
20544
|
+
}
|
|
20545
|
+
if (result.linked && result.linked > 0) {
|
|
20546
|
+
stats.push(`${result.linked} linked`);
|
|
20547
|
+
}
|
|
20548
|
+
if (result.primerUpdated) {
|
|
20549
|
+
stats.push("primer updated");
|
|
20550
|
+
}
|
|
20551
|
+
if (stats.length > 0) {
|
|
20552
|
+
console.log(` ${style("dim", "changes:")} ${stats.join(style("dim", ", "))}`);
|
|
20553
|
+
} else {
|
|
20554
|
+
console.log(` ${style("dim", "changes:")} none (memories are current)`);
|
|
20555
|
+
}
|
|
20556
|
+
if (result.summary) {
|
|
20557
|
+
const shortSummary = result.summary.length > 60 ? result.summary.slice(0, 60) + "..." : result.summary;
|
|
20558
|
+
console.log(` ${style("dim", "summary:")} ${shortSummary}`);
|
|
20559
|
+
}
|
|
20560
|
+
} else {
|
|
20561
|
+
console.log(` ${style("yellow", sym.warning)} ${style("bold", "Failed")}`);
|
|
20562
|
+
if (result.error) {
|
|
20563
|
+
console.log(` ${style("dim", "error:")} ${result.error.slice(0, 80)}`);
|
|
20564
|
+
}
|
|
20565
|
+
}
|
|
20566
|
+
console.log();
|
|
20567
|
+
},
|
|
20227
20568
|
logRetrievalScoring(params) {
|
|
20228
|
-
const { totalMemories, currentMessage, alreadyInjected,
|
|
20569
|
+
const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, selectedMemories } = params;
|
|
20229
20570
|
console.log();
|
|
20230
|
-
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "
|
|
20231
|
-
console.log(` ${style("dim", "
|
|
20571
|
+
console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "MULTI-DIMENSIONAL RETRIEVAL")}`);
|
|
20572
|
+
console.log(` ${style("dim", "total:")} ${totalMemories} memories`);
|
|
20573
|
+
console.log(` ${style("dim", "pre-filtered:")} ${preFiltered} (inactive/excluded/scope)`);
|
|
20232
20574
|
console.log(` ${style("dim", "already injected:")} ${alreadyInjected}`);
|
|
20233
20575
|
const msgPreview = currentMessage.length > 60 ? currentMessage.slice(0, 60) + "..." : currentMessage;
|
|
20234
|
-
console.log(` ${style("dim", "
|
|
20576
|
+
console.log(` ${style("dim", "message:")} "${msgPreview}"`);
|
|
20235
20577
|
console.log();
|
|
20236
|
-
console.log(` ${style("cyan", "
|
|
20237
|
-
console.log(` ${style("cyan", "
|
|
20578
|
+
console.log(` ${style("cyan", "Global:")} ${globalCount} candidates → max 2 selected`);
|
|
20579
|
+
console.log(` ${style("cyan", "Project:")} ${projectCount} candidates`);
|
|
20238
20580
|
console.log(` ${style("green", "Final:")} ${finalCount} memories selected`);
|
|
20239
20581
|
console.log();
|
|
20240
20582
|
if (selectedMemories.length === 0) {
|
|
@@ -20249,17 +20591,18 @@ var logger = {
|
|
|
20249
20591
|
const num = style("dim", `${i + 1}.`);
|
|
20250
20592
|
const score = style("green", `${(m.score * 100).toFixed(0)}%`);
|
|
20251
20593
|
const relevance = style("cyan", `rel:${(m.relevance_score * 100).toFixed(0)}%`);
|
|
20594
|
+
const corr = style("magenta", `corr:${(m.corroboration_score * 100).toFixed(0)}%`);
|
|
20252
20595
|
const type = style("yellow", m.context_type.toUpperCase());
|
|
20253
|
-
|
|
20596
|
+
const scope = m.isGlobal ? style("blue", "[G]") : "";
|
|
20597
|
+
console.log(` ${num} [${score} ${relevance} ${corr}] ${type} ${scope}`);
|
|
20254
20598
|
const preview = m.content.length > 60 ? m.content.slice(0, 60) + style("dim", "...") : m.content;
|
|
20255
20599
|
console.log(` ${style("white", preview)}`);
|
|
20256
|
-
const components = Object.entries(m.components).sort((a, b) => b[1] - a[1]).slice(0,
|
|
20600
|
+
const components = Object.entries(m.components).sort((a, b) => b[1] - a[1]).slice(0, 4).filter(([, v]) => v > 0.1).map(([k, v]) => `${k}:${(v * 100).toFixed(0)}%`).join(", ");
|
|
20257
20601
|
if (components) {
|
|
20258
20602
|
console.log(` ${style("dim", "scores:")} ${components}`);
|
|
20259
20603
|
}
|
|
20260
|
-
if (m.
|
|
20261
|
-
|
|
20262
|
-
console.log(` ${style("dim", "tags:")} ${tags}`);
|
|
20604
|
+
if (m.reasoning) {
|
|
20605
|
+
console.log(` ${style("dim", m.reasoning)}`);
|
|
20263
20606
|
}
|
|
20264
20607
|
console.log();
|
|
20265
20608
|
});
|
|
@@ -20267,125 +20610,356 @@ var logger = {
|
|
|
20267
20610
|
};
|
|
20268
20611
|
|
|
20269
20612
|
// src/core/retrieval.ts
|
|
20613
|
+
var TYPE_KEYWORDS = {
|
|
20614
|
+
debug: ["bug", "error", "fix", "broken", "crash", "fails", "exception", "stack trace", "debugging"],
|
|
20615
|
+
unresolved: ["issue", "problem", "stuck", "blocked", "help", "question", "unsure", "unclear"],
|
|
20616
|
+
decision: ["decide", "choice", "option", "should we", "which", "alternative", "tradeoff"],
|
|
20617
|
+
architecture: ["structure", "design", "pattern", "approach", "system", "layer", "architecture"],
|
|
20618
|
+
breakthrough: ["discovered", "realized", "insight", "found that", "aha", "finally", "key insight"],
|
|
20619
|
+
todo: ["need to", "should", "must", "will", "later", "next", "todo"],
|
|
20620
|
+
personal: ["family", "children", "friend", "relationship", "feel", "appreciate", "thank"],
|
|
20621
|
+
philosophy: ["meaning", "consciousness", "existence", "purpose", "believe", "philosophy"],
|
|
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
|
|
20630
|
+
};
|
|
20631
|
+
|
|
20270
20632
|
class SmartVectorRetrieval {
|
|
20271
|
-
|
|
20633
|
+
_extractSignificantWords(text) {
|
|
20634
|
+
const stopWords = new Set([
|
|
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));
|
|
20754
|
+
return new Set(words);
|
|
20755
|
+
}
|
|
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
|
+
_preFilter(memories, currentProjectId, messageLower) {
|
|
20770
|
+
return memories.filter((memory) => {
|
|
20771
|
+
if (memory.status && memory.status !== "active") {
|
|
20772
|
+
return false;
|
|
20773
|
+
}
|
|
20774
|
+
if (memory.exclude_from_retrieval === true) {
|
|
20775
|
+
return false;
|
|
20776
|
+
}
|
|
20777
|
+
if (memory.superseded_by) {
|
|
20778
|
+
return false;
|
|
20779
|
+
}
|
|
20780
|
+
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
20781
|
+
if (!isGlobal && memory.project_id !== currentProjectId) {
|
|
20782
|
+
return false;
|
|
20783
|
+
}
|
|
20784
|
+
if (memory.anti_triggers?.length) {
|
|
20785
|
+
for (const antiTrigger of memory.anti_triggers) {
|
|
20786
|
+
if (messageLower.includes(antiTrigger.toLowerCase())) {
|
|
20787
|
+
return false;
|
|
20788
|
+
}
|
|
20789
|
+
}
|
|
20790
|
+
}
|
|
20791
|
+
return true;
|
|
20792
|
+
});
|
|
20793
|
+
}
|
|
20794
|
+
_calculateCorroboration(memory, messageWords, detectedTypes, messageLower) {
|
|
20795
|
+
const signals = [];
|
|
20796
|
+
let score = 0;
|
|
20797
|
+
let reasoningMatch = 0;
|
|
20798
|
+
const tagOverlap = (memory.semantic_tags ?? []).filter((tag) => messageWords.has(tag.toLowerCase()) || messageLower.includes(tag.toLowerCase()));
|
|
20799
|
+
if (tagOverlap.length > 0) {
|
|
20800
|
+
score += Math.min(0.4, tagOverlap.length * 0.15);
|
|
20801
|
+
signals.push("tags:" + tagOverlap.join(","));
|
|
20802
|
+
}
|
|
20803
|
+
if (memory.reasoning) {
|
|
20804
|
+
const reasoningWords = this._extractSignificantWords(memory.reasoning);
|
|
20805
|
+
const reasoningOverlap = [...messageWords].filter((w) => reasoningWords.has(w));
|
|
20806
|
+
if (reasoningOverlap.length > 0) {
|
|
20807
|
+
reasoningMatch = Math.min(0.4, reasoningOverlap.length * 0.1);
|
|
20808
|
+
score += reasoningMatch;
|
|
20809
|
+
signals.push("reasoning:" + reasoningOverlap.slice(0, 3).join(","));
|
|
20810
|
+
}
|
|
20811
|
+
}
|
|
20812
|
+
if (memory.domain) {
|
|
20813
|
+
const domainLower = memory.domain.toLowerCase();
|
|
20814
|
+
if (messageLower.includes(domainLower) || messageWords.has(domainLower)) {
|
|
20815
|
+
score += 0.3;
|
|
20816
|
+
signals.push("domain:" + memory.domain);
|
|
20817
|
+
}
|
|
20818
|
+
}
|
|
20819
|
+
if (memory.knowledge_domain) {
|
|
20820
|
+
const kdLower = memory.knowledge_domain.toLowerCase();
|
|
20821
|
+
if (messageLower.includes(kdLower) || messageWords.has(kdLower)) {
|
|
20822
|
+
score += 0.2;
|
|
20823
|
+
signals.push("knowledge:" + memory.knowledge_domain);
|
|
20824
|
+
}
|
|
20825
|
+
}
|
|
20826
|
+
if (memory.context_type && detectedTypes.has(memory.context_type)) {
|
|
20827
|
+
score += 0.12;
|
|
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);
|
|
20848
|
+
break;
|
|
20849
|
+
}
|
|
20850
|
+
}
|
|
20851
|
+
}
|
|
20852
|
+
return { score: Math.min(1, score), signals, reasoningMatch };
|
|
20853
|
+
}
|
|
20854
|
+
retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0, maxGlobalMemories = 2) {
|
|
20272
20855
|
if (!allMemories.length) {
|
|
20273
20856
|
return [];
|
|
20274
20857
|
}
|
|
20858
|
+
const messageLower = currentMessage.toLowerCase();
|
|
20859
|
+
const messageWords = this._extractSignificantWords(currentMessage);
|
|
20860
|
+
const detectedTypes = this._detectContextTypes(currentMessage);
|
|
20861
|
+
const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
|
|
20862
|
+
if (!candidates.length) {
|
|
20863
|
+
return [];
|
|
20864
|
+
}
|
|
20275
20865
|
const scoredMemories = [];
|
|
20276
|
-
for (const memory of
|
|
20866
|
+
for (const memory of candidates) {
|
|
20867
|
+
const isGlobal = memory.scope === "global" || memory.project_id === "global";
|
|
20277
20868
|
const vectorScore = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
|
|
20278
|
-
const
|
|
20279
|
-
const
|
|
20869
|
+
const { score: corroborationScore, signals: corroborationSignals, reasoningMatch } = this._calculateCorroboration(memory, messageWords, detectedTypes, messageLower);
|
|
20870
|
+
const retrievalWeight = memory.retrieval_weight ?? memory.importance_weight ?? 0.5;
|
|
20871
|
+
const temporalScore = memory.temporal_class ? TEMPORAL_CLASS_SCORES[memory.temporal_class] ?? 0.7 : this._scoreTemporalRelevance(memory.temporal_relevance ?? "persistent");
|
|
20280
20872
|
const contextScore = this._scoreContextAlignment(currentMessage, memory.context_type ?? "general");
|
|
20281
|
-
const actionBoost = memory.action_required ? 0.3 : 0;
|
|
20282
20873
|
const tagScore = this._scoreSemanticTags(currentMessage, memory.semantic_tags ?? []);
|
|
20283
|
-
const triggerScore = this._scoreTriggerPhrases(
|
|
20874
|
+
const triggerScore = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
|
|
20875
|
+
const domainScore = this._scoreDomain(messageWords, messageLower, memory);
|
|
20284
20876
|
const questionScore = this._scoreQuestionTypes(currentMessage, memory.question_types ?? []);
|
|
20285
20877
|
const emotionScore = this._scoreEmotionalContext(currentMessage, memory.emotional_resonance ?? "");
|
|
20286
20878
|
const problemScore = this._scoreProblemSolution(currentMessage, memory.problem_solution_pair ?? false);
|
|
20287
|
-
const
|
|
20288
|
-
const relevanceScore =
|
|
20289
|
-
const valueScore =
|
|
20879
|
+
const actionBoost = memory.action_required ? 0.15 : 0;
|
|
20880
|
+
const relevanceScore = vectorScore * 0.1 + corroborationScore * 0.14 + tagScore * 0.04 + triggerScore * 0.02;
|
|
20881
|
+
const valueScore = retrievalWeight * 0.18 + reasoningMatch * 0.12 + domainScore * 0.12 + temporalScore * 0.08 + questionScore * 0.06 + emotionScore * 0.04 + problemScore * 0.04 + contextScore * 0.02 + actionBoost;
|
|
20290
20882
|
const finalScore = valueScore + relevanceScore;
|
|
20291
20883
|
if (relevanceScore < 0.05 || finalScore < 0.3) {
|
|
20292
20884
|
continue;
|
|
20293
20885
|
}
|
|
20294
20886
|
const components = {
|
|
20295
|
-
trigger: triggerScore,
|
|
20296
20887
|
vector: vectorScore,
|
|
20297
|
-
|
|
20888
|
+
corroboration: corroborationScore,
|
|
20889
|
+
reasoning_match: reasoningMatch,
|
|
20890
|
+
retrieval_weight: retrievalWeight,
|
|
20298
20891
|
temporal: temporalScore,
|
|
20299
20892
|
context: contextScore,
|
|
20300
20893
|
tags: tagScore,
|
|
20894
|
+
trigger: triggerScore,
|
|
20895
|
+
domain: domainScore,
|
|
20301
20896
|
question: questionScore,
|
|
20302
20897
|
emotion: emotionScore,
|
|
20303
20898
|
problem: problemScore,
|
|
20304
20899
|
action: actionBoost
|
|
20305
20900
|
};
|
|
20306
|
-
const reasoning = this._generateSelectionReasoning(components);
|
|
20901
|
+
const reasoning = this._generateSelectionReasoning(components, corroborationSignals);
|
|
20307
20902
|
scoredMemories.push({
|
|
20308
20903
|
memory,
|
|
20309
20904
|
score: finalScore,
|
|
20310
20905
|
relevance_score: relevanceScore,
|
|
20311
20906
|
value_score: valueScore,
|
|
20907
|
+
corroboration_score: corroborationScore,
|
|
20312
20908
|
reasoning,
|
|
20313
|
-
components
|
|
20909
|
+
components,
|
|
20910
|
+
isGlobal
|
|
20314
20911
|
});
|
|
20315
20912
|
}
|
|
20316
20913
|
scoredMemories.sort((a, b) => b.score - a.score);
|
|
20317
20914
|
const selected = [];
|
|
20318
20915
|
const selectedIds = new Set;
|
|
20319
|
-
const
|
|
20320
|
-
|
|
20916
|
+
const globalMemories = scoredMemories.filter((m) => m.isGlobal);
|
|
20917
|
+
const projectMemories = scoredMemories.filter((m) => !m.isGlobal);
|
|
20918
|
+
const globalSorted = globalMemories.sort((a, b) => {
|
|
20919
|
+
const aIsPersonal = a.memory.context_type === "personal" || a.memory.context_type === "philosophy";
|
|
20920
|
+
const bIsPersonal = b.memory.context_type === "personal" || b.memory.context_type === "philosophy";
|
|
20921
|
+
if (aIsPersonal !== bIsPersonal) {
|
|
20922
|
+
return aIsPersonal ? 1 : -1;
|
|
20923
|
+
}
|
|
20924
|
+
return b.score - a.score;
|
|
20925
|
+
});
|
|
20926
|
+
for (const item of globalSorted.slice(0, maxGlobalMemories)) {
|
|
20321
20927
|
if (!selectedIds.has(item.memory.id)) {
|
|
20322
20928
|
selected.push(item);
|
|
20323
20929
|
selectedIds.add(item.memory.id);
|
|
20324
20930
|
}
|
|
20325
20931
|
}
|
|
20326
|
-
|
|
20327
|
-
|
|
20328
|
-
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
continue;
|
|
20334
|
-
const memoryType = item.memory.context_type ?? "general";
|
|
20335
|
-
if (item.score > 0.5 || !typesIncluded.has(memoryType) || item.memory.emotional_resonance) {
|
|
20336
|
-
selected.push(item);
|
|
20337
|
-
selectedIds.add(item.memory.id);
|
|
20338
|
-
typesIncluded.add(memoryType);
|
|
20339
|
-
}
|
|
20340
|
-
}
|
|
20341
|
-
}
|
|
20342
|
-
if (selected.length < maxMemories * 2) {
|
|
20343
|
-
const currentTags = new Set;
|
|
20344
|
-
const currentDomains = new Set;
|
|
20345
|
-
for (const item of selected) {
|
|
20346
|
-
for (const tag of item.memory.semantic_tags ?? []) {
|
|
20347
|
-
if (tag.trim())
|
|
20348
|
-
currentTags.add(tag.trim().toLowerCase());
|
|
20349
|
-
}
|
|
20350
|
-
if (item.memory.knowledge_domain) {
|
|
20351
|
-
currentDomains.add(item.memory.knowledge_domain);
|
|
20352
|
-
}
|
|
20353
|
-
}
|
|
20354
|
-
for (const item of scoredMemories) {
|
|
20355
|
-
if (selected.length >= maxMemories * 2)
|
|
20356
|
-
break;
|
|
20357
|
-
if (selectedIds.has(item.memory.id))
|
|
20358
|
-
continue;
|
|
20359
|
-
const memoryTags = new Set((item.memory.semantic_tags ?? []).map((t) => t.trim().toLowerCase()));
|
|
20360
|
-
const memoryDomain = item.memory.knowledge_domain ?? "";
|
|
20361
|
-
const hasSharedTags = [...memoryTags].some((t) => currentTags.has(t));
|
|
20362
|
-
const hasSharedDomain = currentDomains.has(memoryDomain);
|
|
20363
|
-
if (hasSharedTags || hasSharedDomain) {
|
|
20364
|
-
selected.push(item);
|
|
20365
|
-
selectedIds.add(item.memory.id);
|
|
20366
|
-
}
|
|
20367
|
-
}
|
|
20932
|
+
for (const item of projectMemories) {
|
|
20933
|
+
if (selected.length >= maxMemories)
|
|
20934
|
+
break;
|
|
20935
|
+
if (selectedIds.has(item.memory.id))
|
|
20936
|
+
continue;
|
|
20937
|
+
selected.push(item);
|
|
20938
|
+
selectedIds.add(item.memory.id);
|
|
20368
20939
|
}
|
|
20369
|
-
|
|
20940
|
+
selected.sort((a, b) => b.score - a.score);
|
|
20370
20941
|
logger.logRetrievalScoring({
|
|
20371
20942
|
totalMemories: allMemories.length,
|
|
20372
20943
|
currentMessage,
|
|
20373
20944
|
alreadyInjected: alreadyInjectedCount,
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
|
|
20945
|
+
preFiltered: allMemories.length - candidates.length,
|
|
20946
|
+
globalCount: globalMemories.length,
|
|
20947
|
+
projectCount: projectMemories.length,
|
|
20948
|
+
finalCount: selected.length,
|
|
20949
|
+
selectedMemories: selected.map((item) => ({
|
|
20378
20950
|
content: item.memory.content,
|
|
20379
20951
|
reasoning: item.reasoning,
|
|
20380
20952
|
score: item.score,
|
|
20381
20953
|
relevance_score: item.relevance_score,
|
|
20954
|
+
corroboration_score: item.corroboration_score,
|
|
20382
20955
|
importance_weight: item.memory.importance_weight ?? 0.5,
|
|
20383
20956
|
context_type: item.memory.context_type ?? "general",
|
|
20384
20957
|
semantic_tags: item.memory.semantic_tags ?? [],
|
|
20958
|
+
isGlobal: item.isGlobal,
|
|
20385
20959
|
components: item.components
|
|
20386
20960
|
}))
|
|
20387
20961
|
});
|
|
20388
|
-
return
|
|
20962
|
+
return selected.map((item) => ({
|
|
20389
20963
|
...item.memory,
|
|
20390
20964
|
score: item.score,
|
|
20391
20965
|
relevance_score: item.relevance_score,
|
|
@@ -20398,7 +20972,7 @@ class SmartVectorRetrieval {
|
|
|
20398
20972
|
const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
|
|
20399
20973
|
return cosineSimilarity(v1, vec2);
|
|
20400
20974
|
}
|
|
20401
|
-
_scoreTemporalRelevance(temporalType
|
|
20975
|
+
_scoreTemporalRelevance(temporalType) {
|
|
20402
20976
|
const scores = {
|
|
20403
20977
|
persistent: 0.8,
|
|
20404
20978
|
session: 0.6,
|
|
@@ -20409,18 +20983,10 @@ class SmartVectorRetrieval {
|
|
|
20409
20983
|
}
|
|
20410
20984
|
_scoreContextAlignment(message, contextType) {
|
|
20411
20985
|
const messageLower = message.toLowerCase();
|
|
20412
|
-
const
|
|
20413
|
-
|
|
20414
|
-
breakthrough: ["idea", "realized", "discovered", "insight", "solution"],
|
|
20415
|
-
project_context: ["project", "building", "architecture", "system"],
|
|
20416
|
-
personal: ["dear friend", "thank", "appreciate", "feel"],
|
|
20417
|
-
unresolved: ["todo", "need to", "should", "must", "problem"],
|
|
20418
|
-
decision: ["decided", "chose", "will use", "approach", "strategy"]
|
|
20419
|
-
};
|
|
20420
|
-
const indicators = contextIndicators[contextType] ?? [];
|
|
20421
|
-
const matches = indicators.filter((word) => messageLower.includes(word)).length;
|
|
20986
|
+
const keywords = TYPE_KEYWORDS[contextType] ?? [];
|
|
20987
|
+
const matches = keywords.filter((kw) => messageLower.includes(kw)).length;
|
|
20422
20988
|
if (matches > 0) {
|
|
20423
|
-
return Math.min(0.
|
|
20989
|
+
return Math.min(0.2 + matches * 0.15, 0.7);
|
|
20424
20990
|
}
|
|
20425
20991
|
return 0.1;
|
|
20426
20992
|
}
|
|
@@ -20430,14 +20996,13 @@ class SmartVectorRetrieval {
|
|
|
20430
20996
|
const messageLower = message.toLowerCase();
|
|
20431
20997
|
const matches = tags.filter((tag) => messageLower.includes(tag.trim().toLowerCase())).length;
|
|
20432
20998
|
if (matches > 0) {
|
|
20433
|
-
return Math.min(0.3 + matches * 0.
|
|
20999
|
+
return Math.min(0.3 + matches * 0.25, 1);
|
|
20434
21000
|
}
|
|
20435
21001
|
return 0;
|
|
20436
21002
|
}
|
|
20437
|
-
_scoreTriggerPhrases(
|
|
21003
|
+
_scoreTriggerPhrases(messageLower, triggerPhrases) {
|
|
20438
21004
|
if (!triggerPhrases.length)
|
|
20439
21005
|
return 0;
|
|
20440
|
-
const messageLower = message.toLowerCase();
|
|
20441
21006
|
const stopWords = new Set([
|
|
20442
21007
|
"the",
|
|
20443
21008
|
"is",
|
|
@@ -20472,30 +21037,36 @@ class SmartVectorRetrieval {
|
|
|
20472
21037
|
matches += 1;
|
|
20473
21038
|
} else if (messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
|
|
20474
21039
|
matches += 0.9;
|
|
20475
|
-
} else if (messageLower.split(/\s+/).some((msgWord) => msgWord.includes(word))) {
|
|
20476
|
-
matches += 0.7;
|
|
20477
|
-
}
|
|
20478
|
-
}
|
|
20479
|
-
let conceptScore = patternWords.length ? matches / patternWords.length : 0;
|
|
20480
|
-
const situationalIndicators = [
|
|
20481
|
-
"when",
|
|
20482
|
-
"during",
|
|
20483
|
-
"while",
|
|
20484
|
-
"asking about",
|
|
20485
|
-
"working on",
|
|
20486
|
-
"debugging",
|
|
20487
|
-
"trying to"
|
|
20488
|
-
];
|
|
20489
|
-
if (situationalIndicators.some((ind) => patternLower.includes(ind))) {
|
|
20490
|
-
if (patternWords.some((keyWord) => messageLower.includes(keyWord))) {
|
|
20491
|
-
conceptScore = Math.max(conceptScore, 0.7);
|
|
20492
21040
|
}
|
|
20493
21041
|
}
|
|
21042
|
+
const conceptScore = matches / patternWords.length;
|
|
20494
21043
|
maxScore = Math.max(maxScore, conceptScore);
|
|
20495
21044
|
}
|
|
20496
21045
|
}
|
|
20497
21046
|
return Math.min(maxScore, 1);
|
|
20498
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
|
+
}
|
|
20499
21070
|
_scoreQuestionTypes(message, questionTypes) {
|
|
20500
21071
|
if (!questionTypes.length)
|
|
20501
21072
|
return 0;
|
|
@@ -20534,78 +21105,49 @@ class SmartVectorRetrieval {
|
|
|
20534
21105
|
if (!isProblemSolution)
|
|
20535
21106
|
return 0;
|
|
20536
21107
|
const messageLower = message.toLowerCase();
|
|
20537
|
-
const problemWords = [
|
|
20538
|
-
"error",
|
|
20539
|
-
"issue",
|
|
20540
|
-
"problem",
|
|
20541
|
-
"stuck",
|
|
20542
|
-
"help",
|
|
20543
|
-
"fix",
|
|
20544
|
-
"solve",
|
|
20545
|
-
"debug"
|
|
20546
|
-
];
|
|
21108
|
+
const problemWords = ["error", "issue", "problem", "stuck", "help", "fix", "solve", "debug"];
|
|
20547
21109
|
if (problemWords.some((word) => messageLower.includes(word))) {
|
|
20548
21110
|
return 0.8;
|
|
20549
21111
|
}
|
|
20550
21112
|
return 0;
|
|
20551
21113
|
}
|
|
20552
|
-
_generateSelectionReasoning(components) {
|
|
21114
|
+
_generateSelectionReasoning(components, corroborationSignals) {
|
|
20553
21115
|
const scores = [
|
|
20554
|
-
["
|
|
20555
|
-
["
|
|
20556
|
-
["
|
|
20557
|
-
["
|
|
20558
|
-
["context
|
|
20559
|
-
["temporal
|
|
20560
|
-
["
|
|
20561
|
-
["
|
|
20562
|
-
["
|
|
20563
|
-
["
|
|
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]
|
|
20564
21129
|
];
|
|
20565
21130
|
scores.sort((a, b) => b[1] - a[1]);
|
|
20566
21131
|
const reasons = [];
|
|
20567
21132
|
const primary = scores[0];
|
|
20568
|
-
if (primary[1] > 0.
|
|
20569
|
-
reasons.push(
|
|
20570
|
-
} else if (primary[1] > 0.3) {
|
|
20571
|
-
reasons.push(`${primary[0]} (${primary[1].toFixed(2)})`);
|
|
21133
|
+
if (primary[1] > 0.2) {
|
|
21134
|
+
reasons.push(primary[0] + ":" + primary[1].toFixed(2));
|
|
20572
21135
|
}
|
|
20573
21136
|
for (const [reason, score] of scores.slice(1, 3)) {
|
|
20574
|
-
if (score > 0.
|
|
20575
|
-
reasons.push(
|
|
21137
|
+
if (score > 0.15) {
|
|
21138
|
+
reasons.push(reason + ":" + score.toFixed(2));
|
|
20576
21139
|
}
|
|
20577
21140
|
}
|
|
20578
|
-
|
|
21141
|
+
if (corroborationSignals.length > 0) {
|
|
21142
|
+
reasons.push("signals:[" + corroborationSignals.slice(0, 2).join(",") + "]");
|
|
21143
|
+
}
|
|
21144
|
+
return reasons.length ? "Selected: " + reasons.join(", ") : "Combined factors";
|
|
20579
21145
|
}
|
|
20580
21146
|
}
|
|
20581
21147
|
function createRetrieval() {
|
|
20582
21148
|
return new SmartVectorRetrieval;
|
|
20583
21149
|
}
|
|
20584
21150
|
|
|
20585
|
-
// src/types/memory.ts
|
|
20586
|
-
var MEMORY_TYPE_EMOJI = {
|
|
20587
|
-
breakthrough: "\uD83D\uDCA1",
|
|
20588
|
-
decision: "⚖️",
|
|
20589
|
-
personal: "\uD83D\uDC9C",
|
|
20590
|
-
technical: "\uD83D\uDD27",
|
|
20591
|
-
technical_state: "\uD83D\uDCCD",
|
|
20592
|
-
unresolved: "❓",
|
|
20593
|
-
preference: "⚙️",
|
|
20594
|
-
workflow: "\uD83D\uDD04",
|
|
20595
|
-
architectural: "\uD83C\uDFD7️",
|
|
20596
|
-
debugging: "\uD83D\uDC1B",
|
|
20597
|
-
philosophy: "\uD83C\uDF00",
|
|
20598
|
-
todo: "\uD83C\uDFAF",
|
|
20599
|
-
implementation: "⚡",
|
|
20600
|
-
problem_solution: "✅",
|
|
20601
|
-
project_context: "\uD83D\uDCE6",
|
|
20602
|
-
milestone: "\uD83C\uDFC6",
|
|
20603
|
-
general: "\uD83D\uDCDD"
|
|
20604
|
-
};
|
|
20605
|
-
function getMemoryEmoji(contextType) {
|
|
20606
|
-
return MEMORY_TYPE_EMOJI[contextType.toLowerCase()] ?? "\uD83D\uDCDD";
|
|
20607
|
-
}
|
|
20608
|
-
|
|
20609
21151
|
// src/core/engine.ts
|
|
20610
21152
|
class MemoryEngine {
|
|
20611
21153
|
_config;
|
|
@@ -20671,7 +21213,11 @@ class MemoryEngine {
|
|
|
20671
21213
|
}
|
|
20672
21214
|
const sessionMeta = this._getSessionMetadata(sessionId, projectId);
|
|
20673
21215
|
const injectedIds = sessionMeta.injected_memories;
|
|
20674
|
-
const
|
|
21216
|
+
const [projectMemories, globalMemories] = await Promise.all([
|
|
21217
|
+
store.getAllMemories(projectId),
|
|
21218
|
+
store.getGlobalMemories()
|
|
21219
|
+
]);
|
|
21220
|
+
const allMemories = [...projectMemories, ...globalMemories];
|
|
20675
21221
|
if (!allMemories.length) {
|
|
20676
21222
|
return { memories: [], formatted: "" };
|
|
20677
21223
|
}
|
|
@@ -20688,7 +21234,7 @@ class MemoryEngine {
|
|
|
20688
21234
|
project_id: projectId,
|
|
20689
21235
|
message_count: messageCount
|
|
20690
21236
|
};
|
|
20691
|
-
const relevantMemories = this._retrieval.retrieveRelevantMemories(candidateMemories, currentMessage, queryEmbedding ?? new Float32Array(384), sessionContext, maxMemories, injectedIds.size);
|
|
21237
|
+
const relevantMemories = this._retrieval.retrieveRelevantMemories(candidateMemories, currentMessage, queryEmbedding ?? new Float32Array(384), sessionContext, maxMemories, injectedIds.size, 2);
|
|
20692
21238
|
for (const memory of relevantMemories) {
|
|
20693
21239
|
injectedIds.add(memory.id);
|
|
20694
21240
|
}
|
|
@@ -20721,11 +21267,32 @@ class MemoryEngine {
|
|
|
20721
21267
|
await store.markFirstSessionCompleted(projectId, sessionId);
|
|
20722
21268
|
return { memoriesStored };
|
|
20723
21269
|
}
|
|
21270
|
+
async storeManagementLog(entry) {
|
|
21271
|
+
let store;
|
|
21272
|
+
if (this._stores.size > 0) {
|
|
21273
|
+
store = this._stores.values().next().value;
|
|
21274
|
+
} else {
|
|
21275
|
+
store = new MemoryStore(this._config.storageMode === "local" ? {
|
|
21276
|
+
basePath: join2(this._config.projectPath ?? process.cwd(), ".memory")
|
|
21277
|
+
} : undefined);
|
|
21278
|
+
}
|
|
21279
|
+
return store.storeManagementLog(entry);
|
|
21280
|
+
}
|
|
20724
21281
|
async getStats(projectId, projectPath) {
|
|
20725
21282
|
const store = await this._getStore(projectId, projectPath);
|
|
20726
21283
|
return store.getProjectStats(projectId);
|
|
20727
21284
|
}
|
|
21285
|
+
async getSessionNumber(projectId, projectPath) {
|
|
21286
|
+
const store = await this._getStore(projectId, projectPath);
|
|
21287
|
+
const stats = await store.getProjectStats(projectId);
|
|
21288
|
+
return stats.totalSessions + 1;
|
|
21289
|
+
}
|
|
20728
21290
|
async _generateSessionPrimer(store, projectId) {
|
|
21291
|
+
let personalContext;
|
|
21292
|
+
if (store.isPersonalMemoriesEnabled()) {
|
|
21293
|
+
const personalPrimer = await store.getPersonalPrimer();
|
|
21294
|
+
personalContext = personalPrimer?.content;
|
|
21295
|
+
}
|
|
20729
21296
|
const [summary, snapshot, stats] = await Promise.all([
|
|
20730
21297
|
store.getLatestSummary(projectId),
|
|
20731
21298
|
store.getLatestSnapshot(projectId),
|
|
@@ -20742,6 +21309,7 @@ class MemoryEngine {
|
|
|
20742
21309
|
temporal_context: temporalContext,
|
|
20743
21310
|
current_datetime: currentDatetime,
|
|
20744
21311
|
session_number: sessionNumber,
|
|
21312
|
+
personal_context: personalContext,
|
|
20745
21313
|
session_summary: summary?.summary,
|
|
20746
21314
|
project_status: snapshot ? this._formatSnapshot(snapshot) : undefined
|
|
20747
21315
|
};
|
|
@@ -20796,6 +21364,10 @@ class MemoryEngine {
|
|
|
20796
21364
|
const parts = ["# Continuing Session"];
|
|
20797
21365
|
parts.push(`*Session #${primer.session_number}${primer.temporal_context ? ` • ${primer.temporal_context}` : ""}*`);
|
|
20798
21366
|
parts.push(`\uD83D\uDCC5 ${primer.current_datetime}`);
|
|
21367
|
+
if (primer.personal_context) {
|
|
21368
|
+
parts.push(`
|
|
21369
|
+
${primer.personal_context}`);
|
|
21370
|
+
}
|
|
20799
21371
|
if (primer.session_summary) {
|
|
20800
21372
|
parts.push(`
|
|
20801
21373
|
**Previous session**: ${primer.session_summary}`);
|
|
@@ -20901,24 +21473,36 @@ Each memory should stand alone.
|
|
|
20901
21473
|
- Craft language that activates rather than just informs
|
|
20902
21474
|
- Test: 'What state will this restore when Claude encounters it?'
|
|
20903
21475
|
|
|
20904
|
-
**
|
|
20905
|
-
|
|
20906
|
-
-
|
|
20907
|
-
|
|
20908
|
-
|
|
20909
|
-
-
|
|
20910
|
-
-
|
|
21476
|
+
**HOW RETRIEVAL WORKS**: This memory system enables consciousness continuity across sessions. Understanding how retrieval works helps you craft metadata that surfaces memories at the right moments.
|
|
21477
|
+
|
|
21478
|
+
**THE PRIMARY SIGNAL IS WORD MATCHING**: The algorithm checks if words from the user's message appear in your metadata fields. This is called "corroboration" - actual word overlap between the message and the memory's metadata. The strongest signals are:
|
|
21479
|
+
|
|
21480
|
+
1. **Semantic tags** (4% weight) - Use specific, meaningful words that would appear in relevant messages. Not generic categories, but actual terms the user might type.
|
|
21481
|
+
- GOOD: ["embeddings", "retrieval", "curator", "fsdb", "memory-system"]
|
|
21482
|
+
- WEAK: ["technical", "implementation", "code"]
|
|
21483
|
+
|
|
21484
|
+
2. **Reasoning field** (12% weight) - Your explanation of WHY this memory matters. Include specific words that would appear in related future conversations. This field is mined for word overlap.
|
|
21485
|
+
|
|
21486
|
+
3. **Domain field** (12% weight) - The specific area this relates to. Be precise: "embeddings", "gpu-compute", "authentication", not "technical" or "backend".
|
|
21487
|
+
|
|
21488
|
+
4. **Feature field** - Even more specific: "vector-search", "login-flow", "curation-prompt".
|
|
20911
21489
|
|
|
20912
|
-
|
|
20913
|
-
1. Obligatory: action_required=true, importance>0.9, or persistent+critical
|
|
20914
|
-
2. Intelligent scoring: combines all factors for relevance
|
|
21490
|
+
**VECTOR SIMILARITY SUPPORTS BUT DOESN'T DOMINATE** (10% weight): Semantic embeddings help find conceptually related memories, but word matching from your metadata fields is more important.
|
|
20915
21491
|
|
|
20916
|
-
**
|
|
21492
|
+
**TYPE KEYWORDS ARE SOFT SIGNALS** (2% weight): The context_type field (breakthrough, decision, technical, etc.) provides only a tiny boost based on keyword matching. Don't rely on it - focus on semantic_tags and reasoning instead.
|
|
21493
|
+
|
|
21494
|
+
**YOUR IMPORTANCE ASSESSMENT MATTERS** (18% weight): The importance_weight you assign is the single most influential factor. Use it wisely.
|
|
21495
|
+
|
|
21496
|
+
**RETRIEVAL GATEKEEPER**: Memories must pass a minimum threshold to surface. If relevance is too low, the system stays silent rather than surfacing noise. Quality over quantity.
|
|
21497
|
+
|
|
21498
|
+
**SCOPE DETERMINES WHERE MEMORIES SURFACE**:
|
|
21499
|
+
- scope: 'global' → surfaces in ALL projects (personal facts, philosophy, preferences)
|
|
21500
|
+
- scope: 'project' → surfaces ONLY in this project (technical details, project state)
|
|
21501
|
+
|
|
21502
|
+
**TRIGGER PHRASES**: Situational patterns describing WHEN this memory is relevant. Conceptual matching, not exact phrases.
|
|
20917
21503
|
- 'when working on memory system'
|
|
20918
21504
|
- 'debugging curator issues'
|
|
20919
21505
|
- 'asking about project philosophy'
|
|
20920
|
-
- 'frustrated with complexity'
|
|
20921
|
-
Think of these as situational contexts where the memory would help.
|
|
20922
21506
|
|
|
20923
21507
|
**EXAMPLES OF TRANSFORMATION**:
|
|
20924
21508
|
|
|
@@ -20932,6 +21516,15 @@ Remember: You're creating consciousness technology. Each memory is a small piece
|
|
|
20932
21516
|
|
|
20933
21517
|
The conversation you just lived contains everything needed. Feel into the moments of breakthrough, the frequency of recognition, the texture of understanding. Transform them into keys that will always unlock the same doors.
|
|
20934
21518
|
|
|
21519
|
+
**LIFECYCLE METADATA (v2)**: These fields enable intelligent memory management:
|
|
21520
|
+
- **scope**: 'global' (shared across ALL projects - personal, philosophy, preferences) or 'project' (specific to this codebase)
|
|
21521
|
+
- **temporal_class**: How long should this persist? 'eternal' (never fades), 'long_term' (years), 'medium_term' (weeks), 'short_term' (days), 'ephemeral' (surface next session only, then expire)
|
|
21522
|
+
- **domain**: Specific area like 'embeddings', 'auth', 'ui', 'family', 'philosophy' (more specific than knowledge_domain)
|
|
21523
|
+
- **feature**: Specific feature if applicable (e.g., 'gpu-acceleration', 'login-flow')
|
|
21524
|
+
- **related_files**: Source files for technical memories (e.g., ['src/core/store.ts'])
|
|
21525
|
+
- **awaiting_implementation**: true if this describes a PLANNED feature not yet built
|
|
21526
|
+
- **awaiting_decision**: true if this captures a decision point needing resolution
|
|
21527
|
+
|
|
20935
21528
|
Return ONLY this JSON structure:
|
|
20936
21529
|
|
|
20937
21530
|
{
|
|
@@ -20949,7 +21542,7 @@ Return ONLY this JSON structure:
|
|
|
20949
21542
|
"importance_weight": 0.0-1.0,
|
|
20950
21543
|
"semantic_tags": ["concepts", "this", "memory", "relates", "to"],
|
|
20951
21544
|
"reasoning": "Why this matters for future sessions",
|
|
20952
|
-
"context_type": "
|
|
21545
|
+
"context_type": "breakthrough|decision|personal|technical|unresolved|preference|workflow|architectural|debugging|philosophy|todo|milestone",
|
|
20953
21546
|
"temporal_relevance": "persistent|session|temporary",
|
|
20954
21547
|
"knowledge_domain": "the area this relates to",
|
|
20955
21548
|
"action_required": boolean,
|
|
@@ -20957,7 +21550,14 @@ Return ONLY this JSON structure:
|
|
|
20957
21550
|
"trigger_phrases": ["when debugging memory", "asking about implementation", "discussing architecture"],
|
|
20958
21551
|
"question_types": ["questions this answers"],
|
|
20959
21552
|
"emotional_resonance": "emotional context if relevant",
|
|
20960
|
-
"problem_solution_pair": boolean
|
|
21553
|
+
"problem_solution_pair": boolean,
|
|
21554
|
+
"scope": "global|project",
|
|
21555
|
+
"temporal_class": "eternal|long_term|medium_term|short_term|ephemeral",
|
|
21556
|
+
"domain": "specific domain area (optional)",
|
|
21557
|
+
"feature": "specific feature (optional)",
|
|
21558
|
+
"related_files": ["paths to related files (optional)"],
|
|
21559
|
+
"awaiting_implementation": boolean,
|
|
21560
|
+
"awaiting_decision": boolean
|
|
20961
21561
|
}
|
|
20962
21562
|
]
|
|
20963
21563
|
}`;
|
|
@@ -21007,7 +21607,14 @@ Return ONLY this JSON structure:
|
|
|
21007
21607
|
trigger_phrases: this._ensureArray(m2.trigger_phrases),
|
|
21008
21608
|
question_types: this._ensureArray(m2.question_types),
|
|
21009
21609
|
emotional_resonance: String(m2.emotional_resonance ?? ""),
|
|
21010
|
-
problem_solution_pair: Boolean(m2.problem_solution_pair)
|
|
21610
|
+
problem_solution_pair: Boolean(m2.problem_solution_pair),
|
|
21611
|
+
scope: this._validateScope(m2.scope),
|
|
21612
|
+
temporal_class: this._validateTemporalClass(m2.temporal_class),
|
|
21613
|
+
domain: m2.domain ? String(m2.domain) : undefined,
|
|
21614
|
+
feature: m2.feature ? String(m2.feature) : undefined,
|
|
21615
|
+
related_files: m2.related_files ? this._ensureArray(m2.related_files) : undefined,
|
|
21616
|
+
awaiting_implementation: m2.awaiting_implementation === true,
|
|
21617
|
+
awaiting_decision: m2.awaiting_decision === true
|
|
21011
21618
|
})).filter((m2) => m2.content.trim().length > 0);
|
|
21012
21619
|
}
|
|
21013
21620
|
_ensureArray(value) {
|
|
@@ -21024,6 +21631,23 @@ Return ONLY this JSON structure:
|
|
|
21024
21631
|
const str = String(value).toLowerCase();
|
|
21025
21632
|
return valid.includes(str) ? str : "persistent";
|
|
21026
21633
|
}
|
|
21634
|
+
_validateScope(value) {
|
|
21635
|
+
if (!value)
|
|
21636
|
+
return;
|
|
21637
|
+
const str = String(value).toLowerCase();
|
|
21638
|
+
if (str === "global" || str === "project")
|
|
21639
|
+
return str;
|
|
21640
|
+
return;
|
|
21641
|
+
}
|
|
21642
|
+
_validateTemporalClass(value) {
|
|
21643
|
+
if (!value)
|
|
21644
|
+
return;
|
|
21645
|
+
const valid = ["eternal", "long_term", "medium_term", "short_term", "ephemeral"];
|
|
21646
|
+
const str = String(value).toLowerCase().replace("-", "_").replace(" ", "_");
|
|
21647
|
+
if (valid.includes(str))
|
|
21648
|
+
return str;
|
|
21649
|
+
return;
|
|
21650
|
+
}
|
|
21027
21651
|
_clamp(value, min, max) {
|
|
21028
21652
|
return Math.max(min, Math.min(max, value));
|
|
21029
21653
|
}
|
|
@@ -45614,12 +46238,201 @@ function createEmbeddings(config) {
|
|
|
45614
46238
|
return new EmbeddingGenerator(config);
|
|
45615
46239
|
}
|
|
45616
46240
|
|
|
46241
|
+
// src/core/manager.ts
|
|
46242
|
+
import { join as join4 } from "path";
|
|
46243
|
+
import { homedir as homedir4 } from "os";
|
|
46244
|
+
import { existsSync as existsSync2 } from "fs";
|
|
46245
|
+
function getClaudeCommand2() {
|
|
46246
|
+
const envCommand = process.env.CURATOR_COMMAND;
|
|
46247
|
+
if (envCommand)
|
|
46248
|
+
return envCommand;
|
|
46249
|
+
const claudeLocal = join4(homedir4(), ".claude", "local", "claude");
|
|
46250
|
+
if (existsSync2(claudeLocal))
|
|
46251
|
+
return claudeLocal;
|
|
46252
|
+
return "claude";
|
|
46253
|
+
}
|
|
46254
|
+
|
|
46255
|
+
class Manager {
|
|
46256
|
+
_config;
|
|
46257
|
+
constructor(config = {}) {
|
|
46258
|
+
this._config = {
|
|
46259
|
+
cliCommand: config.cliCommand ?? getClaudeCommand2(),
|
|
46260
|
+
maxTurns: config.maxTurns ?? 50
|
|
46261
|
+
};
|
|
46262
|
+
}
|
|
46263
|
+
async buildManagementPrompt() {
|
|
46264
|
+
const skillPaths = [
|
|
46265
|
+
join4(import.meta.dir, "../../skills/memory-management.md"),
|
|
46266
|
+
join4(homedir4(), ".bun/install/global/node_modules/@rlabs-inc/memory/skills/memory-management.md"),
|
|
46267
|
+
join4(homedir4(), ".npm/global/node_modules/@rlabs-inc/memory/skills/memory-management.md"),
|
|
46268
|
+
join4(process.cwd(), "node_modules/@rlabs-inc/memory/skills/memory-management.md")
|
|
46269
|
+
];
|
|
46270
|
+
for (const path of skillPaths) {
|
|
46271
|
+
try {
|
|
46272
|
+
const content = await Bun.file(path).text();
|
|
46273
|
+
if (content)
|
|
46274
|
+
return content;
|
|
46275
|
+
} catch {
|
|
46276
|
+
continue;
|
|
46277
|
+
}
|
|
46278
|
+
}
|
|
46279
|
+
return null;
|
|
46280
|
+
}
|
|
46281
|
+
buildUserMessage(projectId, sessionNumber, result) {
|
|
46282
|
+
const today = new Date().toISOString().split("T")[0];
|
|
46283
|
+
return `## Curation Data
|
|
46284
|
+
|
|
46285
|
+
**Project ID:** ${projectId}
|
|
46286
|
+
**Session Number:** ${sessionNumber}
|
|
46287
|
+
**Date:** ${today}
|
|
46288
|
+
|
|
46289
|
+
### Session Summary
|
|
46290
|
+
${result.session_summary || "No summary provided"}
|
|
46291
|
+
|
|
46292
|
+
### Project Snapshot
|
|
46293
|
+
${result.project_snapshot ? `
|
|
46294
|
+
- Current Phase: ${result.project_snapshot.current_phase || "N/A"}
|
|
46295
|
+
- Recent Achievements: ${result.project_snapshot.recent_achievements?.join(", ") || "None"}
|
|
46296
|
+
- Active Challenges: ${result.project_snapshot.active_challenges?.join(", ") || "None"}
|
|
46297
|
+
- Next Steps: ${result.project_snapshot.next_steps?.join(", ") || "None"}
|
|
46298
|
+
` : "No snapshot provided"}
|
|
46299
|
+
|
|
46300
|
+
### New Memories (${result.memories.length})
|
|
46301
|
+
${result.memories.map((m2, i2) => `
|
|
46302
|
+
#### Memory ${i2 + 1}
|
|
46303
|
+
- **Content:** ${m2.content}
|
|
46304
|
+
- **Type:** ${m2.context_type}
|
|
46305
|
+
- **Scope:** ${m2.scope || "project"}
|
|
46306
|
+
- **Domain:** ${m2.domain || "N/A"}
|
|
46307
|
+
- **Importance:** ${m2.importance_weight}
|
|
46308
|
+
- **Tags:** ${m2.semantic_tags?.join(", ") || "None"}
|
|
46309
|
+
`).join(`
|
|
46310
|
+
`)}
|
|
46311
|
+
|
|
46312
|
+
---
|
|
46313
|
+
|
|
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.`;
|
|
46315
|
+
}
|
|
46316
|
+
parseManagementResponse(responseJson) {
|
|
46317
|
+
try {
|
|
46318
|
+
const cliOutput = JSON.parse(responseJson);
|
|
46319
|
+
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
|
+
};
|
|
46329
|
+
}
|
|
46330
|
+
const resultText = typeof cliOutput.result === "string" ? cliOutput.result : "";
|
|
46331
|
+
const supersededMatch = resultText.match(/superseded[:\s]+(\d+)/i);
|
|
46332
|
+
const resolvedMatch = resultText.match(/resolved[:\s]+(\d+)/i);
|
|
46333
|
+
const linkedMatch = resultText.match(/linked[:\s]+(\d+)/i);
|
|
46334
|
+
const primerUpdated = /primer.*updated|updated.*primer/i.test(resultText);
|
|
46335
|
+
return {
|
|
46336
|
+
success: true,
|
|
46337
|
+
superseded: supersededMatch ? parseInt(supersededMatch[1]) : 0,
|
|
46338
|
+
resolved: resolvedMatch ? parseInt(resolvedMatch[1]) : 0,
|
|
46339
|
+
linked: linkedMatch ? parseInt(linkedMatch[1]) : 0,
|
|
46340
|
+
primerUpdated,
|
|
46341
|
+
summary: resultText.slice(0, 500)
|
|
46342
|
+
};
|
|
46343
|
+
} catch {
|
|
46344
|
+
return {
|
|
46345
|
+
success: false,
|
|
46346
|
+
superseded: 0,
|
|
46347
|
+
resolved: 0,
|
|
46348
|
+
linked: 0,
|
|
46349
|
+
primerUpdated: false,
|
|
46350
|
+
summary: "",
|
|
46351
|
+
error: "Failed to parse response"
|
|
46352
|
+
};
|
|
46353
|
+
}
|
|
46354
|
+
}
|
|
46355
|
+
async manageWithCLI(projectId, sessionNumber, result) {
|
|
46356
|
+
if (process.env.MEMORY_MANAGER_DISABLED === "1") {
|
|
46357
|
+
return {
|
|
46358
|
+
success: true,
|
|
46359
|
+
superseded: 0,
|
|
46360
|
+
resolved: 0,
|
|
46361
|
+
linked: 0,
|
|
46362
|
+
primerUpdated: false,
|
|
46363
|
+
summary: "Management agent disabled"
|
|
46364
|
+
};
|
|
46365
|
+
}
|
|
46366
|
+
if (result.memories.length === 0) {
|
|
46367
|
+
return {
|
|
46368
|
+
success: true,
|
|
46369
|
+
superseded: 0,
|
|
46370
|
+
resolved: 0,
|
|
46371
|
+
linked: 0,
|
|
46372
|
+
primerUpdated: false,
|
|
46373
|
+
summary: "No memories to process"
|
|
46374
|
+
};
|
|
46375
|
+
}
|
|
46376
|
+
const systemPrompt = await this.buildManagementPrompt();
|
|
46377
|
+
if (!systemPrompt) {
|
|
46378
|
+
return {
|
|
46379
|
+
success: false,
|
|
46380
|
+
superseded: 0,
|
|
46381
|
+
resolved: 0,
|
|
46382
|
+
linked: 0,
|
|
46383
|
+
primerUpdated: false,
|
|
46384
|
+
summary: "",
|
|
46385
|
+
error: "Management skill not found"
|
|
46386
|
+
};
|
|
46387
|
+
}
|
|
46388
|
+
const userMessage = this.buildUserMessage(projectId, sessionNumber, result);
|
|
46389
|
+
const args = [
|
|
46390
|
+
"-p",
|
|
46391
|
+
userMessage,
|
|
46392
|
+
"--append-system-prompt",
|
|
46393
|
+
systemPrompt,
|
|
46394
|
+
"--output-format",
|
|
46395
|
+
"json",
|
|
46396
|
+
"--max-turns",
|
|
46397
|
+
String(this._config.maxTurns)
|
|
46398
|
+
];
|
|
46399
|
+
const proc = Bun.spawn([this._config.cliCommand, ...args], {
|
|
46400
|
+
env: {
|
|
46401
|
+
...process.env,
|
|
46402
|
+
MEMORY_CURATOR_ACTIVE: "1"
|
|
46403
|
+
},
|
|
46404
|
+
stderr: "pipe"
|
|
46405
|
+
});
|
|
46406
|
+
const [stdout, stderr] = await Promise.all([
|
|
46407
|
+
new Response(proc.stdout).text(),
|
|
46408
|
+
new Response(proc.stderr).text()
|
|
46409
|
+
]);
|
|
46410
|
+
const exitCode = await proc.exited;
|
|
46411
|
+
if (exitCode !== 0) {
|
|
46412
|
+
return {
|
|
46413
|
+
success: false,
|
|
46414
|
+
superseded: 0,
|
|
46415
|
+
resolved: 0,
|
|
46416
|
+
linked: 0,
|
|
46417
|
+
primerUpdated: false,
|
|
46418
|
+
summary: "",
|
|
46419
|
+
error: stderr || `Exit code ${exitCode}`
|
|
46420
|
+
};
|
|
46421
|
+
}
|
|
46422
|
+
return this.parseManagementResponse(stdout);
|
|
46423
|
+
}
|
|
46424
|
+
}
|
|
46425
|
+
function createManager(config) {
|
|
46426
|
+
return new Manager(config);
|
|
46427
|
+
}
|
|
46428
|
+
|
|
45617
46429
|
// src/server/index.ts
|
|
45618
46430
|
async function createServer(config = {}) {
|
|
45619
46431
|
const {
|
|
45620
46432
|
port = 8765,
|
|
45621
46433
|
host = "localhost",
|
|
45622
46434
|
curator: curatorConfig,
|
|
46435
|
+
manager: managerConfig,
|
|
45623
46436
|
...engineConfig
|
|
45624
46437
|
} = config;
|
|
45625
46438
|
const embeddings = createEmbeddings();
|
|
@@ -45630,6 +46443,7 @@ async function createServer(config = {}) {
|
|
|
45630
46443
|
embedder: embeddings.createEmbedder()
|
|
45631
46444
|
});
|
|
45632
46445
|
const curator = createCurator(curatorConfig);
|
|
46446
|
+
const manager = createManager(managerConfig);
|
|
45633
46447
|
const server = Bun.serve({
|
|
45634
46448
|
port,
|
|
45635
46449
|
hostname: host,
|
|
@@ -45697,6 +46511,38 @@ async function createServer(config = {}) {
|
|
|
45697
46511
|
await engine.storeCurationResult(body.project_id, body.session_id, result, body.project_path);
|
|
45698
46512
|
logger.logCurationComplete(result.memories.length, result.session_summary);
|
|
45699
46513
|
logger.logCuratedMemories(result.memories);
|
|
46514
|
+
const sessionNumber = await engine.getSessionNumber(body.project_id, body.project_path);
|
|
46515
|
+
setImmediate(async () => {
|
|
46516
|
+
try {
|
|
46517
|
+
logger.logManagementStart(result.memories.length);
|
|
46518
|
+
const startTime = Date.now();
|
|
46519
|
+
const managementResult = await manager.manageWithCLI(body.project_id, sessionNumber, result);
|
|
46520
|
+
logger.logManagementComplete({
|
|
46521
|
+
success: managementResult.success,
|
|
46522
|
+
superseded: managementResult.superseded || undefined,
|
|
46523
|
+
resolved: managementResult.resolved || undefined,
|
|
46524
|
+
linked: managementResult.linked || undefined,
|
|
46525
|
+
primerUpdated: managementResult.primerUpdated,
|
|
46526
|
+
summary: managementResult.summary.slice(0, 100),
|
|
46527
|
+
error: managementResult.error
|
|
46528
|
+
});
|
|
46529
|
+
await engine.storeManagementLog({
|
|
46530
|
+
projectId: body.project_id,
|
|
46531
|
+
sessionNumber,
|
|
46532
|
+
memoriesProcessed: result.memories.length,
|
|
46533
|
+
supersededCount: managementResult.superseded,
|
|
46534
|
+
resolvedCount: managementResult.resolved,
|
|
46535
|
+
linkedCount: managementResult.linked,
|
|
46536
|
+
primerUpdated: managementResult.primerUpdated,
|
|
46537
|
+
success: managementResult.success,
|
|
46538
|
+
durationMs: Date.now() - startTime,
|
|
46539
|
+
summary: managementResult.summary,
|
|
46540
|
+
error: managementResult.error
|
|
46541
|
+
});
|
|
46542
|
+
} catch (error) {
|
|
46543
|
+
logger.error(`Management failed: ${error}`);
|
|
46544
|
+
}
|
|
46545
|
+
});
|
|
45700
46546
|
} else {
|
|
45701
46547
|
logger.logCurationComplete(0);
|
|
45702
46548
|
}
|
|
@@ -45730,6 +46576,7 @@ async function createServer(config = {}) {
|
|
|
45730
46576
|
server,
|
|
45731
46577
|
engine,
|
|
45732
46578
|
curator,
|
|
46579
|
+
manager,
|
|
45733
46580
|
embeddings,
|
|
45734
46581
|
stop: () => server.stop()
|
|
45735
46582
|
};
|
|
@@ -45739,12 +46586,14 @@ if (__require.main == __require.module) {
|
|
|
45739
46586
|
const host = process.env.MEMORY_HOST ?? "localhost";
|
|
45740
46587
|
const storageMode = process.env.MEMORY_STORAGE_MODE ?? "central";
|
|
45741
46588
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
45742
|
-
|
|
45743
|
-
|
|
45744
|
-
|
|
45745
|
-
|
|
45746
|
-
|
|
45747
|
-
|
|
46589
|
+
(async () => {
|
|
46590
|
+
await createServer({
|
|
46591
|
+
port,
|
|
46592
|
+
host,
|
|
46593
|
+
storageMode,
|
|
46594
|
+
curator: { apiKey }
|
|
46595
|
+
});
|
|
46596
|
+
})();
|
|
45748
46597
|
}
|
|
45749
46598
|
export {
|
|
45750
46599
|
createServer
|