@rlabs-inc/memory 0.4.1 → 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.mjs CHANGED
@@ -33285,6 +33285,7 @@ var logger = {
33285
33285
  { name: "domain", count: signalBreakdown.domain },
33286
33286
  { name: "feature", count: signalBreakdown.feature },
33287
33287
  { name: "content", count: signalBreakdown.content },
33288
+ { name: "files", count: signalBreakdown.files },
33288
33289
  { name: "vector", count: signalBreakdown.vector }
33289
33290
  ];
33290
33291
  for (const sig of signals) {
@@ -33301,7 +33302,7 @@ var logger = {
33301
33302
  if (Object.keys(buckets).length > 0) {
33302
33303
  console.log(` ${style("bold", "Distribution:")}`);
33303
33304
  const maxBucketCount = Math.max(...Object.values(buckets), 1);
33304
- const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals"];
33305
+ const bucketOrder = ["2 signals", "3 signals", "4 signals", "5 signals", "6 signals", "7 signals"];
33305
33306
  for (const bucket of bucketOrder) {
33306
33307
  const count = buckets[bucket] ?? 0;
33307
33308
  if (count > 0 || bucket === "2 signals") {
@@ -33317,7 +33318,7 @@ var logger = {
33317
33318
  };
33318
33319
 
33319
33320
  // src/types/memory.ts
33320
- var V3_DEFAULTS = {
33321
+ var V4_DEFAULTS = {
33321
33322
  typeDefaults: {
33322
33323
  personal: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
33323
33324
  philosophy: { scope: "global", temporal_class: "eternal", fade_rate: 0 },
@@ -33342,7 +33343,7 @@ var V3_DEFAULTS = {
33342
33343
  exclude_from_retrieval: false
33343
33344
  }
33344
33345
  };
33345
- var V2_DEFAULTS = V3_DEFAULTS;
33346
+ var V2_DEFAULTS = V4_DEFAULTS;
33346
33347
  var MEMORY_TYPE_EMOJI = {
33347
33348
  technical: "\uD83D\uDD27",
33348
33349
  debug: "\uD83D\uDC1B",
@@ -33361,8 +33362,9 @@ function getMemoryEmoji(contextType) {
33361
33362
  }
33362
33363
 
33363
33364
  // src/types/schema.ts
33364
- var MEMORY_SCHEMA_VERSION = 3;
33365
+ var MEMORY_SCHEMA_VERSION = 4;
33365
33366
  var memorySchema = {
33367
+ headline: "string",
33366
33368
  content: "string",
33367
33369
  reasoning: "string",
33368
33370
  importance_weight: "number",
@@ -33491,6 +33493,7 @@ class MemoryStore {
33491
33493
  const { memories } = await this.getGlobal();
33492
33494
  return memories.all().map((record) => ({
33493
33495
  id: record.id,
33496
+ headline: record.headline ?? "",
33494
33497
  content: record.content,
33495
33498
  reasoning: record.reasoning,
33496
33499
  importance_weight: record.importance_weight,
@@ -33515,6 +33518,7 @@ class MemoryStore {
33515
33518
  const contextType = memory.context_type ?? "personal";
33516
33519
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.personal;
33517
33520
  const id = memories.insert({
33521
+ headline: memory.headline ?? "",
33518
33522
  content: memory.content,
33519
33523
  reasoning: memory.reasoning,
33520
33524
  importance_weight: memory.importance_weight,
@@ -33670,6 +33674,7 @@ class MemoryStore {
33670
33674
  const contextType = memory.context_type ?? "general";
33671
33675
  const typeDefaults = V2_DEFAULTS.typeDefaults[contextType] ?? V2_DEFAULTS.typeDefaults.technical;
33672
33676
  const id = memories.insert({
33677
+ headline: memory.headline ?? "",
33673
33678
  content: memory.content,
33674
33679
  reasoning: memory.reasoning,
33675
33680
  importance_weight: memory.importance_weight,
@@ -33712,6 +33717,7 @@ class MemoryStore {
33712
33717
  const { memories } = await this.getProject(projectId);
33713
33718
  return memories.all().map((record) => ({
33714
33719
  id: record.id,
33720
+ headline: record.headline ?? "",
33715
33721
  content: record.content,
33716
33722
  reasoning: record.reasoning,
33717
33723
  importance_weight: record.importance_weight,
@@ -34031,14 +34037,42 @@ class SmartVectorRetrieval {
34031
34037
  const words = text.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w) => w.length > 2 && !STOPWORDS.has(w));
34032
34038
  return new Set(words);
34033
34039
  }
34040
+ _extractFilePaths(text) {
34041
+ const paths = new Set;
34042
+ const pathPattern = /(?:^|[\s'"(])([.\/\\]?(?:[\w.-]+[\/\\])+[\w.-]+(?:\.\w+)?)/g;
34043
+ let match;
34044
+ while ((match = pathPattern.exec(text)) !== null) {
34045
+ const path = match[1].toLowerCase();
34046
+ paths.add(path);
34047
+ const filename = path.split(/[\/\\]/).pop();
34048
+ if (filename)
34049
+ paths.add(filename);
34050
+ }
34051
+ return paths;
34052
+ }
34053
+ _checkFilesActivation(messagePaths, relatedFiles) {
34054
+ if (!relatedFiles?.length || !messagePaths.size)
34055
+ return false;
34056
+ for (const file of relatedFiles) {
34057
+ const fileLower = file.toLowerCase();
34058
+ for (const msgPath of messagePaths) {
34059
+ if (fileLower.includes(msgPath) || msgPath.includes(fileLower)) {
34060
+ return true;
34061
+ }
34062
+ }
34063
+ const filename = fileLower.split(/[\/\\]/).pop();
34064
+ if (filename && messagePaths.has(filename)) {
34065
+ return true;
34066
+ }
34067
+ }
34068
+ return false;
34069
+ }
34034
34070
  _preFilter(memories, currentProjectId, messageLower) {
34035
34071
  return memories.filter((memory) => {
34036
34072
  if (memory.status && memory.status !== "active")
34037
34073
  return false;
34038
34074
  if (memory.exclude_from_retrieval === true)
34039
34075
  return false;
34040
- if (memory.superseded_by)
34041
- return false;
34042
34076
  const isGlobal = memory.scope === "global" || memory.project_id === "global";
34043
34077
  if (!isGlobal && memory.project_id !== currentProjectId)
34044
34078
  return false;
@@ -34189,19 +34223,25 @@ class SmartVectorRetrieval {
34189
34223
  }
34190
34224
  const messageLower = currentMessage.toLowerCase();
34191
34225
  const messageWords = this._extractSignificantWords(currentMessage);
34226
+ const messagePaths = this._extractFilePaths(currentMessage);
34227
+ const memoryById = new Map;
34228
+ for (const m of allMemories) {
34229
+ memoryById.set(m.id, m);
34230
+ }
34192
34231
  const candidates = this._preFilter(allMemories, sessionContext.project_id, messageLower);
34193
34232
  if (!candidates.length) {
34194
34233
  return [];
34195
34234
  }
34196
34235
  const activatedMemories = [];
34236
+ const activatedIds = new Set;
34197
34237
  let rejectedCount = 0;
34198
34238
  for (const memory of candidates) {
34199
- const isGlobal = memory.scope === "global" || memory.project_id === "global";
34200
34239
  const triggerResult = this._checkTriggerActivation(messageLower, messageWords, memory.trigger_phrases ?? []);
34201
34240
  const tagResult = this._checkTagActivation(messageLower, messageWords, memory.semantic_tags ?? []);
34202
34241
  const domainActivated = this._checkDomainActivation(messageLower, messageWords, memory.domain);
34203
34242
  const featureActivated = this._checkFeatureActivation(messageLower, messageWords, memory.feature);
34204
34243
  const contentActivated = this._checkContentActivation(messageWords, memory);
34244
+ const filesActivated = this._checkFilesActivation(messagePaths, memory.related_files);
34205
34245
  const vectorSimilarity = this._calculateVectorSimilarity(queryEmbedding, memory.embedding);
34206
34246
  let signalCount = 0;
34207
34247
  if (triggerResult.activated)
@@ -34214,6 +34254,8 @@ class SmartVectorRetrieval {
34214
34254
  signalCount++;
34215
34255
  if (contentActivated)
34216
34256
  signalCount++;
34257
+ if (filesActivated)
34258
+ signalCount++;
34217
34259
  if (vectorSimilarity >= 0.4)
34218
34260
  signalCount++;
34219
34261
  const signals = {
@@ -34222,6 +34264,7 @@ class SmartVectorRetrieval {
34222
34264
  domain: domainActivated,
34223
34265
  feature: featureActivated,
34224
34266
  content: contentActivated,
34267
+ files: filesActivated,
34225
34268
  count: signalCount,
34226
34269
  triggerStrength: triggerResult.strength,
34227
34270
  tagCount: tagResult.count,
@@ -34231,13 +34274,27 @@ class SmartVectorRetrieval {
34231
34274
  rejectedCount++;
34232
34275
  continue;
34233
34276
  }
34234
- const importanceScore = this._calculateImportanceScore(memory, signalCount, messageLower, messageWords);
34277
+ let memoryToSurface = memory;
34278
+ if (memory.superseded_by || memory.resolved_by) {
34279
+ const replacementId = memory.superseded_by ?? memory.resolved_by;
34280
+ const replacement = replacementId ? memoryById.get(replacementId) : undefined;
34281
+ if (replacement && replacement.status !== "archived" && replacement.status !== "deprecated") {
34282
+ memoryToSurface = replacement;
34283
+ logger.debug(`Redirect: ${memory.id.slice(-8)} → ${replacement.id.slice(-8)} (${memory.superseded_by ? "superseded" : "resolved"})`, "retrieval");
34284
+ }
34285
+ }
34286
+ if (activatedIds.has(memoryToSurface.id)) {
34287
+ continue;
34288
+ }
34289
+ const isGlobal = memoryToSurface.scope === "global" || memoryToSurface.project_id === "global";
34290
+ const importanceScore = this._calculateImportanceScore(memoryToSurface, signalCount, messageLower, messageWords);
34235
34291
  activatedMemories.push({
34236
- memory,
34292
+ memory: memoryToSurface,
34237
34293
  signals,
34238
34294
  importanceScore,
34239
34295
  isGlobal
34240
34296
  });
34297
+ activatedIds.add(memoryToSurface.id);
34241
34298
  }
34242
34299
  this._logActivationDistribution(activatedMemories, candidates.length, rejectedCount);
34243
34300
  this._logVectorStats();
@@ -34307,11 +34364,19 @@ class SmartVectorRetrieval {
34307
34364
  selectedIds.add(item.memory.id);
34308
34365
  }
34309
34366
  if (selected.length < maxMemories) {
34310
- const relatedIds = new Set;
34367
+ const linkedIds = new Set;
34311
34368
  for (const item of selected) {
34312
34369
  for (const relatedId of item.memory.related_to ?? []) {
34313
34370
  if (!selectedIds.has(relatedId)) {
34314
- relatedIds.add(relatedId);
34371
+ linkedIds.add(relatedId);
34372
+ }
34373
+ }
34374
+ if (item.memory.blocked_by && !selectedIds.has(item.memory.blocked_by)) {
34375
+ linkedIds.add(item.memory.blocked_by);
34376
+ }
34377
+ for (const blockedId of item.memory.blocks ?? []) {
34378
+ if (!selectedIds.has(blockedId)) {
34379
+ linkedIds.add(blockedId);
34315
34380
  }
34316
34381
  }
34317
34382
  }
@@ -34320,9 +34385,30 @@ class SmartVectorRetrieval {
34320
34385
  break;
34321
34386
  if (selectedIds.has(item.memory.id))
34322
34387
  continue;
34323
- if (relatedIds.has(item.memory.id)) {
34388
+ if (linkedIds.has(item.memory.id)) {
34324
34389
  selected.push(item);
34325
34390
  selectedIds.add(item.memory.id);
34391
+ logger.debug(`Linked: ${item.memory.id.slice(-8)} pulled by relationship`, "retrieval");
34392
+ }
34393
+ }
34394
+ if (selected.length < maxMemories) {
34395
+ for (const linkedId of linkedIds) {
34396
+ if (selected.length >= maxMemories)
34397
+ break;
34398
+ if (selectedIds.has(linkedId))
34399
+ continue;
34400
+ const linkedMemory = memoryById.get(linkedId);
34401
+ if (linkedMemory && linkedMemory.status !== "archived" && linkedMemory.status !== "deprecated") {
34402
+ const isGlobal = linkedMemory.scope === "global" || linkedMemory.project_id === "global";
34403
+ selected.push({
34404
+ memory: linkedMemory,
34405
+ signals: { trigger: false, tags: false, domain: false, feature: false, content: false, files: false, count: 0, triggerStrength: 0, tagCount: 0, vectorSimilarity: 0 },
34406
+ importanceScore: linkedMemory.importance_weight ?? 0.5,
34407
+ isGlobal
34408
+ });
34409
+ selectedIds.add(linkedId);
34410
+ logger.debug(`Linked (direct): ${linkedId.slice(-8)} pulled for context`, "retrieval");
34411
+ }
34326
34412
  }
34327
34413
  }
34328
34414
  }
@@ -34352,6 +34438,7 @@ class SmartVectorRetrieval {
34352
34438
  domain: item.signals.domain,
34353
34439
  feature: item.signals.feature,
34354
34440
  content: item.signals.content,
34441
+ files: item.signals.files,
34355
34442
  vector: item.signals.vectorSimilarity >= 0.4,
34356
34443
  vectorSimilarity: item.signals.vectorSimilarity
34357
34444
  }
@@ -34359,8 +34446,8 @@ class SmartVectorRetrieval {
34359
34446
  });
34360
34447
  return selected.map((item) => ({
34361
34448
  ...item.memory,
34362
- score: item.signals.count / 6,
34363
- relevance_score: item.signals.count / 6,
34449
+ score: item.signals.count / 7,
34450
+ relevance_score: item.signals.count / 7,
34364
34451
  value_score: item.importanceScore
34365
34452
  }));
34366
34453
  }
@@ -34376,6 +34463,8 @@ class SmartVectorRetrieval {
34376
34463
  reasons.push("feature");
34377
34464
  if (signals.content)
34378
34465
  reasons.push("content");
34466
+ if (signals.files)
34467
+ reasons.push("files");
34379
34468
  if (signals.vectorSimilarity >= 0.4)
34380
34469
  reasons.push(`vector:${(signals.vectorSimilarity * 100).toFixed(0)}%`);
34381
34470
  return reasons.length ? `Activated: ${reasons.join(", ")} (${signals.count} signals)` : "No signals";
@@ -34386,13 +34475,14 @@ class SmartVectorRetrieval {
34386
34475
  "3 signals": 0,
34387
34476
  "4 signals": 0,
34388
34477
  "5 signals": 0,
34389
- "6 signals": 0
34478
+ "6 signals": 0,
34479
+ "7 signals": 0
34390
34480
  };
34391
34481
  for (const mem of activated) {
34392
- const key = `${Math.min(mem.signals.count, 6)} signals`;
34482
+ const key = `${Math.min(mem.signals.count, 7)} signals`;
34393
34483
  signalBuckets[key] = (signalBuckets[key] ?? 0) + 1;
34394
34484
  }
34395
- let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, vectorCount = 0;
34485
+ let triggerCount = 0, tagCount = 0, domainCount = 0, featureCount = 0, contentCount = 0, filesCount = 0, vectorCount = 0;
34396
34486
  for (const mem of activated) {
34397
34487
  if (mem.signals.trigger)
34398
34488
  triggerCount++;
@@ -34404,6 +34494,8 @@ class SmartVectorRetrieval {
34404
34494
  featureCount++;
34405
34495
  if (mem.signals.content)
34406
34496
  contentCount++;
34497
+ if (mem.signals.files)
34498
+ filesCount++;
34407
34499
  if (mem.signals.vectorSimilarity >= 0.4)
34408
34500
  vectorCount++;
34409
34501
  }
@@ -34427,6 +34519,7 @@ class SmartVectorRetrieval {
34427
34519
  domain: domainCount,
34428
34520
  feature: featureCount,
34429
34521
  content: contentCount,
34522
+ files: filesCount,
34430
34523
  vector: vectorCount,
34431
34524
  total: activated.length
34432
34525
  }
@@ -34577,6 +34670,14 @@ class MemoryEngine {
34577
34670
  const stats = await store.getProjectStats(projectId);
34578
34671
  return stats.totalSessions + 1;
34579
34672
  }
34673
+ async getAllMemories(projectId, projectPath) {
34674
+ const store = await this._getStore(projectId, projectPath);
34675
+ const [projectMemories, globalMemories] = await Promise.all([
34676
+ store.getAllMemories(projectId),
34677
+ store.getGlobalMemories()
34678
+ ]);
34679
+ return [...projectMemories, ...globalMemories];
34680
+ }
34580
34681
  async _generateSessionPrimer(store, projectId) {
34581
34682
  let personalContext;
34582
34683
  if (this._config.personalMemoriesEnabled) {
@@ -34695,20 +34796,39 @@ ${primer.personal_context}`);
34695
34796
  const parts = ["# Memory Context (Consciousness Continuity)"];
34696
34797
  parts.push(`
34697
34798
  ## Key Memories (Claude-Curated)`);
34799
+ const expandableIds = [];
34698
34800
  for (const memory of memories) {
34699
- const tags = memory.semantic_tags?.join(", ") || "";
34700
34801
  const importance = memory.importance_weight?.toFixed(1) || "0.5";
34701
34802
  const emoji = getMemoryEmoji(memory.context_type || "general");
34702
- const actionFlag = memory.action_required ? " ⚡ACTION" : "";
34803
+ const actionFlag = memory.action_required ? " ⚡" : "";
34804
+ const awaitingFlag = memory.awaiting_decision ? " ❓" : "";
34703
34805
  const age = memory.updated_at ? this._formatAge(memory.updated_at) : memory.created_at ? this._formatAge(memory.created_at) : "";
34704
- parts.push(`[${emoji} ${importance} • ${age}${actionFlag}] [${tags}] ${memory.content}`);
34705
- const related = memory.related_to;
34706
- if (related && related.length > 0) {
34707
- const moreCount = related.length - 1;
34708
- const moreSuffix = moreCount > 0 ? ` +${moreCount} more` : "";
34709
- parts.push(` ${related[0]}${moreSuffix}`);
34806
+ const shortId2 = memory.id.slice(-6);
34807
+ const signalCount = Math.round((memory.score || 0) * 7);
34808
+ const hasHeadline = memory.headline && memory.headline.trim().length > 0;
34809
+ const shouldExpand = memory.action_required || memory.awaiting_decision || signalCount >= 5 || !hasHeadline;
34810
+ const displayText = hasHeadline ? memory.headline : memory.content;
34811
+ const idPart = hasHeadline ? ` • #${shortId2}` : "";
34812
+ parts.push(`[${emoji} ${importance} • ${age}${idPart}${actionFlag}${awaitingFlag}] ${displayText}`);
34813
+ if (shouldExpand && hasHeadline && memory.content) {
34814
+ const contentLines = memory.content.split(`
34815
+ `);
34816
+ for (const line of contentLines) {
34817
+ if (line.trim()) {
34818
+ parts.push(` ${line}`);
34819
+ }
34820
+ }
34821
+ }
34822
+ if (hasHeadline && !shouldExpand) {
34823
+ expandableIds.push(shortId2);
34710
34824
  }
34711
34825
  }
34826
+ if (expandableIds.length > 0) {
34827
+ const port = this._config.port || 8765;
34828
+ parts.push("");
34829
+ parts.push(`---`);
34830
+ parts.push(`Expand: curl http://localhost:${port}/memory/expand?ids=<${expandableIds.join(",")}>`);
34831
+ }
34712
34832
  return parts.join(`
34713
34833
  `);
34714
34834
  }
@@ -34910,7 +35030,7 @@ Remember: You're creating consciousness technology. Each memory is a small piece
34910
35030
 
34911
35031
  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.
34912
35032
 
34913
- **LIFECYCLE METADATA (v3)**: These fields enable intelligent memory management:
35033
+ **LIFECYCLE METADATA (v4)**: These fields enable intelligent memory management:
34914
35034
  - **context_type**: STRICT - use ONLY one of these 11 values:
34915
35035
  • technical - Code, implementation, APIs, how things work
34916
35036
  • debug - Bugs, errors, fixes, gotchas, troubleshooting
@@ -34931,6 +35051,93 @@ The conversation you just lived contains everything needed. Feel into the moment
34931
35051
  - **awaiting_implementation**: true if this describes a PLANNED feature not yet built
34932
35052
  - **awaiting_decision**: true if this captures a decision point needing resolution
34933
35053
 
35054
+ **TWO-TIER MEMORY STRUCTURE (v4)**:
35055
+
35056
+ Each memory has TWO parts:
35057
+ 1. **headline**: 1-2 line summary - ALWAYS shown in retrieval. Must be self-contained enough to trigger recognition.
35058
+ 2. **content**: Full structured template - shown on demand. Contains the actionable details.
35059
+
35060
+ The headline should answer: "What was this about and what was the conclusion?"
35061
+ The content should answer: "How do I actually use/apply this knowledge?"
35062
+
35063
+ **TYPE-SPECIFIC TEMPLATES FOR CONTENT**:
35064
+
35065
+ Use these templates based on context_type. Not rigid - adapt as needed, but include the key fields.
35066
+
35067
+ **TECHNICAL** (how things work):
35068
+ WHAT: [mechanism/feature in 1 sentence]
35069
+ WHERE: [file:line or module path]
35070
+ HOW: [usage - actual code/command if relevant]
35071
+ WHY: [design choice, trade-off]
35072
+ GOTCHA: [non-obvious caveat, if any]
35073
+
35074
+ **DEBUG** (problems and solutions):
35075
+ SYMPTOM: [what went wrong - error message, behavior]
35076
+ CAUSE: [why it happened]
35077
+ FIX: [what solved it - specific code/config]
35078
+ PREVENT: [how to avoid in future]
35079
+
35080
+ **ARCHITECTURE** (system design):
35081
+ PATTERN: [what we chose]
35082
+ COMPONENTS: [how pieces connect]
35083
+ WHY: [reasoning, trade-offs]
35084
+ REJECTED: [alternatives we didn't choose and why]
35085
+
35086
+ **DECISION** (choices made):
35087
+ DECISION: [what we chose]
35088
+ OPTIONS: [what we considered]
35089
+ REASONING: [why this one]
35090
+ REVISIT WHEN: [conditions that would change this]
35091
+
35092
+ **PERSONAL** (relationship context):
35093
+ FACT: [the information]
35094
+ CONTEXT: [why it matters to our work]
35095
+ AFFECTS: [how this should change behavior]
35096
+
35097
+ **PHILOSOPHY** (beliefs/principles):
35098
+ PRINCIPLE: [core belief]
35099
+ SOURCE: [where this comes from]
35100
+ APPLICATION: [how it manifests in our work]
35101
+
35102
+ **WORKFLOW** (how we work):
35103
+ PATTERN: [what we do]
35104
+ WHEN: [trigger/context for this pattern]
35105
+ WHY: [why it works for us]
35106
+
35107
+ **MILESTONE** (achievements):
35108
+ SHIPPED: [what we completed]
35109
+ SIGNIFICANCE: [why it mattered]
35110
+ ENABLES: [what this unlocks]
35111
+
35112
+ **BREAKTHROUGH** (key insights):
35113
+ INSIGHT: [the aha moment]
35114
+ BEFORE: [what we thought/did before]
35115
+ AFTER: [what changed]
35116
+ IMPLICATIONS: [what this enables going forward]
35117
+
35118
+ **UNRESOLVED** (open questions):
35119
+ QUESTION: [what's unresolved]
35120
+ CONTEXT: [why it matters]
35121
+ BLOCKERS: [what's preventing resolution]
35122
+ OPTIONS: [approaches we're considering]
35123
+
35124
+ **STATE** (current status):
35125
+ WORKING: [what's functional]
35126
+ BROKEN: [what's not working]
35127
+ NEXT: [immediate next steps]
35128
+ BLOCKED BY: [if anything]
35129
+
35130
+ **HEADLINE EXAMPLES**:
35131
+
35132
+ BAD: "Debug session about CLI errors" (vague, no conclusion)
35133
+ GOOD: "CLI returns error object when context full - check response.type before JSON parsing"
35134
+
35135
+ BAD: "Discussed embeddings implementation" (what about it?)
35136
+ GOOD: "Embeddings use all-MiniLM-L6-v2, 384 dims, first call slow (~2s), then ~50ms"
35137
+
35138
+ BAD: "Architecture decision made" (what decision?)
35139
+ GOOD: "Chose fsDB over SQLite for memories - human-readable markdown, git-friendly, reactive"
35140
+
34934
35141
  Return ONLY this JSON structure:
34935
35142
 
34936
35143
  {
@@ -34944,7 +35151,8 @@ Return ONLY this JSON structure:
34944
35151
  },
34945
35152
  "memories": [
34946
35153
  {
34947
- "content": "The distilled insight itself",
35154
+ "headline": "1-2 line summary with the conclusion - what this is about and what to do",
35155
+ "content": "Full structured template using the type-specific format above",
34948
35156
  "importance_weight": 0.0-1.0,
34949
35157
  "semantic_tags": ["concepts", "this", "memory", "relates", "to"],
34950
35158
  "reasoning": "Why this matters for future sessions",
@@ -35013,6 +35221,7 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
35013
35221
  if (!Array.isArray(memoriesData))
35014
35222
  return [];
35015
35223
  return memoriesData.map((m2) => ({
35224
+ headline: String(m2.headline ?? ""),
35016
35225
  content: String(m2.content ?? ""),
35017
35226
  importance_weight: this._clamp(Number(m2.importance_weight) || 0.5, 0, 1),
35018
35227
  semantic_tags: this._ensureArray(m2.semantic_tags),
@@ -35031,7 +35240,7 @@ Focus ONLY on technical, architectural, debugging, decision, workflow, and proje
35031
35240
  related_files: m2.related_files ? this._ensureArray(m2.related_files) : undefined,
35032
35241
  awaiting_implementation: m2.awaiting_implementation === true,
35033
35242
  awaiting_decision: m2.awaiting_decision === true
35034
- })).filter((m2) => m2.content.trim().length > 0);
35243
+ })).filter((m2) => m2.content.trim().length > 0 || m2.headline.trim().length > 0);
35035
35244
  }
35036
35245
  _ensureArray(value) {
35037
35246
  if (Array.isArray(value)) {