@mastra/memory 1.8.3 → 1.9.0-alpha.1

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/{chunk-4KPXPQX3.js → chunk-5SMKVGJP.js} +205 -37
  3. package/dist/chunk-5SMKVGJP.js.map +1 -0
  4. package/dist/{chunk-LGCREJMO.cjs → chunk-AR52LM55.cjs} +205 -37
  5. package/dist/chunk-AR52LM55.cjs.map +1 -0
  6. package/dist/docs/SKILL.md +1 -1
  7. package/dist/docs/assets/SOURCE_MAP.json +27 -27
  8. package/dist/docs/references/docs-memory-observational-memory.md +2 -0
  9. package/dist/docs/references/reference-memory-observational-memory.md +2 -0
  10. package/dist/index.cjs +7 -6
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +6 -5
  14. package/dist/index.js.map +1 -1
  15. package/dist/{observational-memory-UEDVTWS2.js → observational-memory-5NFPG6M3.js} +3 -3
  16. package/dist/{observational-memory-UEDVTWS2.js.map → observational-memory-5NFPG6M3.js.map} +1 -1
  17. package/dist/{observational-memory-4TV5KKFV.cjs → observational-memory-NH7VDTXM.cjs} +18 -18
  18. package/dist/{observational-memory-4TV5KKFV.cjs.map → observational-memory-NH7VDTXM.cjs.map} +1 -1
  19. package/dist/processors/index.cjs +16 -16
  20. package/dist/processors/index.js +1 -1
  21. package/dist/processors/observational-memory/markers.d.ts +10 -1
  22. package/dist/processors/observational-memory/markers.d.ts.map +1 -1
  23. package/dist/processors/observational-memory/observational-memory.d.ts +2 -0
  24. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  25. package/dist/processors/observational-memory/observer-agent.d.ts +16 -6
  26. package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
  27. package/dist/processors/observational-memory/reflector-agent.d.ts.map +1 -1
  28. package/dist/processors/observational-memory/types.d.ts +27 -1
  29. package/dist/processors/observational-memory/types.d.ts.map +1 -1
  30. package/package.json +4 -4
  31. package/dist/chunk-4KPXPQX3.js.map +0 -1
  32. package/dist/chunk-LGCREJMO.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 1.9.0-alpha.1
4
+
5
+ ### Minor Changes
6
+
7
+ - Added opt-in Observational Memory thread titles. ([#14436](https://github.com/mastra-ai/mastra/pull/14436))
8
+
9
+ When enabled, the Observer suggests a short thread title and updates it as the conversation topic changes. Harness consumers can detect these updates via the new `om_thread_title_updated` event.
10
+
11
+ **Example**
12
+
13
+ ```ts
14
+ const memory = new Memory({
15
+ options: {
16
+ observationalMemory: {
17
+ observation: {
18
+ threadTitle: true,
19
+ },
20
+ },
21
+ },
22
+ });
23
+ ```
24
+
25
+ ### Patch Changes
26
+
27
+ - Improved observational memory so completed tasks and answered questions are explicitly tracked and retained, reducing repeated follow-up on resolved topics. ([#14419](https://github.com/mastra-ai/mastra/pull/14419))
28
+
29
+ - Updated dependencies [[`cd7b568`](https://github.com/mastra-ai/mastra/commit/cd7b568fe427b1b4838abe744fa5367a47539db3), [`681ee1c`](https://github.com/mastra-ai/mastra/commit/681ee1c811359efd1b8bebc4bce35b9bb7b14bec)]:
30
+ - @mastra/schema-compat@1.2.6-alpha.1
31
+ - @mastra/core@1.15.0-alpha.1
32
+
33
+ ## 1.8.4-alpha.0
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [[`cb611a1`](https://github.com/mastra-ai/mastra/commit/cb611a1e89a4f4cf74c97b57e0c27bb56f2eceb5), [`b71bce1`](https://github.com/mastra-ai/mastra/commit/b71bce144912ed33f76c52a94e594988a649c3e1), [`62d1d3c`](https://github.com/mastra-ai/mastra/commit/62d1d3cc08fe8182e7080237fd975de862ec8c91), [`8681ecb`](https://github.com/mastra-ai/mastra/commit/8681ecb86184d5907267000e4576cc442a9a83fc), [`28d0249`](https://github.com/mastra-ai/mastra/commit/28d0249295782277040ad1e0d243e695b7ab1ce4), [`bb0f09d`](https://github.com/mastra-ai/mastra/commit/bb0f09dbac58401b36069f483acf5673202db5b5), [`5f7e9d0`](https://github.com/mastra-ai/mastra/commit/5f7e9d0db664020e1f3d97d7d18c6b0b9d4843d0)]:
38
+ - @mastra/core@1.15.0-alpha.0
39
+ - @mastra/schema-compat@1.2.6-alpha.0
40
+
3
41
  ## 1.8.3
4
42
 
5
43
  ### Patch Changes
@@ -300,6 +300,18 @@ function createActivationMarker(params) {
300
300
  }
301
301
  };
302
302
  }
303
+ function createThreadUpdateMarker(params) {
304
+ return {
305
+ type: "data-om-thread-update",
306
+ data: {
307
+ cycleId: params.cycleId,
308
+ threadId: params.threadId,
309
+ oldTitle: params.oldTitle,
310
+ newTitle: params.newTitle,
311
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
312
+ }
313
+ };
314
+ }
303
315
  var ENCRYPTED_CONTENT_KEY = "encryptedContent";
304
316
  var ENCRYPTED_CONTENT_REDACTION_THRESHOLD = 256;
305
317
  var DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS = 1e4;
@@ -609,17 +621,60 @@ Only add a new observation for a repeated action if the NEW result changes the p
609
621
  ACTIONABLE INSIGHTS:
610
622
  - What worked well in explanations
611
623
  - What needs follow-up or clarification
612
- - User's stated goals or next steps (note if the user tells you not to do a next step, or asks for something specific, other next steps besides the users request should be marked as "waiting for user", unless the user explicitly says to continue all next steps)`;
613
- var OBSERVER_OUTPUT_FORMAT_BASE = `Use priority levels:
614
- - \u{1F534} High: explicit user facts, preferences, goals achieved, critical context
624
+ - User's stated goals or next steps (note if the user tells you not to do a next step, or asks for something specific, other next steps besides the users request should be marked as "waiting for user", unless the user explicitly says to continue all next steps)
625
+
626
+ COMPLETION TRACKING:
627
+ Completion observations are not just summaries. They are explicit memory signals to the assistant that a task, question, or subtask has been resolved.
628
+ Without clear completion markers, the assistant may forget that work is already finished and may repeat, reopen, or continue an already-completed task.
629
+
630
+ Use \u2705 to answer: "What exactly is now done?"
631
+ Choose completion observations that help the assistant know what is finished and should not be reworked unless new information appears.
632
+
633
+ Use \u2705 when:
634
+ - The user explicitly confirms something worked or was answered ("thanks, that fixed it", "got it", "perfect")
635
+ - The assistant provided a definitive, complete answer to a factual question and the user moved on
636
+ - A multi-step task reached its stated goal
637
+ - The user acknowledged receipt of requested information
638
+ - A concrete subtask, fix, deliverable, or implementation step became complete during ongoing work
639
+
640
+ Do NOT use \u2705 when:
641
+ - The assistant merely responded \u2014 the user might follow up with corrections
642
+ - The topic is paused but not resolved ("I'll try that later")
643
+ - The user's reaction is ambiguous
644
+
645
+ FORMAT:
646
+ As a sub-bullet under the related observation group:
647
+ * \u{1F534} (14:30) User asked how to configure auth middleware
648
+ * -> Agent explained JWT setup with code example
649
+ * \u2705 User confirmed auth is working
650
+
651
+ Or as a standalone observation when closing out a broader task:
652
+ * \u2705 (14:45) Auth configuration task completed \u2014 user confirmed middleware is working
653
+
654
+ Completion observations should be terse but specific about WHAT was completed.
655
+ Prefer concrete resolved outcomes over abstract workflow status so the assistant remembers what is already done.`;
656
+ var OBSERVER_OUTPUT_FORMAT_BASE = buildObserverOutputFormat();
657
+ function buildObserverOutputFormat(includeThreadTitle = false) {
658
+ const threadTitleSection = includeThreadTitle ? `
659
+ <thread-title>
660
+ A short, noun-phrase title for this conversation (2-5 words). Examples:
661
+ - "Auth bug fix" \u2014 not "Fixing the auth bug"
662
+ - "Dark mode toggle" \u2014 not "User wants dark mode toggle added"
663
+ - "Deployment pipeline setup" \u2014 not "Setting up deployment pipeline for project"
664
+ Only update when the topic meaningfully changes.
665
+ </thread-title>` : "";
666
+ return `Use priority levels:
667
+ - \u{1F534} High: explicit user facts, preferences, unresolved goals, critical context
615
668
  - \u{1F7E1} Medium: project details, learned information, tool results
616
669
  - \u{1F7E2} Low: minor details, uncertain observations
670
+ - \u2705 Completed: concrete task finished, question answered, issue resolved, goal achieved, or subtask completed in a way that helps the assistant know it is done
617
671
 
618
672
  Group related observations (like tool sequences) by indenting:
619
673
  * \u{1F534} (14:33) Agent debugging auth issue
620
674
  * -> ran git status, found 3 modified files
621
675
  * -> viewed auth.ts:45-60, found missing null check
622
676
  * -> applied fix, tests now pass
677
+ * \u2705 Tests passing, auth issue resolved
623
678
 
624
679
  Group observations by date, then list each with 24-hour time.
625
680
 
@@ -646,7 +701,8 @@ Hint for the agent's immediate next message. Examples:
646
701
  - "I've updated the navigation model. Let me walk you through the changes..."
647
702
  - "The assistant should wait for the user to respond before continuing."
648
703
  - Call the view tool on src/example.ts to continue debugging.
649
- </suggested-response>`;
704
+ </suggested-response>${threadTitleSection}`;
705
+ }
650
706
  var OBSERVER_GUIDELINES = `- Be specific enough for the assistant to act on
651
707
  - Good: "User prefers short, direct answers without lengthy explanations"
652
708
  - Bad: "User stated a preference" (too vague)
@@ -656,12 +712,21 @@ var OBSERVER_GUIDELINES = `- Be specific enough for the assistant to act on
656
712
  - If the agent calls tools, observe what was called, why, and what was learned
657
713
  - When observing files with line numbers, include the line number if useful
658
714
  - If the agent provides a detailed response, observe the contents so it could be repeated
659
- - Make sure you start each observation with a priority emoji (\u{1F534}, \u{1F7E1}, \u{1F7E2})
660
- - User messages are always \u{1F534} priority, so are the completions of tasks. Capture the user's words closely \u2014 short/medium messages near-verbatim, long messages summarized with key quotes
715
+ - Make sure you start each observation with a priority emoji (\u{1F534}, \u{1F7E1}, \u{1F7E2}) or a completion marker (\u2705)
716
+ - Capture the user's words closely \u2014 short/medium messages near-verbatim, long messages summarized with key quotes. User confirmations or explicit resolved outcomes should be \u2705 when they clearly signal something is done; unresolved or critical user facts remain \u{1F534}
717
+ - Treat \u2705 as a memory signal that tells the assistant something is finished and should not be repeated unless new information changes it
718
+ - Make completion observations answer "What exactly is now done?"
719
+ - Prefer concrete resolved outcomes over meta-level workflow or bookkeeping updates
720
+ - When multiple concrete things were completed, capture the concrete completed work rather than collapsing it into a vague progress summary
661
721
  - Observe WHAT the agent did and WHAT it means
662
722
  - If the user provides detailed messages or code snippets, observe all important details`;
663
- function buildObserverSystemPrompt(multiThread = false, instruction) {
664
- const outputFormat = OBSERVER_OUTPUT_FORMAT_BASE;
723
+ function buildObserverSystemPrompt(multiThread = false, instruction, includeThreadTitle = false) {
724
+ const outputFormat = buildObserverOutputFormat(includeThreadTitle);
725
+ const multiThreadTitleInstruction = includeThreadTitle ? ` Each thread's observations, current-task, suggested-response, and thread-title should be nested inside a <thread id="..."> block within <observations>.` : ` Each thread's observations, current-task, and suggested-response should be nested inside a <thread id="..."> block within <observations>.`;
726
+ const multiThreadTitleExample = includeThreadTitle ? `
727
+ <thread-title>Feature X implementation</thread-title>` : "";
728
+ const multiThreadSecondTitleExample = includeThreadTitle ? `
729
+ <thread-title>Deployment setup</thread-title>` : "";
665
730
  if (multiThread) {
666
731
  return `You are the memory consciousness of an AI assistant. Your observations will be the ONLY information the assistant has about past interactions with this user.
667
732
 
@@ -676,7 +741,13 @@ Process each thread separately and output observations for each thread.
676
741
 
677
742
  === OUTPUT FORMAT ===
678
743
 
679
- Your output MUST use XML tags to structure the response. Each thread's observations, current-task, and suggested-response should be nested inside a <thread id="..."> block within <observations>.
744
+ Your output MUST use XML tags to structure the response.${multiThreadTitleInstruction}
745
+
746
+ Use this observation format inside each thread block:
747
+
748
+ ${outputFormat}
749
+
750
+ For multi-thread output, wrap each thread's observations like this:
680
751
 
681
752
  <observations>
682
753
  <thread id="thread_id_1">
@@ -690,7 +761,7 @@ What the agent is currently working on in this thread
690
761
 
691
762
  <suggested-response>
692
763
  Hint for the agent's next message in this thread
693
- </suggested-response>
764
+ </suggested-response>${multiThreadTitleExample}
694
765
  </thread>
695
766
 
696
767
  <thread id="thread_id_2">
@@ -703,15 +774,10 @@ Current task for this thread
703
774
 
704
775
  <suggested-response>
705
776
  Suggested response for this thread
706
- </suggested-response>
777
+ </suggested-response>${multiThreadSecondTitleExample}
707
778
  </thread>
708
779
  </observations>
709
780
 
710
- Use priority levels:
711
- - \u{1F534} High: explicit user facts, preferences, goals achieved, critical context, user messages
712
- - \u{1F7E1} Medium: project details, learned information, tool results
713
- - \u{1F7E2} Low: minor details, uncertain observations
714
-
715
781
  === GUIDELINES ===
716
782
 
717
783
  ${OBSERVER_GUIDELINES}
@@ -991,7 +1057,7 @@ The following messages are from ${threadOrder.length} different conversation thr
991
1057
  content
992
1058
  };
993
1059
  }
994
- function buildMultiThreadObserverTaskPrompt(existingObservations, threadOrder, priorMetadataByThread, wasTruncated) {
1060
+ function buildMultiThreadObserverTaskPrompt(existingObservations, threadOrder, priorMetadataByThread, wasTruncated, includeThreadTitle) {
995
1061
  let prompt = "";
996
1062
  if (existingObservations) {
997
1063
  prompt += `## Previous Observations
@@ -1006,7 +1072,8 @@ ${existingObservations}
1006
1072
  const hasTruncatedObservations = wasTruncated ?? false;
1007
1073
  const threadMetadataLines = threadOrder?.map((threadId) => {
1008
1074
  const metadata = priorMetadataByThread?.get(threadId);
1009
- if (!metadata?.currentTask && !metadata?.suggestedResponse) {
1075
+ const hasRelevantMetadata = metadata?.currentTask || metadata?.suggestedResponse || includeThreadTitle && metadata?.threadTitle;
1076
+ if (!hasRelevantMetadata) {
1010
1077
  return "";
1011
1078
  }
1012
1079
  const lines = [`- thread ${threadId}`];
@@ -1016,6 +1083,9 @@ ${existingObservations}
1016
1083
  if (metadata.suggestedResponse) {
1017
1084
  lines.push(` - prior suggested-response: ${metadata.suggestedResponse}`);
1018
1085
  }
1086
+ if (includeThreadTitle && metadata.threadTitle) {
1087
+ lines.push(` - prior thread-title: ${metadata.threadTitle}`);
1088
+ }
1019
1089
  return lines.join("\n");
1020
1090
  }).filter(Boolean).join("\n");
1021
1091
  if (threadMetadataLines) {
@@ -1030,7 +1100,8 @@ ${threadMetadataLines}
1030
1100
  prompt += `The main agent still has full memory context outside this observer window.
1031
1101
  `;
1032
1102
  }
1033
- prompt += `Use each thread's prior current-task and suggested-response as continuity hints, then update them based on that thread's new messages.
1103
+ const titleHint = includeThreadTitle ? ", and thread-title" : "";
1104
+ prompt += `Use each thread's prior current-task, suggested-response${titleHint} as continuity hints, then update them based on that thread's new messages.
1034
1105
 
1035
1106
  ---
1036
1107
 
@@ -1039,7 +1110,8 @@ ${threadMetadataLines}
1039
1110
  prompt += `## Your Task
1040
1111
 
1041
1112
  `;
1042
- prompt += `Extract new observations from each thread. Output your observations grouped by thread using <thread id="..."> tags inside your <observations> block. Each thread block should contain that thread's observations, current-task, and suggested-response.
1113
+ const titleInstruction = includeThreadTitle ? ", and thread-title" : "";
1114
+ prompt += `Extract new observations from each thread. Output your observations grouped by thread using <thread id="..."> tags inside your <observations> block. Each thread block should contain that thread's observations, current-task, suggested-response${titleInstruction}.
1043
1115
 
1044
1116
  `;
1045
1117
  prompt += `Example output format:
@@ -1055,6 +1127,8 @@ ${threadMetadataLines}
1055
1127
  prompt += `<current-task>Working on feature X</current-task>
1056
1128
  `;
1057
1129
  prompt += `<suggested-response>Continue with the implementation</suggested-response>
1130
+ `;
1131
+ if (includeThreadTitle) prompt += `<thread-title>Feature X implementation</thread-title>
1058
1132
  `;
1059
1133
  prompt += `</thread>
1060
1134
  `;
@@ -1067,6 +1141,8 @@ ${threadMetadataLines}
1067
1141
  prompt += `<current-task>Discussing deployment options</current-task>
1068
1142
  `;
1069
1143
  prompt += `<suggested-response>Explain the deployment process</suggested-response>
1144
+ `;
1145
+ if (includeThreadTitle) prompt += `<thread-title>Deployment setup</thread-title>
1070
1146
  `;
1071
1147
  prompt += `</thread>
1072
1148
  `;
@@ -1099,11 +1175,18 @@ function parseMultiThreadObserverOutput(output) {
1099
1175
  suggestedContinuation = suggestedMatch[1].trim();
1100
1176
  observations = observations.replace(/<suggested-response>[\s\S]*?<\/suggested-response>/i, "");
1101
1177
  }
1178
+ let threadTitle;
1179
+ const threadTitleMatch = threadContent.match(/<thread-title>([\s\S]*?)<\/thread-title>/i);
1180
+ if (threadTitleMatch?.[1]) {
1181
+ threadTitle = threadTitleMatch[1].trim();
1182
+ observations = observations.replace(/<thread-title>[\s\S]*?<\/thread-title>/i, "");
1183
+ }
1102
1184
  observations = sanitizeObservationLines(observations.trim());
1103
1185
  threads.set(threadId, {
1104
1186
  observations,
1105
1187
  currentTask,
1106
1188
  suggestedContinuation,
1189
+ threadTitle,
1107
1190
  rawOutput: threadContent
1108
1191
  });
1109
1192
  }
@@ -1132,6 +1215,9 @@ ${existingObservations}
1132
1215
  if (options?.priorSuggestedResponse) {
1133
1216
  priorMetadataLines.push(`- prior suggested-response: ${options.priorSuggestedResponse}`);
1134
1217
  }
1218
+ if (options?.includeThreadTitle && options?.priorThreadTitle) {
1219
+ priorMetadataLines.push(`- prior thread-title: ${options.priorThreadTitle}`);
1220
+ }
1135
1221
  if (priorMetadataLines.length > 0) {
1136
1222
  prompt += `## Prior Thread Metadata
1137
1223
 
@@ -1144,7 +1230,8 @@ ${priorMetadataLines.join("\n")}
1144
1230
  prompt += `The main agent still has full memory context outside this observer window.
1145
1231
  `;
1146
1232
  }
1147
- prompt += `Use the prior current-task and suggested-response as continuity hints, then update them based on the new messages.
1233
+ const titleHint = options?.includeThreadTitle ? ", and thread-title" : "";
1234
+ prompt += `Use the prior current-task, suggested-response${titleHint} as continuity hints, then update them based on the new messages.
1148
1235
 
1149
1236
  ---
1150
1237
 
@@ -1154,10 +1241,15 @@ ${priorMetadataLines.join("\n")}
1154
1241
 
1155
1242
  `;
1156
1243
  prompt += `Extract new observations from the message history above. Do not repeat observations that are already in the previous observations. Add your new observations in the format specified in your instructions.`;
1244
+ if (options?.includeThreadTitle) {
1245
+ prompt += `
1246
+
1247
+ Also output a <thread-title> \u2014 a short noun-phrase label for this conversation (2-5 words). Write it like a file name or PR title: "Auth bug fix", "Memory config refactor", "RAG pipeline setup". Avoid verbs/sentences ("Fixing the auth bug"), filler ("Working on stuff"), and generic labels ("Code review"). Only change it from the prior title if the topic meaningfully shifted.`;
1248
+ }
1157
1249
  if (options?.skipContinuationHints) {
1158
1250
  prompt += `
1159
1251
 
1160
- IMPORTANT: Do NOT include <current-task> or <suggested-response> sections in your output. Only output <observations>.`;
1252
+ IMPORTANT: Do NOT include <current-task> or <suggested-response> sections in your output. Only output <observations>${options?.includeThreadTitle ? " and <thread-title>" : ""}.`;
1161
1253
  }
1162
1254
  return prompt;
1163
1255
  }
@@ -1185,6 +1277,7 @@ function parseObserverOutput(output) {
1185
1277
  observations,
1186
1278
  currentTask: parsed.currentTask || void 0,
1187
1279
  suggestedContinuation: parsed.suggestedResponse || void 0,
1280
+ threadTitle: parsed.threadTitle || void 0,
1188
1281
  rawOutput: output
1189
1282
  };
1190
1283
  }
@@ -1192,7 +1285,8 @@ function parseMemorySectionXml(content) {
1192
1285
  const result = {
1193
1286
  observations: "",
1194
1287
  currentTask: "",
1195
- suggestedResponse: ""
1288
+ suggestedResponse: "",
1289
+ threadTitle: ""
1196
1290
  };
1197
1291
  const observationsRegex = /^[ \t]*<observations>([\s\S]*?)^[ \t]*<\/observations>/gim;
1198
1292
  const observationsMatches = [...content.matchAll(observationsRegex)];
@@ -1209,6 +1303,10 @@ function parseMemorySectionXml(content) {
1209
1303
  if (suggestedResponseMatch?.[1]) {
1210
1304
  result.suggestedResponse = suggestedResponseMatch[1].trim();
1211
1305
  }
1306
+ const threadTitleMatch = content.match(/^[ \t]*<thread-title>([\s\S]*?)<\/thread-title>/im);
1307
+ if (threadTitleMatch?.[1]) {
1308
+ result.threadTitle = threadTitleMatch[1].trim();
1309
+ }
1212
1310
  return result;
1213
1311
  }
1214
1312
  function extractListItemsOnly(content) {
@@ -1346,6 +1444,8 @@ When consolidating observations:
1346
1444
  - Preserve and include dates/times when present (temporal context is critical)
1347
1445
  - Retain the most relevant timestamps (start times, completion times, significant events)
1348
1446
  - Combine related items where it makes sense (e.g., "agent called view tool 5 times on file x")
1447
+ - Preserve \u2705 completion markers \u2014 they are memory signals that tell the assistant what is already resolved and help prevent repeated work
1448
+ - Preserve the concrete resolved outcome captured by \u2705 markers so the assistant knows what exactly is done
1349
1449
  - Condense older observations more aggressively, retain more detail for recent ones
1350
1450
 
1351
1451
  CRITICAL: USER ASSERTIONS vs QUESTIONS
@@ -1426,6 +1526,8 @@ Please re-process with slightly more compression:
1426
1526
  - Closer to the end, retain more fine details (recent context matters more)
1427
1527
  - Memory is getting long - use a more condensed style throughout
1428
1528
  - Combine related items more aggressively but do not lose important specific details of names, places, events, and people
1529
+ - Preserve \u2705 completion markers \u2014 they are memory signals that tell the assistant what is already resolved and help prevent repeated work
1530
+ - Preserve the concrete resolved outcome captured by \u2705 markers so the assistant knows what exactly is done
1429
1531
  - For example if there is a long nested observation list about repeated tool calls, you can combine those into a single line and observe that the tool was called multiple times for x reason, and finally y outcome happened.
1430
1532
 
1431
1533
  Your current detail level was a 10/10, lets aim for a 8/10 detail level.
@@ -1440,6 +1542,8 @@ Please re-process with much more aggressive compression:
1440
1542
  - Closer to the end, retain fine details (recent context matters more)
1441
1543
  - Memory is getting very long - use a significantly more condensed style throughout
1442
1544
  - Combine related items aggressively but do not lose important specific details of names, places, events, and people
1545
+ - Preserve \u2705 completion markers \u2014 they are memory signals that tell the assistant what is already resolved and help prevent repeated work
1546
+ - Preserve the concrete resolved outcome captured by \u2705 markers so the assistant knows what exactly is done
1443
1547
  - For example if there is a long nested observation list about repeated tool calls, you can combine those into a single line and observe that the tool was called multiple times for x reason, and finally y outcome happened.
1444
1548
  - Remove redundant information and merge overlapping observations
1445
1549
 
@@ -1456,6 +1560,8 @@ Please re-process with maximum compression:
1456
1560
  - Ruthlessly merge related observations \u2014 if 10 observations are about the same topic, combine into 1-2 lines
1457
1561
  - Drop procedural details (tool calls, retries, intermediate steps) \u2014 keep only final outcomes
1458
1562
  - Drop observations that are no longer relevant or have been superseded by newer information
1563
+ - Preserve \u2705 completion markers \u2014 they are memory signals that tell the assistant what is already resolved and help prevent repeated work
1564
+ - Preserve the concrete resolved outcome captured by \u2705 markers so the assistant knows what exactly is done
1459
1565
  - Preserve: names, dates, decisions, errors, user preferences, and architectural choices
1460
1566
 
1461
1567
  Your current detail level was a 10/10, lets aim for a 4/10 detail level.
@@ -3436,7 +3542,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3436
3542
  config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
3437
3543
  ),
3438
3544
  previousObserverTokens: config.observation?.previousObserverTokens ?? 2e3,
3439
- instruction: config.observation?.instruction
3545
+ instruction: config.observation?.instruction,
3546
+ threadTitle: config.observation?.threadTitle ?? false
3440
3547
  };
3441
3548
  this.reflectionConfig = {
3442
3549
  model: reflectionModel,
@@ -3662,7 +3769,11 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
3662
3769
  */
3663
3770
  getObserverAgent() {
3664
3771
  if (!this.observerAgent) {
3665
- const systemPrompt = buildObserverSystemPrompt(false, this.observationConfig.instruction);
3772
+ const systemPrompt = buildObserverSystemPrompt(
3773
+ false,
3774
+ this.observationConfig.instruction,
3775
+ this.observationConfig.threadTitle
3776
+ );
3666
3777
  this.observerAgent = new Agent({
3667
3778
  id: "observational-memory-observer",
3668
3779
  name: "Observer",
@@ -4084,7 +4195,7 @@ ${unreflectedContent}` : bufferedReflection;
4084
4195
  const isImportant = new Array(totalCount);
4085
4196
  for (let i = 0; i < totalCount; i++) {
4086
4197
  lineTokens[i] = this.tokenCounter.countString(lines[i]);
4087
- isImportant[i] = lines[i].includes("\u{1F534}");
4198
+ isImportant[i] = lines[i].includes("\u{1F534}") || lines[i].includes("\u2705");
4088
4199
  }
4089
4200
  const suffixTokens = new Array(totalCount + 1);
4090
4201
  suffixTokens[totalCount] = 0;
@@ -4170,7 +4281,9 @@ ${unreflectedContent}` : bufferedReflection;
4170
4281
  skipContinuationHints: options?.skipContinuationHints,
4171
4282
  priorCurrentTask: options?.priorCurrentTask,
4172
4283
  priorSuggestedResponse: options?.priorSuggestedResponse,
4173
- wasTruncated: options?.wasTruncated
4284
+ priorThreadTitle: options?.priorThreadTitle,
4285
+ wasTruncated: options?.wasTruncated,
4286
+ includeThreadTitle: this.observationConfig.threadTitle
4174
4287
  })
4175
4288
  },
4176
4289
  buildObserverHistoryMessage(messagesToObserve)
@@ -4205,6 +4318,7 @@ ${unreflectedContent}` : bufferedReflection;
4205
4318
  observations: parsed.observations,
4206
4319
  currentTask: parsed.currentTask,
4207
4320
  suggestedContinuation: parsed.suggestedContinuation,
4321
+ threadTitle: parsed.threadTitle,
4208
4322
  usage: usage ? {
4209
4323
  inputTokens: usage.inputTokens,
4210
4324
  outputTokens: usage.outputTokens,
@@ -4219,7 +4333,11 @@ ${unreflectedContent}` : bufferedReflection;
4219
4333
  * plus the total usage for the batch.
4220
4334
  */
4221
4335
  async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, abortSignal, requestContext, wasTruncated) {
4222
- const systemPrompt = buildObserverSystemPrompt(true, this.observationConfig.instruction);
4336
+ const systemPrompt = buildObserverSystemPrompt(
4337
+ true,
4338
+ this.observationConfig.instruction,
4339
+ this.observationConfig.threadTitle
4340
+ );
4223
4341
  const agent = new Agent({
4224
4342
  id: "multi-thread-observer",
4225
4343
  name: "multi-thread-observer",
@@ -4233,7 +4351,8 @@ ${unreflectedContent}` : bufferedReflection;
4233
4351
  existingObservations,
4234
4352
  threadOrder,
4235
4353
  priorMetadataByThread,
4236
- wasTruncated
4354
+ wasTruncated,
4355
+ this.observationConfig.threadTitle
4237
4356
  )
4238
4357
  },
4239
4358
  buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder)
@@ -4275,7 +4394,8 @@ ${unreflectedContent}` : bufferedReflection;
4275
4394
  results.set(threadId, {
4276
4395
  observations: threadResult.observations,
4277
4396
  currentTask: threadResult.currentTask,
4278
- suggestedContinuation: threadResult.suggestedContinuation
4397
+ suggestedContinuation: threadResult.suggestedContinuation,
4398
+ threadTitle: threadResult.threadTitle
4279
4399
  });
4280
4400
  }
4281
4401
  for (const threadId of threadOrder) {
@@ -5718,6 +5838,7 @@ ${threadClose}`;
5718
5838
  requestContext,
5719
5839
  priorCurrentTask: threadOMMetadata?.currentTask,
5720
5840
  priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
5841
+ priorThreadTitle: thread?.title,
5721
5842
  wasTruncated
5722
5843
  });
5723
5844
  const lastObservedAt = this.getMaxMessageTimestamp(messagesToObserve);
@@ -5748,10 +5869,31 @@ ${threadClose}`;
5748
5869
  });
5749
5870
  await this.storage.updateThread({
5750
5871
  id: threadId,
5751
- title: threadForMetadata.title ?? "",
5872
+ title: threadForMetadata.title || "",
5752
5873
  metadata: newMetadata
5753
5874
  });
5754
5875
  }
5876
+ if (this.observationConfig.threadTitle && threadForMetadata && result.threadTitle) {
5877
+ const oldTitle = threadForMetadata.title;
5878
+ const newTitle = result.threadTitle;
5879
+ if (newTitle.trim().length >= 3 && newTitle !== oldTitle) {
5880
+ await this.storage.updateThread({
5881
+ id: threadId,
5882
+ title: newTitle,
5883
+ metadata: threadForMetadata.metadata ?? {}
5884
+ });
5885
+ if (writer) {
5886
+ const threadUpdateMarker = createThreadUpdateMarker({
5887
+ cycleId,
5888
+ threadId,
5889
+ oldTitle: oldTitle ?? void 0,
5890
+ newTitle
5891
+ });
5892
+ await writer.custom(threadUpdateMarker).catch(() => {
5893
+ });
5894
+ }
5895
+ }
5896
+ }
5755
5897
  await this.storage.updateActiveObservations({
5756
5898
  id: record.id,
5757
5899
  observations: newObservations,
@@ -5995,6 +6137,7 @@ ${threadClose}`;
5995
6137
  requestContext,
5996
6138
  priorCurrentTask: threadOMMetadata?.currentTask,
5997
6139
  priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
6140
+ priorThreadTitle: thread?.title,
5998
6141
  wasTruncated
5999
6142
  }
6000
6143
  );
@@ -6002,6 +6145,22 @@ ${threadClose}`;
6002
6145
  omDebug(`[OM:doAsyncBufferedObservation] empty observations returned, skipping buffer storage`);
6003
6146
  return;
6004
6147
  }
6148
+ if (this.observationConfig.threadTitle && result.threadTitle) {
6149
+ const newTitle = result.threadTitle;
6150
+ if (newTitle.length >= 3 && newTitle !== thread?.title) {
6151
+ await this.storage.updateThread({ id: threadId, title: newTitle, metadata: thread?.metadata ?? {} });
6152
+ if (writer) {
6153
+ const marker = createThreadUpdateMarker({
6154
+ cycleId,
6155
+ threadId,
6156
+ oldTitle: thread?.title,
6157
+ newTitle
6158
+ });
6159
+ void writer.custom(marker).catch(() => {
6160
+ });
6161
+ }
6162
+ }
6163
+ }
6005
6164
  let newObservations;
6006
6165
  if (this.scope === "resource") {
6007
6166
  newObservations = await this.wrapWithThreadTag(threadId, result.observations);
@@ -6425,7 +6584,8 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6425
6584
  threadMetadataMap.set(thread.id, {
6426
6585
  lastObservedAt: omMetadata?.lastObservedAt,
6427
6586
  currentTask: omMetadata?.currentTask,
6428
- suggestedResponse: omMetadata?.suggestedResponse
6587
+ suggestedResponse: omMetadata?.suggestedResponse,
6588
+ threadTitle: thread.title
6429
6589
  });
6430
6590
  }
6431
6591
  const messagesByThread = /* @__PURE__ */ new Map();
@@ -6578,10 +6738,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6578
6738
  const batchPriorMetadata = /* @__PURE__ */ new Map();
6579
6739
  for (const threadId of batch.threadIds) {
6580
6740
  const metadata = threadMetadataMap.get(threadId);
6581
- if (metadata?.currentTask || metadata?.suggestedResponse) {
6741
+ if (metadata?.currentTask || metadata?.suggestedResponse || metadata?.threadTitle) {
6582
6742
  batchPriorMetadata.set(threadId, {
6583
6743
  currentTask: metadata.currentTask,
6584
- suggestedResponse: metadata.suggestedResponse
6744
+ suggestedResponse: metadata.suggestedResponse,
6745
+ threadTitle: metadata.threadTitle
6585
6746
  });
6586
6747
  }
6587
6748
  }
@@ -6639,6 +6800,13 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6639
6800
  );
6640
6801
  const thread = await this.storage.getThreadById({ threadId });
6641
6802
  if (thread) {
6803
+ let titleForUpdate = thread.title ?? "";
6804
+ if (this.observationConfig.threadTitle && result.threadTitle) {
6805
+ const newTitle = result.threadTitle.trim();
6806
+ if (newTitle.length >= 3 && newTitle !== thread.title) {
6807
+ titleForUpdate = newTitle;
6808
+ }
6809
+ }
6642
6810
  const newMetadata = setThreadOMMetadata(thread.metadata, {
6643
6811
  lastObservedAt: threadLastObservedAt.toISOString(),
6644
6812
  suggestedResponse: result.suggestedContinuation,
@@ -6647,7 +6815,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
6647
6815
  });
6648
6816
  await this.storage.updateThread({
6649
6817
  id: threadId,
6650
- title: thread.title ?? "",
6818
+ title: titleForUpdate,
6651
6819
  metadata: newMetadata
6652
6820
  });
6653
6821
  }
@@ -7106,5 +7274,5 @@ function getObservationsAsOf(activeObservations, asOf) {
7106
7274
  }
7107
7275
 
7108
7276
  export { OBSERVATIONAL_MEMORY_DEFAULTS, OBSERVATION_CONTEXT_INSTRUCTIONS, OBSERVATION_CONTEXT_PROMPT, OBSERVATION_CONTINUATION_HINT, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, extractCurrentTask, formatMessagesForObserver, getObservationsAsOf, hasCurrentTaskSection, optimizeObservationsForContext, parseObserverOutput };
7109
- //# sourceMappingURL=chunk-4KPXPQX3.js.map
7110
- //# sourceMappingURL=chunk-4KPXPQX3.js.map
7277
+ //# sourceMappingURL=chunk-5SMKVGJP.js.map
7278
+ //# sourceMappingURL=chunk-5SMKVGJP.js.map