Appearance
本规范说明如何使 Codex app-server 调用 OpenClaw 上下文引擎插件的 assemble、afterTurn、ingest 等生命周期,实现与 PI 嵌入式智能体相同的上下文管理行为。关键操作:配置 agents.defaults.embeddedHarness.runtime: "codex" 或使用 codex/* 模型,并启用上下文引擎插件。注意:Codex 原生线程状态不可直接修改,上下文引擎组装的结果只能通过提示文本注入;若 assemble 失败,回退到原始提示路径;上下文引擎拥有的压缩仍作为 OpenClaw/插件状态的主结果,Codex 原生压缩独立记录。
OpenClaw Codex 上下文引擎集成:生命周期配置与实现
状态
草案实现规范。
目标
使内置的 Codex app-server 嵌入式智能体满足与 OpenClaw 上下文引擎相同的生命周期契约,该契约目前由 PI 嵌入式智能体实现。
使用 agents.defaults.embeddedHarness.runtime: "codex" 或 codex/* 模型的会话,应仍能让选中的上下文引擎插件(如 lossless-claw)控制上下文组装、轮次后 ingest、维护以及在 Codex app-server 允许范围内的 OpenClaw 层级压缩策略。
非目标
- 不要重写 Codex app-server 内部。
- 不要让 Codex 原生线程压缩产生 lossless-claw 摘要。
- 不要要求非 Codex 模型使用 Codex 嵌入式智能体。
- 不要改变 ACP/acpx 会话行为。本规范仅适用于非 ACP 的嵌入式智能体路径。
- 不要要求第三方插件注册 Codex app-server 扩展工厂;现有的内置插件信任边界保持不变。
当前架构
嵌入式运行循环在每次运行前解析配置的上下文引擎,再选择具体的低层级智能体:
src/agents/pi-embedded-runner/run.ts- 初始化上下文引擎插件
- 调用
resolveContextEngine(params.config) - 将
contextEngine和contextTokenBudget传入runEmbeddedAttemptWithBackend(...)
runEmbeddedAttemptWithBackend(...) 委托给选中的智能体:
src/agents/pi-embedded-runner/run/backend.tssrc/agents/harness/selection.ts
Codex app-server 智能体由内置的 Codex 插件注册:
extensions/codex/index.tsextensions/codex/harness.ts
Codex 智能体实现接收与 PI 智能体相同的 EmbeddedRunAttemptParams:
extensions/codex/src/app-server/run-attempt.ts
这意味着所需的钩子点在 OpenClaw 控制的代码中。外部边界是 Codex app-server 协议本身:OpenClaw 可以控制发送给 thread/start、thread/resume、turn/start 的数据,并可以观察通知,但无法改变 Codex 内部的线程存储或原生压缩器。
当前差距
PI 嵌入式智能体直接调用上下文引擎生命周期:
- 运行前 bootstrap/维护
- 模型调用前 assemble
- 运行后 afterTurn 或 ingest
- 成功轮次后的维护
- 对拥有压缩的引擎进行的上下文引擎压缩
相关 PI 代码:
src/agents/pi-embedded-runner/run/attempt.tssrc/agents/pi-embedded-runner/run/attempt.context-engine-helpers.tssrc/agents/pi-embedded-runner/context-engine-maintenance.ts
Codex app-server 智能体目前运行通用的智能体钩子并镜像转录,但不调用 params.contextEngine.bootstrap、params.contextEngine.assemble、params.contextEngine.afterTurn、params.contextEngine.ingestBatch、params.contextEngine.ingest 或 params.contextEngine.maintain。
相关 Codex 代码:
extensions/codex/src/app-server/run-attempt.tsextensions/codex/src/app-server/thread-lifecycle.tsextensions/codex/src/app-server/event-projector.tsextensions/codex/src/app-server/compact.ts
期望行为
对于 Codex 智能体的轮次,OpenClaw 应保持以下生命周期:
- 读取 OpenClaw 会话转录镜像。
- 当存在之前的会话文件时,bootstrap 活动的上下文引擎。
- 在可用时运行 bootstrap 维护。
- 使用活动的上下文引擎组装上下文。
- 将组装后的上下文转换为 Codex 兼容的输入。
- 使用包含上下文引擎
systemPromptAddition的开发者指令启动或恢复 Codex 线程。 - 使用组装后的用户提示启动 Codex 轮次。
- 将 Codex 结果镜像回 OpenClaw 转录。
- 如果实现了
afterTurn则调用,否则调用ingestBatch/ingest,使用镜像的转录快照。 - 在成功的非中止轮次后运行轮次维护。
- 保留 Codex 原生压缩信号和 OpenClaw 压缩钩子。
设计约束
Codex app-server 仍然是原生线程状态的权威
Codex 拥有自己的原生线程和任何内部扩展历史。OpenClaw 不应试图改变 app-server 的内部历史,除非通过支持的协议调用。
OpenClaw 的转录镜像仍是 OpenClaw 特性(聊天历史、搜索、/new 和 /reset 记录、未来模型或智能体切换、上下文引擎插件状态)的来源。
上下文引擎组装必须投影为 Codex 输入
上下文引擎接口返回 OpenClaw AgentMessage[],而非 Codex 线程补丁。Codex app-server turn/start 接受当前用户输入,而 thread/start 和 thread/resume 接受开发者指令。
因此需要一个投影层。安全的初始版本应避免假装可以替换 Codex 内部历史,而是将组装后的上下文作为确定性提示/开发者指令材料注入到当前轮次周围。
提示缓存稳定性很重要
对于 lossless-claw 等引擎,组装后的上下文应对不变输入具有确定性。不要在生成的上下文文本中添加时间戳、随机 id 或不稳定顺序。
运行时选择语义不变
智能体选择保持不变:
runtime: "pi"强制 PIruntime: "codex"选择已注册的 Codex 智能体runtime: "auto"让插件声明支持的供应商- 不匹配的
auto运行使用 PI
本工作改变的是选中 Codex 智能体后发生的行为。
实现计划
1. 导出或重构可重用的上下文引擎智能体辅助函数
目前可重用的生命周期辅助函数位于 PI 运行器下:
src/agents/pi-embedded-runner/run/attempt.context-engine-helpers.tssrc/agents/pi-embedded-runner/run/attempt.prompt-helpers.tssrc/agents/pi-embedded-runner/context-engine-maintenance.ts
如果可以避免,Codex 不应从名称暗示 PI 的实现路径中导入。
创建一个智能体中立的模块,例如:
src/agents/harness/context-engine-lifecycle.ts
移动或重新导出:
runAttemptContextEngineBootstrapassembleAttemptContextEnginefinalizeAttemptContextEngineTurnbuildAfterTurnRuntimeContextbuildAfterTurnRuntimeContextFromUsage- 一个对
runContextEngineMaintenance的薄封装
通过从旧文件重新导出或更新同一 PR 中的 PI 调用点来保持 PI 导入正常工作。
新的辅助函数名称不应提及 PI。
建议名称:
bootstrapHarnessContextEngineassembleHarnessContextEnginefinalizeHarnessContextEngineTurnbuildHarnessContextEngineRuntimeContextrunHarnessContextEngineMaintenance
2. 添加 Codex 上下文投影辅助函数
添加一个新模块:
extensions/codex/src/app-server/context-engine-projection.ts
职责:
- 接受组装后的
AgentMessage[]、原始镜像历史以及当前提示。 - 确定哪些上下文属于开发者指令,哪些属于当前用户输入。
- 将当前用户提示保留为最终可操作请求。
- 以稳定、显式的格式渲染先前的消息。
- 避免易变元数据。
建议 API:
ts
export type CodexContextProjection = {
developerInstructionAddition?: string;
promptText: string;
assembledMessages: AgentMessage[];
prePromptMessageCount: number;
};
export function projectContextEngineAssemblyForCodex(params: {
assembledMessages: AgentMessage[];
originalHistoryMessages: AgentMessage[];
prompt: string;
systemPromptAddition?: string;
}): CodexContextProjection;推荐的首个投影方案:
- 将
systemPromptAddition放入开发者指令。 - 将组装后的转录上下文放在
promptText中当前提示之前。 - 明确标记为 OpenClaw 组装上下文。
- 将当前提示放在最后。
- 如果当前用户提示已出现在尾部,则排除重复。
示例提示形状:
text
OpenClaw assembled context for this turn:
<conversation_context>
[user]
...
[assistant]
...
</conversation_context>
Current user request:
...这不如原生 Codex 历史操作优雅,但它可以在 OpenClaw 内实现并保留上下文引擎语义。
未来改进:如果 Codex app-server 暴露了替换或补充线程历史的协议,可将此投影层切换为使用该 API。
3. 在 Codex 线程启动前接入 bootstrap
在 extensions/codex/src/app-server/run-attempt.ts 中:
- 照常读取镜像会话历史。
- 判断在本次运行前会话文件是否存在。建议在镜像写入前使用辅助函数检查
fs.stat(params.sessionFile)。 - 如果辅助函数需要,打开一个
SessionManager或使用窄适配器。 - 当
params.contextEngine存在时,调用中立的 bootstrap 辅助函数。
伪流程:
ts
const hadSessionFile = await fileExists(params.sessionFile);
const sessionManager = SessionManager.open(params.sessionFile);
const historyMessages = sessionManager.buildSessionContext().messages;
await bootstrapHarnessContextEngine({
hadSessionFile,
contextEngine: params.contextEngine,
sessionId: params.sessionId,
sessionKey: sandboxSessionKey,
sessionFile: params.sessionFile,
sessionManager,
runtimeContext: buildHarnessContextEngineRuntimeContext(...),
runMaintenance: runHarnessContextEngineMaintenance,
warn,
});使用与 Codex 工具桥和转录镜像相同的 sessionKey 约定。目前 Codex 从 params.sessionKey 或 params.sessionId 计算 sandboxSessionKey;除非有理由保留原始 params.sessionKey,否则一致使用它。
4. 在 thread/start / thread/resume 和 turn/start 前接入 assemble
在 runCodexAppServerAttempt 中:
- 先构建动态工具,使上下文引擎看到实际可用的工具名称。
- 读取镜像会话历史。
- 当
params.contextEngine存在时,运行上下文引擎assemble(...)。 - 将组装结果投影为:
- 开发者指令附加内容
- 供
turn/start使用的提示文本
现有的钩子调用:
ts
resolveAgentHarnessBeforePromptBuildResult({
prompt: params.prompt,
developerInstructions: buildDeveloperInstructions(params),
messages: historyMessages,
ctx: hookContext,
});应变为上下文感知:
- 使用
buildDeveloperInstructions(params)计算基础开发者指令。 - 应用上下文引擎组装/投影。
- 使用投影后的提示/开发者指令运行
before_prompt_build。
此顺序使通用提示钩子看到 Codex 将接收到的相同提示。如果需要严格的 PI 对等性,应在钩子组合之前运行上下文引擎组装,因为 PI 在其提示管线之后将上下文引擎 systemPromptAddition 应用到最终系统提示。重要的不变性是:上下文引擎和钩子都有确定性的、文档化的顺序。
首次实现的推荐顺序:
buildDeveloperInstructions(params)- 上下文引擎
assemble() - 将
systemPromptAddition追加/前置到开发者指令 - 将组装消息投影到提示文本
resolveAgentHarnessBeforePromptBuildResult(...)- 将最终开发者指令传递给
startOrResumeThread(...) - 将最终提示文本传递给
buildTurnStartParams(...)
规范应编码在测试中,以免将来意外更改顺序。
5. 保持提示缓存稳定的格式
投影辅助函数必须为相同输入产生字节稳定的输出:
- 稳定的消息顺序
- 稳定的角色标签
- 无生成的时间戳
- 无对象键顺序泄漏
- 无随机定界符
- 无每次运行的 id
使用固定定界符和显式节。
6. 在轮次后转录镜像后接入 finalize
Codex 的 CodexAppServerEventProjector 为当前轮次构建本地 messagesSnapshot。mirrorTranscriptBestEffort(...) 将该快照写入 OpenClaw 转录镜像。
在镜像成功或失败后,使用最佳可用消息快照调用上下文引擎 finalizer:
- 优先使用写入后的完整镜像会话上下文,因为
afterTurn期望会话快照而非仅当前轮次。 - 如果无法重新打开会话文件,回退到
historyMessages + result.messagesSnapshot。
伪流程:
ts
const prePromptMessageCount = historyMessages.length;
await mirrorTranscriptBestEffort(...);
const finalMessages = readMirroredSessionHistoryMessages(params.sessionFile)
?? [...historyMessages, ...result.messagesSnapshot];
await finalizeHarnessContextEngineTurn({
contextEngine: params.contextEngine,
promptError: Boolean(finalPromptError),
aborted: finalAborted,
yieldAborted,
sessionIdUsed: params.sessionId,
sessionKey: sandboxSessionKey,
sessionFile: params.sessionFile,
messagesSnapshot: finalMessages,
prePromptMessageCount,
tokenBudget: params.contextTokenBudget,
runtimeContext: buildHarnessContextEngineRuntimeContextFromUsage({
attempt: params,
workspaceDir: effectiveWorkspace,
agentDir,
tokenBudget: params.contextTokenBudget,
lastCallUsage: result.attemptUsage,
promptCache: result.promptCache,
}),
runMaintenance: runHarnessContextEngineMaintenance,
sessionManager,
warn,
});如果镜像失败,仍应将回退快照传递给 afterTurn,但记录日志说明上下文引擎正在使用回退轮次数据。
7. 规范化使用情况和提示缓存运行时上下文
Codex 结果包含来自 app-server 令牌通知(可用时)的规范化使用情况。将该使用情况传递给上下文引擎运行时上下文。
如果 Codex app-server 最终暴露缓存读/写详细信息,将其映射为 ContextEnginePromptCacheInfo。在此之前,省略 promptCache 而非虚构零值。
8. 压缩策略
有两个压缩系统:
- OpenClaw 上下文引擎
compact() - Codex app-server 原生
thread/compact/start
不要静默混淆它们。
/compact 和显式 OpenClaw 压缩
当选中的上下文引擎具有 info.ownsCompaction === true 时,显式 OpenClaw 压缩应优先使用上下文引擎的 compact() 结果来更新 OpenClaw 转录镜像和插件状态。
当选中的 Codex 智能体具有原生线程绑定时,我们可以额外请求 Codex 原生压缩以保持 app-server 线程健康,但这必须在 details 中作为单独的后端操作报告。
推荐行为:
- 如果
contextEngine.info.ownsCompaction === true:- 先调用上下文引擎
compact() - 然后最佳努力地调用 Codex 原生压缩(当存在线程绑定时)
- 将上下文引擎结果作为主要结果返回
- 在
details.codexNativeCompaction中包含 Codex 原生压缩状态
- 先调用上下文引擎
- 如果活动的上下文引擎不拥有压缩:
- 保持当前的 Codex 原生压缩行为
这可能需要修改 extensions/codex/src/app-server/compact.ts 或从通用压缩路径中包装它,具体取决于 maybeCompactAgentHarnessSession(...) 的调用位置。
轮次中 Codex 原生 contextCompaction 事件
Codex 可能在轮次期间发出 contextCompaction 条目事件。保持 event-projector.ts 中当前的压缩前后钩子发射,但不要将其视为完成的上下文引擎压缩。
对于拥有压缩的引擎,当 Codex 仍然执行原生压缩时,发出显式诊断:
- stream/event 名称:现有的
compactionstream 可以接受 - details:
{ backend: "codex-app-server", ownsCompaction: true }
这使得拆分可审计。
9. 会话重置和绑定行为
现有的 Codex 智能体 reset(...) 从 OpenClaw 会话文件中清除 Codex app-server 绑定。保留该行为。
同时确保上下文引擎状态清理继续通过现有的 OpenClaw 会话生命周期路径进行。除非上下文引擎生命周期目前对所有智能体缺少 reset/delete 事件,否则不要添加 Codex 特定的清理。
10. 错误处理
遵循 PI 语义:
- bootstrap 失败时警告并继续
- assemble 失败时警告并回退到未组装的管线消息/提示
- afterTurn/ingest 失败时警告并将后轮次 finalization 标记为不成功
- 维护仅在进行成功、非中止、非 yield 的轮次后运行
- 压缩错误不应作为新的提示重试
Codex 特定补充:
- 如果上下文投影失败,警告并回退到原始提示
- 如果转录镜像失败,仍尝试使用回退消息进行上下文引擎 finalization
- 如果上下文引擎压缩成功后 Codex 原生压缩失败,当上下文引擎是主要压缩时,不应导致整个 OpenClaw 压缩失败
测试计划
单元测试
在 extensions/codex/src/app-server 下添加测试:
run-attempt.context-engine.test.ts- 当会话文件存在时,Codex 调用
bootstrap - Codex 使用镜像消息、令牌预算、工具名称、引用模式、模型 id 和提示调用
assemble systemPromptAddition包含在开发者指令中- 组装消息在请求之前投影到提示中
- Codex 在转录镜像后调用
afterTurn - 没有
afterTurn时,Codex 调用ingestBatch或逐消息ingest - 成功轮次后运行轮次维护
- 提示错误、中止或 yield 中止时不运行轮次维护
- 当会话文件存在时,Codex 调用
context-engine-projection.test.ts- 相同输入产生稳定输出
- 当组装历史包含当前提示时,不重复当前提示
- 处理空历史
- 保持角色顺序
- 系统提示补充仅出现在开发者指令中
compact.context-engine.test.ts- 拥有压缩的上下文引擎主要结果获胜
- 当也尝试时,Codex 原生压缩状态出现在 details 中
- Codex 原生失败不会导致拥有压缩的上下文引擎失败
- 不拥有压缩的上下文引擎保持当前原生压缩行为
需要更新的现有测试
extensions/codex/src/app-server/run-attempt.test.ts(如果存在),否则最近的 Codex app-server 运行测试extensions/codex/src/app-server/event-projector.test.ts仅当压缩事件详情改变时src/agents/harness/selection.test.ts除非配置行为改变,否则不应需要更改;应保持稳定- PI 上下文引擎测试应继续通过,无需更改
集成本地测试
添加或扩展 Codex 智能体冒烟测试:
- 配置
plugins.slots.contextEngine为测试引擎 - 配置
agents.defaults.model为codex/*模型 - 配置
agents.defaults.embeddedHarness.runtime = "codex" - 断言测试引擎观察到:
- bootstrap
- assemble
- afterTurn 或 ingest
- maintenance
避免在 OpenClaw 核心测试中要求 lossless-claw。使用仓库内的小型假上下文引擎插件。
可观测性
在 Codex 上下文引擎生命周期调用周围添加调试日志:
codex context engine bootstrap started/completed/failedcodex context engine assemble appliedcodex context engine finalize completed/failedcodex context engine maintenance skipped含原因codex native compaction completed alongside context-engine compaction
避免记录完整提示或转录内容。
在有用的地方添加结构化字段:
sessionIdsessionKey根据现有日志实践脱敏或省略engineIdthreadIdturnIdassembledMessageCountestimatedTokenshasSystemPromptAddition
迁移/兼容性
这应该是向后兼容的:
- 如果没有配置上下文引擎,旧版上下文引擎行为应与当前 Codex 智能体行为等效
- 如果上下文引擎
assemble失败,Codex 应继续使用原始提示路径 - 现有的 Codex 线程绑定应保持有效
- 动态工具指纹不应包含上下文引擎输出;否则每一次上下文变化都可能强制创建新的 Codex 线程。只有工具目录应影响动态工具指纹
待定问题
组装后的上下文应完全注入用户提示、完全注入开发者指令,还是拆分?
推荐:拆分。将
systemPromptAddition放入开发者指令;将组装后的转录上下文放入用户提示包装器中。这最符合当前的 Codex 协议,且无需修改原生线程历史。当上下文引擎拥有压缩时,是否应禁用 Codex 原生压缩?
推荐:暂时不。Codex 原生压缩可能仍需保持 app-server 线程活动。但必须将其报告为原生 Codex 压缩,而非上下文引擎压缩。
before_prompt_build应在上下文引擎组装之前还是之后运行?推荐:对于 Codex,在上下文引擎投影之后,这样通用智能体钩子看到的是 Codex 将接收到的实际提示/开发者指令。如果 PI 对等性要求相反的顺序,将选定的顺序编码在测试中并在此文档中记录。
Codex app-server 未来能否接受结构化的上下文/历史覆盖?
未知。如果可以,用该协议替换文本投影层,并保持生命周期调用不变。
验收标准
codex/*嵌入式智能体轮次调用选中的上下文引擎的 assemble 生命周期- 上下文引擎
systemPromptAddition影响 Codex 开发者指令 - 组装上下文确定性影响 Codex 轮次输入
- 成功的 Codex 轮次调用
afterTurn或 ingest 回退 - 成功的 Codex 轮次运行上下文引擎轮次维护
- 失败/中止/yield 中止的轮次不运行轮次维护
- 上下文引擎拥有的压缩对 OpenClaw/插件状态保持首要
- Codex 原生压缩保持可审计,作为原生 Codex 行为
- 现有 PI 上下文引擎行为不变
- 当未选中非旧版上下文引擎或组装失败时,现有 Codex 智能体行为不变
常见问题
我怎么让 Codex 智能体使用 lossless-claw 或其他上下文引擎?
确保在配置中启用了上下文引擎插件(如 plugins.slots.contextEngine),并为模型设置 agents.defaults.embeddedHarness.runtime: "codex" 或使用 codex/* 模型。OpenClaw 会自动将上下文引擎生命周期集成到 Codex 智能体运行中。
Codex 的 assemble 失败了,智能体会怎样?
如果上下文引擎的 assemble 失败,OpenClaw 会记录警告并回退到未组装的管线消息和原始提示。Codex 智能体将继续使用默认的提示构建流程运行。
上下文引擎拥有压缩时,Codex 原生压缩是否仍然执行?
是的,暂时仍会执行。上下文引擎的 compact() 结果是 OpenClaw/插件状态的主数据;Codex 原生压缩作为独立后端操作运行,其状态记录在 details.codexNativeCompaction 中。这不会干扰上下文引擎的压缩结果。