@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.
- package/CHANGELOG.md +38 -0
- package/dist/{chunk-4KPXPQX3.js → chunk-5SMKVGJP.js} +205 -37
- package/dist/chunk-5SMKVGJP.js.map +1 -0
- package/dist/{chunk-LGCREJMO.cjs → chunk-AR52LM55.cjs} +205 -37
- package/dist/chunk-AR52LM55.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +27 -27
- package/dist/docs/references/docs-memory-observational-memory.md +2 -0
- package/dist/docs/references/reference-memory-observational-memory.md +2 -0
- package/dist/index.cjs +7 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-UEDVTWS2.js → observational-memory-5NFPG6M3.js} +3 -3
- package/dist/{observational-memory-UEDVTWS2.js.map → observational-memory-5NFPG6M3.js.map} +1 -1
- package/dist/{observational-memory-4TV5KKFV.cjs → observational-memory-NH7VDTXM.cjs} +18 -18
- package/dist/{observational-memory-4TV5KKFV.cjs.map → observational-memory-NH7VDTXM.cjs.map} +1 -1
- package/dist/processors/index.cjs +16 -16
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/markers.d.ts +10 -1
- package/dist/processors/observational-memory/markers.d.ts.map +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +2 -0
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts +16 -6
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/reflector-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +27 -1
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-4KPXPQX3.js.map +0 -1
- 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
|
-
|
|
614
|
-
|
|
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
|
-
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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-
|
|
7110
|
-
//# sourceMappingURL=chunk-
|
|
7277
|
+
//# sourceMappingURL=chunk-5SMKVGJP.js.map
|
|
7278
|
+
//# sourceMappingURL=chunk-5SMKVGJP.js.map
|