Files
Genarrative/rust/README.md
kdletters c709b3d9b2 新增独立 Agent App Server
为 agent-cli 增加 stdio JSON-RPC 2.0 服务入口与运行控制协议

补充 Host 实时流监听和流式取消入口

新增 App Server 子进程闭环测试及协议文档

更新 README、架构、TODO 与共享决策记录
2026-09-10 00:05:02 +08:00

648 lines
48 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# agent-runtime
一个与具体业务项目解耦的 Rust Agent 内核和单 Agent 主程序。
原始 P0–P6 建设计划与逐项状态见
[`docs/【计划】独立通用Agent内核与单Agent程序建设计划-2026-09-02.md`](docs/【计划】独立通用Agent内核与单Agent程序建设计划-2026-09-02.md)。本 README 只描述当前已实现的子集,不能作为完整计划的替代。
当前消息持久化按 Host 执行尝试内的已提交事件位置衔接 checkpoint 与 trace,避免正常工具
完成、连续工具和审批恢复重复写入同一消息;压缩前先提交旧上下文的工具结果。集成回归逐条比较
Engine 输出与 Runtime 消息,并从空快照重放事件;Fake CLI smoke 也会重新打开 SQLite 只读核验消息。
Host 的 durable 控制面已进一步下沉到 `agent-runtime-sqlite::RuntimeService`:取消、无主失败收口、
审批决议和 Provider/Tool 对账由 Runtime 负责,Host 只保留薄委托;Engine 执行、checkpoint/trace、
worker 和外部工具桥仍留在 Host。该拆分不改公开 Host API、SQLite schema 或 Cargo.lock。
Host 内部桥接按职责位于私有 `tools.rs``mcp.rs``context.rs``external.rs``checkpoint.rs`
`codex.rs``execution.rs`;根模块显式 re-export 稳定类型。`execution.rs` 只承接 run 入口、
worker/lease 领取和执行恢复编排,checkpoint/trace 与具体适配器仍由各自模块负责。
公开许可证、registry、自动 webhook、跨主机调度及全量 Codex schema 不属于本期完成门槛,
以权威计划「原始范围复核」为准,不采用下方历史增量中的扩大范围表述。
当前仓库先保证一个最小闭环:中立运行时契约、已提交边界内可重放的事件状态、模型与工具循环、SQLite durable Runtime,以及可替换的 MCP/Skill/Provider 适配器。Codex 外部 backend 和 DAG 编排基础已经作为独立库提供;编排器目前包含有界的配额、消息去重、节点隔离/修复和可选的任务图/协调器原子快照,但真实 Codex wire、HTTP 服务和持久化的完整多 Agent 调度仍不进入内核依赖。
`agent app-server --stdio` 是独立 Agent 自己的 JSON-RPC 2.0 JSONL 服务入口,不是 Codex
App Server 兼容层。它复用 `agent-host` 的 run/lease/checkpoint/approval 控制,在一个连接
内按“先接受响应、后异步通知”运行单个 worker,支持查询、取消、审批恢复和 durable 事件;
具体协议见 `rust/docs/【协议】Agent应用服务标准输入输出-2026-09-09.md`。AGC 可以把它当作
外部 Agent 子进程连接,双方不共享 Rust 类型或 SQLite。
独立 workspace 的工具链由 [`rust-toolchain.toml`](./rust-toolchain.toml) 固定为
Rust 1.96,并声明 `rustfmt``clippy` 组件;独立 CI runner 需要在执行 workflow
前预装同一工具链和组件。这样 `cargo check`、测试、格式化与 Clippy 使用同一编译器,
不会依赖父仓库的工具链配置。
workspace 内部 path 依赖带有 `0.1.0` 版本要求,便于后续按依赖顺序发布到私有
registry;当前仍未发布独立远程仓库,`cargo package --list` 只作为本地 manifest
预检,不代表 registry 中已经存在这些 crate。
`scripts/check-package-manifests.sh` 使用离线 `cargo package --list --no-verify`
检查 15 个 crate 的 Rust 版本、描述、license、内部 path 依赖和待发布文件边界;
未显式设置 `CARGO_TARGET_DIR` 时,临时 target 放在 `~/data/tmp`(可用
`AGENT_PACKAGE_TMPDIR` 覆盖)并在退出时清理。它不会联网、上传 crate 或改变 registry。
依赖其它内部 crate 的完整
`cargo package` 校验必须在目标 registry 按依赖顺序发布后进行。
依赖边界可用 `./scripts/check-dependencies.sh Cargo.toml` 离线复核;发布前的
manifest/打包边界可用 `./scripts/check-package-manifests.sh Cargo.toml` 复核;它们检查
workspace path 依赖、`agent-runtime-core` 的反向依赖和 `agent-runtime-contracts`
适配器黑名单,不替代带漏洞数据库的
`cargo-audit`/`cargo-deny` 报告。审计范围和独立 workspace 复制验收见
[`docs/【审计】独立Agent依赖边界与CI验收-2026-09-02.md`](docs/【审计】独立Agent依赖边界与CI验收-2026-09-02.md)。
该脚本还单独检查 portable `agent-runtime --no-default-features` 不带 SQLite,并确认
`agent-runtime-sqlite` 只沿 `agent-runtime + agent-storage-sqlite` 单向装配。
## 2026-09-04 当前增量
- SQLite RuntimeStore 从空快照重放完整 event history,校验 revision 连续性与事件语义;
event log 缺失、断档或语义篡改均 fail-closed。新增
`runtime_store_rejects_event_history_gap_on_load_and_commit`Storage all-features
5+45=50,直接 no-default 4+36=40。
- Skill discovery/list 从 discovery 阶段即受 `max_body_bytes` 硬上限,正文超限返回
`BodyTooLarge`;新增 `discovery_and_list_reject正文超过配置上限`Skill 当前 28。
- Codex process backend runtime event bridge 显式映射 request→notification→result,并
保留显式 handler 变体;新增
`app_server_process_backend_runtime_event_bridge_maps_notification_order`Codex 当前 81。
- 原始 P0–P6 仍部分完成;根 Vitest 的 `3189/3189` 为历史记录,本轮工作区未安装
`vitest`,尝试以退出码 127 结束,未计入当前通过项。
## 2026-09-05 当前增量
- Core 扩展值对象和外部端口(`ToolBinding``SkillDefinition``SkillActivation`
`AgentDescriptor``BackendRequest``BackendResult``ToolContext`)在公开 serde/
兼容入口复验;`backend_result_as_tool` 拒绝身份错配和未知副作用,Core 当前为
30 个单元、20 个 `core_contracts` 集成、1 个 `tool_context_contracts` 集成和 1 个 doctest。
- Engine 在调用 `ContextSource` 前验证 `ContextRequest`Host/Engine 在工具、Skill、MCP
和外部 backend dispatch 前验证上下文与调用合同。Runtime/Storage/Host 提供固定排序、
状态/run 过滤和硬上限的只读 `list_external_sessions`,不会自动对账或重放。
- `agent-runtime` 现在只包含 portable `DurableRuntime<S>``RuntimeSnapshotService`
`WorkerLease`/`RunHandle` 与中立 `DurableStore` 合同;SQLite-specific `RuntimeService`
records、错误和跨表事务已迁移到 `agent-runtime-sqlite`。后者依赖
`agent-runtime` + `agent-storage-sqlite`,不反向污染 portable runtime。
- `agent-runtime-sqlite``RuntimeService` 通过 `SqliteDurableStore` 提供
`prepare_run*`、run/session 查询、claim/heartbeat/release、checkpoint fencing、approval
pending-only CAS、external-session 候选、request-cancel/stale 扫描和 runtime-aware
finish/recovery commandHost 统一从该 crate 装配,未保留 `agent_runtime::RuntimeService`
平行 re-export。
- `DurableRuntime<S: DurableStore>` 提供不绑定 SQLite 的拥有式控制面 facade;可直接注入、
借用或取回任意 durable adapter。SQLite convenience API 位于
`agent-runtime-sqlite::RuntimeService`,不复制连接或状态。
- Runtime 测试还提供 `cfg(test)``InMemoryDurableStore` contract harness,实际覆盖
bundle、lease、snapshot CAS、safe requeue、finish 和 expired recovery;它只证明泛型 facade
可接入非 SQLite adapter,不改变生产构建。
- Host 的工具请求/结果现在写入 `tool_calls` durable 表,支持按 run 查询、JSONL 导出和
相同 identity 幂等重试;请求/结果与对应 Core runtime snapshot/events 通过
`DurableToolCallRuntimeCommit` 在 SQLite 单事务中提交,checkpoint 仍保持独立。
- 当调用方已同时拥有工具结果和 Engine 游标时,可使用
`DurableToolCallCheckpointRuntimeCommit``DurableStore`/`RuntimeService` 会把工具行、
checkpoint 与 Core runtime snapshot/events 在同一个 SQLite `IMMEDIATE` 事务中提交,
并以 lease/CAS 失败整体回滚。Host 的 `ToolCompleted` trace 在已有 checkpoint 时已复用
该边界;Host 对首次 `awaiting_approval` checkpoint 已把 `ToolRequested`、工具行、
checkpoint 和 Core runtime event 放进同一事务,后续普通 checkpoint 与终态仍保持各自
边界,不能把这条窄接线误读成全链路全局事务。
- 最新隔离门禁:all/no-default workspace 测试、check、Clippy `-D warnings`、rustdoc、
fmt、Storage 直接 no-default、依赖/manifest、独立复制、Fake 2/2、能力 10/10、shell、
编码和 `git diff --check` 均通过;Storage 为 all-features 7+53、直接 no-default 6+36
Runtime portable 8 + Runtime SQLite 31、Host 80(另有 7 个消息持久化集成测试)、Engine 55、Codex 101;编排、Host、
Skill、SQLite Storage 测试默认回退 `~/data/tmp`,显式 `TMPDIR` 仍可覆盖。
- 原始 P0–P6 的本地可交付出口已逐项通过;整体交付仍保留独立远端 CI/registry/许可证、
真实 Provider/Codex session、自动外部对账/订阅以及真实 Codex generated wire 的外部证据边界。
## 2026-09-06 当前增量
- `CodexSessionMetadataSink` 增加向后兼容的 `persist_lifecycle` 扩展;ProcessBackend 在
request 成功、post-dispatch 失败和匹配 cancel 收束后发送中立生命周期观察,Host sink
通过 read/merge/write 更新 `external_sessions`,保留 custom audit 字段并防止 terminal
状态复活。
- 新增 `agent-runtime-contracts` crate,承接 DurableStore command/view/trait`agent-runtime`
已完成 portable 化;SQLite-specific Service/adapter 位于独立的
`agent-runtime-sqlite` crate,最终依赖树不再把 SQLite 带入 `agent-runtime`
- 长连接 `thread/start`/`turn/start` accepted response 在 child 仍存活时记录为 `active`
`ProcessControl` 可读取已回收 child 的退出码;进程级 sink 支持一次性自然退出、显式终止、
cancel/timeout/drop 观察,并可由 session sink 转发。
- Codex lifecycle fixture 与 Host merge/terminal 回归通过(含
`app_server_process_lifecycle_sink_reports_timeout`
`app_server_process_lifecycle_sink_distinguishes_reader_eof`);当前定向 Codex 101、Host 80、MCP
52Clippy `-D warnings` 通过。协议级 turn interrupt 和真实 generated wire/session
仍未验收。
- `JsonRpcAppServerRouter` 提供独立的有界 pending map、后台 reader、乱序 response
分发和并发 `turn/interrupt` transport 接缝;`CodexAppServerProcessRouter` 将其接入
ProcessControl 管理的真实 stdio child,但不猜测具体 Codex wire。
- Host `from_host` 构造的 Codex 中立/0.152.1 typed handler 复用同一 Runtime 的
`tool_calls`:已完成 call_id 返回缓存,in-flight 重复拒绝,工具错误写入 `error` 终态;
直接 `new` 构造仍不要求 SQLite。
- 后台 queue metadata 会在 worker claim 前核对带 `providerKind` marker 的 persisted model
模型错配保持 queued 且不触发 Providerlegacy provider-only metadata 继续兼容。
## 2026-09-04 当前门禁增量
queued 取消的最终条件由 SQLite `RunFinishGuard::QueuedUnclaimed` 在同一
`BEGIN IMMEDIATE` 事务内复核;领取竞争会返回明确冲突并由 Host 发出 cooperative
cancel,不会把仍持有 lease 的 run 写成 cancelled。Codex JSONL channel 对空白 keep-alive
行采用循环跳过,连续 8192 行空帧有回归测试。当前 Runtime/Host/Codex/Storage 计数和
完整 P0P6 状态以 [`docs/【计划】独立通用Agent内核与单Agent程序建设计划-2026-09-02.md`](docs/【计划】独立通用Agent内核与单Agent程序建设计划-2026-09-02.md)
及最新验收记录为准;远端仓库/CI、真实 Provider/Codex wire/session 和自动外部恢复仍
未完成,Runtime/SQLite 物理拆分已由 `agent-runtime-sqlite` 完成。
Runtime-only snapshot/event 还提供公开 `DynRuntimeStore` newtype,可承载
`Box<dyn RuntimeStore>` 并注入 `RuntimeSnapshotService``agent-runtime-sqlite::RuntimeService::snapshot_store()`
还可把同一 SQLite 状态以该窄 facade 暴露给调用方;它保留 typed CAS/error
不把 SQLite durable run/session/lease/checkpoint 伪装成已经可替换。
上下文压缩器被视为不可信扩展:Engine 会拒绝其返回的 `Tool` 角色或结构化
`ToolCall`/`ToolResult`,避免下一次 Provider 请求伪造工具历史。Core 的默认
`ModelProvider::stream` 在只有 `complete` 实现时也会保留 tool-call delta 和 usage
事件,流式观察不会静默丢失结构化结果;压缩 Provider 响应的 content 中出现结构化
工具块也会直接拒绝,不会静默过滤。
清理构建目录后可用临时数据库直接验证程序入口:
```bash
mkdir -p "$HOME/data/tmp"
AGENT_DB="$HOME/data/tmp/agent.db" cargo run --locked -p agent-cli -- run "hello"
```
该命令默认选择 Fake Provider,完成一次工具调用闭环;真实 OpenAI 请求仍需显式配置
凭据和 endpoint。
真实 LLM 能力评测(固定输入、最终输出和流式轨迹断言)使用:
```bash
./scripts/run-agent-llm-eval.sh --real --from-codex-config
```
该命令显式读取当前 Codex 配置并产生真实请求;默认回归不会联网。
## 2026-09-03 继续执行的外部会话边界(历史快照)
Codex App Server 可通过 `CodexAppServerBackend::invoke_node_with_runtime_events` 显式
把 request、事件和结果映射成 Core `RuntimeEvent`;调用方负责把 sink 事件交给 reducer
或 RuntimeStore,未知事件和未知副作用不会被静默完成。Host 外部工具桥在重开进程后可
通过 `AgentHost::cancel_external_request(tool_name, request_id)` 查 durable request-id
别名并调用 backend cancel;没有记录时才执行兼容的无归属 cancel,成功/失败状态仍需按
`cancelled`/`unknown` 和显式 reconciliation 处理。
本轮后 Codex 为 78 个测试、Host 为 62 个测试,Engine 为 52 个测试,CLI 为 23 个测试,
Runtime 默认特性为 27 个测试(package-only no-default-features 为 6 个),OpenAI 为 29 个测试;
Storage 为 all-features 5+44、直接 no-default-features 4+36;运行时快照篡改回归与相关 all/no-default 回归和静态门禁通过。原始 P0–P6 仍为
“部分完成”:真实 Codex wire/session、自动外部对账、远端仓库/CI、registry/许可证策略、
完整持久化多 Agent 调度和最终 Host/Runtime 拆分不由本地 fixture 代替。
## 2026-09-03 最新复核(历史快照)
当时本地实现的测试计数为:Engine 51、Host 61、MCP 47、CLI 23、Codex 78、Runtime 27
package-only no-default-features 为 6)、Orchestration 38、Skill 27、OpenAI 29、Fake 4Core 为 25 个单元测试 + 16 个集成测试,
Storage 为 all-features 的 5 个单元测试 + 43 个集成测试(48),以及直接
no-default-features 的 4 个单元测试 + 36 个集成测试(40)。
MCP resources/prompts 只会在调用方通过显式 selection 选择后注入上下文;这些外部上下文
保持不可信,Engine 在模型边界把不可信的 system/developer/assistant 角色降级为 User,避免
外部文本获得高权限角色;其中结构化 tool-call/tool-result 也会渲染为普通 User 文本,不能
伪造工具历史。Engine 对 `ProviderResponse.content` 只接受 `Text`/`Image`,结构化
tool call/result 必须分别走 `tool_calls` 或工具结果回填路径。Host 的
`AgentHost::with_runtime` 可注入已经装配好的 `RuntimeService`Host 与调用方共享同一
durable 控制面,不会重新打开或平行持有 SQLite。Host 的 `CodexHostServerRequestHandler` 处理
`item/tool/call`,在审批或执行前校验已注册工具的 JSON Schema,再使用 Host 的审批策略和
ToolRouter;它是同步的低层桥接,不创建 durable approval/checkpoint/audit`Ask`
JSON-RPC error 返回。另有明确命名的 `codex_01521_server_request_handler`,将已核对的
0.152.1 `tool`/`callId` 请求转换为 `contentItems`/`success` typed response;它仍只覆盖
dynamic-tool 子集。需要持久化审批和恢复时仍应走 Engine/Runtime 执行路径。
Host 已提供显式 `NamespaceToolResolver` 端口和 `StaticNamespaceToolResolver` 映射表。
默认未注入映射时,中立和 0.152.1 typed bridge 对显式非 `null` namespace 仍在
approval/execution 前 fail-closed;调用方显式注册 `(namespace, tool) -> registered_tool`
后才会继续走同一套工具定义、Schema 和审批校验。缺省或 JSON `null` namespace 仍按全局
工具名处理,Host 不猜测分隔符或静默改写工具名。
MCP stdio 同步 client 等待响应时暂存的 pending 消息队列也有硬上限
`MAX_STDIO_PENDING_MESSAGES`,当前为 4096);溢出返回协议错误,不会把跨请求的通知
积累成无界内存。
MCP initialize 在发送 `notifications/initialized` 前会校验服务端返回的
`protocolVersion`;默认候选是 `2025-06-18`,调用方可配置一个有界候选列表。明确版本拒绝时,
只有带可重建配置的连接会创建新 transport 尝试下一个版本;缺失、非字符串或未知版本会保持
连接未初始化,工具调用不会自动重放。
Skill loader 的 frontmatter 仍是轻量、有界的行式子集,但现在会拒绝未闭合引号/括号、空列表项
和空工具名,避免畸形元数据被静默解释。需要在同一进程内让多个 Runtime facade 共享一个
存储实例时,可使用 Core 的 `SharedRuntimeStore<S>`;它只提供 `Arc<Mutex<_>>` 同步和
`Unavailable` 锁错误映射,不改变 `RuntimeStore` trait 或跨进程语义。
通用 `CodexAppServerBackend` 的同步 channel 若需要在阻塞 invoke 期间中断,可显式使用
`with_interrupt_hook` 注入独立 control transporthook 不获取 channel mutex,也不自动
修改 Host/Runtime 状态。未配置独立 transport 时仍使用 channel 自带的串行 interrupt
因此不能把该 opt-in 接线当成真实 Codex control wire 或强制取消实现。
P4 还提供随 crate 分发的本地 fixture`agent-mcp/fixtures/stdio-jsonrpc-server.sh` 可用于
stdio 握手、工具发现和调用回归;`agent-skills/fixtures/skills/review/SKILL.md` 与非法
frontmatter fixture 用于 metadata-first/显式激活测试。fixture 只返回固定数据,不读取密钥
或执行外部脚本。
编排 JSON 文件快照的 revision CAS 通过同目录 sidecar advisory lock 保护跨进程写入;
该锁只覆盖本机文件临界区,不是跨主机锁,也不会启动自动 scheduler。
原始 P0–P6 仍全部为“部分完成”。尚未有证据的出口包括独立远程仓库/CI、registry 与正式
许可证策略、真实 Provider/Codex wire/session、自动外部对账/订阅、完整持久化多 Agent 调度、
跨主机协调,以及最终 Host/Runtime 拆分。
上下文、提示词和 Skill 元数据的确定性边界见
[`docs/【审计】上下文提示词与Skill边界-2026-09-03.md`](docs/【审计】上下文提示词与Skill边界-2026-09-03.md)。
## 离线依赖漏洞审计
`scripts/run-cargo-audit.sh` 是 CI 和本地共用的离线 wrapper。它从脚本自身位置
解析 workspace 根目录,因而不会因为调用者的当前目录而误审其它 `Cargo.lock`
workspace manifest/锁文件、advisory DB 或工具缺失时会直接失败。runner 必须预先
提供本地 RustSec advisory-db checkout`RUSTSEC_ADVISORY_DB`),并预装固定版本的
`cargo-audit`;需要指定可执行文件时设置 `CARGO_AUDIT_BIN`
```bash
RUSTSEC_ADVISORY_DB=/path/to/advisory-db \
CARGO_AUDIT_BIN=/path/to/cargo-audit \
./scripts/run-cargo-audit.sh
```
脚本实际调用 `cargo-audit audit --no-fetch --db <db> --file Cargo.lock`,不会
安装工具、联网拉取或改写 advisory DB。仓库已用 fake binary 自测参数、工作目录
及缺失输入;2026-09-04 另用隔离临时目录安装的 `cargo-audit 0.22.2` 与 RustSec advisory-db
提交 `5a0ebedfe8bdd2e295b171f4162f8c977bcad9a5` 完成真实离线扫描(1239 条 advisory、
188 个锁定依赖,退出码 0,无漏洞或 warning)。临时工具和数据库已清理;独立远端
CI 仍需由 runner 提供固定输入并留下运行记录。
## 快速开始
```bash
# 从 Genarrative-master 根目录进入独立 workspace;这样 agent.db 也会留在这里。
cd rust
cargo test --workspace
cargo run -p agent-cli -- "把这句话复述一遍"
AGENT_DB=agent.db cargo run -p agent-cli -- run --background "后台执行一个任务"
# 设置 OPENAI_API_KEY 后,CLI 会切换到 OpenAI Responses
OPENAI_API_KEY=... cargo run -p agent-cli -- run "回答一个问题"
# OpenAI-compatible 网关:base URL 会自动补成 /responses
OPENAI_BASE_URL=https://your-gateway.example/v1 \
OPENAI_API_KEY=... \
cargo run -p agent-cli -- run "通过网关回答"
# 面向脚本的逐行 JSON 输出(前台最后一行 type=result
AGENT_PROVIDER=fake cargo run -p agent-cli -- run --no-stream --jsonl "脚本任务"
```
`run --jsonl` 只改变 CLI 的呈现格式,不改变 SQLite 持久化或默认的 pretty JSON
输出。前台 run 在 Host 返回后将已收集的事件按顺序编码为 NDJSON:每个
Engine 事件一条 `type=engine_event`,每个 Provider 流事件一条
`type=stream_event`,最后一条 `type=result` 携带完整 `HostRunOutput`;每行都带
`session_id``run_id``runtime_id`。因此它是稳定的完成后批次,不是实时流日志。
后台 `run --background --jsonl` 只输出一条 `type=queued`(含 worker PID 和运行
身份),worker 的最终结果需另用 `inspect``export` 查询。
## 测试集与真实 Provider smoke
仓库提供一组可重复的 Agent 回归用例,先用 Fake Provider 验证工具闭环、流式路径、
SQLite 持久化和 JSONL 导出,再按需追加一次真实 Provider 请求:
```bash
./scripts/run-agent-test-set.sh # Cargo 测试 + 离线测试集
./scripts/run-agent-test-set.sh --quick # 只跑离线测试集
./scripts/run-agent-test-set.sh --quick --real # 追加真实 Provider smoke
```
测试数据和真实 Provider 配置说明见
[`docs/【测试】Agent测试集与真实Provider接入-2026-09-02.md`](docs/【测试】Agent测试集与真实Provider接入-2026-09-02.md)。真实 smoke 只在显式
`--real`(或 `AGENT_TEST_REAL_PROVIDER=1`)时读取 key;默认不访问网络。
测试脚本默认在 `~/data/tmp` 下创建本轮的 `agent-test-set.*` 目录,数据库、Cargo
target 和中间文件都放在其中;显式设置 `TMPDIR``AGENT_TEST_TMPDIR` 时尊重调用方的
临时父目录。脚本只删除自己创建的目录,不会把本轮构建产物留在 workspace 的 `target/`
中。
本地 workspace 回归基线(2026-09-06`cargo test --locked --workspace
--all-features --no-fail-fast`)为:CLI 24、Host 80(另有 7 个消息持久化集成测试)、MCP 52、Engine 55、OpenAI
29、Fake 4、Runtime 36、Codex 101、Orchestration 40、Skill 28Core 为 30 个
单元测试加 21 个集成测试,Storage 为 7 个单元测试加 53 个集成测试(共 60,含
工具调用联合事务、event-history gap、外部 session 候选回归)。
直接以 `--no-default-features` 测试 Storage 时为 6 个单元测试加 36 个集成测试(共
42)。Runtime package-only `cargo test --locked --offline -p agent-runtime
--no-default-features` 另有 6 个 portable 测试;workspace 级 `--no-default-features`
会因 Host 的默认依赖特性合并 SQLite,不能替代该 package-only 检查。这些是本地命令计数,
不代表远端 CI 或真实 Provider/许可证数据库已经验收。
CI 样例同时执行 all-features 与 no-default-features 的 check、test 和 Clippy,避免
只在默认特性下验证隐藏的可选依赖边界。
公开 `validate_tool_arguments` 也会在 JSON Schema 校验前复验 `ToolCall`
`ToolDefinition` 及二者的工具名匹配;因此 approval UI 或入队方即使直接使用该预检
入口,也不会把 serde/兼容构造出的非法调用当成可执行参数。Engine 当前定向测试为
55 个,内部执行路径仍会在真正审批和工具副作用前再次校验。
CLI 默认使用确定性的 Fake Provider,便于离线运行和回归测试;检测到配置的 key
环境变量(默认 `OPENAI_API_KEY`)时切换到 `agent-provider-openai`。真实 Provider 通过
`AgentHost` 注入,不把密钥写入会话或日志。
OpenAI endpoint 有四类装配入口:
- `OpenAiProvider::new(key)` 使用官方默认的
`https://api.openai.com/v1/responses`
- `OpenAiProvider::with_endpoint(key, full_endpoint)` 使用手动指定的完整地址。
- `OpenAiProvider::with_base_url(key, base_url)`
`OpenAiProvider::with_config(key, &OpenAiProviderConfig)` 使用配置对象;base
URL 会自动追加 `/responses`
- 已创建的 Provider 也可调用 `set_endpoint(full_endpoint)`
`set_base_url(base_url)` 热切换地址;两者先完成同一套 URL/凭据校验,失败时
保留原 endpoint。
`OpenAiProvider::from_env()` 读取 `OPENAI_API_KEY`(可用
`OPENAI_API_KEY_ENV` 改名),并读取 `OPENAI_ENDPOINT``OPENAI_BASE_URL`
完整 endpoint 优先于 base URL。CLI 还接受 `OPENAI_MODEL`、TOML 的
`openai_endpoint`/`openai_base_url`;环境变量优先于 `agent.toml`。该适配器发送
Responses API 的 `model``input``tools` 等字段,网关需要兼容同一请求/响应
协议,详见 [OpenAI Responses API reference](https://developers.openai.com/api/reference/cli/resources/responses/methods/create)。
Provider 注册时,`ProviderInstanceId` 表示具体实例(例如 `openai-prod`),
`ProviderProtocolId` 表示 wire 协议(例如 `openai-responses`),两者是不同的强类型。
`ModelProvider::protocol_id()` 是可选的适配器自描述端口;OpenAI Responses adapter
会报告 `openai-responses`,Registry 在注册和解析时核对 descriptor,省略 descriptor
协议时自动补齐。旧/自定义 Provider 返回 `None` 时仍可由调用方声明协议;这项本地
校验用于防止错配,不替代真实网关兼容矩阵。
OpenAI 的 Host 装配 helper 会同时绑定一个拥有式上下文压缩器;长消息超过预算时
会复用同一 Provider 做摘要。自定义 Provider 可在最终选定实例后调用
`with_provider_context_compressor()`,专用摘要 Provider 则继续使用
`with_context_compressor()`
最近的适配器增量还收紧了四个边界:MCP 的认证环境变量会在高层连接、HTTP
直接构造和 stdio 直接构造三条路径一致解析;Skill 激活正文用有界读取抵抗文件在
检查后的增长;通用 Codex JSON-RPC channel 提供显式 server-request handler(默认
仍拒绝未知请求),不会把这些适配器能力下沉到 Core。Engine 在 Provider 响应和
ToolExecutor 结果进入事件、历史或下一次请求前重新执行 Core 构造校验;Codex CLI
参数过滤也会规范化检查大小写、连字符和 header/bearer 形式的凭据参数。审批记录的专用 binding token
在 SQLite JSONL 导出和 CLI `approval list/get/allow/deny` 展示边界都会递归移除,
但 Host 内部仍保留完整记录供显式 `resume` 校验。
## Prompt、Skill 与 MCP
CLI 不把这些扩展写死在项目代码里,而是通过环境变量显式接入:
- `AGENT_SYSTEM_PROMPT``AGENT_DEVELOPER_PROMPT``AGENT_CONTEXT_PROMPT` 会分别
形成独立的初始消息;context section 在当前兼容消息合同中走 user 通道。
- Skill 需要同时设置 `AGENT_SKILL_ROOT`(或冒号分隔的
`AGENT_SKILL_ROOTS`)和 `AGENT_SKILLS`。名称必须显式列出,不会扫描后自动
激活:
AGENT_SKILL_ROOT=./skills AGENT_SKILLS=review,writer \
cargo run -p agent-cli -- run "检查这段代码"
- MCP 二选一配置 `AGENT_MCP_STDIO_COMMAND`(可选
`AGENT_MCP_STDIO_ARGS`,空白分隔或 JSON 字符串数组)或
`AGENT_MCP_HTTP_URL`。HTTP 认证头可通过
`AGENT_MCP_HTTP_HEADERS`='{"Authorization":"Bearer ..."}' 传入;可选
`AGENT_MCP_SERVER``AGENT_MCP_TIMEOUT_SECS`
AGENT_MCP_SERVER=workspace \
AGENT_MCP_STDIO_COMMAND=npx \
AGENT_MCP_STDIO_ARGS='["-y","@modelcontextprotocol/server-filesystem","."]' \
AGENT_MCP_ALLOW=read_file \
cargo run -p agent-cli -- run "列出工作区文件"
MCP 工具会被命名为 `mcp:<server>:<tool>`。发现目录不会自动授予权限;
`AGENT_MCP_ALLOW` 中的原始工具名会按当前 server 补全命名空间,也可直接
写完整名称。stdio 参数按 argv 传递,不经过 shell。
transport 对远端不可信输入使用固定上限:单条 stdio JSON-RPC 消息最多 1 MiB
Streamable HTTP 响应最多 4 MiBSSE 单行最多 1 MiB、单事件累计 `data` 最多
4 MiB`tools/list``resources/list``prompts/list` 各最多跟随 1024 页。
超限会返回协议错误,不会静默截断;这些是 transport 读取上限,不替代 Engine
的上下文/工具结果预算。
`agent.toml` 也可以用 `[[mcp.auth]]` 保存认证环境变量引用,而不把 token
写进 TOML
[[mcp.auth]]
variable = "TEAM_MCP_TOKEN"
target = "http_bearer"
stdio 子进程认证使用 `target = "stdio_environment"` 并额外设置 `name`
自定义 HTTP 头使用 `target = "http_header"``name` 和可选 `prefix`。旧的
`AGENT_MCP_HTTP_HEADERS` 仅作为运行时兼容入口。
Resource/prompt 默认只做能力发现,不会自动进入上下文。需要显式读取时,在
`[mcp]` 下列出 `context_resources`URI 数组)或 `context_prompts`(名称数组),
或设置 `AGENT_MCP_CONTEXT_RESOURCES` / `AGENT_MCP_CONTEXT_PROMPTS`(逗号或空白
分隔);CLI prompt 使用空参数。Host 会先按 URI/name 精确匹配,再把读取结果作为
`ContextSource` 的不可信候选项交给 Engine;带 prompt 参数的场景使用库 API 的
`McpContextSelection::with_prompt_selection`,不会隐式读取整个远端目录。CLI `mcp list`
只调用一次 `McpClient::capability_snapshot()`,从同一份快照生成 tools 和 fingerprint
因而不会重复发 `tools/list`resources/prompts 的发现错误也会沿原路径返回。
Skill 文件采用一个有界的 `SKILL.md` frontmatter 子集:`name` 必填,支持
`description``version``allowed-tools` 和字符串扩展字段;解析器不是完整 YAML
实现。目录发现只读元数据,正文在显式激活时读取并重新校验路径、UTF-8 和大小;
空正文会被激活拒绝。每个逻辑字段只能出现一次;历史别名 `allowed_tools`
`allowed-tools` 视为同一字段,重复时直接拒绝,避免后值覆盖造成激活语义漂移。
`allowed-tools` 只作为元数据,不会自动放行工具。
`run --background` 只把任务消息和运行身份写入 SQLite;隐藏 worker 会从继承的
环境重新加载 Skill 并重新建立 MCP 连接。因此扩展配置和 Skill 文件应在 worker
启动前保持可用;库调用方若自行创建 worker,也要在每个 worker 进程重建这些适配器。
排队和终态是原子边界:Host 会一次事务写入 session、queued run、runtime 初始
快照/事件;正常完成、失败或取消会一次事务更新 run/runtime/session 并清理
checkpoint。worker 在 claim 前发现 Provider、Skill 或 MCP 配置错误时会把 run
记录为 `failed`,不会静默留下 queued;claim 后的本地准备错误会先进入
`reconciling`,保留恢复证据后再释放 lease。
Host 默认审批策略只放行无副作用的内置 `echo`;接入自己的工具时,通过
`with_approval` 显式提供策略。Engine 会把可选的 session/run 身份传给上下文源
和工具执行器,但不会自动把这些身份拼进模型提示词。
可用命令:`run``inspect``checkpoint``export``doctor``cancel``resume``resume-safe``reconcile``reconcile --stale [limit]``reconcile-provider``reconcile-tool``approval list/get/allow/deny/resume``codex validate`。需要后台运行时使用
`run --background`;它先持久化 `queued` run,再启动一个隐藏 worker。`cancel`
通过 SQLite 发出跨进程可见的 cooperative 请求,当前 Provider/工具调用返回后在
下一个 step 收口。每个 worker 还持有带 token 的 lease 并定期心跳,避免第二个
worker 写回迟到结果。`resume` 只重新领取尚未启动的 `queued` run,不会重放已经运行
过的外部调用;Engine 会在 Provider/审批/工具边界写入当前 run 的增量 checkpoint
`reconcile` 只把 lease 已过期或历史上没有 lease 的 `running` run 转成 `reconciling`
`reconcile --stale [limit]` 对同一类 run 做一次固定排序、有界批量扫描(省略 limit
时最多 256 项),并保留最后游标供宿主读取和完成外部副作用对账。Host 在尝试启动一个仍为
`running`/`cancel_requested` 的 run 时也会先做一次有条件的过期 lease 探测:有效
lease 会拒绝第二个 worker,过期 lease 会原子进入 `reconciling`,不会启动 Engine
或重放调用。库调用方还可使用 `agent-runtime-sqlite::RuntimeService::reconcile_stale_runs(limit)`
一次固定排序、最多 256 项的候选扫描;每项会重新检查 lease 后再进入同一原子 gate。
对账方先在 Provider/工具外部系统
确认结果,再提交完整 Core 消息历史:
```bash
# messages.json 是完整历史,不是仅新增的一条消息;也可把 - 换成 stdin,或直接传入以 [ 开头的 JSON
AGENT_DB=agent.db agent reconcile-provider <run_id> <provider_request_id> messages.json
AGENT_DB=agent.db agent reconcile-tool <run_id> <tool_call_id> messages.json
```
消息格式沿用 Core 的 serde 字段;例如工具结果后缀形如:
```json
[
{"role":"user","content":[{"type":"text","text":"执行任务"}]},
{"role":"assistant","content":[{"type":"tool-call","id":"call-1","name":"echo","arguments":{"text":"hello"}}]},
{"role":"tool","content":[{"type":"tool-result","toolCallId":"call-1","output":{"ok":true},"isError":false}]}
]
```
实际提交时应以 `agent checkpoint <run_id>` 返回的历史为前缀,只追加已经从外部
系统核对到的结果。
提交消息必须保留 checkpoint 的逐项前缀,并追加已观察的 assistant 响应(Provider
或匹配的 `tool` 消息中的 `tool-result` 内容块(工具);Host/SQLite 会校验 phase、调用 ID、step、
attempt、角色、顺序和重复项。命令只写入 `safe` checkpoint,不会查询外部系统、
执行 Provider 或再次执行工具;成功后使用 `resume-safe`,它先把 run 重新排队(尚未启动 worker 时重复执行也幂等);worker 领取后在启动 Engine 前用一次
RuntimeStore CAS 补齐已观察的消息/工具结果,再从 `next_step` 继续。两步之间
退出仍可重试;`provider_in_flight``tool_in_flight` 仍不会自动重放。
`AGENT_DB` 指定 SQLite 文件路径;不设置时使用当前目录的 `agent.db`
Provider 在进入工具调度前会校验 response 的 request/model identity 和批次内
tool-call ID 唯一性。审批策略返回 `Ask` 时,worker 保留
`awaiting_approval` checkpoint 并进入 reconciliation gate,不会把它当作 deny
或自动执行。控制端先用 `approval list/get` 读取请求,再用 `approval allow`
`approval deny <id> <原因>` 做 pending-only 决议,最后用 `approval resume <id>`
显式重新排队并启动 worker;同一决议可安全重试,已取消或已决议记录不能被覆盖。
审批 `Deny` 会生成失败的 tool result;若同一批次还有后续调用,checkpoint 会
指向下一调用,只有整批处理完成才可标记 `safe`
MCP stdio 子进程只继承 `PATH` 和显式环境变量,MCP 配置/请求/错误的调试与 CLI
展示会脱敏;tools/list 会限制分页并拒绝循环游标。同步 client 提供显式
`poll_notification`、有界 `McpReconnectScheduler`(可输出调度审计)和调用前
权限审计 gate;重连只重做握手,不缓存或重放 `tools/call`,也不能强杀已经阻塞
的同步 I/O。已完成握手的 stdio client 还可通过
`into_notification_subscription` 转成独占的后台通知订阅:队列有界、支持
cooperative cancel`Drop`/`join` 会回收线程和子进程;订阅期间不能再对原 client
发 request。Streamable HTTP 也支持独占 GET/SSE 长连接(内部 worker 使用私有
current-thread runtime),自定义 transport 仍需显式覆盖订阅 hook;订阅线程不会
自动应答、重连或重放调用。transport 对远端不可信输入使用固定上限:单条 stdio JSON-RPC 消息
最多 1 MiBStreamable HTTP 响应最多 4 MiBSSE 单行最多 1 MiB、单事件累计
`data` 最多 4 MiB`tools/list``resources/list``prompts/list` 各最多跟随
1024 页。超限会返回协议错误,不会静默截断;这些上限不替代 Engine 的上下文/
工具结果预算。CLI `mcp list` 使用一次 capability snapshot 同时产出工具列表和指纹,
不会为指纹再次请求 `tools/list`。OpenAI adapter 在 complete/stream 请求中以本地
request id 发送 `Idempotency-Key`,并将 HTTP 408/429/502/503/504 归类为可限次重试的
`Unavailable`,其它非 2xx 归类为 `Upstream`;厂商 response id 仍只是外部对账线索,
adapter 不会自行查询结果或重放。若网关不承诺幂等,应把调用方的重试预算设为 0。
## Crate 边界
- `agent-runtime-core`:纯数据契约、端口和状态 reducer;不依赖异步运行时或外部协议。
- `agent-runtime-contracts`:数据库无关的 durable command/view 与 `DurableStore` trait
只依赖 Core 和 JSON,不持有 SQLite、连接或线程生命周期。
- `agent-runtime-engine`:单 Agent Loop、上下文预算、工具调度与 checkpoint 边界回调;
`ContextObservation` 记录候选上下文的 selected/skipped 及 trusted/untrusted 计数,
不复制正文。进入 Engine 的 serde 输入、ContextSource 项、压缩器输出和压缩响应
身份都会在边界重新校验;Provider response 的 `content` 只允许 Text/Image,结构化
tool call/result 不得混入该字段;校验失败发生在 Provider、checkpoint 或工具副作用之前。
- `agent-runtime`portable durable command/view facade、`DurableRuntime<S: DurableStore>`
`RuntimeSnapshotService<S>``RuntimeRunHandle``WorkerLease`;无 SQLite feature、
`rusqlite``agent-storage-sqlite` 依赖,package-only no-default 测试覆盖其 portable
合同。不启动 Engine 或 worker。
- `agent-runtime-sqlite`SQLite-backed `RuntimeService`、records/error 转换、journal mode、
JSONL/export 和跨表 lease/checkpoint/终态/recovery 事务;依赖 `agent-runtime`
`agent-storage-sqlite`Host 统一通过它装配,未保留 `agent_runtime::RuntimeService`
平行 re-export。
- `AgentHost::load_runtime_snapshot`RuntimeSnapshot 的只读观察入口,同样通过
Runtime facade,不领取 lease、不改变状态;跨表原子事务由 Runtime 的 SQLite
实现负责,Host 只保留 `store()` 等旧兼容 accessor,不形成第二份连接所有权。
- `AgentHost::observe_external`:通过 Runtime facade 调用 Core
`ExternalObservationSource` 查询已有 provider/tool/external 引用;这是只读观察,
不写 checkpoint、消息、requeue 或 reconciliation,外部错误分类会原样返回。
- `DurableApprovalCheckpointRuntimeCommit`Host 在 Engine 生成 approval binding 后,
通过 Runtime/SQLite 的单事务重新校验 live lease、awaiting checkpoint 和 runtime
revision,再幂等写入 pending approvalEngine callback 早于 binding 生成的窄窗口仍需
reconciliation/failure gate 处理。
- Host 的 Runtime-only 事件/快照 CAS 通过 `agent-runtime-sqlite::RuntimeService::commit_runtime_snapshot`
该入口不替代跨表 run/session/lease 事务。
- `agent-storage-sqlite`SQLite 事件/快照、增量 checkpoint,以及 worker lease/fencing
具体 RuntimeService 装配位于 `agent-runtime-sqlite`
- `agent-mcp``agent-skills`:外部协议和文件格式适配器;前者提供同步
stdio/Streamable HTTP JSON/SSE、显式通知轮询、stdio 与 HTTP/SSE 的有界独占通知
订阅、有界重连调度和权限审计;stdio 同步等待响应时的 pending 消息队列同样有
`MAX_STDIO_PENDING_MESSAGES` 硬上限,后者实现 Core `SkillSource` 并执行显式激活。
- `agent-provider-openai`OpenAI Responses 请求/响应映射。
- `agent-provider-fake`:可脚本化的离线 Provider fixture。
- `agent-codex`:受限 Codex CLI 一次性调用(含超时、取消、Unix process-group
终止和输出上限;子进程启动后的非法输出、超限或异常退出均保留为未知副作用)、
可注入 App Server channel、带版本字段的本地 JSONL fixture,以及不绑定发行版的
最小 JSON-RPC/JSONL channel;另提供窄 V2 `CodexAppServerClient` 和真实 stdio
`CodexAppServerProcess`,覆盖 initialize/initialized、thread/start、turn/start 接受、
通知轮询、可选通用 server-request handler 和精确 `threadId`/`turnId` interrupt。真实进程配置只接受显式
`program + args`,有界 supervisor 负责 stdin/stdout 排空、deadline、取消、进程组
终止、wait/reap 和 reader/writer join;不会自动重连或重放请求。不嵌入 `codex-core`
也不替宿主持有 Host 的会话和持久化真相;V2 不是具体 Codex 发行版兼容承诺,完整
generated schema、服务端审批/工具请求和版本特定 wire 仍由上层适配器负责;
`codex_0_152_1` 只接受调用方已核对的 `codex-cli 0.152.1`,并提供当前 typed
子集与 provenance/hash 清单,不是任意 v2 发行版兼容层。
另提供独立 `JsonRpcAppServerRouter`,以有界 pending map 和后台 reader 支持
乱序 response/并发 `turn/interrupt`;另有 `CodexAppServerProcessRouter` 将其接入
ProcessControl 管理的真实 stdio child,并在 timeout/protocol/cancel/Drop 时收束。
ProcessBackend 可注入 `CodexSessionMetadataSink`,在 request/cancel 收束后报告中立
lifecycleHost 的实现把状态、external ID、退出码和取消结果合并进 `external_sessions`
但不覆盖进程级 EOF/terminate/Drop 或自动外部对账。
- `agent-runtime-orchestration`:通用 DAG、Delegation/Join/Proposal、状态和下游修复,
以及 Coordinator 的配额、消息去重、节点隔离和显式修复;持久协调器还提供
revision-CAS 保护的 `cancel_run`,原子标记 `Cancelled` 并释放活动配额;支持把任务图与协调器
控制面作为同一版本化快照保存的 `PersistentCoordinator`,并提供带 revision CAS
的内存/原子 JSON 文件 `OrchestrationSnapshotStore`。其中
`JsonFileOrchestrationSnapshotStore` 通过同目录 sidecar advisory lock 把 revision
CAS 与 rename 串成遵守该适配器的本机跨进程写临界区;旧
`JsonFileCoordinatorStore` 仍只有进程内锁。它们都不直接接 SQLite、不创建 Runtime
run、不调度线程,也不提供自动多 Agent scheduler。
- `agent-host`:组合依赖并提供稳定库 API`AgentBuilder`/`AgentService` 当前是
`AgentHost` 的类型别名,`AgentHost::new`/`builder` 是轻量装配入口,不创建第二
套 Runtime 或生命周期。Host 内部不再重复持有 Store;`store()` 已标记为 deprecated
仅作为旧导出/诊断调用的兼容 accessor 委托给 RuntimeRuntime 仍是当前 SQLite-backed
具体实现。新代码可使用 `list_events``list_runtime_events``get_session`
`export_jsonl``export_runtime_jsonl` 等窄 facade。
- `agent-app`:无状态的通用程序配置与装配输入层,承接 `AgentTomlConfig`、环境/TOML
优先级、OpenAI endpoint、MCP 认证引用、effective model 和 queued metadata;不依赖
Host、Runtime、线程或数据库,不拥有运行状态。
- `agent-cli`:面向人和脚本的最小命令行入口。
可选的真实 Codex wire 探测使用 `scripts/probe-codex-app-server.sh`。默认只在隔离
`CODEX_HOME` 中执行 `initialize`/`initialized`/`thread/start`;加 `--schema` 可生成
本机 v2 schema 的大小/hash 摘要。它不需要 API key、不发送 `turn/start`,也不进入默认 CI。
库调用方也可直接使用 `AgentHost::read_checkpoint` 观察游标,再调用
`reconcile_provider_result` / `reconcile_tool_result``requeue_safe_run`;这些
入口只接受已核对的完整消息历史。若宿主已有具体外部查询实现,可注入
`ExternalSessionResolver` 调用 `reconcile_external_sessions(limit, resolver)` 做有界批处理:
Completed 只推进 safe checkpointPending/NotFound 保守保留 unknown,仍不会自动 requeue、
重放或启动 Engine。自动幂等键查询、长连接结果订阅和未知调用的自动重放需要绑定具体外部
系统,暂不由通用内核假设。`agent-codex` 的 Host 接入
方式是实现/注入 Core `ExternalBackend`,调用 `AgentHost::with_external_backend`
再通过审批策略显式放行;未知结果会写 `external_sessions` 并停在
`tool_in_flight`,不会被自动当作成功。多工具批次在首个 `Ask` 后恢复时只会把当前
pending call 及其前缀物化到 Core,后续调用仍留在 checkpoint;MCP 工具桥对发送后
timeout/断线/协议/编码/HTTP/远端错误统一标记为未知副作用,避免显式 failed 重试
策略重放未知调用。`doctor` 不联网、不启动外部进程,但会打开并按需初始化/迁移本地
SQLite/WAL,再分项校验 SQLite、Provider key/endpoint、Skill、MCP 和 Codex 配置;Codex
program 只输出已配置标记,不回显路径。
需要观察 durable 审计时,可注入 `with_durable_event_listener`
`with_durable_event_callback`。回调在每条 `events` 行的 SQLite append 事务提交后
同步执行,并收到 `(run_id, revision, event)`;它只证明该行已落盘,不代表 runtime
trace、approval 或终态事务已完成,也不能回滚或直接修改 Host 状态。Engine 的普通
`EventListener`/`StreamEventListener` 仍是提交前的观察接口。
Engine 的当前边界回归还覆盖五类容易被 serde 绕过的输入:顶层 AgentInput 的消息、工具
定义和请求字段;ContextSource 返回的消息项;压缩器返回的消息集合;压缩响应的
`request_id`/`model` 身份;以及压缩阶段的取消传播。它们统一在进入 Provider、checkpoint
或工具副作用前失败,避免不合法数据被截断、重写或继续执行。
Codex 一次性 CLI 的配置可放在 `agent.toml``[codex.cli]` 下,并运行
`agent codex validate` 做无副作用的参数白名单检查;实际调用由
`CodexCliBackend` 在有界 supervisor 中管理 child、超时和取消。JSONL App Server
channel 只规定本仓库的中立 `request/event/result/interrupt` frame,并以
`protocolVersion` 做本地 fixture 握手;需要当前窄 V2 生命周期时可使用
`CodexAppServerClient``initialize``thread_start``turn_start`
`poll_notification``turn_interrupt`。若需要直接启动本地 app-server,可使用
`CodexAppServerProcessConfig` 配置显式 `program + args`,再通过
`CodexAppServerProcess::spawn` 获取同一组方法;它会为 stdin/stdout 使用有界的
reader/writer supervisor,并在 timeout、cancel、EOF 或 Drop 时完成 process-group
终止、wait/reap 和线程 join。每个 argv 必须匹配调用方提供的
`allowed_arg_prefixes`,不会把一整段字符串再交给 shell 解析。客户端提供
`CodexServerRequestHandler` 这一中立回调,用于回应审批/动态工具等 server requestHost
还提供只覆盖 Codex 0.152.1 dynamic-tool wire 的 `codex_01521_server_request_handler`
返回该版本的 `contentItems`/`success` 形状。具体参数 schema、审批策略、工具执行、异步
通知订阅或自动重连仍需由上层适配器负责,避免把厂商协议写进 Kernel。进一步的依赖/CI 证据见
[`docs/【审计】独立Agent依赖边界与CI验收-2026-09-02.md`](docs/【审计】独立Agent依赖边界与CI验收-2026-09-02.md)。