快速开始
Copilot SDK harness
外部 @openclaw/copilot 插件通过 GitHub Copilot CLI(@github/copilot-sdk)运行嵌入式订阅 Copilot 智能体轮次,而不是使用 OpenClaw 的内置 harness。Copilot CLI 会话负责底层 Agent loop:原生工具执行、原生压缩(infiniteSessions),以及 copilotHome 下由 CLI 管理的线程状态。OpenClaw 仍负责聊天渠道、会话文件、模型选择、动态工具(通过桥接)、审批、媒体投递、可见的对话记录镜像、/btw 附带问题(参见附带问题(/btw))以及 openclaw doctor。
有关更广泛的模型、提供商与运行时职责划分,请先阅读 Agent Runtimes。
要求
- 已安装
@openclaw/copilot插件的 OpenClaw。 - 如果你的配置使用
plugins.allow,请加入copilot(插件声明的清单 ID)。npm 软件包名称@openclaw/copilot的允许列表条目不会匹配;即使设置了agentRuntime.id: "copilot",插件仍会被阻止。 - 可驱动 Copilot CLI 的 GitHub Copilot 订阅;对于无头运行或 cron 运行,也可使用
gitHubToken环境变量或身份验证配置文件条目。 - 可写的
copilotHome目录。当 OpenClaw 提供 Agent 目录时,默认值为<agentDir>/copilot;否则为~/.openclaw/agents/<agentId>/copilot。
openclaw doctor 会针对会话状态所有权和未来的配置迁移运行插件的 Doctor 契约。它不会探测 Copilot CLI 环境。
安装
Copilot 运行时以外部插件的形式交付,因此核心 openclaw 软件包无需携带 @github/copilot-sdk 或其特定于平台的 @github/copilot-<platform>-<arch> CLI 二进制文件(合计约 260 MB)。仅为选择使用此运行时的智能体安装该插件:
openclaw plugins install @openclaw/copilot首次选择 github-copilot/* 模型,并且你的配置通过 agentRuntime: { id: "copilot" } 将该模型(或其提供商)路由到 Copilot 运行时时,设置向导会自动安装该插件;参见快速开始。如果没有进行此项选择,OpenClaw 会使用其内置的 GitHub Copilot provider,并且绝不会安装此插件。
运行时按以下顺序解析 SDK:
- 从已安装的
@openclaw/copilot软件包加载import("@github/copilot-sdk")。 - 后备目录
~/.openclaw/npm-runtime/copilot/(旧版按需安装目标)。
缺少 SDK 时,会显示一个代码为 COPILOT_SDK_MISSING 的错误以及上述重新安装命令。
快速开始
将一个模型(或一个提供商)固定到 harness:
{ agents: { defaults: { model: "github-copilot/auto", models: { "github-copilot/auto": { agentRuntime: { id: "copilot" }, }, }, }, },}在单个模型条目上设置 agentRuntime.id,可仅通过 harness 路由该模型;在提供商上设置,则会路由该提供商下的每个模型。
github-copilot/auto 是可移植的起点。具名 Copilot 模型取决于账号和组织策略;固定模型前,请确认已完成身份验证的 Copilot CLI 确实公开了该模型。
支持的提供商
harness 支持规范的 github-copilot 提供商(由 extensions/github-copilot 负责),还支持自定义 models.providers 条目,前提是模型具有非空的 baseUrl,并采用以下 api 形式之一:
anthropic-messagesazure-openai-responsesollama(兼容 OpenAI 的补全)openai-completionsopenai-responses
原生提供商 ID(openai、anthropic、google、ollama)仍由各自的原生运行时负责。若要通过 Copilot BYOK 路由端点,请改用不同的自定义提供商 ID。
Copilot BYOK 端点必须是公共 HTTPS URL。harness 会为 Copilot SDK 提供按尝试创建的回环代理,然后通过 OpenClaw 受保护的提取路径转发提供商流量,使 DNS 固定和 SSRF 策略仍由 OpenClaw 负责。对于本地 Ollama、LM Studio 或局域网模型服务器,请使用 OpenClaw 原生运行时。
BYOK
Copilot BYOK 使用 SDK 的会话级自定义提供商契约。OpenClaw 会传入解析后的模型端点、API 密钥、持有者令牌模式、标头、模型 ID,以及上下文和输出限制;提供商传输逻辑保留在 SDK 中,而不是核心中。
{ agents: { defaults: { model: "custom-proxy/llama-3.1-8b", models: { "custom-proxy/llama-3.1-8b": { agentRuntime: { id: "copilot" }, }, }, }, }, models: { mode: "merge", providers: { "custom-proxy": { baseUrl: "https://api.example.com/v1", apiKey: "${CUSTOM_PROXY_API_KEY}", api: "openai-responses", authHeader: true, models: [{ id: "llama-3.1-8b", name: "Llama 3.1 8B" }], }, }, },}BYOK 会话与订阅会话使用不同的键,不同 BYOK 端点或凭据之间也分别使用不同的键。轮换密钥、标头、模型或端点时,会启动新的 Copilot SDK 会话,而不是恢复不兼容的状态。
身份验证
在 runCopilotAttempt 期间按智能体应用以下优先级:
-
尝试输入中显式设置
useLoggedInUser: true——使用智能体copilotHome下 Copilot CLI 的已登录用户。 -
尝试输入中显式设置
gitHubToken(需要profileId+profileVersion)。用于需要绕过身份验证配置文件解析的直接 CLI 调用和测试。 -
通过契约解析的
resolvedApiKey+authProfileId——生产环境的主路径。核心在调用 harness 前,会解析智能体配置的github-copilot身份验证配置文件(src/infra/provider-usage.auth.ts:resolveProviderAuths),因此github-copilot:<profile>身份验证配置文件可以在无头、cron 或多配置文件设置中端到端工作,无需环境变量。 -
环境变量后备方案,按以下顺序检查(第一个非空值生效;空字符串视为不存在;与
extensions/github-copilot/auth.ts中已交付的github-copilot提供商优先级一致):OPENCLAW_GITHUB_TOKEN——harness 专用覆盖项;允许你为 OpenClaw harness 固定令牌,而不影响系统范围的gh/ Copilot CLI 配置。COPILOT_GITHUB_TOKEN——标准 Copilot SDK / CLI 环境变量。GH_TOKEN——标准ghCLI 环境变量。GITHUB_TOKEN——通用 GitHub 令牌后备项。
合成的池配置文件 ID 为
env:<NAME>;配置文件版本是令牌的不可逆 sha256 指纹,因此轮换环境变量值会彻底使客户端池失效。 -
没有可用令牌信号时,默认使用
useLoggedInUser。
每个智能体都有自己的 copilotHome,因此同一台机器上的 Copilot CLI 令牌、会话和配置绝不会在智能体之间泄漏。默认值为 <agentDir>/copilot(使 SDK 状态不与 OpenClaw 的 models.json / auth-profiles.json 位于同一目录);未提供 Agent 目录时,则为 ~/.openclaw/agents/<agentId>/copilot。若要使用自定义位置(例如用于迁移的共享挂载点),请在尝试输入中通过 copilotHome: <path> 覆盖。
实时 harness 测试使用 OPENCLAW_COPILOT_AGENT_LIVE_TOKEN 提供直接令牌。共享实时测试设置将真实身份验证配置文件暂存到隔离的测试主目录后,会清除 COPILOT_GITHUB_TOKEN、GH_TOKEN 和 GITHUB_TOKEN,因此通过专用变量传入的 gh auth token 值既能避免错误跳过,又不会泄漏到无关的测试套件。
配置界面
harness 从每次尝试的输入(runCopilotAttempt({...}))以及 extensions/copilot/src/ 内少量环境变量默认值中读取配置:
| 字段 | 用途 |
|---|---|
copilotHome |
每个智能体的 CLI 状态目录(默认值见上文)。 |
model |
字符串或 { provider, id, api?, baseUrl?, headers?, authHeader? }。省略时使用智能体的常规模型选择;harness 会验证解析出的提供商是否受支持。 |
reasoningEffort |
"low" | "medium" | "high" | "xhigh"。映射自 auto-reply/thinking.ts 中 OpenClaw 的 ThinkLevel / ReasoningLevel 解析结果。 |
infiniteSessionConfig |
由 harness.compact 驱动的 SDK infiniteSessions 块的可选覆盖项。保持原样即可安全使用。 |
hooksConfig |
用于工具/MCP、用户提示、会话和错误回调的可选原生 Copilot SDK SessionHooks 配置。它与 OpenClaw 的可移植生命周期钩子分离。 |
permissionPolicy |
SDK 内置工具类型(shell、write、read、url、mcp、memory、hook)的 onPermissionRequest 处理程序可选覆盖项。作为安全保障,默认值为 rejectAllPolicy;有关它为何实际上绝不会触发,请参阅权限和 ask_user。 |
enableSessionTelemetry |
可选的 SDK 会话遥测标志。 |
OpenClaw 插件钩子无需任何 Copilot 专用的尝试配置。harness 会通过标准 harness 辅助函数运行 before_prompt_build(以及旧版 before_agent_start 兼容性钩子)、llm_input、llm_output 和 agent_end。SDK 成功执行压缩时,还会运行 before_compaction 和 after_compaction。桥接的 OpenClaw 工具会运行 before_tool_call 并报告 after_tool_call;hooksConfig 仍用于没有可移植等效项的 SDK 原生专用回调。
OpenClaw 中的其他部分无需了解这些字段。其他插件、渠道和核心代码只会看到标准的 AgentHarnessAttemptParams / AgentHarnessAttemptResult 形式。
压缩
运行 harness.compact 时,Copilot SDK harness 会:
- 恢复受跟踪的 SDK 会话,但不继续处理待办工作。
- 调用 SDK 的会话级历史记录压缩 RPC。
- 返回 SDK 压缩结果,而不在工作区下写入兼容性标记文件。
OpenClaw 侧的对话记录镜像(见下文)会继续接收压缩后的消息,因此面向用户的聊天历史记录保持一致。
对话记录镜像
runCopilotAttempt 会通过 extensions/copilot/src/dual-write-transcripts.ts,将每个轮次中可镜像的消息双写到 OpenClaw 审计对话记录中。镜像按会话划分作用域(copilot:${sessionId}),并按消息设置键(${role}:${sha256_16(role,content)}),因此重新发出的先前轮次条目会与磁盘上的现有键冲突,而不会产生重复项。
两层故障隔离机制包裹着镜像,因此转录写入失败绝不会导致本次尝试失败:一层是内部尽力而为的包装器,另一层是在尝试级别设置的纵深防御 .catch(...)。失败只会记录到日志中,不会向上暴露。
附带问题(/btw)
此 harness 原生不支持 /btw。createCopilotAgentHarness() 有意让 harness.runSideQuestion 保持未定义状态(在 extensions/copilot/harness.test.ts 的 describe("runSideQuestion") 中断言),因此 OpenClaw 的 /btw 分发器(src/agents/btw.ts)会回退到所有非 Codex 运行时使用的相同路径:直接调用已配置的模型提供商,传入简短的附带问题提示,并通过 streamSimple 流式返回结果(不使用 CLI 会话,也不占用额外的池槽位)。
这样可将 Copilot CLI 会话保留给智能体的主轮次循环,同时使 /btw 的行为与其他非 Codex 运行时保持一致。
Doctor
extensions/copilot/doctor-contract-api.ts 由 src/plugins/doctor-contract-registry.ts 自动加载。它提供:
- 空的
legacyConfigRules(目前还没有已停用字段)。 - 不执行任何操作的
normalizeCompatibilityConfig(保留它,以便未来停用字段时在代码树内有一个稳定的归属位置)。 - 一个
sessionRouteStateOwners条目:提供商github-copilot、运行时copilot、CLI 会话键copilot、身份验证配置文件前缀github-copilot:。
限制
- 此 harness 会认领
github-copilot以及无所有者的自定义 BYOK 提供商 ID。即使将agentRuntime.id强制设为copilot,由清单声明所有权的原生提供商 ID 仍归其所有者运行时处理。 - 没有 TUI 界面;对于没有同级界面的运行时,PI 的 TUI 仍作为回退方案。
- 当智能体切换到
copilot时,不会迁移 PI 会话状态。运行时按每次尝试选择;现有 PI 会话仍然有效。 ask_user使用与 Codex harness 相同的 OpenClaw 提示与回复路径:当 Copilot SDK 请求用户输入时,OpenClaw 会向当前渠道或 TUI 发布阻塞式提示,下一条进入队列的用户消息将完成该 SDK 请求。
权限和 ask_user
桥接 OpenClaw 工具的权限强制执行发生在工具包装器内部,而不是通过 SDK 的 onPermissionRequest 回调执行。createOpenClawCodingTools 会对每个编码工具应用 PI 所使用的同一个 wrapToolWithBeforeToolCallHook(src/agents/agent-tools.before-tool-call.ts):循环检测、可信插件策略、工具调用前钩子,以及通过 Gateway 网关(plugin.approval.request)执行的两阶段插件审批,全部经过与原生 PI 尝试完全相同的代码路径。
convertOpenClawToolToSdkTool 返回的 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/requestApproval、item/fileChange/requestApproval、item/permissions/requestApproval)则通过 plugin.approval.request 路由(extensions/codex/src/app-server/approval-bridge.ts)。Copilot SDK 中的对应机制,是针对任何到达 onPermissionRequest 的非 custom-tool 类型采用故障关闭的 rejectAllPolicy;这构成相同的安全保障。实际上它永远不会触发,因为 overridesBuiltInTool: true 会取代所有内置工具。
为了让包装工具层作出与 PI 等效的策略决策,此 harness 会将完整的 PI 尝试工具上下文转发给 createOpenClawCodingTools:身份信息(senderIsOwner、memberRoleIds、ownerOnlyToolAllowlist 等)、渠道与路由信息(groupId、currentChannelId、replyToMode、消息工具开关)、身份验证信息(authProfileStore)、运行身份(从 sandboxSessionKey 派生的 sessionKey / runSessionKey、runId)、模型上下文(modelApi、modelContextWindowTokens、modelCompat、modelHasVision),以及运行钩子(onToolOutcome、onYield)。缺少这些字段时,仅限所有者的允许列表默认会静默拒绝,插件信任策略无法解析到正确的作用域,而 session_status: "current" 会解析到过期的沙箱键。桥接构建器位于 extensions/copilot/src/tool-bridge.ts,与 PI 在 src/agents/embedded-agent-runner/run/attempt.ts:1262 中的权威调用保持一致。runAttempt 通过共享的 resolveSandboxContext 接缝解析沙箱上下文,向 SDK 传递有效工作目录,并将 sandbox 及子智能体生成工作区转发到工具桥接层。该桥接层还会转发它能在 SDK 边界强制执行的有界工具构建控制项:includeCoreTools、运行时工具允许列表和 toolConstructionPlan。
该桥接层还使用来自 openclaw/plugin-sdk/agent-harness-tool-runtime 的共享 harness 工具界面辅助程序,以与 PI 保持一致。启用工具搜索后,SDK 看到的是精简的控制工具和隐藏的目录执行器,而不是每个 OpenClaw 工具的模式。启用代码模式后,该辅助程序会构建与其他 Agent harness 相同的代码模式控制界面和目录生命周期。本地模型的精简默认值、运行时兼容的模式过滤、目录注水以及目录清理都保留在共享辅助程序中,从而避免 Copilot 与 Codex 相邻的 harness 发生偏差。
会话级 GitHub 令牌
Copilot SDK 合约区分客户端级 GitHub 令牌(CopilotClientOptions.gitHubToken,用于对 CLI 进程本身进行身份验证)和会话级令牌(SessionConfig.gitHubToken,决定该会话的内容排除、模型路由和配额;createSession 和 resumeSession 都会采用它)。此 harness 通过 resolveCopilotAuth 解析一次身份验证;当身份验证模式为 gitHubToken 时,会同时设置这两个字段(来源可以是显式的 auth.gitHubToken,也可以是从已配置的 github-copilot 身份验证配置文件按合约解析出的 resolvedApiKey)。当解析后的模式为 useLoggedInUser 时,会省略会话级字段,让 SDK 继续从已登录身份推导身份信息。
ask_user 使用 SessionConfig.onUserInputRequest。对于固定选项请求,桥接层接受选项索引或标签;当 SDK 请求允许自由格式回答时,它也接受自由格式答案;当 OpenClaw 尝试被中止时,它会取消待处理的请求。