Skip to content

[opencode 请求流程分析] OpenCode 向 GitHub Copilot 发送请求的流程、Header 与 Body 分析(2026-07-10) #1

Description

@nickhou1983

本报告基于当前 dev 分支代码(已与上游 anomalyco/opencode 同步至 8a03fc265)。仅做只读分析,未修改任何业务代码。

0. 本次同步提示

本次从上游同步进来 6 个提交d09d5612f..8a03fc265):

SHA 提交
8a03fc265 feat(app): extract prompt state and add composer model selection (anomalyco#36227)
d0ba53892 zen: track openai cache_write_tokens
772e976d8 fix(app): timeline width jumps around when review panel is open/closed (anomalyco#36202)
c76938077 feat(app): visual improvements (anomalyco#36073)
01131c75d fix(app): focus input on new session (anomalyco#36077)
ea982c383 fix(app): wrap session creation state updates in startTransition (anomalyco#36182)

这些提交集中在 packages/app(UI/会话交互)与 zen 计费统计,均未触及 GitHub Copilot 的请求流程 / Header / Body。故本报告结论相对上一次分析无变化。


1. 入口与整体架构

GitHub Copilot 的集成主要由一个插件驱动:

  • packages/opencode/src/plugin/github-copilot/copilot.ts —— CopilotAuthPlugin,负责鉴权(OAuth 设备码流程)、请求 fetch 包装(注入 Header)、模型列表拉取、以及 chat.params / chat.headers 钩子。注册于 packages/opencode/src/plugin/index.ts:15,72
  • packages/opencode/src/plugin/github-copilot/models.ts —— 拉取 {base}/models 并根据 supported_endpoints 决定每个模型走哪个端点(messages / responses / chat)及使用哪个 ai-sdk 包。
  • packages/core/src/github-copilot/ —— OpenAI 兼容的 Chat / Responses 语言模型实现(真正构造 HTTP body 的地方)。
  • packages/llm/src/providers/github-copilot.tspackages/core/src/plugin/provider/github-copilot.ts —— v2/effect 架构下的路由与端点选择。

Base URLcopilot.ts:26-28):

  • 公有云:https://api.githubcopilot.com
  • 企业版(GHE):https://copilot-api.<归一化后的企业域名>

端点与 npm 适配models.ts:92-111):

模型 supported_endpoints endpoint api.npm 实际路径
/v1/messages messages @ai-sdk/anthropic https://api.githubcopilot.com/v1/messages
/responses responses @ai-sdk/github-copilot .../responses
/chat/completions chat @ai-sdk/github-copilot .../chat/completions

GPT‑5 类模型(gpt-N,N≥5,且非 gpt-5-mini)默认走 Responses API,否则走 Chat Completions —— shouldUseResponsesApipackages/llm/src/providers/github-copilot.ts:19-25packages/core/src/plugin/provider/github-copilot.ts:42-48)。


2. GitHub 身份验证与 Token 交换流程

采用 OAuth 2.0 设备授权码流程(Device Authorization Grant, RFC 8628),全部实现在 copilot.tsauth.methods[0]copilot.ts:182-337)。

关键常量:CLIENT_ID = "Ov23li8tweQw6odWQebz"copilot.ts:9),scope = "read:user"copilot.ts:244)。

sequenceDiagram
    participant U as 用户
    participant OC as opencode (CopilotAuthPlugin)
    participant GH as github.com (OAuth)
    participant API as api.githubcopilot.com

    Note over OC: authorize() copilot.ts:222
    OC->>GH: POST /login/device/code<br/>{client_id, scope:"read:user"}
    GH-->>OC: {device_code, user_code, verification_uri, interval}
    OC-->>U: 显示 verification_uri + user_code
    U->>GH: 浏览器打开并输入 user_code

    Note over OC: callback() 轮询 copilot.ts:262
    loop 每 interval(+3s) 秒
        OC->>GH: POST /login/oauth/access_token<br/>{client_id, device_code,<br/>grant_type: device_code}
        GH-->>OC: authorization_pending / slow_down / access_token
    end
    GH-->>OC: {access_token}
    Note over OC: 存储 oauth{access=refresh=token, expires:0}<br/>provider/auth.ts:211-220

    Note over OC,API: 后续每次推理请求
    OC->>API: POST /chat|responses|v1/messages<br/>Authorization: Bearer GitHub-OAuth-token
    API-->>OC: SSE 流式响应
Loading

要点:

  1. 设备码申请copilot.ts:234-256):POST https://{domain}/login/device/code,请求体 { client_id, scope: "read:user" }。企业版 domain 来自用户输入的 enterpriseUrlcopilot.ts:222-232)。
  2. 轮询换取 access_tokencopilot.ts:262-333):POST https://{domain}/login/oauth/access_token,请求体 { client_id, device_code, grant_type: "urn:ietf:params:oauth:grant-type:device_code" };正确处理 authorization_pendingslow_down(按 RFC 8628 §3.5 增加 5s,并叠加 3s 安全余量 OAUTH_POLLING_SAFETY_MARGIN_MS)。
  3. 凭据落库copilot.ts:286-305provider/auth.ts:188-221):成功后写入 auth.json,类型 oauthaccess = refresh = <GitHub OAuth token>expires: 0,企业版另存 enterpriseUrl

⚠️ 关于「Token 交换」的重要结论

当前代码不存在把 GitHub OAuth token 兑换成短时 Copilot token 的步骤。 全仓库检索确认 没有https://api.github.com/copilot_internal/v2/token 之类端点的调用(git grep copilot_internal / api.github.com 无命中)。opencode 直接把设备码流程拿到的 GitHub OAuth 访问令牌 作为 Bearer 令牌发给 api.githubcopilot.com

  • 令牌注入位置:鉴权 loader 返回 { apiKey: "", fetch },其包装 fetch 在每次请求里设置 Authorization: Bearer <access>copilot.ts:96-180,第 164 行)。
  • getAuth() 只是读取已存储的凭据(provider/auth.tsprovider.ts:1543),不做刷新;Copilot 的 auth 方法里 没有 refresh 回调expires:0 也不会触发轮换。
  • 对 OAuth 类 provider,provider.ts:893-915 明确「只用插件 auth loader 的合并 fetch」,跳过 provider 级 fetch,因此上面的 Header 集合就是最终生效版本。

3. 客户端发送给 GitHub Copilot 的 Request Header

推理请求的最终 Header 由两处叠加而成:chat.headers 钩子(copilot.ts:360-411,其输出经 request.ts:134-146 传入 ai-sdk 的 options.headers)+ 鉴权 loader 的包装 fetchcopilot.ts:160-173,最后一道、优先级最高)。

3.1 推理请求(/chat/completions、/responses、/v1/messages 通用)

Header 取值 来源(代码位置) 作用 / 说明
Authorization Bearer <GitHub OAuth token> copilot.ts:164(强制覆盖) 鉴权;直接使用 GitHub OAuth 令牌
User-Agent opencode/<InstallationVersion> copilot.ts:163(强制覆盖) 客户端标识
Openai-Intent conversation-edits copilot.ts:165 Copilot 意图标记
x-initiator useragent copilot.ts:161;可被 chat.headers 覆盖(:394,:411 标记请求由用户还是 agent 发起;依据「最后一条消息角色 / 合成图片消息 / tool_result / 压缩续跑 / 子 agent 会话」判定
X-GitHub-Api-Version 2026-06-01 copilot.ts:363API_VERSION:10 Copilot API 版本
X-Interaction-Type agent-session-name-generation copilot.ts:364-366 仅在 title(会话命名)agent 时添加
anthropic-beta interleaved-thinking-2025-05-14 copilot.ts:368-370 仅当模型走 @ai-sdk/anthropic(即 Claude 走 /v1/messages)时添加
Copilot-Vision-Request true copilot.ts:168-170 仅当请求体包含图片(vision)时添加
Content-Type application/json ai-sdk postJsonToApi JSON 请求体
x-api-key / authorization(小写) —(删除) copilot.ts:172-173 主动剔除,避免与 Authorization 冲突
provider.options.headers.* 用户配置 copilot.ts:162 展开 init.headers 允许通过配置追加自定义 Header

Header 优先级:包装 fetchUser-AgentAuthorizationOpenai-Intent 位于 ...init.headers 展开之后,始终覆盖上游值;而 x-initiator 位于展开之前,故 chat.headers 钩子设置的 x-initiator 会生效。

说明:opencode 有意不发送 GitHub 官方 Copilot 客户端常见的 Editor-VersionEditor-Plugin-VersionCopilot-Integration-Id 等 Header(全仓库无命中),使用的是上面这套精简自定义 Header。

3.2 拉取模型列表请求(GET {base}/models

copilot.ts:62-92models.ts:213-226

Header 取值 说明
Authorization Bearer <GitHub OAuth token> copilot.ts:74
User-Agent opencode/<InstallationVersion> copilot.ts:75
X-GitHub-Api-Version 2026-06-01 copilot.ts:76
provider.options.headers.* 用户配置 copilot.ts:73

3.3 设备码流程请求(github.com)

copilot.ts:236-240:266-270Accept: application/jsonContent-Type: application/jsonUser-Agent: opencode/<InstallationVersion>


4. 客户端发送给 GitHub Copilot 的 Request Body

请求体由 ai-sdk 语言模型类构造,通用参数先经 chat.params 钩子(copilot.ts:340-354)与 request.ts:114-175 处理:

  • GPT 系列模型 省略 max_tokenschat.paramsmaxOutputTokens=undefinedcopilot.ts:343-346)。
  • @ai-sdk/anthropic 的模型关闭 eager_input_streamingtoolStreaming=falsecopilot.ts:351-353),以兼容 Copilot 的 /v1/messages shim。
  • 回放历史 tool 调用但当前无启用工具时,注入占位 _noop 工具以满足 Copilot 对 tools 字段的要求(request.ts:159-175)。
  • 通用参数 temperature / topP / topK / maxOutputTokens 来自 ProviderTransformrequest.ts:124-129)。

4.1 Chat Completions(POST /chat/completions

构造于 packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:139-212(流式 doStream:306-320,追加 stream:truestream_options):

主要字段:modelmessagesconvertToOpenAICompatibleChatMessages(prompt))、toolstool_choicemax_tokenstemperaturetop_pfrequency_penaltypresence_penaltyresponse_formatjson_schema/json_object)、stopseedreasoning_effortverbositythinking_budgetuser,以及 provider options 透传字段。

4.2 Responses API(POST /responses

构造于 packages/core/src/github-copilot/responses/openai-responses-language-model.ts:253-300(路径 :382-396、流式 :781-788 stream:true):

主要字段:modelinputtemperaturetop_pmax_output_tokenstext{format,verbosity}toolstool_choicereasoning{effort,summary}includestore(默认 true:206)、previous_response_idparallel_tool_callsmax_tool_callsmetadataprompt_cache_keysafety_identifierservice_tieruserinstructionstop_logprobs

4.3 Messages API(POST /v1/messages,Claude 系模型)

模型 api.npm = @ai-sdk/anthropic、base 为 {base}/v1models.ts:109-110)。请求体为 Anthropic Messages 格式,由 @ai-sdk/anthropic 生成;opencode 侧通过 chat.params 关闭 toolStreaming,并补 anthropic-beta: interleaved-thinking-2025-05-14 Header。


5. 关键代码位置索引

主题 文件:行
Copilot 插件(鉴权/fetch/hooks) packages/opencode/src/plugin/github-copilot/copilot.ts:9-28 常量/Base、:56-93 models、:96-181 loader+fetch、:182-337 OAuth 设备码、:340-354 chat.params、:360-411 chat.headers)
模型列表 + 端点映射 packages/opencode/src/plugin/github-copilot/models.ts:82-256
插件注册 packages/opencode/src/plugin/index.ts:15,72
OAuth 回调落库 packages/opencode/src/provider/auth.ts:188-221
Provider 装配 / OAuth fetch 选择 packages/opencode/src/provider/provider.ts:131-132,225,893-915,1531-1550,1720-1751
body/headers 组装管线 packages/opencode/src/session/llm/request.ts:114-175
OpenAI 兼容 Provider 工厂 packages/core/src/github-copilot/copilot-provider.ts
Chat 语言模型(/chat/completions) packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:139-212,306-320
Responses 语言模型(/responses) packages/core/src/github-copilot/responses/openai-responses-language-model.ts:206,253-300,382-396,781-788
v2 端点选择 packages/core/src/plugin/provider/github-copilot.ts:20-50 / packages/llm/src/providers/github-copilot.ts:19-64

结论摘要

  1. 鉴权:GitHub 设备码 OAuth → 拿到 GitHub OAuth 访问令牌并原样存储(expires:0,不刷新、无 Copilot token 兑换)。
  2. HeaderAuthorization: Bearer <GH token>User-Agent: opencode/<ver>Openai-Intentx-initiatorX-GitHub-Api-Version: 2026-06-01 为核心,另有 X-Interaction-Type / anthropic-beta / Copilot-Vision-Request 条件性追加。
  3. Body:按模型分别走 /chat/completions(OpenAI 兼容)、/responses(OpenAI Responses)、/v1/messages(Anthropic shim)三种形态,字段由 packages/core/src/github-copilot/* 构造,并经 chat.params 做 Copilot 适配。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions