Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 62 additions & 33 deletions docs/architecture/agent-runtime-deployment-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,35 @@ Agent Runtime 的模块职责见 [`agent-runtime-services-design.md`](agent-runt
BitFun 只有一套 Agent Runtime 行为。`Embedded` 和 `Shared` 只描述同一套 Runtime 的物理部署方式,不是两套实现。

```mermaid
flowchart LR
flowchart TB
subgraph "产品入口"
GUI["GUI / TUI"]
CLI["Headless CLI"]
SDK["Agent SDK"]
GUI["Desktop GUI"]
TUI["TUI / Headless CLI"]
ACP["ACP"]
SDK["Agent SDK · SDK Host"]
Server["Server agent bootstrap"]
end

GUI --> Adapter["first-party adapter"]
CLI --> Adapter
SDK --> SDKAdapter["SDK Host adapter"]
GUI --> Adapter["同级 first-party adapters"]
TUI --> Adapter
ACP --> Adapter
SDK --> Adapter
Server --> Adapter
Adapter --> API["Agent Runtime API"]
SDKAdapter --> API
API --> Owners["Session / Tool / Permission / MCP owners"]
API --> Coordinator["ConversationCoordinator"]
Coordinator --> Owners["Session / Tool / Permission / MCP owners"]
Coordinator -. "local attach / mutation" .-> Ownership["CoreRuntimeOwnership"]
```

当前代码状态必须和目标设计分开阅读:

| 范围 | 当前状态 |
|---|---|
| Embedded Desktop GUI | 继续使用现有 Desktop 事件投影和 Tauri adapter;本设计没有改变其依赖或生命周期 |
| Embedded Desktop GUI | 继续使用 Desktop 事件投影和 Tauri adapter;按实际打开的本机 workspace 延迟取得并持有 Embedded ownership,不增加后台进程 |
| Embedded TUI/Headless CLI/Peer Host | Session、Turn、Permission 和事件订阅统一通过同一个 Rust Runtime SDK(当前 preview);CLI crate 只保留第一方 adapter 和各形态自己的展示/断流策略 |
| ACP/SDK Host | 使用同一个 Runtime 事件入口的 session-scoped 订阅;各自协议和进程生命周期保持独立 |
| Runtime ownership | CLI 的 Embedded deployment 取得共享锁;Shared TUI deployment 取得独占锁,二者在同一 workspace 互斥;其他产品入口尚未接入该锁 |
| Runtime ownership | Desktop、CLI、ACP、SDK Host 和现有 Server agent bootstrap 共用 Core owner;Embedded 取得共享锁,Shared TUI 取得独占锁,同一 workspace 上两种 deployment 互斥 |
| 当前 HTTP Server | 只提供 health/info/WebSocket 外壳,未装配 Agent Runtime,因此不取得 workspace ownership;`bootstrap.rs` 仅保持 agent-enabled composition 的一致边界,不由当前入口启动 |
| Shared local IPC | 未发布的本机协议已有 discovery、实例锁、严格握手、Session 控制租约、有界事件流和 cleanup;唯一 consumer 是第一方交互式 TUI adapter |
| Shared TUI | `bitfun --shared` / `bitfun chat --shared` 可列出、创建、恢复 Session,读取 transcript,提交/取消 Turn,处理 Permission 和 UserInput;默认仍是 Embedded |
| Shared GUI/Headless/ACP/SDK Host/Remote | 未交付,也不会由 `--shared` 隐式启用;Replay、Observer、Controller transfer、Session delete/fork 同样不在当前协议中 |
Expand Down Expand Up @@ -95,19 +101,37 @@ flowchart LR

### 4.1 Runtime ownership

`services-core::runtime_ownership` 提供进程级 RAII 文件锁
ownership 分成“产品决策”和“文件锁原语”两层;入口不再各自拼 key、目录或锁模式

```mermaid
flowchart LR
E1["Embedded A"] -->|"shared lock"| Key["workspace + product ownership key"]
E2["Embedded B"] -->|"shared lock"| Key
S["Shared deployment"] -->|"exclusive lock"| Key
flowchart TB
Entrypoints["Desktop · CLI · ACP · SDK Host · Server bootstrap"]
Entrypoints --> Core["CoreRuntimeOwnership<br/>deployment · product identity · process leases"]
Core --> Primitive["services-core::runtime_ownership<br/>canonical key · RAII file lock"]
Primitive --> E["Embedded · shared lock"]
Primitive --> S["Shared · exclusive lock"]
```

```mermaid
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 --> Lease["按 canonical workspace 保留进程期 lease"]
```

- 多个 Embedded 进程可继续并存。
- 在当前 CLI 边界内,Shared TUI 与 Embedded CLI Runtime 互斥;多个 Embedded CLI 进程仍可并存。
- CLI 每次初始化 Runtime 时都调用该原语;Desktop、SDK Host、Server 等入口尚未接入,也不会被误报为已共享或已互斥。
- 该锁不选择 workspace、不启动 Runtime、不缓存实例,也不替代 Session 写入权或文件冲突控制。
| 场景 | 行为 | 原因 |
|---|---|---|
| 多个 Embedded 进程访问同一 workspace | 共享锁允许并存 | 保持单实例、CI 和隔离测试的既有成本模型 |
| Shared 与任一 Embedded 访问同一 workspace | 后启动者返回稳定错误码和启动建议 | 防止同一 workspace 同时存在两种 Runtime deployment |
| Desktop 打开多个 workspace | 首次 attach/write 时逐个取得并保留 lease | 不把窗口数、Session 数等同于 Runtime 进程数 |
| 只读 list/view | 不加锁 | ownership 只管理 Runtime deployment,不扩大成读取权限 |
| 已解析且带有效 `connection_id` 的 remote workspace | 本机不加锁 | 与 Session storage 的远端判据一致;`host` 提示本身不能绕过本地锁 |
| 当前只读 HTTP Server | 不创建 Core owner | 没有 Agent Runtime 就没有 ownership 可声明 |

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

### 4.2 私有本机 IPC

Expand Down Expand Up @@ -163,18 +187,23 @@ sequenceDiagram
## 5. 产品入口保持同级

```mermaid
flowchart LR
GUI["GUI"] --> GA["GUI adapter"]
TUI["TUI"] --> TA["TUI adapter"]
CLI["Headless CLI"] --> CA["CLI adapter"]
SDK["Agent SDK"] --> SA["SDK Host adapter"]
ACP["ACP"] --> AA["ACP adapter"]

GA --> API["Agent Runtime API"]
TA --> API
CA --> API
SA --> API
AA --> API
flowchart TB
GUI["GUI adapter"] --> API["Agent Runtime API"]
TUI["TUI adapter"] --> 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"]
TUI -. "Embedded / opt-in Shared" .-> Ownership
CLI -. "Embedded" .-> Ownership
SDK -. "Embedded" .-> Ownership
ACP -. "Embedded" .-> Ownership
Server -. "only when Runtime is assembled" .-> Ownership
Ownership -. "injected once" .-> Coordinator
```

- CLI 不依赖 SDK Host,GUI/TUI 也不依赖公开 SDK package。
Expand Down Expand Up @@ -238,7 +267,7 @@ Session/Turn、事件恢复、Permission/UserInput、Controller、配置管理
- 只有一套 Agent Runtime 业务实现;部署差异不能产生第二套 Session、Tool、Permission 或 MCP owner。
- Client、窗口、Session 或 workspace 数量不会自动等量增加 Runtime 或 Plugin Host 进程。
- 私有 IPC 不成为公开 SDK、Remote、Peer、HTTP 或浏览器协议。
- 默认 GUI/TUI/Headless CLI 保持 Embedded;只有交互式 TUI 的显式 `--shared` 选择 Shared,当前互斥范围也只覆盖 CLI deployment
- 默认 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 操作。
38 changes: 26 additions & 12 deletions docs/architecture/agent-sdk-product-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,27 +171,29 @@ Python SDK、TypeScript SDK、managed Host 和连接预启动 Host 不是四种
### 4.1 产品入口

```mermaid
flowchart LR
flowchart TB
GUI["GUI / TUI"] --> UIA["UI adapter"]
CLI["bitfun exec"] --> CLIA["CLI adapter"]
SDK["Agent SDK"] --> SDKA["SDK Host"]
ACP["ACP"] --> ACPA["ACP adapter"]
Server["Server / Remote"] --> RemoteA["Server / Remote adapter"]
UIA --> API["Runtime API"]
CLIA --> API
SDKA --> API
API --> Runtime["Agent Runtime owners"]
ACPA --> API["Runtime API"]
RemoteA --> API
API --> Coordinator["ConversationCoordinator"]
Coordinator --> Runtime["Agent Runtime owners"]

Composition["first-party composition roots"] -. "inject once" .-> Ownership["CoreRuntimeOwnership"]
Ownership -. "local attach / mutation gate" .-> Coordinator
```

### 4.2 互操作入口

```mermaid
flowchart LR
ACP["ACP"] --> ACPA["ACP adapter"]
Remote["Server / Remote"] --> RemoteA["Remote adapter"]
ACPA --> API["Runtime API"]
RemoteA --> API
```
上图中的 ACP、Server/Remote 和 SDK Host 都是同级 adapter;虚线只表示第一方进程装配 ownership,不表示某个入口依赖另一个入口。

以上两图固定四条架构结论
上图固定四条架构结论

- GUI/TUI/CLI 同样使用 Query、MCP、Permission 和 Hook,但它们直接经过各自 adapter 调用共享 Runtime API,
不依赖 Python/TypeScript SDK,也不依赖 SDK Host。
Expand All @@ -203,8 +205,9 @@ flowchart LR
一次性 Headless CLI 继续 Embedded;公开 SDK 默认连接私有 SDK Host。Shared Agent Runtime process 和 SDK Host 都是 Rust 产品进程,
与运行第三方 JS/TS 的 Node/Bun Plugin Host 不同;三者不能共享名称或业务归属。

当前代码只具备 Shared deployment 的本机 IPC、身份、握手、Health 和 ownership 基础;没有 GUI/TUI/Remote consumer,
也没有 Shared Session/Turn 协议。图中 Shared deployment 是目标架构,不是已交付产品能力。
当前代码已经交付显式启用的 Shared TUI 最小切片,包含本机 IPC、身份、握手、Session/Turn、Permission/UserInput、
ownership 和生命周期治理;GUI、Headless CLI、ACP、SDK Host、Server/Remote 仍没有 Shared consumer。该图中的多入口逻辑复用是
当前事实,除 Shared TUI 外的跨进程 Shared deployment 仍是目标架构。

### 4.3 各形态能做什么

Expand Down Expand Up @@ -242,6 +245,7 @@ flowchart LR
| `bitfun-sdk-host` | 独立组装入口,选择 SDK profile | 依赖 CLI crate;成为第二个 Server 或 Runtime |
| SDK Host adapter | 协议、能力协商、连接/Query 资源清理责任和 DTO 转换 | stdin/stdout 入口、Agent 业务状态、Tool/MCP 注册表 |
| Python/TypeScript SDK | 管理或连接匹配 Host,提供一致公开 API | 要求用户安装 `bitfun` CLI;暴露内部 wire DTO |
| `CoreRuntimeOwnership` | 第一方 Rust 入口选择 Embedded/Shared,并把本机 workspace lease 注入 Coordinator | 进入公开 SDK/wire;成为 Session 单写或 Server 路由 owner |

### 5.2 一次 Query 的运行时序

Expand Down Expand Up @@ -468,6 +472,7 @@ CLI 和 SDK 共享能力事实,但不是上下层关系:
因此:

- CLI 不默认依赖 SDK Host,也不通过 SDK package 运行。
- CLI、ACP、Desktop 与 SDK Host 只共享 Core ownership 和 Runtime 行为 owner;共享这些内部 owner 不构成产品依赖,也不新增第二种 SDK。
- 一次性 `bitfun exec` 默认使用 Embedded Runtime;只有恢复或控制 Shared Agent Runtime 中的共享 Session 时,才使用第一方
client adapter attach,且不经过 SDK Host。
- SDK 不解析 CLI `stream-json` 作为正式双向协议。
Expand All @@ -481,6 +486,10 @@ CLI 和 SDK 共享能力事实,但不是上下层关系:
```mermaid
flowchart LR
Runtime["Runtime domain contracts"] --> HostSchema["SDK Host schema\nstable + experimental"]
Runtime --> SessionCreate["AgentSessionCreateResult\nshared session-create facts"]
SessionCreate --> HostSchema
SessionCreate --> CLIProjection
SessionCreate --> UIProjection
HostSchema --> TSClient["generated internal TS wire client"]
HostSchema --> PyClient["generated internal Python wire client"]
TSClient --> TSApi["curated TypeScript public API"]
Expand All @@ -494,6 +503,11 @@ flowchart LR
Fixtures --> CLIProjection
```

会话创建是这条规则的当前实例:`AgentSessionCreateResult` 由 Session owner 生成并携带规范化的
workspace 与 execution-target 事实;Desktop 的 `CreateSessionResponse` 只是该类型的宿主命名,SDK Host 的
`SessionCreateResult` 则保留 `agent`、`lifetime` 等协议字段并从同一结果转换。adapter 可以改变 wire 形状,
但不能重新计算或持有第二份 Session 创建事实。

生成的 wire 类型保持 SDK 内部;公开 API 必须经过人工策划,不能把协议 DTO 原样暴露给用户。

### 10.2 防止持续迭代造成不一致
Expand Down
12 changes: 10 additions & 2 deletions docs/architecture/product-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,20 @@ flowchart LR
当前本机入口组装:

```mermaid
flowchart LR
flowchart TB
Desktop["Desktop"] --> Full["product-full"]
CLI["CLI / TUI"] --> Full
ACP["ACP"] --> Parts["Runtime Parts"]
SDKHost["SDK Host"] --> Parts
ServerBootstrap["Server agent bootstrap · dormant"] --> Full

Full --> Coordinator["ConversationCoordinator"]
Parts --> Coordinator
Ownership["CoreRuntimeOwnership"] -. "first-party composition injects once" .-> Coordinator
```

当前公开 HTTP Server 不调用 agent bootstrap,因此不创建 Runtime 或 workspace ownership;图中的 Server 节点只记录已有 agent-enabled composition 边界,不能据此宣称 Server Agent API 已交付。

当前 Peer 运行连接:

```mermaid
Expand All @@ -400,7 +408,7 @@ flowchart LR
| Desktop | 使用 `product-full`;显示外部来源、审批、冲突、诊断和 Host 能力 | 可执行能力在事实所在 Host 运行;Safe Mode 只阻止新调用,不改来源、不取消正在运行的调用 |
| CLI / TUI | 使用 `product-full`;提供 `/extensions`、`/hooks_external`、`/tools` 和 `/agents` | 不解析生态文件,不启动第二套 Agent Runtime;远程能力未接入时不回退本机 |
| ACP | 使用 `DeliveryProfile::Acp` 和 Runtime Parts | load 成功后才发布活动状态;close 排空后再卸载;完整历史和配置仍由 Core/ACP 管理 |
| Peer / Server | Server 提供 control/catalog;Peer Host 执行真实工作区操作 | 控制端不替远端发现或执行;旧 Host 明确降级,SSH Remote 未接入时返回不支持 |
| Peer / Server | Server 提供 control/catalog;Peer Host 执行真实工作区操作;当前 HTTP Server 不装配 Agent Runtime | 控制端不替远端发现或执行;旧 Host 明确降级,SSH Remote 未接入时返回不支持;只读 Server 不声明 Runtime ownership |
| Web / Mobile Web | 依赖现有后端入口 | 不持有插件执行单元,也不能据空 profile 宣称独立能力 |
| HarmonyOS 手机 Remote | phone-only ArkTS 远程入口 | 不等于 HarmonyOS PC 本地 Runtime、CLI/TUI 或 GUI |

Expand Down
5 changes: 3 additions & 2 deletions scripts/core-boundaries/rules/source/required-rules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1482,8 +1482,9 @@ export const requiredContentRules = [
'the standalone SDK Host must inject its selected delivery profile into the Core tool owner before agentic system construction',
patterns: [
{
regex: /\binit_agentic_system_for_profile\b/,
message: 'SDK Host runtime must initialize Core with its selected delivery profile',
regex: /\binit_agentic_system_for_profile_with_runtime_ownership\b/,
message:
'SDK Host runtime must initialize Core with its selected delivery profile and Runtime ownership owner',
},
{
regex: /\bselect_agentic_system_profile\b/,
Expand Down
13 changes: 11 additions & 2 deletions src/apps/cli/src/agent/agentic_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use anyhow::{Context, Result};

use bitfun_core::product_assembly::DeliveryProfile;
use bitfun_core::product_runtime::CoreRuntimeServicesProvider;
use bitfun_core::runtime_ownership::CoreRuntimeOwnership;
use std::sync::Arc;

pub(crate) use bitfun_core::agentic::system::AgenticSystem;

Expand All @@ -10,8 +12,15 @@ pub(crate) fn select_agentic_system_profile(profile: DeliveryProfile) -> Result<
.context("Failed to select agentic system delivery profile")
}

pub(crate) async fn init_agentic_system(profile: DeliveryProfile) -> Result<AgenticSystem> {
let system = bitfun_core::agentic::system::init_agentic_system_for_profile(profile)
pub(crate) async fn init_agentic_system(
profile: DeliveryProfile,
runtime_ownership: Arc<CoreRuntimeOwnership>,
) -> Result<AgenticSystem> {
let system =
bitfun_core::agentic::system::init_agentic_system_for_profile_with_runtime_ownership(
profile,
runtime_ownership,
)
.await
.context("Failed to initialize agentic system")?;
system
Expand Down
23 changes: 21 additions & 2 deletions src/apps/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,26 @@ async fn initialize_core_services_for_deployment(
.await
.map_err(|error| anyhow!("Failed to initialize global config service: {error}"))?;
tracing::info!("Global config service initialized");
let runtime_ownership = shared_runtime::acquire_ownership(workspace_root, deployment)?;
let path_manager = bitfun_core::infrastructure::try_get_path_manager_arc()
.map_err(|error| anyhow!(error.to_string()))?;
let entrypoint = match (deployment, bootstrap_profile) {
(
bitfun_services_core::runtime_ownership::RuntimeDeployment::Embedded,
BootstrapProfile::Interactive,
) => "cli-interactive",
(bitfun_services_core::runtime_ownership::RuntimeDeployment::Embedded, _) => "cli-headless",
(bitfun_services_core::runtime_ownership::RuntimeDeployment::Shared, _) => {
"shared-tui-runtime"
}
};
let runtime_ownership = bitfun_core::runtime_ownership::CoreRuntimeOwnership::fixed_workspace(
path_manager.as_ref(),
entrypoint,
workspace_root,
deployment,
)
.map_err(|error| anyhow!(error.startup_message(deployment, entrypoint)))?;
let runtime_ownership = std::sync::Arc::new(runtime_ownership);

let config_service = bitfun_core::service::config::get_global_config_service()
.await
Expand All @@ -667,6 +686,7 @@ async fn initialize_core_services_for_deployment(

let agentic_system = agent::agentic_system::init_agentic_system(
bitfun_core::product_assembly::DeliveryProfile::Cli,
runtime_ownership,
)
.await
.map_err(|error| anyhow!("Failed to initialize agentic system: {error}"))?;
Expand All @@ -676,7 +696,6 @@ async fn initialize_core_services_for_deployment(
agentic_system,
workspace_root,
approval_policy,
runtime_ownership,
)?);
debug_assert!(runtime
.product()
Expand Down
Loading