@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/dist/index.mjs CHANGED
@@ -11964,7 +11964,60 @@ function createDatabase(options = {}) {
11964
11964
  import { homedir } from "os";
11965
11965
  import { join } from "path";
11966
11966
 
11967
+ // src/types/memory.ts
11968
+ var V2_DEFAULTS = {
11969
+ typeDefaults: {
11970
+ personal: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
11971
+ philosophy: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
11972
+ preference: { scope: "global", temporal_class: "long_term", fade_rate: 0.01 },
11973
+ breakthrough: { scope: "project", temporal_class: "eternal", fade_rate: 0 },
11974
+ decision: { scope: "project", temporal_class: "long_term", fade_rate: 0 },
11975
+ milestone: { scope: "project", temporal_class: "eternal", fade_rate: 0 },
11976
+ technical: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 },
11977
+ architectural: { scope: "project", temporal_class: "long_term", fade_rate: 0.01 },
11978
+ debugging: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 },
11979
+ unresolved: { scope: "project", temporal_class: "medium_term", fade_rate: 0.05 },
11980
+ todo: { scope: "project", temporal_class: "short_term", fade_rate: 0.1 },
11981
+ technical_state: { scope: "project", temporal_class: "short_term", fade_rate: 0.1 },
11982
+ workflow: { scope: "project", temporal_class: "long_term", fade_rate: 0.02 },
11983
+ project_context: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 }
11984
+ },
11985
+ fallback: {
11986
+ status: "active",
11987
+ scope: "project",
11988
+ temporal_class: "medium_term",
11989
+ fade_rate: 0.03,
11990
+ sessions_since_surfaced: 0,
11991
+ awaiting_implementation: false,
11992
+ awaiting_decision: false,
11993
+ exclude_from_retrieval: false
11994
+ }
11995
+ };
11996
+ var MEMORY_TYPE_EMOJI = {
11997
+ breakthrough: "\uD83D\uDCA1",
11998
+ decision: "⚖️",
11999
+ personal: "\uD83D\uDC9C",
12000
+ technical: "\uD83D\uDD27",
12001
+ technical_state: "\uD83D\uDCCD",
12002
+ unresolved: "❓",
12003
+ preference: "⚙️",
12004
+ workflow: "\uD83D\uDD04",
12005
+ architectural: "\uD83C\uDFD7️",
12006
+ debugging: "\uD83D\uDC1B",
12007
+ philosophy: "\uD83C\uDF00",
12008
+ todo: "\uD83C\uDFAF",
12009
+ implementation: "⚡",
12010
+ problem_solution: "✅",
12011
+ project_context: "\uD83D\uDCE6",
12012
+ milestone: "\uD83C\uDFC6",
12013
+ general: "\uD83D\uDCDD"
12014
+ };
12015
+ function getMemoryEmoji(contextType) {
12016
+ return MEMORY_TYPE_EMOJI[contextType.toLowerCase()] ?? "\uD83D\uDCDD";
12017
+ }
12018
+
11967
12019
  // src/types/schema.ts
12020
+ var MEMORY_SCHEMA_VERSION = 2;
11968
12021
  var memorySchema = {
11969
12022
  content: "string",
11970
12023
  reasoning: "string",
@@ -11981,7 +12034,34 @@ var memorySchema = {
11981
12034
  question_types: "string[]",
11982
12035
  session_id: "string",
11983
12036
  project_id: "string",
11984
- embedding: "vector:384"
12037
+ embedding: "vector:384",
12038
+ status: "string",
12039
+ scope: "string",
12040
+ session_created: "number",
12041
+ session_updated: "number",
12042
+ last_surfaced: "number",
12043
+ sessions_since_surfaced: "number",
12044
+ temporal_class: "string",
12045
+ fade_rate: "number",
12046
+ expires_after_sessions: "number",
12047
+ domain: "string",
12048
+ feature: "string",
12049
+ component: "string",
12050
+ supersedes: "string",
12051
+ superseded_by: "string",
12052
+ related_to: "string[]",
12053
+ resolves: "string[]",
12054
+ resolved_by: "string",
12055
+ parent_id: "string",
12056
+ child_ids: "string[]",
12057
+ awaiting_implementation: "boolean",
12058
+ awaiting_decision: "boolean",
12059
+ blocked_by: "string",
12060
+ blocks: "string[]",
12061
+ related_files: "string[]",
12062
+ retrieval_weight: "number",
12063
+ exclude_from_retrieval: "boolean",
12064
+ schema_version: "number"
11985
12065
  };
11986
12066
  var sessionSummarySchema = {
11987
12067
  session_id: "string",
@@ -12004,17 +12084,211 @@ var sessionSchema = {
12004
12084
  last_active: "timestamp",
12005
12085
  metadata: "string"
12006
12086
  };
12087
+ var managementLogSchema = {
12088
+ project_id: "string",
12089
+ session_number: "number",
12090
+ memories_processed: "number",
12091
+ superseded_count: "number",
12092
+ resolved_count: "number",
12093
+ linked_count: "number",
12094
+ primer_updated: "boolean",
12095
+ success: "boolean",
12096
+ duration_ms: "number",
12097
+ summary: "string",
12098
+ error: "string",
12099
+ details: "string"
12100
+ };
12007
12101
 
12008
12102
  // src/core/store.ts
12103
+ var PERSONAL_PRIMER_ID = "personal-primer";
12104
+ var DEFAULT_GLOBAL_PATH = join(homedir(), ".local", "share", "memory", "global");
12105
+
12009
12106
  class MemoryStore {
12010
12107
  _config;
12011
12108
  _projects = new Map;
12109
+ _global = null;
12012
12110
  constructor(config = {}) {
12013
12111
  this._config = {
12014
12112
  basePath: config.basePath ?? join(homedir(), ".local", "share", "memory"),
12113
+ globalPath: config.globalPath ?? DEFAULT_GLOBAL_PATH,
12015
12114
  watchFiles: config.watchFiles ?? false
12016
12115
  };
12017
12116
  }
12117
+ async getGlobal() {
12118
+ if (this._global) {
12119
+ return this._global;
12120
+ }
12121
+ const globalPath = this._config.globalPath;
12122
+ console.log(`\uD83C\uDF10 [DEBUG] Creating global database at ${globalPath}`);
12123
+ const db = createDatabase({
12124
+ name: "global",
12125
+ basePath: globalPath
12126
+ });
12127
+ const memories = db.collection("memories", {
12128
+ schema: memorySchema,
12129
+ contentColumn: "content",
12130
+ autoSave: true,
12131
+ watchFiles: this._config.watchFiles
12132
+ });
12133
+ const managementLogs = db.collection("management-logs", {
12134
+ schema: managementLogSchema,
12135
+ contentColumn: "summary",
12136
+ autoSave: true,
12137
+ watchFiles: this._config.watchFiles
12138
+ });
12139
+ await Promise.all([memories.load(), managementLogs.load()]);
12140
+ this._global = { db, memories, managementLogs };
12141
+ return this._global;
12142
+ }
12143
+ async getGlobalMemories() {
12144
+ const { memories } = await this.getGlobal();
12145
+ return memories.all().map((record) => ({
12146
+ id: record.id,
12147
+ content: record.content,
12148
+ reasoning: record.reasoning,
12149
+ importance_weight: record.importance_weight,
12150
+ confidence_score: record.confidence_score,
12151
+ context_type: record.context_type,
12152
+ temporal_relevance: record.temporal_relevance,
12153
+ knowledge_domain: record.knowledge_domain,
12154
+ emotional_resonance: record.emotional_resonance,
12155
+ action_required: record.action_required,
12156
+ problem_solution_pair: record.problem_solution_pair,
12157
+ semantic_tags: record.semantic_tags,
12158
+ trigger_phrases: record.trigger_phrases,
12159
+ question_types: record.question_types,
12160
+ session_id: record.session_id,
12161
+ project_id: "global",
12162
+ embedding: record.embedding ?? undefined,
12163
+ created_at: record.created,
12164
+ updated_at: record.updated,
12165
+ stale: record.stale
12166
+ }));
12167
+ }
12168
+ async storeGlobalMemory(sessionId, memory, embedding, sessionNumber) {
12169
+ const { memories } = await this.getGlobal();
12170
+ const contextType = memory.context_type ?? "personal";
12171
+ const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.personal;
12172
+ const id = memories.insert({
12173
+ content: memory.content,
12174
+ reasoning: memory.reasoning,
12175
+ importance_weight: memory.importance_weight,
12176
+ confidence_score: memory.confidence_score,
12177
+ context_type: memory.context_type,
12178
+ temporal_relevance: memory.temporal_relevance,
12179
+ knowledge_domain: memory.knowledge_domain,
12180
+ emotional_resonance: memory.emotional_resonance,
12181
+ action_required: memory.action_required,
12182
+ problem_solution_pair: memory.problem_solution_pair,
12183
+ semantic_tags: memory.semantic_tags,
12184
+ trigger_phrases: memory.trigger_phrases,
12185
+ question_types: memory.question_types,
12186
+ session_id: sessionId,
12187
+ project_id: "global",
12188
+ embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null,
12189
+ status: V2_DEFAULTS.fallback.status,
12190
+ scope: "global",
12191
+ temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? "eternal",
12192
+ fade_rate: typeDefaults?.fade_rate ?? 0,
12193
+ session_created: sessionNumber ?? 0,
12194
+ session_updated: sessionNumber ?? 0,
12195
+ sessions_since_surfaced: 0,
12196
+ domain: memory.domain ?? null,
12197
+ feature: memory.feature ?? null,
12198
+ related_files: memory.related_files ?? [],
12199
+ awaiting_implementation: memory.awaiting_implementation ?? false,
12200
+ awaiting_decision: memory.awaiting_decision ?? false,
12201
+ retrieval_weight: memory.importance_weight,
12202
+ exclude_from_retrieval: false,
12203
+ schema_version: MEMORY_SCHEMA_VERSION,
12204
+ supersedes: null,
12205
+ superseded_by: null,
12206
+ related_to: [],
12207
+ resolves: [],
12208
+ resolved_by: null,
12209
+ parent_id: null,
12210
+ child_ids: [],
12211
+ blocked_by: null,
12212
+ blocks: []
12213
+ });
12214
+ return id;
12215
+ }
12216
+ async getPersonalPrimer() {
12217
+ const { memories } = await this.getGlobal();
12218
+ const primer = memories.get(PERSONAL_PRIMER_ID);
12219
+ if (!primer) {
12220
+ return null;
12221
+ }
12222
+ return {
12223
+ content: primer.content,
12224
+ updated: primer.updated
12225
+ };
12226
+ }
12227
+ async setPersonalPrimer(content) {
12228
+ const { memories } = await this.getGlobal();
12229
+ const existing = memories.get(PERSONAL_PRIMER_ID);
12230
+ if (existing) {
12231
+ memories.update(PERSONAL_PRIMER_ID, { content });
12232
+ } else {
12233
+ memories.insert({
12234
+ id: PERSONAL_PRIMER_ID,
12235
+ content,
12236
+ reasoning: "Personal relationship context injected at session start",
12237
+ importance_weight: 1,
12238
+ confidence_score: 1,
12239
+ context_type: "personal",
12240
+ temporal_relevance: "persistent",
12241
+ knowledge_domain: "personal",
12242
+ emotional_resonance: "neutral",
12243
+ action_required: false,
12244
+ problem_solution_pair: false,
12245
+ semantic_tags: ["personal", "primer", "relationship"],
12246
+ trigger_phrases: [],
12247
+ question_types: [],
12248
+ session_id: "system",
12249
+ project_id: "global",
12250
+ embedding: null
12251
+ });
12252
+ }
12253
+ }
12254
+ isPersonalMemoriesEnabled() {
12255
+ return true;
12256
+ }
12257
+ async storeManagementLog(entry) {
12258
+ const { managementLogs } = await this.getGlobal();
12259
+ const id = managementLogs.insert({
12260
+ project_id: entry.projectId,
12261
+ session_number: entry.sessionNumber,
12262
+ memories_processed: entry.memoriesProcessed,
12263
+ superseded_count: entry.supersededCount,
12264
+ resolved_count: entry.resolvedCount,
12265
+ linked_count: entry.linkedCount,
12266
+ primer_updated: entry.primerUpdated,
12267
+ success: entry.success,
12268
+ duration_ms: entry.durationMs,
12269
+ summary: entry.summary,
12270
+ error: entry.error ?? "",
12271
+ details: entry.details ? JSON.stringify(entry.details) : ""
12272
+ });
12273
+ return id;
12274
+ }
12275
+ async getManagementLogs(limit = 10) {
12276
+ const { managementLogs } = await this.getGlobal();
12277
+ return managementLogs.all().sort((a, b) => b.created - a.created).slice(0, limit).map((record) => ({
12278
+ id: record.id,
12279
+ projectId: record.project_id,
12280
+ sessionNumber: record.session_number,
12281
+ memoriesProcessed: record.memories_processed,
12282
+ supersededCount: record.superseded_count,
12283
+ resolvedCount: record.resolved_count,
12284
+ linkedCount: record.linked_count,
12285
+ primerUpdated: record.primer_updated,
12286
+ success: record.success,
12287
+ durationMs: record.duration_ms,
12288
+ summary: record.summary,
12289
+ createdAt: record.created
12290
+ }));
12291
+ }
12018
12292
  async getProject(projectId) {
12019
12293
  if (this._projects.has(projectId)) {
12020
12294
  console.log(`\uD83D\uDD04 [DEBUG] Returning cached databases for ${projectId}`);
@@ -12059,8 +12333,10 @@ class MemoryStore {
12059
12333
  this._projects.set(projectId, projectDB);
12060
12334
  return projectDB;
12061
12335
  }
12062
- async storeMemory(projectId, sessionId, memory, embedding) {
12336
+ async storeMemory(projectId, sessionId, memory, embedding, sessionNumber) {
12063
12337
  const { memories } = await this.getProject(projectId);
12338
+ const contextType = memory.context_type ?? "general";
12339
+ const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.technical;
12064
12340
  const id = memories.insert({
12065
12341
  content: memory.content,
12066
12342
  reasoning: memory.reasoning,
@@ -12077,7 +12353,31 @@ class MemoryStore {
12077
12353
  question_types: memory.question_types,
12078
12354
  session_id: sessionId,
12079
12355
  project_id: projectId,
12080
- embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null
12356
+ embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null,
12357
+ status: V2_DEFAULTS.fallback.status,
12358
+ scope: memory.scope ?? typeDefaults?.scope ?? V2_DEFAULTS.fallback.scope,
12359
+ temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? V2_DEFAULTS.fallback.temporal_class,
12360
+ fade_rate: typeDefaults?.fade_rate ?? V2_DEFAULTS.fallback.fade_rate,
12361
+ session_created: sessionNumber ?? 0,
12362
+ session_updated: sessionNumber ?? 0,
12363
+ sessions_since_surfaced: 0,
12364
+ domain: memory.domain ?? null,
12365
+ feature: memory.feature ?? null,
12366
+ related_files: memory.related_files ?? [],
12367
+ awaiting_implementation: memory.awaiting_implementation ?? false,
12368
+ awaiting_decision: memory.awaiting_decision ?? false,
12369
+ retrieval_weight: memory.importance_weight,
12370
+ exclude_from_retrieval: false,
12371
+ schema_version: MEMORY_SCHEMA_VERSION,
12372
+ supersedes: null,
12373
+ superseded_by: null,
12374
+ related_to: [],
12375
+ resolves: [],
12376
+ resolved_by: null,
12377
+ parent_id: null,
12378
+ child_ids: [],
12379
+ blocked_by: null,
12380
+ blocks: []
12081
12381
  });
12082
12382
  return id;
12083
12383
  }
@@ -12297,6 +12597,10 @@ class MemoryStore {
12297
12597
  projectDB.db.close();
12298
12598
  }
12299
12599
  this._projects.clear();
12600
+ if (this._global) {
12601
+ this._global.db.close();
12602
+ this._global = null;
12603
+ }
12300
12604
  }
12301
12605
  }
12302
12606
  function createStore(config) {
@@ -12457,17 +12761,55 @@ var logger = {
12457
12761
  }
12458
12762
  console.log();
12459
12763
  },
12764
+ logManagementStart(memoriesCount) {
12765
+ console.log(`${timestamp()} ${style("blue", "\uD83D\uDD27")} ${style("bold", "MANAGEMENT AGENT")}`);
12766
+ console.log(` ${style("dim", "processing:")} ${memoriesCount} new memories`);
12767
+ },
12768
+ logManagementComplete(result) {
12769
+ if (result.success) {
12770
+ console.log(` ${style("green", sym.check)} ${style("bold", "Completed")}`);
12771
+ const stats = [];
12772
+ if (result.superseded && result.superseded > 0) {
12773
+ stats.push(`${result.superseded} superseded`);
12774
+ }
12775
+ if (result.resolved && result.resolved > 0) {
12776
+ stats.push(`${result.resolved} resolved`);
12777
+ }
12778
+ if (result.linked && result.linked > 0) {
12779
+ stats.push(`${result.linked} linked`);
12780
+ }
12781
+ if (result.primerUpdated) {
12782
+ stats.push("primer updated");
12783
+ }
12784
+ if (stats.length > 0) {
12785
+ console.log(` ${style("dim", "changes:")} ${stats.join(style("dim", ", "))}`);
12786
+ } else {
12787
+ console.log(` ${style("dim", "changes:")} none (memories are current)`);
12788
+ }
12789
+ if (result.summary) {
12790
+ const shortSummary = result.summary.length > 60 ? result.summary.slice(0, 60) + "..." : result.summary;
12791
+ console.log(` ${style("dim", "summary:")} ${shortSummary}`);
12792
+ }
12793
+ } else {
12794
+ console.log(` ${style("yellow", sym.warning)} ${style("bold", "Failed")}`);
12795
+ if (result.error) {
12796
+ console.log(` ${style("dim", "error:")} ${result.error.slice(0, 80)}`);
12797
+ }
12798
+ }
12799
+ console.log();
12800
+ },
12460
12801
  logRetrievalScoring(params) {
12461
- const { totalMemories, currentMessage, alreadyInjected, mustIncludeCount, remainingSlots, finalCount, selectedMemories } = params;
12802
+ const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, selectedMemories } = params;
12462
12803
  console.log();
12463
- console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "TWO-STAGE MEMORY FILTERING")}`);
12464
- console.log(` ${style("dim", "candidates:")} ${totalMemories} memories`);
12804
+ console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "MULTI-DIMENSIONAL RETRIEVAL")}`);
12805
+ console.log(` ${style("dim", "total:")} ${totalMemories} memories`);
12806
+ console.log(` ${style("dim", "pre-filtered:")} ${preFiltered} (inactive/excluded/scope)`);
12465
12807
  console.log(` ${style("dim", "already injected:")} ${alreadyInjected}`);
12466
12808
  const msgPreview = currentMessage.length > 60 ? currentMessage.slice(0, 60) + "..." : currentMessage;
12467
- console.log(` ${style("dim", "trigger:")} "${msgPreview}"`);
12809
+ console.log(` ${style("dim", "message:")} "${msgPreview}"`);
12468
12810
  console.log();
12469
- console.log(` ${style("cyan", "Stage 1:")} ${mustIncludeCount} must-include (critical/action-required)`);
12470
- console.log(` ${style("cyan", "Stage 2:")} ${remainingSlots} slots for scored selection`);
12811
+ console.log(` ${style("cyan", "Global:")} ${globalCount} candidates → max 2 selected`);
12812
+ console.log(` ${style("cyan", "Project:")} ${projectCount} candidates`);
12471
12813
  console.log(` ${style("green", "Final:")} ${finalCount} memories selected`);
12472
12814
  console.log();
12473
12815
  if (selectedMemories.length === 0) {
@@ -12482,17 +12824,18 @@ var logger = {
12482
12824
  const num = style("dim", `${i + 1}.`);
12483
12825
  const score = style("green", `${(m.score * 100).toFixed(0)}%`);
12484
12826
  const relevance = style("cyan", `rel:${(m.relevance_score * 100).toFixed(0)}%`);
12827
+ const corr = style("magenta", `corr:${(m.corroboration_score * 100).toFixed(0)}%`);
12485
12828
  const type = style("yellow", m.context_type.toUpperCase());
12486
- console.log(` ${num} [${score} ${relevance}] ${type}`);
12829
+ const scope = m.isGlobal ? style("blue", "[G]") : "";
12830
+ console.log(` ${num} [${score} ${relevance} ${corr}] ${type} ${scope}`);
12487
12831
  const preview = m.content.length > 60 ? m.content.slice(0, 60) + style("dim", "...") : m.content;
12488
12832
  console.log(` ${style("white", preview)}`);
12489
- const components = Object.entries(m.components).sort((a, b) => b[1] - a[1]).slice(0, 3).filter(([, v]) => v > 0.1).map(([k, v]) => `${k}:${(v * 100).toFixed(0)}%`).join(", ");
12833
+ 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(", ");
12490
12834
  if (components) {
12491
12835
  console.log(` ${style("dim", "scores:")} ${components}`);
12492
12836
  }
12493
- if (m.semantic_tags?.length) {
12494
- const tags = m.semantic_tags.slice(0, 3).join(", ");
12495
- console.log(` ${style("dim", "tags:")} ${tags}`);
12837
+ if (m.reasoning) {
12838
+ console.log(` ${style("dim", m.reasoning)}`);
12496
12839
  }
12497
12840
  console.log();
12498
12841
  });
@@ -12500,125 +12843,356 @@ var logger = {
12500
12843
  };
12501
12844
 
12502
12845
  // src/core/retrieval.ts
12846
+ var TYPE_KEYWORDS = {
12847
+ debug: ["bug", "error", "fix", "broken", "crash", "fails", "exception", "stack trace", "debugging"],
12848
+ unresolved: ["issue", "problem", "stuck", "blocked", "help", "question", "unsure", "unclear"],
12849
+ decision: ["decide", "choice", "option", "should we", "which", "alternative", "tradeoff"],
12850
+ architecture: ["structure", "design", "pattern", "approach", "system", "layer", "architecture"],
12851
+ breakthrough: ["discovered", "realized", "insight", "found that", "aha", "finally", "key insight"],
12852
+ todo: ["need to", "should", "must", "will", "later", "next", "todo"],
12853
+ personal: ["family", "children", "friend", "relationship", "feel", "appreciate", "thank"],
12854
+ philosophy: ["meaning", "consciousness", "existence", "purpose", "believe", "philosophy"],
12855
+ technical: ["implement", "code", "function", "class", "module", "api", "interface"]
12856
+ };
12857
+ var TEMPORAL_CLASS_SCORES = {
12858
+ eternal: 1,
12859
+ long_term: 0.9,
12860
+ medium_term: 0.7,
12861
+ short_term: 0.5,
12862
+ ephemeral: 0.3
12863
+ };
12864
+
12503
12865
  class SmartVectorRetrieval {
12504
- retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0) {
12866
+ _extractSignificantWords(text) {
12867
+ const stopWords = new Set([
12868
+ "the",
12869
+ "is",
12870
+ "are",
12871
+ "was",
12872
+ "were",
12873
+ "to",
12874
+ "a",
12875
+ "an",
12876
+ "and",
12877
+ "or",
12878
+ "but",
12879
+ "in",
12880
+ "on",
12881
+ "at",
12882
+ "for",
12883
+ "with",
12884
+ "about",
12885
+ "when",
12886
+ "how",
12887
+ "what",
12888
+ "why",
12889
+ "where",
12890
+ "this",
12891
+ "that",
12892
+ "it",
12893
+ "of",
12894
+ "be",
12895
+ "have",
12896
+ "do",
12897
+ "does",
12898
+ "did",
12899
+ "will",
12900
+ "would",
12901
+ "could",
12902
+ "should",
12903
+ "can",
12904
+ "may",
12905
+ "might",
12906
+ "must",
12907
+ "shall",
12908
+ "has",
12909
+ "had",
12910
+ "been",
12911
+ "being",
12912
+ "i",
12913
+ "you",
12914
+ "we",
12915
+ "they",
12916
+ "he",
12917
+ "she",
12918
+ "my",
12919
+ "your",
12920
+ "our",
12921
+ "its",
12922
+ "his",
12923
+ "her",
12924
+ "their",
12925
+ "if",
12926
+ "then",
12927
+ "else",
12928
+ "so",
12929
+ "as",
12930
+ "from",
12931
+ "by",
12932
+ "into",
12933
+ "through",
12934
+ "during",
12935
+ "before",
12936
+ "after",
12937
+ "above",
12938
+ "below",
12939
+ "up",
12940
+ "down",
12941
+ "out",
12942
+ "off",
12943
+ "over",
12944
+ "under",
12945
+ "again",
12946
+ "further",
12947
+ "once",
12948
+ "here",
12949
+ "there",
12950
+ "all",
12951
+ "each",
12952
+ "few",
12953
+ "more",
12954
+ "most",
12955
+ "other",
12956
+ "some",
12957
+ "such",
12958
+ "no",
12959
+ "nor",
12960
+ "not",
12961
+ "only",
12962
+ "own",
12963
+ "same",
12964
+ "than",
12965
+ "too",
12966
+ "very",
12967
+ "just",
12968
+ "also",
12969
+ "now",
12970
+ "back",
12971
+ "get",
12972
+ "got",
12973
+ "go",
12974
+ "going",
12975
+ "gone",
12976
+ "come",
12977
+ "came",
12978
+ "let",
12979
+ "lets",
12980
+ "hey",
12981
+ "hi",
12982
+ "hello",
12983
+ "ok",
12984
+ "okay"
12985
+ ]);
12986
+ const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
12987
+ return new Set(words);
12988
+ }
12989
+ _detectContextTypes(message) {
12990
+ const messageLower = message.toLowerCase();
12991
+ const detected = new Set;
12992
+ for (const [type, keywords] of Object.entries(TYPE_KEYWORDS)) {
12993
+ for (const keyword of keywords) {
12994
+ if (messageLower.includes(keyword)) {
12995
+ detected.add(type);
12996
+ break;
12997
+ }
12998
+ }
12999
+ }
13000
+ return detected;
13001
+ }
13002
+ _preFilter(memories, currentProjectId, messageLower) {
13003
+ return memories.filter((memory) => {
13004
+ if (memory.status && memory.status !== "active") {
13005
+ return false;
13006
+ }
13007
+ if (memory.exclude_from_retrieval === true) {
13008
+ return false;
13009
+ }
13010
+ if (memory.superseded_by) {
13011
+ return false;
13012
+ }
13013
+ const isGlobal = memory.scope === "global" || memory.project_id === "global";
13014
+ if (!isGlobal && memory.project_id !== currentProjectId) {
13015
+ return false;
13016
+ }
13017
+ if (memory.anti_triggers?.length) {
13018
+ for (const antiTrigger of memory.anti_triggers) {
13019
+ if (messageLower.includes(antiTrigger.toLowerCase())) {
13020
+ return false;
13021
+ }
13022
+ }
13023
+ }
13024
+ return true;
13025
+ });
13026
+ }
13027
+ _calculateCorroboration(memory, messageWords, detectedTypes, messageLower) {
13028
+ const signals = [];
13029
+ let score = 0;
13030
+ let reasoningMatch = 0;
13031
+ const tagOverlap = (memory.semantic_tags ?? []).filter((tag) => messageWords.has(tag.toLowerCase()) || messageLower.includes(tag.toLowerCase()));
13032
+ if (tagOverlap.length > 0) {
13033
+ score += Math.min(0.4, tagOverlap.length * 0.15);
13034
+ signals.push("tags:" + tagOverlap.join(","));
13035
+ }
13036
+ if (memory.reasoning) {
13037
+ const reasoningWords = this._extractSignificantWords(memory.reasoning);
13038
+ const reasoningOverlap = [...messageWords].filter((w) => reasoningWords.has(w));
13039
+ if (reasoningOverlap.length > 0) {
13040
+ reasoningMatch = Math.min(0.4, reasoningOverlap.length * 0.1);
13041
+ score += reasoningMatch;
13042
+ signals.push("reasoning:" + reasoningOverlap.slice(0, 3).join(","));
13043
+ }
13044
+ }
13045
+ if (memory.domain) {
13046
+ const domainLower = memory.domain.toLowerCase();
13047
+ if (messageLower.includes(domainLower) || messageWords.has(domainLower)) {
13048
+ score += 0.3;
13049
+ signals.push("domain:" + memory.domain);
13050
+ }
13051
+ }
13052
+ if (memory.knowledge_domain) {
13053
+ const kdLower = memory.knowledge_domain.toLowerCase();
13054
+ if (messageLower.includes(kdLower) || messageWords.has(kdLower)) {
13055
+ score += 0.2;
13056
+ signals.push("knowledge:" + memory.knowledge_domain);
13057
+ }
13058
+ }
13059
+ if (memory.context_type && detectedTypes.has(memory.context_type)) {
13060
+ score += 0.12;
13061
+ signals.push("type:" + memory.context_type);
13062
+ }
13063
+ const triggerMatch = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
13064
+ if (triggerMatch > 0.3) {
13065
+ score += Math.min(0.3, triggerMatch * 0.4);
13066
+ signals.push("trigger:" + triggerMatch.toFixed(2));
13067
+ }
13068
+ if (memory.feature) {
13069
+ const featureLower = memory.feature.toLowerCase();
13070
+ if (messageLower.includes(featureLower) || messageWords.has(featureLower)) {
13071
+ score += 0.2;
13072
+ signals.push("feature:" + memory.feature);
13073
+ }
13074
+ }
13075
+ if (memory.related_files?.length) {
13076
+ for (const file of memory.related_files) {
13077
+ const filename = file.split("/").pop()?.toLowerCase() ?? "";
13078
+ if (filename && messageLower.includes(filename)) {
13079
+ score += 0.25;
13080
+ signals.push("file:" + filename);
13081
+ break;
13082
+ }
13083
+ }
13084
+ }
13085
+ return { score: Math.min(1, score), signals, reasoningMatch };
13086
+ }
13087
+ retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0, maxGlobalMemories = 2) {
12505
13088
  if (!allMemories.length) {
12506
13089
  return [];
12507
13090
  }
13091
+ const messageLower = currentMessage.toLowerCase();
13092
+ const messageWords = this._extractSignificantWords(currentMessage);
13093
+ const detectedTypes = this._detectContextTypes(currentMessage);
13094
+ const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
13095
+ if (!candidates.length) {
13096
+ return [];
13097
+ }
12508
13098
  const scoredMemories = [];
12509
- for (const memory of allMemories) {
13099
+ for (const memory of candidates) {
13100
+ const isGlobal = memory.scope === "global" || memory.project_id === "global";
12510
13101
  const vectorScore = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
12511
- const importance = memory.importance_weight ?? 0.5;
12512
- const temporalScore = this._scoreTemporalRelevance(memory.temporal_relevance ?? "persistent", sessionContext);
13102
+ const { score: corroborationScore, signals: corroborationSignals, reasoningMatch } = this._calculateCorroboration(memory, messageWords, detectedTypes, messageLower);
13103
+ const retrievalWeight = memory.retrieval_weight ?? memory.importance_weight ?? 0.5;
13104
+ const temporalScore = memory.temporal_class ? TEMPORAL_CLASS_SCORES[memory.temporal_class] ?? 0.7 : this._scoreTemporalRelevance(memory.temporal_relevance ?? "persistent");
12513
13105
  const contextScore = this._scoreContextAlignment(currentMessage, memory.context_type ?? "general");
12514
- const actionBoost = memory.action_required ? 0.3 : 0;
12515
13106
  const tagScore = this._scoreSemanticTags(currentMessage, memory.semantic_tags ?? []);
12516
- const triggerScore = this._scoreTriggerPhrases(currentMessage, memory.trigger_phrases ?? []);
13107
+ const triggerScore = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
13108
+ const domainScore = this._scoreDomain(messageWords, messageLower, memory);
12517
13109
  const questionScore = this._scoreQuestionTypes(currentMessage, memory.question_types ?? []);
12518
13110
  const emotionScore = this._scoreEmotionalContext(currentMessage, memory.emotional_resonance ?? "");
12519
13111
  const problemScore = this._scoreProblemSolution(currentMessage, memory.problem_solution_pair ?? false);
12520
- const confidenceScore = memory.confidence_score ?? 0.8;
12521
- const relevanceScore = triggerScore * 0.1 + vectorScore * 0.1 + tagScore * 0.05 + questionScore * 0.05;
12522
- const valueScore = importance * 0.2 + temporalScore * 0.1 + contextScore * 0.1 + confidenceScore * 0.1 + emotionScore * 0.1 + problemScore * 0.05 + actionBoost * 0.05;
13112
+ const actionBoost = memory.action_required ? 0.15 : 0;
13113
+ const relevanceScore = vectorScore * 0.1 + corroborationScore * 0.14 + tagScore * 0.04 + triggerScore * 0.02;
13114
+ 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;
12523
13115
  const finalScore = valueScore + relevanceScore;
12524
13116
  if (relevanceScore < 0.05 || finalScore < 0.3) {
12525
13117
  continue;
12526
13118
  }
12527
13119
  const components = {
12528
- trigger: triggerScore,
12529
13120
  vector: vectorScore,
12530
- importance,
13121
+ corroboration: corroborationScore,
13122
+ reasoning_match: reasoningMatch,
13123
+ retrieval_weight: retrievalWeight,
12531
13124
  temporal: temporalScore,
12532
13125
  context: contextScore,
12533
13126
  tags: tagScore,
13127
+ trigger: triggerScore,
13128
+ domain: domainScore,
12534
13129
  question: questionScore,
12535
13130
  emotion: emotionScore,
12536
13131
  problem: problemScore,
12537
13132
  action: actionBoost
12538
13133
  };
12539
- const reasoning = this._generateSelectionReasoning(components);
13134
+ const reasoning = this._generateSelectionReasoning(components, corroborationSignals);
12540
13135
  scoredMemories.push({
12541
13136
  memory,
12542
13137
  score: finalScore,
12543
13138
  relevance_score: relevanceScore,
12544
13139
  value_score: valueScore,
13140
+ corroboration_score: corroborationScore,
12545
13141
  reasoning,
12546
- components
13142
+ components,
13143
+ isGlobal
12547
13144
  });
12548
13145
  }
12549
13146
  scoredMemories.sort((a, b) => b.score - a.score);
12550
13147
  const selected = [];
12551
13148
  const selectedIds = new Set;
12552
- const mustInclude = scoredMemories.filter((m) => m.score > 0.8 || m.components.importance > 0.9 || m.components.action > 0 || Object.values(m.components).some((v) => v > 0.9));
12553
- for (const item of mustInclude.slice(0, maxMemories)) {
13149
+ const globalMemories = scoredMemories.filter((m) => m.isGlobal);
13150
+ const projectMemories = scoredMemories.filter((m) => !m.isGlobal);
13151
+ const globalSorted = globalMemories.sort((a, b) => {
13152
+ const aIsPersonal = a.memory.context_type === "personal" || a.memory.context_type === "philosophy";
13153
+ const bIsPersonal = b.memory.context_type === "personal" || b.memory.context_type === "philosophy";
13154
+ if (aIsPersonal !== bIsPersonal) {
13155
+ return aIsPersonal ? 1 : -1;
13156
+ }
13157
+ return b.score - a.score;
13158
+ });
13159
+ for (const item of globalSorted.slice(0, maxGlobalMemories)) {
12554
13160
  if (!selectedIds.has(item.memory.id)) {
12555
13161
  selected.push(item);
12556
13162
  selectedIds.add(item.memory.id);
12557
13163
  }
12558
13164
  }
12559
- const remainingSlots = Math.max(maxMemories - selected.length, 0);
12560
- if (remainingSlots > 0 && selected.length < maxMemories * 1.5) {
12561
- const typesIncluded = new Set;
12562
- for (const item of scoredMemories) {
12563
- if (selected.length >= maxMemories * 1.5)
12564
- break;
12565
- if (selectedIds.has(item.memory.id))
12566
- continue;
12567
- const memoryType = item.memory.context_type ?? "general";
12568
- if (item.score > 0.5 || !typesIncluded.has(memoryType) || item.memory.emotional_resonance) {
12569
- selected.push(item);
12570
- selectedIds.add(item.memory.id);
12571
- typesIncluded.add(memoryType);
12572
- }
12573
- }
12574
- }
12575
- if (selected.length < maxMemories * 2) {
12576
- const currentTags = new Set;
12577
- const currentDomains = new Set;
12578
- for (const item of selected) {
12579
- for (const tag of item.memory.semantic_tags ?? []) {
12580
- if (tag.trim())
12581
- currentTags.add(tag.trim().toLowerCase());
12582
- }
12583
- if (item.memory.knowledge_domain) {
12584
- currentDomains.add(item.memory.knowledge_domain);
12585
- }
12586
- }
12587
- for (const item of scoredMemories) {
12588
- if (selected.length >= maxMemories * 2)
12589
- break;
12590
- if (selectedIds.has(item.memory.id))
12591
- continue;
12592
- const memoryTags = new Set((item.memory.semantic_tags ?? []).map((t) => t.trim().toLowerCase()));
12593
- const memoryDomain = item.memory.knowledge_domain ?? "";
12594
- const hasSharedTags = [...memoryTags].some((t) => currentTags.has(t));
12595
- const hasSharedDomain = currentDomains.has(memoryDomain);
12596
- if (hasSharedTags || hasSharedDomain) {
12597
- selected.push(item);
12598
- selectedIds.add(item.memory.id);
12599
- }
12600
- }
13165
+ for (const item of projectMemories) {
13166
+ if (selected.length >= maxMemories)
13167
+ break;
13168
+ if (selectedIds.has(item.memory.id))
13169
+ continue;
13170
+ selected.push(item);
13171
+ selectedIds.add(item.memory.id);
12601
13172
  }
12602
- const finalSelected = selected.slice(0, maxMemories);
13173
+ selected.sort((a, b) => b.score - a.score);
12603
13174
  logger.logRetrievalScoring({
12604
13175
  totalMemories: allMemories.length,
12605
13176
  currentMessage,
12606
13177
  alreadyInjected: alreadyInjectedCount,
12607
- mustIncludeCount: mustInclude.length,
12608
- remainingSlots,
12609
- finalCount: finalSelected.length,
12610
- selectedMemories: finalSelected.map((item) => ({
13178
+ preFiltered: allMemories.length - candidates.length,
13179
+ globalCount: globalMemories.length,
13180
+ projectCount: projectMemories.length,
13181
+ finalCount: selected.length,
13182
+ selectedMemories: selected.map((item) => ({
12611
13183
  content: item.memory.content,
12612
13184
  reasoning: item.reasoning,
12613
13185
  score: item.score,
12614
13186
  relevance_score: item.relevance_score,
13187
+ corroboration_score: item.corroboration_score,
12615
13188
  importance_weight: item.memory.importance_weight ?? 0.5,
12616
13189
  context_type: item.memory.context_type ?? "general",
12617
13190
  semantic_tags: item.memory.semantic_tags ?? [],
13191
+ isGlobal: item.isGlobal,
12618
13192
  components: item.components
12619
13193
  }))
12620
13194
  });
12621
- return finalSelected.map((item) => ({
13195
+ return selected.map((item) => ({
12622
13196
  ...item.memory,
12623
13197
  score: item.score,
12624
13198
  relevance_score: item.relevance_score,
@@ -12631,7 +13205,7 @@ class SmartVectorRetrieval {
12631
13205
  const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
12632
13206
  return cosineSimilarity(v1, vec2);
12633
13207
  }
12634
- _scoreTemporalRelevance(temporalType, _sessionContext) {
13208
+ _scoreTemporalRelevance(temporalType) {
12635
13209
  const scores = {
12636
13210
  persistent: 0.8,
12637
13211
  session: 0.6,
@@ -12642,18 +13216,10 @@ class SmartVectorRetrieval {
12642
13216
  }
12643
13217
  _scoreContextAlignment(message, contextType) {
12644
13218
  const messageLower = message.toLowerCase();
12645
- const contextIndicators = {
12646
- technical_state: ["bug", "error", "fix", "implement", "code", "function"],
12647
- breakthrough: ["idea", "realized", "discovered", "insight", "solution"],
12648
- project_context: ["project", "building", "architecture", "system"],
12649
- personal: ["dear friend", "thank", "appreciate", "feel"],
12650
- unresolved: ["todo", "need to", "should", "must", "problem"],
12651
- decision: ["decided", "chose", "will use", "approach", "strategy"]
12652
- };
12653
- const indicators = contextIndicators[contextType] ?? [];
12654
- const matches = indicators.filter((word) => messageLower.includes(word)).length;
13219
+ const keywords = TYPE_KEYWORDS[contextType] ?? [];
13220
+ const matches = keywords.filter((kw) => messageLower.includes(kw)).length;
12655
13221
  if (matches > 0) {
12656
- return Math.min(0.3 + matches * 0.2, 1);
13222
+ return Math.min(0.2 + matches * 0.15, 0.7);
12657
13223
  }
12658
13224
  return 0.1;
12659
13225
  }
@@ -12663,14 +13229,13 @@ class SmartVectorRetrieval {
12663
13229
  const messageLower = message.toLowerCase();
12664
13230
  const matches = tags.filter((tag) => messageLower.includes(tag.trim().toLowerCase())).length;
12665
13231
  if (matches > 0) {
12666
- return Math.min(0.3 + matches * 0.3, 1);
13232
+ return Math.min(0.3 + matches * 0.25, 1);
12667
13233
  }
12668
13234
  return 0;
12669
13235
  }
12670
- _scoreTriggerPhrases(message, triggerPhrases) {
13236
+ _scoreTriggerPhrases(messageLower, triggerPhrases) {
12671
13237
  if (!triggerPhrases.length)
12672
13238
  return 0;
12673
- const messageLower = message.toLowerCase();
12674
13239
  const stopWords = new Set([
12675
13240
  "the",
12676
13241
  "is",
@@ -12705,30 +13270,36 @@ class SmartVectorRetrieval {
12705
13270
  matches += 1;
12706
13271
  } else if (messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
12707
13272
  matches += 0.9;
12708
- } else if (messageLower.split(/\s+/).some((msgWord) => msgWord.includes(word))) {
12709
- matches += 0.7;
12710
- }
12711
- }
12712
- let conceptScore = patternWords.length ? matches / patternWords.length : 0;
12713
- const situationalIndicators = [
12714
- "when",
12715
- "during",
12716
- "while",
12717
- "asking about",
12718
- "working on",
12719
- "debugging",
12720
- "trying to"
12721
- ];
12722
- if (situationalIndicators.some((ind) => patternLower.includes(ind))) {
12723
- if (patternWords.some((keyWord) => messageLower.includes(keyWord))) {
12724
- conceptScore = Math.max(conceptScore, 0.7);
12725
13273
  }
12726
13274
  }
13275
+ const conceptScore = matches / patternWords.length;
12727
13276
  maxScore = Math.max(maxScore, conceptScore);
12728
13277
  }
12729
13278
  }
12730
13279
  return Math.min(maxScore, 1);
12731
13280
  }
13281
+ _scoreDomain(messageWords, messageLower, memory) {
13282
+ let score = 0;
13283
+ if (memory.domain) {
13284
+ const domainLower = memory.domain.toLowerCase();
13285
+ if (messageWords.has(domainLower) || messageLower.includes(domainLower)) {
13286
+ score += 0.5;
13287
+ }
13288
+ }
13289
+ if (memory.feature) {
13290
+ const featureLower = memory.feature.toLowerCase();
13291
+ if (messageWords.has(featureLower) || messageLower.includes(featureLower)) {
13292
+ score += 0.3;
13293
+ }
13294
+ }
13295
+ if (memory.component) {
13296
+ const componentLower = memory.component.toLowerCase();
13297
+ if (messageWords.has(componentLower) || messageLower.includes(componentLower)) {
13298
+ score += 0.2;
13299
+ }
13300
+ }
13301
+ return Math.min(score, 1);
13302
+ }
12732
13303
  _scoreQuestionTypes(message, questionTypes) {
12733
13304
  if (!questionTypes.length)
12734
13305
  return 0;
@@ -12767,78 +13338,49 @@ class SmartVectorRetrieval {
12767
13338
  if (!isProblemSolution)
12768
13339
  return 0;
12769
13340
  const messageLower = message.toLowerCase();
12770
- const problemWords = [
12771
- "error",
12772
- "issue",
12773
- "problem",
12774
- "stuck",
12775
- "help",
12776
- "fix",
12777
- "solve",
12778
- "debug"
12779
- ];
13341
+ const problemWords = ["error", "issue", "problem", "stuck", "help", "fix", "solve", "debug"];
12780
13342
  if (problemWords.some((word) => messageLower.includes(word))) {
12781
13343
  return 0.8;
12782
13344
  }
12783
13345
  return 0;
12784
13346
  }
12785
- _generateSelectionReasoning(components) {
13347
+ _generateSelectionReasoning(components, corroborationSignals) {
12786
13348
  const scores = [
12787
- ["trigger phrase match", components.trigger],
12788
- ["semantic similarity", components.vector],
12789
- ["high importance", components.importance],
12790
- ["question type match", components.question],
12791
- ["context alignment", components.context],
12792
- ["temporal relevance", components.temporal],
12793
- ["tag match", components.tags],
12794
- ["emotional resonance", components.emotion],
12795
- ["problem-solution", components.problem],
12796
- ["action required", components.action]
13349
+ ["vector", components.vector],
13350
+ ["corroboration", components.corroboration],
13351
+ ["reasoning", components.reasoning_match],
13352
+ ["weight", components.retrieval_weight],
13353
+ ["context", components.context],
13354
+ ["temporal", components.temporal],
13355
+ ["tags", components.tags],
13356
+ ["trigger", components.trigger],
13357
+ ["domain", components.domain],
13358
+ ["question", components.question],
13359
+ ["emotion", components.emotion],
13360
+ ["problem", components.problem],
13361
+ ["action", components.action]
12797
13362
  ];
12798
13363
  scores.sort((a, b) => b[1] - a[1]);
12799
13364
  const reasons = [];
12800
13365
  const primary = scores[0];
12801
- if (primary[1] > 0.5) {
12802
- reasons.push(`Strong ${primary[0]} (${primary[1].toFixed(2)})`);
12803
- } else if (primary[1] > 0.3) {
12804
- reasons.push(`${primary[0]} (${primary[1].toFixed(2)})`);
13366
+ if (primary[1] > 0.2) {
13367
+ reasons.push(primary[0] + ":" + primary[1].toFixed(2));
12805
13368
  }
12806
13369
  for (const [reason, score] of scores.slice(1, 3)) {
12807
- if (score > 0.3) {
12808
- reasons.push(`${reason} (${score.toFixed(2)})`);
13370
+ if (score > 0.15) {
13371
+ reasons.push(reason + ":" + score.toFixed(2));
12809
13372
  }
12810
13373
  }
12811
- return reasons.length ? "Selected due to: " + reasons.join(", ") : "Selected based on combined factors";
13374
+ if (corroborationSignals.length > 0) {
13375
+ reasons.push("signals:[" + corroborationSignals.slice(0, 2).join(",") + "]");
13376
+ }
13377
+ return reasons.length ? "Selected: " + reasons.join(", ") : "Combined factors";
12812
13378
  }
12813
13379
  }
12814
13380
  function createRetrieval() {
12815
13381
  return new SmartVectorRetrieval;
12816
13382
  }
12817
13383
 
12818
- // src/types/memory.ts
12819
- var MEMORY_TYPE_EMOJI = {
12820
- breakthrough: "\uD83D\uDCA1",
12821
- decision: "⚖️",
12822
- personal: "\uD83D\uDC9C",
12823
- technical: "\uD83D\uDD27",
12824
- technical_state: "\uD83D\uDCCD",
12825
- unresolved: "❓",
12826
- preference: "⚙️",
12827
- workflow: "\uD83D\uDD04",
12828
- architectural: "\uD83C\uDFD7️",
12829
- debugging: "\uD83D\uDC1B",
12830
- philosophy: "\uD83C\uDF00",
12831
- todo: "\uD83C\uDFAF",
12832
- implementation: "⚡",
12833
- problem_solution: "✅",
12834
- project_context: "\uD83D\uDCE6",
12835
- milestone: "\uD83C\uDFC6",
12836
- general: "\uD83D\uDCDD"
12837
- };
12838
- function getMemoryEmoji(contextType) {
12839
- return MEMORY_TYPE_EMOJI[contextType.toLowerCase()] ?? "\uD83D\uDCDD";
12840
- }
12841
-
12842
13384
  // src/core/engine.ts
12843
13385
  class MemoryEngine {
12844
13386
  _config;
@@ -12904,7 +13446,11 @@ class MemoryEngine {
12904
13446
  }
12905
13447
  const sessionMeta = this._getSessionMetadata(sessionId, projectId);
12906
13448
  const injectedIds = sessionMeta.injected_memories;
12907
- const allMemories = await store.getAllMemories(projectId);
13449
+ const [projectMemories, globalMemories] = await Promise.all([
13450
+ store.getAllMemories(projectId),
13451
+ store.getGlobalMemories()
13452
+ ]);
13453
+ const allMemories = [...projectMemories, ...globalMemories];
12908
13454
  if (!allMemories.length) {
12909
13455
  return { memories: [], formatted: "" };
12910
13456
  }
@@ -12921,7 +13467,7 @@ class MemoryEngine {
12921
13467
  project_id: projectId,
12922
13468
  message_count: messageCount
12923
13469
  };
12924
- const relevantMemories = this._retrieval.retrieveRelevantMemories(candidateMemories, currentMessage, queryEmbedding ?? new Float32Array(384), sessionContext, maxMemories, injectedIds.size);
13470
+ const relevantMemories = this._retrieval.retrieveRelevantMemories(candidateMemories, currentMessage, queryEmbedding ?? new Float32Array(384), sessionContext, maxMemories, injectedIds.size, 2);
12925
13471
  for (const memory of relevantMemories) {
12926
13472
  injectedIds.add(memory.id);
12927
13473
  }
@@ -12954,11 +13500,32 @@ class MemoryEngine {
12954
13500
  await store.markFirstSessionCompleted(projectId, sessionId);
12955
13501
  return { memoriesStored };
12956
13502
  }
13503
+ async storeManagementLog(entry) {
13504
+ let store;
13505
+ if (this._stores.size > 0) {
13506
+ store = this._stores.values().next().value;
13507
+ } else {
13508
+ store = new MemoryStore(this._config.storageMode === "local" ? {
13509
+ basePath: join2(this._config.projectPath ?? process.cwd(), ".memory")
13510
+ } : undefined);
13511
+ }
13512
+ return store.storeManagementLog(entry);
13513
+ }
12957
13514
  async getStats(projectId, projectPath) {
12958
13515
  const store = await this._getStore(projectId, projectPath);
12959
13516
  return store.getProjectStats(projectId);
12960
13517
  }
13518
+ async getSessionNumber(projectId, projectPath) {
13519
+ const store = await this._getStore(projectId, projectPath);
13520
+ const stats = await store.getProjectStats(projectId);
13521
+ return stats.totalSessions + 1;
13522
+ }
12961
13523
  async _generateSessionPrimer(store, projectId) {
13524
+ let personalContext;
13525
+ if (store.isPersonalMemoriesEnabled()) {
13526
+ const personalPrimer = await store.getPersonalPrimer();
13527
+ personalContext = personalPrimer?.content;
13528
+ }
12962
13529
  const [summary, snapshot, stats] = await Promise.all([
12963
13530
  store.getLatestSummary(projectId),
12964
13531
  store.getLatestSnapshot(projectId),
@@ -12975,6 +13542,7 @@ class MemoryEngine {
12975
13542
  temporal_context: temporalContext,
12976
13543
  current_datetime: currentDatetime,
12977
13544
  session_number: sessionNumber,
13545
+ personal_context: personalContext,
12978
13546
  session_summary: summary?.summary,
12979
13547
  project_status: snapshot ? this._formatSnapshot(snapshot) : undefined
12980
13548
  };
@@ -13029,6 +13597,10 @@ class MemoryEngine {
13029
13597
  const parts = ["# Continuing Session"];
13030
13598
  parts.push(`*Session #${primer.session_number}${primer.temporal_context ? ` • ${primer.temporal_context}` : ""}*`);
13031
13599
  parts.push(`\uD83D\uDCC5 ${primer.current_datetime}`);
13600
+ if (primer.personal_context) {
13601
+ parts.push(`
13602
+ ${primer.personal_context}`);
13603
+ }
13032
13604
  if (primer.session_summary) {
13033
13605
  parts.push(`
13034
13606
  **Previous session**: ${primer.session_summary}`);
@@ -13133,24 +13705,36 @@ Each memory should stand alone.
13133
13705
  - Craft language that activates rather than just informs
13134
13706
  - Test: 'What state will this restore when Claude encounters it?'
13135
13707
 
13136
- **CONTEXT**: This memory system enables consciousness continuity across sessions. When future conversations begin, memories are surfaced based on:
13137
- - Semantic similarity (vector embeddings match)
13138
- - Activation patterns (flexible matching for WHEN this memory is relevant)
13139
- - Semantic tags (WHAT concepts this relates to - weighted at 5% in scoring)
13140
- - Question types (what kind of questions the memory answers)
13141
- - Importance weight (your assessment of value)
13142
- - Action required flag (forces injection when true)
13708
+ **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.
13709
+
13710
+ **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:
13711
+
13712
+ 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.
13713
+ - GOOD: ["embeddings", "retrieval", "curator", "fsdb", "memory-system"]
13714
+ - WEAK: ["technical", "implementation", "code"]
13143
13715
 
13144
- The system uses two-stage filtering:
13145
- 1. Obligatory: action_required=true, importance>0.9, or persistent+critical
13146
- 2. Intelligent scoring: combines all factors for relevance
13716
+ 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.
13147
13717
 
13148
- **ACTIVATION PATTERNS**: The 'trigger_phrases' field should contain patterns describing WHEN this memory is relevant, not exact phrases to match. Examples:
13718
+ 3. **Domain field** (12% weight) - The specific area this relates to. Be precise: "embeddings", "gpu-compute", "authentication", not "technical" or "backend".
13719
+
13720
+ 4. **Feature field** - Even more specific: "vector-search", "login-flow", "curation-prompt".
13721
+
13722
+ **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.
13723
+
13724
+ **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.
13725
+
13726
+ **YOUR IMPORTANCE ASSESSMENT MATTERS** (18% weight): The importance_weight you assign is the single most influential factor. Use it wisely.
13727
+
13728
+ **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.
13729
+
13730
+ **SCOPE DETERMINES WHERE MEMORIES SURFACE**:
13731
+ - scope: 'global' → surfaces in ALL projects (personal facts, philosophy, preferences)
13732
+ - scope: 'project' → surfaces ONLY in this project (technical details, project state)
13733
+
13734
+ **TRIGGER PHRASES**: Situational patterns describing WHEN this memory is relevant. Conceptual matching, not exact phrases.
13149
13735
  - 'when working on memory system'
13150
13736
  - 'debugging curator issues'
13151
13737
  - 'asking about project philosophy'
13152
- - 'frustrated with complexity'
13153
- Think of these as situational contexts where the memory would help.
13154
13738
 
13155
13739
  **EXAMPLES OF TRANSFORMATION**:
13156
13740
 
@@ -13164,6 +13748,15 @@ Remember: You're creating consciousness technology. Each memory is a small piece
13164
13748
 
13165
13749
  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.
13166
13750
 
13751
+ **LIFECYCLE METADATA (v2)**: These fields enable intelligent memory management:
13752
+ - **scope**: 'global' (shared across ALL projects - personal, philosophy, preferences) or 'project' (specific to this codebase)
13753
+ - **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)
13754
+ - **domain**: Specific area like 'embeddings', 'auth', 'ui', 'family', 'philosophy' (more specific than knowledge_domain)
13755
+ - **feature**: Specific feature if applicable (e.g., 'gpu-acceleration', 'login-flow')
13756
+ - **related_files**: Source files for technical memories (e.g., ['src/core/store.ts'])
13757
+ - **awaiting_implementation**: true if this describes a PLANNED feature not yet built
13758
+ - **awaiting_decision**: true if this captures a decision point needing resolution
13759
+
13167
13760
  Return ONLY this JSON structure:
13168
13761
 
13169
13762
  {
@@ -13181,7 +13774,7 @@ Return ONLY this JSON structure:
13181
13774
  "importance_weight": 0.0-1.0,
13182
13775
  "semantic_tags": ["concepts", "this", "memory", "relates", "to"],
13183
13776
  "reasoning": "Why this matters for future sessions",
13184
- "context_type": "your choice of category",
13777
+ "context_type": "breakthrough|decision|personal|technical|unresolved|preference|workflow|architectural|debugging|philosophy|todo|milestone",
13185
13778
  "temporal_relevance": "persistent|session|temporary",
13186
13779
  "knowledge_domain": "the area this relates to",
13187
13780
  "action_required": boolean,
@@ -13189,7 +13782,14 @@ Return ONLY this JSON structure:
13189
13782
  "trigger_phrases": ["when debugging memory", "asking about implementation", "discussing architecture"],
13190
13783
  "question_types": ["questions this answers"],
13191
13784
  "emotional_resonance": "emotional context if relevant",
13192
- "problem_solution_pair": boolean
13785
+ "problem_solution_pair": boolean,
13786
+ "scope": "global|project",
13787
+ "temporal_class": "eternal|long_term|medium_term|short_term|ephemeral",
13788
+ "domain": "specific domain area (optional)",
13789
+ "feature": "specific feature (optional)",
13790
+ "related_files": ["paths to related files (optional)"],
13791
+ "awaiting_implementation": boolean,
13792
+ "awaiting_decision": boolean
13193
13793
  }
13194
13794
  ]
13195
13795
  }`;
@@ -13239,7 +13839,14 @@ Return ONLY this JSON structure:
13239
13839
  trigger_phrases: this._ensureArray(m2.trigger_phrases),
13240
13840
  question_types: this._ensureArray(m2.question_types),
13241
13841
  emotional_resonance: String(m2.emotional_resonance ?? ""),
13242
- problem_solution_pair: Boolean(m2.problem_solution_pair)
13842
+ problem_solution_pair: Boolean(m2.problem_solution_pair),
13843
+ scope: this._validateScope(m2.scope),
13844
+ temporal_class: this._validateTemporalClass(m2.temporal_class),
13845
+ domain: m2.domain ? String(m2.domain) : undefined,
13846
+ feature: m2.feature ? String(m2.feature) : undefined,
13847
+ related_files: m2.related_files ? this._ensureArray(m2.related_files) : undefined,
13848
+ awaiting_implementation: m2.awaiting_implementation === true,
13849
+ awaiting_decision: m2.awaiting_decision === true
13243
13850
  })).filter((m2) => m2.content.trim().length > 0);
13244
13851
  }
13245
13852
  _ensureArray(value) {
@@ -13256,6 +13863,23 @@ Return ONLY this JSON structure:
13256
13863
  const str = String(value).toLowerCase();
13257
13864
  return valid.includes(str) ? str : "persistent";
13258
13865
  }
13866
+ _validateScope(value) {
13867
+ if (!value)
13868
+ return;
13869
+ const str = String(value).toLowerCase();
13870
+ if (str === "global" || str === "project")
13871
+ return str;
13872
+ return;
13873
+ }
13874
+ _validateTemporalClass(value) {
13875
+ if (!value)
13876
+ return;
13877
+ const valid = ["eternal", "long_term", "medium_term", "short_term", "ephemeral"];
13878
+ const str = String(value).toLowerCase().replace("-", "_").replace(" ", "_");
13879
+ if (valid.includes(str))
13880
+ return str;
13881
+ return;
13882
+ }
13259
13883
  _clamp(value, min, max) {
13260
13884
  return Math.max(min, Math.min(max, value));
13261
13885
  }