@mastra/server 1.18.0-alpha.2 → 1.18.0-alpha.3

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.
Files changed (26) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/{chunk-NTZOZIKF.cjs → chunk-GDWCOWNR.cjs} +584 -43
  3. package/dist/chunk-GDWCOWNR.cjs.map +1 -0
  4. package/dist/{chunk-DJQT32SV.cjs → chunk-HUAXEKGI.cjs} +353 -105
  5. package/dist/chunk-HUAXEKGI.cjs.map +1 -0
  6. package/dist/{chunk-FGZC4JP7.js → chunk-SDKSW2BQ.js} +571 -30
  7. package/dist/chunk-SDKSW2BQ.js.map +1 -0
  8. package/dist/{chunk-PVUR75AM.js → chunk-YUTITKH2.js} +353 -105
  9. package/dist/chunk-YUTITKH2.js.map +1 -0
  10. package/dist/docs/SKILL.md +1 -1
  11. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  12. package/dist/{observational-memory-UGDENJPE-NVMIXNI4.js → observational-memory-SN7GKMHZ-IWVBFBS6.js} +3 -3
  13. package/dist/{observational-memory-UGDENJPE-NVMIXNI4.js.map → observational-memory-SN7GKMHZ-IWVBFBS6.js.map} +1 -1
  14. package/dist/{observational-memory-UGDENJPE-DM3C7ZXI.cjs → observational-memory-SN7GKMHZ-WOK4TGDH.cjs} +26 -26
  15. package/dist/{observational-memory-UGDENJPE-DM3C7ZXI.cjs.map → observational-memory-SN7GKMHZ-WOK4TGDH.cjs.map} +1 -1
  16. package/dist/server/handlers/agent-builder.cjs +16 -16
  17. package/dist/server/handlers/agent-builder.js +1 -1
  18. package/dist/server/handlers.cjs +2 -2
  19. package/dist/server/handlers.js +1 -1
  20. package/dist/server/server-adapter/index.cjs +16 -16
  21. package/dist/server/server-adapter/index.js +1 -1
  22. package/package.json +4 -4
  23. package/dist/chunk-DJQT32SV.cjs.map +0 -1
  24. package/dist/chunk-FGZC4JP7.js.map +0 -1
  25. package/dist/chunk-NTZOZIKF.cjs.map +0 -1
  26. package/dist/chunk-PVUR75AM.js.map +0 -1
@@ -3,7 +3,7 @@ import { __commonJS as __commonJS$3, require_token_error as require_token_error$
3
3
  import { actionIdPathParams, actionRunPathParams, streamAgentBuilderBodySchema, startAsyncAgentBuilderBodySchema, resumeAgentBuilderBodySchema, streamLegacyAgentBuilderBodySchema } from './chunk-S7PYDU5I.js';
4
4
  import { __commonJS as __commonJS$1, require_token_error, __toESM } from './chunk-BZZVTO7B.js';
5
5
  import { __commonJS as __commonJS$2, require_token_error as require_token_error$1, __toESM as __toESM$1 } from './chunk-PWAXLHKP.js';
6
- import { e, truncateStringByTokens, resolveToolResultValue, formatToolResultForObserver, estimateTokenCount } from './chunk-PVUR75AM.js';
6
+ import { e, truncateStringByTokens, estimateTokenCount, resolveToolResultValue, formatToolResultForObserver } from './chunk-YUTITKH2.js';
7
7
  import { LIST_WORKFLOWS_ROUTE, GET_WORKFLOW_BY_ID_ROUTE, LIST_WORKFLOW_RUNS_ROUTE, GET_WORKFLOW_RUN_BY_ID_ROUTE, CREATE_WORKFLOW_RUN_ROUTE, STREAM_WORKFLOW_ROUTE, START_ASYNC_WORKFLOW_ROUTE, START_WORKFLOW_RUN_ROUTE, OBSERVE_STREAM_WORKFLOW_ROUTE, RESUME_ASYNC_WORKFLOW_ROUTE, RESUME_WORKFLOW_ROUTE, RESUME_STREAM_WORKFLOW_ROUTE, CANCEL_WORKFLOW_RUN_ROUTE, STREAM_LEGACY_WORKFLOW_ROUTE, OBSERVE_STREAM_LEGACY_WORKFLOW_ROUTE } from './chunk-G2ZZKKQI.js';
8
8
  import { listWorkflowsResponseSchema, workflowInfoSchema, workflowRunsResponseSchema, listWorkflowRunsQuerySchema, workflowRunResultSchema, workflowRunResultQuerySchema, createWorkflowRunResponseSchema, workflowExecutionResultSchema, workflowControlResponseSchema } from './chunk-7X4P2I6L.js';
9
9
  import { streamResponseSchema } from './chunk-V7EVEI4C.js';
@@ -16143,7 +16143,7 @@ function parseRangeFormat(cursor) {
16143
16143
  }
16144
16144
  return null;
16145
16145
  }
16146
- async function resolveCursorMessage(memory, cursor) {
16146
+ async function resolveCursorMessage(memory, cursor, access) {
16147
16147
  const normalized = cursor.trim();
16148
16148
  if (!normalized) {
16149
16149
  throw new Error("Cursor is required");
@@ -16161,8 +16161,171 @@ async function resolveCursorMessage(memory, cursor) {
16161
16161
  if (!message) {
16162
16162
  throw new Error(`Could not resolve cursor message: ${cursor}`);
16163
16163
  }
16164
+ if (access?.resourceId && message.resourceId !== access.resourceId) {
16165
+ throw new Error(`Could not resolve cursor message: ${cursor}`);
16166
+ }
16167
+ if (access?.threadScope && message.threadId !== access.threadScope) {
16168
+ throw new Error(`Could not resolve cursor message: ${cursor}`);
16169
+ }
16164
16170
  return message;
16165
16171
  }
16172
+ async function listThreadsForResource({
16173
+ memory,
16174
+ resourceId,
16175
+ currentThreadId,
16176
+ page = 0,
16177
+ limit = 20,
16178
+ before,
16179
+ after
16180
+ }) {
16181
+ if (!resourceId) {
16182
+ throw new Error("Resource ID is required to list threads");
16183
+ }
16184
+ const MAX_LIMIT = 50;
16185
+ const normalizedLimit = Math.min(Math.max(limit, 1), MAX_LIMIT);
16186
+ const hasDateFilter = !!(before || after);
16187
+ const beforeDate = before ? new Date(before) : null;
16188
+ const afterDate = after ? new Date(after) : null;
16189
+ const result = await memory.listThreads({
16190
+ filter: { resourceId },
16191
+ page: hasDateFilter ? 0 : page,
16192
+ perPage: hasDateFilter ? false : normalizedLimit,
16193
+ orderBy: { field: "updatedAt", direction: "DESC" }
16194
+ });
16195
+ let threads = result.threads;
16196
+ if (beforeDate) {
16197
+ threads = threads.filter((t) => t.createdAt < beforeDate);
16198
+ }
16199
+ if (afterDate) {
16200
+ threads = threads.filter((t) => t.createdAt > afterDate);
16201
+ }
16202
+ let hasMore;
16203
+ if (hasDateFilter) {
16204
+ const offset = page * normalizedLimit;
16205
+ hasMore = offset + normalizedLimit < threads.length;
16206
+ threads = threads.slice(offset, offset + normalizedLimit);
16207
+ } else {
16208
+ hasMore = result.hasMore;
16209
+ }
16210
+ if (threads.length === 0) {
16211
+ return {
16212
+ threads: "No threads found matching the criteria.",
16213
+ count: 0,
16214
+ page,
16215
+ hasMore: false
16216
+ };
16217
+ }
16218
+ const lines = [];
16219
+ for (const thread of threads) {
16220
+ const isCurrent = thread.id === currentThreadId;
16221
+ const title = thread.title || "(untitled)";
16222
+ const updated = formatTimestamp(thread.updatedAt);
16223
+ const created = formatTimestamp(thread.createdAt);
16224
+ const marker21 = isCurrent ? " \u2190 current" : "";
16225
+ lines.push(`- **${title}**${marker21}`);
16226
+ lines.push(` id: ${thread.id}`);
16227
+ lines.push(` updated: ${updated} | created: ${created}`);
16228
+ }
16229
+ return {
16230
+ threads: lines.join("\n"),
16231
+ count: threads.length,
16232
+ page,
16233
+ hasMore
16234
+ };
16235
+ }
16236
+ async function searchMessagesForResource({
16237
+ memory,
16238
+ resourceId,
16239
+ currentThreadId,
16240
+ query,
16241
+ topK = 10,
16242
+ maxTokens = DEFAULT_MAX_RESULT_TOKENS,
16243
+ before,
16244
+ after,
16245
+ threadScope
16246
+ }) {
16247
+ if (!memory.searchMessages) {
16248
+ return {
16249
+ results: "Search is not configured. Enable it with `retrieval: { vector: true }` and configure a vector store and embedder on your Memory instance.",
16250
+ count: 0
16251
+ };
16252
+ }
16253
+ const MAX_TOPK = 20;
16254
+ const clampedTopK = Math.min(Math.max(topK, 1), MAX_TOPK);
16255
+ const effectiveTopK = threadScope || before || after ? Math.max(clampedTopK * 3, clampedTopK + 10) : clampedTopK;
16256
+ const searchTopK = Math.min(MAX_TOPK, effectiveTopK);
16257
+ const beforeDate = before ? new Date(before) : void 0;
16258
+ const afterDate = after ? new Date(after) : void 0;
16259
+ const { results } = await memory.searchMessages({
16260
+ query,
16261
+ resourceId,
16262
+ topK: searchTopK,
16263
+ filter: {
16264
+ ...threadScope ? { threadId: threadScope } : {},
16265
+ ...afterDate ? { observedAfter: afterDate } : {},
16266
+ ...beforeDate ? { observedBefore: beforeDate } : {}
16267
+ }
16268
+ });
16269
+ if (results.length === 0) {
16270
+ return {
16271
+ results: "No matching messages found.",
16272
+ count: 0
16273
+ };
16274
+ }
16275
+ const threadIds = [...new Set(results.map((r) => r.threadId))];
16276
+ const threadMap = /* @__PURE__ */ new Map();
16277
+ if (memory.getThreadById) {
16278
+ await Promise.all(
16279
+ threadIds.map(async (id) => {
16280
+ const thread = await memory.getThreadById({ threadId: id });
16281
+ if (thread) threadMap.set(id, thread);
16282
+ })
16283
+ );
16284
+ }
16285
+ const filteredMatches = results.filter((match) => {
16286
+ if (threadScope && match.threadId !== threadScope) return false;
16287
+ if (beforeDate && match.observedAt && match.observedAt >= beforeDate) return false;
16288
+ if (afterDate && match.observedAt && match.observedAt <= afterDate) return false;
16289
+ return true;
16290
+ });
16291
+ if (filteredMatches.length === 0) {
16292
+ return { results: "No matching messages found.", count: 0 };
16293
+ }
16294
+ const limitedMatches = filteredMatches.slice(0, clampedTopK);
16295
+ const sections = limitedMatches.map((match) => {
16296
+ const thread = threadMap.get(match.threadId);
16297
+ const title = thread?.title || "(untitled)";
16298
+ const isCurrentThread = match.threadId === currentThreadId;
16299
+ const generationLabel = isCurrentThread ? "Current thread memory" : "Older memory from another thread";
16300
+ const generationDetail = isCurrentThread ? "This result came from the current thread." : "This result came from an older memory generation in another thread.";
16301
+ const threadLine = `- thread: ${match.threadId}${thread ? ` (${title})` : ""}`;
16302
+ const sourceLine = match.range ? `- source: raw messages from ID ${match.range.split(":")[0] ?? "(unknown)"} through ID ${match.range.split(":")[1] ?? "(unknown)"}` : "- source: raw message range unavailable";
16303
+ const updatedLine = thread ? `- thread updated: ${formatTimestamp(thread.updatedAt)}` : void 0;
16304
+ const groupLine = match.groupId ? `- observation group: ${match.groupId}` : void 0;
16305
+ const scoreLine = `- score: ${match.score.toFixed(2)}`;
16306
+ const body = (match.text || "").trim() || "_Observation text unavailable._";
16307
+ return [
16308
+ `### ${generationLabel}`,
16309
+ "",
16310
+ generationDetail,
16311
+ threadLine,
16312
+ sourceLine,
16313
+ updatedLine,
16314
+ groupLine,
16315
+ scoreLine,
16316
+ "",
16317
+ "```text",
16318
+ body,
16319
+ "```"
16320
+ ].filter(Boolean).join("\n");
16321
+ });
16322
+ const assembled = sections.join("\n\n");
16323
+ const { text: limited } = truncateByTokens(assembled, maxTokens);
16324
+ return {
16325
+ results: limited,
16326
+ count: limitedMatches.length
16327
+ };
16328
+ }
16166
16329
  var LOW_DETAIL_PART_TOKENS = 30;
16167
16330
  var AUTO_EXPAND_TEXT_TOKENS = 100;
16168
16331
  var AUTO_EXPAND_TOOL_TOKENS = 20;
@@ -16313,8 +16476,10 @@ function renderFormattedParts(parts, timestamps, options) {
16313
16476
  async function recallPart({
16314
16477
  memory,
16315
16478
  threadId,
16479
+ resourceId,
16316
16480
  cursor,
16317
16481
  partIndex,
16482
+ threadScope,
16318
16483
  maxTokens = DEFAULT_MAX_RESULT_TOKENS
16319
16484
  }) {
16320
16485
  if (!memory || typeof memory.getMemoryStore !== "function") {
@@ -16323,13 +16488,10 @@ async function recallPart({
16323
16488
  if (!threadId) {
16324
16489
  throw new Error("Thread ID is required for recall");
16325
16490
  }
16326
- const resolved = await resolveCursorMessage(memory, cursor);
16491
+ const resolved = await resolveCursorMessage(memory, cursor, { resourceId, threadScope });
16327
16492
  if ("hint" in resolved) {
16328
16493
  throw new Error(resolved.hint);
16329
16494
  }
16330
- if (resolved.threadId !== threadId) {
16331
- throw new Error("The requested cursor does not belong to the current thread");
16332
- }
16333
16495
  const allParts = formatMessageParts(resolved, "high");
16334
16496
  if (allParts.length === 0) {
16335
16497
  throw new Error(
@@ -16361,6 +16523,7 @@ async function recallMessages({
16361
16523
  page = 1,
16362
16524
  limit = 20,
16363
16525
  detail = "low",
16526
+ threadScope,
16364
16527
  maxTokens = DEFAULT_MAX_RESULT_TOKENS
16365
16528
  }) {
16366
16529
  if (!memory) {
@@ -16377,7 +16540,7 @@ async function recallMessages({
16377
16540
  const rawPage = page === 0 ? 1 : page;
16378
16541
  const normalizedPage = Math.max(Math.min(rawPage, MAX_PAGE), -MAX_PAGE);
16379
16542
  const normalizedLimit = Math.min(limit, MAX_LIMIT);
16380
- const resolved = await resolveCursorMessage(memory, cursor);
16543
+ const resolved = await resolveCursorMessage(memory, cursor, { resourceId, threadScope });
16381
16544
  if ("hint" in resolved) {
16382
16545
  return {
16383
16546
  messages: resolved.hint,
@@ -16393,15 +16556,27 @@ async function recallMessages({
16393
16556
  };
16394
16557
  }
16395
16558
  const anchor = resolved;
16396
- if (anchor.threadId !== threadId) {
16397
- throw new Error("The requested cursor does not belong to the current thread");
16559
+ if (anchor.threadId && anchor.threadId !== threadId) {
16560
+ return {
16561
+ messages: `Cursor does not belong to the active thread. Expected thread "${threadId}" but cursor "${cursor}" belongs to "${anchor.threadId}".`,
16562
+ count: 0,
16563
+ cursor,
16564
+ page: normalizedPage,
16565
+ limit: normalizedLimit,
16566
+ detail,
16567
+ hasNextPage: false,
16568
+ hasPrevPage: false,
16569
+ truncated: false,
16570
+ tokenOffset: 0
16571
+ };
16398
16572
  }
16573
+ const resolvedThreadId = threadId;
16399
16574
  const isForward = normalizedPage > 0;
16400
16575
  const pageIndex = Math.max(Math.abs(normalizedPage), 1) - 1;
16401
16576
  const skip = pageIndex * normalizedLimit;
16402
16577
  const fetchCount = skip + normalizedLimit + 1;
16403
16578
  const result = await memory.recall({
16404
- threadId,
16579
+ threadId: resolvedThreadId,
16405
16580
  resourceId,
16406
16581
  page: 0,
16407
16582
  perPage: fetchCount,
@@ -16484,55 +16659,233 @@ High detail returns 1 part at a time. To continue: ${hints.join(", or ")}.`;
16484
16659
  tokenOffset: rendered.tokenOffset
16485
16660
  };
16486
16661
  }
16487
- var recallTool = (_memoryConfig) => {
16662
+ async function recallThreadFromStart({
16663
+ memory,
16664
+ threadId,
16665
+ resourceId,
16666
+ page = 1,
16667
+ limit = 20,
16668
+ detail = "low",
16669
+ maxTokens = DEFAULT_MAX_RESULT_TOKENS
16670
+ }) {
16671
+ if (!memory) {
16672
+ throw new Error("Memory instance is required for recall");
16673
+ }
16674
+ if (!threadId) {
16675
+ throw new Error("Thread ID is required for recall");
16676
+ }
16677
+ if (resourceId && memory.getThreadById) {
16678
+ const thread = await memory.getThreadById({ threadId });
16679
+ if (!thread || thread.resourceId !== resourceId) {
16680
+ throw new Error("Thread not found");
16681
+ }
16682
+ }
16683
+ const MAX_PAGE = 50;
16684
+ const MAX_LIMIT = 20;
16685
+ const normalizedPage = Math.max(Math.min(page, MAX_PAGE), 1);
16686
+ const normalizedLimit = Math.min(Math.max(limit, 1), MAX_LIMIT);
16687
+ const pageIndex = normalizedPage - 1;
16688
+ const fetchCount = pageIndex * normalizedLimit + normalizedLimit + 1;
16689
+ const result = await memory.recall({
16690
+ threadId,
16691
+ resourceId,
16692
+ page: 0,
16693
+ perPage: fetchCount,
16694
+ orderBy: { field: "createdAt", direction: "ASC" }
16695
+ });
16696
+ const visibleMessages = result.messages.filter(hasVisibleParts);
16697
+ const total = visibleMessages.length;
16698
+ const skip = pageIndex * normalizedLimit;
16699
+ const hasMore = total > skip + normalizedLimit;
16700
+ const messages = visibleMessages.slice(skip, skip + normalizedLimit);
16701
+ const allParts = [];
16702
+ const timestamps = /* @__PURE__ */ new Map();
16703
+ for (const msg of messages) {
16704
+ timestamps.set(msg.id, msg.createdAt);
16705
+ allParts.push(...formatMessageParts(msg, detail));
16706
+ }
16707
+ const rendered = renderFormattedParts(allParts, timestamps, { maxTokens });
16708
+ return {
16709
+ messages: rendered.text || "(no messages in this thread)",
16710
+ count: messages.length,
16711
+ cursor: messages[0]?.id || "",
16712
+ page: normalizedPage,
16713
+ limit: normalizedLimit,
16714
+ detail,
16715
+ hasNextPage: hasMore,
16716
+ hasPrevPage: pageIndex > 0,
16717
+ truncated: rendered.truncated,
16718
+ tokenOffset: rendered.tokenOffset
16719
+ };
16720
+ }
16721
+ var recallTool = (_memoryConfig, options) => {
16722
+ const retrievalScope = options?.retrievalScope ?? "thread";
16723
+ const isResourceScope = retrievalScope === "resource";
16724
+ const description = isResourceScope ? 'Browse conversation history. Use mode="threads" to list all threads for the current user. Use mode="messages" (default) to browse messages in the current thread or pass threadId to browse another thread in the active resource. If you pass only a cursor, it must belong to the current thread. Use mode="search" to find messages by content across all threads.' : `Browse conversation history in the current thread. Use mode="messages" (default) to page through messages near a cursor. Use mode="search" to find messages by content in this thread. Use mode="threads" to get the current thread's ID and title.`;
16488
16725
  return createTool({
16489
16726
  id: "recall",
16490
- description: 'Retrieve raw message history near an observation group cursor. Observation group ranges use the format startId:endId. Pass either the start or end message ID as the cursor. Use detail="low" (default) for an overview, detail="high" for full content, or provide partIndex to fetch a specific part from the cursor message.',
16727
+ description,
16491
16728
  inputSchema: z.object({
16492
- cursor: z.string().min(1).describe("A single message ID to use as the pagination cursor. Extract it from the start or end of a range."),
16729
+ ...isResourceScope ? {
16730
+ mode: z.enum(["messages", "threads", "search"]).optional().describe(
16731
+ '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.'
16732
+ ),
16733
+ threadId: z.string().min(1).optional().describe('Browse a different thread. Use mode="threads" first to discover thread IDs.'),
16734
+ before: z.string().optional().describe(
16735
+ '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").'
16736
+ ),
16737
+ after: z.string().optional().describe(
16738
+ 'For mode="threads": only show threads created after this date. ISO 8601 or natural date string (e.g. "2026-03-01", "2026-03-10T00:00:00Z").'
16739
+ )
16740
+ } : {
16741
+ mode: z.enum(["messages", "threads", "search"]).optional().describe(
16742
+ 'What to retrieve. "messages" (default) pages through message history. "threads" returns info about the current thread. "search" finds messages by semantic similarity in this thread.'
16743
+ )
16744
+ },
16745
+ query: z.string().min(1).optional().describe('Search query for mode="search". Finds messages semantically similar to this text.'),
16746
+ cursor: z.string().min(1).optional().describe(
16747
+ '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.'
16748
+ ),
16493
16749
  page: z.number().int().min(-50).max(50).optional().describe(
16494
- "Pagination offset from the cursor. Positive pages move forward, negative pages move backward, and 0 is treated as 1."
16750
+ "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."
16495
16751
  ),
16496
- limit: z.number().int().positive().max(20).optional().describe("Maximum number of messages to return. Defaults to 20."),
16752
+ limit: z.number().int().positive().max(20).optional().describe("Maximum number of items to return per page. Defaults to 20."),
16497
16753
  detail: z.enum(["low", "high"]).optional().describe(
16498
- 'Detail level. "low" (default) returns truncated text and tool names. "high" returns full content with tool args/results.'
16754
+ 'Detail level for messages. "low" (default) returns truncated text and tool names. "high" returns full content with tool args/results.'
16499
16755
  ),
16500
16756
  partIndex: z.number().int().min(0).optional().describe(
16501
16757
  "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."
16502
16758
  )
16503
16759
  }),
16504
16760
  execute: async ({
16761
+ mode,
16762
+ query,
16505
16763
  cursor,
16764
+ threadId: explicitThreadId,
16506
16765
  page,
16507
16766
  limit,
16508
16767
  detail,
16509
- partIndex
16768
+ partIndex,
16769
+ before,
16770
+ after
16510
16771
  }, context2) => {
16511
16772
  const memory = context2?.memory;
16512
- const threadId = context2?.agent?.threadId;
16773
+ const currentThreadId = context2?.agent?.threadId;
16513
16774
  const resourceId = context2?.agent?.resourceId;
16514
16775
  if (!memory) {
16515
16776
  throw new Error("Memory instance is required for recall");
16516
16777
  }
16517
- if (!threadId) {
16778
+ if (mode === "search") {
16779
+ if (!query) {
16780
+ throw new Error('query is required for mode="search"');
16781
+ }
16782
+ if (!resourceId) {
16783
+ throw new Error("Resource ID is required for recall");
16784
+ }
16785
+ return searchMessagesForResource({
16786
+ memory,
16787
+ resourceId,
16788
+ currentThreadId: currentThreadId || void 0,
16789
+ query,
16790
+ topK: limit ?? 10,
16791
+ before,
16792
+ after,
16793
+ threadScope: !isResourceScope ? currentThreadId || void 0 : void 0
16794
+ });
16795
+ }
16796
+ if (mode === "threads") {
16797
+ if (!isResourceScope) {
16798
+ if (!currentThreadId || !memory.getThreadById) {
16799
+ return { error: "Could not resolve current thread." };
16800
+ }
16801
+ const thread = await memory.getThreadById({ threadId: currentThreadId });
16802
+ if (!thread) {
16803
+ return { error: "Could not resolve current thread." };
16804
+ }
16805
+ return {
16806
+ threads: `- **${thread.title || "(untitled)"}** \u2190 current
16807
+ id: ${thread.id}
16808
+ updated: ${formatTimestamp(thread.updatedAt)} | created: ${formatTimestamp(thread.createdAt)}`,
16809
+ count: 1,
16810
+ page: 0,
16811
+ hasMore: false
16812
+ };
16813
+ }
16814
+ if (!resourceId) {
16815
+ throw new Error("Resource ID is required for recall");
16816
+ }
16817
+ return listThreadsForResource({
16818
+ memory,
16819
+ resourceId,
16820
+ currentThreadId: currentThreadId || "",
16821
+ page: page ?? 0,
16822
+ limit: limit ?? 20,
16823
+ before,
16824
+ after
16825
+ });
16826
+ }
16827
+ const hasExplicitThreadId = typeof explicitThreadId === "string" && explicitThreadId.length > 0;
16828
+ const hasCursor = typeof cursor === "string" && cursor.length > 0;
16829
+ if (!hasExplicitThreadId && !hasCursor) {
16830
+ throw new Error('Either cursor or threadId is required for mode="messages"');
16831
+ }
16832
+ let targetThreadId;
16833
+ let threadScope;
16834
+ if (!isResourceScope) {
16835
+ targetThreadId = currentThreadId;
16836
+ threadScope = currentThreadId || void 0;
16837
+ } else if (hasExplicitThreadId) {
16838
+ if (!resourceId) {
16839
+ throw new Error("Resource ID is required for recall");
16840
+ }
16841
+ if (!memory.getThreadById) {
16842
+ throw new Error("Memory instance cannot verify thread access for recall");
16843
+ }
16844
+ const thread = await memory.getThreadById({ threadId: explicitThreadId });
16845
+ if (!thread || thread.resourceId !== resourceId) {
16846
+ throw new Error("Thread does not belong to the active resource");
16847
+ }
16848
+ targetThreadId = thread.id;
16849
+ threadScope = thread.id;
16850
+ } else {
16851
+ targetThreadId = currentThreadId;
16852
+ threadScope = currentThreadId || void 0;
16853
+ }
16854
+ if (hasCursor && !hasExplicitThreadId && !currentThreadId) {
16855
+ throw new Error("Current thread is required when browsing by cursor");
16856
+ }
16857
+ if (!targetThreadId) {
16518
16858
  throw new Error("Thread ID is required for recall");
16519
16859
  }
16860
+ if (!cursor) {
16861
+ return recallThreadFromStart({
16862
+ memory,
16863
+ threadId: targetThreadId,
16864
+ resourceId: isResourceScope ? resourceId : void 0,
16865
+ page: page ?? 1,
16866
+ limit: limit ?? 20,
16867
+ detail: detail ?? "low"
16868
+ });
16869
+ }
16520
16870
  if (partIndex !== void 0 && partIndex !== null) {
16521
16871
  return recallPart({
16522
16872
  memory,
16523
- threadId,
16873
+ threadId: targetThreadId,
16874
+ resourceId: isResourceScope ? resourceId : void 0,
16524
16875
  cursor,
16525
- partIndex
16876
+ partIndex,
16877
+ threadScope
16526
16878
  });
16527
16879
  }
16528
16880
  return recallMessages({
16529
16881
  memory,
16530
- threadId,
16531
- resourceId,
16882
+ threadId: targetThreadId,
16883
+ resourceId: isResourceScope ? resourceId : void 0,
16532
16884
  cursor,
16533
16885
  page,
16534
16886
  limit,
16535
- detail: detail ?? "low"
16887
+ detail: detail ?? "low",
16888
+ threadScope
16536
16889
  });
16537
16890
  }
16538
16891
  });
@@ -16784,6 +17137,19 @@ var Memory = class extends MastraMemory {
16784
17137
  observationalMemory: config.options?.observationalMemory
16785
17138
  });
16786
17139
  this.threadConfig = mergedConfig;
17140
+ const omConfig = normalizeObservationalMemoryConfig(mergedConfig.observationalMemory);
17141
+ if (omConfig?.retrieval && typeof omConfig.retrieval === "object" && omConfig.retrieval.vector) {
17142
+ if (!this.vector) {
17143
+ throw new Error(
17144
+ "`retrieval: { vector: true }` requires a vector store. Pass a `vector` option to your Memory instance."
17145
+ );
17146
+ }
17147
+ if (!this.embedder) {
17148
+ throw new Error(
17149
+ "`retrieval: { vector: true }` requires an embedder. Pass an `embedder` option to your Memory instance."
17150
+ );
17151
+ }
17152
+ }
16787
17153
  }
16788
17154
  /**
16789
17155
  * Gets the memory storage domain, throwing if not available.
@@ -17553,13 +17919,17 @@ ${workingMemory}`;
17553
17919
  "Observational memory requires @mastra/core support for request-response-id-rotation. Please bump @mastra/core to a newer version."
17554
17920
  );
17555
17921
  }
17556
- const { ObservationalMemory: OMClass } = await import('./observational-memory-UGDENJPE-NVMIXNI4.js');
17922
+ const { ObservationalMemory: OMClass } = await import('./observational-memory-SN7GKMHZ-IWVBFBS6.js');
17923
+ const onIndexObservations = this.hasRetrievalSearch(omConfig.retrieval) ? async (observation) => {
17924
+ await this.indexObservation(observation);
17925
+ } : void 0;
17557
17926
  return new OMClass({
17558
17927
  storage: memoryStore,
17559
17928
  scope: omConfig.scope,
17560
17929
  retrieval: omConfig.retrieval,
17561
17930
  shareTokenBudget: omConfig.shareTokenBudget,
17562
17931
  model: omConfig.model,
17932
+ onIndexObservations,
17563
17933
  observation: omConfig.observation ? {
17564
17934
  model: omConfig.observation.model,
17565
17935
  messageTokens: omConfig.observation.messageTokens,
@@ -17695,6 +18065,176 @@ Notes:
17695
18065
  const isMDWorkingMemory = !(`schema` in config.workingMemory) && (typeof config.workingMemory.template === `string` || config.workingMemory.template) && config.workingMemory;
17696
18066
  return Boolean(isMDWorkingMemory && isMDWorkingMemory.version === `vnext`);
17697
18067
  }
18068
+ getObservationEmbeddingIndexName(dimensions) {
18069
+ const defaultDimensions = 384;
18070
+ const usedDimensions = dimensions ?? defaultDimensions;
18071
+ const separator = this.vector?.indexSeparator ?? "_";
18072
+ return `memory${separator}observations${separator}${usedDimensions}`;
18073
+ }
18074
+ async createObservationEmbeddingIndex(dimensions) {
18075
+ const defaultDimensions = 384;
18076
+ const usedDimensions = dimensions ?? defaultDimensions;
18077
+ const indexName = this.getObservationEmbeddingIndexName(dimensions);
18078
+ if (typeof this.vector === `undefined`) {
18079
+ throw new Error(
18080
+ `Tried to create observation embedding index but no vector db is attached to this Memory instance.`
18081
+ );
18082
+ }
18083
+ await this.vector.createIndex({
18084
+ indexName,
18085
+ dimension: usedDimensions
18086
+ });
18087
+ return { indexName };
18088
+ }
18089
+ /**
18090
+ * Search observation groups across threads by semantic similarity.
18091
+ * Requires a vector store and embedder to be configured.
18092
+ */
18093
+ async searchMessages({
18094
+ query,
18095
+ resourceId,
18096
+ topK = 10,
18097
+ filter: filter32
18098
+ }) {
18099
+ if (!this.vector) {
18100
+ throw new Error("searchMessages requires a vector store. Configure vector and embedder on your Memory instance.");
18101
+ }
18102
+ const { embeddings, dimension } = await this.embedMessageContent(query);
18103
+ const { indexName } = await this.createObservationEmbeddingIndex(dimension);
18104
+ const vectorFilter = { resource_id: resourceId };
18105
+ if (filter32?.threadId) {
18106
+ vectorFilter.thread_id = filter32.threadId;
18107
+ }
18108
+ if (filter32?.observedAfter || filter32?.observedBefore) {
18109
+ vectorFilter.observed_at = {
18110
+ ...filter32.observedAfter ? { $gt: filter32.observedAfter.toISOString() } : {},
18111
+ ...filter32.observedBefore ? { $lt: filter32.observedBefore.toISOString() } : {}
18112
+ };
18113
+ }
18114
+ const queryResults = [];
18115
+ await Promise.all(
18116
+ embeddings.map(async (embedding) => {
18117
+ const results2 = await this.vector.query({
18118
+ indexName,
18119
+ queryVector: embedding,
18120
+ topK,
18121
+ filter: vectorFilter
18122
+ });
18123
+ for (const r of results2) {
18124
+ if (!r.metadata?.thread_id) {
18125
+ continue;
18126
+ }
18127
+ const groupId = typeof r.metadata.group_id === "string" ? r.metadata.group_id : void 0;
18128
+ if (!groupId) {
18129
+ continue;
18130
+ }
18131
+ queryResults.push({
18132
+ threadId: r.metadata.thread_id,
18133
+ score: r.score,
18134
+ groupId,
18135
+ range: typeof r.metadata.range === "string" ? r.metadata.range : void 0,
18136
+ text: typeof r.metadata.text === "string" ? r.metadata.text : void 0,
18137
+ observedAt: typeof r.metadata.observed_at === "string" || r.metadata.observed_at instanceof Date ? new Date(r.metadata.observed_at) : void 0
18138
+ });
18139
+ }
18140
+ })
18141
+ );
18142
+ const bestByGroup = /* @__PURE__ */ new Map();
18143
+ for (const result of queryResults) {
18144
+ if (!result.groupId) {
18145
+ continue;
18146
+ }
18147
+ const existing = bestByGroup.get(result.groupId);
18148
+ if (!existing || result.score > existing.score) {
18149
+ bestByGroup.set(result.groupId, result);
18150
+ }
18151
+ }
18152
+ const results = [...bestByGroup.values()].sort((a, b) => b.score - a.score);
18153
+ return { results };
18154
+ }
18155
+ /**
18156
+ * Index a single observation group into the observation vector store.
18157
+ */
18158
+ async indexObservation({
18159
+ text: text42,
18160
+ groupId,
18161
+ range,
18162
+ threadId,
18163
+ resourceId,
18164
+ observedAt
18165
+ }) {
18166
+ if (!this.vector || !this.embedder) return;
18167
+ const embedResult = await this.embedMessageContent(text42);
18168
+ if (embedResult.embeddings.length === 0 || embedResult.dimension === void 0) {
18169
+ return;
18170
+ }
18171
+ const { indexName } = await this.createObservationEmbeddingIndex(embedResult.dimension);
18172
+ await this.vector.upsert({
18173
+ indexName,
18174
+ vectors: embedResult.embeddings,
18175
+ metadata: embedResult.chunks.map((chunk) => ({
18176
+ group_id: groupId,
18177
+ range,
18178
+ thread_id: threadId,
18179
+ resource_id: resourceId,
18180
+ observed_at: observedAt?.toISOString(),
18181
+ text: chunk
18182
+ }))
18183
+ });
18184
+ }
18185
+ /**
18186
+ * Index a list of messages directly (without querying storage).
18187
+ * Used by observe-time indexing to vectorize newly-observed messages.
18188
+ */
18189
+ async indexMessagesList(messages) {
18190
+ if (!this.vector || !this.embedder) return;
18191
+ const embeddingData = [];
18192
+ let dimension;
18193
+ await Promise.all(
18194
+ messages.map(async (message) => {
18195
+ let textForEmbedding = null;
18196
+ if (message.content.content && typeof message.content.content === "string" && message.content.content.trim() !== "") {
18197
+ textForEmbedding = message.content.content;
18198
+ } else if (message.content.parts && message.content.parts.length > 0) {
18199
+ const joined = message.content.parts.filter((part) => part.type === "text").map((part) => part.text).join(" ").trim();
18200
+ if (joined) textForEmbedding = joined;
18201
+ }
18202
+ if (!textForEmbedding) return;
18203
+ const embedResult = await this.embedMessageContent(textForEmbedding);
18204
+ dimension = embedResult.dimension;
18205
+ embeddingData.push({
18206
+ embeddings: embedResult.embeddings,
18207
+ metadata: embedResult.chunks.map(() => ({
18208
+ message_id: message.id,
18209
+ thread_id: message.threadId,
18210
+ resource_id: message.resourceId
18211
+ }))
18212
+ });
18213
+ })
18214
+ );
18215
+ if (embeddingData.length > 0 && dimension !== void 0) {
18216
+ const { indexName } = await this.createEmbeddingIndex(dimension);
18217
+ const allVectors = [];
18218
+ const allMetadata = [];
18219
+ for (const data of embeddingData) {
18220
+ allVectors.push(...data.embeddings);
18221
+ allMetadata.push(...data.metadata);
18222
+ }
18223
+ await this.vector.upsert({
18224
+ indexName,
18225
+ vectors: allVectors,
18226
+ metadata: allMetadata
18227
+ });
18228
+ }
18229
+ }
18230
+ /**
18231
+ * Check whether retrieval search (vector-based) is enabled.
18232
+ * Returns true when `retrieval: { vector: true }` and Memory has vector + embedder configured.
18233
+ */
18234
+ hasRetrievalSearch(retrieval) {
18235
+ if (!retrieval || retrieval === true) return false;
18236
+ return !!retrieval.vector && !!this.vector && !!this.embedder;
18237
+ }
17698
18238
  listTools(config) {
17699
18239
  const mergedConfig = this.getMergedThreadConfig(config);
17700
18240
  const tools = {};
@@ -17702,8 +18242,9 @@ Notes:
17702
18242
  tools.updateWorkingMemory = this.isVNextWorkingMemoryConfig(mergedConfig) ? __experimental_updateWorkingMemoryToolVNext(mergedConfig) : updateWorkingMemoryTool(mergedConfig);
17703
18243
  }
17704
18244
  const omConfig = normalizeObservationalMemoryConfig(mergedConfig.observationalMemory);
17705
- if (omConfig?.retrieval && (omConfig.scope ?? "thread") === "thread") {
17706
- tools.recall = recallTool();
18245
+ if (omConfig?.retrieval) {
18246
+ const retrievalScope = typeof omConfig.retrieval === "object" ? omConfig.retrieval.scope ?? "resource" : "resource";
18247
+ tools.recall = recallTool(mergedConfig, { retrievalScope });
17707
18248
  }
17708
18249
  return tools;
17709
18250
  }
@@ -18266,7 +18807,7 @@ Notes:
18266
18807
  if (!effectiveConfig) return null;
18267
18808
  const engine = await this.omEngine;
18268
18809
  if (!engine) return null;
18269
- const { ObservationalMemoryProcessor } = await import('./observational-memory-UGDENJPE-NVMIXNI4.js');
18810
+ const { ObservationalMemoryProcessor } = await import('./observational-memory-SN7GKMHZ-IWVBFBS6.js');
18270
18811
  return new ObservationalMemoryProcessor(engine, this);
18271
18812
  }
18272
18813
  };
@@ -29467,5 +30008,5 @@ var OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE = createRoute({
29467
30008
  });
29468
30009
 
29469
30010
  export { CANCEL_AGENT_BUILDER_ACTION_RUN_ROUTE, CREATE_AGENT_BUILDER_ACTION_RUN_ROUTE, GET_AGENT_BUILDER_ACTION_BY_ID_ROUTE, GET_AGENT_BUILDER_ACTION_RUN_BY_ID_ROUTE, LIST_AGENT_BUILDER_ACTIONS_ROUTE, LIST_AGENT_BUILDER_ACTION_RUNS_ROUTE, OBSERVE_STREAM_AGENT_BUILDER_ACTION_ROUTE, OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE, RESUME_AGENT_BUILDER_ACTION_ROUTE, RESUME_ASYNC_AGENT_BUILDER_ACTION_ROUTE, RESUME_STREAM_AGENT_BUILDER_ACTION_ROUTE, START_AGENT_BUILDER_ACTION_RUN_ROUTE, START_ASYNC_AGENT_BUILDER_ACTION_ROUTE, STREAM_AGENT_BUILDER_ACTION_ROUTE, STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE, agent_builder_exports };
29470
- //# sourceMappingURL=chunk-FGZC4JP7.js.map
29471
- //# sourceMappingURL=chunk-FGZC4JP7.js.map
30011
+ //# sourceMappingURL=chunk-SDKSW2BQ.js.map
30012
+ //# sourceMappingURL=chunk-SDKSW2BQ.js.map