Skip to main content
外部的 @openclaw/copilot 插件通过 GitHub Copilot CLI(@github/copilot-sdk)运行嵌入式订阅版 Copilot 代理轮次,而不是使用 OpenClaw 内置的 harness。Copilot CLI 会负责底层的 代理循环:原生工具执行、原生压缩(infiniteSessions),以及由 CLI 在 copilotHome 下管理的线程状态。OpenClaw 仍然负责聊天 通道、会话文件、模型选择、动态工具(桥接)、审批、 媒体传递、可见的对话镜像、/btw 附加问题(参见 附加问题 (/btw)),以及 openclaw doctor 关于更广泛的模型 / provider / runtime 拆分,请从 代理运行时 开始。

要求

  • 安装了 @openclaw/copilot 插件的 OpenClaw。
  • 如果你的配置使用了 plugins.allow,请包含 copilot(插件声明的 manifest id)。针对 npm 包名 @openclaw/copilot 的允许列表项不会匹配,即使设置了 agentRuntime.id: "copilot" 也会使插件被阻止。
  • 一个可以驱动 Copilot CLI 的 GitHub Copilot 订阅,或者用于无头运行或定时任务运行的 gitHubToken 环境变量 / auth-profile 条目。
  • 一个可写的 copilotHome 目录。当 OpenClaw 提供 agent 目录时,默认为 <agentDir>/copilot,否则为 ~/.openclaw/agents/<agentId>/copilot
openclaw doctor 会运行插件的 doctor contract,用于 session-state 所有权和未来的配置迁移。它不会探测 Copilot CLI 环境。

安装

Copilot 运行时作为外部插件随附,因此核心 openclaw 包不包含 @github/copilot-sdk 或其平台特定的 @github/copilot-<platform>-<arch> CLI 二进制文件(总计大约 260 MB)。 仅为选择启用此运行时的 agent 安装它:
设置向导会在你第一次选择 github-copilot/* 模型并且你的配置通过 agentRuntime: { id: "copilot" } 将该模型(或其提供方)路由到 Copilot 运行时时自动安装该插件;请参见 快速开始。如果没有启用该选项,OpenClaw 会使用其内置的 GitHub Copilot 提供方,并且不会安装此插件。 该运行时按以下顺序解析 SDK:
  1. 来自已安装的 @openclaw/copilot 包的 import("@github/copilot-sdk")
  2. 回退目录 ~/.openclaw/npm-runtime/copilot/(旧版按需 安装目标)。
缺少 SDK 时会出现一个错误,错误代码为 COPILOT_SDK_MISSING,并附带 上面的重新安装命令。

快速开始

将一个模型(或一个 provider)固定到该 harness:
在单个模型条目上设置 agentRuntime.id,即可仅将该模型路由到 该 harness;或者在 provider 上设置,以路由该 provider 下的每个模型。 github-copilot/auto 是可移植的起点。命名的 Copilot 模型取决于 账号和组织策略;在固定之前,请确认你已通过身份验证的 Copilot CLI 实际上暴露了该模型。

支持的 provider

harness 支持规范的 github-copilot provider(由 extensions/github-copilot 拥有),以及当 模型具有非空的 baseUrl 且具备以下任一 api 形状时的自定义 models.providers 条目:
  • anthropic-messages
  • azure-openai-responses
  • ollama(OpenAI 兼容的 completions)
  • openai-completions
  • openai-responses
原生 provider id(openaianthropicgoogleollama)仍由 其原生运行时拥有。请改用不同的自定义 provider id,才能通过 Copilot BYOK 来路由端点。 Copilot BYOK 端点必须是公开的 HTTPS URL。harness 会为 Copilot SDK 提供 每次尝试对应的 loopback 代理,然后通过 OpenClaw 的受控 fetch 路径转发 provider 流量, 从而使 DNS pinning 和 SSRF 策略仍由 OpenClaw 管理。对于本地 Ollama、LM Studio 或局域网模型服务器,请使用原生 OpenClaw 运行时。

BYOK

Copilot BYOK 使用 SDK 的会话级自定义提供商契约。OpenClaw 会传递解析后的模型端点、API 密钥、Bearer 令牌模式、请求头、模型 ID,以及上下文/输出限制;提供商传输逻辑保留在 SDK 中,而不是 核心中。
BYOK 会话与订阅会话,以及与其他 BYOK 端点或凭证,分别单独标识。轮换密钥、请求头、模型或端点 会启动一个新的 Copilot SDK 会话,而不是恢复不兼容的状态。

认证

runCopilotAttempt 中按每个 agent 应用的优先级:
  1. 在 attempt 输入中显式设置 useLoggedInUser: true —— 使用 该 agent 的 copilotHome 下 Copilot CLI 已登录的用户。
  2. 在 attempt 输入中显式设置 gitHubToken(需要 profileId + profileVersion)。用于直接调用 CLI 和需要绕过 auth-profile 解析的测试。
  3. 合约解析得到的 resolvedApiKey + authProfileId —— 生产环境 主路径。Core 在调用 harness 之前会先解析 agent 配置的 github-copilot auth profile(src/infra/provider-usage.auth.ts:resolveProviderAuths), 因此 github-copilot:<profile> auth profile 可以在无头、cron 或多 profile 场景中端到端工作,而无需环境变量。
  4. 环境变量回退,按以下顺序检查(第一个非空值生效,空字符串视为缺失;与 已发布的 github-copilot provider 优先级一致,见 extensions/github-copilot/auth.ts):
    1. OPENCLAW_GITHUB_TOKEN —— harness 专用覆盖;可让你为 OpenClaw harness 固定一个 token,而不影响系统范围的 gh / Copilot CLI 配置。
    2. COPILOT_GITHUB_TOKEN —— 标准 Copilot SDK / CLI 环境变量。
    3. GH_TOKEN —— 标准 gh CLI 环境变量。
    4. GITHUB_TOKEN —— 通用 GitHub token 回退。
    合成的 pool profile id 为 env:<NAME>;profile version 是 token 的 不可逆 sha256 指纹,因此轮换环境变量值时会干净地使客户端池失效。
  5. 在没有 token 信号时的默认 useLoggedInUser
每个 agent 都拥有自己的 copilotHome,因此 Copilot CLI 的 token、会话和 配置绝不会在同一台机器上的 agent 之间泄漏。默认值: <agentDir>/copilot(使 SDK 状态与 OpenClaw 的 models.json / auth-profiles.json 不放在同一目录下),或者在未提供 agent 目录时使用 ~/.openclaw/agents/<agentId>/copilot。 可在 attempt 输入中通过 copilotHome: <path> 覆盖为自定义位置(例如用于迁移的 共享挂载)。 实时 harness 测试使用 OPENCLAW_COPILOT_AGENT_LIVE_TOKEN 作为直接 token。 共享的 live-test 设置会在将真实 auth profiles 暂存到隔离的测试 home 后清除 COPILOT_GITHUB_TOKENGH_TOKENGITHUB_TOKEN,因此通过专用变量传入的 gh auth token 值可以避免误判为跳过,同时不会泄漏到无关的测试套件中。

配置面

该 harness 从每次尝试的输入(runCopilotAttempt({...}))以及 extensions/copilot/src/ 中的一小组环境默认值读取配置: OpenClaw 插件 hooks 无需任何 Copilot 专属的尝试配置。该 harness 通过标准 harness 辅助函数运行 before_prompt_buildllm_inputllm_outputagent_end。 成功的 SDK 压缩还会运行 before_compactionafter_compaction。桥接的 OpenClaw 工具会运行 before_tool_call 并报告 after_tool_call;对于没有可移植等价项的原生 SDK 专属回调,仍使用 hooksConfig OpenClaw 中没有其他内容需要了解这些字段。其他插件、通道和核心代码只会看到标准的 AgentHarnessAttemptParams / AgentHarnessAttemptResult 形状。

压缩

harness.compact 运行时,Copilot SDK harness 会:
  1. 在不继续未完成工作的情况下恢复已跟踪的 SDK 会话。
  2. 调用 SDK 的会话作用域历史压缩 RPC。
  3. 返回 SDK 的压缩结果,而不在工作区下写入兼容性标记 文件。
OpenClaw 端的对话记录镜像(如下)会继续接收压缩后的 消息,因此面向用户的聊天历史会保持一致。

转录镜像

runCopilotAttempt 会通过 extensions/copilot/src/dual-write-transcripts.ts 将每一轮可镜像的消息双写到 OpenClaw 审计转录中。该镜像按会话(copilot:${sessionId})进行作用域划分,并按消息(${role}:${sha256_16(role,content)})进行键控,因此重新发出的先前轮次条目会与现有磁盘键发生冲突,而不会重复创建。 镜像外层包裹了两层故障隔离,以确保转录写入失败绝不会导致尝试失败:一层是内部尽力而为的包装器,另一层是尝试级别上的防御性 .catch(...)。失败只会被记录,不会向外抛出。

侧边问题(/btw

/btw 在这个 harness 中不是原生支持的。createCopilotAgentHarness() 故意将 harness.runSideQuestion 留空未定义 (在 extensions/copilot/harness.test.ts 中的 describe("runSideQuestion") 里有断言), 因此 OpenClaw 的 /btw 分发器(src/agents/btw.ts)会回落到 它对所有非 Codex 运行时使用的相同路径:直接用一段简短的侧边问题提示调用 已配置的模型提供方,并通过 streamSimple 流式返回(不使用 CLI 会话,也不占用额外的池槽位)。 这使得 Copilot CLI 会话仅保留给代理的主轮次循环, 并让 /btw 的行为与其他非 Codex 运行时保持一致。

Doctor

Copilot 插件通过其清单和 doctor 契约提供修复元数据:
  • 空的 legacyConfigRules(目前尚无已弃用字段)。
  • 无操作的 normalizeCompatibilityConfig(保留此项,以便未来弃用字段时 在代码库中拥有稳定的归属位置)。
  • 其清单声明了一个 sessionRouteStateOwners 条目:提供方为 github-copilot,运行时为 copilot,CLI 会话键为 copilot,身份验证配置文件前缀为 github-copilot:

局限性

  • 该 harness 声称支持 github-copilot 以及未被拥有的自定义 BYOK provider id。 Manifest 所拥有的原生 provider id 仍由其所属的 runtime 处理,即使将 agentRuntime.id 强制设为 copilot
  • 没有 TUI 界面;对于没有对等界面的 runtime,PI 的 TUI 仍是备用方案。
  • 当 agent 切换到 copilot 时,PI 会话状态不会迁移。 选择按每次尝试进行;现有的 PI 会话仍然有效。
  • ask_user 使用与 provider 无关的网关提问 runtime。Control UI 显示与其他 OpenClaw 问题相同的问题卡片,受支持的渠道会渲染选项按钮,并且下一条排队中的纯文本消息会在 SDK 请求返回前解析该网关记录。

权限和 ask_user

桥接的 OpenClaw 工具的权限强制执行发生在工具包装器内部,而不是通过 SDK 的 onPermissionRequest 回调。PI 使用的同一个 wrapToolWithBeforeToolCallHooksrc/agents/agent-tools.before-tool-call.ts)也被 createOpenClawCodingTools 应用于每个编码工具:循环检测、受信任的插件策略、before-tool-call 钩子,以及通过网关(plugin.approval.request)进行的两阶段插件审批,全部都沿着与原生 PI 尝试完全相同的代码路径运行。 Copilot 工具桥接返回的每个 SDK 工具都标记为:
  • overridesBuiltInTool: true — 替换 Copilot CLI 中同名的内置工具(edit、read、write、bash、…),因此每次工具调用都会路由回 OpenClaw。
  • skipPermission: true — 告诉 SDK 在调用工具之前不要触发 onPermissionRequest({kind: "custom-tool"})。包装后的 execute() 已经执行了更丰富的 OpenClaw 策略检查;如果在 SDK 层再弹出提示,要么会短路 OpenClaw 的强制执行(全部允许),要么会阻止每一次工具调用(全部拒绝)——这两种情况都不符合 PI 的对等行为。
树内的 Codex harness 使用相同的拆分:桥接的 OpenClaw 工具被包装(extensions/codex/src/app-server/dynamic-tools.ts),而 codex-app-server 自己原生的审批类型 (item/commandExecution/requestApprovalitem/fileChange/requestApprovalitem/permissions/requestApproval)则通过 plugin.approval.request 路由(extensions/codex/src/app-server/approval-bridge.ts)。Copilot SDK 的对应机制——对任何最终进入 onPermissionRequest 的非 custom-tool 类型使用 fail-closed 的 rejectAllPolicy——是同样的安全网,而实际上它从不会触发,因为 overridesBuiltInTool: true 会替换掉所有内置工具。 为了让被包装的工具层做出与 PI 等价的策略决定,harness 会将完整的 PI attempt-tool 上下文转发给 createOpenClawCodingTools:身份信息(senderIsOwnermemberRoleIdsownerOnlyToolAllowlist、…)、频道/路由(groupIdcurrentChannelIdreplyToMode、消息工具开关)、认证(authProfileStore)、运行身份(由 sandboxSessionKeyrunId 派生的 sessionKey / runSessionKey)、模型上下文(modelApimodelContextWindowTokensmodelCompatmodelHasVision)、以及运行钩子(onToolOutcomeonYield)。如果缺少这些字段,owner-only allowlist 会默认静默拒绝,插件信任策略无法解析到正确的作用域,而 session_status: "current" 会解析到过期的 sandbox key。桥接构建器是 extensions/copilot/src/tool-bridge.ts,它与 PI 的权威调用 src/agents/embedded-agent-runner/run/attempt.ts:1262 保持一致。runAttempt 通过共享的 resolveSandboxContext 接口解析 sandbox 上下文,向 SDK 传递有效的工作目录,并把 sandbox 以及子代理启动工作区转发到工具桥接中。该桥接还会转发它能在 SDK 边界强制执行的受限工具构建控制:includeCoreTools、运行时工具 allowlist,以及 toolConstructionPlan 该桥接还使用来自 openclaw/plugin-sdk/agent-harness-tool-runtime 的共享 harness 工具面辅助实现 PI parity。启用 tool-search 时,SDK 会看到紧凑的控制工具加一个隐藏的 catalog 执行器,而不是每一个 OpenClaw 工具 schema。启用代码模式时,helper 会构建与其他 agent harness 使用的相同代码模式控制面和 catalog 生命周期。轻量本地模型默认值、 运行时兼容的 schema 过滤、目录 hydration,以及 catalog 清理都保留在共享 helper 中,这样 Copilot 和 Codex 相关 harness 就不会分叉。

会话级 GitHub token

Copilot SDK 合同区分客户端级 GitHub token (CopilotClientOptions.gitHubToken,用于认证 CLI 进程本身) 和会话级 token(SessionConfig.gitHubToken,决定该会话的内容排除、模型路由和配额;在 createSessionresumeSession 中都会生效)。harness 只通过一次 resolveCopilotAuth 解析认证,并在认证模式为 gitHubToken 时设置这两个字段(即显式的 auth.gitHubToken,或者从已配置的 github-copilot 认证配置中由契约解析出的 resolvedApiKey)。当解析出的模式是 useLoggedInUser 时,会省略会话级字段,这样 SDK 就会继续从已登录身份中推导身份信息。 ask_user 使用 SessionConfig.onUserInputRequest。桥接会将 SDK 的选择题或无选项的自由文本提示注册为网关问题;对于固定选项请求,会接受选项索引或标签;当 SDK 请求允许时,则接受自由格式的回答。中止 OpenClaw 尝试会取消网关记录,并返回一个空的 SDK 回答。

相关