跳转到内容

Copilot SDK 工具

外部 @openclaw/copilotOpenClawGitHubCLI 插件允许 OpenClaw 通过 GitHub Copilot CLI (@github/copilot-sdk) 运行嵌入式订阅 Copilot agent 轮次,而不是使用内置的 PI 套件。

当您希望 Copilot CLI 会话拥有低级代理循环时,请使用 Copilot SDK 工具:原生工具执行、原生压缩 (CLIinfiniteSessions) 以及由 CLI 管理的 copilotHome 下的线程状态。OpenClaw 仍然拥有聊天通道、会话文件、模型选择、OpenClaw 动态工具(桥接)、批准、媒体交付、可见的转录镜像、/btw 侧问题(由树内 PI 回退处理 — 请参阅 Side questions (/btw)) 以及 openclaw doctor

有关更广泛的模型/提供商/运行时划分,请从 Agent runtimes 开始。

  • 安装了 OpenClaw@openclaw/copilot 插件的 OpenClaw。
  • 如果您的配置使用 plugins.allow,请包含 copilotnpm(插件声明的清单 ID)。如果限制性允许列表使用 npm 风格的 @openclaw/copilot 包名称,则插件将保持阻止状态,并且即使在 agentRuntime.id: "copilot" 下运行时也不会加载。
  • 可以驱动 Copilot CLI 的 GitHub Copilot 订阅(或用于无头/定时运行的 GitHubCLIgitHubToken env / auth-profile 条目)。
  • 一个可写的 copilotHome 目录。该工具默认使用 ~/.openclaw/agents/<agentId>/copilot 以实现完全的每个代理隔离。当 没有显式设置主目录时,平台默认值(Windows 上的 %APPDATA%\copilot,或其他地方的 $XDG_CONFIG_HOME/copilot~/.config/copilot)将用作诊断探针的回退项。

openclaw doctor 为该扩展运行插件 doctor contract;那里的失败是在选择加入代理之前确认环境准备就绪的标准方式。

Copilot 运行时是一个外部插件,因此核心 openclaw 包 不携带 @github/copilot-sdk 依赖项或其特定于平台的 @github/copilot-<platform>-<arch> CLI 二进制文件。它们加起来大约 260 MB,因此仅为选择加入此运行时的代理安装它们:

Terminal window
openclaw plugins install @openclaw/copilot

向导会在您第一次选择 github-copilot/* 模型并且您的配置通过 agentRuntime: { id: "copilot" } 将该模型(或其提供商)选择加入 Copilot 代理运行时时安装该插件(请参阅下方的 Quickstart)。如果没有选择加入,openclaw 将使用其内置的 GitHub Copilot 提供商,并且永远不会安装运行时插件。

运行时按以下顺序解析 SDK:

  1. 来自已安装 @openclaw/copilot 包的 import("@github/copilot-sdk")
  2. 众所周知的回退目录 ~/.openclaw/npm-runtime/copilot/(即 传统的按需安装目标)。

缺少 SDK 会显示代码为 COPILOT_SDK_MISSING 的单个错误 以及上面的插件重新安装命令。

将一个模型(或一个提供商)固定到该工具:

{
agents: {
defaults: {
model: "github-copilot/gpt-5.5",
models: {
"github-copilot/gpt-5.5": {
agentRuntime: { id: "copilot" },
},
},
},
},
}

这两种方法是等效的。当仅应通过该工具路由该模型时,在单个模型条目上使用 agentRuntime.id;当该提供商下的每个模型 都应使用它时,在提供商上设置 agentRuntime.id

该工具声明支持规范的 github-copilot 提供商 (由 extensions/github-copilot 拥有的相同 id):

  • github-copilot

超出该集合的任何内容都会通过 selection.tsauto_pi 分支回退到 PI。

每个代理的优先级,在 runCopilotAttempt 期间应用:

  1. 显式 useLoggedInUser: true 位于尝试输入上。使用在代理的 copilotHome 下解析的 Copilot CLI 已登录用户。

  2. 显式 gitHubToken 位于尝试输入上(带有 profileId + profileVersion)。适用于调用方想要绕过身份验证配置文件解析的直接 CLI 调用和测试。

  3. EmbeddedRunAttemptParams 形状解析的 resolvedApiKey + authProfileId。这是生产主要路径: core 在调用 harness 之前解析代理已配置的 github-copilot 身份验证配置文件 (通过 src/infra/provider-usage.auth.ts:resolveProviderAuths),并且 harness 直接使用这两个字段。 这使得 github-copilot:<profile> 身份验证配置文件可以在无头 / cron / 多配置文件设置中端到端工作,而无需环境变量。

  4. 环境变量回退,适用于未配置身份验证配置文件的直接 CLI / dogfood 运行。运行时按优先级顺序检查以下变量, 映射已发布的 github-copilot 提供商 (extensions/github-copilot/auth.ts)和文档化的 Copilot SDK 设置:

    1. OPENCLAW_GITHUB_TOKEN — harness 特定覆盖;设置此项 可为 OpenClaw harness 固定令牌,而不会干扰 系统范围的 gh / Copilot CLI 配置。
    2. COPILOT_GITHUB_TOKEN — 标准 Copilot SDK / CLI 环境变量。
    3. GH_TOKEN — 标准 gh CLI 环境变量(匹配现有的 github-copilot 提供商优先级)。
    4. GITHUB_TOKEN — 通用 GitHub 令牌回退。

    第一个非空值优先;空字符串被视为不存在。综合的 pool profile id 为 env:<NAME>,而 profileVersion 是 token 的不可逆 sha256 指纹,因此轮换环境变量值会彻底清除客户端池。

  5. 当没有可用的 token 信号时的默认 useLoggedInUser

每个代理都会获得一个专用的 copilotHome,以便 Copilot CLI token、会话和配置不会在同一台机器上的代理之间泄漏。默认情况下,当宿主向工具提供代理目录时(将 SDK 状态与同一目录中的 OpenClaw 的 models.json / auth-profiles.json 隔离),默认值为 <agentDir>/copilot,否则为 ~/.openclaw/agents/<agentId>/copilot。如果需要自定义位置(例如,用于迁移的共享挂载),可以在 attempt 输入中使用 copilotHome: <path> 进行覆盖。

probeCopilotAuthShape(请参阅 Doctor and probes)是纯形状检查,用于验证将使用上述哪种模式。它不执行实时 SDK 握手。

工具从每次尝试的输入 (runCopilotAttempt({...})) 以及 extensions/copilot/src/ 内部的一小组环境变量默认值中读取其配置:

  • copilotHome — 每个代理的 CLI 状态目录(默认值如上所述)。
  • model — 字符串或 { provider, id, api? }。如果省略,OpenClaw 将使用代理的常规模型选择,并且工具会验证解析出的提供商是否在支持的集合中。
  • reasoningEffort"low" | "medium" | "high" | "xhigh"。从 OpenClaw 在 auto-reply/thinking.ts 中的 ThinkLevel / ReasoningLevel 解析进行映射。
  • infiniteSessionConfig — 由 harness.compact 驱动的 SDK infiniteSessions 块的可选覆盖。默认值保持原样即可。
  • hooksConfig — 可选的桥接配置,向 SDK 循环暴露 OpenClaw 的消息写入前/后挂钩。
  • permissionPolicy — 用于 SDK 内置工具类型(shellwritereadurlmcpmemoryhook)的 onPermissionRequest 处理程序的可选覆盖。 默认为 rejectAllPolicy 作为安全网;实际上 SDK 从不调用这些类型,因为每个桥接的 OpenClaw 工具 都注册了 overridesBuiltInTool: trueskipPermission: true,所以 100%% 的工具调用都通过 OpenClaw 包装的 execute() 流转。 请参阅 权限和 ask_user
  • enableSessionTelemetry — 通过 telemetry-bridge.ts 选择加入 OpenTelemetry 路由。

OpenClaw 的其余部分不需要了解这些字段。其他插件、通道和核心代码只能看到标准的 AgentHarnessAttemptParams / AgentHarnessAttemptResult 形状。

harness.compact 运行时,Copilot SDK 套件:

  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(...)。故障会被记录但不会 surfaced。

/btw 在此工具中不是原生的。createCopilotAgentHarness() 故意将 harness.runSideQuestionOpenClaw 保留为未定义,因此 OpenClaw 的 /btw 调度程序(src/agents/btw.ts)会回退到它为每个非 Codex 运行时使用的相同树内 PI 回退路径:直接使用简短的问题提示调用配置的模型提供商,并通过 streamSimpleCLI 流式传回(无 CLI 会话,无额外的池槽位)。

这将保留 Copilot CLI 会话供代理的主循环使用,并使 CLI/btw 行为与其他基于 PI 的运行时保持一致。该约定在 extensions/copilot/harness.test.ts 中的 describe("runSideQuestion") 下进行了断言。

extensions/copilot/doctor-contract-api.tssrc/plugins/doctor-contract-registry.ts 自动加载。它提供:

  • 一个空的 legacyConfigRules(在 MVP 阶段没有废弃的字段)。
  • 一个空操作的 normalizeCompatibilityConfig(保留以便将来字段废弃 时在树内有一个稳定的归属)。
  • 一个 sessionRouteStateOwners 条目,声明提供商 github-copilot; 运行时 copilotCLI;CLI 会话密钥 copilot;身份验证配置文件 前缀 github-copilot:

extensions/copilot/src/doctor-probes.ts 导出了三个命令式探针, 主机(包括 openclaw doctor)可以调用这些探针来验证环境:

探针检查内容可能失败的原因
probeCopilotCliVersioncopilot --version 退出码为 0 并带有非空版本字符串non-zero-exitempty-versionspawn-failedspawn-errorprobe-timeout
probeCopilotHomeWritablemkdir -p copilotHome + 写入 + 删除标记文件copilothome-not-writable(并在 details.rawError 中包含底层 fs 错误)
probeCopilotAuthShape至少需要 useLoggedInUsergitHubTokenprofileId+profileVersion 之一no-auth-source

每个探针都接受一个 DI 缝合点 (spawnFn, fsApi),以便测试不会生成真正的 Copilot CLI 或触及主机文件系统。

  • 在 MVP 阶段,该连接器仅支持规范的 github-copilot 提供商。 额外的提供商(BYOK 或其他)应该在后续的 PR 中落地,这些 PR 将适配器与连接代码一起发布。
  • 该驱动不提供 TUI;PI 的 TUI 不受影响,并仍是 没有对等表面的任何运行时的回退方案。
  • 当代理切换到 copilot 时,PI 会话状态不会被迁移。 选择是基于每次尝试的;现有的 PI 会话仍然有效。
  • 交互式 ask_user 尚未连接。 故意未注册 SDK 的 onUserInputRequest 处理程序,根据 SDK 合同,这会完全从模型中 隐藏 ask_user 工具。在此连接器下运行的代理会根据初始提示 做出最佳判断决策,而不是在轮次中途询问澄清问题。 后续工作将把 extensions/codex/src/app-server/user-input-bridge.ts 中的 codex 模式移植过来,以便通过 OpenClaw 渠道/TUI 提示路径路由 SDK UserInputRequestextensions/copilot/src/user-input-bridge.ts 中 的休眠脚手架是后续工作将要连接的表面。

桥接的 OpenClaw 工具的权限强制执行发生在工具包装器内部, 而不是通过 SDK 的 onPermissionRequest 回调。PI 使用的 相同 wrapToolWithBeforeToolCallHook (src/agents/pi-tools.before-tool-call.ts) 由 createOpenClawCodingTools 应用于每个编码工具:循环检测、 受信任的插件策略、工具调用前挂钩以及通过网关 (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 对等性不匹配。

代码树内(in-tree)的 codex 控制线使用了相同的划分:桥接的 OpenClaw 工具 被封装(extensions/codex/src/app-server/dynamic-tools.ts),而 codex-app-server 自身的原生审批类型 (item/commandExecution/requestApprovalitem/fileChange/requestApprovalitem/permissions/requestApproval)则通过 plugin.approval.requestextensions/codex/src/app-server/approval-bridge.ts)进行路由。Copilot SDK 的等效机制——对任何到达 onPermissionRequest 的非 custom-tool 类型进行失效闭合(fail-closed)rejectAllPolicy—— 是相同的安全保障, 它在实践中不会触发,因为 overridesBuiltInTool: true 取代了每一个内置工具。

为了使封装工具层能够做出与 PI 等效的策略决策, 此连接器将完整的 PI attempt-工具 上下文转发给 createOpenClawCodingTools —— 身份信息(senderIsOwnermemberRoleIdsownerOnlyToolAllowlist, …)、渠道/路由 (groupIdcurrentChannelIdreplyToMode,消息工具开关)、 身份验证(authProfileStore)、运行身份 (从 sandboxSessionKeyrunId 派生的 sessionKey/runSessionKey), 模型上下文(modelApimodelContextWindowTokensmodelCompatmodelHasVision)以及运行钩子(onToolOutcomeonYield)。如果没有这些字段,仅限所有者的允许列表将 静默地表现为默认拒绝,插件信任策略将无法解析到 正确的作用域,并且 session_status: "current" 将解析为过时的 沙箱密钥。桥接构建器位于 extensions/copilot/src/tool-bridge.ts 中,并镜像 PI 在 src/agents/pi-embedded-runner/run/attempt.ts:1029-1117 的权威调用。有两个 PI 字段 在 MVP 版本中故意转发,并被跟踪为后续工作: sandbox(连接器尚未通过 resolveSandboxContext 进行路由) 以及 PI 工具搜索/代码模式机制 (toolSearchCatalogRefincludeCoreToolsincludeToolSearchControlstoolSearchCatalogExecutortoolConstructionPlan),这些机制在 SDK 边界没有对应项。

Copilot SDK 契约区分了 客户端级别 的 GitHub 令牌 (GitHubCopilotClientOptions.gitHubTokenCLI,用于验证 CLI 进程本身) 和 会话级别 的令牌 (SessionConfig.gitHubToken,它决定了该会话的内容排除、 模型路由和配额,并且在 createSessionresumeSession 上均受支持)。该通过 resolveCopilotAuth 一次解析身份验证,并在身份验证模式为 gitHubToken(显式的 auth.gitHubToken 或来自已配置 github-copilot 身份验证配置文件 的契约解析 resolvedApiKey)时设置这两个字段。 当解析的模式为 useLoggedInUser 时,会话级别字段 将被省略,以便 SDK 继续从已登录的 身份派生身份。

ask_user 是故意隐藏的 —— 请参阅上面的限制。