OpenCode 向 GitHub Copilot 发送请求的流程、Header 与 Body 分析(2026-07-20)
本报告聚焦 GitHub Copilot provider,逐一梳理用户关心的三部分:① GitHub 身份验证与 Token 交换流程 、② 客户端发送给 Copilot 的 Request Header 、③ 客户端发送给 Copilot 的 Request Body 。所有结论均以仓库实际代码为准,并标注 path:line。
0. 本次上游同步说明
使用 GitHub MCP(list_commits)检测到上游 anomalyco/opencode 的 dev 领先本 fork 11 个提交 (本地 HEAD/origin/dev = 317044017e,上游最新 = 7985c2066a)。由于本 fork 的 dev 因 fork 专属提交(b1e2d230de 新增每日同步工作流)已与上游分叉,无法快进,故以 merge 方式同步(合并提交 0e58547962,无冲突)。同步进来的提交:
SHA
提交
7985c2066a
fix(app): show keybind tooltips on prompt input controls (anomalyco#37824 )
67caf894e0
fix(opencode): increase OpenAI header timeout (anomalyco#37770 )
78587c141b
fix(app): restore question pager segments (anomalyco#37575 )
f5573281ca
chore: generate
4817228f96
docs(go): update Kimi K3 request estimates (anomalyco#37725 )
5a5117b36c
Merge branch 'dev'
6a288426b8
go: support openai route
573ac85983
feat(desktop): align new session project picker (anomalyco#37461 )
1221bbbe14
Merge branch 'dev'
6bea0f4fa4
zen: grok 4.5 adjusted price
cc34084dfe
feat(desktop): prevent overlapping composer borders (anomalyco#37490 )
对 Copilot 请求流程 / Header 的影响:无。 唯一触及 provider.ts 的是 67caf894e0,它把 OPENAI_HEADER_TIMEOUT_DEFAULT 由 10_000 改为 300_000(provider.ts:35),但该超时只应用于 openai provider (provider.ts:208),github-copilot(provider.ts:225-239)并未使用,故对 Copilot 无影响。其余提交集中在 app/desktop/go/docs/zen,均未触及 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/provider/auth.ts、packages/opencode/src/auth/index.ts
OAuth authorize/callback、auth.json 持久化
请求准备(编排)
packages/opencode/src/session/llm/request.ts
组装 system/messages/tools/params,触发 hooks,生成基础 Header
Provider 装配(v2)
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 构造与发送
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、provider.ts:234-236)。
sequenceDiagram
autonumber
participant S as Session/Prompt
participant R as request.ts (prepare)
participant PL as copilot.ts hooks
participant SDK as AI SDK 语言模型
participant F as loader.fetch (copilot.ts)
participant API as api.githubcopilot.com
S->>R: system / messages / tools / params
R->>PL: trigger chat.params & chat.headers
PL-->>R: params(maxOutputTokens 等) + headers(X-GitHub-Api-Version 等)
R->>SDK: streamText(model, messages, headers)
SDK->>SDK: getArgs() 构造 body (JSON)
SDK->>F: postJsonToApi(url, combineHeaders, body)
F->>F: 注入 Authorization/Openai-Intent/x-initiator/UA;删除 x-api-key
F->>API: POST /chat/completions | /responses | /v1/messages (SSE)
API-->>SDK: text/event-stream 流式响应
Loading
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:361、xai.ts:492、snowflake-cortex.ts:443 都有 expires < Date.now() + grant_type: refresh_token 的刷新逻辑)。
实际用作 Bearer 的是 refresh 字段(长期 GitHub token),而非 access:
模型清单请求:Authorization: \Bearer ${auth.refresh}`(copilot.ts:74`)
聊天/推理请求:Authorization: \Bearer ${info.refresh}`(copilot.ts:164`)
存储时 refresh 与 access 都等于 data.access_token,expires: 0(copilot.ts:296-298),且无人读取 expires 触发刷新,故 token 原样长期使用。
2.2 客户端标识与常量(copilot.ts:9-28)
OAuth App client_id = "Ov23li8tweQw6odWQebz"
API 版本 API_VERSION = "2026-06-01"(用于 X-GitHub-Api-Version)
轮询安全余量 OAUTH_POLLING_SAFETY_MARGIN_MS = 3000
域名:默认 github.com;企业版 normalizeDomain(enterpriseUrl),API base 变为 https://copilot-api.<domain>;公有云为 https://api.githubcopilot.com
2.3 设备码登录流程(authorize,copilot.ts:222-336)
选择部署类型 :GitHub.com 或 GitHub Enterprise(企业版需再输入 URL/域名,带校验)(copilot.ts:186-221)。
发起设备码 :POST https://<domain>/login/device/code(copilot.ts:234-245)
Header:Accept: application/json、Content-Type: application/json、User-Agent: opencode/<version>
Body:{ "client_id": "Ov23li8tweQw6odWQebz", "scope": "read:user" }
返回: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)
Body:{ "client_id", "device_code", "grant_type": "urn:ietf:params:oauth:grant-type:device_code" }
authorization_pending → 按 interval + 3s 继续轮询(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 → provider/auth.ts:211-220 → auth/index.ts:73-81):
写入 ~/.local/share/opencode/auth.json(Global.Path.data/auth.json,权限 0o600),结构:
2.4 请求期的 Token 注入(loader,copilot.ts:96-181)
loader(getAuth) 返回 { apiKey: "", fetch },即一个 provider 级自定义 fetch 。
getAuth = () => auth.get("github-copilot")(provider.ts:1553-1558)——只读取存储的 token,不做刷新 。
每次请求进入自定义 fetch 后:读取 info.refresh 作为 Bearer 注入(详见第 3 节)。apiKey: "" 使 AI SDK 基础层写入的空 Authorization 最终被自定义 fetch 覆盖为真实 token。
sequenceDiagram
autonumber
participant U as 用户
participant OC as opencode (copilot.ts)
participant GH as github.com (OAuth)
participant API as api.githubcopilot.com
U->>OC: auth login → 选 GitHub Copilot(github.com / 企业版)
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
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: 写 auth.json {refresh = access = access_token, expires: 0}
Note over OC,API: 不再二次换取 Copilot token,无刷新
OC->>API: 后续请求 Authorization: Bearer <refresh>
Loading
3. 客户端发送给 Copilot 的 Request Header
最终 Header 由四层叠加而成,自定义 fetch(第 4 层)拥有最终决定权 。
3.1 模型清单请求 GET {base}/models(copilot.ts:70-79 + models.ts:213-226)
Header
取值来源
说明
Authorization
Bearer ${auth.refresh}(copilot.ts:74)
GitHub OAuth token
User-Agent
opencode/${InstallationVersion}(copilot.ts:75)
客户端标识
X-GitHub-Api-Version
2026-06-01(copilot.ts:76)
Copilot API 版本
...provider.options.headers
既有 provider 配置(copilot.ts:73)
透传
该请求带 5s 超时(models.ts:220),失败时回退为「用内置模型 + 直连 base」。
3.2 聊天 / 推理请求(/chat/completions、/responses、/v1/messages)
第 1 层 — request.ts 基础 Header(request.ts:187-204,非 opencode provider 分支)
Header
取值来源
说明
x-session-affinity
input.sessionID(request.ts:197)
会话粘性
X-Session-Id
input.sessionID(request.ts:198)
会话 ID
x-parent-session-id
input.parentSessionID(request.ts:199,子代理时)
父会话 ID
User-Agent
opencode/${InstallationVersion}(request.ts:200,USER_AGENT=request.ts:18)
客户端标识
...input.model.headers
模型级 Header(request.ts:202)
透传
第 2 层 — chat.headers hook(copilot.ts:360-412)
Header
取值来源
说明
X-GitHub-Api-Version
2026-06-01(copilot.ts:363)
恒定注入
X-Interaction-Type
agent-session-name-generation(copilot.ts:365)
仅当 agent === "title"(生成会话标题)
anthropic-beta
interleaved-thinking-2025-05-14(copilot.ts:369)
仅当模型走 @ai-sdk/anthropic(messages 端点)
x-initiator
agent(copilot.ts:394/411)
命中压缩(compaction)续写或子代理会话时置为 agent
第 3 层 — AI SDK 兼容 Provider 基础 Header(copilot-provider.ts:60-66)
Header
取值来源
说明
Authorization
Bearer ${apiKey}(copilot-provider.ts:62)
apiKey="",占位,随后被覆盖
User-Agent(后缀)
withUserAgentSuffix(..., "ai-sdk/openai-compatible/0.1.0")(copilot-provider.ts:66)
AI SDK 标识后缀
Content-Type
application/json(由 postJsonToApi 添加)
JSON body
...options.headers
上面 1/2 层合并的 headers
透传(combineHeaders,chat...:323、responses...:406)
第 4 层 — 自定义 fetch 最终覆盖(copilot.ts:160-173)——真正写到线上的鉴权/意图 Header
Header
取值来源
说明
Authorization
Bearer ${info.refresh}(copilot.ts:164)
真正的 GitHub OAuth token
User-Agent
opencode/${InstallationVersion}(copilot.ts:163)
覆盖 AI SDK 的 UA
Openai-Intent
conversation-edits(copilot.ts:165)
Copilot 侧意图标识
x-initiator
agent / user(copilot.ts:161)
由请求体推断(见 3.3)
Copilot-Vision-Request
true(copilot.ts:168-170)
仅当请求体含图片
x-api-key(删除)
delete headers["x-api-key"](copilot.ts:172)
去除 Anthropic 风格 key
authorization(删除小写)
delete headers["authorization"](copilot.ts:173)
防止大小写重复
综合结果(发往 api.githubcopilot.com 的有效 Header):
Authorization: Bearer <gho token>、User-Agent: opencode/<version>、Openai-Intent: conversation-edits、X-GitHub-Api-Version: 2026-06-01、x-initiator: user|agent、Content-Type: application/json、X-Session-Id/x-session-affinity: <sessionID>,以及按需的 Copilot-Vision-Request: true、anthropic-beta、X-Interaction-Type、x-parent-session-id。
3.3 x-initiator 的判定逻辑(copilot.ts:107-158)
自定义 fetch 会解析请求体,区分「用户主动」与「代理自动」:
Completions API (body.messages 且 url 含 completions):末条消息 role !== "user" 或为合成图片消息 → agent。
Responses API (body.input):同理判断 input 末元素。
Messages API (body.messages,Anthropic):末条非「纯 user 文本」→ agent。
isVision :任一消息含 image_url / input_image / image(含嵌套在 tool_result 内的图片)→ 追加 Copilot-Vision-Request: true。
4. 客户端发送给 Copilot 的 Request Body
请求体由 AI SDK 语言模型的 getArgs() 构造,params(温度/topP/最大输出等)来自 request.ts:123-131 与 chat.params hook。
4.1 chat.params hook 的 Copilot 特化(copilot.ts:340-354)
模型 id 含 gpt → maxOutputTokens = undefined(对齐官方 Copilot CLI,不传 max_tokens)。
走 @ai-sdk/anthropic(messages 端点)→ options.toolStreaming = false(Copilot 的 /v1/messages shim 拒绝 GA 的 eager_input_streaming 字段)。
4.2 Chat Completions body — POST {base}/chat/completions(openai-compatible-chat-language-model.ts:139-190)
流式 (doStream,...:308-314)追加:"stream": true,并在 includeUsage 时加 "stream_options": { "include_usage": true }。
messages 为 OpenAI 兼容结构:system / user / assistant(含 tool_calls)/ tool(工具结果)等 role,content 可为字符串或 text/image_url 分块。
_noop 工具占位 (request.ts:159-175):当当前无启用工具但历史里存在工具调用时,Copilot 要求 body 里必须带 tools 字段,故注入一个「永不调用」的占位工具。
4.3 Responses body — POST {base}/responses(openai-responses-language-model.ts)
GPT-5+(除 mini)默认走此端点,body 关键字段:model、input(由 convert-to-openai-responses-input.ts 生成)、instructions(系统提示,:287)、tools、store(默认 true,:206)、include(如 reasoning.encrypted_content,:214)、reasoning: { effort, summary }(:297)、以及 max_output_tokens/temperature/top_p 等标准项;流式同样 POST /responses(:396、:782)。
4.4 Messages body — POST {base}/v1/messages(@ai-sdk/anthropic)
对 supported_endpoints 含 /v1/messages 的模型(models.ts:92),使用 Anthropic Messages 协议格式(system、messages、tools、max_tokens、thinking 等),并按 4.1 关闭 toolStreaming。
5. 鉴权机制小结
provider
凭据获取
注入位置
是否刷新
github-copilot
GitHub OAuth 设备码流程 (scope: read:user),token 直接复用
自定义 fetch:Authorization: Bearer ${info.refresh}(copilot.ts:164)
否 (无 refresh 实现,expires:0 不触发换取)
对比参照(同仓其它 provider 均实现了刷新):openai/codex.ts:361、xai.ts:492、snowflake-cortex.ts:443。这反衬出 Copilot「直接复用 GitHub token」的设计是有意为之。
6. 关键代码位置索引
认证/Token/Header/自定义 fetch:packages/opencode/src/plugin/github-copilot/copilot.ts:9,21-28,70-79,96-181,222-336,340-354,360-412
模型清单/端点判定:packages/opencode/src/plugin/github-copilot/models.ts:92-111,213-226
OAuth 存储:packages/opencode/src/provider/auth.ts:188-221、packages/opencode/src/auth/index.ts:14-21,73-81
Loader 装配/端点注册/超时:packages/opencode/src/provider/provider.ts:225-239,1543-1562(+ 35,208 的 OpenAI 超时对比)
请求编排/基础 Header/params/_noop:packages/opencode/src/session/llm/request.ts:18,123-146,159-175,181-205
Provider 装配(v2):packages/core/src/plugin/provider/github-copilot.ts:20-49
OpenAI 兼容 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-205,305-329
Responses body/发送:packages/core/src/github-copilot/responses/openai-responses-language-model.ts:206-297,396,782
本报告为只读分析,未修改任何业务代码。分支:nickhou1983-copilot-auth-headers-body,分析基线合并提交 0e58547962。
OpenCode 向 GitHub Copilot 发送请求的流程、Header 与 Body 分析(2026-07-20)
0. 本次上游同步说明
使用 GitHub MCP(
list_commits)检测到上游anomalyco/opencode的dev领先本 fork 11 个提交(本地HEAD/origin/dev=317044017e,上游最新 =7985c2066a)。由于本 fork 的dev因 fork 专属提交(b1e2d230de新增每日同步工作流)已与上游分叉,无法快进,故以 merge 方式同步(合并提交0e58547962,无冲突)。同步进来的提交:7985c2066a67caf894e078587c141bf5573281ca4817228f965a5117b36c6a288426b8573ac859831221bbbe146bea0f4fa4cc34084dfe对 Copilot 请求流程 / Header 的影响:无。 唯一触及
provider.ts的是67caf894e0,它把OPENAI_HEADER_TIMEOUT_DEFAULT由10_000改为300_000(provider.ts:35),但该超时只应用于openaiprovider(provider.ts:208),github-copilot(provider.ts:225-239)并未使用,故对 Copilot 无影响。其余提交集中在 app/desktop/go/docs/zen,均未触及 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/provider/auth.ts、packages/opencode/src/auth/index.tsauth.json持久化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/completionsbody 构造与发送packages/core/src/github-copilot/responses/openai-responses-language-model.ts/responsesbody 构造与发送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、provider.ts:234-236)。2. GitHub 身份验证与 Token 交换流程
2.1 核心结论
关键证据:
copilot_internal//v2/token端点调用;github-copilot的auth.methods里只有type: "oauth",无refresh实现(对比openai/codex.ts:361、xai.ts:492、snowflake-cortex.ts:443都有expires < Date.now()+grant_type: refresh_token的刷新逻辑)。refresh字段(长期 GitHub token),而非access:Authorization: \Bearer ${auth.refresh}`(copilot.ts:74`)Authorization: \Bearer ${info.refresh}`(copilot.ts:164`)refresh与access都等于data.access_token,expires: 0(copilot.ts:296-298),且无人读取expires触发刷新,故 token 原样长期使用。2.2 客户端标识与常量(
copilot.ts:9-28)client_id = "Ov23li8tweQw6odWQebz"API_VERSION = "2026-06-01"(用于X-GitHub-Api-Version)OAUTH_POLLING_SAFETY_MARGIN_MS = 3000github.com;企业版normalizeDomain(enterpriseUrl),API base 变为https://copilot-api.<domain>;公有云为https://api.githubcopilot.com2.3 设备码登录流程(
authorize,copilot.ts:222-336)GitHub.com或GitHub Enterprise(企业版需再输入 URL/域名,带校验)(copilot.ts:186-221)。POST https://<domain>/login/device/code(copilot.ts:234-245)Accept: application/json、Content-Type: application/json、User-Agent: opencode/<version>{ "client_id": "Ov23li8tweQw6odWQebz", "scope": "read:user" }verification_uri、user_code、device_code、intervalverification_uri并输入Enter code: <user_code>(copilot.ts:258-261)。POST https://<domain>/login/oauth/access_token(copilot.ts:264-276){ "client_id", "device_code", "grant_type": "urn:ietf:params:oauth:grant-type:device_code" }authorization_pending→ 按interval + 3s继续轮询(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→provider/auth.ts:211-220→auth/index.ts:73-81):写入
~/.local/share/opencode/auth.json(Global.Path.data/auth.json,权限0o600),结构:{ "type": "oauth", "refresh": "<gho_...>", "access": "<gho_...>", "expires": 0, "enterpriseUrl": "<可选>" }2.4 请求期的 Token 注入(
loader,copilot.ts:96-181)loader(getAuth)返回{ apiKey: "", fetch },即一个 provider 级自定义fetch。getAuth = () => auth.get("github-copilot")(provider.ts:1553-1558)——只读取存储的 token,不做刷新。fetch后:读取info.refresh作为 Bearer 注入(详见第 3 节)。apiKey: ""使 AI SDK 基础层写入的空Authorization最终被自定义 fetch 覆盖为真实 token。sequenceDiagram autonumber participant U as 用户 participant OC as opencode (copilot.ts) participant GH as github.com (OAuth) participant API as api.githubcopilot.com U->>OC: auth login → 选 GitHub Copilot(github.com / 企业版) 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 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: 写 auth.json {refresh = access = access_token, expires: 0} Note over OC,API: 不再二次换取 Copilot token,无刷新 OC->>API: 后续请求 Authorization: Bearer <refresh>3. 客户端发送给 Copilot 的 Request Header
最终 Header 由四层叠加而成,自定义 fetch(第 4 层)拥有最终决定权。
3.1 模型清单请求
GET {base}/models(copilot.ts:70-79+models.ts:213-226)AuthorizationBearer ${auth.refresh}(copilot.ts:74)User-Agentopencode/${InstallationVersion}(copilot.ts:75)X-GitHub-Api-Version2026-06-01(copilot.ts:76)...provider.options.headerscopilot.ts:73)3.2 聊天 / 推理请求(
/chat/completions、/responses、/v1/messages)第 1 层 —
request.ts基础 Header(request.ts:187-204,非 opencode provider 分支)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:200,USER_AGENT=request.ts:18)...input.model.headersrequest.ts:202)第 2 层 —
chat.headershook(copilot.ts:360-412)X-GitHub-Api-Version2026-06-01(copilot.ts:363)X-Interaction-Typeagent-session-name-generation(copilot.ts:365)agent === "title"(生成会话标题)anthropic-betainterleaved-thinking-2025-05-14(copilot.ts:369)@ai-sdk/anthropic(messages 端点)x-initiatoragent(copilot.ts:394/411)第 3 层 — AI SDK 兼容 Provider 基础 Header(
copilot-provider.ts:60-66)AuthorizationBearer ${apiKey}(copilot-provider.ts:62)apiKey="",占位,随后被覆盖User-Agent(后缀)withUserAgentSuffix(..., "ai-sdk/openai-compatible/0.1.0")(copilot-provider.ts:66)Content-Typeapplication/json(由postJsonToApi添加)...options.headerscombineHeaders,chat...:323、responses...:406)第 4 层 — 自定义
fetch最终覆盖(copilot.ts:160-173)——真正写到线上的鉴权/意图 HeaderAuthorizationBearer ${info.refresh}(copilot.ts:164)User-Agentopencode/${InstallationVersion}(copilot.ts:163)Openai-Intentconversation-edits(copilot.ts:165)x-initiatoragent/user(copilot.ts:161)Copilot-Vision-Requesttrue(copilot.ts:168-170)x-api-key(删除)delete headers["x-api-key"](copilot.ts:172)authorization(删除小写)delete headers["authorization"](copilot.ts:173)综合结果(发往
api.githubcopilot.com的有效 Header):Authorization: Bearer <gho token>、User-Agent: opencode/<version>、Openai-Intent: conversation-edits、X-GitHub-Api-Version: 2026-06-01、x-initiator: user|agent、Content-Type: application/json、X-Session-Id/x-session-affinity: <sessionID>,以及按需的Copilot-Vision-Request: true、anthropic-beta、X-Interaction-Type、x-parent-session-id。3.3
x-initiator的判定逻辑(copilot.ts:107-158)自定义 fetch 会解析请求体,区分「用户主动」与「代理自动」:
body.messages且 url 含completions):末条消息role !== "user"或为合成图片消息 →agent。body.input):同理判断input末元素。body.messages,Anthropic):末条非「纯 user 文本」→agent。isVision:任一消息含image_url/input_image/image(含嵌套在tool_result内的图片)→ 追加Copilot-Vision-Request: true。4. 客户端发送给 Copilot 的 Request Body
请求体由 AI SDK 语言模型的
getArgs()构造,params(温度/topP/最大输出等)来自request.ts:123-131与chat.paramshook。4.1
chat.paramshook 的 Copilot 特化(copilot.ts:340-354)gpt→maxOutputTokens = undefined(对齐官方 Copilot CLI,不传max_tokens)。@ai-sdk/anthropic(messages 端点)→options.toolStreaming = false(Copilot 的/v1/messagesshim 拒绝 GA 的eager_input_streaming字段)。4.2 Chat Completions body —
POST {base}/chat/completions(openai-compatible-chat-language-model.ts:139-190){ "model": "<api.id>", "user": "<可选>", "max_tokens": <maxOutputTokens 或 undefined(gpt)>, "temperature": <number>, "top_p": <number>, "frequency_penalty": <number>, "presence_penalty": <number>, "response_format": { "type": "json_schema|json_object" }, // 可选 "stop": [...], // 可选 "seed": <number>, // 可选 "reasoning_effort": "<low|medium|high...>", // 可选 "verbosity": "<...>", // 可选 "messages": [ /* convertToOpenAICompatibleChatMessages(prompt) */ ], "tools": [ /* function tools */ ], "tool_choice": <...>, "thinking_budget": <number> // 可选 }doStream,...:308-314)追加:"stream": true,并在includeUsage时加"stream_options": { "include_usage": true }。messages为 OpenAI 兼容结构:system/user/assistant(含tool_calls)/tool(工具结果)等 role,content可为字符串或text/image_url分块。_noop工具占位(request.ts:159-175):当当前无启用工具但历史里存在工具调用时,Copilot 要求 body 里必须带tools字段,故注入一个「永不调用」的占位工具。4.3 Responses body —
POST {base}/responses(openai-responses-language-model.ts)GPT-5+(除 mini)默认走此端点,body 关键字段:
model、input(由convert-to-openai-responses-input.ts生成)、instructions(系统提示,:287)、tools、store(默认true,:206)、include(如reasoning.encrypted_content,:214)、reasoning: { effort, summary }(:297)、以及max_output_tokens/temperature/top_p等标准项;流式同样POST /responses(:396、:782)。4.4 Messages body —
POST {base}/v1/messages(@ai-sdk/anthropic)对
supported_endpoints含/v1/messages的模型(models.ts:92),使用 Anthropic Messages 协议格式(system、messages、tools、max_tokens、thinking等),并按 4.1 关闭toolStreaming。5. 鉴权机制小结
scope: read:user),token 直接复用Authorization: Bearer ${info.refresh}(copilot.ts:164)refresh实现,expires:0不触发换取)对比参照(同仓其它 provider 均实现了刷新):
openai/codex.ts:361、xai.ts:492、snowflake-cortex.ts:443。这反衬出 Copilot「直接复用 GitHub token」的设计是有意为之。6. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts:9,21-28,70-79,96-181,222-336,340-354,360-412packages/opencode/src/plugin/github-copilot/models.ts:92-111,213-226packages/opencode/src/provider/auth.ts:188-221、packages/opencode/src/auth/index.ts:14-21,73-81packages/opencode/src/provider/provider.ts:225-239,1543-1562(+35,208的 OpenAI 超时对比)_noop:packages/opencode/src/session/llm/request.ts:18,123-146,159-175,181-205packages/core/src/plugin/provider/github-copilot.ts:20-49packages/core/src/github-copilot/copilot-provider.ts:52-97packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:87-205,305-329packages/core/src/github-copilot/responses/openai-responses-language-model.ts:206-297,396,782本报告为只读分析,未修改任何业务代码。分支:
nickhou1983-copilot-auth-headers-body,分析基线合并提交0e58547962。