ADR 0011 - zuohaisu/viva GitHub Wiki

Provenance — Migrated 2026-10-06 from docs/decisions/0011-rust-host-and-tui.md @ zuohaisu/viva@871a00a · 迁移自主仓库该文件;正文相对链接已改为 Wiki 链接,原始状态/日期/作者行保留,原文以 Git 历史为准。

ADR 0011 — Rust 本地宿主、Pi 对话宿主与 SQLite 状态存储

Status: Accepted(Rust/Pi:Haisu 显式裁决,2026-09-27;SQLite:2026-09-28)· Implementation: 已交付(2026-09-28,首版主链 PR #34):Rust 宿主(crates/viva/)、SQLite 状态层、Pi 对话宿主(extensions/pi/)均已实现并有验收测试;Python 运行时已于 V13 退役(docs/validation/v13-python-retirement.md)。真实 Pi 端到端与 Intel 平台验收保持 pending(见 V12 记录)。· Amendment: 2026-10-01 起 §6(关闭界面的语义)由 ADR-0012 修订(常驻运行时、detach/attach、退出暂停语义);终端原语 API 与 §5.4"RPC/SDK 未来可选"的区分见 ADR 0012;其余条款不变。

效力说明:以下三项均为用户裁决:Rust 本地宿主;Pi 作为 Samuel 的首个默认对话宿主,以交互终端与小型扩展接入;SQLite + 文件保存 Viva 状态。Tokio + Ratatui + Crossterm 是随 Rust 方向记录的实施默认组合。本文是技术选型的唯一权威结论,历史研究只保留来源与讨论过程。

Context

Viva 是本地优先个人 AI 协作系统。成员身份、任务、授权与工作历史必须独立于模型、工具和进程存在(ADR 0006–0010)。用户故事需要:协调成员作为入口、多项目 workspace、谈话分叉、知识退出机制、模型与工具绑定、任务 worktree、可复用工作流、技能策展、维护,以及浏览器和原生应用操作。

首版以终端 TUI 为主。常态为一个或少量执行,偶尔在同一台机器上并发约 16 个 Agent;目标还包括内存较小的 Intel Mac。这是应用运行时负载,不是每轮开发或 CI 必须启动 16 个编译实例。未来桌面与 Windows 客户端只影响接口边界,本次不选择桌面框架。

用户明确:迁移成本、实现复杂度不作为否决条件;资源占用、交互速度、真实任务完成速度和构建反馈时间需要考虑。关闭 TUI 后,任务与定期维护应暂停。随后进一步裁决:Python 实现可直接替换,既有实现不构成历史负担或兼容义务;Samuel 可在不同 harness 中表达,首个默认宿主选 Pi,Viva 提供终端而不接管 Agent 循环。

Decision

1. 技术组合与范围

层 决定 范围
Viva 宿主、CLI、执行监督 Rust 拥有成员/任务/执行/授权关系、恢复与资源生命周期
异步 I/O 与监督 Tokio 等待模型、工具和进程;阻塞工作与绘制分离,队列有界
终端界面 Ratatui + Crossterm 一个终端渲染者,CLI/TUI 共用 Viva 能力接口
首个默认对话宿主 Pi 交互终端 + 小型扩展 首版在 Viva 的终端中使用 Pi 自己的聊天界面;其他 harness 可替换,不自建 Agent 循环
Pi RPC/SDK 未来可选,不是首版前提 只有统一聊天界面或更深集成的真实需求出现时再评估
外部记忆 独立于 harness 接入 Hermes Holographic 的公开实现已定位;用户实际版本与跨 harness 接入未验收,与 Viva 状态存储分开
Viva 状态存储 SQLite + 普通文件 SQLite 保存结构化状态与关系/索引;文件保存技能、文档、原始会话与大型日志;尚未实施
浏览器/原生应用操作 复用成熟能力,按 driver 接入 Playwright、系统 API/小型 helper 是候选;具体方案待验证
Desktop / Windows 未选型 保持 domain 与 surface、平台 driver 分离,无首版实现承诺

用户从 terminal 启动 viva,程序留在 terminal 中运行。目标发行方式是对应平台的可执行程序,最终用户不需要安装 Rust 编译器;外部 Worker、浏览器及系统权限仍有各自前提。开发与 CI 需要 Rust 工具链,具体最低版本和依赖版本在实现时固定。

Rust 决定的是 Viva 宿主,不要求把 Pi、浏览器引擎、模型 SDK 或所有 helper 重写为 Rust。Viva ≠ Samuel,成员名与职责仍为配置数据。

2. 为什么选 Rust

第一性需求是:让成员、任务与执行的事实持续存在,以小且受控的本地运行时监督可替换工具,并让交互在外部任务等待或大量输出时保持响应。

Rust 的优势是显式所有权与资源生命周期、无需 tracing GC 的宿主、原生可执行文件,以及访问操作系统能力的路径。用户不以实现难度淘汰候选,因此可以接受 Rust 的开发与构建代价,换取宿主层对资源和边界的控制。这是工程选择,不是已测得的速度冠军。Rust 所有权

Pi 的轻量体验与可复用核心支持“复用 Agent 能力”,并不要求 Viva 的本地宿主也使用 TypeScript。反过来,采用 Rust 不证明 Viva 会比 Pi 或 Claude Code 更快:模型延迟、上下文量、工具调用、扩展加载、浏览器和外部 CLI 都可能主导任务耗时。当前没有 Viva 候选栈在目标 Intel Mac 上的同负载基准。

3. 候选比较与代价

候选 核心优势 核心代价 / 本次取舍
Rust + Tokio + Ratatui/Crossterm 无 tracing GC;资源生命周期明确;原生分发;适合执行与平台边界 编译和链接反馈可能较慢;异步取消、FFI、进程树回收仍需正确实现;本次选择
Go + Bubble Tea 单二进制;并发与 TUI 成熟;通常有较短构建反馈 GC 与堆预算需要考虑;不代表性能不合格;是可行替代,暂无同负载数据证明输给 Rust
TypeScript + Node + Pi TUI/SDK 可直接复用 Pi 的 Agent/TUI 能力;工具生态丰富;构建迭代便利 V8 堆、GC 与依赖运行时需要预算;系统操作仍需 driver;更适合作为可复用认知/执行组件候选
TypeScript + React + Ink React 组件化与生态;适合复杂终端交互组织 引入 React 的运行与渲染路径;当前没有必须复用 React 的需求;不能凭架构断言慢
Python + Textual 当前能力已实现并有测试;CLI/MCP/自动化生态成熟;可并发等待多个子进程 解释器与对象开销、原生分发需要考虑;不以迁移成本保留,不以 GIL 否定 I/O 并发
Swift 原生宿主 macOS API 接入直接,未来原生桌面便利 首版终端与跨平台收益不足以成为本次首选;可保留为 macOS helper 候选

Python 实现只提供可参考的能力审计与行为证据,不约束 Rust 的结构或交付路线。需要替换时直接替换,不为保留已有代码而增加架构层。

4. 能力复用与直接替换

默认按 Rust 目标直接实现与替换,不要求延续 Python 实现。 旧源码、模块划分、CLI 参数、安装入口、内部 schema、数据格式和测试实现均可调整或退役;不要求逐模块翻译、不要求维护 Python 兼容层,也不把双运行时或渐进桥接作为前置条件。是否使用过渡适配,仅由当下产品需求与实测收益决定。

复用优先指复用成熟能力,不指永久保留旧代码。先检查 Git、gh、CLI/MCP、Agent 与界面库;已有 Python 能力可作为参考,无法直接复用或复用反而阻碍目标时,以最小 Rust 实现补足已记录缺口。验收依据是有效的产品契约、授权边界与真实使用场景,旧测试可据此重写,不能为“保持旧测试全绿”固化旧设计。

用户数据与成员/任务历史是用户资产,不是实现包袱:需要变更存储时先确认实际存在的数据,按需要提供导入、导出或归档,不要求旧格式继续作为运行时接口。历史数据的保留规则继续有效。下表记录已检查能力与目标缺口,不代表要求保留这些 Python 模块。

拟建/调整的部分 已检查的能力 需要填补的缺口 / 最小动作
Rust 本地宿主 src/viva/ 成员、Task、Execution、grant、恢复、知识注册表 按有效产品契约实现 Viva 的控制面;成员、任务、授权和历史独立于 Worker,内部结构可重设
Rust 执行监督 executions/runner.py、Worker registry、外部 Agent CLI、Tokio process 监督按需启动的进程,落实输出预算、取消、进程组退出与回收;不重写模型循环
Rust TUI / 终端承载 当前 Textual surface、Ratatui/Crossterm、Pi 交互 TUI、成熟 PTY/终端仿真能力 Viva 负责项目与任务导航及终端承载,Pi 做自己的聊天界面;先复用 PTY/终端仿真库,具体库待实现验证
Pi 小型扩展 Pi 扩展事件/工具接口、现有顶层 viva … CLI、成员记录与 task brief 注入成员与任务上下文,按 grant 调用受控动作,保存交接引用;不重建 harness、存储或记忆系统
Git/worktree worktrees/service.py、Git、gh、现有 authority 满足隔离与受保护动作的规则;复用 Git/gh 能力,Python 服务可直接替换
脱敏与授权 core/redaction.py、permissions/authority.py、grant ledger Rust 路径覆盖有效授权与脱敏需求;可重写实现与测试,不能扩大权限
计算机操作 driver 浏览器自动化能力、macOS Accessibility/系统 API、现成 helper 按实际任务验证后补最小适配;前台焦点/鼠标/键盘的动作序列须协调
SQLite 状态层 当前原子 JSON/JSONL、SQLite 事务/查询能力、普通文件 成员/任务/执行/授权等跨对象关系需要一致更新与查询;复用 SQLite,以最小状态层承载,文件正文不重复维护

5. 状态与进程的所有权

Viva 拥有成员身份、Task、Execution 归属与状态、grant、workspace/project 关系及知识归属。模型与 Worker 可替换。外部工具可以拥有原始 transcript、模型消息格式及工具内部状态;Viva 保存稳定引用和必要交接记录,不同时维护两套互相竞争的会话事实。

5.1 成员在哪里:身份、长期资产、当前表达分开

Samuel 的身份、职责与工作关系属于 Viva 的成员记录;长期记忆可以由独立的外部记忆系统承载;当下的对话、模型上下文与工具循环由当前 harness 承载。Pi、Codex 或其他 harness 是可替换的表达/执行工具,换工具不删除成员或其资产,也不承诺不同模型有完全相同的行为。名字与默认工具仍为配置数据,不能在代码中硬编码 Samuel 或要求所有成员使用 Pi。

用户表示在 Hermes 中使用 Holographic。本文确认外部记忆与 Viva 状态存储分离。2026-09-28 的源码研究已定位 Hermes bundled Holographic:本地 SQLite + FTS5 + Jaccard/HRR + 使用反馈;另有独立 handoff 和社区增强版,不能混用其能力声明。用户实际安装的版本和跨 harness 接入尚未验证,不能声称已集成。Viva 保存知识归属、来源、有效性及使用证据,并可关联外部记忆引用;不因引入外部服务就假装自动形成记忆。2026-09-28,用户批准 SQLite + 文件用于 Viva 状态;该决定不替代外部记忆系统,也不证明 Holographic 已接入。Bundled 实现的信任反馈不是事实真伪判断,硬删除不满足可恢复归档;成员/项目作用域、来源及退出活跃状态仍需受控适配。研究结果不新增后端或接口裁决。

2026-09-28 的记忆架构比较进一步核查 OpenViking、Claude Code 与 MemGPT / 当前 Letta:提出有界工作上下文、分层导航、策展/有效性及混合检索,建议优先验证 OpenViking 成品。该建议不是记忆后端或插件默认行为的批准;尤其 Pi context takeover、自动写回、成员/项目映射及失效归档仍需受控验证。Rust / Pi / SQLite 已批准边界不变。

5.2 首版 Pi 接入:交互终端与最小扩展

首版默认在 Viva 提供的交互终端中运行 Pi,使用 Pi 自己的聊天界面。 Viva 管成员/任务/workspace/worktree 导航、工作位置及终端进程生命周期;Pi 管模型调用、工具循环、压缩与当前内部会话。不额外构建一套 Viva 聊天界面,不以 RPC/SDK 接管 Pi 循环。

开源有利于检查与修复,但选择 Pi 的直接依据是可复用的交互界面、会话分支及明确扩展接口。Pi 已提供扩展事件、工具注册和上下文修改能力;会话结构已有分支记录。是否完整满足 Viva 的谈话树 UX,仍需验收,不能把库能力视为产品已经交付。Pi 扩展、Pi 会话格式

小型 Pi 扩展只补已记录的 Viva 接入缺口:开始会话时读取所绑定成员的资料、当前任务简报及授权范围内的相关记忆;工作中通过现有 Viva CLI 或经核实的受控接口查询/派发/跟进任务;在明确交接节点记录结果、未完成事项与原始记录引用。记忆写入按外部记忆系统及策展规则执行,不默认把每次退出或完整 transcript 自动晋升成长期记忆。该扩展可用 TypeScript 实现,不改变 Rust 宿主裁决。

成员发起的受控动作由 Viva 校验真实请求来源和 live grant;扩展不能把 worker 请求冒充用户、不能自行扩大权限。Pi 内部的文件/命令等工具仍需对应权限策略,扩展钩子与提示词都不能被宣称为 OS 沙箱或完整安全边界。

提供终端并不足以判定任务完成:退出码不是开发完成、QA 通过或交付成功的证明。要支持自动跟进,必须由受控接口提交明确结果与证据,Viva 根据其语义记录状态;终端输出只作为原始工作记录,不能靠 ANSI 屏幕文本猜出权威结论。

5.3 PTY 与界面边界

嵌入交互式 Agent 需要 PTY、终端状态解析/仿真、输入路由与尺寸更新,不能把 stdout 当普通文本面板直接显示。先检查成熟终端库,最小适配处理焦点、快捷键、粘贴、滚动、Unicode、resize 与关闭;具体库尚未选择。Pi 的终端输出进入其独立 PTY/仿真状态,再由 Viva 渲染所选终端视图;不能让父子 TUI 同时无协调地写入同一个物理终端。后台终端的缓存/scrollback 仍受资源预算约束。

5.4 跨 harness 连续性与未来接口

切换 harness 时传递成员资料、授权范围内的相关记忆、任务交接简报与必要历史,原始记录另行保留并关联。无需把 Pi 原生会话格式机械转换成另一工具的原生格式;当前模型上下文重建能力按各工具实际接口验证,不承诺所有分支可无损续跑。

RPC/SDK 留作未来可选路径:出现统一自有聊天界面或更深自动化的真实需求时,才重新评估事件、取消、分叉、重连及持久化映射。本次批准交互终端接入,不同时批准 RPC 首版架构或共享 SDK 服务。Pi RPC、Pi SDK

一个 Viva/TUI 可以监督多个按需启动的执行;16 个 Agent 不要求启动 16 个 Viva 界面。交互 Worker 可以各有独立 PTY 与内部 TUI,其进程数量由工具能力决定,不保证共享一个运行时;后台终端状态与缓存同样需要预算。

6. 关闭界面的语义

批准的目标行为:关闭 TUI 后暂停任务派发与定期维护,不提供独立于界面的常驻 daemon。 “暂停”指保存未完成状态并停止继续推进,不承诺 OS 冻结整个进程或任意 CLI 能无损续跑。

正常退出应先停止新派发,持久化交接/执行状态,对 Viva 拥有的运行进程请求安全停止,等待并验证退出,必要时按策略升级终止。重新打开后先恢复事实,再由明确恢复动作或已授予的策略继续;无法确认的外部写入不得盲目重放。非 Viva 拥有的服务不因退出被误杀。

进程句柄丢弃、kill_on_drop 或 Rust 的内存安全不能替代进程树回收。崩溃/强制退出仍需要恢复协议。本行为尚未在当前 Python TUI 中完成验收。Tokio Command

7. 用户故事带来的边界

  • 协调成员的聊天、greeting/安静模式和谈话树,是 surface 与认知接入需求;谈话分叉不自动等于 Task 派发。成员名字不会固化为产品身份。
  • Workspace 的项目、仓库路径与阶段性 reference 需要显式关系;参考 self-model/dsh-ai-soul 不产生运行依赖。
  • 工作/短期/长期记忆及技能需要来源、取用、复审、失效与归档;不把全量历史塞入上下文,不把 raw event 自动标成 memory。自动机制仍属后续能力。
  • 开发→验证→复核→交付可作为 Task 的可配置 workflow;不能复活 ticket/run/verdict 对象或固定流水线。PR/merge、删除代码/文档/worktree 等维护动作仍遵守授权边界。
  • 定期维护只在 Viva 运行期间按策略执行。模型+工具组合须显式验证可用性,不静默换模型。
  • 沙箱按动作风险与授权选择,避免一刀切;沙箱、凭证访问和操作权限是不同维度。复用已认证工具需要验证其实际运行环境,语言不能绕过 OS 或外部工具限制。grant 不因“不用沙箱”而扩大。

8. Viva 状态存储:SQLite + 普通文件

2026-09-28,Haisu 批准采用 SQLite。该裁决对应讨论中的“SQLite + 普通文件”方案,Viva 的结构化事实与大型/可编辑材料分开;不要求兼容旧 Python 文件格式,不引入独立数据库服务。

内容 权威来源 / 保存位置
成员、角色、模型/工具绑定、workspace/project/repository 关系 SQLite
Task、Execution 状态与归属、授权授予/撤回/拒绝记录、工作事件 SQLite;审计/事件记录保留追加语义,不因数据库可更新就改写历史
用户层会话名称、分叉关联、原始记录索引 SQLite;harness 当前模型上下文与其原生会话仍由 harness 管理
知识归属、来源、有效/归档/撤回状态、使用证据及外部记忆引用 SQLite;不自动生成记忆,不复制外部记忆系统的职责
SKILL.md、项目文档、附件、截图、大型执行日志、harness 原始会话文件 普通文件;SQLite 保存必要路径/索引,避免另一份可独立修改的正文

每份事实只设一个权威来源,数据库保存关系与必要索引,文件保存对应正文/原始材料。Viva 状态数据库默认放本机磁盘;另一台 Mac 可独立运行,不通过网络共享目录让两台机器共同打开同一个数据库。跨设备同步不是本次范围。

选择 SQLite 的理由是复用成熟事务与索引能力:授权使用、执行记录与 Task 状态等相关数据库变更可以在一个事务中提交。外部进程启动、harness 文件及数据库不能被宣称为一个原子事务;实施时需记录启动意图/结果,处理退出或崩溃后的对账与恢复。SQLite 事务

并发写入采用短事务及有界排队,日志/流式字符不逐字符触发数据库事务;读取与数据库工作不能阻塞界面。WAL、同步级别、连接管理及 Rust 库由实现验证决定;若使用 WAL,需处理单写者、checkpoint、备份与 WAL 体积预算,不只复制正在运行的主数据库文件。锁定经过核查的 SQLite 版本,验证正确备份/恢复流程。SQLite WAL

实际 schema、文件布局和导出方式在实现切片中确定,仍须做到用户数据可保全、可备份、可导出,权限与脱敏规则继续有效。现有 JSON/JSONL 实现是当前事实而非保留义务;本次裁决没有创建数据库或实施数据转换。

Consequences and validation

资源与速度

宿主需采用有界队列与单条消息/总字节预算、日志落盘与轮转、按需载入历史、输出批处理和单一渲染者。并发槽位须考虑内存、CPU、I/O、浏览器、供应商限额;内存可能是主要约束,不能假设 CPU 永不受限。共享桌面的前台操作另行协调。

Rust 实现的首个真实任务垂直切片,同时承担技术验收。先记录目标 Intel Mac 的硬件/系统与空闲资源,再固定 Worker/模型/扩展/任务/输出负载,覆盖空闲、常态 1/少量执行及 16 路峰值:

验证项 必须留下的证据
内存 分开测 Viva、各 Worker 及其后代、浏览器与整机 memory pressure/swap;记录稳态与峰值,不能简单把共享页 RSS 相加当物理总量
交互与速度 冷启动、输入到绘制延迟 p50/p95、模型首输出、任务完成时间、CPU;区分本地与网络/模型耗时
输出与背压 高频输出下界面可响应、完整持久记录与可见截断策略、队列/日志不无限增长
生命周期 单独 Stop 不影响邻居;正常关闭停止所拥有执行;重启不丢归属、不重跑完成任务、不误杀其他进程
领域与授权 换模型/harness 保留成员资产并提供交接;workspace/Task 独立;worker 请求不冒充用户;子 grant 不扩大;脱敏语义保留
Pi / 终端接入 真正交互 Pi:输入、流式显示、Unicode、粘贴、resize、分叉、切换/停止邻居互不影响;扩展绑定正确成员/任务,原始记录可追溯;退出码不冒充验收结果
存储 事务回滚/并发更新不丢授权或归属;进程/文件与数据库中断后可对账;备份恢复与导出可验证;日志增长有界,旧用户数据未被误删
工程反馈 冷构建、增量构建、检查/测试时间与缓存命中;记录 CI 总耗时,不把语言名当编译成本数据

量化预算需在实现切片前根据目标机器可用资源和用户响应要求确定,写进该切片的验收记录;本 ADR 不编造内存或毫秒门槛。语义验收要求零越权、零误杀、零归属丢失与零完成任务重放。性能预算未设或目标机器未测,不能报告“16 路低内存 Intel Mac 验收通过”。

若预算失败,先定位宿主、Worker、浏览器或调度的贡献;压缩外部负载或降低槽位可能比换语言有效。只有宿主被证明是主要瓶颈且替代栈在同负载达标,才据证据重新裁决。无需为了形式同时完整实现多套平台。

CI 与迁移

Rust PR 需要检查/测试及必要构建,但不必每次丢弃缓存重编所有依赖。Cargo 支持构建缓存,cargo check 可提供不生成最终可执行文件的编译检查;两者都不替代测试。发布、平台/工具链或依赖变化可能需要额外构建,缓存也不能保证每次有效。Cargo build cache、cargo check

本次仅更新决策文档:不添加 Cargo 项目、Pi 扩展或 PTY 实现,不移植代码、不改变 Python 安装入口或 CI。后续实施可直接用 Rust 替换 Python,并单独交付可运行切片、产品行为证据、按实际数据需要制定的保全方案与目标平台验收;不要求维持旧 Python 入口、格式、兼容层或双版本回退。历史 Ticket Autopilot 数据保持原样。研究文档存档仅表示决策已收敛,不构成运行能力进度。