Skip to content

Latest commit

 

History

History
525 lines (436 loc) · 40.6 KB

agent-runtime-deployment-design.md

File metadata and controls

525 lines (436 loc) · 40.6 KB

Agent Runtime 部署与多实例边界

本文定义 Desktop、TUI、Headless CLI、Agent SDK 与本机控制端并存时,BitFun Agent Runtime 的部署、所有权和隔离边界。

Agent Runtime 的模块职责见 agent-runtime-services-design.md,公开 SDK 见 agent-sdk-product-architecture.md,第三方 JS/TS 进程见 extensions/plugin-runtime-design.md。Rich Client 的 App Server 协议、Embedded/Shared Host 和 transport 提案见 app-server-architecture.md。该提案通过架构评审前,当前部署和调用路径以本文及 已接线代码为准。

1. 决策与当前状态

BitFun 只有一套 Agent Runtime 行为。Embedded 和 Shared 只描述同一套 Runtime 的物理部署方式,不是两套实现。

1.1 Current request paths

flowchart TB
  Desktop["Desktop GUI"] --> DesktopAdapter["Desktop / Tauri adapter"]
  Web["Web UI"] --> WebAS["loopback WebSocket App Server"]
  TUI["Interactive TUI"] --> Backend["TuiBackend"]
  Backend -->|"Embedded"| EmbeddedAS["in-process App Server"]
  Backend -->|"--shared"| SharedIPC["private Runtime IPC v17"]
  Other["Headless CLI · ACP · Peer Host · SDK Host"] --> Adapter["独立 first-party adapters"]
  DesktopAdapter --> API["Agent Runtime API / owner ports"]
  WebAS --> API
  EmbeddedAS --> API
  SharedIPC --> API
  Adapter --> API
  API --> Coordinator["ConversationCoordinator"]
  Coordinator --> Owners["Session / Tool / Permission / MCP owners"]
Loading

Server bootstrap 是 composition root,不是客户端请求的第二条 Runtime 旁路:

flowchart LR
  Bootstrap["Server bootstrap / product assembly"] -. "constructs" .-> Host["transport + BitfunAppServer"]
  Bootstrap -. "constructs" .-> Runtime["Embedded Runtime / owners"]
  Runtime -. "injects Runtime API and owner ports" .-> Host
Loading

两张图中的实线表示当前业务请求,虚线只表示启动期构造与依赖注入。

1.2 Proposed Rich Client target

flowchart TB
  Rich["Desktop GUI · Web UI · Interactive TUI"] --> Host["Rich Client Host"]
  Host --> Client["App Server client"]
  Client --> Transport["Host-selected Embedded / Shared transport"]
  Transport --> AppServer["App Server"]
  Other["Headless CLI · ACP · Peer Host"] --> Adapter["独立 first-party adapters"]
  SDK["Public Agent SDK"] --> SDKHost["SDK Host adapter"]
  AppServer --> API["Agent Runtime API / owner ports"]
  Adapter --> API
  SDKHost --> API
Loading

该图是待评审目标,不是当前调用链。Shared App Server 只有达到 v17 的连接治理、安全、恢复、取消、限制、性能和回滚门槛后, 才可替换 compatibility transport;评审也可以决定保留 private v17 作为 Shared TUI 的物理 wire。

1.3 Current implementation facts

范围 当前状态
Embedded Desktop GUI 继续使用 Desktop 事件投影和 Tauri adapter;按实际打开的本机 workspace 延迟取得并持有 Embedded ownership,不增加后台进程;目标迁入同进程私有 App Server
Embedded interactive TUI 已组装同进程私有 App Server,通过 in-memory transport、AppServerClient 和 AppServerTuiBackend 完成当前核心聊天与 Session 路径;剩余管理面继续迁移
Embedded Headless CLI/Peer Host 保留各自独立 Runtime adapter、展示和断流策略;不因交互式 TUI 迁移而强制使用 App Server
ACP/SDK Host 使用同一个 Runtime 事件入口的 session-scoped 订阅;各自协议和进程生命周期保持独立
Runtime ownership Desktop、CLI、ACP、SDK Host 和现有 Server agent bootstrap 共用 Core owner;Embedded 取得共享锁,Shared TUI 取得独占锁,同一 workspace 上两种 deployment 互斥
Session 写入 BitFun Runtime 的持久化 Session 由 SessionManager 管理;同一存储位置中的同一 Session 同时只允许一个本机进程写入,list/view 等只读操作不受影响
当前 HTTP Server 已组装 Embedded Runtime 和 BitfunAppServer,每个 /ws 连接通过 WebSocket transport 运行一条 App Server connection;当前固定 loopback、单用户且缺少连接级身份与作用域绑定,不构成远程或多用户 Server API
Shared local IPC 未发布的 v17 本机协议已有 discovery、实例锁、严格握手、Session 控制权、有界事件流和 cleanup;唯一 consumer 是第一方交互式 TUI compatibility adapter;是否由 Shared App Server 替换仍待评审与等价证据
Shared TUI bitfun --shared / bitfun chat --shared 可列出、创建、恢复 Session,删除未被控制的空闲非当前 Session,通过 /fork 从完整历史或选中提示词之前创建分支,重命名当前 Session,读取 transcript,通过 View subagents 只读查看当前根 Session 的子会话并定向取消子会话活动 Turn,切换当前 Session 的 Agent mode/model,通过 `/reload [skills
Shared GUI/Headless/ACP/SDK Host/Remote 未交付,也不会由 --shared 隐式启用;Replay、Observer、通用 Controller transfer 和 Session archive 同样不在当前协议中

因此当前交付的是 Embedded TUI App Server 与一条窄的、显式启用的 Shared TUI compatibility deployment,不是通用本机 Server。 具体 EventQueue 仍由 Core 产品装配;当前 Shared IPC 只把 TUI 必需的强类型操作和事件映射到同一个 Runtime owner, 没有公开协议承诺。是否以 App Server Shared transport 替换并删除它,由行为等价、性能、安全和回滚证据决定。

2. 最少名词

名词 唯一含义 不等于
Agent Runtime 负责 Session、Turn、Tool、MCP、Permission、Hook、事件和持久化行为的既有模块 进程名、Server 或 SDK
Embedded deployment Runtime 与调用入口位于同一 Rust 进程 简化版 Runtime
Shared deployment 同一 Runtime 由一个本机进程承载,多个第一方 Client 通过私有 IPC 使用 新 Runtime、公开 Server 或 Agent SDK
Embedded App Server 与 Rich Client Host 同进程的私有 App Server 实例和 in-memory transport Runtime 直连、后台进程或网络 Server
Shared App Server 独立本机 Host 承载、由多个已认证 Rich Client 通过受控 transport 使用的 App Server 公网 API 或每个 Client 一个 Runtime
Agent SDK Host 将公开 SDK 合同映射到 Runtime API 的私有进程/adapter CLI、Shared deployment 或 Plugin Host
Plugin Host 运行 Node/Bun 和第三方插件代码的受监督子进程 Agent Runtime 或 Rust IPC client

Host 只表示“一个进程承载某些模块”的内部关系,不新增普通用户必须理解或管理的产品入口。

3. Logical View · Level 1

flowchart TB
  subgraph "逻辑层:始终只有一套"
    API["Agent Runtime API"] --> Session["Session / Turn"]
    API --> Permission["Permission"]
    API --> Tool["Tool / MCP"]
    API --> Events["Authoritative events"]
  end

  Desktop["Desktop GUI"] --> DesktopAdapter["Desktop / Tauri adapter"]
  Web["Web UI"] --> AppServer["loopback WebSocket App Server"]
  EmbeddedTUI["Embedded TUI"] --> AppServer
  DesktopAdapter --> API
  AppServer --> API
  SharedCompat["Shared Runtime IPC · temporary compatibility"] --> API
  Headless["Headless / ACP adapters"] --> API
  SDK["SDK Host adapter"] --> API
  Remote["Remote adapter"] --> API
Loading

当前复用的是 Runtime API、权威事实和 owner;Web 与 Embedded TUI 额外复用 App Server wire,Shared TUI 使用 private v17,Desktop 仍使用自己的 adapter。第 1.2 节目标只有通过评审并完成迁移后才扩大 App Server 复用范围。各入口不复用 renderer、CLI 参数、SDK wire、远程认证或平台窗口生命周期。任何新能力必须先进入既有 Runtime owner,再由 App Server 或需要它的独立 adapter 映射,禁止 在 Embedded、Shared 或其他入口复制业务实现。

3.1 Embedded 事件交付

flowchart LR
  Queue["EventQueue"] --> Owner["Core product event queue owner"]
  Owner -->|"injects read-only AgentEventSource"| Runtime["Agent Runtime API"]
  Runtime --> AppServer["Embedded App Server"]
  AppServer --> TUI["Interactive TUI client"]
  AppServer --> GUI["Desktop GUI client · target"]
  Runtime --> Exec["Headless adapter"]
  Runtime --> Peer["Peer fanout adapter"]
  Runtime --> ACP["ACP adapter"]
  Runtime --> SDK["SDK Host adapter"]
Loading
  • Core product assembly 创建事件 source,并维持旧消费队列的排空 task;第一方产品入口不再获得第二个订阅 API。
  • App Server server 从注入的 AgentEventSource 转发 Rich Client 权威事件;Rich Client 不得从 AgentRuntime 或 Core EventQueue 旁路订阅。
  • Headless CLI、Peer Host、ACP 和 SDK Host 从各自独立 Runtime adapter 订阅,不能直接持有 Core-specific event source。
  • bitfun-core 的旧 event-source/builder API 仅保留为 deprecated 源码兼容 facade;它们委托给同一个 Core owner,不形成第二套运行时或第一方调用路径。
  • 各 adapter 继续拥有自己的失败投影:TUI 标记当前视图不可信,Headless CLI 返回非成功终态,Peer Host 中断其拥有的 turns,ACP 取消 turn 并返回协议错误,SDK Host 终结 Query 并提供 RestartHost recovery。
  • 当前 App Server 为每条 connection/stream 发送单调 sequence 和 connection-local cursor;app/syncEvents 返回当前连接的 cursor 与 pending Permission snapshot,session/sync 恢复 Session state、transcript、workspace binding 和 pending Permission。它没有跨连接 持久化 replay/resume:重连后的旧 cursor 不能继续消费,client 必须重新 initialize 并执行权威 sync。
  • Shared Runtime IPC v17 不复用 App Server cursor。它按自己的有界队列规则处理 lag/closed:Agent 流失效后 fail closed;Permission lag 尝试从 Runtime 的 pending 集合重建,重建失败或流关闭时取消当前 Turn 并退出。任何路径都不能把流失效伪装成透明恢复。
  • 这条链路仍全部位于当前 Embedded 进程;Rich Client 使用 private in-memory transport,不增加 SDK Host、跨进程 IPC 或后台进程依赖。

4. Process View · Level 1

4.1 Runtime ownership

ownership 分成“产品决策”和“文件锁原语”两层;入口不再各自拼 key、目录或锁模式:

flowchart TB
  Entrypoints["Desktop · CLI · ACP · SDK Host · Server bootstrap"]
  Entrypoints --> Core["CoreRuntimeOwnership<br/>deployment · product identity · process-held lock"]
  Core --> Primitive["services-core::runtime_ownership<br/>canonical key · RAII file lock"]
  Primitive --> E["Embedded · shared lock"]
  Primitive --> S["Shared · exclusive lock"]
Loading
flowchart TD
  Op["Session operation"] --> Read{"read-only view/list?"}
  Read -->|"yes"| NoLock["不取得 ownership"]
  Read -->|"no · attach/mutate/turn"| Remote{"structured remote facts?"}
  Remote -->|"yes"| RemoteHost["由目标 execution host 负责"]
  Remote -->|"no"| Gate["Coordinator → CoreRuntimeOwnership"]
  Gate --> Lock["按 canonical workspace 持有文件锁"]
Loading
场景 行为 原因
多个 Embedded 进程访问同一 workspace 共享锁允许并存 保持单实例、CI 和隔离测试的既有成本模型
Shared 与任一 Embedded 访问同一 workspace 后启动者返回稳定错误码和启动建议 防止同一 workspace 同时存在两种 Runtime deployment
Desktop 打开多个 workspace 首次 attach/write 时逐个取得并持有文件锁 不把窗口数、Session 数等同于 Runtime 进程数
只读 list/view 不加锁 ownership 只管理 Runtime deployment,不扩大成读取权限
已解析且带有效 connection_id 的 remote workspace 本机不加锁 与 Session storage 的远端判据一致;host 提示本身不能绕过本地锁
当前 loopback HTTP Server 通过 server bootstrap 创建 Embedded Core owner 只覆盖 Server Host 实际打开的本机 workspace;不因存在 WebSocket route 扩大为远程或多用户 ownership

CoreRuntimeOwnership 只选择 deployment、产品 identity 并在进程存活期间持有锁;services-core 只负责 canonical key 和跨进程锁。二者都不选择 workspace、不启动 Runtime,也不替代 Session 单写、数据库事务、文件冲突控制或安全沙箱。

4.2 Session 单写

workspace 可以被多个 Embedded 进程同时打开,但持久化 Session 不能被多个进程同时写入。保护粒度是“实际 Session 存储位置 + Session ID”,不是窗口、TUI 实例或 workspace。

flowchart LR
  subgraph W["同一 workspace"]
    A["Session A"]
    B["Session B"]
  end

  GUI["GUI 进程"] -->|"写入"| A
  TUI["TUI 进程"] -->|"写入"| B
  CLI["另一个 CLI 进程"] -.->|"写入 A:session_in_use"| A
  View["任意入口的 list / view"] -.->|"只读"| A
  View -.->|"只读"| B
Loading

BitFun Runtime Session 只有 SessionManager 决定何时开始和结束写入;底层持久化方法复用同一文件锁,不再实现第二套判断。各产品入口只投影同一个 session_in_use 事实,不重新判断锁状态:

入口 冲突呈现 恢复方式
Agent SDK / BitFun ACP 结构化 session_in_use;SDK Host 映射为可重试的 action_required 调用方在原实例关闭 Session 后重试
Embedded / Shared TUI 明确提示 Session 已在另一实例打开;切换失败时保留当前 Session 用户关闭另一实例后再次选择;不自动等待或切换
Desktop / Peer GUI 历史视图保持只读可见;首次写入显示持久提示和显式“重试”操作 用户关闭另一实例后点击重试;不自动提交消息
Headless json 失败结果带 error_code=session_in_use,详细说明进入结果和 stderr 调用方依据稳定码决定是否重试
Headless stream-json 复用已有 SystemError,error=session_in_use、recoverable=true 调用方结束本次非零退出后重新执行

Desktop 作为 ACP client 管理的外部 agent Session 不经过该 Runtime owner,不在本节的 Session 单写范围内。recoverable 只表示关闭现有 writer 后可以重新调用,不表示自动等待、自动抢占或恢复当前调用。

场景 行为
同一进程重复 restore 同一 Session 返回已加载的 Session,不重复取得或释放写入权
另一个进程打开同一存储位置中的同一 Session 立即返回 session_in_use;不等待、不自动抢占
多个进程打开同一 workspace 中的不同 Session 允许,各 Session 独立写入
多个进程更新同一 Session 列表索引 按存储位置串行更新共享索引,不影响不同 Session 文件并行写入
.、..、符号链接或 Windows 路径大小写指向同一存储位置 视为同一个 Session 存储位置
相同 Session ID 位于不同存储位置 文件锁相互独立;同一 SessionManager 仍按 Session ID 保持唯一绑定,不能同时加载
Session 存储路径无法解析或错误地指向文件系统根目录 在发布内存状态前返回错误,不创建可写 Session
create/restore 在发布到内存前失败、取消或超时 临时文件锁随操作释放;后续进程可以重试
save、cleanup 或 unload 失败 已加载 Session 继续持有写入权,避免另一个进程接手不完整状态
unload 或 delete 成功 释放写入权
进程崩溃或被强制结束 操作系统释放文件锁;残留锁文件本身不代表 Session 仍在使用
Remote workspace 在实际 Session 存储所在机器执行同一检查;控制端不得用本机路径替代

该机制不增加后台进程、轮询、连接或常驻线程,也不改变 Shared TUI 的连接控制规则。临时 Session 不写入磁盘,因此不参与此检查。

4.3 私有本机 IPC

sequenceDiagram
  participant C as Shared TUI client
  participant D as User-private discovery
  participant S as Shared Runtime Host process

  C->>D: read endpoint + token + identity + protocol
  C->>S: connect via Named Pipe / UDS
  C->>S: initialize(identity, protocol, token)
  alt valid
    S-->>C: initialized(health + interactive_tui)
    C->>S: create or restore Session
    S-->>C: Session control + Session facts
    C->>S: rename or update current Session
    C->>S: delete idle non-current Session
    C->>S: reload current Session context
    C->>S: compact current Session context
    C->>S: submit/cancel Turn or answer Permission/UserInput
    S-->>C: Session-filtered authoritative events
  else invalid
    S-->>C: typed error and close
  end
Loading

当前私有协议(v17)只覆盖 TUI 已有用户旅程需要的窄操作:

已支持 明确不支持
Health、只读 workspace-scoped main Agent 摘要、Session list/create、原子 restore(含 transcript 与 pending Permission)、删除未被控制的空闲 Session、当前 Session fork(含 transcript)、rename、Agent mode/model update、声明式上下文 reload、根 Session lineage 查询与后代 transcript 读取 Session archive、跨 workspace attach、transcript 分页、模型目录/默认值和完整 Agent/Subagent 管理
Turn submit/cancel、当前 Session 手动 context compaction、lineage 成员校验后的单个后代执行子树取消 replay、cursor、resume event stream、独立的根级批量后代取消 API
pending/respond Permission、submit UserInput answers、只读 workspace diff observer、通用 controller transfer、多 Session multiplex
连接断开清理、Session-filtered events detach/observer/通用 controller transfer、SDK callbacks、GUI/Remote/Peer/ACP/Headless wire

这些操作先满足以下本机 IPC 地基,而不把协议升级为公开 SDK:

  • workspace、产品、release channel、用户和协议版本共同生成实例身份;
  • instance lock 而不是 PID/discovery 文件决定唯一 server owner;
  • Windows 使用拒绝远程连接的 Named Pipe;Unix 使用短且由 instance identity 决定的稳定 Domain Socket 名称,权限为 0600;
  • discovery 所在目录必须由未来 composition 选择为当前用户私有目录;
  • discovery 通过同目录临时文件原子替换;Unix endpoint 保留原生路径字节,路径过长时在 bind 前返回明确错误;
  • 第一帧必须完成 token、instance identity 和 protocol version 校验;
  • 未认证握手预算为 2 秒;认证后的单次操作、响应写入和断线取消预算为 120 秒,避免坏客户端长期占用连接或 Runtime handler;
  • JSON frame 使用 4-byte 长度前缀;request 在发送前执行 128 KiB 上限(覆盖 TUI 已有的 64 KiB 粘贴输入及类型化信封),response/event 在序列化时执行 8 MiB 上限。超限返回类型化错误,不能进行无界分配;超过该上限的历史 Session 暂由 Embedded TUI 打开,不在本阶段引入分页协议;
  • 未认证连接也计入有界 connection budget,单个客户端不能无限制造 server task;
  • 未知 frame/operation 信封字段、未知 operation、错误身份和不兼容版本 fail closed;复用的 Runtime DTO 按其既有反序列化契约处理字段;
  • v10 增加两个只读、current-controller 限定的工作区引用 operation:按当前 Session 搜索文件/目录,以及按 user message ID 读取已持久化的结构化引用。两者复用 Agent Runtime 的 workspace-reference port,不赋予 IPC adapter 文件系统或 Session 持久化所有权,也不扩展为 Remote 或公开 SDK 协议。
  • v11 增加无请求体、无 Session lease 的只读 workspace diff operation。它只在当前连接没有活动 Turn 时查询 Runtime 启动时绑定的 canonical workspace,避免单连接请求排序阻塞流事件或 Turn 控制,并返回 Runtime Port DTO;Git 行为仍由 services-integrations provider 持有。文本 patch 总量限制为 3 MiB,为 JSON 转义和 envelope 预留既有 8 MiB response frame 的空间;该 operation 不隐式获得 stage/reset/commit、Remote 或公开 SDK 能力。
  • v12 增加用户显式 Shell Turn;v13 增加活动 Turn steering。两者都复用 Agent Runtime 的原有准入、Tool、权限、持久化和取消 owner,不在 IPC 内复制执行状态机。
  • v14 增加三个 current-root-controller 限定的 lineage operation:查询 Runtime 归一化后的扁平 lineage、读取已验证后代的权威 transcript,以及取消指定后代的活动执行子树。查询和读取可在根 Turn 活动时执行;取消复用现有 Session abort 语义,但不切换 controller,也不引入 observer、detach、分页或通用 Session RPC。
  • v15 为后代 transcript 读取增加 required_settled_turn_ids 一致性前置条件:Runtime 必须确认这些 Turn 已由 owner 持久化为终态,否则返回 outcome_unknown,由 TUI 在同一绝对期限内退避重试;TUI 只保留事件投影和该读屏障,不合并或重写权威 transcript。后代取消同时携带用户实际看到的 expected_active_turn_id,并在 owner 锁内拒绝已经切换的 Turn,避免迟到操作取消后续执行。lineage 查询和 transcript 读取是每连接至多一个的可抢占推测读取;更新的请求会取消旧读取,使后代取消和 Session 切换不会排在慢 transcript I/O 之后。该行为不放宽 controller 校验,不引入 observer 或通用多路复用。
  • v16 增加只读、workspace-scoped main Agent 摘要,用于 Shared TUI 与 Runtime host 的 selector 投影一致。启动页以 Runtime 启动工作区查询且不取得 Session lease;已有 Session 由 Runtime owner 解析其执行工作区并要求当前 controller。响应只包含逻辑 ID、描述、可选固定 model ID 与 ecosystem-neutral 的 external-source 分类;发现、审批、冲突消解、generation 与执行仍由既有 Agent Registry 和 external-source owner 负责,不经 IPC 暴露安装、变更、激活、Subagent 管理或 runtime lifecycle API。
  • v17 扩展原子 restore,使响应带回 Runtime Session state;增加结构化 Session usage、等待指定 Turn settlement,以及记录本地命令 transcript turn 的 operation。它只补齐当前 Shared TUI 与 TuiBackend 的行为等价,没有增加 replay、observer、通用 controller transfer、多 Session multiplex 或公开 SDK 能力。
  • 一个连接最多控制一个 Session、同时最多提交一个活动 Turn;一个 Session 同时只有一个 controller。create/restore/fork 在完整结果通过大小检查后才原子切换控制权,失败时保留原 Session。fork 只接受当前 controller 的空闲 Session;无选中 Turn 时复制到最新持久化 Turn,指定 before_turn_id 时只复制该 Turn 之前的历史。活动 Turn 期间不能切换或 fork Session,也不能修改其名称、Agent mode 或 model;删除只作用于非当前且未被任何连接控制的 Session。
  • Submit 与手动 context compaction 都使用调用方已有的 turn_id 标识不确定结果;若操作超时,返回 outcome_unknown、关闭连接并按该 ID 取消。手动 compaction 要求当前 controller 且 Session 空闲,由 Core 通过与普通对话 Turn 共用的原子准入路径创建一个可审计 maintenance Turn,并在取得所有权后读取压缩上下文:planning 阶段允许取消,atomic commit 开始后忽略晚到取消并保持 Processing 直至终态持久化完成。maintenance Turn 保留在权威 transcript 中但不进入模型上下文,live/restored payload 使用同一 compression ID 和 applied 事实;commit 后的持久化故障发布明确失败终态而不是遗留 Processing。断连取消只有得到确认后才释放 Session 控制权;无法确认时继续隔离该 Session,直到 Runtime 进程退出。
  • Session delete/rename 和 Agent mode/model update 复用既有 Runtime 端口和校验,Runtime 对最终结果保持权威并拒绝无效目标。它们都是有副作用操作;发送前编码或 frame 上限失败表示请求未执行,连接仍可使用。rename 写入失败时恢复旧 metadata:确认恢复后返回明确失败,无法确认时返回 outcome_unknown。Shared Client 在请求写入后响应超时或丢失连接时也返回 outcome_unknown 并断开连接。两种情况都不自动重试:rename 由用户恢复 Session 并核对当前值;delete 由用户重新打开 /sessions 核对目标是否仍存在。模型目录以及完整 Agent/Subagent 管理仍是同版本第一方产品事实,不加入 IPC;v16 的 main Agent 摘要只是 host-owned selector 所需的最小只读投影。
  • 声明式上下文 reload 只失效当前 Session 的 instructions 缓存,并按目标复用 Skill Registry 刷新;它可在活动 Turn 中执行但不改写该 Turn,generation 保护保证下一条消息重建上下文。它不引入 watcher、热替换或第二套 Runtime owner。
  • Shared TUI 的模型选择器复用 Client 已有的只读产品配置来显示同版本模型目录;它只把选中的 model ID 通过 update current Session model 交给 Runtime。Client 不持有 Session 写入权,也不通过 IPC 管理模型目录或默认值。
  • Agent 事件流 lag/closed 后 fail closed;Permission lag 先从 Runtime 权威 pending 集合重建,重建失败或流关闭时取消当前 Turn 并退出。路由到父 Session 的嵌套 Permission 与 AskUserQuestion 复用现有 TUI 交互,不新增第二套 UI 状态。
  • Windows Shared Runtime 在初始化前把自身放入 kill-on-close Job;Unix 仅在应用内优雅退出路径中通过受管子进程组回收后代。Runtime 被 SIGTERM、SIGKILL 或崩溃直接终止后的 Unix 后代回收不在当前保证内。两者都只负责生命周期,不是安全沙箱。
  • 最后一个连接离开后等待 30 秒再退出;新连接会取消 idle 退出。退出只删除自己发布的 discovery;Unix 下继任 owner 会在持有实例锁后清理同一 identity 的陈旧 socket。

这是一条本机同用户边界,不是沙箱、远程协议或公开兼容承诺。

4.4 Serialization、并发与性能

flowchart LR
  T1["TUI 1"] --> IPC["有界本机 IPC"]
  T2["TUI 2"] --> IPC
  TN["TUI N"] --> IPC
  IPC --> Runtime["一个 Shared Runtime"]
  Runtime --> Tasks["Tokio tasks"]
  Runtime --> Owner["一个 Session owner"]
Loading

多个 Shared TUI 复用一个 Runtime 进程。每个连接使用独立异步任务,但连接、命令队列和事件队列都有上限;达到连接上限时暂停接收新连接,慢客户端不能建立无界任务或队列。默认不增加 Runtime 进程池,因为复制 Session 状态、模型连接和缓存会扩大一致性成本。只有经测量证明某类无状态 CPU 工作可独立分片时,才评审额外 worker 进程。

路径 数据边界 性能约束
Embedded Rich Client AppServerClient 通过 private in-memory transport 调用同进程 App Server 不初始化跨进程 IPC 或后台进程;保持与 Shared 相同的 JSON-RPC、DTO、错误和事件语义,编解码成本通过测量优化而不增加直连旁路
Embedded non-Rich Client Headless、ACP、Peer 和 SDK Host 的独立 adapter 以 Rust 类型调用 Runtime API 不因 Rich Client 合同承担 App Server wire;保持各自协议和生命周期
Shared request Client 将 operation 编码一次并写入一个长度前缀 frame 请求保持 128 KiB 上限;业务层只接收类型化 operation
Shared response/event Server 将结果或事件编码一次后写出 响应/事件保持 8 MiB 上限;超限使事件流明确失效,不能无界分配
Shared receive 每个方向只有一个严格 transport decode 边界 未知信封字段和不兼容版本 fail closed;严格校验可以检查规范化 JSON,但不能把动态 JSON 传入 Runtime owner
多 TUI 一个 Runtime、最多 64 个连接;每个 Client 的 command channel 容量为 64、event channel 容量为 256 request gate 使每个 Client 同时只有一个控制请求进入 channel;lineage 推测读取写入后释放 gate,Server 同时只保留一个且允许更新请求抢占;Client 使用并发 reader 与单一有序 writer,避免大 transcript 响应和后续大请求互相阻塞;事件落后时失效而非无限缓存

协议只承载当前交互所需的小型控制请求、受 3 MiB 文本上限保护的 workspace diff 快照和既有事件。大 transcript 继续受 frame 上限约束;本阶段不为假设场景增加通用分页、二进制 side channel、压缩或批处理协议。

5. Development and Physical Views · Level 1

5.1 Development View

flowchart TB
  GUI["Desktop GUI"] --> DesktopAdapter["Desktop / Tauri adapter"]
  Web["Web UI"] --> AppServer["App Server"]
  EmbeddedTUI["Embedded TUI"] --> AppServer
  SharedTUI["Shared TUI"] --> SharedCompat["Runtime IPC v17 compatibility"]
  DesktopAdapter --> API["Agent Runtime API / owner ports"]
  AppServer --> API["Agent Runtime API / owner ports"]
  SharedCompat --> API
  CLI["Headless CLI adapter"] --> API
  SDK["SDK Host adapter"] --> API
  ACP["ACP adapter"] --> API
  Server["Server adapter · when assembled"] --> API
  API --> Coordinator["ConversationCoordinator"]
  Coordinator --> Behavior["single behavior owners"]

  GUI -. "composition" .-> Ownership["CoreRuntimeOwnership"]
  Web -. "composition" .-> Ownership
  EmbeddedTUI -. "Embedded" .-> Ownership
  SharedTUI -. "Shared" .-> Ownership
  CLI -. "Embedded" .-> Ownership
  SDK -. "Embedded" .-> Ownership
  ACP -. "Embedded" .-> Ownership
  Server -. "only when Runtime is assembled" .-> Ownership
  Ownership -. "injected once" .-> Coordinator
Loading
flowchart LR
  TUI["Interactive TUI"] --> Backend["TuiBackend"]
  Backend -->|"Embedded"| Client["AppServerClient"]
  Client --> Memory["in-memory transport"]
  Memory --> AppServer["BitfunAppServer"]
  Backend -->|"Shared compatibility"| IPC["adapters/agent-runtime-ipc v17"]
  IPC --> Handler["CLI Shared handler"]
  AppServer --> Runtime["execution/agent-runtime / owners"]
  Handler --> Runtime
Loading

CLI Host 负责命令解析、TUI 状态、错误文案、App Server 组装和 transport 生命周期;TuiBackend 隔离当前 Shared compatibility adapter。 App Server 或私有 IPC 只负责协议、连接控制和类型映射;Agent Runtime 与 owner 负责 Session 校验、持久化和权威结果。 TUI 业务代码不根据部署形态复制业务分支,Shared 达到 App Server 语义等价后替换 compatibility adapter。

  • CLI 不依赖 SDK Host,GUI/TUI 也不依赖公开 SDK package。
  • 交互式 TUI 的启动页和会话页复用 app-local TuiBackend;Embedded backend 使用正式 AppServerClient,Shared backend 暂时映射 private Runtime IPC v17。TUI 不直接依赖 Rust Runtime SDK、Core/Service owner 或 IPC operation。
  • Headless CLI 和 Peer Host 使用同一 Runtime 订阅入口,但分别保留确定性退出与 Peer fanout 语义;共享订阅入口不等于共享 renderer 或产品生命周期。
  • TUI 不是 Server;Embedded Host 在同进程组装私有 App Server,是否连接 Shared deployment 是部署选择,不改变 TUI 的 renderer/键位职责或 App Server 行为合同。
  • Agent SDK Host 只服务外部 SDK 合同,不成为第一方 rich-client 的通用底座。
  • Headless CLI 默认继续 Embedded;CI 或测试可保持独立进程和独立 workspace,不承担后台实例成本。
  • Tauri 仍负责窗口和桌面能力,并逐步收窄为 App Server Host adapter;未来它可以管理 Shared process 的启动/重连,但不拥有 Agent Runtime 业务生命周期。

5.2 Physical View

flowchart TB
  subgraph Embedded["默认 Embedded"]
    TUI["Interactive TUI"] --> AppServer["private in-process App Server"]
    AppServer --> Runtime["in-process Agent Runtime"]
    Headless["Headless / CI"] --> Runtime
  end
  subgraph Shared["显式 --shared"]
    Clients["one or more TUI processes"] -->|"Named Pipe / UDS · current compatibility"| SharedRuntime["Shared Runtime Host process"]
  end
  Runtime --> Data["workspace + Session storage"]
  SharedRuntime --> Data
Loading

默认交互式 TUI、Headless CLI 和 CI 保持 Embedded;交互式 TUI 通过 private in-process App Server,Headless/CI 保留独立 adapter。 只有显式 --shared 的交互式 TUI 进入 Shared;同一 workspace 的两种部署互斥。多开 TUI 增加 Client 进程和有界连接, 不按 Client 数量复制 Runtime、Session owner 或 Plugin Host。

5.3 Scenario (+1) · Rename current Session

sequenceDiagram
  participant U as User
  participant T as TUI adapter
  participant B as TuiBackend
  participant E as Embedded App Server adapter
  participant S as Shared Runtime IPC v17 adapter
  participant R as Agent Runtime

  U->>T: /rename Auth refactor
  T->>T: trim + require idle Session
  T->>B: typed TuiBackend request
  alt Embedded
    B->>E: typed App Server request
    E->>R: owner port call
    R->>R: validate ownership + persist
    R-->>E: applied / failed / outcome_unknown
    E-->>B: mapped typed result
  else Shared compatibility
    B->>S: Runtime IPC v17 request
    S->>R: owner port call
    R->>R: validate ownership + persist
    R-->>S: applied / failed / outcome_unknown
    S-->>B: mapped typed result
  end
  B-->>T: typed result
  T-->>U: update name only after applied
Loading

Embedded 和 Shared 最终调用同一 AgentRuntime::rename_session。Runtime 只有在确认旧名称已保留时才返回明确失败;持久化恢复无法确认时,两种部署都返回 outcome_unknown。Shared 还会在请求已发送但权威响应丢失时返回该结果并关闭连接。用户恢复 Session、检查当前名称后再决定是否重试。

5.4 Scenario (+1) · Delete an idle Session

sequenceDiagram
  participant U as User
  participant T as TUI adapter
  participant B as TuiBackend
  participant E as Embedded App Server adapter
  participant S as Shared Runtime IPC v17 adapter
  participant R as Agent Runtime

  U->>T: /sessions then Ctrl+D
  T->>T: reject current or active target
  T->>B: typed TuiBackend request
  alt Embedded
    B->>E: typed App Server request
    E->>R: owner port call
    R->>R: existing delete owner
    R-->>E: applied / failed / outcome_unknown
    E-->>B: mapped typed result
  else Shared compatibility
    B->>S: Runtime IPC v17 request
    S->>R: owner port call
    R->>R: existing delete owner
    R-->>S: applied / failed / outcome_unknown
    S-->>B: mapped typed result
  end
  B-->>T: typed result
  T-->>U: remove only after applied
Loading

Embedded 和 Shared 最终调用同一个 Agent Runtime。Shared Runtime Host 通过 v17 handler 调用 Runtime;它不是 Shared App Server。Shared Runtime Host 只在请求方没有活动 Turn、目标 Session 未被任何 Client 控制时调用 Runtime owner;session_in_use 和 not_found 保持结构化错误。TUI 复用现有单个 Session 异步任务槽位,不阻塞事件循环,也不自动重试结果不确定的删除。

6. 隔离和生命周期原则

实例身份与 ownership key 分工不同:

事实 用途
canonical workspace + product 防止 Embedded 与 Shared 同时拥有同一工作区 Runtime
workspace + product + release channel + user + protocol 定位兼容的本机 Shared instance
stable local endpoint + bearer token + owner id endpoint 定位同一 instance;随机 token 认证本轮 server;owner id 防止旧实例误删新 discovery
实际 Session 存储位置 + Session ID 限制持久化 Session 的跨进程并发写入;不由 IPC 协议定义

当前 Shared TUI 只有 controller,没有 observer 或 detached Query:一个 Client 关闭不会删除 Session;它会取消仍拥有的活动 Turn,只有取消得到确认才释放 Session 控制权,否则继续隔离该 Session,直到 Runtime 退出。最后一个 Client 关闭后,Runtime 进入 30 秒空闲期;期间重连可继续使用,超时后 Runtime 正常关闭。若未来增加后台任务、observer 或 Remote 引用,必须先扩展 Runtime-aware drain,不能把这些引用塞进当前简单连接计数。

对普通单实例用户,未显式启用 Shared deployment 时不增加后台进程、连接、发现扫描或常驻内存。

7. 能力扩展原则

未来每增加一类 Shared 能力,都必须同时满足:

  1. 已有明确第一方 consumer 和用户旅程;
  2. 行为由现有 Runtime owner 提供,IPC 只映射 typed request/result/event;
  3. 定义权限、取消、deadline、断线、背压和副作用结果不确定性;
  4. Embedded 与 Shared 使用同一行为 fixture;
  5. 新能力不被顺带发布为 Agent SDK、Remote 或浏览器 API。

Session/Turn、事件恢复、Permission/UserInput、Controller、配置管理和 Remote 应分别通过上述门槛,不能一次性加入一个“全量 Shared API”。

当前 IPC crate 只是一条可删除的预集成边界:

约束 当前决定
当前 consumer 仅第一方交互式 TUI adapter;不自动包含 GUI、Headless CLI、Remote 或 SDK Host
稳定测试合同 本机 endpoint、initialize-first、128 KiB request / 8 MiB response-event 上限、连接上限、owner-checked cleanup、原子 Session controller 切换、单连接单活动 Turn、事件流失效后 fail closed、断连取消、30 秒空闲退出
当前业务范围 Session list/create/restore/delete、Turn/transcript、当前 Session name/Agent mode/model、Permission/UserInput 的 TUI 必需子集;任何新增操作都需要真实 consumer 和 owner 等价测试
协议地位 crate 保持 publish = false;这是 workspace 内私有协议,不是 Agent SDK 或远程兼容承诺

架构守卫只允许 CLI 消费该 crate;IPC 可以复用稳定的 Event、Product Domain 与 Runtime Port DTO,但禁止依赖 Runtime 实现、SDK Host、services、Tauri 或远程网络 transport。

8. 与竞品的取舍

产品 已验证做法 BitFun 采用 不照搬
OpenCode Server/SDK Server-first;类型化 SDK 直接消费 Server API 一个 Runtime owner 可以服务多个第一方 Client 不要求 Rich Client 使用 HTTP/OpenAPI,也不把全量 route 固化为私有 Shared wire
Codex App Server App Server 为 rich client 和 remote TUI 提供 JSON-RPC;自动化继续使用 SDK;WebSocket transport 仍是实验性接口 Rich Client 使用 App Server,自动化/公开 SDK 保持独立,并为 Shared 入口保留有界本机 transport 不复制其完整 schema,也不把实验性远程 transport 当作已交付公网 API
Claude Agent SDK Agent loop 由长期运行的 CLI 子进程承载,并提供 startup() 预热以减少首次请求成本 长期 Shared 交互可以复用已启动进程,空闲后回收 Embedded Rich Client 不增加子进程,多 TUI 也不映射为多个 Runtime

三种产品说明了不同部署的有效边界:稳定 Rich Client 合同可以同时承载进程内和多客户端 transport,长期子进程适合 Shared 交互或语言 SDK,独立强类型 adapter 适合 Headless/ACP 等非 Rich Client。BitFun 采用混合部署,不把 App Server 强制成所有入口的 公共底座;当前也没有为了追赶功能表一次性增加 Session/Tool/Permission 超集。

9. 不变量

  • 只有一套 Agent Runtime 业务实现;部署差异不能产生第二套 Session、Tool、Permission 或 MCP owner。
  • 当前入口使用第 1.1 节列出的 adapter;若第 1.2 节目标通过评审并迁移完成,Desktop GUI、Web UI 和交互式 TUI 才统一使用 App Server。
  • Client、窗口、Session 或 workspace 数量不会自动等量增加 Runtime 或 Plugin Host 进程。
  • 当前 Shared Runtime IPC 是第一方 TUI 的 private compatibility transport,不成为公开 SDK、Remote、Peer、HTTP 或浏览器协议;是否由 App Server Shared transport 替换仍待评审。
  • 默认 GUI/TUI/Headless CLI、ACP 与 SDK Host 保持 Embedded;只有交互式 TUI 的显式 --shared 选择 Shared。互斥按 workspace + product 生效,不再按入口名称缩窄。
  • Account/session cloud sync 仍使用既有 Core compatibility 边界,不属于 Shared Runtime 支持。
  • Remote workspace 的文件、凭据、进程和 Runtime 位于目标执行域,禁止静默回落本机。
  • 未经真实 consumer 验证的接口不进入 wire;当前 wire 只包含表中列出的 Shared TUI 操作。