架构
A3S Code 把配置解析、逐 run 执行、稳定 wire contract 与持久化分成清晰边界。 TUI 和 SDK 使用同一个运行时内核,不存在另一套执行路径。
异步优先的 Session 构建
SessionOptions 是公开的配置 patch,不是半初始化的运行时状态。异步构建路径会把
它与 CodeConfig 合并,校验冲突选项,初始化异步资源,再生成唯一的内部
ResolvedSessionConfig。后续 session assembly 只消费这个已解析值,不会让不同
层各自重复决定同一个配置。
Rust 宿主应优先使用:
Agent::session_async、resume_session_async、session_for_agent_async 与
session_for_worker_async 使用同一个构建内核。文件型 memory/session store、
queue、trajectory recorder 与 session MCP discovery 都在异步阶段初始化;失败时
返回带具体 resource 的 SessionConfiguration 或 SessionInitialization 类型错误。
同步 Agent::session 只为已经显式传入预初始化 memory store、并初始化好其他全部
资源的宿主保留兼容。它不会启动或阻塞 Tokio runtime。任何需要异步工作的配置都会返回
CodeError::AsyncSessionBuildRequired;运行时不会悄悄换成更容易构建的 backend。
通过 SessionOptions::with_mcp 传入的 manager 始终需要异步 capability discovery;
同步路径只能继承 agent 启动时已经缓存的 global MCP tools。
对话状态 Single-Flight
对话历史通过 admission 串行化,而不是依赖乐观锁。同一个 session 同时只能有一个
会影响 transcript 的操作,包括 send、stream、两种 attachment variant、
slash command 与 resume_run。重叠调用会在读取历史或派发命令前立即返回
CodeError::SessionBusy。
流式调用会一直持有 admission lease,直到 stream runtime 真正结束。丢弃或中止 公开 handle 不会在原 producer 仍写入 event/history 时短暂放行第二个操作。直接 宿主工具调用属于控制面操作,不占用 conversation lease。
Invocation Context
每个已准入 run 都创建一个不可变的 InvocationContext,其中包含:
- run ID 与 session ID
- run cancellation token
- event sender
- governance snapshot,包括当前 budget guard
它是 provider 与 tool 工作的单一事实来源,也会把同一个 cancellation token 和
session identity 安装进 ToolContext。因此取消可以传递到 queued tools、嵌套
batch/program、委派任务、planning、structured-output repair、compaction 和
其他属于该 run 的 helper call。
LLM 调用边界
属于 run 的 provider 工作统一经过 scoped LLM invoker。它在每次 provider call 前检查预算与取消,在成功响应后记录 usage;streaming path 会代理 terminal usage, 并合并 caller cancellation 与 run cancellation。普通 turn、planning、structured output/repair、compaction 以及 memory/helper path 都使用这个边界,不再各自维护 预算逻辑。
硬预算拒绝会作为错误返回,绝不会转换成不受治理的 fallback。soft limit 会发出
budget_threshold_hit 事件,然后继续当前调用。
Tool 调用边界
Tool invoker 是 model-selected、nested、programmatic、delegated 与 direct host
call 的统一治理内核。对模型发起的工作,它会执行 active-skill restriction、
permission policy、pre/post hook、budget check、human confirmation、queue/timeout、
cancellation、递归调用保护与 output sanitization。batch 和 program 接收的是
scoped invoker,而不是 raw registry,因此内部调用不能绕过这些检查。
直接 SDK helper 使用显式的 HostDirectPolicy::TrustedControlPlane origin。宿主已经是
选择该操作的权威,因此跳过面向模型的 permission/HITL 决策;但 pre-hook 仍可
阻止调用,budget、queue/timeout、cancellation、递归保护、post-hook 与 output
sanitization 仍然生效。应用在把这条特权路径暴露给终端用户前,必须自行完成授权。
稳定事件协议
AgentEvent 是 Rust 内部运行时 enum。跨语言 contract 是无损 envelope:
v1 事件目录与 Rust exhaustive mapping 共享一个事实源,因此新增 runtime variant
却没有 canonical wire name 会直接导致编译失败。Node 与 Python 使用统一投影生成
text、toolName、tool_name 等 convenience field。type 保持开放字符串:
未来未知类型仍完整保留 payload 与 metadata,不会被压成 unknown sentinel。
原子 Session 持久化
SessionSnapshotV1 表示一个有版本的完整持久化 generation,包含 conversation、
artifact、trace event、run record、verification report 与 delegated-task snapshot。
session.save() 会物化这个 aggregate,并且只调用一次
SessionStore::save_snapshot。
File store 先写入并同步临时文件,再用 atomic replacement 发布一个完整 JSON
envelope;memory store 在同一把锁下替换整个 aggregate entry。两者都会声明 atomic
snapshot capability。历史 bare SessionData 与 fragment directory 仍可读取以便
迁移,但新保存不会发布碎片化 generation。自定义 store 必须显式实现 aggregate
save;默认方法返回错误,不会把 partial/no-op write 当成成功。
MCP 所有权与隔离
MCP manager 的所有权是显式的:
- Agent-global manager 持有全局配置加载的 server。
- Session option 中由宿主传入的 manager 是继承的只读 capability source。
- 每个 session 都新建一个私有 live manager。
Capability 按以上顺序组装,因此 session-local tool 只能在当前 session 内 shadow
继承工具。实时 add_mcp_server / remove_mcp_server 只改变私有 manager;移除本地
shadow 后会重新显露继承能力。Sibling session 不能互相修改;delegated child 会
继承调用同一批工具所需的有序 manager source,但不会取得它们的所有权。
Programmatic Tool Calling
program 工具在内嵌 QuickJS VM 中运行 JavaScript,只暴露受控 ctx 对象。VM
没有直接文件系统、网络、子进程或环境变量访问权;有用能力都通过 scoped tool
invoker 回到 A3S Code。下一步需要判断时使用普通模型工具调用;动作序列已经确定、
只需让模型理解结果时,使用有边界的 program。
扩展点
通过 typed session options、skills、agent definitions、hooks、MCP servers、 memory/session stores、security providers、queue configuration 与 workspace services 扩展运行时。优先使用显式 policy 和可回放证据,不要引入平行执行路径。