Skip to content

OpenCode 请求流程与请求 Header/Body 分析:GitHub Copilot(2026-07-13) #2

Description

@nickhou1983

本报告分析 opencodeGitHub Copilot 发起请求的完整流程,重点覆盖:GitHub 身份验证与 Token 交换、客户端构造的 Request Header、客户端构造的 Request Body。分析基于当前 dev(已与上游 anomalyco/opencode 完成同步)的实际代码,未做任何代码修改。

0. 本次同步与变更提示

本次运行检测到上游 anomalyco/opencode6 个新提交,已快进/合并同步到本 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.paramschat.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

关键环节与代码位置

  1. Provider/Model 解析与 endpoint 选择
    • packages/opencode/src/plugin/github-copilot/models.ts:82-202build):依据 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-25shouldUseResponsesApi):gpt-N(N≥5 且非 gpt-5-mini)走 Responses,否则 Chat Completions。
    • packages/core/src/plugin/provider/github-copilot.ts:27-50:按 endpoint/模型 id 绑定到 sdk.responsessdk.chat
  2. 鉴权凭据获取copilot.ts:96-181auth.loader)。
  3. 请求参数 / Header 钩子request.ts:114-146(触发 chat.params / chat.headers)+ copilot.ts:340-412
  4. Body 构造.../chat/openai-compatible-chat-language-model.ts:87-190getArgs)、.../responses/openai-responses-language-model.ts:253-309
  5. 传输发送 + 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 = 3000copilot.ts:14
  • API Base:copilot.ts:26-28 — 企业版 https://copilot-api.{domain},公有云 https://api.githubcopilot.com
  • 鉴权域名:公有云 github.com;企业版取用户输入的 enterpriseUrlcopilot.ts:222-232)。

流程步骤

  1. 请求设备码copilot.ts:234-256
    POST https://{domain}/login/device/code
    Body:{ "client_id": "Ov23li8tweQw6odWQebz", "scope": "read:user" }
    返回 verification_uriuser_codedevice_codeinterval。用户在浏览器输入 user_code 完成授权。

  2. 轮询交换 Access Tokencopilot.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 → 返回成功。
  3. 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.comexpires: 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 的自定义 fetchcopilot.ts:164),Token 取自 getAuth()access 字段。

  1. 凭据注入到每个请求:auth loader(copilot.ts:96-181)返回一个自定义 fetch,在每次真正发出请求前用最新的 getAuth() 结果注入 Authorization,并做 Header 收尾(见第 3 节)。

3. 客户端发送给 GitHub Copilot 的 Request Header

Header 分 多层叠加,后写覆盖先写;最终收尾在 auth loader 的自定义 fetchcopilot.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_VERSION2026-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-editscopilot.ts:165 Copilot 侧意图标记
x-initiator agent / usercopilot.ts:161 依据 body 判定:末条非 user 消息或合成附件消息 → agent,否则 usercopilot.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 系列 去掉 maxOutputTokensAnthropic(Messages shim)toolStreaming = false(规避 eager_input_streaming 被拒)。

4.1 Chat Completions —— POST {base}/chat/completions

构造于 .../chat/openai-compatible-chat-language-model.ts:139-189getArgs):

字段 说明
model 模型 id(api.id
messages convertToOpenAICompatibleChatMessages(prompt) 产出:system / usercontent 支持 textimage_url)/ assistant(含 tool_calls、Copilot 专有 reasoning_textreasoning_opaque)/ toolconvert-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/anthropicapi.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 组装与钩子触发

只读分析,未修改任何代码。

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions