@mastra/code-sdk 1.7.3-alpha.0 → 1.8.0-alpha.10
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/agent-connections/tools.d.ts.map +1 -1
- package/dist/agent-connections/tools.js +26 -8
- package/dist/agent-connections/tools.js.map +1 -1
- package/dist/agents/background-completion-events.d.ts +16 -0
- package/dist/agents/background-completion-events.d.ts.map +1 -0
- package/dist/agents/background-completion-events.js +19 -0
- package/dist/agents/background-completion-events.js.map +1 -0
- package/dist/agents/background-completion.d.ts +9 -0
- package/dist/agents/background-completion.d.ts.map +1 -0
- package/dist/agents/background-completion.js +59 -0
- package/dist/agents/background-completion.js.map +1 -0
- package/dist/agents/mastracode-gateway.d.ts +2 -0
- package/dist/agents/mastracode-gateway.d.ts.map +1 -1
- package/dist/agents/mastracode-gateway.js +29 -10
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/memory.d.ts +1 -1
- package/dist/agents/memory.d.ts.map +1 -1
- package/dist/agents/memory.js +32 -25
- package/dist/agents/memory.js.map +1 -1
- package/dist/agents/model.d.ts +41 -1
- package/dist/agents/model.d.ts.map +1 -1
- package/dist/agents/model.js +141 -7
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/tool-availability.d.ts +2 -0
- package/dist/agents/tool-availability.d.ts.map +1 -1
- package/dist/agents/tool-availability.js +33 -4
- package/dist/agents/tool-availability.js.map +1 -1
- package/dist/agents/tools.d.ts +1 -1
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +30 -2
- package/dist/agents/tools.js.map +1 -1
- package/dist/agents/workspace.d.ts +2 -1
- package/dist/agents/workspace.d.ts.map +1 -1
- package/dist/agents/workspace.js +3 -3
- package/dist/agents/workspace.js.map +1 -1
- package/dist/auth/account-rotation-processor.d.ts +207 -0
- package/dist/auth/account-rotation-processor.d.ts.map +1 -0
- package/dist/auth/account-rotation-processor.js +669 -0
- package/dist/auth/account-rotation-processor.js.map +1 -0
- package/dist/auth/account-routing-context.d.ts +13 -0
- package/dist/auth/account-routing-context.d.ts.map +1 -0
- package/dist/auth/account-routing-context.js +41 -0
- package/dist/auth/account-routing-context.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +2 -1
- package/dist/auth/providers/anthropic.d.ts.map +1 -1
- package/dist/auth/providers/anthropic.js +2 -8
- package/dist/auth/providers/anthropic.js.map +1 -1
- package/dist/auth/providers/github-copilot.d.ts.map +1 -1
- package/dist/auth/providers/github-copilot.js +34 -12
- package/dist/auth/providers/github-copilot.js.map +1 -1
- package/dist/auth/providers/kimi-coding.d.ts.map +1 -1
- package/dist/auth/providers/kimi-coding.js +2 -6
- package/dist/auth/providers/kimi-coding.js.map +1 -1
- package/dist/auth/providers/openai-codex.d.ts +3 -1
- package/dist/auth/providers/openai-codex.d.ts.map +1 -1
- package/dist/auth/providers/openai-codex.js +50 -24
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/providers/xai.d.ts +1 -1
- package/dist/auth/providers/xai.d.ts.map +1 -1
- package/dist/auth/providers/xai.js +36 -16
- package/dist/auth/providers/xai.js.map +1 -1
- package/dist/auth/storage.d.ts +130 -8
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/auth/storage.js +602 -39
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/types.d.ts +69 -2
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/goal-signal.d.ts +1 -1
- package/dist/goal-signal.d.ts.map +1 -1
- package/dist/goal-signal.js +1 -1
- package/dist/goal-signal.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +155 -38
- package/dist/index.js.map +1 -1
- package/dist/mcp/config.d.ts +17 -2
- package/dist/mcp/config.d.ts.map +1 -1
- package/dist/mcp/config.js +18 -3
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/manager.d.ts +2 -1
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +13 -8
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/types.d.ts +15 -3
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/onboarding/custom-packs.d.ts.map +1 -1
- package/dist/onboarding/custom-packs.js +3 -0
- package/dist/onboarding/custom-packs.js.map +1 -1
- package/dist/onboarding/index.js +1 -1
- package/dist/onboarding/packs.d.ts +44 -0
- package/dist/onboarding/packs.d.ts.map +1 -1
- package/dist/onboarding/packs.js +80 -2
- package/dist/onboarding/packs.js.map +1 -1
- package/dist/onboarding/settings.d.ts +33 -0
- package/dist/onboarding/settings.d.ts.map +1 -1
- package/dist/onboarding/settings.js +88 -8
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +2 -1
- package/dist/plugins/loader.js.map +1 -1
- package/dist/providers/claude-max.d.ts.map +1 -1
- package/dist/providers/claude-max.js +4 -5
- package/dist/providers/claude-max.js.map +1 -1
- package/dist/providers/github-copilot.d.ts +1 -1
- package/dist/providers/github-copilot.d.ts.map +1 -1
- package/dist/providers/github-copilot.js +89 -37
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/kimi-coding.d.ts.map +1 -1
- package/dist/providers/kimi-coding.js +16 -2
- package/dist/providers/kimi-coding.js.map +1 -1
- package/dist/providers/openai-codex.d.ts.map +1 -1
- package/dist/providers/openai-codex.js +42 -27
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/schema.d.ts +28 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +9 -0
- package/dist/schema.js.map +1 -1
- package/dist/tools/workflows/save-workflow.d.ts +1 -1
- package/dist/tools/workflows/save-workflow.d.ts.map +1 -1
- package/dist/workflows/register-primitives.js +2 -2
- package/dist/workflows/register-primitives.js.map +1 -1
- package/package.json +17 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","names":[],"sources":["../../src/plugins/loader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { isSignalProvider } from '@mastra/core/signals';\nimport type { SignalProvider } from '@mastra/core/signals';\n\nimport type {\n MastraCodePlugin,\n MastraCodePluginConfigSchema,\n MastraCodePluginConfigValues,\n MastraCodePluginContext,\n MastraCodePluginProcessorEntries,\n MastraCodePluginRuntime,\n MastraCodePluginSignalProviderEntries,\n MastraCodePluginToolEntries,\n MastraCodePluginTools,\n MastraCodeToolRenderConfig,\n} from '../plugin.js';\nimport { getPluginRoot } from './paths.js';\nimport type { PluginPathOptions } from './paths.js';\nimport { loadPluginRegistry, mergePluginRegistries } from './registry.js';\nimport type { LoadedPlugin, LoadedPluginProcessors, PluginRegistry, ScopedInstalledPluginRecord } from './types.js';\n\nexport type LoadPluginRecordOptions = PluginPathOptions & {\n /** Lazy accessors for Mastra Code's runtime, passed on to plugin field resolvers. */\n runtime?: MastraCodePluginRuntime;\n};\n\nexport type LoadPluginsOptions = LoadPluginRecordOptions & {\n globalRegistry?: PluginRegistry;\n projectRegistry?: PluginRegistry;\n};\n\nexport async function loadPlugins(options: LoadPluginsOptions): Promise<LoadedPlugin[]> {\n const globalRegistry =\n options.globalRegistry ?? loadPluginRegistry(path.join(getPluginRoot('global', options), 'plugins.json'));\n const projectRegistry =\n options.projectRegistry ?? loadPluginRegistry(path.join(getPluginRoot('project', options), 'plugins.json'));\n const records = mergePluginRegistries(globalRegistry, projectRegistry);\n const loaded: LoadedPlugin[] = [];\n\n for (const record of records) {\n if (record.blocked) {\n loaded.push({ ...record, status: 'blocked', tools: {}, toolNames: [] });\n continue;\n }\n if (!record.enabled) {\n loaded.push({ ...record, status: 'inactive', tools: {}, toolNames: [] });\n continue;\n }\n\n loaded.push(await loadPluginRecord(record, options));\n }\n\n return markToolConflicts(loaded);\n}\n\nexport async function loadPluginRecord(\n record: ScopedInstalledPluginRecord,\n options: LoadPluginRecordOptions,\n): Promise<LoadedPlugin> {\n try {\n const entryPath = resolvePluginEntryPath(record, options);\n const plugin = await importPluginModule(entryPath);\n if (plugin.id !== record.id) {\n throw new Error(`Plugin id mismatch: registry has \"${record.id}\" but module exports \"${plugin.id}\"`);\n }\n\n const configSchema = validatePluginConfigSchema(plugin.config);\n const configValues = resolvePluginConfigValues(configSchema, record.config);\n const pluginDir = path.dirname(entryPath);\n const pluginRoot = resolvePluginRoot(record, options);\n const context: MastraCodePluginContext = {\n cwd: options.projectRoot,\n scope: record.scope,\n pluginDir,\n config: configValues,\n // Accessors, not instances: plugins load before the controller and the\n // session exist, so a plugin resolves these when it needs them, not now.\n getController: options.runtime?.getController,\n getActiveSession: options.runtime?.getActiveSession,\n };\n const { tools, renderConfigs } = await resolvePluginTools(plugin, context);\n const processors = await resolvePluginProcessors(plugin, context);\n const signalProviders = await resolvePluginSignalProviders(plugin, context);\n const instructions = await resolvePluginInstructions(plugin, context);\n\n return {\n ...record,\n name: plugin.name,\n version: plugin.version ?? record.version,\n description: plugin.description,\n instructions,\n status: 'active',\n tools,\n renderConfigs,\n toolNames: Object.keys(tools).sort(),\n processors,\n signalProviders,\n skillPaths: resolveExistingAssetDirs(pluginRoot, 'skills'),\n commandPaths: resolveExistingAssetDirs(pluginRoot, 'commands'),\n configSchema,\n configValues,\n };\n } catch (error) {\n return {\n ...record,\n status: 'load failed',\n error: error instanceof Error ? error.message : String(error),\n tools: {},\n toolNames: [],\n };\n }\n}\n\nexport async function loadPluginFromEntry(entryPath: string): Promise<MastraCodePlugin> {\n return validatePluginExport(await importPluginModule(entryPath));\n}\n\nexport function resolvePluginRoot(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const scopeRoot = path.resolve(getPluginRoot(record.scope, options));\n const pluginRoot = path.resolve(path.isAbsolute(record.path) ? record.path : path.join(scopeRoot, record.path));\n if (record.source === 'github' && !isInsideDirectory(pluginRoot, scopeRoot)) {\n throw new Error(`Plugin path for \"${record.id}\" must be inside the ${record.scope} plugin directory`);\n }\n return pluginRoot;\n}\n\nexport function resolvePluginEntryPath(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const pluginRoot = resolvePluginRoot(record, options);\n const entryPath = path.resolve(pluginRoot, record.entry);\n if (!isInsideDirectory(entryPath, pluginRoot)) {\n throw new Error(`Plugin entry for \"${record.id}\" must be inside the plugin directory`);\n }\n return entryPath;\n}\n\nexport function isInsideDirectory(targetPath: string, root: string): boolean {\n const resolvedTarget = path.resolve(targetPath);\n const resolvedRoot = path.resolve(root);\n return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path.sep);\n}\n\nfunction resolveExistingAssetDirs(pluginRoot: string, dirname: 'skills' | 'commands'): string[] {\n const dir = path.join(pluginRoot, dirname);\n try {\n return fs.statSync(dir).isDirectory() ? [dir] : [];\n } catch {\n return [];\n }\n}\n\nasync function importPluginModule(entryPath: string): Promise<MastraCodePlugin> {\n if (path.extname(entryPath) !== '.ts') {\n throw new Error(\n `Unsupported plugin entry extension \"${path.extname(entryPath)}\". V1 plugins must use .ts entries.`,\n );\n }\n\n const url = pathToFileURL(entryPath);\n const stat = fs.statSync(entryPath, { bigint: true });\n url.searchParams.set('mtimeNs', stat.mtimeNs.toString());\n url.searchParams.set('size', stat.size.toString());\n const mod = (await import(url.href)) as { default?: unknown; plugin?: unknown };\n return validatePluginExport(mod.default ?? mod.plugin);\n}\n\nfunction validatePluginExport(value: unknown): MastraCodePlugin {\n if (!value || typeof value !== 'object') {\n throw new Error('Plugin module must export a plugin object as default or named \"plugin\" export');\n }\n\n const plugin = value as MastraCodePlugin;\n if (typeof plugin.id !== 'string' || plugin.id.trim().length === 0) {\n throw new Error('Plugin id must be a non-empty string');\n }\n\n if (plugin.tools !== undefined && typeof plugin.tools !== 'object' && typeof plugin.tools !== 'function') {\n throw new Error('Plugin tools must be an object or function');\n }\n\n if (\n plugin.processors !== undefined &&\n typeof plugin.processors !== 'object' &&\n typeof plugin.processors !== 'function'\n ) {\n throw new Error('Plugin processors must be an array, object, or function');\n }\n\n if (\n plugin.signalProviders !== undefined &&\n !Array.isArray(plugin.signalProviders) &&\n typeof plugin.signalProviders !== 'function'\n ) {\n throw new Error('Plugin signal providers must be an array or function');\n }\n\n return plugin;\n}\n\nasync function resolvePluginTools(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<{ tools: MastraCodePluginTools; renderConfigs: Record<string, MastraCodeToolRenderConfig> }> {\n if (!plugin.tools) return { tools: {}, renderConfigs: {} };\n const entries = typeof plugin.tools === 'function' ? await plugin.tools(context) : plugin.tools;\n if (!entries || typeof entries !== 'object' || Array.isArray(entries)) {\n throw new Error('Plugin tools function must return an object');\n }\n return normalizePluginToolEntries(entries);\n}\n\n/** Mirrors {@link resolvePluginTools}: object-or-function, resolved with the same context. */\nasync function resolvePluginProcessors(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<LoadedPluginProcessors> {\n if (!plugin.processors) return { input: [], output: [] };\n const entries = typeof plugin.processors === 'function' ? await plugin.processors(context) : plugin.processors;\n if (!entries || typeof entries !== 'object') {\n throw new Error('Plugin processors function must return an array or object');\n }\n return normalizePluginProcessorEntries(entries);\n}\n\nasync function resolvePluginSignalProviders(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<MastraCodePluginSignalProviderEntries> {\n if (!plugin.signalProviders) return [];\n const entries =\n typeof plugin.signalProviders === 'function' ? await plugin.signalProviders(context) : plugin.signalProviders;\n if (!Array.isArray(entries)) {\n throw new Error('Plugin signal providers function must return an array');\n }\n for (const [index, provider] of entries.entries()) {\n if (!isPluginSignalProvider(provider)) {\n throw new Error(\n `Plugin signal provider at index ${index} must be a SignalProvider (an object with an id that implements connect, startPolling, stop and __registerMastra)`,\n );\n }\n }\n return entries;\n}\n\n/**\n * Structural, not `instanceof`.\n *\n * A plugin that depends on a published provider package — the motivating case, a plugin wrapping\n * `@mastra/github-signals` — installs that package's own copy of `@mastra/core`, so its provider is\n * never an instance of the `SignalProvider` class Mastra Code loaded. `isSignalProvider` would reject\n * a perfectly working provider. Nothing in the lifecycle needs class identity: the lane only calls\n * these methods, so the methods are what is checked.\n */\nfunction isPluginSignalProvider(value: unknown): value is SignalProvider<string> {\n if (isSignalProvider(value)) return true;\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<SignalProvider<string>> & { __registerMastra?: unknown };\n return (\n typeof candidate.id === 'string' &&\n candidate.id.length > 0 &&\n typeof candidate.connect === 'function' &&\n typeof candidate.startPolling === 'function' &&\n typeof candidate.stop === 'function' &&\n typeof candidate.__registerMastra === 'function'\n );\n}\n\n/** A bare array is shorthand for the input lane, the common case. */\nfunction normalizePluginProcessorEntries(entries: MastraCodePluginProcessorEntries): LoadedPluginProcessors {\n const input = Array.isArray(entries) ? entries : (entries.input ?? []);\n const output = Array.isArray(entries) ? [] : (entries.output ?? []);\n if (!Array.isArray(input) || !Array.isArray(output)) {\n throw new Error('Plugin processor lanes must be arrays');\n }\n for (const processor of [...input, ...output]) {\n if (!processor || typeof processor !== 'object' || typeof processor.id !== 'string') {\n throw new Error('Plugin processors must be objects with an id');\n }\n }\n return { input, output };\n}\n\nasync function resolvePluginInstructions(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<string | undefined> {\n if (plugin.instructions === undefined) return undefined;\n const instructions =\n typeof plugin.instructions === 'function' ? await plugin.instructions(context) : plugin.instructions;\n if (typeof instructions !== 'string') {\n throw new Error('Plugin instructions must be a string');\n }\n const trimmed = instructions.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction normalizePluginToolEntries(entries: MastraCodePluginToolEntries): {\n tools: MastraCodePluginTools;\n renderConfigs: Record<string, MastraCodeToolRenderConfig>;\n} {\n const tools: MastraCodePluginTools = {};\n const renderConfigs: Record<string, MastraCodeToolRenderConfig> = {};\n for (const [name, entry] of Object.entries(entries)) {\n if (!isToolEntryObject(entry)) {\n throw new Error(`Plugin tool \"${name}\" must be an object with a tool property`);\n }\n tools[name] = entry.tool;\n if (entry.render) renderConfigs[name] = entry.render;\n }\n return { tools, renderConfigs };\n}\n\nfunction isToolEntryObject(entry: MastraCodePluginToolEntries[string]): entry is MastraCodePluginToolEntries[string] {\n if (!entry || typeof entry !== 'object' || !('tool' in entry)) return false;\n const tool = (entry as { tool?: unknown }).tool;\n return !!tool && typeof tool === 'object' && !Array.isArray(tool);\n}\n\nfunction validatePluginConfigSchema(schema: unknown): MastraCodePluginConfigSchema | undefined {\n if (!schema || typeof schema !== 'object' || Array.isArray(schema)) return undefined;\n const validated: MastraCodePluginConfigSchema = {};\n for (const [key, option] of Object.entries(schema)) {\n if (!option || typeof option !== 'object' || Array.isArray(option)) continue;\n const record = option as Record<string, unknown>;\n if (record.type !== 'model' && record.type !== 'boolean' && record.type !== 'string') continue;\n validated[key] = {\n type: record.type,\n ...(typeof record.label === 'string' ? { label: record.label } : {}),\n ...(typeof record.description === 'string' ? { description: record.description } : {}),\n ...(typeof record.default === 'string' || typeof record.default === 'boolean' ? { default: record.default } : {}),\n };\n }\n return Object.keys(validated).length > 0 ? validated : undefined;\n}\n\nfunction resolvePluginConfigValues(\n schema: MastraCodePluginConfigSchema | undefined,\n recordValues: Record<string, unknown> | undefined,\n): MastraCodePluginConfigValues {\n const values: MastraCodePluginConfigValues = {};\n if (!schema) return values;\n for (const [key, option] of Object.entries(schema)) {\n const value = recordValues?.[key];\n if (option.type === 'boolean') {\n values[key] = typeof value === 'boolean' ? value : typeof option.default === 'boolean' ? option.default : false;\n continue;\n }\n values[key] = typeof value === 'string' ? value : typeof option.default === 'string' ? option.default : undefined;\n }\n return values;\n}\n\nexport function collectActivePluginTools(plugins: LoadedPlugin[]): MastraCodePluginTools {\n const tools: MastraCodePluginTools = {};\n for (const plugin of plugins) {\n if (plugin.status !== 'active') continue;\n for (const [name, tool] of Object.entries(plugin.tools)) {\n if (!(name in tools)) {\n tools[name] = tool;\n }\n }\n }\n return tools;\n}\n\nfunction markToolConflicts(plugins: LoadedPlugin[]): LoadedPlugin[] {\n const seen = new Map<string, string>();\n return plugins.map(plugin => {\n if (plugin.status !== 'active') return plugin;\n const conflicts = plugin.toolNames.filter(toolName => seen.has(toolName));\n for (const toolName of plugin.toolNames) {\n if (!seen.has(toolName)) seen.set(toolName, plugin.id);\n }\n return conflicts.length > 0 ? { ...plugin, status: 'conflicted', conflicts } : plugin;\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loader.js","names":[],"sources":["../../src/plugins/loader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport { isSignalProvider } from '@mastra/core/signals';\nimport type { SignalProvider } from '@mastra/core/signals';\n\nimport type {\n MastraCodePlugin,\n MastraCodePluginConfigSchema,\n MastraCodePluginConfigValues,\n MastraCodePluginContext,\n MastraCodePluginProcessorEntries,\n MastraCodePluginRuntime,\n MastraCodePluginSignalProviderEntries,\n MastraCodePluginToolEntries,\n MastraCodePluginTools,\n MastraCodeToolRenderConfig,\n} from '../plugin.js';\nimport { getPluginRoot } from './paths.js';\nimport type { PluginPathOptions } from './paths.js';\nimport { loadPluginRegistry, mergePluginRegistries } from './registry.js';\nimport type { LoadedPlugin, LoadedPluginProcessors, PluginRegistry, ScopedInstalledPluginRecord } from './types.js';\n\nexport type LoadPluginRecordOptions = PluginPathOptions & {\n /** Lazy accessors for Mastra Code's runtime, passed on to plugin field resolvers. */\n runtime?: MastraCodePluginRuntime;\n};\n\nexport type LoadPluginsOptions = LoadPluginRecordOptions & {\n globalRegistry?: PluginRegistry;\n projectRegistry?: PluginRegistry;\n};\n\nexport async function loadPlugins(options: LoadPluginsOptions): Promise<LoadedPlugin[]> {\n const globalRegistry =\n options.globalRegistry ?? loadPluginRegistry(path.join(getPluginRoot('global', options), 'plugins.json'));\n const projectRegistry =\n options.projectRegistry ?? loadPluginRegistry(path.join(getPluginRoot('project', options), 'plugins.json'));\n const records = mergePluginRegistries(globalRegistry, projectRegistry);\n const loaded: LoadedPlugin[] = [];\n\n for (const record of records) {\n if (record.blocked) {\n loaded.push({ ...record, status: 'blocked', tools: {}, toolNames: [] });\n continue;\n }\n if (!record.enabled) {\n loaded.push({ ...record, status: 'inactive', tools: {}, toolNames: [] });\n continue;\n }\n\n loaded.push(await loadPluginRecord(record, options));\n }\n\n return markToolConflicts(loaded);\n}\n\nexport async function loadPluginRecord(\n record: ScopedInstalledPluginRecord,\n options: LoadPluginRecordOptions,\n): Promise<LoadedPlugin> {\n try {\n const entryPath = resolvePluginEntryPath(record, options);\n const plugin = await importPluginModule(entryPath);\n if (plugin.id !== record.id) {\n throw new Error(`Plugin id mismatch: registry has \"${record.id}\" but module exports \"${plugin.id}\"`);\n }\n\n const configSchema = validatePluginConfigSchema(plugin.config);\n const configValues = resolvePluginConfigValues(configSchema, record.config);\n const pluginDir = path.dirname(entryPath);\n const pluginRoot = resolvePluginRoot(record, options);\n const context: MastraCodePluginContext = {\n cwd: options.projectRoot,\n scope: record.scope,\n pluginDir,\n config: configValues,\n // Accessors, not instances: plugins load before the controller and the\n // session exist, so a plugin resolves these when it needs them, not now.\n getController: options.runtime?.getController,\n getActiveSession: options.runtime?.getActiveSession,\n getStorage: options.runtime?.getStorage,\n };\n const { tools, renderConfigs } = await resolvePluginTools(plugin, context);\n const processors = await resolvePluginProcessors(plugin, context);\n const signalProviders = await resolvePluginSignalProviders(plugin, context);\n const instructions = await resolvePluginInstructions(plugin, context);\n\n return {\n ...record,\n name: plugin.name,\n version: plugin.version ?? record.version,\n description: plugin.description,\n instructions,\n status: 'active',\n tools,\n renderConfigs,\n toolNames: Object.keys(tools).sort(),\n processors,\n signalProviders,\n skillPaths: resolveExistingAssetDirs(pluginRoot, 'skills'),\n commandPaths: resolveExistingAssetDirs(pluginRoot, 'commands'),\n configSchema,\n configValues,\n };\n } catch (error) {\n return {\n ...record,\n status: 'load failed',\n error: error instanceof Error ? error.message : String(error),\n tools: {},\n toolNames: [],\n };\n }\n}\n\nexport async function loadPluginFromEntry(entryPath: string): Promise<MastraCodePlugin> {\n return validatePluginExport(await importPluginModule(entryPath));\n}\n\nexport function resolvePluginRoot(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const scopeRoot = path.resolve(getPluginRoot(record.scope, options));\n const pluginRoot = path.resolve(path.isAbsolute(record.path) ? record.path : path.join(scopeRoot, record.path));\n if (record.source === 'github' && !isInsideDirectory(pluginRoot, scopeRoot)) {\n throw new Error(`Plugin path for \"${record.id}\" must be inside the ${record.scope} plugin directory`);\n }\n return pluginRoot;\n}\n\nexport function resolvePluginEntryPath(record: ScopedInstalledPluginRecord, options: PluginPathOptions): string {\n const pluginRoot = resolvePluginRoot(record, options);\n const entryPath = path.resolve(pluginRoot, record.entry);\n if (!isInsideDirectory(entryPath, pluginRoot)) {\n throw new Error(`Plugin entry for \"${record.id}\" must be inside the plugin directory`);\n }\n return entryPath;\n}\n\nexport function isInsideDirectory(targetPath: string, root: string): boolean {\n const resolvedTarget = path.resolve(targetPath);\n const resolvedRoot = path.resolve(root);\n return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path.sep);\n}\n\nfunction resolveExistingAssetDirs(pluginRoot: string, dirname: 'skills' | 'commands'): string[] {\n const dir = path.join(pluginRoot, dirname);\n try {\n return fs.statSync(dir).isDirectory() ? [dir] : [];\n } catch {\n return [];\n }\n}\n\nasync function importPluginModule(entryPath: string): Promise<MastraCodePlugin> {\n if (path.extname(entryPath) !== '.ts') {\n throw new Error(\n `Unsupported plugin entry extension \"${path.extname(entryPath)}\". V1 plugins must use .ts entries.`,\n );\n }\n\n const url = pathToFileURL(entryPath);\n const stat = fs.statSync(entryPath, { bigint: true });\n url.searchParams.set('mtimeNs', stat.mtimeNs.toString());\n url.searchParams.set('size', stat.size.toString());\n const mod = (await import(url.href)) as { default?: unknown; plugin?: unknown };\n return validatePluginExport(mod.default ?? mod.plugin);\n}\n\nfunction validatePluginExport(value: unknown): MastraCodePlugin {\n if (!value || typeof value !== 'object') {\n throw new Error('Plugin module must export a plugin object as default or named \"plugin\" export');\n }\n\n const plugin = value as MastraCodePlugin;\n if (typeof plugin.id !== 'string' || plugin.id.trim().length === 0) {\n throw new Error('Plugin id must be a non-empty string');\n }\n\n if (plugin.tools !== undefined && typeof plugin.tools !== 'object' && typeof plugin.tools !== 'function') {\n throw new Error('Plugin tools must be an object or function');\n }\n\n if (\n plugin.processors !== undefined &&\n typeof plugin.processors !== 'object' &&\n typeof plugin.processors !== 'function'\n ) {\n throw new Error('Plugin processors must be an array, object, or function');\n }\n\n if (\n plugin.signalProviders !== undefined &&\n !Array.isArray(plugin.signalProviders) &&\n typeof plugin.signalProviders !== 'function'\n ) {\n throw new Error('Plugin signal providers must be an array or function');\n }\n\n return plugin;\n}\n\nasync function resolvePluginTools(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<{ tools: MastraCodePluginTools; renderConfigs: Record<string, MastraCodeToolRenderConfig> }> {\n if (!plugin.tools) return { tools: {}, renderConfigs: {} };\n const entries = typeof plugin.tools === 'function' ? await plugin.tools(context) : plugin.tools;\n if (!entries || typeof entries !== 'object' || Array.isArray(entries)) {\n throw new Error('Plugin tools function must return an object');\n }\n return normalizePluginToolEntries(entries);\n}\n\n/** Mirrors {@link resolvePluginTools}: object-or-function, resolved with the same context. */\nasync function resolvePluginProcessors(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<LoadedPluginProcessors> {\n if (!plugin.processors) return { input: [], output: [] };\n const entries = typeof plugin.processors === 'function' ? await plugin.processors(context) : plugin.processors;\n if (!entries || typeof entries !== 'object') {\n throw new Error('Plugin processors function must return an array or object');\n }\n return normalizePluginProcessorEntries(entries);\n}\n\nasync function resolvePluginSignalProviders(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<MastraCodePluginSignalProviderEntries> {\n if (!plugin.signalProviders) return [];\n const entries =\n typeof plugin.signalProviders === 'function' ? await plugin.signalProviders(context) : plugin.signalProviders;\n if (!Array.isArray(entries)) {\n throw new Error('Plugin signal providers function must return an array');\n }\n for (const [index, provider] of entries.entries()) {\n if (!isPluginSignalProvider(provider)) {\n throw new Error(\n `Plugin signal provider at index ${index} must be a SignalProvider (an object with an id that implements connect, startPolling, stop and __registerMastra)`,\n );\n }\n }\n return entries;\n}\n\n/**\n * Structural, not `instanceof`.\n *\n * A plugin that depends on a published provider package — the motivating case, a plugin wrapping\n * `@mastra/github-signals` — installs that package's own copy of `@mastra/core`, so its provider is\n * never an instance of the `SignalProvider` class Mastra Code loaded. `isSignalProvider` would reject\n * a perfectly working provider. Nothing in the lifecycle needs class identity: the lane only calls\n * these methods, so the methods are what is checked.\n */\nfunction isPluginSignalProvider(value: unknown): value is SignalProvider<string> {\n if (isSignalProvider(value)) return true;\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<SignalProvider<string>> & { __registerMastra?: unknown };\n return (\n typeof candidate.id === 'string' &&\n candidate.id.length > 0 &&\n typeof candidate.connect === 'function' &&\n typeof candidate.startPolling === 'function' &&\n typeof candidate.stop === 'function' &&\n typeof candidate.__registerMastra === 'function'\n );\n}\n\n/** A bare array is shorthand for the input lane, the common case. */\nfunction normalizePluginProcessorEntries(entries: MastraCodePluginProcessorEntries): LoadedPluginProcessors {\n const input = Array.isArray(entries) ? entries : (entries.input ?? []);\n const output = Array.isArray(entries) ? [] : (entries.output ?? []);\n if (!Array.isArray(input) || !Array.isArray(output)) {\n throw new Error('Plugin processor lanes must be arrays');\n }\n for (const processor of [...input, ...output]) {\n if (!processor || typeof processor !== 'object' || typeof processor.id !== 'string') {\n throw new Error('Plugin processors must be objects with an id');\n }\n }\n return { input, output };\n}\n\nasync function resolvePluginInstructions(\n plugin: MastraCodePlugin,\n context: MastraCodePluginContext,\n): Promise<string | undefined> {\n if (plugin.instructions === undefined) return undefined;\n const instructions =\n typeof plugin.instructions === 'function' ? await plugin.instructions(context) : plugin.instructions;\n if (typeof instructions !== 'string') {\n throw new Error('Plugin instructions must be a string');\n }\n const trimmed = instructions.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction normalizePluginToolEntries(entries: MastraCodePluginToolEntries): {\n tools: MastraCodePluginTools;\n renderConfigs: Record<string, MastraCodeToolRenderConfig>;\n} {\n const tools: MastraCodePluginTools = {};\n const renderConfigs: Record<string, MastraCodeToolRenderConfig> = {};\n for (const [name, entry] of Object.entries(entries)) {\n if (!isToolEntryObject(entry)) {\n throw new Error(`Plugin tool \"${name}\" must be an object with a tool property`);\n }\n tools[name] = entry.tool;\n if (entry.render) renderConfigs[name] = entry.render;\n }\n return { tools, renderConfigs };\n}\n\nfunction isToolEntryObject(entry: MastraCodePluginToolEntries[string]): entry is MastraCodePluginToolEntries[string] {\n if (!entry || typeof entry !== 'object' || !('tool' in entry)) return false;\n const tool = (entry as { tool?: unknown }).tool;\n return !!tool && typeof tool === 'object' && !Array.isArray(tool);\n}\n\nfunction validatePluginConfigSchema(schema: unknown): MastraCodePluginConfigSchema | undefined {\n if (!schema || typeof schema !== 'object' || Array.isArray(schema)) return undefined;\n const validated: MastraCodePluginConfigSchema = {};\n for (const [key, option] of Object.entries(schema)) {\n if (!option || typeof option !== 'object' || Array.isArray(option)) continue;\n const record = option as Record<string, unknown>;\n if (record.type !== 'model' && record.type !== 'boolean' && record.type !== 'string') continue;\n validated[key] = {\n type: record.type,\n ...(typeof record.label === 'string' ? { label: record.label } : {}),\n ...(typeof record.description === 'string' ? { description: record.description } : {}),\n ...(typeof record.default === 'string' || typeof record.default === 'boolean' ? { default: record.default } : {}),\n };\n }\n return Object.keys(validated).length > 0 ? validated : undefined;\n}\n\nfunction resolvePluginConfigValues(\n schema: MastraCodePluginConfigSchema | undefined,\n recordValues: Record<string, unknown> | undefined,\n): MastraCodePluginConfigValues {\n const values: MastraCodePluginConfigValues = {};\n if (!schema) return values;\n for (const [key, option] of Object.entries(schema)) {\n const value = recordValues?.[key];\n if (option.type === 'boolean') {\n values[key] = typeof value === 'boolean' ? value : typeof option.default === 'boolean' ? option.default : false;\n continue;\n }\n values[key] = typeof value === 'string' ? value : typeof option.default === 'string' ? option.default : undefined;\n }\n return values;\n}\n\nexport function collectActivePluginTools(plugins: LoadedPlugin[]): MastraCodePluginTools {\n const tools: MastraCodePluginTools = {};\n for (const plugin of plugins) {\n if (plugin.status !== 'active') continue;\n for (const [name, tool] of Object.entries(plugin.tools)) {\n if (!(name in tools)) {\n tools[name] = tool;\n }\n }\n }\n return tools;\n}\n\nfunction markToolConflicts(plugins: LoadedPlugin[]): LoadedPlugin[] {\n const seen = new Map<string, string>();\n return plugins.map(plugin => {\n if (plugin.status !== 'active') return plugin;\n const conflicts = plugin.toolNames.filter(toolName => seen.has(toolName));\n for (const toolName of plugin.toolNames) {\n if (!seen.has(toolName)) seen.set(toolName, plugin.id);\n }\n return conflicts.length > 0 ? { ...plugin, status: 'conflicted', conflicts } : plugin;\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-max.d.ts","sourceRoot":"","sources":["../../src/providers/claude-max.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,IAAI,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAoBvD;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAK5C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAErE;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,uBAkBlC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,uBA2DnC,CAAC;AA+CF;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,aAAa,GAC5B,uBAAuB,GAAG,SAAS,CAiCrC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,eAAe,CAAA;CAAO,GAAG,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"claude-max.d.ts","sourceRoot":"","sources":["../../src/providers/claude-max.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,IAAI,CAAC;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAoBvD;;GAEG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAK5C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAErE;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,uBAkBlC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,uBA2DnC,CAAC;AA+CF;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,aAAa,GAC5B,uBAAuB,GAAG,SAAS,CAiCrC;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,eAAe,CAAA;CAAO,GAAG,OAAO,KAAK,CAuCnG;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,MAAmC,EAC5C,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,WAAW,CAAC,EAAE,eAAe,CAAC;IAAC,aAAa,CAAC,EAAE,aAAa,CAAA;CAAE,GAC3G,iBAAiB,CA4BnB"}
|
|
@@ -198,11 +198,10 @@ function buildAnthropicOAuthFetch(opts = {}) {
|
|
|
198
198
|
if (storage.get("anthropic")?.type === "api_key") throw new Error("Anthropic API key credential is configured, but OAuth is required.");
|
|
199
199
|
const accessToken = await storage.getApiKey("anthropic");
|
|
200
200
|
if (!accessToken) throw new ProviderAuthRequiredError("Not logged in to Anthropic.");
|
|
201
|
-
const headers = new Headers();
|
|
202
|
-
if (init?.headers)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
});
|
|
201
|
+
const headers = new Headers(url instanceof Request ? url.headers : void 0);
|
|
202
|
+
if (init?.headers) new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
203
|
+
headers.delete("authorization");
|
|
204
|
+
headers.delete("x-api-key");
|
|
206
205
|
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
207
206
|
const requestBetas = (headers.get("anthropic-beta") ?? "").split(",").map((beta) => beta.trim()).filter(Boolean);
|
|
208
207
|
headers.set("anthropic-beta", Array.from(/* @__PURE__ */ new Set([...OAUTH_REQUIRED_BETAS, ...requestBetas])).join(","));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-max.js","names":[],"sources":["../../src/providers/claude-max.ts"],"sourcesContent":["/**\n * Claude Max OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with Claude Max plan.\n * The OAuth endpoint requires a specific system message to be present.\n */\n\nimport { createAnthropic } from '@ai-sdk/anthropic';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\nimport type { ThinkingLevel } from './openai-codex.js';\n\n// Required for Claude Max plan OAuth - the endpoint checks for this system message\nconst claudeCodeIdentity = \"You are Claude Code, Anthropic's official CLI for Claude.\";\n\n// Betas required for Claude Max plan OAuth. Merged with (not replacing) any\n// betas the AI SDK already set on the request — e.g. the SDK adds\n// `server-side-fallback-2026-06-01` when `providerOptions.anthropic.fallbacks`\n// is configured; dropping it makes the API reject the `fallbacks` body field\n// with \"Extra inputs are not permitted\".\nconst OAUTH_REQUIRED_BETAS = [\n 'oauth-2025-04-20',\n 'claude-code-20250219',\n 'interleaved-thinking-2025-05-14',\n 'fine-grained-tool-streaming-2025-05-14',\n];\n\n// Singleton auth storage instance\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/**\n * Middleware that injects the Claude Code identity system message\n * Required for Claude Max OAuth authentication\n */\nexport const claudeCodeMiddleware: LanguageModelMiddleware = {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n // Prepend the Claude Code identity as the first system message\n const systemMessage = {\n role: 'system' as const,\n content: claudeCodeIdentity,\n };\n\n if (params.temperature) {\n delete params.topP;\n }\n\n return {\n ...params,\n prompt: [systemMessage, ...params.prompt],\n };\n },\n};\n\n/**\n * Prompt caching middleware for Anthropic\n *\n * Adds cache breakpoints at strategic locations:\n * 1. Last system message (end of static instructions + dynamic memory)\n * 2. Most recent user/assistant message (conversation context)\n *\n * This allows Anthropic to cache:\n * - System prompts and instructions (rarely change)\n * - Conversation history up to the last message\n */\nexport const promptCacheMiddleware: LanguageModelMiddleware = {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n const prompt = [...params.prompt];\n\n const cacheControl = { type: 'ephemeral' as const, ttl: '5m' as const };\n\n // Helper to add cache control to a message's last content part\n const addCacheToMessage = (msg: any) => {\n // For system messages with string content\n if (typeof msg.content === 'string') {\n return {\n ...msg,\n providerOptions: {\n ...msg.providerOptions,\n anthropic: { ...msg.providerOptions?.anthropic, cacheControl },\n },\n };\n }\n\n // For messages with array content, add to last part\n if (Array.isArray(msg.content) && msg.content.length > 0) {\n const content = [...msg.content];\n const lastPart = content[content.length - 1];\n content[content.length - 1] = {\n ...lastPart,\n providerOptions: {\n ...lastPart.providerOptions,\n anthropic: { ...lastPart.providerOptions?.anthropic, cacheControl },\n },\n };\n return { ...msg, content };\n }\n\n return msg;\n };\n\n // Find the last system message index\n let lastSystemIdx = -1;\n for (let i = prompt.length - 1; i >= 0; i--) {\n if ((prompt[i] as any).role === 'system') {\n lastSystemIdx = i;\n break;\n }\n }\n\n // Add cache breakpoint to last system message\n if (lastSystemIdx >= 0) {\n prompt[lastSystemIdx] = addCacheToMessage(prompt[lastSystemIdx]);\n }\n\n // Add cache breakpoint to the most recent message (last in array)\n const lastIdx = prompt.length - 1;\n if (lastIdx >= 0 && lastIdx !== lastSystemIdx) {\n prompt[lastIdx] = addCacheToMessage(prompt[lastIdx]);\n }\n\n return { ...params, prompt };\n },\n};\n\ntype ActiveThinkingLevel = Exclude<ThinkingLevel, 'off'>;\n\n// Anthropic's effort scale matches mastracode thinking levels 1:1 (minus 'off'),\n// including 'max' — the level OpenAI's scale stops short of.\nconst ANTHROPIC_EFFORT: Record<ActiveThinkingLevel, 'low' | 'medium' | 'high' | 'xhigh' | 'max'> = {\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'xhigh',\n max: 'max',\n};\n\nconst ANTHROPIC_XHIGH_EFFORT_RE = /claude-(?:opus-4-[78]|opus-5|sonnet-5|fable-5)/;\n\nfunction getAnthropicEffort(modelId: string, level: ActiveThinkingLevel) {\n if (level === 'xhigh' && !ANTHROPIC_XHIGH_EFFORT_RE.test(modelId)) return 'high';\n return ANTHROPIC_EFFORT[level];\n}\n\n// Extended-thinking budgets for models that predate adaptive thinking/effort.\n// Budgets count toward max_tokens, so they stay well below the smallest\n// output ceiling of the budget-era models (32k on Opus 4.0/4.1).\nconst ANTHROPIC_THINKING_BUDGET_TOKENS: Record<ActiveThinkingLevel, number> = {\n low: 4096,\n medium: 8192,\n high: 16384,\n xhigh: 24576,\n max: 24576,\n};\n\n/** Claude generations that support adaptive thinking + `output_config.effort`. */\nconst ADAPTIVE_THINKING_RE = /claude-(?:sonnet-4-6|opus-4-[678]|opus-5|sonnet-5|fable-5)/;\n/** Older generations that support extended thinking via `budget_tokens`. */\nconst BUDGET_THINKING_RE = /claude-(?:3-7|sonnet-4|opus-4|haiku-4-5)/;\n/** Generations with no extended-thinking support at all. */\nconst NO_THINKING_RE = /claude-(?:instant|v?2(?:[-.:]|$)|3(?:[-.]|$)|3-5)/;\n\nfunction getAnthropicThinkingCapability(modelId: string): 'adaptive' | 'budget' | 'none' {\n if (ADAPTIVE_THINKING_RE.test(modelId)) return 'adaptive';\n if (BUDGET_THINKING_RE.test(modelId)) return 'budget';\n if (NO_THINKING_RE.test(modelId)) return 'none';\n // Unknown (i.e. newer) Claude models: assume the current API surface.\n return 'adaptive';\n}\n\n/**\n * Middleware that maps the session thinking level onto Anthropic extended\n * thinking. Returns `undefined` (no middleware) when the level is `off` or the\n * model doesn't support thinking, preserving the previous request shape.\n *\n * - Adaptive-era models (Sonnet 4.6+/Opus 4.6+): `thinking: adaptive` plus\n * `output_config.effort` mapped 1:1 from the level (including `max`).\n * - Budget-era models (Claude 3.7 – Opus 4.5): `thinking: enabled` with a\n * `budget_tokens` value derived from the level.\n */\nexport function createAnthropicThinkingMiddleware(\n modelId: string,\n thinkingLevel?: ThinkingLevel,\n): LanguageModelMiddleware | undefined {\n if (!thinkingLevel || thinkingLevel === 'off') return undefined;\n const capability = getAnthropicThinkingCapability(modelId);\n if (capability === 'none') return undefined;\n const level = thinkingLevel as ActiveThinkingLevel;\n\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n const anthropicOptions = (params.providerOptions?.anthropic ?? {}) as Record<string, unknown>;\n // Don't override explicit per-request thinking configuration.\n if (anthropicOptions.thinking !== undefined || anthropicOptions.effort !== undefined) {\n return params;\n }\n\n // Anthropic rejects sampling parameters when thinking is enabled.\n delete params.temperature;\n delete params.topP;\n delete params.topK;\n\n params.providerOptions = {\n ...params.providerOptions,\n anthropic: {\n ...anthropicOptions,\n ...(capability === 'adaptive'\n ? { thinking: { type: 'adaptive', display: 'summarized' }, effort: getAnthropicEffort(modelId, level) }\n : { thinking: { type: 'enabled', budgetTokens: ANTHROPIC_THINKING_BUDGET_TOKENS[level] } }),\n },\n } as typeof params.providerOptions;\n\n return params;\n },\n };\n}\n\n/**\n * Build a fetch function that handles Anthropic OAuth.\n * Preserves non-auth headers from init (critical for gateway auth header to survive\n * when used with the gateway). Strips `authorization` and `x-api-key`.\n */\nexport function buildAnthropicOAuthFetch(opts: { authStorage?: CredentialStore } = {}): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const storedCred = storage.get('anthropic');\n if (storedCred?.type === 'api_key') {\n throw new Error('Anthropic API key credential is configured, but OAuth is required.');\n }\n\n const accessToken = await storage.getApiKey('anthropic');\n if (!accessToken) {\n throw new ProviderAuthRequiredError('Not logged in to Anthropic.');\n }\n\n // Preserve existing headers, strip auth-related ones\n const headers = new Headers();\n if (init?.headers) {\n const source =\n init.headers instanceof Headers\n ? init.headers\n : Array.isArray(init.headers)\n ? new Headers(init.headers as Array<[string, string]>)\n : new Headers(init.headers as Record<string, string>);\n source.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower !== 'authorization' && lower !== 'x-api-key') {\n headers.set(key, value);\n }\n });\n }\n\n headers.set('Authorization', `Bearer ${accessToken}`);\n const requestBetas = (headers.get('anthropic-beta') ?? '')\n .split(',')\n .map(beta => beta.trim())\n .filter(Boolean);\n headers.set('anthropic-beta', Array.from(new Set([...OAUTH_REQUIRED_BETAS, ...requestBetas])).join(','));\n headers.set('anthropic-version', '2023-06-01');\n\n try {\n return await fetch(url, { ...init, headers });\n } catch (error) {\n if (error && typeof error === 'object') {\n Object.assign(error as Record<string, unknown>, {\n requestUrl: url instanceof URL ? url.toString() : typeof url === 'string' ? url : url.url,\n });\n }\n throw error;\n }\n }) as typeof fetch;\n}\n\n/**\n * Creates an Anthropic model using Claude Max OAuth authentication\n * Uses OAuth tokens from AuthStorage (auto-refreshes when needed)\n */\nexport function opencodeClaudeMaxProvider(\n modelId: string = 'claude-sonnet-4-20250514',\n options?: { headers?: Record<string, string>; authStorage?: CredentialStore; thinkingLevel?: ThinkingLevel },\n): MastraModelConfig {\n const headers = options?.headers;\n const thinkingMiddleware = createAnthropicThinkingMiddleware(modelId, options?.thinkingLevel);\n const middleware = [claudeCodeMiddleware, promptCacheMiddleware, ...(thinkingMiddleware ? [thinkingMiddleware] : [])];\n\n // Test environment: use API key\n if (process.env.NODE_ENV === 'test' || process.env.VITEST) {\n const anthropic = createAnthropic({\n apiKey: 'test-api-key',\n headers,\n });\n return wrapLanguageModel({\n model: anthropic(modelId),\n middleware,\n });\n }\n\n const anthropic = createAnthropic({\n apiKey: 'oauth-placeholder',\n headers,\n fetch: buildAnthropicOAuthFetch({ authStorage: options?.authStorage }) as any,\n });\n\n // Wrap with middleware to inject Claude Code identity and enable prompt caching\n return wrapLanguageModel({\n model: anthropic(modelId),\n middleware,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"claude-max.js","names":[],"sources":["../../src/providers/claude-max.ts"],"sourcesContent":["/**\n * Claude Max OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with Claude Max plan.\n * The OAuth endpoint requires a specific system message to be present.\n */\n\nimport { createAnthropic } from '@ai-sdk/anthropic';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\nimport type { ThinkingLevel } from './openai-codex.js';\n\n// Required for Claude Max plan OAuth - the endpoint checks for this system message\nconst claudeCodeIdentity = \"You are Claude Code, Anthropic's official CLI for Claude.\";\n\n// Betas required for Claude Max plan OAuth. Merged with (not replacing) any\n// betas the AI SDK already set on the request — e.g. the SDK adds\n// `server-side-fallback-2026-06-01` when `providerOptions.anthropic.fallbacks`\n// is configured; dropping it makes the API reject the `fallbacks` body field\n// with \"Extra inputs are not permitted\".\nconst OAUTH_REQUIRED_BETAS = [\n 'oauth-2025-04-20',\n 'claude-code-20250219',\n 'interleaved-thinking-2025-05-14',\n 'fine-grained-tool-streaming-2025-05-14',\n];\n\n// Singleton auth storage instance\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/**\n * Middleware that injects the Claude Code identity system message\n * Required for Claude Max OAuth authentication\n */\nexport const claudeCodeMiddleware: LanguageModelMiddleware = {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n // Prepend the Claude Code identity as the first system message\n const systemMessage = {\n role: 'system' as const,\n content: claudeCodeIdentity,\n };\n\n if (params.temperature) {\n delete params.topP;\n }\n\n return {\n ...params,\n prompt: [systemMessage, ...params.prompt],\n };\n },\n};\n\n/**\n * Prompt caching middleware for Anthropic\n *\n * Adds cache breakpoints at strategic locations:\n * 1. Last system message (end of static instructions + dynamic memory)\n * 2. Most recent user/assistant message (conversation context)\n *\n * This allows Anthropic to cache:\n * - System prompts and instructions (rarely change)\n * - Conversation history up to the last message\n */\nexport const promptCacheMiddleware: LanguageModelMiddleware = {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n const prompt = [...params.prompt];\n\n const cacheControl = { type: 'ephemeral' as const, ttl: '5m' as const };\n\n // Helper to add cache control to a message's last content part\n const addCacheToMessage = (msg: any) => {\n // For system messages with string content\n if (typeof msg.content === 'string') {\n return {\n ...msg,\n providerOptions: {\n ...msg.providerOptions,\n anthropic: { ...msg.providerOptions?.anthropic, cacheControl },\n },\n };\n }\n\n // For messages with array content, add to last part\n if (Array.isArray(msg.content) && msg.content.length > 0) {\n const content = [...msg.content];\n const lastPart = content[content.length - 1];\n content[content.length - 1] = {\n ...lastPart,\n providerOptions: {\n ...lastPart.providerOptions,\n anthropic: { ...lastPart.providerOptions?.anthropic, cacheControl },\n },\n };\n return { ...msg, content };\n }\n\n return msg;\n };\n\n // Find the last system message index\n let lastSystemIdx = -1;\n for (let i = prompt.length - 1; i >= 0; i--) {\n if ((prompt[i] as any).role === 'system') {\n lastSystemIdx = i;\n break;\n }\n }\n\n // Add cache breakpoint to last system message\n if (lastSystemIdx >= 0) {\n prompt[lastSystemIdx] = addCacheToMessage(prompt[lastSystemIdx]);\n }\n\n // Add cache breakpoint to the most recent message (last in array)\n const lastIdx = prompt.length - 1;\n if (lastIdx >= 0 && lastIdx !== lastSystemIdx) {\n prompt[lastIdx] = addCacheToMessage(prompt[lastIdx]);\n }\n\n return { ...params, prompt };\n },\n};\n\ntype ActiveThinkingLevel = Exclude<ThinkingLevel, 'off'>;\n\n// Anthropic's effort scale matches mastracode thinking levels 1:1 (minus 'off'),\n// including 'max' — the level OpenAI's scale stops short of.\nconst ANTHROPIC_EFFORT: Record<ActiveThinkingLevel, 'low' | 'medium' | 'high' | 'xhigh' | 'max'> = {\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'xhigh',\n max: 'max',\n};\n\nconst ANTHROPIC_XHIGH_EFFORT_RE = /claude-(?:opus-4-[78]|opus-5|sonnet-5|fable-5)/;\n\nfunction getAnthropicEffort(modelId: string, level: ActiveThinkingLevel) {\n if (level === 'xhigh' && !ANTHROPIC_XHIGH_EFFORT_RE.test(modelId)) return 'high';\n return ANTHROPIC_EFFORT[level];\n}\n\n// Extended-thinking budgets for models that predate adaptive thinking/effort.\n// Budgets count toward max_tokens, so they stay well below the smallest\n// output ceiling of the budget-era models (32k on Opus 4.0/4.1).\nconst ANTHROPIC_THINKING_BUDGET_TOKENS: Record<ActiveThinkingLevel, number> = {\n low: 4096,\n medium: 8192,\n high: 16384,\n xhigh: 24576,\n max: 24576,\n};\n\n/** Claude generations that support adaptive thinking + `output_config.effort`. */\nconst ADAPTIVE_THINKING_RE = /claude-(?:sonnet-4-6|opus-4-[678]|opus-5|sonnet-5|fable-5)/;\n/** Older generations that support extended thinking via `budget_tokens`. */\nconst BUDGET_THINKING_RE = /claude-(?:3-7|sonnet-4|opus-4|haiku-4-5)/;\n/** Generations with no extended-thinking support at all. */\nconst NO_THINKING_RE = /claude-(?:instant|v?2(?:[-.:]|$)|3(?:[-.]|$)|3-5)/;\n\nfunction getAnthropicThinkingCapability(modelId: string): 'adaptive' | 'budget' | 'none' {\n if (ADAPTIVE_THINKING_RE.test(modelId)) return 'adaptive';\n if (BUDGET_THINKING_RE.test(modelId)) return 'budget';\n if (NO_THINKING_RE.test(modelId)) return 'none';\n // Unknown (i.e. newer) Claude models: assume the current API surface.\n return 'adaptive';\n}\n\n/**\n * Middleware that maps the session thinking level onto Anthropic extended\n * thinking. Returns `undefined` (no middleware) when the level is `off` or the\n * model doesn't support thinking, preserving the previous request shape.\n *\n * - Adaptive-era models (Sonnet 4.6+/Opus 4.6+): `thinking: adaptive` plus\n * `output_config.effort` mapped 1:1 from the level (including `max`).\n * - Budget-era models (Claude 3.7 – Opus 4.5): `thinking: enabled` with a\n * `budget_tokens` value derived from the level.\n */\nexport function createAnthropicThinkingMiddleware(\n modelId: string,\n thinkingLevel?: ThinkingLevel,\n): LanguageModelMiddleware | undefined {\n if (!thinkingLevel || thinkingLevel === 'off') return undefined;\n const capability = getAnthropicThinkingCapability(modelId);\n if (capability === 'none') return undefined;\n const level = thinkingLevel as ActiveThinkingLevel;\n\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n const anthropicOptions = (params.providerOptions?.anthropic ?? {}) as Record<string, unknown>;\n // Don't override explicit per-request thinking configuration.\n if (anthropicOptions.thinking !== undefined || anthropicOptions.effort !== undefined) {\n return params;\n }\n\n // Anthropic rejects sampling parameters when thinking is enabled.\n delete params.temperature;\n delete params.topP;\n delete params.topK;\n\n params.providerOptions = {\n ...params.providerOptions,\n anthropic: {\n ...anthropicOptions,\n ...(capability === 'adaptive'\n ? { thinking: { type: 'adaptive', display: 'summarized' }, effort: getAnthropicEffort(modelId, level) }\n : { thinking: { type: 'enabled', budgetTokens: ANTHROPIC_THINKING_BUDGET_TOKENS[level] } }),\n },\n } as typeof params.providerOptions;\n\n return params;\n },\n };\n}\n\n/**\n * Build a fetch function that handles Anthropic OAuth.\n * Preserves non-auth headers from init (critical for gateway auth header to survive\n * when used with the gateway). Strips `authorization` and `x-api-key`.\n */\nexport function buildAnthropicOAuthFetch(opts: { authStorage?: CredentialStore } = {}): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const storedCred = storage.get('anthropic');\n if (storedCred?.type === 'api_key') {\n throw new Error('Anthropic API key credential is configured, but OAuth is required.');\n }\n\n const accessToken = await storage.getApiKey('anthropic');\n if (!accessToken) {\n throw new ProviderAuthRequiredError('Not logged in to Anthropic.');\n }\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 (init?.headers) new Headers(init.headers).forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('x-api-key');\n headers.set('Authorization', `Bearer ${accessToken}`);\n const requestBetas = (headers.get('anthropic-beta') ?? '')\n .split(',')\n .map(beta => beta.trim())\n .filter(Boolean);\n headers.set('anthropic-beta', Array.from(new Set([...OAUTH_REQUIRED_BETAS, ...requestBetas])).join(','));\n headers.set('anthropic-version', '2023-06-01');\n\n try {\n return await fetch(url, { ...init, headers });\n } catch (error) {\n if (error && typeof error === 'object') {\n Object.assign(error as Record<string, unknown>, {\n requestUrl: url instanceof URL ? url.toString() : typeof url === 'string' ? url : url.url,\n });\n }\n throw error;\n }\n }) as typeof fetch;\n}\n\n/**\n * Creates an Anthropic model using Claude Max OAuth authentication\n * Uses OAuth tokens from AuthStorage (auto-refreshes when needed)\n */\nexport function opencodeClaudeMaxProvider(\n modelId: string = 'claude-sonnet-4-20250514',\n options?: { headers?: Record<string, string>; authStorage?: CredentialStore; thinkingLevel?: ThinkingLevel },\n): MastraModelConfig {\n const headers = options?.headers;\n const thinkingMiddleware = createAnthropicThinkingMiddleware(modelId, options?.thinkingLevel);\n const middleware = [claudeCodeMiddleware, promptCacheMiddleware, ...(thinkingMiddleware ? [thinkingMiddleware] : [])];\n\n // Test environment: use API key\n if (process.env.NODE_ENV === 'test' || process.env.VITEST) {\n const anthropic = createAnthropic({\n apiKey: 'test-api-key',\n headers,\n });\n return wrapLanguageModel({\n model: anthropic(modelId),\n middleware,\n });\n }\n\n const anthropic = createAnthropic({\n apiKey: 'oauth-placeholder',\n headers,\n fetch: buildAnthropicOAuthFetch({ authStorage: options?.authStorage }) as any,\n });\n\n // Wrap with middleware to inject Claude Code identity and enable prompt caching\n return wrapLanguageModel({\n model: anthropic(modelId),\n middleware,\n });\n}\n"],"mappings":"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"}
|
|
@@ -55,6 +55,6 @@ export declare function clearCopilotCatalogCache(): void;
|
|
|
55
55
|
* Concurrent calls during a fetch share the inflight promise.
|
|
56
56
|
*/
|
|
57
57
|
export declare function getCopilotModelCatalog(opts?: {
|
|
58
|
-
authStorage?:
|
|
58
|
+
authStorage?: CredentialStore;
|
|
59
59
|
}): Promise<CopilotModelEntry[]>;
|
|
60
60
|
//# sourceMappingURL=github-copilot.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../src/providers/github-copilot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAO1D,OAAO,KAAK,EAAE,iBAAiB,EAA4B,MAAM,qCAAqC,CAAC;AACvG,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../src/providers/github-copilot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAO1D,OAAO,KAAK,EAAE,iBAAiB,EAA4B,MAAM,qCAAqC,CAAC;AACvG,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AAOzE,qDAAqD;AACrD,wBAAgB,cAAc,IAAI,WAAW,CAK5C;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAErE;AAqED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,eAAe,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACjE,OAAO,KAAK,CA+Ed;AAwED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,MAAkB,EAC3B,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,WAAW,CAAC,EAAE,eAAe,CAAA;CAAE,GAC5E,iBAAiB,CAiBnB;AAsDD,wFAAwF;AACxF,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,GAAE;IAAE,WAAW,CAAC,EAAE,eAAe,CAAA;CAAO,GAC3C,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA2F9B"}
|
|
@@ -75,38 +75,59 @@ function detectIsVision(body) {
|
|
|
75
75
|
*/
|
|
76
76
|
function buildGitHubCopilotOAuthFetch(opts = {}) {
|
|
77
77
|
return (async (url, init) => {
|
|
78
|
+
const { headers: initHeaders, ...requestInit } = init ?? {};
|
|
79
|
+
const request = new Request(url, requestInit);
|
|
78
80
|
const storage = opts.authStorage ?? getAuthStorage();
|
|
79
81
|
storage.reload();
|
|
80
82
|
const cred = storage.get(COPILOT_PROVIDER_ID);
|
|
81
83
|
if (!cred || cred.type !== "oauth") throw new ProviderAuthRequiredError("Not logged in to GitHub Copilot.");
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
storage.
|
|
85
|
-
|
|
84
|
+
let accessToken;
|
|
85
|
+
let activeCred;
|
|
86
|
+
if (storage.getOAuthCredential) {
|
|
87
|
+
activeCred = await storage.getOAuthCredential(COPILOT_PROVIDER_ID);
|
|
88
|
+
accessToken = activeCred?.access;
|
|
89
|
+
} else {
|
|
90
|
+
accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);
|
|
91
|
+
storage.reload();
|
|
92
|
+
const reloaded = storage.get(COPILOT_PROVIDER_ID);
|
|
93
|
+
activeCred = reloaded?.type === "oauth" ? {
|
|
94
|
+
...reloaded,
|
|
95
|
+
access: accessToken ?? reloaded.access
|
|
96
|
+
} : void 0;
|
|
97
|
+
}
|
|
98
|
+
if (!accessToken || !activeCred) throw new ProviderAuthRequiredError("Failed to refresh the GitHub Copilot token.");
|
|
99
|
+
const enterpriseUrl = activeCred.enterpriseUrl;
|
|
86
100
|
let parsedBody;
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
try {
|
|
102
|
+
const body = await request.clone().text();
|
|
103
|
+
parsedBody = body ? JSON.parse(body) : void 0;
|
|
89
104
|
} catch {
|
|
90
105
|
parsedBody = void 0;
|
|
91
106
|
}
|
|
92
107
|
const isAgent = detectIsAgent(parsedBody);
|
|
93
108
|
const isVision = detectIsVision(parsedBody);
|
|
94
|
-
const headers = new Headers();
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
});
|
|
109
|
+
const headers = new Headers(url instanceof Request ? url.headers : void 0);
|
|
110
|
+
if (initHeaders) new Headers(initHeaders).forEach((value, key) => headers.set(key, value));
|
|
111
|
+
headers.delete("authorization");
|
|
112
|
+
headers.delete("x-api-key");
|
|
99
113
|
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
100
114
|
headers.set("x-initiator", isAgent ? "agent" : "user");
|
|
101
115
|
headers.set("Openai-Intent", "conversation-edits");
|
|
102
116
|
if (isVision) headers.set("Copilot-Vision-Request", "true");
|
|
103
117
|
for (const [key, value] of Object.entries(COPILOT_HEADERS)) if (!headers.has(key)) headers.set(key, value);
|
|
104
|
-
const finalUrl = opts.rewriteUrl !== false ? rewriteToCopilotBase(
|
|
118
|
+
const finalUrl = opts.rewriteUrl !== false ? rewriteToCopilotBase(request, accessToken, enterpriseUrl) : new URL(request.url);
|
|
119
|
+
const body = request.method === "GET" || request.method === "HEAD" ? void 0 : request.body;
|
|
120
|
+
const finalRequest = new Request(finalUrl, {
|
|
121
|
+
method: request.method,
|
|
122
|
+
headers,
|
|
123
|
+
body,
|
|
124
|
+
signal: request.signal,
|
|
125
|
+
redirect: request.redirect,
|
|
126
|
+
integrity: request.integrity,
|
|
127
|
+
...body ? { duplex: "half" } : {}
|
|
128
|
+
});
|
|
105
129
|
try {
|
|
106
|
-
return await fetch(
|
|
107
|
-
...init,
|
|
108
|
-
headers
|
|
109
|
-
});
|
|
130
|
+
return await fetch(finalRequest);
|
|
110
131
|
} catch (error) {
|
|
111
132
|
if (error && typeof error === "object") Object.assign(error, { requestUrl: finalUrl.toString() });
|
|
112
133
|
throw error;
|
|
@@ -196,12 +217,27 @@ const COPILOT_FALLBACK_MODELS = [{
|
|
|
196
217
|
const CATALOG_TTL_MS = 600 * 1e3;
|
|
197
218
|
const CATALOG_FAILURE_TTL_MS = 60 * 1e3;
|
|
198
219
|
const CATALOG_FETCH_TIMEOUT_MS = 5e3;
|
|
199
|
-
|
|
200
|
-
|
|
220
|
+
const catalogCache = /* @__PURE__ */ new Map();
|
|
221
|
+
const inflightFetches = /* @__PURE__ */ new Map();
|
|
222
|
+
/**
|
|
223
|
+
* Resolve the account identity for a credential read through `getApiKey`, which
|
|
224
|
+
* may await a refresh with a rotation landing inside it. The registry is
|
|
225
|
+
* consulted on both sides of that await, and only an id that survived it names
|
|
226
|
+
* the account that produced the token: an id that moved describes whichever
|
|
227
|
+
* account is active *now*, so trusting it would file this fetch's models under
|
|
228
|
+
* the wrong account and serve them to it. Unidentified is the safe answer — the
|
|
229
|
+
* caller then skips the TTL cache, exactly as it does when the store exposes no
|
|
230
|
+
* registry at all. (`AuthStorage` never needs this: its snapshots name their
|
|
231
|
+
* account.)
|
|
232
|
+
*/
|
|
233
|
+
function readStableAccountId(storage, idBeforeToken) {
|
|
234
|
+
const idAfterToken = storage.getActiveAccount?.(COPILOT_PROVIDER_ID)?.id;
|
|
235
|
+
return idBeforeToken !== void 0 && idBeforeToken === idAfterToken ? idAfterToken : void 0;
|
|
236
|
+
}
|
|
201
237
|
/** Reset the in-process Copilot catalog cache (test seam, also useful after logout). */
|
|
202
238
|
function clearCopilotCatalogCache() {
|
|
203
|
-
catalogCache
|
|
204
|
-
|
|
239
|
+
catalogCache.clear();
|
|
240
|
+
inflightFetches.clear();
|
|
205
241
|
}
|
|
206
242
|
/**
|
|
207
243
|
* Return the user's currently-available Copilot models.
|
|
@@ -217,18 +253,33 @@ function clearCopilotCatalogCache() {
|
|
|
217
253
|
*/
|
|
218
254
|
async function getCopilotModelCatalog(opts = {}) {
|
|
219
255
|
const storage = opts.authStorage ?? getAuthStorage();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
256
|
+
let accessToken;
|
|
257
|
+
let accountInstanceId;
|
|
258
|
+
let enterpriseUrl;
|
|
259
|
+
if (storage.getOAuthCredential) {
|
|
260
|
+
const credential = await storage.getOAuthCredential(COPILOT_PROVIDER_ID);
|
|
261
|
+
if (!credential || credential.type !== "oauth") return [];
|
|
262
|
+
accessToken = credential.access;
|
|
263
|
+
accountInstanceId = credential.accountInstanceId;
|
|
264
|
+
enterpriseUrl = credential.enterpriseUrl;
|
|
265
|
+
} else {
|
|
266
|
+
const idBeforeToken = storage.getActiveAccount?.(COPILOT_PROVIDER_ID)?.id;
|
|
267
|
+
accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);
|
|
268
|
+
if (!accessToken) return [];
|
|
269
|
+
accountInstanceId = readStableAccountId(storage, idBeforeToken);
|
|
270
|
+
const stored = storage.get(COPILOT_PROVIDER_ID);
|
|
271
|
+
if (stored?.type === "oauth") enterpriseUrl = stored.enterpriseUrl;
|
|
272
|
+
}
|
|
273
|
+
const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);
|
|
274
|
+
const credentialKey = accountInstanceId === void 0 ? void 0 : `${accountInstanceId}\0${baseUrl}`;
|
|
275
|
+
const dedupeKey = credentialKey;
|
|
276
|
+
const now = Date.now();
|
|
277
|
+
const cached = credentialKey ? catalogCache.get(credentialKey) : void 0;
|
|
278
|
+
if (cached && now - cached.fetchedAt < cached.ttl) return cached.models;
|
|
279
|
+
const existingFetch = dedupeKey === void 0 ? void 0 : inflightFetches.get(dedupeKey);
|
|
280
|
+
if (existingFetch) return existingFetch;
|
|
281
|
+
const fetchPromise = (async () => {
|
|
226
282
|
try {
|
|
227
|
-
const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);
|
|
228
|
-
if (!accessToken) throw new Error("No Copilot bearer token");
|
|
229
|
-
storage.reload();
|
|
230
|
-
const enterpriseUrl = storage.get(COPILOT_PROVIDER_ID)?.enterpriseUrl;
|
|
231
|
-
const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);
|
|
232
283
|
const controller = new AbortController();
|
|
233
284
|
const timer = setTimeout(() => controller.abort(), CATALOG_FETCH_TIMEOUT_MS);
|
|
234
285
|
try {
|
|
@@ -237,28 +288,29 @@ async function getCopilotModelCatalog(opts = {}) {
|
|
|
237
288
|
bearerToken: accessToken,
|
|
238
289
|
signal: controller.signal
|
|
239
290
|
});
|
|
240
|
-
catalogCache
|
|
291
|
+
if (credentialKey) catalogCache.set(credentialKey, {
|
|
241
292
|
fetchedAt: Date.now(),
|
|
242
293
|
ttl: CATALOG_TTL_MS,
|
|
243
294
|
models
|
|
244
|
-
};
|
|
295
|
+
});
|
|
245
296
|
return models;
|
|
246
297
|
} finally {
|
|
247
298
|
clearTimeout(timer);
|
|
248
299
|
}
|
|
249
300
|
} catch (error) {
|
|
250
|
-
catalogCache
|
|
301
|
+
if (credentialKey) catalogCache.set(credentialKey, {
|
|
251
302
|
fetchedAt: Date.now(),
|
|
252
303
|
ttl: CATALOG_FAILURE_TTL_MS,
|
|
253
304
|
models: COPILOT_FALLBACK_MODELS
|
|
254
|
-
};
|
|
305
|
+
});
|
|
255
306
|
console.warn("Failed to fetch live GitHub Copilot models, using fallback list:", error instanceof Error ? error.message : error);
|
|
256
307
|
return COPILOT_FALLBACK_MODELS;
|
|
257
308
|
} finally {
|
|
258
|
-
|
|
309
|
+
if (dedupeKey !== void 0) inflightFetches.delete(dedupeKey);
|
|
259
310
|
}
|
|
260
311
|
})();
|
|
261
|
-
|
|
312
|
+
if (dedupeKey !== void 0) inflightFetches.set(dedupeKey, fetchPromise);
|
|
313
|
+
return fetchPromise;
|
|
262
314
|
}
|
|
263
315
|
//#endregion
|
|
264
316
|
export { buildGitHubCopilotOAuthFetch, clearCopilotCatalogCache, getAuthStorage, getCopilotModelCatalog, githubCopilotProvider, setAuthStorage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-copilot.js","names":[],"sources":["../../src/providers/github-copilot.ts"],"sourcesContent":["/**\n * GitHub Copilot OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with GitHub Copilot's chat API.\n * The Copilot API speaks an OpenAI-compatible chat format, so we plug\n * `@ai-sdk/openai-compatible` into Copilot's API URL and use a custom fetch to inject\n * the bearer token and Copilot-specific headers.\n *\n * Inspired by:\n * - opencode: https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/plugin/github-copilot/copilot.ts\n * - pi-mono: https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/github-copilot.ts\n */\n\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport type { JSONSchema7 } from '@mastra/schema-compat';\nimport { applyCompatLayer, GoogleSchemaCompatLayer } from '@mastra/schema-compat';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport { COPILOT_HEADERS, fetchCopilotModels, getGitHubCopilotBaseUrl } from '../auth/providers/github-copilot.js';\nimport type { CopilotModelEntry, GitHubCopilotCredentials } from '../auth/providers/github-copilot.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\n\nconst COPILOT_PROVIDER_ID = 'github-copilot';\n\n// Singleton auth storage instance (shared with claude-max.ts / openai-codex.ts when not overridden).\nlet authStorageInstance: AuthStorage | null = null;\n\n/** Get or create the shared AuthStorage instance. */\nexport function getAuthStorage(): AuthStorage {\n if (!authStorageInstance) {\n authStorageInstance = new AuthStorage();\n }\n return authStorageInstance;\n}\n\n/** Set a custom AuthStorage instance (useful for tests / TUI integration). */\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\n/**\n * Heuristic: did this request come from the agent (e.g. tool result follow-ups) rather\n * than a fresh user turn? Mirrors opencode's `isAgent` logic — Copilot bills these\n * differently via the `x-initiator` header.\n */\nfunction detectIsAgent(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const messages = obj.messages;\n if (Array.isArray(messages) && messages.length > 0) {\n const last = messages[messages.length - 1] as { role?: string; content?: unknown };\n if (last?.role && last.role !== 'user') return true;\n if (Array.isArray(last?.content)) {\n // If the last user turn carries any tool_result parts, treat it as an agent turn.\n const hasToolResult = last.content.some(\n (part: unknown) => part && typeof part === 'object' && (part as { type?: string }).type === 'tool_result',\n );\n if (hasToolResult) return true;\n }\n }\n\n const input = obj.input;\n if (Array.isArray(input) && input.length > 0) {\n const last = input[input.length - 1] as { role?: string };\n if (last?.role && last.role !== 'user') return true;\n }\n\n return false;\n}\n\n/** Detect image/vision content in a request body. */\nfunction detectIsVision(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const matchPart = (part: unknown): boolean => {\n if (!part || typeof part !== 'object') return false;\n const t = (part as { type?: string }).type;\n return t === 'image' || t === 'image_url' || t === 'input_image';\n };\n\n const messages = obj.messages;\n if (Array.isArray(messages)) {\n return messages.some(\n (msg: unknown) =>\n msg &&\n typeof msg === 'object' &&\n Array.isArray((msg as { content?: unknown }).content) &&\n ((msg as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n const input = obj.input;\n if (Array.isArray(input)) {\n return input.some(\n (item: unknown) =>\n item &&\n typeof item === 'object' &&\n Array.isArray((item as { content?: unknown }).content) &&\n ((item as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n return false;\n}\n\n/**\n * Build a fetch wrapper that authenticates with GitHub Copilot OAuth.\n *\n * - Injects the short-lived Copilot bearer token (auto-refreshed by AuthStorage).\n * - Adds the VS Code-like Copilot headers required by the API.\n * - Rewrites the request URL onto the per-token API base when `rewriteUrl` is true.\n */\nexport function buildGitHubCopilotOAuthFetch(\n opts: { authStorage?: CredentialStore; rewriteUrl?: boolean } = {},\n): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const cred = storage.get(COPILOT_PROVIDER_ID);\n if (!cred || cred.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to GitHub Copilot.');\n }\n\n // getApiKey() refreshes the Copilot bearer if it has expired.\n const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n if (!accessToken) {\n throw new ProviderAuthRequiredError('Failed to refresh the GitHub Copilot token.');\n }\n storage.reload();\n\n const enterpriseUrl = (cred as GitHubCopilotCredentials).enterpriseUrl;\n\n let parsedBody: unknown;\n if (typeof init?.body === 'string') {\n try {\n parsedBody = JSON.parse(init.body);\n } catch {\n parsedBody = undefined;\n }\n }\n const isAgent = detectIsAgent(parsedBody);\n const isVision = detectIsVision(parsedBody);\n\n // Preserve non-auth headers from caller.\n const headers = new Headers();\n if (init?.headers) {\n const source =\n init.headers instanceof Headers\n ? init.headers\n : Array.isArray(init.headers)\n ? new Headers(init.headers as Array<[string, string]>)\n : new Headers(init.headers as Record<string, string>);\n source.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower !== 'authorization' && lower !== 'x-api-key') {\n headers.set(key, value);\n }\n });\n }\n\n headers.set('Authorization', `Bearer ${accessToken}`);\n headers.set('x-initiator', isAgent ? 'agent' : 'user');\n headers.set('Openai-Intent', 'conversation-edits');\n if (isVision) {\n headers.set('Copilot-Vision-Request', 'true');\n }\n for (const [key, value] of Object.entries(COPILOT_HEADERS)) {\n // Only set if caller didn't already provide it (allow overrides for tests).\n if (!headers.has(key)) {\n headers.set(key, value);\n }\n }\n\n const finalUrl =\n opts.rewriteUrl !== false\n ? rewriteToCopilotBase(url, accessToken, enterpriseUrl)\n : url instanceof URL\n ? url\n : typeof url === 'string'\n ? new URL(url)\n : new URL((url as Request).url);\n\n try {\n return await fetch(finalUrl, { ...init, headers });\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\nfunction rewriteToCopilotBase(url: string | URL | Request, token: string, enterpriseDomain?: string): URL {\n const original = url instanceof URL ? url : new URL(typeof url === 'string' ? url : (url as Request).url);\n const base = new URL(getGitHubCopilotBaseUrl(token, enterpriseDomain));\n // Copilot's OpenAI-compatible API serves endpoints at the root of the base host\n // (`/chat/completions`, `/responses`, `/models`, ...) — not under a `/v1/` prefix\n // like api.openai.com does. The @ai-sdk/openai default baseURL is\n // `https://api.openai.com/v1`, so the SDK builds requests like\n // `https://api.openai.com/v1/chat/completions`. Strip the leading `/v1` segment\n // when rewriting onto the Copilot base or Copilot will return 404 Not Found.\n const pathname = original.pathname.replace(/^\\/v1(\\/|$)/, '/');\n return new URL(`${pathname}${original.search}`, base);\n}\n\nfunction isGeminiModel(modelId: string): boolean {\n return modelId.startsWith('gemini-');\n}\n\nfunction applyGeminiSchemaCompatToTools(modelId: string, tools: unknown): unknown {\n if (!Array.isArray(tools)) {\n return tools;\n }\n\n const compatLayer = new GoogleSchemaCompatLayer({\n provider: COPILOT_PROVIDER_ID,\n modelId,\n supportsStructuredOutputs: false,\n });\n\n return tools.map(tool => {\n if (!tool || typeof tool !== 'object' || (tool as { type?: unknown }).type !== 'function') {\n return tool;\n }\n\n const functionTool = tool as { inputSchema?: JSONSchema7 };\n if (!functionTool.inputSchema) {\n return tool;\n }\n\n return {\n ...functionTool,\n inputSchema: applyCompatLayer({\n schema: functionTool.inputSchema,\n compatLayers: [compatLayer],\n mode: 'aiSdkSchema',\n }).jsonSchema as JSONSchema7,\n };\n });\n}\n\n/** Middleware that prevents sending parameters Copilot's endpoint rejects. */\nfunction createCopilotMiddleware(modelId: string): LanguageModelMiddleware {\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n if (params.temperature !== undefined && params.temperature !== null) {\n delete params.topP;\n }\n\n if (isGeminiModel(modelId)) {\n (params as { tools?: unknown }).tools = applyGeminiSchemaCompatToTools(\n modelId,\n (params as { tools?: unknown }).tools,\n );\n }\n\n return params;\n },\n };\n}\n\n/**\n * Creates a model that talks to GitHub Copilot using OAuth credentials.\n *\n * Copilot's `/chat/completions` endpoint is OpenAI-compatible, but GitHub Copilot\n * is not OpenAI. Use the generic OpenAI-compatible adapter with Copilot's base URL\n * instead of the OpenAI provider plus URL rewriting.\n */\nexport function githubCopilotProvider(\n modelId: string = 'gpt-4.1',\n options?: { headers?: Record<string, string>; authStorage?: CredentialStore },\n): MastraModelConfig {\n const headers = options?.headers;\n const copilot = createOpenAICompatible({\n name: COPILOT_PROVIDER_ID,\n baseURL: 'https://api.githubcopilot.com',\n apiKey: process.env.NODE_ENV === 'test' || process.env.VITEST ? 'test-api-key' : 'oauth-placeholder',\n headers,\n fetch:\n process.env.NODE_ENV === 'test' || process.env.VITEST\n ? undefined\n : (buildGitHubCopilotOAuthFetch({ rewriteUrl: false, authStorage: options?.authStorage }) as any),\n });\n\n return wrapLanguageModel({\n model: copilot.chatModel(modelId),\n middleware: [createCopilotMiddleware(modelId)],\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live model catalog\n// ---------------------------------------------------------------------------\n\n/**\n * Hard-coded fallback advertised when the live `/models` request fails (network\n * down, expired token, etc.). Keep this conservative because the live catalog is\n * the source of truth for the user's currently-enabled Copilot models.\n *\n * Available across all paid Copilot tiers and free of premium-request charges.\n */\nconst COPILOT_FALLBACK_MODELS: CopilotModelEntry[] = [\n {\n id: 'gpt-4.1',\n name: 'GPT-4.1',\n vendor: 'OpenAI',\n supportedEndpoints: ['/chat/completions'],\n isAnthropicShaped: false,\n supportsVision: true,\n supportsToolCalls: true,\n },\n];\n\nconst CATALOG_TTL_MS = 10 * 60 * 1000;\nconst CATALOG_FAILURE_TTL_MS = 60 * 1000;\nconst CATALOG_FETCH_TIMEOUT_MS = 5_000;\n\ninterface CatalogCacheEntry {\n fetchedAt: number;\n ttl: number;\n models: CopilotModelEntry[];\n}\n\nlet catalogCache: CatalogCacheEntry | null = null;\nlet inflightFetch: Promise<CopilotModelEntry[]> | null = null;\n\n/** Reset the in-process Copilot catalog cache (test seam, also useful after logout). */\nexport function clearCopilotCatalogCache(): void {\n catalogCache = null;\n inflightFetch = null;\n}\n\n/**\n * Return the user's currently-available Copilot models.\n *\n * - Returns `[]` when the user is not logged in to GitHub Copilot.\n * - Returns the cached list when a recent fetch succeeded.\n * - On the cache-miss / expired path, fetches `/models` with a 5s timeout, filters\n * to picker-enabled and non-policy-disabled models, then caches for 10 minutes.\n * - On fetch failure, returns a small hard-coded fallback (so packs still work\n * offline) and caches that for 1 minute to avoid hammering the API.\n *\n * Concurrent calls during a fetch share the inflight promise.\n */\nexport async function getCopilotModelCatalog(opts: { authStorage?: AuthStorage } = {}): Promise<CopilotModelEntry[]> {\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const cred = storage.get(COPILOT_PROVIDER_ID);\n if (!cred || cred.type !== 'oauth') {\n return [];\n }\n\n const now = Date.now();\n if (catalogCache && now - catalogCache.fetchedAt < catalogCache.ttl) {\n return catalogCache.models;\n }\n\n if (inflightFetch) return inflightFetch;\n\n inflightFetch = (async (): Promise<CopilotModelEntry[]> => {\n try {\n // getApiKey() refreshes the Copilot bearer if it has expired.\n const accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n if (!accessToken) throw new Error('No Copilot bearer token');\n storage.reload();\n\n const refreshed = storage.get(COPILOT_PROVIDER_ID);\n const enterpriseUrl = (refreshed as GitHubCopilotCredentials | undefined)?.enterpriseUrl;\n const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), CATALOG_FETCH_TIMEOUT_MS);\n try {\n const models = await fetchCopilotModels({\n baseUrl,\n bearerToken: accessToken,\n signal: controller.signal,\n });\n catalogCache = { fetchedAt: Date.now(), ttl: CATALOG_TTL_MS, models };\n return models;\n } finally {\n clearTimeout(timer);\n }\n } catch (error) {\n catalogCache = {\n fetchedAt: Date.now(),\n ttl: CATALOG_FAILURE_TTL_MS,\n models: COPILOT_FALLBACK_MODELS,\n };\n console.warn(\n 'Failed to fetch live GitHub Copilot models, using fallback list:',\n error instanceof Error ? error.message : error,\n );\n return COPILOT_FALLBACK_MODELS;\n } finally {\n inflightFetch = null;\n }\n })();\n\n return inflightFetch;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"github-copilot.js","names":[],"sources":["../../src/providers/github-copilot.ts"],"sourcesContent":["/**\n * GitHub Copilot OAuth Provider\n *\n * Uses OAuth tokens from AuthStorage to authenticate with GitHub Copilot's chat API.\n * The Copilot API speaks an OpenAI-compatible chat format, so we plug\n * `@ai-sdk/openai-compatible` into Copilot's API URL and use a custom fetch to inject\n * the bearer token and Copilot-specific headers.\n *\n * Inspired by:\n * - opencode: https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/plugin/github-copilot/copilot.ts\n * - pi-mono: https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/github-copilot.ts\n */\n\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport type { JSONSchema7 } from '@mastra/schema-compat';\nimport { applyCompatLayer, GoogleSchemaCompatLayer } from '@mastra/schema-compat';\nimport { wrapLanguageModel } from 'ai';\nimport type { LanguageModelMiddleware } from 'ai';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport { COPILOT_HEADERS, fetchCopilotModels, getGitHubCopilotBaseUrl } from '../auth/providers/github-copilot.js';\nimport type { CopilotModelEntry, GitHubCopilotCredentials } from '../auth/providers/github-copilot.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore, OAuthCredential } from '../auth/types.js';\n\nconst COPILOT_PROVIDER_ID = 'github-copilot';\n\n// Singleton auth storage instance (shared with claude-max.ts / openai-codex.ts when not overridden).\nlet authStorageInstance: AuthStorage | null = null;\n\n/** Get or create the shared AuthStorage instance. */\nexport function getAuthStorage(): AuthStorage {\n if (!authStorageInstance) {\n authStorageInstance = new AuthStorage();\n }\n return authStorageInstance;\n}\n\n/** Set a custom AuthStorage instance (useful for tests / TUI integration). */\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\n/**\n * Heuristic: did this request come from the agent (e.g. tool result follow-ups) rather\n * than a fresh user turn? Mirrors opencode's `isAgent` logic — Copilot bills these\n * differently via the `x-initiator` header.\n */\nfunction detectIsAgent(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const messages = obj.messages;\n if (Array.isArray(messages) && messages.length > 0) {\n const last = messages[messages.length - 1] as { role?: string; content?: unknown };\n if (last?.role && last.role !== 'user') return true;\n if (Array.isArray(last?.content)) {\n // If the last user turn carries any tool_result parts, treat it as an agent turn.\n const hasToolResult = last.content.some(\n (part: unknown) => part && typeof part === 'object' && (part as { type?: string }).type === 'tool_result',\n );\n if (hasToolResult) return true;\n }\n }\n\n const input = obj.input;\n if (Array.isArray(input) && input.length > 0) {\n const last = input[input.length - 1] as { role?: string };\n if (last?.role && last.role !== 'user') return true;\n }\n\n return false;\n}\n\n/** Detect image/vision content in a request body. */\nfunction detectIsVision(body: unknown): boolean {\n if (!body || typeof body !== 'object') return false;\n const obj = body as Record<string, unknown>;\n\n const matchPart = (part: unknown): boolean => {\n if (!part || typeof part !== 'object') return false;\n const t = (part as { type?: string }).type;\n return t === 'image' || t === 'image_url' || t === 'input_image';\n };\n\n const messages = obj.messages;\n if (Array.isArray(messages)) {\n return messages.some(\n (msg: unknown) =>\n msg &&\n typeof msg === 'object' &&\n Array.isArray((msg as { content?: unknown }).content) &&\n ((msg as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n const input = obj.input;\n if (Array.isArray(input)) {\n return input.some(\n (item: unknown) =>\n item &&\n typeof item === 'object' &&\n Array.isArray((item as { content?: unknown }).content) &&\n ((item as { content: unknown[] }).content as unknown[]).some(matchPart),\n );\n }\n\n return false;\n}\n\n/**\n * Build a fetch wrapper that authenticates with GitHub Copilot OAuth.\n *\n * - Injects the short-lived Copilot bearer token (auto-refreshed by AuthStorage).\n * - Adds the VS Code-like Copilot headers required by the API.\n * - Rewrites the request URL onto the per-token API base when `rewriteUrl` is true.\n */\nexport function buildGitHubCopilotOAuthFetch(\n opts: { authStorage?: CredentialStore; rewriteUrl?: boolean } = {},\n): typeof fetch {\n return (async (url: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const { headers: initHeaders, ...requestInit } = init ?? {};\n const request = new Request(url, requestInit);\n const storage = opts.authStorage ?? getAuthStorage();\n storage.reload();\n\n const cred = storage.get(COPILOT_PROVIDER_ID);\n if (!cred || cred.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to GitHub Copilot.');\n }\n\n let accessToken: string | undefined;\n let activeCred: OAuthCredential | undefined;\n if (storage.getOAuthCredential) {\n activeCred = await storage.getOAuthCredential(COPILOT_PROVIDER_ID);\n accessToken = activeCred?.access;\n } else {\n accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n storage.reload();\n const reloaded = storage.get(COPILOT_PROVIDER_ID);\n activeCred = reloaded?.type === 'oauth' ? { ...reloaded, access: accessToken ?? reloaded.access } : undefined;\n }\n if (!accessToken || !activeCred) {\n throw new ProviderAuthRequiredError('Failed to refresh the GitHub Copilot token.');\n }\n const enterpriseUrl = (activeCred as GitHubCopilotCredentials).enterpriseUrl;\n\n let parsedBody: unknown;\n try {\n const body = await request.clone().text();\n parsedBody = body ? JSON.parse(body) : undefined;\n } catch {\n parsedBody = undefined;\n }\n const isAgent = detectIsAgent(parsedBody);\n const isVision = detectIsVision(parsedBody);\n\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('x-initiator', isAgent ? 'agent' : 'user');\n headers.set('Openai-Intent', 'conversation-edits');\n if (isVision) {\n headers.set('Copilot-Vision-Request', 'true');\n }\n for (const [key, value] of Object.entries(COPILOT_HEADERS)) {\n // Only set if caller didn't already provide it (allow overrides for tests).\n if (!headers.has(key)) {\n headers.set(key, value);\n }\n }\n\n const finalUrl =\n opts.rewriteUrl !== false ? rewriteToCopilotBase(request, accessToken, enterpriseUrl) : new URL(request.url);\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 fetch(finalRequest);\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\nfunction rewriteToCopilotBase(url: string | URL | Request, token: string, enterpriseDomain?: string): URL {\n const original = url instanceof URL ? url : new URL(typeof url === 'string' ? url : (url as Request).url);\n const base = new URL(getGitHubCopilotBaseUrl(token, enterpriseDomain));\n // Copilot's OpenAI-compatible API serves endpoints at the root of the base host\n // (`/chat/completions`, `/responses`, `/models`, ...) — not under a `/v1/` prefix\n // like api.openai.com does. The @ai-sdk/openai default baseURL is\n // `https://api.openai.com/v1`, so the SDK builds requests like\n // `https://api.openai.com/v1/chat/completions`. Strip the leading `/v1` segment\n // when rewriting onto the Copilot base or Copilot will return 404 Not Found.\n const pathname = original.pathname.replace(/^\\/v1(\\/|$)/, '/');\n return new URL(`${pathname}${original.search}`, base);\n}\n\nfunction isGeminiModel(modelId: string): boolean {\n return modelId.startsWith('gemini-');\n}\n\nfunction applyGeminiSchemaCompatToTools(modelId: string, tools: unknown): unknown {\n if (!Array.isArray(tools)) {\n return tools;\n }\n\n const compatLayer = new GoogleSchemaCompatLayer({\n provider: COPILOT_PROVIDER_ID,\n modelId,\n supportsStructuredOutputs: false,\n });\n\n return tools.map(tool => {\n if (!tool || typeof tool !== 'object' || (tool as { type?: unknown }).type !== 'function') {\n return tool;\n }\n\n const functionTool = tool as { inputSchema?: JSONSchema7 };\n if (!functionTool.inputSchema) {\n return tool;\n }\n\n return {\n ...functionTool,\n inputSchema: applyCompatLayer({\n schema: functionTool.inputSchema,\n compatLayers: [compatLayer],\n mode: 'aiSdkSchema',\n }).jsonSchema as JSONSchema7,\n };\n });\n}\n\n/** Middleware that prevents sending parameters Copilot's endpoint rejects. */\nfunction createCopilotMiddleware(modelId: string): LanguageModelMiddleware {\n return {\n specificationVersion: 'v3',\n transformParams: async ({ params }) => {\n if (params.temperature !== undefined && params.temperature !== null) {\n delete params.topP;\n }\n\n if (isGeminiModel(modelId)) {\n (params as { tools?: unknown }).tools = applyGeminiSchemaCompatToTools(\n modelId,\n (params as { tools?: unknown }).tools,\n );\n }\n\n return params;\n },\n };\n}\n\n/**\n * Creates a model that talks to GitHub Copilot using OAuth credentials.\n *\n * Copilot's `/chat/completions` endpoint is OpenAI-compatible, but GitHub Copilot\n * is not OpenAI. Use the generic OpenAI-compatible adapter with Copilot's base URL\n * instead of the OpenAI provider plus URL rewriting.\n */\nexport function githubCopilotProvider(\n modelId: string = 'gpt-4.1',\n options?: { headers?: Record<string, string>; authStorage?: CredentialStore },\n): MastraModelConfig {\n const headers = options?.headers;\n const copilot = createOpenAICompatible({\n name: COPILOT_PROVIDER_ID,\n baseURL: 'https://api.githubcopilot.com',\n apiKey: process.env.NODE_ENV === 'test' || process.env.VITEST ? 'test-api-key' : 'oauth-placeholder',\n headers,\n fetch:\n process.env.NODE_ENV === 'test' || process.env.VITEST\n ? undefined\n : (buildGitHubCopilotOAuthFetch({ rewriteUrl: false, authStorage: options?.authStorage }) as any),\n });\n\n return wrapLanguageModel({\n model: copilot.chatModel(modelId),\n middleware: [createCopilotMiddleware(modelId)],\n });\n}\n\n// ---------------------------------------------------------------------------\n// Live model catalog\n// ---------------------------------------------------------------------------\n\n/**\n * Hard-coded fallback advertised when the live `/models` request fails (network\n * down, expired token, etc.). Keep this conservative because the live catalog is\n * the source of truth for the user's currently-enabled Copilot models.\n *\n * Available across all paid Copilot tiers and free of premium-request charges.\n */\nconst COPILOT_FALLBACK_MODELS: CopilotModelEntry[] = [\n {\n id: 'gpt-4.1',\n name: 'GPT-4.1',\n vendor: 'OpenAI',\n supportedEndpoints: ['/chat/completions'],\n isAnthropicShaped: false,\n supportsVision: true,\n supportsToolCalls: true,\n },\n];\n\nconst CATALOG_TTL_MS = 10 * 60 * 1000;\nconst CATALOG_FAILURE_TTL_MS = 60 * 1000;\nconst CATALOG_FETCH_TIMEOUT_MS = 5_000;\n\ninterface CatalogCacheEntry {\n fetchedAt: number;\n ttl: number;\n models: CopilotModelEntry[];\n}\n\nconst catalogCache = new Map<string, CatalogCacheEntry>();\nconst inflightFetches = new Map<string, Promise<CopilotModelEntry[]>>();\n\n/**\n * Resolve the account identity for a credential read through `getApiKey`, which\n * may await a refresh with a rotation landing inside it. The registry is\n * consulted on both sides of that await, and only an id that survived it names\n * the account that produced the token: an id that moved describes whichever\n * account is active *now*, so trusting it would file this fetch's models under\n * the wrong account and serve them to it. Unidentified is the safe answer — the\n * caller then skips the TTL cache, exactly as it does when the store exposes no\n * registry at all. (`AuthStorage` never needs this: its snapshots name their\n * account.)\n */\nfunction readStableAccountId(storage: CredentialStore, idBeforeToken: string | undefined): string | undefined {\n const idAfterToken = storage.getActiveAccount?.(COPILOT_PROVIDER_ID)?.id;\n return idBeforeToken !== undefined && idBeforeToken === idAfterToken ? idAfterToken : undefined;\n}\n\n/** Reset the in-process Copilot catalog cache (test seam, also useful after logout). */\nexport function clearCopilotCatalogCache(): void {\n catalogCache.clear();\n inflightFetches.clear();\n}\n\n/**\n * Return the user's currently-available Copilot models.\n *\n * - Returns `[]` when the user is not logged in to GitHub Copilot.\n * - Returns the cached list when a recent fetch succeeded.\n * - On the cache-miss / expired path, fetches `/models` with a 5s timeout, filters\n * to picker-enabled and non-policy-disabled models, then caches for 10 minutes.\n * - On fetch failure, returns a small hard-coded fallback (so packs still work\n * offline) and caches that for 1 minute to avoid hammering the API.\n *\n * Concurrent calls during a fetch share the inflight promise.\n */\nexport async function getCopilotModelCatalog(\n opts: { authStorage?: CredentialStore } = {},\n): Promise<CopilotModelEntry[]> {\n const storage = opts.authStorage ?? getAuthStorage();\n\n // Resolve one coherent credential snapshot before consulting the cache so a\n // token can never be paired with another account's enterprise endpoint.\n // `getOAuthCredential` is optional (deployed stores have no local registry),\n // so fall back to the always-present `getApiKey`, taking any enterprise\n // endpoint from the same slot via `get()`.\n let accessToken: string | undefined;\n let accountInstanceId: string | undefined;\n let enterpriseUrl: string | undefined;\n if (storage.getOAuthCredential) {\n const credential = await storage.getOAuthCredential(COPILOT_PROVIDER_ID);\n if (!credential || credential.type !== 'oauth') return [];\n accessToken = credential.access;\n // A snapshot that names its account is coherent by construction — the store\n // read the identity and the token in the same breath. One that does not\n // cannot be attributed to a registry entry we then read afterwards, because\n // a rotation may have landed in between; leave it unnamed so the caller\n // skips the TTL cache instead of guessing. (`AuthStorage` always stamps.)\n accountInstanceId = credential.accountInstanceId;\n enterpriseUrl = (credential as GitHubCopilotCredentials).enterpriseUrl;\n } else {\n // This store republishes its registry but not the OAuth snapshot API, so the\n // identity has to come from the registry. `getApiKey` may await a refresh and\n // a rotation can land inside it, so bracket the fetch: only an id that held\n // still names the account that produced this token.\n const idBeforeToken = storage.getActiveAccount?.(COPILOT_PROVIDER_ID)?.id;\n accessToken = await storage.getApiKey(COPILOT_PROVIDER_ID);\n if (!accessToken) return [];\n accountInstanceId = readStableAccountId(storage, idBeforeToken);\n const stored = storage.get(COPILOT_PROVIDER_ID);\n if (stored?.type === 'oauth') {\n enterpriseUrl = (stored as GitHubCopilotCredentials).enterpriseUrl;\n }\n // The id is non-secret, and keying per account is what keeps entitlements\n // (per account, per `github-copilot-catalog.test.ts`) from leaking across a\n // rotation. Deriving the key from the token is what `cba11389e7` removed.\n }\n const baseUrl = getGitHubCopilotBaseUrl(accessToken, enterpriseUrl);\n // No identity means the store cannot name the account it just served, so the\n // TTL cache cannot be keyed per account — one entry would outlive the request\n // and serve those models to every other account of the store. The in-flight\n // map is no safer: two callers that resolved *different* accounts of an\n // unnamed store would otherwise collide on one key and share whichever\n // catalog was fetched first. So both caches are identified-only, and an\n // unnamed store pays one `/models` request per call.\n const credentialKey = accountInstanceId === undefined ? undefined : `${accountInstanceId}\\0${baseUrl}`;\n const dedupeKey = credentialKey;\n\n const now = Date.now();\n const cached = credentialKey ? catalogCache.get(credentialKey) : undefined;\n if (cached && now - cached.fetchedAt < cached.ttl) return cached.models;\n\n const existingFetch = dedupeKey === undefined ? undefined : inflightFetches.get(dedupeKey);\n if (existingFetch) return existingFetch;\n\n const fetchPromise = (async (): Promise<CopilotModelEntry[]> => {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), CATALOG_FETCH_TIMEOUT_MS);\n try {\n const models = await fetchCopilotModels({\n baseUrl,\n bearerToken: accessToken,\n signal: controller.signal,\n });\n if (credentialKey) catalogCache.set(credentialKey, { fetchedAt: Date.now(), ttl: CATALOG_TTL_MS, models });\n return models;\n } finally {\n clearTimeout(timer);\n }\n } catch (error) {\n if (credentialKey) {\n catalogCache.set(credentialKey, {\n fetchedAt: Date.now(),\n ttl: CATALOG_FAILURE_TTL_MS,\n models: COPILOT_FALLBACK_MODELS,\n });\n }\n console.warn(\n 'Failed to fetch live GitHub Copilot models, using fallback list:',\n error instanceof Error ? error.message : error,\n );\n return COPILOT_FALLBACK_MODELS;\n } finally {\n if (dedupeKey !== undefined) inflightFetches.delete(dedupeKey);\n }\n })();\n if (dedupeKey !== undefined) inflightFetches.set(dedupeKey, fetchPromise);\n return fetchPromise;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../src/providers/kimi-coding.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../src/providers/kimi-coding.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AAOzE,eAAO,MAAM,kBAAkB,4EAA6E,CAAC;AAI7G,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAErE;AAOD,wBAAgB,yBAAyB,CAAC,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,eAAe,CAAA;CAAO,GAAG,OAAO,KAAK,CAoC3G;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,KAAK,CASvE;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,eAAe,CAAC,EAAE,eAAe,CAAA;CAAE,GAC/F,iBAAiB,CAYnB"}
|
|
@@ -26,9 +26,23 @@ function buildKimiCodingOAuthFetch(options = {}) {
|
|
|
26
26
|
const credential = store.get(PROVIDER_ID);
|
|
27
27
|
if (!credential || credential.type !== "oauth") throw new ProviderAuthRequiredError("Not logged in to Kimi For Coding.");
|
|
28
28
|
if (!isKimiCodingDeviceId(credential.deviceId)) throw new ProviderAuthRequiredError("Kimi For Coding credentials are invalid. Please reconnect the account.");
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
let token;
|
|
30
|
+
let activeCredential = credential;
|
|
31
|
+
if (store.getOAuthCredential) {
|
|
32
|
+
activeCredential = await store.getOAuthCredential(PROVIDER_ID);
|
|
33
|
+
token = activeCredential?.access;
|
|
34
|
+
} else {
|
|
35
|
+
token = await store.getApiKey(PROVIDER_ID);
|
|
36
|
+
store.reload();
|
|
37
|
+
const reloaded = store.get(PROVIDER_ID);
|
|
38
|
+
activeCredential = reloaded?.type === "oauth" ? {
|
|
39
|
+
...reloaded,
|
|
40
|
+
access: token ?? reloaded.access
|
|
41
|
+
} : void 0;
|
|
42
|
+
}
|
|
31
43
|
if (!token) throw new ProviderAuthRequiredError("Failed to refresh the Kimi For Coding token.");
|
|
44
|
+
if (!activeCredential || !isKimiCodingDeviceId(activeCredential.deviceId)) throw new ProviderAuthRequiredError("Kimi For Coding credentials are invalid. Please reconnect the account.");
|
|
45
|
+
const deviceHeaders = getKimiCodingDeviceHeaders(activeCredential.deviceId);
|
|
32
46
|
const headers = new Headers(input instanceof Request ? input.headers : void 0);
|
|
33
47
|
if (init?.headers) new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
34
48
|
for (const [key, value] of Object.entries(deviceHeaders)) headers.set(key, value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kimi-coding.js","names":[],"sources":["../../src/providers/kimi-coding.ts"],"sourcesContent":["import { createAnthropic } from '@ai-sdk/anthropic';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport { getKimiCodingDeviceHeaders, isKimiCodingDeviceId } from '../auth/providers/kimi-coding.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore } from '../auth/types.js';\n\nconst PROVIDER_ID = 'kimi-for-coding';\n// Pi's provider root is https://api.kimi.com/coding. The AI SDK expects the\n// versioned Anthropic base URL and appends /messages itself.\nconst BASE_URL = 'https://api.kimi.com/coding/v1';\n\nexport const KIMI_CODING_MODELS = ['k3', 'k3-256k', 'kimi-for-coding', 'kimi-for-coding-highspeed'] as const;\n\nlet authStorageInstance: AuthStorage | null = null;\n\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\nfunction getAuthStorage(): AuthStorage {\n if (!authStorageInstance) authStorageInstance = new AuthStorage();\n return authStorageInstance;\n}\n\nexport function buildKimiCodingOAuthFetch(options: { credentialStore?: CredentialStore } = {}): typeof fetch {\n return (async (input: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const store = options.credentialStore ?? getAuthStorage();\n store.reload();\n const credential = store.get(PROVIDER_ID);\n if (!credential || credential.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to Kimi For Coding.');\n }\n if (!isKimiCodingDeviceId(credential.deviceId)) {\n throw new ProviderAuthRequiredError('Kimi For Coding credentials are invalid. Please reconnect the account.');\n }\n
|
|
1
|
+
{"version":3,"file":"kimi-coding.js","names":[],"sources":["../../src/providers/kimi-coding.ts"],"sourcesContent":["import { createAnthropic } from '@ai-sdk/anthropic';\nimport type { MastraModelConfig } from '@mastra/core/llm';\nimport { ProviderAuthRequiredError } from '../auth/provider-auth-error.js';\nimport { getKimiCodingDeviceHeaders, isKimiCodingDeviceId } from '../auth/providers/kimi-coding.js';\nimport { AuthStorage } from '../auth/storage.js';\nimport type { CredentialStore, OAuthCredential } from '../auth/types.js';\n\nconst PROVIDER_ID = 'kimi-for-coding';\n// Pi's provider root is https://api.kimi.com/coding. The AI SDK expects the\n// versioned Anthropic base URL and appends /messages itself.\nconst BASE_URL = 'https://api.kimi.com/coding/v1';\n\nexport const KIMI_CODING_MODELS = ['k3', 'k3-256k', 'kimi-for-coding', 'kimi-for-coding-highspeed'] as const;\n\nlet authStorageInstance: AuthStorage | null = null;\n\nexport function setAuthStorage(storage: AuthStorage | undefined): void {\n authStorageInstance = storage ?? null;\n}\n\nfunction getAuthStorage(): AuthStorage {\n if (!authStorageInstance) authStorageInstance = new AuthStorage();\n return authStorageInstance;\n}\n\nexport function buildKimiCodingOAuthFetch(options: { credentialStore?: CredentialStore } = {}): typeof fetch {\n return (async (input: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const store = options.credentialStore ?? getAuthStorage();\n store.reload();\n const credential = store.get(PROVIDER_ID);\n if (!credential || credential.type !== 'oauth') {\n throw new ProviderAuthRequiredError('Not logged in to Kimi For Coding.');\n }\n if (!isKimiCodingDeviceId(credential.deviceId)) {\n throw new ProviderAuthRequiredError('Kimi For Coding credentials are invalid. Please reconnect the account.');\n }\n let token: string | undefined;\n let activeCredential: OAuthCredential | undefined = credential;\n if (store.getOAuthCredential) {\n activeCredential = await store.getOAuthCredential(PROVIDER_ID);\n token = activeCredential?.access;\n } else {\n token = await store.getApiKey(PROVIDER_ID);\n store.reload();\n const reloaded = store.get(PROVIDER_ID);\n activeCredential = reloaded?.type === 'oauth' ? { ...reloaded, access: token ?? reloaded.access } : undefined;\n }\n if (!token) throw new ProviderAuthRequiredError('Failed to refresh the Kimi For Coding token.');\n if (!activeCredential || !isKimiCodingDeviceId(activeCredential.deviceId)) {\n throw new ProviderAuthRequiredError('Kimi For Coding credentials are invalid. Please reconnect the account.');\n }\n const deviceHeaders = getKimiCodingDeviceHeaders(activeCredential.deviceId);\n\n const headers = new Headers(input instanceof Request ? input.headers : undefined);\n if (init?.headers) new Headers(init.headers).forEach((value, key) => headers.set(key, value));\n for (const [key, value] of Object.entries(deviceHeaders)) headers.set(key, value);\n headers.delete('authorization');\n headers.delete('x-api-key');\n headers.set('Authorization', `Bearer ${token}`);\n return fetch(input, { ...init, headers });\n }) as typeof fetch;\n}\n\nexport function buildKimiCodingApiKeyFetch(apiKey: string): typeof fetch {\n return (async (input: string | URL | Request, init?: Parameters<typeof fetch>[1]) => {\n const headers = new Headers(input instanceof Request ? input.headers : undefined);\n if (init?.headers) new Headers(init.headers).forEach((value, key) => headers.set(key, value));\n headers.delete('authorization');\n headers.delete('x-api-key');\n headers.set('Authorization', `Bearer ${apiKey}`);\n return fetch(input, { ...init, headers });\n }) as typeof fetch;\n}\n\nexport function kimiCodingProvider(\n modelId: string,\n options: { apiKey: string; headers?: Record<string, string>; credentialStore?: CredentialStore },\n): MastraModelConfig {\n const usesOAuth = options.credentialStore?.get(PROVIDER_ID)?.type === 'oauth';\n const provider = createAnthropic({\n name: PROVIDER_ID,\n apiKey: 'auth-placeholder',\n baseURL: BASE_URL,\n headers: options.headers,\n fetch: usesOAuth\n ? (buildKimiCodingOAuthFetch({ credentialStore: options.credentialStore }) as any)\n : (buildKimiCodingApiKeyFetch(options.apiKey) as any),\n });\n return provider(modelId) as unknown as MastraModelConfig;\n}\n"],"mappings":";;;;;AAOA,MAAM,cAAc;AAGpB,MAAM,WAAW;AAEjB,MAAa,qBAAqB;CAAC;CAAM;CAAW;CAAmB;AAA2B;AAElG,IAAI,sBAA0C;AAE9C,SAAgB,eAAe,SAAwC;CACrE,sBAAsB,WAAW;AACnC;AAEA,SAAS,iBAA8B;CACrC,IAAI,CAAC,qBAAqB,sBAAsB,IAAI,YAAY;CAChE,OAAO;AACT;AAEA,SAAgB,0BAA0B,UAAiD,CAAC,GAAiB;CAC3G,QAAQ,OAAO,OAA+B,SAAuC;EACnF,MAAM,QAAQ,QAAQ,mBAAmB,eAAe;EACxD,MAAM,OAAO;EACb,MAAM,aAAa,MAAM,IAAI,WAAW;EACxC,IAAI,CAAC,cAAc,WAAW,SAAS,SACrC,MAAM,IAAI,0BAA0B,mCAAmC;EAEzE,IAAI,CAAC,qBAAqB,WAAW,QAAQ,GAC3C,MAAM,IAAI,0BAA0B,wEAAwE;EAE9G,IAAI;EACJ,IAAI,mBAAgD;EACpD,IAAI,MAAM,oBAAoB;GAC5B,mBAAmB,MAAM,MAAM,mBAAmB,WAAW;GAC7D,QAAQ,kBAAkB;EAC5B,OAAO;GACL,QAAQ,MAAM,MAAM,UAAU,WAAW;GACzC,MAAM,OAAO;GACb,MAAM,WAAW,MAAM,IAAI,WAAW;GACtC,mBAAmB,UAAU,SAAS,UAAU;IAAE,GAAG;IAAU,QAAQ,SAAS,SAAS;GAAO,IAAI,KAAA;EACtG;EACA,IAAI,CAAC,OAAO,MAAM,IAAI,0BAA0B,8CAA8C;EAC9F,IAAI,CAAC,oBAAoB,CAAC,qBAAqB,iBAAiB,QAAQ,GACtE,MAAM,IAAI,0BAA0B,wEAAwE;EAE9G,MAAM,gBAAgB,2BAA2B,iBAAiB,QAAQ;EAE1E,MAAM,UAAU,IAAI,QAAQ,iBAAiB,UAAU,MAAM,UAAU,KAAA,CAAS;EAChF,IAAI,MAAM,SAAS,IAAI,QAAQ,KAAK,OAAO,CAAC,CAAC,SAAS,OAAO,QAAQ,QAAQ,IAAI,KAAK,KAAK,CAAC;EAC5F,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,aAAa,GAAG,QAAQ,IAAI,KAAK,KAAK;EAChF,QAAQ,OAAO,eAAe;EAC9B,QAAQ,OAAO,WAAW;EAC1B,QAAQ,IAAI,iBAAiB,UAAU,OAAO;EAC9C,OAAO,MAAM,OAAO;GAAE,GAAG;GAAM;EAAQ,CAAC;CAC1C;AACF;AAEA,SAAgB,2BAA2B,QAA8B;CACvE,QAAQ,OAAO,OAA+B,SAAuC;EACnF,MAAM,UAAU,IAAI,QAAQ,iBAAiB,UAAU,MAAM,UAAU,KAAA,CAAS;EAChF,IAAI,MAAM,SAAS,IAAI,QAAQ,KAAK,OAAO,CAAC,CAAC,SAAS,OAAO,QAAQ,QAAQ,IAAI,KAAK,KAAK,CAAC;EAC5F,QAAQ,OAAO,eAAe;EAC9B,QAAQ,OAAO,WAAW;EAC1B,QAAQ,IAAI,iBAAiB,UAAU,QAAQ;EAC/C,OAAO,MAAM,OAAO;GAAE,GAAG;GAAM;EAAQ,CAAC;CAC1C;AACF;AAEA,SAAgB,mBACd,SACA,SACmB;CACnB,MAAM,YAAY,QAAQ,iBAAiB,IAAI,WAAW,CAAC,EAAE,SAAS;CAUtE,OATiB,gBAAgB;EAC/B,MAAM;EACN,QAAQ;EACR,SAAS;EACT,SAAS,QAAQ;EACjB,OAAO,YACF,0BAA0B,EAAE,iBAAiB,QAAQ,gBAAgB,CAAC,IACtE,2BAA2B,QAAQ,MAAM;CAChD,CACc,CAAC,CAAC,OAAO;AACzB"}
|