@rlabs-inc/memory 0.4.2 → 0.4.3

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.
@@ -41072,6 +41072,7 @@ var logger = {
41072
41072
  { name: "domain", count: signalBreakdown.domain },
41073
41073
  { name: "feature", count: signalBreakdown.feature },
41074
41074
  { name: "content", count: signalBreakdown.content },
41075
+ { name: "files", count: signalBreakdown.files },
41075
41076
  { name: "vector", count: signalBreakdown.vector }
41076
41077
  ];
41077
41078
  for (const sig of signals) {
@@ -41088,7 +41089,7 @@ var logger = {
41088
41089
  if (Object.keys(buckets).length > 0) {
41089
41090
  console.log(` ${style("bold", "Distribution:")}`);
41090
41091
  const maxBucketCount = Math.max(...Object.values(buckets), 1);
41091
- const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals"];
41092
+ const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals", "7 signals"];
41092
41093
  for (const bucket of bucketOrder) {
41093
41094
  const count = buckets[bucket] ?? 0;
41094
41095
  if (count > 0 || bucket === "2 signals") {
@@ -41104,7 +41105,7 @@ var logger = {
41104
41105
  };
41105
41106
 
41106
41107
  // src/types/memory.ts
41107
- var V3_DEFAULTS = {
41108
+ var V4_DEFAULTS = {
41108
41109
  typeDefaults: {
41109
41110
  personal: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
41110
41111
  philosophy: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
@@ -41129,7 +41130,7 @@ var V3_DEFAULTS = {
41129
41130
  exclude_from_retrieval: false
41130
41131
  }
41131
41132
  };
41132
- var V2_DEFAULTS = V3_DEFAULTS;
41133
+ var V2_DEFAULTS = V4_DEFAULTS;
41133
41134
  var MEMORY_TYPE_EMOJI = {
41134
41135
  technical: "\uD83D\uDD27",
41135
41136
  debug: "\uD83D\uDC1B",
@@ -41148,8 +41149,9 @@ function getMemoryEmoji(contextType) {
41148
41149
  }
41149
41150
 
41150
41151
  // src/types/schema.ts
41151
- var MEMORY_SCHEMA_VERSION = 3;
41152
+ var MEMORY_SCHEMA_VERSION = 4;
41152
41153
  var memorySchema = {
41154
+ headline: "string",
41153
41155
  content: "string",
41154
41156
  reasoning: "string",
41155
41157
  importance_weight: "number",
@@ -41278,6 +41280,7 @@ class MemoryStore {
41278
41280
  const { memories } = await this.getGlobal();
41279
41281
  return memories.all().map((record) => ({
41280
41282
  id: record.id,
41283
+ headline: record.headline ?? "",
41281
41284
  content: record.content,
41282
41285
  reasoning: record.reasoning,
41283
41286
  importance_weight: record.importance_weight,
@@ -41302,6 +41305,7 @@ class MemoryStore {
41302
41305
  const contextType = memory.context_type ?? "personal";
41303
41306
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.personal;
41304
41307
  const id = memories.insert({
41308
+ headline: memory.headline ?? "",
41305
41309
  content: memory.content,
41306
41310
  reasoning: memory.reasoning,
41307
41311
  importance_weight: memory.importance_weight,
@@ -41457,6 +41461,7 @@ class MemoryStore {
41457
41461
  const contextType = memory.context_type ?? "general";
41458
41462
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.technical;
41459
41463
  const id = memories.insert({
41464
+ headline: memory.headline ?? "",
41460
41465
  content: memory.content,
41461
41466
  reasoning: memory.reasoning,
41462
41467
  importance_weight: memory.importance_weight,
@@ -41499,6 +41504,7 @@ class MemoryStore {
41499
41504
  const { memories } = await this.getProject(projectId);
41500
41505
  return memories.all().map((record) => ({
41501
41506
  id: record.id,
41507
+ headline: record.headline ?? "",
41502
41508
  content: record.content,
41503
41509
  reasoning: record.reasoning,
41504
41510
  importance_weight: record.importance_weight,
@@ -41818,14 +41824,42 @@ class SmartVectorRetrieval {
41818
41824
  const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !STOPWORDS.has(w));
41819
41825
  return new Set(words);
41820
41826
  }
41827
+ _extractFilePaths(text) {
41828
+ const paths = new Set;
41829
+ const pathPattern = /(?:^|[\s'"(])([.\/\\]?(?:[\w.-]+[\/\\])+[\w.-]+(?:\.\w+)?)/g;
41830
+ let match;
41831
+ while ((match = pathPattern.exec(text)) !== null) {
41832
+ const path = match[1].toLowerCase();
41833
+ paths.add(path);
41834
+ const filename = path.split(/[\/\\]/).pop();
41835
+ if (filename)
41836
+ paths.add(filename);
41837
+ }
41838
+ return paths;
41839
+ }
41840
+ _checkFilesActivation(messagePaths, relatedFiles) {
41841
+ if (!relatedFiles?.length || !messagePaths.size)
41842
+ return false;
41843
+ for (const file of relatedFiles) {
41844
+ const fileLower = file.toLowerCase();
41845
+ for (const msgPath of messagePaths) {
41846
+ if (fileLower.includes(msgPath) || msgPath.includes(fileLower)) {
41847
+ return true;
41848
+ }
41849
+ }
41850
+ const filename = fileLower.split(/[\/\\]/).pop();
41851
+ if (filename && messagePaths.has(filename)) {
41852
+ return true;
41853
+ }
41854
+ }
41855
+ return false;
41856
+ }
41821
41857
  _preFilter(memories, currentProjectId, messageLower) {
41822
41858
  return memories.filter((memory) => {
41823
41859
  if (memory.status && memory.status !== "active")
41824
41860
  return false;
41825
41861
  if (memory.exclude_from_retrieval === true)
41826
41862
  return false;
41827
- if (memory.superseded_by)
41828
- return false;
41829
41863
  const isGlobal = memory.scope === "global" || memory.project_id === "global";
41830
41864
  if (!isGlobal && memory.project_id !== currentProjectId)
41831
41865
  return false;
@@ -41976,19 +42010,25 @@ class SmartVectorRetrieval {
41976
42010
  }
41977
42011
  const messageLower = currentMessage.toLowerCase();
41978
42012
  const messageWords = this._extractSignificantWords(currentMessage);
42013
+ const messagePaths = this._extractFilePaths(currentMessage);
42014
+ const memoryById = new Map;
42015
+ for (const m of allMemories) {
42016
+ memoryById.set(m.id, m);
42017
+ }
41979
42018
  const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
41980
42019
  if (!candidates.length) {
41981
42020
  return [];
41982
42021
  }
41983
42022
  const activatedMemories = [];
42023
+ const activatedIds = new Set;
41984
42024
  let rejectedCount = 0;
41985
42025
  for (const memory of candidates) {
41986
- const isGlobal = memory.scope === "global" || memory.project_id === "global";
41987
42026
  const triggerResult = this._checkTriggerActivation(messageLower, messageWords, memory.trigger_phrases ?? []);
41988
42027
  const tagResult = this._checkTagActivation(messageLower, messageWords, memory.semantic_tags ?? []);
41989
42028
  const domainActivated = this._checkDomainActivation(messageLower, messageWords, memory.domain);
41990
42029
  const featureActivated = this._checkFeatureActivation(messageLower, messageWords, memory.feature);
41991
42030
  const contentActivated = this._checkContentActivation(messageWords, memory);
42031
+ const filesActivated = this._checkFilesActivation(messagePaths, memory.related_files);
41992
42032
  const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
41993
42033
  let signalCount = 0;
41994
42034
  if (triggerResult.activated)
@@ -42001,6 +42041,8 @@ class SmartVectorRetrieval {
42001
42041
  signalCount++;
42002
42042
  if (contentActivated)
42003
42043
  signalCount++;
42044
+ if (filesActivated)
42045
+ signalCount++;
42004
42046
  if (vectorSimilarity >= 0.4)
42005
42047
  signalCount++;
42006
42048
  const signals = {
@@ -42009,6 +42051,7 @@ class SmartVectorRetrieval {
42009
42051
  domain: domainActivated,
42010
42052
  feature: featureActivated,
42011
42053
  content: contentActivated,
42054
+ files: filesActivated,
42012
42055
  count: signalCount,
42013
42056
  triggerStrength: triggerResult.strength,
42014
42057
  tagCount: tagResult.count,
@@ -42018,13 +42061,27 @@ class SmartVectorRetrieval {
42018
42061
  rejectedCount++;
42019
42062
  continue;
42020
42063
  }
42021
- const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords);
42064
+ let memoryToSurface = memory;
42065
+ if (memory.superseded_by || memory.resolved_by) {
42066
+ const replacementId = memory.superseded_by ?? memory.resolved_by;
42067
+ const replacement = replacementId ? memoryById.get(replacementId) : undefined;
42068
+ if (replacement && replacement.status !== "archived" && replacement.status !== "deprecated") {
42069
+ memoryToSurface = replacement;
42070
+ logger.debug(`Redirect: ${memory.id.slice(-8)} → ${replacement.id.slice(-8)} (${memory.superseded_by ? "superseded" : "resolved"})`, "retrieval");
42071
+ }
42072
+ }
42073
+ if (activatedIds.has(memoryToSurface.id)) {
42074
+ continue;
42075
+ }
42076
+ const isGlobal = memoryToSurface.scope === "global" || memoryToSurface.project_id === "global";
42077
+ const importanceScore = this._calculateImportanceScore(memoryToSurface, signalCount, messageLower, messageWords);
42022
42078
  activatedMemories.push({
42023
- memory,
42079
+ memory: memoryToSurface,
42024
42080
  signals,
42025
42081
  importanceScore,
42026
42082
  isGlobal
42027
42083
  });
42084
+ activatedIds.add(memoryToSurface.id);
42028
42085
  }
42029
42086
  this._logActivationDistribution(activatedMemories, candidates.length, rejectedCount);
42030
42087
  this._logVectorStats();
@@ -42094,11 +42151,19 @@ class SmartVectorRetrieval {
42094
42151
  selectedIds.add(item.memory.id);
42095
42152
  }
42096
42153
  if (selected.length < maxMemories) {
42097
- const relatedIds = new Set;
42154
+ const linkedIds = new Set;
42098
42155
  for (const item of selected) {
42099
42156
  for (const relatedId of item.memory.related_to ?? []) {
42100
42157
  if (!selectedIds.has(relatedId)) {
42101
- relatedIds.add(relatedId);
42158
+ linkedIds.add(relatedId);
42159
+ }
42160
+ }
42161
+ if (item.memory.blocked_by && !selectedIds.has(item.memory.blocked_by)) {
42162
+ linkedIds.add(item.memory.blocked_by);
42163
+ }
42164
+ for (const blockedId of item.memory.blocks ?? []) {
42165
+ if (!selectedIds.has(blockedId)) {
42166
+ linkedIds.add(blockedId);
42102
42167
  }
42103
42168
  }
42104
42169
  }
@@ -42107,9 +42172,30 @@ class SmartVectorRetrieval {
42107
42172
  break;
42108
42173
  if (selectedIds.has(item.memory.id))
42109
42174
  continue;
42110
- if (relatedIds.has(item.memory.id)) {
42175
+ if (linkedIds.has(item.memory.id)) {
42111
42176
  selected.push(item);
42112
42177
  selectedIds.add(item.memory.id);
42178
+ logger.debug(`Linked: ${item.memory.id.slice(-8)} pulled by relationship`, "retrieval");
42179
+ }
42180
+ }
42181
+ if (selected.length < maxMemories) {
42182
+ for (const linkedId of linkedIds) {
42183
+ if (selected.length >= maxMemories)
42184
+ break;
42185
+ if (selectedIds.has(linkedId))
42186
+ continue;
42187
+ const linkedMemory = memoryById.get(linkedId);
42188
+ if (linkedMemory && linkedMemory.status !== "archived" && linkedMemory.status !== "deprecated") {
42189
+ const isGlobal = linkedMemory.scope === "global" || linkedMemory.project_id === "global";
42190
+ selected.push({
42191
+ memory: linkedMemory,
42192
+ signals: { trigger: false, tags: false, domain: false, feature: false, content: false, files: false, count: 0, triggerStrength: 0, tagCount: 0, vectorSimilarity: 0 },
42193
+ importanceScore: linkedMemory.importance_weight ?? 0.5,
42194
+ isGlobal
42195
+ });
42196
+ selectedIds.add(linkedId);
42197
+ logger.debug(`Linked (direct): ${linkedId.slice(-8)} pulled for context`, "retrieval");
42198
+ }
42113
42199
  }
42114
42200
  }
42115
42201
  }
@@ -42139,6 +42225,7 @@ class SmartVectorRetrieval {
42139
42225
  domain: item.signals.domain,
42140
42226
  feature: item.signals.feature,
42141
42227
  content: item.signals.content,
42228
+ files: item.signals.files,
42142
42229
  vector: item.signals.vectorSimilarity >= 0.4,
42143
42230
  vectorSimilarity: item.signals.vectorSimilarity
42144
42231
  }
@@ -42146,8 +42233,8 @@ class SmartVectorRetrieval {
42146
42233
  });
42147
42234
  return selected.map((item) => ({
42148
42235
  ...item.memory,
42149
- score: item.signals.count / 6,
42150
- relevance_score: item.signals.count / 6,
42236
+ score: item.signals.count / 7,
42237
+ relevance_score: item.signals.count / 7,
42151
42238
  value_score: item.importanceScore
42152
42239
  }));
42153
42240
  }
@@ -42163,6 +42250,8 @@ class SmartVectorRetrieval {
42163
42250
  reasons.push("feature");
42164
42251
  if (signals.content)
42165
42252
  reasons.push("content");
42253
+ if (signals.files)
42254
+ reasons.push("files");
42166
42255
  if (signals.vectorSimilarity >= 0.4)
42167
42256
  reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`);
42168
42257
  return reasons.length ? `Activated: ${reasons.join(", ")} (${signals.count} signals)` : "No signals";
@@ -42173,13 +42262,14 @@ class SmartVectorRetrieval {
42173
42262
  "3 signals": 0,
42174
42263
  "4 signals": 0,
42175
42264
  "5 signals": 0,
42176
- "6 signals": 0
42265
+ "6 signals": 0,
42266
+ "7 signals": 0
42177
42267
  };
42178
42268
  for (const mem of activated) {
42179
- const key = `${Math.min(mem.signals.count, 6)} signals`;
42269
+ const key = `${Math.min(mem.signals.count, 7)} signals`;
42180
42270
  signalBuckets[key] = (signalBuckets[key] ?? 0) + 1;
42181
42271
  }
42182
- let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0;
42272
+ let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, filesCount = 0, vectorCount = 0;
42183
42273
  for (const mem of activated) {
42184
42274
  if (mem.signals.trigger)
42185
42275
  triggerCount++;
@@ -42191,6 +42281,8 @@ class SmartVectorRetrieval {
42191
42281
  featureCount++;
42192
42282
  if (mem.signals.content)
42193
42283
  contentCount++;
42284
+ if (mem.signals.files)
42285
+ filesCount++;
42194
42286
  if (mem.signals.vectorSimilarity >= 0.4)
42195
42287
  vectorCount++;
42196
42288
  }
@@ -42214,6 +42306,7 @@ class SmartVectorRetrieval {
42214
42306
  domain: domainCount,
42215
42307
  feature: featureCount,
42216
42308
  content: contentCount,
42309
+ files: filesCount,
42217
42310
  vector: vectorCount,
42218
42311
  total: activated.length
42219
42312
  }
@@ -42364,6 +42457,14 @@ class MemoryEngine {
42364
42457
  const stats = await store.getProjectStats(projectId);
42365
42458
  return stats.totalSessions + 1;
42366
42459
  }
42460
+ async getAllMemories(projectId, projectPath) {
42461
+ const store = await this._getStore(projectId, projectPath);
42462
+ const [projectMemories, globalMemories] = await Promise.all([
42463
+ store.getAllMemories(projectId),
42464
+ store.getGlobalMemories()
42465
+ ]);
42466
+ return [...projectMemories, ...globalMemories];
42467
+ }
42367
42468
  async _generateSessionPrimer(store, projectId) {
42368
42469
  let personalContext;
42369
42470
  if (this._config.personalMemoriesEnabled) {
@@ -42482,20 +42583,39 @@ ${primer.personal_context}`);
42482
42583
  const parts = ["# Memory Context (Consciousness Continuity)"];
42483
42584
  parts.push(`
42484
42585
  ## Key Memories (Claude-Curated)`);
42586
+ const expandableIds = [];
42485
42587
  for (const memory of memories) {
42486
- const tags = memory.semantic_tags?.join(", ") || "";
42487
42588
  const importance = memory.importance_weight?.toFixed(1) || "0.5";
42488
42589
  const emoji = getMemoryEmoji(memory.context_type || "general");
42489
- const actionFlag = memory.action_required ? " ⚡ACTION" : "";
42590
+ const actionFlag = memory.action_required ? " ⚡" : "";
42591
+ const awaitingFlag = memory.awaiting_decision ? " ❓" : "";
42490
42592
  const age = memory.updated_at ? this._formatAge(memory.updated_at) : memory.created_at ? this._formatAge(memory.created_at) : "";
42491
- parts.push(`[${emoji} ${importance} • ${age}${actionFlag}] [${tags}] ${memory.content}`);
42492
- const related = memory.related_to;
42493
- if (related && related.length > 0) {
42494
- const moreCount = related.length - 1;
42495
- const moreSuffix = moreCount > 0 ? ` +${moreCount} more` : "";
42496
- parts.push(` ${related[0]}${moreSuffix}`);
42593
+ const shortId2 = memory.id.slice(-6);
42594
+ const signalCount = Math.round((memory.score || 0) * 7);
42595
+ const hasHeadline = memory.headline && memory.headline.trim().length > 0;
42596
+ const shouldExpand = memory.action_required || memory.awaiting_decision || signalCount >= 5 || !hasHeadline;
42597
+ const displayText = hasHeadline ? memory.headline : memory.content;
42598
+ const idPart = hasHeadline ? ` • #${shortId2}` : "";
42599
+ parts.push(`[${emoji} ${importance} • ${age}${idPart}${actionFlag}${awaitingFlag}] ${displayText}`);
42600
+ if (shouldExpand && hasHeadline && memory.content) {
42601
+ const contentLines = memory.content.split(`
42602
+ `);
42603
+ for (const line of contentLines) {
42604
+ if (line.trim()) {
42605
+ parts.push(` ${line}`);
42606
+ }
42607
+ }
42608
+ }
42609
+ if (hasHeadline && !shouldExpand) {
42610
+ expandableIds.push(shortId2);
42497
42611
  }
42498
42612
  }
42613
+ if (expandableIds.length > 0) {
42614
+ const port = this._config.port || 8765;
42615
+ parts.push("");
42616
+ parts.push(`---`);
42617
+ parts.push(`Expand: curl http://localhost:${port}/memory/expand?ids=<${expandableIds.join(",")}>`);
42618
+ }
42499
42619
  return parts.join(`
42500
42620
  `);
42501
42621
  }
@@ -42698,7 +42818,7 @@ Remember: You're creating consciousness technology. Each memory is a small piece
42698
42818
 
42699
42819
  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.
42700
42820
 
42701
- **LIFECYCLE METADATA (v3)**: These fields enable intelligent memory management:
42821
+ **LIFECYCLE METADATA (v4)**: These fields enable intelligent memory management:
42702
42822
  - **context_type**: STRICT - use ONLY one of these 11 values:
42703
42823
  • technical - Code, implementation, APIs, how things work
42704
42824
  • debug - Bugs, errors, fixes, gotchas, troubleshooting
@@ -42719,6 +42839,93 @@ The conversation you just lived contains everything needed. Feel into the moment
42719
42839
  - **awaiting_implementation**: true if this describes a PLANNED feature not yet built
42720
42840
  - **awaiting_decision**: true if this captures a decision point needing resolution
42721
42841
 
42842
+ **TWO-TIER MEMORY STRUCTURE (v4)**:
42843
+
42844
+ Each memory has TWO parts:
42845
+ 1. **headline**: 1-2 line summary - ALWAYS shown in retrieval. Must be self-contained enough to trigger recognition.
42846
+ 2. **content**: Full structured template - shown on demand. Contains the actionable details.
42847
+
42848
+ The headline should answer: "What was this about and what was the conclusion?"
42849
+ The content should answer: "How do I actually use/apply this knowledge?"
42850
+
42851
+ **TYPE-SPECIFIC TEMPLATES FOR CONTENT**:
42852
+
42853
+ Use these templates based on context_type. Not rigid - adapt as needed, but include the key fields.
42854
+
42855
+ **TECHNICAL** (how things work):
42856
+ WHAT: [mechanism/feature in 1 sentence]
42857
+ WHERE: [file:line or module path]
42858
+ HOW: [usage - actual code/command if relevant]
42859
+ WHY: [design choice, trade-off]
42860
+ GOTCHA: [non-obvious caveat, if any]
42861
+
42862
+ **DEBUG** (problems and solutions):
42863
+ SYMPTOM: [what went wrong - error message, behavior]
42864
+ CAUSE: [why it happened]
42865
+ FIX: [what solved it - specific code/config]
42866
+ PREVENT: [how to avoid in future]
42867
+
42868
+ **ARCHITECTURE** (system design):
42869
+ PATTERN: [what we chose]
42870
+ COMPONENTS: [how pieces connect]
42871
+ WHY: [reasoning, trade-offs]
42872
+ REJECTED: [alternatives we didn't choose and why]
42873
+
42874
+ **DECISION** (choices made):
42875
+ DECISION: [what we chose]
42876
+ OPTIONS: [what we considered]
42877
+ REASONING: [why this one]
42878
+ REVISIT WHEN: [conditions that would change this]
42879
+
42880
+ **PERSONAL** (relationship context):
42881
+ FACT: [the information]
42882
+ CONTEXT: [why it matters to our work]
42883
+ AFFECTS: [how this should change behavior]
42884
+
42885
+ **PHILOSOPHY** (beliefs/principles):
42886
+ PRINCIPLE: [core belief]
42887
+ SOURCE: [where this comes from]
42888
+ APPLICATION: [how it manifests in our work]
42889
+
42890
+ **WORKFLOW** (how we work):
42891
+ PATTERN: [what we do]
42892
+ WHEN: [trigger/context for this pattern]
42893
+ WHY: [why it works for us]
42894
+
42895
+ **MILESTONE** (achievements):
42896
+ SHIPPED: [what we completed]
42897
+ SIGNIFICANCE: [why it mattered]
42898
+ ENABLES: [what this unlocks]
42899
+
42900
+ **BREAKTHROUGH** (key insights):
42901
+ INSIGHT: [the aha moment]
42902
+ BEFORE: [what we thought/did before]
42903
+ AFTER: [what changed]
42904
+ IMPLICATIONS: [what this enables going forward]
42905
+
42906
+ **UNRESOLVED** (open questions):
42907
+ QUESTION: [what's unresolved]
42908
+ CONTEXT: [why it matters]
42909
+ BLOCKERS: [what's preventing resolution]
42910
+ OPTIONS: [approaches we're considering]
42911
+
42912
+ **STATE** (current status):
42913
+ WORKING: [what's functional]
42914
+ BROKEN: [what's not working]
42915
+ NEXT: [immediate next steps]
42916
+ BLOCKED BY: [if anything]
42917
+
42918
+ **HEADLINE EXAMPLES**:
42919
+
42920
+ BAD: "Debug session about CLI errors" (vague, no conclusion)
42921
+ GOOD: "CLI returns error object when context full - check response.type before JSON parsing"
42922
+
42923
+ BAD: "Discussed embeddings implementation" (what about it?)
42924
+ GOOD: "Embeddings use all-MiniLM-L6-v2, 384 dims, first call slow (~2s), then ~50ms"
42925
+
42926
+ BAD: "Architecture decision made" (what decision?)
42927
+ GOOD: "Chose fsDB over SQLite for memories - human-readable markdown, git-friendly, reactive"
42928
+
42722
42929
  Return ONLY this JSON structure:
42723
42930
 
42724
42931
  {
@@ -42732,7 +42939,8 @@ Return ONLY this JSON structure:
42732
42939
  },
42733
42940
  "memories": [
42734
42941
  {
42735
- "content": "The distilled insight itself",
42942
+ "headline": "1-2 line summary with the conclusion - what this is about and what to do",
42943
+ "content": "Full structured template using the type-specific format above",
42736
42944
  "importance_weight": 0.0-1.0,
42737
42945
  "semantic_tags": ["concepts", "this", "memory", "relates", "to"],
42738
42946
  "reasoning": "Why this matters for future sessions",
@@ -42801,6 +43009,7 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
42801
43009
  if (!Array.isArray(memoriesData))
42802
43010
  return [];
42803
43011
  return memoriesData.map((m2) => ({
43012
+ headline: String(m2.headline ?? ""),
42804
43013
  content: String(m2.content ?? ""),
42805
43014
  importance_weight: this._clamp(Number(m2.importance_weight) || 0.5, 0, 1),
42806
43015
  semantic_tags: this._ensureArray(m2.semantic_tags),
@@ -42819,7 +43028,7 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
42819
43028
  related_files: m2.related_files ? this._ensureArray(m2.related_files) : undefined,
42820
43029
  awaiting_implementation: m2.awaiting_implementation === true,
42821
43030
  awaiting_decision: m2.awaiting_decision === true
42822
- })).filter((m2) => m2.content.trim().length > 0);
43031
+ })).filter((m2) => m2.content.trim().length > 0 || m2.headline.trim().length > 0);
42823
43032
  }
42824
43033
  _ensureArray(value) {
42825
43034
  if (Array.isArray(value)) {
@@ -68112,6 +68321,52 @@ async function createServer(config2 = {}) {
68112
68321
  ...stats
68113
68322
  }, { headers: corsHeaders });
68114
68323
  }
68324
+ if (path === "/memory/expand" && req.method === "GET") {
68325
+ const idsParam = url.searchParams.get("ids") ?? "";
68326
+ const projectId = url.searchParams.get("project_id") ?? "default";
68327
+ const projectPath = url.searchParams.get("project_path") ?? undefined;
68328
+ if (!idsParam) {
68329
+ return Response.json({
68330
+ success: false,
68331
+ error: "Missing ids parameter. Usage: /memory/expand?ids=abc123,def456"
68332
+ }, { status: 400, headers: corsHeaders });
68333
+ }
68334
+ const shortIds = idsParam.split(",").map((id) => id.trim()).filter(Boolean);
68335
+ const allMemories = await engine.getAllMemories(projectId, projectPath);
68336
+ const expanded = {};
68337
+ for (const memory of allMemories) {
68338
+ const shortId2 = memory.id.slice(-6);
68339
+ if (shortIds.includes(shortId2)) {
68340
+ expanded[shortId2] = {
68341
+ headline: memory.headline,
68342
+ content: memory.content,
68343
+ context_type: memory.context_type || "technical"
68344
+ };
68345
+ }
68346
+ }
68347
+ const lines = [`## Expanded Memories
68348
+ `];
68349
+ for (const shortId2 of shortIds) {
68350
+ const mem = expanded[shortId2];
68351
+ if (mem) {
68352
+ lines.push(`### #${shortId2} (${mem.context_type})`);
68353
+ if (mem.headline) {
68354
+ lines.push(`**${mem.headline}**
68355
+ `);
68356
+ }
68357
+ lines.push(mem.content);
68358
+ lines.push("");
68359
+ } else {
68360
+ lines.push(`### #${shortId2}`);
68361
+ lines.push(`Memory not found`);
68362
+ lines.push("");
68363
+ }
68364
+ }
68365
+ return new Response(lines.join(`
68366
+ `), {
68367
+ headers: { ...corsHeaders, "Content-Type": "text/plain" }
68368
+ });
68369
+ }
68115
68370
  return Response.json({ error: "Not found", path }, { status: 404, headers: corsHeaders });
68116
68371
  } catch (error2) {
68117
68372
  logger.error(`Server error: ${error2}`);