桌面运行时
Foya 桌面应用由 Vue、Tauri 和 Go 三个运行层组成。界面不直接持有 Agent Runtime; Go Sidecar 才是 Session、工具和持久状态的所有者。
三层结构
标题链接flowchart LR
VUE["Vue WebView"] -->|Tauri Command| RUST["Rust Host"]
RUST -->|HTTP over Unix Socket| GO["Go Sidecar"]
GO -->|SSE| RUST
RUST -->|Tauri Channel| VUE
RUST --> NATIVE["窗口、文件选择、Browser WebView"]
Vue WebView
标题链接负责:
- 路由和页面渲染;
- Session、Message 和 Tool 状态投影;
- 输入、审批、提问和文件审查交互;
- 设置管理;
- Workbar、Terminal、Browser 和 Canvas 界面。
Rust Host
标题链接负责:
- 启动和监管 Go Sidecar;
- 连接 Unix Domain Socket、SSH 隧道或 HTTPS 远程内核;
- 探测 SSH 主机并自动部署匹配架构的 Linux 内核;
- 把 Tauri Command 转成 REST;
- 把 SSE 帧转发到 WebView;
- 处理原生文件选择和外部编辑器;
- 创建与控制 Browser WebView。
Go Sidecar
标题链接负责:
- 所有业务规则;
- Agent Runtime;
- Provider 与工具;
- 权限和沙箱;
- Session 与持久状态;
- 外部 Channel 和 Automation。
Sidecar 启动
标题链接Tauri 启动时选择当前平台对应的 Sidecar 二进制,并设置父进程 ID。Go Kernel 根据 父进程生命周期退出,避免桌面窗口关闭后长期留下孤立服务。
开发和打包前,Makefile 会按 Tauri Target Triple 生成 Sidecar:
foya-aarch64-apple-darwinfoya-x86_64-apple-darwinfoya-x86_64-unknown-linux-gnufoya-x86_64-pc-windows-msvc.exe正式 App 不内置远程内核。首次使用 SSH 连接时,Rust Host 会从同版本 GitHub
Release 下载匹配架构的 gzip 内核,校验 SHA-256 和 ELF 后写入版本化缓存;后续
部署复用缓存。开发模式仍会在本地生成 linux/amd64 和 linux/arm64 内核。
桌面进程会转发必要的 FOYA_PROVIDER_* 环境变量给 Sidecar。
内核连接模式
标题链接设置页支持三种模式:
- 本机:启动并连接打包的 Sidecar;
- SSH:复用系统 OpenSSH 配置,上传内核并维护本地端口转发;
- HTTPS:连接已经部署并配置 Bearer Token 的远程内核。
SSH 和 HTTPS 模式不会启动本地 Sidecar。连接配置保存在桌面应用配置目录的
kernel-connection.json,原始访问令牌不会由远端内核保存。
Command Bridge
标题链接Vue 的 API 层集中调用 Tauri Command。Rust Command 再调用 Go HTTP API。
这种两段桥接提供:
- 统一错误转换;
- 二进制上传和下载;
- 平台路径处理;
- 持续事件 Channel;
- 避免 Vue 直接依赖 Socket 实现。
业务组件不应自行拼接 Kernel URL。所有内核调用应集中在 API 层。
前端状态
标题链接useKernel 维护应用级共享状态:
- Session 与 Project 列表;
- 每个 Session 的 Message Bucket;
- 运行、压缩和队列状态;
- Usage;
- SubAgent 与 Workflow;
- 文件审查;
- 等待中的 Approval、Question 和 Browser Action;
- 后台命令。
切换当前 Session 不会停止其他 Session。每个 Session 的消息和运行标记按 ID 分别保存。
事件投影
标题链接前端通过 SSE 更新本地视图:
- Delta 追加到当前流式 Assistant Message;
message_end用持久结果收敛临时内容;- Tool Event 更新 Assistant Message 内的 Tool Segment;
turn_complete清理流式状态;- Session Delete 阻止迟到事件重新建立数据桶。
UI 中的 Message Segment 保存“推理 → 文本 → 工具 → 推理”的显示顺序,但它是 客户端视图,不是 Kernel Canonical Message 的替代品。
首次打开 Session 时,前端先读取 History,再建立订阅。Child Session 在 SubAgent 状态出现后按需加载并订阅。
路由
标题链接桌面端使用 Hash Router,主要区域包括:
- Chat;
- Studio;
- Automations;
- Settings。
Settings 再按 Connection、Default Model、Rules、Memory、Skills、Commands、 Hooks、MCP、Web Search、Channel、Agent、Usage 和 Appearance 分页。
Hash Router 避免桌面静态资源加载依赖服务端 History Fallback。
Browser WebView
标题链接Browser 不在普通 Vue DOM 中模拟。Rust Host 创建独立 WebView,并负责:
- 导航和窗口位置;
- 页面 Snapshot;
- 元素选择;
- 点击、输入和滚动;
- 截图;
- 把执行结果返回 Go Browser Controller。
Agent 发出的 Browser Action 仍通过 Kernel Event 和 Tool 生命周期协调。
Project Files 与 Terminal
标题链接Project File Explorer 的本机文件操作由 Rust Command 提供。Interactive Terminal 则由 Go Kernel 的 Terminal Manager 创建和持有 PTY、输出 Buffer、Replay Seq 与 进程生命周期;Rust/Tauri Command 只负责把 WebView 请求和输出流桥接到内核。
Terminal 输出通过独立 Channel 流式返回。关闭 Session 时,Go Kernel 会停止对应 Terminal 和后台命令。
故障收敛
标题链接桌面端把 Kernel 持久快照视为恢复依据:
- 流中断后可重新读取 Session History;
- 最终
message_end覆盖不完整 Delta; - 删除集合过滤迟到事件;
- 队列、Usage 和 File Review 可以重新通过 REST 获取。
前端状态不应成为唯一持久来源。
当前边界
标题链接- macOS 和 Linux 是当前主要桌面目标。
- Windows 存在构建目标,但 Unix Socket 桥接和部分本机能力尚未完整实现。
- 远程 Kernel 中的项目路径属于服务器;本机 Project File Explorer 不会自动浏览 服务器文件系统。
- 前端事件投影与 Go 协议类型需要在同一仓库同步演进。