OpenCode 请求流程与请求 Header/Body 分析:GitHub Copilot(2026-07-17)
本报告基于 fork nickhou1983/opencode 的 dev 分支代码,只读分析,未修改任何代码。
0. 本次上游同步与变更提示
上游 anomalyco/opencode@dev 检测到 1 个新提交 :3a1c6df9 fix(app): deduplicate diff summaries linearly (#37414)。
已通过 GitHub merge-upstream API 将本 fork 的 dev 快进同步(新的合并提交 2c7507f6)。
该提交属于 packages/app 的 diff 摘要去重修复,与 GitHub Copilot 的请求流程 / Header / Body 无关 ,本次分析结论不受影响。
1. GitHub 身份验证与 Token 交换流程
OpenCode 对 GitHub Copilot 采用 OAuth 2.0 设备授权码流程(Device Authorization Grant,RFC 8628) ,核心实现全部位于
packages/opencode/src/plugin/github-copilot/copilot.ts 的 CopilotAuthPlugin。
关键常量与端点
项
取值
代码位置
OAuth 客户端 ID
Ov23li8tweQw6odWQebz
copilot.ts:9
API 版本
2026-06-01
copilot.ts:10
设备码端点
https://{domain}/login/device/code
copilot.ts:19-24 (getUrls)
Token 端点
https://{domain}/login/oauth/access_token
copilot.ts:19-24
请求作用域
read:user
copilot.ts:244
轮询安全余量
3000ms
copilot.ts:14
Copilot API Base
https://api.githubcopilot.com(公有)/ https://copilot-api.{企业域名}(企业版)
copilot.ts:26-28 (base)
domain 默认为 github.com;选择 “GitHub Enterprise” 时由用户输入的企业 URL 经 normalizeDomain 归一化得到(copilot.ts:222-230)。
交换时序
sequenceDiagram
participant U as 用户
participant OC as OpenCode (CopilotAuthPlugin)
participant GH as GitHub OAuth (github.com / GHE)
participant CAPI as Copilot API (api.githubcopilot.com)
U->>OC: 选择 "Login with GitHub Copilot" (device flow)
OC->>GH: POST /login/device/code {client_id, scope:"read:user"}
GH-->>OC: {verification_uri, user_code, device_code, interval}
OC-->>U: 显示 verification_uri + user_code
U->>GH: 浏览器打开 verification_uri,输入 user_code 授权
loop 轮询直至授权完成 (interval + 3s)
OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type=device_code}
GH-->>OC: authorization_pending / slow_down / {access_token}
end
OC->>OC: 持久化 {access=refresh=access_token, expires:0, enterpriseUrl?}
Note over OC,CAPI: 之后每次请求由 auth loader 的 fetch 包装器注入凭据
OC->>CAPI: GET /models (Authorization: Bearer <token>) 拉取模型清单
OC->>CAPI: POST /chat/completions | /responses | /v1/messages (推理请求)
Loading
流程要点
发起设备码请求 (copilot.ts:234-256):POST /login/device/code,请求体 {client_id, scope:"read:user"},Header 含 Accept: application/json、Content-Type: application/json、User-Agent: opencode/{版本}。
展示待授权信息 :返回 verification_uri 与 user_code,method: "auto" 自动打开浏览器(copilot.ts:258-261)。
轮询换取 Token (copilot.ts:262-334):POST /login/oauth/access_token,grant_type=urn:ietf:params:oauth:grant-type:device_code。
authorization_pending:按 interval + 3s 休眠后继续(:308-311)。
slow_down:按 RFC 8628 §3.5 将间隔 +5s(若服务端返回新 interval 则采用),再加安全余量(:313-327)。
持久化凭据 (copilot.ts:286-306):{ type:"success", refresh: access_token, access: access_token, expires: 0, enterpriseUrl? }。
⚠️ 关键结论:无独立的 Copilot Token 交换
与部分 Copilot 集成不同,本仓库不调用 api.github.com/copilot_internal/v2/token 交换短期 Copilot Token (全仓库无 copilot_internal 匹配)。OpenCode 直接把设备流拿到的 GitHub OAuth access token 当作 Copilot API 的 Bearer 使用:
expires: 0 且 auth loader 未定义 refresh 回调 (对比 xai.ts/codex.ts 有刷新逻辑);
loader 内的 fetch 包装器每次请求都重新 getAuth() 读取当前存储的 token 后直接注入(copilot.ts:102-104,160-166)。
凭据注入位置
模型发现 :provider.models() 携带 Authorization: Bearer {auth.access} + User-Agent + X-GitHub-Api-Version 调 CopilotModels.get()(copilot.ts:62-92),后者 GET {base}/models(models.ts:213-256)。
推理请求 :由 auth.loader 返回的自定义 fetch 包装器统一注入(见第 2 节)。
2. 客户端发送给 GitHub Copilot 的 Request Header
请求 Header 分两个阶段组装,最终由 auth loader 的 fetch 包装器覆盖写入并发送 。
阶段 A — LLMRequestPrep.prepare(packages/opencode/src/session/llm/request.ts)
对非 opencode 自有 provider(含 Copilot)注入基础 Header,并触发 chat.headers 插件钩子合并:
Header
取值来源(代码位置)
作用 / 说明
x-session-affinity
input.sessionID(request.ts:197)
会话粘性路由
X-Session-Id
input.sessionID(request.ts:198)
会话标识
x-parent-session-id
input.parentSessionID(子代理时,request.ts:199)
父会话标识
User-Agent
opencode/{InstallationVersion}(request.ts:194/200)
客户端标识
X-GitHub-Api-Version
常量 2026-06-01(copilot.ts:363,chat.headers 钩子)
指定 Copilot API 版本
X-Interaction-Type
agent-session-name-generation(仅 title 代理,copilot.ts:364-366)
会话标题生成场景标记
anthropic-beta
interleaved-thinking-2025-05-14(模型 npm 为 @ai-sdk/anthropic 时,copilot.ts:368-370)
开启交错思考 beta
x-initiator
agent(压缩续写 / 子代理会话,copilot.ts:385-411)
标记请求由 agent 发起
随后 model.headers 与钩子 Header 合并进最终 headers(request.ts:187-204)。
阶段 B — auth loader 的 fetch 包装器(copilot.ts:102-179,最终生效层)
包装器在真正发起 fetch 前,检查请求体判断 isVision/isAgent,并组装最终 Header:
Header
取值来源(代码位置)
作用 / 说明
Authorization
Bearer {info.access}(copilot.ts:164)
鉴权 :直接使用 GitHub OAuth access token
User-Agent
opencode/{InstallationVersion}(copilot.ts:163)
客户端标识(覆盖上游)
Openai-Intent
常量 conversation-edits(copilot.ts:165)
Copilot 侧意图标记
x-initiator
agent / user(由请求体推断,copilot.ts:160-161)
计费 / 配额区分;若上游钩子已设 agent 则以其为准(init.headers 在其后展开覆盖)
Copilot-Vision-Request
true(请求含图片时,copilot.ts:168-170)
声明多模态视觉请求
x-api-key
delete(copilot.ts:172)
移除,避免与 Bearer 冲突
authorization(小写)
delete(copilot.ts:173)
移除小写重复项
isAgent 判定 (copilot.ts:107-158):解析请求体,兼容三种 API 形态——
Completions(body.messages 且 URL 含 completions)、Responses(body.input)、Messages(body.messages);
末条消息非 user、或为附带图片的合成用户消息(imgMsg)时判为 agent。
isVision :任一消息含 image_url / input_image / image(含嵌套于 tool_result 内的图片)。
Header 组装与覆盖顺序
flowchart LR
A[request.ts 基础 Header<br/>x-session-affinity / X-Session-Id / User-Agent] --> M[合并]
B[chat.headers 钩子<br/>X-GitHub-Api-Version / X-Interaction-Type<br/>anthropic-beta / x-initiator] --> M
C[model.headers] --> M
M --> D[AI SDK doStream:<br/>combineHeaders provider.headers + 请求 Header]
D --> E[auth loader fetch 包装器<br/>覆盖 Authorization / User-Agent / Openai-Intent<br/>x-initiator / Copilot-Vision-Request<br/>删除 x-api-key、authorization]
E --> F[(发往 api.githubcopilot.com)]
Loading
3. 客户端发送给 GitHub Copilot 的 Request Body
请求体由 AI SDK 语言模型构造。Copilot 依据 /models 返回的 supported_endpoints 为每个模型选择端点
(models.ts:92-99):/v1/messages → Anthropic 协议、/responses → Responses、/chat/completions → Chat。
请求参数源头统一在 request.ts 的 params(temperature/topP/topK/maxOutputTokens/options,来自 ProviderTransform),
系统提示注入为 system 消息(Responses 端点则注入 instructions)。
3.1 Chat Completions(POST {base}/chat/completions,npm @ai-sdk/github-copilot)
Body 由 getArgs 构造(packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:139-189):
字段
说明 / 来源
model
模型 API id(:142)
messages
convertToOpenAICompatibleChatMessages(prompt)(:179)
max_tokens
maxOutputTokens(:148;gpt 模型经 chat.params 置空以省略)
temperature / top_p
:149-150
frequency_penalty / presence_penalty
:151-152
response_format
JSON / json_schema(结构化输出时,:153-165)
stop / seed
:167-168
reasoning_effort / verbosity
推理模型(:175-176)
thinking_budget
:186
tools / tool_choice
prepareTools 结果(:182-183)
stream
流式时 true(doStream,:310)
3.2 Responses(POST {base}/responses,GPT‑5 类模型,非 gpt-5-mini)
Body 由 packages/core/src/github-copilot/responses/openai-responses-language-model.ts:253-300 构造:
model、input、instructions(= 系统提示)、max_output_tokens、temperature、top_p、
store(默认 true)、tools、include、reasoning(effort/summary)、text.verbosity、
parallel_tool_calls、previous_response_id、stream: true(:786-788)等。
3.3 Messages / Anthropic(POST {base}/v1/messages,npm @ai-sdk/anthropic)
对 supported_endpoints 含 /v1/messages 的模型,api.url 置为 {base}/v1、npm 置为 @ai-sdk/anthropic
(models.ts:92,109-110)。Body 为标准 Anthropic Messages 格式(model、system、messages、max_tokens、tools 等)。
3.4 Copilot 专属 Body 调整(chat.params 钩子,copilot.ts:340-354)
GPT 模型 :maxOutputTokens = undefined → 省略 max_tokens,对齐官方 Copilot CLI(:344-346)。
Anthropic 模型 :toolStreaming = false,规避 Copilot /v1/messages shim 拒绝 eager_input_streaming 字段(:348-353)。
工具占位 :回放历史工具调用但当前无启用工具时,注入 _noop 工具以满足 Copilot API 对 tools 字段的要求(request.ts:159-175)。
4. 关键代码位置索引
关注点
文件 : 行
设备流 / 凭据 / fetch 包装器 / 钩子
packages/opencode/src/plugin/github-copilot/copilot.ts:9-14, 26-28, 56-92, 102-179, 222-337, 340-354, 360-412
模型发现与端点映射
packages/opencode/src/plugin/github-copilot/models.ts:82-159, 213-256
请求 Header / Body 参数组装
packages/opencode/src/session/llm/request.ts:56-206
Chat Completions Body / 发送
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:87-210, 306-323
Responses Body / 发送
packages/core/src/github-copilot/responses/openai-responses-language-model.ts:253-300, 782-788
核心插件端点选择
packages/core/src/plugin/provider/github-copilot.ts:20-50
新 llm 栈 provider 定义
packages/llm/src/providers/github-copilot.ts:19-69
AI SDK 事件桥接 / Copilot 计费
packages/opencode/src/session/llm/ai-sdk.ts:30-42
5. 结论摘要
鉴权 :OAuth 设备授权码流程(client_id Ov23li8tweQw6odWQebz,scope read:user),直接使用 GitHub OAuth token 作为 Bearer,无 copilot_internal 二次交换 。
Header :最终由 auth loader 的 fetch 包装器统一注入 Authorization: Bearer <token>、User-Agent: opencode/{ver}、Openai-Intent: conversation-edits、x-initiator、按需 Copilot-Vision-Request;上游另加 X-GitHub-Api-Version: 2026-06-01、会话相关及场景性 Header。
Body :按模型 supported_endpoints 分流到 /chat/completions、/responses、/v1/messages 三种协议,并针对 Copilot 做 max_tokens 省略、toolStreaming 关闭、_noop 工具占位等适配。
OpenCode 请求流程与请求 Header/Body 分析:GitHub Copilot(2026-07-17)
0. 本次上游同步与变更提示
anomalyco/opencode@dev检测到 1 个新提交:3a1c6df9 fix(app): deduplicate diff summaries linearly (#37414)。merge-upstreamAPI 将本 fork 的dev快进同步(新的合并提交2c7507f6)。packages/app的 diff 摘要去重修复,与 GitHub Copilot 的请求流程 / Header / Body 无关,本次分析结论不受影响。1. GitHub 身份验证与 Token 交换流程
OpenCode 对 GitHub Copilot 采用 OAuth 2.0 设备授权码流程(Device Authorization Grant,RFC 8628),核心实现全部位于
packages/opencode/src/plugin/github-copilot/copilot.ts的CopilotAuthPlugin。关键常量与端点
Ov23li8tweQw6odWQebzcopilot.ts:92026-06-01copilot.ts:10https://{domain}/login/device/codecopilot.ts:19-24(getUrls)https://{domain}/login/oauth/access_tokencopilot.ts:19-24read:usercopilot.ts:244copilot.ts:14https://api.githubcopilot.com(公有)/https://copilot-api.{企业域名}(企业版)copilot.ts:26-28(base)domain默认为github.com;选择 “GitHub Enterprise” 时由用户输入的企业 URL 经normalizeDomain归一化得到(copilot.ts:222-230)。交换时序
sequenceDiagram participant U as 用户 participant OC as OpenCode (CopilotAuthPlugin) participant GH as GitHub OAuth (github.com / GHE) participant CAPI as Copilot API (api.githubcopilot.com) U->>OC: 选择 "Login with GitHub Copilot" (device flow) OC->>GH: POST /login/device/code {client_id, scope:"read:user"} GH-->>OC: {verification_uri, user_code, device_code, interval} OC-->>U: 显示 verification_uri + user_code U->>GH: 浏览器打开 verification_uri,输入 user_code 授权 loop 轮询直至授权完成 (interval + 3s) OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type=device_code} GH-->>OC: authorization_pending / slow_down / {access_token} end OC->>OC: 持久化 {access=refresh=access_token, expires:0, enterpriseUrl?} Note over OC,CAPI: 之后每次请求由 auth loader 的 fetch 包装器注入凭据 OC->>CAPI: GET /models (Authorization: Bearer <token>) 拉取模型清单 OC->>CAPI: POST /chat/completions | /responses | /v1/messages (推理请求)流程要点
copilot.ts:234-256):POST /login/device/code,请求体{client_id, scope:"read:user"},Header 含Accept: application/json、Content-Type: application/json、User-Agent: opencode/{版本}。verification_uri与user_code,method: "auto"自动打开浏览器(copilot.ts:258-261)。copilot.ts:262-334):POST /login/oauth/access_token,grant_type=urn:ietf:params:oauth:grant-type:device_code。authorization_pending:按interval + 3s休眠后继续(:308-311)。slow_down:按 RFC 8628 §3.5 将间隔 +5s(若服务端返回新interval则采用),再加安全余量(:313-327)。copilot.ts:286-306):{ type:"success", refresh: access_token, access: access_token, expires: 0, enterpriseUrl? }。与部分 Copilot 集成不同,本仓库不调用
api.github.com/copilot_internal/v2/token交换短期 Copilot Token(全仓库无copilot_internal匹配)。OpenCode 直接把设备流拿到的 GitHub OAuth access token 当作 Copilot API 的 Bearer 使用:expires: 0且 authloader未定义refresh回调(对比xai.ts/codex.ts有刷新逻辑);loader内的fetch包装器每次请求都重新getAuth()读取当前存储的 token 后直接注入(copilot.ts:102-104,160-166)。凭据注入位置
provider.models()携带Authorization: Bearer {auth.access}+User-Agent+X-GitHub-Api-Version调CopilotModels.get()(copilot.ts:62-92),后者GET {base}/models(models.ts:213-256)。auth.loader返回的自定义fetch包装器统一注入(见第 2 节)。2. 客户端发送给 GitHub Copilot 的 Request Header
请求 Header 分两个阶段组装,最终由 auth loader 的
fetch包装器覆盖写入并发送。阶段 A —
LLMRequestPrep.prepare(packages/opencode/src/session/llm/request.ts)对非
opencode自有 provider(含 Copilot)注入基础 Header,并触发chat.headers插件钩子合并:x-session-affinityinput.sessionID(request.ts:197)X-Session-Idinput.sessionID(request.ts:198)x-parent-session-idinput.parentSessionID(子代理时,request.ts:199)User-Agentopencode/{InstallationVersion}(request.ts:194/200)X-GitHub-Api-Version2026-06-01(copilot.ts:363,chat.headers钩子)X-Interaction-Typeagent-session-name-generation(仅title代理,copilot.ts:364-366)anthropic-betainterleaved-thinking-2025-05-14(模型 npm 为@ai-sdk/anthropic时,copilot.ts:368-370)x-initiatoragent(压缩续写 / 子代理会话,copilot.ts:385-411)随后
model.headers与钩子 Header 合并进最终headers(request.ts:187-204)。阶段 B — auth loader 的
fetch包装器(copilot.ts:102-179,最终生效层)包装器在真正发起
fetch前,检查请求体判断isVision/isAgent,并组装最终 Header:AuthorizationBearer {info.access}(copilot.ts:164)User-Agentopencode/{InstallationVersion}(copilot.ts:163)Openai-Intentconversation-edits(copilot.ts:165)x-initiatoragent/user(由请求体推断,copilot.ts:160-161)agent则以其为准(init.headers在其后展开覆盖)Copilot-Vision-Requesttrue(请求含图片时,copilot.ts:168-170)x-api-keydelete(copilot.ts:172)authorization(小写)delete(copilot.ts:173)isAgent判定(copilot.ts:107-158):解析请求体,兼容三种 API 形态——Completions(
body.messages且 URL 含completions)、Responses(body.input)、Messages(body.messages);末条消息非
user、或为附带图片的合成用户消息(imgMsg)时判为agent。isVision:任一消息含image_url/input_image/image(含嵌套于tool_result内的图片)。Header 组装与覆盖顺序
flowchart LR A[request.ts 基础 Header<br/>x-session-affinity / X-Session-Id / User-Agent] --> M[合并] B[chat.headers 钩子<br/>X-GitHub-Api-Version / X-Interaction-Type<br/>anthropic-beta / x-initiator] --> M C[model.headers] --> M M --> D[AI SDK doStream:<br/>combineHeaders provider.headers + 请求 Header] D --> E[auth loader fetch 包装器<br/>覆盖 Authorization / User-Agent / Openai-Intent<br/>x-initiator / Copilot-Vision-Request<br/>删除 x-api-key、authorization] E --> F[(发往 api.githubcopilot.com)]3. 客户端发送给 GitHub Copilot 的 Request Body
请求体由 AI SDK 语言模型构造。Copilot 依据
/models返回的supported_endpoints为每个模型选择端点(
models.ts:92-99):/v1/messages→ Anthropic 协议、/responses→ Responses、/chat/completions→ Chat。请求参数源头统一在
request.ts的params(temperature/topP/topK/maxOutputTokens/options,来自ProviderTransform),系统提示注入为
system消息(Responses 端点则注入instructions)。3.1 Chat Completions(
POST {base}/chat/completions,npm@ai-sdk/github-copilot)Body 由
getArgs构造(packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:139-189):model:142)messagesconvertToOpenAICompatibleChatMessages(prompt)(:179)max_tokensmaxOutputTokens(:148;gpt 模型经chat.params置空以省略)temperature/top_p:149-150frequency_penalty/presence_penalty:151-152response_format:153-165)stop/seed:167-168reasoning_effort/verbosity:175-176)thinking_budget:186tools/tool_choiceprepareTools结果(:182-183)streamtrue(doStream,:310)3.2 Responses(
POST {base}/responses,GPT‑5 类模型,非gpt-5-mini)Body 由
packages/core/src/github-copilot/responses/openai-responses-language-model.ts:253-300构造:model、input、instructions(= 系统提示)、max_output_tokens、temperature、top_p、store(默认true)、tools、include、reasoning(effort/summary)、text.verbosity、parallel_tool_calls、previous_response_id、stream: true(:786-788)等。3.3 Messages / Anthropic(
POST {base}/v1/messages,npm@ai-sdk/anthropic)对
supported_endpoints含/v1/messages的模型,api.url置为{base}/v1、npm置为@ai-sdk/anthropic(
models.ts:92,109-110)。Body 为标准 Anthropic Messages 格式(model、system、messages、max_tokens、tools等)。3.4 Copilot 专属 Body 调整(
chat.params钩子,copilot.ts:340-354)maxOutputTokens = undefined→ 省略max_tokens,对齐官方 Copilot CLI(:344-346)。toolStreaming = false,规避 Copilot/v1/messagesshim 拒绝eager_input_streaming字段(:348-353)。_noop工具以满足 Copilot API 对tools字段的要求(request.ts:159-175)。4. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts:9-14, 26-28, 56-92, 102-179, 222-337, 340-354, 360-412packages/opencode/src/plugin/github-copilot/models.ts:82-159, 213-256packages/opencode/src/session/llm/request.ts:56-206packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:87-210, 306-323packages/core/src/github-copilot/responses/openai-responses-language-model.ts:253-300, 782-788packages/core/src/plugin/provider/github-copilot.ts:20-50packages/llm/src/providers/github-copilot.ts:19-69packages/opencode/src/session/llm/ai-sdk.ts:30-425. 结论摘要
Ov23li8tweQw6odWQebz,scoperead:user),直接使用 GitHub OAuth token 作为 Bearer,无copilot_internal二次交换。fetch包装器统一注入Authorization: Bearer <token>、User-Agent: opencode/{ver}、Openai-Intent: conversation-edits、x-initiator、按需Copilot-Vision-Request;上游另加X-GitHub-Api-Version: 2026-06-01、会话相关及场景性 Header。supported_endpoints分流到/chat/completions、/responses、/v1/messages三种协议,并针对 Copilot 做max_tokens省略、toolStreaming关闭、_noop工具占位等适配。