@mastra/memory 1.14.0-alpha.2 → 1.15.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 CHANGED
@@ -1,5 +1,79 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 1.15.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Updated the recall tool to support more precise message browsing for agents. ([#15116](https://github.com/mastra-ai/mastra/pull/15116))
8
+
9
+ Agents using `recall` can now pass `partType` and `toolName` to narrow message results to specific parts, such as tool calls or tool results for one tool. This change also adds `threadId: "current"` support across recall modes and `anchor: "start" | "end"` for no-cursor message paging, making it easier to inspect recent thread activity and past tool usage.
10
+
11
+ ## 1.14.0
12
+
13
+ ### Minor Changes
14
+
15
+ - Added usage data to ObserveHooks callbacks and standalone reflect() return. ([#15047](https://github.com/mastra-ai/mastra/pull/15047))
16
+
17
+ **ObserveHooks:** `onObservationEnd` and `onReflectionEnd` now receive a result object containing token usage from the underlying LLM call. This enables reliable usage tracking across all observation and reflection paths (sync, async buffered, and resource-scoped).
18
+
19
+ **Standalone reflect():** `reflect()` now returns `{ reflected, record, usage? }` so callers can capture token usage without hooks.
20
+
21
+ **Examples**
22
+
23
+ ```ts
24
+ // Via hooks
25
+ await memory.observe({
26
+ threadId,
27
+ messages,
28
+ hooks: {
29
+ onObservationEnd: ({ usage }) => {
30
+ // usage: { inputTokens, outputTokens, totalTokens }
31
+ },
32
+ onReflectionEnd: ({ usage }) => {
33
+ // usage: { inputTokens, outputTokens, totalTokens }
34
+ },
35
+ },
36
+ });
37
+
38
+ // Via standalone reflect()
39
+ const { reflected, usage } = await memory.reflect(threadId, resourceId);
40
+ ```
41
+
42
+ Existing callbacks that accept no arguments continue to work without changes.
43
+
44
+ - Added tracing support to Memory operations (recall, save, delete, update working memory). When an `observabilityContext` is provided, Memory creates `MEMORY_OPERATION` spans that capture operation type, message counts, embedding token usage, and vector result counts. Tracing is fully opt-in — existing usage without `observabilityContext` is unaffected. ([#14305](https://github.com/mastra-ai/mastra/pull/14305))
45
+
46
+ **Example usage:**
47
+
48
+ ```typescript
49
+ import { Memory } from '@mastra/memory';
50
+ import { InMemoryStore } from '@mastra/core/storage';
51
+
52
+ const memory = new Memory({ storage: new InMemoryStore() });
53
+
54
+ // Pass observabilityContext to create observable spans
55
+ await memory.recall({
56
+ threadId: 'thread-1',
57
+ observabilityContext: { tracingContext: { currentSpan: parentSpan } },
58
+ });
59
+
60
+ await memory.saveMessages({
61
+ messages: [userMessage, assistantMessage],
62
+ observabilityContext: { tracingContext: { currentSpan: parentSpan } },
63
+ });
64
+ ```
65
+
66
+ - Added per-record config overrides for observation and reflection thresholds in Observational Memory. Each thread can now have its own `messageTokens` and `observationTokens` thresholds that override the instance-level defaults, without requiring a process restart or cache invalidation. If no per-record override is set, the instance-level config is used as before. ([#15102](https://github.com/mastra-ai/mastra/pull/15102))
67
+
68
+ ### Patch Changes
69
+
70
+ - Fixed observational memory buffering so sealed assistant chunks stay split instead of being merged back into one persisted message during long tool runs. ([#14995](https://github.com/mastra-ai/mastra/pull/14995))
71
+
72
+ - Fixed recall() to hide dynamic system reminder messages by default, with includeSystemReminders available when callers need raw reminder history. ([#15100](https://github.com/mastra-ai/mastra/pull/15100))
73
+
74
+ - Updated dependencies [[`f32b9e1`](https://github.com/mastra-ai/mastra/commit/f32b9e115a3c754d1c8cfa3f4256fba87b09cfb7), [`7d6f521`](https://github.com/mastra-ai/mastra/commit/7d6f52164d0cca099f0b07cb2bba334360f1c8ab), [`a50d220`](https://github.com/mastra-ai/mastra/commit/a50d220b01ecbc5644d489a3d446c3bd4ab30245), [`665477b`](https://github.com/mastra-ai/mastra/commit/665477bc104fd52cfef8e7610d7664781a70c220), [`4cc2755`](https://github.com/mastra-ai/mastra/commit/4cc2755a7194cb08720ff2ab4dffb4b4a5103dfd), [`ac7baf6`](https://github.com/mastra-ai/mastra/commit/ac7baf66ef1db15e03975ef4ebb02724f015a391), [`ed425d7`](https://github.com/mastra-ai/mastra/commit/ed425d78e7c66cbda8209fee910856f98c6c6b82), [`1371703`](https://github.com/mastra-ai/mastra/commit/1371703835080450ef3f9aea58059a95d0da2e5a), [`0df8321`](https://github.com/mastra-ai/mastra/commit/0df832196eeb2450ab77ce887e8553abdd44c5a6), [`98f8a8b`](https://github.com/mastra-ai/mastra/commit/98f8a8bdf5761b9982f3ad3acbe7f1cc3efa71f3), [`ba6f7e9`](https://github.com/mastra-ai/mastra/commit/ba6f7e9086d8281393f2acae60fda61de3bff1f9), [`7eb2596`](https://github.com/mastra-ai/mastra/commit/7eb25960d607e07468c9a10c5437abd2deaf1e9a), [`1805ddc`](https://github.com/mastra-ai/mastra/commit/1805ddc9c9b3b14b63749735a13c05a45af43a80), [`fff91cf`](https://github.com/mastra-ai/mastra/commit/fff91cf914de0e731578aacebffdeebef82f0440), [`61109b3`](https://github.com/mastra-ai/mastra/commit/61109b34feb0e38d54bee4b8ca83eb7345b1d557), [`33f1ead`](https://github.com/mastra-ai/mastra/commit/33f1eadfa19c86953f593478e5fa371093b33779)]:
75
+ - @mastra/core@1.23.0
76
+
3
77
  ## 1.14.0-alpha.2
4
78
 
5
79
  ### Minor Changes
@@ -3,7 +3,7 @@ name: mastra-memory
3
3
  description: Documentation for @mastra/memory. Use when working with @mastra/memory APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/memory"
6
- version: "1.14.0-alpha.2"
6
+ version: "1.15.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.14.0-alpha.2",
2
+ "version": "1.15.0-alpha.0",
3
3
  "package": "@mastra/memory",
4
4
  "exports": {
5
5
  "ModelByInputTokens": {
package/dist/index.cjs CHANGED
@@ -15448,10 +15448,16 @@ var DefaultEmbedManyResult3 = class {
15448
15448
  };
15449
15449
  createIdGenerator3({ prefix: "aiobj", size: 24 });
15450
15450
  createIdGenerator3({ prefix: "aiobj", size: 24 });
15451
+ function getMessageParts(msg) {
15452
+ if (typeof msg.content === "string") return [];
15453
+ if (Array.isArray(msg.content)) return msg.content;
15454
+ const parts = msg.content?.parts;
15455
+ return Array.isArray(parts) ? parts : [];
15456
+ }
15451
15457
  function hasVisibleParts(msg) {
15452
15458
  if (typeof msg.content === "string") return msg.content.length > 0;
15453
- const parts = msg.content?.parts;
15454
- if (!parts || !Array.isArray(parts)) return false;
15459
+ const parts = getMessageParts(msg);
15460
+ if (parts.length === 0) return Boolean(msg.content?.content);
15455
15461
  return parts.some((p) => !p.type?.startsWith("data-"));
15456
15462
  }
15457
15463
  function parseRangeFormat(cursor) {
@@ -15712,13 +15718,13 @@ function lowDetailPartLimit(type) {
15712
15718
  if (type === "tool-result" || type === "tool-call") return AUTO_EXPAND_TOOL_TOKENS;
15713
15719
  return LOW_DETAIL_PART_TOKENS;
15714
15720
  }
15715
- function makePart(msg, partIndex, type, fullText, detail) {
15721
+ function makePart(msg, partIndex, type, fullText, detail, toolName) {
15716
15722
  if (detail === "high") {
15717
- return { messageId: msg.id, partIndex, role: msg.role, type, text: fullText, fullText };
15723
+ return { messageId: msg.id, partIndex, role: msg.role, type, text: fullText, fullText, toolName };
15718
15724
  }
15719
15725
  const hint = `recall cursor="${msg.id}" partIndex=${partIndex} detail="high"`;
15720
15726
  const { text: text4 } = truncateByTokens(fullText, lowDetailPartLimit(type), hint);
15721
- return { messageId: msg.id, partIndex, role: msg.role, type, text: text4, fullText };
15727
+ return { messageId: msg.id, partIndex, role: msg.role, type, text: text4, fullText, toolName };
15722
15728
  }
15723
15729
  function formatMessageParts(msg, detail) {
15724
15730
  const parts = [];
@@ -15726,32 +15732,73 @@ function formatMessageParts(msg, detail) {
15726
15732
  parts.push(makePart(msg, 0, "text", msg.content, detail));
15727
15733
  return parts;
15728
15734
  }
15729
- if (msg.content?.parts && Array.isArray(msg.content.parts)) {
15730
- for (let i = 0; i < msg.content.parts.length; i++) {
15731
- const part = msg.content.parts[i];
15735
+ const messageParts = getMessageParts(msg);
15736
+ if (messageParts.length > 0) {
15737
+ for (let i = 0; i < messageParts.length; i++) {
15738
+ const part = messageParts[i];
15732
15739
  const partType = part.type;
15733
15740
  if (partType === "text") {
15734
15741
  const text4 = part.text;
15735
- parts.push(makePart(msg, i, "text", text4, detail));
15742
+ if (text4) {
15743
+ parts.push(makePart(msg, i, "text", text4, detail));
15744
+ }
15736
15745
  } else if (partType === "tool-invocation") {
15737
15746
  const inv = part.toolInvocation;
15738
- if (inv.state === "result") {
15739
- const { value: resultValue } = chunkZVRO2GUN_cjs.resolveToolResultValue(
15740
- part,
15741
- inv.result
15742
- );
15743
- const resultStr = chunkZVRO2GUN_cjs.formatToolResultForObserver(resultValue, { maxTokens: HIGH_DETAIL_TOOL_RESULT_TOKENS });
15744
- const fullText = `[Tool Result: ${inv.toolName}]
15745
- ${resultStr}`;
15746
- parts.push(makePart(msg, i, "tool-result", fullText, detail));
15747
- } else {
15748
- const argsStr = detail === "low" ? "" : `
15747
+ if (inv?.toolName) {
15748
+ const hasArgs = inv.args != null;
15749
+ if (inv.state !== "partial-call" && hasArgs) {
15750
+ const argsStr = detail === "low" ? "" : `
15749
15751
  ${JSON.stringify(inv.args, null, 2)}`;
15750
- const fullText = `[Tool Call: ${inv.toolName}]${argsStr}`;
15751
- parts.push({ messageId: msg.id, partIndex: i, role: msg.role, type: "tool-call", text: fullText, fullText });
15752
+ const fullText = `[Tool Call: ${inv.toolName}]${argsStr}`;
15753
+ parts.push({
15754
+ messageId: msg.id,
15755
+ partIndex: i,
15756
+ role: msg.role,
15757
+ type: "tool-call",
15758
+ text: fullText,
15759
+ fullText,
15760
+ toolName: inv.toolName
15761
+ });
15762
+ }
15763
+ if (inv.state === "result") {
15764
+ const { value: resultValue } = chunkZVRO2GUN_cjs.resolveToolResultValue(
15765
+ part,
15766
+ inv.result
15767
+ );
15768
+ const resultStr = chunkZVRO2GUN_cjs.formatToolResultForObserver(resultValue, { maxTokens: HIGH_DETAIL_TOOL_RESULT_TOKENS });
15769
+ const fullText = `[Tool Result: ${inv.toolName}]
15770
+ ${resultStr}`;
15771
+ parts.push(makePart(msg, i, "tool-result", fullText, detail, inv.toolName));
15772
+ }
15773
+ }
15774
+ } else if (partType === "tool-call") {
15775
+ const toolName = part.toolName;
15776
+ if (toolName) {
15777
+ const rawArgs = part.input ?? part.args;
15778
+ const argsStr = detail === "low" || rawArgs == null ? "" : `
15779
+ ${typeof rawArgs === "string" ? rawArgs : JSON.stringify(rawArgs, null, 2)}`;
15780
+ const fullText = `[Tool Call: ${toolName}]${argsStr}`;
15781
+ parts.push({
15782
+ messageId: msg.id,
15783
+ partIndex: i,
15784
+ role: msg.role,
15785
+ type: "tool-call",
15786
+ text: fullText,
15787
+ fullText,
15788
+ toolName
15789
+ });
15790
+ }
15791
+ } else if (partType === "tool-result") {
15792
+ const toolName = part.toolName;
15793
+ if (toolName) {
15794
+ const rawResult = part.output ?? part.result;
15795
+ const resultStr = chunkZVRO2GUN_cjs.formatToolResultForObserver(rawResult, { maxTokens: HIGH_DETAIL_TOOL_RESULT_TOKENS });
15796
+ const fullText = `[Tool Result: ${toolName}]
15797
+ ${resultStr}`;
15798
+ parts.push(makePart(msg, i, "tool-result", fullText, detail, toolName));
15752
15799
  }
15753
15800
  } else if (partType === "reasoning") {
15754
- const reasoning = part.reasoning;
15801
+ const reasoning = part.reasoning ?? part.text;
15755
15802
  if (reasoning) {
15756
15803
  parts.push(makePart(msg, i, "reasoning", reasoning, detail));
15757
15804
  }
@@ -15889,7 +15936,7 @@ async function recallPart({
15889
15936
  `Message ${cursor} has no visible content (it may be an internal system message). Try a neighboring message ID instead.`
15890
15937
  );
15891
15938
  }
15892
- const target = allParts.find((p) => p.partIndex === partIndex);
15939
+ const target = [...allParts].reverse().find((p) => p.partIndex === partIndex);
15893
15940
  if (!target) {
15894
15941
  const availableIndices = allParts.map((p) => p.partIndex).join(", ");
15895
15942
  const highestVisiblePartIndex = Math.max(...allParts.map((p) => p.partIndex));
@@ -15942,6 +15989,8 @@ async function recallMessages({
15942
15989
  page = 1,
15943
15990
  limit = 20,
15944
15991
  detail = "low",
15992
+ partType,
15993
+ toolName,
15945
15994
  threadScope,
15946
15995
  maxTokens = DEFAULT_MAX_RESULT_TOKENS
15947
15996
  }) {
@@ -16031,12 +16080,18 @@ async function recallMessages({
16031
16080
  }
16032
16081
  const hasNextPage = isForward ? hasMore : pageIndex > 0;
16033
16082
  const hasPrevPage = isForward ? pageIndex > 0 : hasMore;
16034
- const allParts = [];
16083
+ let allParts = [];
16035
16084
  const timestamps = /* @__PURE__ */ new Map();
16036
16085
  for (const msg of messages) {
16037
16086
  timestamps.set(msg.id, msg.createdAt);
16038
16087
  allParts.push(...formatMessageParts(msg, detail));
16039
16088
  }
16089
+ if (toolName) {
16090
+ allParts = allParts.filter((p) => (p.type === "tool-call" || p.type === "tool-result") && p.toolName === toolName);
16091
+ }
16092
+ if (partType) {
16093
+ allParts = allParts.filter((p) => p.type === partType);
16094
+ }
16040
16095
  if (detail === "high" && allParts.length > 0) {
16041
16096
  const firstPart = allParts[0];
16042
16097
  const sameMsgParts = allParts.filter((p) => p.messageId === firstPart.messageId);
@@ -16073,8 +16128,9 @@ High detail returns 1 part at a time. To continue: ${hints.join(", or ")}.`;
16073
16128
  };
16074
16129
  }
16075
16130
  const rendered = renderFormattedParts(allParts, timestamps, { maxTokens });
16131
+ const emptyMessage = allParts.length === 0 ? partType || toolName ? "(no message parts matched the current filters)" : "(no visible message parts found for this page)" : "(no messages found)";
16076
16132
  return {
16077
- messages: rendered.text,
16133
+ messages: rendered.text || emptyMessage,
16078
16134
  count: messages.length,
16079
16135
  cursor,
16080
16136
  page: normalizedPage,
@@ -16093,6 +16149,9 @@ async function recallThreadFromStart({
16093
16149
  page = 1,
16094
16150
  limit = 20,
16095
16151
  detail = "low",
16152
+ partType,
16153
+ toolName,
16154
+ anchor = "start",
16096
16155
  maxTokens = DEFAULT_MAX_RESULT_TOKENS
16097
16156
  }) {
16098
16157
  if (!memory) {
@@ -16118,29 +16177,37 @@ async function recallThreadFromStart({
16118
16177
  resourceId,
16119
16178
  page: 0,
16120
16179
  perPage: fetchCount,
16121
- orderBy: { field: "createdAt", direction: "ASC" }
16180
+ orderBy: { field: "createdAt", direction: anchor === "end" ? "DESC" : "ASC" }
16122
16181
  });
16123
- const visibleMessages = result.messages.filter(hasVisibleParts);
16124
- const total = visibleMessages.length;
16182
+ const visibleMessages = anchor === "end" ? result.messages.slice(0, fetchCount).filter(hasVisibleParts).reverse() : result.messages.slice(0, fetchCount).filter(hasVisibleParts);
16125
16183
  const skip = pageIndex * normalizedLimit;
16126
- const hasMore = total > skip + normalizedLimit;
16127
16184
  const messages = visibleMessages.slice(skip, skip + normalizedLimit);
16128
- const allParts = [];
16185
+ const hasExtraMessage = visibleMessages.length > skip + messages.length;
16186
+ const hasNextPage = messages.length > 0 ? anchor === "end" ? pageIndex > 0 : hasExtraMessage : false;
16187
+ const hasPrevPage = messages.length > 0 ? anchor === "end" ? hasExtraMessage : pageIndex > 0 : pageIndex > 0;
16188
+ let allParts = [];
16129
16189
  const timestamps = /* @__PURE__ */ new Map();
16130
16190
  for (const msg of messages) {
16131
16191
  timestamps.set(msg.id, msg.createdAt);
16132
16192
  allParts.push(...formatMessageParts(msg, detail));
16133
16193
  }
16194
+ if (toolName) {
16195
+ allParts = allParts.filter((p) => (p.type === "tool-call" || p.type === "tool-result") && p.toolName === toolName);
16196
+ }
16197
+ if (partType) {
16198
+ allParts = allParts.filter((p) => p.type === partType);
16199
+ }
16134
16200
  const rendered = renderFormattedParts(allParts, timestamps, { maxTokens });
16201
+ const emptyMessage = messages.length === 0 ? pageIndex > 0 ? `(no messages found on page ${normalizedPage} for this thread)` : "(no messages in this thread)" : partType || toolName ? "(no message parts matched the current filters)" : "(no messages found)";
16135
16202
  return {
16136
- messages: rendered.text || "(no messages in this thread)",
16203
+ messages: rendered.text || emptyMessage,
16137
16204
  count: messages.length,
16138
16205
  cursor: messages[0]?.id || "",
16139
16206
  page: normalizedPage,
16140
16207
  limit: normalizedLimit,
16141
16208
  detail,
16142
- hasNextPage: hasMore,
16143
- hasPrevPage: pageIndex > 0,
16209
+ hasNextPage,
16210
+ hasPrevPage,
16144
16211
  truncated: rendered.truncated,
16145
16212
  tokenOffset: rendered.tokenOffset
16146
16213
  };
@@ -16157,7 +16224,9 @@ var recallTool = (_memoryConfig, options) => {
16157
16224
  mode: zod.z.enum(["messages", "threads", "search"]).optional().describe(
16158
16225
  'What to retrieve. "messages" (default) pages through message history. "threads" lists all threads for the current user. "search" finds messages by semantic similarity across all threads.'
16159
16226
  ),
16160
- threadId: zod.z.string().min(1).optional().describe('Browse a different thread. Use mode="threads" first to discover thread IDs.'),
16227
+ threadId: zod.z.string().min(1).optional().describe(
16228
+ 'Browse a different thread, or use "current" for the active thread. Use mode="threads" first to discover thread IDs.'
16229
+ ),
16161
16230
  before: zod.z.string().optional().describe(
16162
16231
  'For mode="threads": only show threads created before this date. ISO 8601 or natural date string (e.g. "2026-03-15", "2026-03-10T00:00:00Z").'
16163
16232
  ),
@@ -16173,6 +16242,9 @@ var recallTool = (_memoryConfig, options) => {
16173
16242
  cursor: zod.z.string().min(1).optional().describe(
16174
16243
  'A message ID to use as the pagination cursor. For mode="messages", provide either cursor or threadId. If only cursor is provided, it must belong to the current thread. Extract it from the start or end of an observation group range.'
16175
16244
  ),
16245
+ anchor: zod.z.enum(["start", "end"]).optional().describe(
16246
+ 'For mode="messages" without a cursor, page from the start (oldest-first) or end (newest-first) of the thread. Defaults to "start".'
16247
+ ),
16176
16248
  page: zod.z.number().int().min(-50).max(50).optional().describe(
16177
16249
  "Pagination offset. For messages: positive pages move forward from cursor, negative move backward. For threads: page number (0-indexed). 0 is treated as 1 for messages."
16178
16250
  ),
@@ -16180,6 +16252,10 @@ var recallTool = (_memoryConfig, options) => {
16180
16252
  detail: zod.z.enum(["low", "high"]).optional().describe(
16181
16253
  'Detail level for messages. "low" (default) returns truncated text and tool names. "high" returns full content with tool args/results.'
16182
16254
  ),
16255
+ partType: zod.z.enum(["text", "tool-call", "tool-result", "reasoning", "image", "file"]).optional().describe('Filter results to only include parts of this type. Only applies to mode="messages".'),
16256
+ toolName: zod.z.string().min(1).optional().describe(
16257
+ 'Filter results to only include tool-call and tool-result parts matching this tool name. Only applies to mode="messages".'
16258
+ ),
16183
16259
  partIndex: zod.z.number().int().min(0).optional().describe(
16184
16260
  "Fetch a single part from the cursor message by its positional index. When provided, returns only that part at high detail. Indices are shown as [p0], [p1], etc. in recall results."
16185
16261
  )
@@ -16189,9 +16265,12 @@ var recallTool = (_memoryConfig, options) => {
16189
16265
  query,
16190
16266
  cursor,
16191
16267
  threadId: explicitThreadId,
16268
+ anchor,
16192
16269
  page,
16193
16270
  limit,
16194
16271
  detail,
16272
+ partType,
16273
+ toolName,
16195
16274
  partIndex,
16196
16275
  before,
16197
16276
  after
@@ -16199,9 +16278,13 @@ var recallTool = (_memoryConfig, options) => {
16199
16278
  const memory = context2?.memory;
16200
16279
  const currentThreadId = context2?.agent?.threadId;
16201
16280
  const resourceId = context2?.agent?.resourceId;
16281
+ const resolvedExplicitThreadId = explicitThreadId === "current" ? currentThreadId : explicitThreadId;
16202
16282
  if (!memory) {
16203
16283
  throw new Error("Memory instance is required for recall");
16204
16284
  }
16285
+ if (explicitThreadId === "current" && !currentThreadId) {
16286
+ throw new Error("Could not resolve current thread.");
16287
+ }
16205
16288
  if (mode === "search") {
16206
16289
  if (!query) {
16207
16290
  throw new Error('query is required for mode="search"');
@@ -16217,11 +16300,12 @@ var recallTool = (_memoryConfig, options) => {
16217
16300
  topK: limit ?? 10,
16218
16301
  before,
16219
16302
  after,
16220
- threadScope: !isResourceScope ? currentThreadId || void 0 : void 0
16303
+ threadScope: !isResourceScope ? currentThreadId || void 0 : resolvedExplicitThreadId || void 0
16221
16304
  });
16222
16305
  }
16223
16306
  if (mode === "threads") {
16224
- if (!isResourceScope) {
16307
+ const requestedCurrentThread = explicitThreadId === "current";
16308
+ if (!isResourceScope || requestedCurrentThread) {
16225
16309
  if (!currentThreadId || !memory.getThreadById) {
16226
16310
  return { error: "Could not resolve current thread." };
16227
16311
  }
@@ -16229,6 +16313,9 @@ var recallTool = (_memoryConfig, options) => {
16229
16313
  if (!thread) {
16230
16314
  return { error: "Could not resolve current thread." };
16231
16315
  }
16316
+ if (isResourceScope && resourceId && thread.resourceId !== resourceId) {
16317
+ throw new Error("Thread does not belong to the active resource");
16318
+ }
16232
16319
  return {
16233
16320
  threads: `- **${thread.title || "(untitled)"}** \u2190 current
16234
16321
  id: ${thread.id}
@@ -16251,7 +16338,7 @@ var recallTool = (_memoryConfig, options) => {
16251
16338
  after
16252
16339
  });
16253
16340
  }
16254
- const hasExplicitThreadId = typeof explicitThreadId === "string" && explicitThreadId.length > 0;
16341
+ const hasExplicitThreadId = typeof resolvedExplicitThreadId === "string" && resolvedExplicitThreadId.length > 0;
16255
16342
  const hasCursor = typeof cursor === "string" && cursor.length > 0;
16256
16343
  if (!hasExplicitThreadId && !hasCursor) {
16257
16344
  throw new Error('Either cursor or threadId is required for mode="messages"');
@@ -16268,7 +16355,7 @@ var recallTool = (_memoryConfig, options) => {
16268
16355
  if (!memory.getThreadById) {
16269
16356
  throw new Error("Memory instance cannot verify thread access for recall");
16270
16357
  }
16271
- const thread = await memory.getThreadById({ threadId: explicitThreadId });
16358
+ const thread = await memory.getThreadById({ threadId: resolvedExplicitThreadId });
16272
16359
  if (!thread || thread.resourceId !== resourceId) {
16273
16360
  throw new Error("Thread does not belong to the active resource");
16274
16361
  }
@@ -16309,7 +16396,10 @@ var recallTool = (_memoryConfig, options) => {
16309
16396
  resourceId: isResourceScope ? resourceId : void 0,
16310
16397
  page: page ?? 1,
16311
16398
  limit: limit ?? 20,
16312
- detail: detail ?? "low"
16399
+ detail: detail ?? "low",
16400
+ partType,
16401
+ toolName,
16402
+ anchor: anchor ?? "start"
16313
16403
  });
16314
16404
  }
16315
16405
  if (partIndex !== void 0 && partIndex !== null) {
@@ -16330,6 +16420,8 @@ var recallTool = (_memoryConfig, options) => {
16330
16420
  page,
16331
16421
  limit,
16332
16422
  detail: detail ?? "low",
16423
+ partType,
16424
+ toolName,
16333
16425
  threadScope
16334
16426
  });
16335
16427
  }