# 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`、`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 command;Host 统一从该 crate 装配,未保留 `agent_runtime::RuntimeService` 平行 re-export。 - `DurableRuntime` 提供不绑定 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 52,Clippy `-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 且不触发 Provider,legacy 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 计数和 完整 P0–P6 状态以 [`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` 并注入 `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 4;Core 为 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`;它只提供 `Arc>` 同步和 `Unavailable` 锁错误映射,不改变 `RuntimeStore` trait 或跨进程语义。 通用 `CodexAppServerBackend` 的同步 channel 若需要在阻塞 invoke 期间中断,可显式使用 `with_interrupt_hook` 注入独立 control transport;hook 不获取 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 --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 28;Core 为 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::`。发现目录不会自动授予权限; `AGENT_MCP_ALLOW` 中的原始工具名会按当前 server 补全命名空间,也可直接 写完整名称。stdio 参数按 argv 传递,不经过 shell。 transport 对远端不可信输入使用固定上限:单条 stdio JSON-RPC 消息最多 1 MiB, Streamable HTTP 响应最多 4 MiB,SSE 单行最多 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 messages.json AGENT_DB=agent.db agent reconcile-tool 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 ` 返回的历史为前缀,只追加已经从外部 系统核对到的结果。 提交消息必须保留 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 <原因>` 做 pending-only 决议,最后用 `approval resume ` 显式重新排队并启动 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 MiB,Streamable HTTP 响应最多 4 MiB,SSE 单行最多 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`、 `RuntimeSnapshotService`、`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 approval;Engine 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 收束后报告中立 lifecycle;Host 的实现把状态、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 委托给 Runtime,Runtime 仍是当前 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 checkpoint,Pending/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 request;Host 还提供只覆盖 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)。