支持使用 Fake Provider 验证跨进程 Agent 回合 修正独立 Agent 与排队模型的匹配和子进程超时回收 补充 AGC 到独立 App Server 的真实子进程 smoke
agent-runtime
一个与具体业务项目解耦的 Rust Agent 内核和单 Agent 主程序。
原始 P0–P6 建设计划与逐项状态见
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 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。
该脚本还单独检查 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现在只包含 portableDurableRuntime<S>、RuntimeSnapshotService、WorkerLease/RunHandle与中立DurableStore合同;SQLite-specificRuntimeService、 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<S: DurableStore>提供不绑定 SQLite 的拥有式控制面 facade;可直接注入、 借用或取回任意 durable adapter。SQLite convenience API 位于agent-runtime-sqlite::RuntimeService,不复制连接或状态。- Runtime 测试还提供
cfg(test)的InMemoryDurableStorecontract harness,实际覆盖 bundle、lease、snapshot CAS、safe requeue、finish 和 expired recovery;它只证明泛型 facade 可接入非 SQLite adapter,不改变生产构建。 - Host 的工具请求/结果现在写入
tool_callsdurable 表,支持按 run 查询、JSONL 导出和 相同 identity 幂等重试;请求/结果与对应 Core runtime snapshot/events 通过DurableToolCallRuntimeCommit在 SQLite 单事务中提交,checkpoint 仍保持独立。 - 当调用方已同时拥有工具结果和 Engine 游标时,可使用
DurableToolCallCheckpointRuntimeCommit;DurableStore/RuntimeService会把工具行、 checkpoint 与 Core runtime snapshot/events 在同一个 SQLiteIMMEDIATE事务中提交, 并以 lease/CAS 失败整体回滚。Host 的ToolCompletedtrace 在已有 checkpoint 时已复用 该边界;Host 对首次awaiting_approvalcheckpoint 已把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-contractscrate,承接 DurableStore command/view/trait;agent-runtime已完成 portable 化;SQLite-specific Service/adapter 位于独立的agent-runtime-sqlitecrate,最终依赖树不再把 SQLite 带入agent-runtime。 - 长连接
thread/start/turn/startaccepted 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/interrupttransport 接缝;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 前核对带
providerKindmarker 的 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
及最新验收记录为准;远端仓库/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 中出现结构化
工具块也会直接拒绝,不会静默过滤。
清理构建目录后可用临时数据库直接验证程序入口:
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 能力评测(固定输入、最终输出和流式轨迹断言)使用:
./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<S>;它只提供 Arc<Mutex<_>> 同步和
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。
离线依赖漏洞审计
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:
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 提供固定输入并留下运行记录。
快速开始
# 从 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 请求:
./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。真实 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。
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 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,不会隐式读取整个远端目录。CLImcp 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 消息历史:
# 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 字段;例如工具结果后缀形如:
[
{"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 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 与DurableStoretrait; 只依赖 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-backedRuntimeService、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 调用 CoreExternalObservationSource查询已有 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硬上限,后者实现 CoreSkillSource并执行显式激活。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;另提供窄 V2CodexAppServerClient和真实 stdioCodexAppServerProcess,覆盖 initialize/initialized、thread/start、turn/start 接受、 通知轮询、可选通用 server-request handler 和精确threadId/turnIdinterrupt。真实进程配置只接受显式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。