@mastra/memory 1.7.0-alpha.1 → 1.8.0-alpha.0
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 +40 -0
- package/dist/{chunk-M7RAJAZ6.js → chunk-SUU4IAZJ.js} +307 -39
- package/dist/chunk-SUU4IAZJ.js.map +1 -0
- package/dist/{chunk-SHID74TI.cjs → chunk-YPFNHFT6.cjs} +307 -39
- package/dist/chunk-YPFNHFT6.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +24 -24
- package/dist/docs/references/docs-memory-observational-memory.md +23 -0
- package/dist/docs/references/reference-memory-observational-memory.md +2 -0
- package/dist/index.cjs +64 -368
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +64 -368
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-AU6MIH4Q.cjs → observational-memory-3HFM7PY2.cjs} +17 -17
- package/dist/{observational-memory-AU6MIH4Q.cjs.map → observational-memory-3HFM7PY2.cjs.map} +1 -1
- package/dist/{observational-memory-YRWU6CY3.js → observational-memory-XXD6E2SO.js} +3 -3
- package/dist/{observational-memory-YRWU6CY3.js.map → observational-memory-XXD6E2SO.js.map} +1 -1
- package/dist/processors/index.cjs +15 -15
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/observational-memory.d.ts +21 -0
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts +14 -2
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/types.d.ts +7 -0
- package/dist/processors/observational-memory/types.d.ts.map +1 -1
- package/package.json +8 -8
- package/dist/chunk-M7RAJAZ6.js.map +0 -1
- package/dist/chunk-SHID74TI.cjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# @mastra/memory
|
|
2
2
|
|
|
3
|
+
## 1.8.0-alpha.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added observer context optimization for Observational Memory. The `observation.previousObserverTokens` field reduces Observer input token costs for long-running conversations: ([#13568](https://github.com/mastra-ai/mastra/pull/13568))
|
|
8
|
+
- **previousObserverTokens** (default: `2000`): Truncates the 'Previous Observations' section to a token budget, keeping the most recent observations and automatically replacing already-reflected lines with the buffered reflection summary. Set to `0` to omit previous observations entirely, or `false` to disable truncation and keep the full observation history.
|
|
9
|
+
|
|
10
|
+
```typescript
|
|
11
|
+
const memory = new Memory({
|
|
12
|
+
options: {
|
|
13
|
+
observationalMemory: {
|
|
14
|
+
model: 'google/gemini-2.5-flash',
|
|
15
|
+
observation: {
|
|
16
|
+
previousObserverTokens: 10_000,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [[`ea86967`](https://github.com/mastra-ai/mastra/commit/ea86967449426e0a3673253bd1c2c052a99d970d), [`db21c21`](https://github.com/mastra-ai/mastra/commit/db21c21a6ae5f33539262cc535342fa8757eb359), [`a1d6b9c`](https://github.com/mastra-ai/mastra/commit/a1d6b9c907c909f259632a7ea26e9e3c221fb691), [`11f5dbe`](https://github.com/mastra-ai/mastra/commit/11f5dbe9a1e7ad8ef3b1ea34fb4a9fa3631d1587), [`c562ec2`](https://github.com/mastra-ai/mastra/commit/c562ec228f1af63693e2984ffa9712aa6db8fea8), [`6751354`](https://github.com/mastra-ai/mastra/commit/67513544d1a64be891d9de7624d40aadc895d56e), [`c958cd3`](https://github.com/mastra-ai/mastra/commit/c958cd36627c1eea122ec241b2b15492977a263a), [`86f2426`](https://github.com/mastra-ai/mastra/commit/86f242631d252a172d2f9f9a2ea0feb8647a76b0), [`950eb07`](https://github.com/mastra-ai/mastra/commit/950eb07b7e7354629630e218d49550fdd299c452)]:
|
|
26
|
+
- @mastra/core@1.13.0-alpha.0
|
|
27
|
+
- @mastra/schema-compat@1.2.2-alpha.0
|
|
28
|
+
|
|
29
|
+
## 1.7.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- Observational Memory now performs local threshold checks with lower CPU and memory overhead. ([#14178](https://github.com/mastra-ai/mastra/pull/14178))
|
|
34
|
+
|
|
35
|
+
This update keeps the same multimodal thresholding behavior for image-aware inputs, so existing Observational Memory configurations continue to work without changes.
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- Updated dependencies [[`709362d`](https://github.com/mastra-ai/mastra/commit/709362d67b80d8832729bbf9e449cad27640a5d2), [`cddf895`](https://github.com/mastra-ai/mastra/commit/cddf895532b8ee7f9fa814136ec672f53d37a9ba), [`9cede11`](https://github.com/mastra-ai/mastra/commit/9cede110abac9d93072e0521bb3c8bcafb9fdadf), [`a59f126`](https://github.com/mastra-ai/mastra/commit/a59f1269104f54726699c5cdb98c72c93606d2df), [`ed8fd75`](https://github.com/mastra-ai/mastra/commit/ed8fd75cbff03bb5e19971ddb30ab7040fc60447), [`c510833`](https://github.com/mastra-ai/mastra/commit/c5108333e8cbc19dafee5f8bfefbcb5ee935335c), [`c4c7dad`](https://github.com/mastra-ai/mastra/commit/c4c7dadfe2e4584f079f6c24bfabdb8c4981827f), [`787f3ac`](https://github.com/mastra-ai/mastra/commit/787f3ac08b3bb77413645a7ab5c447fa851708fd), [`45c3112`](https://github.com/mastra-ai/mastra/commit/45c31122666a0cc56b94727099fcb1871ed1b3f6), [`7296fcc`](https://github.com/mastra-ai/mastra/commit/7296fcc599c876a68699a71c7054a16d5aaf2337), [`00c27f9`](https://github.com/mastra-ai/mastra/commit/00c27f9080731433230a61be69c44e39a7a7b4c7), [`5e7c287`](https://github.com/mastra-ai/mastra/commit/5e7c28701f2bce795dd5c811e4c3060bf2ea2242), [`7e17d3f`](https://github.com/mastra-ai/mastra/commit/7e17d3f656fdda2aad47c4beb8c491636d70820c), [`ee19c9b`](https://github.com/mastra-ai/mastra/commit/ee19c9ba3ec3ed91feb214ad539bdc766c53bb01)]:
|
|
40
|
+
- @mastra/schema-compat@1.2.1
|
|
41
|
+
- @mastra/core@1.12.0
|
|
42
|
+
|
|
3
43
|
## 1.7.0-alpha.1
|
|
4
44
|
|
|
5
45
|
### Minor Changes
|
|
@@ -796,6 +796,11 @@ ${maybeTruncate(resultStr, maxLen)}`;
|
|
|
796
796
|
${maybeTruncate(argsStr, maxLen)}`;
|
|
797
797
|
}
|
|
798
798
|
const partType = part.type;
|
|
799
|
+
if (partType === "reasoning") {
|
|
800
|
+
const reasoning = part.reasoning;
|
|
801
|
+
if (reasoning) return maybeTruncate(reasoning, maxLen);
|
|
802
|
+
return "";
|
|
803
|
+
}
|
|
799
804
|
if (partType === "image" || partType === "file") {
|
|
800
805
|
const attachment = part;
|
|
801
806
|
const inputAttachment = toObserverInputAttachmentPart(attachment);
|
|
@@ -810,6 +815,9 @@ ${maybeTruncate(argsStr, maxLen)}`;
|
|
|
810
815
|
} else if (msg.content?.content) {
|
|
811
816
|
content = maybeTruncate(msg.content.content, maxLen);
|
|
812
817
|
}
|
|
818
|
+
if (!content && attachments.length === 0) {
|
|
819
|
+
return { text: "", attachments };
|
|
820
|
+
}
|
|
813
821
|
return {
|
|
814
822
|
text: `**${role}${timestampStr}:**
|
|
815
823
|
${content}`,
|
|
@@ -818,18 +826,21 @@ ${content}`,
|
|
|
818
826
|
}
|
|
819
827
|
function formatMessagesForObserver(messages, options) {
|
|
820
828
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
821
|
-
return messages.map((msg) => formatObserverMessage(msg, counter, options).text).join("\n\n---\n\n");
|
|
829
|
+
return messages.map((msg) => formatObserverMessage(msg, counter, options).text).filter(Boolean).join("\n\n---\n\n");
|
|
822
830
|
}
|
|
823
831
|
function buildObserverHistoryMessage(messages) {
|
|
824
832
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
825
833
|
const content = [{ type: "text", text: "## New Message History to Observe\n\n" }];
|
|
826
|
-
|
|
834
|
+
let visibleCount = 0;
|
|
835
|
+
messages.forEach((message) => {
|
|
827
836
|
const formatted = formatObserverMessage(message, counter);
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (index < messages.length - 1) {
|
|
837
|
+
if (!formatted.text && formatted.attachments.length === 0) return;
|
|
838
|
+
if (visibleCount > 0) {
|
|
831
839
|
content.push({ type: "text", text: "\n\n---\n\n" });
|
|
832
840
|
}
|
|
841
|
+
content.push({ type: "text", text: formatted.text });
|
|
842
|
+
content.push(...formatted.attachments);
|
|
843
|
+
visibleCount++;
|
|
833
844
|
});
|
|
834
845
|
return {
|
|
835
846
|
role: "user",
|
|
@@ -858,16 +869,22 @@ The following messages are from ${threadOrder.length} different conversation thr
|
|
|
858
869
|
threadOrder.forEach((threadId, threadIndex) => {
|
|
859
870
|
const messages = messagesByThread.get(threadId);
|
|
860
871
|
if (!messages || messages.length === 0) return;
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
messages.forEach((message
|
|
872
|
+
const threadContent = [];
|
|
873
|
+
let visibleCount = 0;
|
|
874
|
+
messages.forEach((message) => {
|
|
864
875
|
const formatted = formatObserverMessage(message, counter);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
content.push({ type: "text", text: "\n\n---\n\n" });
|
|
876
|
+
if (!formatted.text && formatted.attachments.length === 0) return;
|
|
877
|
+
if (visibleCount > 0) {
|
|
878
|
+
threadContent.push({ type: "text", text: "\n\n---\n\n" });
|
|
869
879
|
}
|
|
880
|
+
threadContent.push({ type: "text", text: formatted.text });
|
|
881
|
+
threadContent.push(...formatted.attachments);
|
|
882
|
+
visibleCount++;
|
|
870
883
|
});
|
|
884
|
+
if (visibleCount === 0) return;
|
|
885
|
+
content.push({ type: "text", text: `<thread id="${threadId}">
|
|
886
|
+
` });
|
|
887
|
+
content.push(...threadContent);
|
|
871
888
|
content.push({ type: "text", text: "\n</thread>" });
|
|
872
889
|
if (threadIndex < threadOrder.length - 1) {
|
|
873
890
|
content.push({ type: "text", text: "\n\n" });
|
|
@@ -878,7 +895,7 @@ The following messages are from ${threadOrder.length} different conversation thr
|
|
|
878
895
|
content
|
|
879
896
|
};
|
|
880
897
|
}
|
|
881
|
-
function buildMultiThreadObserverTaskPrompt(existingObservations) {
|
|
898
|
+
function buildMultiThreadObserverTaskPrompt(existingObservations, threadOrder, priorMetadataByThread, wasTruncated) {
|
|
882
899
|
let prompt = "";
|
|
883
900
|
if (existingObservations) {
|
|
884
901
|
prompt += `## Previous Observations
|
|
@@ -890,6 +907,39 @@ ${existingObservations}
|
|
|
890
907
|
`;
|
|
891
908
|
prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
|
|
892
909
|
}
|
|
910
|
+
const hasTruncatedObservations = wasTruncated ?? false;
|
|
911
|
+
const threadMetadataLines = threadOrder?.map((threadId) => {
|
|
912
|
+
const metadata = priorMetadataByThread?.get(threadId);
|
|
913
|
+
if (!metadata?.currentTask && !metadata?.suggestedResponse) {
|
|
914
|
+
return "";
|
|
915
|
+
}
|
|
916
|
+
const lines = [`- thread ${threadId}`];
|
|
917
|
+
if (metadata.currentTask) {
|
|
918
|
+
lines.push(` - prior current-task: ${metadata.currentTask}`);
|
|
919
|
+
}
|
|
920
|
+
if (metadata.suggestedResponse) {
|
|
921
|
+
lines.push(` - prior suggested-response: ${metadata.suggestedResponse}`);
|
|
922
|
+
}
|
|
923
|
+
return lines.join("\n");
|
|
924
|
+
}).filter(Boolean).join("\n");
|
|
925
|
+
if (threadMetadataLines) {
|
|
926
|
+
prompt += `## Prior Thread Metadata
|
|
927
|
+
|
|
928
|
+
${threadMetadataLines}
|
|
929
|
+
|
|
930
|
+
`;
|
|
931
|
+
if (hasTruncatedObservations) {
|
|
932
|
+
prompt += `Previous observations were truncated for context budget reasons.
|
|
933
|
+
`;
|
|
934
|
+
prompt += `The main agent still has full memory context outside this observer window.
|
|
935
|
+
`;
|
|
936
|
+
}
|
|
937
|
+
prompt += `Use each thread's prior current-task and suggested-response as continuity hints, then update them based on that thread's new messages.
|
|
938
|
+
|
|
939
|
+
---
|
|
940
|
+
|
|
941
|
+
`;
|
|
942
|
+
}
|
|
893
943
|
prompt += `## Your Task
|
|
894
944
|
|
|
895
945
|
`;
|
|
@@ -978,6 +1028,32 @@ ${existingObservations}
|
|
|
978
1028
|
`;
|
|
979
1029
|
prompt += "Do not repeat these existing observations. Your new observations will be appended to the existing observations.\n\n";
|
|
980
1030
|
}
|
|
1031
|
+
const hasTruncatedObservations = options?.wasTruncated ?? false;
|
|
1032
|
+
const priorMetadataLines = [];
|
|
1033
|
+
if (options?.priorCurrentTask) {
|
|
1034
|
+
priorMetadataLines.push(`- prior current-task: ${options.priorCurrentTask}`);
|
|
1035
|
+
}
|
|
1036
|
+
if (options?.priorSuggestedResponse) {
|
|
1037
|
+
priorMetadataLines.push(`- prior suggested-response: ${options.priorSuggestedResponse}`);
|
|
1038
|
+
}
|
|
1039
|
+
if (priorMetadataLines.length > 0) {
|
|
1040
|
+
prompt += `## Prior Thread Metadata
|
|
1041
|
+
|
|
1042
|
+
${priorMetadataLines.join("\n")}
|
|
1043
|
+
|
|
1044
|
+
`;
|
|
1045
|
+
if (hasTruncatedObservations) {
|
|
1046
|
+
prompt += `Previous observations were truncated for context budget reasons.
|
|
1047
|
+
`;
|
|
1048
|
+
prompt += `The main agent still has full memory context outside this observer window.
|
|
1049
|
+
`;
|
|
1050
|
+
}
|
|
1051
|
+
prompt += `Use the prior current-task and suggested-response as continuity hints, then update them based on the new messages.
|
|
1052
|
+
|
|
1053
|
+
---
|
|
1054
|
+
|
|
1055
|
+
`;
|
|
1056
|
+
}
|
|
981
1057
|
prompt += `## Your Task
|
|
982
1058
|
|
|
983
1059
|
`;
|
|
@@ -3271,6 +3347,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3271
3347
|
config.observation?.blockAfter ?? (config.observation?.bufferTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.bufferTokens ? 1.2 : void 0),
|
|
3272
3348
|
config.observation?.messageTokens ?? OBSERVATIONAL_MEMORY_DEFAULTS.observation.messageTokens
|
|
3273
3349
|
),
|
|
3350
|
+
previousObserverTokens: config.observation?.previousObserverTokens ?? 2e3,
|
|
3274
3351
|
instruction: config.observation?.instruction
|
|
3275
3352
|
};
|
|
3276
3353
|
this.reflectionConfig = {
|
|
@@ -3307,7 +3384,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3307
3384
|
return {
|
|
3308
3385
|
scope: this.scope,
|
|
3309
3386
|
observation: {
|
|
3310
|
-
messageTokens: this.observationConfig.messageTokens
|
|
3387
|
+
messageTokens: this.observationConfig.messageTokens,
|
|
3388
|
+
previousObserverTokens: this.observationConfig.previousObserverTokens
|
|
3311
3389
|
},
|
|
3312
3390
|
reflection: {
|
|
3313
3391
|
observationTokens: this.reflectionConfig.observationTokens
|
|
@@ -3387,7 +3465,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3387
3465
|
scope: this.scope,
|
|
3388
3466
|
observation: {
|
|
3389
3467
|
messageTokens: this.observationConfig.messageTokens,
|
|
3390
|
-
model: observationModelName
|
|
3468
|
+
model: observationModelName,
|
|
3469
|
+
previousObserverTokens: this.observationConfig.previousObserverTokens
|
|
3391
3470
|
},
|
|
3392
3471
|
reflection: {
|
|
3393
3472
|
observationTokens: this.reflectionConfig.observationTokens,
|
|
@@ -3452,6 +3531,13 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3452
3531
|
);
|
|
3453
3532
|
}
|
|
3454
3533
|
}
|
|
3534
|
+
if (this.observationConfig.previousObserverTokens !== void 0 && this.observationConfig.previousObserverTokens !== false) {
|
|
3535
|
+
if (!Number.isFinite(this.observationConfig.previousObserverTokens) || this.observationConfig.previousObserverTokens < 0) {
|
|
3536
|
+
throw new Error(
|
|
3537
|
+
`observation.previousObserverTokens must be false or a finite number >= 0, got ${this.observationConfig.previousObserverTokens}`
|
|
3538
|
+
);
|
|
3539
|
+
}
|
|
3540
|
+
}
|
|
3455
3541
|
if (this.reflectionConfig.bufferActivation !== void 0) {
|
|
3456
3542
|
if (this.reflectionConfig.bufferActivation <= 0 || this.reflectionConfig.bufferActivation > 1) {
|
|
3457
3543
|
throw new Error(
|
|
@@ -3849,6 +3935,141 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3849
3935
|
}
|
|
3850
3936
|
return result;
|
|
3851
3937
|
}
|
|
3938
|
+
/**
|
|
3939
|
+
* Prepare optimized observer context by applying truncation and buffered-reflection inclusion.
|
|
3940
|
+
*
|
|
3941
|
+
* Returns the (possibly optimized) observations string to pass as "Previous Observations"
|
|
3942
|
+
* to the observer prompt. When no optimization options are set, returns the input unchanged.
|
|
3943
|
+
*/
|
|
3944
|
+
prepareObserverContext(existingObservations, record) {
|
|
3945
|
+
const { previousObserverTokens } = this.observationConfig;
|
|
3946
|
+
const tokenBudget = previousObserverTokens === void 0 || previousObserverTokens === false ? void 0 : previousObserverTokens;
|
|
3947
|
+
if (tokenBudget === void 0) {
|
|
3948
|
+
return { context: existingObservations, wasTruncated: false };
|
|
3949
|
+
}
|
|
3950
|
+
const bufferedReflection = record?.bufferedReflection && record?.reflectedObservationLineCount ? record.bufferedReflection : void 0;
|
|
3951
|
+
if (!existingObservations) {
|
|
3952
|
+
return { context: bufferedReflection, wasTruncated: false };
|
|
3953
|
+
}
|
|
3954
|
+
let observations = existingObservations;
|
|
3955
|
+
if (bufferedReflection && record?.reflectedObservationLineCount) {
|
|
3956
|
+
const allLines = observations.split("\n");
|
|
3957
|
+
const unreflectedLines = allLines.slice(record.reflectedObservationLineCount);
|
|
3958
|
+
const unreflectedContent = unreflectedLines.join("\n").trim();
|
|
3959
|
+
observations = unreflectedContent ? `${bufferedReflection}
|
|
3960
|
+
|
|
3961
|
+
${unreflectedContent}` : bufferedReflection;
|
|
3962
|
+
}
|
|
3963
|
+
let wasTruncated = false;
|
|
3964
|
+
if (tokenBudget !== void 0) {
|
|
3965
|
+
if (tokenBudget === 0) {
|
|
3966
|
+
return { context: "", wasTruncated: true };
|
|
3967
|
+
}
|
|
3968
|
+
const currentTokens = this.tokenCounter.countObservations(observations);
|
|
3969
|
+
if (currentTokens > tokenBudget) {
|
|
3970
|
+
observations = this.truncateObservationsToTokenBudget(observations, tokenBudget);
|
|
3971
|
+
wasTruncated = true;
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
return { context: observations, wasTruncated };
|
|
3975
|
+
}
|
|
3976
|
+
/**
|
|
3977
|
+
* Truncate observations to fit within a token budget.
|
|
3978
|
+
*
|
|
3979
|
+
* Strategy:
|
|
3980
|
+
* 1. Keep a raw tail of recent observations (end of block).
|
|
3981
|
+
* 2. Add a truncation marker: [X observations truncated here], placed at the hidden gap.
|
|
3982
|
+
* 3. Try to preserve important observations (🔴) from older context, newest-first.
|
|
3983
|
+
* 4. Enforce that at least 50% of kept observations remain raw tail observations.
|
|
3984
|
+
*/
|
|
3985
|
+
truncateObservationsToTokenBudget(observations, budget) {
|
|
3986
|
+
if (budget === 0) {
|
|
3987
|
+
return "";
|
|
3988
|
+
}
|
|
3989
|
+
const totalTokens = this.tokenCounter.countObservations(observations);
|
|
3990
|
+
if (totalTokens <= budget) {
|
|
3991
|
+
return observations;
|
|
3992
|
+
}
|
|
3993
|
+
const lines = observations.split("\n");
|
|
3994
|
+
const totalCount = lines.length;
|
|
3995
|
+
const lineTokens = new Array(totalCount);
|
|
3996
|
+
const isImportant = new Array(totalCount);
|
|
3997
|
+
for (let i = 0; i < totalCount; i++) {
|
|
3998
|
+
lineTokens[i] = this.tokenCounter.countString(lines[i]);
|
|
3999
|
+
isImportant[i] = lines[i].includes("\u{1F534}");
|
|
4000
|
+
}
|
|
4001
|
+
const suffixTokens = new Array(totalCount + 1);
|
|
4002
|
+
suffixTokens[totalCount] = 0;
|
|
4003
|
+
for (let i = totalCount - 1; i >= 0; i--) {
|
|
4004
|
+
suffixTokens[i] = suffixTokens[i + 1] + lineTokens[i];
|
|
4005
|
+
}
|
|
4006
|
+
const headImportantIndexes = [];
|
|
4007
|
+
const buildCandidateString = (tailStart, selectedImportantIndexes) => {
|
|
4008
|
+
const keptIndexes = [
|
|
4009
|
+
...selectedImportantIndexes,
|
|
4010
|
+
...Array.from({ length: totalCount - tailStart }, (_, i) => tailStart + i)
|
|
4011
|
+
].sort((a, b) => a - b);
|
|
4012
|
+
if (keptIndexes.length === 0) {
|
|
4013
|
+
return `[${totalCount} observations truncated here]`;
|
|
4014
|
+
}
|
|
4015
|
+
const outputLines = [];
|
|
4016
|
+
let previousKeptIndex = -1;
|
|
4017
|
+
for (const keptIndex of keptIndexes) {
|
|
4018
|
+
const hiddenCount = keptIndex - previousKeptIndex - 1;
|
|
4019
|
+
if (hiddenCount === 1) {
|
|
4020
|
+
outputLines.push(lines[previousKeptIndex + 1]);
|
|
4021
|
+
} else if (hiddenCount > 1) {
|
|
4022
|
+
outputLines.push(`[${hiddenCount} observations truncated here]`);
|
|
4023
|
+
}
|
|
4024
|
+
outputLines.push(lines[keptIndex]);
|
|
4025
|
+
previousKeptIndex = keptIndex;
|
|
4026
|
+
}
|
|
4027
|
+
const trailingHiddenCount = totalCount - previousKeptIndex - 1;
|
|
4028
|
+
if (trailingHiddenCount === 1) {
|
|
4029
|
+
outputLines.push(lines[totalCount - 1]);
|
|
4030
|
+
} else if (trailingHiddenCount > 1) {
|
|
4031
|
+
outputLines.push(`[${trailingHiddenCount} observations truncated here]`);
|
|
4032
|
+
}
|
|
4033
|
+
return outputLines.join("\n");
|
|
4034
|
+
};
|
|
4035
|
+
const estimateKeptContentCost = (tailStart, selectedImportantIndexes) => {
|
|
4036
|
+
let cost = suffixTokens[tailStart];
|
|
4037
|
+
for (const idx of selectedImportantIndexes) {
|
|
4038
|
+
cost += lineTokens[idx];
|
|
4039
|
+
}
|
|
4040
|
+
return cost;
|
|
4041
|
+
};
|
|
4042
|
+
let bestCandidate;
|
|
4043
|
+
let bestImportantCount = -1;
|
|
4044
|
+
let bestRawTailLength = -1;
|
|
4045
|
+
for (let tailStart = 1; tailStart < totalCount; tailStart++) {
|
|
4046
|
+
if (isImportant[tailStart - 1]) {
|
|
4047
|
+
headImportantIndexes.push(tailStart - 1);
|
|
4048
|
+
}
|
|
4049
|
+
const rawTailLength = totalCount - tailStart;
|
|
4050
|
+
const maxImportantByRatio = rawTailLength;
|
|
4051
|
+
let importantToKeep = Math.min(headImportantIndexes.length, maxImportantByRatio);
|
|
4052
|
+
const getSelectedImportant = (count) => count > 0 ? headImportantIndexes.slice(Math.max(0, headImportantIndexes.length - count)) : [];
|
|
4053
|
+
while (importantToKeep > 0 && estimateKeptContentCost(tailStart, getSelectedImportant(importantToKeep)) > budget) {
|
|
4054
|
+
importantToKeep -= 1;
|
|
4055
|
+
}
|
|
4056
|
+
if (estimateKeptContentCost(tailStart, getSelectedImportant(importantToKeep)) > budget) {
|
|
4057
|
+
continue;
|
|
4058
|
+
}
|
|
4059
|
+
if (importantToKeep > bestImportantCount || importantToKeep === bestImportantCount && rawTailLength > bestRawTailLength) {
|
|
4060
|
+
const candidate = buildCandidateString(tailStart, getSelectedImportant(importantToKeep));
|
|
4061
|
+
if (this.tokenCounter.countObservations(candidate) <= budget) {
|
|
4062
|
+
bestCandidate = candidate;
|
|
4063
|
+
bestImportantCount = importantToKeep;
|
|
4064
|
+
bestRawTailLength = rawTailLength;
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
if (!bestCandidate) {
|
|
4069
|
+
return `[${totalCount} observations truncated here]`;
|
|
4070
|
+
}
|
|
4071
|
+
return bestCandidate;
|
|
4072
|
+
}
|
|
3852
4073
|
/**
|
|
3853
4074
|
* Call the Observer agent to extract observations.
|
|
3854
4075
|
*/
|
|
@@ -3857,12 +4078,17 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3857
4078
|
const observerMessages = [
|
|
3858
4079
|
{
|
|
3859
4080
|
role: "user",
|
|
3860
|
-
content: buildObserverTaskPrompt(existingObservations,
|
|
4081
|
+
content: buildObserverTaskPrompt(existingObservations, {
|
|
4082
|
+
skipContinuationHints: options?.skipContinuationHints,
|
|
4083
|
+
priorCurrentTask: options?.priorCurrentTask,
|
|
4084
|
+
priorSuggestedResponse: options?.priorSuggestedResponse,
|
|
4085
|
+
wasTruncated: options?.wasTruncated
|
|
4086
|
+
})
|
|
3861
4087
|
},
|
|
3862
4088
|
buildObserverHistoryMessage(messagesToObserve)
|
|
3863
4089
|
];
|
|
3864
4090
|
const doGenerate = async () => {
|
|
3865
|
-
|
|
4091
|
+
const result2 = await this.withAbortCheck(async () => {
|
|
3866
4092
|
const streamResult = await agent.stream(observerMessages, {
|
|
3867
4093
|
modelSettings: {
|
|
3868
4094
|
...this.observationConfig.modelSettings
|
|
@@ -3873,6 +4099,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3873
4099
|
});
|
|
3874
4100
|
return streamResult.getFullOutput();
|
|
3875
4101
|
}, abortSignal);
|
|
4102
|
+
return result2;
|
|
3876
4103
|
};
|
|
3877
4104
|
let result = await doGenerate();
|
|
3878
4105
|
let parsed = parseObserverOutput(result.text);
|
|
@@ -3903,17 +4130,23 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3903
4130
|
* Returns per-thread results with observations, currentTask, and suggestedContinuation,
|
|
3904
4131
|
* plus the total usage for the batch.
|
|
3905
4132
|
*/
|
|
3906
|
-
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, abortSignal, requestContext) {
|
|
4133
|
+
async callMultiThreadObserver(existingObservations, messagesByThread, threadOrder, priorMetadataByThread, abortSignal, requestContext, wasTruncated) {
|
|
4134
|
+
const systemPrompt = buildObserverSystemPrompt(true, this.observationConfig.instruction);
|
|
3907
4135
|
const agent = new Agent({
|
|
3908
4136
|
id: "multi-thread-observer",
|
|
3909
4137
|
name: "multi-thread-observer",
|
|
3910
4138
|
model: this.observationConfig.model,
|
|
3911
|
-
instructions:
|
|
4139
|
+
instructions: systemPrompt
|
|
3912
4140
|
});
|
|
3913
4141
|
const observerMessages = [
|
|
3914
4142
|
{
|
|
3915
4143
|
role: "user",
|
|
3916
|
-
content: buildMultiThreadObserverTaskPrompt(
|
|
4144
|
+
content: buildMultiThreadObserverTaskPrompt(
|
|
4145
|
+
existingObservations,
|
|
4146
|
+
threadOrder,
|
|
4147
|
+
priorMetadataByThread,
|
|
4148
|
+
wasTruncated
|
|
4149
|
+
)
|
|
3917
4150
|
},
|
|
3918
4151
|
buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder)
|
|
3919
4152
|
];
|
|
@@ -3925,7 +4158,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3925
4158
|
this.observedMessageIds.add(msg.id);
|
|
3926
4159
|
}
|
|
3927
4160
|
const doGenerate = async () => {
|
|
3928
|
-
|
|
4161
|
+
const result2 = await this.withAbortCheck(async () => {
|
|
3929
4162
|
const streamResult = await agent.stream(observerMessages, {
|
|
3930
4163
|
modelSettings: {
|
|
3931
4164
|
...this.observationConfig.modelSettings
|
|
@@ -3936,6 +4169,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3936
4169
|
});
|
|
3937
4170
|
return streamResult.getFullOutput();
|
|
3938
4171
|
}, abortSignal);
|
|
4172
|
+
return result2;
|
|
3939
4173
|
};
|
|
3940
4174
|
let result = await doGenerate();
|
|
3941
4175
|
let parsed = parseMultiThreadObserverOutput(result.text);
|
|
@@ -5369,12 +5603,18 @@ ${newThreadSection}`;
|
|
|
5369
5603
|
);
|
|
5370
5604
|
}
|
|
5371
5605
|
}
|
|
5372
|
-
const
|
|
5606
|
+
const { context: observerContext, wasTruncated } = this.prepareObserverContext(
|
|
5373
5607
|
freshRecord?.activeObservations ?? record.activeObservations,
|
|
5374
|
-
|
|
5375
|
-
abortSignal,
|
|
5376
|
-
{ requestContext }
|
|
5608
|
+
freshRecord ?? record
|
|
5377
5609
|
);
|
|
5610
|
+
const thread = await this.storage.getThreadById({ threadId });
|
|
5611
|
+
const threadOMMetadata = getThreadOMMetadata(thread?.metadata);
|
|
5612
|
+
const result = await this.callObserver(observerContext, messagesToObserve, abortSignal, {
|
|
5613
|
+
requestContext,
|
|
5614
|
+
priorCurrentTask: threadOMMetadata?.currentTask,
|
|
5615
|
+
priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
|
|
5616
|
+
wasTruncated
|
|
5617
|
+
});
|
|
5378
5618
|
const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
|
|
5379
5619
|
let newObservations;
|
|
5380
5620
|
if (this.scope === "resource") {
|
|
@@ -5391,16 +5631,16 @@ ${result.observations}` : result.observations;
|
|
|
5391
5631
|
const newMessageIds = messagesToObserve.map((m) => m.id);
|
|
5392
5632
|
const existingIds = freshRecord?.observedMessageIds ?? record.observedMessageIds ?? [];
|
|
5393
5633
|
const allObservedIds = [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])];
|
|
5394
|
-
const
|
|
5395
|
-
if (
|
|
5396
|
-
const newMetadata = setThreadOMMetadata(
|
|
5634
|
+
const threadForMetadata = await this.storage.getThreadById({ threadId });
|
|
5635
|
+
if (threadForMetadata) {
|
|
5636
|
+
const newMetadata = setThreadOMMetadata(threadForMetadata.metadata, {
|
|
5397
5637
|
suggestedResponse: result.suggestedContinuation,
|
|
5398
5638
|
currentTask: result.currentTask,
|
|
5399
5639
|
lastObservedMessageCursor: this.getLastObservedMessageCursor(messagesToObserve)
|
|
5400
5640
|
});
|
|
5401
5641
|
await this.storage.updateThread({
|
|
5402
5642
|
id: threadId,
|
|
5403
|
-
title:
|
|
5643
|
+
title: threadForMetadata.title ?? "",
|
|
5404
5644
|
metadata: newMetadata
|
|
5405
5645
|
});
|
|
5406
5646
|
}
|
|
@@ -5634,12 +5874,21 @@ ${result.observations}` : result.observations;
|
|
|
5634
5874
|
const bufferedChunks = this.getBufferedChunks(record);
|
|
5635
5875
|
const bufferedChunksText = bufferedChunks.map((c) => c.observations).join("\n\n");
|
|
5636
5876
|
const combinedObservations = this.combineObservationsForBuffering(record.activeObservations, bufferedChunksText);
|
|
5877
|
+
const { context: observerContext, wasTruncated } = this.prepareObserverContext(combinedObservations, record);
|
|
5878
|
+
const thread = await this.storage.getThreadById({ threadId });
|
|
5879
|
+
const threadOMMetadata = getThreadOMMetadata(thread?.metadata);
|
|
5637
5880
|
const result = await this.callObserver(
|
|
5638
|
-
|
|
5881
|
+
observerContext,
|
|
5639
5882
|
messagesToBuffer,
|
|
5640
5883
|
void 0,
|
|
5641
5884
|
// No abort signal for background ops
|
|
5642
|
-
{
|
|
5885
|
+
{
|
|
5886
|
+
skipContinuationHints: true,
|
|
5887
|
+
requestContext,
|
|
5888
|
+
priorCurrentTask: threadOMMetadata?.currentTask,
|
|
5889
|
+
priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
|
|
5890
|
+
wasTruncated
|
|
5891
|
+
}
|
|
5643
5892
|
);
|
|
5644
5893
|
if (!result.observations) {
|
|
5645
5894
|
omDebug(`[OM:doAsyncBufferedObservation] empty observations returned, skipping buffer storage`);
|
|
@@ -5706,8 +5955,6 @@ ${result.observations}` : result.observations;
|
|
|
5706
5955
|
}
|
|
5707
5956
|
return `${activeObservations}
|
|
5708
5957
|
|
|
5709
|
-
--- BUFFERED (pending activation) ---
|
|
5710
|
-
|
|
5711
5958
|
${bufferedObservations}`;
|
|
5712
5959
|
}
|
|
5713
5960
|
/**
|
|
@@ -6067,7 +6314,11 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6067
6314
|
const threadMetadataMap = /* @__PURE__ */ new Map();
|
|
6068
6315
|
for (const thread of allThreads) {
|
|
6069
6316
|
const omMetadata = getThreadOMMetadata(thread.metadata);
|
|
6070
|
-
threadMetadataMap.set(thread.id, {
|
|
6317
|
+
threadMetadataMap.set(thread.id, {
|
|
6318
|
+
lastObservedAt: omMetadata?.lastObservedAt,
|
|
6319
|
+
currentTask: omMetadata?.currentTask,
|
|
6320
|
+
suggestedResponse: omMetadata?.suggestedResponse
|
|
6321
|
+
});
|
|
6071
6322
|
}
|
|
6072
6323
|
const messagesByThread = /* @__PURE__ */ new Map();
|
|
6073
6324
|
for (const thread of allThreads) {
|
|
@@ -6147,7 +6398,12 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6147
6398
|
return;
|
|
6148
6399
|
}
|
|
6149
6400
|
}
|
|
6150
|
-
const
|
|
6401
|
+
const rawExistingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
|
|
6402
|
+
const { context: optimizedObservations, wasTruncated } = this.prepareObserverContext(
|
|
6403
|
+
rawExistingObservations,
|
|
6404
|
+
freshRecord ?? record
|
|
6405
|
+
);
|
|
6406
|
+
const existingObservations = optimizedObservations ?? rawExistingObservations;
|
|
6151
6407
|
for (const threadId of threadOrder) {
|
|
6152
6408
|
const msgs = messagesByThread.get(threadId);
|
|
6153
6409
|
if (msgs && msgs.length > 0) {
|
|
@@ -6211,12 +6467,24 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6211
6467
|
batches.push(currentBatch);
|
|
6212
6468
|
}
|
|
6213
6469
|
const batchPromises = batches.map(async (batch) => {
|
|
6470
|
+
const batchPriorMetadata = /* @__PURE__ */ new Map();
|
|
6471
|
+
for (const threadId of batch.threadIds) {
|
|
6472
|
+
const metadata = threadMetadataMap.get(threadId);
|
|
6473
|
+
if (metadata?.currentTask || metadata?.suggestedResponse) {
|
|
6474
|
+
batchPriorMetadata.set(threadId, {
|
|
6475
|
+
currentTask: metadata.currentTask,
|
|
6476
|
+
suggestedResponse: metadata.suggestedResponse
|
|
6477
|
+
});
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6214
6480
|
const batchResult = await this.callMultiThreadObserver(
|
|
6215
6481
|
existingObservations,
|
|
6216
6482
|
batch.threadMap,
|
|
6217
6483
|
batch.threadIds,
|
|
6484
|
+
batchPriorMetadata,
|
|
6218
6485
|
abortSignal,
|
|
6219
|
-
requestContext
|
|
6486
|
+
requestContext,
|
|
6487
|
+
wasTruncated
|
|
6220
6488
|
);
|
|
6221
6489
|
return batchResult;
|
|
6222
6490
|
});
|
|
@@ -6247,7 +6515,7 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6247
6515
|
result
|
|
6248
6516
|
});
|
|
6249
6517
|
}
|
|
6250
|
-
let currentObservations =
|
|
6518
|
+
let currentObservations = rawExistingObservations;
|
|
6251
6519
|
let cycleObservationTokens = 0;
|
|
6252
6520
|
for (const obsResult of observationResults) {
|
|
6253
6521
|
if (!obsResult) continue;
|
|
@@ -6701,5 +6969,5 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6701
6969
|
};
|
|
6702
6970
|
|
|
6703
6971
|
export { OBSERVATIONAL_MEMORY_DEFAULTS, OBSERVATION_CONTEXT_INSTRUCTIONS, OBSERVATION_CONTEXT_PROMPT, OBSERVATION_CONTINUATION_HINT, OBSERVER_SYSTEM_PROMPT, ObservationalMemory, TokenCounter, buildObserverPrompt, buildObserverSystemPrompt, extractCurrentTask, formatMessagesForObserver, hasCurrentTaskSection, optimizeObservationsForContext, parseObserverOutput };
|
|
6704
|
-
//# sourceMappingURL=chunk-
|
|
6705
|
-
//# sourceMappingURL=chunk-
|
|
6972
|
+
//# sourceMappingURL=chunk-SUU4IAZJ.js.map
|
|
6973
|
+
//# sourceMappingURL=chunk-SUU4IAZJ.js.map
|