OpenCode 向 GitHub Copilot 发送请求的流程与请求头分析(2026-07-19)
本报告聚焦 GitHub Copilot provider,逐一梳理三部分:① GitHub 身份验证与 Token 交换流程 、② 客户端发送给 Copilot 的 Request Header 、③ 客户端发送给 Copilot 的 Request Body 。所有结论均以仓库实际代码为准,并标注 path:line。
0. 本次上游同步说明
本次运行检测到上游 anomalyco/opencode 的 dev 领先本 fork 8 个提交 ,已合并同步到 origin/dev(合并提交 317044017e,无冲突)。同步进来的提交如下:
SHA
提交
b8142c7aa8
chore: update nix node_modules hashes
ba3f966ed0
build: bump opentui to 0.4.5 (anomalyco#37616 )
b95fe7b2d7
fix(nix): restore desktop integration (anomalyco#37197 )
9da9ed9de6
fix(nix): relax Bun version check for desktop build (anomalyco#36767 )
fab2133129
chore: configure trust center domain (anomalyco#36910 )
901c9e7329
fix(app): disable unavailable file navigation (anomalyco#37595 )
3476e6baa5
chore: update nix node_modules hashes
86e04d4174
revert(tui): downgrade opentui to 0.4.3 (anomalyco#37582 )
对请求流程/Header 的影响:无。 这些提交集中在 nix 打包、opentui/tui 版本、desktop、app 导航与信任中心域名配置,均未触及 Copilot 的鉴权、Header 或 Body 构造代码。
1. 架构分层与关键文件
OpenCode 把「Copilot 特有逻辑」放在插件里,把「HTTP 传输 / body 构造」交给 AI SDK 兼容层,二者通过 provider 的自定义 fetch 与 plugin hooks 衔接:
层
位置
职责
Copilot 认证插件
packages/opencode/src/plugin/github-copilot/copilot.ts
OAuth 设备码登录、注入鉴权与 Header 的自定义 fetch、chat.params / chat.headers hook
Copilot 模型清单
packages/opencode/src/plugin/github-copilot/models.ts
拉取 /models、决定端点(chat/responses/messages)
请求准备(编排)
packages/opencode/src/session/llm/request.ts
组装 system/messages/tools/params,触发 hooks,生成基础 Header
Provider 装配
packages/core/src/plugin/provider/github-copilot.ts
端点路由,注入 @ai-sdk/github-copilot SDK
OpenAI 兼容 Provider
packages/core/src/github-copilot/copilot-provider.ts
组装 baseURL / headers / 自定义 fetch
Chat 语言模型
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts
/chat/completions 的 body 构造与请求发送
Responses 语言模型
packages/core/src/github-copilot/responses/openai-responses-language-model.ts
/responses 的 body 构造与请求发送
消息转换
.../chat/convert-to-openai-compatible-chat-messages.ts、.../responses/convert-to-openai-responses-input.ts
LanguageModelV3 prompt → 端点 body 的 messages/input
Copilot 三种端点(由 /models 返回的 supported_endpoints 决定,见 models.ts:92-111):
chat → POST https://api.githubcopilot.com/chat/completions(npm @ai-sdk/github-copilot)
responses → POST https://api.githubcopilot.com/responses(npm @ai-sdk/github-copilot)
messages → POST https://api.githubcopilot.com/v1/messages(npm @ai-sdk/anthropic,api.url 追加 /v1)
GPT-5 及以上(除 gpt-5-mini)默认走 Responses,其余走 Chat(github-copilot.ts:42-48)。
2. GitHub 身份验证与 Token 交换流程
2.1 核心结论
OpenCode 使用 GitHub OAuth「设备授权码流程」(Device Authorization Grant,RFC 8628)拿到一个 GitHub OAuth token,然后把它 直接 作为 Authorization: Bearer 用于 api.githubcopilot.com。仓库中 不存在 传统的 POST /copilot_internal/v2/token 二次换取短期 Copilot token 的步骤,也 没有 为 github-copilot 注册任何 refresh 刷新函数。
关键证据:
全仓无 copilot_internal / /v2/token 端点调用;github-copilot 的 auth.methods 里只有 type: "oauth",无 refresh 实现(对比 openai/codex.ts、xai.ts 都有 refresh)。
实际用作 Bearer 的字段是 refresh (长期 GitHub token),而非 access:
模型清单请求:Authorization: \Bearer ${auth.refresh}`(copilot.ts:74`)
聊天/推理请求:Authorization: \Bearer ${info.refresh}`(copilot.ts:164`)
2.2 客户端标识
OAuth App client_id = "Ov23li8tweQw6odWQebz"(copilot.ts:9)
API 版本 X-GitHub-Api-Version = "2026-06-01"(copilot.ts:10)
域名:默认 github.com;企业版 normalizeDomain(enterpriseUrl),API base 变为 https://copilot-api.<domain>(copilot.ts:26-28)
2.3 设备码登录(authorize,copilot.ts:222-336)
发起设备码 :POST https://<domain>/login/device/code(copilot.ts:234-245)
{ "client_id" : " Ov23li8tweQw6odWQebz" , "scope" : " read:user" }
Header:Accept: application/json、Content-Type: application/json、User-Agent: opencode/<version>。
返回 verification_uri、user_code、device_code、interval。
提示用户 :显示 verification_uri 与 Enter code: <user_code>(copilot.ts:258-261)。
轮询换取 token :POST https://<domain>/login/oauth/access_token(copilot.ts:264-276)
{
"client_id" : " Ov23li8tweQw6odWQebz" ,
"device_code" : " <device_code>" ,
"grant_type" : " urn:ietf:params:oauth:grant-type:device_code"
}
authorization_pending → 按 interval 轮询(含 3s 安全余量 OAUTH_POLLING_SAFETY_MARGIN_MS,copilot.ts:308-311)
slow_down → 依 RFC 8628 §3.5,间隔 +5s(或用服务端返回的 interval)后重试(copilot.ts:313-327)
其它 error → 返回 failed(copilot.ts:329)
持久化凭据 (copilot.ts:286-306):
{ type : "success" , refresh : access_token , access : access_token , expires : 0 , enterpriseUrl ? }
access 与 refresh 都被赋成同一个 GitHub token;expires: 0 表示不做基于过期时间的缓存/刷新,请求时始终取长期的 refresh 字段作为 Bearer。
2.4 凭据如何注入到每次请求
auth.loader(getAuth) 返回一个自定义 fetch(copilot.ts:96-181)。AI SDK 兼容层最终用这个 fetch 发请求(copilot-provider.ts:73 → fetch: options.fetch),因此凭据/Header 在这里统一注入。loader 同时返回 apiKey: ""(copilot.ts:101),避免 SDK 用默认 Authorization/x-api-key(随后也会被显式删除,见 §3)。
2.5 鉴权时序图
sequenceDiagram
participant U as 用户
participant OC as OpenCode CLI
participant GH as github.com (OAuth)
participant CAPI 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 轮询直到授权
OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type}
GH-->>OC: authorization_pending / slow_down / access_token
end
GH-->>OC: access_token (GitHub OAuth token)
Note over OC: 存储 {refresh=access=token, expires:0}<br/>无二次 token 交换
OC->>CAPI: GET /models (Authorization: Bearer <token>)
CAPI-->>OC: 可用模型清单
Note over OC,CAPI: 后续每次对话请求都用<br/>同一个 GitHub token 作 Bearer
Loading
3. 客户端发送给 GitHub Copilot 的 Request Header
Header 由 三处 叠加而成(后者覆盖前者):
request.ts 基础 Header (prepare(),非 opencode provider 分支,request.ts:196-204)
chat.headers hook (Copilot 插件,copilot.ts:360-412)
自定义 fetch 强制层 (Copilot 插件,copilot.ts:160-173)——最终决定权在此,强制写入鉴权类 Header 并删除 SDK 默认鉴权头。
3.1 最终发往 api.githubcopilot.com 的 Header 明细
Header
取值
来源(代码位置)
作用 / 说明
Authorization
Bearer <GitHub OAuth token>
copilot.ts:164(取 info.refresh)
强制 。鉴权凭据,直接用 GitHub token
User-Agent
opencode/<InstallationVersion>
copilot.ts:163(覆盖 SDK 追加的 UA 后缀)
强制 。客户端标识
Openai-Intent
conversation-edits
copilot.ts:165
强制 。声明请求意图,Copilot 后端据此计费/路由
x-initiator
agent 或 user
copilot.ts:161(按 body 启发式)/ copilot.ts:394,411(hook 对 compaction、子代理强制 agent)
标识本轮由 agent 还是用户发起
X-GitHub-Api-Version
2026-06-01
copilot.ts:363(chat.headers)
固定 API 版本
Copilot-Vision-Request
true
copilot.ts:168-170
仅当请求含图片时 。开启视觉请求
X-Interaction-Type
agent-session-name-generation
copilot.ts:364-366
仅当 title 代理 (生成会话标题)时
anthropic-beta
interleaved-thinking-2025-05-14
copilot.ts:368-370
仅当模型 npm 为 @ai-sdk/anthropic (即 /v1/messages 端点)时
x-session-affinity
<sessionID>
request.ts:197
会话亲和/路由
X-Session-Id
<sessionID>
request.ts:198
会话标识
x-parent-session-id
<parentSessionID>
request.ts:199
仅子代理会话存在时
Content-Type
application/json
AI SDK postJsonToApi 默认
JSON 请求体
x-api-key
(删除)
copilot.ts:172
移除 SDK 默认头,避免与 Authorization 冲突
authorization(小写)
(删除)
copilot.ts:173
同上,去重
chat.params hook 还会影响 body 而非 Header:GPT 模型省略 maxOutputTokens(copilot.ts:344-346);@ai-sdk/anthropic 模型关闭 toolStreaming,避免 /v1/messages shim 拒绝 eager_input_streaming 字段(copilot.ts:351-353)。
3.2 模型清单请求(GET /models)的 Header
见 copilot.ts:70-79:Authorization: Bearer <auth.refresh>、User-Agent: opencode/<version>、X-GitHub-Api-Version: 2026-06-01,外加 provider.options.headers。
3.3 x-initiator 判定逻辑(body 启发式)
自定义 fetch 解析 init.body,按端点判断「最后一条消息是否由用户直接发出」(copilot.ts:107-158):
Completions :messages 最后一条 role !== "user" 或为合成附件消息 → agent
Responses :input 最后一项同理
Messages(Anthropic) :最后一条非「纯 user 且含非 tool_result 内容」→ agent
任一端点消息里含图片(image_url / input_image / image)→ isVision = true → 加 Copilot-Vision-Request
此外 chat.headers hook 会在自动压缩续写 (compaction)或子代理会话 时把 x-initiator 强制为 agent(copilot.ts:385-411)。
4. 客户端发送给 GitHub Copilot 的 Request Body
Body 由 AI SDK 语言模型的 getArgs() 构造,再 JSON.stringify 发送。三种端点结构不同。
4.1 Chat Completions(POST /chat/completions)
Body 组装见 openai-compatible-chat-language-model.ts:139-190;doStream 额外加流式字段(:306-320):
字段
来源
说明
model
this.modelId
模型 API id
messages
convertToOpenAICompatibleChatMessages(prompt)
见 §4.4
max_tokens
maxOutputTokens
GPT 模型会被 chat.params 置空而省略
temperature / top_p
params
采样参数
frequency_penalty / presence_penalty / stop / seed
调用选项
可选
tools / tool_choice
prepareTools(...)
工具定义与选择策略
reasoning_effort / verbosity / thinking_budget
provider options(copilot)
推理相关
response_format
responseFormat
json_schema / json_object(仅结构化输出模型)
stream
true(doStream,:310)
流式
stream_options
includeUsage ? {include_usage:true} : undefined(:313)
Copilot 未设 includeUsage,故 省略
请求发送:postJsonToApi({ url: <base>/chat/completions, headers: combineHeaders(config.headers(), options.headers), body: args, fetch: config.fetch })(:201-212)。
4.2 Responses(POST /responses)
Body 组装见 openai-responses-language-model.ts:253-309;doStream 加 stream: true(:786-793):
字段
来源
说明
model
this.modelId
input
convertToOpenAIResponsesInput(...)
Responses 输入项数组(见 §4.4)
temperature / top_p
params
推理模型会被置空 并告警(:311-330)
max_output_tokens
maxOutputTokens
reasoning
{effort, summary}
仅推理模型且设置了 effort/summary 时
include
动态
如 reasoning.encrypted_content(由模型 variants 决定)、web_search sources、logprobs 等
text
{format, verbosity}
JSON schema / 详尽度
store
openaiOptions.store ?? true(转换时默认 true,:206)
是否服务端存储
metadata / parallel_tool_calls / previous_response_id / max_tool_calls / service_tier / truncation 等
provider options
可选
stream
true(doStream,:788)
流式
请求发送:url: config.url({ path: "/responses" })、fetch: config.fetch(:394-396、:781-793)。
4.3 Messages(POST /v1/messages,Anthropic 家族)
由外部包 @ai-sdk/anthropic 构造 body(model、system、messages、tools、max_tokens、thinking、stream 等)。OpenCode 侧的定制:
chat.params 关闭 toolStreaming(不注入 eager_input_streaming,copilot.ts:351-353);
chat.headers 加 anthropic-beta: interleaved-thinking-2025-05-14(copilot.ts:368-370);
models.ts:109-110 将该类模型 api.url 设为 <base>/v1,故最终路径为 /v1/messages。
4.4 消息转换(body.messages / body.input 的形状)
Chat (convert-to-openai-compatible-chat-messages.ts):
system → {role:"system", content}(:18-25)
user → 纯文本折叠为字符串;多模态为数组,图片转 {type:"image_url", image_url:{url}}(data: base64 或 URL,:45-58)
assistant → content + 可选 tool_calls:[{id,type:"function",function:{name,arguments:JSON.stringify(input)}}] + reasoning_text/reasoning_opaque(:73-123)
tool → {role:"tool", tool_call_id, content},其中 content 依 output 类型序列化(:128-158)
Responses (convert-to-openai-responses-input.ts):文本 → input_text;图片 → input_image(image_url);文件 → input_file(file_url)(:83-95)。
4.5 Copilot 专属 body 兜底
当 Copilot 请求「重放历史工具调用」但当前无启用工具时,request.ts:159-175 会注入一个 _noop 占位工具(tools._noop),因为 Copilot 在存在历史 tool_call 时要求 body 必须带 tools 字段。
5. 完整请求时序(会话 → HTTP)
sequenceDiagram
participant S as Session/Prompt
participant P as LLMRequestPrep.prepare (request.ts)
participant H as Copilot 插件 hooks (copilot.ts)
participant SDK as AI SDK 语言模型 (core/github-copilot)
participant F as 自定义 fetch (copilot.ts loader)
participant C as api.githubcopilot.com
S->>P: model/agent/messages/tools
P->>P: 组 system+messages, resolveTools, (Copilot 注入 _noop)
P->>H: trigger chat.params (GPT 省略 maxTokens; anthropic 关 toolStreaming)
P->>H: trigger chat.headers (X-GitHub-Api-Version, x-initiator, ...)
P-->>SDK: {messages, tools, params, headers}
SDK->>SDK: getArgs() 构造 body (/chat/completions 或 /responses)
SDK->>F: postJsonToApi(url, combineHeaders, body, fetch=config.fetch)
F->>F: 解析 body 判定 isAgent/isVision
F->>F: 强制 Authorization/User-Agent/Openai-Intent, 删除 x-api-key/authorization
F->>C: POST body (stream:true)
C-->>F: SSE 流
F-->>SDK: Response
SDK-->>S: 归一化为 LLMEvent (ai-sdk.ts)
Loading
6. 关键代码位置索引
认证插件:packages/opencode/src/plugin/github-copilot/copilot.ts
常量::9(CLIENT_ID)、:10(API_VERSION)、:26-28(base URL)
设备码流程::222-336(authorize / 轮询 / 持久化)
自定义 fetch 注入 Header::96-181(:160-173 强制层)
provider.models 拉清单 Header::70-79
chat.params::340-354;chat.headers::360-412
模型/端点:packages/opencode/src/plugin/github-copilot/models.ts:92-111,218
请求编排:packages/opencode/src/session/llm/request.ts:56-206(Header :187-204,_noop :159-175)
Provider 装配:packages/core/src/plugin/provider/github-copilot.ts:20-52
兼容 Provider:packages/core/src/github-copilot/copilot-provider.ts:52-97
Chat body:packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:87-212,306-320
Responses body:packages/core/src/github-copilot/responses/openai-responses-language-model.ts:152-309,394-396,778-793
消息转换:.../chat/convert-to-openai-compatible-chat-messages.ts、.../responses/convert-to-openai-responses-input.ts:83-95
事件归一化:packages/opencode/src/session/llm/ai-sdk.ts
本报告由每日「上游同步 + 请求流程分析」定时任务自动生成。仅只读分析,未修改任何业务代码。
OpenCode 向 GitHub Copilot 发送请求的流程与请求头分析(2026-07-19)
0. 本次上游同步说明
本次运行检测到上游
anomalyco/opencode的dev领先本 fork 8 个提交,已合并同步到origin/dev(合并提交317044017e,无冲突)。同步进来的提交如下:b8142c7aa8ba3f966ed0b95fe7b2d79da9ed9de6fab2133129901c9e73293476e6baa586e04d4174对请求流程/Header 的影响:无。 这些提交集中在 nix 打包、opentui/tui 版本、desktop、app 导航与信任中心域名配置,均未触及 Copilot 的鉴权、Header 或 Body 构造代码。
1. 架构分层与关键文件
OpenCode 把「Copilot 特有逻辑」放在插件里,把「HTTP 传输 / body 构造」交给 AI SDK 兼容层,二者通过 provider 的自定义
fetch与 plugin hooks 衔接:packages/opencode/src/plugin/github-copilot/copilot.tsfetch、chat.params/chat.headershookpackages/opencode/src/plugin/github-copilot/models.ts/models、决定端点(chat/responses/messages)packages/opencode/src/session/llm/request.tspackages/core/src/plugin/provider/github-copilot.ts@ai-sdk/github-copilotSDKpackages/core/src/github-copilot/copilot-provider.tspackages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts/chat/completions的 body 构造与请求发送packages/core/src/github-copilot/responses/openai-responses-language-model.ts/responses的 body 构造与请求发送.../chat/convert-to-openai-compatible-chat-messages.ts、.../responses/convert-to-openai-responses-input.tsCopilot 三种端点(由
/models返回的supported_endpoints决定,见models.ts:92-111):chat→POST https://api.githubcopilot.com/chat/completions(npm@ai-sdk/github-copilot)responses→POST https://api.githubcopilot.com/responses(npm@ai-sdk/github-copilot)messages→POST https://api.githubcopilot.com/v1/messages(npm@ai-sdk/anthropic,api.url追加/v1)GPT-5 及以上(除
gpt-5-mini)默认走 Responses,其余走 Chat(github-copilot.ts:42-48)。2. GitHub 身份验证与 Token 交换流程
2.1 核心结论
关键证据:
copilot_internal//v2/token端点调用;github-copilot的auth.methods里只有type: "oauth",无refresh实现(对比openai/codex.ts、xai.ts都有refresh)。refresh(长期 GitHub token),而非access:Authorization: \Bearer ${auth.refresh}`(copilot.ts:74`)Authorization: \Bearer ${info.refresh}`(copilot.ts:164`)2.2 客户端标识
client_id = "Ov23li8tweQw6odWQebz"(copilot.ts:9)X-GitHub-Api-Version = "2026-06-01"(copilot.ts:10)github.com;企业版normalizeDomain(enterpriseUrl),API base 变为https://copilot-api.<domain>(copilot.ts:26-28)2.3 设备码登录(authorize,
copilot.ts:222-336)POST https://<domain>/login/device/code(copilot.ts:234-245){ "client_id": "Ov23li8tweQw6odWQebz", "scope": "read:user" }Accept: application/json、Content-Type: application/json、User-Agent: opencode/<version>。返回
verification_uri、user_code、device_code、interval。verification_uri与Enter code: <user_code>(copilot.ts:258-261)。POST https://<domain>/login/oauth/access_token(copilot.ts:264-276){ "client_id": "Ov23li8tweQw6odWQebz", "device_code": "<device_code>", "grant_type": "urn:ietf:params:oauth:grant-type:device_code" }authorization_pending→ 按interval轮询(含 3s 安全余量OAUTH_POLLING_SAFETY_MARGIN_MS,copilot.ts:308-311)slow_down→ 依 RFC 8628 §3.5,间隔 +5s(或用服务端返回的interval)后重试(copilot.ts:313-327)error→ 返回failed(copilot.ts:329)copilot.ts:286-306):access与refresh都被赋成同一个 GitHub token;expires: 0表示不做基于过期时间的缓存/刷新,请求时始终取长期的refresh字段作为 Bearer。2.4 凭据如何注入到每次请求
auth.loader(getAuth)返回一个自定义fetch(copilot.ts:96-181)。AI SDK 兼容层最终用这个fetch发请求(copilot-provider.ts:73→fetch: options.fetch),因此凭据/Header 在这里统一注入。loader同时返回apiKey: ""(copilot.ts:101),避免 SDK 用默认Authorization/x-api-key(随后也会被显式删除,见 §3)。2.5 鉴权时序图
sequenceDiagram participant U as 用户 participant OC as OpenCode CLI participant GH as github.com (OAuth) participant CAPI 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 轮询直到授权 OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type} GH-->>OC: authorization_pending / slow_down / access_token end GH-->>OC: access_token (GitHub OAuth token) Note over OC: 存储 {refresh=access=token, expires:0}<br/>无二次 token 交换 OC->>CAPI: GET /models (Authorization: Bearer <token>) CAPI-->>OC: 可用模型清单 Note over OC,CAPI: 后续每次对话请求都用<br/>同一个 GitHub token 作 Bearer3. 客户端发送给 GitHub Copilot 的 Request Header
Header 由 三处叠加而成(后者覆盖前者):
request.ts基础 Header(prepare(),非 opencode provider 分支,request.ts:196-204)chat.headershook(Copilot 插件,copilot.ts:360-412)fetch强制层(Copilot 插件,copilot.ts:160-173)——最终决定权在此,强制写入鉴权类 Header 并删除 SDK 默认鉴权头。3.1 最终发往
api.githubcopilot.com的 Header 明细AuthorizationBearer <GitHub OAuth token>copilot.ts:164(取info.refresh)User-Agentopencode/<InstallationVersion>copilot.ts:163(覆盖 SDK 追加的 UA 后缀)Openai-Intentconversation-editscopilot.ts:165x-initiatoragent或usercopilot.ts:161(按 body 启发式)/copilot.ts:394,411(hook 对 compaction、子代理强制agent)X-GitHub-Api-Version2026-06-01copilot.ts:363(chat.headers)Copilot-Vision-Requesttruecopilot.ts:168-170X-Interaction-Typeagent-session-name-generationcopilot.ts:364-366title代理(生成会话标题)时anthropic-betainterleaved-thinking-2025-05-14copilot.ts:368-370@ai-sdk/anthropic(即/v1/messages端点)时x-session-affinity<sessionID>request.ts:197X-Session-Id<sessionID>request.ts:198x-parent-session-id<parentSessionID>request.ts:199Content-Typeapplication/jsonpostJsonToApi默认x-api-keycopilot.ts:172Authorization冲突(小写)authorizationcopilot.ts:1733.2 模型清单请求(
GET /models)的 Header见
copilot.ts:70-79:Authorization: Bearer <auth.refresh>、User-Agent: opencode/<version>、X-GitHub-Api-Version: 2026-06-01,外加provider.options.headers。3.3
x-initiator判定逻辑(body 启发式)自定义
fetch解析init.body,按端点判断「最后一条消息是否由用户直接发出」(copilot.ts:107-158):messages最后一条role !== "user"或为合成附件消息 →agentinput最后一项同理agentimage_url/input_image/image)→isVision = true→ 加Copilot-Vision-Request此外
chat.headershook 会在自动压缩续写(compaction)或子代理会话时把x-initiator强制为agent(copilot.ts:385-411)。4. 客户端发送给 GitHub Copilot 的 Request Body
Body 由 AI SDK 语言模型的
getArgs()构造,再JSON.stringify发送。三种端点结构不同。4.1 Chat Completions(
POST /chat/completions)Body 组装见
openai-compatible-chat-language-model.ts:139-190;doStream额外加流式字段(:306-320):modelthis.modelIdmessagesconvertToOpenAICompatibleChatMessages(prompt)max_tokensmaxOutputTokenschat.params置空而省略temperature/top_pfrequency_penalty/presence_penalty/stop/seedtools/tool_choiceprepareTools(...)reasoning_effort/verbosity/thinking_budgetcopilot)response_formatjson_schema/json_object(仅结构化输出模型)streamtrue(doStream,:310)stream_optionsincludeUsage ? {include_usage:true} : undefined(:313)includeUsage,故 省略请求发送:
postJsonToApi({ url: <base>/chat/completions, headers: combineHeaders(config.headers(), options.headers), body: args, fetch: config.fetch })(:201-212)。4.2 Responses(
POST /responses)Body 组装见
openai-responses-language-model.ts:253-309;doStream加stream: true(:786-793):modelthis.modelIdinputconvertToOpenAIResponsesInput(...)temperature/top_p:311-330)max_output_tokensmaxOutputTokensreasoning{effort, summary}includereasoning.encrypted_content(由模型 variants 决定)、web_search sources、logprobs 等text{format, verbosity}storeopenaiOptions.store ?? true(转换时默认true,:206)metadata/parallel_tool_calls/previous_response_id/max_tool_calls/service_tier/truncation等streamtrue(doStream,:788)请求发送:
url: config.url({ path: "/responses" })、fetch: config.fetch(:394-396、:781-793)。4.3 Messages(
POST /v1/messages,Anthropic 家族)由外部包
@ai-sdk/anthropic构造 body(model、system、messages、tools、max_tokens、thinking、stream等)。OpenCode 侧的定制:chat.params关闭toolStreaming(不注入eager_input_streaming,copilot.ts:351-353);chat.headers加anthropic-beta: interleaved-thinking-2025-05-14(copilot.ts:368-370);models.ts:109-110将该类模型api.url设为<base>/v1,故最终路径为/v1/messages。4.4 消息转换(body.messages / body.input 的形状)
Chat(
convert-to-openai-compatible-chat-messages.ts):system→{role:"system", content}(:18-25)user→ 纯文本折叠为字符串;多模态为数组,图片转{type:"image_url", image_url:{url}}(data:base64 或 URL,:45-58)assistant→content+ 可选tool_calls:[{id,type:"function",function:{name,arguments:JSON.stringify(input)}}]+reasoning_text/reasoning_opaque(:73-123)tool→{role:"tool", tool_call_id, content},其中 content 依 output 类型序列化(:128-158)Responses(
convert-to-openai-responses-input.ts):文本 →input_text;图片 →input_image(image_url);文件 →input_file(file_url)(:83-95)。4.5 Copilot 专属 body 兜底
当 Copilot 请求「重放历史工具调用」但当前无启用工具时,
request.ts:159-175会注入一个_noop占位工具(tools._noop),因为 Copilot 在存在历史tool_call时要求 body 必须带tools字段。5. 完整请求时序(会话 → HTTP)
sequenceDiagram participant S as Session/Prompt participant P as LLMRequestPrep.prepare (request.ts) participant H as Copilot 插件 hooks (copilot.ts) participant SDK as AI SDK 语言模型 (core/github-copilot) participant F as 自定义 fetch (copilot.ts loader) participant C as api.githubcopilot.com S->>P: model/agent/messages/tools P->>P: 组 system+messages, resolveTools, (Copilot 注入 _noop) P->>H: trigger chat.params (GPT 省略 maxTokens; anthropic 关 toolStreaming) P->>H: trigger chat.headers (X-GitHub-Api-Version, x-initiator, ...) P-->>SDK: {messages, tools, params, headers} SDK->>SDK: getArgs() 构造 body (/chat/completions 或 /responses) SDK->>F: postJsonToApi(url, combineHeaders, body, fetch=config.fetch) F->>F: 解析 body 判定 isAgent/isVision F->>F: 强制 Authorization/User-Agent/Openai-Intent, 删除 x-api-key/authorization F->>C: POST body (stream:true) C-->>F: SSE 流 F-->>SDK: Response SDK-->>S: 归一化为 LLMEvent (ai-sdk.ts)6. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts:9(CLIENT_ID)、:10(API_VERSION)、:26-28(base URL):222-336(authorize / 轮询 / 持久化):96-181(:160-173强制层)provider.models拉清单 Header::70-79chat.params::340-354;chat.headers::360-412packages/opencode/src/plugin/github-copilot/models.ts:92-111,218packages/opencode/src/session/llm/request.ts:56-206(Header:187-204,_noop:159-175)packages/core/src/plugin/provider/github-copilot.ts:20-52packages/core/src/github-copilot/copilot-provider.ts:52-97packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:87-212,306-320packages/core/src/github-copilot/responses/openai-responses-language-model.ts:152-309,394-396,778-793.../chat/convert-to-openai-compatible-chat-messages.ts、.../responses/convert-to-openai-responses-input.ts:83-95packages/opencode/src/session/llm/ai-sdk.ts本报告由每日「上游同步 + 请求流程分析」定时任务自动生成。仅只读分析,未修改任何业务代码。