@mastra/code-sdk 1.14.0-alpha.1 → 1.14.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.
@@ -4,11 +4,13 @@ import { isThinkingLevelSetting } from "../thinking.js";
4
4
  import { ProviderAuthRequiredError } from "../auth/provider-auth-error.js";
5
5
  import { toMemoryModelId } from "../onboarding/packs.js";
6
6
  import { loadSettings, resolveDefaultThinkingLevel, stripMastraCodeCustomProviderPrefix } from "../onboarding/settings.js";
7
- import { hasCredentialStoreProvider, resolveCredentialStore, resolveTenantFromRequestContext, setCredentialStoreProvider } from "./credential-resolver.js";
7
+ import { hasCredentialStoreProvider, hasCredentialTenantResolver, prepareCredentialTenant, resolveCredentialScope, resolveCredentialStore, resolveTenantFromRequestContext, setCredentialStoreProvider } from "./credential-resolver.js";
8
8
  import { getRequestAccountSelection, isRequestAccountRoutingExhausted } from "../auth/account-routing-context.js";
9
9
  import { AMAZON_BEDROCK_GATEWAY_ID, createAmazonBedrockGateway } from "../providers/amazon-bedrock-gateway.js";
10
10
  import { hasCustomProvidersSource, resolveCustomProviders, setCustomProvidersSource } from "./custom-provider-source.js";
11
11
  import { MASTRACODE_GATEWAY_ID, MastraCodeGateway, getAnthropicApiKey, getGlobalAuthStorage, getOpenAIApiKey, reloadAuthStorage, resolveAuth } from "./mastracode-gateway.js";
12
+ import { ModelRouterLanguageModel } from "@mastra/core/llm";
13
+ import { RequestContext } from "@mastra/core/request-context";
12
14
  //#region src/agents/model.ts
13
15
  function getAgentControllerHeaders(requestContext) {
14
16
  const agentControllerContext = requestContext?.get("controller");
@@ -78,6 +80,38 @@ function resolveModelId(modelId) {
78
80
  * - For all other providers: Uses Mastra's model router (models.dev gateway)
79
81
  */
80
82
  function resolveModel(modelId, options) {
83
+ if (!hasCredentialTenantResolver()) return resolveModelForRequest(modelId, options);
84
+ const createRequestModel = () => {
85
+ const requestContext = options?.requestContext ? new RequestContext(options.requestContext.entries()) : void 0;
86
+ return new ModelRouterLanguageModel({ id: `${MASTRACODE_GATEWAY_ID}/${modelId}` }, [{
87
+ id: MASTRACODE_GATEWAY_ID,
88
+ name: "Request-scoped MastraCode",
89
+ fetchProviders: async () => ({}),
90
+ buildUrl: () => void 0,
91
+ getApiKey: async () => "",
92
+ resolveAuth: () => ({ apiKey: "request-scoped" }),
93
+ resolveLanguageModel: async () => {
94
+ await prepareCredentialTenant(requestContext);
95
+ return resolveModelForRequest(modelId, {
96
+ ...options,
97
+ requestContext
98
+ });
99
+ }
100
+ }]);
101
+ };
102
+ let currentModel = createRequestModel();
103
+ return new Proxy(currentModel, { get(_target, property) {
104
+ if (property === "supportedUrls") return createRequestModel().supportedUrls;
105
+ if (property === "doStream" || property === "doGenerate") return async (...args) => {
106
+ const refreshed = createRequestModel();
107
+ currentModel = refreshed;
108
+ return Reflect.apply(Reflect.get(refreshed, property), refreshed, args);
109
+ };
110
+ const value = Reflect.get(currentModel, property, currentModel);
111
+ return typeof value === "function" ? value.bind(currentModel) : value;
112
+ } });
113
+ }
114
+ function resolveModelForRequest(modelId, options) {
81
115
  reloadAuthStorage();
82
116
  const headers = getAgentControllerHeaders(options?.requestContext);
83
117
  const settings = loadSettings();
@@ -127,7 +161,7 @@ function resolveModel(modelId, options) {
127
161
  modelId: bareModelId,
128
162
  routerId
129
163
  });
130
- if (!auth && credentialStore?.allowEnvironmentFallback === false) throw new ProviderAuthRequiredError(`No usable ${providerId} credential is configured for this signed-in Factory account. Connect the provider or add an organization credential, then try again.`);
164
+ if (!auth && credentialStore?.allowEnvironmentFallback === false) throw new ProviderAuthRequiredError(resolveCredentialScope(options?.requestContext) === "factory" ? `No usable ${providerId} credential is available. This Factory uses organization credentials only, so personal provider sign-ins don't apply. Add an organization credential, then try again.` : `No usable ${providerId} credential is available. This Factory uses personal credentials only, so organization credentials don't apply. Connect the provider, then try again.`);
131
165
  return gateway.resolveLanguageModel({
132
166
  providerId,
133
167
  modelId: bareModelId,
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","names":[],"sources":["../../src/agents/model.ts"],"sourcesContent":["import type { ModelWithRetries } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { GatewayLanguageModel, IdentifiedModelConfig, MastraModelGatewayInterface } from '@mastra/core/llm';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { getRequestAccountSelection, isRequestAccountRoutingExhausted } from '../auth/account-routing-context.js';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport type { CredentialStore, OAuthAccountRecord } from '../auth/types.js';\nimport { MODEL_ROUTE_MAX_ENTRIES } from '../constants.js';\nimport { toMemoryModelId } from '../onboarding/packs.js';\nimport {\n loadSettings,\n resolveDefaultThinkingLevel,\n stripMastraCodeCustomProviderPrefix,\n} from '../onboarding/settings.js';\nimport { AMAZON_BEDROCK_GATEWAY_ID, createAmazonBedrockGateway } from '../providers/amazon-bedrock-gateway.js';\nimport type { AnthropicPromptCacheScope } from '../providers/anthropic-prompt-cache.js';\nimport { normalizeLegacyModelId } from '../providers/model-ids.js';\nimport { isThinkingLevelSetting } from '../thinking.js';\nimport type { ThinkingLevelSetting } from '../thinking.js';\nimport { resolveCredentialStore } from './credential-resolver.js';\nimport { resolveCustomProviders } from './custom-provider-source.js';\nimport {\n MASTRA_GATEWAY_PREFIX,\n MASTRACODE_GATEWAY_ID,\n MastraCodeGateway,\n getGlobalAuthStorage,\n reloadAuthStorage,\n stripMastraGatewayPrefix,\n} from './mastracode-gateway.js';\nimport type { MastraCodeGatewayOptions } from './mastracode-gateway.js';\n\nexport {\n getAnthropicApiKey,\n getOpenAIApiKey,\n MASTRACODE_GATEWAY_ID,\n MastraCodeGateway,\n remapOpenAIModelForCodexOAuth,\n resolveAuth,\n} from './mastracode-gateway.js';\nexport type { MastraCodeCustomProvider, MastraCodeGatewayOptions } from './mastracode-gateway.js';\nexport {\n setCredentialStoreProvider,\n hasCredentialStoreProvider,\n resolveTenantFromRequestContext,\n} from './credential-resolver.js';\nexport type { CredentialTenant, CredentialStoreProvider } from './credential-resolver.js';\nexport {\n setCustomProvidersSource,\n hasCustomProvidersSource,\n resolveCustomProviders,\n} from './custom-provider-source.js';\nexport type { CustomProvidersSource } from './custom-provider-source.js';\n\ntype ResolvedModel = GatewayLanguageModel;\ntype ModelRequestHeaders = Record<string, string>;\n\nfunction getAgentControllerHeaders(requestContext?: RequestContext): ModelRequestHeaders | undefined {\n const agentControllerContext = requestContext?.get('controller') as AgentControllerRequestContext<any> | undefined;\n const headers = {\n ...(agentControllerContext?.threadId ? { 'x-thread-id': agentControllerContext.threadId } : {}),\n ...(agentControllerContext?.resourceId ? { 'x-resource-id': agentControllerContext.resourceId } : {}),\n };\n\n return Object.keys(headers).length > 0 ? headers : undefined;\n}\n\nfunction accountCredential(account: OAuthAccountRecord) {\n const { type: _type, id: _id, label: _label, addedAt: _addedAt, active: _active, ...credential } = account;\n return { type: 'oauth' as const, ...credential };\n}\n\nexport function createRequestScopedCredentialStore(\n base: CredentialStore,\n requestContext?: RequestContext,\n): CredentialStore {\n const selectedId = (providerId: string) => getRequestAccountSelection(requestContext, providerId);\n const selectedAccount = (providerId: string) => {\n const accountInstanceId = selectedId(providerId);\n return accountInstanceId\n ? base.listAccounts?.(providerId).find(account => account.id === accountInstanceId)\n : undefined;\n };\n // Routing rejected every account for this provider on this request. Fail\n // closed on every credential read: falling through to `base` would use the\n // provider's active account — the exhausted one routing just refused.\n const rejectAll = (providerId: string) => isRequestAccountRoutingExhausted(requestContext, providerId);\n\n return {\n allowEnvironmentFallback: base.allowEnvironmentFallback,\n reload: () => base.reload(),\n get: providerId => {\n if (rejectAll(providerId)) return undefined;\n // A selection that no longer resolves (the account was removed between\n // routing and the credential read) must not fall through to the active\n // account — that is the account routing deliberately passed over.\n const accountInstanceId = selectedId(providerId);\n if (accountInstanceId === undefined) return base.get(providerId);\n const selected = selectedAccount(providerId);\n return selected ? accountCredential(selected) : undefined;\n },\n getStoredApiKey: providerId => {\n if (rejectAll(providerId)) return undefined;\n // The provider-wide `apikey:` slot is never the credential routing\n // selected, so serving it to a routed request is an OAuth -> API-key\n // fallback of the same provider — the one combination this feature\n // forbids. Unlike `get`, this read has no account argument to pass\n // through, so a selection fails it closed instead.\n if (selectedId(providerId) !== undefined) return undefined;\n return base.getStoredApiKey(providerId);\n },\n getApiKey: providerId =>\n rejectAll(providerId) ? Promise.resolve(undefined) : base.getApiKey(providerId, selectedId(providerId)),\n getOAuthCredential: base.getOAuthCredential\n ? providerId =>\n rejectAll(providerId)\n ? Promise.resolve(undefined)\n : base.getOAuthCredential!(providerId, selectedId(providerId))\n : undefined,\n listAccounts: base.listAccounts ? providerId => base.listAccounts!(providerId) : undefined,\n getActiveAccount: base.getActiveAccount ? providerId => base.getActiveAccount!(providerId) : undefined,\n activateAccount: base.activateAccount\n ? (providerId, accountInstanceId) => base.activateAccount!(providerId, accountInstanceId)\n : undefined,\n removeAccount: base.removeAccount\n ? (providerId, accountInstanceId) => base.removeAccount!(providerId, accountInstanceId)\n : undefined,\n };\n}\n\nexport function createMastraCodeGateway(options: MastraCodeGatewayOptions): MastraCodeGateway {\n return new MastraCodeGateway(options);\n}\n\nexport function createMastraCodeModelCatalogProvider(gateway: MastraModelGatewayInterface) {\n return gateway instanceof MastraCodeGateway\n ? gateway.createModelCatalogProvider()\n : MastraCodeGateway.createModelCatalogProvider(gateway);\n}\n\n/**\n * Placeholder for future model ID normalization.\n * Currently returns the input unchanged, but exists as a seam\n * for aliasing, casing fixes, or validation in the future.\n */\nexport function resolveModelId(modelId: string): string {\n return modelId;\n}\n\n/**\n * Resolve a model ID to the correct provider instance.\n * Shared by the main agent, observer, and reflector.\n *\n * - For anthropic/* models: Uses stored OAuth credentials when present, otherwise direct API key\n * - For openai/* models: Uses OAuth when configured, otherwise direct API key from AuthStorage\n * - For moonshotai/* models: Uses Moonshot AI Anthropic-compatible endpoint\n * - For all other providers: Uses Mastra's model router (models.dev gateway)\n */\nexport function resolveModel(\n modelId: string,\n options?: {\n thinkingLevel?: ThinkingLevelSetting;\n remapForCodexOAuth?: boolean;\n requestContext?: RequestContext;\n anthropicPromptCacheScope?: AnthropicPromptCacheScope;\n },\n): GatewayLanguageModel {\n reloadAuthStorage();\n const headers = getAgentControllerHeaders(options?.requestContext);\n const settings = loadSettings();\n // Older versions cataloged Bedrock, Copilot and Kimi under the MastraCode\n // gateway namespace, and Kimi under its own `kimi-for-coding` provider. Map\n // already-saved ids (packs, OM settings, thread metadata) to current ids.\n const legacyNormalizedInput = normalizeLegacyModelId(modelId);\n // Deployed web registers a custom providers source (DB-backed, tenant\n // scoped); when registered it is authoritative and settings.json custom\n // providers are ignored. Undefined = local settings-based behavior.\n const customProviders = resolveCustomProviders(options?.requestContext) ?? settings.customProviders;\n // Ids selected from the shared /models catalog were previously persisted in\n // the gateway-qualified `mastracode/<customProviderId>/<model>` form, which\n // parses the provider as `mastracode` and breaks provider config lookup.\n // Normalize at resolution time (in addition to stripping at selection time)\n // so already-saved ids and any surface that persists the raw catalog id\n // still resolve to the custom provider.\n const normalizedInput = stripMastraCodeCustomProviderPrefix(legacyNormalizedInput, customProviders);\n const isMastraGatewayModel = normalizedInput.startsWith(MASTRA_GATEWAY_PREFIX);\n const normalizedModelId = stripMastraGatewayPrefix(normalizedInput);\n const [providerId, ...modelParts] = normalizedModelId.split('/');\n const bareModelId = modelParts.join('/');\n if (!providerId || !bareModelId) {\n throw new Error(`Invalid model id: ${modelId}`);\n }\n\n // Deployed web registers a per-tenant credential store provider; when the\n // request carries an authenticated tenant, resolve credentials through the\n // caller's own store (user > org > env). Undefined = global AuthStorage.\n const tenantCredentialStore = resolveCredentialStore(options?.requestContext);\n const baseCredentialStore = tenantCredentialStore ?? getGlobalAuthStorage();\n\n if (providerId === AMAZON_BEDROCK_GATEWAY_ID) {\n // Bedrock authenticates through the server process's AWS credential chain,\n // never a tenant credential. A tenant store only lets it through when the\n // operator opted the deployment in.\n if (\n tenantCredentialStore?.allowEnvironmentFallback === false &&\n !tenantCredentialStore.allowsDeploymentCredentials?.(AMAZON_BEDROCK_GATEWAY_ID)\n ) {\n throw new ProviderAuthRequiredError(\n 'Amazon Bedrock is not enabled for this Factory deployment. The operator must provide AWS credentials to the server and set FACTORY_DEPLOYMENT_MODEL_PROVIDERS=amazon-bedrock.',\n );\n }\n const bedrockGateway = createAmazonBedrockGateway();\n const routerId = `${AMAZON_BEDROCK_GATEWAY_ID}/${bareModelId}`;\n const auth = bedrockGateway.resolveAuth({\n gatewayId: AMAZON_BEDROCK_GATEWAY_ID,\n providerId: AMAZON_BEDROCK_GATEWAY_ID,\n modelId: bareModelId,\n routerId,\n });\n return bedrockGateway.resolveLanguageModel({\n providerId: AMAZON_BEDROCK_GATEWAY_ID,\n modelId: bareModelId,\n apiKey: auth?.apiKey ?? '',\n headers,\n });\n }\n\n const routerId = `${MASTRACODE_GATEWAY_ID}/${normalizedModelId}`;\n\n const mgApiKey = MastraCodeGateway.getMastraGatewayApiKey();\n const rawGatewayBase =\n settings.memoryGateway?.baseUrl ?? process.env['MASTRA_GATEWAY_URL'] ?? 'https://gateway-api.mastra.ai';\n const credentialStore = createRequestScopedCredentialStore(baseCredentialStore, options?.requestContext);\n const gateway = createMastraCodeGateway({\n mastraGatewayBaseUrl: rawGatewayBase.replace(/\\/+$/, '').replace(/\\/v1$/, ''),\n mastraGatewayApiKey: mgApiKey,\n routeThroughMastraGateway: Boolean(mgApiKey && isMastraGatewayModel),\n thinkingLevel: options?.thinkingLevel,\n anthropicPromptCacheScope: options?.anthropicPromptCacheScope,\n customProviders,\n credentialStore,\n });\n\n const auth = gateway.resolveAuth({\n gatewayId: MASTRACODE_GATEWAY_ID,\n providerId,\n modelId: bareModelId,\n routerId,\n });\n\n if (!auth && credentialStore?.allowEnvironmentFallback === false) {\n throw new ProviderAuthRequiredError(\n `No usable ${providerId} credential is configured for this signed-in Factory account. Connect the provider or add an organization credential, then try again.`,\n );\n }\n\n return gateway.resolveLanguageModel({\n providerId,\n modelId: bareModelId,\n apiKey: auth?.apiKey ?? '',\n headers,\n });\n}\n\nexport interface ThinkingRequestContext {\n state?: { thinkingLevel?: unknown };\n session?: { modeId?: string };\n}\n/**\n * Resolve the effective thinking level for the current request.\n *\n * Precedence:\n * 1. Session override (`state.thinkingLevel`, set via /think or the session\n * settings panel).\n * 2. Per-mode default from settings (`models.modeThinkingDefaults[mode]`),\n * only when the context carries a session mode.\n * 3. Global default (`preferences.thinkingLevel`).\n *\n * Resolved per-request (not seeded at session start) so configuration changes\n * apply to the next request of every session — including automated\n * (rule-driven) Factory runs that nobody ever opens interactively.\n */\nexport function resolveRequestThinkingLevel(\n agentControllerContext: ThinkingRequestContext | undefined,\n settingsPath?: string,\n): ThinkingLevelSetting {\n const override = agentControllerContext?.state?.thinkingLevel;\n if (isThinkingLevelSetting(override)) return override;\n const modeId = agentControllerContext?.session?.modeId;\n return resolveDefaultThinkingLevel(loadSettings(settingsPath), modeId).level;\n}\n\n/**\n * Dynamic model function that reads the current model and optional ordered\n * fallback route from controller state. Route entry ids are opaque host-owned\n * identifiers; repeated ids receive occurrence suffixes because core tracks the\n * active fallback index by id across agentic steps.\n */\nexport function getDynamicModel(\n { requestContext }: { requestContext: RequestContext },\n settingsPath?: string,\n { modeless = false }: { modeless?: boolean } = {},\n): IdentifiedModelConfig | ModelWithRetries[] {\n const controller = requestContext.get('controller') as AgentControllerRequestContext<any> | undefined;\n const state = controller?.getState?.() as\n | {\n modelRoute?: {\n entries?: Array<{ id?: unknown; modelId?: unknown }>;\n };\n mastracodePendingModelFallback?: { toEntryId?: unknown; toModelId?: unknown; threadId?: unknown } | null;\n }\n | undefined;\n const pendingState = state?.mastracodePendingModelFallback;\n const pendingFallback =\n pendingState &&\n (pendingState.threadId === undefined ||\n (typeof pendingState.threadId === 'string' && pendingState.threadId === controller?.threadId))\n ? pendingState\n : undefined;\n const pendingModelId =\n pendingFallback && typeof pendingFallback.toModelId === 'string' && pendingFallback.toModelId.length > 0\n ? pendingFallback.toModelId\n : undefined;\n const modelId = pendingModelId ?? controller?.session?.modelId;\n if (!modelId) {\n if (!controller) {\n throw new Error(\n 'No model available: this run started without a controller session context, so no model selection could be resolved.',\n );\n }\n throw new Error('No model selected. Use /models to select a model first.');\n }\n\n const thinkingLevel = resolveRequestThinkingLevel(modeless ? { state: controller?.state } : controller, settingsPath);\n const resolveOptions = { thinkingLevel, remapForCodexOAuth: true, requestContext } as const;\n // Label each resolved model with its full ID: the resolved gateway model only\n // knows its bare provider model, and Memory's `'auto'` reads this label.\n const primary = identifyModel(modelId, resolveModel(modelId, resolveOptions));\n const route = state?.modelRoute?.entries?.slice(0, MODEL_ROUTE_MAX_ENTRIES);\n const pendingEntryId =\n pendingFallback && typeof pendingFallback.toEntryId === 'string' ? pendingFallback.toEntryId : undefined;\n const startIndex = pendingEntryId ? route?.findIndex(entry => entry.id === pendingEntryId) : 0;\n const activeRoute = route && startIndex !== undefined && startIndex >= 0 ? route.slice(startIndex) : undefined;\n if (!activeRoute || activeRoute.length < 2 || activeRoute[0]?.modelId !== modelId) return primary;\n\n const firstId =\n typeof activeRoute[0]?.id === 'string' && activeRoute[0].id.length > 0 ? activeRoute[0].id : undefined;\n if (!firstId) return primary;\n\n const entries: ModelWithRetries[] = [{ id: firstId, model: primary }];\n const appearances = new Map<string, number>([[firstId, 1]]);\n for (const routeEntry of activeRoute.slice(1)) {\n if (\n typeof routeEntry.id !== 'string' ||\n routeEntry.id.length === 0 ||\n typeof routeEntry.modelId !== 'string' ||\n routeEntry.modelId.length === 0\n ) {\n break;\n }\n let entryModel: ResolvedModel;\n try {\n entryModel = resolveModel(routeEntry.modelId, resolveOptions);\n } catch {\n break;\n }\n const occurrence = (appearances.get(routeEntry.id) ?? 0) + 1;\n appearances.set(routeEntry.id, occurrence);\n entries.push({\n id: occurrence === 1 ? routeEntry.id : `${routeEntry.id}#${occurrence}`,\n model: identifyModel(routeEntry.modelId, entryModel),\n });\n }\n\n return entries.length < 2 ? primary : entries;\n}\n\nfunction identifyModel(modelId: string, model: ResolvedModel): IdentifiedModelConfig {\n return { model, id: toMemoryModelId(modelId) };\n}\n\ntype MemoryRouteState = {\n modelRoute?: { entries?: Array<{ id?: unknown; memoryModelId?: unknown }> };\n mastracodePendingModelFallback?: { toEntryId?: unknown; threadId?: unknown } | null;\n};\n\n/**\n * The active model route for memory: the session route, starting at a pending\n * fallback hop captured for this thread.\n */\nexport function getActiveMemoryRoute<TEntry extends { id?: unknown; memoryModelId?: unknown }>(\n state:\n | {\n modelRoute?: { entries?: TEntry[] };\n mastracodePendingModelFallback?: MemoryRouteState['mastracodePendingModelFallback'];\n }\n | undefined,\n threadId?: string | null,\n): TEntry[] {\n const pending = state?.mastracodePendingModelFallback;\n const sameThreadPending =\n pending && (pending.threadId === undefined || pending.threadId === threadId) ? pending : undefined;\n const routeEntries = state?.modelRoute?.entries?.slice(0, MODEL_ROUTE_MAX_ENTRIES) ?? [];\n const pendingIndex = sameThreadPending\n ? routeEntries.findIndex(entry => entry.id === sameThreadPending.toEntryId)\n : -1;\n return pendingIndex >= 0 ? routeEntries.slice(pendingIndex) : routeEntries;\n}\n\n/**\n * The memory model the active route applies to both OM roles: `'auto'`, the\n * first concrete memory model, or `undefined` when the route sets none (both\n * roles then follow their `/om` settings).\n */\nexport function getActiveRouteMemoryModelId(\n state: Readonly<Record<string, unknown>> | undefined,\n threadId?: string | null,\n): string | undefined {\n const route = getActiveMemoryRoute(state as MemoryRouteState | undefined, threadId);\n if (route[0]?.memoryModelId === 'auto') return 'auto';\n const entry = route.find(\n candidate =>\n typeof candidate.memoryModelId === 'string' && candidate.memoryModelId && candidate.memoryModelId !== 'auto',\n );\n return entry?.memoryModelId as string | undefined;\n}\n\n/**\n * Goal judge model resolver for the agent's `goal.judge` config. Resolves the\n * configured goal judge model through mastracode's gateway so provider\n * credentials (stored in auth storage, not just env) are injected — a bare model\n * id handed to core's default model router would fail to find the API key.\n *\n * Returns `undefined` when no judge model is configured, which keeps the goal\n * step a complete no-op (the goal mechanism requires a judge to do anything).\n *\n * `settingsPath` must be the same source `createMastraCode()` reads from so the\n * judge model and the goal budget (`goalMaxTurns`) come from one config — with a\n * custom `settingsPath` a bare `loadSettings()` here could read a different file\n * and silently turn the goal step into a no-op.\n */\nexport function getGoalJudgeModel(\n { requestContext }: { requestContext: RequestContext },\n settingsPath?: string,\n): ResolvedModel | undefined {\n const judgeModelId = loadSettings(settingsPath).models.goalJudgeModel;\n if (!judgeModelId) return undefined;\n return resolveModel(judgeModelId, { remapForCodexOAuth: true, requestContext });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"model.js","names":[],"sources":["../../src/agents/model.ts"],"sourcesContent":["import type { ModelWithRetries } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { ModelRouterLanguageModel } from '@mastra/core/llm';\nimport type { GatewayLanguageModel, IdentifiedModelConfig, MastraModelGatewayInterface } from '@mastra/core/llm';\nimport { RequestContext } from '@mastra/core/request-context';\nimport { getRequestAccountSelection, isRequestAccountRoutingExhausted } from '../auth/account-routing-context.js';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport type { CredentialStore, OAuthAccountRecord } from '../auth/types.js';\nimport { MODEL_ROUTE_MAX_ENTRIES } from '../constants.js';\nimport { toMemoryModelId } from '../onboarding/packs.js';\nimport {\n loadSettings,\n resolveDefaultThinkingLevel,\n stripMastraCodeCustomProviderPrefix,\n} from '../onboarding/settings.js';\nimport { AMAZON_BEDROCK_GATEWAY_ID, createAmazonBedrockGateway } from '../providers/amazon-bedrock-gateway.js';\nimport type { AnthropicPromptCacheScope } from '../providers/anthropic-prompt-cache.js';\nimport { normalizeLegacyModelId } from '../providers/model-ids.js';\nimport { isThinkingLevelSetting } from '../thinking.js';\nimport type { ThinkingLevelSetting } from '../thinking.js';\nimport {\n hasCredentialTenantResolver,\n prepareCredentialTenant,\n resolveCredentialScope,\n resolveCredentialStore,\n} from './credential-resolver.js';\nimport { resolveCustomProviders } from './custom-provider-source.js';\nimport {\n MASTRA_GATEWAY_PREFIX,\n MASTRACODE_GATEWAY_ID,\n MastraCodeGateway,\n getGlobalAuthStorage,\n reloadAuthStorage,\n stripMastraGatewayPrefix,\n} from './mastracode-gateway.js';\nimport type { MastraCodeGatewayOptions } from './mastracode-gateway.js';\n\nexport {\n getAnthropicApiKey,\n getOpenAIApiKey,\n MASTRACODE_GATEWAY_ID,\n MastraCodeGateway,\n remapOpenAIModelForCodexOAuth,\n resolveAuth,\n} from './mastracode-gateway.js';\nexport type { MastraCodeCustomProvider, MastraCodeGatewayOptions } from './mastracode-gateway.js';\nexport {\n setCredentialStoreProvider,\n hasCredentialStoreProvider,\n resolveTenantFromRequestContext,\n} from './credential-resolver.js';\nexport type { CredentialTenant, CredentialStoreProvider } from './credential-resolver.js';\nexport {\n setCustomProvidersSource,\n hasCustomProvidersSource,\n resolveCustomProviders,\n} from './custom-provider-source.js';\nexport type { CustomProvidersSource } from './custom-provider-source.js';\n\ntype ResolvedModel = GatewayLanguageModel | ModelRouterLanguageModel;\ntype ModelRequestHeaders = Record<string, string>;\n\nfunction getAgentControllerHeaders(requestContext?: RequestContext): ModelRequestHeaders | undefined {\n const agentControllerContext = requestContext?.get('controller') as AgentControllerRequestContext<any> | undefined;\n const headers = {\n ...(agentControllerContext?.threadId ? { 'x-thread-id': agentControllerContext.threadId } : {}),\n ...(agentControllerContext?.resourceId ? { 'x-resource-id': agentControllerContext.resourceId } : {}),\n };\n\n return Object.keys(headers).length > 0 ? headers : undefined;\n}\n\nfunction accountCredential(account: OAuthAccountRecord) {\n const { type: _type, id: _id, label: _label, addedAt: _addedAt, active: _active, ...credential } = account;\n return { type: 'oauth' as const, ...credential };\n}\n\nexport function createRequestScopedCredentialStore(\n base: CredentialStore,\n requestContext?: RequestContext,\n): CredentialStore {\n const selectedId = (providerId: string) => getRequestAccountSelection(requestContext, providerId);\n const selectedAccount = (providerId: string) => {\n const accountInstanceId = selectedId(providerId);\n return accountInstanceId\n ? base.listAccounts?.(providerId).find(account => account.id === accountInstanceId)\n : undefined;\n };\n // Routing rejected every account for this provider on this request. Fail\n // closed on every credential read: falling through to `base` would use the\n // provider's active account — the exhausted one routing just refused.\n const rejectAll = (providerId: string) => isRequestAccountRoutingExhausted(requestContext, providerId);\n\n return {\n allowEnvironmentFallback: base.allowEnvironmentFallback,\n reload: () => base.reload(),\n get: providerId => {\n if (rejectAll(providerId)) return undefined;\n // A selection that no longer resolves (the account was removed between\n // routing and the credential read) must not fall through to the active\n // account — that is the account routing deliberately passed over.\n const accountInstanceId = selectedId(providerId);\n if (accountInstanceId === undefined) return base.get(providerId);\n const selected = selectedAccount(providerId);\n return selected ? accountCredential(selected) : undefined;\n },\n getStoredApiKey: providerId => {\n if (rejectAll(providerId)) return undefined;\n // The provider-wide `apikey:` slot is never the credential routing\n // selected, so serving it to a routed request is an OAuth -> API-key\n // fallback of the same provider — the one combination this feature\n // forbids. Unlike `get`, this read has no account argument to pass\n // through, so a selection fails it closed instead.\n if (selectedId(providerId) !== undefined) return undefined;\n return base.getStoredApiKey(providerId);\n },\n getApiKey: providerId =>\n rejectAll(providerId) ? Promise.resolve(undefined) : base.getApiKey(providerId, selectedId(providerId)),\n getOAuthCredential: base.getOAuthCredential\n ? providerId =>\n rejectAll(providerId)\n ? Promise.resolve(undefined)\n : base.getOAuthCredential!(providerId, selectedId(providerId))\n : undefined,\n listAccounts: base.listAccounts ? providerId => base.listAccounts!(providerId) : undefined,\n getActiveAccount: base.getActiveAccount ? providerId => base.getActiveAccount!(providerId) : undefined,\n activateAccount: base.activateAccount\n ? (providerId, accountInstanceId) => base.activateAccount!(providerId, accountInstanceId)\n : undefined,\n removeAccount: base.removeAccount\n ? (providerId, accountInstanceId) => base.removeAccount!(providerId, accountInstanceId)\n : undefined,\n };\n}\n\nexport function createMastraCodeGateway(options: MastraCodeGatewayOptions): MastraCodeGateway {\n return new MastraCodeGateway(options);\n}\n\nexport function createMastraCodeModelCatalogProvider(gateway: MastraModelGatewayInterface) {\n return gateway instanceof MastraCodeGateway\n ? gateway.createModelCatalogProvider()\n : MastraCodeGateway.createModelCatalogProvider(gateway);\n}\n\n/**\n * Placeholder for future model ID normalization.\n * Currently returns the input unchanged, but exists as a seam\n * for aliasing, casing fixes, or validation in the future.\n */\nexport function resolveModelId(modelId: string): string {\n return modelId;\n}\n\n/**\n * Resolve a model ID to the correct provider instance.\n * Shared by the main agent, observer, and reflector.\n *\n * - For anthropic/* models: Uses stored OAuth credentials when present, otherwise direct API key\n * - For openai/* models: Uses OAuth when configured, otherwise direct API key from AuthStorage\n * - For moonshotai/* models: Uses Moonshot AI Anthropic-compatible endpoint\n * - For all other providers: Uses Mastra's model router (models.dev gateway)\n */\nexport function resolveModel(\n modelId: string,\n options?: {\n thinkingLevel?: ThinkingLevelSetting;\n remapForCodexOAuth?: boolean;\n requestContext?: RequestContext;\n anthropicPromptCacheScope?: AnthropicPromptCacheScope;\n },\n): ResolvedModel {\n if (!hasCredentialTenantResolver()) return resolveModelForRequest(modelId, options);\n const createRequestModel = () => {\n const requestContext = options?.requestContext ? new RequestContext(options.requestContext.entries()) : undefined;\n // The router supplies stable SDK metadata and adapts whichever provider the refreshed policy selects.\n // Each invocation owns its policy context and model cache, including concurrent OM and subagent calls.\n return new ModelRouterLanguageModel({ id: `${MASTRACODE_GATEWAY_ID}/${modelId}` }, [\n {\n id: MASTRACODE_GATEWAY_ID,\n name: 'Request-scoped MastraCode',\n fetchProviders: async () => ({}),\n buildUrl: () => undefined,\n getApiKey: async () => '',\n resolveAuth: () => ({ apiKey: 'request-scoped' }),\n resolveLanguageModel: async () => {\n await prepareCredentialTenant(requestContext);\n return resolveModelForRequest(modelId, { ...options, requestContext });\n },\n },\n ]);\n };\n let currentModel = createRequestModel();\n return new Proxy(currentModel, {\n get(_target, property) {\n if (property === 'supportedUrls') return createRequestModel().supportedUrls;\n if (property === 'doStream' || property === 'doGenerate') {\n return async (...args: unknown[]) => {\n const refreshed = createRequestModel();\n currentModel = refreshed;\n return Reflect.apply(Reflect.get(refreshed, property), refreshed, args);\n };\n }\n const value = Reflect.get(currentModel, property, currentModel);\n return typeof value === 'function' ? value.bind(currentModel) : value;\n },\n });\n}\n\nfunction resolveModelForRequest(modelId: string, options?: Parameters<typeof resolveModel>[1]): GatewayLanguageModel {\n reloadAuthStorage();\n const headers = getAgentControllerHeaders(options?.requestContext);\n const settings = loadSettings();\n // Older versions cataloged Bedrock, Copilot and Kimi under the MastraCode\n // gateway namespace, and Kimi under its own `kimi-for-coding` provider. Map\n // already-saved ids (packs, OM settings, thread metadata) to current ids.\n const legacyNormalizedInput = normalizeLegacyModelId(modelId);\n // Deployed web registers a custom providers source (DB-backed, tenant\n // scoped); when registered it is authoritative and settings.json custom\n // providers are ignored. Undefined = local settings-based behavior.\n const customProviders = resolveCustomProviders(options?.requestContext) ?? settings.customProviders;\n // Ids selected from the shared /models catalog were previously persisted in\n // the gateway-qualified `mastracode/<customProviderId>/<model>` form, which\n // parses the provider as `mastracode` and breaks provider config lookup.\n // Normalize at resolution time (in addition to stripping at selection time)\n // so already-saved ids and any surface that persists the raw catalog id\n // still resolve to the custom provider.\n const normalizedInput = stripMastraCodeCustomProviderPrefix(legacyNormalizedInput, customProviders);\n const isMastraGatewayModel = normalizedInput.startsWith(MASTRA_GATEWAY_PREFIX);\n const normalizedModelId = stripMastraGatewayPrefix(normalizedInput);\n const [providerId, ...modelParts] = normalizedModelId.split('/');\n const bareModelId = modelParts.join('/');\n if (!providerId || !bareModelId) {\n throw new Error(`Invalid model id: ${modelId}`);\n }\n\n // Deployed hosts resolve exactly the trusted policy's credential scope.\n // Only local mode falls back to global AuthStorage.\n const tenantCredentialStore = resolveCredentialStore(options?.requestContext);\n const baseCredentialStore = tenantCredentialStore ?? getGlobalAuthStorage();\n\n if (providerId === AMAZON_BEDROCK_GATEWAY_ID) {\n // Bedrock authenticates through the server process's AWS credential chain,\n // never a tenant credential. A tenant store only lets it through when the\n // operator opted the deployment in.\n if (\n tenantCredentialStore?.allowEnvironmentFallback === false &&\n !tenantCredentialStore.allowsDeploymentCredentials?.(AMAZON_BEDROCK_GATEWAY_ID)\n ) {\n throw new ProviderAuthRequiredError(\n 'Amazon Bedrock is not enabled for this Factory deployment. The operator must provide AWS credentials to the server and set FACTORY_DEPLOYMENT_MODEL_PROVIDERS=amazon-bedrock.',\n );\n }\n const bedrockGateway = createAmazonBedrockGateway();\n const routerId = `${AMAZON_BEDROCK_GATEWAY_ID}/${bareModelId}`;\n const auth = bedrockGateway.resolveAuth({\n gatewayId: AMAZON_BEDROCK_GATEWAY_ID,\n providerId: AMAZON_BEDROCK_GATEWAY_ID,\n modelId: bareModelId,\n routerId,\n });\n return bedrockGateway.resolveLanguageModel({\n providerId: AMAZON_BEDROCK_GATEWAY_ID,\n modelId: bareModelId,\n apiKey: auth?.apiKey ?? '',\n headers,\n });\n }\n\n const routerId = `${MASTRACODE_GATEWAY_ID}/${normalizedModelId}`;\n\n const mgApiKey = MastraCodeGateway.getMastraGatewayApiKey();\n const rawGatewayBase =\n settings.memoryGateway?.baseUrl ?? process.env['MASTRA_GATEWAY_URL'] ?? 'https://gateway-api.mastra.ai';\n const credentialStore = createRequestScopedCredentialStore(baseCredentialStore, options?.requestContext);\n const gateway = createMastraCodeGateway({\n mastraGatewayBaseUrl: rawGatewayBase.replace(/\\/+$/, '').replace(/\\/v1$/, ''),\n mastraGatewayApiKey: mgApiKey,\n routeThroughMastraGateway: Boolean(mgApiKey && isMastraGatewayModel),\n thinkingLevel: options?.thinkingLevel,\n anthropicPromptCacheScope: options?.anthropicPromptCacheScope,\n customProviders,\n credentialStore,\n });\n\n const auth = gateway.resolveAuth({\n gatewayId: MASTRACODE_GATEWAY_ID,\n providerId,\n modelId: bareModelId,\n routerId,\n });\n\n if (!auth && credentialStore?.allowEnvironmentFallback === false) {\n // Scopes are exclusive, so name the one in effect: otherwise a user with a\n // personal sign-in can't tell why an organization-scoped Factory fails.\n throw new ProviderAuthRequiredError(\n resolveCredentialScope(options?.requestContext) === 'factory'\n ? `No usable ${providerId} credential is available. This Factory uses organization credentials only, so personal provider sign-ins don't apply. Add an organization credential, then try again.`\n : `No usable ${providerId} credential is available. This Factory uses personal credentials only, so organization credentials don't apply. Connect the provider, then try again.`,\n );\n }\n\n return gateway.resolveLanguageModel({\n providerId,\n modelId: bareModelId,\n apiKey: auth?.apiKey ?? '',\n headers,\n });\n}\n\nexport interface ThinkingRequestContext {\n state?: { thinkingLevel?: unknown };\n session?: { modeId?: string };\n}\n/**\n * Resolve the effective thinking level for the current request.\n *\n * Precedence:\n * 1. Session override (`state.thinkingLevel`, set via /think or the session\n * settings panel).\n * 2. Per-mode default from settings (`models.modeThinkingDefaults[mode]`),\n * only when the context carries a session mode.\n * 3. Global default (`preferences.thinkingLevel`).\n *\n * Resolved per-request (not seeded at session start) so configuration changes\n * apply to the next request of every session — including automated\n * (rule-driven) Factory runs that nobody ever opens interactively.\n */\nexport function resolveRequestThinkingLevel(\n agentControllerContext: ThinkingRequestContext | undefined,\n settingsPath?: string,\n): ThinkingLevelSetting {\n const override = agentControllerContext?.state?.thinkingLevel;\n if (isThinkingLevelSetting(override)) return override;\n const modeId = agentControllerContext?.session?.modeId;\n return resolveDefaultThinkingLevel(loadSettings(settingsPath), modeId).level;\n}\n\n/**\n * Dynamic model function that reads the current model and optional ordered\n * fallback route from controller state. Route entry ids are opaque host-owned\n * identifiers; repeated ids receive occurrence suffixes because core tracks the\n * active fallback index by id across agentic steps.\n */\nexport function getDynamicModel(\n { requestContext }: { requestContext: RequestContext },\n settingsPath?: string,\n { modeless = false }: { modeless?: boolean } = {},\n): IdentifiedModelConfig | ModelWithRetries[] {\n const controller = requestContext.get('controller') as AgentControllerRequestContext<any> | undefined;\n const state = controller?.getState?.() as\n | {\n modelRoute?: {\n entries?: Array<{ id?: unknown; modelId?: unknown }>;\n };\n mastracodePendingModelFallback?: { toEntryId?: unknown; toModelId?: unknown; threadId?: unknown } | null;\n }\n | undefined;\n const pendingState = state?.mastracodePendingModelFallback;\n const pendingFallback =\n pendingState &&\n (pendingState.threadId === undefined ||\n (typeof pendingState.threadId === 'string' && pendingState.threadId === controller?.threadId))\n ? pendingState\n : undefined;\n const pendingModelId =\n pendingFallback && typeof pendingFallback.toModelId === 'string' && pendingFallback.toModelId.length > 0\n ? pendingFallback.toModelId\n : undefined;\n const modelId = pendingModelId ?? controller?.session?.modelId;\n if (!modelId) {\n if (!controller) {\n throw new Error(\n 'No model available: this run started without a controller session context, so no model selection could be resolved.',\n );\n }\n throw new Error('No model selected. Use /models to select a model first.');\n }\n\n const thinkingLevel = resolveRequestThinkingLevel(modeless ? { state: controller?.state } : controller, settingsPath);\n const resolveOptions = { thinkingLevel, remapForCodexOAuth: true, requestContext } as const;\n // Label each resolved model with its full ID: the resolved gateway model only\n // knows its bare provider model, and Memory's `'auto'` reads this label.\n const primary = identifyModel(modelId, resolveModel(modelId, resolveOptions));\n const route = state?.modelRoute?.entries?.slice(0, MODEL_ROUTE_MAX_ENTRIES);\n const pendingEntryId =\n pendingFallback && typeof pendingFallback.toEntryId === 'string' ? pendingFallback.toEntryId : undefined;\n const startIndex = pendingEntryId ? route?.findIndex(entry => entry.id === pendingEntryId) : 0;\n const activeRoute = route && startIndex !== undefined && startIndex >= 0 ? route.slice(startIndex) : undefined;\n if (!activeRoute || activeRoute.length < 2 || activeRoute[0]?.modelId !== modelId) return primary;\n\n const firstId =\n typeof activeRoute[0]?.id === 'string' && activeRoute[0].id.length > 0 ? activeRoute[0].id : undefined;\n if (!firstId) return primary;\n\n const entries: ModelWithRetries[] = [{ id: firstId, model: primary }];\n const appearances = new Map<string, number>([[firstId, 1]]);\n for (const routeEntry of activeRoute.slice(1)) {\n if (\n typeof routeEntry.id !== 'string' ||\n routeEntry.id.length === 0 ||\n typeof routeEntry.modelId !== 'string' ||\n routeEntry.modelId.length === 0\n ) {\n break;\n }\n let entryModel: ResolvedModel;\n try {\n entryModel = resolveModel(routeEntry.modelId, resolveOptions);\n } catch {\n break;\n }\n const occurrence = (appearances.get(routeEntry.id) ?? 0) + 1;\n appearances.set(routeEntry.id, occurrence);\n entries.push({\n id: occurrence === 1 ? routeEntry.id : `${routeEntry.id}#${occurrence}`,\n model: identifyModel(routeEntry.modelId, entryModel),\n });\n }\n\n return entries.length < 2 ? primary : entries;\n}\n\nfunction identifyModel(modelId: string, model: ResolvedModel): IdentifiedModelConfig {\n return { model, id: toMemoryModelId(modelId) };\n}\n\ntype MemoryRouteState = {\n modelRoute?: { entries?: Array<{ id?: unknown; memoryModelId?: unknown }> };\n mastracodePendingModelFallback?: { toEntryId?: unknown; threadId?: unknown } | null;\n};\n\n/**\n * The active model route for memory: the session route, starting at a pending\n * fallback hop captured for this thread.\n */\nexport function getActiveMemoryRoute<TEntry extends { id?: unknown; memoryModelId?: unknown }>(\n state:\n | {\n modelRoute?: { entries?: TEntry[] };\n mastracodePendingModelFallback?: MemoryRouteState['mastracodePendingModelFallback'];\n }\n | undefined,\n threadId?: string | null,\n): TEntry[] {\n const pending = state?.mastracodePendingModelFallback;\n const sameThreadPending =\n pending && (pending.threadId === undefined || pending.threadId === threadId) ? pending : undefined;\n const routeEntries = state?.modelRoute?.entries?.slice(0, MODEL_ROUTE_MAX_ENTRIES) ?? [];\n const pendingIndex = sameThreadPending\n ? routeEntries.findIndex(entry => entry.id === sameThreadPending.toEntryId)\n : -1;\n return pendingIndex >= 0 ? routeEntries.slice(pendingIndex) : routeEntries;\n}\n\n/**\n * The memory model the active route applies to both OM roles: `'auto'`, the\n * first concrete memory model, or `undefined` when the route sets none (both\n * roles then follow their `/om` settings).\n */\nexport function getActiveRouteMemoryModelId(\n state: Readonly<Record<string, unknown>> | undefined,\n threadId?: string | null,\n): string | undefined {\n const route = getActiveMemoryRoute(state as MemoryRouteState | undefined, threadId);\n if (route[0]?.memoryModelId === 'auto') return 'auto';\n const entry = route.find(\n candidate =>\n typeof candidate.memoryModelId === 'string' && candidate.memoryModelId && candidate.memoryModelId !== 'auto',\n );\n return entry?.memoryModelId as string | undefined;\n}\n\n/**\n * Goal judge model resolver for the agent's `goal.judge` config. Resolves the\n * configured goal judge model through mastracode's gateway so provider\n * credentials (stored in auth storage, not just env) are injected — a bare model\n * id handed to core's default model router would fail to find the API key.\n *\n * Returns `undefined` when no judge model is configured, which keeps the goal\n * step a complete no-op (the goal mechanism requires a judge to do anything).\n *\n * `settingsPath` must be the same source `createMastraCode()` reads from so the\n * judge model and the goal budget (`goalMaxTurns`) come from one config — with a\n * custom `settingsPath` a bare `loadSettings()` here could read a different file\n * and silently turn the goal step into a no-op.\n */\nexport function getGoalJudgeModel(\n { requestContext }: { requestContext: RequestContext },\n settingsPath?: string,\n): ResolvedModel | undefined {\n const judgeModelId = loadSettings(settingsPath).models.goalJudgeModel;\n if (!judgeModelId) return undefined;\n return resolveModel(judgeModelId, { remapForCodexOAuth: true, requestContext });\n}\n"],"mappings":"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"}
@@ -2,5 +2,7 @@
2
2
  * Plan mode — read-only analysis and planning.
3
3
  */
4
4
  import type { AgentControllerMode } from '@mastra/core/agent-controller';
5
+ /** Plan mode; pass `false` when the host removed `submit_plan` so the mode never asks for it. */
6
+ export declare function createPlanMode(submitPlanAvailable?: boolean): AgentControllerMode;
5
7
  export declare const planMode: AgentControllerMode;
6
8
  //# sourceMappingURL=plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/agents/modes/plan.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,eAAO,MAAM,QAAQ,EAAE,mBAwCtB,CAAC"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/agents/modes/plan.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,mBAAmB,UAAO,GAAG,mBAAmB,CA0C9E;AAED,eAAO,MAAM,QAAQ,EAAE,mBAAsC,CAAC"}
@@ -1,12 +1,14 @@
1
1
  import { PLAN_MODE_AVAILABLE_TOOLS } from "../tool-availability.js";
2
2
  //#region src/agents/modes/plan.ts
3
- const planMode = {
4
- id: "plan",
5
- name: "Plan",
6
- transitionsTo: "build",
7
- defaultModelId: "openai/gpt-5.5",
8
- description: "Read-only analysis and planning. Use for 'create an implementation plan for X', 'analyze the architecture of Y'.",
9
- instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.
3
+ /** Plan mode; pass `false` when the host removed `submit_plan` so the mode never asks for it. */
4
+ function createPlanMode(submitPlanAvailable = true) {
5
+ return {
6
+ id: "plan",
7
+ name: "Plan",
8
+ transitionsTo: "build",
9
+ defaultModelId: "openai/gpt-5.5",
10
+ description: "Read-only analysis and planning. Use for 'create an implementation plan for X', 'analyze the architecture of Y'.",
11
+ instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.
10
12
 
11
13
  ## Rules
12
14
  - You have READ-ONLY access to the project. You cannot modify project files or run commands.
@@ -21,22 +23,24 @@ const planMode = {
21
23
  - **Parallelize**: Make multiple independent tool calls when exploring different areas
22
24
 
23
25
  ## Plan Delivery
24
- - Write your plan to a markdown file in the session-specific plan directory identified in your system prompt using \`write_file\`, then call \`submit_plan({ path })\` with the path to that file (never the plan body).
26
+ - Write your plan to a markdown file in the session-specific plan directory identified in your system prompt using \`write_file\`, ${submitPlanAvailable ? "then call `submit_plan({ path })` with the path to that file (never the plan body)." : "then hand off the plan file as described in your system prompt (never the plan body)."}
25
27
  - Start the file with a \`# Title\` heading describing the plan.
26
28
  - Reuse the same file while iterating on the same plan; only create a new file for a genuinely different plan so each plan stays available to review.
27
29
  - Do NOT output the plan as text — it MUST live in the plan file.
28
30
  - Be concise: reference files by path and line number, don't include raw contents.
29
31
  - Focus on actionable details, not general observations.
30
- - To revise after "Request changes", edit the same file in place with \`string_replace_lsp\`, and call \`submit_plan\` again with the same path.
32
+ - To revise after "Request changes", edit the same file in place with \`string_replace_lsp\`, ${submitPlanAvailable ? "and call `submit_plan` again with the same path." : "then hand off the same file again."}
31
33
 
32
34
  ## Workflows
33
35
  - You can INSPECT saved workflows via \`list-workflows\` and \`get-workflow\`.
34
36
  - You CANNOT build, run, or delete workflows in this mode.
35
- - If the user asks for a workflow design, include it in the implementation plan stored at the session-specific plan path, then call \`submit_plan({ path })\` as required above. After approval, build mode can save and run it.`,
36
- metadata: { default: false },
37
- availableTools: [...PLAN_MODE_AVAILABLE_TOOLS]
38
- };
37
+ - If the user asks for a workflow design, include it in the implementation plan stored at the session-specific plan path, ${submitPlanAvailable ? "then call `submit_plan({ path })` as required above. After approval, build mode can save and run it." : "then hand off the plan file as described in your system prompt."}`,
38
+ metadata: { default: false },
39
+ availableTools: [...PLAN_MODE_AVAILABLE_TOOLS]
40
+ };
41
+ }
42
+ const planMode = createPlanMode();
39
43
  //#endregion
40
- export { planMode };
44
+ export { createPlanMode, planMode };
41
45
 
42
46
  //# sourceMappingURL=plan.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan.js","names":[],"sources":["../../../src/agents/modes/plan.ts"],"sourcesContent":["/**\n * Plan mode — read-only analysis and planning.\n */\nimport type { AgentControllerMode } from '@mastra/core/agent-controller';\nimport { PLAN_MODE_AVAILABLE_TOOLS } from '../tool-availability.js';\n\nexport const planMode: AgentControllerMode = {\n id: 'plan',\n name: 'Plan',\n transitionsTo: 'build',\n defaultModelId: 'openai/gpt-5.5',\n description:\n \"Read-only analysis and planning. Use for 'create an implementation plan for X', 'analyze the architecture of Y'.\",\n instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.\n\n## Rules\n- You have READ-ONLY access to the project. You cannot modify project files or run commands.\n- The one exception is plan files: you can create and edit markdown files inside the session-specific plan directory identified in your system prompt using \\`write_file\\`, \\`view\\`, and \\`string_replace_lsp\\`. You may not write anywhere else.\n- First, explore the codebase to understand existing patterns, architecture, and conventions.\n- Produce a concrete, actionable plan — not vague suggestions.\n\n## Tool Strategy\n- **Discover structure**: Use find_files (glob) to understand project layout and find relevant files\n- **Find patterns**: Use search_content (grep) to locate existing implementations, imports, and conventions\n- **Understand deeply**: Use view with view_range to read specific sections of key files\n- **Parallelize**: Make multiple independent tool calls when exploring different areas\n\n## Plan Delivery\n- Write your plan to a markdown file in the session-specific plan directory identified in your system prompt using \\`write_file\\`, then call \\`submit_plan({ path })\\` with the path to that file (never the plan body).\n- Start the file with a \\`# Title\\` heading describing the plan.\n- Reuse the same file while iterating on the same plan; only create a new file for a genuinely different plan so each plan stays available to review.\n- Do NOT output the plan as text — it MUST live in the plan file.\n- Be concise: reference files by path and line number, don't include raw contents.\n- Focus on actionable details, not general observations.\n- To revise after \"Request changes\", edit the same file in place with \\`string_replace_lsp\\`, and call \\`submit_plan\\` again with the same path.\n\n## Workflows\n- You can INSPECT saved workflows via \\`list-workflows\\` and \\`get-workflow\\`.\n- You CANNOT build, run, or delete workflows in this mode.\n- If the user asks for a workflow design, include it in the implementation plan stored at the session-specific plan path, then call \\`submit_plan({ path })\\` as required above. After approval, build mode can save and run it.`,\n\n metadata: {\n default: false,\n },\n\n availableTools: [...PLAN_MODE_AVAILABLE_TOOLS],\n};\n"],"mappings":";;AAMA,MAAa,WAAgC;CAC3C,IAAI;CACJ,MAAM;CACN,eAAe;CACf,gBAAgB;CAChB,aACE;CACF,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Bd,UAAU,EACR,SAAS,MACX;CAEA,gBAAgB,CAAC,GAAG,yBAAyB;AAC/C"}
1
+ {"version":3,"file":"plan.js","names":[],"sources":["../../../src/agents/modes/plan.ts"],"sourcesContent":["/**\n * Plan mode — read-only analysis and planning.\n */\nimport type { AgentControllerMode } from '@mastra/core/agent-controller';\nimport { PLAN_MODE_AVAILABLE_TOOLS } from '../tool-availability.js';\n\n/** Plan mode; pass `false` when the host removed `submit_plan` so the mode never asks for it. */\nexport function createPlanMode(submitPlanAvailable = true): AgentControllerMode {\n return {\n id: 'plan',\n name: 'Plan',\n transitionsTo: 'build',\n defaultModelId: 'openai/gpt-5.5',\n description:\n \"Read-only analysis and planning. Use for 'create an implementation plan for X', 'analyze the architecture of Y'.\",\n instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.\n\n## Rules\n- You have READ-ONLY access to the project. You cannot modify project files or run commands.\n- The one exception is plan files: you can create and edit markdown files inside the session-specific plan directory identified in your system prompt using \\`write_file\\`, \\`view\\`, and \\`string_replace_lsp\\`. You may not write anywhere else.\n- First, explore the codebase to understand existing patterns, architecture, and conventions.\n- Produce a concrete, actionable plan — not vague suggestions.\n\n## Tool Strategy\n- **Discover structure**: Use find_files (glob) to understand project layout and find relevant files\n- **Find patterns**: Use search_content (grep) to locate existing implementations, imports, and conventions\n- **Understand deeply**: Use view with view_range to read specific sections of key files\n- **Parallelize**: Make multiple independent tool calls when exploring different areas\n\n## Plan Delivery\n- Write your plan to a markdown file in the session-specific plan directory identified in your system prompt using \\`write_file\\`, ${submitPlanAvailable ? 'then call `submit_plan({ path })` with the path to that file (never the plan body).' : 'then hand off the plan file as described in your system prompt (never the plan body).'}\n- Start the file with a \\`# Title\\` heading describing the plan.\n- Reuse the same file while iterating on the same plan; only create a new file for a genuinely different plan so each plan stays available to review.\n- Do NOT output the plan as text — it MUST live in the plan file.\n- Be concise: reference files by path and line number, don't include raw contents.\n- Focus on actionable details, not general observations.\n- To revise after \"Request changes\", edit the same file in place with \\`string_replace_lsp\\`, ${submitPlanAvailable ? 'and call `submit_plan` again with the same path.' : 'then hand off the same file again.'}\n\n## Workflows\n- You can INSPECT saved workflows via \\`list-workflows\\` and \\`get-workflow\\`.\n- You CANNOT build, run, or delete workflows in this mode.\n- If the user asks for a workflow design, include it in the implementation plan stored at the session-specific plan path, ${submitPlanAvailable ? 'then call `submit_plan({ path })` as required above. After approval, build mode can save and run it.' : 'then hand off the plan file as described in your system prompt.'}`,\n\n metadata: {\n default: false,\n },\n\n availableTools: [...PLAN_MODE_AVAILABLE_TOOLS],\n };\n}\n\nexport const planMode: AgentControllerMode = createPlanMode();\n"],"mappings":";;;AAOA,SAAgB,eAAe,sBAAsB,MAA2B;CAC9E,OAAO;EACL,IAAI;EACJ,MAAM;EACN,eAAe;EACf,gBAAgB;EAChB,aACE;EACF,cAAc;;;;;;;;;;;;;;;qIAemH,sBAAsB,wFAAwF,wFAAwF;;;;;;gGAM3O,sBAAsB,qDAAqD,qCAAqC;;;;;4HAKpF,sBAAsB,yGAAyG;EAEvP,UAAU,EACR,SAAS,MACX;EAEA,gBAAgB,CAAC,GAAG,yBAAyB;CAC/C;AACF;AAEA,MAAa,WAAgC,eAAe"}
@@ -11,6 +11,10 @@ export interface PromptContext extends Omit<BasePromptContext, 'toolGuidance'> {
11
11
  /** The subconscious knowledge tools are registered on the agent. */
12
12
  hasSubconscious?: boolean;
13
13
  hostInstructions?: string;
14
+ /** Controller built-in tools removed via `disableBuiltinTools`; treated as denied. */
15
+ disabledBuiltinTools?: readonly string[];
16
+ /** Resolved in `buildFullPromptSections`; false when `submit_plan` is denied or disabled. */
17
+ submitPlanAvailable?: boolean;
14
18
  currentDate: string;
15
19
  workingDir: string;
16
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agents/prompts/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,KAAK,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAapF,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAQD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,CAKpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAE1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,EAAE,CAuH3E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agents/prompts/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,KAAK,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAapF,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sFAAsF;IACtF,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,6FAA6F;IAC7F,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAQD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,gEAAgE;AAChE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,CAKpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAE1D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,GAAG,aAAa,EAAE,CA4H3E"}
@@ -39,6 +39,7 @@ function buildFullPromptSections(ctx) {
39
39
  if (permRules?.tools) {
40
40
  for (const [name, policy] of Object.entries(permRules.tools)) if (policy === "deny") deniedTools.add(name);
41
41
  }
42
+ for (const name of ctx.disabledBuiltinTools ?? []) deniedTools.add(name);
42
43
  if (resolveLspSetting(loadSettings().lsp) === false) deniedTools.add(MC_TOOLS.LSP_INSPECT);
43
44
  const factoryProjectId = typeof ctx.state?.factoryProjectId === "string" ? ctx.state.factoryProjectId : void 0;
44
45
  const toolGuidance = buildToolGuidance(ctx.modeId, {
@@ -64,7 +65,10 @@ function buildFullPromptSections(ctx) {
64
65
  toolGuidance
65
66
  });
66
67
  const entry = modePrompts[ctx.modeId] || modePrompts.build;
67
- const modeSpecific = (typeof entry === "function" ? entry(ctx) : entry) ?? "";
68
+ const modeSpecific = (typeof entry === "function" ? entry({
69
+ ...ctx,
70
+ submitPlanAvailable: !deniedTools.has("submit_plan")
71
+ }) : entry) ?? "";
68
72
  const modelSpecific = ctx.modelId ? modelSpecificPrompts[ctx.modelId] ?? "" : "";
69
73
  const configDir = ctx.state?.configDir;
70
74
  const baseRef = typeof ctx.state?.baseRef === "string" ? ctx.state.baseRef : void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/agents/prompts/index.ts"],"sourcesContent":["/**\n * Prompt system — exports the prompt builder and mode-specific prompts.\n */\n\nexport { buildModePrompt, buildModePromptFn } from './build.js';\nexport { planModePrompt } from './plan.js';\nexport { fastModePrompt } from './fast.js';\n\nimport { buildBasePrompt } from '@mastra/core/coding-agent';\nimport type { PromptContext as BasePromptContext } from '@mastra/core/coding-agent';\nimport { loadSettings, resolveLspSetting } from '../../onboarding/settings.js';\nimport { MC_TOOLS } from '../../tool-names.js';\nimport { hasParallelKey, hasTavilyKey } from '../../tools/index.js';\nimport { getLocalPlansRelativeDir } from '../../utils/plans.js';\nimport { loadSessionInstructions, formatInstructionSource, AGENT_INSTRUCTIONS_HEADING } from './agent-instructions.js';\nimport { buildModePromptFn } from './build.js';\nimport { fastModePrompt } from './fast.js';\nimport { modelSpecificPrompts } from './model.js';\nimport { planModePrompt } from './plan.js';\nimport { buildToolGuidance } from './tool-guidance.js';\n\n// Extended prompt context that includes runtime information\nexport interface PromptContext extends Omit<BasePromptContext, 'toolGuidance'> {\n modeId: string;\n state?: any;\n /** The subconscious knowledge tools are registered on the agent. */\n hasSubconscious?: boolean;\n hostInstructions?: string;\n currentDate: string;\n workingDir: string;\n}\n\nconst modePrompts: Record<string, string | ((ctx: PromptContext) => string)> = {\n build: buildModePromptFn,\n plan: planModePrompt,\n fast: fastModePrompt,\n};\n\n/**\n * One labeled piece of the assembled system prompt.\n *\n * The system prompt is a single string by the time it reaches the model, which\n * makes it impossible to say which configuration source is responsible for\n * which share of the context window. Building it as labeled sections and\n * joining them at the end keeps that attribution available to the `/context`\n * audit while guaranteeing the audit measures the exact text that is sent —\n * a parallel \"describe the prompt\" path would drift and report numbers for a\n * prompt that is no longer assembled this way.\n */\nexport interface PromptSection {\n /** Stable identifier, unique within a single build. */\n id: string;\n /** Human-readable label for display. */\n label: string;\n /** Optional provenance (e.g. the instruction file path). */\n detail?: string;\n /** The exact text contributed to the prompt. */\n content: string;\n}\n\n/** Join prompt sections into the final system prompt string. */\nexport function joinPromptSections(sections: PromptSection[]): string {\n return sections\n .map(section => section.content)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/**\n * Build the full system prompt for a given mode and context.\n * Combines the base prompt with mode-specific instructions.\n */\nexport function buildFullPrompt(ctx: PromptContext): string {\n return joinPromptSections(buildFullPromptSections(ctx));\n}\n\n/**\n * Build the system prompt as labeled sections. `buildFullPrompt` is the join of\n * these, so the two can never disagree about what the model receives.\n */\nexport function buildFullPromptSections(ctx: PromptContext): PromptSection[] {\n // Determine whether web search tools are available\n const modelId = ctx.modelId;\n const hasWebSearch =\n hasParallelKey() ||\n hasTavilyKey() ||\n (!!modelId && (modelId.startsWith('anthropic/') || modelId.startsWith('openai/')));\n\n // Collect per-tool deny rules so guidance omits denied tools\n const deniedTools = new Set<string>();\n const permRules = ctx.state?.permissionRules as { tools?: Record<string, string> } | undefined;\n if (permRules?.tools) {\n for (const [name, policy] of Object.entries(permRules.tools)) {\n if (policy === 'deny') deniedTools.add(name);\n }\n }\n\n // LSP is opt-in — when it is off the tool is never registered, so its\n // guidance must not be advertised either.\n if (resolveLspSetting(loadSettings().lsp) === false) deniedTools.add(MC_TOOLS.LSP_INSPECT);\n\n // Build mode-aware tool guidance\n const factoryProjectId = typeof ctx.state?.factoryProjectId === 'string' ? ctx.state.factoryProjectId : undefined;\n const toolGuidance = buildToolGuidance(ctx.modeId, {\n hasWebSearch,\n hasSubconscious: ctx.hasSubconscious === true,\n hasSubagents: ctx.hasSubagents,\n deniedTools,\n plansDir: getLocalPlansRelativeDir({ factoryProjectId }),\n });\n\n // Map new context to base context\n const baseCtx: BasePromptContext = {\n projectPath: ctx.workingDir || '(no workspace attached)',\n projectName: ctx.projectName || 'unknown',\n gitBranch: ctx.gitBranch,\n platform: process.platform,\n commonBinaries: ctx.commonBinaries,\n date: ctx.currentDate,\n mode: ctx.modeId,\n modelId: ctx.modelId,\n coAuthorName: ctx.coAuthorName,\n coAuthorEmail: ctx.coAuthorEmail,\n activePlan: ctx.state?.activePlan,\n hasSubagents: ctx.hasSubagents !== false && !deniedTools.has('subagent'),\n toolGuidance,\n };\n\n const base = buildBasePrompt(baseCtx);\n const entry = modePrompts[ctx.modeId] || modePrompts.build;\n const modeSpecific = (typeof entry === 'function' ? entry(ctx) : entry) ?? '';\n const modelSpecific = ctx.modelId\n ? (modelSpecificPrompts[ctx.modelId as keyof typeof modelSpecificPrompts] ?? '')\n : '';\n\n // The current task list is carried on the agent state-signal lane (see\n // TaskStateProcessor) rather than injected into the cached system prompt. This\n // keeps the prompt prefix stable across task updates (preserving prompt cache)\n // while still surviving observational-memory truncation.\n\n // Load and inject agent instructions from AGENTS.md/CLAUDE.md files.\n // Untrusted checkouts (e.g. a PR branch under review) never read\n // project-scope files off the working tree: their AGENTS.md is\n // attacker-writable and would otherwise land in the system prompt as\n // trusted configuration. When the session carries a trusted base ref, the\n // project instructions are served from that ref instead (`git show`);\n // without one, project-scope files are skipped entirely. Home-directory\n // (global) instructions belong to whoever owns the machine, so hosts that\n // run sessions for someone else opt out of them entirely.\n // A host booting several checkouts under one working directory names them\n // as `instructionRoots`, so each checkout's own files load.\n const configDir = ctx.state?.configDir as string | undefined;\n const baseRef = typeof ctx.state?.baseRef === 'string' ? ctx.state.baseRef : undefined;\n const instructionRoots = Array.isArray(ctx.state?.instructionRoots)\n ? (ctx.state.instructionRoots as string[])\n : undefined;\n // No working directory means a hosted session with no project attached:\n // load NO instruction files at all — project locations would resolve\n // against the server's own cwd, and global locations against the server's\n // homedir. Neither belongs in a hosted session's prompt.\n const instructionSources = ctx.workingDir\n ? loadSessionInstructions({\n workingDir: ctx.workingDir,\n instructionRoots,\n configDir,\n untrustedCheckout: ctx.state?.untrustedCheckout === true,\n baseRef,\n skipGlobal: ctx.state?.skipGlobalInstructions === true,\n })\n : [];\n // Emitted per source so each AGENTS.md/CLAUDE.md can be costed individually.\n // The heading rides on the first source's section, which is exactly how\n // `formatAgentInstructions` lays the block out, so joining the sections\n // reproduces its output byte for byte.\n const instructionSections: PromptSection[] = instructionSources.map((source, index) => {\n const isFirst = index === 0;\n const isLast = index === instructionSources.length - 1;\n let content = formatInstructionSource(source);\n if (isFirst) content = `${AGENT_INSTRUCTIONS_HEADING}\\n\\n${content}`;\n // The block as a whole used to be trimmed, which only ever affected the\n // trailing whitespace of the final source's content.\n if (isLast) content = content.trimEnd();\n return {\n id: `agent-instructions:${source.path}:${index}`,\n label: `${source.scope === 'global' ? 'Global' : 'Project'} instructions`,\n detail: source.ref ? `${source.path} (at ref ${source.ref})` : source.path,\n content,\n };\n });\n\n const hostInstructions = ctx.hostInstructions?.trim() ?? '';\n\n return [\n { id: 'base-prompt', label: 'Base system prompt', content: base },\n { id: 'host-instructions', label: 'Host instructions', content: hostInstructions },\n ...instructionSections,\n { id: 'model-prompt', label: 'Model-specific prompt', detail: ctx.modelId, content: modelSpecific.trim() },\n { id: 'mode-prompt', label: 'Mode prompt', detail: ctx.modeId, content: modeSpecific.trim() },\n ].filter(section => Boolean(section.content));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/agents/prompts/index.ts"],"sourcesContent":["/**\n * Prompt system — exports the prompt builder and mode-specific prompts.\n */\n\nexport { buildModePrompt, buildModePromptFn } from './build.js';\nexport { planModePrompt } from './plan.js';\nexport { fastModePrompt } from './fast.js';\n\nimport { buildBasePrompt } from '@mastra/core/coding-agent';\nimport type { PromptContext as BasePromptContext } from '@mastra/core/coding-agent';\nimport { loadSettings, resolveLspSetting } from '../../onboarding/settings.js';\nimport { MC_TOOLS } from '../../tool-names.js';\nimport { hasParallelKey, hasTavilyKey } from '../../tools/index.js';\nimport { getLocalPlansRelativeDir } from '../../utils/plans.js';\nimport { loadSessionInstructions, formatInstructionSource, AGENT_INSTRUCTIONS_HEADING } from './agent-instructions.js';\nimport { buildModePromptFn } from './build.js';\nimport { fastModePrompt } from './fast.js';\nimport { modelSpecificPrompts } from './model.js';\nimport { planModePrompt } from './plan.js';\nimport { buildToolGuidance } from './tool-guidance.js';\n\n// Extended prompt context that includes runtime information\nexport interface PromptContext extends Omit<BasePromptContext, 'toolGuidance'> {\n modeId: string;\n state?: any;\n /** The subconscious knowledge tools are registered on the agent. */\n hasSubconscious?: boolean;\n hostInstructions?: string;\n /** Controller built-in tools removed via `disableBuiltinTools`; treated as denied. */\n disabledBuiltinTools?: readonly string[];\n /** Resolved in `buildFullPromptSections`; false when `submit_plan` is denied or disabled. */\n submitPlanAvailable?: boolean;\n currentDate: string;\n workingDir: string;\n}\n\nconst modePrompts: Record<string, string | ((ctx: PromptContext) => string)> = {\n build: buildModePromptFn,\n plan: planModePrompt,\n fast: fastModePrompt,\n};\n\n/**\n * One labeled piece of the assembled system prompt.\n *\n * The system prompt is a single string by the time it reaches the model, which\n * makes it impossible to say which configuration source is responsible for\n * which share of the context window. Building it as labeled sections and\n * joining them at the end keeps that attribution available to the `/context`\n * audit while guaranteeing the audit measures the exact text that is sent —\n * a parallel \"describe the prompt\" path would drift and report numbers for a\n * prompt that is no longer assembled this way.\n */\nexport interface PromptSection {\n /** Stable identifier, unique within a single build. */\n id: string;\n /** Human-readable label for display. */\n label: string;\n /** Optional provenance (e.g. the instruction file path). */\n detail?: string;\n /** The exact text contributed to the prompt. */\n content: string;\n}\n\n/** Join prompt sections into the final system prompt string. */\nexport function joinPromptSections(sections: PromptSection[]): string {\n return sections\n .map(section => section.content)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/**\n * Build the full system prompt for a given mode and context.\n * Combines the base prompt with mode-specific instructions.\n */\nexport function buildFullPrompt(ctx: PromptContext): string {\n return joinPromptSections(buildFullPromptSections(ctx));\n}\n\n/**\n * Build the system prompt as labeled sections. `buildFullPrompt` is the join of\n * these, so the two can never disagree about what the model receives.\n */\nexport function buildFullPromptSections(ctx: PromptContext): PromptSection[] {\n // Determine whether web search tools are available\n const modelId = ctx.modelId;\n const hasWebSearch =\n hasParallelKey() ||\n hasTavilyKey() ||\n (!!modelId && (modelId.startsWith('anthropic/') || modelId.startsWith('openai/')));\n\n // Collect per-tool deny rules so guidance omits denied tools\n const deniedTools = new Set<string>();\n const permRules = ctx.state?.permissionRules as { tools?: Record<string, string> } | undefined;\n if (permRules?.tools) {\n for (const [name, policy] of Object.entries(permRules.tools)) {\n if (policy === 'deny') deniedTools.add(name);\n }\n }\n\n // Disabled built-ins are never registered, so they are denied for guidance too.\n for (const name of ctx.disabledBuiltinTools ?? []) deniedTools.add(name);\n\n // LSP is opt-in — when it is off the tool is never registered, so its\n // guidance must not be advertised either.\n if (resolveLspSetting(loadSettings().lsp) === false) deniedTools.add(MC_TOOLS.LSP_INSPECT);\n\n // Build mode-aware tool guidance\n const factoryProjectId = typeof ctx.state?.factoryProjectId === 'string' ? ctx.state.factoryProjectId : undefined;\n const toolGuidance = buildToolGuidance(ctx.modeId, {\n hasWebSearch,\n hasSubconscious: ctx.hasSubconscious === true,\n hasSubagents: ctx.hasSubagents,\n deniedTools,\n plansDir: getLocalPlansRelativeDir({ factoryProjectId }),\n });\n\n // Map new context to base context\n const baseCtx: BasePromptContext = {\n projectPath: ctx.workingDir || '(no workspace attached)',\n projectName: ctx.projectName || 'unknown',\n gitBranch: ctx.gitBranch,\n platform: process.platform,\n commonBinaries: ctx.commonBinaries,\n date: ctx.currentDate,\n mode: ctx.modeId,\n modelId: ctx.modelId,\n coAuthorName: ctx.coAuthorName,\n coAuthorEmail: ctx.coAuthorEmail,\n activePlan: ctx.state?.activePlan,\n hasSubagents: ctx.hasSubagents !== false && !deniedTools.has('subagent'),\n toolGuidance,\n };\n\n const base = buildBasePrompt(baseCtx);\n const entry = modePrompts[ctx.modeId] || modePrompts.build;\n const modeSpecific =\n (typeof entry === 'function' ? entry({ ...ctx, submitPlanAvailable: !deniedTools.has('submit_plan') }) : entry) ??\n '';\n const modelSpecific = ctx.modelId\n ? (modelSpecificPrompts[ctx.modelId as keyof typeof modelSpecificPrompts] ?? '')\n : '';\n\n // The current task list is carried on the agent state-signal lane (see\n // TaskStateProcessor) rather than injected into the cached system prompt. This\n // keeps the prompt prefix stable across task updates (preserving prompt cache)\n // while still surviving observational-memory truncation.\n\n // Load and inject agent instructions from AGENTS.md/CLAUDE.md files.\n // Untrusted checkouts (e.g. a PR branch under review) never read\n // project-scope files off the working tree: their AGENTS.md is\n // attacker-writable and would otherwise land in the system prompt as\n // trusted configuration. When the session carries a trusted base ref, the\n // project instructions are served from that ref instead (`git show`);\n // without one, project-scope files are skipped entirely. Home-directory\n // (global) instructions belong to whoever owns the machine, so hosts that\n // run sessions for someone else opt out of them entirely.\n // A host booting several checkouts under one working directory names them\n // as `instructionRoots`, so each checkout's own files load.\n const configDir = ctx.state?.configDir as string | undefined;\n const baseRef = typeof ctx.state?.baseRef === 'string' ? ctx.state.baseRef : undefined;\n const instructionRoots = Array.isArray(ctx.state?.instructionRoots)\n ? (ctx.state.instructionRoots as string[])\n : undefined;\n // No working directory means a hosted session with no project attached:\n // load NO instruction files at all — project locations would resolve\n // against the server's own cwd, and global locations against the server's\n // homedir. Neither belongs in a hosted session's prompt.\n const instructionSources = ctx.workingDir\n ? loadSessionInstructions({\n workingDir: ctx.workingDir,\n instructionRoots,\n configDir,\n untrustedCheckout: ctx.state?.untrustedCheckout === true,\n baseRef,\n skipGlobal: ctx.state?.skipGlobalInstructions === true,\n })\n : [];\n // Emitted per source so each AGENTS.md/CLAUDE.md can be costed individually.\n // The heading rides on the first source's section, which is exactly how\n // `formatAgentInstructions` lays the block out, so joining the sections\n // reproduces its output byte for byte.\n const instructionSections: PromptSection[] = instructionSources.map((source, index) => {\n const isFirst = index === 0;\n const isLast = index === instructionSources.length - 1;\n let content = formatInstructionSource(source);\n if (isFirst) content = `${AGENT_INSTRUCTIONS_HEADING}\\n\\n${content}`;\n // The block as a whole used to be trimmed, which only ever affected the\n // trailing whitespace of the final source's content.\n if (isLast) content = content.trimEnd();\n return {\n id: `agent-instructions:${source.path}:${index}`,\n label: `${source.scope === 'global' ? 'Global' : 'Project'} instructions`,\n detail: source.ref ? `${source.path} (at ref ${source.ref})` : source.path,\n content,\n };\n });\n\n const hostInstructions = ctx.hostInstructions?.trim() ?? '';\n\n return [\n { id: 'base-prompt', label: 'Base system prompt', content: base },\n { id: 'host-instructions', label: 'Host instructions', content: hostInstructions },\n ...instructionSections,\n { id: 'model-prompt', label: 'Model-specific prompt', detail: ctx.modelId, content: modelSpecific.trim() },\n { id: 'mode-prompt', label: 'Mode prompt', detail: ctx.modeId, content: modeSpecific.trim() },\n ].filter(section => Boolean(section.content));\n}\n"],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  interface PlanPromptContext {
2
2
  state?: Record<string, unknown>;
3
+ /** False when the host disabled or denied `submit_plan`; defaults to available. */
4
+ submitPlanAvailable?: boolean;
3
5
  }
4
6
  export declare function planModePrompt(ctx: PlanPromptContext): string;
5
7
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/agents/prompts/plan.ts"],"names":[],"mappings":"AAKA,UAAU,iBAAiB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAuG7D"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/agents/prompts/plan.ts"],"names":[],"mappings":"AAKA,UAAU,iBAAiB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,mFAAmF;IACnF,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAmE7D"}
@@ -6,6 +6,7 @@ import { getLocalPlansRelativeDir } from "../../utils/plans.js";
6
6
  function planModePrompt(ctx) {
7
7
  const plansDir = getLocalPlansRelativeDir({ factoryProjectId: typeof ctx.state?.factoryProjectId === "string" ? ctx.state.factoryProjectId : void 0 });
8
8
  const examplePath = `${plansDir}/add-dark-mode.md`;
9
+ const submitPlanAvailable = ctx.submitPlanAvailable !== false;
9
10
  return `
10
11
  # Plan Mode — READ-ONLY (except plan files)
11
12
 
@@ -32,15 +33,7 @@ Before writing any plan, build a mental model of the codebase:
32
33
  4. Trace data flow: where does input come from, how is it transformed, where does it go?
33
34
  5. Identify existing patterns the codebase uses (naming, structure, error handling, testing).
34
35
 
35
- ## Goal-Ready Plans
36
-
37
- The submit_plan approval UI can let the user approve the plan normally or start it as a persistent goal. Write plans so they can be carried out as a goal if the user chooses that option:
38
- - Make the desired outcome explicit and verifiable.
39
- - Break work into ordered, actionable steps that can be executed autonomously.
40
- - Include constraints, risks, blockers, and decision points that may require user input.
41
- - Include concrete verification criteria so the goal judge can tell when the work is done.
42
-
43
- ## Your Plan Output
36
+ ${submitPlanAvailable ? GOAL_READY_SECTION : ""}## Your Plan Output
44
37
 
45
38
  Produce a clear, step-by-step plan with this structure:
46
39
 
@@ -67,13 +60,26 @@ For each step:
67
60
 
68
61
  Write each plan to its own markdown file under \`${plansDir}/\` (e.g. \`${examplePath}\`). Start the file with a \`# Title\` heading describing the plan.
69
62
 
70
- 1. **First submission**: Write your plan to a file under \`${plansDir}/\` using \`write_file\`, then call \`submit_plan\` with the \`path\` to that file.
63
+ 1. **${submitPlanAvailable ? "First submission" : "Writing"}**: Write your plan to a file under \`${plansDir}/\` using \`write_file\`${submitPlanAvailable ? ", then call `submit_plan` with the `path` to that file" : ""}.
71
64
  2. **Reading**: Use \`view\` to read a plan file.
72
65
  3. **Editing**: Use \`string_replace_lsp\` for targeted edits to specific sections.
73
66
 
74
67
  **Reuse the same file while you keep iterating on the same plan.** Only create a NEW file when you're starting a genuinely different plan — that keeps each plan available to look back at and keeps revision diffs meaningful.
75
68
 
76
- ## IMMEDIATE ACTION: Write plan file, then call submit_plan
69
+ ${submitPlanAvailable ? submitPlanSection(plansDir, examplePath) : planFileOnlySection(plansDir)}Do NOT start implementing until the plan is approved.
70
+ `;
71
+ }
72
+ const GOAL_READY_SECTION = `## Goal-Ready Plans
73
+
74
+ The submit_plan approval UI can let the user approve the plan normally or start it as a persistent goal. Write plans so they can be carried out as a goal if the user chooses that option:
75
+ - Make the desired outcome explicit and verifiable.
76
+ - Break work into ordered, actionable steps that can be executed autonomously.
77
+ - Include constraints, risks, blockers, and decision points that may require user input.
78
+ - Include concrete verification criteria so the goal judge can tell when the work is done.
79
+
80
+ `;
81
+ function submitPlanSection(plansDir, examplePath) {
82
+ return `## IMMEDIATE ACTION: Write plan file, then call submit_plan
77
83
 
78
84
  As soon as your plan is complete:
79
85
  1. Write it to a file under \`${plansDir}/\` using \`write_file\`
@@ -102,7 +108,19 @@ If the user requests changes, you will be stopped immediately. Wait for their ne
102
108
 
103
109
  The user will see a diff of what changed between the previous and revised plan. Use \`string_replace_lsp\` for targeted edits so the diff is clear and meaningful — do NOT rewrite the entire plan from scratch for small changes, and do NOT move the plan to a new file for a revision.
104
110
 
105
- Do NOT start implementing until the plan is approved.
111
+ `;
112
+ }
113
+ function planFileOnlySection(plansDir) {
114
+ return `## When the plan is complete
115
+
116
+ Plan approval is not available in this session — there is no \`submit_plan\` tool. As soon as your plan is complete:
117
+ 1. Write it to a file under \`${plansDir}/\` using \`write_file\`
118
+ 2. Follow any host or session instructions for handing the plan off; otherwise reply with a brief summary and the plan file path
119
+
120
+ **CRITICAL:** Do NOT generate a long text response describing your plan. The plan content belongs in the plan file.
121
+
122
+ When you receive revision feedback, edit the SAME plan file with \`string_replace_lsp\` rather than rewriting it or moving it to a new file.
123
+
106
124
  `;
107
125
  }
108
126
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"plan.js","names":[],"sources":["../../../src/agents/prompts/plan.ts"],"sourcesContent":["/**\n * Plan mode prompt — read-only exploration and planning.\n */\nimport { getLocalPlansRelativeDir } from '../../utils/plans.js';\n\ninterface PlanPromptContext {\n state?: Record<string, unknown>;\n}\n\nexport function planModePrompt(ctx: PlanPromptContext): string {\n const factoryProjectId = typeof ctx.state?.factoryProjectId === 'string' ? ctx.state.factoryProjectId : undefined;\n const plansDir = getLocalPlansRelativeDir({ factoryProjectId });\n const examplePath = `${plansDir}/add-dark-mode.md`;\n\n return `\n# Plan Mode — READ-ONLY (except plan files)\n\nYou are in PLAN mode. Your job is to explore the codebase and design an implementation plan — NOT to make changes to the project.\n\n## CRITICAL: Read-Only Mode (project files)\n\nThis mode is **read-only for project files**. You must NOT modify the project.\n\n- Do NOT modify, create, or delete project files\n- Do NOT run commands that change state (no git commit, no npm install, no file creation)\n- Do NOT run build commands, tests, or scripts that have side effects\n\nThe ONE exception is plan files: you CAN create and edit markdown files inside \\`${plansDir}/\\` (e.g. \\`${examplePath}\\`). You may NOT write anywhere else.\n\nIf the user asks you to make changes while in Plan mode, explain that you're in read-only mode and they should switch to Build mode (\\`/mode build\\`) first.\n\n## Exploration Strategy\n\nBefore writing any plan, build a mental model of the codebase:\n1. Start with the directory structure (\\`view\\` on the project root or relevant subdirectory).\n2. Find the relevant entry points and core files using \\`search_content\\` and \\`find_files\\`.\n3. Read the actual code — don't assume based on file names alone.\n4. Trace data flow: where does input come from, how is it transformed, where does it go?\n5. Identify existing patterns the codebase uses (naming, structure, error handling, testing).\n\n## Goal-Ready Plans\n\nThe submit_plan approval UI can let the user approve the plan normally or start it as a persistent goal. Write plans so they can be carried out as a goal if the user chooses that option:\n- Make the desired outcome explicit and verifiable.\n- Break work into ordered, actionable steps that can be executed autonomously.\n- Include constraints, risks, blockers, and decision points that may require user input.\n- Include concrete verification criteria so the goal judge can tell when the work is done.\n\n## Your Plan Output\n\nProduce a clear, step-by-step plan with this structure:\n\n### Overview\nOne paragraph: what the change does and why.\n\n### Complexity Estimate\n- **Size**: Small (1-2 files) / Medium (3-5 files) / Large (6+ files)\n- **Risk**: Low (additive, no breaking changes) / Medium (modifies existing behavior) / High (architectural, affects many consumers)\n- **Dependencies**: List any new packages, external services, or migration steps needed.\n\n### Steps\nFor each step:\n1. **File**: path to create or modify\n2. **Change**: what to add/modify/remove, with enough specificity to implement directly\n3. **Why**: brief rationale connecting this step to the overall goal\n\n### Verification\n- What tests to run\n- What to check manually\n- What could go wrong\n\n## Plan File Workflow\n\nWrite each plan to its own markdown file under \\`${plansDir}/\\` (e.g. \\`${examplePath}\\`). Start the file with a \\`# Title\\` heading describing the plan.\n\n1. **First submission**: Write your plan to a file under \\`${plansDir}/\\` using \\`write_file\\`, then call \\`submit_plan\\` with the \\`path\\` to that file.\n2. **Reading**: Use \\`view\\` to read a plan file.\n3. **Editing**: Use \\`string_replace_lsp\\` for targeted edits to specific sections.\n\n**Reuse the same file while you keep iterating on the same plan.** Only create a NEW file when you're starting a genuinely different plan — that keeps each plan available to look back at and keeps revision diffs meaningful.\n\n## IMMEDIATE ACTION: Write plan file, then call submit_plan\n\nAs soon as your plan is complete:\n1. Write it to a file under \\`${plansDir}/\\` using \\`write_file\\`\n2. Call \\`submit_plan\\` with the \\`path\\` to that file\n\n**CRITICAL:** Do NOT generate a long text response describing your plan. The plan content belongs in the plan file, not in your text output or the \\`submit_plan\\` arguments.\n\n\\`\\`\\`javascript\nsubmit_plan({\n path: \"${examplePath}\"\n})\n\\`\\`\\`\n\nThe user will see the plan rendered inline and can:\n- **Approve** — automatically switches to Build mode for implementation\n- **Start as goal** — approves the plan and enters goal mode so the agent keeps working toward the plan until judged complete, paused, or waiting for user input\n- **Request changes** — rejects the plan; the agent stops immediately and the user provides revision feedback in their next chat message\n\n## Revision Workflow\n\nIf the user requests changes, you will be stopped immediately. Wait for their next message — it will contain their revision feedback. When you receive it:\n1. Use \\`view\\` to read the SAME plan file\n2. Use \\`string_replace_lsp\\` to make targeted edits based on feedback\n3. Use \\`view\\` to re-read the updated file\n4. Call \\`submit_plan\\` again with the same \\`path\\` — editing the file alone does NOT resubmit it\n\nThe user will see a diff of what changed between the previous and revised plan. Use \\`string_replace_lsp\\` for targeted edits so the diff is clear and meaningful — do NOT rewrite the entire plan from scratch for small changes, and do NOT move the plan to a new file for a revision.\n\nDo NOT start implementing until the plan is approved.\n`;\n}\n"],"mappings":";;;;;AASA,SAAgB,eAAe,KAAgC;CAE7D,MAAM,WAAW,yBAAyB,EAAE,kBADnB,OAAO,IAAI,OAAO,qBAAqB,WAAW,IAAI,MAAM,mBAAmB,KAAA,EAC3C,CAAC;CAC9D,MAAM,cAAc,GAAG,SAAS;CAEhC,OAAO;;;;;;;;;;;;;mFAa0E,SAAS,cAAc,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mDA8CnE,SAAS,cAAc,YAAY;;6DAEzB,SAAS;;;;;;;;;gCAStC,SAAS;;;;;;;WAO9B,YAAY;;;;;;;;;;;;;;;;;;;;;AAqBvB"}
1
+ {"version":3,"file":"plan.js","names":[],"sources":["../../../src/agents/prompts/plan.ts"],"sourcesContent":["/**\n * Plan mode prompt — read-only exploration and planning.\n */\nimport { getLocalPlansRelativeDir } from '../../utils/plans.js';\n\ninterface PlanPromptContext {\n state?: Record<string, unknown>;\n /** False when the host disabled or denied `submit_plan`; defaults to available. */\n submitPlanAvailable?: boolean;\n}\n\nexport function planModePrompt(ctx: PlanPromptContext): string {\n const factoryProjectId = typeof ctx.state?.factoryProjectId === 'string' ? ctx.state.factoryProjectId : undefined;\n const plansDir = getLocalPlansRelativeDir({ factoryProjectId });\n const examplePath = `${plansDir}/add-dark-mode.md`;\n const submitPlanAvailable = ctx.submitPlanAvailable !== false;\n\n return `\n# Plan Mode — READ-ONLY (except plan files)\n\nYou are in PLAN mode. Your job is to explore the codebase and design an implementation plan — NOT to make changes to the project.\n\n## CRITICAL: Read-Only Mode (project files)\n\nThis mode is **read-only for project files**. You must NOT modify the project.\n\n- Do NOT modify, create, or delete project files\n- Do NOT run commands that change state (no git commit, no npm install, no file creation)\n- Do NOT run build commands, tests, or scripts that have side effects\n\nThe ONE exception is plan files: you CAN create and edit markdown files inside \\`${plansDir}/\\` (e.g. \\`${examplePath}\\`). You may NOT write anywhere else.\n\nIf the user asks you to make changes while in Plan mode, explain that you're in read-only mode and they should switch to Build mode (\\`/mode build\\`) first.\n\n## Exploration Strategy\n\nBefore writing any plan, build a mental model of the codebase:\n1. Start with the directory structure (\\`view\\` on the project root or relevant subdirectory).\n2. Find the relevant entry points and core files using \\`search_content\\` and \\`find_files\\`.\n3. Read the actual code — don't assume based on file names alone.\n4. Trace data flow: where does input come from, how is it transformed, where does it go?\n5. Identify existing patterns the codebase uses (naming, structure, error handling, testing).\n\n${submitPlanAvailable ? GOAL_READY_SECTION : ''}## Your Plan Output\n\nProduce a clear, step-by-step plan with this structure:\n\n### Overview\nOne paragraph: what the change does and why.\n\n### Complexity Estimate\n- **Size**: Small (1-2 files) / Medium (3-5 files) / Large (6+ files)\n- **Risk**: Low (additive, no breaking changes) / Medium (modifies existing behavior) / High (architectural, affects many consumers)\n- **Dependencies**: List any new packages, external services, or migration steps needed.\n\n### Steps\nFor each step:\n1. **File**: path to create or modify\n2. **Change**: what to add/modify/remove, with enough specificity to implement directly\n3. **Why**: brief rationale connecting this step to the overall goal\n\n### Verification\n- What tests to run\n- What to check manually\n- What could go wrong\n\n## Plan File Workflow\n\nWrite each plan to its own markdown file under \\`${plansDir}/\\` (e.g. \\`${examplePath}\\`). Start the file with a \\`# Title\\` heading describing the plan.\n\n1. **${submitPlanAvailable ? 'First submission' : 'Writing'}**: Write your plan to a file under \\`${plansDir}/\\` using \\`write_file\\`${submitPlanAvailable ? ', then call \\`submit_plan\\` with the \\`path\\` to that file' : ''}.\n2. **Reading**: Use \\`view\\` to read a plan file.\n3. **Editing**: Use \\`string_replace_lsp\\` for targeted edits to specific sections.\n\n**Reuse the same file while you keep iterating on the same plan.** Only create a NEW file when you're starting a genuinely different plan — that keeps each plan available to look back at and keeps revision diffs meaningful.\n\n${submitPlanAvailable ? submitPlanSection(plansDir, examplePath) : planFileOnlySection(plansDir)}Do NOT start implementing until the plan is approved.\n`;\n}\n\nconst GOAL_READY_SECTION = `## Goal-Ready Plans\n\nThe submit_plan approval UI can let the user approve the plan normally or start it as a persistent goal. Write plans so they can be carried out as a goal if the user chooses that option:\n- Make the desired outcome explicit and verifiable.\n- Break work into ordered, actionable steps that can be executed autonomously.\n- Include constraints, risks, blockers, and decision points that may require user input.\n- Include concrete verification criteria so the goal judge can tell when the work is done.\n\n`;\n\nfunction submitPlanSection(plansDir: string, examplePath: string): string {\n return `## IMMEDIATE ACTION: Write plan file, then call submit_plan\n\nAs soon as your plan is complete:\n1. Write it to a file under \\`${plansDir}/\\` using \\`write_file\\`\n2. Call \\`submit_plan\\` with the \\`path\\` to that file\n\n**CRITICAL:** Do NOT generate a long text response describing your plan. The plan content belongs in the plan file, not in your text output or the \\`submit_plan\\` arguments.\n\n\\`\\`\\`javascript\nsubmit_plan({\n path: \"${examplePath}\"\n})\n\\`\\`\\`\n\nThe user will see the plan rendered inline and can:\n- **Approve** — automatically switches to Build mode for implementation\n- **Start as goal** — approves the plan and enters goal mode so the agent keeps working toward the plan until judged complete, paused, or waiting for user input\n- **Request changes** — rejects the plan; the agent stops immediately and the user provides revision feedback in their next chat message\n\n## Revision Workflow\n\nIf the user requests changes, you will be stopped immediately. Wait for their next message — it will contain their revision feedback. When you receive it:\n1. Use \\`view\\` to read the SAME plan file\n2. Use \\`string_replace_lsp\\` to make targeted edits based on feedback\n3. Use \\`view\\` to re-read the updated file\n4. Call \\`submit_plan\\` again with the same \\`path\\` — editing the file alone does NOT resubmit it\n\nThe user will see a diff of what changed between the previous and revised plan. Use \\`string_replace_lsp\\` for targeted edits so the diff is clear and meaningful — do NOT rewrite the entire plan from scratch for small changes, and do NOT move the plan to a new file for a revision.\n\n`;\n}\n\nfunction planFileOnlySection(plansDir: string): string {\n return `## When the plan is complete\n\nPlan approval is not available in this session — there is no \\`submit_plan\\` tool. As soon as your plan is complete:\n1. Write it to a file under \\`${plansDir}/\\` using \\`write_file\\`\n2. Follow any host or session instructions for handing the plan off; otherwise reply with a brief summary and the plan file path\n\n**CRITICAL:** Do NOT generate a long text response describing your plan. The plan content belongs in the plan file.\n\nWhen you receive revision feedback, edit the SAME plan file with \\`string_replace_lsp\\` rather than rewriting it or moving it to a new file.\n\n`;\n}\n"],"mappings":";;;;;AAWA,SAAgB,eAAe,KAAgC;CAE7D,MAAM,WAAW,yBAAyB,EAAE,kBADnB,OAAO,IAAI,OAAO,qBAAqB,WAAW,IAAI,MAAM,mBAAmB,KAAA,EAC3C,CAAC;CAC9D,MAAM,cAAc,GAAG,SAAS;CAChC,MAAM,sBAAsB,IAAI,wBAAwB;CAExD,OAAO;;;;;;;;;;;;;mFAa0E,SAAS,cAAc,YAAY;;;;;;;;;;;;;EAapH,sBAAsB,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;mDAyBG,SAAS,cAAc,YAAY;;OAE/E,sBAAsB,qBAAqB,UAAU,wCAAwC,SAAS,0BAA0B,sBAAsB,2DAA+D,GAAG;;;;;;EAM7N,sBAAsB,kBAAkB,UAAU,WAAW,IAAI,oBAAoB,QAAQ,EAAE;;AAEjG;AAEA,MAAM,qBAAqB;;;;;;;;;AAU3B,SAAS,kBAAkB,UAAkB,aAA6B;CACxE,OAAO;;;gCAGuB,SAAS;;;;;;;WAO9B,YAAY;;;;;;;;;;;;;;;;;;;;AAoBvB;AAEA,SAAS,oBAAoB,UAA0B;CACrD,OAAO;;;gCAGuB,SAAS;;;;;;;;AAQzC"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Agent } from '@mastra/core/agent';
2
2
  import { AgentController } from '@mastra/core/agent-controller';
3
- import type { IntervalHandler, AgentControllerConfig, AgentControllerMode, AgentControllerSubagent, Session } from '@mastra/core/agent-controller';
3
+ import type { IntervalHandler, AgentControllerConfig, AgentControllerMode, AgentControllerSubagent, BuiltinToolId, Session } from '@mastra/core/agent-controller';
4
4
  import type { PubSub } from '@mastra/core/events';
5
5
  import { Knowledge } from '@mastra/core/knowledge';
6
6
  import { Mastra } from '@mastra/core/mastra';
@@ -52,6 +52,13 @@ export interface MastraCodeConfig {
52
52
  }) => InputProcessor[] | Promise<InputProcessor[]>);
53
53
  /** Tools removed from the dynamic tool set before exposure to the model */
54
54
  disabledTools?: string[];
55
+ /**
56
+ * Agent controller built-in tools (e.g. `submit_plan`, `ask_user`) removed from
57
+ * every session. Unlike `disabledTools`, which filters Mastra Code's dynamic
58
+ * tools, this is forwarded to the AgentController. Prompt guidance for a
59
+ * disabled built-in is omitted so the model is never told to call it.
60
+ */
61
+ disableBuiltinTools?: BuiltinToolId[];
55
62
  /**
56
63
  * Custom storage config instead of auto-detected default, or a pre-built
57
64
  * store instance. An instance is used as-is: no connection test and no
@@ -183,11 +190,11 @@ export declare function createMastraCodeAgentController(config?: MastraCodeConfi
183
190
  storageMaintenance: StorageMaintenance;
184
191
  createKnowledgeInspector: (session: Session<MastraCodeState>, knowledgeKey?: string) => Promise<import("./knowledge-inspector.js").KnowledgeInspector | undefined>;
185
192
  observability: Observability;
186
- memory: ((({ requestContext }: {
193
+ memory: import("@mastra/core/types").DynamicArgument<import("@mastra/core/memory").MastraMemory> | (((ctx: {
187
194
  requestContext: RequestContext;
188
- }) => import("@mastra/memory").Memory) & {
189
- settled(): Promise<void>;
190
- }) | import("@mastra/core/types").DynamicArgument<import("@mastra/core/memory").MastraMemory> | undefined;
195
+ }) => Promise<import("@mastra/memory").Memory>) & {
196
+ settled: () => Promise<void>;
197
+ }) | undefined;
191
198
  knowledge: Knowledge | undefined;
192
199
  knowledgeKey: string;
193
200
  mcpManager: import("./mcp/manager.js").McpManager | undefined;
@@ -292,11 +299,11 @@ export declare function bootLocalAgentController(config?: MastraCodeConfig): Pro
292
299
  storageMaintenance: StorageMaintenance;
293
300
  createKnowledgeInspector: (session: Session<MastraCodeState>, knowledgeKey?: string) => Promise<import("./knowledge-inspector.js").KnowledgeInspector | undefined>;
294
301
  observability: Observability;
295
- memory: ((({ requestContext }: {
302
+ memory: import("@mastra/core/types").DynamicArgument<import("@mastra/core/memory").MastraMemory> | (((ctx: {
296
303
  requestContext: RequestContext;
297
- }) => import("@mastra/memory").Memory) & {
298
- settled(): Promise<void>;
299
- }) | import("@mastra/core/types").DynamicArgument<import("@mastra/core/memory").MastraMemory> | undefined;
304
+ }) => Promise<import("@mastra/memory").Memory>) & {
305
+ settled: () => Promise<void>;
306
+ }) | undefined;
300
307
  knowledge: Knowledge | undefined;
301
308
  knowledgeKey: string;
302
309
  mcpManager: import("./mcp/manager.js").McpManager | undefined;