| 1 | # Extension Runtime v2(时空可组合性) |
| 2 | |
| 3 | Reasonix 插件/运行时 v2 模型说明。英文版:[EXTENSION_RUNTIME_V2.md](./EXTENSION_RUNTIME_V2.md)。 |
| 4 | |
| 5 | ## 协议与 Manifest |
| 6 | |
| 7 | - 插件清单:仅接受 `apiVersion: reasonix.io/plugin/v2`(拒绝 v1 与无 apiVersion 的 native)。 |
| 8 | - 兼容边界:不提供 v1 双读或自动迁移。扩展 Manifest v1 从未公开发布, |
| 9 | 因此 v2 是首个受支持的 Runtime Manifest。 |
| 10 | - 扩展线协议:`reasonix.extension.v2`(major 2)。 |
| 11 | - Handshake 的 `provides` 不得超过 manifest 的 provides 上限。 |
| 12 | |
| 13 | ## 运行时模型 |
| 14 | |
| 15 | ```text |
| 16 | RuntimeSnapshot = 不可变配置与依赖视图 |
| 17 | RuntimeSet / EffectScope = 一代 live 资源 |
| 18 | RuntimePlan = 旧 → 新 的转移(子图分类) |
| 19 | RuntimeOwner = 单个 session lineage 的 gate + receipt + stream/file 证据 |
| 20 | Controller = 已发布 generation 的消费者(admission 绑定 RuntimeOwner) |
| 21 | ``` |
| 22 | |
| 23 | 组件状态:`Inactive → Preparing → Active → Draining → Inactive`(或 `Failed`)。 |
| 24 | |
| 25 | ## 重载 |
| 26 | |
| 27 | - 优先 `boot.RebuildFrom(previousBuildResult, opts)`。 |
| 28 | - no-op / interceptor / UI / provider / MCP-only 走 **真子图 patch**(不进 `BuildRuntime`);`ReusedController` 时调用方不得 `Close` 旧指针。 |
| 29 | - Provider/MCP 子图:`WithLiveContributions` 刷新 interceptor/provider/UI 目录;**system prompt + tool schemas + CacheHash 保持稳定**(只滚 backend)。工具 schema 改名仍需全量 rebuild。 |
| 30 | - 窄路径为 **stage → ready → commit**(失败原子): |
| 31 | - **Stage**:启动/收养 sidecar,构建下一代 dispatcher/resolver;**不**安装 stream router,也**不**对 UI hub 调用 `BindGeneration`。 |
| 32 | - **Ready**:等待 sidecar 就绪。 |
| 33 | - **Commit**:安装 stream router、绑定 UI generation、替换 controller 绑定,再 publish。 |
| 34 | - stage/ready/commit 任一步失败:`RollbackPlanStart` 回挂 Unchanged 客户端;stage 前的 stream router 继续消费 `stream/chunk` / `stream/end`。 |
| 35 | - **Stage 期间的 UI**:在 commit 之前仍绑定旧 generation。sidecar 在 handshake/ready 期间若用**下一代** generation 发 `host/ui/publish` 或 `host/ui/request`,会被当作 stale **丢弃**。协议约定:在 runtime generation 发布前,不要依赖 UI 可见性。 |
| 36 | - 迁移成功后:**发布** 新 generation,再 **排空** 旧 sidecar。 |
| 37 | - Drain 超时先 cancel 已注册工作(被替换的 controller、**host provider stream**(`HostStreamRegistry`)、extension provider stream、StableProxy/MCP 在途),再写 `drain-timeout` receipt。 |
| 38 | - Draining 中的 Controller 拒绝新 turn(`turnDroppedDraining`)。 |
| 39 | |
| 40 | ## 诊断 |
| 41 | |
| 42 | ```bash |
| 43 | reasonix doctor runtime |
| 44 | reasonix doctor runtime --json |
| 45 | reasonix plugin doctor <name> |
| 46 | ``` |
| 47 | |
| 48 | 输出组件状态、计划、effect receipt、可恢复性、lifecycle metrics,以及进程内的 |
| 49 | `runtimeOwnerFallbacks` 计数。产品启动路径会绑定独立 owner;该值非零表示仍有 |
| 50 | 兼容路径落到了共享默认 owner,需要补齐显式接线。 |
| 51 | 计划诊断拆分两个事实:`prefixChanged` 在构建完成后比较新旧 snapshot 的 |
| 52 | `CacheHash` 得出;`providerChanged` 表示 Provider capability 的新增、删除或 |
| 53 | 重载。因此仅滚动 Provider backend 且 system prompt、tool schemas 字节不变时, |
| 54 | 诊断为 `prefixChanged=false, providerChanged=true`。 |
| 55 | |
| 56 | ## Effect receipt |
| 57 | |
| 58 | 不可逆外部动作记入当前 `RuntimeOwner` 的 receipt store。独立 session |
| 59 | 不共享 publish/drain 状态或恢复证据。Recovery **不得** 对 irreversible |
| 60 | 声称 rollback 成功;使用 owner 级的 `AssessRecoverability(generation)` / |
| 61 | `DecideResume(generation)`。 |
| 62 | |
| 63 | Receipt ledger **只支持进程内 rebuild/resume**,不会持久化;进程崩溃后的恢复 |
| 64 | 不在本次 Runtime v2 的范围内。内存最多保留最近 32 个 generation、每代 256 条 |
| 65 | receipt。淘汰时也会释放关联的文件 prior 字节,并将该代证据标记为已截断;证据 |
| 66 | 不完整时,诊断不会声称 clean rollback。 |
| 67 | 消息发送去重与 receipt 使用同一保留周期:消息 receipt 被淘汰时会同步释放对应的 |
| 68 | `(generation, messageID)` 键,避免形成第二份无界账本。文件 prior 每条最多 8 MiB、 |
| 69 | 每个 `RuntimeOwner` 合计最多 32 MiB;超过任一上限会记录 `prior_truncated`,恢复 |
| 70 | 判断按保守策略处理,不会声称 clean rollback。 |
| 71 | |
| 72 | - Provider stream open 会记录 `provider-submit:<id>`(不可逆)。 |
| 73 | - 已完成的 Provider stream 会注销 drain 回调;generation gate 只保留仍在途的 stream。 |
| 74 | - Drain 超时 force-expire 记录 `drain-timeout:<gen>`。`ScheduleDrainWatch` 仅在确有 |
| 75 | generation 正在 drain 时启动,并将快速连续 publish 合并为每个 owner 一个 watcher; |
| 76 | doctor sweep 仍作为兜底。 |
| 77 | - 每个 owner 最多保留最近 256 个 late-cancel 过期 generation 标记。 |
| 78 | |
| 79 | ## EffectScope 归属 |
| 80 | |
| 81 | 一代运行时的 live 资源挂在 `RuntimeSet` / `EffectScope`: |
| 82 | |
| 83 | | 资源 | 跟踪方式 | |
| 84 | | --- | --- | |
| 85 | | Sidecar manager | Activator 中 Cancelable effect | |
| 86 | | UI hub 绑定 | `TrackUIHub` | |
| 87 | | MCP plugin host | 清单 + `session-resources` dispose | |
| 88 | | LSP manager | `TrackWatcher` 清单 | |
| 89 | | Session cleanup 链 | `TrackControllerCleanup` | |
| 90 | | Provider 已提交 | `RecordProviderSubmit` receipt | |
| 91 | |
| 92 | ## 验收对照 |
| 93 | |
| 94 | | 规格验收项 | 状态 | |
| 95 | | --- | --- | |
| 96 | | 每个资源与 session lineage 有明确 owner | 完成(`RuntimeOwner` + sidecar/MCP/UI/LSP/stream/file receipt 接线) | |
| 97 | | 激活失败不泄漏 / 不 publish | 完成 | |
| 98 | | 缺依赖 → Inactive 诊断 | 完成(结构化 missing requirement + Unavailable) | |
| 99 | | 真子图 rebuild(不进完整 BuildRuntime) | 完成(None/Interceptor/UI/Provider/MCP) | |
| 100 | | Publish/drain 顺序 + drain 取消 | 完成 | |
| 101 | | 不可逆永不标 rollback 成功 | 完成(recovery `AssessRuntimeResume`) | |
| 102 | | 原生 Runtime Manifest 严格 v2-only | 完成(无 v1 双读 / 自动迁移) | |
| 103 | | 缓存稳定性守卫 | 完成 | |
| 104 | | Doctor 解释 inactive + resume | 完成(CLI + desktop RuntimeDoctor UI) | |
| 105 | | 无外部运行时依赖 | 完成 | |
| 106 | |
| 107 | ## Phase 5 决策 |
| 108 | |
| 109 | - **兼容性**:v2 是首个受支持的扩展 Runtime Manifest;install/doctor/boot |
| 110 | 不双读 v1,也不自动迁移。 |
| 111 | - **性能**:见 [EXTENSION_RUNTIME_V2_PERF.zh-CN.md](./EXTENSION_RUNTIME_V2_PERF.zh-CN.md)。 |
| 112 | - **README / SDK**:示例 manifest 在 `sdk/go/examples/*`;wire 类型以 protocol gen 为准。 |
| 113 |