Finch 扩展系统设计
状态:随 Finch 演进 定位:Finch 小工具(mini tools / extensions)系统的正式设计文档。回答三个问题:是什么、为什么这样设计、往哪走。 读者:小工具作者、Finch 开发者、以及对扩展系统设计感兴趣的外部读者。 事实标注:代码路径 + 行号指向本仓库当前实现;论文引用指向《A Programming Paradigm for Spatiotemporal Composability》(北大 & DeepSeek-AI,2026-08-13 草案,
github.com/cordiverse/paper)。
1. 愿景与设计原则
1.1 愿景
Finch 是一个桌面 Agent 客户端。Agent 的能力来自工具、记忆、自动化与外部服务的组合,而这些能力不可能也不应该全部由宿主内置——模型在演进、服务在增多、每个人的工作流不同。因此 Finch 把"扩展"当作一等公民:任何能力都可以由一个小工具提供,小工具与内置能力走同一条执行管线。
对比参照:DeepSeek Harness 的口号是 "Everything is a Plugin"(
github.com/deepseek-ai/deepseek-harness),其底层框架 Cordis 的设计理念与 Finch 的扩展系统有大量共鸣(见第 4 章)。区别在于 Finch 的扩展系统是在桌面应用场景里独立演化出来的,形态更贴近"应用插件",而 Cordis 是通用"动态组合元框架"。
1.2 四条设计原则
- 进程隔离优先。每个小工具运行在独立的宿主子进程中,不共享进程、不信任对方的内存。小工具崩溃不应影响宿主或其他小工具;恶意或有缺陷的代码被限制在自身进程内。
- 声明优于执行。小工具能静态声明的东西尽量静态声明:id、版本、最低 Finch 版本、权限、能力提供/依赖、贡献点(工具/按钮/设置项/会话容器)。声明是安装时审查与运行时门控的共同基础。
- 生命周期纪律。小工具的每个注册动作返回一个可清理的句柄(Disposable),停用时宿主统一回收。作者无需理解清理顺序,宿主按登记逆序执行。
- 可恢复性。破坏性操作优先选择可逆路径:卸载进废纸篓而不是直接删除、更新保留回滚可能、禁用不丢数据。
2. 核心抽象
2.1 清单(Manifest)
一个小工具是一个 npm 风格包目录,携带 package.json#finch 或独立 finch.json 清单(src/main/services/extension/scanner.ts)。清单声明:
- 身份:
id(npm 包名派生,见 3.7)、name(支持本地化)、version - 兼容性:
manifestVersion(当前支持 = 1,scanner.ts:28)、minVersion(低于当前 Finch 版本则拒绝加载) - 权限:
permissions.filesystem / network / shell / secrets / oauth / sessions / sessionInteractions(src/shared/types.ts:55) - 能力:
provides.capabilities/requires.capabilities(跨扩展依赖的静态声明) - 贡献点(
contributes):tools、composerActions(Composer 工具栏按钮)、settingsMenu、iconPacks/icons、skills(随包携带技能)、mcpServers(注入官方 MCP bridge 的服务器)、sessionContainers(会话容器)、agentProfiles(角色提示词) - 设置项:
settings声明式 schema,宿主原生渲染编辑表单,值存入扩展私有数据目录
2.2 运行模型
┌────────────────────────── Electron Main Process ─────────────────────────┐
│ ExtensionService │
│ ├─ scanner(扫描 global + personal 两级目录) │
│ ├─ CapabilityBroker(跨进程能力路由) │
│ ├─ runner(工具注册/调度/权限管线) │
│ └─ 每个小工具对应一个 ActiveExtension 记录(disposers / toolDisposers) │
└──────────────┬──────────────────────────────────────────┬────────────────┘
fork 子进程 fork 子进程
┌──────────────▼──────────────────┐ ┌──────────────▼──────────────────┐
│ ExtensionHost(小工具 A) │ │ ExtensionHost(小工具 B) │
│ - 执行 activate(ctx)/deactivate│ │ - 执行 activate(ctx)/deactivate│
│ - ctx 能力面(tools/storage/…) │ │ - ctx 能力面 │
│ - IPC 与主进程通信(hostProtocol)│ │ - IPC 与主进程通信 │
└──────────────────────────────────┘ └─────────────────────────────────┘
- 每个小工具独占一个宿主进程:
fork(hostPath, { env: { ELECTRON_RUN_AS_NODE: "1" } })(src/main/services/extension/hostClient.ts:186)。 - 宿主与主进程通过结构化消息协议通信(
hostProtocol.ts);能力调用跨进程时经主进程的CapabilityBroker路由(capabilities.ts)。
2.3 能力面 API(ctx)
小工具在 activate(ctx) 中获得一个 MiniToolContext(类型契约见 packages/minitool-api/finch.d.ts,零运行时依赖):
| 能力 | 说明 |
|---|---|
ctx.tools.register(def) | 注册 Agent 工具(与一方工具同管线,autoAllow: false) |
ctx.composerActions.register(id, provider) | Composer 工具栏按钮:badge / menu / execute |
ctx.capabilities.provide/get/has | 跨扩展能力提供与获取(见 2.4) |
ctx.storage / ctx.settings / ctx.secrets | 私有 KV 存储 / 声明式设置读写 / 系统加密密钥 |
ctx.oauth | 隔离的 OAuth 连接与授权(provider 需 manifest 白名单) |
ctx.sessions / ctx.session | 会话创建与当前会话上下文(只读) |
ctx.ui | Toast / 确认框 / 表单 / 浮动 Canvas 窗口(桌宠等) |
ctx.i18n / ctx.logger / ctx.app / ctx.workspace | 国际化、带前缀日志、App 信息、工作区信息 |
ctx.events | Agent 事件、会话状态、通知订阅 |
ctx.subscriptions | Disposable 登记数组,停用时宿主自动 dispose(见 3.3) |
2.4 依赖路由:CapabilityBroker
小工具之间通过具名 capability 协作(capabilities.ts):
consumer host ──capabilityInvoke──▶ main (broker) ──invokeCapability──▶ provider host
provide(name, impl):注册一个能力对象(一组异步方法),须与 manifestprovides.capabilities一致(hostProcess.ts:820);get<T>(name):取得能力对象,须与 manifestrequires.capabilities一致(hostProcess.ts:844);has(name):跨宿主同步查询(主进程广播可用能力列表);- provider 停用时
unregister自动清空,并广播刷新所有宿主的has()视图。
每个宿主进程持有跨宿主能力视图(availableCapabilities),激活时播种、变化时刷新,保证 has() 可同步作答。
3. 关键设计决策
这一章是本文档的核心:每一条决策都讲"为什么这么做"。
3.1 为什么每扩展一个进程
决策:每个小工具独占一个 fork 子进程(ELECTRON_RUN_AS_NODE),而非 VSCode 式的共享"扩展宿主"进程。
理由:
- 崩溃隔离:小工具内存错误、死循环、内存泄漏不拖垮宿主和其他小工具(宿主只需处理子进程 exit 事件并标记 crashed,hostClient.ts:197-216)。
- 卸载即终止:停用小工具 = 优雅 deactivate + 终止进程,进程内残留(定时器、连接、全局状态)随进程消亡,不需要逐项排查泄漏——这是"进程即垃圾回收器"。
- 沙箱边界:论文《时空可组合性》§6.3 明确指出:语言级访问控制拦不住恶意代码,沙箱必须外部化(SFI / 独立运行时 / 进程 / 容器)。进程隔离是其中最轻量的工程选项;Finch 已具备,权限裁决的强化是路线图(第 7 章)。
- 升级安全:宿主进程绝不在自己的内存空间里执行第三方代码。
代价:每扩展一个 OS 进程,重载成本高(fork + 重新 activate + 重新注册工具),内存占用随扩展数线性增长。桌面单用户场景下可接受;也因此我们选择"进程内模块级重载"作为未来的 HMR 方向(第 7 章),而不是给每个扩展再细分。
3.2 为什么声明式权限 + 少量运行时强制
决策:manifest 静态声明权限;运行时只对可白名单化的 API 强制执行(ctx.oauth provider 白名单 hostProcess.ts:775、ctx.secrets key 白名单、capability provide/get 与 manifest 一致性 hostProcess.ts:820-864);filesystem/network/shell 权限目前是信任信号(安装时审查展示),而非执行期拦截。
理由:
- 桌面场景的信任模型:Finch 是用户在场的桌面应用,安装小工具本身就是一个信任决策;工具调用层面还有权限卡兜底(见 5.2)。声明式权限的短期价值是"可审查、可追溯、可展示",而不是"可执行"。
- 诚实:在 Node 子进程里对 fs/net 做执行期拦截,要么包一层薄 API(作者可用
require('fs')绕过),要么上 seccomp/utilityProcess sandbox——都是大工程。与其假装有强制,不如明确声明边界,把真实强制列入路线图(P1-6)。 - 论文印证:论文 §6.3 承认"语言级访问控制不足,需要外部机制"——进程边界已有,进程内裁决是下一步。
3.3 为什么 Disposable 登记制(而不是自动追踪)
决策:小工具把需要清理的资源 push 进 ctx.subscriptions(或直接使用各注册 API 返回的 Disposable);宿主在 deactivate 时统一 dispose(packages/minitool-api/finch.d.ts;主进程侧 record.disposers,service.ts:1063 起)。
理由:
- 简单且可审计:作者看得见自己在清理什么;宿主停用逻辑只是一遍
dispose(),不依赖对第三方代码的静态分析或魔法代理。 - 作者可控:有些资源生命周期长于 activate(如全局单例、跨会话连接),作者可以选择不登记或自行管理。
- 诚实承认边界:自动追踪(论文"revertible effects"——每个上下文变换携带逆函数、运行时 LIFO 恢复,§3.1)更强大,但要求"一切状态变更都经过 ctx",这是对 API 形态的重塑,不是增量改动。
代价与路线图:漏登记即泄漏(进程终止可兜底,但运行期内仍可能累积)。P0-1(第 7 章)计划在宿主内部状态(storage/settings/secrets)上先做"写时快照 + 停用回滚",把"作者自觉"升级为"宿主兜底"。
3.4 为什么幂等 reconcile + 按 id 独立重启
决策:reload() 是幂等 reconcile——只停"不再 enabled 或已从磁盘消失"的扩展、激活新 enabled 的扩展,已运行的扩展保持运行(service.ts:672-680);restartActive(ids?) 支持按 id 单扩展重启(service.ts:1248),IPC extensions:reload(ids?) 透传(src/main/ipc/extensions/index.ts:58)。
理由:
- 最小影响:改一个小工具的配置/代码,只影响它自己;宿主和其他小工具的运行状态(会话、连接、缓存)不受扰动。这与论文 §5.2.2 的"stale entries 最小集合"精神一致。
- 声明式目标状态:
enabled集合就是"期望状态",reload()只是把现实收敛到期望——幂等、可重复、失败可重试。这比指令式"先停 A 再起 B"更不容易出错。 - 代价:每次重启仍是"停旧→起新"裸切换:无备份、activate 失败不回滚、子进程内存状态丢失。事务化是 P0-3(第 7 章)。
3.5 为什么安装直下 tarball、不跑安装脚本
决策:CLI(packages/minitools/bin/minitools.mjs)从 npm registry 直接下载 dist.tarball 解压安装,不经 npm install,第三方安装脚本永远不会执行(minitools.mjs 头注释)。
理由:npm 包安装脚本(preinstall/postinstall)是供应链攻击的经典载体。小工具是"要装进宿主生态的可执行代码",其安装过程本身必须是确定、可审计的。直下 tarball 同时带来:无依赖树(不装第三方依赖,小工具自包含)、快速、可缓存(cache/minitools/)。
3.6 为什么默认禁用 + 信任状态持久化
决策:发现小工具 ≠ 启用小工具。安装后默认 disabled,用户显式 enable 后其工具才会注册(src/main/services/extension/state.ts 头注释:"discovery alone never registers an extension's tools")。信任状态(enabled / grantedPermissions / autoUpdate)持久化在 extensions.json。
理由:
- 安装与授权分离:把"下载了文件"与"赋予了能力"分成两个动作,符合最小权限直觉;也避免了"装了个包就默默生效"的惊吓。
- 可审计:
enabled集合 + 权限授予时间戳(grantedAt)构成可回溯的信任记录。 - 对比:多数插件市场"安装即启用",Finch 选择多一步显式授权,代价是用户多一次点击,收益是心智模型清晰。
3.7 为什么用 npm 包名派生 id
决策:新安装小工具的 id = sanitizeExtensionId(packageName)(scoped 包如 @finchtoys/mcp-client → finchtoys@mcp-client),安装时写入 .finch-id sentinel(scanner.ts:35 与 minitools.mjs 保持字面同步)。
理由:npm 保证包名全局唯一;而作者自由声明的 finch.id 是自由文本,两个社区扩展可能撞同一个短 id。用包名派生 id 使"身份唯一性"从约定变成结构事实,且不可被无关包伪造(npm 只允许 @ 出现在 scope 首位)。旧安装通过 sentinel 缺失保留旧解析方式,零迁移(idMigration.ts)。
3.8 为什么卸载进废纸篓、数据目录保留
决策:uninstall() = deactivate → 清信任记录 → shell.trashItem(OS 废纸篓,可恢复)→ reload(service.ts:1599);extension-data/<id>/(paths.ts:113)不随卸载删除。
理由:破坏性操作优先可逆(原则 4)。废纸篓让"误卸载"可恢复;数据目录保留让"重装即恢复"成为可能。代价是隐私残留——完全卸载选项列入路线图(P1-5)。
3.9 为什么无 project 级作用域
决策:小工具只装到 personal / global 两级,没有 <cwd>/.finch/extensions/ 项目级(CLI 对 --cwd 显式拒绝;scanner.ts 注释)。
理由:小工具是"用户级能力",绑定的是用户身份而不是某个目录;项目级能力由 Skills 承担(Skills 支持 project tier,走独立的 @finchtoys/skills CLI)。两套系统刻意分工:小工具 = 可执行代码能力,Skills = 提示词/流程资产。避免一个包同时出现在两级目录造成版本混乱。
4. 与「时空可组合性」论文的对照
论文《A Programming Paradigm for Spatiotemporal Composability》(北大 & DeepSeek-AI,2026-08-13 草案)把动态组合的两个正交维度形式化:时间可组合性(卸载时完全撤销组件副作用)与空间可组合性(依赖出现/消失/替换时自动调整),并给出两个核心机制:可逆效应(revertible effects,每个上下文变换携带运行时追踪的逆函数)与响应式共效应(reactive coeffects,上下文变化按组件依赖规范通知并驱动启停)。实现为元框架 Cordis,DeepSeek Harness 基于它构建。
本节不是学术评审,而是用论文作为一面镜子,检验 Finch 的设计直觉在形式化框架下的位置——哪些已经站住了、哪些只是骨架。
4.1 概念对照
| 论文概念(出处) | Finch 对应物 | 对应程度 | 差距 |
|---|---|---|---|
| 可逆效应(§3.1) | ctx.subscriptions + Disposable;主进程 record.disposers | ◐ 手工版 | 逆函数由作者手写,未登记即泄漏;无执行期自动捕获 |
| 响应式共效应(§3.2.2) | CapabilityBroker + broadcastAvailableCapabilities | ✗ 查询版 | 只更新 has() 视图;消费者不被通知、不自动停用/重载 |
| 统一上下文 Γ∞(§3.3.1) | 每扩展一个 ctx 能力面(finch.d.ts) | ◐ 能力面 | ctx 是"能力集合"而非"可恢复状态容器";无父子上下文树 |
| 组件 = (d, p, e)(§4.1 Def 43) | manifest requires/provides + activate(ctx) | ◐ 同构声明 | p 的强制仅限 capabilities API,普通副作用写入无 p 约束 |
| fiber 状态机 + 惯性过渡(§4.3 Alg 5) | enable/disable + restartActive(ids?) 独立重启 | ✗ 无状态机 | 无 LOADING/UNLOADING 中间态、无"卸载先等依赖者"、无 target 概念 |
| 声明式配置调和(§5.2.1 Def 74) | manifest + extensions.json;reload() 幂等 reconcile | ◐ 目标状态 | 无 entry 字段级 diff(id/url/isolate/intercept/config/disabled 最小化操作) |
| HMR 事务重载(§5.2.2 Alg 8-10) | restartActive 裸切换,无备份无回滚 | ✗ 缺 | activate 失败即功能缺失;无开发期文件 watch |
| 系统边界与补偿(§6.1) | shell.trashItem 卸载(service.ts:1599) | ◐ 补偿实例 | 运行期副作用无边界/补偿模型 |
| 沙箱(§6.3) | fork 子进程隔离(hostClient.ts:186) | ◐ 进程级 | 语言级强制薄弱(见第 5 章) |
| interception 能力衰减(§6.3 Def 30-31) | 无 | ✗ 缺 | 无法"给社区扩展只读 DB"这类运行时衰减 |
| service broker / 多提供者(§6.2) | CapabilityBroker 单 provider(后注册覆盖) | ✗ 缺 | 换 provider 会扰动消费者;无负载均衡/滚动更新模式 |
4.2 三个本质分岔
分岔一:副作用追踪是"登记式"还是"捕获式"(时间维)。
Finch 的清理等价于"每个注册动作返回清理函数,作者记得 push 进 subscriptions"。论文的清理等价于"任何通过 ctx 的状态变更自动登记逆函数,卸载时按 LIFO 自动恢复"(§3.1,ctx.effect 是唯一变更通道,Alg 1)。差异后果:Finch 中扩展若绕过 ctx 改共享状态,deactivate 不感知;论文中卸载语义是结构性保证而非作者纪律(§5.3:"even an inexperienced author obtains ordered cleanup…without writing an uninstall path")。
分岔二:依赖管理是"主动查"还是"被通知"(空间维)。
Finch 的 capability 消费者激活时查 has(),provider 消失后只能等下次调用报错;论文的 notify → refresh 让上下文每次变化都按依赖规范分类,provider 进入 UNLOADING 的瞬间依赖者就开始 teardown,且 unload 会等待依赖者到达 INACTIVE(§5.1.3 Alg 5)——"依赖先于卸载被撤走"的顺序纪律。Finch 缺这整条链路。
分岔三:更新是"事务"还是"裸切换"(维护维)。 论文 HMR 三阶段(分类/检测/事务回滚)保证系统永不进入半重载状态。Finch 的重启粒度已经不错(按 id 独立、幂等 reconcile),但每次重启仍是"停旧→起新"裸切换:无备份、activate 失败不回滚。
4.3 结论
Finch 的扩展理念在"插件宿主"层面已经达到甚至超过论文所批评的行业基准(VSCode 式共宿主、无法热卸载、依赖机制形同虚设):进程隔离、逐扩展启停、Disposable 纪律、声明式依赖骨架。论文的价值在于把这些工程直觉形式化并自动化。差距不在理念,在"保证"——Finch 目前靠作者纪律和进程兜底,论文把它变成"不做就不会错"。第 7 章路线图即按此方向收敛。
5. 安全模型
5.1 分层信任
| 层 | 机制 | 状态 |
|---|---|---|
| 安装层 | manifest 权限审查(filesystem/network/shell/secrets/oauth/sessions) | 展示 + 持久化(grantedPermissions) |
| 执行层 | 进程隔离(fork 子进程) | 强制 |
| API 层 | oauth provider 白名单(hostProcess.ts:775)、secrets key 白名单、capability 与 manifest 一致性(hostProcess.ts:820-864) | 强制 |
| 工具调用层 | 扩展工具与一方工具同管线,autoAllow: false(runner/extension-tools.ts) | 强制 |
| 数据层 | ctx.secrets 走 Electron safeStorage 加密,明文回退被显式拒绝(secretStore.ts) | 强制 |
5.2 工具调用权限
扩展工具经 extensionToolToRegistration 进入 Finch 内部工具注册表,与一方工具走完全相同的调度/权限管线:模型调用工具时按用户设置的权限模式裁决(默认模式逐次确认;acceptCalls 模式下普通扩展工具放行、高风险工具仍要求确认)。来源(provenance)通过 ToolSource 与规范化名 extension:<id>.<name> 携带,权限与 UI 分组都跟随来源。
5.3 已知薄弱点与边界
- fs/network/shell 权限未执行期强制:子进程是普通 Node 环境,
require('fs')可直接读写任意路径。对可信小工具这是可接受的信任模型;对社区生态,需要 P1-6(fs 路径白名单)乃至 utilityProcess sandbox。 - 无 interception:无法对同一依赖按调用方衰减能力(论文 §6.3 的 interception 表)。安全路线是声明式权限 → 白名单强制 → interception 的渐进路径。
- 卸载数据残留:
extension-data/<id>不随卸载删除(隐私权衡,P1-5 提供选项)。 - 依赖链风险:小工具自包含(不装第三方依赖树),但其内部 npm 依赖由作者自己构建进产物——供应链审查范围 = 包产物本身,这也正是"直下 tarball + doctor 检查"流程存在的原因。
6. 运维与发布
6.1 安装
- CLI:
npx @finchtoys/minitools add <pkg>(或本地 zip / 目录)。直接从 npm registry 下载 dist.tarball,写.finch-idsentinel(npm 包名派生 id)与.plugins-lock.json(source / installedAt 记录)。 - GUI:Toolcase 中从目录安装(
installFromDirectory,service.ts:1286)或从社区目录搜索安装。 - 作用域:personal(默认,
<FINCH_AGENT_HOME>/.finch/extensions/)与 global(~/.finch/extensions/);无 project 级(见 3.9)。 - 官方 bundled 扩展:随 App 打包,
bundledSeededVersion门控在新安装或 App 升级时(重新)部署——同一 App 版本内卸载官方扩展保持卸载状态,不会被重启覆盖(state.ts)。
6.2 启停
- 默认禁用;enable 后
reload()激活。disable 只 deactivate,保留信任记录与数据。 - 停用流程(
deactivate,service.ts:1223):宿主优雅 deactivate(5s 超时)→ 终止子进程 → 逐个跑 disposers → 注销工具/搜索提供者/图标包/capabilities → 关闭扩展的 Canvas 窗口 → 广播能力视图刷新。 - 重启:
restartActive(ids?)按 id 独立重启;MiniTool reload省略 id 时重启全部活跃扩展。
6.3 更新与回滚
- 检查:主进程后台轮询 npm registry(按 locale 选官方/镜像源),
autoUpdate开关控制是否自动安装。 - 更新:CLI
update或 App 内更新:下载新 tarball → 覆盖目录 → 重载该扩展。 - 回滚:当前无自动回滚。新版本 activate 失败时错误展示在扩展详情页,但旧版本已被 deactivate。事务化重载是 P0-3。
6.4 健康与诊断
- 宿主子进程 stderr 统一转主进程日志与扩展控制台面板(
consoleService.ts);非预期退出打extensionHost.exitedUnexpectedly事件。 npx @finchtoys/minitools doctor <dir>:发布前诊断清单(manifest 字段校验、入口存在性、权限声明、本地化完整性等)。
7. 路线图
按"收益/成本"排序。P0 直接对应第 4 章的两个本质分岔 + 事务重载;改动集中在现有模块,不需要重写架构。
P0-1 自动效应追踪:卸载即回滚(宿主内部状态)
把 ctx.subscriptions 的"作者自觉"升级为"宿主兜底":
ctx.storage/ctx.settings/ctx.secrets加写时快照:activate 时记录初始值,deactivate 时生成差异恢复函数(先覆盖最常见的隐性副作用;对应论文"任何 ctx 变更都是 effect")。- 新增
ctx.effect(callback)(兼容层):执行 callback,把每次返回的 dispose/逆函数按 LIFO 折叠进上下文级 accumulator;deactivate 时自动执行。现有ctx.subscriptions保留并等价为 accumulator 的一部分,老扩展零迁移。 - 主进程侧
record.disposers/toolDisposers清理顺序改为严格 LIFO 并记录日志,使停用顺序可审计。
边界:只承诺"宿主内部状态";文件系统/网络等外部位置只能补偿不能真回滚(论文 §6.1)。
P0-2 响应式 capability 通知:provider 变化联动消费者
CapabilityBroker维护consumers: Map<capability, Set<extensionId>>(信息来自 manifestrequires.capabilities,已在hostProcess.ts:844门控处存在)。- provider 注册/注销时通知相关消费者宿主:消费者执行
refresh()——所有 requires 满足则激活,缺失则停用(或降级)。对应论文 Alg 3notify+ Alg 5refresh。 - 卸载顺序纪律:deactivate provider 时先通知依赖者、等其完成 teardown,再执行 provider 自身清理(论文 §5.1.3 "unload 先 drain dependents")。
P0-3 事务化重载/更新:备份 + 失败回滚
在现有按 id 独立重启(restartActive([id]))之上补事务包装:
- 更新前把旧目录复制到临时备份(
cache/minitools/已存在); - activate 失败时恢复备份目录 + 清理失败残留 + 重载;
- 多扩展更新逐个提交,成功才继续下一个(对应论文 Alg 10 的
backup/restore_caches语义); - 开发期 HMR(可选):
fs.watch监听dist/变化 → 对该扩展执行同样的单扩展事务重载。论文 §5.2.2 指出:fiber 模型下 HMR 无需作者标注 accept 边界——Finch 的 Disposable 模型同理。
P1-4 引入 provider 身份
给每次 capability provider 注册分配单调递增 uid,随能力视图广播;消费者缓存"我依赖的 provider uid",变化时即使值相等也触发 refresh。解决"换了 provider 但值相等"的误判(论文 §4.1 fiber uid 思想),为"换实现不扰动消费者"铺路。
P1-5 数据清理选项
卸载对话框增加"同时删除该小工具的本地数据(storage/settings/secrets)"选项,默认保留(恢复友好)。若 P0-1 落地,可顺带输出"本扩展曾修改的宿主状态清单"。
P1-6 权限强制化(渐进式)
优先做 fs 路径白名单(宿主进程入口包装 fs 访问,限定 storagePath 与授权目录),网络/壳命令留待后续;或评估 Electron utilityProcess + sandbox。让"声明式权限"从展示变成约束。
P2-7 声明式配置调和(远期)
把"当前加载了哪些小工具、各自配置"收敛为可 diff 的声明式状态(论文 Def 74 entry + 字段级最小化操作:id/url 变→重建,config 变→仅重载配置,disabled 变→仅启停),取代"开关 + 幂等 reconcile"。建议在 P0-3 的单扩展事务重载落地后自然演进。
明确不做的部分
- 统一上下文 Γ∞ 的完整复刻(论文 §3.3):ctx 是能力面,"一切状态都经 ctx 且可逆"会伤及桌面宿主灵活性;只采纳其可逆性纪律(P0-1)。
- 全量形式化/元理论(论文 §4.4):工程系统按需借鉴机制,不引入演算证明。
- fiber 级多实例:Finch 每扩展一进程,多实例成本高且桌面单用户场景少,保留单实例。
8. 附录:主要事实来源
packages/minitool-api/finch.d.ts— MiniToolContext / ExtensionContext 类型契约(subscriptions、tools、composerActions、capabilities、storage、secrets、oauth、sessions、ui、i18n、settingsMenu、canvas)src/main/services/extension/scanner.ts— 清单解析:manifestVersion=1(28 行)、.finch-idsentinel(35 行)、id 策略(65 行)、finch.json 优先级、minVersion 门控、personal 覆盖 globalsrc/main/services/extension/service.ts—reload()幂等 reconcile(672 行)、激活(902 行起)、deactivate(1223 行)、restartActive(ids?)(1248 行)、installFromDirectory(1286 行)、setSettings单扩展重载(1586 行)、uninstall→trash(1599 行)src/main/services/extension/hostClient.ts— fork 子进程(186 行)、deactivate→terminateGracefully、executeTool 超时src/main/services/extension/hostProcess.ts— oauth 白名单(775 行)、capability provide/get 门控(820-864 行)、storage.json/settings.json(711/731 行)src/main/services/extension/capabilities.ts— CapabilityBroker(register/unregister/has/getVersion/invoke)src/main/services/extension/state.ts— 默认禁用、extensions.json、autoUpdate、grantedPermissionssrc/main/services/extension/secretStore.ts、settings.ts、paths.ts(extension-data,113 行)src/main/services/runner/extension-tools.ts— 扩展工具autoAllow: false、与一方工具同管线packages/minitools/bin/minitools.mjs— CLI:tarball 直下不跑脚本、.finch-id、cmdRemove、cmdEnable权限提示、doctor- 对照论文:《A Programming Paradigm for Spatiotemporal Composability》,北大 & DeepSeek-AI,2026-08-13 草案(
github.com/cordiverse/paper);Cordis 实现见论文 §5;DeepSeek Harness 见github.com/deepseek-ai/deepseek-harness - 配套对比分析:
docs/analysis/spatiotemporal-composability-vs-finch-minitools.md(含逐项差距与更细的事实行号)
评论