本报告分析 opencode 向 GitHub Copilot 发起请求的完整流程,重点覆盖:GitHub 身份验证与 Token 交换、客户端构造的 Request Header、客户端构造的 Request Body。分析基于当前 dev(已与上游 anomalyco/opencode 完成同步)的实际代码,未做任何代码修改。
0. 本次同步与变更提示
本次运行检测到上游 anomalyco/opencode 有 6 个新提交 ,已快进/合并同步到本 fork 的 dev:
SHA
提交
6f8e1dda1
fix(provider): derive variants from reasoning options (anomalyco#36543 )
d7c0db8ce
fix(openai): use codex context limits for gpt-5.6 (anomalyco#36248 )
184da0e42
test(opencode): refresh stale model references (anomalyco#36546 )
a244d82ab
test(opencode): refresh models.dev fixture (anomalyco#36541 )
4dcfd9182 / cf7503687
chore: generate(生成物 / fixture)
对 Copilot 请求流程的影响 :这些提交主要涉及 models.dev 元数据的 reasoning_options → variants 派生、OpenAI codex 系列(gpt-5.6)的上下文上限、以及模型 fixture 刷新,均未改动 GitHub Copilot 的 Header / Body / 鉴权构造逻辑 。因此本报告结论对同步前后一致。
1. 请求流程(时序)
GitHub Copilot 在 opencode 中以 插件(plugin) 形式实现,核心分两块:
鉴权 + Header 注入 :packages/opencode/src/plugin/github-copilot/copilot.ts
协议 / Body 构造 :packages/core/src/github-copilot/(chat/ 与 responses/ 两套语言模型实现,外加 Anthropic Messages shim)
会话层在 packages/opencode/src/session/llm/request.ts 组织通用请求参数,并触发 chat.params、chat.headers 两个钩子;最终由鉴权 loader 返回的自定义 fetch 完成 Header 收尾与 Token 注入。
sequenceDiagram
participant S as Session / Prompt
participant R as session/llm/request.ts
participant P as copilot.ts (plugin hooks)
participant LM as core/github-copilot LanguageModel
participant F as Auth loader 自定义 fetch
participant GH as GitHub Copilot API
S->>R: 发起一次模型调用
R->>R: 组装基础 Header<br/>(X-Session-Id / User-Agent ...)
R->>P: trigger("chat.params")
P-->>R: 调整 body 参数<br/>(gpt→去 maxOutputTokens; anthropic→toolStreaming=false)
R->>P: trigger("chat.headers")
P-->>R: 追加 X-GitHub-Api-Version /<br/>X-Interaction-Type / anthropic-beta / x-initiator
R->>LM: 交给对应 endpoint 的 LanguageModel
LM->>LM: getArgs() 构造 JSON Body
LM->>F: postJsonToApi(url, headers, body)
F->>F: 解析 body 判定 isAgent / isVision<br/>注入 Authorization: Bearer <token><br/>Openai-Intent / Copilot-Vision-Request<br/>删除 x-api-key / authorization
F->>GH: POST /chat/completions | /responses | /v1/messages
GH-->>F: SSE / JSON 响应
F-->>S: 流式回传
Loading
关键环节与代码位置
Provider/Model 解析与 endpoint 选择
packages/opencode/src/plugin/github-copilot/models.ts:82-202(build):依据 supported_endpoints 判定 messages / responses / chat,并设置 api.npm(@ai-sdk/anthropic 或 @ai-sdk/github-copilot)、api.url。
packages/core/src/github-copilot/chat/openai-compatible-chat-options.ts:19-25(shouldUseResponsesApi):gpt-N(N≥5 且非 gpt-5-mini)走 Responses,否则 Chat Completions。
packages/core/src/plugin/provider/github-copilot.ts:27-50:按 endpoint/模型 id 绑定到 sdk.responses 或 sdk.chat。
鉴权凭据获取 :copilot.ts:96-181(auth.loader)。
请求参数 / Header 钩子 :request.ts:114-146(触发 chat.params / chat.headers)+ copilot.ts:340-412。
Body 构造 :.../chat/openai-compatible-chat-language-model.ts:87-190(getArgs)、.../responses/openai-responses-language-model.ts:253-309。
传输发送 + Token 注入 :copilot.ts:102-178(自定义 fetch)。
2. GitHub 身份验证与 Token 交换流程
实现位置:packages/opencode/src/plugin/github-copilot/copilot.ts。采用 GitHub OAuth 2.0 Device Authorization Grant(设备码流程,RFC 8628) 。
关键常量
CLIENT_ID = "Ov23li8tweQw6odWQebz"(copilot.ts:9)
API_VERSION = "2026-06-01"(copilot.ts:10)
轮询安全余量 OAUTH_POLLING_SAFETY_MARGIN_MS = 3000(copilot.ts:14)
API Base:copilot.ts:26-28 — 企业版 https://copilot-api.{domain},公有云 https://api.githubcopilot.com。
鉴权域名:公有云 github.com;企业版取用户输入的 enterpriseUrl(copilot.ts:222-232)。
流程步骤
请求设备码 (copilot.ts:234-256)
POST https://{domain}/login/device/code
Body:{ "client_id": "Ov23li8tweQw6odWQebz", "scope": "read:user" }
返回 verification_uri、user_code、device_code、interval。用户在浏览器输入 user_code 完成授权。
轮询交换 Access Token (copilot.ts:262-333)
POST https://{domain}/login/oauth/access_token
Body:{ "client_id": "...", "device_code": "...", "grant_type": "urn:ietf:params:oauth:grant-type:device_code" }
authorization_pending → 按 interval + 安全余量 等待后重试;
slow_down → 按 RFC 8628 将间隔 +5s(或采用服务端返回的新间隔)后重试;
拿到 access_token → 返回成功。
Token 存储 (copilot.ts:286-306)
{ type: "success", refresh: access_token, access: access_token, expires: 0 }
企业版额外记录 enterpriseUrl。
关键结论(Token 交换) :opencode 没有 把 GitHub OAuth Token 再交换成短时效的「Copilot Token」。它把 GitHub OAuth Access Token 直接 当作 Authorization: Bearer <token> 用于调用 api.githubcopilot.com;expires: 0 表示按永不过期处理(GitHub OAuth token 长期有效)。
印证:vendored provider packages/core/src/github-copilot/copilot-provider.ts:62 仅在 options.apiKey 存在时才写 Authorization;而 auth loader 返回 apiKey: ""(copilot.ts:101),所以真正注入 Bearer 的是 loader 的自定义 fetch(copilot.ts:164),Token 取自 getAuth() 的 access 字段。
凭据注入到每个请求 :auth loader(copilot.ts:96-181)返回一个自定义 fetch,在每次真正发出请求前用最新的 getAuth() 结果注入 Authorization,并做 Header 收尾(见第 3 节)。
3. 客户端发送给 GitHub Copilot 的 Request Header
Header 分 多层叠加 ,后写覆盖先写;最终收尾在 auth loader 的自定义 fetch (copilot.ts:160-174)。
3.1 基础层 —— session/llm/request.ts:187-204(非 opencode provider 分支)
Header
取值来源
作用 / 说明
x-session-affinity
input.sessionID
会话亲和(路由/粘性)
X-Session-Id
input.sessionID
会话标识
x-parent-session-id
input.parentSessionID(存在时)
子会话标识父会话
User-Agent
opencode/${InstallationVersion}(request.ts:18)
客户端标识
...input.model.headers
模型级 Header
每模型可附带
3.2 chat.headers 钩子层 —— copilot.ts:360-412
Header
取值来源
作用 / 说明
X-GitHub-Api-Version
API_VERSION(2026-06-01)
指定 Copilot API 版本
X-Interaction-Type
agent-session-name-generation(当 agent === "title")
标记标题生成类交互
anthropic-beta
interleaved-thinking-2025-05-14(当 api.npm === "@ai-sdk/anthropic")
Messages shim 的交错思考 beta
x-initiator
agent(压缩续写 compaction / 子会话时)
标记本次由 agent 发起
3.3 传输收尾层(最终值)—— auth loader 自定义 fetch copilot.ts:160-174
Header
取值来源
作用 / 说明
Authorization
Bearer ${info.access}(copilot.ts:164)
鉴权 :GitHub OAuth Token 直接作为 Bearer
User-Agent
opencode/${InstallationVersion}
覆盖为客户端标识
Openai-Intent
固定 conversation-edits(copilot.ts:165)
Copilot 侧意图标记
x-initiator
agent / user(copilot.ts:161)
依据 body 判定:末条非 user 消息或合成附件消息 → agent,否则 user(copilot.ts:107-158)
Copilot-Vision-Request
true(含图片时,copilot.ts:168-170)
视觉请求标记
(删除)x-api-key
copilot.ts:172
移除,避免与 Bearer 冲突
(删除)authorization(小写)
copilot.ts:173
移除小写重复项
3.4 /models 列表接口的 Header —— copilot.ts:70-79
Header
取值来源
作用
Authorization
Bearer ${auth.access}
拉取可用模型列表鉴权
User-Agent
opencode/${InstallationVersion}
客户端标识
X-GitHub-Api-Version
API_VERSION
API 版本
isAgent / isVision 的判定对三种 Body 格式分别处理(Completions / Responses / Messages),见 copilot.ts:107-158。
4. 客户端发送给 GitHub Copilot 的 Request Body
Copilot 模型按 endpoint 分三套 Body。chat.params 钩子(copilot.ts:340-353)会先做统一调整:gpt 系列 去掉 maxOutputTokens;Anthropic(Messages shim) 设 toolStreaming = false(规避 eager_input_streaming 被拒)。
4.1 Chat Completions —— POST {base}/chat/completions
构造于 .../chat/openai-compatible-chat-language-model.ts:139-189(getArgs):
字段
说明
model
模型 id(api.id)
messages
convertToOpenAICompatibleChatMessages(prompt) 产出:system / user(content 支持 text 与 image_url)/ assistant(含 tool_calls、Copilot 专有 reasoning_text、reasoning_opaque)/ tool(convert-to-openai-compatible-chat-messages.ts:15-164)
max_tokens
maxOutputTokens
temperature / top_p / frequency_penalty / presence_penalty / stop / seed
标准采样参数
response_format
json_schema / json_object(结构化输出时)
reasoning_effort
推理强度
verbosity
文本冗长度
tools / tool_choice
工具定义与选择
thinking_budget
Anthropic 思考预算(Copilot 侧)
user
端用户标识(可选)
stream / stream_options
流式时 stream: true,严格模式附 stream_options.include_usage(:308-314)
补充:当 Copilot 在回放历史工具调用但当前无启用工具时,request.ts:159-175 会注入一个 _noop 占位工具以满足 API 对 tools 字段的要求。
4.2 Responses API —— POST {base}/responses
构造于 .../responses/openai-responses-language-model.ts:253-309:
字段
说明
model / input
模型 id 与 Responses 输入项数组
temperature / top_p / max_output_tokens
采样与长度(推理模型会移除 temperature/top_p)
text
含 format(json_schema/json_object)与 verbosity
reasoning
{ effort, summary }(推理模型)
include
如 reasoning.encrypted_content 等
store
默认 true
instructions / user / metadata
指令 / 用户 / 元数据
parallel_tool_calls / max_tool_calls / previous_response_id
工具并行 / 上限 / 续接上一响应
truncation
需要时 auto
4.3 Anthropic Messages Shim —— POST {base}/v1/messages
当模型 supported_endpoints 含 /v1/messages 时(models.ts:92-110),api.npm 置为 @ai-sdk/anthropic、api.url 为 {base}/v1,走标准 Anthropic Messages Body;配合 anthropic-beta Header 与 toolStreaming=false。
5. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts
:9-14 常量(CLIENT_ID / API_VERSION / 安全余量)
:26-28 API Base
:96-181 auth loader(自定义 fetch + Token 注入 + Header 收尾)
:107-158 isAgent/isVision 判定
:160-174 最终 Header 构造
:222-336 OAuth 设备码授权与轮询交换
:340-353 chat.params;:360-412 chat.headers
packages/opencode/src/plugin/github-copilot/models.ts:82-256 模型/endpoint 派生与 /models 拉取
packages/core/src/github-copilot/copilot-provider.ts:62-82 vendored provider(仅在 apiKey 存在时写 Authorization)
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:87-190,192-329 Chat Body/请求
packages/core/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts:15-164 消息转换
packages/core/src/github-copilot/responses/openai-responses-language-model.ts:253-396 Responses Body/请求
packages/core/src/github-copilot/chat/openai-compatible-chat-options.ts:19-25 endpoint 选择
packages/core/src/plugin/provider/github-copilot.ts:20-50 SDK/语言模型绑定
packages/opencode/src/session/llm/request.ts:114-205 通用参数/Header 组装与钩子触发
只读分析,未修改任何代码。
0. 本次同步与变更提示
本次运行检测到上游
anomalyco/opencode有 6 个新提交,已快进/合并同步到本 fork 的dev:6f8e1dda1d7c0db8ce184da0e42a244d82ab4dcfd9182/cf7503687对 Copilot 请求流程的影响:这些提交主要涉及
models.dev元数据的reasoning_options → variants派生、OpenAI codex 系列(gpt-5.6)的上下文上限、以及模型 fixture 刷新,均未改动 GitHub Copilot 的 Header / Body / 鉴权构造逻辑。因此本报告结论对同步前后一致。1. 请求流程(时序)
GitHub Copilot 在 opencode 中以 插件(plugin) 形式实现,核心分两块:
packages/opencode/src/plugin/github-copilot/copilot.tspackages/core/src/github-copilot/(chat/与responses/两套语言模型实现,外加 Anthropic Messages shim)会话层在
packages/opencode/src/session/llm/request.ts组织通用请求参数,并触发chat.params、chat.headers两个钩子;最终由鉴权 loader 返回的自定义fetch完成 Header 收尾与 Token 注入。sequenceDiagram participant S as Session / Prompt participant R as session/llm/request.ts participant P as copilot.ts (plugin hooks) participant LM as core/github-copilot LanguageModel participant F as Auth loader 自定义 fetch participant GH as GitHub Copilot API S->>R: 发起一次模型调用 R->>R: 组装基础 Header<br/>(X-Session-Id / User-Agent ...) R->>P: trigger("chat.params") P-->>R: 调整 body 参数<br/>(gpt→去 maxOutputTokens; anthropic→toolStreaming=false) R->>P: trigger("chat.headers") P-->>R: 追加 X-GitHub-Api-Version /<br/>X-Interaction-Type / anthropic-beta / x-initiator R->>LM: 交给对应 endpoint 的 LanguageModel LM->>LM: getArgs() 构造 JSON Body LM->>F: postJsonToApi(url, headers, body) F->>F: 解析 body 判定 isAgent / isVision<br/>注入 Authorization: Bearer <token><br/>Openai-Intent / Copilot-Vision-Request<br/>删除 x-api-key / authorization F->>GH: POST /chat/completions | /responses | /v1/messages GH-->>F: SSE / JSON 响应 F-->>S: 流式回传关键环节与代码位置
packages/opencode/src/plugin/github-copilot/models.ts:82-202(build):依据supported_endpoints判定messages/responses/chat,并设置api.npm(@ai-sdk/anthropic或@ai-sdk/github-copilot)、api.url。packages/core/src/github-copilot/chat/openai-compatible-chat-options.ts:19-25(shouldUseResponsesApi):gpt-N(N≥5 且非gpt-5-mini)走 Responses,否则 Chat Completions。packages/core/src/plugin/provider/github-copilot.ts:27-50:按endpoint/模型 id 绑定到sdk.responses或sdk.chat。copilot.ts:96-181(auth.loader)。request.ts:114-146(触发chat.params/chat.headers)+copilot.ts:340-412。.../chat/openai-compatible-chat-language-model.ts:87-190(getArgs)、.../responses/openai-responses-language-model.ts:253-309。copilot.ts:102-178(自定义fetch)。2. GitHub 身份验证与 Token 交换流程
实现位置:
packages/opencode/src/plugin/github-copilot/copilot.ts。采用 GitHub OAuth 2.0 Device Authorization Grant(设备码流程,RFC 8628)。关键常量
CLIENT_ID = "Ov23li8tweQw6odWQebz"(copilot.ts:9)API_VERSION = "2026-06-01"(copilot.ts:10)OAUTH_POLLING_SAFETY_MARGIN_MS = 3000(copilot.ts:14)copilot.ts:26-28— 企业版https://copilot-api.{domain},公有云https://api.githubcopilot.com。github.com;企业版取用户输入的enterpriseUrl(copilot.ts:222-232)。流程步骤
请求设备码(
copilot.ts:234-256)POST https://{domain}/login/device/codeBody:
{ "client_id": "Ov23li8tweQw6odWQebz", "scope": "read:user" }返回
verification_uri、user_code、device_code、interval。用户在浏览器输入user_code完成授权。轮询交换 Access Token(
copilot.ts:262-333)POST https://{domain}/login/oauth/access_tokenBody:
{ "client_id": "...", "device_code": "...", "grant_type": "urn:ietf:params:oauth:grant-type:device_code" }authorization_pending→ 按interval + 安全余量等待后重试;slow_down→ 按 RFC 8628 将间隔 +5s(或采用服务端返回的新间隔)后重试;access_token→ 返回成功。Token 存储(
copilot.ts:286-306)企业版额外记录
enterpriseUrl。copilot.ts:96-181)返回一个自定义fetch,在每次真正发出请求前用最新的getAuth()结果注入Authorization,并做 Header 收尾(见第 3 节)。3. 客户端发送给 GitHub Copilot 的 Request Header
Header 分 多层叠加,后写覆盖先写;最终收尾在 auth loader 的自定义
fetch(copilot.ts:160-174)。3.1 基础层 ——
session/llm/request.ts:187-204(非 opencode provider 分支)x-session-affinityinput.sessionIDX-Session-Idinput.sessionIDx-parent-session-idinput.parentSessionID(存在时)User-Agentopencode/${InstallationVersion}(request.ts:18)...input.model.headers3.2
chat.headers钩子层 ——copilot.ts:360-412X-GitHub-Api-VersionAPI_VERSION(2026-06-01)X-Interaction-Typeagent-session-name-generation(当agent === "title")anthropic-betainterleaved-thinking-2025-05-14(当api.npm === "@ai-sdk/anthropic")x-initiatoragent(压缩续写 compaction / 子会话时)3.3 传输收尾层(最终值)—— auth loader 自定义
fetchcopilot.ts:160-174AuthorizationBearer ${info.access}(copilot.ts:164)User-Agentopencode/${InstallationVersion}Openai-Intentconversation-edits(copilot.ts:165)x-initiatoragent/user(copilot.ts:161)agent,否则user(copilot.ts:107-158)Copilot-Vision-Requesttrue(含图片时,copilot.ts:168-170)x-api-keycopilot.ts:172authorization(小写)copilot.ts:1733.4
/models列表接口的 Header ——copilot.ts:70-79AuthorizationBearer ${auth.access}User-Agentopencode/${InstallationVersion}X-GitHub-Api-VersionAPI_VERSION4. 客户端发送给 GitHub Copilot 的 Request Body
Copilot 模型按 endpoint 分三套 Body。
chat.params钩子(copilot.ts:340-353)会先做统一调整:gpt 系列 去掉maxOutputTokens;Anthropic(Messages shim) 设toolStreaming = false(规避eager_input_streaming被拒)。4.1 Chat Completions ——
POST {base}/chat/completions构造于
.../chat/openai-compatible-chat-language-model.ts:139-189(getArgs):modelapi.id)messagesconvertToOpenAICompatibleChatMessages(prompt)产出:system/user(content支持text与image_url)/assistant(含tool_calls、Copilot 专有reasoning_text、reasoning_opaque)/tool(convert-to-openai-compatible-chat-messages.ts:15-164)max_tokensmaxOutputTokenstemperature/top_p/frequency_penalty/presence_penalty/stop/seedresponse_formatjson_schema/json_object(结构化输出时)reasoning_effortverbositytools/tool_choicethinking_budgetuserstream/stream_optionsstream: true,严格模式附stream_options.include_usage(:308-314)4.2 Responses API ——
POST {base}/responses构造于
.../responses/openai-responses-language-model.ts:253-309:model/inputtemperature/top_p/max_output_tokenstextformat(json_schema/json_object)与verbosityreasoning{ effort, summary }(推理模型)includereasoning.encrypted_content等storetrueinstructions/user/metadataparallel_tool_calls/max_tool_calls/previous_response_idtruncationauto4.3 Anthropic Messages Shim ——
POST {base}/v1/messages当模型
supported_endpoints含/v1/messages时(models.ts:92-110),api.npm置为@ai-sdk/anthropic、api.url为{base}/v1,走标准 Anthropic Messages Body;配合anthropic-betaHeader 与toolStreaming=false。5. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts:9-14常量(CLIENT_ID / API_VERSION / 安全余量):26-28API Base:96-181auth loader(自定义 fetch + Token 注入 + Header 收尾):107-158isAgent/isVision 判定:160-174最终 Header 构造:222-336OAuth 设备码授权与轮询交换:340-353chat.params;:360-412chat.headerspackages/opencode/src/plugin/github-copilot/models.ts:82-256模型/endpoint 派生与/models拉取packages/core/src/github-copilot/copilot-provider.ts:62-82vendored provider(仅在 apiKey 存在时写 Authorization)packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:87-190,192-329Chat Body/请求packages/core/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts:15-164消息转换packages/core/src/github-copilot/responses/openai-responses-language-model.ts:253-396Responses Body/请求packages/core/src/github-copilot/chat/openai-compatible-chat-options.ts:19-25endpoint 选择packages/core/src/plugin/provider/github-copilot.ts:20-50SDK/语言模型绑定packages/opencode/src/session/llm/request.ts:114-205通用参数/Header 组装与钩子触发只读分析,未修改任何代码。