OpenCode → GitHub Copilot 请求分析:身份验证 / Token 交换、Request Header 与 Request Body(2026-08-03)
只读分析,基于当前仓库 dev 分支代码(已与上游 anomalyco/opencode 完成同步)。所有结论以仓库实际代码为准,并标注 path:line。
0. 本次同步变更提示
本次从上游 anomalyco/opencode 快进同步进来 3 个提交:
SHA
说明
是否影响 Copilot 请求流程
1882c33827
fix gemini reasoning tokens not counted
否(仅改 packages/console/app/src/routes/zen/util/provider/google.ts,属 console/zen 计费侧)
01624c8c87
chore: generate
否(生成物)
0891ecd8e0
docs(go): update DeepSeek privacy policy (anomalyco#40120 )
否(文档 / i18n)
结论:本次同步未触及 GitHub Copilot 的鉴权、Header 或 Body 构造逻辑。 以下分析对同步前后一致有效。
1. 关键结论(TL;DR)
鉴权用的是 GitHub OAuth「设备码流程」拿到的 access token,并被“原样”作为 Authorization: Bearer 直连 https://api.githubcopilot.com。
不存在二次 Token 交换 :仓库中没有 copilot_internal/v2/token 之类的换取端点,github-copilot 的 loader 也没有任何 refresh 逻辑 (与 xai.ts / codex.ts / snowflake-cortex.ts 形成鲜明对比)。token 以 expires: 0 落盘,长期复用。
Header 分三层叠加 :request.ts 通用层 → chat.headers 插件钩子层 → loader.fetch 最终注入层(Authorization / User-Agent / Openai-Intent 在这里定型,并删除小写 authorization / x-api-key)。
Body 由 AI SDK 按端点构造 ,Copilot 三条协议线:/chat/completions(OpenAI 兼容)、/responses(GPT‑5 类)、/v1/messages(Anthropic 线)。chat.params 会对 gpt 模型去掉 max_tokens、对 anthropic 线关闭 toolStreaming。
OpenCode 不发送官方编辑器插件那套 Copilot-Integration-Id / Editor-Version / Editor-Plugin-Version 头 ,仅用 User-Agent: opencode/<version> + X-GitHub-Api-Version + x-initiator + Openai-Intent 等最小集合标识自己。
2. GitHub 身份验证与 Token 交换流程
核心文件:packages/opencode/src/plugin/github-copilot/copilot.ts
凭据存储:packages/opencode/src/auth/index.ts
2.1 常量与端点
项
值
位置
OAuth App Client ID
Ov23li8tweQw6odWQebz
copilot.ts:9
API 版本
2026-06-01
copilot.ts:10
设备码端点
https://{domain}/login/device/code
copilot.ts:21
令牌端点
https://{domain}/login/oauth/access_token
copilot.ts:22
推理 API Base
https://api.githubcopilot.com(企业版:https://copilot-api.{enterprise})
copilot.ts:26-28
OAuth Scope
read:user
copilot.ts:244
Grant Type
urn:ietf:params:oauth:grant-type:device_code
copilot.ts:274
domain 默认 github.com;选择「GitHub Enterprise」时由用户输入的 URL 规范化而来(normalizeDomain,copilot.ts:15-17,225-230)。
2.2 时序图
sequenceDiagram
autonumber
participant U as 用户
participant OC as OpenCode (copilot.ts)
participant GH as github.com (OAuth)
participant CAPI as api.githubcopilot.com
U->>OC: 选择 /login GitHub Copilot(github.com 或 Enterprise)
OC->>GH: POST /login/device/code {client_id, scope:"read:user"}
GH-->>OC: {device_code, user_code, verification_uri, interval}
OC-->>U: 展示 user_code + 打开 verification_uri
loop 轮询直到授权(间隔 interval + 3s 安全余量)
OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type}
GH-->>OC: authorization_pending / slow_down / {access_token}
end
Note over OC: 落盘 auth.json:{refresh=access_token, access=access_token, expires:0}
OC->>CAPI: GET /models (Authorization: Bearer <access_token>, X-GitHub-Api-Version, User-Agent)
CAPI-->>OC: 模型清单 → 决定 chat/responses/messages 端点与定价
OC->>CAPI: POST /chat/completions | /responses | /v1/messages(推理请求,仍用同一 Bearer)
Loading
2.3 关键步骤说明
发起设备码 (copilot.ts:234-256):POST /login/device/code,body {client_id, scope:"read:user"},请求头带 Accept: application/json、Content-Type: application/json、User-Agent: opencode/<version>。
轮询换取 access token (copilot.ts:262-334):POST /login/oauth/access_token。处理 authorization_pending(继续轮询)、slow_down(按 RFC 8628 §3.5 增加 5s 或采用服务端返回的 interval)、其它 error(失败)。轮询间隔统一加 OAUTH_POLLING_SAFETY_MARGIN_MS = 3000(copilot.ts:14)以规避时钟漂移。
落盘 (copilot.ts:286-305):成功后返回 {type:"success", refresh: access_token, access: access_token, expires: 0};企业版额外写入 enterpriseUrl。凭据经 Auth.set 写入 ~/.local/share/opencode/auth.json,权限 0o600(auth/index.ts:73-81)。Oauth 结构见 auth/index.ts:14-21(refresh / access / expires / accountId? / enterpriseUrl?)。
拉取模型清单 (copilot.ts:62-92 → models.ts:213-256):以 Authorization: Bearer <access> + X-GitHub-Api-Version + User-Agent 请求 {base}/models,据 supported_endpoints 决定每个模型走 messages / responses / chat(models.ts:92-99),并换算定价、能力、变体。
2.4 「Token 交换」的真相:无二次换取
很多 Copilot 客户端会把 GitHub OAuth token 再向 https://api.github.com/copilot_internal/v2/token 换一个短期 Copilot token。OpenCode 没有这一步 :
全仓 grep copilot_internal / v2/token / Copilot-Integration-Id 均无命中。
github-copilot 的 loader(copilot.ts:96-181)拿到 getAuth() 后直接 Authorization: Bearer ${info.access}(copilot.ts:164),既不判断 expires 也不 refresh。
对照组:xai.ts:462-524、openai/codex.ts:322-388、snowflake-cortex.ts:286-366 都实现了 refreshAccessToken + 单飞刷新;github-copilot 完全没有 。这解释了为何落盘时 expires: 0 —— 该字段对 Copilot 分支不参与任何过期判定,GitHub OAuth token 被长期直用。
影响:凭据轮换/失效完全依赖 GitHub 侧;一旦该 access token 失效,只能重新走 /login 设备码流程。
3. 客户端发送给 GitHub Copilot 的 Request Header
3.1 叠加管线(三层)
flowchart LR
A["request.ts 通用基座头<br/>x-session-affinity / X-Session-Id / User-Agent"]
--> B["chat.headers 钩子(copilot.ts:360-412)<br/>X-GitHub-Api-Version / x-initiator / anthropic-beta / X-Interaction-Type"]
--> C["AI SDK config.headers()<br/>+ withUserAgentSuffix ai-sdk/openai-compatible/VERSION(copilot-provider.ts:66)"]
--> D["loader.fetch 最终注入(copilot.ts:160-178)<br/>Authorization / User-Agent / Openai-Intent / Copilot-Vision-Request;删除小写 authorization、x-api-key"]
--> E[[api.githubcopilot.com]]
Loading
第 1 层 (request.ts:187-204):非 opencode provider 走 else 分支,注入 x-session-affinity、X-Session-Id、可选 x-parent-session-id、User-Agent(USER_AGENT = opencode/${InstallationVersion},request.ts:18),再并入 input.model.headers 与钩子产出的 headers。
第 2 层 (chat.headers,copilot.ts:360-412):仅当 providerID 含 github-copilot 时生效。
第 3 层 (loader.fetch,copilot.ts:100-179):真正发出前的最终改写,Authorization 在此定型。
3.2 最终发出的 Header 明细
Header
取值
来源(代码位置)
作用
Authorization
Bearer <GitHub OAuth access token>
copilot.ts:164(推理)/ copilot.ts:74(/models)
鉴权凭据(原样直用,无二次交换)
User-Agent
opencode/<version>
copilot.ts:163;request.ts:18;version.ts:6
客户端标识(最终以 opencode 版本为准,覆盖 AI SDK 后缀)
X-GitHub-Api-Version
2026-06-01
copilot.ts:363(钩子)/ copilot.ts:76(/models)
指定 Copilot API 版本
x-initiator
agent 或 user
copilot.ts:161(body 探测)+ copilot.ts:394,411(compaction/子会话强制 agent)
标记本轮由用户还是 agent 发起(计费/风控口径)
Openai-Intent
conversation-edits
copilot.ts:165
声明调用意图
Copilot-Vision-Request
true(仅含图片时)
copilot.ts:168-170
声明多模态/视觉请求
X-Interaction-Type
agent-session-name-generation(仅 title agent)
copilot.ts:364-366
标记「会话标题生成」这类轻量交互
anthropic-beta
interleaved-thinking-2025-05-14(仅 @ai-sdk/anthropic 线)
copilot.ts:368-370
走 /v1/messages 时开启交错思考
x-session-affinity / X-Session-Id
当前 sessionID
request.ts:197-198
会话亲和 / 路由
x-parent-session-id
父会话 ID(子会话时)
request.ts:199
子会话溯源
清理动作(copilot.ts:172-173):发出前 delete headers["x-api-key"] 与 delete headers["authorization"](小写),避免其它 provider 的鉴权头串入,确保只保留大写 Authorization。
注:AI SDK 的 config.headers() 会用 withUserAgentSuffix 追加 ai-sdk/openai-compatible/<VERSION>(copilot-provider.ts:66),但随后被 loader.fetch 用 opencode/<version> 覆盖(copilot.ts:163),故最终 User-Agent 以 opencode 版本为准。
3.3 x-initiator 的判定(body 探测)
loader.fetch 会解析 init.body,按三种 API 形态判断 isAgent / isVision(copilot.ts:107-158):
Completions (body.messages + url 含 completions):最后一条非 user,或为「合成附件提示」→ agent;任一消息含 image_url → vision。
Responses (body.input):最后一项非 user 或合成附件 → agent;含 input_image → vision。
Messages (body.messages,Anthropic 线):最后一条不是「纯 user 文本」或为合成附件 → agent;含 image(含嵌套在 tool_result 里的图片)→ vision。
此外 chat.headers 钩子会把「压缩续写(compaction)」与「子会话(有 parentID)」强制标为 x-initiator: agent(copilot.ts:385-411)。合成附件提示常量为 MessageV2.SYNTHETIC_ATTACHMENT_PROMPT(copilot.ts:31-43)。
4. 客户端发送给 GitHub Copilot 的 Request Body
4.1 端点 / 协议路由
由 /models 返回的 supported_endpoints 决定(models.ts:92-111):
Copilot 端点
AI SDK 包(api.npm)
触发条件
最终 URL
messages
@ai-sdk/anthropic
supported_endpoints 含 /v1/messages
{base}/v1/messages(api.url = {base}/v1,models.ts:109)
responses
@ai-sdk/github-copilot
含 /responses
{base}/responses
chat
@ai-sdk/github-copilot
含 /chat/completions
{base}/chat/completions
对 @ai-sdk/github-copilot,GPT‑5 类模型(非 gpt-5-mini)优先走 Responses、否则 Chat(core/src/plugin/provider/github-copilot.ts:42-48);并隐藏与 Responses 冲突的 gpt-5-chat-latest 别名(同文件 :9-19)。
4.2 Chat Completions 请求体
构造于 packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:139-189(getArgs):
字段
来源
model
this.modelId
messages
convertToOpenAICompatibleChatMessages(prompt)(:179)
max_tokens
maxOutputTokens(gpt 模型会被 chat.params 置空,见 4.5)
temperature / top_p
标准采样参数
frequency_penalty / presence_penalty
惩罚项
response_format
JSON 时 json_schema(支持结构化输出)或 json_object(:153-165)
stop / seed
停止序列 / 随机种子
reasoning_effort / verbosity
由 provider options 解析(:175-176)
thinking_budget
思考预算(:186)
tools / tool_choice
prepareTools(...) 产出(:130-137,182-183)
user
provider options(:145)
流式(doStream,:308-314)在上述基础上追加 stream: true,并在严格兼容模式下带 stream_options: { include_usage: true };请求发往 POST {base}/chat/completions(:203-204,320-321)。
4.3 Responses 请求体
构造于 packages/core/src/github-copilot/responses/openai-responses-language-model.ts:254-388:model、input(非 messages,为 Responses 事件项数组)、temperature、top_p、max_output_tokens、text(format/verbosity)、store(默认 true ,:206,285)、user、instructions、service_tier、include、reasoning{ effort, summary }(:297-299)、tools、tool_choice,流式 stream: true(:788)。
4.4 消息形态与 Messages(Anthropic)线
系统提示注入 :非 OpenAI‑OAuth / 非 workflow 场景,系统提示以 role:"system" 前置到消息数组(request.ts:101-112);首段+其余会被压成两段(request.ts:74-78)。
Anthropic 线 :api.npm === "@ai-sdk/anthropic" 时由 @ai-sdk/anthropic 构造 /v1/messages 请求体,并叠加 anthropic-beta: interleaved-thinking-2025-05-14 头(copilot.ts:368-370)。
4.5 针对 Copilot 的 Body 级调整
chat.params 钩子(copilot.ts:340-353):
gpt 模型去掉 maxOutputTokens (output.maxOutputTokens = undefined)——对齐官方 GitHub Copilot CLI 行为(:344-346)。
Anthropic 线关闭 toolStreaming (:351-353)——Copilot 的 /v1/messages shim 会拒绝工具定义里的 eager_input_streaming 字段("Extra inputs are not permitted"),故关闭以避免注入。
request.ts 侧的 Copilot 专属处理:当无启用工具但历史里存在工具调用回放时,注入一个 _noop 占位工具(request.ts:159-175),因为 Copilot 在回放工具调用时要求 tools 字段存在。小工具模型(标题生成)用 experimental.provider.small_model 从 UTILITY_MODELS(gpt-5.4-nano / gpt-4.1 / gpt-4o / gpt-4o-mini)中挑选(copilot.ts:11,355-359)。
5. 关键代码位置索引
主题
文件:行
OAuth 设备码流程 / 常量 / loader.fetch / 钩子
packages/opencode/src/plugin/github-copilot/copilot.ts(9-28, 96-181, 222-336, 340-412)
凭据存储(auth.json,0600)
packages/opencode/src/auth/index.ts(14-21, 58-92)
模型清单拉取与端点路由
packages/opencode/src/plugin/github-copilot/models.ts(82-111, 213-256)
请求装配:基座头 / 系统提示 / chat.params / chat.headers / _noop 工具
packages/opencode/src/session/llm/request.ts(18, 60-146, 159-205)
Chat Completions 请求体与传输
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts(87-210, 306-330)
Responses 请求体
packages/core/src/github-copilot/responses/openai-responses-language-model.ts(254-388, 780-790)
Copilot provider 装配(chat/responses 选择、隐藏别名、SDK 装配)
packages/core/src/plugin/provider/github-copilot.ts(6-51)
AI SDK provider 配置(config.headers/url、UA 后缀)
packages/core/src/github-copilot/copilot-provider.ts(53-82)
版本常量 opencode/<version>
packages/core/src/installation/version.ts(6)
OpenCode → GitHub Copilot 请求分析:身份验证 / Token 交换、Request Header 与 Request Body(2026-08-03)
0. 本次同步变更提示
本次从上游
anomalyco/opencode快进同步进来 3 个提交:1882c33827packages/console/app/src/routes/zen/util/provider/google.ts,属 console/zen 计费侧)01624c8c870891ecd8e0结论:本次同步未触及 GitHub Copilot 的鉴权、Header 或 Body 构造逻辑。 以下分析对同步前后一致有效。
1. 关键结论(TL;DR)
Authorization: Bearer直连https://api.githubcopilot.com。copilot_internal/v2/token之类的换取端点,github-copilot 的loader也没有任何 refresh 逻辑(与xai.ts/codex.ts/snowflake-cortex.ts形成鲜明对比)。token 以expires: 0落盘,长期复用。request.ts通用层 →chat.headers插件钩子层 →loader.fetch最终注入层(Authorization/User-Agent/Openai-Intent在这里定型,并删除小写authorization/x-api-key)。/chat/completions(OpenAI 兼容)、/responses(GPT‑5 类)、/v1/messages(Anthropic 线)。chat.params会对 gpt 模型去掉max_tokens、对 anthropic 线关闭toolStreaming。Copilot-Integration-Id/Editor-Version/Editor-Plugin-Version头,仅用User-Agent: opencode/<version>+X-GitHub-Api-Version+x-initiator+Openai-Intent等最小集合标识自己。2. GitHub 身份验证与 Token 交换流程
核心文件:
packages/opencode/src/plugin/github-copilot/copilot.ts凭据存储:
packages/opencode/src/auth/index.ts2.1 常量与端点
Ov23li8tweQw6odWQebzcopilot.ts:92026-06-01copilot.ts:10https://{domain}/login/device/codecopilot.ts:21https://{domain}/login/oauth/access_tokencopilot.ts:22https://api.githubcopilot.com(企业版:https://copilot-api.{enterprise})copilot.ts:26-28read:usercopilot.ts:244urn:ietf:params:oauth:grant-type:device_codecopilot.ts:274domain默认github.com;选择「GitHub Enterprise」时由用户输入的 URL 规范化而来(normalizeDomain,copilot.ts:15-17,225-230)。2.2 时序图
sequenceDiagram autonumber participant U as 用户 participant OC as OpenCode (copilot.ts) participant GH as github.com (OAuth) participant CAPI as api.githubcopilot.com U->>OC: 选择 /login GitHub Copilot(github.com 或 Enterprise) OC->>GH: POST /login/device/code {client_id, scope:"read:user"} GH-->>OC: {device_code, user_code, verification_uri, interval} OC-->>U: 展示 user_code + 打开 verification_uri loop 轮询直到授权(间隔 interval + 3s 安全余量) OC->>GH: POST /login/oauth/access_token {client_id, device_code, grant_type} GH-->>OC: authorization_pending / slow_down / {access_token} end Note over OC: 落盘 auth.json:{refresh=access_token, access=access_token, expires:0} OC->>CAPI: GET /models (Authorization: Bearer <access_token>, X-GitHub-Api-Version, User-Agent) CAPI-->>OC: 模型清单 → 决定 chat/responses/messages 端点与定价 OC->>CAPI: POST /chat/completions | /responses | /v1/messages(推理请求,仍用同一 Bearer)2.3 关键步骤说明
copilot.ts:234-256):POST /login/device/code,body{client_id, scope:"read:user"},请求头带Accept: application/json、Content-Type: application/json、User-Agent: opencode/<version>。copilot.ts:262-334):POST /login/oauth/access_token。处理authorization_pending(继续轮询)、slow_down(按 RFC 8628 §3.5 增加 5s 或采用服务端返回的interval)、其它error(失败)。轮询间隔统一加OAUTH_POLLING_SAFETY_MARGIN_MS = 3000(copilot.ts:14)以规避时钟漂移。copilot.ts:286-305):成功后返回{type:"success", refresh: access_token, access: access_token, expires: 0};企业版额外写入enterpriseUrl。凭据经Auth.set写入~/.local/share/opencode/auth.json,权限0o600(auth/index.ts:73-81)。Oauth 结构见auth/index.ts:14-21(refresh/access/expires/accountId?/enterpriseUrl?)。copilot.ts:62-92→models.ts:213-256):以Authorization: Bearer <access>+X-GitHub-Api-Version+User-Agent请求{base}/models,据supported_endpoints决定每个模型走messages/responses/chat(models.ts:92-99),并换算定价、能力、变体。2.4 「Token 交换」的真相:无二次换取
很多 Copilot 客户端会把 GitHub OAuth token 再向
https://api.github.com/copilot_internal/v2/token换一个短期 Copilot token。OpenCode 没有这一步:grepcopilot_internal/v2/token/Copilot-Integration-Id均无命中。loader(copilot.ts:96-181)拿到getAuth()后直接Authorization: Bearer ${info.access}(copilot.ts:164),既不判断expires也不 refresh。xai.ts:462-524、openai/codex.ts:322-388、snowflake-cortex.ts:286-366都实现了refreshAccessToken+ 单飞刷新;github-copilot 完全没有。这解释了为何落盘时expires: 0—— 该字段对 Copilot 分支不参与任何过期判定,GitHub OAuth token 被长期直用。3. 客户端发送给 GitHub Copilot 的 Request Header
3.1 叠加管线(三层)
flowchart LR A["request.ts 通用基座头<br/>x-session-affinity / X-Session-Id / User-Agent"] --> B["chat.headers 钩子(copilot.ts:360-412)<br/>X-GitHub-Api-Version / x-initiator / anthropic-beta / X-Interaction-Type"] --> C["AI SDK config.headers()<br/>+ withUserAgentSuffix ai-sdk/openai-compatible/VERSION(copilot-provider.ts:66)"] --> D["loader.fetch 最终注入(copilot.ts:160-178)<br/>Authorization / User-Agent / Openai-Intent / Copilot-Vision-Request;删除小写 authorization、x-api-key"] --> E[[api.githubcopilot.com]]request.ts:187-204):非 opencode provider 走else分支,注入x-session-affinity、X-Session-Id、可选x-parent-session-id、User-Agent(USER_AGENT = opencode/${InstallationVersion},request.ts:18),再并入input.model.headers与钩子产出的headers。chat.headers,copilot.ts:360-412):仅当providerID含github-copilot时生效。loader.fetch,copilot.ts:100-179):真正发出前的最终改写,Authorization在此定型。3.2 最终发出的 Header 明细
AuthorizationBearer <GitHub OAuth access token>copilot.ts:164(推理)/copilot.ts:74(/models)User-Agentopencode/<version>copilot.ts:163;request.ts:18;version.ts:6X-GitHub-Api-Version2026-06-01copilot.ts:363(钩子)/copilot.ts:76(/models)x-initiatoragent或usercopilot.ts:161(body 探测)+copilot.ts:394,411(compaction/子会话强制 agent)Openai-Intentconversation-editscopilot.ts:165Copilot-Vision-Requesttrue(仅含图片时)copilot.ts:168-170X-Interaction-Typeagent-session-name-generation(仅 title agent)copilot.ts:364-366anthropic-betainterleaved-thinking-2025-05-14(仅@ai-sdk/anthropic线)copilot.ts:368-370/v1/messages时开启交错思考x-session-affinity/X-Session-Idrequest.ts:197-198x-parent-session-idrequest.ts:199清理动作(
copilot.ts:172-173):发出前delete headers["x-api-key"]与delete headers["authorization"](小写),避免其它 provider 的鉴权头串入,确保只保留大写Authorization。3.3
x-initiator的判定(body 探测)loader.fetch会解析init.body,按三种 API 形态判断isAgent/isVision(copilot.ts:107-158):body.messages+ url 含completions):最后一条非user,或为「合成附件提示」→agent;任一消息含image_url→ vision。body.input):最后一项非user或合成附件 →agent;含input_image→ vision。body.messages,Anthropic 线):最后一条不是「纯 user 文本」或为合成附件 →agent;含image(含嵌套在tool_result里的图片)→ vision。此外
chat.headers钩子会把「压缩续写(compaction)」与「子会话(有parentID)」强制标为x-initiator: agent(copilot.ts:385-411)。合成附件提示常量为MessageV2.SYNTHETIC_ATTACHMENT_PROMPT(copilot.ts:31-43)。4. 客户端发送给 GitHub Copilot 的 Request Body
4.1 端点 / 协议路由
由
/models返回的supported_endpoints决定(models.ts:92-111):api.npm)messages@ai-sdk/anthropicsupported_endpoints含/v1/messages{base}/v1/messages(api.url = {base}/v1,models.ts:109)responses@ai-sdk/github-copilot/responses{base}/responseschat@ai-sdk/github-copilot/chat/completions{base}/chat/completions对
@ai-sdk/github-copilot,GPT‑5 类模型(非gpt-5-mini)优先走 Responses、否则 Chat(core/src/plugin/provider/github-copilot.ts:42-48);并隐藏与 Responses 冲突的gpt-5-chat-latest别名(同文件:9-19)。4.2 Chat Completions 请求体
构造于
packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts:139-189(getArgs):modelthis.modelIdmessagesconvertToOpenAICompatibleChatMessages(prompt)(:179)max_tokensmaxOutputTokens(gpt 模型会被chat.params置空,见 4.5)temperature/top_pfrequency_penalty/presence_penaltyresponse_formatjson_schema(支持结构化输出)或json_object(:153-165)stop/seedreasoning_effort/verbosity:175-176)thinking_budget:186)tools/tool_choiceprepareTools(...)产出(:130-137,182-183)user:145)流式(
doStream,:308-314)在上述基础上追加stream: true,并在严格兼容模式下带stream_options: { include_usage: true };请求发往POST {base}/chat/completions(:203-204,320-321)。4.3 Responses 请求体
构造于
packages/core/src/github-copilot/responses/openai-responses-language-model.ts:254-388:model、input(非messages,为 Responses 事件项数组)、temperature、top_p、max_output_tokens、text(format/verbosity)、store(默认true,:206,285)、user、instructions、service_tier、include、reasoning{ effort, summary }(:297-299)、tools、tool_choice,流式stream: true(:788)。4.4 消息形态与 Messages(Anthropic)线
role:"system"前置到消息数组(request.ts:101-112);首段+其余会被压成两段(request.ts:74-78)。api.npm === "@ai-sdk/anthropic"时由@ai-sdk/anthropic构造/v1/messages请求体,并叠加anthropic-beta: interleaved-thinking-2025-05-14头(copilot.ts:368-370)。4.5 针对 Copilot 的 Body 级调整
chat.params钩子(copilot.ts:340-353):maxOutputTokens(output.maxOutputTokens = undefined)——对齐官方 GitHub Copilot CLI 行为(:344-346)。toolStreaming(:351-353)——Copilot 的/v1/messagesshim 会拒绝工具定义里的eager_input_streaming字段("Extra inputs are not permitted"),故关闭以避免注入。request.ts侧的 Copilot 专属处理:当无启用工具但历史里存在工具调用回放时,注入一个_noop占位工具(request.ts:159-175),因为 Copilot 在回放工具调用时要求tools字段存在。小工具模型(标题生成)用experimental.provider.small_model从UTILITY_MODELS(gpt-5.4-nano/gpt-4.1/gpt-4o/gpt-4o-mini)中挑选(copilot.ts:11,355-359)。5. 关键代码位置索引
packages/opencode/src/plugin/github-copilot/copilot.ts(9-28, 96-181, 222-336, 340-412)packages/opencode/src/auth/index.ts(14-21, 58-92)packages/opencode/src/plugin/github-copilot/models.ts(82-111, 213-256)packages/opencode/src/session/llm/request.ts(18, 60-146, 159-205)packages/core/src/github-copilot/chat/openai-compatible-chat-language-model.ts(87-210, 306-330)packages/core/src/github-copilot/responses/openai-responses-language-model.ts(254-388, 780-790)packages/core/src/plugin/provider/github-copilot.ts(6-51)packages/core/src/github-copilot/copilot-provider.ts(53-82)opencode/<version>packages/core/src/installation/version.ts(6)