@mastra/memory 1.9.0-alpha.1 → 1.9.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 +61 -0
- package/dist/{chunk-5SMKVGJP.js → chunk-JJBSFPC5.js} +315 -25
- package/dist/chunk-JJBSFPC5.js.map +1 -0
- package/dist/{chunk-AR52LM55.cjs → chunk-LVV2RT42.cjs} +327 -24
- package/dist/chunk-LVV2RT42.cjs.map +1 -0
- package/dist/docs/SKILL.md +5 -7
- package/dist/docs/assets/SOURCE_MAP.json +77 -27
- package/dist/docs/references/docs-agents-agent-approval.md +114 -193
- package/dist/docs/references/docs-agents-networks.md +88 -205
- package/dist/docs/references/docs-agents-supervisor-agents.md +24 -18
- package/dist/docs/references/docs-memory-observational-memory.md +30 -2
- package/dist/docs/references/docs-memory-overview.md +219 -24
- package/dist/docs/references/docs-memory-semantic-recall.md +1 -1
- package/dist/docs/references/docs-memory-storage.md +4 -4
- package/dist/docs/references/docs-memory-working-memory.md +1 -1
- package/dist/docs/references/reference-core-getMemory.md +1 -2
- package/dist/docs/references/reference-core-listMemory.md +1 -2
- package/dist/docs/references/reference-memory-cloneThread.md +1 -1
- package/dist/docs/references/reference-memory-observational-memory.md +39 -1
- package/dist/index.cjs +432 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +432 -10
- package/dist/index.js.map +1 -1
- package/dist/observational-memory-3XFCO6MX.js +3 -0
- package/dist/{observational-memory-5NFPG6M3.js.map → observational-memory-3XFCO6MX.js.map} +1 -1
- package/dist/observational-memory-MJJFU26W.cjs +108 -0
- package/dist/{observational-memory-NH7VDTXM.cjs.map → observational-memory-MJJFU26W.cjs.map} +1 -1
- package/dist/processors/index.cjs +56 -16
- package/dist/processors/index.js +1 -1
- package/dist/processors/observational-memory/anchor-ids.d.ts +4 -0
- package/dist/processors/observational-memory/anchor-ids.d.ts.map +1 -0
- package/dist/processors/observational-memory/index.d.ts +2 -0
- package/dist/processors/observational-memory/index.d.ts.map +1 -1
- package/dist/processors/observational-memory/observation-groups.d.ts +15 -0
- package/dist/processors/observational-memory/observation-groups.d.ts.map +1 -0
- package/dist/processors/observational-memory/observational-memory.d.ts +14 -0
- package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
- package/dist/processors/observational-memory/observer-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/reflector-agent.d.ts +1 -1
- package/dist/processors/observational-memory/reflector-agent.d.ts.map +1 -1
- package/dist/processors/observational-memory/tool-result-helpers.d.ts.map +1 -1
- package/dist/tools/om-tools.d.ts +77 -0
- package/dist/tools/om-tools.d.ts.map +1 -0
- package/package.json +8 -8
- package/dist/chunk-5SMKVGJP.js.map +0 -1
- package/dist/chunk-AR52LM55.cjs.map +0 -1
- package/dist/docs/references/docs-agents-agent-memory.md +0 -209
- package/dist/docs/references/docs-agents-network-approval.md +0 -278
- package/dist/observational-memory-5NFPG6M3.js +0 -3
- package/dist/observational-memory-NH7VDTXM.cjs +0 -68
|
@@ -8,8 +8,8 @@ var llm = require('@mastra/core/llm');
|
|
|
8
8
|
var memory = require('@mastra/core/memory');
|
|
9
9
|
var processors = require('@mastra/core/processors');
|
|
10
10
|
var xxhash = require('xxhash-wasm');
|
|
11
|
-
var tokenx = require('tokenx');
|
|
12
11
|
var crypto$1 = require('crypto');
|
|
12
|
+
var tokenx = require('tokenx');
|
|
13
13
|
var async_hooks = require('async_hooks');
|
|
14
14
|
var imageSize = require('image-size');
|
|
15
15
|
|
|
@@ -319,6 +319,222 @@ function createThreadUpdateMarker(params) {
|
|
|
319
319
|
}
|
|
320
320
|
};
|
|
321
321
|
}
|
|
322
|
+
var OBSERVATION_GROUP_PATTERN = /<observation-group\s([^>]*)>([\s\S]*?)<\/observation-group>/g;
|
|
323
|
+
var ATTRIBUTE_PATTERN = /([\w][\w-]*)="([^"]*)"/g;
|
|
324
|
+
var REFLECTION_GROUP_SPLIT_PATTERN = /^##\s+Group\s+/m;
|
|
325
|
+
function parseObservationGroupAttributes(attributeString) {
|
|
326
|
+
const attributes = {};
|
|
327
|
+
for (const match of attributeString.matchAll(ATTRIBUTE_PATTERN)) {
|
|
328
|
+
const [, key, value] = match;
|
|
329
|
+
if (key && value !== void 0) {
|
|
330
|
+
attributes[key] = value;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return attributes;
|
|
334
|
+
}
|
|
335
|
+
function parseReflectionObservationGroupSections(content) {
|
|
336
|
+
const normalizedContent = content.trim();
|
|
337
|
+
if (!normalizedContent || !REFLECTION_GROUP_SPLIT_PATTERN.test(normalizedContent)) {
|
|
338
|
+
return [];
|
|
339
|
+
}
|
|
340
|
+
return normalizedContent.split(REFLECTION_GROUP_SPLIT_PATTERN).map((section) => section.trim()).filter(Boolean).map((section) => {
|
|
341
|
+
const newlineIndex = section.indexOf("\n");
|
|
342
|
+
const heading = (newlineIndex >= 0 ? section.slice(0, newlineIndex) : section).trim();
|
|
343
|
+
const body = (newlineIndex >= 0 ? section.slice(newlineIndex + 1) : "").trim();
|
|
344
|
+
return {
|
|
345
|
+
heading,
|
|
346
|
+
body: stripReflectionGroupMetadata(body)
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function stripReflectionGroupMetadata(body) {
|
|
351
|
+
return body.replace(/^_range:\s*`[^`]*`_\s*\n?/m, "").trim();
|
|
352
|
+
}
|
|
353
|
+
function generateAnchorId() {
|
|
354
|
+
return crypto$1.randomBytes(8).toString("hex");
|
|
355
|
+
}
|
|
356
|
+
function wrapInObservationGroup(observations, range, id = generateAnchorId(), sourceGroupIds) {
|
|
357
|
+
const content = observations.trim();
|
|
358
|
+
const sourceGroupIdsAttr = sourceGroupIds?.length ? ` source-group-ids="${sourceGroupIds.join(",")}"` : "";
|
|
359
|
+
return `<observation-group id="${id}" range="${range}"${sourceGroupIdsAttr}>
|
|
360
|
+
${content}
|
|
361
|
+
</observation-group>`;
|
|
362
|
+
}
|
|
363
|
+
function parseObservationGroups(observations) {
|
|
364
|
+
if (!observations) {
|
|
365
|
+
return [];
|
|
366
|
+
}
|
|
367
|
+
const groups = [];
|
|
368
|
+
let match;
|
|
369
|
+
while ((match = OBSERVATION_GROUP_PATTERN.exec(observations)) !== null) {
|
|
370
|
+
const attributes = parseObservationGroupAttributes(match[1] ?? "");
|
|
371
|
+
const id = attributes.id;
|
|
372
|
+
const range = attributes.range;
|
|
373
|
+
if (!id || !range) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
groups.push({
|
|
377
|
+
id,
|
|
378
|
+
range,
|
|
379
|
+
content: match[2].trim(),
|
|
380
|
+
sourceGroupIds: attributes["source-group-ids"]?.split(",").map((part) => part.trim()).filter(Boolean)
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
return groups;
|
|
384
|
+
}
|
|
385
|
+
function stripObservationGroups(observations) {
|
|
386
|
+
if (!observations) {
|
|
387
|
+
return observations;
|
|
388
|
+
}
|
|
389
|
+
return observations.replace(OBSERVATION_GROUP_PATTERN, (_match, _attributes, content) => content.trim()).replace(/\n{3,}/g, "\n\n").trim();
|
|
390
|
+
}
|
|
391
|
+
function combineObservationGroupRanges(groups) {
|
|
392
|
+
return Array.from(
|
|
393
|
+
new Set(
|
|
394
|
+
groups.flatMap((group) => group.range.split(",")).map((range) => range.trim()).filter(Boolean)
|
|
395
|
+
)
|
|
396
|
+
).join(",");
|
|
397
|
+
}
|
|
398
|
+
function renderObservationGroupsForReflection(observations) {
|
|
399
|
+
const groups = parseObservationGroups(observations);
|
|
400
|
+
if (groups.length === 0) {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
const groupsByContent = new Map(groups.map((g) => [g.content.trim(), g]));
|
|
404
|
+
const result = observations.replace(OBSERVATION_GROUP_PATTERN, (_match, _attrs, content) => {
|
|
405
|
+
const group = groupsByContent.get(content.trim());
|
|
406
|
+
if (!group) return content.trim();
|
|
407
|
+
return `## Group \`${group.id}\`
|
|
408
|
+
_range: \`${group.range}\`_
|
|
409
|
+
|
|
410
|
+
${group.content}`;
|
|
411
|
+
});
|
|
412
|
+
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
413
|
+
}
|
|
414
|
+
function getCanonicalGroupId(sectionHeading, fallbackIndex) {
|
|
415
|
+
const match = sectionHeading.match(/`([^`]+)`/);
|
|
416
|
+
return match?.[1]?.trim() || `derived-group-${fallbackIndex + 1}`;
|
|
417
|
+
}
|
|
418
|
+
function deriveObservationGroupProvenance(content, groups) {
|
|
419
|
+
const sections = parseReflectionObservationGroupSections(content);
|
|
420
|
+
if (sections.length === 0 || groups.length === 0) {
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
return sections.map((section, index) => {
|
|
424
|
+
const bodyLines = new Set(
|
|
425
|
+
section.body.split("\n").map((line) => line.trim()).filter(Boolean)
|
|
426
|
+
);
|
|
427
|
+
const matchingGroups = groups.filter((group) => {
|
|
428
|
+
const groupLines = group.content.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
429
|
+
return groupLines.some((line) => bodyLines.has(line));
|
|
430
|
+
});
|
|
431
|
+
const fallbackGroup = groups[Math.min(index, groups.length - 1)];
|
|
432
|
+
const resolvedGroups = matchingGroups.length > 0 ? matchingGroups : fallbackGroup ? [fallbackGroup] : [];
|
|
433
|
+
const sourceGroupIds = Array.from(
|
|
434
|
+
new Set(resolvedGroups.flatMap((group) => [group.id, ...group.sourceGroupIds ?? []]))
|
|
435
|
+
);
|
|
436
|
+
const canonicalGroupId = getCanonicalGroupId(section.heading, index);
|
|
437
|
+
return {
|
|
438
|
+
id: canonicalGroupId,
|
|
439
|
+
range: combineObservationGroupRanges(resolvedGroups),
|
|
440
|
+
content: section.body,
|
|
441
|
+
sourceGroupIds
|
|
442
|
+
};
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
function reconcileObservationGroupsFromReflection(content, sourceObservations) {
|
|
446
|
+
const sourceGroups = parseObservationGroups(sourceObservations);
|
|
447
|
+
if (sourceGroups.length === 0) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
const normalizedContent = content.trim();
|
|
451
|
+
if (!normalizedContent) {
|
|
452
|
+
return "";
|
|
453
|
+
}
|
|
454
|
+
const derivedGroups = deriveObservationGroupProvenance(normalizedContent, sourceGroups);
|
|
455
|
+
if (derivedGroups.length > 0) {
|
|
456
|
+
return derivedGroups.map((group) => wrapInObservationGroup(group.content, group.range, group.id, group.sourceGroupIds)).join("\n\n");
|
|
457
|
+
}
|
|
458
|
+
return wrapInObservationGroup(
|
|
459
|
+
normalizedContent,
|
|
460
|
+
combineObservationGroupRanges(sourceGroups),
|
|
461
|
+
generateAnchorId(),
|
|
462
|
+
Array.from(new Set(sourceGroups.flatMap((group) => [group.id, ...group.sourceGroupIds ?? []])))
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// src/processors/observational-memory/anchor-ids.ts
|
|
467
|
+
var ANCHOR_ID_PATTERN = /^\[(O\d+(?:-N\d+)?)\]\s*/;
|
|
468
|
+
var OBSERVATION_DATE_HEADER_PATTERN = /^\s*Date:\s+/;
|
|
469
|
+
var XML_TAG_PATTERN = /^\s*<\/?[a-z][^>]*>\s*$/i;
|
|
470
|
+
var MARKDOWN_GROUP_HEADING_PATTERN = /^\s*##\s+Group\s+`[^`]+`\s*$/;
|
|
471
|
+
var MARKDOWN_GROUP_METADATA_PATTERN = /^\s*_range:\s*`[^`]*`_\s*$/;
|
|
472
|
+
function buildEphemeralAnchorId(topLevelCounter, nestedCounter) {
|
|
473
|
+
return nestedCounter === 0 ? `O${topLevelCounter}` : `O${topLevelCounter}-N${nestedCounter}`;
|
|
474
|
+
}
|
|
475
|
+
function parseAnchorId(line) {
|
|
476
|
+
const match = line.match(ANCHOR_ID_PATTERN);
|
|
477
|
+
return match?.[1] ?? null;
|
|
478
|
+
}
|
|
479
|
+
function shouldAnchorLine(line) {
|
|
480
|
+
const trimmed = line.trim();
|
|
481
|
+
if (!trimmed) {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
if (parseAnchorId(trimmed)) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
if (OBSERVATION_DATE_HEADER_PATTERN.test(trimmed)) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
if (XML_TAG_PATTERN.test(trimmed)) {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
if (MARKDOWN_GROUP_HEADING_PATTERN.test(trimmed) || MARKDOWN_GROUP_METADATA_PATTERN.test(trimmed)) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
function getIndentationDepth(line) {
|
|
499
|
+
const leadingWhitespace = line.match(/^\s*/)?.[0] ?? "";
|
|
500
|
+
return Math.floor(leadingWhitespace.replace(/\t/g, " ").length / 2);
|
|
501
|
+
}
|
|
502
|
+
function injectAnchorIds(observations) {
|
|
503
|
+
if (!observations) {
|
|
504
|
+
return observations;
|
|
505
|
+
}
|
|
506
|
+
const lines = observations.split("\n");
|
|
507
|
+
let topLevelCounter = 0;
|
|
508
|
+
let nestedCounter = 0;
|
|
509
|
+
let changed = false;
|
|
510
|
+
for (let i = 0; i < lines.length; i++) {
|
|
511
|
+
const line = lines[i];
|
|
512
|
+
if (!shouldAnchorLine(line)) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const indentationDepth = getIndentationDepth(line);
|
|
516
|
+
if (indentationDepth === 0) {
|
|
517
|
+
topLevelCounter += 1;
|
|
518
|
+
nestedCounter = 0;
|
|
519
|
+
} else {
|
|
520
|
+
if (topLevelCounter === 0) {
|
|
521
|
+
topLevelCounter = 1;
|
|
522
|
+
}
|
|
523
|
+
nestedCounter += 1;
|
|
524
|
+
}
|
|
525
|
+
const anchorId = buildEphemeralAnchorId(topLevelCounter, nestedCounter);
|
|
526
|
+
const leadingWhitespace = line.match(/^\s*/)?.[0] ?? "";
|
|
527
|
+
lines[i] = `${leadingWhitespace}[${anchorId}] ${line.slice(leadingWhitespace.length)}`;
|
|
528
|
+
changed = true;
|
|
529
|
+
}
|
|
530
|
+
return changed ? lines.join("\n") : observations;
|
|
531
|
+
}
|
|
532
|
+
function stripEphemeralAnchorIds(observations) {
|
|
533
|
+
if (!observations) {
|
|
534
|
+
return observations;
|
|
535
|
+
}
|
|
536
|
+
return observations.replace(/(^|\n)([^\S\n]*)\[(O\d+(?:-N\d+)?)\][^\S\n]*/g, "$1$2");
|
|
537
|
+
}
|
|
322
538
|
var ENCRYPTED_CONTENT_KEY = "encryptedContent";
|
|
323
539
|
var ENCRYPTED_CONTENT_REDACTION_THRESHOLD = 256;
|
|
324
540
|
var DEFAULT_OBSERVER_TOOL_RESULT_MAX_TOKENS = 1e4;
|
|
@@ -385,9 +601,8 @@ function truncateStringByTokens(text, maxTokens) {
|
|
|
385
601
|
}
|
|
386
602
|
const buildCandidate = (sliceEnd) => {
|
|
387
603
|
const visible = text.slice(0, sliceEnd);
|
|
388
|
-
const omittedChars = text.length - sliceEnd;
|
|
389
604
|
return `${visible}
|
|
390
|
-
... [truncated ~${totalTokens - tokenx.estimateTokenCount(visible)} tokens
|
|
605
|
+
... [truncated ~${totalTokens - tokenx.estimateTokenCount(visible)} tokens]`;
|
|
391
606
|
};
|
|
392
607
|
let low = 0;
|
|
393
608
|
let high = text.length;
|
|
@@ -1386,7 +1601,7 @@ function extractCurrentTask(observations) {
|
|
|
1386
1601
|
return content || null;
|
|
1387
1602
|
}
|
|
1388
1603
|
function optimizeObservationsForContext(observations) {
|
|
1389
|
-
let optimized = observations;
|
|
1604
|
+
let optimized = stripEphemeralAnchorIds(observations);
|
|
1390
1605
|
optimized = optimized.replace(/🟡\s*/g, "");
|
|
1391
1606
|
optimized = optimized.replace(/🟢\s*/g, "");
|
|
1392
1607
|
optimized = optimized.replace(/\[(?![\d\s]*items collapsed)[^\]]+\]/g, "");
|
|
@@ -1576,9 +1791,11 @@ Your current detail level was a 10/10, lets aim for a 4/10 detail level.
|
|
|
1576
1791
|
};
|
|
1577
1792
|
function buildReflectorPrompt(observations, manualPrompt, compressionLevel, skipContinuationHints) {
|
|
1578
1793
|
const level = typeof compressionLevel === "number" ? compressionLevel : compressionLevel ? 1 : 0;
|
|
1794
|
+
const reflectionView = renderObservationGroupsForReflection(observations) ?? observations;
|
|
1795
|
+
const anchoredObservations = injectAnchorIds(reflectionView);
|
|
1579
1796
|
let prompt = `## OBSERVATIONS TO REFLECT ON
|
|
1580
1797
|
|
|
1581
|
-
${
|
|
1798
|
+
${anchoredObservations}
|
|
1582
1799
|
|
|
1583
1800
|
---
|
|
1584
1801
|
|
|
@@ -1603,7 +1820,7 @@ IMPORTANT: Do NOT include <current-task> or <suggested-response> sections in you
|
|
|
1603
1820
|
}
|
|
1604
1821
|
return prompt;
|
|
1605
1822
|
}
|
|
1606
|
-
function parseReflectorOutput(output) {
|
|
1823
|
+
function parseReflectorOutput(output, sourceObservations) {
|
|
1607
1824
|
if (detectDegenerateRepetition(output)) {
|
|
1608
1825
|
return {
|
|
1609
1826
|
observations: "",
|
|
@@ -1611,9 +1828,10 @@ function parseReflectorOutput(output) {
|
|
|
1611
1828
|
};
|
|
1612
1829
|
}
|
|
1613
1830
|
const parsed = parseReflectorSectionXml(output);
|
|
1614
|
-
const
|
|
1831
|
+
const sanitizedObservations = sanitizeObservationLines(stripEphemeralAnchorIds(parsed.observations || ""));
|
|
1832
|
+
const reconciledObservations = sourceObservations ? reconcileObservationGroupsFromReflection(sanitizedObservations, sourceObservations) : null;
|
|
1615
1833
|
return {
|
|
1616
|
-
observations,
|
|
1834
|
+
observations: reconciledObservations ?? sanitizedObservations,
|
|
1617
1835
|
suggestedContinuation: parsed.suggestedResponse || void 0
|
|
1618
1836
|
// Note: Reflector's currentTask is not used - thread metadata preserves per-thread tasks
|
|
1619
1837
|
};
|
|
@@ -3109,7 +3327,24 @@ if (OM_DEBUG_LOG) {
|
|
|
3109
3327
|
_origConsoleError.apply(console, args);
|
|
3110
3328
|
};
|
|
3111
3329
|
}
|
|
3330
|
+
function messageHasVisibleContent(msg) {
|
|
3331
|
+
const content = msg.content;
|
|
3332
|
+
if (content?.parts && Array.isArray(content.parts)) {
|
|
3333
|
+
return content.parts.some((p) => {
|
|
3334
|
+
const t = p?.type;
|
|
3335
|
+
return t && !t.startsWith("data-") && t !== "step-start";
|
|
3336
|
+
});
|
|
3337
|
+
}
|
|
3338
|
+
if (content?.content) return true;
|
|
3339
|
+
return false;
|
|
3340
|
+
}
|
|
3341
|
+
function buildMessageRange(messages) {
|
|
3342
|
+
const first = messages.find(messageHasVisibleContent) ?? messages[0];
|
|
3343
|
+
const last = [...messages].reverse().find(messageHasVisibleContent) ?? messages[messages.length - 1];
|
|
3344
|
+
return `${first.id}:${last.id}`;
|
|
3345
|
+
}
|
|
3112
3346
|
var OBSERVATIONAL_MEMORY_DEFAULTS = {
|
|
3347
|
+
retrieval: false,
|
|
3113
3348
|
observation: {
|
|
3114
3349
|
model: "google/gemini-2.5-flash",
|
|
3115
3350
|
messageTokens: 3e4,
|
|
@@ -3164,12 +3399,54 @@ KNOWLEDGE UPDATES: When asked about current state (e.g., "where do I currently..
|
|
|
3164
3399
|
PLANNED ACTIONS: If the user stated they planned to do something (e.g., "I'm going to...", "I'm looking forward to...", "I will...") and the date they planned to do it is now in the past (check the relative time like "3 weeks ago"), assume they completed the action unless there's evidence they didn't. For example, if someone said "I'll start my new diet on Monday" and that was 2 weeks ago, assume they started the diet.
|
|
3165
3400
|
|
|
3166
3401
|
MOST RECENT USER INPUT: Treat the most recent user message as the highest-priority signal for what to do next. Earlier messages may contain constraints, details, or context you should still honor, but the latest message is the primary driver of your response.`;
|
|
3402
|
+
var OBSERVATION_RETRIEVAL_INSTRUCTIONS = `## Recall \u2014 looking up source messages
|
|
3403
|
+
|
|
3404
|
+
Your memory is comprised of observations which are sometimes wrapped in <observation-group> xml tags containing ranges like <observation-group range="startId:endId">. These ranges point back to the raw messages that each observation group was derived from. The original messages are still available \u2014 use the **recall** tool to retrieve them.
|
|
3405
|
+
|
|
3406
|
+
### When to use recall
|
|
3407
|
+
- The user asks you to **repeat, show, or reproduce** something from a past conversation
|
|
3408
|
+
- The user asks for **exact content** \u2014 code, text, quotes, error messages, URLs, file paths, specific numbers
|
|
3409
|
+
- Your observations mention something but your memory lacks the detail needed to fully answer (e.g. you know a blog post was shared but only have a summary of it)
|
|
3410
|
+
- You want to **verify or expand on** an observation before responding
|
|
3411
|
+
|
|
3412
|
+
**Default to using recall when the user references specific past content.** Your observations capture the gist, not the details. If there's any doubt whether your memory is complete enough, use recall.
|
|
3413
|
+
|
|
3414
|
+
### How to use recall
|
|
3415
|
+
Each range has the format \`startId:endId\` where both are message IDs separated by a colon.
|
|
3416
|
+
|
|
3417
|
+
1. Find the observation group relevant to the user's question and extract the start or end ID from its range.
|
|
3418
|
+
2. Call \`recall\` with that ID as the \`cursor\`.
|
|
3419
|
+
3. Use \`page: 1\` (or omit) to read forward from the cursor, \`page: -1\` to read backward.
|
|
3420
|
+
4. If the first page doesn't have what you need, increment the page number to keep paginating.
|
|
3421
|
+
5. Check \`hasNextPage\`/\`hasPrevPage\` in the result to know if more pages exist in each direction.
|
|
3422
|
+
|
|
3423
|
+
### Detail levels
|
|
3424
|
+
By default recall returns **low** detail: truncated text and tool names only. Each message shows its ID and each part has a positional index like \`[p0]\`, \`[p1]\`, etc.
|
|
3425
|
+
|
|
3426
|
+
- Use \`detail: "high"\` to get full message content including tool arguments and results. This will only return the high detail version of a single message part at a time.
|
|
3427
|
+
- Use \`partIndex\` with a cursor to fetch a single part at full detail \u2014 for example, to read one specific tool result or code block without loading every part.
|
|
3428
|
+
|
|
3429
|
+
If the result says \`truncated: true\`, the output was cut to fit the token budget. You can paginate or use \`partIndex\` to target specific content.
|
|
3430
|
+
|
|
3431
|
+
### Following up on truncated parts
|
|
3432
|
+
Low-detail results may include truncation hints like:
|
|
3433
|
+
\`[truncated \u2014 call recall cursor="..." partIndex=N detail="high" for full content]\`
|
|
3434
|
+
|
|
3435
|
+
**When you see these hints and need the full content, make the exact call described in the hint.** This is the normal workflow: first recall at low detail to scan, then drill into specific parts at high detail. Do not stop at the low-detail result if the user asked for exact content.
|
|
3436
|
+
|
|
3437
|
+
### When recall is NOT needed
|
|
3438
|
+
- The user is asking for a high-level summary and your observations already cover it
|
|
3439
|
+
- The question is about general preferences or facts that don't require source text
|
|
3440
|
+
- There is no relevant range in your observations for the topic
|
|
3441
|
+
|
|
3442
|
+
Observation groups with range IDs and your recall tool allows you to think back and remember details you're fuzzy on.`;
|
|
3167
3443
|
var ObservationalMemory = class _ObservationalMemory {
|
|
3168
3444
|
id = "observational-memory";
|
|
3169
3445
|
name = "Observational Memory";
|
|
3170
3446
|
storage;
|
|
3171
3447
|
tokenCounter;
|
|
3172
3448
|
scope;
|
|
3449
|
+
retrieval = false;
|
|
3173
3450
|
observationConfig;
|
|
3174
3451
|
reflectionConfig;
|
|
3175
3452
|
onDebugEvent;
|
|
@@ -3482,6 +3759,7 @@ var ObservationalMemory = class _ObservationalMemory {
|
|
|
3482
3759
|
this.shouldObscureThreadIds = config.obscureThreadIds || false;
|
|
3483
3760
|
this.storage = config.storage;
|
|
3484
3761
|
this.scope = config.scope ?? "thread";
|
|
3762
|
+
this.retrieval = this.scope === "thread" && (config.retrieval ?? OBSERVATIONAL_MEMORY_DEFAULTS.retrieval);
|
|
3485
3763
|
const resolveModel = (m) => m === "default" ? OBSERVATIONAL_MEMORY_DEFAULTS.observation.model : m;
|
|
3486
3764
|
const observationModel = resolveModel(config.model) ?? resolveModel(config.observation?.model) ?? resolveModel(config.reflection?.model);
|
|
3487
3765
|
const reflectionModel = resolveModel(config.model) ?? resolveModel(config.reflection?.model) ?? resolveModel(config.observation?.model);
|
|
@@ -3585,6 +3863,7 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3585
3863
|
get config() {
|
|
3586
3864
|
return {
|
|
3587
3865
|
scope: this.scope,
|
|
3866
|
+
retrieval: this.retrieval,
|
|
3588
3867
|
observation: {
|
|
3589
3868
|
messageTokens: this.observationConfig.messageTokens,
|
|
3590
3869
|
previousObserverTokens: this.observationConfig.previousObserverTokens
|
|
@@ -3841,7 +4120,8 @@ Async buffering is enabled by default \u2014 this opt-out is only needed when us
|
|
|
3841
4120
|
config: {
|
|
3842
4121
|
observation: this.observationConfig,
|
|
3843
4122
|
reflection: this.reflectionConfig,
|
|
3844
|
-
scope: this.scope
|
|
4123
|
+
scope: this.scope,
|
|
4124
|
+
retrieval: this.retrieval
|
|
3845
4125
|
},
|
|
3846
4126
|
observedTimezone
|
|
3847
4127
|
});
|
|
@@ -4482,7 +4762,7 @@ ${unreflectedContent}` : bufferedReflection;
|
|
|
4482
4762
|
totalUsage.outputTokens += usage.outputTokens ?? 0;
|
|
4483
4763
|
totalUsage.totalTokens += usage.totalTokens ?? 0;
|
|
4484
4764
|
}
|
|
4485
|
-
parsed = parseReflectorOutput(result.text);
|
|
4765
|
+
parsed = parseReflectorOutput(result.text, observations);
|
|
4486
4766
|
if (parsed.degenerate) {
|
|
4487
4767
|
omDebug(
|
|
4488
4768
|
`[OM:callReflector] attempt #${attemptNumber}: degenerate repetition detected, treating as compression failure`
|
|
@@ -4549,14 +4829,18 @@ ${unreflectedContent}` : bufferedReflection;
|
|
|
4549
4829
|
* @param suggestedResponse - Thread-specific suggested response (from thread metadata)
|
|
4550
4830
|
* @param unobservedContextBlocks - Formatted <unobserved-context> blocks from other threads
|
|
4551
4831
|
*/
|
|
4552
|
-
formatObservationsForContext(observations, currentTask, suggestedResponse, unobservedContextBlocks, currentDate) {
|
|
4553
|
-
let optimized = optimizeObservationsForContext(observations);
|
|
4832
|
+
formatObservationsForContext(observations, currentTask, suggestedResponse, unobservedContextBlocks, currentDate, retrieval = false) {
|
|
4833
|
+
let optimized = retrieval ? renderObservationGroupsForReflection(observations) ?? observations : optimizeObservationsForContext(observations);
|
|
4554
4834
|
if (currentDate) {
|
|
4555
4835
|
optimized = addRelativeTimeToObservations(optimized, currentDate);
|
|
4556
4836
|
}
|
|
4557
|
-
const messages = [
|
|
4837
|
+
const messages = [
|
|
4838
|
+
`${OBSERVATION_CONTEXT_PROMPT}
|
|
4839
|
+
|
|
4840
|
+
${OBSERVATION_CONTEXT_INSTRUCTIONS}${retrieval ? `
|
|
4558
4841
|
|
|
4559
|
-
${
|
|
4842
|
+
${OBSERVATION_RETRIEVAL_INSTRUCTIONS}` : ""}`
|
|
4843
|
+
];
|
|
4560
4844
|
if (unobservedContextBlocks) {
|
|
4561
4845
|
messages.push(
|
|
4562
4846
|
`The following content is from OTHER conversations different from the current conversation, they're here for reference, but they're not necessarily your focus:
|
|
@@ -5041,7 +5325,8 @@ ${suggestedResponse}
|
|
|
5041
5325
|
currentTask,
|
|
5042
5326
|
suggestedResponse,
|
|
5043
5327
|
unobservedContextBlocks,
|
|
5044
|
-
currentDate
|
|
5328
|
+
currentDate,
|
|
5329
|
+
this.retrieval
|
|
5045
5330
|
);
|
|
5046
5331
|
messageList.clearSystemMessages("observational-memory");
|
|
5047
5332
|
messageList.addSystem(observationSystemMessages, "observational-memory");
|
|
@@ -5709,11 +5994,12 @@ ${formattedMessages}
|
|
|
5709
5994
|
* Wrap observations in a thread attribution tag.
|
|
5710
5995
|
* Used in resource scope to track which thread observations came from.
|
|
5711
5996
|
*/
|
|
5712
|
-
async wrapWithThreadTag(threadId, observations) {
|
|
5997
|
+
async wrapWithThreadTag(threadId, observations, messageRange) {
|
|
5713
5998
|
const cleanObservations = this.stripThreadTags(observations);
|
|
5999
|
+
const groupedObservations = this.retrieval && messageRange ? wrapInObservationGroup(cleanObservations, messageRange) : cleanObservations;
|
|
5714
6000
|
const obscuredId = await this.representThreadIDInContext(threadId);
|
|
5715
6001
|
return `<thread id="${obscuredId}">
|
|
5716
|
-
${
|
|
6002
|
+
${groupedObservations}
|
|
5717
6003
|
</thread>`;
|
|
5718
6004
|
}
|
|
5719
6005
|
/**
|
|
@@ -5850,9 +6136,10 @@ ${threadClose}`;
|
|
|
5850
6136
|
});
|
|
5851
6137
|
const lastObservedAt = this.getMaxMessageTimestamp(messagesToObserve);
|
|
5852
6138
|
const existingObservations = freshRecord?.activeObservations ?? record.activeObservations ?? "";
|
|
6139
|
+
const messageRange = this.retrieval ? buildMessageRange(messagesToObserve) : void 0;
|
|
5853
6140
|
let newObservations;
|
|
5854
6141
|
if (this.scope === "resource") {
|
|
5855
|
-
const threadSection = await this.wrapWithThreadTag(threadId, result.observations);
|
|
6142
|
+
const threadSection = await this.wrapWithThreadTag(threadId, result.observations, messageRange);
|
|
5856
6143
|
newObservations = this.replaceOrAppendThreadSection(
|
|
5857
6144
|
existingObservations,
|
|
5858
6145
|
threadId,
|
|
@@ -5860,7 +6147,8 @@ ${threadClose}`;
|
|
|
5860
6147
|
lastObservedAt
|
|
5861
6148
|
);
|
|
5862
6149
|
} else {
|
|
5863
|
-
|
|
6150
|
+
const groupedObservations = this.retrieval && messageRange ? wrapInObservationGroup(result.observations, messageRange) : result.observations;
|
|
6151
|
+
newObservations = existingObservations ? `${existingObservations}${_ObservationalMemory.createMessageBoundary(lastObservedAt)}${groupedObservations}` : groupedObservations;
|
|
5864
6152
|
}
|
|
5865
6153
|
let totalTokenCount = this.tokenCounter.countObservations(newObservations);
|
|
5866
6154
|
const cycleObservationTokens = this.tokenCounter.countObservations(result.observations);
|
|
@@ -6168,11 +6456,12 @@ ${threadClose}`;
|
|
|
6168
6456
|
}
|
|
6169
6457
|
}
|
|
6170
6458
|
}
|
|
6459
|
+
const messageRange = this.retrieval ? buildMessageRange(messagesToBuffer) : void 0;
|
|
6171
6460
|
let newObservations;
|
|
6172
6461
|
if (this.scope === "resource") {
|
|
6173
|
-
newObservations = await this.wrapWithThreadTag(threadId, result.observations);
|
|
6462
|
+
newObservations = await this.wrapWithThreadTag(threadId, result.observations, messageRange);
|
|
6174
6463
|
} else {
|
|
6175
|
-
newObservations = result.observations;
|
|
6464
|
+
newObservations = this.retrieval && messageRange ? wrapInObservationGroup(result.observations, messageRange) : result.observations;
|
|
6176
6465
|
}
|
|
6177
6466
|
const newTokenCount = this.tokenCounter.countObservations(newObservations);
|
|
6178
6467
|
const newMessageIds = messagesToBuffer.map((m) => m.id);
|
|
@@ -6798,7 +7087,8 @@ ${unreflectedContent}` : freshRecord.bufferedReflection;
|
|
|
6798
7087
|
const { threadId, threadMessages, result } = obsResult;
|
|
6799
7088
|
cycleObservationTokens += this.tokenCounter.countObservations(result.observations);
|
|
6800
7089
|
const threadLastObservedAt = this.getMaxMessageTimestamp(threadMessages);
|
|
6801
|
-
const
|
|
7090
|
+
const messageRange = this.retrieval ? buildMessageRange(threadMessages) : void 0;
|
|
7091
|
+
const threadSection = await this.wrapWithThreadTag(threadId, result.observations, messageRange);
|
|
6802
7092
|
currentObservations = this.replaceOrAppendThreadSection(
|
|
6803
7093
|
currentObservations,
|
|
6804
7094
|
threadId,
|
|
@@ -7289,11 +7579,24 @@ exports.ObservationalMemory = ObservationalMemory;
|
|
|
7289
7579
|
exports.TokenCounter = TokenCounter;
|
|
7290
7580
|
exports.buildObserverPrompt = buildObserverPrompt;
|
|
7291
7581
|
exports.buildObserverSystemPrompt = buildObserverSystemPrompt;
|
|
7582
|
+
exports.combineObservationGroupRanges = combineObservationGroupRanges;
|
|
7583
|
+
exports.deriveObservationGroupProvenance = deriveObservationGroupProvenance;
|
|
7292
7584
|
exports.extractCurrentTask = extractCurrentTask;
|
|
7293
7585
|
exports.formatMessagesForObserver = formatMessagesForObserver;
|
|
7586
|
+
exports.formatToolResultForObserver = formatToolResultForObserver;
|
|
7294
7587
|
exports.getObservationsAsOf = getObservationsAsOf;
|
|
7295
7588
|
exports.hasCurrentTaskSection = hasCurrentTaskSection;
|
|
7589
|
+
exports.injectAnchorIds = injectAnchorIds;
|
|
7296
7590
|
exports.optimizeObservationsForContext = optimizeObservationsForContext;
|
|
7591
|
+
exports.parseAnchorId = parseAnchorId;
|
|
7592
|
+
exports.parseObservationGroups = parseObservationGroups;
|
|
7297
7593
|
exports.parseObserverOutput = parseObserverOutput;
|
|
7298
|
-
|
|
7299
|
-
|
|
7594
|
+
exports.reconcileObservationGroupsFromReflection = reconcileObservationGroupsFromReflection;
|
|
7595
|
+
exports.renderObservationGroupsForReflection = renderObservationGroupsForReflection;
|
|
7596
|
+
exports.resolveToolResultValue = resolveToolResultValue;
|
|
7597
|
+
exports.stripEphemeralAnchorIds = stripEphemeralAnchorIds;
|
|
7598
|
+
exports.stripObservationGroups = stripObservationGroups;
|
|
7599
|
+
exports.truncateStringByTokens = truncateStringByTokens;
|
|
7600
|
+
exports.wrapInObservationGroup = wrapInObservationGroup;
|
|
7601
|
+
//# sourceMappingURL=chunk-LVV2RT42.cjs.map
|
|
7602
|
+
//# sourceMappingURL=chunk-LVV2RT42.cjs.map
|