Skip to content

Commit 17ccc81

Browse files
committed
feat(cli): add opt-in shared TUI runtime
1 parent a5d3a1e commit 17ccc81

43 files changed

Lines changed: 4993 additions & 589 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/architecture/agent-runtime-deployment-design.md

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ flowchart LR
3333
| Embedded Desktop GUI | 继续使用现有 Desktop 事件投影和 Tauri adapter;本设计没有改变其依赖或生命周期 |
3434
| Embedded TUI/Headless CLI/Peer Host | Session、Turn、Permission 和事件订阅统一通过同一个 Rust Runtime SDK(当前 preview);CLI crate 只保留第一方 adapter 和各形态自己的展示/断流策略 |
3535
| ACP/SDK Host | 使用同一个 Runtime 事件入口的 session-scoped 订阅;各自协议和进程生命周期保持独立 |
36-
| Runtime ownership | 已有可选的 Embedded 共享锁 / Shared 独占锁原语;尚未接入产品入口 |
37-
| Shared local IPC | 已有未发布、仅 crate 内可见的 discovery、实例锁、严格握手、Health 和 cleanup 基础;尚无生产 consumer |
38-
| Shared Session/Turn/Tool/Permission | 尚未设计为稳定 wire,也没有产品 consumer |
39-
| Shared GUI/TUI/Remote | 尚未交付,没有 `--shared` 或隐藏 Host 命令 |
36+
| Runtime ownership | CLI 的 Embedded deployment 取得共享锁;Shared TUI deployment 取得独占锁,二者在同一 workspace 互斥;其他产品入口尚未接入该锁 |
37+
| Shared local IPC | 未发布的本机协议已有 discovery、实例锁、严格握手、Session 控制租约、有界事件流和 cleanup;唯一 consumer 是第一方交互式 TUI adapter |
38+
| Shared TUI | `bitfun --shared` / `bitfun chat --shared` 可列出、创建、恢复 Session,读取 transcript,提交/取消 Turn,处理 Permission 和 UserInput;默认仍是 Embedded |
39+
| Shared GUI/Headless/ACP/SDK Host/Remote | 未交付,也不会由 `--shared` 隐式启用;Replay、Observer、Controller transfer、Session delete/fork 同样不在当前协议中 |
4040

41-
因此当前完成的是 Embedded 入口的调用边界收敛,不是用户可用的 Shared Runtime 产品。具体 `EventQueue` 仍由 Core 产品装配,Runtime SDK 只提供同进程订阅入口;没有 Shared event wire、事件重放或 Shared consumer
41+
因此当前交付的是一条窄的、显式启用的 Shared TUI deployment,不是通用本机 Server。具体 `EventQueue` 仍由 Core 产品装配;IPC 只把当前 TUI 必需的强类型操作和事件映射到同一个 Runtime owner,没有事件重放或公开协议承诺
4242

4343
## 2. 最少名词
4444

@@ -64,7 +64,7 @@ flowchart TB
6464
end
6565
6666
Embedded["Embedded adapter"] --> API
67-
Shared["Shared local IPC adapter · future"] -.-> API
67+
Shared["Shared local IPC adapter · opt-in TUI"] --> API
6868
SDK["SDK Host adapter"] --> API
6969
Remote["Remote adapter"] --> API
7070
```
@@ -105,42 +105,58 @@ flowchart LR
105105
```
106106

107107
- 多个 Embedded 进程可继续并存。
108-
- Shared 与任何 Embedded owner 互斥,避免同一工作区出现两个 Runtime owner
109-
- 当前没有入口调用该原语,所以现有产品行为不变
108+
- 在当前 CLI 边界内,Shared TUI 与 Embedded CLI Runtime 互斥;多个 Embedded CLI 进程仍可并存
109+
- CLI 每次初始化 Runtime 时都调用该原语;Desktop、SDK Host、Server 等入口尚未接入,也不会被误报为已共享或已互斥
110110
- 该锁不选择 workspace、不启动 Runtime、不缓存实例,也不替代 Session 写入权或文件冲突控制。
111111

112112
### 4.2 私有本机 IPC
113113

114114
```mermaid
115115
sequenceDiagram
116-
participant C as Foundation client
116+
participant C as Shared TUI client
117117
participant D as User-private discovery
118-
participant S as Foundation server
118+
participant S as Shared Runtime process
119119
120120
C->>D: read endpoint + token + identity + protocol
121121
C->>S: connect via Named Pipe / UDS
122122
C->>S: initialize(identity, protocol, token)
123123
alt valid
124-
S-->>C: initialized(capabilities = health)
125-
C->>S: health
126-
S-->>C: instance identity + PID
124+
S-->>C: initialized(health + interactive_tui)
125+
C->>S: create or restore Session
126+
S-->>C: controller lease + Session facts
127+
C->>S: submit/cancel Turn or answer Permission/UserInput
128+
S-->>C: Session-filtered authoritative events
127129
else invalid
128130
S-->>C: typed error and close
129131
end
130132
```
131133

132-
当前协议刻意只有 Health。它验证以下地基,而不提前冻结业务 wire:
134+
当前协议只覆盖第一个 TUI 纵向切片:
135+
136+
| 已支持 | 明确不支持 |
137+
|---|---|
138+
| Health、Session list/create、原子 restore(含 transcript 与 pending Permission) | Session delete/fork、跨 workspace attach、transcript 分页 |
139+
| Turn submit/cancel | replay、cursor、resume event stream |
140+
| pending/respond Permission、submit UserInput answers | observer、controller transfer、多 Session multiplex |
141+
| 连接断开清理、Session-filtered events | detach/observer/controller transfer、SDK callbacks、GUI/Remote/Peer/ACP/Headless wire |
142+
143+
这些操作先满足以下本机 IPC 地基,而不把协议升级为公开 SDK:
133144

134145
- workspace、产品、release channel、用户和协议版本共同生成实例身份;
135146
- instance lock 而不是 PID/discovery 文件决定唯一 server owner;
136147
- Windows 使用拒绝远程连接的 Named Pipe;Unix 使用短且由 instance identity 决定的稳定 Domain Socket 名称,权限为 `0600`
137148
- discovery 所在目录必须由未来 composition 选择为当前用户私有目录;
138149
- discovery 通过同目录临时文件原子替换;Unix endpoint 保留原生路径字节,路径过长时在 bind 前返回明确错误;
139150
- 第一帧必须完成 token、instance identity 和 protocol version 校验;
140-
- JSON frame 使用 4-byte 长度前缀,并在分配前执行 64 KiB 硬上限;
151+
- 未认证握手预算为 2 秒;认证后的单次操作、响应写入和断线取消预算为 120 秒,避免坏客户端长期占用连接或 Runtime handler;
152+
- JSON frame 使用 4-byte 长度前缀;request 在发送前执行 128 KiB 上限(覆盖 TUI 已有的 64 KiB 粘贴输入及类型化信封),response/event 在序列化时执行 8 MiB 上限。超限返回类型化错误,不能进行无界分配;超过该上限的历史 Session 暂由 Embedded TUI 打开,不在本阶段引入分页协议;
141153
- 未认证连接也计入有界 connection budget,单个客户端不能无限制造 server task;
142154
- 未知字段、未知 operation、错误身份和不兼容版本 fail closed;
143-
- 无连接后按调用方配置的 idle timeout 退出,并只删除自己发布的 discovery;Unix 下继任 owner 会在持有实例锁后清理同一 identity 的陈旧 socket。
155+
- 一个连接最多控制一个 Session、同时最多提交一个活动 Turn;一个 Session 同时只有一个 controller。create/restore 在完整结果通过大小检查后才原子切换控制权,失败时保留原 Session。活动 Turn 期间不能切换 Session。
156+
- Submit 使用调用方已有的 `turn_id` 标识不确定结果;若提交超时,返回 `outcome_unknown`、关闭连接并按该 ID 取消。断连取消只有得到确认后才释放 Session 租约;无法确认时租约保持隔离,直到 Runtime 进程退出。
157+
- Agent 事件流 lag/closed 后 fail closed;Permission lag 先从 Runtime 权威 pending 集合重建,重建失败或流关闭时取消当前 Turn 并退出。路由到父 Session 的嵌套 Permission 与 AskUserQuestion 复用现有 TUI 交互,不新增第二套 UI 状态。
158+
- Windows Shared Runtime 在初始化前把自身放入 kill-on-close Job;Unix 仅在应用内优雅退出路径中通过受管子进程组回收后代。Runtime 被 `SIGTERM``SIGKILL` 或崩溃直接终止后的 Unix 后代回收不在当前保证内。两者都只负责生命周期,不是安全沙箱。
159+
- 最后一个连接离开后等待 30 秒再退出;新连接会取消 idle 退出。退出只删除自己发布的 discovery;Unix 下继任 owner 会在持有实例锁后清理同一 identity 的陈旧 socket。
144160

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

@@ -180,7 +196,7 @@ flowchart LR
180196
| stable local endpoint + bearer token + owner id | endpoint 定位同一 instance;随机 token 认证本轮 server;owner id 防止旧实例误删新 discovery |
181197
| Session identity | 未来 Runtime 内的持久化和写入隔离;不由 IPC foundation 定义 |
182198

183-
一个 Client 关闭不应推导 Session 或 Runtime 必须退出;真正的 Shared lifecycle 需要综合 Client、活动 Query、后台任务和 Remote 引用。当前 Health-only server 没有这些业务引用,因此只实现“无连接后 idle 退出”。后续接入 Runtime 时必须替换为 Runtime-aware drain,不能直接复用 Health server 的简单空闲条件
199+
当前 Shared TUI 只有 controller,没有 observer 或 detached Query:一个 Client 关闭不会删除 Session;它会取消仍拥有的活动 Turn,只有取消得到确认才释放 Session 控制租约,否则该租约隔离到 Runtime 退出。最后一个 Client 关闭后,Runtime 进入 30 秒空闲期;期间重连可继续使用,超时后 Runtime 正常关闭。若未来增加后台任务、observer 或 Remote 引用,必须先扩展 Runtime-aware drain,不能把这些引用塞进当前简单连接计数
184200

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

@@ -200,12 +216,12 @@ Session/Turn、事件恢复、Permission/UserInput、Controller、配置管理
200216

201217
| 约束 | 当前决定 |
202218
|---|---|
203-
| 首个候选 consumer | 仅限另行评审的第一方交互式 TUI attach adapter;不自动包含 GUI、Headless CLI、Remote 或 SDK Host |
204-
| 稳定测试合同 | 本机 endpoint、initialize-first、64 KiB frame、Health、连接上限、owner-checked cleanup |
205-
| 接入门槛 | 必须复用既有 Runtime owners,并用同一 fixture 证明 Embedded/Shared 行为等价 |
206-
| 删除条件 | 若首个 consumer 选择其他 transport,或 Shared 在产品接入前取消,则直接删除该 crate,不保留“未来可能使用”的 API |
219+
| 当前 consumer | 仅第一方交互式 TUI adapter;不自动包含 GUI、Headless CLI、Remote 或 SDK Host |
220+
| 稳定测试合同 | 本机 endpoint、initialize-first、128 KiB request / 8 MiB response-event 上限、连接上限、owner-checked cleanup、原子 Session controller 切换、单连接单活动 Turn、事件流失效后 fail closed、断连取消、30 秒空闲退出 |
221+
| 当前业务范围 | Session/Turn/transcript/Permission/UserInput 的 TUI 必需子集;任何新增操作都需要真实 consumer 和 owner 等价测试 |
222+
| 协议地位 | crate 保持 `publish = false`;这是 workspace 内私有协议,不是 Agent SDK 或远程兼容承诺 |
207223

208-
在首个 consumer 通过评审前,crate 保持 `publish = false`,所有 Rust API 保持 crate 内可见;架构守卫禁止增加 Runtime、SDK Host、services、CLI/TUI、远程网络依赖及 Health 之外的 operation
224+
架构守卫只允许 CLI 消费该 crate;IPC 可以复用稳定的 Event、Product Domain 与 Runtime Port DTO,但禁止依赖 Runtime 实现、SDK Host、services、Tauri 或远程网络 transport
209225

210226
## 8. 与竞品的取舍
211227

@@ -222,7 +238,7 @@ Session/Turn、事件恢复、Permission/UserInput、Controller、配置管理
222238
- 只有一套 Agent Runtime 业务实现;部署差异不能产生第二套 Session、Tool、Permission 或 MCP owner。
223239
- Client、窗口、Session 或 workspace 数量不会自动等量增加 Runtime 或 Plugin Host 进程。
224240
- 私有 IPC 不成为公开 SDK、Remote、Peer、HTTP 或浏览器协议。
225-
- 默认 GUI/TUI/Headless CLI 在 Shared 产品能力正式交付前保持现有 Embedded 行为
241+
- 默认 GUI/TUI/Headless CLI 保持 Embedded;只有交互式 TUI 的显式 `--shared` 选择 Shared,当前互斥范围也只覆盖 CLI deployment
226242
- Account/session cloud sync 仍使用既有 Core compatibility 边界,不属于 Shared Runtime 支持。
227243
- Remote workspace 的文件、凭据、进程和 Runtime 位于目标执行域,禁止静默回落本机。
228-
- 未经真实 consumer 验证的接口不进入 wire;当前唯一 operation 是 Health
244+
- 未经真实 consumer 验证的接口不进入 wire;当前 wire 只包含表中列出的 Shared TUI 操作

docs/architecture/cli-product-line-design.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,17 @@ Headless CLI 和公开 Agent SDK 都调用同一 Agent Runtime API,但交付
252252
- 两者的能力对照、共同 fixture 和等价门槛以
253253
[Agent SDK 产品与宿主架构第 9 节](agent-sdk-product-architecture.md#9-headless-cli-与-agent-sdk)为唯一事实源。
254254

255+
交互式 TUI 另有一个显式部署选项:`bitfun --shared``bitfun chat --shared`。它通过 CLI 私有本机 IPC adapter 连接同一 Agent Runtime,不经过 SDK Host,也不改变 Headless CLI 或公开 SDK 的协议。当前范围如下:
256+
257+
| 形态 | 默认部署 | 当前 Shared 范围 |
258+
|---|---|---|
259+
| 交互式 TUI | Embedded | 显式 `--shared` 后支持 Session list/create/restore、transcript、Turn submit/cancel、Permission 和 UserInput |
260+
| `bitfun exec` / CI | Embedded | 不接受 Shared;保持独立进程、stdout/stderr 和退出码语义 |
261+
| ACP / SDK Host / GUI / Remote / Peer | 各自既有部署 | 不消费 TUI IPC,也不因本开关改变生命周期 |
262+
263+
Shared TUI 首版不提供 Session delete/fork、模式/模型、MCP/扩展、账号同步、用量、observer、replay 或 controller transfer;对应入口给出明确的 Embedded 恢复建议,不在 Client 进程初始化第二套 Core owner。
264+
Shared 模式的命令面板、快捷键帮助和底部提示使用同一能力投影:不支持的管理动作不显示为可执行入口。Session 切换失败保留原控制权,单个连接已有活动 Turn 时拒绝重复提交;事件订阅失效后当前视图立即失效并要求重启 Shared TUI。
265+
255266
#### 管理与诊断
256267

257268
CLI-P1 应统一以下命令的文本和结构化只读视图:
@@ -301,12 +312,17 @@ TUI renderer、实验性接口和完整外部 Server 协议按总矩阵明确降
301312
flowchart LR
302313
Exec["bitfun exec"] --> Choice{"Session"}
303314
Choice -->|"new / free"| Embedded["Embedded"]
304-
Choice -->|"already owned"| Attach["Attach Host or reject"]
315+
Choice -->|"already owned"| Reject["typed occupied error"]
316+
317+
TUI["bitfun chat"] --> Deploy{"deployment"}
318+
Deploy -->|"default"| EmbeddedTui["Embedded"]
319+
Deploy -->|"--shared"| SharedTui["private local IPC"]
320+
SharedTui --> Runtime["one Shared Runtime owner"]
305321
```
306322

307323
Embedded 只意味着 Runtime 与 CLI 同进程,不意味着绕过持久化单写规则。新 Session 取得自己的写入权;恢复既有 Session 时,
308-
CLI 必须先取得该 Session 的写入权。如果 Shared Agent Runtime 或另一个 `exec` 已持有,CLI 连接现有 Runtime 或返回明确的
309-
“Session 已占用”,不能并发写入同一 Session。
324+
CLI 必须先取得该 Session 的写入权。如果 Shared Agent Runtime 或另一个 `exec` 已持有,Headless CLI 返回明确的
325+
“Session 已占用”;它不会自动切换部署。只有用户显式选择 `--shared` 的交互式 TUI 才连接 Shared Runtime,且同一 Session 同时只有一个 controller
310326

311327
CLI/TUI 的会话创建、列出、删除、恢复和历史转录读取通过 Rust Runtime SDK 的类型化端口完成;TUI 只把
312328
`SessionTranscript` 转换为本地渲染状态,不再消费 Core `Message`。Peer Host 的对话提交、精确取消、基础会话控制、thread-goal 查询、会话模型更新和

scripts/core-boundaries/rules/crate-rules.mjs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,20 @@ const agentRuntimeIpcForbiddenDeps = [
1010
'bitfun-codex-adapter',
1111
'bitfun-core',
1212
'bitfun-core-types',
13-
'bitfun-events',
1413
'bitfun-external-sources',
1514
'bitfun-harness',
1615
'bitfun-opencode-adapter',
1716
'bitfun-page-function-runtime',
1817
'bitfun-plugin-runtime-client',
1918
'bitfun-product-capabilities',
2019
'bitfun-relay-service',
21-
'bitfun-runtime-ports',
2220
'bitfun-runtime-services',
2321
'bitfun-sdk-host',
2422
'bitfun-services-core',
2523
'bitfun-services-integrations',
2624
'bitfun-static-hook-support',
2725
'bitfun-tool-call-jsonrepair',
2826
'bitfun-tool-packs',
29-
'bitfun-product-domains',
3027
'bitfun-transport',
3128
'bitfun-webdriver',
3229
'terminal-core',
@@ -72,6 +69,15 @@ export const noCoreDependencyCrates = [
7269
];
7370

7471
export const forbiddenManifestDependencyRules = [
72+
{
73+
dependencyNames: ['bitfun-agent-runtime-ipc'],
74+
scanRoots: ['src/apps', 'src/crates', 'BitFun-Installer/src-tauri'],
75+
workspaceManifestPath: 'Cargo.toml',
76+
allowManifestPaths: ['src/apps/cli/Cargo.toml'],
77+
reason: 'the private local IPC protocol has one reviewed first-party Shared TUI consumer',
78+
message:
79+
'agent-runtime-ipc may only be consumed by the CLI Shared TUI adapter; SDK Host, GUI, remote, and other products require separate review',
80+
},
7581
{
7682
dependencyNames: ['sherpa-onnx'],
7783
scanRoots: ['src/apps', 'src/crates', 'BitFun-Installer/src-tauri'],
@@ -130,7 +136,7 @@ export const lightweightBoundaryRules = [
130136
{
131137
crateName: 'agent-runtime-ipc',
132138
reason:
133-
'agent-runtime-ipc is a non-published Health-only local transport seam, not a Runtime, SDK Host, service, or product surface',
139+
'agent-runtime-ipc is the non-published local protocol for the reviewed Shared TUI adapter, not a Runtime, SDK Host, service, or remote product surface',
134140
forbiddenDeps: agentRuntimeIpcForbiddenDeps,
135141
},
136142
{
@@ -389,9 +395,9 @@ export const lightweightBoundaryRules = [
389395
export const dependencyProfileRules = [
390396
{
391397
crateName: 'agent-runtime-ipc',
392-
profileName: 'private Health-only local IPC profile',
398+
profileName: 'private Shared TUI local IPC profile',
393399
reason:
394-
'agent-runtime-ipc must not acquire Runtime, SDK Host, service, remote transport, or product dependencies before its first reviewed consumer',
400+
'agent-runtime-ipc may share stable event and Runtime DTO contracts but must not acquire Runtime owners, SDK Host, services, remote transports, or product implementations',
395401
forbiddenNonOptionalDeps: agentRuntimeIpcForbiddenDeps,
396402
},
397403
{

scripts/core-boundaries/rules/source/forbidden-rules.mjs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
// Boundary rules for source ownership, facades, and required owner content.
22

33
export const forbiddenContentRules = [
4-
{
5-
path: 'src/crates/adapters/agent-runtime-ipc/src/lib.rs',
6-
reason:
7-
'agent-runtime-ipc must remain crate-internal until its first reviewed production consumer',
8-
patterns: [
9-
{
10-
regex: /^\s*pub\s+(?!\(crate\))/,
11-
message:
12-
'agent-runtime-ipc must not expose any externally public Rust item before consumer review',
13-
},
14-
],
15-
},
164
{
175
path: 'src/crates/adapters/agent-runtime-ipc/src/operation.rs',
18-
reason: 'agent-runtime-ipc operation scope is frozen at Health',
6+
reason: 'agent-runtime-ipc operation scope is frozen to the first Shared TUI slice',
197
patterns: [
208
{
21-
regex: /^\s+(?!Health\b)[A-Z][A-Za-z0-9_]*\b/,
9+
regex: /^\s+(?!(?:Health|ListSessions|CreateSession|RestoreSession|ReadTranscript|SubmitTurn|CancelTurn|PendingPermissions|RespondPermission|SubmitUserAnswers|DetachSession|Unit|Sessions|SessionCreated|SessionRestored|Transcript|TurnAccepted|TurnCancelled|Self|AgentDialogTurnRequest|AgentSessionCreateRequest|AgentSessionCreateResult|AgentSessionListRequest|AgentSessionSummary|AgentTurnCancellationRequest|AgentTurnCancellationResult|SessionTranscript|SessionTranscriptRequest)\b)[A-Z][A-Za-z0-9_]*\b/,
2210
message:
23-
'agent-runtime-ipc may not add operations or results beyond Health in this foundation',
11+
'agent-runtime-ipc may not add replay, observer, controller-transfer, deletion, fork, or other operations beyond the reviewed Shared TUI slice',
2412
},
2513
],
2614
},

0 commit comments

Comments
 (0)