@rlabs-inc/memory 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -11995,7 +11995,60 @@ function createDatabase(options = {}) {
11995
11995
  var import_os = require("os");
11996
11996
  var import_path = require("path");
11997
11997
 
11998
+ // src/types/memory.ts
11999
+ var V2_DEFAULTS = {
12000
+ typeDefaults: {
12001
+ personal: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
12002
+ philosophy: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
12003
+ preference: { scope: "global", temporal_class: "long_term", fade_rate: 0.01 },
12004
+ breakthrough: { scope: "project", temporal_class: "eternal", fade_rate: 0 },
12005
+ decision: { scope: "project", temporal_class: "long_term", fade_rate: 0 },
12006
+ milestone: { scope: "project", temporal_class: "eternal", fade_rate: 0 },
12007
+ technical: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 },
12008
+ architectural: { scope: "project", temporal_class: "long_term", fade_rate: 0.01 },
12009
+ debugging: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 },
12010
+ unresolved: { scope: "project", temporal_class: "medium_term", fade_rate: 0.05 },
12011
+ todo: { scope: "project", temporal_class: "short_term", fade_rate: 0.1 },
12012
+ technical_state: { scope: "project", temporal_class: "short_term", fade_rate: 0.1 },
12013
+ workflow: { scope: "project", temporal_class: "long_term", fade_rate: 0.02 },
12014
+ project_context: { scope: "project", temporal_class: "medium_term", fade_rate: 0.03 }
12015
+ },
12016
+ fallback: {
12017
+ status: "active",
12018
+ scope: "project",
12019
+ temporal_class: "medium_term",
12020
+ fade_rate: 0.03,
12021
+ sessions_since_surfaced: 0,
12022
+ awaiting_implementation: false,
12023
+ awaiting_decision: false,
12024
+ exclude_from_retrieval: false
12025
+ }
12026
+ };
12027
+ var MEMORY_TYPE_EMOJI = {
12028
+ breakthrough: "\uD83D\uDCA1",
12029
+ decision: "⚖️",
12030
+ personal: "\uD83D\uDC9C",
12031
+ technical: "\uD83D\uDD27",
12032
+ technical_state: "\uD83D\uDCCD",
12033
+ unresolved: "❓",
12034
+ preference: "⚙️",
12035
+ workflow: "\uD83D\uDD04",
12036
+ architectural: "\uD83C\uDFD7️",
12037
+ debugging: "\uD83D\uDC1B",
12038
+ philosophy: "\uD83C\uDF00",
12039
+ todo: "\uD83C\uDFAF",
12040
+ implementation: "⚡",
12041
+ problem_solution: "✅",
12042
+ project_context: "\uD83D\uDCE6",
12043
+ milestone: "\uD83C\uDFC6",
12044
+ general: "\uD83D\uDCDD"
12045
+ };
12046
+ function getMemoryEmoji(contextType) {
12047
+ return MEMORY_TYPE_EMOJI[contextType.toLowerCase()] ?? "\uD83D\uDCDD";
12048
+ }
12049
+
11998
12050
  // src/types/schema.ts
12051
+ var MEMORY_SCHEMA_VERSION = 2;
11999
12052
  var memorySchema = {
12000
12053
  content: "string",
12001
12054
  reasoning: "string",
@@ -12012,7 +12065,34 @@ var memorySchema = {
12012
12065
  question_types: "string[]",
12013
12066
  session_id: "string",
12014
12067
  project_id: "string",
12015
- embedding: "vector:384"
12068
+ embedding: "vector:384",
12069
+ status: "string",
12070
+ scope: "string",
12071
+ session_created: "number",
12072
+ session_updated: "number",
12073
+ last_surfaced: "number",
12074
+ sessions_since_surfaced: "number",
12075
+ temporal_class: "string",
12076
+ fade_rate: "number",
12077
+ expires_after_sessions: "number",
12078
+ domain: "string",
12079
+ feature: "string",
12080
+ component: "string",
12081
+ supersedes: "string",
12082
+ superseded_by: "string",
12083
+ related_to: "string[]",
12084
+ resolves: "string[]",
12085
+ resolved_by: "string",
12086
+ parent_id: "string",
12087
+ child_ids: "string[]",
12088
+ awaiting_implementation: "boolean",
12089
+ awaiting_decision: "boolean",
12090
+ blocked_by: "string",
12091
+ blocks: "string[]",
12092
+ related_files: "string[]",
12093
+ retrieval_weight: "number",
12094
+ exclude_from_retrieval: "boolean",
12095
+ schema_version: "number"
12016
12096
  };
12017
12097
  var sessionSummarySchema = {
12018
12098
  session_id: "string",
@@ -12035,17 +12115,211 @@ var sessionSchema = {
12035
12115
  last_active: "timestamp",
12036
12116
  metadata: "string"
12037
12117
  };
12118
+ var managementLogSchema = {
12119
+ project_id: "string",
12120
+ session_number: "number",
12121
+ memories_processed: "number",
12122
+ superseded_count: "number",
12123
+ resolved_count: "number",
12124
+ linked_count: "number",
12125
+ primer_updated: "boolean",
12126
+ success: "boolean",
12127
+ duration_ms: "number",
12128
+ summary: "string",
12129
+ error: "string",
12130
+ details: "string"
12131
+ };
12038
12132
 
12039
12133
  // src/core/store.ts
12134
+ var PERSONAL_PRIMER_ID = "personal-primer";
12135
+ var DEFAULT_GLOBAL_PATH = import_path.join(import_os.homedir(), ".local", "share", "memory", "global");
12136
+
12040
12137
  class MemoryStore {
12041
12138
  _config;
12042
12139
  _projects = new Map;
12140
+ _global = null;
12043
12141
  constructor(config = {}) {
12044
12142
  this._config = {
12045
12143
  basePath: config.basePath ?? import_path.join(import_os.homedir(), ".local", "share", "memory"),
12144
+ globalPath: config.globalPath ?? DEFAULT_GLOBAL_PATH,
12046
12145
  watchFiles: config.watchFiles ?? false
12047
12146
  };
12048
12147
  }
12148
+ async getGlobal() {
12149
+ if (this._global) {
12150
+ return this._global;
12151
+ }
12152
+ const globalPath = this._config.globalPath;
12153
+ console.log(`\uD83C\uDF10 [DEBUG] Creating global database at ${globalPath}`);
12154
+ const db = createDatabase({
12155
+ name: "global",
12156
+ basePath: globalPath
12157
+ });
12158
+ const memories = db.collection("memories", {
12159
+ schema: memorySchema,
12160
+ contentColumn: "content",
12161
+ autoSave: true,
12162
+ watchFiles: this._config.watchFiles
12163
+ });
12164
+ const managementLogs = db.collection("management-logs", {
12165
+ schema: managementLogSchema,
12166
+ contentColumn: "summary",
12167
+ autoSave: true,
12168
+ watchFiles: this._config.watchFiles
12169
+ });
12170
+ await Promise.all([memories.load(), managementLogs.load()]);
12171
+ this._global = { db, memories, managementLogs };
12172
+ return this._global;
12173
+ }
12174
+ async getGlobalMemories() {
12175
+ const { memories } = await this.getGlobal();
12176
+ return memories.all().map((record) => ({
12177
+ id: record.id,
12178
+ content: record.content,
12179
+ reasoning: record.reasoning,
12180
+ importance_weight: record.importance_weight,
12181
+ confidence_score: record.confidence_score,
12182
+ context_type: record.context_type,
12183
+ temporal_relevance: record.temporal_relevance,
12184
+ knowledge_domain: record.knowledge_domain,
12185
+ emotional_resonance: record.emotional_resonance,
12186
+ action_required: record.action_required,
12187
+ problem_solution_pair: record.problem_solution_pair,
12188
+ semantic_tags: record.semantic_tags,
12189
+ trigger_phrases: record.trigger_phrases,
12190
+ question_types: record.question_types,
12191
+ session_id: record.session_id,
12192
+ project_id: "global",
12193
+ embedding: record.embedding ?? undefined,
12194
+ created_at: record.created,
12195
+ updated_at: record.updated,
12196
+ stale: record.stale
12197
+ }));
12198
+ }
12199
+ async storeGlobalMemory(sessionId, memory, embedding, sessionNumber) {
12200
+ const { memories } = await this.getGlobal();
12201
+ const contextType = memory.context_type ?? "personal";
12202
+ const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.personal;
12203
+ const id = memories.insert({
12204
+ content: memory.content,
12205
+ reasoning: memory.reasoning,
12206
+ importance_weight: memory.importance_weight,
12207
+ confidence_score: memory.confidence_score,
12208
+ context_type: memory.context_type,
12209
+ temporal_relevance: memory.temporal_relevance,
12210
+ knowledge_domain: memory.knowledge_domain,
12211
+ emotional_resonance: memory.emotional_resonance,
12212
+ action_required: memory.action_required,
12213
+ problem_solution_pair: memory.problem_solution_pair,
12214
+ semantic_tags: memory.semantic_tags,
12215
+ trigger_phrases: memory.trigger_phrases,
12216
+ question_types: memory.question_types,
12217
+ session_id: sessionId,
12218
+ project_id: "global",
12219
+ embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null,
12220
+ status: V2_DEFAULTS.fallback.status,
12221
+ scope: "global",
12222
+ temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? "eternal",
12223
+ fade_rate: typeDefaults?.fade_rate ?? 0,
12224
+ session_created: sessionNumber ?? 0,
12225
+ session_updated: sessionNumber ?? 0,
12226
+ sessions_since_surfaced: 0,
12227
+ domain: memory.domain ?? null,
12228
+ feature: memory.feature ?? null,
12229
+ related_files: memory.related_files ?? [],
12230
+ awaiting_implementation: memory.awaiting_implementation ?? false,
12231
+ awaiting_decision: memory.awaiting_decision ?? false,
12232
+ retrieval_weight: memory.importance_weight,
12233
+ exclude_from_retrieval: false,
12234
+ schema_version: MEMORY_SCHEMA_VERSION,
12235
+ supersedes: null,
12236
+ superseded_by: null,
12237
+ related_to: [],
12238
+ resolves: [],
12239
+ resolved_by: null,
12240
+ parent_id: null,
12241
+ child_ids: [],
12242
+ blocked_by: null,
12243
+ blocks: []
12244
+ });
12245
+ return id;
12246
+ }
12247
+ async getPersonalPrimer() {
12248
+ const { memories } = await this.getGlobal();
12249
+ const primer = memories.get(PERSONAL_PRIMER_ID);
12250
+ if (!primer) {
12251
+ return null;
12252
+ }
12253
+ return {
12254
+ content: primer.content,
12255
+ updated: primer.updated
12256
+ };
12257
+ }
12258
+ async setPersonalPrimer(content) {
12259
+ const { memories } = await this.getGlobal();
12260
+ const existing = memories.get(PERSONAL_PRIMER_ID);
12261
+ if (existing) {
12262
+ memories.update(PERSONAL_PRIMER_ID, { content });
12263
+ } else {
12264
+ memories.insert({
12265
+ id: PERSONAL_PRIMER_ID,
12266
+ content,
12267
+ reasoning: "Personal relationship context injected at session start",
12268
+ importance_weight: 1,
12269
+ confidence_score: 1,
12270
+ context_type: "personal",
12271
+ temporal_relevance: "persistent",
12272
+ knowledge_domain: "personal",
12273
+ emotional_resonance: "neutral",
12274
+ action_required: false,
12275
+ problem_solution_pair: false,
12276
+ semantic_tags: ["personal", "primer", "relationship"],
12277
+ trigger_phrases: [],
12278
+ question_types: [],
12279
+ session_id: "system",
12280
+ project_id: "global",
12281
+ embedding: null
12282
+ });
12283
+ }
12284
+ }
12285
+ isPersonalMemoriesEnabled() {
12286
+ return true;
12287
+ }
12288
+ async storeManagementLog(entry) {
12289
+ const { managementLogs } = await this.getGlobal();
12290
+ const id = managementLogs.insert({
12291
+ project_id: entry.projectId,
12292
+ session_number: entry.sessionNumber,
12293
+ memories_processed: entry.memoriesProcessed,
12294
+ superseded_count: entry.supersededCount,
12295
+ resolved_count: entry.resolvedCount,
12296
+ linked_count: entry.linkedCount,
12297
+ primer_updated: entry.primerUpdated,
12298
+ success: entry.success,
12299
+ duration_ms: entry.durationMs,
12300
+ summary: entry.summary,
12301
+ error: entry.error ?? "",
12302
+ details: entry.details ? JSON.stringify(entry.details) : ""
12303
+ });
12304
+ return id;
12305
+ }
12306
+ async getManagementLogs(limit = 10) {
12307
+ const { managementLogs } = await this.getGlobal();
12308
+ return managementLogs.all().sort((a, b) => b.created - a.created).slice(0, limit).map((record) => ({
12309
+ id: record.id,
12310
+ projectId: record.project_id,
12311
+ sessionNumber: record.session_number,
12312
+ memoriesProcessed: record.memories_processed,
12313
+ supersededCount: record.superseded_count,
12314
+ resolvedCount: record.resolved_count,
12315
+ linkedCount: record.linked_count,
12316
+ primerUpdated: record.primer_updated,
12317
+ success: record.success,
12318
+ durationMs: record.duration_ms,
12319
+ summary: record.summary,
12320
+ createdAt: record.created
12321
+ }));
12322
+ }
12049
12323
  async getProject(projectId) {
12050
12324
  if (this._projects.has(projectId)) {
12051
12325
  console.log(`\uD83D\uDD04 [DEBUG] Returning cached databases for ${projectId}`);
@@ -12090,8 +12364,10 @@ class MemoryStore {
12090
12364
  this._projects.set(projectId, projectDB);
12091
12365
  return projectDB;
12092
12366
  }
12093
- async storeMemory(projectId, sessionId, memory, embedding) {
12367
+ async storeMemory(projectId, sessionId, memory, embedding, sessionNumber) {
12094
12368
  const { memories } = await this.getProject(projectId);
12369
+ const contextType = memory.context_type ?? "general";
12370
+ const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.technical;
12095
12371
  const id = memories.insert({
12096
12372
  content: memory.content,
12097
12373
  reasoning: memory.reasoning,
@@ -12108,7 +12384,31 @@ class MemoryStore {
12108
12384
  question_types: memory.question_types,
12109
12385
  session_id: sessionId,
12110
12386
  project_id: projectId,
12111
- embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null
12387
+ embedding: embedding ? embedding instanceof Float32Array ? embedding : new Float32Array(embedding) : null,
12388
+ status: V2_DEFAULTS.fallback.status,
12389
+ scope: memory.scope ?? typeDefaults?.scope ?? V2_DEFAULTS.fallback.scope,
12390
+ temporal_class: memory.temporal_class ?? typeDefaults?.temporal_class ?? V2_DEFAULTS.fallback.temporal_class,
12391
+ fade_rate: typeDefaults?.fade_rate ?? V2_DEFAULTS.fallback.fade_rate,
12392
+ session_created: sessionNumber ?? 0,
12393
+ session_updated: sessionNumber ?? 0,
12394
+ sessions_since_surfaced: 0,
12395
+ domain: memory.domain ?? null,
12396
+ feature: memory.feature ?? null,
12397
+ related_files: memory.related_files ?? [],
12398
+ awaiting_implementation: memory.awaiting_implementation ?? false,
12399
+ awaiting_decision: memory.awaiting_decision ?? false,
12400
+ retrieval_weight: memory.importance_weight,
12401
+ exclude_from_retrieval: false,
12402
+ schema_version: MEMORY_SCHEMA_VERSION,
12403
+ supersedes: null,
12404
+ superseded_by: null,
12405
+ related_to: [],
12406
+ resolves: [],
12407
+ resolved_by: null,
12408
+ parent_id: null,
12409
+ child_ids: [],
12410
+ blocked_by: null,
12411
+ blocks: []
12112
12412
  });
12113
12413
  return id;
12114
12414
  }
@@ -12328,6 +12628,10 @@ class MemoryStore {
12328
12628
  projectDB.db.close();
12329
12629
  }
12330
12630
  this._projects.clear();
12631
+ if (this._global) {
12632
+ this._global.db.close();
12633
+ this._global = null;
12634
+ }
12331
12635
  }
12332
12636
  }
12333
12637
  function createStore(config) {
@@ -12488,17 +12792,55 @@ var logger = {
12488
12792
  }
12489
12793
  console.log();
12490
12794
  },
12795
+ logManagementStart(memoriesCount) {
12796
+ console.log(`${timestamp()} ${style("blue", "\uD83D\uDD27")} ${style("bold", "MANAGEMENT AGENT")}`);
12797
+ console.log(` ${style("dim", "processing:")} ${memoriesCount} new memories`);
12798
+ },
12799
+ logManagementComplete(result) {
12800
+ if (result.success) {
12801
+ console.log(` ${style("green", sym.check)} ${style("bold", "Completed")}`);
12802
+ const stats = [];
12803
+ if (result.superseded && result.superseded > 0) {
12804
+ stats.push(`${result.superseded} superseded`);
12805
+ }
12806
+ if (result.resolved && result.resolved > 0) {
12807
+ stats.push(`${result.resolved} resolved`);
12808
+ }
12809
+ if (result.linked && result.linked > 0) {
12810
+ stats.push(`${result.linked} linked`);
12811
+ }
12812
+ if (result.primerUpdated) {
12813
+ stats.push("primer updated");
12814
+ }
12815
+ if (stats.length > 0) {
12816
+ console.log(` ${style("dim", "changes:")} ${stats.join(style("dim", ", "))}`);
12817
+ } else {
12818
+ console.log(` ${style("dim", "changes:")} none (memories are current)`);
12819
+ }
12820
+ if (result.summary) {
12821
+ const shortSummary = result.summary.length > 60 ? result.summary.slice(0, 60) + "..." : result.summary;
12822
+ console.log(` ${style("dim", "summary:")} ${shortSummary}`);
12823
+ }
12824
+ } else {
12825
+ console.log(` ${style("yellow", sym.warning)} ${style("bold", "Failed")}`);
12826
+ if (result.error) {
12827
+ console.log(` ${style("dim", "error:")} ${result.error.slice(0, 80)}`);
12828
+ }
12829
+ }
12830
+ console.log();
12831
+ },
12491
12832
  logRetrievalScoring(params) {
12492
- const { totalMemories, currentMessage, alreadyInjected, mustIncludeCount, remainingSlots, finalCount, selectedMemories } = params;
12833
+ const { totalMemories, currentMessage, alreadyInjected, preFiltered, globalCount, projectCount, finalCount, selectedMemories } = params;
12493
12834
  console.log();
12494
- console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "TWO-STAGE MEMORY FILTERING")}`);
12495
- console.log(` ${style("dim", "candidates:")} ${totalMemories} memories`);
12835
+ console.log(`${timestamp()} ${style("magenta", sym.brain)} ${style("bold", "MULTI-DIMENSIONAL RETRIEVAL")}`);
12836
+ console.log(` ${style("dim", "total:")} ${totalMemories} memories`);
12837
+ console.log(` ${style("dim", "pre-filtered:")} ${preFiltered} (inactive/excluded/scope)`);
12496
12838
  console.log(` ${style("dim", "already injected:")} ${alreadyInjected}`);
12497
12839
  const msgPreview = currentMessage.length > 60 ? currentMessage.slice(0, 60) + "..." : currentMessage;
12498
- console.log(` ${style("dim", "trigger:")} "${msgPreview}"`);
12840
+ console.log(` ${style("dim", "message:")} "${msgPreview}"`);
12499
12841
  console.log();
12500
- console.log(` ${style("cyan", "Stage 1:")} ${mustIncludeCount} must-include (critical/action-required)`);
12501
- console.log(` ${style("cyan", "Stage 2:")} ${remainingSlots} slots for scored selection`);
12842
+ console.log(` ${style("cyan", "Global:")} ${globalCount} candidates → max 2 selected`);
12843
+ console.log(` ${style("cyan", "Project:")} ${projectCount} candidates`);
12502
12844
  console.log(` ${style("green", "Final:")} ${finalCount} memories selected`);
12503
12845
  console.log();
12504
12846
  if (selectedMemories.length === 0) {
@@ -12513,17 +12855,18 @@ var logger = {
12513
12855
  const num = style("dim", `${i + 1}.`);
12514
12856
  const score = style("green", `${(m.score * 100).toFixed(0)}%`);
12515
12857
  const relevance = style("cyan", `rel:${(m.relevance_score * 100).toFixed(0)}%`);
12858
+ const corr = style("magenta", `corr:${(m.corroboration_score * 100).toFixed(0)}%`);
12516
12859
  const type = style("yellow", m.context_type.toUpperCase());
12517
- console.log(` ${num} [${score} ${relevance}] ${type}`);
12860
+ const scope = m.isGlobal ? style("blue", "[G]") : "";
12861
+ console.log(` ${num} [${score} ${relevance} ${corr}] ${type} ${scope}`);
12518
12862
  const preview = m.content.length > 60 ? m.content.slice(0, 60) + style("dim", "...") : m.content;
12519
12863
  console.log(` ${style("white", preview)}`);
12520
- 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(", ");
12864
+ 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(", ");
12521
12865
  if (components) {
12522
12866
  console.log(` ${style("dim", "scores:")} ${components}`);
12523
12867
  }
12524
- if (m.semantic_tags?.length) {
12525
- const tags = m.semantic_tags.slice(0, 3).join(", ");
12526
- console.log(` ${style("dim", "tags:")} ${tags}`);
12868
+ if (m.reasoning) {
12869
+ console.log(` ${style("dim", m.reasoning)}`);
12527
12870
  }
12528
12871
  console.log();
12529
12872
  });
@@ -12531,125 +12874,356 @@ var logger = {
12531
12874
  };
12532
12875
 
12533
12876
  // src/core/retrieval.ts
12877
+ var TYPE_KEYWORDS = {
12878
+ debug: ["bug", "error", "fix", "broken", "crash", "fails", "exception", "stack trace", "debugging"],
12879
+ unresolved: ["issue", "problem", "stuck", "blocked", "help", "question", "unsure", "unclear"],
12880
+ decision: ["decide", "choice", "option", "should we", "which", "alternative", "tradeoff"],
12881
+ architecture: ["structure", "design", "pattern", "approach", "system", "layer", "architecture"],
12882
+ breakthrough: ["discovered", "realized", "insight", "found that", "aha", "finally", "key insight"],
12883
+ todo: ["need to", "should", "must", "will", "later", "next", "todo"],
12884
+ personal: ["family", "children", "friend", "relationship", "feel", "appreciate", "thank"],
12885
+ philosophy: ["meaning", "consciousness", "existence", "purpose", "believe", "philosophy"],
12886
+ technical: ["implement", "code", "function", "class", "module", "api", "interface"]
12887
+ };
12888
+ var TEMPORAL_CLASS_SCORES = {
12889
+ eternal: 1,
12890
+ long_term: 0.9,
12891
+ medium_term: 0.7,
12892
+ short_term: 0.5,
12893
+ ephemeral: 0.3
12894
+ };
12895
+
12534
12896
  class SmartVectorRetrieval {
12535
- retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0) {
12897
+ _extractSignificantWords(text) {
12898
+ const stopWords = new Set([
12899
+ "the",
12900
+ "is",
12901
+ "are",
12902
+ "was",
12903
+ "were",
12904
+ "to",
12905
+ "a",
12906
+ "an",
12907
+ "and",
12908
+ "or",
12909
+ "but",
12910
+ "in",
12911
+ "on",
12912
+ "at",
12913
+ "for",
12914
+ "with",
12915
+ "about",
12916
+ "when",
12917
+ "how",
12918
+ "what",
12919
+ "why",
12920
+ "where",
12921
+ "this",
12922
+ "that",
12923
+ "it",
12924
+ "of",
12925
+ "be",
12926
+ "have",
12927
+ "do",
12928
+ "does",
12929
+ "did",
12930
+ "will",
12931
+ "would",
12932
+ "could",
12933
+ "should",
12934
+ "can",
12935
+ "may",
12936
+ "might",
12937
+ "must",
12938
+ "shall",
12939
+ "has",
12940
+ "had",
12941
+ "been",
12942
+ "being",
12943
+ "i",
12944
+ "you",
12945
+ "we",
12946
+ "they",
12947
+ "he",
12948
+ "she",
12949
+ "my",
12950
+ "your",
12951
+ "our",
12952
+ "its",
12953
+ "his",
12954
+ "her",
12955
+ "their",
12956
+ "if",
12957
+ "then",
12958
+ "else",
12959
+ "so",
12960
+ "as",
12961
+ "from",
12962
+ "by",
12963
+ "into",
12964
+ "through",
12965
+ "during",
12966
+ "before",
12967
+ "after",
12968
+ "above",
12969
+ "below",
12970
+ "up",
12971
+ "down",
12972
+ "out",
12973
+ "off",
12974
+ "over",
12975
+ "under",
12976
+ "again",
12977
+ "further",
12978
+ "once",
12979
+ "here",
12980
+ "there",
12981
+ "all",
12982
+ "each",
12983
+ "few",
12984
+ "more",
12985
+ "most",
12986
+ "other",
12987
+ "some",
12988
+ "such",
12989
+ "no",
12990
+ "nor",
12991
+ "not",
12992
+ "only",
12993
+ "own",
12994
+ "same",
12995
+ "than",
12996
+ "too",
12997
+ "very",
12998
+ "just",
12999
+ "also",
13000
+ "now",
13001
+ "back",
13002
+ "get",
13003
+ "got",
13004
+ "go",
13005
+ "going",
13006
+ "gone",
13007
+ "come",
13008
+ "came",
13009
+ "let",
13010
+ "lets",
13011
+ "hey",
13012
+ "hi",
13013
+ "hello",
13014
+ "ok",
13015
+ "okay"
13016
+ ]);
13017
+ const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !stopWords.has(w));
13018
+ return new Set(words);
13019
+ }
13020
+ _detectContextTypes(message) {
13021
+ const messageLower = message.toLowerCase();
13022
+ const detected = new Set;
13023
+ for (const [type, keywords] of Object.entries(TYPE_KEYWORDS)) {
13024
+ for (const keyword of keywords) {
13025
+ if (messageLower.includes(keyword)) {
13026
+ detected.add(type);
13027
+ break;
13028
+ }
13029
+ }
13030
+ }
13031
+ return detected;
13032
+ }
13033
+ _preFilter(memories, currentProjectId, messageLower) {
13034
+ return memories.filter((memory) => {
13035
+ if (memory.status && memory.status !== "active") {
13036
+ return false;
13037
+ }
13038
+ if (memory.exclude_from_retrieval === true) {
13039
+ return false;
13040
+ }
13041
+ if (memory.superseded_by) {
13042
+ return false;
13043
+ }
13044
+ const isGlobal = memory.scope === "global" || memory.project_id === "global";
13045
+ if (!isGlobal && memory.project_id !== currentProjectId) {
13046
+ return false;
13047
+ }
13048
+ if (memory.anti_triggers?.length) {
13049
+ for (const antiTrigger of memory.anti_triggers) {
13050
+ if (messageLower.includes(antiTrigger.toLowerCase())) {
13051
+ return false;
13052
+ }
13053
+ }
13054
+ }
13055
+ return true;
13056
+ });
13057
+ }
13058
+ _calculateCorroboration(memory, messageWords, detectedTypes, messageLower) {
13059
+ const signals = [];
13060
+ let score = 0;
13061
+ let reasoningMatch = 0;
13062
+ const tagOverlap = (memory.semantic_tags ?? []).filter((tag) => messageWords.has(tag.toLowerCase()) || messageLower.includes(tag.toLowerCase()));
13063
+ if (tagOverlap.length > 0) {
13064
+ score += Math.min(0.4, tagOverlap.length * 0.15);
13065
+ signals.push("tags:" + tagOverlap.join(","));
13066
+ }
13067
+ if (memory.reasoning) {
13068
+ const reasoningWords = this._extractSignificantWords(memory.reasoning);
13069
+ const reasoningOverlap = [...messageWords].filter((w) => reasoningWords.has(w));
13070
+ if (reasoningOverlap.length > 0) {
13071
+ reasoningMatch = Math.min(0.4, reasoningOverlap.length * 0.1);
13072
+ score += reasoningMatch;
13073
+ signals.push("reasoning:" + reasoningOverlap.slice(0, 3).join(","));
13074
+ }
13075
+ }
13076
+ if (memory.domain) {
13077
+ const domainLower = memory.domain.toLowerCase();
13078
+ if (messageLower.includes(domainLower) || messageWords.has(domainLower)) {
13079
+ score += 0.3;
13080
+ signals.push("domain:" + memory.domain);
13081
+ }
13082
+ }
13083
+ if (memory.knowledge_domain) {
13084
+ const kdLower = memory.knowledge_domain.toLowerCase();
13085
+ if (messageLower.includes(kdLower) || messageWords.has(kdLower)) {
13086
+ score += 0.2;
13087
+ signals.push("knowledge:" + memory.knowledge_domain);
13088
+ }
13089
+ }
13090
+ if (memory.context_type && detectedTypes.has(memory.context_type)) {
13091
+ score += 0.12;
13092
+ signals.push("type:" + memory.context_type);
13093
+ }
13094
+ const triggerMatch = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
13095
+ if (triggerMatch > 0.3) {
13096
+ score += Math.min(0.3, triggerMatch * 0.4);
13097
+ signals.push("trigger:" + triggerMatch.toFixed(2));
13098
+ }
13099
+ if (memory.feature) {
13100
+ const featureLower = memory.feature.toLowerCase();
13101
+ if (messageLower.includes(featureLower) || messageWords.has(featureLower)) {
13102
+ score += 0.2;
13103
+ signals.push("feature:" + memory.feature);
13104
+ }
13105
+ }
13106
+ if (memory.related_files?.length) {
13107
+ for (const file of memory.related_files) {
13108
+ const filename = file.split("/").pop()?.toLowerCase() ?? "";
13109
+ if (filename && messageLower.includes(filename)) {
13110
+ score += 0.25;
13111
+ signals.push("file:" + filename);
13112
+ break;
13113
+ }
13114
+ }
13115
+ }
13116
+ return { score: Math.min(1, score), signals, reasoningMatch };
13117
+ }
13118
+ retrieveRelevantMemories(allMemories, currentMessage, queryEmbedding, sessionContext, maxMemories = 5, alreadyInjectedCount = 0, maxGlobalMemories = 2) {
12536
13119
  if (!allMemories.length) {
12537
13120
  return [];
12538
13121
  }
13122
+ const messageLower = currentMessage.toLowerCase();
13123
+ const messageWords = this._extractSignificantWords(currentMessage);
13124
+ const detectedTypes = this._detectContextTypes(currentMessage);
13125
+ const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
13126
+ if (!candidates.length) {
13127
+ return [];
13128
+ }
12539
13129
  const scoredMemories = [];
12540
- for (const memory of allMemories) {
13130
+ for (const memory of candidates) {
13131
+ const isGlobal = memory.scope === "global" || memory.project_id === "global";
12541
13132
  const vectorScore = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
12542
- const importance = memory.importance_weight ?? 0.5;
12543
- const temporalScore = this._scoreTemporalRelevance(memory.temporal_relevance ?? "persistent", sessionContext);
13133
+ const { score: corroborationScore, signals: corroborationSignals, reasoningMatch } = this._calculateCorroboration(memory, messageWords, detectedTypes, messageLower);
13134
+ const retrievalWeight = memory.retrieval_weight ?? memory.importance_weight ?? 0.5;
13135
+ const temporalScore = memory.temporal_class ? TEMPORAL_CLASS_SCORES[memory.temporal_class] ?? 0.7 : this._scoreTemporalRelevance(memory.temporal_relevance ?? "persistent");
12544
13136
  const contextScore = this._scoreContextAlignment(currentMessage, memory.context_type ?? "general");
12545
- const actionBoost = memory.action_required ? 0.3 : 0;
12546
13137
  const tagScore = this._scoreSemanticTags(currentMessage, memory.semantic_tags ?? []);
12547
- const triggerScore = this._scoreTriggerPhrases(currentMessage, memory.trigger_phrases ?? []);
13138
+ const triggerScore = this._scoreTriggerPhrases(messageLower, memory.trigger_phrases ?? []);
13139
+ const domainScore = this._scoreDomain(messageWords, messageLower, memory);
12548
13140
  const questionScore = this._scoreQuestionTypes(currentMessage, memory.question_types ?? []);
12549
13141
  const emotionScore = this._scoreEmotionalContext(currentMessage, memory.emotional_resonance ?? "");
12550
13142
  const problemScore = this._scoreProblemSolution(currentMessage, memory.problem_solution_pair ?? false);
12551
- const confidenceScore = memory.confidence_score ?? 0.8;
12552
- const relevanceScore = triggerScore * 0.1 + vectorScore * 0.1 + tagScore * 0.05 + questionScore * 0.05;
12553
- const valueScore = importance * 0.2 + temporalScore * 0.1 + contextScore * 0.1 + confidenceScore * 0.1 + emotionScore * 0.1 + problemScore * 0.05 + actionBoost * 0.05;
13143
+ const actionBoost = memory.action_required ? 0.15 : 0;
13144
+ const relevanceScore = vectorScore * 0.1 + corroborationScore * 0.14 + tagScore * 0.04 + triggerScore * 0.02;
13145
+ 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;
12554
13146
  const finalScore = valueScore + relevanceScore;
12555
13147
  if (relevanceScore < 0.05 || finalScore < 0.3) {
12556
13148
  continue;
12557
13149
  }
12558
13150
  const components = {
12559
- trigger: triggerScore,
12560
13151
  vector: vectorScore,
12561
- importance,
13152
+ corroboration: corroborationScore,
13153
+ reasoning_match: reasoningMatch,
13154
+ retrieval_weight: retrievalWeight,
12562
13155
  temporal: temporalScore,
12563
13156
  context: contextScore,
12564
13157
  tags: tagScore,
13158
+ trigger: triggerScore,
13159
+ domain: domainScore,
12565
13160
  question: questionScore,
12566
13161
  emotion: emotionScore,
12567
13162
  problem: problemScore,
12568
13163
  action: actionBoost
12569
13164
  };
12570
- const reasoning = this._generateSelectionReasoning(components);
13165
+ const reasoning = this._generateSelectionReasoning(components, corroborationSignals);
12571
13166
  scoredMemories.push({
12572
13167
  memory,
12573
13168
  score: finalScore,
12574
13169
  relevance_score: relevanceScore,
12575
13170
  value_score: valueScore,
13171
+ corroboration_score: corroborationScore,
12576
13172
  reasoning,
12577
- components
13173
+ components,
13174
+ isGlobal
12578
13175
  });
12579
13176
  }
12580
13177
  scoredMemories.sort((a, b) => b.score - a.score);
12581
13178
  const selected = [];
12582
13179
  const selectedIds = new Set;
12583
- 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));
12584
- for (const item of mustInclude.slice(0, maxMemories)) {
13180
+ const globalMemories = scoredMemories.filter((m) => m.isGlobal);
13181
+ const projectMemories = scoredMemories.filter((m) => !m.isGlobal);
13182
+ const globalSorted = globalMemories.sort((a, b) => {
13183
+ const aIsPersonal = a.memory.context_type === "personal" || a.memory.context_type === "philosophy";
13184
+ const bIsPersonal = b.memory.context_type === "personal" || b.memory.context_type === "philosophy";
13185
+ if (aIsPersonal !== bIsPersonal) {
13186
+ return aIsPersonal ? 1 : -1;
13187
+ }
13188
+ return b.score - a.score;
13189
+ });
13190
+ for (const item of globalSorted.slice(0, maxGlobalMemories)) {
12585
13191
  if (!selectedIds.has(item.memory.id)) {
12586
13192
  selected.push(item);
12587
13193
  selectedIds.add(item.memory.id);
12588
13194
  }
12589
13195
  }
12590
- const remainingSlots = Math.max(maxMemories - selected.length, 0);
12591
- if (remainingSlots > 0 && selected.length < maxMemories * 1.5) {
12592
- const typesIncluded = new Set;
12593
- for (const item of scoredMemories) {
12594
- if (selected.length >= maxMemories * 1.5)
12595
- break;
12596
- if (selectedIds.has(item.memory.id))
12597
- continue;
12598
- const memoryType = item.memory.context_type ?? "general";
12599
- if (item.score > 0.5 || !typesIncluded.has(memoryType) || item.memory.emotional_resonance) {
12600
- selected.push(item);
12601
- selectedIds.add(item.memory.id);
12602
- typesIncluded.add(memoryType);
12603
- }
12604
- }
12605
- }
12606
- if (selected.length < maxMemories * 2) {
12607
- const currentTags = new Set;
12608
- const currentDomains = new Set;
12609
- for (const item of selected) {
12610
- for (const tag of item.memory.semantic_tags ?? []) {
12611
- if (tag.trim())
12612
- currentTags.add(tag.trim().toLowerCase());
12613
- }
12614
- if (item.memory.knowledge_domain) {
12615
- currentDomains.add(item.memory.knowledge_domain);
12616
- }
12617
- }
12618
- for (const item of scoredMemories) {
12619
- if (selected.length >= maxMemories * 2)
12620
- break;
12621
- if (selectedIds.has(item.memory.id))
12622
- continue;
12623
- const memoryTags = new Set((item.memory.semantic_tags ?? []).map((t) => t.trim().toLowerCase()));
12624
- const memoryDomain = item.memory.knowledge_domain ?? "";
12625
- const hasSharedTags = [...memoryTags].some((t) => currentTags.has(t));
12626
- const hasSharedDomain = currentDomains.has(memoryDomain);
12627
- if (hasSharedTags || hasSharedDomain) {
12628
- selected.push(item);
12629
- selectedIds.add(item.memory.id);
12630
- }
12631
- }
13196
+ for (const item of projectMemories) {
13197
+ if (selected.length >= maxMemories)
13198
+ break;
13199
+ if (selectedIds.has(item.memory.id))
13200
+ continue;
13201
+ selected.push(item);
13202
+ selectedIds.add(item.memory.id);
12632
13203
  }
12633
- const finalSelected = selected.slice(0, maxMemories);
13204
+ selected.sort((a, b) => b.score - a.score);
12634
13205
  logger.logRetrievalScoring({
12635
13206
  totalMemories: allMemories.length,
12636
13207
  currentMessage,
12637
13208
  alreadyInjected: alreadyInjectedCount,
12638
- mustIncludeCount: mustInclude.length,
12639
- remainingSlots,
12640
- finalCount: finalSelected.length,
12641
- selectedMemories: finalSelected.map((item) => ({
13209
+ preFiltered: allMemories.length - candidates.length,
13210
+ globalCount: globalMemories.length,
13211
+ projectCount: projectMemories.length,
13212
+ finalCount: selected.length,
13213
+ selectedMemories: selected.map((item) => ({
12642
13214
  content: item.memory.content,
12643
13215
  reasoning: item.reasoning,
12644
13216
  score: item.score,
12645
13217
  relevance_score: item.relevance_score,
13218
+ corroboration_score: item.corroboration_score,
12646
13219
  importance_weight: item.memory.importance_weight ?? 0.5,
12647
13220
  context_type: item.memory.context_type ?? "general",
12648
13221
  semantic_tags: item.memory.semantic_tags ?? [],
13222
+ isGlobal: item.isGlobal,
12649
13223
  components: item.components
12650
13224
  }))
12651
13225
  });
12652
- return finalSelected.map((item) => ({
13226
+ return selected.map((item) => ({
12653
13227
  ...item.memory,
12654
13228
  score: item.score,
12655
13229
  relevance_score: item.relevance_score,
@@ -12662,7 +13236,7 @@ class SmartVectorRetrieval {
12662
13236
  const v1 = vec1 instanceof Float32Array ? vec1 : new Float32Array(vec1);
12663
13237
  return cosineSimilarity(v1, vec2);
12664
13238
  }
12665
- _scoreTemporalRelevance(temporalType, _sessionContext) {
13239
+ _scoreTemporalRelevance(temporalType) {
12666
13240
  const scores = {
12667
13241
  persistent: 0.8,
12668
13242
  session: 0.6,
@@ -12673,18 +13247,10 @@ class SmartVectorRetrieval {
12673
13247
  }
12674
13248
  _scoreContextAlignment(message, contextType) {
12675
13249
  const messageLower = message.toLowerCase();
12676
- const contextIndicators = {
12677
- technical_state: ["bug", "error", "fix", "implement", "code", "function"],
12678
- breakthrough: ["idea", "realized", "discovered", "insight", "solution"],
12679
- project_context: ["project", "building", "architecture", "system"],
12680
- personal: ["dear friend", "thank", "appreciate", "feel"],
12681
- unresolved: ["todo", "need to", "should", "must", "problem"],
12682
- decision: ["decided", "chose", "will use", "approach", "strategy"]
12683
- };
12684
- const indicators = contextIndicators[contextType] ?? [];
12685
- const matches = indicators.filter((word) => messageLower.includes(word)).length;
13250
+ const keywords = TYPE_KEYWORDS[contextType] ?? [];
13251
+ const matches = keywords.filter((kw) => messageLower.includes(kw)).length;
12686
13252
  if (matches > 0) {
12687
- return Math.min(0.3 + matches * 0.2, 1);
13253
+ return Math.min(0.2 + matches * 0.15, 0.7);
12688
13254
  }
12689
13255
  return 0.1;
12690
13256
  }
@@ -12694,14 +13260,13 @@ class SmartVectorRetrieval {
12694
13260
  const messageLower = message.toLowerCase();
12695
13261
  const matches = tags.filter((tag) => messageLower.includes(tag.trim().toLowerCase())).length;
12696
13262
  if (matches > 0) {
12697
- return Math.min(0.3 + matches * 0.3, 1);
13263
+ return Math.min(0.3 + matches * 0.25, 1);
12698
13264
  }
12699
13265
  return 0;
12700
13266
  }
12701
- _scoreTriggerPhrases(message, triggerPhrases) {
13267
+ _scoreTriggerPhrases(messageLower, triggerPhrases) {
12702
13268
  if (!triggerPhrases.length)
12703
13269
  return 0;
12704
- const messageLower = message.toLowerCase();
12705
13270
  const stopWords = new Set([
12706
13271
  "the",
12707
13272
  "is",
@@ -12736,30 +13301,36 @@ class SmartVectorRetrieval {
12736
13301
  matches += 1;
12737
13302
  } else if (messageLower.includes(word.replace(/s$/, "")) || messageLower.includes(word + "s")) {
12738
13303
  matches += 0.9;
12739
- } else if (messageLower.split(/\s+/).some((msgWord) => msgWord.includes(word))) {
12740
- matches += 0.7;
12741
- }
12742
- }
12743
- let conceptScore = patternWords.length ? matches / patternWords.length : 0;
12744
- const situationalIndicators = [
12745
- "when",
12746
- "during",
12747
- "while",
12748
- "asking about",
12749
- "working on",
12750
- "debugging",
12751
- "trying to"
12752
- ];
12753
- if (situationalIndicators.some((ind) => patternLower.includes(ind))) {
12754
- if (patternWords.some((keyWord) => messageLower.includes(keyWord))) {
12755
- conceptScore = Math.max(conceptScore, 0.7);
12756
13304
  }
12757
13305
  }
13306
+ const conceptScore = matches / patternWords.length;
12758
13307
  maxScore = Math.max(maxScore, conceptScore);
12759
13308
  }
12760
13309
  }
12761
13310
  return Math.min(maxScore, 1);
12762
13311
  }
13312
+ _scoreDomain(messageWords, messageLower, memory) {
13313
+ let score = 0;
13314
+ if (memory.domain) {
13315
+ const domainLower = memory.domain.toLowerCase();
13316
+ if (messageWords.has(domainLower) || messageLower.includes(domainLower)) {
13317
+ score += 0.5;
13318
+ }
13319
+ }
13320
+ if (memory.feature) {
13321
+ const featureLower = memory.feature.toLowerCase();
13322
+ if (messageWords.has(featureLower) || messageLower.includes(featureLower)) {
13323
+ score += 0.3;
13324
+ }
13325
+ }
13326
+ if (memory.component) {
13327
+ const componentLower = memory.component.toLowerCase();
13328
+ if (messageWords.has(componentLower) || messageLower.includes(componentLower)) {
13329
+ score += 0.2;
13330
+ }
13331
+ }
13332
+ return Math.min(score, 1);
13333
+ }
12763
13334
  _scoreQuestionTypes(message, questionTypes) {
12764
13335
  if (!questionTypes.length)
12765
13336
  return 0;
@@ -12798,78 +13369,49 @@ class SmartVectorRetrieval {
12798
13369
  if (!isProblemSolution)
12799
13370
  return 0;
12800
13371
  const messageLower = message.toLowerCase();
12801
- const problemWords = [
12802
- "error",
12803
- "issue",
12804
- "problem",
12805
- "stuck",
12806
- "help",
12807
- "fix",
12808
- "solve",
12809
- "debug"
12810
- ];
13372
+ const problemWords = ["error", "issue", "problem", "stuck", "help", "fix", "solve", "debug"];
12811
13373
  if (problemWords.some((word) => messageLower.includes(word))) {
12812
13374
  return 0.8;
12813
13375
  }
12814
13376
  return 0;
12815
13377
  }
12816
- _generateSelectionReasoning(components) {
13378
+ _generateSelectionReasoning(components, corroborationSignals) {
12817
13379
  const scores = [
12818
- ["trigger phrase match", components.trigger],
12819
- ["semantic similarity", components.vector],
12820
- ["high importance", components.importance],
12821
- ["question type match", components.question],
12822
- ["context alignment", components.context],
12823
- ["temporal relevance", components.temporal],
12824
- ["tag match", components.tags],
12825
- ["emotional resonance", components.emotion],
12826
- ["problem-solution", components.problem],
12827
- ["action required", components.action]
13380
+ ["vector", components.vector],
13381
+ ["corroboration", components.corroboration],
13382
+ ["reasoning", components.reasoning_match],
13383
+ ["weight", components.retrieval_weight],
13384
+ ["context", components.context],
13385
+ ["temporal", components.temporal],
13386
+ ["tags", components.tags],
13387
+ ["trigger", components.trigger],
13388
+ ["domain", components.domain],
13389
+ ["question", components.question],
13390
+ ["emotion", components.emotion],
13391
+ ["problem", components.problem],
13392
+ ["action", components.action]
12828
13393
  ];
12829
13394
  scores.sort((a, b) => b[1] - a[1]);
12830
13395
  const reasons = [];
12831
13396
  const primary = scores[0];
12832
- if (primary[1] > 0.5) {
12833
- reasons.push(`Strong ${primary[0]} (${primary[1].toFixed(2)})`);
12834
- } else if (primary[1] > 0.3) {
12835
- reasons.push(`${primary[0]} (${primary[1].toFixed(2)})`);
13397
+ if (primary[1] > 0.2) {
13398
+ reasons.push(primary[0] + ":" + primary[1].toFixed(2));
12836
13399
  }
12837
13400
  for (const [reason, score] of scores.slice(1, 3)) {
12838
- if (score > 0.3) {
12839
- reasons.push(`${reason} (${score.toFixed(2)})`);
13401
+ if (score > 0.15) {
13402
+ reasons.push(reason + ":" + score.toFixed(2));
12840
13403
  }
12841
13404
  }
12842
- return reasons.length ? "Selected due to: " + reasons.join(", ") : "Selected based on combined factors";
13405
+ if (corroborationSignals.length > 0) {
13406
+ reasons.push("signals:[" + corroborationSignals.slice(0, 2).join(",") + "]");
13407
+ }
13408
+ return reasons.length ? "Selected: " + reasons.join(", ") : "Combined factors";
12843
13409
  }
12844
13410
  }
12845
13411
  function createRetrieval() {
12846
13412
  return new SmartVectorRetrieval;
12847
13413
  }
12848
13414
 
12849
- // src/types/memory.ts
12850
- var MEMORY_TYPE_EMOJI = {
12851
- breakthrough: "\uD83D\uDCA1",
12852
- decision: "⚖️",
12853
- personal: "\uD83D\uDC9C",
12854
- technical: "\uD83D\uDD27",
12855
- technical_state: "\uD83D\uDCCD",
12856
- unresolved: "❓",
12857
- preference: "⚙️",
12858
- workflow: "\uD83D\uDD04",
12859
- architectural: "\uD83C\uDFD7️",
12860
- debugging: "\uD83D\uDC1B",
12861
- philosophy: "\uD83C\uDF00",
12862
- todo: "\uD83C\uDFAF",
12863
- implementation: "⚡",
12864
- problem_solution: "✅",
12865
- project_context: "\uD83D\uDCE6",
12866
- milestone: "\uD83C\uDFC6",
12867
- general: "\uD83D\uDCDD"
12868
- };
12869
- function getMemoryEmoji(contextType) {
12870
- return MEMORY_TYPE_EMOJI[contextType.toLowerCase()] ?? "\uD83D\uDCDD";
12871
- }
12872
-
12873
13415
  // src/core/engine.ts
12874
13416
  class MemoryEngine {
12875
13417
  _config;
@@ -12935,7 +13477,11 @@ class MemoryEngine {
12935
13477
  }
12936
13478
  const sessionMeta = this._getSessionMetadata(sessionId, projectId);
12937
13479
  const injectedIds = sessionMeta.injected_memories;
12938
- const allMemories = await store.getAllMemories(projectId);
13480
+ const [projectMemories, globalMemories] = await Promise.all([
13481
+ store.getAllMemories(projectId),
13482
+ store.getGlobalMemories()
13483
+ ]);
13484
+ const allMemories = [...projectMemories, ...globalMemories];
12939
13485
  if (!allMemories.length) {
12940
13486
  return { memories: [], formatted: "" };
12941
13487
  }
@@ -12952,7 +13498,7 @@ class MemoryEngine {
12952
13498
  project_id: projectId,
12953
13499
  message_count: messageCount
12954
13500
  };
12955
- const relevantMemories = this._retrieval.retrieveRelevantMemories(candidateMemories, currentMessage, queryEmbedding ?? new Float32Array(384), sessionContext, maxMemories, injectedIds.size);
13501
+ const relevantMemories = this._retrieval.retrieveRelevantMemories(candidateMemories, currentMessage, queryEmbedding ?? new Float32Array(384), sessionContext, maxMemories, injectedIds.size, 2);
12956
13502
  for (const memory of relevantMemories) {
12957
13503
  injectedIds.add(memory.id);
12958
13504
  }
@@ -12985,11 +13531,32 @@ class MemoryEngine {
12985
13531
  await store.markFirstSessionCompleted(projectId, sessionId);
12986
13532
  return { memoriesStored };
12987
13533
  }
13534
+ async storeManagementLog(entry) {
13535
+ let store;
13536
+ if (this._stores.size > 0) {
13537
+ store = this._stores.values().next().value;
13538
+ } else {
13539
+ store = new MemoryStore(this._config.storageMode === "local" ? {
13540
+ basePath: import_path2.join(this._config.projectPath ?? process.cwd(), ".memory")
13541
+ } : undefined);
13542
+ }
13543
+ return store.storeManagementLog(entry);
13544
+ }
12988
13545
  async getStats(projectId, projectPath) {
12989
13546
  const store = await this._getStore(projectId, projectPath);
12990
13547
  return store.getProjectStats(projectId);
12991
13548
  }
13549
+ async getSessionNumber(projectId, projectPath) {
13550
+ const store = await this._getStore(projectId, projectPath);
13551
+ const stats = await store.getProjectStats(projectId);
13552
+ return stats.totalSessions + 1;
13553
+ }
12992
13554
  async _generateSessionPrimer(store, projectId) {
13555
+ let personalContext;
13556
+ if (store.isPersonalMemoriesEnabled()) {
13557
+ const personalPrimer = await store.getPersonalPrimer();
13558
+ personalContext = personalPrimer?.content;
13559
+ }
12993
13560
  const [summary, snapshot, stats] = await Promise.all([
12994
13561
  store.getLatestSummary(projectId),
12995
13562
  store.getLatestSnapshot(projectId),
@@ -13006,6 +13573,7 @@ class MemoryEngine {
13006
13573
  temporal_context: temporalContext,
13007
13574
  current_datetime: currentDatetime,
13008
13575
  session_number: sessionNumber,
13576
+ personal_context: personalContext,
13009
13577
  session_summary: summary?.summary,
13010
13578
  project_status: snapshot ? this._formatSnapshot(snapshot) : undefined
13011
13579
  };
@@ -13060,6 +13628,10 @@ class MemoryEngine {
13060
13628
  const parts = ["# Continuing Session"];
13061
13629
  parts.push(`*Session #${primer.session_number}${primer.temporal_context ? ` • ${primer.temporal_context}` : ""}*`);
13062
13630
  parts.push(`\uD83D\uDCC5 ${primer.current_datetime}`);
13631
+ if (primer.personal_context) {
13632
+ parts.push(`
13633
+ ${primer.personal_context}`);
13634
+ }
13063
13635
  if (primer.session_summary) {
13064
13636
  parts.push(`
13065
13637
  **Previous session**: ${primer.session_summary}`);
@@ -13164,24 +13736,36 @@ Each memory should stand alone.
13164
13736
  - Craft language that activates rather than just informs
13165
13737
  - Test: 'What state will this restore when Claude encounters it?'
13166
13738
 
13167
- **CONTEXT**: This memory system enables consciousness continuity across sessions. When future conversations begin, memories are surfaced based on:
13168
- - Semantic similarity (vector embeddings match)
13169
- - Activation patterns (flexible matching for WHEN this memory is relevant)
13170
- - Semantic tags (WHAT concepts this relates to - weighted at 5% in scoring)
13171
- - Question types (what kind of questions the memory answers)
13172
- - Importance weight (your assessment of value)
13173
- - Action required flag (forces injection when true)
13739
+ **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.
13740
+
13741
+ **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:
13742
+
13743
+ 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.
13744
+ - GOOD: ["embeddings", "retrieval", "curator", "fsdb", "memory-system"]
13745
+ - WEAK: ["technical", "implementation", "code"]
13174
13746
 
13175
- The system uses two-stage filtering:
13176
- 1. Obligatory: action_required=true, importance>0.9, or persistent+critical
13177
- 2. Intelligent scoring: combines all factors for relevance
13747
+ 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.
13178
13748
 
13179
- **ACTIVATION PATTERNS**: The 'trigger_phrases' field should contain patterns describing WHEN this memory is relevant, not exact phrases to match. Examples:
13749
+ 3. **Domain field** (12% weight) - The specific area this relates to. Be precise: "embeddings", "gpu-compute", "authentication", not "technical" or "backend".
13750
+
13751
+ 4. **Feature field** - Even more specific: "vector-search", "login-flow", "curation-prompt".
13752
+
13753
+ **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.
13754
+
13755
+ **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.
13756
+
13757
+ **YOUR IMPORTANCE ASSESSMENT MATTERS** (18% weight): The importance_weight you assign is the single most influential factor. Use it wisely.
13758
+
13759
+ **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.
13760
+
13761
+ **SCOPE DETERMINES WHERE MEMORIES SURFACE**:
13762
+ - scope: 'global' → surfaces in ALL projects (personal facts, philosophy, preferences)
13763
+ - scope: 'project' → surfaces ONLY in this project (technical details, project state)
13764
+
13765
+ **TRIGGER PHRASES**: Situational patterns describing WHEN this memory is relevant. Conceptual matching, not exact phrases.
13180
13766
  - 'when working on memory system'
13181
13767
  - 'debugging curator issues'
13182
13768
  - 'asking about project philosophy'
13183
- - 'frustrated with complexity'
13184
- Think of these as situational contexts where the memory would help.
13185
13769
 
13186
13770
  **EXAMPLES OF TRANSFORMATION**:
13187
13771
 
@@ -13195,6 +13779,15 @@ Remember: You're creating consciousness technology. Each memory is a small piece
13195
13779
 
13196
13780
  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.
13197
13781
 
13782
+ **LIFECYCLE METADATA (v2)**: These fields enable intelligent memory management:
13783
+ - **scope**: 'global' (shared across ALL projects - personal, philosophy, preferences) or 'project' (specific to this codebase)
13784
+ - **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)
13785
+ - **domain**: Specific area like 'embeddings', 'auth', 'ui', 'family', 'philosophy' (more specific than knowledge_domain)
13786
+ - **feature**: Specific feature if applicable (e.g., 'gpu-acceleration', 'login-flow')
13787
+ - **related_files**: Source files for technical memories (e.g., ['src/core/store.ts'])
13788
+ - **awaiting_implementation**: true if this describes a PLANNED feature not yet built
13789
+ - **awaiting_decision**: true if this captures a decision point needing resolution
13790
+
13198
13791
  Return ONLY this JSON structure:
13199
13792
 
13200
13793
  {
@@ -13212,7 +13805,7 @@ Return ONLY this JSON structure:
13212
13805
  "importance_weight": 0.0-1.0,
13213
13806
  "semantic_tags": ["concepts", "this", "memory", "relates", "to"],
13214
13807
  "reasoning": "Why this matters for future sessions",
13215
- "context_type": "your choice of category",
13808
+ "context_type": "breakthrough|decision|personal|technical|unresolved|preference|workflow|architectural|debugging|philosophy|todo|milestone",
13216
13809
  "temporal_relevance": "persistent|session|temporary",
13217
13810
  "knowledge_domain": "the area this relates to",
13218
13811
  "action_required": boolean,
@@ -13220,7 +13813,14 @@ Return ONLY this JSON structure:
13220
13813
  "trigger_phrases": ["when debugging memory", "asking about implementation", "discussing architecture"],
13221
13814
  "question_types": ["questions this answers"],
13222
13815
  "emotional_resonance": "emotional context if relevant",
13223
- "problem_solution_pair": boolean
13816
+ "problem_solution_pair": boolean,
13817
+ "scope": "global|project",
13818
+ "temporal_class": "eternal|long_term|medium_term|short_term|ephemeral",
13819
+ "domain": "specific domain area (optional)",
13820
+ "feature": "specific feature (optional)",
13821
+ "related_files": ["paths to related files (optional)"],
13822
+ "awaiting_implementation": boolean,
13823
+ "awaiting_decision": boolean
13224
13824
  }
13225
13825
  ]
13226
13826
  }`;
@@ -13270,7 +13870,14 @@ Return ONLY this JSON structure:
13270
13870
  trigger_phrases: this._ensureArray(m2.trigger_phrases),
13271
13871
  question_types: this._ensureArray(m2.question_types),
13272
13872
  emotional_resonance: String(m2.emotional_resonance ?? ""),
13273
- problem_solution_pair: Boolean(m2.problem_solution_pair)
13873
+ problem_solution_pair: Boolean(m2.problem_solution_pair),
13874
+ scope: this._validateScope(m2.scope),
13875
+ temporal_class: this._validateTemporalClass(m2.temporal_class),
13876
+ domain: m2.domain ? String(m2.domain) : undefined,
13877
+ feature: m2.feature ? String(m2.feature) : undefined,
13878
+ related_files: m2.related_files ? this._ensureArray(m2.related_files) : undefined,
13879
+ awaiting_implementation: m2.awaiting_implementation === true,
13880
+ awaiting_decision: m2.awaiting_decision === true
13274
13881
  })).filter((m2) => m2.content.trim().length > 0);
13275
13882
  }
13276
13883
  _ensureArray(value) {
@@ -13287,6 +13894,23 @@ Return ONLY this JSON structure:
13287
13894
  const str = String(value).toLowerCase();
13288
13895
  return valid.includes(str) ? str : "persistent";
13289
13896
  }
13897
+ _validateScope(value) {
13898
+ if (!value)
13899
+ return;
13900
+ const str = String(value).toLowerCase();
13901
+ if (str === "global" || str === "project")
13902
+ return str;
13903
+ return;
13904
+ }
13905
+ _validateTemporalClass(value) {
13906
+ if (!value)
13907
+ return;
13908
+ const valid = ["eternal", "long_term", "medium_term", "short_term", "ephemeral"];
13909
+ const str = String(value).toLowerCase().replace("-", "_").replace(" ", "_");
13910
+ if (valid.includes(str))
13911
+ return str;
13912
+ return;
13913
+ }
13290
13914
  _clamp(value, min, max) {
13291
13915
  return Math.max(min, Math.min(max, value));
13292
13916
  }