@mastra/code-sdk 1.15.0-alpha.9 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp/agent.js +2 -1
- package/dist/acp/agent.js.map +1 -1
- package/dist/acp/event-mapper.js +0 -2
- package/dist/acp/event-mapper.js.map +1 -1
- package/dist/acp.js +1 -2
- package/dist/agent-connections/registry.js.map +1 -1
- package/dist/agent-connections/signal-provider.js.map +1 -1
- package/dist/agent-connections/state-processor.js.map +1 -1
- package/dist/agent-connections/tools.js +26 -24
- package/dist/agent-connections/tools.js.map +1 -1
- package/dist/agents/background-completion.d.ts +15 -2
- package/dist/agents/background-completion.d.ts.map +1 -1
- package/dist/agents/background-completion.js +19 -2
- package/dist/agents/background-completion.js.map +1 -1
- package/dist/agents/instructions.js +3 -2
- package/dist/agents/instructions.js.map +1 -1
- package/dist/agents/mastracode-gateway.js +35 -27
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/memory.d.ts.map +1 -1
- package/dist/agents/memory.js +17 -3
- package/dist/agents/memory.js.map +1 -1
- package/dist/agents/prompts/agent-instructions.js +1 -1
- package/dist/agents/prompts/agent-instructions.js.map +1 -1
- package/dist/agents/prompts/index.js +3 -2
- package/dist/agents/prompts/index.js.map +1 -1
- package/dist/agents/prompts/plan.js +2 -1
- package/dist/agents/prompts/plan.js.map +1 -1
- package/dist/agents/sandbox-filesystem.d.ts +28 -0
- package/dist/agents/sandbox-filesystem.d.ts.map +1 -1
- package/dist/agents/sandbox-filesystem.js +81 -2
- package/dist/agents/sandbox-filesystem.js.map +1 -1
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +18 -12
- package/dist/agents/tools.js.map +1 -1
- package/dist/agents/workspace.js +2 -1
- package/dist/agents/workspace.js.map +1 -1
- package/dist/auth/account-rotation-processor.js +2 -1
- package/dist/auth/account-rotation-processor.js.map +1 -1
- package/dist/auth/providers/anthropic.js +2 -2
- package/dist/auth/providers/anthropic.js.map +1 -1
- package/dist/auth/providers/github-copilot.js +1 -1
- package/dist/auth/providers/github-copilot.js.map +1 -1
- package/dist/auth/providers/openai-codex.js +3 -2
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/providers/xai.js +1 -1
- package/dist/auth/providers/xai.js.map +1 -1
- package/dist/clipboard/index.js +5 -4
- package/dist/clipboard/index.js.map +1 -1
- package/dist/evals/context-builder.js +1 -3
- package/dist/evals/context-builder.js.map +1 -1
- package/dist/evals/scorers/outcome.js +2 -1
- package/dist/evals/scorers/outcome.js.map +1 -1
- package/dist/factory/remote-controller.js.map +1 -1
- package/dist/factory/remote-session.d.ts +1 -1
- package/dist/factory/remote-session.d.ts.map +1 -1
- package/dist/factory/remote-session.js +15 -13
- package/dist/factory/remote-session.js.map +1 -1
- package/dist/headless/cli.js +2 -1
- package/dist/headless/cli.js.map +1 -1
- package/dist/headless/run-mc.js +9 -9
- package/dist/headless/run-mc.js.map +1 -1
- package/dist/hooks/executor.js +3 -1
- package/dist/hooks/executor.js.map +1 -1
- package/dist/hooks/manager.js +40 -26
- package/dist/hooks/manager.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38 -21
- package/dist/index.js.map +1 -1
- package/dist/knowledge-inspector.js +2 -2
- package/dist/knowledge-inspector.js.map +1 -1
- package/dist/lsp/server.js +4 -2
- package/dist/lsp/server.js.map +1 -1
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +14 -2
- package/dist/mcp/manager.js.map +1 -1
- package/dist/onboarding/settings.js +5 -2
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/platform/index.js +5 -3
- package/dist/platform/index.js.map +1 -1
- package/dist/plugins/loader.js +3 -1
- package/dist/plugins/loader.js.map +1 -1
- package/dist/plugins/manager.js +2 -1
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/package-link.js.map +1 -1
- package/dist/plugins/scaffold.js +4 -2
- package/dist/plugins/scaffold.js.map +1 -1
- package/dist/plugins/signal-lane.js.map +1 -1
- package/dist/process-memory-diagnostics.js +2 -1
- package/dist/process-memory-diagnostics.js.map +1 -1
- package/dist/providers/amazon-bedrock-gateway.js +2 -1
- package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
- package/dist/providers/amazon-bedrock.js +2 -2
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/claude-max.js +13 -9
- package/dist/providers/claude-max.js.map +1 -1
- package/dist/providers/github-copilot-gateway.js.map +1 -1
- package/dist/providers/github-copilot.js +13 -12
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/openai-codex.js +14 -11
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/providers/provider-thinking.js.map +1 -1
- package/dist/providers/thinking-request.js +1 -1
- package/dist/providers/thinking-request.js.map +1 -1
- package/dist/schedules/run-script.js +1 -1
- package/dist/schedules/run-script.js.map +1 -1
- package/dist/schedules/scheduler.js.map +1 -1
- package/dist/tools/request-sandbox-access.js +7 -5
- package/dist/tools/request-sandbox-access.js.map +1 -1
- package/dist/tools/utils.js +3 -1
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/web-search.d.ts +13 -11
- package/dist/tools/web-search.d.ts.map +1 -1
- package/dist/tools/web-search.js +21 -15
- package/dist/tools/web-search.js.map +1 -1
- package/dist/utils/debug-log.js +2 -2
- package/dist/utils/debug-log.js.map +1 -1
- package/dist/utils/gateway-sync.js +1 -1
- package/dist/utils/gateway-sync.js.map +1 -1
- package/dist/utils/local-observability.d.ts +1 -1
- package/dist/utils/local-observability.d.ts.map +1 -1
- package/dist/utils/local-observability.js +1 -1
- package/dist/utils/local-observability.js.map +1 -1
- package/dist/utils/prune-cli.js +10 -8
- package/dist/utils/prune-cli.js.map +1 -1
- package/dist/utils/signals-pubsub.js.map +1 -1
- package/dist/utils/slash-command-processor.js +1 -1
- package/dist/utils/slash-command-processor.js.map +1 -1
- package/dist/utils/stdin-pipe.js +1 -1
- package/dist/utils/stdin-pipe.js.map +1 -1
- package/dist/utils/storage-factory.d.ts.map +1 -1
- package/dist/utils/storage-factory.js +1 -1
- package/dist/utils/storage-factory.js.map +1 -1
- package/dist/utils/storage-maintenance.js +1 -1
- package/dist/utils/storage-maintenance.js.map +1 -1
- package/dist/utils/update-check.js +2 -1
- package/dist/utils/update-check.js.map +1 -1
- package/dist/utils/wake-session.d.ts +1 -1
- package/dist/utils/wake-session.d.ts.map +1 -1
- package/dist/utils/wake-session.js +8 -1
- package/dist/utils/wake-session.js.map +1 -1
- package/dist/workflows/register-primitives.js +1 -1
- package/dist/workflows/register-primitives.js.map +1 -1
- package/package.json +19 -19
|
@@ -271,7 +271,6 @@ async function aggregateCodexStream(response) {
|
|
|
271
271
|
break;
|
|
272
272
|
case "response.error":
|
|
273
273
|
case "error": throw new Error(`Codex stream error: ${JSON.stringify(payload.error ?? payload)}`);
|
|
274
|
-
default: break;
|
|
275
274
|
}
|
|
276
275
|
};
|
|
277
276
|
const processChunk = (chunk) => {
|
|
@@ -326,21 +325,25 @@ function openaiCodexProvider(modelId = "codex-mini-latest", options) {
|
|
|
326
325
|
const middleware = createCodexMiddleware(reasoningEffort);
|
|
327
326
|
const headers = options?.headers;
|
|
328
327
|
const baseURL = process.env.OPENAI_BASE_URL;
|
|
329
|
-
if (process.env.NODE_ENV === "test" || process.env.VITEST)
|
|
330
|
-
|
|
328
|
+
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
329
|
+
const openai = createOpenAI({
|
|
331
330
|
apiKey: "test-api-key",
|
|
332
331
|
baseURL,
|
|
333
332
|
headers
|
|
334
|
-
})
|
|
335
|
-
|
|
333
|
+
});
|
|
334
|
+
return wrapLanguageModel({
|
|
335
|
+
model: openai.responses(modelId),
|
|
336
|
+
middleware: [middleware]
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
const openai = createOpenAI({
|
|
340
|
+
apiKey: "oauth-dummy-key",
|
|
341
|
+
baseURL,
|
|
342
|
+
headers,
|
|
343
|
+
fetch: buildOpenAICodexOAuthFetch({ authStorage: options?.authStorage })
|
|
336
344
|
});
|
|
337
345
|
return wrapLanguageModel({
|
|
338
|
-
model:
|
|
339
|
-
apiKey: "oauth-dummy-key",
|
|
340
|
-
baseURL,
|
|
341
|
-
headers,
|
|
342
|
-
fetch: buildOpenAICodexOAuthFetch({ authStorage: options?.authStorage })
|
|
343
|
-
}).responses(modelId),
|
|
346
|
+
model: openai.responses(modelId),
|
|
344
347
|
middleware: [middleware]
|
|
345
348
|
});
|
|
346
349
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-codex.js","names":[],"sources":["../../src/providers/openai-codex.ts"],"sourcesContent":["/**\n * OpenAI Codex OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with ChatGPT Plus/Pro subscription.\n * This allows access to OpenAI models through the ChatGPT OAuth flow.\n *\n * Inspired by opencode's Codex plugin implementation:\n * https://github.com/sst/opencode/blob/main/packages/opencode/src/plugin/codex.ts\n */\n\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { ProviderAuthRequiredError, rejectUnauthorizedResponse } from '../auth/provider-auth-error.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\nimport { runCatalogThinkingLevel } from '../thinking-catalog.js';\nimport type { ThinkingLevelSetting } from '../thinking.js';\nexport { supportsMaxReasoningEffort } from '../thinking.js';\n\n// Codex API endpoint (not standard OpenAI API)\nconst CODEX_API_ENDPOINT = 'https://chatgpt.com/backend-api/codex/responses';\nconst CODEX_ORIGINATOR = 'mastracode';\nconst CODEX_USER_AGENT = 'mastracode';\n\n// Singleton auth storage instance (shared with claude-max.ts)\nlet authStorageInstance: AuthStorage | null = null;\n\n/**\n * Get or create the shared AuthStorage instance\n */\nexport function getAuthStorage(): AuthStorage {\n if (!authStorageInstance) {\n authStorageInstance = new AuthStorage();\n }\n return authStorageInstance;\n}\n\n/**\n * Set a custom AuthStorage instance (useful for TUI integration)\n */\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\n// Default instructions for Codex API (required)\nconst CODEX_INSTRUCTIONS = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.\n\nIMPORTANT: You should be concise, direct, and helpful. Focus on solving the user's problem efficiently.`;\n\nexport type ThinkingLevel = ThinkingLevelSetting;\n\nconst GPT5_MODEL_RE = /^gpt-5(?:\\.|-|$)/;\n\nexport function getEffectiveThinkingLevel(modelId: string, level: ThinkingLevel): ThinkingLevel {\n // GPT-5.* models on Codex require at least low reasoning.\n const requestedLevel = GPT5_MODEL_RE.test(modelId) && level === 'off' ? 'low' : level;\n return runCatalogThinkingLevel(`openai/${modelId}`, requestedLevel);\n}\n\n// Map thinkingLevel state values to OpenAI reasoningEffort values.\n// undefined means omit the parameter (no reasoning). Model-dependent fitting\n// happens in getEffectiveThinkingLevel before this lookup.\nexport const THINKING_LEVEL_TO_REASONING_EFFORT: Record<ThinkingLevel, string | undefined> = {\n off: undefined,\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'xhigh',\n max: 'max',\n};\n\n/**\n * Create Codex middleware with the given reasoning effort level.\n */\nexport function createCodexMiddleware(reasoningEffort?: string): LanguageModelMiddleware {\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n // Remove topP if temperature is set (OpenAI doesn't like both)\n if (params.temperature !== undefined && params.temperature !== null) {\n delete params.topP;\n }\n\n // Codex API requires specific settings via providerOptions\n // Use type assertion to satisfy JSONValue constraints\n params.providerOptions = {\n ...params.providerOptions,\n openai: {\n ...(params.providerOptions?.openai ?? {}),\n instructions: CODEX_INSTRUCTIONS,\n // Codex API requires store to be false\n store: false,\n // Enable reasoning for Codex models — without this, the model\n // skips the reasoning/action phase and goes straight to final_answer,\n // resulting in narration instead of tool calls.\n ...(reasoningEffort ? { reasoningEffort } : {}),\n },\n } as typeof params.providerOptions;\n\n return params;\n },\n };\n}\n\n/**\n * Create middleware that forwards a resolved reasoning effort to an\n * OpenAI-compatible provider as `providerOptions[providerKey].reasoningEffort`.\n *\n * Returns `undefined` when `reasoningEffort` is unset so callers wrap nothing\n * (e.g. thinking level `off`), leaving the wire request without a\n * `reasoning_effort` field. Unlike {@link createCodexMiddleware}, it does not\n * inject Codex-only options (instructions/store); it only sets reasoning\n * effort, keyed by the caller-supplied provider id.\n */\nexport function createReasoningEffortMiddleware(\n providerKey: string,\n reasoningEffort?: string,\n): LanguageModelMiddleware | undefined {\n if (!reasoningEffort) return undefined;\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n params.providerOptions = {\n ...params.providerOptions,\n [providerKey]: {\n ...(params.providerOptions?.[providerKey] ?? {}),\n reasoningEffort,\n },\n } as typeof params.providerOptions;\n\n return params;\n },\n };\n}\n\n/**\n * Get a live OAuth bearer token for the Codex OAuth credential.\n *\n * Refreshes the token if it's expired, and returns the credential's\n * accountId alongside the access token. Throws if the user isn't logged in\n * or if the refresh fails.\n *\n * This is the only piece of Codex auth that is genuinely shared between\n * the main agent's fetch (`buildOpenAICodexOAuthFetch`) and the Stagehand\n * fetch (`buildCodexStagehandFetch`).\n */\nasync function getCodexBearer(\n authStorage?: CredentialStore,\n): Promise<{ accessToken: string; accountId: string | undefined }> {\n const storage = authStorage ?? getAuthStorage();\n storage.reload();\n\n let cred = storage.get('openai-codex');\n if (!cred || cred.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to OpenAI Codex.');\n }\n\n if (storage.getOAuthCredential) {\n cred = await storage.getOAuthCredential('openai-codex');\n if (!cred) throw new ProviderAuthRequiredError('Failed to refresh the OpenAI Codex token.');\n } else if (Date.now() >= cred.expires) {\n const refreshedToken = await storage.getApiKey('openai-codex');\n if (!refreshedToken) {\n throw new ProviderAuthRequiredError('Failed to refresh the OpenAI Codex token.');\n }\n storage.reload();\n const reloaded = storage.get('openai-codex');\n if (!reloaded || reloaded.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to OpenAI Codex.');\n }\n cred = { ...reloaded, access: refreshedToken };\n }\n\n return { accessToken: cred.access, accountId: (cred as any).accountId as string | undefined };\n}\n\n/**\n * Build a fetch function that handles OpenAI Codex OAuth.\n * Preserves non-authorization headers from init.\n * When rewriteUrl is true (default), rewrites /v1/responses and /chat/completions\n * to the Codex API endpoint. Set rewriteUrl: false for gateway usage where the\n * SDK already targets the correct URL.\n */\nexport function buildOpenAICodexOAuthFetch(\n opts: { authStorage?: CredentialStore; rewriteUrl?: boolean } = {},\n): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const { accessToken, accountId } = await getCodexBearer(opts.authStorage);\n const { headers: initHeaders, ...requestInit } = init ?? {};\n const request = new Request(url, requestInit);\n\n // Preserve Request headers and let explicit init headers override them.\n const headers = new Headers(url instanceof Request ? url.headers : undefined);\n if (initHeaders) new Headers(initHeaders).forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('x-api-key');\n headers.set('Authorization', `Bearer ${accessToken}`);\n if (!headers.has('originator')) {\n headers.set('originator', CODEX_ORIGINATOR);\n }\n if (!headers.has('User-Agent')) {\n headers.set('User-Agent', CODEX_USER_AGENT);\n }\n if (accountId) {\n headers.set('ChatGPT-Account-ID', accountId);\n }\n\n // URL rewriting — only when rewriteUrl !== false\n const parsed = new URL(request.url);\n const shouldRewrite =\n opts.rewriteUrl !== false &&\n (parsed.pathname.includes('/v1/responses') || parsed.pathname.includes('/chat/completions'));\n const finalUrl = shouldRewrite ? new URL(CODEX_API_ENDPOINT) : parsed;\n\n const body = request.method === 'GET' || request.method === 'HEAD' ? undefined : request.body;\n const finalRequest = new Request(finalUrl, {\n method: request.method,\n headers,\n body,\n signal: request.signal,\n redirect: request.redirect,\n integrity: request.integrity,\n ...(body ? ({ duplex: 'half' } as RequestInit) : {}),\n });\n try {\n return await rejectUnauthorizedResponse(await fetch(finalRequest), {\n providerName: 'OpenAI Codex',\n requestUrl: finalUrl.toString(),\n });\n } catch (error) {\n if (error && typeof error === 'object') {\n Object.assign(error as Record<string, unknown>, {\n requestUrl: finalUrl.toString(),\n });\n }\n throw error;\n }\n }) as typeof fetch;\n}\n\n/**\n * Build a fetch function for Stagehand-on-Codex.\n *\n * The Codex backend has two requirements that AI SDK's non-streaming\n * `generateText` path doesn't naturally satisfy:\n *\n * 1. `stream: true` must be set on every request body.\n * 2. The response is delivered as Server-Sent Events; AI SDK's\n * non-streaming code path expects a single JSON body.\n *\n * This fetch forces streaming on the outgoing request, collects the SSE\n * events, and synthesizes the non-streaming JSON shape that\n * `@ai-sdk/openai`'s Responses API parser expects.\n *\n * Headers, OAuth refresh, and URL targeting are handled by the caller via\n * `baseURL` / `headers` on the AI SDK provider; this fetch only injects the\n * live OAuth bearer per call.\n */\nexport function buildCodexStagehandFetch(authStorage: AuthStorage): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n // Refresh + inject the OAuth bearer per call.\n const { accessToken } = await getCodexBearer(authStorage);\n const { headers: initHeaders, ...requestInit } = init ?? {};\n const request = new Request(url, requestInit);\n const headers = new Headers(url instanceof Request ? url.headers : undefined);\n if (initHeaders) new Headers(initHeaders).forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('x-api-key');\n headers.set('Authorization', `Bearer ${accessToken}`);\n headers.set('Accept', 'text/event-stream');\n\n // Stagehand sends JSON; force stream: true while preserving Request bodies.\n let body = await request.clone().text();\n try {\n const parsed = JSON.parse(body) as Record<string, unknown>;\n parsed.stream = true;\n body = JSON.stringify(parsed);\n if (!headers.has('content-type')) headers.set('content-type', 'application/json');\n } catch {\n // Not JSON; leave as-is.\n }\n\n const upstream = await fetch(\n new Request(request, {\n headers,\n ...(request.method === 'GET' || request.method === 'HEAD' ? {} : { body }),\n }),\n );\n if (!upstream.ok) return upstream;\n\n // Aggregate SSE -> synthesized non-streaming Response\n const aggregated = await aggregateCodexStream(upstream);\n return new Response(aggregated, {\n status: 200,\n headers: { 'content-type': 'application/json' },\n });\n }) as typeof fetch;\n}\n\n/**\n * Read an SSE Response and reduce it to a single JSON string matching the\n * non-streaming OpenAI Responses-API shape.\n *\n * Event vocabulary we care about (per OpenAI Responses API streaming):\n * - response.created → carries `response` object (id, model, usage stub)\n * - response.output_item.added/done → output items (message, reasoning, etc.)\n * - response.output_text.delta → text chunks\n * - response.completed → final `response` snapshot incl. usage\n * - response.error / error → bubble up as a thrown body\n *\n * Reasoning events (`response.reasoning_summary.*`) are intentionally ignored\n * for the non-streaming text response.\n */\nasync function aggregateCodexStream(response: Response): Promise<string> {\n if (!response.body) {\n throw new Error('Codex streaming response had no body');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n\n let finalResponse: any = null;\n let createdResponse: any = null;\n // Track output_items by index so we can rebuild the final array\n const items = new Map<number, any>();\n // Accumulate output_text deltas keyed by item_index + content_index\n const textBuffers = new Map<string, string>();\n\n const handleEvent = (event: { event?: string; data?: string }) => {\n if (!event.data || event.data === '[DONE]') return;\n let payload: any;\n try {\n payload = JSON.parse(event.data);\n } catch {\n return;\n }\n const type: string = payload.type ?? event.event ?? '';\n\n switch (type) {\n case 'response.created': {\n createdResponse = payload.response ?? createdResponse;\n break;\n }\n case 'response.output_item.added': {\n if (typeof payload.output_index === 'number' && payload.item) {\n items.set(payload.output_index, payload.item);\n }\n break;\n }\n case 'response.output_item.done': {\n if (typeof payload.output_index === 'number' && payload.item) {\n items.set(payload.output_index, payload.item);\n }\n break;\n }\n case 'response.output_text.delta': {\n const key = `${payload.output_index}:${payload.content_index ?? 0}`;\n textBuffers.set(key, (textBuffers.get(key) ?? '') + (payload.delta ?? ''));\n break;\n }\n case 'response.completed': {\n finalResponse = payload.response ?? finalResponse;\n break;\n }\n case 'response.error':\n case 'error': {\n throw new Error(`Codex stream error: ${JSON.stringify(payload.error ?? payload)}`);\n }\n default:\n // Ignore reasoning / unknown events\n break;\n }\n };\n\n // SSE parser: events separated by blank line; lines like \"event: x\" / \"data: y\"\n // Normalize CRLF→LF so \\r\\n\\r\\n event boundaries parse correctly (SSE spec allows CRLF).\n const processChunk = (chunk: string) => {\n buffer += chunk.replace(/\\r\\n/g, '\\n');\n let sepIdx: number;\n while ((sepIdx = buffer.indexOf('\\n\\n')) !== -1) {\n const raw = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + 2);\n const event: { event?: string; data?: string } = {};\n const dataLines: string[] = [];\n for (const line of raw.split('\\n')) {\n if (line.startsWith('event:')) {\n event.event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5).trimStart());\n }\n }\n if (dataLines.length > 0) {\n event.data = dataLines.join('\\n');\n }\n handleEvent(event);\n }\n };\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n processChunk(decoder.decode(value, { stream: true }));\n }\n processChunk(decoder.decode());\n } finally {\n reader.releaseLock();\n }\n\n // Stitch accumulated text deltas back into their items\n const base = finalResponse ?? createdResponse ?? { output: [] };\n const finalItems = Array.from(items.entries())\n .sort(([a], [b]) => a - b)\n .map(([index, item]) => {\n // Patch message-type items' content text using buffered deltas\n if (item?.type === 'message' && Array.isArray(item.content)) {\n item.content = item.content.map((c: any, ci: number) => {\n const key = `${index}:${ci}`;\n if (textBuffers.has(key)) {\n return { ...c, text: textBuffers.get(key) };\n }\n return c;\n });\n }\n return item;\n });\n\n base.output = finalItems.length > 0 ? finalItems : (base.output ?? []);\n\n return JSON.stringify(base);\n}\n\n/**\n * Creates an OpenAI model using ChatGPT OAuth authentication\n * Uses OAuth tokens from AuthStorage (auto-refreshes when needed)\n *\n * IMPORTANT: This uses the Codex API endpoint, not the standard OpenAI API.\n * URLs are rewritten from /v1/responses or /chat/completions to the Codex endpoint.\n */\nexport function openaiCodexProvider(\n modelId: string = 'codex-mini-latest',\n options?: { thinkingLevel?: ThinkingLevel; headers?: Record<string, string>; authStorage?: CredentialStore },\n): MastraModelConfig {\n const requestedLevel: ThinkingLevel = options?.thinkingLevel ?? 'medium';\n const effectiveLevel = getEffectiveThinkingLevel(modelId, requestedLevel);\n const reasoningEffort = THINKING_LEVEL_TO_REASONING_EFFORT[effectiveLevel];\n const middleware = createCodexMiddleware(reasoningEffort);\n const headers = options?.headers;\n\n const baseURL = process.env.OPENAI_BASE_URL;\n\n // Test environment: use API key\n if (process.env.NODE_ENV === 'test' || process.env.VITEST) {\n const openai = createOpenAI({\n apiKey: 'test-api-key',\n baseURL,\n headers,\n });\n return wrapLanguageModel({\n model: openai.responses(modelId),\n middleware: [middleware],\n });\n }\n\n const openai = createOpenAI({\n apiKey: 'oauth-dummy-key',\n baseURL,\n headers,\n fetch: buildOpenAICodexOAuthFetch({ authStorage: options?.authStorage }) as any,\n });\n\n // Use the responses API for Codex models\n // Wrap with middleware\n return wrapLanguageModel({\n model: openai.responses(modelId),\n middleware: [middleware],\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"openai-codex.js","names":[],"sources":["../../src/providers/openai-codex.ts"],"sourcesContent":["/**\n * OpenAI Codex OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with ChatGPT Plus/Pro subscription.\n * This allows access to OpenAI models through the ChatGPT OAuth flow.\n *\n * Inspired by opencode's Codex plugin implementation:\n * https://github.com/sst/opencode/blob/main/packages/opencode/src/plugin/codex.ts\n */\n\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { ProviderAuthRequiredError, rejectUnauthorizedResponse } from '../auth/provider-auth-error.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\nimport { runCatalogThinkingLevel } from '../thinking-catalog.js';\nimport type { ThinkingLevelSetting } from '../thinking.js';\nexport { supportsMaxReasoningEffort } from '../thinking.js';\n\n// Codex API endpoint (not standard OpenAI API)\nconst CODEX_API_ENDPOINT = 'https://chatgpt.com/backend-api/codex/responses';\nconst CODEX_ORIGINATOR = 'mastracode';\nconst CODEX_USER_AGENT = 'mastracode';\n\n// Singleton auth storage instance (shared with claude-max.ts)\nlet authStorageInstance: AuthStorage | null = null;\n\n/**\n * Get or create the shared AuthStorage instance\n */\nexport function getAuthStorage(): AuthStorage {\n if (!authStorageInstance) {\n authStorageInstance = new AuthStorage();\n }\n return authStorageInstance;\n}\n\n/**\n * Set a custom AuthStorage instance (useful for TUI integration)\n */\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\n// Default instructions for Codex API (required)\nconst CODEX_INSTRUCTIONS = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.\n\nIMPORTANT: You should be concise, direct, and helpful. Focus on solving the user's problem efficiently.`;\n\nexport type ThinkingLevel = ThinkingLevelSetting;\n\nconst GPT5_MODEL_RE = /^gpt-5(?:\\.|-|$)/;\n\nexport function getEffectiveThinkingLevel(modelId: string, level: ThinkingLevel): ThinkingLevel {\n // GPT-5.* models on Codex require at least low reasoning.\n const requestedLevel = GPT5_MODEL_RE.test(modelId) && level === 'off' ? 'low' : level;\n return runCatalogThinkingLevel(`openai/${modelId}`, requestedLevel);\n}\n\n// Map thinkingLevel state values to OpenAI reasoningEffort values.\n// undefined means omit the parameter (no reasoning). Model-dependent fitting\n// happens in getEffectiveThinkingLevel before this lookup.\nexport const THINKING_LEVEL_TO_REASONING_EFFORT: Record<ThinkingLevel, string | undefined> = {\n off: undefined,\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'xhigh',\n max: 'max',\n};\n\n/**\n * Create Codex middleware with the given reasoning effort level.\n */\nexport function createCodexMiddleware(reasoningEffort?: string): LanguageModelMiddleware {\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n // Remove topP if temperature is set (OpenAI doesn't like both)\n if (params.temperature !== undefined && params.temperature !== null) {\n delete params.topP;\n }\n\n // Codex API requires specific settings via providerOptions\n // Use type assertion to satisfy JSONValue constraints\n params.providerOptions = {\n ...params.providerOptions,\n openai: {\n ...(params.providerOptions?.openai ?? {}),\n instructions: CODEX_INSTRUCTIONS,\n // Codex API requires store to be false\n store: false,\n // Enable reasoning for Codex models — without this, the model\n // skips the reasoning/action phase and goes straight to final_answer,\n // resulting in narration instead of tool calls.\n ...(reasoningEffort ? { reasoningEffort } : {}),\n },\n } as typeof params.providerOptions;\n\n return params;\n },\n };\n}\n\n/**\n * Create middleware that forwards a resolved reasoning effort to an\n * OpenAI-compatible provider as `providerOptions[providerKey].reasoningEffort`.\n *\n * Returns `undefined` when `reasoningEffort` is unset so callers wrap nothing\n * (e.g. thinking level `off`), leaving the wire request without a\n * `reasoning_effort` field. Unlike {@link createCodexMiddleware}, it does not\n * inject Codex-only options (instructions/store); it only sets reasoning\n * effort, keyed by the caller-supplied provider id.\n */\nexport function createReasoningEffortMiddleware(\n providerKey: string,\n reasoningEffort?: string,\n): LanguageModelMiddleware | undefined {\n if (!reasoningEffort) return undefined;\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n params.providerOptions = {\n ...params.providerOptions,\n [providerKey]: {\n ...(params.providerOptions?.[providerKey] ?? {}),\n reasoningEffort,\n },\n } as typeof params.providerOptions;\n\n return params;\n },\n };\n}\n\n/**\n * Get a live OAuth bearer token for the Codex OAuth credential.\n *\n * Refreshes the token if it's expired, and returns the credential's\n * accountId alongside the access token. Throws if the user isn't logged in\n * or if the refresh fails.\n *\n * This is the only piece of Codex auth that is genuinely shared between\n * the main agent's fetch (`buildOpenAICodexOAuthFetch`) and the Stagehand\n * fetch (`buildCodexStagehandFetch`).\n */\nasync function getCodexBearer(\n authStorage?: CredentialStore,\n): Promise<{ accessToken: string; accountId: string | undefined }> {\n const storage = authStorage ?? getAuthStorage();\n storage.reload();\n\n let cred = storage.get('openai-codex');\n if (!cred || cred.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to OpenAI Codex.');\n }\n\n if (storage.getOAuthCredential) {\n cred = await storage.getOAuthCredential('openai-codex');\n if (!cred) throw new ProviderAuthRequiredError('Failed to refresh the OpenAI Codex token.');\n } else if (Date.now() >= cred.expires) {\n const refreshedToken = await storage.getApiKey('openai-codex');\n if (!refreshedToken) {\n throw new ProviderAuthRequiredError('Failed to refresh the OpenAI Codex token.');\n }\n storage.reload();\n const reloaded = storage.get('openai-codex');\n if (!reloaded || reloaded.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to OpenAI Codex.');\n }\n cred = { ...reloaded, access: refreshedToken };\n }\n\n return { accessToken: cred.access, accountId: (cred as any).accountId as string | undefined };\n}\n\n/**\n * Build a fetch function that handles OpenAI Codex OAuth.\n * Preserves non-authorization headers from init.\n * When rewriteUrl is true (default), rewrites /v1/responses and /chat/completions\n * to the Codex API endpoint. Set rewriteUrl: false for gateway usage where the\n * SDK already targets the correct URL.\n */\nexport function buildOpenAICodexOAuthFetch(\n opts: { authStorage?: CredentialStore; rewriteUrl?: boolean } = {},\n): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const { accessToken, accountId } = await getCodexBearer(opts.authStorage);\n const { headers: initHeaders, ...requestInit } = init ?? {};\n const request = new Request(url, requestInit);\n\n // Preserve Request headers and let explicit init headers override them.\n const headers = new Headers(url instanceof Request ? url.headers : undefined);\n if (initHeaders) new Headers(initHeaders).forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('x-api-key');\n headers.set('Authorization', `Bearer ${accessToken}`);\n if (!headers.has('originator')) {\n headers.set('originator', CODEX_ORIGINATOR);\n }\n if (!headers.has('User-Agent')) {\n headers.set('User-Agent', CODEX_USER_AGENT);\n }\n if (accountId) {\n headers.set('ChatGPT-Account-ID', accountId);\n }\n\n // URL rewriting — only when rewriteUrl !== false\n const parsed = new URL(request.url);\n const shouldRewrite =\n opts.rewriteUrl !== false &&\n (parsed.pathname.includes('/v1/responses') || parsed.pathname.includes('/chat/completions'));\n const finalUrl = shouldRewrite ? new URL(CODEX_API_ENDPOINT) : parsed;\n\n const body = request.method === 'GET' || request.method === 'HEAD' ? undefined : request.body;\n const finalRequest = new Request(finalUrl, {\n method: request.method,\n headers,\n body,\n signal: request.signal,\n redirect: request.redirect,\n integrity: request.integrity,\n ...(body ? ({ duplex: 'half' } as RequestInit) : {}),\n });\n try {\n return await rejectUnauthorizedResponse(await fetch(finalRequest), {\n providerName: 'OpenAI Codex',\n requestUrl: finalUrl.toString(),\n });\n } catch (error) {\n if (error && typeof error === 'object') {\n Object.assign(error as Record<string, unknown>, {\n requestUrl: finalUrl.toString(),\n });\n }\n throw error;\n }\n }) as typeof fetch;\n}\n\n/**\n * Build a fetch function for Stagehand-on-Codex.\n *\n * The Codex backend has two requirements that AI SDK's non-streaming\n * `generateText` path doesn't naturally satisfy:\n *\n * 1. `stream: true` must be set on every request body.\n * 2. The response is delivered as Server-Sent Events; AI SDK's\n * non-streaming code path expects a single JSON body.\n *\n * This fetch forces streaming on the outgoing request, collects the SSE\n * events, and synthesizes the non-streaming JSON shape that\n * `@ai-sdk/openai`'s Responses API parser expects.\n *\n * Headers, OAuth refresh, and URL targeting are handled by the caller via\n * `baseURL` / `headers` on the AI SDK provider; this fetch only injects the\n * live OAuth bearer per call.\n */\nexport function buildCodexStagehandFetch(authStorage: AuthStorage): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n // Refresh + inject the OAuth bearer per call.\n const { accessToken } = await getCodexBearer(authStorage);\n const { headers: initHeaders, ...requestInit } = init ?? {};\n const request = new Request(url, requestInit);\n const headers = new Headers(url instanceof Request ? url.headers : undefined);\n if (initHeaders) new Headers(initHeaders).forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('x-api-key');\n headers.set('Authorization', `Bearer ${accessToken}`);\n headers.set('Accept', 'text/event-stream');\n\n // Stagehand sends JSON; force stream: true while preserving Request bodies.\n let body = await request.clone().text();\n try {\n const parsed = JSON.parse(body) as Record<string, unknown>;\n parsed.stream = true;\n body = JSON.stringify(parsed);\n if (!headers.has('content-type')) headers.set('content-type', 'application/json');\n } catch {\n // Not JSON; leave as-is.\n }\n\n const upstream = await fetch(\n new Request(request, {\n headers,\n ...(request.method === 'GET' || request.method === 'HEAD' ? {} : { body }),\n }),\n );\n if (!upstream.ok) return upstream;\n\n // Aggregate SSE -> synthesized non-streaming Response\n const aggregated = await aggregateCodexStream(upstream);\n return new Response(aggregated, {\n status: 200,\n headers: { 'content-type': 'application/json' },\n });\n }) as typeof fetch;\n}\n\n/**\n * Read an SSE Response and reduce it to a single JSON string matching the\n * non-streaming OpenAI Responses-API shape.\n *\n * Event vocabulary we care about (per OpenAI Responses API streaming):\n * - response.created → carries `response` object (id, model, usage stub)\n * - response.output_item.added/done → output items (message, reasoning, etc.)\n * - response.output_text.delta → text chunks\n * - response.completed → final `response` snapshot incl. usage\n * - response.error / error → bubble up as a thrown body\n *\n * Reasoning events (`response.reasoning_summary.*`) are intentionally ignored\n * for the non-streaming text response.\n */\nasync function aggregateCodexStream(response: Response): Promise<string> {\n if (!response.body) {\n throw new Error('Codex streaming response had no body');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n\n let finalResponse: any = null;\n let createdResponse: any = null;\n // Track output_items by index so we can rebuild the final array\n const items = new Map<number, any>();\n // Accumulate output_text deltas keyed by item_index + content_index\n const textBuffers = new Map<string, string>();\n\n const handleEvent = (event: { event?: string; data?: string }) => {\n if (!event.data || event.data === '[DONE]') return;\n let payload: any;\n try {\n payload = JSON.parse(event.data);\n } catch {\n return;\n }\n const type: string = payload.type ?? event.event ?? '';\n\n switch (type) {\n case 'response.created': {\n createdResponse = payload.response ?? createdResponse;\n break;\n }\n case 'response.output_item.added': {\n if (typeof payload.output_index === 'number' && payload.item) {\n items.set(payload.output_index, payload.item);\n }\n break;\n }\n case 'response.output_item.done': {\n if (typeof payload.output_index === 'number' && payload.item) {\n items.set(payload.output_index, payload.item);\n }\n break;\n }\n case 'response.output_text.delta': {\n const key = `${payload.output_index}:${payload.content_index ?? 0}`;\n textBuffers.set(key, (textBuffers.get(key) ?? '') + (payload.delta ?? ''));\n break;\n }\n case 'response.completed': {\n finalResponse = payload.response ?? finalResponse;\n break;\n }\n case 'response.error':\n case 'error': {\n throw new Error(`Codex stream error: ${JSON.stringify(payload.error ?? payload)}`);\n }\n default:\n // Ignore reasoning / unknown events\n break;\n }\n };\n\n // SSE parser: events separated by blank line; lines like \"event: x\" / \"data: y\"\n // Normalize CRLF→LF so \\r\\n\\r\\n event boundaries parse correctly (SSE spec allows CRLF).\n const processChunk = (chunk: string) => {\n buffer += chunk.replace(/\\r\\n/g, '\\n');\n let sepIdx: number;\n while ((sepIdx = buffer.indexOf('\\n\\n')) !== -1) {\n const raw = buffer.slice(0, sepIdx);\n buffer = buffer.slice(sepIdx + 2);\n const event: { event?: string; data?: string } = {};\n const dataLines: string[] = [];\n for (const line of raw.split('\\n')) {\n if (line.startsWith('event:')) {\n event.event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5).trimStart());\n }\n }\n if (dataLines.length > 0) {\n event.data = dataLines.join('\\n');\n }\n handleEvent(event);\n }\n };\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n processChunk(decoder.decode(value, { stream: true }));\n }\n processChunk(decoder.decode());\n } finally {\n reader.releaseLock();\n }\n\n // Stitch accumulated text deltas back into their items\n const base = finalResponse ?? createdResponse ?? { output: [] };\n const finalItems = Array.from(items.entries())\n .sort(([a], [b]) => a - b)\n .map(([index, item]) => {\n // Patch message-type items' content text using buffered deltas\n if (item?.type === 'message' && Array.isArray(item.content)) {\n item.content = item.content.map((c: any, ci: number) => {\n const key = `${index}:${ci}`;\n if (textBuffers.has(key)) {\n return { ...c, text: textBuffers.get(key) };\n }\n return c;\n });\n }\n return item;\n });\n\n base.output = finalItems.length > 0 ? finalItems : (base.output ?? []);\n\n return JSON.stringify(base);\n}\n\n/**\n * Creates an OpenAI model using ChatGPT OAuth authentication\n * Uses OAuth tokens from AuthStorage (auto-refreshes when needed)\n *\n * IMPORTANT: This uses the Codex API endpoint, not the standard OpenAI API.\n * URLs are rewritten from /v1/responses or /chat/completions to the Codex endpoint.\n */\nexport function openaiCodexProvider(\n modelId: string = 'codex-mini-latest',\n options?: { thinkingLevel?: ThinkingLevel; headers?: Record<string, string>; authStorage?: CredentialStore },\n): MastraModelConfig {\n const requestedLevel: ThinkingLevel = options?.thinkingLevel ?? 'medium';\n const effectiveLevel = getEffectiveThinkingLevel(modelId, requestedLevel);\n const reasoningEffort = THINKING_LEVEL_TO_REASONING_EFFORT[effectiveLevel];\n const middleware = createCodexMiddleware(reasoningEffort);\n const headers = options?.headers;\n\n const baseURL = process.env.OPENAI_BASE_URL;\n\n // Test environment: use API key\n if (process.env.NODE_ENV === 'test' || process.env.VITEST) {\n const openai = createOpenAI({\n apiKey: 'test-api-key',\n baseURL,\n headers,\n });\n return wrapLanguageModel({\n model: openai.responses(modelId),\n middleware: [middleware],\n });\n }\n\n const openai = createOpenAI({\n apiKey: 'oauth-dummy-key',\n baseURL,\n headers,\n fetch: buildOpenAICodexOAuthFetch({ authStorage: options?.authStorage }) as any,\n });\n\n // Use the responses API for Codex models\n // Wrap with middleware\n return wrapLanguageModel({\n model: openai.responses(modelId),\n middleware: [middleware],\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-thinking.js","names":[
|
|
1
|
+
{"version":3,"file":"provider-thinking.js","names":[],"sources":["../../src/providers/provider-thinking.ts"],"sourcesContent":["import { getModelReasoningOptions, ModelRouterLanguageModel } from '@mastra/core/llm';\nimport { wrapLanguageModel } from 'ai';\nimport type { JSONValue } from 'ai';\nimport { requestableReasoningOptions, runThinkingLevel } from '../thinking.js';\nimport type { ThinkingLevelSetting } from '../thinking.js';\nimport { thinkingRequestFormatFor } from './thinking-request.js';\n\ntype ProviderOptions = Record<string, Record<string, JSONValue | undefined>>;\n\nexport interface ProviderThinkingOptions {\n optionsKey: string;\n options: Record<string, JSONValue>;\n controlKeys: ReadonlySet<string>;\n}\n\nexport function providerThinkingOptions(\n routedModelId: string,\n level: ThinkingLevelSetting | undefined,\n): ProviderThinkingOptions | undefined {\n const format = thinkingRequestFormatFor(routedModelId);\n if (!format || !level) return undefined;\n const catalogOptions = getModelReasoningOptions(routedModelId);\n const requestable = requestableReasoningOptions(routedModelId, catalogOptions);\n const runLevel = runThinkingLevel(routedModelId, level, catalogOptions);\n if (!requestable?.length || runLevel === 'off') return undefined;\n const effortOptions = format.sendEffort?.(runLevel);\n const sendsEffort = requestable.some(option => option.type === 'effort');\n const options = sendsEffort ? effortOptions : format.enableThinking;\n if (!options) return undefined;\n const controlKeys = new Set([...Object.keys(effortOptions ?? {}), ...Object.keys(format.enableThinking ?? {})]);\n return { optionsKey: format.optionsKey, options, controlKeys };\n}\n\nfunction withThinkingOptions<CallOptions extends { providerOptions?: ProviderOptions }>(\n callOptions: CallOptions,\n thinking: ProviderThinkingOptions | undefined,\n): CallOptions {\n if (!thinking) return callOptions;\n const callerOptions = callOptions.providerOptions?.[thinking.optionsKey] ?? {};\n const callerSetThinking = Object.keys(callerOptions).some(key => thinking.controlKeys.has(key));\n if (callerSetThinking) return callOptions;\n return {\n ...callOptions,\n providerOptions: {\n ...callOptions.providerOptions,\n [thinking.optionsKey]: { ...callerOptions, ...thinking.options },\n },\n };\n}\n\ntype AiSdkLanguageModel = Parameters<typeof wrapLanguageModel>[0]['model'];\n\nexport function withThinkingOptionsModel(\n model: AiSdkLanguageModel,\n thinking: ProviderThinkingOptions | undefined,\n): AiSdkLanguageModel {\n if (!thinking) return model;\n return wrapLanguageModel({\n model,\n middleware: {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => withThinkingOptions(params, thinking),\n },\n });\n}\n\ntype RouterConfig = ConstructorParameters<typeof ModelRouterLanguageModel>[0];\ntype RouterCallOptions = Parameters<ModelRouterLanguageModel['doStream']>[0];\ntype RouterCallResult = ReturnType<ModelRouterLanguageModel['doStream']>;\n\nexport class ModelRouterLanguageModelWithThinking extends ModelRouterLanguageModel {\n readonly #thinking: ProviderThinkingOptions | undefined;\n\n constructor(config: RouterConfig, thinking: ProviderThinkingOptions | undefined) {\n super(config);\n this.#thinking = thinking;\n }\n\n override doGenerate(options: RouterCallOptions): RouterCallResult {\n return super.doGenerate(withThinkingOptions(options, this.#thinking));\n }\n\n override doStream(options: RouterCallOptions): RouterCallResult {\n return super.doStream(withThinkingOptions(options, this.#thinking));\n }\n}\n"],"mappings":";;;;;AAeA,SAAgB,wBACd,eACA,OACqC;CACrC,MAAM,SAAS,yBAAyB,aAAa;CACrD,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO,KAAA;CAC9B,MAAM,iBAAiB,yBAAyB,aAAa;CAC7D,MAAM,cAAc,4BAA4B,eAAe,cAAc;CAC7E,MAAM,WAAW,iBAAiB,eAAe,OAAO,cAAc;CACtE,IAAI,CAAC,aAAa,UAAU,aAAa,OAAO,OAAO,KAAA;CACvD,MAAM,gBAAgB,OAAO,aAAa,QAAQ;CAElD,MAAM,UADc,YAAY,MAAK,WAAU,OAAO,SAAS,QACrC,IAAI,gBAAgB,OAAO;CACrD,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,MAAM,8BAAc,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,iBAAiB,CAAC,CAAC,GAAG,GAAG,OAAO,KAAK,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC;CAC9G,OAAO;EAAE,YAAY,OAAO;EAAY;EAAS;CAAY;AAC/D;AAEA,SAAS,oBACP,aACA,UACa;CACb,IAAI,CAAC,UAAU,OAAO;CACtB,MAAM,gBAAgB,YAAY,kBAAkB,SAAS,eAAe,CAAC;CAE7E,IAD0B,OAAO,KAAK,aAAa,CAAC,CAAC,MAAK,QAAO,SAAS,YAAY,IAAI,GAAG,CACzE,GAAG,OAAO;CAC9B,OAAO;EACL,GAAG;EACH,iBAAiB;GACf,GAAG,YAAY;IACd,SAAS,aAAa;IAAE,GAAG;IAAe,GAAG,SAAS;GAAQ;EACjE;CACF;AACF;AAIA,SAAgB,yBACd,OACA,UACoB;CACpB,IAAI,CAAC,UAAU,OAAO;CACtB,OAAO,kBAAkB;EACvB;EACA,YAAY;GACV,sBAAsB;GACtB,iBAAiB,OAAO,EAAE,aAAa,oBAAoB,QAAQ,QAAQ;EAC7E;CACF,CAAC;AACH;AAMA,IAAa,uCAAb,cAA0D,yBAAyB;CACjF;CAEA,YAAY,QAAsB,UAA+C;EAC/E,MAAM,MAAM;EACZ,KAAK,YAAY;CACnB;CAEA,WAAoB,SAA8C;EAChE,OAAO,MAAM,WAAW,oBAAoB,SAAS,KAAK,SAAS,CAAC;CACtE;CAEA,SAAkB,SAA8C;EAC9D,OAAO,MAAM,SAAS,oBAAoB,SAAS,KAAK,SAAS,CAAC;CACpE;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinking-request.js","names":[],"sources":["../../src/providers/thinking-request.ts"],"sourcesContent":["import type { JSONValue } from 'ai';\nimport type { ActiveThinkingLevel } from '../thinking.js';\nimport { MASTRA_GATEWAY_PREFIX, stripMastraGatewayPrefix } from './model-ids.js';\n\ntype ThinkingProviderOptions = Record<string, JSONValue>;\n\nexport interface ThinkingRequestFormat {\n optionsKey: string;\n sendEffort?: (effort: ActiveThinkingLevel) => ThinkingProviderOptions;\n acceptedEfforts?: readonly ActiveThinkingLevel[];\n enableThinking?: ThinkingProviderOptions;\n}\n\nconst LOW_TO_HIGH: readonly ActiveThinkingLevel[] = ['low', 'medium', 'high'];\nconst sendReasoningEffort = (reasoningEffort: ActiveThinkingLevel): ThinkingProviderOptions => ({ reasoningEffort });\nconst ENABLE_REASONING: ThinkingProviderOptions = { reasoning: { enabled: true } };\n\nconst OPENROUTER_FORMAT: ThinkingRequestFormat = {\n optionsKey: 'openrouter',\n sendEffort: effort => ({ reasoning: { effort } }),\n enableThinking: ENABLE_REASONING,\n};\n\nconst REQUEST_FORMATS_BY_PROVIDER: Partial<Record<string, ThinkingRequestFormat>> = {\n deepseek: {\n optionsKey: 'deepseek',\n sendEffort: sendReasoningEffort,\n enableThinking: { thinking: { type: 'enabled' } },\n },\n openrouter: OPENROUTER_FORMAT,\n alibaba: { optionsKey: 'alibaba', enableThinking: { enableThinking: true } },\n groq: { optionsKey: 'groq', sendEffort: sendReasoningEffort, acceptedEfforts: LOW_TO_HIGH },\n mistral: { optionsKey: 'mistral', sendEffort: sendReasoningEffort, acceptedEfforts: ['high'] },\n xai: { optionsKey: 'xai', sendEffort: sendReasoningEffort, acceptedEfforts: LOW_TO_HIGH },\n togetherai: {\n optionsKey: 'togetherai',\n sendEffort: sendReasoningEffort,\n acceptedEfforts: LOW_TO_HIGH,\n enableThinking: ENABLE_REASONING,\n },\n deepinfra: {\n optionsKey: 'deepinfra',\n sendEffort: sendReasoningEffort,\n acceptedEfforts: LOW_TO_HIGH,\n enableThinking: ENABLE_REASONING,\n },\n cerebras: { optionsKey: 'cerebras', sendEffort: sendReasoningEffort },\n perplexity: { optionsKey: 'perplexity', sendEffort: effort => ({ reasoning_effort: effort }) },\n};\n\nconst THINKING_ADAPTER_PROVIDERS: ReadonlySet<string> = new Set(['anthropic', 'openai', 'google']);\n\nexport function isThinkingAdapterProvider(provider: string): boolean {\n return THINKING_ADAPTER_PROVIDERS.has(provider);\n}\n\nconst PROVIDERS_ROUTED_WITHOUT_THINKING: ReadonlySet<string> = new Set([\n 'github-copilot',\n 'kimi-for-coding',\n 'moonshotai',\n 'amazon-bedrock',\n]);\n\nexport function isRoutedWithoutThinking(modelId: string): boolean {\n const [provider = ''] = stripMastraGatewayPrefix(modelId).split('/');\n return PROVIDERS_ROUTED_WITHOUT_THINKING.has(provider);\n}\n\nexport function thinkingRequestFormatFor(modelId: string): ThinkingRequestFormat | undefined {\n const routedThroughMastraGateway = modelId.startsWith(MASTRA_GATEWAY_PREFIX);\n const [provider = ''] = stripMastraGatewayPrefix(modelId).split('/');\n if (isThinkingAdapterProvider(provider)) return undefined;\n if (routedThroughMastraGateway) return OPENROUTER_FORMAT;\n return REQUEST_FORMATS_BY_PROVIDER[provider.includes('alibaba') ? 'alibaba' : provider];\n}\n"],"mappings":";;AAaA,MAAM,cAA8C;CAAC;CAAO;CAAU;AAAM;AAC5E,MAAM,uBAAuB,qBAAmE,EAAE,gBAAgB;AAClH,MAAM,mBAA4C,EAAE,WAAW,EAAE,SAAS,KAAK,EAAE;AAEjF,MAAM,oBAA2C;CAC/C,YAAY;CACZ,aAAY,YAAW,EAAE,WAAW,EAAE,OAAO,EAAE;CAC/C,gBAAgB;AAClB;AAEA,MAAM,8BAA8E;CAClF,UAAU;EACR,YAAY;EACZ,YAAY;EACZ,gBAAgB,EAAE,UAAU,EAAE,MAAM,UAAU,EAAE;CAClD;CACA,YAAY;CACZ,SAAS;EAAE,YAAY;EAAW,gBAAgB,EAAE,gBAAgB,KAAK;CAAE;CAC3E,MAAM;EAAE,YAAY;EAAQ,YAAY;EAAqB,iBAAiB;CAAY;CAC1F,SAAS;EAAE,YAAY;EAAW,YAAY;EAAqB,iBAAiB,CAAC,MAAM;CAAE;CAC7F,KAAK;EAAE,YAAY;EAAO,YAAY;EAAqB,iBAAiB;
|
|
1
|
+
{"version":3,"file":"thinking-request.js","names":[],"sources":["../../src/providers/thinking-request.ts"],"sourcesContent":["import type { JSONValue } from 'ai';\nimport type { ActiveThinkingLevel } from '../thinking.js';\nimport { MASTRA_GATEWAY_PREFIX, stripMastraGatewayPrefix } from './model-ids.js';\n\ntype ThinkingProviderOptions = Record<string, JSONValue>;\n\nexport interface ThinkingRequestFormat {\n optionsKey: string;\n sendEffort?: (effort: ActiveThinkingLevel) => ThinkingProviderOptions;\n acceptedEfforts?: readonly ActiveThinkingLevel[];\n enableThinking?: ThinkingProviderOptions;\n}\n\nconst LOW_TO_HIGH: readonly ActiveThinkingLevel[] = ['low', 'medium', 'high'];\nconst sendReasoningEffort = (reasoningEffort: ActiveThinkingLevel): ThinkingProviderOptions => ({ reasoningEffort });\nconst ENABLE_REASONING: ThinkingProviderOptions = { reasoning: { enabled: true } };\n\nconst OPENROUTER_FORMAT: ThinkingRequestFormat = {\n optionsKey: 'openrouter',\n sendEffort: effort => ({ reasoning: { effort } }),\n enableThinking: ENABLE_REASONING,\n};\n\nconst REQUEST_FORMATS_BY_PROVIDER: Partial<Record<string, ThinkingRequestFormat>> = {\n deepseek: {\n optionsKey: 'deepseek',\n sendEffort: sendReasoningEffort,\n enableThinking: { thinking: { type: 'enabled' } },\n },\n openrouter: OPENROUTER_FORMAT,\n alibaba: { optionsKey: 'alibaba', enableThinking: { enableThinking: true } },\n groq: { optionsKey: 'groq', sendEffort: sendReasoningEffort, acceptedEfforts: LOW_TO_HIGH },\n mistral: { optionsKey: 'mistral', sendEffort: sendReasoningEffort, acceptedEfforts: ['high'] },\n xai: { optionsKey: 'xai', sendEffort: sendReasoningEffort, acceptedEfforts: [...LOW_TO_HIGH, 'xhigh'] },\n togetherai: {\n optionsKey: 'togetherai',\n sendEffort: sendReasoningEffort,\n acceptedEfforts: LOW_TO_HIGH,\n enableThinking: ENABLE_REASONING,\n },\n deepinfra: {\n optionsKey: 'deepinfra',\n sendEffort: sendReasoningEffort,\n acceptedEfforts: LOW_TO_HIGH,\n enableThinking: ENABLE_REASONING,\n },\n cerebras: { optionsKey: 'cerebras', sendEffort: sendReasoningEffort },\n perplexity: { optionsKey: 'perplexity', sendEffort: effort => ({ reasoning_effort: effort }) },\n};\n\nconst THINKING_ADAPTER_PROVIDERS: ReadonlySet<string> = new Set(['anthropic', 'openai', 'google']);\n\nexport function isThinkingAdapterProvider(provider: string): boolean {\n return THINKING_ADAPTER_PROVIDERS.has(provider);\n}\n\nconst PROVIDERS_ROUTED_WITHOUT_THINKING: ReadonlySet<string> = new Set([\n 'github-copilot',\n 'kimi-for-coding',\n 'moonshotai',\n 'amazon-bedrock',\n]);\n\nexport function isRoutedWithoutThinking(modelId: string): boolean {\n const [provider = ''] = stripMastraGatewayPrefix(modelId).split('/');\n return PROVIDERS_ROUTED_WITHOUT_THINKING.has(provider);\n}\n\nexport function thinkingRequestFormatFor(modelId: string): ThinkingRequestFormat | undefined {\n const routedThroughMastraGateway = modelId.startsWith(MASTRA_GATEWAY_PREFIX);\n const [provider = ''] = stripMastraGatewayPrefix(modelId).split('/');\n if (isThinkingAdapterProvider(provider)) return undefined;\n if (routedThroughMastraGateway) return OPENROUTER_FORMAT;\n return REQUEST_FORMATS_BY_PROVIDER[provider.includes('alibaba') ? 'alibaba' : provider];\n}\n"],"mappings":";;AAaA,MAAM,cAA8C;CAAC;CAAO;CAAU;AAAM;AAC5E,MAAM,uBAAuB,qBAAmE,EAAE,gBAAgB;AAClH,MAAM,mBAA4C,EAAE,WAAW,EAAE,SAAS,KAAK,EAAE;AAEjF,MAAM,oBAA2C;CAC/C,YAAY;CACZ,aAAY,YAAW,EAAE,WAAW,EAAE,OAAO,EAAE;CAC/C,gBAAgB;AAClB;AAEA,MAAM,8BAA8E;CAClF,UAAU;EACR,YAAY;EACZ,YAAY;EACZ,gBAAgB,EAAE,UAAU,EAAE,MAAM,UAAU,EAAE;CAClD;CACA,YAAY;CACZ,SAAS;EAAE,YAAY;EAAW,gBAAgB,EAAE,gBAAgB,KAAK;CAAE;CAC3E,MAAM;EAAE,YAAY;EAAQ,YAAY;EAAqB,iBAAiB;CAAY;CAC1F,SAAS;EAAE,YAAY;EAAW,YAAY;EAAqB,iBAAiB,CAAC,MAAM;CAAE;CAC7F,KAAK;EAAE,YAAY;EAAO,YAAY;EAAqB,iBAAiB,CAAC,GAAG,aAAa,OAAO;CAAE;CACtG,YAAY;EACV,YAAY;EACZ,YAAY;EACZ,iBAAiB;EACjB,gBAAgB;CAClB;CACA,WAAW;EACT,YAAY;EACZ,YAAY;EACZ,iBAAiB;EACjB,gBAAgB;CAClB;CACA,UAAU;EAAE,YAAY;EAAY,YAAY;CAAoB;CACpE,YAAY;EAAE,YAAY;EAAc,aAAY,YAAW,EAAE,kBAAkB,OAAO;CAAG;AAC/F;AAEA,MAAM,6CAAkD,IAAI,IAAI;CAAC;CAAa;CAAU;AAAQ,CAAC;AAEjG,SAAgB,0BAA0B,UAA2B;CACnE,OAAO,2BAA2B,IAAI,QAAQ;AAChD;AAEA,MAAM,oDAAyD,IAAI,IAAI;CACrE;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,wBAAwB,SAA0B;CAChE,MAAM,CAAC,WAAW,MAAM,yBAAyB,OAAO,CAAC,CAAC,MAAM,GAAG;CACnE,OAAO,kCAAkC,IAAI,QAAQ;AACvD;AAEA,SAAgB,yBAAyB,SAAoD;CAC3F,MAAM,6BAA6B,QAAQ,WAAW,qBAAqB;CAC3E,MAAM,CAAC,WAAW,MAAM,yBAAyB,OAAO,CAAC,CAAC,MAAM,GAAG;CACnE,IAAI,0BAA0B,QAAQ,GAAG,OAAO,KAAA;CAChD,IAAI,4BAA4B,OAAO;CACvC,OAAO,4BAA4B,SAAS,SAAS,SAAS,IAAI,YAAY;AAChF"}
|
|
@@ -18,7 +18,7 @@ const INTERPRETERS = {
|
|
|
18
18
|
".ts": () => process.execPath,
|
|
19
19
|
".py": () => "python3"
|
|
20
20
|
};
|
|
21
|
-
const MAX_OUTPUT_BYTES =
|
|
21
|
+
const MAX_OUTPUT_BYTES = 1048576;
|
|
22
22
|
function isExecutableFile(absPath) {
|
|
23
23
|
try {
|
|
24
24
|
fs$1.accessSync(absPath, fs$1.constants.X_OK);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-script.js","names":["fs","path"],"sources":["../../src/schedules/run-script.ts"],"sourcesContent":["/**\n * Runs a schedule's script file at fire time.\n *\n * The file path is always passed as the literal command (or as the single\n * argument to a fixed interpreter) — never through a shell string — so the\n * user's extra prompt can never be interpolated into a command line.\n */\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { RunScript, ScriptResult } from './prompt.js';\n\n/** Interpreter for scripts that lack an executable bit, keyed by extension. */\nconst INTERPRETERS: Record<string, () => string> = {\n '.sh': () => '/bin/sh',\n '.js': () => process.execPath,\n '.mjs': () => process.execPath,\n '.cjs': () => process.execPath,\n '.ts': () => process.execPath,\n '.py': () => 'python3',\n};\n\nconst MAX_OUTPUT_BYTES = 1024 * 1024;\n\nexport function isExecutableFile(absPath: string): boolean {\n try {\n fs.accessSync(absPath, fs.constants.X_OK);\n return fs.statSync(absPath).isFile();\n } catch {\n return false;\n }\n}\n\nexport function resolveScriptCommand(absPath: string): { command: string; args: string[] } {\n if (isExecutableFile(absPath)) return { command: absPath, args: [] };\n const interpreter = INTERPRETERS[path.extname(absPath).toLowerCase()];\n if (!interpreter) {\n throw new Error(`${absPath} is not executable and has no known interpreter`);\n }\n return { command: interpreter(), args: [absPath] };\n}\n\nexport const runScript: RunScript = async (absPath, { cwd, timeoutMs }) => {\n const { command, args } = resolveScriptCommand(absPath);\n return new Promise<ScriptResult>((resolve, reject) => {\n execFile(\n command,\n args,\n { cwd, timeout: timeoutMs, maxBuffer: MAX_OUTPUT_BYTES, windowsHide: true },\n (error, stdout, stderr) => {\n if (!error) {\n resolve({ stdout, stderr, exitCode: 0 });\n return;\n }\n const spawnError = error as NodeJS.ErrnoException & { code?: number | string; killed?: boolean };\n if (typeof spawnError.code === 'number') {\n resolve({ stdout, stderr, exitCode: spawnError.code });\n return;\n }\n if (spawnError.killed) {\n resolve({ stdout, stderr: `${stderr}\\n[timed out after ${timeoutMs}ms]`.trim(), exitCode: null });\n return;\n }\n reject(error);\n },\n );\n });\n};\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,eAA6C;CACjD,aAAa;CACb,aAAa,QAAQ;CACrB,cAAc,QAAQ;CACtB,cAAc,QAAQ;CACtB,aAAa,QAAQ;CACrB,aAAa;AACf;AAEA,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"run-script.js","names":["fs","path"],"sources":["../../src/schedules/run-script.ts"],"sourcesContent":["/**\n * Runs a schedule's script file at fire time.\n *\n * The file path is always passed as the literal command (or as the single\n * argument to a fixed interpreter) — never through a shell string — so the\n * user's extra prompt can never be interpolated into a command line.\n */\nimport { execFile } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { RunScript, ScriptResult } from './prompt.js';\n\n/** Interpreter for scripts that lack an executable bit, keyed by extension. */\nconst INTERPRETERS: Record<string, () => string> = {\n '.sh': () => '/bin/sh',\n '.js': () => process.execPath,\n '.mjs': () => process.execPath,\n '.cjs': () => process.execPath,\n '.ts': () => process.execPath,\n '.py': () => 'python3',\n};\n\nconst MAX_OUTPUT_BYTES = 1024 * 1024;\n\nexport function isExecutableFile(absPath: string): boolean {\n try {\n fs.accessSync(absPath, fs.constants.X_OK);\n return fs.statSync(absPath).isFile();\n } catch {\n return false;\n }\n}\n\nexport function resolveScriptCommand(absPath: string): { command: string; args: string[] } {\n if (isExecutableFile(absPath)) return { command: absPath, args: [] };\n const interpreter = INTERPRETERS[path.extname(absPath).toLowerCase()];\n if (!interpreter) {\n throw new Error(`${absPath} is not executable and has no known interpreter`);\n }\n return { command: interpreter(), args: [absPath] };\n}\n\nexport const runScript: RunScript = async (absPath, { cwd, timeoutMs }) => {\n const { command, args } = resolveScriptCommand(absPath);\n return new Promise<ScriptResult>((resolve, reject) => {\n execFile(\n command,\n args,\n { cwd, timeout: timeoutMs, maxBuffer: MAX_OUTPUT_BYTES, windowsHide: true },\n (error, stdout, stderr) => {\n if (!error) {\n resolve({ stdout, stderr, exitCode: 0 });\n return;\n }\n const spawnError = error as NodeJS.ErrnoException & { code?: number | string; killed?: boolean };\n if (typeof spawnError.code === 'number') {\n resolve({ stdout, stderr, exitCode: spawnError.code });\n return;\n }\n if (spawnError.killed) {\n resolve({ stdout, stderr: `${stderr}\\n[timed out after ${timeoutMs}ms]`.trim(), exitCode: null });\n return;\n }\n reject(error);\n },\n );\n });\n};\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,eAA6C;CACjD,aAAa;CACb,aAAa,QAAQ;CACrB,cAAc,QAAQ;CACtB,cAAc,QAAQ;CACtB,aAAa,QAAQ;CACrB,aAAa;AACf;AAEA,MAAM,mBAAmB;AAEzB,SAAgB,iBAAiB,SAA0B;CACzD,IAAI;EACF,KAAG,WAAW,SAASA,KAAG,UAAU,IAAI;EACxC,OAAOA,KAAG,SAAS,OAAO,CAAC,CAAC,OAAO;CACrC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,qBAAqB,SAAsD;CACzF,IAAI,iBAAiB,OAAO,GAAG,OAAO;EAAE,SAAS;EAAS,MAAM,CAAC;CAAE;CACnE,MAAM,cAAc,aAAaC,OAAK,QAAQ,OAAO,CAAC,CAAC,YAAY;CACnE,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,GAAG,QAAQ,gDAAgD;CAE7E,OAAO;EAAE,SAAS,YAAY;EAAG,MAAM,CAAC,OAAO;CAAE;AACnD;AAEA,MAAa,YAAuB,OAAO,SAAS,EAAE,KAAK,gBAAgB;CACzE,MAAM,EAAE,SAAS,SAAS,qBAAqB,OAAO;CACtD,OAAO,IAAI,SAAuB,SAAS,WAAW;EACpD,SACE,SACA,MACA;GAAE;GAAK,SAAS;GAAW,WAAW;GAAkB,aAAa;EAAK,IACzE,OAAO,QAAQ,WAAW;GACzB,IAAI,CAAC,OAAO;IACV,QAAQ;KAAE;KAAQ;KAAQ,UAAU;IAAE,CAAC;IACvC;GACF;GACA,MAAM,aAAa;GACnB,IAAI,OAAO,WAAW,SAAS,UAAU;IACvC,QAAQ;KAAE;KAAQ;KAAQ,UAAU,WAAW;IAAK,CAAC;IACrD;GACF;GACA,IAAI,WAAW,QAAQ;IACrB,QAAQ;KAAE;KAAQ,QAAQ,GAAG,OAAO,qBAAqB,UAAU,KAAK,KAAK;KAAG,UAAU;IAAK,CAAC;IAChG;GACF;GACA,OAAO,KAAK;EACd,CACF;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.js","names":["#options","#now","#entries","#arm","#fire"],"sources":["../../src/schedules/scheduler.ts"],"sourcesContent":["/**\n * Process-local scheduler behind `/schedules`.\n *\n * Schedules live only in this process: timers fire them and they die with it.\n * Nothing is written to shared storage, so other Mastra Code processes on the\n * same database never see — or fire — this session's schedules.\n */\nimport { randomUUID } from 'node:crypto';\nimport { nextFireTime, validateInterval } from './interval.js';\nimport type { ParsedInterval } from './interval.js';\nimport type { AssembledPrompt } from './prompt.js';\n\n/** `source` attribute on fired signals; the TUI labels these turns `schedule`. */\nexport const SCHEDULE_SIGNAL_SOURCE = 'schedule';\n\n/** When a schedule fires. Only recurring `every` triggers exist today. */\nexport type ScheduleTrigger = { kind: 'every'; interval: ParsedInterval };\n\n/** A file a schedule runs as a script, or re-reads and sends as the prompt, on every fire. */\nexport type ScheduleFile = { path: string; displayPath: string; mode: 'script' | 'prompt' };\n\n/** What to send on each fire: `prompt` text, or a `file` (plus `extraPrompt` appended after it). */\nexport type ThreadScheduleSource = { prompt: string } | { file: ScheduleFile; extraPrompt?: string };\n\nexport type ThreadScheduleSpec = ThreadScheduleSource & {\n trigger: ScheduleTrigger;\n /** Who is creating it: the user via `/schedules`, or the agent via its schedule tools. Defaults to `user`. */\n createdBy?: 'user' | 'agent';\n};\n\nexport type ThreadSchedule = {\n id: string;\n threadId: string;\n resourceId: string;\n trigger: ScheduleTrigger;\n prompt?: string;\n file?: ScheduleFile;\n extraPrompt?: string;\n createdBy: 'user' | 'agent';\n status: 'active' | 'paused';\n /** Next boundary this schedule fires at; only meaningful while active. */\n nextFireAt: number;\n createdAt: number;\n};\n\nexport type ThreadSchedulerOptions = {\n /** Build the prompt for one fire (runs scripts / reads files). */\n assemblePrompt: (schedule: ThreadSchedule) => Promise<AssembledPrompt>;\n /** Hand the prompt to the agent for the schedule's thread. */\n deliver: (schedule: ThreadSchedule, assembled: AssembledPrompt) => Promise<void>;\n /** A fire failed before reaching the agent. */\n onError?: (error: unknown, schedule: ThreadSchedule) => void;\n now?: () => number;\n};\n\n/** How late a timer may run before its fire counts as missed (sleep, stalled event loop). */\nexport const LATE_FIRE_GRACE_MS = 60_000;\n\n/**\n * Outcome of a manual `run()`. `failed` means nothing reached the agent: the\n * prompt could not be built or the agent refused it.\n */\nexport type ScheduleRunResult =\n | { status: 'fired' }\n | { status: 'failed'; error: unknown }\n | { status: 'busy' }\n | { status: 'not-found' };\n\ntype FireOutcome = { kind: 'delivered' } | { kind: 'deleted' } | { kind: 'failed'; error: unknown };\n\ntype Entry = { schedule: ThreadSchedule; timer?: ReturnType<typeof setTimeout>; inFlight: number };\n\nexport class ThreadScheduler {\n #entries = new Map<string, Entry>();\n #options: ThreadSchedulerOptions;\n\n constructor(options: ThreadSchedulerOptions) {\n this.#options = options;\n }\n\n #now(): number {\n return this.#options.now?.() ?? Date.now();\n }\n\n /** Throws when the interval is not a supported wall-clock cadence (see `validateInterval`) or the prompt is empty. */\n create(spec: ThreadScheduleSpec, target: { threadId: string; resourceId: string }): ThreadSchedule {\n const { interval } = spec.trigger;\n const check = validateInterval(interval);\n if ('error' in check) {\n throw new Error(\n `Invalid schedule interval: ${check.error}${check.suggestion ? ` Try ${check.suggestion}.` : ''}`,\n );\n }\n if ('prompt' in spec && !spec.prompt.trim()) throw new Error('Schedule prompt is empty.');\n const now = this.#now();\n const schedule: ThreadSchedule = {\n id: randomUUID(),\n threadId: target.threadId,\n resourceId: target.resourceId,\n trigger: { kind: 'every', interval: { ...interval } },\n ...('file' in spec\n ? { file: { ...spec.file }, ...(spec.extraPrompt ? { extraPrompt: spec.extraPrompt } : {}) }\n : { prompt: spec.prompt }),\n createdBy: spec.createdBy ?? 'user',\n status: 'active',\n nextFireAt: nextFireTime(interval.ms, now),\n createdAt: now,\n };\n const entry: Entry = { schedule, inFlight: 0 };\n this.#entries.set(schedule.id, entry);\n this.#arm(entry);\n return { ...schedule };\n }\n\n list(filter: { threadId?: string } = {}): ThreadSchedule[] {\n return [...this.#entries.values()]\n .map(entry => ({ ...entry.schedule }))\n .filter(schedule => !filter.threadId || schedule.threadId === filter.threadId)\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n delete(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n clearTimeout(entry.timer);\n this.#entries.delete(id);\n return true;\n }\n\n pause(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n clearTimeout(entry.timer);\n entry.timer = undefined;\n entry.schedule.status = 'paused';\n return true;\n }\n\n resume(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n if (entry.schedule.status === 'active') return true;\n entry.schedule.status = 'active';\n entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, this.#now());\n this.#arm(entry);\n return true;\n }\n\n /** Whether a fire of this schedule (timer or manual) is still assembling or delivering. */\n isFiring(id: string): boolean {\n return (this.#entries.get(id)?.inFlight ?? 0) > 0;\n }\n\n /**\n * Fire once now, outside the cadence. Returns `busy` without firing when a\n * fire of this schedule is still running (a slow script, say), so the same\n * script never runs twice at once. Resolves once the fire finished; a fire\n * that never reached the agent resolves `failed` instead of calling `onError`.\n */\n async run(id: string): Promise<ScheduleRunResult> {\n const entry = this.#entries.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.inFlight > 0) return { status: 'busy' };\n const result = await this.#fire(entry);\n if (result.kind === 'failed') return { status: 'failed', error: result.error };\n return result.kind === 'delivered' ? { status: 'fired' } : { status: 'not-found' };\n }\n\n /** Drop every schedule and timer (process shutdown). */\n stop(): void {\n for (const entry of this.#entries.values()) clearTimeout(entry.timer);\n this.#entries.clear();\n }\n\n #arm(entry: Entry): void {\n clearTimeout(entry.timer);\n const scheduledAt = entry.schedule.nextFireAt;\n entry.timer = setTimeout(\n () => {\n if (this.#entries.get(entry.schedule.id) !== entry || entry.schedule.status !== 'active') return;\n // Timers can fire a hair early; step from the later of now and the\n // intended boundary so the same boundary is never fired twice.\n const now = this.#now();\n entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, Math.max(now, scheduledAt));\n this.#arm(entry);\n // A timer that ran long after its boundary (machine asleep) is a missed fire, not a late one.\n if (now - scheduledAt > LATE_FIRE_GRACE_MS) return;\n // A slow script can outlast a short interval; don't stack fires.\n if (entry.inFlight === 0) {\n void this.#fire(entry).then(result => {\n if (result.kind === 'failed') this.#options.onError?.(result.error, { ...entry.schedule });\n });\n }\n },\n Math.max(0, scheduledAt - this.#now()),\n );\n entry.timer.unref?.();\n }\n\n async #fire(entry: Entry): Promise<FireOutcome> {\n const schedule = { ...entry.schedule };\n entry.inFlight++;\n try {\n const assembled = await this.#options.assemblePrompt(schedule);\n // Deleted while its prompt was being built: drop the fire quietly.\n if (this.#entries.get(schedule.id) !== entry) return { kind: 'deleted' };\n await this.#options.deliver(schedule, assembled);\n return { kind: 'delivered' };\n } catch (error) {\n return { kind: 'failed', error };\n } finally {\n entry.inFlight--;\n }\n }\n}\n\nexport function shortScheduleId(id: string): string {\n return id.slice(0, 8);\n}\n\n/** One-line human summary of what a schedule sends: `run ./check.sh + \"extra\"`, `read notes.md`, `\"prompt\"`. */\nexport function describeScheduleSource(schedule: Pick<ThreadSchedule, 'prompt' | 'file' | 'extraPrompt'>): string {\n if (!schedule.file) return `\"${schedule.prompt ?? ''}\"`;\n const verb = schedule.file.mode === 'script' ? 'run' : 'read';\n const extra = schedule.extraPrompt ? ` + \"${schedule.extraPrompt}\"` : '';\n return `${verb} ${schedule.file.displayPath}${extra}`;\n}\n\n/**\n * Attributes on a fired schedule signal. They carry everything the transcript\n * header needs, so reloaded history renders without the process-local entry.\n */\nexport function scheduleSignalAttributes(\n schedule: ThreadSchedule,\n assembled: Pick<AssembledPrompt, 'outcome'> = {},\n): Record<string, string> {\n return {\n source: SCHEDULE_SIGNAL_SOURCE,\n scheduleId: schedule.id,\n scheduleCadence: schedule.trigger.interval.label,\n scheduleSource: describeScheduleSource(schedule),\n scheduleCreatedBy: schedule.createdBy,\n ...(assembled.outcome ? { scheduleOutcome: assembled.outcome } : {}),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scheduler.js","names":[],"sources":["../../src/schedules/scheduler.ts"],"sourcesContent":["/**\n * Process-local scheduler behind `/schedules`.\n *\n * Schedules live only in this process: timers fire them and they die with it.\n * Nothing is written to shared storage, so other Mastra Code processes on the\n * same database never see — or fire — this session's schedules.\n */\nimport { randomUUID } from 'node:crypto';\nimport { nextFireTime, validateInterval } from './interval.js';\nimport type { ParsedInterval } from './interval.js';\nimport type { AssembledPrompt } from './prompt.js';\n\n/** `source` attribute on fired signals; the TUI labels these turns `schedule`. */\nexport const SCHEDULE_SIGNAL_SOURCE = 'schedule';\n\n/** When a schedule fires. Only recurring `every` triggers exist today. */\nexport type ScheduleTrigger = { kind: 'every'; interval: ParsedInterval };\n\n/** A file a schedule runs as a script, or re-reads and sends as the prompt, on every fire. */\nexport type ScheduleFile = { path: string; displayPath: string; mode: 'script' | 'prompt' };\n\n/** What to send on each fire: `prompt` text, or a `file` (plus `extraPrompt` appended after it). */\nexport type ThreadScheduleSource = { prompt: string } | { file: ScheduleFile; extraPrompt?: string };\n\nexport type ThreadScheduleSpec = ThreadScheduleSource & {\n trigger: ScheduleTrigger;\n /** Who is creating it: the user via `/schedules`, or the agent via its schedule tools. Defaults to `user`. */\n createdBy?: 'user' | 'agent';\n};\n\nexport type ThreadSchedule = {\n id: string;\n threadId: string;\n resourceId: string;\n trigger: ScheduleTrigger;\n prompt?: string;\n file?: ScheduleFile;\n extraPrompt?: string;\n createdBy: 'user' | 'agent';\n status: 'active' | 'paused';\n /** Next boundary this schedule fires at; only meaningful while active. */\n nextFireAt: number;\n createdAt: number;\n};\n\nexport type ThreadSchedulerOptions = {\n /** Build the prompt for one fire (runs scripts / reads files). */\n assemblePrompt: (schedule: ThreadSchedule) => Promise<AssembledPrompt>;\n /** Hand the prompt to the agent for the schedule's thread. */\n deliver: (schedule: ThreadSchedule, assembled: AssembledPrompt) => Promise<void>;\n /** A fire failed before reaching the agent. */\n onError?: (error: unknown, schedule: ThreadSchedule) => void;\n now?: () => number;\n};\n\n/** How late a timer may run before its fire counts as missed (sleep, stalled event loop). */\nexport const LATE_FIRE_GRACE_MS = 60_000;\n\n/**\n * Outcome of a manual `run()`. `failed` means nothing reached the agent: the\n * prompt could not be built or the agent refused it.\n */\nexport type ScheduleRunResult =\n | { status: 'fired' }\n | { status: 'failed'; error: unknown }\n | { status: 'busy' }\n | { status: 'not-found' };\n\ntype FireOutcome = { kind: 'delivered' } | { kind: 'deleted' } | { kind: 'failed'; error: unknown };\n\ntype Entry = { schedule: ThreadSchedule; timer?: ReturnType<typeof setTimeout>; inFlight: number };\n\nexport class ThreadScheduler {\n #entries = new Map<string, Entry>();\n #options: ThreadSchedulerOptions;\n\n constructor(options: ThreadSchedulerOptions) {\n this.#options = options;\n }\n\n #now(): number {\n return this.#options.now?.() ?? Date.now();\n }\n\n /** Throws when the interval is not a supported wall-clock cadence (see `validateInterval`) or the prompt is empty. */\n create(spec: ThreadScheduleSpec, target: { threadId: string; resourceId: string }): ThreadSchedule {\n const { interval } = spec.trigger;\n const check = validateInterval(interval);\n if ('error' in check) {\n throw new Error(\n `Invalid schedule interval: ${check.error}${check.suggestion ? ` Try ${check.suggestion}.` : ''}`,\n );\n }\n if ('prompt' in spec && !spec.prompt.trim()) throw new Error('Schedule prompt is empty.');\n const now = this.#now();\n const schedule: ThreadSchedule = {\n id: randomUUID(),\n threadId: target.threadId,\n resourceId: target.resourceId,\n trigger: { kind: 'every', interval: { ...interval } },\n ...('file' in spec\n ? { file: { ...spec.file }, ...(spec.extraPrompt ? { extraPrompt: spec.extraPrompt } : {}) }\n : { prompt: spec.prompt }),\n createdBy: spec.createdBy ?? 'user',\n status: 'active',\n nextFireAt: nextFireTime(interval.ms, now),\n createdAt: now,\n };\n const entry: Entry = { schedule, inFlight: 0 };\n this.#entries.set(schedule.id, entry);\n this.#arm(entry);\n return { ...schedule };\n }\n\n list(filter: { threadId?: string } = {}): ThreadSchedule[] {\n return [...this.#entries.values()]\n .map(entry => ({ ...entry.schedule }))\n .filter(schedule => !filter.threadId || schedule.threadId === filter.threadId)\n .sort((a, b) => a.createdAt - b.createdAt);\n }\n\n delete(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n clearTimeout(entry.timer);\n this.#entries.delete(id);\n return true;\n }\n\n pause(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n clearTimeout(entry.timer);\n entry.timer = undefined;\n entry.schedule.status = 'paused';\n return true;\n }\n\n resume(id: string): boolean {\n const entry = this.#entries.get(id);\n if (!entry) return false;\n if (entry.schedule.status === 'active') return true;\n entry.schedule.status = 'active';\n entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, this.#now());\n this.#arm(entry);\n return true;\n }\n\n /** Whether a fire of this schedule (timer or manual) is still assembling or delivering. */\n isFiring(id: string): boolean {\n return (this.#entries.get(id)?.inFlight ?? 0) > 0;\n }\n\n /**\n * Fire once now, outside the cadence. Returns `busy` without firing when a\n * fire of this schedule is still running (a slow script, say), so the same\n * script never runs twice at once. Resolves once the fire finished; a fire\n * that never reached the agent resolves `failed` instead of calling `onError`.\n */\n async run(id: string): Promise<ScheduleRunResult> {\n const entry = this.#entries.get(id);\n if (!entry) return { status: 'not-found' };\n if (entry.inFlight > 0) return { status: 'busy' };\n const result = await this.#fire(entry);\n if (result.kind === 'failed') return { status: 'failed', error: result.error };\n return result.kind === 'delivered' ? { status: 'fired' } : { status: 'not-found' };\n }\n\n /** Drop every schedule and timer (process shutdown). */\n stop(): void {\n for (const entry of this.#entries.values()) clearTimeout(entry.timer);\n this.#entries.clear();\n }\n\n #arm(entry: Entry): void {\n clearTimeout(entry.timer);\n const scheduledAt = entry.schedule.nextFireAt;\n entry.timer = setTimeout(\n () => {\n if (this.#entries.get(entry.schedule.id) !== entry || entry.schedule.status !== 'active') return;\n // Timers can fire a hair early; step from the later of now and the\n // intended boundary so the same boundary is never fired twice.\n const now = this.#now();\n entry.schedule.nextFireAt = nextFireTime(entry.schedule.trigger.interval.ms, Math.max(now, scheduledAt));\n this.#arm(entry);\n // A timer that ran long after its boundary (machine asleep) is a missed fire, not a late one.\n if (now - scheduledAt > LATE_FIRE_GRACE_MS) return;\n // A slow script can outlast a short interval; don't stack fires.\n if (entry.inFlight === 0) {\n void this.#fire(entry).then(result => {\n if (result.kind === 'failed') this.#options.onError?.(result.error, { ...entry.schedule });\n });\n }\n },\n Math.max(0, scheduledAt - this.#now()),\n );\n entry.timer.unref?.();\n }\n\n async #fire(entry: Entry): Promise<FireOutcome> {\n const schedule = { ...entry.schedule };\n entry.inFlight++;\n try {\n const assembled = await this.#options.assemblePrompt(schedule);\n // Deleted while its prompt was being built: drop the fire quietly.\n if (this.#entries.get(schedule.id) !== entry) return { kind: 'deleted' };\n await this.#options.deliver(schedule, assembled);\n return { kind: 'delivered' };\n } catch (error) {\n return { kind: 'failed', error };\n } finally {\n entry.inFlight--;\n }\n }\n}\n\nexport function shortScheduleId(id: string): string {\n return id.slice(0, 8);\n}\n\n/** One-line human summary of what a schedule sends: `run ./check.sh + \"extra\"`, `read notes.md`, `\"prompt\"`. */\nexport function describeScheduleSource(schedule: Pick<ThreadSchedule, 'prompt' | 'file' | 'extraPrompt'>): string {\n if (!schedule.file) return `\"${schedule.prompt ?? ''}\"`;\n const verb = schedule.file.mode === 'script' ? 'run' : 'read';\n const extra = schedule.extraPrompt ? ` + \"${schedule.extraPrompt}\"` : '';\n return `${verb} ${schedule.file.displayPath}${extra}`;\n}\n\n/**\n * Attributes on a fired schedule signal. They carry everything the transcript\n * header needs, so reloaded history renders without the process-local entry.\n */\nexport function scheduleSignalAttributes(\n schedule: ThreadSchedule,\n assembled: Pick<AssembledPrompt, 'outcome'> = {},\n): Record<string, string> {\n return {\n source: SCHEDULE_SIGNAL_SOURCE,\n scheduleId: schedule.id,\n scheduleCadence: schedule.trigger.interval.label,\n scheduleSource: describeScheduleSource(schedule),\n scheduleCreatedBy: schedule.createdBy,\n ...(assembled.outcome ? { scheduleOutcome: assembled.outcome } : {}),\n };\n}\n"],"mappings":"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"}
|
|
@@ -14,13 +14,14 @@ function expandTilde(p) {
|
|
|
14
14
|
if (p.startsWith("~/") || p.startsWith("~\\")) return path$1.join(os$1.homedir(), p.slice(2));
|
|
15
15
|
return p;
|
|
16
16
|
}
|
|
17
|
+
const requestSandboxAccessInputSchema = z.object({
|
|
18
|
+
path: z.string().min(1).describe("The absolute path to the directory you need access to."),
|
|
19
|
+
reason: z.string().min(1).describe("Brief explanation of why you need access to this directory.")
|
|
20
|
+
});
|
|
17
21
|
const requestSandboxAccessTool = createTool({
|
|
18
22
|
id: "request_access",
|
|
19
23
|
description: `Request permission to access a directory outside the current project. Use this when you need to read or write files in a directory that is not within the project root. The user will be prompted to approve or deny the request.`,
|
|
20
|
-
inputSchema:
|
|
21
|
-
path: z.string().min(1).describe("The absolute path to the directory you need access to."),
|
|
22
|
-
reason: z.string().min(1).describe("Brief explanation of why you need access to this directory.")
|
|
23
|
-
}),
|
|
24
|
+
inputSchema: requestSandboxAccessInputSchema,
|
|
24
25
|
suspendSchema: z.object({
|
|
25
26
|
kind: z.literal("sandbox_access_request"),
|
|
26
27
|
path: z.string(),
|
|
@@ -33,7 +34,8 @@ const requestSandboxAccessTool = createTool({
|
|
|
33
34
|
const projectRoot = agentControllerCtx?.getState()?.projectPath ?? process.cwd();
|
|
34
35
|
const expanded = expandTilde(requestedPath);
|
|
35
36
|
const absolutePath = path$1.isAbsolute(expanded) ? expanded : path$1.resolve(projectRoot, expanded);
|
|
36
|
-
|
|
37
|
+
const allowedPaths = getAllowedPathsFromContext(context);
|
|
38
|
+
if (isPathAllowed(absolutePath, projectRoot, allowedPaths)) return {
|
|
37
39
|
content: `Access already granted: "${absolutePath}" is within the project root or allowed paths.`,
|
|
38
40
|
isError: false
|
|
39
41
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-sandbox-access.js","names":["os","path"],"sources":["../../src/tools/request-sandbox-access.ts"],"sourcesContent":["/**\n * request_access tool — requests permission to access a directory outside the project root.\n * The user can approve or deny the request via TUI dialog.\n */\n\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { createTool } from '@mastra/core/tools';\nimport { LocalFilesystem } from '@mastra/core/workspace';\nimport { z } from 'zod';\nimport type { MastraCodeState } from '../schema.js';\nimport { isPathAllowed, getAllowedPathsFromContext } from './utils.js';\n\nfunction expandTilde(p: string): string {\n if (p === '~') return os.homedir();\n if (p.startsWith('~/') || p.startsWith('~\\\\')) return path.join(os.homedir(), p.slice(2));\n return p;\n}\n\ntype RequestSandboxAccessInput = {\n path: string;\n reason: string;\n};\n\nconst requestSandboxAccessInputSchema = z.object({\n path: z.string().min(1).describe('The absolute path to the directory you need access to.'),\n reason: z.string().min(1).describe('Brief explanation of why you need access to this directory.'),\n});\n\nexport const requestSandboxAccessTool = createTool({\n id: 'request_access',\n description: `Request permission to access a directory outside the current project. Use this when you need to read or write files in a directory that is not within the project root. The user will be prompted to approve or deny the request.`,\n inputSchema: requestSandboxAccessInputSchema,\n suspendSchema: z.object({\n kind: z.literal('sandbox_access_request'),\n path: z.string(),\n reason: z.string(),\n }),\n resumeSchema: z.union([z.string(), z.array(z.string())]),\n execute: async ({ path: requestedPath, reason }: RequestSandboxAccessInput, context: any) => {\n try {\n const agentControllerCtx = context?.requestContext?.get('controller') as\n | AgentControllerRequestContext<MastraCodeState>\n | undefined;\n\n // Resolve to absolute path (expand ~ first since Node path APIs don't handle it)\n const projectRoot = agentControllerCtx?.getState()?.projectPath ?? process.cwd();\n const expanded = expandTilde(requestedPath);\n const absolutePath = path.isAbsolute(expanded) ? expanded : path.resolve(projectRoot, expanded);\n\n // Check if already allowed\n const allowedPaths = getAllowedPathsFromContext(context);\n if (isPathAllowed(absolutePath, projectRoot, allowedPaths)) {\n return {\n content: `Access already granted: \"${absolutePath}\" is within the project root or allowed paths.`,\n isError: false,\n };\n }\n\n const suspend = context?.agent?.suspend ?? context?.suspend;\n const resumeData = context?.agent?.resumeData ?? context?.resumeData;\n\n // First pass: pause via the native tool-suspension primitive so the host can\n // prompt the user. The suspend payload carries the request details; the host\n // resumes with the user's answer as resume data.\n if (resumeData === undefined) {\n if (!suspend) {\n return {\n content: `Cannot request sandbox access: interactive context not available. The user should manually run /sandbox add ${absolutePath}`,\n isError: true,\n };\n }\n await suspend({ kind: 'sandbox_access_request', path: absolutePath, reason });\n return;\n }\n\n const answerText = Array.isArray(resumeData) ? resumeData.join(', ') : String(resumeData);\n const approved = answerText.toLowerCase().startsWith('y') || answerText.toLowerCase() === 'approve';\n if (approved) {\n // Persist to controller state first (and await it) so the value is\n // committed before the next tool call re-derives the workspace's\n // allowed paths from state. The workspace factory rebuilds the\n // filesystem allowlist from `sandboxAllowedPaths` on every call\n // (getDynamicWorkspace), so an unawaited setState would let that\n // rebuild clobber the in-turn widen below before the grant lands.\n const controllerState = agentControllerCtx?.getState();\n const currentAllowed = (controllerState?.sandboxAllowedPaths as string[] | undefined) ?? [];\n const nextAllowed = currentAllowed.includes(absolutePath) ? currentAllowed : [...currentAllowed, absolutePath];\n if (!currentAllowed.includes(absolutePath)) {\n await agentControllerCtx?.setState({\n sandboxAllowedPaths: nextAllowed,\n });\n }\n\n // Also update the live workspace filesystem immediately so tools in\n // the same turn (e.g. `view`) can access the path without waiting for\n // a fresh workspace build. The resolved workspace is carried on the\n // controller request context — the tool-execution context does not expose\n // it — so read the filesystem from there.\n const fs = agentControllerCtx?.workspace?.filesystem ?? context?.workspace?.filesystem;\n if (fs instanceof LocalFilesystem) {\n fs.setAllowedPaths((prev: readonly string[]) => [...prev, absolutePath]);\n }\n\n return {\n content: `Access granted: \"${absolutePath}\" has been added to allowed paths. You can now access files in this directory.`,\n isError: false,\n };\n } else {\n return {\n content: `Access denied: The user declined access to \"${absolutePath}\".`,\n isError: false,\n };\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : 'Unknown error';\n return {\n content: `Failed to request sandbox access: ${msg}`,\n isError: true,\n };\n }\n },\n} as any);\n"],"mappings":";;;;;;;;;;;AAcA,SAAS,YAAY,GAAmB;CACtC,IAAI,MAAM,KAAK,OAAOA,KAAG,QAAQ;CACjC,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE,WAAW,KAAK,GAAG,OAAOC,OAAK,KAAKD,KAAG,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;CACxF,OAAO;AACT;
|
|
1
|
+
{"version":3,"file":"request-sandbox-access.js","names":["os","path"],"sources":["../../src/tools/request-sandbox-access.ts"],"sourcesContent":["/**\n * request_access tool — requests permission to access a directory outside the project root.\n * The user can approve or deny the request via TUI dialog.\n */\n\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { createTool } from '@mastra/core/tools';\nimport { LocalFilesystem } from '@mastra/core/workspace';\nimport { z } from 'zod';\nimport type { MastraCodeState } from '../schema.js';\nimport { isPathAllowed, getAllowedPathsFromContext } from './utils.js';\n\nfunction expandTilde(p: string): string {\n if (p === '~') return os.homedir();\n if (p.startsWith('~/') || p.startsWith('~\\\\')) return path.join(os.homedir(), p.slice(2));\n return p;\n}\n\ntype RequestSandboxAccessInput = {\n path: string;\n reason: string;\n};\n\nconst requestSandboxAccessInputSchema = z.object({\n path: z.string().min(1).describe('The absolute path to the directory you need access to.'),\n reason: z.string().min(1).describe('Brief explanation of why you need access to this directory.'),\n});\n\nexport const requestSandboxAccessTool = createTool({\n id: 'request_access',\n description: `Request permission to access a directory outside the current project. Use this when you need to read or write files in a directory that is not within the project root. The user will be prompted to approve or deny the request.`,\n inputSchema: requestSandboxAccessInputSchema,\n suspendSchema: z.object({\n kind: z.literal('sandbox_access_request'),\n path: z.string(),\n reason: z.string(),\n }),\n resumeSchema: z.union([z.string(), z.array(z.string())]),\n execute: async ({ path: requestedPath, reason }: RequestSandboxAccessInput, context: any) => {\n try {\n const agentControllerCtx = context?.requestContext?.get('controller') as\n | AgentControllerRequestContext<MastraCodeState>\n | undefined;\n\n // Resolve to absolute path (expand ~ first since Node path APIs don't handle it)\n const projectRoot = agentControllerCtx?.getState()?.projectPath ?? process.cwd();\n const expanded = expandTilde(requestedPath);\n const absolutePath = path.isAbsolute(expanded) ? expanded : path.resolve(projectRoot, expanded);\n\n // Check if already allowed\n const allowedPaths = getAllowedPathsFromContext(context);\n if (isPathAllowed(absolutePath, projectRoot, allowedPaths)) {\n return {\n content: `Access already granted: \"${absolutePath}\" is within the project root or allowed paths.`,\n isError: false,\n };\n }\n\n const suspend = context?.agent?.suspend ?? context?.suspend;\n const resumeData = context?.agent?.resumeData ?? context?.resumeData;\n\n // First pass: pause via the native tool-suspension primitive so the host can\n // prompt the user. The suspend payload carries the request details; the host\n // resumes with the user's answer as resume data.\n if (resumeData === undefined) {\n if (!suspend) {\n return {\n content: `Cannot request sandbox access: interactive context not available. The user should manually run /sandbox add ${absolutePath}`,\n isError: true,\n };\n }\n await suspend({ kind: 'sandbox_access_request', path: absolutePath, reason });\n return;\n }\n\n const answerText = Array.isArray(resumeData) ? resumeData.join(', ') : String(resumeData);\n const approved = answerText.toLowerCase().startsWith('y') || answerText.toLowerCase() === 'approve';\n if (approved) {\n // Persist to controller state first (and await it) so the value is\n // committed before the next tool call re-derives the workspace's\n // allowed paths from state. The workspace factory rebuilds the\n // filesystem allowlist from `sandboxAllowedPaths` on every call\n // (getDynamicWorkspace), so an unawaited setState would let that\n // rebuild clobber the in-turn widen below before the grant lands.\n const controllerState = agentControllerCtx?.getState();\n const currentAllowed = (controllerState?.sandboxAllowedPaths as string[] | undefined) ?? [];\n const nextAllowed = currentAllowed.includes(absolutePath) ? currentAllowed : [...currentAllowed, absolutePath];\n if (!currentAllowed.includes(absolutePath)) {\n await agentControllerCtx?.setState({\n sandboxAllowedPaths: nextAllowed,\n });\n }\n\n // Also update the live workspace filesystem immediately so tools in\n // the same turn (e.g. `view`) can access the path without waiting for\n // a fresh workspace build. The resolved workspace is carried on the\n // controller request context — the tool-execution context does not expose\n // it — so read the filesystem from there.\n const fs = agentControllerCtx?.workspace?.filesystem ?? context?.workspace?.filesystem;\n if (fs instanceof LocalFilesystem) {\n fs.setAllowedPaths((prev: readonly string[]) => [...prev, absolutePath]);\n }\n\n return {\n content: `Access granted: \"${absolutePath}\" has been added to allowed paths. You can now access files in this directory.`,\n isError: false,\n };\n } else {\n return {\n content: `Access denied: The user declined access to \"${absolutePath}\".`,\n isError: false,\n };\n }\n } catch (error) {\n const msg = error instanceof Error ? error.message : 'Unknown error';\n return {\n content: `Failed to request sandbox access: ${msg}`,\n isError: true,\n };\n }\n },\n} as any);\n"],"mappings":";;;;;;;;;;;AAcA,SAAS,YAAY,GAAmB;CACtC,IAAI,MAAM,KAAK,OAAOA,KAAG,QAAQ;CACjC,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE,WAAW,KAAK,GAAG,OAAOC,OAAK,KAAKD,KAAG,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;CACxF,OAAO;AACT;AAOA,MAAM,kCAAkC,EAAE,OAAO;CAC/C,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,wDAAwD;CACzF,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,6DAA6D;AAClG,CAAC;AAED,MAAa,2BAA2B,WAAW;CACjD,IAAI;CACJ,aAAa;CACb,aAAa;CACb,eAAe,EAAE,OAAO;EACtB,MAAM,EAAE,QAAQ,wBAAwB;EACxC,MAAM,EAAE,OAAO;EACf,QAAQ,EAAE,OAAO;CACnB,CAAC;CACD,cAAc,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD,SAAS,OAAO,EAAE,MAAM,eAAe,UAAqC,YAAiB;EAC3F,IAAI;GACF,MAAM,qBAAqB,SAAS,gBAAgB,IAAI,YAAY;GAKpE,MAAM,cAAc,oBAAoB,SAAS,CAAC,EAAE,eAAe,QAAQ,IAAI;GAC/E,MAAM,WAAW,YAAY,aAAa;GAC1C,MAAM,eAAeC,OAAK,WAAW,QAAQ,IAAI,WAAWA,OAAK,QAAQ,aAAa,QAAQ;GAG9F,MAAM,eAAe,2BAA2B,OAAO;GACvD,IAAI,cAAc,cAAc,aAAa,YAAY,GACvD,OAAO;IACL,SAAS,4BAA4B,aAAa;IAClD,SAAS;GACX;GAGF,MAAM,UAAU,SAAS,OAAO,WAAW,SAAS;GACpD,MAAM,aAAa,SAAS,OAAO,cAAc,SAAS;GAK1D,IAAI,eAAe,KAAA,GAAW;IAC5B,IAAI,CAAC,SACH,OAAO;KACL,SAAS,+GAA+G;KACxH,SAAS;IACX;IAEF,MAAM,QAAQ;KAAE,MAAM;KAA0B,MAAM;KAAc;IAAO,CAAC;IAC5E;GACF;GAEA,MAAM,aAAa,MAAM,QAAQ,UAAU,IAAI,WAAW,KAAK,IAAI,IAAI,OAAO,UAAU;GAExF,IADiB,WAAW,YAAY,CAAC,CAAC,WAAW,GAAG,KAAK,WAAW,YAAY,MAAM,WAC5E;IAQZ,MAAM,kBADkB,oBAAoB,SAAS,EAAA,EACZ,uBAAgD,CAAC;IAC1F,MAAM,cAAc,eAAe,SAAS,YAAY,IAAI,iBAAiB,CAAC,GAAG,gBAAgB,YAAY;IAC7G,IAAI,CAAC,eAAe,SAAS,YAAY,GACvC,MAAM,oBAAoB,SAAS,EACjC,qBAAqB,YACvB,CAAC;IAQH,MAAM,KAAK,oBAAoB,WAAW,cAAc,SAAS,WAAW;IAC5E,IAAI,cAAc,iBAChB,GAAG,iBAAiB,SAA4B,CAAC,GAAG,MAAM,YAAY,CAAC;IAGzE,OAAO;KACL,SAAS,oBAAoB,aAAa;KAC1C,SAAS;IACX;GACF,OACE,OAAO;IACL,SAAS,+CAA+C,aAAa;IACrE,SAAS;GACX;EAEJ,SAAS,OAAO;GAEd,OAAO;IACL,SAAS,qCAFC,iBAAiB,QAAQ,MAAM,UAAU;IAGnD,SAAS;GACX;EACF;CACF;AACF,CAAQ"}
|
package/dist/tools/utils.js
CHANGED
|
@@ -22,7 +22,9 @@ function isPathAllowed(targetPath, projectRoot, allowedPaths = []) {
|
|
|
22
22
|
function getAllowedPathsFromContext(toolContext) {
|
|
23
23
|
const agentControllerCtx = toolContext?.requestContext?.get("controller");
|
|
24
24
|
const state = agentControllerCtx?.getState?.() ?? agentControllerCtx?.session?.state?.get?.();
|
|
25
|
-
const
|
|
25
|
+
const projectPath = state?.projectPath ? path$1.resolve(state.projectPath) : process.cwd();
|
|
26
|
+
const configDir = state?.configDir ?? ".mastracode";
|
|
27
|
+
const skillPaths = buildSkillPaths(projectPath, configDir);
|
|
26
28
|
const sandboxPaths = state?.sandboxAllowedPaths ?? [];
|
|
27
29
|
return [...skillPaths, ...sandboxPaths];
|
|
28
30
|
}
|
package/dist/tools/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":["path"],"sources":["../../src/tools/utils.ts"],"sourcesContent":["import * as path from 'node:path';\nimport { buildSkillPaths } from '../agents/workspace.js';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\n\n/**\n * Check whether `targetPath` falls inside `projectRoot` or any of the\n * additional `allowedPaths`. All arguments are expected to be absolute.\n *\n * Returns `true` when access should be **allowed**.\n */\nexport function isPathAllowed(targetPath: string, projectRoot: string, allowedPaths: string[] = []): boolean {\n const resolved = path.resolve(targetPath);\n const roots = [projectRoot, ...allowedPaths].map(p => path.resolve(p));\n\n return roots.some(root => resolved === root || resolved.startsWith(root + path.sep));\n}\n\n/**\n * Read allowed paths from the Mastra controller runtime context.\n * Combines skill paths (computed dynamically from projectPath and configDir)\n * with user-approved sandbox paths from controller state so that both parent\n * and subagent tools have the same access.\n * Returns default skill paths when the context is unavailable (e.g. in tests).\n */\nexport function getAllowedPathsFromContext(\n toolContext: { requestContext?: { get: (key: string) => unknown } } | undefined,\n): string[] {\n const agentControllerCtx = toolContext?.requestContext?.get('controller') as\n | {\n getState?: () => { sandboxAllowedPaths?: string[]; projectPath?: string; configDir?: string };\n session?: {\n state?: { get?: () => { sandboxAllowedPaths?: string[]; projectPath?: string; configDir?: string } };\n };\n }\n | undefined;\n const state = agentControllerCtx?.getState?.() ?? agentControllerCtx?.session?.state?.get?.();\n const projectPath = state?.projectPath ? path.resolve(state.projectPath) : process.cwd();\n const configDir = state?.configDir ?? DEFAULT_CONFIG_DIR;\n const skillPaths = buildSkillPaths(projectPath, configDir);\n const sandboxPaths = state?.sandboxAllowedPaths ?? [];\n\n return [...skillPaths, ...sandboxPaths];\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,cAAc,YAAoB,aAAqB,eAAyB,CAAC,GAAY;CAC3G,MAAM,WAAWA,OAAK,QAAQ,UAAU;CAGxC,OAFc,CAAC,aAAa,GAAG,YAAY,CAAC,CAAC,KAAI,MAAKA,OAAK,QAAQ,CAAC,CAEzD,CAAC,CAAC,MAAK,SAAQ,aAAa,QAAQ,SAAS,WAAW,OAAOA,OAAK,GAAG,CAAC;AACrF;;;;;;;;AASA,SAAgB,2BACd,aACU;CACV,MAAM,qBAAqB,aAAa,gBAAgB,IAAI,YAAY;CAQxE,MAAM,QAAQ,oBAAoB,WAAW,KAAK,oBAAoB,SAAS,OAAO,MAAM;
|
|
1
|
+
{"version":3,"file":"utils.js","names":["path"],"sources":["../../src/tools/utils.ts"],"sourcesContent":["import * as path from 'node:path';\nimport { buildSkillPaths } from '../agents/workspace.js';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\n\n/**\n * Check whether `targetPath` falls inside `projectRoot` or any of the\n * additional `allowedPaths`. All arguments are expected to be absolute.\n *\n * Returns `true` when access should be **allowed**.\n */\nexport function isPathAllowed(targetPath: string, projectRoot: string, allowedPaths: string[] = []): boolean {\n const resolved = path.resolve(targetPath);\n const roots = [projectRoot, ...allowedPaths].map(p => path.resolve(p));\n\n return roots.some(root => resolved === root || resolved.startsWith(root + path.sep));\n}\n\n/**\n * Read allowed paths from the Mastra controller runtime context.\n * Combines skill paths (computed dynamically from projectPath and configDir)\n * with user-approved sandbox paths from controller state so that both parent\n * and subagent tools have the same access.\n * Returns default skill paths when the context is unavailable (e.g. in tests).\n */\nexport function getAllowedPathsFromContext(\n toolContext: { requestContext?: { get: (key: string) => unknown } } | undefined,\n): string[] {\n const agentControllerCtx = toolContext?.requestContext?.get('controller') as\n | {\n getState?: () => { sandboxAllowedPaths?: string[]; projectPath?: string; configDir?: string };\n session?: {\n state?: { get?: () => { sandboxAllowedPaths?: string[]; projectPath?: string; configDir?: string } };\n };\n }\n | undefined;\n const state = agentControllerCtx?.getState?.() ?? agentControllerCtx?.session?.state?.get?.();\n const projectPath = state?.projectPath ? path.resolve(state.projectPath) : process.cwd();\n const configDir = state?.configDir ?? DEFAULT_CONFIG_DIR;\n const skillPaths = buildSkillPaths(projectPath, configDir);\n const sandboxPaths = state?.sandboxAllowedPaths ?? [];\n\n return [...skillPaths, ...sandboxPaths];\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,cAAc,YAAoB,aAAqB,eAAyB,CAAC,GAAY;CAC3G,MAAM,WAAWA,OAAK,QAAQ,UAAU;CAGxC,OAFc,CAAC,aAAa,GAAG,YAAY,CAAC,CAAC,KAAI,MAAKA,OAAK,QAAQ,CAAC,CAEzD,CAAC,CAAC,MAAK,SAAQ,aAAa,QAAQ,SAAS,WAAW,OAAOA,OAAK,GAAG,CAAC;AACrF;;;;;;;;AASA,SAAgB,2BACd,aACU;CACV,MAAM,qBAAqB,aAAa,gBAAgB,IAAI,YAAY;CAQxE,MAAM,QAAQ,oBAAoB,WAAW,KAAK,oBAAoB,SAAS,OAAO,MAAM;CAC5F,MAAM,cAAc,OAAO,cAAcA,OAAK,QAAQ,MAAM,WAAW,IAAI,QAAQ,IAAI;CACvF,MAAM,YAAY,OAAO,aAAA;CACzB,MAAM,aAAa,gBAAgB,aAAa,SAAS;CACzD,MAAM,eAAe,OAAO,uBAAuB,CAAC;CAEpD,OAAO,CAAC,GAAG,YAAY,GAAG,YAAY;AACxC"}
|
|
@@ -12,7 +12,7 @@ export declare function hasParallelKey(): boolean;
|
|
|
12
12
|
* relevance filtering, markdown string formatting, and token truncation.
|
|
13
13
|
* The underlying Tavily tool handles client init, input validation, and the API call.
|
|
14
14
|
*/
|
|
15
|
-
export declare function createWebSearchTool(): import("@mastra/core/tools").Tool<{
|
|
15
|
+
export declare function createWebSearchTool(): Promise<import("@mastra/core/tools").Tool<{
|
|
16
16
|
query: string;
|
|
17
17
|
searchDepth?: "advanced" | "basic" | "fast" | "ultra-fast" | undefined;
|
|
18
18
|
maxResults?: number | undefined;
|
|
@@ -23,30 +23,30 @@ export declare function createWebSearchTool(): import("@mastra/core/tools").Tool
|
|
|
23
23
|
includeDomains?: string[] | undefined;
|
|
24
24
|
excludeDomains?: string[] | undefined;
|
|
25
25
|
timeRange?: "day" | "month" | "week" | "year" | undefined;
|
|
26
|
-
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-search", unknown
|
|
26
|
+
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-search", unknown>>;
|
|
27
27
|
/**
|
|
28
28
|
* Wraps the @mastra/tavily extract tool with mastracode-specific behavior:
|
|
29
29
|
* markdown string formatting and token truncation.
|
|
30
30
|
*/
|
|
31
|
-
export declare function createWebExtractTool(): import("@mastra/core/tools").Tool<{
|
|
31
|
+
export declare function createWebExtractTool(): Promise<import("@mastra/core/tools").Tool<{
|
|
32
32
|
urls: string[];
|
|
33
33
|
extractDepth?: "advanced" | "basic" | undefined;
|
|
34
34
|
query?: string | undefined;
|
|
35
35
|
includeImages?: boolean | undefined;
|
|
36
36
|
format?: "markdown" | "text" | undefined;
|
|
37
|
-
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-extract", unknown
|
|
37
|
+
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-extract", unknown>>;
|
|
38
38
|
/**
|
|
39
39
|
* Wraps the @mastra/parallel search tool with Mastra Code's standard tool id,
|
|
40
40
|
* markdown string output, and token truncation.
|
|
41
41
|
*/
|
|
42
|
-
export declare function createParallelWebSearchTool(): import("@mastra/core/tools").Tool<{
|
|
42
|
+
export declare function createParallelWebSearchTool(): Promise<import("@mastra/core/tools").Tool<{
|
|
43
43
|
query: string;
|
|
44
|
-
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-search", unknown
|
|
44
|
+
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-search", unknown>>;
|
|
45
45
|
/**
|
|
46
46
|
* Wraps the @mastra/parallel extract tool with Mastra Code's standard tool id,
|
|
47
47
|
* markdown string output, and token truncation.
|
|
48
48
|
*/
|
|
49
|
-
export declare function createParallelWebExtractTool(): import("@mastra/core/tools").Tool<{
|
|
49
|
+
export declare function createParallelWebExtractTool(): Promise<import("@mastra/core/tools").Tool<{
|
|
50
50
|
urls: string[];
|
|
51
51
|
objective?: string | undefined;
|
|
52
52
|
searchQueries?: string[] | undefined;
|
|
@@ -60,7 +60,7 @@ export declare function createParallelWebExtractTool(): import("@mastra/core/too
|
|
|
60
60
|
disableCacheFallback?: boolean | undefined;
|
|
61
61
|
} | undefined;
|
|
62
62
|
sessionId?: string | undefined;
|
|
63
|
-
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-extract", unknown
|
|
63
|
+
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-extract", unknown>>;
|
|
64
64
|
/**
|
|
65
65
|
* Resolve which model-independent web provider to use. An explicit user
|
|
66
66
|
* preference wins while its API key is configured; otherwise `auto` picks the
|
|
@@ -70,8 +70,10 @@ export declare function resolveWebSearchProvider(preference?: WebSearchProviderS
|
|
|
70
70
|
/**
|
|
71
71
|
* Create the configured model-independent web tools for the provider selected
|
|
72
72
|
* via the `webSearchProvider` preference (set in the TUI settings panel).
|
|
73
|
+
* Returns `undefined` synchronously when no provider key is configured, so the
|
|
74
|
+
* provider SDK (Tavily pulls in tiktoken, ~15 MB) only loads when one is.
|
|
73
75
|
*/
|
|
74
|
-
export declare function createConfiguredWebTools(): {
|
|
76
|
+
export declare function createConfiguredWebTools(): Promise<{
|
|
75
77
|
web_search: import("@mastra/core/tools").Tool<{
|
|
76
78
|
query: string;
|
|
77
79
|
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-search", unknown>;
|
|
@@ -90,7 +92,7 @@ export declare function createConfiguredWebTools(): {
|
|
|
90
92
|
} | undefined;
|
|
91
93
|
sessionId?: string | undefined;
|
|
92
94
|
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-extract", unknown>;
|
|
93
|
-
} | {
|
|
95
|
+
}> | Promise<{
|
|
94
96
|
web_search: import("@mastra/core/tools").Tool<{
|
|
95
97
|
query: string;
|
|
96
98
|
searchDepth?: "advanced" | "basic" | "fast" | "ultra-fast" | undefined;
|
|
@@ -110,5 +112,5 @@ export declare function createConfiguredWebTools(): {
|
|
|
110
112
|
includeImages?: boolean | undefined;
|
|
111
113
|
format?: "markdown" | "text" | undefined;
|
|
112
114
|
}, unknown, unknown, unknown, import("@mastra/core/tools").ToolExecutionContext<unknown, unknown, unknown>, "web-extract", unknown>;
|
|
113
|
-
} | undefined;
|
|
115
|
+
}> | undefined;
|
|
114
116
|
//# sourceMappingURL=web-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-search.d.ts","sourceRoot":"","sources":["../../src/tools/web-search.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"web-search.d.ts","sourceRoot":"","sources":["../../src/tools/web-search.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAwB1E;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED,wEAAwE;AACxE,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB;;;;;;;;;;;oIA+BxC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB;;;;;;qIAyBzC;AAED;;;GAGG;AACH,wBAAsB,2BAA2B;;oIAwBhD;AAED;;;GAGG;AACH,wBAAsB,4BAA4B;;;;;;;;;qBArIjC,CAAC;sBACJ,CAAC;4BAGZ,CAAC;;;qIA0JH;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,GAAE,wBAAiC,GAC5C,QAAQ,GAAG,UAAU,GAAG,SAAS,CAQnC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB;;;;;;;;;;;;;yBAvLvB,CAAC;0BACJ,CAAC;gCAGZ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;eA+LH"}
|