@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.
package/dist/index.js CHANGED
@@ -33316,6 +33316,7 @@ var logger = {
33316
33316
  { name: "domain", count: signalBreakdown.domain },
33317
33317
  { name: "feature", count: signalBreakdown.feature },
33318
33318
  { name: "content", count: signalBreakdown.content },
33319
+ { name: "files", count: signalBreakdown.files },
33319
33320
  { name: "vector", count: signalBreakdown.vector }
33320
33321
  ];
33321
33322
  for (const sig of signals) {
@@ -33332,7 +33333,7 @@ var logger = {
33332
33333
  if (Object.keys(buckets).length > 0) {
33333
33334
  console.log(` ${style("bold", "Distribution:")}`);
33334
33335
  const maxBucketCount = Math.max(...Object.values(buckets), 1);
33335
- const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals"];
33336
+ const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals", "7 signals"];
33336
33337
  for (const bucket of bucketOrder) {
33337
33338
  const count = buckets[bucket] ?? 0;
33338
33339
  if (count > 0 || bucket === "2 signals") {
@@ -33348,7 +33349,7 @@ var logger = {
33348
33349
  };
33349
33350
 
33350
33351
  // src/types/memory.ts
33351
- var V3_DEFAULTS = {
33352
+ var V4_DEFAULTS = {
33352
33353
  typeDefaults: {
33353
33354
  personal: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
33354
33355
  philosophy: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
@@ -33373,7 +33374,7 @@ var V3_DEFAULTS = {
33373
33374
  exclude_from_retrieval: false
33374
33375
  }
33375
33376
  };
33376
- var V2_DEFAULTS = V3_DEFAULTS;
33377
+ var V2_DEFAULTS = V4_DEFAULTS;
33377
33378
  var MEMORY_TYPE_EMOJI = {
33378
33379
  technical: "\uD83D\uDD27",
33379
33380
  debug: "\uD83D\uDC1B",
@@ -33392,8 +33393,9 @@ function getMemoryEmoji(contextType) {
33392
33393
  }
33393
33394
 
33394
33395
  // src/types/schema.ts
33395
- var MEMORY_SCHEMA_VERSION = 3;
33396
+ var MEMORY_SCHEMA_VERSION = 4;
33396
33397
  var memorySchema = {
33398
+ headline: "string",
33397
33399
  content: "string",
33398
33400
  reasoning: "string",
33399
33401
  importance_weight: "number",
@@ -33522,6 +33524,7 @@ class MemoryStore {
33522
33524
  const { memories } = await this.getGlobal();
33523
33525
  return memories.all().map((record) => ({
33524
33526
  id: record.id,
33527
+ headline: record.headline ?? "",
33525
33528
  content: record.content,
33526
33529
  reasoning: record.reasoning,
33527
33530
  importance_weight: record.importance_weight,
@@ -33546,6 +33549,7 @@ class MemoryStore {
33546
33549
  const contextType = memory.context_type ?? "personal";
33547
33550
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.personal;
33548
33551
  const id = memories.insert({
33552
+ headline: memory.headline ?? "",
33549
33553
  content: memory.content,
33550
33554
  reasoning: memory.reasoning,
33551
33555
  importance_weight: memory.importance_weight,
@@ -33701,6 +33705,7 @@ class MemoryStore {
33701
33705
  const contextType = memory.context_type ?? "general";
33702
33706
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.technical;
33703
33707
  const id = memories.insert({
33708
+ headline: memory.headline ?? "",
33704
33709
  content: memory.content,
33705
33710
  reasoning: memory.reasoning,
33706
33711
  importance_weight: memory.importance_weight,
@@ -33743,6 +33748,7 @@ class MemoryStore {
33743
33748
  const { memories } = await this.getProject(projectId);
33744
33749
  return memories.all().map((record) => ({
33745
33750
  id: record.id,
33751
+ headline: record.headline ?? "",
33746
33752
  content: record.content,
33747
33753
  reasoning: record.reasoning,
33748
33754
  importance_weight: record.importance_weight,
@@ -34062,14 +34068,42 @@ class SmartVectorRetrieval {
34062
34068
  const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !STOPWORDS.has(w));
34063
34069
  return new Set(words);
34064
34070
  }
34071
+ _extractFilePaths(text) {
34072
+ const paths = new Set;
34073
+ const pathPattern = /(?:^|[\s'"(])([.\/\\]?(?:[\w.-]+[\/\\])+[\w.-]+(?:\.\w+)?)/g;
34074
+ let match;
34075
+ while ((match = pathPattern.exec(text)) !== null) {
34076
+ const path = match[1].toLowerCase();
34077
+ paths.add(path);
34078
+ const filename = path.split(/[\/\\]/).pop();
34079
+ if (filename)
34080
+ paths.add(filename);
34081
+ }
34082
+ return paths;
34083
+ }
34084
+ _checkFilesActivation(messagePaths, relatedFiles) {
34085
+ if (!relatedFiles?.length || !messagePaths.size)
34086
+ return false;
34087
+ for (const file of relatedFiles) {
34088
+ const fileLower = file.toLowerCase();
34089
+ for (const msgPath of messagePaths) {
34090
+ if (fileLower.includes(msgPath) || msgPath.includes(fileLower)) {
34091
+ return true;
34092
+ }
34093
+ }
34094
+ const filename = fileLower.split(/[\/\\]/).pop();
34095
+ if (filename && messagePaths.has(filename)) {
34096
+ return true;
34097
+ }
34098
+ }
34099
+ return false;
34100
+ }
34065
34101
  _preFilter(memories, currentProjectId, messageLower) {
34066
34102
  return memories.filter((memory) => {
34067
34103
  if (memory.status && memory.status !== "active")
34068
34104
  return false;
34069
34105
  if (memory.exclude_from_retrieval === true)
34070
34106
  return false;
34071
- if (memory.superseded_by)
34072
- return false;
34073
34107
  const isGlobal = memory.scope === "global" || memory.project_id === "global";
34074
34108
  if (!isGlobal && memory.project_id !== currentProjectId)
34075
34109
  return false;
@@ -34220,19 +34254,25 @@ class SmartVectorRetrieval {
34220
34254
  }
34221
34255
  const messageLower = currentMessage.toLowerCase();
34222
34256
  const messageWords = this._extractSignificantWords(currentMessage);
34257
+ const messagePaths = this._extractFilePaths(currentMessage);
34258
+ const memoryById = new Map;
34259
+ for (const m of allMemories) {
34260
+ memoryById.set(m.id, m);
34261
+ }
34223
34262
  const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
34224
34263
  if (!candidates.length) {
34225
34264
  return [];
34226
34265
  }
34227
34266
  const activatedMemories = [];
34267
+ const activatedIds = new Set;
34228
34268
  let rejectedCount = 0;
34229
34269
  for (const memory of candidates) {
34230
- const isGlobal = memory.scope === "global" || memory.project_id === "global";
34231
34270
  const triggerResult = this._checkTriggerActivation(messageLower, messageWords, memory.trigger_phrases ?? []);
34232
34271
  const tagResult = this._checkTagActivation(messageLower, messageWords, memory.semantic_tags ?? []);
34233
34272
  const domainActivated = this._checkDomainActivation(messageLower, messageWords, memory.domain);
34234
34273
  const featureActivated = this._checkFeatureActivation(messageLower, messageWords, memory.feature);
34235
34274
  const contentActivated = this._checkContentActivation(messageWords, memory);
34275
+ const filesActivated = this._checkFilesActivation(messagePaths, memory.related_files);
34236
34276
  const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
34237
34277
  let signalCount = 0;
34238
34278
  if (triggerResult.activated)
@@ -34245,6 +34285,8 @@ class SmartVectorRetrieval {
34245
34285
  signalCount++;
34246
34286
  if (contentActivated)
34247
34287
  signalCount++;
34288
+ if (filesActivated)
34289
+ signalCount++;
34248
34290
  if (vectorSimilarity >= 0.4)
34249
34291
  signalCount++;
34250
34292
  const signals = {
@@ -34253,6 +34295,7 @@ class SmartVectorRetrieval {
34253
34295
  domain: domainActivated,
34254
34296
  feature: featureActivated,
34255
34297
  content: contentActivated,
34298
+ files: filesActivated,
34256
34299
  count: signalCount,
34257
34300
  triggerStrength: triggerResult.strength,
34258
34301
  tagCount: tagResult.count,
@@ -34262,13 +34305,27 @@ class SmartVectorRetrieval {
34262
34305
  rejectedCount++;
34263
34306
  continue;
34264
34307
  }
34265
- const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords);
34308
+ let memoryToSurface = memory;
34309
+ if (memory.superseded_by || memory.resolved_by) {
34310
+ const replacementId = memory.superseded_by ?? memory.resolved_by;
34311
+ const replacement = replacementId ? memoryById.get(replacementId) : undefined;
34312
+ if (replacement && replacement.status !== "archived" && replacement.status !== "deprecated") {
34313
+ memoryToSurface = replacement;
34314
+ logger.debug(`Redirect: ${memory.id.slice(-8)} → ${replacement.id.slice(-8)} (${memory.superseded_by ? "superseded" : "resolved"})`, "retrieval");
34315
+ }
34316
+ }
34317
+ if (activatedIds.has(memoryToSurface.id)) {
34318
+ continue;
34319
+ }
34320
+ const isGlobal = memoryToSurface.scope === "global" || memoryToSurface.project_id === "global";
34321
+ const importanceScore = this._calculateImportanceScore(memoryToSurface, signalCount, messageLower, messageWords);
34266
34322
  activatedMemories.push({
34267
- memory,
34323
+ memory: memoryToSurface,
34268
34324
  signals,
34269
34325
  importanceScore,
34270
34326
  isGlobal
34271
34327
  });
34328
+ activatedIds.add(memoryToSurface.id);
34272
34329
  }
34273
34330
  this._logActivationDistribution(activatedMemories, candidates.length, rejectedCount);
34274
34331
  this._logVectorStats();
@@ -34338,11 +34395,19 @@ class SmartVectorRetrieval {
34338
34395
  selectedIds.add(item.memory.id);
34339
34396
  }
34340
34397
  if (selected.length < maxMemories) {
34341
- const relatedIds = new Set;
34398
+ const linkedIds = new Set;
34342
34399
  for (const item of selected) {
34343
34400
  for (const relatedId of item.memory.related_to ?? []) {
34344
34401
  if (!selectedIds.has(relatedId)) {
34345
- relatedIds.add(relatedId);
34402
+ linkedIds.add(relatedId);
34403
+ }
34404
+ }
34405
+ if (item.memory.blocked_by && !selectedIds.has(item.memory.blocked_by)) {
34406
+ linkedIds.add(item.memory.blocked_by);
34407
+ }
34408
+ for (const blockedId of item.memory.blocks ?? []) {
34409
+ if (!selectedIds.has(blockedId)) {
34410
+ linkedIds.add(blockedId);
34346
34411
  }
34347
34412
  }
34348
34413
  }
@@ -34351,9 +34416,30 @@ class SmartVectorRetrieval {
34351
34416
  break;
34352
34417
  if (selectedIds.has(item.memory.id))
34353
34418
  continue;
34354
- if (relatedIds.has(item.memory.id)) {
34419
+ if (linkedIds.has(item.memory.id)) {
34355
34420
  selected.push(item);
34356
34421
  selectedIds.add(item.memory.id);
34422
+ logger.debug(`Linked: ${item.memory.id.slice(-8)} pulled by relationship`, "retrieval");
34423
+ }
34424
+ }
34425
+ if (selected.length < maxMemories) {
34426
+ for (const linkedId of linkedIds) {
34427
+ if (selected.length >= maxMemories)
34428
+ break;
34429
+ if (selectedIds.has(linkedId))
34430
+ continue;
34431
+ const linkedMemory = memoryById.get(linkedId);
34432
+ if (linkedMemory && linkedMemory.status !== "archived" && linkedMemory.status !== "deprecated") {
34433
+ const isGlobal = linkedMemory.scope === "global" || linkedMemory.project_id === "global";
34434
+ selected.push({
34435
+ memory: linkedMemory,
34436
+ signals: { trigger: false, tags: false, domain: false, feature: false, content: false, files: false, count: 0, triggerStrength: 0, tagCount: 0, vectorSimilarity: 0 },
34437
+ importanceScore: linkedMemory.importance_weight ?? 0.5,
34438
+ isGlobal
34439
+ });
34440
+ selectedIds.add(linkedId);
34441
+ logger.debug(`Linked (direct): ${linkedId.slice(-8)} pulled for context`, "retrieval");
34442
+ }
34357
34443
  }
34358
34444
  }
34359
34445
  }
@@ -34383,6 +34469,7 @@ class SmartVectorRetrieval {
34383
34469
  domain: item.signals.domain,
34384
34470
  feature: item.signals.feature,
34385
34471
  content: item.signals.content,
34472
+ files: item.signals.files,
34386
34473
  vector: item.signals.vectorSimilarity >= 0.4,
34387
34474
  vectorSimilarity: item.signals.vectorSimilarity
34388
34475
  }
@@ -34390,8 +34477,8 @@ class SmartVectorRetrieval {
34390
34477
  });
34391
34478
  return selected.map((item) => ({
34392
34479
  ...item.memory,
34393
- score: item.signals.count / 6,
34394
- relevance_score: item.signals.count / 6,
34480
+ score: item.signals.count / 7,
34481
+ relevance_score: item.signals.count / 7,
34395
34482
  value_score: item.importanceScore
34396
34483
  }));
34397
34484
  }
@@ -34407,6 +34494,8 @@ class SmartVectorRetrieval {
34407
34494
  reasons.push("feature");
34408
34495
  if (signals.content)
34409
34496
  reasons.push("content");
34497
+ if (signals.files)
34498
+ reasons.push("files");
34410
34499
  if (signals.vectorSimilarity >= 0.4)
34411
34500
  reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`);
34412
34501
  return reasons.length ? `Activated: ${reasons.join(", ")} (${signals.count} signals)` : "No signals";
@@ -34417,13 +34506,14 @@ class SmartVectorRetrieval {
34417
34506
  "3 signals": 0,
34418
34507
  "4 signals": 0,
34419
34508
  "5 signals": 0,
34420
- "6 signals": 0
34509
+ "6 signals": 0,
34510
+ "7 signals": 0
34421
34511
  };
34422
34512
  for (const mem of activated) {
34423
- const key = `${Math.min(mem.signals.count, 6)} signals`;
34513
+ const key = `${Math.min(mem.signals.count, 7)} signals`;
34424
34514
  signalBuckets[key] = (signalBuckets[key] ?? 0) + 1;
34425
34515
  }
34426
- let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0;
34516
+ let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, filesCount = 0, vectorCount = 0;
34427
34517
  for (const mem of activated) {
34428
34518
  if (mem.signals.trigger)
34429
34519
  triggerCount++;
@@ -34435,6 +34525,8 @@ class SmartVectorRetrieval {
34435
34525
  featureCount++;
34436
34526
  if (mem.signals.content)
34437
34527
  contentCount++;
34528
+ if (mem.signals.files)
34529
+ filesCount++;
34438
34530
  if (mem.signals.vectorSimilarity >= 0.4)
34439
34531
  vectorCount++;
34440
34532
  }
@@ -34458,6 +34550,7 @@ class SmartVectorRetrieval {
34458
34550
  domain: domainCount,
34459
34551
  feature: featureCount,
34460
34552
  content: contentCount,
34553
+ files: filesCount,
34461
34554
  vector: vectorCount,
34462
34555
  total: activated.length
34463
34556
  }
@@ -34608,6 +34701,14 @@ class MemoryEngine {
34608
34701
  const stats = await store.getProjectStats(projectId);
34609
34702
  return stats.totalSessions + 1;
34610
34703
  }
34704
+ async getAllMemories(projectId, projectPath) {
34705
+ const store = await this._getStore(projectId, projectPath);
34706
+ const [projectMemories, globalMemories] = await Promise.all([
34707
+ store.getAllMemories(projectId),
34708
+ store.getGlobalMemories()
34709
+ ]);
34710
+ return [...projectMemories, ...globalMemories];
34711
+ }
34611
34712
  async _generateSessionPrimer(store, projectId) {
34612
34713
  let personalContext;
34613
34714
  if (this._config.personalMemoriesEnabled) {
@@ -34726,20 +34827,39 @@ ${primer.personal_context}`);
34726
34827
  const parts = ["# Memory Context (Consciousness Continuity)"];
34727
34828
  parts.push(`
34728
34829
  ## Key Memories (Claude-Curated)`);
34830
+ const expandableIds = [];
34729
34831
  for (const memory of memories) {
34730
- const tags = memory.semantic_tags?.join(", ") || "";
34731
34832
  const importance = memory.importance_weight?.toFixed(1) || "0.5";
34732
34833
  const emoji = getMemoryEmoji(memory.context_type || "general");
34733
- const actionFlag = memory.action_required ? " ⚡ACTION" : "";
34834
+ const actionFlag = memory.action_required ? " ⚡" : "";
34835
+ const awaitingFlag = memory.awaiting_decision ? " ❓" : "";
34734
34836
  const age = memory.updated_at ? this._formatAge(memory.updated_at) : memory.created_at ? this._formatAge(memory.created_at) : "";
34735
- parts.push(`[${emoji} ${importance} • ${age}${actionFlag}] [${tags}] ${memory.content}`);
34736
- const related = memory.related_to;
34737
- if (related && related.length > 0) {
34738
- const moreCount = related.length - 1;
34739
- const moreSuffix = moreCount > 0 ? ` +${moreCount} more` : "";
34740
- parts.push(` ${related[0]}${moreSuffix}`);
34837
+ const shortId2 = memory.id.slice(-6);
34838
+ const signalCount = Math.round((memory.score || 0) * 7);
34839
+ const hasHeadline = memory.headline && memory.headline.trim().length > 0;
34840
+ const shouldExpand = memory.action_required || memory.awaiting_decision || signalCount >= 5 || !hasHeadline;
34841
+ const displayText = hasHeadline ? memory.headline : memory.content;
34842
+ const idPart = hasHeadline ? ` • #${shortId2}` : "";
34843
+ parts.push(`[${emoji} ${importance} • ${age}${idPart}${actionFlag}${awaitingFlag}] ${displayText}`);
34844
+ if (shouldExpand && hasHeadline && memory.content) {
34845
+ const contentLines = memory.content.split(`
34846
+ `);
34847
+ for (const line of contentLines) {
34848
+ if (line.trim()) {
34849
+ parts.push(` ${line}`);
34850
+ }
34851
+ }
34852
+ }
34853
+ if (hasHeadline && !shouldExpand) {
34854
+ expandableIds.push(shortId2);
34741
34855
  }
34742
34856
  }
34857
+ if (expandableIds.length > 0) {
34858
+ const port = this._config.port || 8765;
34859
+ parts.push("");
34860
+ parts.push(`---`);
34861
+ parts.push(`Expand: curl http://localhost:${port}/memory/expand?ids=<${expandableIds.join(",")}>`);
34862
+ }
34743
34863
  return parts.join(`
34744
34864
  `);
34745
34865
  }
@@ -34941,7 +35061,7 @@ Remember: You're creating consciousness technology. Each memory is a small piece
34941
35061
 
34942
35062
  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.
34943
35063
 
34944
- **LIFECYCLE METADATA (v3)**: These fields enable intelligent memory management:
35064
+ **LIFECYCLE METADATA (v4)**: These fields enable intelligent memory management:
34945
35065
  - **context_type**: STRICT - use ONLY one of these 11 values:
34946
35066
  • technical - Code, implementation, APIs, how things work
34947
35067
  • debug - Bugs, errors, fixes, gotchas, troubleshooting
@@ -34962,6 +35082,93 @@ The conversation you just lived contains everything needed. Feel into the moment
34962
35082
  - **awaiting_implementation**: true if this describes a PLANNED feature not yet built
34963
35083
  - **awaiting_decision**: true if this captures a decision point needing resolution
34964
35084
 
35085
+ **TWO-TIER MEMORY STRUCTURE (v4)**:
35086
+
35087
+ Each memory has TWO parts:
35088
+ 1. **headline**: 1-2 line summary - ALWAYS shown in retrieval. Must be self-contained enough to trigger recognition.
35089
+ 2. **content**: Full structured template - shown on demand. Contains the actionable details.
35090
+
35091
+ The headline should answer: "What was this about and what was the conclusion?"
35092
+ The content should answer: "How do I actually use/apply this knowledge?"
35093
+
35094
+ **TYPE-SPECIFIC TEMPLATES FOR CONTENT**:
35095
+
35096
+ Use these templates based on context_type. Not rigid - adapt as needed, but include the key fields.
35097
+
35098
+ **TECHNICAL** (how things work):
35099
+ WHAT: [mechanism/feature in 1 sentence]
35100
+ WHERE: [file:line or module path]
35101
+ HOW: [usage - actual code/command if relevant]
35102
+ WHY: [design choice, trade-off]
35103
+ GOTCHA: [non-obvious caveat, if any]
35104
+
35105
+ **DEBUG** (problems and solutions):
35106
+ SYMPTOM: [what went wrong - error message, behavior]
35107
+ CAUSE: [why it happened]
35108
+ FIX: [what solved it - specific code/config]
35109
+ PREVENT: [how to avoid in future]
35110
+
35111
+ **ARCHITECTURE** (system design):
35112
+ PATTERN: [what we chose]
35113
+ COMPONENTS: [how pieces connect]
35114
+ WHY: [reasoning, trade-offs]
35115
+ REJECTED: [alternatives we didn't choose and why]
35116
+
35117
+ **DECISION** (choices made):
35118
+ DECISION: [what we chose]
35119
+ OPTIONS: [what we considered]
35120
+ REASONING: [why this one]
35121
+ REVISIT WHEN: [conditions that would change this]
35122
+
35123
+ **PERSONAL** (relationship context):
35124
+ FACT: [the information]
35125
+ CONTEXT: [why it matters to our work]
35126
+ AFFECTS: [how this should change behavior]
35127
+
35128
+ **PHILOSOPHY** (beliefs/principles):
35129
+ PRINCIPLE: [core belief]
35130
+ SOURCE: [where this comes from]
35131
+ APPLICATION: [how it manifests in our work]
35132
+
35133
+ **WORKFLOW** (how we work):
35134
+ PATTERN: [what we do]
35135
+ WHEN: [trigger/context for this pattern]
35136
+ WHY: [why it works for us]
35137
+
35138
+ **MILESTONE** (achievements):
35139
+ SHIPPED: [what we completed]
35140
+ SIGNIFICANCE: [why it mattered]
35141
+ ENABLES: [what this unlocks]
35142
+
35143
+ **BREAKTHROUGH** (key insights):
35144
+ INSIGHT: [the aha moment]
35145
+ BEFORE: [what we thought/did before]
35146
+ AFTER: [what changed]
35147
+ IMPLICATIONS: [what this enables going forward]
35148
+
35149
+ **UNRESOLVED** (open questions):
35150
+ QUESTION: [what's unresolved]
35151
+ CONTEXT: [why it matters]
35152
+ BLOCKERS: [what's preventing resolution]
35153
+ OPTIONS: [approaches we're considering]
35154
+
35155
+ **STATE** (current status):
35156
+ WORKING: [what's functional]
35157
+ BROKEN: [what's not working]
35158
+ NEXT: [immediate next steps]
35159
+ BLOCKED BY: [if anything]
35160
+
35161
+ **HEADLINE EXAMPLES**:
35162
+
35163
+ BAD: "Debug session about CLI errors" (vague, no conclusion)
35164
+ GOOD: "CLI returns error object when context full - check response.type before JSON parsing"
35165
+
35166
+ BAD: "Discussed embeddings implementation" (what about it?)
35167
+ GOOD: "Embeddings use all-MiniLM-L6-v2, 384 dims, first call slow (~2s), then ~50ms"
35168
+
35169
+ BAD: "Architecture decision made" (what decision?)
35170
+ GOOD: "Chose fsDB over SQLite for memories - human-readable markdown, git-friendly, reactive"
35171
+
34965
35172
  Return ONLY this JSON structure:
34966
35173
 
34967
35174
  {
@@ -34975,7 +35182,8 @@ Return ONLY this JSON structure:
34975
35182
  },
34976
35183
  "memories": [
34977
35184
  {
34978
- "content": "The distilled insight itself",
35185
+ "headline": "1-2 line summary with the conclusion - what this is about and what to do",
35186
+ "content": "Full structured template using the type-specific format above",
34979
35187
  "importance_weight": 0.0-1.0,
34980
35188
  "semantic_tags": ["concepts", "this", "memory", "relates", "to"],
34981
35189
  "reasoning": "Why this matters for future sessions",
@@ -35044,6 +35252,7 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
35044
35252
  if (!Array.isArray(memoriesData))
35045
35253
  return [];
35046
35254
  return memoriesData.map((m2) => ({
35255
+ headline: String(m2.headline ?? ""),
35047
35256
  content: String(m2.content ?? ""),
35048
35257
  importance_weight: this._clamp(Number(m2.importance_weight) || 0.5, 0, 1),
35049
35258
  semantic_tags: this._ensureArray(m2.semantic_tags),
@@ -35062,7 +35271,7 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
35062
35271
  related_files: m2.related_files ? this._ensureArray(m2.related_files) : undefined,
35063
35272
  awaiting_implementation: m2.awaiting_implementation === true,
35064
35273
  awaiting_decision: m2.awaiting_decision === true
35065
- })).filter((m2) => m2.content.trim().length > 0);
35274
+ })).filter((m2) => m2.content.trim().length > 0 || m2.headline.trim().length > 0);
35066
35275
  }
35067
35276
  _ensureArray(value) {
35068
35277
  if (Array.isArray(value)) {