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
9 changes: 9 additions & 0 deletions docs/architecture/agent-runtime-services-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,9 @@ ping 路由。未接入入口的 profile、枚举分支和单元测试仍不能
或 `if cli` 这样的产品分支。
- Tauri 句柄、窗口、命令宏和桌面 app 状态只能存在于 Desktop 提供方或
传输/接口适配器;运行时部件只接收类型化服务端口、DTO、事件事实和能力可用性。
- 宿主通信的抽取门槛、Tauri 薄适配职责和逐能力迁移顺序以
[`product-architecture.md`](product-architecture.md#22-宿主通信契约与-tauri-薄适配) 为准;不得用通用 API 转发层
包装所有 Runtime SDK 方法。
- 插件运行时客户端只能作为内核可调用的类型化边界注入;智能体内核、工具运行时和工作流不直接加载
OpenCode 插件代码。
- feature group 是构建时能力边界;能力计划和能力可用性是产品运行时能力边界;两者必须在
Expand All @@ -771,6 +774,12 @@ ping 路由。未接入入口的 profile、枚举分支和单元测试仍不能
| ACP | ACP 协议、客户端生命周期、远端探测 | 外部智能体/工具能力、环境事实、权限桥接 |
| Web UI / mobile web | UI 状态、hydration、配对、会话展示、插件状态视图 | 接口/传输 DTO、运行时事件事实、能力服务读模型 |

当前 Runtime SDK 已提供会话创建、列出、删除、恢复和类型化转录读取。`AgentSessionRestoreRequest/Result` 与
`AgentSessionRestorePort` 归 Agent Runtime SDK,以继续复用 Runtime owner 的完整 `SessionState`;类型化
`SessionTranscript` 归 `runtime-ports`。两者都由 `assembly/core` 注入真实 persistence owner,CLI/TUI 是当前恢复与
转录消费方。`CoreAgentRuntimeCompatibility` 仍承载未迁移的持久化、分支、用量、快照和交互操作;不能据此把整个
兼容门面一次性删除,也不能把这些操作提前声明为跨宿主稳定接口。

### 4.3 Product Capability 设计

Product Capability 是产品能力的静态声明,由 `assembly/product-capabilities` 归属。当前实现已经声明能力集合、
Expand Down
5 changes: 5 additions & 0 deletions docs/architecture/cli-product-line-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@ TUI renderer、实验性接口和完整外部 Server 协议按总矩阵明确降
| 脚本执行服务 | 物理进程健康、资源预算、进程树与句柄、类型化脚本请求和回收 | 决定工具权限、业务结果、TUI 或品牌资源 |
| 生态配置适配器 | 解析受支持外部格式并生成导入候选/诊断 | 直接写运行时配置、读取密钥、决定最终权限 |

CLI/TUI 的会话创建、列出、删除、恢复和历史转录读取通过 Runtime SDK 的类型化端口完成;TUI 只把
`SessionTranscript` 投影为本地渲染状态,不再消费 Core `Message`。Peer Host、账户同步、会话分支、用量、快照和
工具交互仍使用经过审查的 Core compatibility 方法,直到各自具备明确 owner、稳定 DTO、远程语义和行为等价测试。
这是一条垂直链路迁移,不是删除整个兼容门面或新建 CLI 专用服务层。

Runtime Configuration Service 当前由 `bitfun-core/service/config` 负责。在经评审的 port/provider
迁移完成前,CLI 和生态适配器不得另建写入器;adapter 只做 discover/parse/normalize,配置服务才能
预览/应用、记录来源,并通过远程工作区 provider 写目标层。产品定义、品牌资源、界面布局选择
Expand Down
37 changes: 36 additions & 1 deletion docs/architecture/product-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,42 @@ handler,不构成生产消费闭环。
client 或未来 CLI/HarmonyOS 计划,不能证明同名 Rust transport adapter 已接入;未接入实现应删除,待端到端
调用链确定后再按宿主边界实现。

### 2.2 入口形态接口规则
### 2.2 宿主通信契约与 Tauri 薄适配

前后端契约按能力语义归属,不按 Tauri command 名称归属。稳定的请求、响应、状态事实和类型化错误放在对应
`contracts/*`、Runtime SDK 或能力 owner;Tauri、HTTP/WebSocket、CLI/TUI 与未来平台宿主只负责把各自协议映射到
这些类型。该规则降低框架耦合,但不要求把每个 Desktop DTO 都搬进共享 crate。

| 层 | 允许 | 禁止 |
|---|---|---|
| 能力 owner / Runtime SDK | 类型化请求/响应、状态事实、权限/取消语义、与框架无关的用例方法 | `tauri::State`、`AppHandle`、窗口/菜单对象、command 宏、HTTP/WebSocket envelope |
| Desktop Tauri adapter | 解包宿主状态、构造稳定请求、调用 owner/SDK、把类型化错误映射为 Desktop 协议、投递桌面事件 | 复制业务校验、持有第二份权威状态、把 Tauri 类型传入下层 |
| Server / Remote adapter | 路由鉴权、协议 envelope、连接生命周期、背压与取消映射 | 为同一能力另建语义不同的 DTO 或 handler |
| GUI / TUI 消费方 | 依赖入口侧 API interface、稳定读模型或 Runtime SDK;各自保留渲染状态 | UI 组件直接持有平台句柄,或让 React/TUI 状态成为后端契约 |

Rust 与 TypeScript 的字段一致性以能力所有者的 DTO 为事实源,不以 Tauri command 参数为事实源。单宿主阶段由
前端基础设施层维护对应接口,并用序列化契约测试锁定字段命名、可选字段和错误形状;达到独立版本化门槛后,才使用
不依赖 Tauri 的 JSON Schema 或类型生成任务输出只读 TypeScript 类型。生成结果只同步数据形状,不承载权限、重试或
业务分支。本阶段不为此新增生成器或框架依赖。

抽取共享契约需要满足以下任一条件:至少两个当前生产宿主复用同一语义,或存在独立版本化的外部消费者。只有一个
Desktop command 使用的序列化对象继续留在 `src/apps/desktop`;即使它不含 Tauri 类型,也不因“未来可能复用”而
提升为公共 DTO。共享的框架中立用例 handler 也遵循同一门槛:它必须拥有真实的编排、权限、取消或错误语义,不能
只是通用转发层。

单条能力按垂直切片迁移:

1. 先确认权威 owner、当前生产消费方、远程/多产品形态语义和现有行为基线。
2. 把稳定事实与请求/响应放到能力所有者的契约模块,并以序列化、错误、取消和行为等价测试锁定。
3. 让非 Desktop 消费方或第二宿主先通过 Runtime SDK / owner 接口形成真实调用链。
4. 将 Tauri command 收敛为薄 adapter;前端基础设施层负责 `invoke` 映射,UI 组件不直接依赖 Tauri API。
5. 删除重复 DTO、旧 handler 或兼容方法;无法证明等价时保留已标注的兼容边界,不做批量迁移。

因此仓库不恢复一个通用 `api-layer` 作为默认中转层。只有达到上述复用门槛且现有 owner 无法合理承载时,才评审
窄范围共享 API 模块。HarmonyOS GUI/TUI 可复用稳定能力契约,但仍需各自的平台宿主、生命周期和交付验证;契约
抽取只是前置条件,不代表 HarmonyOS 已受支持。

### 2.3 入口形态接口规则

入口形态接口只描述宿主可消费的声明,不描述具体渲染实现。TUI 与 GUI 的能力边界不同,不能因为存在一个界面插件就自动扩展为全入口稳定接口。

Expand Down
41 changes: 28 additions & 13 deletions src/apps/cli/src/agent/core_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ use tokio::sync::Mutex;
use super::Agent;
use bitfun_agent_runtime::sdk::{
AgentDialogTurnRequest, AgentRuntime, AgentSessionCreateRequest, AgentSessionDeleteRequest,
AgentSessionListRequest, AgentTurnCancellationRequest,
AgentSessionListRequest, AgentSessionRestoreRequest, AgentTurnCancellationRequest,
SessionTranscript, SessionTranscriptRequest,
};
use bitfun_agent_runtime::user_questions::USER_INPUT_AVAILABLE_CONTEXT_KEY;
use bitfun_core::agentic::core::Message;
use bitfun_core::agentic::persistence::session_branch::SessionBranchResult;
use bitfun_core::product_runtime::CoreAgentRuntimeCompatibility;
use bitfun_core::service::session::DialogTurnData;
Expand Down Expand Up @@ -119,11 +119,18 @@ impl CoreAgentAdapter {
let sessions = self
.list_sessions_in_workspace(&effective_workspace)
.await?;
let summary = validated_session_summary(&sessions, session_id, &effective_workspace)?;
validated_session_summary(&sessions, session_id, &effective_workspace)?;

self.compatibility
.restore_session(&effective_workspace, session_id)
.await?;
let restored = self
.runtime
.restore_session(AgentSessionRestoreRequest {
workspace_path: effective_workspace.to_string_lossy().to_string(),
session_id: session_id.to_string(),
remote_connection_id: None,
remote_ssh_host: None,
})
.await
.map_err(|error| anyhow::anyhow!(error.to_string()))?;

let mut session_id_guard = self.session_id.lock().await;
let mut turn_id_guard = self.current_turn_id.lock().await;
Expand All @@ -135,7 +142,7 @@ impl CoreAgentAdapter {
*session_id_guard = Some(session_id.to_string());
*turn_id_guard = None;

Ok((summary, effective_workspace))
Ok((restored.session, effective_workspace))
}

pub(crate) async fn delete_session(&self, session_id: &str) -> Result<()> {
Expand All @@ -150,11 +157,14 @@ impl CoreAgentAdapter {
.map_err(|error| anyhow::anyhow!(error.to_string()))
}

pub(crate) async fn get_messages(&self, session_id: &str) -> Result<Vec<Message>> {
self.compatibility
.get_messages(session_id)
pub(crate) async fn get_transcript(&self, session_id: &str) -> Result<SessionTranscript> {
self.runtime
.read_session_transcript(SessionTranscriptRequest {
session_id: session_id.to_string(),
turn_id: None,
})
.await
.map_err(Into::into)
.map_err(|error| anyhow::anyhow!(error.to_string()))
}

pub(crate) async fn update_session_model(
Expand Down Expand Up @@ -269,8 +279,13 @@ impl CoreAgentAdapter {
return Ok(());
}
match self
.compatibility
.restore_session(&workspace, session_id)
.runtime
.restore_session(AgentSessionRestoreRequest {
workspace_path: workspace.to_string_lossy().to_string(),
session_id: session_id.to_string(),
remote_connection_id: None,
remote_ssh_host: None,
})
.await
{
Ok(_) => {
Expand Down
Loading
Loading