Code Mode 内部实现
运行时状态¶
| 方面 | 值 |
|---|---|
| 执行器 | Node(node:vm,默认)、QuickJS-WASI(捆绑插件) |
| 默认状态 | 已禁用 |
| 稳定性 | 实验性 OpenClaw 接口面(Codex Code Mode 是独立的、稳定的 Codex 测试框架接口面) |
| 目标接口面 | 通用 OpenClaw 代理运行 |
| 安全态势 | Node 是受信任的主机执行;QuickJS 提供加固的来宾隔离 |
| 面向用户的承诺 | 启用代码模式绝不会静默回退到广泛的直接工具暴露 |
范围¶
代码模式负责已准备好运行的面向模型编排形态。它 不负责模型选择、通道行为、认证、工具策略或工具实现。
范围内:模型可见的控制/直接工具定义、隐藏工具目录构建、执行器选择、用于 search/describe/call 的主机回调、挂起来宾程序的可恢复状态、输出/超时/内存/待处理调用/快照限制,以及嵌套工具调用的遥测/轨迹投影。
范围外:提供商原生的远程代码执行、Shell 执行语义、更改现有工具授权、持久化的用户编写脚本、来宾代码中的包管理器/文件/网络/模块访问,以及直接复用 Codex Code Mode 内部实现。
提供商拥有的工具(例如远程 Python 沙箱)是独立的工具。参见 代码执行。
术语¶
- 代码模式:OpenClaw 运行时模式,隐藏目录兼容的模型工具,并暴露
exec、wait以及所需的仅限直接工具。 - 执行器:拥有 JavaScript 求值及其继续状态的实现。Node 内置;QuickJS 是捆绑插件。核心拥有目录、工具授权和运行生命周期。
- 来宾运行时:评估模型代码的 Node VM 上下文或 QuickJS-WASI VM。
- 主机桥接:从来宾代码返回 OpenClaw 的窄 JSON 兼容回调接口面。
- 目录:在常规工具策略、插件、MCP 和客户端工具解析之后的运行范围有效工具列表。
- 嵌套工具调用:从来宾代码通过主机桥接发起的工具调用。
- 快照:序列化的 QuickJS-WASI VM 状态,保存以便
wait可以恢复挂起的代码模式运行。 - 继续状态:执行器拥有的挂起单元状态。Node 保留活动的工作上下文;QuickJS 保留快照。
嵌套工具执行¶
每个嵌套工具调用都会跨越主机桥接并重新进入 OpenClaw,保留:活动代理 id、会话 id 和密钥、发送方和通道上下文、沙箱策略、审批策略、插件 before_tool_call 钩子、中止信号、可用时的流式更新,以及轨迹/审计事件。
已完成的嵌套调用会持久化为有界、脱敏的仅显示活动,并在历史记录重新加载时保留其原始父级和调用 id。提供商重放仅包含实际的模型调用;子活动不会添加合成模型轮次。开始和部分更新保持临时状态。较早的缺失子历史无法从源代码或外部结果中重建。
嵌套工具故障会作为可捕获的 JavaScript 错误进入来宾。如果来宾代码未捕获错误,exec 或 wait 会返回失败的工具结果,并且代理可以正常继续。遵循 工具错误指南 在选择其他操作之前检查可能的部分影响。网络控制的工具输出和错误保留其现有的不受信任内容包装和清理;故障后继续不会授予新权限或重放已完成的副作用。
嵌套调用遵循每个工具的 executionMode。"sequential" 工具会等待较早的目录调用完成,并阻塞后续调用,直到其结果被接受。支持并行的调用可以在下一个顺序调用之前重叠。使用相同运行目录的单元共享调度;不同目录保持独立。当调用者或目录关闭时,排队调用会被取消。
maxPendingToolCalls 限制的是进行中的桥接请求,而不是普通 Promise.all 批次的大小。超出该上限的调用和计时器会在来宾中与 Swarm 请求一起等待。最多可以排队 128 个普通请求,独立于配置的进行中上限,并使用现有已接受的桥接限制上限。Swarm 启动、笔记和结果等待不会消耗此普通配额;它们现有的组、内存和继续状态限制仍然适用。排队输入和请求标识在 wait 后保留;clearTimeout 会移除排队计时器而不会启动主机计时器。排队计时器的延迟在获得桥接槽位时开始。来宾继续状态在等待请求重新填充可用槽位之前运行,并且快速请求仍会在同一个 exec 或 wait 内处理完毕。
创建超出其队列配额允许的普通请求数量,会使工作器分支以 invalid_input 失败,并给出等待更小批次的指导。捕获立即的 JavaScript 错误不会允许部分批次:不会从该同步边界分派新调用。较早的工作器分支可能已经运行了工具;检查它们的影响,而不是重放该单元。排队不会提高内存、快照、时间或无头总工具调用限制,也不会绕过取消和策略检查。
运行与快照生命周期¶
每个代码模式运行都在进程内映射中按 runId 键跟踪(不持久化到磁盘或数据库)。exec/wait 返回三种结果状态之一:completed、waiting 或 failed。
waiting结果会保留执行器继续状态、待处理桥接请求和范围元数据(代理运行 id、会话 id/密钥),直到wait恢复它或它过期。- 过期、错误会话、错误运行以及未知/正在恢复的
runId值不会产生不同的终止状态;它们会以failed结果(code: "invalid_input")出现,并带有诸如code mode run is unavailable or expired.或code mode run belongs to a different session.之类的消息。 - 运行的继续状态一旦结束为
completed或failed就会释放,或在 Gateway 关闭时被丢弃(重启后不会保留任何内容:这是临时运行时状态)。 - OpenClaw 限制每个进程中并发挂起运行的数量(64),并以
too many suspended code mode runs.拒绝超出该上限的新挂起。
所选执行器在单元格生命周期内保持固定。挂起状态受上述每进程上限和 snapshotTtlSeconds 约束。QuickJS 还会在将待处理工作移交给 Gateway 之前,检查序列化后的 VM 大小(包括引擎元数据)是否超过 maxSnapshotBytes。Node 会保留一个活动 worker,没有可测量的序列化快照。这些限制和 memoryLimitBytes 都不是 Gateway 的总 RSS 限制;worker 开销和主机侧工具值也会消耗内存。Node 的 memoryLimitBytes 配置一个尽力而为的 V8 worker 堆预算,覆盖老年代和新生代,并受引擎最低要求约束。它包括 worker 运行时分配,不包括外部缓冲区。continuation 报告的保留字节数是诊断性估算,不是强制预留或聚合内存配额。Node 的活动上下文可能比受大小限制的 QuickJS 快照使用更多总主机内存。参见 Code Mode 执行器。
显式 results.save(value) 引用会在现有已准入目录的生命周期内保留规范化 JSON,独立于每个单元格的 VM 和输出预算。该存储最多准入 64 个条目以及 min(memoryLimitBytes, maxSnapshotBytes) 编码后的 JSON 字节数(默认 10 MiB),此外还有单元格的程序数据收件箱。这是逻辑数据配额,不是进程 RSS 限制。容量错误会保留现有条目;删除会释放其容量。加载返回分离副本,并将网络内容来源信息传递到接收单元格的常规不可信输出包装器中。
交互式单元格还会在字节数或模型结果适配会导致截断时自动保留最终结构化 JSON。worker 只序列化最终值一次,并最多保留显示配额与现有内存/快照数据配额中较大者;只有符合条件的交互式单元格会请求此捕获。目录准入会在为有界预览构建解析另一份完整 JSON 副本之前,检查剩余字节数和条目数。规范化字符串会移入同一存储。最终投影会在将剩余显示空间分配给采样描述和输出之前,预留一个可用引用;不可显示的引用会被释放。准入失败仍是一个成功的部分结果,并带有精确的未保留原因。无头执行和重启安全执行不会分配自动引用。
目录拆除、替换、限制以及已准入运行的中止会清除已保存数据。追加的客户端工具保留相同的结果存储生命周期,包括已经停在 wait 中的单元格。每个单元格在执行前都会捕获该存储,因此过期单元格无法采用替换存储,也无法在权限变更后保留引用。
已保存的引用是数据快照,绝不是执行权限。它们不会在 Gateway 重启后存活,也不能被其他运行或会话使用。
QuickJS-WASI 运行时¶
捆绑的 code-mode-quickjs 执行器插件拥有其 quickjs-wasi 依赖项和 worker 实现。选择 Node 不需要加载 WASM 运行时。
运行时职责:编译/加载 QuickJS-WASI WebAssembly 模块;为每次 code-mode 运行或恢复创建一个隔离 VM;通过稳定名称注册主机回调;设置内存和中断限制;求值 JavaScript;排空待处理任务;快照挂起的 VM 状态;为 wait 恢复快照;在终止状态后释放 VM 句柄和快照。
快照缓冲区在 worker 和 Gateway 之间直接传输,无需通过存储序列化格式复制 VM 堆。
运行时在 Node.js worker 线程中执行,位于 OpenClaw 主事件循环之外。来宾无限循环不得无限期阻塞 Gateway 进程;worker 的中断处理程序会强制执行墙钟超时,而不依赖来宾代码的配合。
Node 运行时¶
Node 执行器在 Gateway 主事件循环之外的全新 node:vm 上下文中求值 JavaScript。它使用与 QuickJS 相同的来宾控制器和 JSON 工具桥接。挂起会保留 worker、上下文和待处理 Promise;wait 会继续该上下文,而不会重放源代码。取消、过期、终止结果和关闭会释放保留的执行。
成功完成后,Node 最多保持四个空闲 worker 各预热五分钟,只复用具有相同运行时入口和堆限制的 worker。复用范围限于创建它的 Gateway 或 CLI 命令生命周期内;关闭该宿主会触发 worker 退役。没有宿主的独立执行器调用会立即关闭其已完成的 worker。每个新单元格仍会获得一个全新的 VM 上下文。运行时入口变更和严重内存压力会使空闲 worker 退役;内存压力不会丢弃挂起的 continuation。失败、超时或中止的单元格会使它们的 worker 退役。
worker 监督会将失控计算隔离在主事件循环之外。worker 线程和 node:vm 都不提供操作系统安全边界。请使用 执行器指南 选择适当的信任模型。
TypeScript¶
TypeScript 风格的签名通过快速索引、目录句柄和 API.read 声明文件向模型描述工具输入和输出。未知输出保持为 unknown,声明不会授予对额外工具的访问权限。
可执行单元格是纯 JavaScript。Code Mode 不会加载 TypeScript 编译器、剥离注解或执行程序类型检查。所选引擎会直接解析并运行 JavaScript。工具调用仍使用现有的运行时输入和输出验证、策略以及审批所有者。在较早调用已产生效果后,后续调用仍可能失败,因此在重试失败单元格之前,请遵循 恢复指南。
安全边界¶
Node 是可信执行的默认执行器。其预期全局对象和模块守卫并不会使 node:vm 成为安全边界。模型输出可能受到 Prompt 注入影响;当来宾必须与宿主隔离时,请选择捆绑的 QuickJS 执行器。
QuickJS 在一个没有宿主文件系统、网络、子进程、环境或模块访问权限的 WASM 来宾环境中执行。它使用引擎内存和中断限制、父级墙钟截止时间,以及有界序列化快照。
两个执行器都在主事件循环之外运行,使用相同的 JSON 工具桥接,强制执行输出和待处理调用上限,并在超时、中止、会话结束或过期时释放继续执行上下文。核心保留工具策略、审批、钩子和会话所有权。对 Code Mode 和 Tool Search 控制工具的递归访问被排除在来宾目录之外。这些共享工具检查不包含 Node VM 逃逸,并且授予一个强大工具仍会将其能力授予 QuickJS 来宾。有关面向操作人员的选项和操作系统隔离指南,请参阅 Code Mode 执行器。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw