Files
Genarrative/rust/crates/agent-runtime-contracts/src/lib.rs
T
kdletters 202279c6d9 新增独立 Agent Runtime Rust 工作区
新增 Core、Engine、Runtime、SQLite、Provider、MCP、Skill、Codex、CLI 与 DAG crate

补齐 OpenAI endpoint 配置、Provider 实例/协议路由和统一工具权限边界

加入持久化、lease、checkpoint、reconciliation、审批恢复与消息历史回归

加入独立 workspace CI、依赖边界、能力集和 Fake Agent 测试脚本

同步建设计划、TODO、架构、测试与验收文档
2026-09-06 17:44:54 +08:00

638 lines
20 KiB
Rust
Raw 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.
//! Durable Runtime 的中立控制面合同。
//!
//! 这些拥有值 command、view 和 `DurableStore` trait 不携带 SQLite、线程或
//! CLI 状态,供 runtime facade 与持久化适配器共享。跨表原子性仍由具体 adapter
//! 保证。
use agent_runtime_core::{RuntimeEvent, RuntimeSnapshot};
use serde_json::Value;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::time::Duration;
/// 一次原子创建 session、queued run 和 runtime 初始事件的拥有式命令。
#[derive(Clone, Debug)]
pub struct DurableRunBundle {
pub session: DurableSessionInput,
pub run: DurableRunInput,
pub runtime_id: String,
pub snapshot: RuntimeSnapshot,
pub events: Vec<RuntimeEvent>,
}
/// session 创建参数的中立表示,不携带数据库连接或事务句柄。
#[derive(Clone, Debug)]
pub struct DurableSessionInput {
pub id: String,
pub agent_id: Option<String>,
pub status: String,
pub metadata: Value,
}
/// run 创建参数的中立表示。
#[derive(Clone, Debug)]
pub struct DurableRunInput {
pub id: String,
pub session_id: String,
pub status: String,
pub input: Value,
}
/// bundle 原子提交后返回的稳定最小身份。
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableBundleResult {
pub session_id: String,
pub run_id: String,
pub runtime_id: String,
}
/// 供 Runtime/Host 查询的中立 session 投影。
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableSessionView {
pub id: String,
pub agent_id: Option<String>,
pub status: String,
pub metadata: Value,
pub created_at: i64,
pub updated_at: i64,
}
/// 供 Runtime/Host 查询的中立 run 投影。
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableRunView {
pub id: String,
pub session_id: String,
pub status: String,
pub revision: i64,
pub input: Value,
pub output: Option<Value>,
pub cancel_requested: bool,
pub created_at: i64,
pub updated_at: i64,
}
/// 当前 worker lease 的中立投影。
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableLeaseView {
pub run_id: String,
pub worker_id: String,
pub lease_token: String,
pub lease_expires_at: i64,
pub heartbeat_at: i64,
pub attempt: i64,
}
/// claim 的单次原子结果;run 和 lease 必须来自同一适配器事务。
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableClaimResult {
pub run: DurableRunView,
pub lease: DurableLeaseView,
}
/// Engine 边界 checkpoint 的中立输入。
#[derive(Clone, Debug)]
pub struct DurableCheckpointInput {
pub run_id: String,
pub phase: String,
pub step: i64,
pub next_step: i64,
pub messages: Value,
pub provider_request_id: Option<String>,
pub tool_call_id: Option<String>,
pub attempt: i64,
}
/// 持久化后的 checkpoint 观察值。
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableCheckpointView {
pub run_id: String,
pub phase: String,
pub step: i64,
pub next_step: i64,
pub messages: Value,
pub provider_request_id: Option<String>,
pub tool_call_id: Option<String>,
pub attempt: i64,
pub updated_at: i64,
}
/// checkpoint 与 runtime snapshot/event 的单事务提交命令。
#[derive(Clone, Debug)]
pub struct DurableCheckpointRuntimeCommit {
pub checkpoint: DurableCheckpointInput,
pub runtime_id: String,
pub expected_runtime_revision: Option<u64>,
pub snapshot: RuntimeSnapshot,
pub events: Vec<RuntimeEvent>,
}
/// durable approval 创建命令;token 只供内部恢复绑定,不用于展示。
#[derive(Clone, Debug)]
pub struct DurableApprovalInput {
pub id: String,
pub session_id: String,
pub run_id: String,
pub tool_call_id: Option<String>,
pub status: String,
pub request: Value,
pub arguments_hash: String,
pub approval_token: String,
pub expires_at_ms: i64,
}
/// 将一个等待审批的 checkpoint、当前 Core runtime 快照和 approval 记录
/// 放进同一 durable adapter 事务的拥有值命令。
///
/// Engine 在 checkpoint listener 返回后才构造完整的 `ApprovalRequest`,
/// 因此该命令用于 Host 已经拿到 request binding 的收口阶段:adapter 必须
/// 在一个事务内重新校验 live lease、awaiting checkpoint 和 runtime snapshot,
/// 再幂等写入 approval。它不会追加 runtime event;对应的 ToolRequested
/// event 已由 checkpoint 边界事务提交。
#[derive(Clone, Debug)]
pub struct DurableApprovalCheckpointRuntimeCommit {
pub approval: DurableApprovalInput,
pub checkpoint: DurableCheckpointInput,
pub runtime_id: String,
pub expected_runtime_revision: Option<u64>,
pub snapshot: RuntimeSnapshot,
pub lease: DurableLeaseIdentity,
}
/// approval 查询投影;调用方展示时仍应使用专用脱敏投影。
#[derive(Clone, Debug, PartialEq)]
pub struct DurableApprovalView {
pub id: String,
pub session_id: String,
pub run_id: String,
pub tool_call_id: Option<String>,
pub status: String,
pub request: Value,
pub arguments_hash: String,
pub approval_token: String,
pub expires_at_ms: i64,
pub decision: Option<Value>,
pub created_at: i64,
pub updated_at: i64,
}
/// approval pending-only CAS 命令。
#[derive(Clone, Debug)]
pub struct DurableApprovalResolution {
pub approval_id: String,
pub expected_status: String,
pub status: String,
pub decision: Value,
}
/// 外部 backend 会话登记命令;metadata 由 adapter 做非敏感 JSON 校验。
#[derive(Clone, Debug)]
pub struct DurableExternalSessionInput {
pub id: String,
pub session_id: String,
pub run_id: Option<String>,
pub backend: String,
pub external_id: String,
pub status: String,
pub metadata: Value,
}
/// 外部会话的 durable 观察值。
#[derive(Clone, Debug, PartialEq)]
pub struct DurableExternalSessionView {
pub id: String,
pub session_id: String,
pub run_id: Option<String>,
pub backend: String,
pub external_id: String,
pub status: String,
pub metadata: Value,
pub created_at: i64,
pub updated_at: i64,
}
/// 工具调用的 durable 创建命令;调用身份与参数会被 adapter 校验并保留。
#[derive(Clone, Debug)]
pub struct DurableToolCallInput {
pub id: String,
pub session_id: String,
pub run_id: String,
pub tool_name: String,
pub arguments: Value,
pub status: String,
}
/// 将工具调用行和 Core runtime 事件放进同一个 adapter 事务的拥有值命令。
///
/// 该命令只覆盖 `tool_calls` 行与 runtime snapshot/event 的原子边界;
/// Engine checkpoint 目前仍由单独的 checkpoint command 提交,不能把这个
/// 类型误读成 run/runtime/checkpoint 的全局事务。`lease = None` 仅供没有
/// worker fencing 的兼容或控制面路径使用;带 worker 的运行应传入 lease。
#[derive(Clone, Debug)]
pub struct DurableToolCallRuntimeCommit {
pub call: DurableToolCallInput,
pub runtime_id: String,
pub expected_runtime_revision: Option<u64>,
pub snapshot: RuntimeSnapshot,
pub events: Vec<RuntimeEvent>,
pub lease: Option<DurableLeaseIdentity>,
}
/// 将工具调用行、Engine checkpoint 和 Core runtime snapshot/event 放进同一
/// adapter 事务的拥有值命令。
///
/// checkpoint 必须带 worker lease;这条更窄的合同只用于 worker 已经同时
/// 拿到工具调用和 checkpoint 的边界。普通工具事件仍可使用上面的
/// `DurableToolCallRuntimeCommit`,避免调用方为了凑 checkpoint 而重复写入。
#[derive(Clone, Debug)]
pub struct DurableToolCallCheckpointRuntimeCommit {
pub call: DurableToolCallInput,
pub checkpoint: DurableCheckpointInput,
pub runtime_id: String,
pub expected_runtime_revision: Option<u64>,
pub snapshot: RuntimeSnapshot,
pub events: Vec<RuntimeEvent>,
pub lease: DurableLeaseIdentity,
}
/// 工具调用的 durable 观察值,供审计、恢复和导出使用。
#[derive(Clone, Debug, PartialEq)]
pub struct DurableToolCallView {
pub id: String,
pub session_id: String,
pub run_id: String,
pub tool_name: String,
pub arguments: Value,
pub result: Option<Value>,
pub status: String,
pub created_at: i64,
pub updated_at: i64,
}
/// 旧的 `DurableStore` 实现尚未支持工具调用与 runtime 的联合事务时,
/// 默认方法返回的明确错误。新 adapter 可以为自己的错误类型实现
/// `From<DurableStoreUnsupported>`,再覆盖联合事务方法;这样不会强迫
/// 现有 fake/兼容实现立刻增加新的 trait 方法实现。
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DurableStoreUnsupported {
operation: &'static str,
}
impl DurableStoreUnsupported {
pub const fn new(operation: &'static str) -> Self {
Self { operation }
}
pub const fn operation(self) -> &'static str {
self.operation
}
}
impl Display for DurableStoreUnsupported {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
write!(
formatter,
"durable store operation is unsupported: {}",
self.operation
)
}
}
impl Error for DurableStoreUnsupported {}
/// Runtime-aware 终态目标;适配器必须把它与 Core 最后事件保持一致。
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DurableFinishTarget {
Completed,
Failed,
Cancelled,
}
/// 终态 command 是否要求 queued 未领取保护。
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DurableFinishGuard {
None,
QueuedUnclaimed,
}
/// Runtime、run、session、checkpoint 的拥有值终态提交命令。
#[derive(Clone, Debug)]
pub struct DurableFinishCommand {
pub run_id: String,
pub lease: Option<DurableLeaseIdentity>,
pub target: DurableFinishTarget,
pub output: Option<Value>,
pub runtime_id: String,
pub expected_runtime_revision: Option<u64>,
pub snapshot: RuntimeSnapshot,
pub events: Vec<RuntimeEvent>,
pub guard: DurableFinishGuard,
}
/// 终态 command 使用的 opaque worker fencing 身份。
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DurableLeaseIdentity {
pub worker_id: String,
pub lease_token: String,
}
/// 过期 run 的 Runtime-aware recovery 提交命令。
#[derive(Clone, Debug)]
pub struct DurableRecoveryCommit {
pub run_id: String,
pub runtime_id: String,
pub expected_runtime_revision: Option<u64>,
pub snapshot: RuntimeSnapshot,
pub events: Vec<RuntimeEvent>,
}
/// 第一阶段可替换 durable store 合同。
///
/// 所有参数都是拥有值,适合后续放进 trait object;实现方负责保证
/// `create_run_bundle` 和 lease 操作的原子性,runtime snapshot CAS 则保持
/// Core 的 `expected_revision` 语义。这里不暴露 SQL、连接或事务生命周期。
pub trait DurableStore: Send + Sync {
type Error: std::error::Error + 'static;
fn create_run_bundle(
&self,
bundle: DurableRunBundle,
) -> Result<DurableBundleResult, Self::Error>;
fn get_run(&self, run_id: &str) -> Result<Option<DurableRunView>, Self::Error>;
fn get_session(&self, session_id: &str) -> Result<Option<DurableSessionView>, Self::Error>;
/// 查询 cooperative cancel 标记,不改变 run 状态。
fn is_cancel_requested(&self, run_id: &str) -> Result<bool, Self::Error>;
/// 反查 run 所属的 Core runtime 身份。
fn runtime_id_for_run(&self, run_id: &str) -> Result<Option<String>, Self::Error>;
/// 更新 session 投影;`metadata = None` 表示只更新状态。
fn update_session(
&self,
session_id: &str,
status: &str,
metadata: Option<Value>,
) -> Result<DurableSessionView, Self::Error>;
fn claim_run_with_lease(
&self,
run_id: &str,
worker_id: &str,
lease_token: &str,
lease_duration: Duration,
) -> Result<DurableClaimResult, Self::Error>;
fn get_run_lease(&self, run_id: &str) -> Result<Option<DurableLeaseView>, Self::Error>;
fn heartbeat_run(
&self,
run_id: &str,
worker_id: &str,
lease_token: &str,
lease_duration: Duration,
) -> Result<DurableLeaseView, Self::Error>;
fn release_run_lease(
&self,
run_id: &str,
worker_id: &str,
lease_token: &str,
) -> Result<DurableRunView, Self::Error>;
fn request_cancel(&self, run_id: &str) -> Result<DurableRunView, Self::Error>;
fn list_stale_run_ids(&self, limit: usize, now_ms: i64) -> Result<Vec<String>, Self::Error>;
/// 仅恢复带 safe checkpoint 的 run,不启动 Engine。
fn requeue_safe_run(&self, run_id: &str) -> Result<DurableRunView, Self::Error>;
fn read_checkpoint(&self, run_id: &str) -> Result<Option<DurableCheckpointView>, Self::Error>;
fn read_checkpoint_with_lease(
&self,
run_id: &str,
worker_id: &str,
lease_token: &str,
) -> Result<Option<DurableCheckpointView>, Self::Error>;
fn save_checkpoint_with_lease(
&self,
checkpoint: DurableCheckpointInput,
worker_id: &str,
lease_token: &str,
) -> Result<DurableCheckpointView, Self::Error>;
fn save_checkpoint_with_runtime_and_lease(
&self,
commit: DurableCheckpointRuntimeCommit,
worker_id: &str,
lease_token: &str,
) -> Result<DurableCheckpointView, Self::Error>;
fn record_reconciliation_result(
&self,
run_id: &str,
phase: &str,
external_id: &str,
step: i64,
attempt: i64,
messages: Value,
) -> Result<DurableCheckpointView, Self::Error>;
fn create_approval(
&self,
approval: DurableApprovalInput,
) -> Result<DurableApprovalView, Self::Error>;
/// 原子写入 approval,并在同一 adapter 事务中校验/刷新对应的
/// awaiting checkpoint 与 runtime snapshot。旧实现默认返回明确的
/// `Unsupported`,不破坏已有 fake/兼容 adapter。
fn create_approval_with_checkpoint_runtime_and_lease(
&self,
_commit: DurableApprovalCheckpointRuntimeCommit,
) -> Result<DurableApprovalView, Self::Error>
where
Self::Error: From<DurableStoreUnsupported>,
{
Err(
DurableStoreUnsupported::new("create_approval_with_checkpoint_runtime_and_lease")
.into(),
)
}
fn get_approval(&self, approval_id: &str) -> Result<Option<DurableApprovalView>, Self::Error>;
fn list_approvals_for_run(&self, run_id: &str)
-> Result<Vec<DurableApprovalView>, Self::Error>;
fn get_approval_for_run_call(
&self,
run_id: &str,
tool_call_id: &str,
) -> Result<Option<DurableApprovalView>, Self::Error>;
fn resolve_approval(
&self,
resolution: DurableApprovalResolution,
) -> Result<DurableApprovalView, Self::Error>;
fn cancel_pending_approvals(&self, run_id: &str) -> Result<usize, Self::Error>;
fn queue_approved_run(&self, approval_id: &str) -> Result<DurableRunView, Self::Error>;
fn finish_run_with_runtime(
&self,
command: DurableFinishCommand,
) -> Result<DurableRunView, Self::Error>;
/// 兼容旧调用方的带 lease cancelled 收口;不伪造 runtime 事件。
fn mark_cancelled_with_lease(
&self,
run_id: &str,
worker_id: &str,
lease_token: &str,
output: Option<Value>,
) -> Result<DurableRunView, Self::Error>;
/// 兼容旧调用方的无 lease cancelled 收口;不伪造 runtime 事件。
fn mark_cancelled(
&self,
run_id: &str,
output: Option<Value>,
) -> Result<DurableRunView, Self::Error>;
fn recover_expired_run(&self, run_id: &str) -> Result<DurableRunView, Self::Error>;
fn recover_expired_run_with_runtime(
&self,
commit: DurableRecoveryCommit,
) -> Result<DurableRunView, Self::Error>;
fn upsert_external_session(
&self,
session: DurableExternalSessionInput,
) -> Result<DurableExternalSessionView, Self::Error>;
fn update_external_session(
&self,
id: &str,
external_id: &str,
status: &str,
metadata: Value,
) -> Result<DurableExternalSessionView, Self::Error>;
fn create_tool_call(
&self,
call: DurableToolCallInput,
) -> Result<DurableToolCallView, Self::Error>;
fn complete_tool_call(
&self,
call_id: &str,
status: &str,
result: Value,
) -> Result<DurableToolCallView, Self::Error>;
/// 原子创建工具调用行并提交对应 Core runtime 事件。
///
/// 默认实现显式返回 `Unsupported`,保留旧 fake/adapter 的兼容性;支持
/// 该合同的实现必须在自己的事务中同时校验可选 lease、runtime CAS、
/// `call` 身份和事件序列。checkpoint 不属于本命令的原子边界。
fn create_tool_call_with_runtime_and_lease(
&self,
_commit: DurableToolCallRuntimeCommit,
) -> Result<DurableToolCallView, Self::Error>
where
Self::Error: From<DurableStoreUnsupported>,
{
Err(DurableStoreUnsupported::new("create_tool_call_with_runtime_and_lease").into())
}
/// 原子更新工具调用结果并提交对应 Core runtime 事件。
///
/// `status`/`result` 与 `commit.call.id` 配对;适配器应拒绝身份不一致或
/// 重复终态,并在同一事务中完成工具行和 runtime event 写入。checkpoint
/// 仍由现有 checkpoint command 单独提交。
fn complete_tool_call_with_runtime_and_lease(
&self,
_commit: DurableToolCallRuntimeCommit,
_status: &str,
_result: Value,
) -> Result<DurableToolCallView, Self::Error>
where
Self::Error: From<DurableStoreUnsupported>,
{
Err(DurableStoreUnsupported::new("complete_tool_call_with_runtime_and_lease").into())
}
/// 原子创建工具调用、checkpoint 和对应 Core runtime 事件。
///
/// 默认实现显式返回 `Unsupported`;支持该合同的 adapter 必须在自己的
/// 事务中同时执行 lease fencing、runtime CAS、checkpoint upsert 和工具行
/// 写入,不能由 facade 把四次独立调用拼成假事务。
fn create_tool_call_with_checkpoint_runtime_and_lease(
&self,
_commit: DurableToolCallCheckpointRuntimeCommit,
) -> Result<DurableToolCallView, Self::Error>
where
Self::Error: From<DurableStoreUnsupported>,
{
Err(
DurableStoreUnsupported::new("create_tool_call_with_checkpoint_runtime_and_lease")
.into(),
)
}
/// 原子完成工具调用、checkpoint 和对应 Core runtime 事件。
///
/// `status`/`result` 必须和 `commit.call.id` 及最终 runtime snapshot 一致;
/// 任一校验失败都应回滚 checkpoint、runtime event 和工具行。
fn complete_tool_call_with_checkpoint_runtime_and_lease(
&self,
_commit: DurableToolCallCheckpointRuntimeCommit,
_status: &str,
_result: Value,
) -> Result<DurableToolCallView, Self::Error>
where
Self::Error: From<DurableStoreUnsupported>,
{
Err(
DurableStoreUnsupported::new("complete_tool_call_with_checkpoint_runtime_and_lease")
.into(),
)
}
fn get_tool_call(&self, call_id: &str) -> Result<Option<DurableToolCallView>, Self::Error>;
fn list_tool_calls_for_run(
&self,
run_id: &str,
) -> Result<Vec<DurableToolCallView>, Self::Error>;
fn get_external_session(
&self,
id: &str,
) -> Result<Option<DurableExternalSessionView>, Self::Error>;
fn list_external_sessions(
&self,
statuses: &[&str],
run_id: Option<&str>,
limit: usize,
) -> Result<Vec<DurableExternalSessionView>, Self::Error>;
fn load_runtime_snapshot(
&self,
runtime_id: &str,
) -> Result<Option<RuntimeSnapshot>, Self::Error>;
fn commit_runtime_snapshot(
&self,
runtime_id: &str,
expected_revision: Option<u64>,
snapshot: &RuntimeSnapshot,
events: &[RuntimeEvent],
) -> Result<(), Self::Error>;
}