fix: integrate updater downgrade guard
Some checks failed
Electron E2E / Electron E2E (macos-latest) (push) Has been cancelled
Electron E2E / Electron E2E (ubuntu-latest) (push) Has been cancelled
Electron E2E / Electron E2E (windows-latest) (push) Has been cancelled

This commit is contained in:
2026-08-26 16:41:53 +08:00
parent 48a9189939
commit 2eaf0450b8
13 changed files with 276 additions and 47 deletions

View File

@@ -12,11 +12,12 @@
| 提交绑定 | 云端成功上传响应 | Main → submission binding v2 | 只持久化成功的 app/version/review/hash;落盘失败返回固定告警但不反转提交 |
| 运营发布 | Works Square 审核与交付 | 公共 `play_url` | 客户端只消费服务端发布结果;真实合同校验 → OSS/CDN 生产链仍待整链验收 |
| 可信发布门禁 | source+built+artifact contract | 服务端逐字节重算/合同校验 → 不可变 Release 固化 → 人工审核 | 服务端不运行项目 Vite;仍是不可绕过权威,未来 runtime 强门禁需可信 verifier |
| AI 编程 Session 执行提交 | ChatPanel 当前 Agent / Session | Renderer per-Session run state → deferred provider sync → Host API → Main manager FIFO → per-project Agent FIFO → runtime/config + Agent readiness → OpenCode message / command / summarize | 新建 session 已知为空时不等待空历史;普通 prompt、项目命令和压缩不重发模型 override;不同 Session 没有 reply-duration 全局锁。provider/runtime 或 Agent 未应用时以 typed `409`、`promptSent:false` 在执行前终止,且不自动重启、reload 或 dispose 共享 runtime |
| AI 编程 Session 模型切换 | 页面模型选择器或 `/models` / `/model` | Renderer Session model action → Host API → Main OpenCode client → `POST /api/session/{sessionID}/model` | Main client 将内部 `modelID` 映射为 OpenCode 1.18.9 wire `ModelRef.id`;只改变现有 Session 的后续 turn 模型并从 Session metadata 恢复,不写伙伴默认模型、provider 配置或 runtime freshness,也不要求 runtime restart |
| AI 编程启动确认 | Host POST、OpenCode HTTP polling 与 Session events | Renderer `{sessionId, runToken}` watchdog → `sessionRunStates[sessionId]` | Host 接受与 user transcript 不算 ACK;busy/retry、assistant、question、permission 或 typed terminal event 才确认/结束启动。10 秒无确认只终止该 Session 为 `SESSION_START_UNCONFIRMED`,清理其队列且不自动重放 |
| AI 编程上下文压缩 | OpenCode compaction Part / `session.compacted` / `/compact` | Renderer Store → per-session transcript state → Chat mixed timeline | 手动请求先创建 immutable UI event;native identity 负责归并与回放去重;完成只更新压缩项,真实 idle 才结束 run 和释放队列 |
| AI 编程模型代理错误 | OpenCode provider 请求 | Main Host AI proxy → Works 模型上游 | 配额耗尽保持独立终止态;只有明确的分组上游饱和才投影为 OpenCode 终止状态,通用限速仍保留 `429` |
| AI 编程 Conversation 创建与准备 | 当前 project / Agent / 本地 Conversation | Renderer 本地创建 → `/api/coding/*` → Main schema-v2 store → target Pi worker/session lazy prepare | Composer 与 worker 准备解耦;首次 Conversation 可立即编辑。未解析模型必须先验证并持久化,再打开首个 worker;旧 OpenCode 会话只备份,不继续运行 |
| AI 编程 prompt 与流式投影 | 当前 Conversation draft / attachment refs | Renderer mutation → Host `202` acceptance/dedupe → Main target worker → Pi events → Snapshot + `patch-batch` SSE → Renderer reducer | 每个 Conversation 独立 generation/seq;gap 只恢复目标 Conversation。accepted/uncertain mutation 不自动重放,权威 Snapshot 到达前保留相同 optimistic node identity |
| AI 编程模型与思考强度切换 | 当前 Conversation | Renderer → `/api/coding/conversations/{id}` → Main validate/persist → target runtime | 同账号已解析模型复用 `set_model`;跨账号模型等 active run settled 后只重建目标 worker,旧凭据不会进入新 generation;切换失败不销毁可继续使用的旧 worker |
| AI 编程上下文压缩 | Renderer compact action / Pi compaction events | correlated compact RPC → target projector → Snapshot/Patch timeline | Pi `0.84.2` 的手动 compact 不发 `agent_settled`;RPC success 或权威 compaction failure 终结 compact 并 exactly-once 释放对应 ownership,不提前结算普通 prompt |
| AI 编程 Provider 错误 | Pi worker Provider 请求 | selected worker env → Main AI proxy → Works 模型上游 → projector safe error | 确定性的 Works user-context 缺失使缓存 gateway credential 过期、返回非重试认证失败且不重放请求;Renderer 只看到固定 Provider-auth 提示,不能把它解释为 Pi 崩溃 |
| AI 编程子 Agent | parent `subagent.v1` tool call | explicit extension → authenticated Main bridge → scheduler → ephemeral Pi child | child 并发最多 4、单次最多 8、禁止递归;父子共享总进程预算 8,coding child 与 parent 共用项目 write lease,父 abort/crash/generation 失效必须清理 child 与 permit |
| 客户端更新检查 | 设置页 | Renderer update store → IPC → Main AppUpdater → 目标 feed | Main 记录并重抛原始错误;Renderer 只显示去重、脱敏的单条提示,稳定源缺包不伪装为最新版 |
| 设计会话创建/切换 | AI 绘画页面或侧栏 | Renderer API → Main → Workspace Conversation API | 新会话属于现有 Workspace;读取独立消息、Brief、Quote 和 `turnRevision` |
| 设计消息与确认 | 当前 Conversation | Main ↔ 持久 Agent Gateway Session WebSocket → Conversation 快照 | `command.submit`、Run 与设计事件共用连接;传输失败才以同一幂等 ID 回退 REST;结构化业务错误不重试且未知文本由 Main 脱敏;请求和流式结果同时绑定 Workspace + Conversation |
@@ -45,12 +46,13 @@
- 图生图参考图与视频首帧都先归一为当前 Workspace 的 Asset;从作品选择时复用生成结果 Asset,本地选择时先走既有上传接口,再把唯一 Asset ID 随 Turn 提交。选择或上传成功后关闭选择器。
- 本地开发适配器将旧单会话 schema v2 原子迁移为带默认 Conversation 的 schema v3;打包应用不使用该本地适配器作为云端失败回退。
- 注销和退出会关闭本地事件流并清除本机 Conversation Session-id 缓存;服务端持久 Session 保留,下一次访问从 Conversation API 重新读取。
- AI 编程 Store 的 `sessionMessagesBySessionId` own-key 是加载状态契约:键缺失表示历史未知,存在且值为 `[]` 表示已知为空。只有后者可使用不读取历史的快速选择;普通历史会话选择继续刷新消息。
- AI 编程 Store 的运行态、错误、startup deadline 和内部 pending queue 按 Session 持有;top-level error 只表示真实全局错误。任何不确定的远程失败只清理对应 Session 的旧队列,手动重试不得复活失败前已排队的 prompt。
- OpenCode Session 持有现有会话的 active provider/model;伙伴 `model` 只在创建新 Session 时提供默认值。页面与 slash command 通过同一 native Session endpoint 切换,后续执行和压缩不得用伙伴默认值覆盖该选择。
- Main 以 manager identity + runtime generation provenance 持有 runtime-config latch,并按 Agent id 持有 desired/applied 内容状态与本 generation 已知 id。owned `fresh` generation 中首次出现的唯一 id 可在 live `/agent` 暴露后应用,且不会阻断已有未变 Agent;同 id 编辑、删除后重建、live id 缺失以及 attached/unknown generation 均不能越过门禁。托管本地 AI proxy 的 fresh runtime config 直接使用当前进程 Host API token;把该已生效 token 写回 provider storage 不构成 runtime freshness 变化,attached/unknown generation 或 timeout/partial persistence 仍保持 sticky pending。
- AI 编程压缩状态由 transcript 中的 `compactionsById` / `compactionOrder` 单一持有。运行中 hydration 保留 manual pending identity 并用 native Part 合并;completed 不得回退为 running。`session.compacted` 不是 run idle,不能据此释放 queued prompt;失败、中止或 runtime generation 变化只清理对应未完成事件。
- Main Host AI proxy 可为固定 OpenCode 重试契约做窄化的内部状态投影:配额耗尽投影为 `402`,明确上游分组饱和的 `429` 投影为终止 `400`,其他 `429` 原样保留;升级 OpenCode 时必须重新验证该契约。
- project、Agent、Conversation schema v2 与每条 Conversation 的 opaque Pi session binding 由 Main 原子持久化。旧 v1 先做时间戳备份再迁移;旧 OpenCode Session 不进入新 runtime,也不删除无关 `.opencode` 内容。
- Renderer Store 只拥有产品中立 Snapshot、Patch、optimistic request 与 project-scoped metadata。每条 Conversation 的 generation/seq 独立;stale generation 丢弃,gap/reconnect 只为目标 Conversation 取 Snapshot 并重放严格连续的缓冲 tail,不重放 mutation。
- Main 为每条 active/warm Conversation 持有一个 persistent Pi worker/session、Provider/resource revision 和 selected-account secret env。运行中 stale revision 保留本 run snapshot,settled 后重建;其他 Conversation 不受影响。
- top-level worker 并发上限 4、warm idle LRU 上限 4、child 并发上限 4,parent/child 共用 FIFO process budget 8。同项目 coding mutation 共用 write lease;read-only child 可旁路写锁。
- prompt/compact 的 RPC confirmation timeout 只进入 uncertain,不能释放 run permit、process ownership 或 Main background lease。迟到 success/failure/exit/abort 必须单调且 exactly-once 收敛;隐藏页面超过 grace 也不得停止 active/uncertain run。
- AI 编程压缩状态属于目标 Conversation Snapshot。Pi `0.84.2` 手动 compact 的 correlated RPC success 可代替缺失的 `agent_settled` 终结 compact;普通 prompt 仍只由权威 terminal/settled 结束。
- Main Host AI proxy 持有凭据刷新和错误分类。确定性 Works user-context 缺失必须失效缓存 credential 并 fail fast;真实认证、endpoint/proxy/rate-limit、Provider variation、真实并发与跨 worker 凭据隔离仍是用户明确接受但未验证的风险。
- Main AppUpdater 持有 feed、原始异常、下载和安装状态;设置页只消费状态投影。一次 electron-updater `error` 事件覆盖的并发检查不会在 Renderer 重复发错,但独立的后续检查仍有自己的报告生命周期。
- Robot V1 引导状态只在 Renderer 当前进程内保存,不持久化 Wi-Fi 凭据、activation code 或 Binding operation ID。相同进程内的模糊 Binding 重试复用 operation ID;无效码或重启后必须取得新码并使用新 operation ID。
- Robot Hotspot Module 只在 Main 内保存最近一次扫描的短效、不透明候选快照。新的扫描、clear、60 秒过期或进程重启使旧 candidate ID 失效;Renderer 关闭/重开向导以 generation 防止旧扫描/连接结果回写。
@@ -60,7 +62,7 @@
- Works Square 项目创建、版本上传、构建状态与 Release 状态 API。
- Works Square `/api/auth/me` 模块权限 API;Electron Main 持有 Bearer 并只向 Renderer 投影 `programming`/`design`/`learning`/`robot` 对应的四个布尔值。
- 本机 Host API 的发布路由;发布路由要求 Renderer capability。
- 本机 Host API 的 OpenCode session、history 与 prompt 路由,以及 Main-owned AI 模型代理。
- 本机 `/api/coding/*` project/Agent/Conversation、Snapshot/patch-batch SSE、prompt/compact/fork/recover、attachment/file/interaction/diagnostics 路由,以及 Main-owned AI 模型代理。
- Main-owned electron-updater IPC 与 Works Square 平台/架构稳定 feed;正式安装产物发布不由 Renderer 控制。
- 服务端安全投影后的公共 `play_url`;只接受同源 HTTPS、精确 App 路径和可信版本状态。
- Works Square Workspace/Conversation API、每个 Conversation 的持久 Agent Gateway Session、单次 WebSocket ticket、双向命令/事件帧与幂等 REST 传输回退。
@@ -70,4 +72,4 @@
## Last Updated
2026-08-21
2026-08-26

View File

@@ -15,16 +15,15 @@
| `electron/services/project-packager.ts` | 受控项目扫描、静态 ZIP 生成和敏感/历史控制文件排除 | 只允许可发布 `ProjectType`,不提供 Compose 或手工 ZIP 路径 |
| `electron/services/works-submission-binding.ts` | submission binding v2 持久化与旧 schema 迁移 | 旧中间态终止为 `legacy_retired`;文件名暂作安装兼容 |
| `electron/api/works-play-url.ts` | 公共播放 URL 的共享安全校验 | 公共 `play_url` 必须同源 HTTPS 且精确匹配 App 路径 |
| `src/pages/Chat/OpencodeChatPanel.tsx` | AI 编程 Agent 选择、首次 session 创建、per-Session 模型选择、prompt 提交与消息/压缩混合时间线 | 页面模型选择器与 `/models` / `/model` 共用 OpenCode Session 切换动作;伙伴模型只作为新 Session 默认值,选中 Session 只投影自己的 run error |
| `src/lib/opencode-session-state.ts` | OpenCode 会话消息与上下文压缩时间线的规范化、hydration 和事件归并 | 压缩 UI identity 不变;native Part/event identity 用于回放去重,completed 状态不可降级 |
| `src/stores/opencode.ts` | OpenCode runtime、session、消息缓存、per-Session 模型/提交/启动确认与压缩生命周期 | Session 列表 hydration 恢复当前模型;每个 Session 独立持有 model/run/error/queue,10 秒 run-token watchdog 只终止对应 Session 且不自动重放;`session.compacted` 只完成压缩项,真实 idle 才释放 run 队列 |
| `electron/opencode/client.ts` | Makelore 内部 OpenCode DTO 与固定 runtime HTTP wire contract | 内部 `{ providerID, modelID }` 在 Session 模型切换边界映射为 OpenCode 1.18.9 `ModelRef` 的 `{ providerID, id }`;runtime 升级时复核 schema |
| `electron/opencode/provider-config.ts` | Main-owned OpenCode provider/env 配置构建 | 托管本地 AI proxy 在 owned fresh runtime 启动时使用当前进程 Host API token;持久化旧 token 不得覆盖本次 runtime 已注入值 |
| `electron/api/routes/opencode.ts` | Main-owned OpenCode Session 模型切换、execution acceptance、provider/runtime freshness 与项目 Agent 门禁 | Session 模型切换调用 OpenCode native endpoint,不写 provider/伙伴配置;fresh runtime 已生效 proxy token 的持久化不标记 stale,attached/unknown 与不确定写入仍 fail closed;message/command/summarize 共用 bounded manager FIFO,普通执行路径不自动 restart/reload/dispose |
| `electron/opencode/project-agent-runtime.ts` | 项目 Agent 逐 id desired/applied 内容状态、runtime generation provenance、live registry acceptance 与配置 mutation 串行化 | owned fresh generation 中首次出现的唯一 id 可在 live `/agent` 暴露后应用;同 id 编辑、删除后重建及 attached/unknown generation 仍 fail closed,用户修改的 retired Agent 文件不得被删除 |
| `electron/opencode/runtime-config-readiness.ts` | manager + generation scoped provider/runtime stale latch | timeout/partial persistence 保持 sticky uncertain 状态;只有成功显式 apply 后的合格 fresh generation 可解除,迟到 lease 不能改写 readiness |
| `electron/api/routes/ai-proxy.ts` | Main-owned 模型代理、凭据边界与上游响应投影 | 仅对明确上游分组饱和做终止态兼容投影,通用限速保持 `429` |
| `shared/opencode-error-details.ts` | OpenCode 上游饱和错误的窄化共享分类 | 不以通用 `rate_limit_exceeded` 单独判定饱和 |
| `src/pages/Chat/CodingChatPanel.tsx` / `CodingConversationTimeline.tsx` / `CodingComposer.tsx` | Agent/Conversation 选择、Snapshot-first 时间线、prompt/steer/follow-up、交互、压缩、分支与附件 UI | Composer 不等待 worker 准备;所有异步结果按 project/Agent/Conversation 所有权隔离,Renderer 不导入 Pi wire 类型 |
| `src/stores/coding-conversations.ts` / `coding-workspace.ts` | per-Conversation Snapshot/Patch、乐观提交、gap recovery 与项目级元数据 | generation/seq 严格递增;只恢复目标 Conversation,SSE 重连不重放 mutation |
| `shared/coding-conversation-*.ts` / `coding-project-contracts.ts` | Main/Renderer 共用的产品中立 Conversation、project tool、subagent 与 reducer 合同 | 这是唯一公共实现;Pi 事件、RPC 与 Provider 形状保持 Main-private |
| `electron/api/coding-composition.ts` / `electron/api/routes/coding-*.ts` | 唯一 Main-owned Coding composition 与 `/api/coding/*` Host API | 管理 project/Agent/Conversation、SSE、附件、文件、命令、交互和安全诊断;mutation 返回 202 acceptance 且按 request id 去重 |
| `electron/coding-projects/` | schema-v2 项目/Agent/Conversation 数据、原子持久化、旧 v1 备份迁移与项目文件能力 | 旧 OpenCode 会话只备份不续写;modified/unknown legacy Agent 文件先备份,valid v2 不读取旧 runtime 数据 |
| `electron/coding-runtime/conversation-service.ts` | 产品级 Conversation orchestration、模型/思考强度、fork/archive/recover 与运行时门禁 | unresolved Conversation 先验证并持久化模型再首次 prepare;已解析 Conversation 的模型切换复用 target runtime seam |
| `electron/coding-runtime/pi/{worker-pool,worker-process,rpc-client,session-registry,event-projector}.ts` | 一 Conversation 一 persistent Pi worker/session、严格 LF JSONL RPC、generation recovery 与 Snapshot/Patch 投影 | top-level cap 4、共享总进程预算 8、warm idle LRU 4;worker 替换只使目标 generation 失效 |
| `electron/coding-runtime/pi/{provider-config,provider-refresh,resource-loader}.ts` / `electron/api/routes/ai-proxy.ts` | Main-owned Provider catalog、secret/env 投影、资源 revision、一次凭据刷新与上游错误分类 | secret 不进入 argv/catalog/Renderer;确定性的 Works user-context 缺失使缓存凭据过期并 fail fast,不自动重放 mutation |
| `electron/coding-runtime/pi/{extension-host,subagent,write-lease}.ts` / `electron/main/background-lifecycle.ts` | 唯一显式 Makelore extension、交互、子 Agent、同项目写租约与后台运行租约 | child 并发 4、单次最多 8、禁止递归;active/uncertain run 不因页面隐藏或确认超时被回收,所有 stop/replacement 必须有原因 |
| `electron/main/updater.ts` | 目标 feed 解析、electron-updater 生命周期与原始诊断 | 缺少稳定 manifest 保持错误;事件/Promise 重复失败按检查实例去重 |
| `src/components/settings/UpdateSettings.tsx` | 更新状态、重试与用户可读错误展示 | 只显示一条简洁中文提示;技术诊断统一回退到本地化通用文案 |
| `shared/image-workspace.ts` | AI 绘画 Workspace、Conversation、Task、Asset 与事件共享契约 | Conversation 状态与 Workspace 任务归属必须分离 |
@@ -51,7 +50,7 @@
## Dependency Direction
- Renderer UI → Renderer API contract → Main Host routes → Main services → Works Square;Renderer 不反向读取 Main 凭据、文件系统或归档。
- AI 编程 Renderer per-Session model/run state → Host API → Main Session switch 或 manager-scoped execution acceptance → per-project Agent acceptance → OpenCode runtime;OpenCode provider 请求再经 Main AI proxy 访问模型上游。Renderer 不直接持有上游凭据或本地 runtime URL,Main 临界区只覆盖配置/请求 acceptance,不覆盖模型回复时长。
- AI 编程 Renderer product Snapshot/commands → typed `/api/coding/*` Host API → Main Coding composition → target `CodingConversationRuntime` → persistent Pi worker;Pi Provider 请求再经 Main AI proxy 访问模型上游。Renderer 不持有 Pi wire、凭据或本地 runtime URL。
- Project configuration 决定产品分流;Main release builder 生成 source/built/contract,服务端独立重算和校验决定发布安全,本地 `ProjectType` 不是授权结论。
- Built artifact preflight 检查最终上传的同字节快照,但客户端可被绕过且不产生可信 receipt;服务端仍是合同、摘要和不可变 Release 安全权威。
- Robot Renderer → typed AI hardware API → Main Host route → Robot Hotspot Module → Windows/macOS Adapter。云端 Binding 仍由 Main 代理;热点选择/连接移入页面,但家庭 Wi-Fi 凭据输入仍只留在固件 Portal,系统 Wi-Fi 保留为兜底。
@@ -65,10 +64,10 @@
- `electron/agent-browser/module.ts` 的预检必须继续拒绝外域访问、隔离临时 partition 并清理所有 view/listener;不能因本地通过而跳过服务端逐字节校验。生产 opaque-origin 行为不由该 loopback 检查覆盖。
- `works-cloud-deploy.json` 是兼容文件名;不得因名称重新引入自动部署协调语义。
- `runtime_url` 是一个客户端版本的迁移回退;删除前必须确认服务端和存量数据稳定提供 `play_url`。
- `ai-proxy.ts` 的上游饱和状态投影依赖当前固定 OpenCode 的重试语义和窄化错误文案;升级 runtime 或调整上游错误格式时必须复核,不能把所有 `429` 统一终止。
- OpenCode 压缩时间线依赖 native compaction Part、Session run token 与 runtime generation 的关联;hydration 必须保持 completed 单调,不能用 `session.compacted` 提前结束 run 或释放 queued prompt。
- OpenCode execution acceptance 同时涉及 provider persistence、runtime lifecycle、Agent config mutation 和 runtime HTTP。锁顺序必须保持 manager → project,所有等待与 HTTP 都使用同一 hard deadline/AbortSignal;timeout 后 lease 必须撤销,queued cancellation 不能让后续 mutation 绕过前驱。
- OpenCode Agent hot reload 没有 authoritative whole-instance quiescence oracle。owned fresh generation 可接受 live registry 新发现的唯一 id,但不得以 live 同 id 代替内容 freshness;同 id 编辑或删除后重建若要即时生效,必须先获得 upstream directory-scoped invalidation 或权威 quiescence seam,不能从 `/session/status` 推断 dispose/reload 安全。
- Pi RPC confirmation timeout is an uncertainty boundary, not permission to release run/process/background ownership. Late success/failure/exit/abort must converge monotonically and exactly once; no accepted or uncertain mutation may be replayed automatically.
- Pi manual compaction in locked `0.84.2` does not emit `agent_settled`; correlated compact RPC success or an authoritative compaction failure must terminalize the compact operation without prematurely settling an ordinary prompt.
- Parent and child workers share one FIFO process budget of 8. Child reservation may reclaim ready/idle or queued parent capacity without losing FIFO order, session binding, or generation ownership; stop/reclaim failure must release leases in `finally`.
- Provider/resource revisions apply before the next prompt for idle workers and after `agent_settled` for active runs. Same-account model changes may use `set_model`; cross-account changes rebuild only the target Conversation so old credentials never enter the new worker.
- `electron/main/updater.ts` 的稳定源错误归一化必须保持窄化:只识别 Works Square 对应 manifest 的 404,不得吞掉其他 feed/网络/签名错误;Renderer 的脱敏边界不能取代 Main 原始日志。
- 多 Conversation 事件处理必须区分对话快照与 Workspace 任务更新;不得用任务时间戳推进 Conversation 流水位,也不得让旧会话的迟到流覆盖当前会话。
- Quote 编辑、重报价、确认和项目删除都跨 Renderer/Main/Works Square。异步结果必须核对当前 Workspace + Conversation;删除当前项目时必须先使旧选择和事件流失效,再加载剩余 Workspace。

View File

@@ -18,6 +18,11 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展
| Play URL Projection | 校验服务端公共播放地址 | 只接受同源 HTTPS 和精确 `/apps/{app_id}/`;优先 `play_url`,`runtime_url` 仅一版本回退 |
| Built Artifact Preflight | 在上传前对最终 built snapshot 做桌面/移动 UX fail-fast | Main 使用临时 loopback origin 与 Electron WebContents/CDP;不使用 Playwright、不生成可信证明 |
| App Updater | Main 选择平台/架构 feed、驱动 electron-updater 并记录原始诊断 | Renderer 只接收状态和脱敏后的单条用户提示;稳定源缺包保持错误 |
| Makelore Code Workspace | project/Agent/Conversation schema v2、产品中立 Snapshot/Patch、Composer 与时间线 | Conversation 本地创建且不等待 worker;旧 OpenCode 会话备份后不再继续,Renderer 不导入 Pi RPC/event 类型 |
| Coding Host API & Composition | 唯一 `/api/coding/*` composition、202 acceptance/dedupe、SSE、附件/文件/交互/诊断 | Electron Main 拥有 project/Conversation 服务、选中目标、认证和错误脱敏;SSE 公开面只有 Snapshot 与 `patch-batch` |
| Pi Conversation Runtime | 每条 active/warm Conversation 一个 persistent Pi `0.84.2` worker/session | 严格 LF JSONL RPC、generation recovery、Snapshot hydration;top-level 并发 4、warm idle LRU 4、总进程预算 8 |
| Pi Provider & Managed Resources | Provider catalog、secret/env 投影、model/resource revision、Prompt/Skill/extension materialization | 凭据只进入选中 worker env;不扫描项目或用户的 `.pi/.agents/.codex`,不把 secret 放进 argv、catalog 或 Renderer |
| Pi Extension, Subagents & Lifecycle | 唯一显式 Makelore extension、UI interaction、ephemeral child、write lease 与 background run lease | child 并发 4、单次最多 8、禁止递归;active/uncertain run 不因页面隐藏或 confirmation timeout 被停止,replacement/stop 必须可解释并清理所有 ownership |
| AI Design Workspace | 保存项目身份、Conversation 列表、生成任务和资产 | 任务和资产在切换 Conversation 后继续可见 |
| AI Design Conversation | 保存消息、Brief、Quote、`turnRevision` 与服务端 Agent Session 绑定 | 同一 Workspace 内互相隔离;Session 由服务端持久化 |
| AI Design Image Source Picker | 当前 Workspace 已完成图片或本地上传 | 图片 Brief 绑定图生图参考图;视频 Brief 绑定首帧;提交一个真实 Asset ID |
@@ -47,6 +52,10 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展
- Robot model, language, and voice choices are dynamically projected from the Xiaozhi USER catalog through Works Square and Electron Main; the catalog is bounded, account-scoped, and `private, no-store` at each public hop.
- One local account maps to one server-side Xiaozhi account binding. Agents and devices are resources beneath that account binding, not separate Xiaozhi users.
- Robot/Canvas/module-selection routes must not initialize AI Programming projects or providers.
- Makelore Code 的唯一 production runtime 是精确 pin 的 Pi `0.84.2`;不得恢复 OpenCode fallback、双 runtime、兼容执行路径或 Renderer 直连本地 runtime。上游 `@earendil-works/pi-ai` 内名称含 `opencode` 的静态 Provider adapter 不属于产品旧 runtime。
- Renderer 只消费共享的产品中立 project/Conversation/Snapshot/Patch DTO,并经 `src/lib/host-api.ts` 或 `src/lib/api-client.ts` 访问 Main。Pi RPC、event、Provider credential 和本地 runtime 路径保持 Main-private。
- accepted/uncertain Coding mutation 不得自动重放;RPC confirmation timeout 后仍保留 target run permit、process ownership 和 background lease,直到迟到 success/failure/exit/abort 权威收敛。其他 Conversation 必须继续可用。
- selected Provider credential 只投影到目标 worker env;跨账号模型变化必须重建目标 worker。确定性 Works user-context 缺失是 Provider-auth failure:失效缓存 credential、fail fast、固定脱敏提示,不得归类为 Pi worker crash。
- Guided Hotspot Binding is implemented behind a Main-owned capability that is true by default; exact `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` disables it, while capability-read failure falls back to direct six-digit Binding. The guided state is process-local, opener failures expose only the same fixed address for manual copy, and Binding conflicts refresh the safe account overview.
- Robot hotspot scanning and connection are local Main operations that return before Works credentials/upstream access. Renderer may submit only an opaque candidate ID from the latest bounded scan; Main alone filters open printable `Xiaozhi-*` SSIDs, performs platform association, and verifies the exact current SSID.
- Hotspot discovery and connection do not authenticate a Robot. BSSID, interface/profile details, native diagnostics, location data, and Wi-Fi credentials never cross the Main boundary; permission or platform failure keeps the system-settings/manual path available.
@@ -78,8 +87,8 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展
## Related Decisions
- 当前长期边界记录于 README、ADR-001 至 ADR-005、集成任务 `20260807-integrate-login-client-a4f8`、源任务 `20260810-static-release-only-a91c`、`20260812-client-built-release-makelore-7e5b`、`20260812-design-image-to-image-client-c91e` 及本次 Integration Gate;后续如改变唯一入口、凭据所有权、构建执行边界、Conversation 状态归属、Learning 项目分发边界、Robot 配网/绑定安全边界或重新引入客户端部署协调器,应新增 ADR。
- 当前长期边界记录于 README、ADR-001 至 ADR-006、集成任务 `20260807-integrate-login-client-a4f8`、源任务 `20260810-static-release-only-a91c`、`20260812-client-built-release-makelore-7e5b`、`20260812-design-image-to-image-client-c91e` 及本次 Integration Gate;后续如改变唯一入口、凭据所有权、构建执行边界、Conversation 状态归属、Pi 单 runtime/worker/lease 边界、Learning 项目分发边界、Robot 配网/绑定安全边界或重新引入客户端部署协调器,应新增 ADR。
## Last Updated
2026-08-20
2026-08-26