コンテンツにスキップ

Provider ソースコード分析

モジュール概要

Provider は OpenCode の モデル抽象化レイヤーおよびルーティングハブ であり、packages/opencode/src/provider/ に位置しています。TypeScript 版では、HTTP クライアントを直接構築するのではなく、Vercel AI SDKai パッケージ)を使用して 30 以上の LLM Provider と統一的にインターフェースします。上位モジュール(Agent、Session)は Provider.getLanguage() を通じて標準化された LanguageModel オブジェクトを取得し、その後 streamText / generateText を呼び出します。これにより、基盤となる API の差異から完全に分離されています。

コアとなる設計上の選択:

  • Vercel AI SDK を統一アダプターレイヤーとして採用し、各 Provider ごとに HTTP プロトコルを手書きする作業を回避
  • Effect Schema の branded 型ProviderIDModelID)により、型レベルで異なるエンティティを区別
  • models.dev をモデルメタデータの外部データソースとして使用し、実行時に動的にロード
  • CUSTOM_LOADERS 辞書が各 Provider の特別なロジックを処理(Bedrock のリージョンプレフィックス、Copilot SDK 適応、Vertex 認証など)
  • Instance.state がプロセスレベルのシングルトンキャッシュを提供し、Provider の初期化は一度だけ実行

主要ファイル

ファイル担当領域
src/provider/provider.tsコア名前空間:状態管理、Provider ルーティング、SDK ファクトリ、モデル検索、CUSTOM_LOADERS
src/provider/models.tsmodels.dev データソースの読み込み:Zod スキーマ定義、JSON パース、定期リフレッシュ、スナップショットフォールバック
src/provider/schema.tsEffect Schema の branded 型 ProviderID / ModelID、Zod ブリッジを含む
src/provider/transform.tsリクエスト/レスポンス変換:メッセージ正規化、キャッシュマーカー、推論バリアント、temperature/topP 調整、エラーフォーマット
src/provider/auth.tsProvider 認証:OAuth フロー、API Key 管理、Plugin 認証統合
src/provider/error.tsエラー分類:コンテキストオーバーフロー検出(12 以上の Provider エラーパターン)、API エラーパース、再試行可能性判定
src/provider/sdk/copilot/カスタム GitHub Copilot SDK アダプター(chat/ + responses/ サブディレクトリ、約 20 ファイル)

sdk/copilot/ は公式の Vercel AI SDK パッケージに依存しない唯一の Provider です。これは、Copilot 固有のデバイス認証、トークンリフレッシュ、API 互換性の問題を処理する必要があるためです。README では「一時的なパッケージ」としてマークされており、将来的に公式 SDK に置き換えられる可能性があります。

型システム

Branded 型:ProviderID と ModelID

// schema.ts — Effect Schema branded types
const providerIdSchema = Schema.String.pipe(Schema.brand("ProviderID"))
export type ProviderID = typeof providerIdSchema.Type

export const ProviderID = providerIdSchema.pipe(
  withStatics((schema) => ({
    make: (id: string) => schema.makeUnsafe(id),    // Runtime construction
    zod: z.string().pipe(z.custom()),                // Zod validation bridge
    // Built-in constants
    anthropic: schema.makeUnsafe("anthropic"),
    openai: schema.makeUnsafe("openai"),
    githubCopilot: schema.makeUnsafe("github-copilot"),
    amazonBedrock: schema.makeUnsafe("amazon-bedrock"),
    // ...
  })),
)

Branded 型により、ProviderIDModelIDコンパイル時には相互に置き換え不可能 になります。モデル ID を Provider ID を期待する関数に渡すことはできません。makeUnsafe は実行時に文字列から構築するために使用され(例:ユーザー設定のパース)、定数プロパティ(ProviderID.anthropic)は型安全な参照ポイントを提供します。

Provider.Model 完全なモデル定義

// provider.ts — Zod schema
export const Model = z.object({
  id: ModelID.zod,           // Branded type identifier
  providerID: ProviderID.zod,
  api: z.object({
    id: z.string(),          // Actual model name sent to the API
    url: z.string(),         // API base URL
    npm: z.string(),         // Vercel AI SDK package name (e.g. "@ai-sdk/anthropic")
  }),
  name: z.string(),          // Human-readable name
  family: z.string().optional(),  // Model family (e.g. "claude", "gpt")
  capabilities: z.object({
    temperature: z.boolean(),
    reasoning: z.boolean(),       // Whether thinking/reasoning mode is supported
    attachment: z.boolean(),      // Whether attachments are supported
    toolcall: z.boolean(),        // Whether tool calls are supported
    input: z.object({
      text: z.boolean(), audio: z.boolean(),
      image: z.boolean(), video: z.boolean(), pdf: z.boolean(),
    }),
    output: z.object({
      text: z.boolean(), audio: z.boolean(),
      image: z.boolean(), video: z.boolean(), pdf: z.boolean(),
    }),
    interleaved: z.union([       // Interleaved thinking mode
      z.boolean(),
      z.object({ field: z.enum(["reasoning_content", "reasoning_details"]) }),
    ]),
  }),
  cost: z.object({
    input: z.number(),      // $/M tokens (input)
    output: z.number(),     // $/M tokens (output)
    cache: z.object({
      read: z.number(),     // Cache read unit price
      write: z.number(),    // Cache write unit price
    }),
    experimentalOver200K: z.object({ /* ... */ }).optional(),  // Over 200K pricing
  }),
  limit: z.object({
    context: z.number(),    // Context window
    input: z.number().optional(),
    output: z.number(),     // Maximum output tokens
  }),
  status: z.enum(["alpha", "beta", "deprecated", "active"]),
  options: z.record(z.string(), z.any()),  // Provider-specific options
  headers: z.record(z.string(), z.string()), // Custom request headers
  release_date: z.string(),
  variants: z.record(/* reasoning effort variants */).optional(),
})

Model はモデルの能力だけでなく、完全なコスト情報推論バリアント設定 も記述します。variants フィールドは ProviderTransform.variants() によりモデルと SDK パッケージ名に基づいて自動生成されます(例:Anthropic の high/max 思考予算、OpenAI の none/minimal/low/medium/high/xhigh 推論エフォート)。

Provider.Info

export const Info = z.object({
  id: ProviderID.zod,
  name: z.string(),
  source: z.enum(["env", "config", "custom", "api"]),  // Source tracking
  env: z.string().array(),      // Environment variable name list (e.g. ["ANTHROPIC_API_KEY"])
  key: z.string().optional(),   // Resolved API Key
  options: z.record(z.string(), z.any()),  // Provider-level options
  models: z.record(z.string(), Model),     // All models under this Provider
})

source フィールドは Provider 認証情報ソースの優先順位を追跡します:env(環境変数) > api(Auth ストア) > config(設定ファイル) > custom(Plugin/Loader)。

コアフロー

Provider 状態初期化

Instance.state() はプロセスレベルのシングルトンを作成し、以下の順序で利用可能な Provider を組み立てます:

1. Config.get()           → ユーザー設定の読み取り
2. ModelsDev.get()        → models.dev データの読み込み(キャッシュ → スナップショット → ネットワーク取得)
3. fromModelsDevProvider() → Provider.Info データベースへの変換
4. 環境変数スキャン      → env フィールドの照合、source: "env" を設定
5. Auth.all()             → 永続化された API Key / OAuth トークンの読み取り
6. Plugin.list()          → Plugin 提供の認証の処理(例:Copilot デバイス認証)
7. CUSTOM_LOADERS         → 各 Provider のカスタムロジックの実行
8. Config マージ          → モデル定義、オプション、ホワイトリスト/ブラックリストの上書き
9. フィルタリング        → 無効化された Provider、alpha(非実験モード)、モデルリストが空の非推奨 Provider の削除

初期化結果はメモリにキャッシュされ、その後の Provider.list() / Provider.getModel() の呼び出しはキャッシュから直接読み取ります。

SDK ファクトリと 30+ Provider ルーティング

getSDK() 関数は指定されたモデルの Vercel AI SDK Provider インスタンスを作成します:

// Built-in Provider mapping: 20 official SDK packages
const BUNDLED_PROVIDERS: Record<string, (...args: any[]) => SDK> = {
  "@ai-sdk/anthropic": createAnthropic,
  "@ai-sdk/openai": createOpenAI,
  "@ai-sdk/google": createGoogleGenerativeAI,
  "@ai-sdk/amazon-bedrock": createAmazonBedrock,
  "@ai-sdk/azure": createAzure,
  "@openrouter/ai-sdk-provider": createOpenRouter,
  "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible,  // Custom adapter
  // ... also Groq, Mistral, DeepInfra, Cerebras, Cohere, Together, Perplexity, Vercel, GitLab, etc.
}

ルーティングロジック:

  1. BUNDLED_PROVIDERS[model.api.npm] を検索 — 見つかった場合、ファクトリ関数を直接呼び出し
  2. 見つからない場合、BunProc.install() を通じて npm パッケージを動的にインストールし、import する
  3. 各インスタンスは { providerID, npm, options } のハッシュでキャッシュされ、同じ設定を再利用する

getLanguage() はさらに SDK インスタンスを LanguageModel に変換します:

// If this Provider has a custom modelLoader, use it; otherwise call sdk.languageModel()
const language = s.modelLoaders[model.providerID]
  ? await s.modelLoaders[model.providerID](sdk, model.api.id, provider.options)
  : sdk.languageModel(model.api.id)

モデル発見:models.dev データソース

models.ts は 3 段階のフォールバックデータ読み込み戦略を実装しています:

優先順位:
1. ローカルキャッシュファイル (~/.cache/opencode/models.json)
2. ビルド時スナップショット (models-snapshot.ts、バイナリにバンドル)
3. リモート取得 (https://models.dev/api.json)

起動後、60 分ごとにバックグラウンドでリフレッシュします(setInterval + unref()、プロセスの終了をブロックしない)。モデルデータは ModelsDev.Model Zod スキーマで検証された後、Provider.Model に変換され、構造的一貫性が確保されます。

カスタム Provider ローダー(CUSTOM_LOADERS)

CUSTOM_LOADERS 辞書は特別な Provider のカスタムロジックを提供し、各ローダーは { autoload, getModel?, options?, vars? } を返します:

Provider特別なロジック
anthropicベータヘッダーの追加(claude-code-20250219interleaved-thinking
openaiChat の代わりに Responses API(sdk.responses(modelID))を使用
github-copilotモデル ID に基づいて responses() または chat() を選択
amazon-bedrockAWS 認証情報チェーン、リージョン推論、クロスリージョン推論プレフィックス(us./eu./apac.
google-vertexGoogle ADC 認証、project/location パース、カスタム fetch インジェクション
azure動的 baseURL テンプレート置換、resourceName 変数インジェクション
gitlabAgentic Chat API、カスタム User-Agent とフィーチャーフラグ
cloudflare-ai-gatewayai-gateway-provider パッケージの動的ロード、Unified API フォーマットルーティング
opencodeAPI Key の利用可能性を検出し、Key がない場合は無料モデルのみ保持

Copilot カスタム SDK アダプター

provider/sdk/copilot/ ディレクトリにはスタンドアロンの Copilot SDK アダプターが含まれており、構造は以下の通りです:

sdk/copilot/
├── index.ts                  # createOpenaiCompatible factory function
├── copilot-provider.ts       # Provider implementation
├── openai-compatible-error.ts # Error handling
├── chat/                     # Chat Completions API adapter
└── responses/                # Responses API adapter

このアダプターは @ai-sdk/openai に依存せず、Copilot 固有のデバイス認証フロー(OAuth デバイスコードフロー)、トークンリフレッシュ、API 互換性処理を独自に実装しています。README では「一時的」とマークされており、Vercel AI SDK が公式の Copilot サポートを提供する際に削除される予定です。

カスタム fetch インジェクション

各 SDK インスタンスはインジェクションされたカスタム fetch と共に作成され、3 つの主要な機能を提供します:

  1. SSE タイムアウトラッパーwrapSSE):各チャンク読み取りにタイムアウトを設定、デフォルト 5 分。Provider のサイレント切断による接続の無限ハングを防止
  2. OpenAI リクエストボディ最適化:入力配列から id フィールドを削除し、不要なネットワーク送信を削減
  3. Signal マージ:元の signal + chunkAbort + timeoutAbortSignal.any() で統一された中止シグナルに結合
// Signal merging illustration
const combined = AbortSignal.any([
  originalSignal,     // User cancellation
  chunkAbort,         // Chunk-level timeout
  timeoutSignal,      // Global timeout
])

この階層的な signal 設計により、ユーザー主導のキャンセルは即座に有効になり、個々のチャンクタイムアウトは再接続をトリガーし、グローバルタイムアウトが最終的なフォールバックとして機能します。

ProviderAuth OAuth フロー

ProviderAuth 名前空間は 2 つの認証タイプの完全なライフサイクルを処理します:

API Key 認証key タイプ):

  • 環境変数または設定ファイルから API Key を取得
  • provider.key を直接設定し、source: "env" または source: "config" としてマーク

OAuth 認証refresh タイプ):

  • authorize フェーズ:Plugin フックから認証設定を取得(例:Copilot のデバイスコードフロー URL)
  • callback フェーズauto(自動コールバック)と code(手動認証コード入力)のコールバック方式をサポート
  • 認証完了後、リフレッシュトークンが永続化され、自動リフレッシュによるセッション維持
// Authentication type determination
key → api type auth     // Use API Key directly
refresh → oauth type auth  // Use OAuth refresh token

モデルフィルタリングとホワイトリスト/ブラックリスト

Provider の初期化完了後、モデルリストは複数層のフィルタリングを経ます:

  1. autoload チェックCUSTOM_LOADERSautoload: false の Provider はスキップ(例:API Key がない Provider)
  2. disable フラグdisable: true とマークされたモデルは直接スキップ
  3. 無料モデルフォールバック:API Key がない場合、無料とマークされたモデルのみ保持(例:opencode Provider の無料モデル)
  4. SDK インスタンスキャッシュ{ providerID, npm, options } のハッシュでキャッシュ、同じ設定は同じインスタンスを再利用

LiteLLM プロキシ互換性

OpenCode は LiteLLM プロキシを検出し、メッセージフォーマットを自動的に適応させます:

  • レスポンスヘッダーまたはリクエストパスの LiteLLM 識別子を検出
  • メッセージ履歴にツール呼び出しが含まれているが、現在のメッセージにツール定義がない場合、ダミーツールをインジェクション
  • これは、メッセージに tool_call ロールが含まれているのにリクエストにツールが含まれていない場合に LiteLLM がエラーになるためです。

この互換性処理により、OpenCode は LiteLLM プロキシを通じて様々なモデルに透過的にアクセスできます。

リクエスト変換レイヤー

ProviderTransform 名前空間は、メッセージが送信される前に Provider 固有の変換を処理します。変換パイプライン全体は wrapLanguageModel ミドルウェア内で呼び出されます:

wrapLanguageModel ミドルウェア

LLM.stream()streamText() を呼び出す前に wrapLanguageModel() を通じてメッセージ変換ミドルウェアをインジェクションします:

model: wrapLanguageModel({
  model: language,
  middleware: [{
    specificationVersion: "v3" as const,
    async transformParams(args) {
      if (args.type === "stream") {
        args.params.prompt = ProviderTransform.message(args.params.prompt, input.model, options)
      }
      return args.params
    },
  }],
})

ProviderTransform.message() は 4 つの変換ステップを順番に実行します:unsupportedPartsnormalizeMessagesapplyCachingproviderOptions キーリマップ。このミドルウェア設計により、変換ロジックと SDK 呼び出しロジックが完全に分離されます。

メッセージ正規化(normalizeMessages)

  • Anthropic:空のコンテンツメッセージをフィルタリング(API が空文字列を拒否するため)
  • Claude モデル:ツール呼び出し ID の非英数字文字を _ に置換
  • Mistral:ツール呼び出し ID を正確に 9 文字の英数字に正規化。ツールメッセージの直後にユーザーメッセージは不可(自動的に “Done.” アシスタントメッセージを挿入)
  • インターリーブ思考モデル:推論部分をコンテンツから providerOptions.openaiCompatible.reasoning_content に移動

キャッシュマーカー(applyCaching)

Prompt Caching をサポートする Provider に自動的にキャッシュマーカーを追加します。キャッシュポリシーには明確なルールがあります:

  • 最初の 2 つのシステムメッセージ + 最後の 2 つのメッセージ にキャッシュマーカーが付与
  • 各 Provider は異なるフォーマットを使用:
// Anthropic
{ cacheControl: { type: "ephemeral" } }

// Amazon Bedrock
{ cachePoint: { type: "default" } }

Anthropic / OpenRouter / Bedrock など、Prompt Caching がサポートされていることが分かっている Provider に対してのみ有効です。他の Provider ではサイレントにスキップされます。

推論バリアント生成(variants)

model.api.npmmodel.id に基づいて推論エフォートバリアントを自動生成します。各 Provider は異なるバリアントパラメータを持ちます:

  • Anthropic{ thinking: { type: "enabled", budgetTokens } }(high: 16K、max: 32K)
  • OpenAI{ reasoningEffort, reasoningSummary: "auto" }(none から xhigh)
  • Google{ thinkingConfig: { thinkingBudget, includeThoughts } }
  • Bedrock:Anthropic モデルは reasoningConfig.budgetTokens、Nova は maxReasoningEffort を使用

モデル固有のチューニング

// transform.ts — temperature auto-adjusted based on model ID
function temperature(model: Provider.Model) {
  if (id.includes("qwen"))   return 0.55
  if (id.includes("claude")) return undefined  // Use default value
  if (id.includes("gemini")) return 1.0
  if (id.includes("kimi-k2")) return id.includes("thinking") ? 1.0 : 0.6
  return undefined
}

エラー処理

コンテキストオーバーフロー検出

error.ts は 12 以上の Provider をカバーするオーバーフローパターンマッチングリストを維持しています:

const OVERFLOW_PATTERNS = [
  /prompt is too long/i,                      // Anthropic
  /input is too long for requested model/i,   // Amazon Bedrock
  /exceeds the context window/i,              // OpenAI
  /input token count.*exceeds the maximum/i,  // Google Gemini
  /maximum prompt length is \d+/i,            // xAI Grok
  /exceeds the limit of \d+/i,                // GitHub Copilot
  /context[_ ]length[_ ]exceeded/i,           // Generic fallback
  // ... more
]

エラーは 2 つのタイプに分類されます:

  • context_overflow:コンテキストがウィンドウ制限を超過、入力の切り詰めが必要
  • api_error:一般的な API エラー、statusCodeisRetryableresponseBody を含む

OpenAI Provider には特別な再試行ロジックがあります:HTTP 404 も再試行可能と見なされます(一部のモデルは最初のリクエストで 404 を返すが、実際には利用可能)。

カスタムエラータイプ

// provider.ts
ModelNotFoundError  — Contains providerID, modelID, and fuzzysort fuzzy match suggestions
InitError           — SDK initialization failure (package install/load error)

ProviderError.parseAPICallError() は上位レイヤー(Agent/Session)が使用する統一エラーパースのエントリポイントです。

SSE タイムアウト保護

wrapSSE() 関数は SSE ストリームにチャンクごとのタイムアウト(デフォルト 5 分)を追加し、ネットワーク切断時に接続が無限にハングするのを防止します:

function wrapSSE(res: Response, ms: number, ctl: AbortController) {
  // Set timeout for each chunk read
  // On timeout: abort connection + cancel reader
}

状態管理と Provider ホットスワップ

Provider の状態は Instance.state() を通じて管理されます。これは遅延初期化シングルトンファクトリであり、最初の呼び出しで初期化し、その後の呼び出しではキャッシュされた結果を返します。状態には以下が含まれます:

{
  providers: Record<string, Provider.Info>,     // All available Providers
  models: Map<string, LanguageModel>,            // Instantiated LanguageModel cache
  sdk: Map<string, SDK>,                         // Instantiated SDK Provider cache
  modelLoaders: Record<string, CustomModelLoader>, // Custom model loaders
  varsLoaders: Record<string, CustomVarsLoader>,   // Variable injectors (e.g. Azure resourceName)
}

モデルの切り替え時(ユーザーが異なるモデルを選択)、呼び出しチェーンは以下の通りです:

  1. Provider.getModel(newProviderID, newModelID) — モデル情報の検索
  2. Provider.getLanguage(model) — LanguageModel インスタンスの取得または作成
  3. getSDK(model) — SDK Provider インスタンスの取得または作成
  4. インスタンスは { providerID, npm, options } のハッシュでキャッシュ、同じ設定は再利用される

defaultModel() 関数は以下の優先順位でデフォルトモデルを選択します:

  1. 設定ファイルの model フィールド
  2. 最後に使用されたモデル(model.jsonrecent リスト)
  3. 優先順位順の最初の利用可能なモデル(gpt-5 > claude-sonnet-4 > gemini-3-pro

呼び出しチェーンの例

ユーザーがモデルを選択してからストリーミングレスポンスまで

ユーザーが "anthropic/claude-sonnet-4" を選択


Provider.getModel("anthropic", "claude-sonnet-4")
    │  ← state.providers.anthropic.models["claude-sonnet-4"] を検索
    │  ← 見つからない場合? fuzzysort ファジーマッチで ModelNotFoundError をスロー

Provider.getLanguage(model)
    │  ← modelLoaders["anthropic"] を検索
    │  ← カスタムローダーがある場合? loader(sdk, model.api.id, options) を呼び出し
    │  ← ない場合 sdk.languageModel(model.api.id)

getSDK(model)
    │  ← ハッシュキー = hash({ providerID, npm: "@ai-sdk/anthropic", options }) を計算
    │  ← キャッシュヒット? 直接返す
    │  ← キャッシュミス: BUNDLED_PROVIDERS["@ai-sdk/anthropic"] → createAnthropic(options)
    │  ← カスタム fetch をインジェクション(SSE タイムアウト + リクエストボディ最適化)

Agent が streamText({ model: languageModel, messages, tools }) を呼び出し
    │  ← Vercel AI SDK が実際の HTTP リクエストと SSE パースを処理

ストリーミングレスポンスが上位レイヤーに返される

モデル切り替え時の状態更新

ユーザーが claude-sonnet-4 から gpt-5 に切り替え


parseModel("openai/gpt-5") → { providerID: "openai", modelID: "gpt-5" }


Provider.getModel("openai", "gpt-5")
    │  ← state.providers["openai"] が存在するか?
    │  ← state.providers["openai"].models["gpt-5"] が存在するか?

Provider.getLanguage(model)
    │  ← getSDK: BUNDLED_PROVIDERS["@ai-sdk/openai"] → createOpenAI(options)
    │  ← modelLoaders["openai"]: sdk.responses("gpt-5")  (Responses API)

新しい LanguageModel インスタンスが state.models にキャッシュされる

設計上のトレードオフ

決定理由
直接 HTTP の代わりに Vercel AI SDK30 以上の Provider 間のプロトコル差異を SDK が統一的に処理し、大量の HTTP クライアントコードの保守を回避。OpenCode はビジネスロジック(メッセージ変換、キャッシュ、エラー分類)に注力
Effect Schema の branded 型コンパイル時の ProviderID / ModelID / string の混用を防止、ランタイムオーバーヘッドなし(branded 型は string に消去される)
models.dev 外部データソースモデル情報(価格、コンテキストウィンドウ、機能)はコミュニティが管理し、OpenCode がハードコードする必要なし。3 段階フォールバックによりオフライン可用性を確保
サブクラス継承の代わりに CUSTOM_LOADERS 辞書各 Provider の異なるロジック(認証方式、API 選択、リージョン処理)は継承階層で表現するには差が大きすぎる。辞書 + 関数の方が柔軟
カスタム Copilot SDK アダプターCopilot のデバイス認証とトークンリフレッシュフローは標準 OAuth と差が大きく、公式 SDK がサポートしていないため、独立した実装が必要
SSE チャンクごとのタイムアウトProvider のサイレント切断による接続の無限ハングを防止。デフォルト 5 分、chunkTimeout オプションで調整可能
fuzzysort ファジーマッチ提案モデル ID は頻繁にスペルミスやリネームされる。ファジーマッチはハードエラーの代わりに 3 つの提案を提供
バンドルされていない SDK の BunProc 動的インストールコミュニティ Provider(例:@mymediset/sap-ai-provider)をメインパッケージにバンドルする必要がなく、オンデマンドでインストール

他のモジュールとの関係

  • AgentProvider.getLanguage() を通じて LanguageModel を取得し、Vercel AI SDK の streamText() / generateText() に渡して会話ループを駆動
  • Config:Provider 初期化時に config.model(デフォルトモデル)、config.provider(Provider 設定オーバーライド)、config.disabled_providersconfig.enabled_providers を読み取り
  • AuthAuth.get(providerID) が永続化された API Key / OAuth トークンを読み取り。ProviderAuth 名前空間が OAuth 認可フローを処理
  • PluginPlugin.list() が登録された認証方式(例:Copilot デバイス認証)を取得し、plugin.auth.loader を通じて Provider オプションにインジェクション
  • InstanceInstance.state() が Provider 状態のシングルトン管理を提供し、プロジェクトライフサイクルに紐付け
  • TransformstreamText 呼び出し前に、Agent が ProviderTransform.message() を通じてメッセージを正規化し、キャッシュマーカーをインジェクション
  • Error:Agent が Vercel AI SDK の APICallErrorProviderError.parseAPICallError() で構造化された ParsedAPICallError に変換