@mastra/memory 1.8.3-alpha.0 → 1.8.3-alpha.2
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 +33 -0
- package/dist/{chunk-SUU4IAZJ.js → chunk-4KPXPQX3.js} +212 -75
- package/dist/chunk-4KPXPQX3.js.map +1 -0
- package/dist/{chunk-YPFNHFT6.cjs → chunk-LGCREJMO.cjs} +212 -74
- package/dist/chunk-LGCREJMO.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +30 -25
- package/dist/docs/references/docs-agents-agent-approval.md +4 -4
- package/dist/docs/references/docs-agents-network-approval.md +1 -1
- package/dist/docs/references/docs-agents-networks.md +1 -1
- package/dist/docs/references/docs-agents-supervisor-agents.md +3 -3
- package/dist/docs/references/docs-memory-memory-processors.md +6 -6
- package/dist/docs/references/docs-memory-semantic-recall.md +1 -1
- package/dist/docs/references/docs-memory-storage.md +1 -1
- package/dist/docs/references/docs-memory-working-memory.md +2 -2
- package/dist/docs/references/reference-memory-memory-class.md +3 -3
- package/dist/docs/references/reference-memory-observational-memory.md +5 -5
- package/dist/docs/references/reference-processors-token-limiter-processor.md +3 -3
- package/dist/docs/references/reference-storage-mongodb.md +1 -1
- package/dist/docs/references/reference-storage-postgresql.md +1 -1
- package/dist/docs/references/reference-storage-upstash.md +1 -1
- package/dist/docs/references/reference-vectors-libsql.md +1 -1
- package/dist/docs/references/reference-vectors-mongodb.md +1 -1
- package/dist/docs/references/reference-vectors-pg.md +1 -1
- package/dist/docs/references/reference-vectors-upstash.md +1 -1
- package/dist/index.cjs +16 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -10
- package/dist/index.js.map +1 -1
- package/dist/{observational-memory-3HFM7PY2.cjs → observational-memory-4TV5KKFV.cjs} +21 -17
- package/dist/{observational-memory-3HFM7PY2.cjs.map → observational-memory-4TV5KKFV.cjs.map} +1 -1
- package/dist/observational-memory-UEDVTWS2.js +3 -0
- package/dist/{observational-memory-XXD6E2SO.js.map → observational-memory-UEDVTWS2.js.map} +1 -1
- package/dist/processors/index.cjs +19 -15
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/index.d.ts +1 -0
- package/dist/processors/observational-memory/index.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-utils.d.ts +16 -0
- package/dist/processors/observational-memory/observation-utils.d.ts.map +1 -0
- package/dist/processors/observational-memory/observational-memory.d.ts +13 -4
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts +9 -7
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/token-counter.d.ts.map +1 -1
- package/dist/processors/observational-memory/tool-result-helpers.d.ts +12 -0
- package/dist/processors/observational-memory/tool-result-helpers.d.ts.map +1 -0
- package/package.json +6 -6
- package/dist/chunk-SUU4IAZJ.js.map +0 -1
- package/dist/chunk-YPFNHFT6.cjs.map +0 -1
- package/dist/observational-memory-XXD6E2SO.js +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @mastra/memory
|
|
2
2
|
|
|
3
|
+
## 1.8.3-alpha.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed working memory tool description to accurately reflect merge behavior. The previous description incorrectly stated "Set a field to null to remove it" but null values are stripped by validation before reaching the merge logic. The updated description clarifies: omit fields to preserve existing data, and pass complete arrays or omit them since arrays are replaced entirely. ([#14424](https://github.com/mastra-ai/mastra/pull/14424))
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`8b4ce84`](https://github.com/mastra-ai/mastra/commit/8b4ce84aed0808b9805cc4fd7147c1f8a2ef7a36), [`8d4cfe6`](https://github.com/mastra-ai/mastra/commit/8d4cfe6b9a7157d3876206227ec9f04cde6dbc4a), [`68a019d`](https://github.com/mastra-ai/mastra/commit/68a019d30d22251ddd628a2947d60215c03c350a), [`68a019d`](https://github.com/mastra-ai/mastra/commit/68a019d30d22251ddd628a2947d60215c03c350a)]:
|
|
10
|
+
- @mastra/core@1.14.0-alpha.3
|
|
11
|
+
|
|
12
|
+
## 1.8.3-alpha.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Fixed observational memory triggering observation while provider-executed tool calls are still pending, which could split messages and cause errors on follow-up turns. ([#14282](https://github.com/mastra-ai/mastra/pull/14282))
|
|
17
|
+
|
|
18
|
+
- Limit oversized observational-memory tool results before they reach the observer. ([#14344](https://github.com/mastra-ai/mastra/pull/14344))
|
|
19
|
+
|
|
20
|
+
This strips large `encryptedContent` blobs and truncates remaining tool result payloads to keep observer prompts and token estimates aligned with what the model actually sees.
|
|
21
|
+
|
|
22
|
+
- Improved observational memory cache stability by splitting persisted observations into separate prompt chunks using dated message boundary delimiters. ([#14367](https://github.com/mastra-ai/mastra/pull/14367))
|
|
23
|
+
|
|
24
|
+
Added `getObservationsAsOf()` utility to retrieve the observations that were active at a specific point in time. This enables filtering observation history by message creation date.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { getObservationsAsOf } from '@mastra/memory';
|
|
28
|
+
|
|
29
|
+
// Get observations that existed when a specific message was created
|
|
30
|
+
const observations = getObservationsAsOf(record.activeObservations, message.createdAt);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- Updated dependencies [[`4444280`](https://github.com/mastra-ai/mastra/commit/444428094253e916ec077e66284e685fde67021e), [`dbb879a`](https://github.com/mastra-ai/mastra/commit/dbb879af0b809c668e9b3a9d8bac97d806caa267), [`8de3555`](https://github.com/mastra-ai/mastra/commit/8de355572c6fd838f863a3e7e6fe24d0947b774f)]:
|
|
34
|
+
- @mastra/core@1.14.0-alpha.2
|
|
35
|
+
|
|
3
36
|
## 1.8.3-alpha.0
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
|
@@ -6,10 +6,10 @@ import { resolveModelConfig } from '@mastra/core/llm';
|
|
|
6
6
|
import { setThreadOMMetadata, getThreadOMMetadata, parseMemoryRequestContext } from '@mastra/core/memory';
|
|
7
7
|
import { MessageHistory } from '@mastra/core/processors';
|
|
8
8
|
import xxhash from 'xxhash-wasm';
|
|
9
|
+
import { estimateTokenCount } from 'tokenx';
|
|
9
10
|
import { createHash, randomUUID } from 'crypto';
|
|
10
11
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
11
12
|
import imageSize from 'image-size';
|
|
12
|
-
import { estimateTokenCount } from 'tokenx';
|
|
13
13
|
|
|
14
14
|
// src/processors/observational-memory/observational-memory.ts
|
|
15
15
|
|
|
@@ -300,6 +300,97 @@ function createActivationMarker(params) {
|
|
|
300
300
|
}
|
|
301
301
|
};
|
|
302
302
|
}
|
|
303
|
+
var ENCRYPTED_CONTENT_KEY = "encryptedContent";
|
|
304
|
+
var ENCRYPTED_CONTENT_REDACTION_THRESHOLD = 256;
|
|
305
|
+
var DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS = 1e4;
|
|
306
|
+
function isObjectLike(value) {
|
|
307
|
+
return typeof value === "object" && value !== null;
|
|
308
|
+
}
|
|
309
|
+
function sanitizeToolResultValue(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
310
|
+
if (!isObjectLike(value)) {
|
|
311
|
+
return value;
|
|
312
|
+
}
|
|
313
|
+
if (seen.has(value)) {
|
|
314
|
+
return seen.get(value);
|
|
315
|
+
}
|
|
316
|
+
if (Array.isArray(value)) {
|
|
317
|
+
const sanitizedArray = [];
|
|
318
|
+
seen.set(value, sanitizedArray);
|
|
319
|
+
for (const item of value) {
|
|
320
|
+
sanitizedArray.push(sanitizeToolResultValue(item, seen));
|
|
321
|
+
}
|
|
322
|
+
return sanitizedArray;
|
|
323
|
+
}
|
|
324
|
+
const sanitizedObject = {};
|
|
325
|
+
seen.set(value, sanitizedObject);
|
|
326
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
327
|
+
if (key === ENCRYPTED_CONTENT_KEY && typeof entry === "string" && entry.length > ENCRYPTED_CONTENT_REDACTION_THRESHOLD) {
|
|
328
|
+
sanitizedObject[key] = `[stripped encryptedContent: ${entry.length} characters]`;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
sanitizedObject[key] = sanitizeToolResultValue(entry, seen);
|
|
332
|
+
}
|
|
333
|
+
return sanitizedObject;
|
|
334
|
+
}
|
|
335
|
+
function stringifyToolResult(value) {
|
|
336
|
+
if (typeof value === "string") {
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
const sanitized = sanitizeToolResultValue(value);
|
|
340
|
+
try {
|
|
341
|
+
return JSON.stringify(sanitized, null, 2);
|
|
342
|
+
} catch {
|
|
343
|
+
return String(sanitized);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function resolveToolResultValue(part, invocationResult) {
|
|
347
|
+
const mastraMetadata = part?.providerMetadata?.mastra;
|
|
348
|
+
if (mastraMetadata && typeof mastraMetadata === "object" && "modelOutput" in mastraMetadata) {
|
|
349
|
+
return {
|
|
350
|
+
value: mastraMetadata.modelOutput,
|
|
351
|
+
usingStoredModelOutput: true
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
value: invocationResult,
|
|
356
|
+
usingStoredModelOutput: false
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function truncateStringByTokens(text, maxTokens) {
|
|
360
|
+
if (!text || maxTokens <= 0) {
|
|
361
|
+
return "";
|
|
362
|
+
}
|
|
363
|
+
const totalTokens = estimateTokenCount(text);
|
|
364
|
+
if (totalTokens <= maxTokens) {
|
|
365
|
+
return text;
|
|
366
|
+
}
|
|
367
|
+
const buildCandidate = (sliceEnd) => {
|
|
368
|
+
const visible = text.slice(0, sliceEnd);
|
|
369
|
+
const omittedChars = text.length - sliceEnd;
|
|
370
|
+
return `${visible}
|
|
371
|
+
... [truncated ~${totalTokens - estimateTokenCount(visible)} tokens / ${omittedChars} characters]`;
|
|
372
|
+
};
|
|
373
|
+
let low = 0;
|
|
374
|
+
let high = text.length;
|
|
375
|
+
let best = buildCandidate(0);
|
|
376
|
+
while (low <= high) {
|
|
377
|
+
const mid = Math.floor((low + high) / 2);
|
|
378
|
+
const candidate = buildCandidate(mid);
|
|
379
|
+
const candidateTokens = estimateTokenCount(candidate);
|
|
380
|
+
if (candidateTokens <= maxTokens) {
|
|
381
|
+
best = candidate;
|
|
382
|
+
low = mid + 1;
|
|
383
|
+
} else {
|
|
384
|
+
high = mid - 1;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return best;
|
|
388
|
+
}
|
|
389
|
+
function formatToolResultForObserver(value, options) {
|
|
390
|
+
const serialized = stringifyToolResult(value);
|
|
391
|
+
const maxTokens = options?.maxTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
|
|
392
|
+
return truncateStringByTokens(serialized, maxTokens);
|
|
393
|
+
}
|
|
303
394
|
|
|
304
395
|
// src/processors/observational-memory/observer-agent.ts
|
|
305
396
|
var OBSERVER_EXTRACTION_INSTRUCTIONS = `CRITICAL: DISTINGUISH USER ASSERTIONS FROM QUESTIONS
|
|
@@ -774,6 +865,7 @@ function formatObserverAttachmentPlaceholder(part, counter) {
|
|
|
774
865
|
}
|
|
775
866
|
function formatObserverMessage(msg, counter, options) {
|
|
776
867
|
const maxLen = options?.maxPartLength;
|
|
868
|
+
const maxToolResultTokens = options?.maxToolResultTokens ?? DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS;
|
|
777
869
|
const timestamp = formatObserverTimestamp(msg.createdAt);
|
|
778
870
|
const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
|
|
779
871
|
const timestampStr = timestamp ? ` (${timestamp})` : "";
|
|
@@ -787,7 +879,11 @@ function formatObserverMessage(msg, counter, options) {
|
|
|
787
879
|
if (part.type === "tool-invocation") {
|
|
788
880
|
const inv = part.toolInvocation;
|
|
789
881
|
if (inv.state === "result") {
|
|
790
|
-
const
|
|
882
|
+
const { value: resultForObserver } = resolveToolResultValue(
|
|
883
|
+
part,
|
|
884
|
+
inv.result
|
|
885
|
+
);
|
|
886
|
+
const resultStr = formatToolResultForObserver(resultForObserver, { maxTokens: maxToolResultTokens });
|
|
791
887
|
return `[Tool Result: ${inv.toolName}]
|
|
792
888
|
${maybeTruncate(resultStr, maxLen)}`;
|
|
793
889
|
}
|
|
@@ -828,12 +924,12 @@ function formatMessagesForObserver(messages, options) {
|
|
|
828
924
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
829
925
|
return messages.map((msg) => formatObserverMessage(msg, counter, options).text).filter(Boolean).join("\n\n---\n\n");
|
|
830
926
|
}
|
|
831
|
-
function buildObserverHistoryMessage(messages) {
|
|
927
|
+
function buildObserverHistoryMessage(messages, options) {
|
|
832
928
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
833
929
|
const content = [{ type: "text", text: "## New Message History to Observe\n\n" }];
|
|
834
930
|
let visibleCount = 0;
|
|
835
931
|
messages.forEach((message) => {
|
|
836
|
-
const formatted = formatObserverMessage(message, counter);
|
|
932
|
+
const formatted = formatObserverMessage(message, counter, options);
|
|
837
933
|
if (!formatted.text && formatted.attachments.length === 0) return;
|
|
838
934
|
if (visibleCount > 0) {
|
|
839
935
|
content.push({ type: "text", text: "\n\n---\n\n" });
|
|
@@ -854,7 +950,7 @@ function maybeTruncate(str, maxLen) {
|
|
|
854
950
|
return `${truncated}
|
|
855
951
|
... [truncated ${remaining} characters]`;
|
|
856
952
|
}
|
|
857
|
-
function buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder) {
|
|
953
|
+
function buildMultiThreadObserverHistoryMessage(messagesByThread, threadOrder, options) {
|
|
858
954
|
const counter = { nextImageId: 1, nextFileId: 1 };
|
|
859
955
|
const content = [
|
|
860
956
|
{
|
|
@@ -872,7 +968,7 @@ The following messages are from ${threadOrder.length} different conversation thr
|
|
|
872
968
|
const threadContent = [];
|
|
873
969
|
let visibleCount = 0;
|
|
874
970
|
messages.forEach((message) => {
|
|
875
|
-
const formatted = formatObserverMessage(message, counter);
|
|
971
|
+
const formatted = formatObserverMessage(message, counter, options);
|
|
876
972
|
if (!formatted.text && formatted.attachments.length === 0) return;
|
|
877
973
|
if (visibleCount > 0) {
|
|
878
974
|
threadContent.push({ type: "text", text: "\n\n---\n\n" });
|
|
@@ -2516,17 +2612,7 @@ var TokenCounter = class _TokenCounter {
|
|
|
2516
2612
|
return tokens;
|
|
2517
2613
|
}
|
|
2518
2614
|
resolveToolResultForTokenCounting(part, invocationResult) {
|
|
2519
|
-
|
|
2520
|
-
if (mastraMetadata && typeof mastraMetadata === "object" && "modelOutput" in mastraMetadata) {
|
|
2521
|
-
return {
|
|
2522
|
-
value: mastraMetadata.modelOutput,
|
|
2523
|
-
usingStoredModelOutput: true
|
|
2524
|
-
};
|
|
2525
|
-
}
|
|
2526
|
-
return {
|
|
2527
|
-
value: invocationResult,
|
|
2528
|
-
usingStoredModelOutput: false
|
|
2529
|
-
};
|
|
2615
|
+
return resolveToolResultValue(part, invocationResult);
|
|
2530
2616
|
}
|
|
2531
2617
|
estimateImageAssetTokens(part, asset, kind) {
|
|
2532
2618
|
const modelContext = this.getModelContext();
|
|
@@ -2765,19 +2851,13 @@ var TokenCounter = class _TokenCounter {
|
|
|
2765
2851
|
invocation.result
|
|
2766
2852
|
);
|
|
2767
2853
|
if (resultForCounting !== void 0) {
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
const resultJson = JSON.stringify(resultForCounting);
|
|
2776
|
-
tokens += this.readOrPersistPartEstimate(
|
|
2777
|
-
part,
|
|
2778
|
-
usingStoredModelOutput ? "tool-result-model-output-json" : "tool-result-json",
|
|
2779
|
-
resultJson
|
|
2780
|
-
);
|
|
2854
|
+
const formattedResult = formatToolResultForObserver(resultForCounting);
|
|
2855
|
+
tokens += this.readOrPersistPartEstimate(
|
|
2856
|
+
part,
|
|
2857
|
+
usingStoredModelOutput ? "tool-result-model-output-json" : "tool-result-json",
|
|
2858
|
+
formattedResult
|
|
2859
|
+
);
|
|
2860
|
+
if (typeof resultForCounting !== "string") {
|
|
2781
2861
|
overheadDelta -= 12;
|
|
2782
2862
|
}
|
|
2783
2863
|
}
|
|
@@ -2893,6 +2973,14 @@ function omDebug(msg) {
|
|
|
2893
2973
|
} catch {
|
|
2894
2974
|
}
|
|
2895
2975
|
}
|
|
2976
|
+
function getLatestStepParts(parts) {
|
|
2977
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
2978
|
+
if (parts[i]?.type === "step-start") {
|
|
2979
|
+
return parts.slice(i + 1);
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
return parts;
|
|
2983
|
+
}
|
|
2896
2984
|
function omError(msg, err) {
|
|
2897
2985
|
const errStr = err instanceof Error ? err.stack ?? err.message : err !== void 0 ? String(err) : "";
|
|
2898
2986
|
const full = errStr ? `${msg}: ${errStr}` : msg;
|
|
@@ -4339,38 +4427,51 @@ ${unreflectedContent}` : bufferedReflection;
|
|
|
4339
4427
|
if (currentDate) {
|
|
4340
4428
|
optimized = addRelativeTimeToObservations(optimized, currentDate);
|
|
4341
4429
|
}
|
|
4342
|
-
|
|
4343
|
-
${OBSERVATION_CONTEXT_PROMPT}
|
|
4430
|
+
const messages = [`${OBSERVATION_CONTEXT_PROMPT}
|
|
4344
4431
|
|
|
4345
|
-
|
|
4346
|
-
${optimized}
|
|
4347
|
-
</observations>
|
|
4348
|
-
|
|
4349
|
-
${OBSERVATION_CONTEXT_INSTRUCTIONS}`;
|
|
4432
|
+
${OBSERVATION_CONTEXT_INSTRUCTIONS}`];
|
|
4350
4433
|
if (unobservedContextBlocks) {
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
The following content is from OTHER conversations different from the current conversation, they're here for reference, but they're not necessarily your focus:
|
|
4434
|
+
messages.push(
|
|
4435
|
+
`The following content is from OTHER conversations different from the current conversation, they're here for reference, but they're not necessarily your focus:
|
|
4354
4436
|
START_OTHER_CONVERSATIONS_BLOCK
|
|
4355
4437
|
${unobservedContextBlocks}
|
|
4356
|
-
END_OTHER_CONVERSATIONS_BLOCK
|
|
4438
|
+
END_OTHER_CONVERSATIONS_BLOCK`
|
|
4439
|
+
);
|
|
4440
|
+
}
|
|
4441
|
+
const observationChunks = this.splitObservationContextChunks(optimized);
|
|
4442
|
+
if (observationChunks.length > 0) {
|
|
4443
|
+
messages.push("<observations>", ...observationChunks);
|
|
4357
4444
|
}
|
|
4358
4445
|
if (currentTask) {
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
<current-task>
|
|
4446
|
+
messages.push(`<current-task>
|
|
4362
4447
|
${currentTask}
|
|
4363
|
-
</current-task
|
|
4448
|
+
</current-task>`);
|
|
4364
4449
|
}
|
|
4365
4450
|
if (suggestedResponse) {
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
<suggested-response>
|
|
4451
|
+
messages.push(`<suggested-response>
|
|
4369
4452
|
${suggestedResponse}
|
|
4370
|
-
</suggested-response
|
|
4371
|
-
`;
|
|
4453
|
+
</suggested-response>`);
|
|
4372
4454
|
}
|
|
4373
|
-
return
|
|
4455
|
+
return messages;
|
|
4456
|
+
}
|
|
4457
|
+
splitObservationContextChunks(observations) {
|
|
4458
|
+
const trimmed = observations.trim();
|
|
4459
|
+
if (!trimmed) {
|
|
4460
|
+
return [];
|
|
4461
|
+
}
|
|
4462
|
+
return trimmed.split(/\n{2,}--- message boundary \(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\) ---\n{2,}/).map((chunk) => chunk.trim()).filter(Boolean);
|
|
4463
|
+
}
|
|
4464
|
+
/**
|
|
4465
|
+
* Create a message boundary delimiter with an ISO 8601 date.
|
|
4466
|
+
* The date should be the lastObservedAt timestamp — the latest message
|
|
4467
|
+
* timestamp that was observed to produce the observations following this boundary.
|
|
4468
|
+
*/
|
|
4469
|
+
static createMessageBoundary(date) {
|
|
4470
|
+
return `
|
|
4471
|
+
|
|
4472
|
+
--- message boundary (${date.toISOString()}) ---
|
|
4473
|
+
|
|
4474
|
+
`;
|
|
4374
4475
|
}
|
|
4375
4476
|
/**
|
|
4376
4477
|
* Get threadId and resourceId from either RequestContext or MessageList
|
|
@@ -4808,7 +4909,7 @@ ${suggestedResponse}
|
|
|
4808
4909
|
if (!record.activeObservations) {
|
|
4809
4910
|
return;
|
|
4810
4911
|
}
|
|
4811
|
-
const
|
|
4912
|
+
const observationSystemMessages = this.formatObservationsForContext(
|
|
4812
4913
|
record.activeObservations,
|
|
4813
4914
|
currentTask,
|
|
4814
4915
|
suggestedResponse,
|
|
@@ -4816,7 +4917,7 @@ ${suggestedResponse}
|
|
|
4816
4917
|
currentDate
|
|
4817
4918
|
);
|
|
4818
4919
|
messageList.clearSystemMessages("observational-memory");
|
|
4819
|
-
messageList.addSystem(
|
|
4920
|
+
messageList.addSystem(observationSystemMessages, "observational-memory");
|
|
4820
4921
|
const continuationMessage = {
|
|
4821
4922
|
id: `om-continuation`,
|
|
4822
4923
|
role: "user",
|
|
@@ -5092,12 +5193,20 @@ ${suggestedResponse}
|
|
|
5092
5193
|
const sealedIds = /* @__PURE__ */ new Set([...stateSealedIds, ...staticSealedIds]);
|
|
5093
5194
|
state.sealedIds = sealedIds;
|
|
5094
5195
|
const lockKey = this.getLockKey(threadId, resourceId);
|
|
5196
|
+
const lastMessage = allMessages[allMessages.length - 1];
|
|
5197
|
+
const latestStepParts = getLatestStepParts(lastMessage?.content?.parts ?? []);
|
|
5198
|
+
const hasIncompleteToolCalls = latestStepParts.some(
|
|
5199
|
+
(part) => part?.type === "tool-invocation" && part.toolInvocation?.state === "call"
|
|
5200
|
+
);
|
|
5201
|
+
omDebug(
|
|
5202
|
+
`[OM:deferred-check] hasIncompleteToolCalls=${hasIncompleteToolCalls}, latestStepPartsCount=${latestStepParts.length}`
|
|
5203
|
+
);
|
|
5095
5204
|
if (this.isAsyncObservationEnabled() && totalPendingTokens < threshold) {
|
|
5096
5205
|
const shouldTrigger = this.shouldTriggerAsyncObservation(totalPendingTokens, lockKey, record, threshold);
|
|
5097
5206
|
omDebug(
|
|
5098
|
-
`[OM:async-obs] belowThreshold: pending=${totalPendingTokens}, unbuffered=${unbufferedPendingTokens}, threshold=${threshold}, shouldTrigger=${shouldTrigger}, isBufferingObs=${record.isBufferingObservation}, lastBufferedAt=${record.lastBufferedAtTokens}`
|
|
5207
|
+
`[OM:async-obs] belowThreshold: pending=${totalPendingTokens}, unbuffered=${unbufferedPendingTokens}, threshold=${threshold}, shouldTrigger=${shouldTrigger}, isBufferingObs=${record.isBufferingObservation}, lastBufferedAt=${record.lastBufferedAtTokens}, hasIncompleteToolCalls=${hasIncompleteToolCalls}`
|
|
5099
5208
|
);
|
|
5100
|
-
if (shouldTrigger) {
|
|
5209
|
+
if (shouldTrigger && !hasIncompleteToolCalls) {
|
|
5101
5210
|
void this.startAsyncBufferedObservation(
|
|
5102
5211
|
record,
|
|
5103
5212
|
threadId,
|
|
@@ -5111,9 +5220,9 @@ ${suggestedResponse}
|
|
|
5111
5220
|
} else if (this.isAsyncObservationEnabled()) {
|
|
5112
5221
|
const shouldTrigger = this.shouldTriggerAsyncObservation(totalPendingTokens, lockKey, record, threshold);
|
|
5113
5222
|
omDebug(
|
|
5114
|
-
`[OM:async-obs] atOrAboveThreshold: pending=${totalPendingTokens}, unbuffered=${unbufferedPendingTokens}, threshold=${threshold}, step=${stepNumber}, shouldTrigger=${shouldTrigger}`
|
|
5223
|
+
`[OM:async-obs] atOrAboveThreshold: pending=${totalPendingTokens}, unbuffered=${unbufferedPendingTokens}, threshold=${threshold}, step=${stepNumber}, shouldTrigger=${shouldTrigger}, hasIncompleteToolCalls=${hasIncompleteToolCalls}`
|
|
5115
5224
|
);
|
|
5116
|
-
if (shouldTrigger) {
|
|
5225
|
+
if (shouldTrigger && !hasIncompleteToolCalls) {
|
|
5117
5226
|
void this.startAsyncBufferedObservation(
|
|
5118
5227
|
record,
|
|
5119
5228
|
threadId,
|
|
@@ -5128,7 +5237,7 @@ ${suggestedResponse}
|
|
|
5128
5237
|
if (stepNumber > 0) {
|
|
5129
5238
|
await this.handlePerStepSave(messageList, sealedIds, threadId, resourceId, state);
|
|
5130
5239
|
}
|
|
5131
|
-
if (stepNumber > 0 && totalPendingTokens >= threshold) {
|
|
5240
|
+
if (stepNumber > 0 && !hasIncompleteToolCalls && totalPendingTokens >= threshold) {
|
|
5132
5241
|
reproCaptureDetails.thresholdReached = true;
|
|
5133
5242
|
const { observationSucceeded, updatedRecord, activatedMessageIds } = await this.handleThresholdReached(
|
|
5134
5243
|
messageList,
|
|
@@ -5486,16 +5595,14 @@ ${cleanObservations}
|
|
|
5486
5595
|
* merge the observations into that section to reduce token usage.
|
|
5487
5596
|
* Otherwise, append as a new section.
|
|
5488
5597
|
*/
|
|
5489
|
-
replaceOrAppendThreadSection(existingObservations, _threadId, newThreadSection) {
|
|
5598
|
+
replaceOrAppendThreadSection(existingObservations, _threadId, newThreadSection, lastObservedAt) {
|
|
5490
5599
|
if (!existingObservations) {
|
|
5491
5600
|
return newThreadSection;
|
|
5492
5601
|
}
|
|
5493
5602
|
const threadIdMatch = newThreadSection.match(/<thread id="([^"]+)">/);
|
|
5494
5603
|
const dateMatch = newThreadSection.match(/Date:\s*([A-Za-z]+\s+\d+,\s+\d+)/);
|
|
5495
5604
|
if (!threadIdMatch || !dateMatch) {
|
|
5496
|
-
return `${existingObservations}
|
|
5497
|
-
|
|
5498
|
-
${newThreadSection}`;
|
|
5605
|
+
return `${existingObservations}${_ObservationalMemory.createMessageBoundary(lastObservedAt)}${newThreadSection}`;
|
|
5499
5606
|
}
|
|
5500
5607
|
const newThreadId = threadIdMatch[1];
|
|
5501
5608
|
const newDate = dateMatch[1];
|
|
@@ -5530,9 +5637,7 @@ ${threadClose}`;
|
|
|
5530
5637
|
}
|
|
5531
5638
|
}
|
|
5532
5639
|
}
|
|
5533
|
-
return `${existingObservations}
|
|
5534
|
-
|
|
5535
|
-
${newThreadSection}`;
|
|
5640
|
+
return `${existingObservations}${_ObservationalMemory.createMessageBoundary(lastObservedAt)}${newThreadSection}`;
|
|
5536
5641
|
}
|
|
5537
5642
|
/**
|
|
5538
5643
|
* Sort threads by their oldest unobserved message.
|
|
@@ -5615,19 +5720,22 @@ ${newThreadSection}`;
|
|
|
5615
5720
|
priorSuggestedResponse: threadOMMetadata?.suggestedResponse,
|
|
5616
5721
|
wasTruncated
|
|
5617
5722
|
});
|
|
5723
|
+
const lastObservedAt = this.getMaxMessageTimestamp(messagesToObserve);
|
|
5618
5724
|
const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
|
|
5619
5725
|
let newObservations;
|
|
5620
5726
|
if (this.scope === "resource") {
|
|
5621
5727
|
const threadSection = await this.wrapWithThreadTag(threadId, result.observations);
|
|
5622
|
-
newObservations = this.replaceOrAppendThreadSection(
|
|
5728
|
+
newObservations = this.replaceOrAppendThreadSection(
|
|
5729
|
+
existingObservations,
|
|
5730
|
+
threadId,
|
|
5731
|
+
threadSection,
|
|
5732
|
+
lastObservedAt
|
|
5733
|
+
);
|
|
5623
5734
|
} else {
|
|
5624
|
-
newObservations = existingObservations ? `${existingObservations}
|
|
5625
|
-
|
|
5626
|
-
${result.observations}` : result.observations;
|
|
5735
|
+
newObservations = existingObservations ? `${existingObservations}${_ObservationalMemory.createMessageBoundary(lastObservedAt)}${result.observations}` : result.observations;
|
|
5627
5736
|
}
|
|
5628
5737
|
let totalTokenCount = this.tokenCounter.countObservations(newObservations);
|
|
5629
5738
|
const cycleObservationTokens = this.tokenCounter.countObservations(result.observations);
|
|
5630
|
-
const lastObservedAt = this.getMaxMessageTimestamp(messagesToObserve);
|
|
5631
5739
|
const newMessageIds = messagesToObserve.map((m) => m.id);
|
|
5632
5740
|
const existingIds = freshRecord?.observedMessageIds ?? record.observedMessageIds ?? [];
|
|
5633
5741
|
const allObservedIds = [.../* @__PURE__ */ new Set([...Array.isArray(existingIds) ? existingIds : [], ...newMessageIds])];
|
|
@@ -6521,9 +6629,14 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6521
6629
|
if (!obsResult) continue;
|
|
6522
6630
|
const { threadId, threadMessages, result } = obsResult;
|
|
6523
6631
|
cycleObservationTokens += this.tokenCounter.countObservations(result.observations);
|
|
6524
|
-
const threadSection = await this.wrapWithThreadTag(threadId, result.observations);
|
|
6525
|
-
currentObservations = this.replaceOrAppendThreadSection(currentObservations, threadId, threadSection);
|
|
6526
6632
|
const threadLastObservedAt = this.getMaxMessageTimestamp(threadMessages);
|
|
6633
|
+
const threadSection = await this.wrapWithThreadTag(threadId, result.observations);
|
|
6634
|
+
currentObservations = this.replaceOrAppendThreadSection(
|
|
6635
|
+
currentObservations,
|
|
6636
|
+
threadId,
|
|
6637
|
+
threadSection,
|
|
6638
|
+
threadLastObservedAt
|
|
6639
|
+
);
|
|
6527
6640
|
const thread = await this.storage.getThreadById({ threadId });
|
|
6528
6641
|
if (thread) {
|
|
6529
6642
|
const newMetadata = setThreadOMMetadata(thread.metadata, {
|
|
@@ -6968,6 +7081,30 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6968
7081
|
}
|
|
6969
7082
|
};
|
|
6970
7083
|
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
7084
|
+
// src/processors/observational-memory/observation-utils.ts
|
|
7085
|
+
var BOUNDARY_WITH_DATE_RE = /\n{2,}--- message boundary \((\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z)\) ---\n{2,}/;
|
|
7086
|
+
function getObservationsAsOf(activeObservations, asOf) {
|
|
7087
|
+
const trimmed = activeObservations.trim();
|
|
7088
|
+
if (!trimmed) return "";
|
|
7089
|
+
const parts = trimmed.split(BOUNDARY_WITH_DATE_RE);
|
|
7090
|
+
const chunks = [];
|
|
7091
|
+
const firstChunk = parts[0]?.trim();
|
|
7092
|
+
if (firstChunk) {
|
|
7093
|
+
chunks.push(firstChunk);
|
|
7094
|
+
}
|
|
7095
|
+
for (let i = 1; i < parts.length; i += 2) {
|
|
7096
|
+
const dateStr = parts[i];
|
|
7097
|
+
const chunk = parts[i + 1]?.trim();
|
|
7098
|
+
if (!chunk) continue;
|
|
7099
|
+
const boundaryDate = new Date(dateStr);
|
|
7100
|
+
if (isNaN(boundaryDate.getTime())) continue;
|
|
7101
|
+
if (boundaryDate <= asOf) {
|
|
7102
|
+
chunks.push(chunk);
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
return chunks.join("\n\n");
|
|
7106
|
+
}
|
|
7107
|
+
|
|
7108
|
+
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
|