OpenCode → GitHub Copilot 请求分析:身份验证/Token 交换、Request Header 与 Request Body(2026-08-10)
分析对象:nickhou1983/opencode(fork,分支 dev),已与上游 anomalyco/opencode 同步。
本报告聚焦 GitHub Copilot provider 的请求处理链路,以仓库实际代码为准。
0. 上游同步状态与本次变更提示
同步检测(GitHub MCP) :上游 anomalyco/opencode:dev 最新提交为 0bff28de09(fix(stats): fall back after full sync failure,2026-08-09)。fork 的 dev HEAD 为 4e61168ed1(每日合并工作流产生的 Merge branch 'anomalyco:dev' into dev),其父提交即 0bff28de09。
结论:无需同步 。git log dev..upstream/dev 为空,upstream/dev 已是本地 dev 的祖先——上游更新已被每日合并工作流并入,fork 处于最新状态。
变更提示 :最近同步进来的提交(至 0bff28de09)集中在 app / tui / i18n / stats / desktop 与消息排序修复,未触及 GitHub Copilot 的鉴权 / 请求头 / 请求体代码路径。因此本次 Copilot 请求流程与上一轮分析一致,无行为变化。
1. 身份验证与 Token 交换流程
GitHub Copilot 的鉴权由插件 CopilotAuthPlugin 实现,采用 GitHub OAuth 设备码(Device Flow) 。
关键常量与端点 (packages/opencode/src/plugin/github-copilot/copilot.ts)
CLIENT_ID = "Ov23li8tweQw6odWQebz"(opencode 自有 GitHub OAuth App) — L9
API_VERSION = "2026-06-01" — L10
设备码端点 https://{domain}/login/device/code;换取 token 端点 https://{domain}/login/oauth/access_token — L19–24
API Base:公有云 https://api.githubcopilot.com;企业版 https://copilot-api.{enterpriseDomain} — base() L26–28
流程步骤
选择部署类型 :交互式提示 github.com 或 GitHub Enterprise(企业版需填 enterpriseUrl) — L186–221。
发起设备授权 :POST /login/device/code,body={ client_id, scope: "read:user" },得到 device_code / user_code / verification_uri / interval — L234–256。
展示 user_code ,提示用户在浏览器完成授权 — L258–261。
轮询换 token :循环 POST /login/oauth/access_token,body={ client_id, device_code, grant_type: "urn:ietf:params:oauth:grant-type:device_code" };处理 authorization_pending / slow_down(遵循 RFC 8628,退避 + 3s 安全余量 OAUTH_POLLING_SAFETY_MARGIN_MS) — L262–334。
持久化凭据 :成功后写入 auth.json,结构为 Oauth { type:"oauth", refresh, access, expires, enterpriseUrl? };此处 refresh 与 access 都被设为同一个 GitHub OAuth access_token,expires: 0 — copilot.ts L286–305;schema 见 packages/opencode/src/auth/index.ts L14–21。
关键结论:没有独立的 Copilot Token 交换
代码库中 不存在 api.github.com/copilot_internal/v2/token 这类“GitHub OAuth token → 短期 Copilot token”的二次交换(全库检索 copilot_internal / /v2/token 无命中)。
OpenCode 将设备码流程拿到的 GitHub OAuth token(gho_…)直接作为 Authorization: Bearer 打到 api.githubcopilot.com 。
expires: 0 且插件 未注册 OAuth refresh 处理器 (不同于 xai / codex / snowflake 等在 loader 里做刷新);因 GitHub OAuth token 默认长期有效,每次请求由 loader.fetch 直接读取 access 使用。
这与官方 Copilot 客户端(先换取 tid=… 短期 token)不同,是 OpenCode 的一个显著实现特征。
每次请求的凭据注入 (auth.loader → 返回 { apiKey: "", fetch },copilot.ts L96–181)
apiKey 返回空串(配合 OAUTH_DUMMY_KEY,auth/index.ts:8),真正的凭据由自定义 fetch 注入。
自定义 fetch 设置 Authorization: Bearer <access>,并 delete headers["authorization"] / delete headers["x-api-key"] 清掉 AI SDK 依据空 apiKey 写入的小写重复头 — L164、L172–173。
sequenceDiagram
participant U as 用户
participant OC as OpenCode(CopilotAuthPlugin)
participant GH as github.com(OAuth)
participant API as api.githubcopilot.com
U->>OC: opencode auth login (GitHub Copilot)
OC->>GH: POST /login/device/code {client_id, scope:read:user}
GH-->>OC: device_code, user_code, verification_uri, interval
OC-->>U: 打开 verification_uri,输入 user_code
loop 轮询(interval + 3s;处理 pending/slow_down)
OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type:device_code}
GH-->>OC: access_token(或 authorization_pending)
end
OC->>OC: 写 auth.json {refresh=access=access_token, expires:0}
Note over OC,API: 无 copilot_internal/v2/token 二次交换
OC->>API: 请求携带 Authorization: Bearer <access_token>
API-->>OC: 响应(SSE 流)
Loading
2. 客户端发送给 GitHub Copilot 的 Request Header
请求头由两处叠加构成:(a) chat.headers 钩子(copilot.ts L360–412),(b) auth.loader 的自定义 fetch(copilot.ts L160–170)。fetch 中的 User-Agent / Authorization / Openai-Intent 会覆盖前面 init.headers 的同名项。
2.1 聊天/推理请求头
Header
取值
来源(代码位置)
作用 / 说明
Authorization
Bearer <GitHub OAuth access_token>
copilot.ts L164
主鉴权头;直接用 OAuth token(无二次交换)
User-Agent
opencode/<InstallationVersion>
copilot.ts L163
客户端标识
Openai-Intent
conversation-edits
copilot.ts L165
Copilot 侧意图标记
x-initiator
agent 或 user
copilot.ts L161(基于 body)、L394/L411(compaction/子代理强制 agent)
区分“用户发起”/“代理发起”,用于遥测与计费口径
X-GitHub-Api-Version
2026-06-01
copilot.ts L363(API_VERSION L10)
Copilot API 版本
Copilot-Vision-Request
true(仅含图片时)
copilot.ts L168–170
视觉/多模态请求标记
X-Interaction-Type
agent-session-name-generation(仅 title 代理)
copilot.ts L364–366
标题生成这类工具型交互标记
anthropic-beta
interleaved-thinking-2025-05-14(仅 @ai-sdk/anthropic,即 Claude 走 /v1/messages)
copilot.ts L368–370
开启 Claude 交错思考 beta
Content-Type
application/json
AI SDK(postJsonToApi)
JSON 请求体
x-initiator 的取值:loader.fetch 解析 body,若最后一条消息不是“纯 user 文本”(如 assistant / tool_result / 合成图片消息)则判为 isAgent=true → agent,否则 user(copilot.ts L107–158)。chat.headers 钩子对含 compaction 部件的消息或存在 parentID 的子代理会话强制置为 agent(L385–411)。
2.2 模型发现(GET {base}/models)请求头
Header
取值
来源
说明
Authorization
Bearer <access>
copilot.ts L74
同一 OAuth token
User-Agent
opencode/<InstallationVersion>
copilot.ts L75
—
X-GitHub-Api-Version
2026-06-01
copilot.ts L76
—
拉取端点 GET {base}/models,5s 超时,解析出可选模型与 model_picker_enabled 集合(packages/opencode/src/plugin/github-copilot/models.ts L213–255)。
2.3 注意:未发送的官方头
OpenCode 未发送 官方 Copilot 客户端常见的 Copilot-Integration-Id、Editor-Version、Editor-Plugin-Version、Copilot-Language-Server-Version 等头(全库检索无命中);它以 User-Agent: opencode/<version> + Openai-Intent + X-GitHub-Api-Version + x-initiator 作为标识。
3. 客户端发送给 GitHub Copilot 的 Request Body
Copilot 模型按 /models 返回的 supported_endpoints 被路由到 三种端点/协议 ,请求体格式各异(models.ts L92–110):
messages → POST {base}/v1/messages,npm=@ai-sdk/anthropic(Claude 系,Anthropic Messages 格式)
responses → POST {base}/responses,npm=@ai-sdk/github-copilot(OpenAI Responses 格式)
chat → POST {base}/chat/completions,npm=@ai-sdk/github-copilot(OpenAI Chat Completions 格式)
v2/core 侧路由:GPT‑5+(非 gpt-5-mini)默认走 Responses,其余走 Chat(packages/core/src/plugin/provider/github-copilot.ts L27–50)。
3.1 Chat Completions 请求体(/chat/completions)
字段构造见 packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts getArgs L139–189:
model:模型 api.id
messages:convertToOpenAICompatibleChatMessages(prompt)(L179)
max_tokens:maxOutputTokens(L148)——注意 :GPT 系被钩子置空(见 3.4)
temperature / top_p / frequency_penalty / presence_penalty / stop / seed(L149–168)
reasoning_effort(L175)、verbosity(L176)、thinking_budget(L186)
response_format:JSON/结构化输出时(L153–165)
tools / tool_choice:由 prepareTools 生成(L182–183)
user(L145)
流式(doStream)追加 stream: true,可选 stream_options: { include_usage: true }(L306–313)
3.2 Responses 请求体(/responses)
见 packages/core/src/github-copilot/responses/openai-responses-language-model.ts getArgs L152–390:
model、input(由 convert-to-openai-responses-input 生成)
max_output_tokens(L258)
store:默认 true(L206;顶层组装 L285)
instructions(L287)、include(L289,常含 reasoning.encrypted_content)
reasoning: { effort, summary }:含 reasoningEffort / reasoningSummary 时注入(L296–303)
推理模型会移除不支持的 temperature / top_p(L312–344)
tools / tool_choice(L373–386)
流式追加 stream: true(L786–788)
3.3 Messages 请求体(/v1/messages,Claude)
Claude 系模型走 @ai-sdk/anthropic,请求体为 Anthropic Messages 格式(model / messages / system / max_tokens / tools / thinking 等),并叠加 anthropic-beta: interleaved-thinking-2025-05-14 头。
3.4 opencode 对请求体的定制(钩子 + transform)
chat.params 钩子 (copilot.ts L340–354):
GPT 系模型 maxOutputTokens = undefined(对齐 GitHub Copilot CLI,省略 max_tokens)— L344–346
@ai-sdk/anthropic(Claude via messages)options.toolStreaming = false(规避 Copilot /v1/messages shim 拒绝 eager_input_streaming)— L351–353
推理变体注入 providerOptions (packages/opencode/src/provider/transform.ts):
sdkKey("@ai-sdk/github-copilot") = "copilot"(providerOptions 命名空间键)— L42–45
GPT 系:注入 { reasoningEffort, reasoningSummary:"auto", include: <encrypted reasoning> };gpt-5.1-codex-max/5.2/5.3 追加 xhigh — L889–913;单值映射 L1746–1750
Claude via Copilot:仅 { reasoningEffort },且限制为 low/medium/high,opus-4.7 固定 medium — L976–1000、L1746–1749
Gemini via Copilot:不注入 reasoning 变体 — L890–893、L1748
4. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts
常量/端点/Base:L9–28;设备码鉴权:L182–338;凭据持久化:L286–305
auth.loader + 自定义 fetch(注入 Header/Bearer、isAgent/isVision 判定):L96–181
chat.params(请求体定制):L340–354;chat.headers(请求头):L360–412
provider.models(模型发现头):L60–92
packages/opencode/src/auth/index.ts:OAUTH_DUMMY_KEY L8;Oauth schema L14–21
packages/opencode/src/plugin/github-copilot/models.ts:端点路由 L92–110;GET /models L213–255
packages/core/src/plugin/provider/github-copilot.ts:v2 端点(chat/responses)选择 L27–50
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:Chat 请求体 L139–189、流式 L306–313
packages/core/src/github-copilot/responses/openai-responses-language-model.ts:Responses 请求体 L152–390、流式 L786–788
packages/opencode/src/provider/transform.ts:sdkKey L42–60;Copilot 推理变体 L889–913、L976–1000、L1746–1750
OpenCode → GitHub Copilot 请求分析:身份验证/Token 交换、Request Header 与 Request Body(2026-08-10)
0. 上游同步状态与本次变更提示
anomalyco/opencode:dev最新提交为0bff28de09(fix(stats): fall back after full sync failure,2026-08-09)。fork 的devHEAD 为4e61168ed1(每日合并工作流产生的Merge branch 'anomalyco:dev' into dev),其父提交即0bff28de09。git log dev..upstream/dev为空,upstream/dev已是本地dev的祖先——上游更新已被每日合并工作流并入,fork 处于最新状态。0bff28de09)集中在app/tui/i18n/stats/desktop与消息排序修复,未触及 GitHub Copilot 的鉴权 / 请求头 / 请求体代码路径。因此本次 Copilot 请求流程与上一轮分析一致,无行为变化。1. 身份验证与 Token 交换流程
GitHub Copilot 的鉴权由插件
CopilotAuthPlugin实现,采用 GitHub OAuth 设备码(Device Flow)。关键常量与端点(
packages/opencode/src/plugin/github-copilot/copilot.ts)CLIENT_ID = "Ov23li8tweQw6odWQebz"(opencode 自有 GitHub OAuth App) — L9API_VERSION = "2026-06-01"— L10https://{domain}/login/device/code;换取 token 端点https://{domain}/login/oauth/access_token— L19–24https://api.githubcopilot.com;企业版https://copilot-api.{enterpriseDomain}—base()L26–28流程步骤
github.com或GitHub Enterprise(企业版需填enterpriseUrl) — L186–221。POST /login/device/code,body={ client_id, scope: "read:user" },得到device_code / user_code / verification_uri / interval— L234–256。POST /login/oauth/access_token,body={ client_id, device_code, grant_type: "urn:ietf:params:oauth:grant-type:device_code" };处理authorization_pending/slow_down(遵循 RFC 8628,退避 + 3s 安全余量OAUTH_POLLING_SAFETY_MARGIN_MS) — L262–334。auth.json,结构为Oauth { type:"oauth", refresh, access, expires, enterpriseUrl? };此处refresh与access都被设为同一个 GitHub OAuthaccess_token,expires: 0— copilot.ts L286–305;schema 见packages/opencode/src/auth/index.tsL14–21。关键结论:没有独立的 Copilot Token 交换
api.github.com/copilot_internal/v2/token这类“GitHub OAuth token → 短期 Copilot token”的二次交换(全库检索copilot_internal//v2/token无命中)。gho_…)直接作为Authorization: Bearer打到api.githubcopilot.com。expires: 0且插件 未注册 OAuth refresh 处理器(不同于xai/codex/snowflake等在 loader 里做刷新);因 GitHub OAuth token 默认长期有效,每次请求由loader.fetch直接读取access使用。tid=…短期 token)不同,是 OpenCode 的一个显著实现特征。每次请求的凭据注入(
auth.loader→ 返回{ apiKey: "", fetch },copilot.ts L96–181)apiKey返回空串(配合OAUTH_DUMMY_KEY,auth/index.ts:8),真正的凭据由自定义fetch注入。fetch设置Authorization: Bearer <access>,并delete headers["authorization"]/delete headers["x-api-key"]清掉 AI SDK 依据空 apiKey 写入的小写重复头 — L164、L172–173。sequenceDiagram participant U as 用户 participant OC as OpenCode(CopilotAuthPlugin) participant GH as github.com(OAuth) participant API as api.githubcopilot.com U->>OC: opencode auth login (GitHub Copilot) OC->>GH: POST /login/device/code {client_id, scope:read:user} GH-->>OC: device_code, user_code, verification_uri, interval OC-->>U: 打开 verification_uri,输入 user_code loop 轮询(interval + 3s;处理 pending/slow_down) OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type:device_code} GH-->>OC: access_token(或 authorization_pending) end OC->>OC: 写 auth.json {refresh=access=access_token, expires:0} Note over OC,API: 无 copilot_internal/v2/token 二次交换 OC->>API: 请求携带 Authorization: Bearer <access_token> API-->>OC: 响应(SSE 流)2. 客户端发送给 GitHub Copilot 的 Request Header
请求头由两处叠加构成:(a)
chat.headers钩子(copilot.ts L360–412),(b)auth.loader的自定义fetch(copilot.ts L160–170)。fetch中的User-Agent/Authorization/Openai-Intent会覆盖前面init.headers的同名项。2.1 聊天/推理请求头
AuthorizationBearer <GitHub OAuth access_token>User-Agentopencode/<InstallationVersion>Openai-Intentconversation-editsx-initiatoragent或useragent)X-GitHub-Api-Version2026-06-01API_VERSIONL10)Copilot-Vision-Requesttrue(仅含图片时)X-Interaction-Typeagent-session-name-generation(仅title代理)anthropic-betainterleaved-thinking-2025-05-14(仅@ai-sdk/anthropic,即 Claude 走/v1/messages)Content-Typeapplication/jsonpostJsonToApi)2.2 模型发现(
GET {base}/models)请求头AuthorizationBearer <access>User-Agentopencode/<InstallationVersion>X-GitHub-Api-Version2026-06-01拉取端点
GET {base}/models,5s 超时,解析出可选模型与model_picker_enabled集合(packages/opencode/src/plugin/github-copilot/models.tsL213–255)。2.3 注意:未发送的官方头
OpenCode 未发送 官方 Copilot 客户端常见的
Copilot-Integration-Id、Editor-Version、Editor-Plugin-Version、Copilot-Language-Server-Version等头(全库检索无命中);它以User-Agent: opencode/<version>+Openai-Intent+X-GitHub-Api-Version+x-initiator作为标识。3. 客户端发送给 GitHub Copilot 的 Request Body
Copilot 模型按
/models返回的supported_endpoints被路由到 三种端点/协议,请求体格式各异(models.tsL92–110):messages→POST {base}/v1/messages,npm=@ai-sdk/anthropic(Claude 系,Anthropic Messages 格式)responses→POST {base}/responses,npm=@ai-sdk/github-copilot(OpenAI Responses 格式)chat→POST {base}/chat/completions,npm=@ai-sdk/github-copilot(OpenAI Chat Completions 格式)v2/core 侧路由:GPT‑5+(非
gpt-5-mini)默认走 Responses,其余走 Chat(packages/core/src/plugin/provider/github-copilot.tsL27–50)。3.1 Chat Completions 请求体(
/chat/completions)字段构造见
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.tsgetArgsL139–189:model:模型api.idmessages:convertToOpenAICompatibleChatMessages(prompt)(L179)max_tokens:maxOutputTokens(L148)——注意:GPT 系被钩子置空(见 3.4)temperature/top_p/frequency_penalty/presence_penalty/stop/seed(L149–168)reasoning_effort(L175)、verbosity(L176)、thinking_budget(L186)response_format:JSON/结构化输出时(L153–165)tools/tool_choice:由prepareTools生成(L182–183)user(L145)doStream)追加stream: true,可选stream_options: { include_usage: true }(L306–313)3.2 Responses 请求体(
/responses)见
packages/core/src/github-copilot/responses/openai-responses-language-model.tsgetArgsL152–390:model、input(由convert-to-openai-responses-input生成)max_output_tokens(L258)store:默认true(L206;顶层组装 L285)instructions(L287)、include(L289,常含reasoning.encrypted_content)reasoning: { effort, summary }:含reasoningEffort/reasoningSummary时注入(L296–303)temperature/top_p(L312–344)tools/tool_choice(L373–386)stream: true(L786–788)3.3 Messages 请求体(
/v1/messages,Claude)Claude 系模型走
@ai-sdk/anthropic,请求体为 Anthropic Messages 格式(model/messages/system/max_tokens/tools/thinking等),并叠加anthropic-beta: interleaved-thinking-2025-05-14头。3.4 opencode 对请求体的定制(钩子 + transform)
chat.params钩子(copilot.ts L340–354):maxOutputTokens = undefined(对齐 GitHub Copilot CLI,省略max_tokens)— L344–346@ai-sdk/anthropic(Claude via messages)options.toolStreaming = false(规避 Copilot/v1/messagesshim 拒绝eager_input_streaming)— L351–353packages/opencode/src/provider/transform.ts):sdkKey("@ai-sdk/github-copilot") = "copilot"(providerOptions 命名空间键)— L42–45{ reasoningEffort, reasoningSummary:"auto", include: <encrypted reasoning> };gpt-5.1-codex-max/5.2/5.3追加xhigh— L889–913;单值映射 L1746–1750{ reasoningEffort },且限制为low/medium/high,opus-4.7固定medium— L976–1000、L1746–17494. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.tsauth.loader+ 自定义 fetch(注入 Header/Bearer、isAgent/isVision 判定):L96–181chat.params(请求体定制):L340–354;chat.headers(请求头):L360–412provider.models(模型发现头):L60–92packages/opencode/src/auth/index.ts:OAUTH_DUMMY_KEYL8;Oauthschema L14–21packages/opencode/src/plugin/github-copilot/models.ts:端点路由 L92–110;GET /modelsL213–255packages/core/src/plugin/provider/github-copilot.ts:v2 端点(chat/responses)选择 L27–50packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:Chat 请求体 L139–189、流式 L306–313packages/core/src/github-copilot/responses/openai-responses-language-model.ts:Responses 请求体 L152–390、流式 L786–788packages/opencode/src/provider/transform.ts:sdkKeyL42–60;Copilot 推理变体 L889–913、L976–1000、L1746–1750