Files
pikasTech-agentrun/docs/reference/spec-v01-backend-codex.md
T

22 KiB
Raw Blame History

v0.1 Codex Stdio Backend/Profile 规格

Codex stdio backend 是 AgentRun v0.1 的第一真实 Code Agent backend kind。它用于证明 runner、backend adapter、Kubernetes Secret projection、真实 provider 调用、event normalization 和 terminal status 的完整闭环。v0.1 在同一个 backend kind 下支持 codexdeepseekminimax-m3dsflash-go 四个 profile;四者共享 Codex CLI app-server stdio 协议,只通过 profile/config/SecretRef/model catalog 隔离上游和模型。

在系统中的职责划分

  • 作为 backendProfile=codexbackendProfile=deepseekbackendProfile=minimax-m3backendProfile=dsflash-go 的共同具体实现。
  • 使用真实 Codex/Codex-compatible 配置执行最短 turn,不使用 fake provider 作为综合联调通过证据。
  • 消费 Kubernetes Secret projection 提供的 profile 专属 Codex auth.jsonconfig.toml 和 profile-local model-catalog.json
  • 把 Codex 输出归一化为 AgentRun 标准 events 和 terminal status。
  • 将 provider/auth/protocol/timeout/cancel 错误映射为 spec-v01-backend-adapter.md 定义的 failureKind。

协议选型与实现参考

v0.1 Codex stdio backend 的协议固定为 Codex CLI app-server JSON-RPC over stdio。Backend adapter 必须启动受控子进程:

codex app-server --listen stdio://

Adapter 通过 stdin 写入换行分隔 JSON-RPC 请求,通过 stdout 逐行读取 JSON-RPC response 和 notificationstderr 只作为有界诊断日志。最小请求序列是 initializethread/startthread/resumeturn/startresponse 中必须提取 thread/turn identitynotification 和后续输出必须归一化为 backend_statusassistant_messagetool_callcommand_outputerrorterminal_status events。运行中 steer 使用同一 app-server 进程的 turn/steer JSON-RPC 方法,参数为 threadIdexpectedTurnId 和文本 input 数组;取消/中断使用 turn/interrupt,参数为 threadIdturnId。已有 SessionRef.threadId 时只能执行 Codex stdio 原生 thread/resume 后接 turn/start;当 thread/resume 返回 no rollout found for thread id 或任何其他协议错误时,adapter 必须输出 thread-resume-failed 并终止当前 turn。adapter 不得启动替代 thread/start、拼接历史 prompt、回写新 threadId 或用其他上下文模拟继续会话。

若 run 的 ResourceBundleRef 包含 promptRefs 或 gitbundle .agents/skillsCodex adapter 只能消费 runner 已装配好的有界 initialPrompt、skill summary 和 skill registry path。对新 threadadapter 在首个 turn/start 中把 initialPrompt 和 skill facts 放在用户 message 之前;对 thread/resumeadapter 不重复注入 initialPrompt,只发送当前 command 的用户 message。当前 Codex app-server 若只有 input: [{ type: "text", text }],允许使用结构化文本前缀承载 initial prompt;若后续 app-server 支持 developer/runtime instruction item,优先映射到该标准 item。无论哪种 wire shapeevents 只记录 prompt/skill 的 path/hash/bytes/injected 状态,不输出全文。

不得把以下路径作为 v0.1 Codex stdio backend 的正式实现或综合联调通过证据:直接 Responses HTTP 代理、OpenAI SDK wrapper、codex exec 一次性命令输出、fake provider、固定文本回复、只读 shortcut 或本地 shell 模拟。裸 HTTP 或 codex exec --json 可以作为 provider/upstream 诊断,但最终通过必须来自 app-server stdio turn。

实现必须参考成熟代码:

参考 需要吸收的经验
UniDesk src/components/microservices/code-queue/src/code-agent/codex.ts Bun/TS 中 spawn codex app-server --listen stdio://、JSON-RPC request/response、thread start/resume、turn start、stderr 有界采集、exit/failure 分类。
UniDesk src/components/microservices/code-queue/src/code-agent/common.ts backend port/capability、model/profile 边界、文本 input shape、Git/proxy env 处理和 provider 端口归一化。
HWLAB internal/cloud/codex-stdio-session.mjs long-lived stdio session readiness、Codex home/workspace/protocol gate、child env redaction、trace recorder、cancel/timeout/failure kind。
HWLAB scripts/code-agent-chat-smoke.mjs fake app-server 自测试方式、thread/start + turn/start 调用顺序、session reuse、tool trace 和 Secret 不泄露断言。
HWLAB docs/reference/spec-v02-deepseek-proxy.md DeepSeek profile 通过稳定 bridge/Responses-compatible 入口接入,而不是把 DeepSeek 做成绕过 Codex stdio 的直接 HTTP backend。
HWLAB docs/reference/code-agent-chat-readiness.md provider/profile 切换时按 profile overlay、认证、网络、模型、Codex CLI/app-server 分层验证,并防止一个 profile 的成功掩盖另一个 profile 的退化。

这些参考用于协议和质量标准,不复制 UniDesk/HWLAB 的业务 prompt、硬件路径、tenant policy、hostPath Secret 做法或任何明文密钥。

业务 prompt 与 skill 必须通过 spec-v01-runtime-assembly.mdResourceBundleRef.promptRefs 和 gitbundle .agents/skills 完成。Codex backend 不内建 HWLAB 或 UniDesk 的业务文本;缺少 required prompt 时必须由 runner/manager 返回装配 blocker,不能落到 Codex 默认 skill 列表、用户长 prompt 或文本 fallback。

v0.1 Profile 定义

backendProfile SecretRef 配置来源 规则
codex agentrun-v01-provider-codex operator 当前 Codex auth.json/config.toml 现有默认 profile;实现 DeepSeek 时不得改变其默认模型、config authority 或真实联调路径。
deepseek agentrun-v01-provider-deepseek operator 准备的 DeepSeek-compatible Codex auth.json/config.toml 使用同一 codex app-server --listen stdio:// 协议,通过 config.toml 或等价 profile overlay 指向 DeepSeek-compatible upstream/model。
minimax-m3 agentrun-v01-provider-minimax-m3 从 HWLAB Code Queue 现有 MiniMax API key 派生的 MiniMax-M3 Codex auth.json/config.toml 沿 DeepSeek 相同路径使用 codex app-server --listen stdio://config.toml 指向 MiniMax OpenAI-compatible upstream,模型为 MiniMax-M3wire API 必须使用当前 Codex app-server 支持的 responses,不得继续使用已废弃的 chat
dsflash-go agentrun-v01-provider-dsflash-go DeepSeek V4 Flash / OpenCode Zen Go Codex auth.json/config.toml/model-catalog.json 沿 DeepSeek 相同路径使用 codex app-server --listen stdio://config.toml 必须使用 deepseek-v4-flashmodel_context_window=1000000model_auto_compact_token_limit=900000 和固定 model_catalog_json=/home/agentrun/.codex-dsflash-go/model-catalog.json

deepseek 的上游形态借鉴 HWLAB v0.2DeepSeek 是 provider profile,通过 Responses-compatible bridge、Moon Bridge 或等价稳定服务暴露给 Codex CLIAgentRun 不在 backend adapter 里手写 DeepSeek HTTP 转换器,也不把 DeepSeek 作为绕过 Codex app-server 的独立 backend kind。minimax-m3 也遵循同一原则:MiniMax-M3 是 Codex-compatible provider profile,不恢复旧 UniDesk Code Queue 的 MiniMax/OpenCode 直连路线,不新增独立 HTTP backend,不作为 fallback 或 judge backend。dsflash-go 是同一 Codex profile 机制下的 DeepSeek V4 Flash 1M context profile,必须通过 Moon Bridge / OpenCode Zen Go compatible path 和 model catalog 暴露模型元数据;若 compact path 不支持,adapter 必须输出 provider-compact-unsupported。上游 base URL、模型和 provider 名称可以作为 redacted metadata 输出;API Key、auth.jsonconfig.tomlmodel-catalog.json 原文不得输出。

Profile 切换规则:

  • backendProfile 是 run 的显式字段,manager 不得静默改写。
  • runner/backend 只读取与 backendProfile 同名的 provider credential;缺失则 secret-unavailable
  • 每次 run 必须使用 profile-scoped writable CODEX_HOME。Kubernetes Job 默认把选中 profile 的 Secret projection 复制到该 Job 独占的 /home/agentrun/.codex-<profile>host process 或复用进程必须使用 run/profile 独占目录,避免 codexdeepseekminimax-m3dsflash-go 互相污染。
  • deepseekminimax-m3dsflash-go 不得 fallback 到 codex Secret、模型或 upstreamcodex 也不得读取其他 profile Secret。
  • command payload 中显式提供 model 时可以透传给 Codex turn;未显式提供时以 profile config.toml 为 authority,不在 adapter 中写死默认模型。

测试凭据来源

v0.1 综合联调用的 Codex stdio profile 测试凭据源固定为 operator 环境中的以下两个文件形态:

~/.codex/auth.json
~/.codex/config.toml

这些文件只能作为 Kubernetes Secret 创建或轮换的输入源,不能通过 hostPath 挂载进 Pod,不能复制进镜像,不能提交到 source branch、GitOps branch、artifact catalog、issue、PR、event、trace、日志或 CLI 输出。codexdeepseekminimax-m3dsflash-go 可以来自不同 operator profile 目录或显式文件参数,但进入 Kubernetes 后必须是不同 SecretRef,除非后续规格明确批准某个共享 SecretRef 场景。minimax-m3 的 API key 输入源为 HWLAB Code Queue 现有 MiniMax secretdsflash-gomodel-catalog.json 可由 AgentRun manager 生成或由 operator 显式提供;迁移时只允许把值写入 Kubernetes Secret,不得打印或落库。

v0.1 默认 Kubernetes Secret

对象 v0.1 规格
Namespace agentrun-v01
Secret name agentrun-v01-provider-codexagentrun-v01-provider-deepseekagentrun-v01-provider-minimax-m3agentrun-v01-provider-dsflash-go
Secret key auth.json,来自 ~/.codex/auth.json
Secret key config.toml,来自 ~/.codex/config.toml
Secret key model-catalog.json,仅 dsflash-go 必需
Consumer runner 或 backend adapter Pod
Projection target 只读 projection,再复制到当前 run/profile 的 writable CODEX_HOME/auth.jsonCODEX_HOME/config.toml 和 profile 需要的额外文件
File mode 只读,建议 0400 或等价最小权限

Kubernetes Secret 的创建、轮换和权限控制属于集群密钥管理流程;source branch 只声明 SecretRef 名称、key 和 mount intent。deploy/deploy.json 和 rendered GitOps manifest 不得包含 Secret data。

Runtime 行为

  • Adapter 必须在调用 Codex 前验证 auth.jsonconfig.toml 均存在且可读;config.toml 声明 model_catalog_json 时还必须验证目标文件可读;dsflash-go 缺少 model_catalog_json 时也必须在 provider 调用前返回 secret-unavailable
  • Codex 运行时必须使用被投影的 .codex 目录;不得 fallback 到镜像内默认凭据或节点宿主机 ~/.codex
  • Codex stdio backend 不得设置 turn/session/conversation 的总时长 timeoutexecutionPolicy.timeoutMs 只能作为无 app-server 响应、无 notification、无 assistant/tool/event activity 的 idle timeout。长程任务只要持续产生可见 activity,就必须继续等待 turn/completed、取消或真实 transport failure。
  • idle timeout 的活动源至少包括 Codex app-server notification、assistant/message delta、tool call 状态、command output、stderr/diagnostic event 和 backend status event;这些活动必须刷新 lastActivityAt/lastActivitySeq。只有超过 idle budget 没有任何活动时,才允许把当前 command 归为 idle timeoutresult/session 仍必须标记 timeoutKind="idle"hardTimeout=false,避免调用方把它误判成固定 wall-clock backend-timeout。
  • 普通 turn command 失败只终结当前 command,不得把 reusable run/session 置为 terminal;后续 command 必须仍可进入同一个 run/runner。只有显式 cancel、runner lease/claim 失效、资源装配不可恢复或运行面退出才允许终结 run。
  • config.toml 指向 hyueapi 或其他 OpenAI-compatible upstreamrunner/backend Pod 的 proxy 与 NO_PROXY 必须保持该配置可用;不得在日志中打印完整 auth/config 内容。
  • 模型名、provider profile、upstream host 可以作为 redacted metadata 输出;provider credential、token、Authorization header 和文件内容不得输出。
  • 一个最短 turn 至少要产生 backend_status、一个 assistant 或 error event、以及 terminal_status

与 Secret 分发规格的关系

spec-v01-secret-distribution.md 是 SecretRef、Kubernetes projection、redaction 和 missing secret failure 的权威。本文件只定义 Codex backend 对测试凭据文件的消费方式。

Run 的 executionPolicy.secretScope 应引用与 backendProfile 匹配的 provider SecretRef 的 auth.jsonconfig.toml 和 profile 需要的额外文件,尤其是 dsflash-gomodel-catalog.json,而不是携带 provider credential 或文件内容。

测试规格

T1 Codex Secret projection

阅读 AGENTS.md、本文和 spec-v01-secret-distribution.md,然后在 agentrun-v01 中通过 Kubernetes Secret 管理把 operator 的 ~/.codex/auth.json~/.codex/config.toml 注入为 agentrun-v01-provider-codex。确认 runner/backend Pod 只能看到投影后的 ~/.codex/auth.json~/.codex/config.toml,没有 hostPath,日志和 event 不显示文件内容。

T2 真实 Codex 最短 turn

阅读本文和 spec-v01-validation.md,然后用 backendProfile=codex 创建真实 run 并提交一个最短 turn command。确认 runner 调用真实 Codex providermanager 可查询 backend_status、assistant 或 error event、terminal_status,且 Secret value 未泄露。

T2b 真实 DeepSeek profile 最短 turn

阅读本文、HWLAB v0.2 DeepSeek profile 参考和 spec-v01-validation.md,然后用 backendProfile=deepseek 创建真实 run 并提交一个最短 turn command。确认 runner 仍调用 codex app-server --listen stdio://,但使用 agentrun-v01-provider-deepseek 的 profile SecretRef 和独立 CODEX_HOMEmanager 可查询 profile 为 deepseek 的 backend_status、assistant 或 error event、terminal_status,且 Secret value 未泄露。

T2c 真实 MiniMax-M3 profile 最短 turn

阅读本文、spec-v01-secret-distribution.mdspec-v01-validation.md,然后用 backendProfile=minimax-m3 创建真实 run 并提交一个最短 turn command。确认 runner 仍调用 codex app-server --listen stdio://,但使用 agentrun-v01-provider-minimax-m3 的 profile SecretRef 和独立 CODEX_HOMEmanager 可查询 profile 为 minimax-m3 的 backend_status、assistant 或 error event、terminal_status,且 Secret value 未泄露。

T2d 真实 dsflash-go profile 最短 turn

阅读本文、spec-v01-provider-profile-management.mdspec-v01-validation.md,然后用 backendProfile=dsflash-go 创建真实 run 并提交一个最短 turn command。确认 runner 仍调用 codex app-server --listen stdio://,但使用 agentrun-v01-provider-dsflash-go 的 profile SecretRef、独立 CODEX_HOMEmodel-catalog.jsonmanager 可查询 profile 为 dsflash-go、model 为 deepseek-v4-flash、context window 为 1M 的 backend_status、assistant 或 error event、terminal_status,且 Secret value 未泄露。

T3 Missing auth/config failure

阅读本文,然后分别移除或改名 Secret 中的 auth.jsonconfig.toml key,启动真实 run。确认 adapter 在调用 provider 前失败为 secret-unavailablefailure response 为 JSON,日志不包含 Secret value。

T4 Provider auth failure

阅读本文,然后使用无效的 Codex Secret 创建 run。确认 backend 返回 provider-auth-failed 或等价 failureKind,记录上游状态分类和 trace correlation,但不打印 Authorization header、token 或 auth/config 文件内容。

T5 Provider availability failure

阅读本文,然后用 mock/fake Codex app-server 自测试 HTTP 503 Service Unavailable、携带 5xx 的 responseStreamDisconnectedmethod=error retry notification 中 willRetry=true 且嵌套 codexErrorInfo.responseStreamDisconnected.httpStatusCode=503 的结构,或明确 temporary/provider unavailable 文案。确认 Codex adapter 返回 provider-unavailable,不会落到 backend-failed;综合联调若真实 provider 返回同类错误,应记录为外部 provider blocker,而不是本地 runner/backend 执行面 blocker。

T6 Profile switching isolation

阅读本文,然后在真实 agentrun-v01 运行面按顺序执行 backendProfile=codexbackendProfile=deepseekbackendProfile=minimax-m3backendProfile=dsflash-gobackendProfile=codex 五个最短 turn。确认第二个 run 使用 DeepSeek profile,第三个 run 使用 MiniMax-M3 profile,第四个 run 使用 dsflash-go profile,前后两个 codex run 仍使用原 Codex profile;五者的 event、log、backend_status、model/upstream metadata 和 failureKind 不互相污染,且任何一个 profile SecretRef 缺失都不会 fallback 到另一个 profile。

T7 Stale thread resume failed

阅读本文和 spec-v01-runtime-assembly.md,然后构造一个带旧 SessionRef.threadId 的真实或 fake app-server run,使 thread/resume 返回 no rollout found for thread id。确认 adapter 输出 thread-resume-failed 并终止当前 turnevents/result/sessionRef 不得出现 thread/resume:non-resumable、替代 thread/start、新 threadId 回写或历史 prompt 拼接。确认 provider auth、rate limit、model config 或其他 protocol error 仍按各自 failureKind 直接失败,不走替代路径。

T7b Session state 持久化与 eviction

阅读本文和 spec-v01-runtime-assembly.md v0.1.1 SessionRef 持久化节,然后构造一个带 AGENTRUN_SESSION_PVC_NAME 环境变量的真实或 fake app-server run。验证:

  1. initialize 之后 adapter emit codex-rollout-storage-mounted 事件,事件 payload 含 pvcName / mountPath / codexRolloutSubdirvaluesPrinted=false
  2. codex 写 ${CODEX_HOME}/<subdir>/YYYY/MM/DD/rollout-<timestamp>-<threadId>.jsonladapter 不写 PVC 内容,但 runtimeSummary 必须能回答 mount 是否成功。
  3. 删 PVC 后再次 turnAGENTRUN_SESSION_PVC_NAME 已设 + thread/resume 返回 no rollout found for thread id 时,failureKind 升级为 session-store-evicted不是 thread-resume-failed
  4. 同一 session 跨 runner pod 重建,PVC 不动:adapter 仍 emit codex-rollout-storage-mountedthread/resume:completed 路径走通,events 不出现 thread-resume-failed / thread/replacement-start:completed
  5. AGENTRUN_SESSION_PVC_NAME 未设 + thread/resume 失败:仍走 thread-resume-failed,不升级为 session-store-evicted
  6. codex_rollout_subdir 走 env AGENTRUN_CODEX_ROLLOUT_SUBDIR,默认 sessionscodex CLI 改子目录时只改 env,不改装配。

T8 Initial prompt and gitbundle skills

阅读本文和 spec-v01-runtime-assembly.md,然后用 fake app-server 或真实 Codex stdio run 验证 ResourceBundleRef.promptRefs 与 gitbundle .agents/skills。首轮无 threadId 时,turn/start input 必须包含 initial prompt 与 skill facts,并记录 initialPromptInjected=true;第二轮带同一 threadId resume 时,turn/start input 只能包含当前用户 message,记录 initialPromptInjected=false,且不得拼接第一轮 prompt、assistant 回复或旧 skill facts。required prompt 缺失时不得调用 Codex provider。

规格的实现情况

规格项 状态 说明
Codex stdio backend/profile 规格 已定义 本文为 v0.1 Codex app-server stdio backend kind 和 profile 权威。
Codex Secret projection 已实现/已通过主闭环 runner Job 使用只读 Secret projection 和 writable CODEX_HOMECodex 测试凭据来自 agentrun-v01-provider-codexauth.json/config.toml
Codex adapter 已实现主路径和 initial prompt/gitbundle skills 接入 当前代码已实现受控 codex app-server --listen stdio://initialize/thread/start/thread/resume/turn/start response 校验、stale rollout thread-resume-failed、stderr 有界诊断、spawn/JSON parse/response invalid/timeout/provider 5xx/invalid tool-call availability failureKind,以及包含 retry error notification 的 fake app-server 自测试;ResourceBundleRef.promptRefs thread-start 注入和 gitbundle skill facts/registry 消费已接入。
错误可观测与脱敏 已实现主路径 child env、cwd、workspace 和 Codex home 只输出摘要;stderr tail 有界且标记截断;事件和 failure 统一走 redaction。
真实 provider turn 已通过主闭环 真实 Codex provider turn 已经通过 RESTful API 和 CLI 综合联调;每次发布仍按 spec-v01-validation.md 手动复验。
deepseek profile 已实现/已通过主闭环 代码已支持 agentrun-v01-provider-deepseek、独立 CODEX_HOME、同一 codex app-server --listen stdio:// 协议和 profile metadata;真实 Kubernetes SecretRef、runner Job 和 Codex stdio turn 已通过主闭环。
minimax-m3 profile 已实现/待真实主闭环 代码已支持 agentrun-v01-provider-minimax-m3、独立 CODEX_HOME、同一 codex app-server --listen stdio:// 协议和 profile metadata;真实 Kubernetes SecretRef、runner Job 和 Codex stdio turn 需要完成 AgentRun CLI 手动验收。
dsflash-go profile 已实现/待真实主闭环 代码已支持 agentrun-v01-provider-dsflash-go、独立 CODEX_HOMEmodel-catalog.json、同一 codex app-server --listen stdio:// 协议和 profile metadata;真实 Kubernetes SecretRef、runner Job、Codex stdio turn 和 HWLAB 原入口 CaseRun 需要完成验收。
hostPath ~/.codex 不采用 只能通过 Kubernetes Secret projection 注入。