本次变更提示
本次从上游 anomalyco/opencode:dev 同步了 62 个提交 (1882c33827..24470e52a5,已合并为 c44418cc86 并推送到 fork 的 dev)。改动主要集中在 packages/app、packages/ui、packages/desktop、packages/web(前端、i18n、RTL、Markdown 渲染等),未触及 GitHub Copilot 的认证与请求构造代码 。唯一与请求流程沾边的是 #40265(修复 Azure + gpt-5.5 的 reasoningEffort),与 Copilot 无关。故下文结论与上一版一致,且做了 Copilot 专项细化。
说明:以下内容以仓库实际代码为准,不臆测 GitHub 服务端行为。凡涉及 Authorization 头字面量处,源码被 secret scanner 掩码为 ******,本文按其结构描述为 Bearer <access_token>。
1. 请求流程总览
GitHub Copilot 在 opencode 中是一个认证插件 + AI SDK Provider 的组合:
认证与 Header 注入:packages/opencode/src/plugin/github-copilot/copilot.ts(CopilotAuthPlugin)
模型清单与端点路由:packages/opencode/src/plugin/github-copilot/models.ts
请求体构造(AI SDK 适配层,仓库内 vendored 副本):packages/core/src/github-copilot/**
一次对话请求的整体时序:
sequenceDiagram
participant U as 用户/Agent
participant S as opencode 会话层
participant H as chat.headers/params 钩子<br/>(copilot.ts)
participant M as AI SDK 语言模型<br/>(core/github-copilot)
participant F as 自定义 fetch 包装器<br/>(copilot.ts loader)
participant G as api.githubcopilot.com
U->>S: 发起 Prompt
S->>H: 触发 chat.params / chat.headers
H-->>S: 注入 X-GitHub-Api-Version 等 headers<br/>+ 调整 params(gpt* 去 max_tokens / anthropic 关 toolStreaming)
S->>M: 调用 doGenerate / doStream
M->>M: getArgs() 构造 Body(JSON)
M->>F: postJsonToApi(url, combineHeaders(...), body)
F->>F: 注入 Authorization: Bearer <access><br/>+ User-Agent / Openai-Intent / x-initiator<br/>+ (视觉请求)Copilot-Vision-Request<br/>删除小写 authorization / x-api-key
F->>G: HTTP POST /chat/completions | /responses | /v1/messages
G-->>F: SSE / JSON 响应
F-->>M: 透传响应流
M-->>U: 解析为消息/工具调用
Loading
关键点:认证头不是由 AI SDK Provider 注入的 。Provider 以 apiKey: "" 创建(见 copilot-provider.ts:60-64 的 if (options.apiKey) 判定为 false),真正的 Authorization 由 loader 里的自定义 fetch 包装器在每次请求 时统一注入。
2. GitHub 身份验证与 Token 交换流程
采用 OAuth 2.0 设备码流程(Device Authorization Grant, RFC 8628) ,代码位于 copilot.ts。
关键常量:
常量
值
位置
CLIENT_ID
Ov23li8tweQw6odWQebz
copilot.ts 顶部
API_VERSION
2026-06-01
copilot.ts 顶部
OAUTH_POLLING_SAFETY_MARGIN_MS
3000
copilot.ts 顶部
认证域名
默认 github.com(企业版用自定义域名)
authorize/callback
Base API
https://api.githubcopilot.com(企业版 https://copilot-api.{normalizedEnterpriseUrl})
provider.models
流程:
sequenceDiagram
participant C as opencode
participant D as github.com<br/>(login/device/*)
participant A as api.githubcopilot.com
C->>D: POST /login/device/code<br/>body {client_id, scope:"read:user"}<br/>Accept/Content-Type: application/json
D-->>C: device_code, user_code, verification_uri, interval, expires_in
C->>C: 提示用户在浏览器输入 user_code 授权
loop 轮询(间隔 interval + 安全余量)
C->>D: POST /login/oauth/access_token<br/>body {client_id, device_code,<br/>grant_type: urn:ietf:params:oauth:grant-type:device_code}
alt 待授权
D-->>C: error=authorization_pending → 继续轮询
else 频率过快
D-->>C: error=slow_down → interval +5s 后继续
else 成功
D-->>C: access_token
end
end
C->>C: 存储 refresh=access=access_token, expires:0<br/>(+ 可选 enterpriseUrl)
C->>A: 后续请求直接用 access_token 作 Bearer
Loading
关键结论(与"常见 Copilot 流程"不同):
opencode 没有 copilot_internal/v2/token 这一步二次令牌交换。grep 全仓确认不存在该端点。
设备码流程拿到的 GitHub OAuth access_token 被直接当作 Bearer 令牌 发往 api.githubcopilot.com。
令牌存储中 refresh、access 同值,expires 记为 0,即不做本地过期滚动刷新(以实际存储字段为准)。
设备码/令牌交换请求头(copilot.ts):
Header
取值
说明
Accept
application/json
期望 JSON 响应
Content-Type
application/json
请求体为 JSON
3. 客户端发送给 GitHub Copilot 的 Request Header
Header 有 两个来源 ,最终由 combineHeaders() 合并、并由自定义 fetch 包装器做最后覆盖。
3.1 自定义 fetch 包装器(作用于所有 Copilot API 调用)
位置:copilot.ts loader 的 fetch 包装(约 155-175 行)。isAgent/isVision 通过解析请求体判定(兼容 Completions / Responses / Anthropic Messages 三种体形)。
Header
取值来源
作用
Authorization
Bearer <access_token>(源码被掩码 ******)
携带设备流 OAuth 令牌鉴权
User-Agent
opencode/${InstallationVersion}
客户端标识
Openai-Intent
conversation-edits
固定意图声明
x-initiator
agent(isAgent)或 user
请求发起者类型(可被钩子覆盖,见 3.2)
Copilot-Vision-Request
true(仅当检测到图片/视觉内容)
声明视觉请求
(删除)x-api-key、authorization
小写键被删除
避免与注入的鉴权头冲突
3.2 chat.headers 钩子(按会话/模型条件附加,作为 options.headers)
位置:copilot.ts 约 360-412 行。
Header
取值
触发条件
X-GitHub-Api-Version
2026-06-01
所有 github-copilot 请求
X-Interaction-Type
agent-session-name-generation
agent === "title"(标题生成)
anthropic-beta
interleaved-thinking-2025-05-14
模型 npm 为 @ai-sdk/anthropic
x-initiator
agent
压缩/自动压缩会话,或子代理(有 parentID)会话
3.3 /models 清单请求
位置:copilot.ts 约 70-78 行 / models.ts。
Header
取值
作用
Authorization
Bearer <access_token>
鉴权
User-Agent
opencode/${InstallationVersion}
客户端标识
X-GitHub-Api-Version
2026-06-01
API 版本
3.4 合并/覆盖顺序
AI SDK Provider getHeaders():仅追加 User-Agent 后缀 ai-sdk/openai-compatible/0.1.0(因 apiKey:"" 不注入 Authorization)。
combineHeaders(config.headers(), options.headers):并入 3.2 钩子产出的 headers。
自定义 fetch 包装器:先置 x-initiator 默认值 → 展开传入 headers(钩子的 x-initiator 覆盖默认值)→ 最后强制 User-Agent / Authorization / Openai-Intent(这三者始终以包装器为准)。
4. 客户端发送给 GitHub Copilot 的 Request Body
Copilot 模型按 models.ts:92-111 的 supported_endpoints 路由到三种协议,Body 结构各不相同:
端点
URL
npm 适配器
Body 构造
chat
https://api.githubcopilot.com/chat/completions
@ai-sdk/github-copilot
OpenAI Chat Completions
responses
https://api.githubcopilot.com/responses
@ai-sdk/github-copilot
OpenAI Responses
messages
https://api.githubcopilot.com/v1/messages
@ai-sdk/anthropic
Anthropic Messages
端点选择:core/src/plugin/provider/github-copilot.ts:27-50 — GPT-5+(非 gpt-5-mini)优先走 responses,否则 chat;提供 /v1/messages 的模型走 Anthropic 协议。注意 chat/responses 的 baseURL 不含 /v1 ,仅 messages 的 baseURL 为 ${url}/v1。
4.1 chat/completions Body
位置:core/src/github-copilot/chat/openai-compatible-chat-language-model.ts 的 getArgs()(约 87-189 行)。
字段
来源
说明
model
this.modelId
模型 ID
messages
convertToOpenAICompatibleChatMessages(prompt)
消息数组(见 4.2)
max_tokens
maxOutputTokens
输出上限(gpt* 模型被 chat.params 删除,见 4.4)
temperature / top_p
选项
采样参数
frequency_penalty / presence_penalty
选项
惩罚项
stop
stopSequences
停止序列
seed
选项
复现用随机种子
response_format
responseFormat
json_schema 或 json_object
reasoning_effort
compatibleOptions.reasoningEffort
推理力度
verbosity
compatibleOptions.textVerbosity
冗长度
thinking_budget
compatibleOptions.thinking_budget
思考 token 预算
tools / tool_choice
prepareTools()
工具定义与选择策略
user
compatibleOptions.user
用户标识
stream
true(doStream)
流式开关
stream_options
{include_usage: true}(若 includeUsage)
流内返回用量统计
4.2 messages 转换(chat 协议)
位置:convert-to-openai-compatible-chat-messages.ts。将 LanguageModelV3Prompt 转为 OpenAI 消息数组:
system → {role:"system", content}
user → 纯文本走 {role:"user", content:string} 捷径;含附件时 content 为分片数组:{type:"text"} 与 {type:"image_url", image_url:{url}}(图片以 data:{mediaType};base64,... 内联)
assistant → {content, tool_calls?, reasoning_text?, reasoning_opaque?}
tool → {role:"tool", tool_call_id, content}
4.3 responses Body
位置:core/src/github-copilot/responses/openai-responses-language-model.ts(约 253-300 行 baseArgs)。
主要字段:model、input、temperature、top_p、max_output_tokens、text(含 format/verbosity)、max_tool_calls、metadata、parallel_tool_calls、previous_response_id、store(默认 true)、user、instructions、service_tier、include、reasoning(含 effort/summary,仅推理模型)、tools、tool_choice;doStream 追加 stream: true。
4.4 messages(Anthropic)Body 与 chat.params 调整
/v1/messages 的 Body 由 @ai-sdk/anthropic 构造,为标准 Anthropic Messages 形(model、messages、system、max_tokens、temperature、top_p、top_k、stop_sequences、tools、tool_choice、stream、thinking 等)。
chat.params 钩子(copilot.ts 约 340-354 行):
gpt* 模型:删除 maxOutputTokens(即不发 max_tokens)。
@ai-sdk/anthropic:设 toolStreaming = false(Copilot 的 /v1/messages 兼容层会拒绝 eager_input_streaming)。
5. 关键 API 端点汇总
端点
方法
用途
鉴权
https://github.com/login/device/code
POST
发起设备码流程
否(仅 client_id)
https://github.com/login/oauth/access_token
POST
轮询换取 access_token
否(client_id + device_code)
https://api.githubcopilot.com/models
GET
获取模型清单
Bearer
https://api.githubcopilot.com/chat/completions
POST
Chat Completions
Bearer
https://api.githubcopilot.com/responses
POST
Responses(GPT‑5+ 类)
Bearer
https://api.githubcopilot.com/v1/messages
POST
Anthropic Messages
Bearer
https://copilot-api.{enterprise}/...
GET/POST
企业版对应端点
Bearer
6. 代码位置索引
关注点
文件
关键位置
设备码认证 / 令牌 / fetch 头注入 / chat.params / chat.headers
packages/opencode/src/plugin/github-copilot/copilot.ts
9-28 常量;70-78 /models 头;155-175 fetch 注入;222-336 设备流;340-354 chat.params;360-412 chat.headers
模型清单与端点路由
packages/opencode/src/plugin/github-copilot/models.ts
92-111 端点判定;213-256 get()
SDK Provider(url / headers,apiKey 空不注入鉴权)
packages/core/src/github-copilot/copilot-provider.ts
52-97;60-64 Authorization 条件;72/81 url
chat Body 构造 / 发送
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts
87-189 getArgs;192+ doGenerate;305-329 doStream
chat 消息转换
packages/core/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts
13-170
responses Body 构造
packages/core/src/github-copilot/responses/openai-responses-language-model.ts
253-300 baseArgs;396/782 path:"/responses"
SDK 注册与语言模型选择
packages/core/src/plugin/provider/github-copilot.ts
20-25 注册;27-50 端点→模型选择
插件注册
packages/opencode/src/plugin/index.ts
15 导入;73 CopilotAuthPlugin
本报告由定时任务在同步上游后自动生成;分析基于同步点 24470e52a5(2026-08-06)对应的仓库代码。
本次变更提示
本次从上游
anomalyco/opencode:dev同步了 62 个提交(1882c33827..24470e52a5,已合并为c44418cc86并推送到 fork 的dev)。改动主要集中在packages/app、packages/ui、packages/desktop、packages/web(前端、i18n、RTL、Markdown 渲染等),未触及 GitHub Copilot 的认证与请求构造代码。唯一与请求流程沾边的是#40265(修复 Azure + gpt-5.5 的reasoningEffort),与 Copilot 无关。故下文结论与上一版一致,且做了 Copilot 专项细化。1. 请求流程总览
GitHub Copilot 在 opencode 中是一个认证插件 + AI SDK Provider 的组合:
packages/opencode/src/plugin/github-copilot/copilot.ts(CopilotAuthPlugin)packages/opencode/src/plugin/github-copilot/models.tspackages/core/src/github-copilot/**一次对话请求的整体时序:
sequenceDiagram participant U as 用户/Agent participant S as opencode 会话层 participant H as chat.headers/params 钩子<br/>(copilot.ts) participant M as AI SDK 语言模型<br/>(core/github-copilot) participant F as 自定义 fetch 包装器<br/>(copilot.ts loader) participant G as api.githubcopilot.com U->>S: 发起 Prompt S->>H: 触发 chat.params / chat.headers H-->>S: 注入 X-GitHub-Api-Version 等 headers<br/>+ 调整 params(gpt* 去 max_tokens / anthropic 关 toolStreaming) S->>M: 调用 doGenerate / doStream M->>M: getArgs() 构造 Body(JSON) M->>F: postJsonToApi(url, combineHeaders(...), body) F->>F: 注入 Authorization: Bearer <access><br/>+ User-Agent / Openai-Intent / x-initiator<br/>+ (视觉请求)Copilot-Vision-Request<br/>删除小写 authorization / x-api-key F->>G: HTTP POST /chat/completions | /responses | /v1/messages G-->>F: SSE / JSON 响应 F-->>M: 透传响应流 M-->>U: 解析为消息/工具调用关键点:认证头不是由 AI SDK Provider 注入的。Provider 以
apiKey: ""创建(见copilot-provider.ts:60-64的if (options.apiKey)判定为 false),真正的Authorization由 loader 里的自定义fetch包装器在每次请求时统一注入。2. GitHub 身份验证与 Token 交换流程
采用 OAuth 2.0 设备码流程(Device Authorization Grant, RFC 8628),代码位于
copilot.ts。关键常量:
CLIENT_IDOv23li8tweQw6odWQebzAPI_VERSION2026-06-01OAUTH_POLLING_SAFETY_MARGIN_MS3000github.com(企业版用自定义域名)https://api.githubcopilot.com(企业版https://copilot-api.{normalizedEnterpriseUrl})流程:
sequenceDiagram participant C as opencode participant D as github.com<br/>(login/device/*) participant A as api.githubcopilot.com C->>D: POST /login/device/code<br/>body {client_id, scope:"read:user"}<br/>Accept/Content-Type: application/json D-->>C: device_code, user_code, verification_uri, interval, expires_in C->>C: 提示用户在浏览器输入 user_code 授权 loop 轮询(间隔 interval + 安全余量) C->>D: POST /login/oauth/access_token<br/>body {client_id, device_code,<br/>grant_type: urn:ietf:params:oauth:grant-type:device_code} alt 待授权 D-->>C: error=authorization_pending → 继续轮询 else 频率过快 D-->>C: error=slow_down → interval +5s 后继续 else 成功 D-->>C: access_token end end C->>C: 存储 refresh=access=access_token, expires:0<br/>(+ 可选 enterpriseUrl) C->>A: 后续请求直接用 access_token 作 Bearer关键结论(与"常见 Copilot 流程"不同):
copilot_internal/v2/token这一步二次令牌交换。grep 全仓确认不存在该端点。access_token被直接当作Bearer令牌发往api.githubcopilot.com。refresh、access同值,expires记为0,即不做本地过期滚动刷新(以实际存储字段为准)。设备码/令牌交换请求头(
copilot.ts):Acceptapplication/jsonContent-Typeapplication/json3. 客户端发送给 GitHub Copilot 的 Request Header
Header 有 两个来源,最终由
combineHeaders()合并、并由自定义fetch包装器做最后覆盖。3.1 自定义 fetch 包装器(作用于所有 Copilot API 调用)
位置:
copilot.tsloader 的fetch包装(约 155-175 行)。isAgent/isVision通过解析请求体判定(兼容 Completions / Responses / Anthropic Messages 三种体形)。AuthorizationBearer <access_token>(源码被掩码******)User-Agentopencode/${InstallationVersion}Openai-Intentconversation-editsx-initiatoragent(isAgent)或userCopilot-Vision-Requesttrue(仅当检测到图片/视觉内容)x-api-key、authorization3.2
chat.headers钩子(按会话/模型条件附加,作为options.headers)位置:
copilot.ts约 360-412 行。X-GitHub-Api-Version2026-06-01X-Interaction-Typeagent-session-name-generationagent === "title"(标题生成)anthropic-betainterleaved-thinking-2025-05-14@ai-sdk/anthropicx-initiatoragentparentID)会话3.3
/models清单请求位置:
copilot.ts约 70-78 行 /models.ts。AuthorizationBearer <access_token>User-Agentopencode/${InstallationVersion}X-GitHub-Api-Version2026-06-013.4 合并/覆盖顺序
getHeaders():仅追加User-Agent后缀ai-sdk/openai-compatible/0.1.0(因apiKey:""不注入 Authorization)。combineHeaders(config.headers(), options.headers):并入 3.2 钩子产出的 headers。fetch包装器:先置x-initiator默认值 → 展开传入 headers(钩子的x-initiator覆盖默认值)→ 最后强制User-Agent/Authorization/Openai-Intent(这三者始终以包装器为准)。4. 客户端发送给 GitHub Copilot 的 Request Body
Copilot 模型按
models.ts:92-111的supported_endpoints路由到三种协议,Body 结构各不相同:chathttps://api.githubcopilot.com/chat/completions@ai-sdk/github-copilotresponseshttps://api.githubcopilot.com/responses@ai-sdk/github-copilotmessageshttps://api.githubcopilot.com/v1/messages@ai-sdk/anthropic4.1 chat/completions Body
位置:
core/src/github-copilot/chat/openai-compatible-chat-language-model.ts的getArgs()(约 87-189 行)。modelthis.modelIdmessagesconvertToOpenAICompatibleChatMessages(prompt)max_tokensmaxOutputTokensgpt*模型被chat.params删除,见 4.4)temperature/top_pfrequency_penalty/presence_penaltystopstopSequencesseedresponse_formatresponseFormatjson_schema或json_objectreasoning_effortcompatibleOptions.reasoningEffortverbositycompatibleOptions.textVerbositythinking_budgetcompatibleOptions.thinking_budgettools/tool_choiceprepareTools()usercompatibleOptions.userstreamtrue(doStream)stream_options{include_usage: true}(若 includeUsage)4.2 messages 转换(chat 协议)
位置:
convert-to-openai-compatible-chat-messages.ts。将LanguageModelV3Prompt转为 OpenAI 消息数组:system→{role:"system", content}user→ 纯文本走{role:"user", content:string}捷径;含附件时content为分片数组:{type:"text"}与{type:"image_url", image_url:{url}}(图片以data:{mediaType};base64,...内联)assistant→{content, tool_calls?, reasoning_text?, reasoning_opaque?}tool→{role:"tool", tool_call_id, content}4.3 responses Body
位置:
core/src/github-copilot/responses/openai-responses-language-model.ts(约 253-300 行baseArgs)。主要字段:
model、input、temperature、top_p、max_output_tokens、text(含format/verbosity)、max_tool_calls、metadata、parallel_tool_calls、previous_response_id、store(默认true)、user、instructions、service_tier、include、reasoning(含effort/summary,仅推理模型)、tools、tool_choice;doStream追加stream: true。4.4 messages(Anthropic)Body 与
chat.params调整/v1/messages的 Body 由@ai-sdk/anthropic构造,为标准 Anthropic Messages 形(model、messages、system、max_tokens、temperature、top_p、top_k、stop_sequences、tools、tool_choice、stream、thinking等)。chat.params钩子(copilot.ts约 340-354 行):gpt*模型:删除maxOutputTokens(即不发max_tokens)。@ai-sdk/anthropic:设toolStreaming = false(Copilot 的/v1/messages兼容层会拒绝eager_input_streaming)。5. 关键 API 端点汇总
https://github.com/login/device/codehttps://github.com/login/oauth/access_tokenhttps://api.githubcopilot.com/modelshttps://api.githubcopilot.com/chat/completionshttps://api.githubcopilot.com/responseshttps://api.githubcopilot.com/v1/messageshttps://copilot-api.{enterprise}/...6. 代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts/models头;155-175 fetch 注入;222-336 设备流;340-354 chat.params;360-412 chat.headerspackages/opencode/src/plugin/github-copilot/models.tsget()packages/core/src/github-copilot/copilot-provider.tsurlpackages/core/src/github-copilot/chat/openai-compatible-chat-language-model.tsgetArgs;192+doGenerate;305-329doStreampackages/core/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.tspackages/core/src/github-copilot/responses/openai-responses-language-model.tsbaseArgs;396/782path:"/responses"packages/core/src/plugin/provider/github-copilot.tspackages/opencode/src/plugin/index.tsCopilotAuthPlugin本报告由定时任务在同步上游后自动生成;分析基于同步点
24470e52a5(2026-08-06)对应的仓库代码。