Skip to content

Commit 5573693

Browse files
authored
fix(core): respect default memory options in tool discovery (#17175)
## Summary This fixes a user-visible logging issue where supervisor runs could emit `Skipping memory tools (no thread or resource context)` while preparing sub-agent tools. The sub-agent could be correctly configured with memory through dynamic `defaultOptions`, but the supervisor background eligibility probe calls `Agent.getToolsForExecution()` directly, bypassing the normal `stream()` / `generate()` default option merge. This PR updates `getToolsForExecution()` to resolve and merge the agent's `defaultOptions` before converting tools. That makes memory-backed tools discoverable when an agent derives its memory thread/resource from `requestContext`, matching the path used by normal agent execution. ## Alternative considered Another option was to skip or suppress memory tool discovery specifically during the background eligibility probe. That would reduce the log noise, but it would also make the probe less representative of the sub-agent's actual tool surface. Merging the relevant defaults keeps tool discovery aligned with real execution behavior while preserving explicit per-call thread/resource overrides and reserved request-context keys. ## Validation - `pnpm test packages/core/src/agent/__tests__/working-memory-context.test.ts` - `pnpm --filter ./packages/core check` CodeRabbit CLI is installed locally, but `coderabbit doctor` reports it is not signed in, so I could not run a local CodeRabbit review without an auth step. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## ELI5 When the supervisor peeked at what tools a sub-agent could use, it sometimes missed memory tools that should have been available. This change makes the probe use the same defaults-merging logic as normal runs, so it sees memory tools correctly. ## Summary Fixes incorrect debug logs ("Skipping memory tools (no thread or resource context)") emitted during supervisor background eligibility probes by making tool discovery respect an agent's dynamic defaultOptions. ### Root Cause The supervisor probe called Agent.getToolsForExecution() directly, bypassing the normal stream()/generate() merge of dynamic defaultOptions. Sub-agents can derive memory thread/resource from defaultOptions (e.g., via requestContext), so the probe could incorrectly consider memory tools unavailable. ### Solution Agent.getToolsForExecution() now: - Resolves the agent's dynamic defaultOptions for the provided requestContext. - Deep-merges those defaults with call-site options. - Resolves threadId and resourceId using merged defaults while preserving explicit per-call overrides and reserved request-context keys. - Forwards merged runId, memoryConfig, and autoResumeSuspendedTools into convertTools() so memory-backed tools are discoverable during probe discovery. ### Changes - .changeset/dull-comics-tease.md — bump/notes for `@mastra/core` patch release - packages/core/src/agent/__tests__/working-memory-context.test.ts — adds test asserting defaultOptions can supply working-memory context when assembling tools - packages/core/src/agent/agent.ts — update getToolsForExecution() to resolve & merge defaultOptions before converting tools ### Validation - Ran tests: pnpm test packages/core/src/agent/__tests__/working-memory-context.test.ts - Ran type/check: pnpm --filter ./packages/core check - Note: CodeRabbit review not run locally due to auth state <!-- review_stack_entry_start --> [![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/17175?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent dd6a66e commit 5573693

3 files changed

Lines changed: 61 additions & 1 deletion

File tree

.changeset/dull-comics-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@mastra/core': patch
3+
---
4+
5+
Fixed supervisor sub-agent tool preparation so memory tools are available when agents derive their thread and resource from default options. This prevents misleading "Skipping memory tools (no thread or resource context)" debug logs for correctly configured sub-agents.

packages/core/src/agent/__tests__/working-memory-context.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { convertArrayToReadableStream, MockLanguageModelV2 } from '@internal/ai-
22
import { describe, expect, it } from 'vitest';
33
import { z } from 'zod/v4';
44
import { MockMemory } from '../../memory/mock';
5+
import { RequestContext } from '../../request-context';
56
import type { ChunkType } from '../../stream/types';
67
import { createTool, Tool } from '../../tools';
78
import { createWorkflow, createStep } from '../../workflows';
@@ -327,6 +328,35 @@ describe('Working memory tool context propagation', () => {
327328
expect(toolNames).not.toContain('updateWorkingMemory');
328329
});
329330

331+
it('should use defaultOptions memory context when assembling tools for execution', async () => {
332+
const requestContext = new RequestContext();
333+
requestContext.set('testThreadId', 'default-thread');
334+
requestContext.set('testResourceId', 'default-resource');
335+
336+
const mockMemory = new MockMemory({
337+
enableWorkingMemory: true,
338+
workingMemoryTemplate: `# Notes\n- **Key**:\n- **Value**:\n`,
339+
});
340+
341+
const agent = new Agent({
342+
id: 'default-tools-memory-test',
343+
name: 'Default Tools Memory Test',
344+
instructions: 'You are a helpful agent.',
345+
model: createSimpleMockModel(),
346+
memory: mockMemory,
347+
defaultOptions: ({ requestContext }) => ({
348+
memory: {
349+
thread: requestContext.get('testThreadId') as string,
350+
resource: requestContext.get('testResourceId') as string,
351+
},
352+
}),
353+
});
354+
355+
const tools = await agent.getToolsForExecution({ requestContext });
356+
357+
expect(Object.keys(tools)).toContain('updateWorkingMemory');
358+
});
359+
330360
it('should provide memory context when updateWorkingMemory is called inside a workflow step', async () => {
331361
const mockModel = createMockModelWithWorkingMemoryToolCall();
332362
const mockMemory = new MockMemory({

packages/core/src/agent/agent.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5277,9 +5277,34 @@ export class Agent<
52775277
hooks?: ToolHooks;
52785278
}): Promise<Record<string, CoreTool>> {
52795279
const requestContext = options.requestContext ?? new RequestContext();
5280+
const defaultOptions = await this.getDefaultOptions({ requestContext });
5281+
const mergedOptions = deepMerge(
5282+
defaultOptions as Record<string, unknown>,
5283+
{ ...options, requestContext } as Record<string, unknown>,
5284+
) as AgentExecutionOptions & typeof options;
5285+
const optionMemory = (options as { memory?: AgentExecutionOptionsBase<any>['memory'] }).memory;
5286+
const mergedMemory = mergedOptions.memory;
5287+
const threadIdFromContext = requestContext.get(MASTRA_THREAD_ID_KEY) as string | undefined;
5288+
const explicitThreadFromArgs = resolveThreadIdFromArgs({
5289+
memory: optionMemory,
5290+
threadId: options.threadId,
5291+
overrideId: threadIdFromContext,
5292+
});
5293+
const defaultThreadFromArgs = resolveThreadIdFromArgs({
5294+
memory: mergedMemory,
5295+
overrideId: threadIdFromContext,
5296+
});
5297+
const resourceIdFromContext = requestContext.get(MASTRA_RESOURCE_ID_KEY) as string | undefined;
5298+
52805299
return this.convertTools({
5281-
...options,
5300+
toolsets: mergedOptions.toolsets,
5301+
clientTools: mergedOptions.clientTools,
5302+
threadId: explicitThreadFromArgs?.id ?? defaultThreadFromArgs?.id,
5303+
resourceId: resourceIdFromContext || options.resourceId || optionMemory?.resource || mergedMemory?.resource,
5304+
runId: mergedOptions.runId,
52825305
requestContext,
5306+
memoryConfig: options.memoryConfig ?? mergedMemory?.options,
5307+
autoResumeSuspendedTools: mergedOptions.autoResumeSuspendedTools,
52835308
methodType: 'stream',
52845309
});
52855310
}

0 commit comments

Comments
 (0)