用户输入与执行
harness 如何向人提问、将其工具表面绑定到宿主能力、处理已配置 exec reviewer 的三种结果,并收窄配对设备命令权限。属于 Agent harness plugins 参考的一部分。
用户输入与工具表面¶
暴露运行时级用户输入请求的原生 harness 应使用 openclaw/plugin-sdk/agent-harness-runtime 中的用户输入辅助函数来格式化提示、通过 OpenClaw 的阻塞回复路径投递提示,并将选择/自由文本答案规范化回运行时的原生响应形状。该辅助函数保持 channel/TUI 展示一致,同时每个 harness 保留自己的协议解析和待处理请求生命周期。
OpenClaw 自身的阻塞式问题工具——ask_user 和一个 secrets 请求——是另一种情况。它们注册一个 Gateway 问题然后等待,而让人能够回答该问题的提示由运行该工具的一方发布。工具经过嵌入式工具生命周期的 harness 会从其工具启动处理器获得该发布。自行分发工具的 harness 则改为在构建工具表面的每条路径上向 createOpenClawCodingTools 传递 questionPrompt——侧线程就是它自己的一条此类路径:send 是该运行的 onToolResult,messageChannel 是提示将会出现的会话。如果省略它,问题会被注册但永远不会显示,因此该轮次会等待完整超时,然后报告没有人回答。
对于基于 schema 的表单和字面 URL 确认,请使用同一子路径中的 agentHarnessStructuredInput 运行时表面。它在不调用访问器的情况下快照有界的自有数据,将受支持的基本字段编译为 Gateway 问题,并以批处理、秘密输入、超时和取消围栏执行它们。harness 保留其协议信封的所有权,并必须传递精确的轮次信号和活跃所有者检查;run(...) 返回已回答、已拒绝、已取消或不受支持的结果,供适配器转换。
使用原生问题辅助函数时,请将原始准备好的尝试(包括其精确的 hostCapabilities 对象)作为 delivery 传递。Core 会在发布任何 steering 句柄之前捕获问题创建者的已准备调用方策略和生命周期。能力对象的副本不携带该绑定。内置工具捕获其创建作用域;CLI 原生问题在 tool-cap 转换之前保留原始调用方策略。回答轮次的模型选择或排队操作永远不会取代问题创建者的权限。
即使运行时无法接受普通 steering,纯文本 channel 回答也使用此创建者绑定。缺失、已关闭或不匹配的创建者权限会产生可见的拒绝,而不是新的 agent 轮次。传入来源和创建者都必须保持当前有效,直到最终答案分发。由 Gateway 启动的 CLI MCP 工具使用相同的原始调用方快照,并绑定到其精确的实时授权。独立 attach 授权没有准备好的运行快照;它们的问题保留结构化问题控件,但不接受普通 channel 文本。
在 runAgentHarnessGatewayQuestion(...) 或 agentHarnessStructuredInput.run(...) 中省略 gatewayCall,即可使用由 core 拥有的 Gateway 传输。它会携带每个输入的来源和 backing-run 断言,经过注册、持久化、连接准备和 hello,然后在发送 resolve 请求之前立即同步检查。被拒绝的输入只释放其自身的预留:问题保持待处理,其提示和后续有效输入仍可使用。持久化和本地预留并不构成已回答转换。分发后的关闭不会使已接受的答案可重放。纯文本提交在 question.resolve 上携带一个新的、有界的 resolutionId;问题所有者仅在该提交被提交时记录它。宿主等待方在 question.waitAnswer 上请求 includeResolutionId: true,并使用该回执利用问题等待方现有的截止时间恢复丢失的响应,而不是使用单独的更短计时器。另一个 actor 的回答,即使是相同文本,也不会确立对该输入的消耗。明确的 resolve 拒绝会立即释放该输入;已取消或已过期的等待方证明未消耗。
如果回执在等待方结算时缺失、被拒绝或仍为待处理,宿主会将该输入记录为未确认且不可重放,而不是再次通过普通 steering 发送它。这是路由所有权,而不是答案已提交的证明。Channel 回复、Gateway chat、Talk 和 TUI 会呈现这种不确定性,而不会启动另一轮次或取消已独立接受的 backing 工作。通知投递或来源采用失败不会释放该输入以供重放。backing-run 中止、超时和错误清理保留独立权限。
自定义传输必须保留这些请求和响应字段,用于丢失响应恢复。遗留的无回执响应仍为未确认;它不能证明另一个提交已回答该问题。resolutionId 是一个不透明的 1–128 个字符的关联值,而不是解决某个问题或重用闭源权限的许可。普通等待方省略 includeResolutionId(默认 false),并接收现有响应形状;问题记录、查找结果和广播事件永远不会获得该回执。该回执是瞬态问题生命周期状态,而不是持久记录或迁移。
随附的 AgentHarnessQuestionGatewayCall 函数类型保持不变。遗留函数覆盖对普通、无作用域的输入仍然有效,包括运行生命周期检查。仅带有遗留回调的来源绑定输入会在输入持久化或 resolution I/O 之前失败。函数参数数量或回调的存在并不能确立受保护传输支持。
自定义受保护传输则提供一个显式对象:
type QuestionDispatcher = Exclude<
Parameters<typeof agentHarnessStructuredInput.run>[0]["gatewayCall"],
AgentHarnessQuestionGatewayCall | undefined
>;
该对象具有 version: 2 和 call(request)。请求包含 method、
options(timeoutMs?)、params?、signal?,以及必需的 authority:
{ kind: "unscoped" } 或 { kind: "source-bound", assertCurrent }。
源绑定变体要求同步断言。在所有 await 准备完成后、每次分发或重试前立即调用它,中间不得有 await。切勿用观察者或响应后检查替代。
委托给 callGatewayTool 时,在其现有 extra 包中转发受保护断言,作为
dispatchAuthority: { version: 2, kind: "source-bound", assertCurrent }。
同一包接受 kind: "run" 用于仅运行断言。这些是本地代码契约,不是 Gateway 线字段、操作员设置或新的 SDK 导出。
每个准备好的尝试还会收到一个带版本的 params.hostCapabilities
对象。在暴露插件构建的 OpenClaw 工具之前使用 bindToolSurface(...),
并使用其策略和审批操作处理原生操作。工作目录与尝试不同的原生操作可以向
runBeforeToolCall(...) 传递 nativeOperation: { cwd };宿主会规范化该有界操作事实,同时保持身份和策略权限闭包绑定。闭包
绑定宿主解析的 run、sandbox、requester、route 和审批身份;
插件不得重建这些字段,也不得在尝试返回后保留该能力。
在尝试结算后发起的调用将以失败关闭方式处理。
当提供时,assertNativeSubagentSpawnAllowed() 必须在原生生成准入时运行。它拒绝模糊的参与者身份;引导模型使用
sessions_spawn,并将请求方已验证的 requester_profile.id 作为 user。捆绑的原生钩子准入可以返回同步守卫,中继会在 await 准备完成后、允许前立即重新检查;
返回的原因会成为模型可见的拒绝。
参与者检查使用现有的原生模型准入,包括其默认可选模式。当原生准入被禁用或不可用时,单人 Codex 轮次保留原生委托,且当原生生成仍可用时,其他人的输入会排队为后续处理。有效策略已禁用原生委托的线程仍允许跨配置文件引导。
如果回退尝试已包含多人,且原生生成在没有钩子准入的情况下仍可用,Codex 会拒绝该尝试,并要求发送方将请求作为新消息重新发送,使其作为自己的轮次运行。
当引导必须保留在轮次所有者的操作员配置文件中时,后端句柄可以声明 supportsCrossProfileSteering: false;省略该字段则允许跨配置文件引导。回复准入所有者在消息注入前应用此限制,包括通过该路径传递的问题答案。
对于独立保留的原生工作,在宿主能力处于活动状态时调用可选的 retainSourceAuthority()。当存在操作员来源时,返回的 assertCurrent、可选 signal 和幂等 release 会独立于前台完成保留该原始来源。
对于没有操作员来源的运行,该方法返回 undefined。它既不提供新的工具权限,也不提供替代前台能力。将其绑定到通过现有原生策略路径准入的精确资源,在进一步副作用前重新检查它,并在其 signal 中止时停止这些资源。将终止和结算保管与操作权限分开,并在拥有的资源结算后释放来源。报告清理失败,同时保留未结算资源的保管权。不要从会话归属或复用的进程 ID 重建所有权。
宿主发出 Gateway 生命周期退役信号。主动来源撤销也需要原始权限的 signal;Visitor Access 为授权过期和撤销提供它。仅断言的来源约束在副作用前检查,并且不提供异步撤销通知。
对于原生历史恢复,可选的 prepareContextMedia({ message, maxChars }) 会在同一宿主权限和当前媒体策略下重建已保存的用户附件。将其返回的文本和图片计入原生上下文预算;不要将它们作为无界后缀追加。有关限制和旧宿主行为,请参阅运行时媒体契约。
对于转入原生环境,可选的 resolveInputAttachmentMedia() 返回当前已准入输入的原始媒体事实的冻结、分离副本,包括通过内联投影从 params.media 中移除的图片。它在捕获尝试的实时权限下解析延迟的转录媒体;它不改变转录内容,也不授予文件读取权限。将来源验证和转移保留在 harness 的现有附件所有者中。没有此能力的旧宿主仅提供当前尝试的 params.media;不要从提示文本或历史重建缺失来源。
当轨迹捕获具有有效的宿主拥有的会话目标时,params.hostCapabilities.trajectory 提供闭包绑定的 recordEvent(...) 和 flush() 操作。宿主添加会话归属,限制并脱敏事件数据,并通过规范轨迹存储持久化它。将该能力视为可选,仅发送结构化的非机密事实,并在尝试结算前 await flush();当能力缺失时,不要推断存储路径或创建插件端回退。
新 harness 应实现 AgentHarnessV2,并将准备好的尝试类型化为 AgentHarnessAttemptParamsV2、EmbeddedRunAttemptParamsV2 和 AgentHarnessSideQuestionParamsV2;这些契约要求 hostCapabilities。采用 V2 的包必须声明 openclaw.compat.pluginApi: ">=2026.8.1"(或更高的下限),以便旧宿主在加载前拒绝它们。从运行时子路径导入参数类型:
import type {
AgentHarnessAttemptParamsV2,
AgentHarnessSideQuestionParamsV2,
EmbeddedRunAttemptParamsV2,
} from "openclaw/plugin-sdk/agent-harness-runtime";
旧版 AgentHarness、
AgentHarnessAttemptParams 和 EmbeddedRunAttemptParams 名称对现有插件保持
源码兼容,因此在 2026-10-12 之前,这些已弃用参数类型中的 capability 字段是可选的。公开的
AgentHarnessSideQuestionParams 契约具有相同的兼容窗口和可选字段。Core 仍会在每个选中的 attempt 上提供
该 capability。兼容性仅在类型层面:当前 harness 代码不得添加在没有
host capability 的情况下运行的运行时路径。
Compaction 实现使用 AgentHarnessCompactParams<2>(以及私有
native 桥的 AgentHarnessNativeCompactionParams<2>),并带有由 host 创建的
assertActive 和 retainSourceAuthority 视图。Core 会在准备、排队、native 完成和清理过程中保留
原始 source。已注册的
AgentHarness.compact 和 AgentHarnessV2.compact 回调签名保持
不变。在加载或调用 version 2 实现之前,必须在该回调边界处验证提供的 capability。省略类型参数会保留
旧版参数形状;选择 2 需要通过现有 SDK 类型名称要求 host capability。适配器不需要新的 runtime SDK 导出。较旧的
host 如果省略它,会在 native 工作之前收到可操作的失败;缺失
永远不会成为 System 权限。该 capability 的 runtime 版本仍为 1。
需要类似 PI 的 compact 工具路由的 native harness 应使用来自
openclaw/plugin-sdk/agent-harness-tool-runtime 的
createAgentHarnessToolSurfaceRuntime(...)。它负责
tool-search/code-mode 控制选择、local-model 精简默认值、
runtime 兼容的 schema 过滤、隐藏 catalog 执行、目录
填充和 catalog 清理。Harness 仍负责其 SDK 特定的工具
转换和 native 执行回调。
Native 工具适配器可以使用同一
子路径中的 runWithAsyncWorkResources(...),通过 host 拥有的已准入工作保留操作清理,同时
不扣留工具结果。使用其 onAcquired 回调注册清理;
在清理释放之前保持真实取消处于活动状态。当普通
操作清理必须在返回结果之前完成时,在已获取的资源上设置
releaseBeforeResultWhenIdle: true;已接受的受跟踪
工作仍会返回其逻辑结果,而无需等待准入。这保留的是资源,
而不是权限:排队的工作仍必须重新验证其原始 run、caller、
expiry 和 commit 守卫。超时、中止和失败结果必须立即关闭其
操作。手动自动化准入使用现有的每次调用
mutation receipt,仅加入激活,而不加入计划 payload 的生命周期。
在最后一个 policy filter、schema quarantine 和 native 注册
交集之后,在快照工具定义之前,调用来自
openclaw/plugin-sdk/agent-harness-runtime 的 finalizeAgentToolAvailability(tools, options?)。
它返回一个包含相同工具对象的新数组,并且只更新
host 拥有的依赖能力,例如当其 native
result reader 可调用时的 collector 生成。它不会添加工具、更改 profile、替换
executor,或重新绑定授权和审批包装器。
当 run 保留其无法执行的工具的 schema 时,传入 options.toolExecutionAllow。省略时使用提供的工具集;空列表不允许任何执行。
可选的同步 options.onPrepared(tool) 观察者用于识别其 owner 已参与的定义,因此 harness 可以刷新它们的缓存 schema 和
prompt 文本,而不更改无关定义。在后续过滤后重新应用最终化,并在每个工具上保留现有的 attempt 生命周期守卫。
最终化不会更新已在 native runtime 中注册的声明。
保留 native 拥有的 catalog 字节和指纹;当前 executor 守卫
仍会拒绝不可用的模式。新的 host 拥有的声明使用 harness 的
现有 catalog 注册生命周期。
OpenClaw Code Mode 的合并 agents.run() 路径保留内部等待;此
helper 不会在没有 native result reader 的情况下使原始 collector 调用可用。
Exec 审查器结果¶
来自
openclaw/plugin-sdk/agent-harness-exec-review-runtime 的
reviewExecRequestWithConfiguredModel(...) 返回一个
ExecAutoReviewDecision,该类型也由
openclaw/plugin-sdk/agent-harness-runtime 导出。插件使用者必须显式处理所有
三种结果:
allow-once且risk: "low"或risk: "medium":执行一次被审查的命令, 但须符合当前执行策略和权限检查。deny:不要执行该命令。将理由和拒绝指导 返回给 agent;永远不要将此结果升级为人工审批。ask:将该请求路由到人工审批。
拒绝指导会告知 agent 不要通过
变通方法、间接执行或策略规避来追求相同结果。它可以选择
实质更安全的替代方案,或者解释风险并要求用户单独批准
该确切命令。这不会将插件的 deny 结果变成
自动审批请求。
已配置的 exec 审查器在未配置审查器、provider 失败或超时、响应无效,或 allow 响应的风险不是 low 或 medium 时返回 ask。检测到面向审查器的 prompt 注入
会返回高风险的 deny。Facade 加载或审查器构造错误
仍可能拒绝 promise;错误永远不是执行权限。
沙箱子进程清理¶
在构建子进程 exec spec 之前,使用来自
openclaw/plugin-sdk/sandbox 的 prepareSandboxProcessCleanup(backend, env)。将其
返回的环境传递给 buildExecSpec,并将其 terminate 回调与
child owner 一起保留。在生成之前立即调用 exec spec 的 assertCurrent,
并在每次退出或启动失败时最终化 backend token。
Docker 和 Podman 提供 prepareProcessCleanup:一个活动 owner 会铸造一个随机
process marker 和一个仅用于终止的回调,并固定到该 runtime。撤销
执行会阻止新的准备、命令和文件写入,但之前
铸造的回调仍可以停止其标记的进程树。它不能执行任意
脚本或选择不同的 runtime。返回的 interrupt 回调
保留普通的活动执行检查,因为信号处理器可以运行 guest 代码。
没有此可选
capability 的 backend 保留现有的 shell 命令清理路径;它们必须根据自身的生命周期契约保留
清理权限。
配对设备执行¶
声明 cloudPlacement.devicePlacement.requiredNodeCommands,以指定 harness 需要在配对设备上执行的精确节点命令。Core 在创建所选 harness 的主机能力时会快照此集合。已获准的 Full access 会话只能通过节点策略的 invokeNodeWithSessionFull 回调授权这些命令;同一插件拥有的其他命令不会继承该权限。缺失的声明或未列出的命令会返回 undefined,因此策略必须使用其常规批准或拒绝路径。在尝试期间修改该声明无法扩大权限。
此声明会缩小权限范围;它本身不授予权限。配对、命令允许列表、托管同意、节点本地策略以及确切的实时会话、放置和轮次仍会独立强制执行。插件仍然是受信任的代码,不会由该回调进行沙箱隔离。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw