diff --git a/.project-docs/20-architecture/data-flow.md b/.project-docs/20-architecture/data-flow.md index ba87eca..cd6a2d4 100644 --- a/.project-docs/20-architecture/data-flow.md +++ b/.project-docs/20-architecture/data-flow.md @@ -21,6 +21,7 @@ | 可信发布门禁 | source+built+artifact contract | 服务端逐字节重算/合同校验 → 不可变 Release 固化 → 人工审核 | 服务端不运行项目 Vite;仍是不可绕过权威,未来 runtime 强门禁需可信 verifier | | AI 编程 Conversation 创建与准备 | 当前 project / Agent / 本地 Conversation | Renderer 本地创建 → `/api/coding/*` → Main `.makelore` schema-v2 store → shared Agent Server 内 target logical Runtime/Session lazy prepare | Composer 与逻辑线程准备解耦;首次 Conversation 可立即编辑。未解析模型必须先验证并持久化,再打开首个逻辑线程;`.niancode` / `.opencode` 项目元数据不读取、不迁移 | | AI 编程 prompt 与流式投影 | 当前 Conversation draft / attachment refs | Renderer mutation → Host `202` acceptance/dedupe → Main target Agent Server channel → Pi events → Snapshot + `patch-batch` SSE → Renderer reducer | 每个 Conversation 独立 Runtime/Session/credential/extension context/generation/seq;gap 只恢复目标 Conversation。accepted/uncertain mutation 不自动重放,权威 Snapshot 到达前保留相同 optimistic node identity | +| AI 编程前台状态收敛 | Main `lifecycle:sleep`、编程视图挂载/项目上下文变化、页面重新可见或窗口 focus | Renderer 关闭旧 SSE → 静默读取当前选中 Conversation 的 Main-owned Snapshot → target-only reducer | 已选 Conversation 即使身份未变也会与后台 terminal 状态收敛;selection key 防止选择渲染自身造成重复 hydration。该流程只读 Snapshot,绝不重放 accepted/uncertain mutation | | AI 编程模型与思考强度切换 | 当前 Conversation | Renderer → `/api/coding/conversations/{id}` → Main validate/persist → target logical Runtime | 同账号已解析模型复用 `set_model`;跨账号模型等 active run settled 后只重建目标逻辑线程,旧凭据不会进入新 generation;切换失败不销毁可继续使用的旧线程 | | 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 logical-thread Provider 请求 | selected thread-local credential store → Main AI proxy → Works 模型上游 → projector safe error | 确定性的 Works user-context 缺失使缓存 gateway credential 过期、返回非重试认证失败且不重放请求;Renderer 只看到固定 Provider-auth 提示,不能把它解释为 Pi 崩溃 | @@ -54,7 +55,7 @@ - Development 与 packaged Canvas 都使用 Main-owned Works Square V2 adapter。V1 DTO/local semantic adapter 与 local/cloud mode switches 已删除,不存在失败回退。 - 注销和退出关闭本地 event stream、清除 drafts/pending state 和凭据;服务端 current Direction Session 与 semantic history 保持持久化。 - project、Agent、Conversation schema v2 与每条 Conversation 的 opaque Pi session binding 由 Main 在 `.makelore/project.json` 和 `.makelore/conversations.json` 原子持久化。当前客户端不读取、迁移或删除 `.niancode` / `.opencode` 项目元数据。 -- Renderer Store 只拥有产品中立 Snapshot、Patch、optimistic request 与 project-scoped metadata。每条 Conversation 的 generation/seq 独立;stale generation 丢弃,gap/reconnect 只为目标 Conversation 取 Snapshot 并重放严格连续的缓冲 tail,不重放 mutation。 +- Renderer Store 只拥有产品中立 Snapshot、Patch、optimistic request 与 project-scoped metadata。每条 Conversation 的 generation/seq 独立;stale generation 丢弃,gap/reconnect/foreground reconciliation 只为目标 Conversation 取 Snapshot 并应用严格连续的缓冲 tail,不重放 mutation。隐藏 Conversation 的红点只表示新出现的 pending interaction,或当前 run 新进入 completed/failed/aborted terminal;流式助手文字、thinking、工具过程和单个工具失败本身不触发红点。 - Main 以一个长驻 Agent Server 承载每条 active/warm Conversation 的隔离 Pi Runtime/Session/channel、Provider/resource revision 和 thread-local credential store。运行中 stale revision 保留本 run snapshot,settled 后只重建目标逻辑线程;整个 Server 退出则使所有旧 channel 失效,下次恢复只启动一个新 Server。 - top-level 逻辑 turn 并发上限 4、warm idle logical-thread LRU 上限 8、child 并发上限 4,child 使用 FIFO process budget 8;shared parent 线程不各占一个 process lease。同项目 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。 @@ -79,4 +80,4 @@ ## Last Updated -2026-08-31 +2026-09-03 diff --git a/.project-docs/20-architecture/module-map.md b/.project-docs/20-architecture/module-map.md index 723f24b..5b9fc8c 100644 --- a/.project-docs/20-architecture/module-map.md +++ b/.project-docs/20-architecture/module-map.md @@ -22,14 +22,14 @@ | `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/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 | +| `src/pages/Chat/CodingChatPanel.tsx` / `CodingConversationTimeline.tsx` / `CodingComposer.tsx` | Agent/Conversation 选择、Snapshot-first 时间线、prompt/steer/follow-up、交互、压缩、分支与附件 UI | Composer 不等待 worker 准备;`lifecycle:sleep` 关闭旧 SSE,视图挂载、项目上下文变化、页面可见或窗口 focus 静默刷新已选 Snapshot;时间线初始渲染最近 120 个节点并按 100 个节点向前扩展、保持滚动锚点;所有异步结果按 project/Agent/Conversation 所有权隔离,Renderer 不导入 Pi wire 类型 | +| `src/stores/coding-conversations.ts` / `coding-workspace.ts` | per-Conversation Snapshot/Patch、乐观提交、gap/foreground recovery 与项目级元数据 | generation/seq 严格递增;只恢复目标 Conversation,SSE 重连与前台刷新不重放 mutation;隐藏红点只响应新的 pending interaction 或 run terminal transition,不响应普通助手/工具过程 | | `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/` | `.makelore/project.json` 与 `.makelore/conversations.json` schema-v2 项目/Agent/Conversation 数据、原子持久化与项目文件能力 | 不读取或迁移 `.niancode` / `.opencode` 项目元数据;legacy 内容保持 inert 且不删除 | | `electron/coding-runtime/conversation-service.ts` | 产品级 Conversation orchestration、模型/思考强度、fork/archive/recover 与运行时门禁 | unresolved Conversation 先验证并持久化模型再首次 prepare;已解析 Conversation 的模型切换复用 target runtime seam | -| `electron/coding-runtime/pi/{agent-server-process,worker-pool,worker-process,rpc-client,session-registry,event-projector}.ts` / `resources/pi-agent-server.mjs` | 一个长驻父 Agent Server 内的 per-Conversation 逻辑 Runtime/Session/channel、严格 LF JSONL RPC、generation recovery 与 Snapshot/Patch 投影 | 首个 extension 必须是生成的 Makelore bridge,所有后续显式 extension 作为 Pi `additionalExtensionPaths` 加载且不启用 ambient discovery;top-level logical-turn cap 4、warm idle LRU 8;线程级替换只使目标 generation 失效,Server 退出使所有旧 channel 失效并在下次恢复时只重启一个 Server | -| `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/{agent-server-process,worker-pool,worker-process,rpc-client,session-registry,event-projector,session-projector}.ts` / `resources/pi-agent-server.mjs` | 一个长驻父 Agent Server 内的 per-Conversation 逻辑 Runtime/Session/channel、严格 LF JSONL RPC、generation recovery 与 Snapshot/Patch 投影 | 首个 extension 必须是生成的 Makelore bridge,所有后续显式 extension 作为 Pi `additionalExtensionPaths` 加载且不启用 ambient discovery;Session hydration 保留完整 active-branch 可见历史并把 compaction summary 留在 Main-private 模型上下文;正式包按 staged `pi-runtime` 的 manifest/root 解析并校验 Pi 包入口;top-level logical-turn cap 4、warm idle LRU 8;线程级替换只使目标 generation 失效,Server 退出使所有旧 channel 失效并在下次恢复时只重启一个 Server | +| `electron/coding-runtime/pi/{provider-config,provider-refresh,provider-failure,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;精确 Token Point exhaustion 被窄化为安全、不可重试、可持久恢复的产品错误,不透传原始上游正文 | | `electron/coding-runtime/pi/{extension-host,subagent,write-lease}.ts` / `electron/main/background-lifecycle.ts` | 必需 Makelore extension、显式安装且启用的 Device Package extensions、交互、子 Agent、同项目写租约与后台运行租约 | parent generation 加载完整 Main-selected extension 集;child 并发 4、单次最多 8、禁止递归;active/uncertain run 不因页面隐藏或确认超时被回收,所有 stop/replacement 必须有原因 | | `electron/main/updater.ts` | 目标 feed 解析、electron-updater 生命周期与原始诊断 | 缺少稳定 manifest 保持错误;事件/Promise 重复失败按检查实例去重 | | `src/components/settings/UpdateSettings.tsx` | 更新状态、重试与用户可读错误展示 | 只显示一条简洁中文提示;技术诊断统一回退到本地化通用文案 | @@ -56,6 +56,7 @@ ## Dependency Direction - Renderer UI → Renderer API contract → Main Host routes → Main services → Works Square;Renderer 不反向读取 Main 凭据、文件系统或归档。 +- Plugin sidebar/legacy links → canonical `/plugins` projection → existing Renderer stores → bounded Main Marketplace and Device Package routes. Compatibility routes replace-redirect into deterministic filters and do not create a second lifecycle. - Hosted Plugin parent tool → frozen Registry adapter → Main `GameResourceClient` → fixed Works Square game-resource routes. Stable logical operation identity survives response loss and Main restart; ambiguous submission remains reviewable and is never converted into an automatic fresh mutation. Saving a result uses the existing bounded project path and project write lease. - AI 编程 Renderer product Snapshot/commands → typed `/api/coding/*` Host API → Main Coding composition → target `CodingConversationRuntime` → shared Agent Server 内的目标 Pi 逻辑线程;Pi Provider 请求再经 Main AI proxy 访问模型上游。Renderer 不持有 Pi wire、凭据或本地 runtime URL。 - Project configuration 决定产品分流;Main release builder 生成 source/built/contract,服务端独立重算和校验决定发布安全,本地 `ProjectType` 不是授权结论。 @@ -74,6 +75,8 @@ - `runtime_url` 是一个客户端版本的迁移回退;删除前必须确认服务端和存量数据稳定提供 `play_url`。 - 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. +- Pi compaction is a model-context boundary, not a visible-history deletion boundary. Full active-branch hydration must keep compaction summaries private, and Renderer windowing must preserve the user's scroll anchor while prepending older nodes. +- Provider quota classification must remain exact and redacting: unrelated `403`/`429` failures cannot be reinterpreted as Token Point exhaustion, while reopened recognized failures must preserve only the stable product code and safe Chinese message. - Shared parent logical threads do not consume one process lease each. Independent child Agents remain capped at 4 against the FIFO process budget of 8; stop failure must release child leases in `finally`. - Provider/resource revisions apply before the next prompt for idle logical threads and after `agent_settled` for active runs. Same-account model changes may use `set_model`; cross-account changes rebuild only the target Conversation thread so old credentials never enter the new credential store. - `electron/main/updater.ts` 的稳定源错误归一化必须保持窄化:只识别 Works Square 对应 manifest 的 404,不得吞掉其他 feed/网络/签名错误;Renderer 的脱敏边界不能取代 Main 原始日志。 diff --git a/.project-docs/20-architecture/system-overview.md b/.project-docs/20-architecture/system-overview.md index bd764da..dd25c8e 100644 --- a/.project-docs/20-architecture/system-overview.md +++ b/.project-docs/20-architecture/system-overview.md @@ -18,9 +18,9 @@ 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 类型 | +| Makelore Code Workspace | project/Agent/Conversation schema v2、产品中立 Snapshot/Patch、Composer 与时间线 | Conversation 本地创建且不等待 worker;sleep 关闭旧 SSE,视图挂载、项目切换、visibility/focus 会静默刷新已选 Snapshot;旧 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 | 一个长驻 Pi `0.84.2` Agent Server 承载每条 active/warm Conversation 的隔离逻辑 Runtime/Session/JSONL channel | 严格 LF JSONL RPC、generation recovery、Snapshot hydration;top-level 逻辑 turn 并发 4、warm idle LRU 8;Server 退出统一使旧 channel 失效并按需单实例重启 | +| Pi Conversation Runtime | 一个长驻 Pi `0.84.2` Agent Server 承载每条 active/warm Conversation 的隔离逻辑 Runtime/Session/JSONL channel | 严格 LF JSONL RPC、generation recovery、Snapshot hydration;正式包从 staged `pi-runtime` manifest/root 定位并校验 Pi 包入口;top-level 逻辑 turn 并发 4、warm idle LRU 8;Server 退出统一使旧 channel 失效并按需单实例重启 | | Pi Provider & Managed Resources | Provider catalog、thread-local secret projection、model/resource revision、Prompt/Skill/extension materialization、selected-model tools | 父凭据只进入选中逻辑线程的内存 credential store,child 凭据只进入该短命进程;Works `model_capabilities` 由 Main 严格归一化并作为安全 Provider metadata 持久化。Web Search 仅在精确 capability 存在时随冻结的 selected model/provider/credential 进入 parent tool catalog,并走普通模型计费;不回退 `agent_browser` 或独立 Hosted Provider。服务端 reasoning levels 优先于本地 profile,缺字段则清理 override 并回退;不扫描项目或用户的 `.pi/.agents/.codex`,不把 secret 或原始响应放进 argv、catalog 或 Renderer | | Pi Extension, Subagents & Lifecycle | 必需的生成式 Makelore extension、Main 显式选定的已安装 extensions、UI interaction、ephemeral child、write lease 与 background run lease | Makelore bridge 固定为首个 extension,其余选定 extension 全部经 Pi 的 explicit additional paths 加载且 ambient discovery 关闭;child 并发 4、单次最多 8、禁止递归;active/uncertain run 不因页面隐藏或 confirmation timeout 被停止,replacement/stop 必须可解释并清理所有 ownership | | Official Hosted Plugins | Acquired code-owned bundled Game Resource package → effective parent snapshot → code-owned Main adapter → fixed Works Square hosted route | 无设备下载、更新、Beta 或签名步骤;Renderer/Package/Pi 不持有 Provider key、model 或 URL。每次计费操作要求显式确认,child 不继承 hosted tool。历史 Hosted Web Search 不再进入当前客户端。 | @@ -57,6 +57,8 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展 - 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 必须继续可用。 +- Main 发出 `lifecycle:sleep` 时 Coding 页面必须关闭旧事件流;编程视图挂载、项目上下文变化、页面重新可见或窗口 focus 时必须从 Main 静默刷新已选 Conversation 的权威 Snapshot。该只读收敛不得转化为 prompt/interaction 的自动重放。隐藏 Conversation 红点只代表新的 pending interaction 或新的 completed/failed/aborted terminal transition。 +- 正式包中的 Agent Server 必须以显式 staged `pi-runtime` 的 manifest/root 解析 Pi 依赖,校验导入入口未逃逸对应包目录;不得依赖脚本相邻目录、应用 `node_modules` 或系统包解析作为 fallback。 - selected Provider credential 只投影到目标父逻辑线程的内存 credential store 或目标 child 进程;跨账号模型变化必须重建目标逻辑线程。确定性 Works user-context 缺失是 Provider-auth failure:失效缓存 credential、fail fast、固定脱敏提示,不得归类为 Pi crash。 - Works 下发的 per-model reasoning capability 是可选 Main-owned metadata,不是 Renderer 或 one-api 的权威。存在时只接受受支持的安全形状并覆盖目标模型的 @@ -98,4 +100,4 @@ Makelore 是 Electron 桌面客户端。Renderer 负责项目操作与状态展 ## Last Updated -2026-09-01 +2026-09-03 diff --git a/.project-docs/30-worklog/current-state.md b/.project-docs/30-worklog/current-state.md index 7030e0a..417cf58 100644 --- a/.project-docs/30-worklog/current-state.md +++ b/.project-docs/30-worklog/current-state.md @@ -4,6 +4,15 @@ This file is the integrated default-branch snapshot. Feature tasks record progre ## Integrated Through +- Integration task `20260903-complete-remote-merge-b41e7c92` combines local + parent `7a37593e4be3c39a9782975de53f56a76dd53ef0` and fetched + `origin/main` parent `8e947b4f0e1ff0409e52a9d528ae3bb240ceb687` through a normal + two-parent merge. The later reviewed unified `/plugins` workspace remains + authoritative, while the remote runtime-root, Conversation reconciliation, + history, quota, tool-batch, and settlement corrections are retained. The + superseded Project Configuration Plugin Services sheet is not restored; no + rebase, history rewrite, force-push, package, deployment, or publication is + part of this integration. - Unified Plugin workspace product head `d7058e6383f1e9dd72c32570cf83b9f439d91033` is integrated on local `main` by task `20260903-plugin-navigation-integration-4f7c2a96` after fixed-range R4 @@ -38,6 +47,72 @@ This file is the integrated default-branch snapshot. Feature tasks record progre across the two source tasks. The currently installed 1.2.6 client was not replaced; rebuilt exact-main installation and live prepare/confirm/activation remain release acceptance work. +- Integration task `20260903-integrate-latest-6f2c8e1a` preserves the divergent + local and remote mainline histories with normal merge `d25ed08`: fetched + `origin/main` frontier `28e1690` and local frontier `301c149` are both ancestors + of the integrated tree. It also reviewed every local worktree/branch; historical + Learning Player source `b1f51be` remains intentionally excluded under ADR-005, + rather than being mistaken for current product work. No history rewrite or + force-push is part of this integration. +- Packaged Pi runtime-root source `5d7a235` from task + `20260902-build-unsigned-mac-9d7e4a2c` is integrated through merge `4babd6d`. + `resources/pi-agent-server.mjs` now anchors `@earendil-works/pi-ai` discovery to + the staged `pi-runtime` package manifest, selects its import entry, and rejects + entries escaping the package root. The corrected local-only unsigned arm64 DMG + passed exact mounted-image initialize/shutdown with SHA-256 + `6d0216da6c30f7fed537041b37c69811b8e025af3e9cccf85a64c690e29ecb7b`; + it is not signed, notarized, published, or cross-platform release evidence. +- Conversation-attention source `f2c3755` from task + `20260902-conversation-badge-a7c49e` is integrated through merge `bbfe16d`. + Hidden Conversation red markers now represent only a newly pending interaction + or a newly completed/failed/aborted run; assistant/thinking/tool progress and an + individual tool failure remain visible without becoming attention notifications. +- Foreground-reconciliation source `7aa118b` from task + `20260903-diagnose-recurring-stall-a83f5c21` is integrated through merge + `04320dd`. Coding closes its old event stream on `lifecycle:sleep` and silently + reloads the selected Main-owned Snapshot on view/project context, visibility, + or focus transitions. A stale Renderer `live` state can therefore converge to + an already persisted terminal state without replaying an accepted mutation. +- Reconciliation checkpoint `cdbc262` was verified by final-gate task + `20260903-finalize-main-integration-b7e2c4a9`: focused Vitest passed 5 files / + 70 tests, Electron Coding E2E passed 4/4, full Vitest passed 1,832 with 3 + skips plus pressure 1/1, typecheck passed, lint reported 0 errors and 5 existing + warnings, and Renderer/Main/Preload/utility production build passed. A fresh + fetch found the checkpoint 42 commits ahead / 0 behind `origin/main` before + promotion. +- Bash tool-bridge source `49112b6` from task + `20260902-fix-bash-tool-bridge-9c4e7a12` is integrated through merge + `55bf80e` by task `20260902-integrate-bash-tool-bridge-e81a5c6d`. + Pi's prepare-all behavior can no longer deadlock two tools against Makelore's + same-project write lease before either tool starts: built-in `bash`, `edit`, + and `write`, plus dynamically declared product tools that acquire that lease, + execute sequentially within an assistant tool batch. Read-only batches retain + parallel execution, and Bash command timeout remains authoritative once its + child process starts. +- Coding terminal-settlement source `f1fd13a` from task + `20260901-fix-session-settlement-a47d2e91` is integrated through merge + `e76a1e1` by task `20260902-integrate-session-settlement-6e41c8b2`. + Normal prompt completion still uses authoritative `agent_settled`; when that + handshake is missing after Pi has terminalized, Main probes only the accepted + target thread after a bounded 30-second grace period. Exact idle evidence + hydrates the persisted final assistant response and releases ownership once; + contradictory state becomes an explicit target-only protocol failure. Accepted + or uncertain mutations are never replayed, and sibling Conversations remain + available. +- Plugin-navigation sources `b3f4166` and `e237941` from task + `20260831-consolidate-plugin-navigation-3d9a6c71` are integrated through + merge `a278ca3`. Coding history/quota source `d523b72` from task + `20260831-fix-history-quota-errors-6c2a91e4` is integrated through merge + `69832e0`, and assistant progress-preview source `8d878eb` from task + `20260831-show-progress-messages-a14f9c2d` is integrated through merge + `a8fd95f`. At that source frontier, Project Configuration owned the visible + Plugin Services surface; Pi compaction no longer removes visible active-branch history; + recognized Token Point exhaustion remains safely actionable after reopen; + and collapsed process previews remain anchored to their first displayable + line. Those predecessor Project Configuration Plugin surfaces are superseded by + the reviewed unified `/plugins` workspace above and are not restored. Historical + source `b1f51be` remains explicitly excluded because + ADR-005 continues to retire the OpenMAIC Learning Player packaging chain. - Youth-facing AI Design client source `0fd32a2d49045a8f9e7f2e19ba6477f48f93e30c` is integrated over Model Tools frontier `7552cf59526449c29d663a769c0fb62d84a1a759` through merge @@ -56,8 +131,8 @@ This file is the integrated default-branch snapshot. Feature tasks record progre Package tests, typecheck, scoped lint, production build, and 2 Electron E2E flows. The Electron run also exposed and closed an existing current-frontier startup crash: the ESM-only Pi package manager is now loaded only when remote Device Package - installation needs it. No package, deployment, publication, push, or paid request - is claimed. + installation needs it. At that source-task checkpoint no package, deployment, + publication, remote push, or paid request was claimed. - Model Tools and Device Packages client cutover is implemented by task `20260902-model-tools-client-integration-5d8b2f73`. Web Search is now a parent-only core tool of the frozen selected model/provider/credential; it no longer depends on @@ -200,7 +275,7 @@ This file is the integrated default-branch snapshot. Feature tasks record progre rendered exactly Data Service, Game Resource, and Web Search without the unavailable catalog error. At that checkpoint Game Resource and Web Search still showed no stable Release; the official bundled cutover recorded above supersedes that delivery model. -- Packaged Pi Agent Server resolver fix source `7df245af5a04f62be48980831ff41987ba686009` +- Earlier packaged Pi Agent Server resolver source `7df245af5a04f62be48980831ff41987ba686009` from task `20260901-local-runtime-unavailable-8b42c7f1` is promoted to local `main` by task `20260901-promote-local-runtime-main-a7c4e291` through verified integration candidate `42ea83c0cbad52432eca99e4161e0360bef3e219` and source merge @@ -211,8 +286,10 @@ This file is the integrated default-branch snapshot. Feature tasks record progre A production-shaped sibling-layout test reproduces the installed failure before the fix and initializes successfully through Electron Node after it; Pi remains pinned at `0.84.2`, and no bundle layout, Provider, Renderer, or recovery contract changed. - The currently installed application predates this source integration; a rebuilt and - verified Windows artifact plus first-Conversation acceptance remain pending. + This flag-only resolver was later superseded by source `5d7a235` above, which + explicitly resolves the package manifest and entry from the staged runtime root and + has mounted-image macOS arm64 evidence. A signed/notarized artifact, Windows rebuild, + and installed first-Conversation acceptance remain pending. - Native Web Search client tickets MLW-01 through MLW-03 are integrated by source coordinator `20260831-web-search-client-integration-7d2f5b94` and promoted to local `main` by task `20260901-web-search-client-main-merge-5a9d3b82` from reviewed @@ -444,9 +521,11 @@ Canvas 侧栏提供“获取灵感”进入 Prompt Museum。列表、筛选、 Makelore 在会话恢复、登录和刷新后由 Electron Main 请求 Works `/api/auth/me`,Renderer 只获得 Code、Canvas、Learning、Robot 四个布尔权限。缺失 `module_access` 或任一字段时默认开启;服务端 `design` 显式映射客户端 `painting`。被关闭的模块卡片置灰且不可点击,根路由、深层路由和别名路由均在 `MainLayout` 或模块初始化前阻断。Code provider 等待认证权限加载完成;权限查询返回终止性 `401` 时同时清理 Main 和 Renderer 会话。`/settings` 是全局设置,不受 Code 入口策略阻断。该机制只是客户端入口策略,不代替服务端 API 授权。 +插件在编程侧栏只有一个“插件”入口,`/plugins` 是唯一产品页面,并以统一列表投影 Marketplace、账号 Library、官方设备状态、本机 Device Packages、当前项目状态与 retained IDs;旧 `/plugin-marketplace`、`/my-plugins` 与 `/project-plugins` 路由只做确定性筛选重定向。获取、设备安装、项目启用、Agent Skill 分配、运行授权和计费仍是独立生命周期,不因界面统一而自动推进;原生 selected-model Web Search 不进入插件列表。 + AI 学习现在是已启用的运营精选项目目录,并继续受登录和 `module_access.learning` 控制。Renderer 通过 Main-owned Host API 获取分页项目卡片和 README 详情;Markdown 支持 GFM、禁用原始 HTML。服务端发布时只校验图片 URL 为无凭据、默认端口、无 fragment 且当前 DNS 结果全部为公网地址的 HTTPS URL,保留地址而不下载、识别格式、转码或镜像;客户端仅为 README 图片节点启用直连,因此 SVG 和 Electron 支持的其他格式可直接显示,单图失败不阻断详情。封面和历史发布媒体继续走受控路径。详情页的下载按钮打开系统保存对话框;Main 将 ZIP 流式写入临时文件,只允许最多五跳同 Works origin 重定向,不校验 `Content-Length`、`archiveBytes`、实际流字节数或客户端大小上限,校验 SHA-256 和 ZIP 签名后原子保存,Renderer 只接收 `saved` 或 `cancelled`。课程生成、进度、本地课程库、OpenMAIC player、Agent、ASR、课堂 runtime、Learning IPC 和 player artifact 打包已删除且没有兼容读取路径;历史课程数据保留但不再读取。服务端和客户端源码契约已完成,不代表生产部署或真实账号安装包联调已经完成。 -AI 编程已经硬切到精确 pin 的 Pi `0.84.2`,不存在 OpenCode fallback 或双 runtime。project、Agent 与 Conversation 只在 `.makelore/project.json` 和 `.makelore/conversations.json` 使用本地 schema v2;当前客户端不从 `.niancode` 或 `.opencode` 读取、迁移或删除项目元数据。Electron Main 按需启动一个长驻父 Agent Server,每条 active/warm Conversation 在其中拥有独立 Runtime/Session/channel、credential store、extension context、generation/seq、Snapshot/Patch、model/thinking、队列、interaction 与错误状态,Composer 在 lazy prepare 期间仍可编辑。Renderer 只通过 `/api/coding/*` 和 Snapshot-first/`patch-batch` SSE 消费产品中立合同;gap/reconnect 只恢复目标 Conversation,accepted/uncertain mutation 不自动重放。未解析 Conversation 第一次选模先 validate 并持久化 resolved metadata,再 prepare;同账号模型切换使用 target `set_model`,跨账号只重建目标逻辑线程。Main-selected Device Package generation 把所有显式安装且当前启用的 Skill 路径和 extension 路径交给未来或空闲 parent;生成的 Makelore bridge 始终是必需的首个 extension,其余全部经 `additionalExtensionPaths` 加载,同时保持 ambient discovery 关闭。active parent 在 turn settled 后刷新,child 始终为空。top-level 逻辑 turn 并发为 4,warm idle logical-thread LRU 为 8;independent child 进程并发为 4 并使用 FIFO 进程预算 8;coding child 与 parent 共用项目 write lease。prompt/compact confirmation timeout 后仍保留 run/Agent-Server-or-child-process/background ownership,迟到 success/failure/exit/abort 单调且 exactly-once 收敛,页面隐藏不会停止 active/uncertain run。线程级替换只使目标 generation 失效;整个 Agent Server 退出会统一使所有旧 channel 失效,但 Main/Renderer 存活且下次恢复只重启一个 Server。Pi `0.84.2` 手动 compact 不发 `agent_settled`,由 correlated compact RPC 结果终结。父 Provider credential 只进入选中逻辑线程的内存 credential store,child 凭据只进入该短命进程;确定性的 Works user-context 缺失会失效 gateway credential、fail fast、不重放并投影固定 Provider-auth 错误,不归类为 Pi crash。真实 Provider 验证仍为用户明确接受的未验证风险,macOS x64/arm64 与 native non-WSL Linux 也未通过平台发布门禁。 +AI 编程已经硬切到精确 pin 的 Pi `0.84.2`,不存在 OpenCode fallback 或双 runtime。project、Agent 与 Conversation 只在 `.makelore/project.json` 和 `.makelore/conversations.json` 使用本地 schema v2;当前客户端不从 `.niancode` 或 `.opencode` 读取、迁移或删除项目元数据。Electron Main 按需启动一个长驻父 Agent Server,每条 active/warm Conversation 在其中拥有独立 Runtime/Session/channel、credential store、extension context、generation/seq、Snapshot/Patch、model/thinking、队列、interaction 与错误状态,Composer 在 lazy prepare 期间仍可编辑;正式包从 staged `pi-runtime` manifest/root 定位并校验 Pi 包入口,不从脚本目录或应用 `node_modules` 回退。Renderer 只通过 `/api/coding/*` 和 Snapshot-first/`patch-batch` SSE 消费产品中立合同;gap/reconnect 只恢复目标 Conversation,accepted/uncertain mutation 不自动重放。`lifecycle:sleep` 会关闭旧事件流,视图挂载、项目上下文变化、页面重新可见或窗口 focus 会静默刷新已选 Snapshot,使后台 terminal 状态收敛且不重放 mutation。隐藏 Conversation 的红点只在新 pending interaction 或新 completed/failed/aborted terminal transition 出现,不由助手/thinking/工具过程或单个工具失败触发。Session hydration 沿完整 active branch 投影可见历史,Pi compaction 只改变模型上下文并保持 summary 私有;Renderer 首次挂载最近 120 个节点,向上滚动时按 100 个节点追加更早内容并补偿新增高度以保持阅读锚点。折叠的 thinking、助手过程说明与工具输出固定展示第一条可见内容和首个非空行,横向偏移保持为零,展开后仍显示完整内容。未解析 Conversation 第一次选模先 validate 并持久化 resolved metadata,再 prepare;同账号模型切换使用 target `set_model`,跨账号只重建目标逻辑线程。Web Search 只作为所选模型 capability 进入父 worker 并使用相同 model/provider/credential 与普通模型计费,不经过 Marketplace/Hosted client/Plugin Charge 或浏览器 fallback;child 不继承。Conversation 工具可检查并在独立确认后安装 npm、Git、本地 Plugin 目录或 loose `SKILL.md` 为 Main-owned immutable Device Package;不运行生命周期脚本,可执行 extension 具有桌面用户权限,新/idle parent 自动刷新,active parent 结算后刷新,child 为空。每个 Main-selected generation 都保留全部显式安装且当前启用的 Skill 路径;生成的 Makelore bridge 是必需的首个 extension,其余 extension 全部经 `additionalExtensionPaths` 加载,并继续关闭 ambient discovery。top-level 逻辑 turn 并发为 4,warm idle logical-thread LRU 为 8;independent child 进程并发为 4 并使用 FIFO 进程预算 8;coding child 与 parent 共用项目 write lease。同一助手工具批次内,内置 `bash`/`edit`/`write` 与声明需要该 write lease 的动态产品工具按顺序执行,避免 Pi 在批量 prepare 阶段形成租约自锁;纯只读工具批次仍可并行。prompt/compact confirmation timeout 后仍保留 run/Agent-Server-or-child-process/background ownership,迟到 success/failure/exit/abort 单调且 exactly-once 收敛,页面隐藏不会停止 active/uncertain run。线程级替换只使目标 generation 失效;整个 Agent Server 退出会统一使所有旧 channel 失效,但 Main/Renderer 存活且下次恢复只重启一个 Server。Pi `0.84.2` 手动 compact 不发 `agent_settled`,由 correlated compact RPC 结果终结。父 Provider credential 只进入选中逻辑线程的内存 credential store,child 凭据只进入该短命进程;确定性的 Works user-context 缺失会失效 gateway credential、fail fast、不重放并投影固定 Provider-auth 错误,不归类为 Pi crash。精确的 `403 token_point_balance_exhausted` / `词元点数余额不足` 上游响应会在 Main 内归类为不可重试的配额耗尽,Renderer 只收到稳定 `CODING_PROVIDER_QUOTA_EXHAUSTED` 与安全中文提示;重开会话仍保留该提示,不暴露上游 request id 或原始错误正文。真实 Provider 验证仍为用户明确接受的未验证风险;macOS arm64 仅有本地未签名 mounted-image initialize/shutdown 证据,签名/公证/完整 process-enumeration、macOS x64 与 native non-WSL Linux 仍未通过平台发布门禁。 Updater 仍由 Electron Main 选择目标 feed、记录原始诊断并保持失败语义。正式稳定源缺少对应平台 manifest 时,设置页只显示一条简洁中文提示并允许重试,不把缺包误报为已是最新版,也不向普通界面暴露堆栈、URL、路径或错误码;签名产物发布和真实升级安装仍属于外部 Release Gate。 @@ -454,6 +533,12 @@ Robot 绑定设备默认先显示“引导配网 / 已有激活码”路径选 ## Recently Completed +- 2026-09-03: Reconciled local `main@7a37593` with fetched + `origin/main@8e947b4` through a normal two-parent merge. The result keeps the + reviewed single `/plugins` workspace and Device Package multi-extension + activation while integrating the remote Pi runtime-root, Conversation + foreground/history/quota, tool-batch, and settlement corrections; the + predecessor Project Configuration Plugin Services sheet remains retired. - 2026-09-03: Integrated the paired Device Package prepare and activation fixes. Packaged Main now resolves package inspection from the physical bundled Pi runtime, and the parent Agent Server loads the required generated Makelore @@ -463,6 +548,40 @@ Robot 绑定设备默认先显示“引导配网 / 已有激活码”路径选 extension commands, full unit/pressure, typecheck, lint, build, Windows packaging, and artifact checks. The installed 1.2.6 client is unchanged, so exact-main rebuild/install and live activation are still pending. +- 2026-09-03: Reconciled fetched `origin/main` with the complete local mainline and + integrated the three cleanly owned latest source tasks through normal merge + commits. The result contains Model Tools/Device Packages, youth-facing Canvas, + packaged Pi runtime-root resolution, foreground Conversation reconciliation, + and attention-only unread markers without rewriting either mainline history. +- 2026-09-03: Corrected packaged Pi dependency discovery to resolve the package + manifest and entry from staged `pi-runtime`. A local-only unsigned macOS arm64 + DMG passed mounted-image Agent Server initialize/shutdown; signing, notarization, + full macOS process proof, other architectures, publication, and installation of + this corrected build remain outside that evidence. +- 2026-09-03: Integrated Renderer foreground recovery and Conversation attention + semantics. Returning to a selected Conversation now refreshes its authoritative + terminal Snapshot after lifecycle sleep without prompt replay, while hidden red + markers are reserved for new interactions and terminal task outcomes. +- 2026-09-02: Integrated the Pi write-leased tool-batch correction. Multiple + Bash/edit/write or lease-requiring product tools emitted by one assistant + response now execute sequentially instead of waiting on a lease before any + command can start; read-only batches remain parallel. A real Agent Server + regression covers two Bash calls, a command-level timeout, and rejection of + the former HTTP-idle-timeout `fetch failed` path. +- 2026-09-02: Integrated bounded recovery for a missing Pi prompt settlement. + A final persisted assistant response can no longer leave the Composer permanently + processing merely because `agent_settled` was lost: Main waits for the normal + handshake, then uses exact target-thread idle evidence to hydrate and settle once, + or emits a safe target-only protocol failure. Focused runtime/process tests, + typecheck, and the Vite/Electron production build passed on the integrated tree. +- 2026-09-01: Integrated the predecessor Plugin navigation work plus complete + active-branch Coding history with scroll-anchor preservation, narrow Token Point + quota feedback, and stable first-line process previews. The predecessor + Project Configuration sheet is superseded by the reviewed unified `/plugins` + workspace and is not present in the current product. Combined typecheck, + focused/full unit tests, lint, Vite/Electron builds, and five focused Electron + E2E scenarios passed. The obsolete Learning Player packaging branch remains + excluded by explicit human confirmation and ADR-005 remains authoritative. - 2026-08-31: Integrated the provider-neutral `makelore.game-resource` hosted Plugin client with fetched `origin/main`. Generic schema-2 `platform_hosted` packages now materialize frozen Skill/tool snapshots only for eligible parent Pi logical threads; diff --git a/.project-docs/30-worklog/tasks/20260831-consolidate-plugin-navigation-3d9a6c71.md b/.project-docs/30-worklog/tasks/20260831-consolidate-plugin-navigation-3d9a6c71.md new file mode 100644 index 0000000..8d6a955 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-consolidate-plugin-navigation-3d9a6c71.md @@ -0,0 +1,83 @@ +# Task: Consolidate plugin navigation into project configuration + +## Identity + +- Task ID: 20260831-consolidate-plugin-navigation-3d9a6c71 +- Mode: Feature +- Branch: codex/20260831-consolidate-plugin-navigation-3d9a6c71-consolidate-plugin-navigation +- Worktree: /Users/inmanx/Documents/makelore-consolidate-plugin-navigation-3d9a6c71 +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Completed + +## Scope + +- Remove all plugin entries from the programming sidebar and make Project Configuration’s “插件服务” resource the single user-facing plugin surface. +- Combine Marketplace discovery, account/device plugin management, and current-project enablement/configuration as URL-backed “发现插件”, “我的插件”, and “项目启用” tabs. +- Preserve account acquisition, device installation, project enablement, Agent skill assignment, runtime authorization, and billing as independent lifecycle states. +- Keep `/plugin-marketplace`, `/my-plugins`, and `/project-plugins` as compatibility redirects into the appropriate Project Configuration tab. +- Update focused unit tests, Electron E2E coverage, and README product-state documentation. + +## Intent And Constraints + +- Reduce navigation duplication without changing Main-owned plugin contracts or `.makelore/project.json` authority. +- Reuse the existing light visual system, Radix tabs/sheets, stores, and Host API clients; add no dependency or alternate backend path. +- Keep discovery/account/device data global even though its user-facing surface is nested under the active project’s configuration; project enablement and Agent assignment remain project-scoped. +- Do not silently acquire, download, enable, assign, authorize, or bill as a side effect of another action. +- Preserve unavailable/removed plugin IDs and explicit status messaging rather than inventing metadata or hiding retained configuration. + +## Outcome + +- Removed the remaining global Plugin Center from the programming sidebar. Project + Configuration now exposes one “插件服务” resource beside models, Skills, and + knowledge files. +- Added one wider Plugin Services sheet with URL-backed “发现插件”, “我的插件”, and + “项目启用” tabs. The resource card defaults to current-project enablement, while + compatibility and in-product links can deep-link directly to discovery or the + account/device library. +- Reused the existing Marketplace, Library, device installation, project plugin, + and Agent Skill stores. Enabling a project plugin refreshes the Skill projection, + but does not acquire, download, configure a backend, or assign a Skill. +- Preserved compatibility for saved/deep links: `/plugin-marketplace`, + `/my-plugins`, and `/project-plugins` redirect to their corresponding Plugin + Services tab. My Plugins discovery and project actions now target those tabs. +- Reused semantic embedded `section` variants for all three tabs and removed the + routed standalone Plugin Center container. +- Updated README product-state copy and focused unit/Electron E2E coverage. + +## Verification + +- Exact `pnpm@10.33.4` was used for repository commands. +- `pnpm run typecheck`: passed. +- Focused Vitest: 3 files / 19 tests passed. +- `pnpm test`: 213 test files / 1,730 tests passed; 3 existing conditional tests skipped. +- `pnpm run lint:check`: passed with zero errors and five pre-existing warnings + (`Home/index.tsx` hook dependency and four `Makelore/index.tsx` Fast Refresh warnings). +- `pnpm run build:vite`: passed for Renderer, Electron Main, Preload, and utility + worker; existing dynamic-import and large-chunk warnings remain non-fatal. +- Focused Electron E2E: 3/3 passed across Plugin Services discovery/account-device + management, sidebar removal, and current-project enablement. +- `git diff --check`: passed. + +## Follow-ups + +- Canonical `.project-docs` navigation wording should be promoted only by a later + Integration Gate; no plugin lifecycle, trust, billing, or backend contract changed. + +## Promotion Candidates + +- Target canonical documents: `.project-docs/30-worklog/current-state.md` and + `.project-docs/20-architecture/module-map.md`. +- Proposal: record that Renderer exposes no standalone plugin sidebar destination; + Project Configuration’s single Plugin Services sheet owns discovery, + account/device management, and current-project enablement tabs beside models, + Skills, and knowledge. +- Evidence: this task's source diff, 19 focused unit tests, the 1,730-test full unit + suite, three focused Electron E2E flows, typecheck, lint, and production build. +- Future impact: new navigation, documentation, and E2E work should target + `/project-config?resource=plugins&pluginTab=discover|mine|project`; all standalone + plugin routes are compatibility redirects only. +- Semantic conflicts: none identified. The existing independent lifecycle and + Main-owned trust boundaries remain unchanged. +- Human confirmation: not required for the factual promotion; the user explicitly + approved this consolidation, but Integration Gate ownership is still required. diff --git a/.project-docs/30-worklog/tasks/20260831-diagnose-agent-submit-f90e1b3f.md b/.project-docs/30-worklog/tasks/20260831-diagnose-agent-submit-f90e1b3f.md new file mode 100644 index 0000000..926d421 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-diagnose-agent-submit-f90e1b3f.md @@ -0,0 +1,83 @@ +# Task: Diagnose local Agent submission stall + +## Identity + +- Task ID: 20260831-diagnose-agent-submit-f90e1b3f +- Mode: Feature +- Branch: codex/20260831-diagnose-agent-submit-f90e1b3f-diagnose-agent-submit-f90e1b3f +- Worktree: /Users/inmanx/Documents/makelore-diagnose-agent-submit-f90e1b3f +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Diagnose the Makelore Code conversation shown with three settled + `处理失败` groups and one accepted message remaining at `正在提交…`. +- Correlate the screenshot timestamp with the live Electron/Main/Pi process + state, persisted Main logs, Renderer request reconciliation, and AI proxy + error classification. +- Make no product-source, dependency, runtime, account, or external-service + changes. + +## Intent And Constraints + +- Preserve the Main-owned Pi `0.84.2` boundary and do not replay an accepted + mutation while diagnosing it. +- Treat the separate development worktree and running Electron process as + read-only evidence. +- Distinguish expected background runtime sleep from an unexpected Agent + Server crash. + +## Outcome + +- Identified the immediate failure as upstream Token Point exhaustion. At + `2026-08-31T11:25:18Z`, `11:25:22Z`, `11:25:32Z`, and `11:25:59Z` (19:25 + Asia/Shanghai), the Main-owned AI proxy reached + `https://token.nianxx.cn/v1/chat/completions` and received `403 Forbidden` + with code `token_point_balance_exhausted` and message `词元点数余额不足`. +- Correlated the first three responses with the three settled failure groups. + The final bubble had already received Host/Pi acceptance, so Renderer showed + `1 条消息已被本地 Agent 接收。`; the screenshot was taken before the fourth + provider failure terminal patch replaced the optimistic `正在提交…` state. +- Confirmed the later Agent Server disappearance was intentional + `background_sleep` after the app lost foreground programming activity, with + exit code 0 and `server.stopped`; no `server.invalidated` or unexpected crash + occurred in the incident window. +- Found a secondary UX classification gap: the quota helper recognizes + `token_balance_exhausted` and the proxy remaps quota only when upstream status + is 429, while the observed service contract used status 403, code + `token_point_balance_exhausted`, and Chinese `词元点数余额不足`. The response + therefore remained a generic provider failure and the collapsed timeline + exposed only `处理失败` instead of an actionable balance message. +- No product source or dependencies were changed. + +## Verification + +- Inspected the supplied screenshot at original resolution and matched its + 19:25 conversation time to the persisted Main log. +- Read `/Users/inmanx/Library/Application Support/niancode/logs/niancode-2026-08-31.log`; + confirmed seven occurrences of the same balance-exhaustion contract during + the current day, including all four requests in the screenshot window. +- Verified Electron PID `98444`, Host API port `13210`, and Vite port `5173` + remained live while the Pi Agent Server later stopped through the expected + background lifecycle. +- Inspected `src/stores/coding-conversations.ts`, + `src/pages/Chat/CodingChatPanel.tsx`, + `src/pages/Chat/CodingConversationTimeline.tsx`, + `electron/api/routes/ai-proxy.ts`, + `shared/ai-gateway-error-kind.ts`, and the Pi runtime/projector path. +- No tests were run because this task was diagnosis-only and made no behavior + change. + +## Follow-ups + +- Restore sufficient Token Point balance before retrying the model request. +- If a product fix is requested, recognize the observed + `token_point_balance_exhausted` contract independently of its 403/429 status, + project a stable non-retryable quota error, show actionable Chinese copy, and + add focused proxy/projector/Renderer regressions. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260831-diagnose-history-pagination-3eac1d7b.md b/.project-docs/30-worklog/tasks/20260831-diagnose-history-pagination-3eac1d7b.md new file mode 100644 index 0000000..24f5328 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-diagnose-history-pagination-3eac1d7b.md @@ -0,0 +1,83 @@ +# Task: Diagnose older conversation history loading + +## Identity + +- Task ID: 20260831-diagnose-history-pagination-3eac1d7b +- Mode: Feature +- Branch: codex/20260831-diagnose-history-pagination-3eac1d7b-diagnose-history-pagination-3eac1d7b +- Worktree: /Users/inmanx/Documents/makelore-diagnose-history-pagination-3eac1d7b +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Diagnose why older messages stop loading while scrolling upward in the + Makelore Code conversation shown in the supplied screenshot. +- Trace Renderer scroll behavior, the public Snapshot/Host API contract, + Main-owned Pi session hydration, and the persisted session data for the + affected local conversation. +- Make no product-source, dependency, runtime, account, or external-service + changes. + +## Intent And Constraints + +- Preserve the Main-owned Pi `0.84.2` and product-neutral Snapshot/Patch + boundary; do not introduce or infer a Renderer-to-Pi history path. +- Treat the running development worktree, application logs, project metadata, + and Pi session files as read-only evidence. +- Distinguish persisted-data loss from a bounded projection or missing UI/API + pagination mechanism. + +## Outcome + +- Identified a deterministic two-layer history-display boundary rather than a + transport stall or corrupt session. +- The affected Pi JSONL remains intact at 343 records / 782,976 bytes. Its + active branch contains 342 entries from 2026-08-28T08:34:53Z through + 2026-08-31T11:25:59Z, with all parent links traversable to the current leaf. +- The branch contains one compaction. `projectPiSessionSnapshot()` calls + `retainedTail()`, which intentionally discards every active-path entry before + the compaction's `firstKeptEntryId`. For this session that hides 136 records, + including 128 raw messages and approximately 131 product timeline nodes. + The first retained record begins at 2026-08-29T06:11:52Z. +- The remaining tail projects to approximately 195 timeline nodes. Renderer + shows the newest 120 nodes and the `加载更早内容` button only increases that + in-memory window by 100. Its scroll handler merely tracks stick-to-bottom; + it does not request older history. +- The public Snapshot contract and `/snapshot` route expose no history-page + cursor or `before` parameter. After the one in-memory expansion reaches all + 195 projected nodes, no additional Main/Pi request is possible even though + the omitted records are still on disk. +- No product source, dependencies, runtime, account, session, or external + service were changed. + +## Verification + +- Parsed the affected `.makelore/conversations.json` binding and its exact + managed Pi JSONL without writing either file. +- Counted JSONL record types, reconstructed the current leaf-to-root active + path, checked the compaction boundary, and counted retained versus omitted + messages/product nodes. +- Inspected `CodingConversationTimeline`, the Renderer conversation Store, + shared Snapshot contract, Coding Host route, Pi runtime hydration, + `session-projector`, and Agent Server `get_entries` behavior. +- Confirmed current unit coverage explicitly expects pre-`firstKeptEntryId` + history to be absent and covers only in-memory window expansion. +- Searched the current Main log and found no `CODING_SESSION_UNREADABLE`, + missing-entry, or history-load error for this incident. +- No tests were run because this was a read-only diagnosis with no behavior + change; the isolated worktree has no installed `node_modules`. + +## Follow-ups + +- If a fix is requested, keep the full active-branch history available for UI + display independently of the compacted model-context tail. Add a bounded + older-history cursor/route (or, as a minimal correction, project the full + active path) and make Renderer prepend pages while preserving the scroll + anchor. Keep the compaction summary private and retain Snapshot/Patch as the + product-neutral boundary. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260831-fix-history-quota-errors-6c2a91e4.md b/.project-docs/30-worklog/tasks/20260831-fix-history-quota-errors-6c2a91e4.md new file mode 100644 index 0000000..745a9c0 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-fix-history-quota-errors-6c2a91e4.md @@ -0,0 +1,104 @@ +# Task: Fix Coding history loading and quota errors + +## Identity + +- Task ID: 20260831-fix-history-quota-errors-6c2a91e4 +- Mode: Feature +- Branch: codex/20260831-fix-history-quota-errors-6c2a91e4-fix-history-quota-errors-6c2a91e4 +- Worktree: /Users/inmanx/Documents/makelore-fix-history-quota-errors-6c2a91e4 +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Ready for integration + +## Scope + +- Restore access to the full persisted active-branch history of Makelore Code + conversations after Pi compaction, while keeping history display bounded in + the Renderer and preserving the user's scroll anchor when older content is + prepended. +- Recognize the observed upstream `403 token_point_balance_exhausted` contract + as a non-retryable Token Point quota failure and show actionable, safe + Chinese feedback instead of the generic `处理失败` summary. +- Add focused Main/projector/Renderer regression coverage and run the required + typecheck, lint, and production build verification. + +## Intent And Constraints + +- Preserve Pi `0.84.2` as the sole runtime and keep Pi session/wire shapes + inside Electron Main. Renderer continues to consume product-neutral + Snapshot/Patch data through the Host API. +- Full visible history must not change the compacted model context or expose + the private compaction summary. Accepted/uncertain mutations remain + non-replayed. +- Quota classification must be narrow enough not to reinterpret unrelated + `403`, `429`, or Provider failures, and raw upstream text must not leak into + Renderer state. +- The ready-for-integration progress-message task overlaps the timeline source + and test files but has no semantic conflict; keep changes localized to the + history-window and scrolling behavior. + +## Outcome + +- Pi session hydration now projects the complete active branch in chronological + order instead of dropping entries before the latest compaction boundary. + Compaction summaries remain private, and multiple durable compaction markers + reconcile against live IDs from newest to newest. +- The Renderer still mounts only the latest 120 nodes initially, automatically + prepends earlier nodes in 100-node steps near the top, and offsets + `scrollTop` by the added height so the current reading position stays fixed. +- The exact `403 token_point_balance_exhausted` / `词元点数余额不足` response is + classified as quota exhaustion, forwarded as non-retryable HTTP 402, and + projected as `CODING_PROVIDER_QUOTA_EXHAUSTED` with the safe message + `词元点数余额不足,请充值后重试。`. +- Live failures and persisted Pi assistant failures share the same redacting + classifier. Reopened conversations restore only the safe Provider notice; + raw upstream request IDs are not copied into the public Snapshot. +- The collapsed process summary displays the actionable public failure message + for both live runs and reopened persisted failures instead of generic + `处理失败`. + +## Verification + +- `pnpm exec vitest run tests/unit/pi-session-projector.test.ts tests/unit/coding-conversation-timeline.test.tsx tests/unit/ai-proxy-routes.test.ts tests/unit/pi-event-projector.test.ts` + - Passed: 4 files, 45 tests. +- `pnpm run typecheck` + - Passed. +- `pnpm run lint:check` + - Passed with 0 errors and 5 existing warnings in `src/pages/Home/index.tsx` + and `src/pages/Makelore/index.tsx`. +- `pnpm test` + - Passed: 211 regular files / 1,730 tests plus the isolated pressure file / + 1 test; 3 tests skipped. +- `pnpm run build:vite` + - Passed for Renderer, Electron Main, Preload, and release utility worker; + existing Vite dynamic-import and chunk-size warnings remain. +- `node ./node_modules/@playwright/test/cli.js test tests/e2e/pi-coding-first-chat.spec.ts` + - Passed: 2 Electron tests, including persisted quota feedback, automatic + older-history loading, and scroll-anchor preservation. +- `git diff --check` + - Passed. + +## Follow-ups + +- Integration should account for the ready progress-message task because both + tasks intentionally touch `CodingConversationTimeline.tsx` and its unit/E2E + coverage; the edited concerns are separate. + +## Promotion Candidates + +- Target canonical documents: `README.md` and the integrated current-state + summary. +- Proposal: state that Pi compaction changes model context but does not remove + visible active-branch history, and that recognized Provider quota failures + remain actionable after reopening a conversation. +- Evidence: focused projector/Renderer/proxy tests, the 1,731-test full unit + run including pressure coverage, the production build, and the two focused + Electron E2E scenarios all pass. +- Future impact: troubleshooting and future history work should preserve the + distinction between Pi context retention and product-visible session + history, and should reuse the redacted Provider-failure projection. +- Semantic conflicts: none known; the peer progress-message task overlaps the + timeline files but not these semantics. +- Human confirmation required: no product-direction choice is known to be + required, but the integration owner must decide whether this detail belongs + in canonical user-facing documentation. diff --git a/.project-docs/30-worklog/tasks/20260831-push-main-4f8c2a91.md b/.project-docs/30-worklog/tasks/20260831-push-main-4f8c2a91.md new file mode 100644 index 0000000..01467ba --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-push-main-4f8c2a91.md @@ -0,0 +1,82 @@ +# Task: Integrate all local changes and push main + +## Identity + +- Task ID: 20260831-push-main-4f8c2a91 +- Mode: Integration +- Branch: codex/20260831-push-main-4f8c2a91-push-main +- Worktree: /Users/inmanx/Documents/makelore-push-main-4f8c2a91 +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Completed + +## Scope + +- Audit every local worktree, local branch, uncommitted path and the latest + `origin/main` before pushing. +- Integrate all confirmed current product changes into local `main`, preserve + current architecture decisions, and push the verified result to `origin/main`. +- Keep peer task-owned records read-only and do not treat diagnostic-only task + records as product source changes. + +## Intent And Constraints + +- Work only from this isolated Integration Gate worktree while the occupied + `main` worktree remains owned by another completed review task. +- Do not restore the retired Learning course runtime or OpenMAIC player without + explicit confirmation that ADR-005 is being reversed. +- Fetch before integration and verify the remote ref after any authorized push. +- Preserve the Main-owned Host API, Canvas V2, `.makelore` authority, Pi single + runtime and current four-module light-product boundaries. + +## Outcome + +- Fetched `origin`; remote `main` remains + `62304dc85b3c1069cd656dfacb61ee820e216fa2`, while local `main` is + `38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5` and is ahead by three commits. +- Audited all seven local worktrees. Their only uncommitted paths are task-owned + project records; no uncommitted product source exists. +- Found exactly one local product commit not ancestral to `main`: + `b1f51beb13d2efd46ff2c62865f481fe4b52cb4c` on + `codex/learning-player-release-fix`. The clean branch is one commit ahead and + 280 commits behind current `main`. +- The commit changes the retired OpenMAIC Learning Player artifact download, + synchronization, manifest, README contract and packaging tests. Current main + deliberately removed that runtime and those scripts under accepted ADR-005, + so integrating it would reverse current product behavior rather than apply an + ordinary packaging fix. +- The user explicitly chose to exclude `b1f51be`. ADR-005 remains authoritative, + and the obsolete Learning Player branch will not be merged or pushed. +- Fast-forward pushed local `main` from remote + `62304dc85b3c1069cd656dfacb61ee820e216fa2` to verified product tip + `38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5`. +- Queried the remote ref after the push and fetched it again; local `main` and + `origin/main` now resolve to the same object with divergence `0 0`. +- Peer diagnostic/review task records remain in their owning worktrees and were + not promoted as product source. No remote branch other than `main` changed. + +## Verification + +- `git fetch --prune origin`: passed. +- Worktree and branch audit: seven worktrees checked; one unmerged product commit + identified. +- Inspected the complete five-file patch for `b1f51be` and compared it with the + current Learning catalog boundary, ADR-005 and integrated current state. +- `git merge-base --is-ancestor origin/main main` before push: passed. +- `git diff-tree --check origin/main main` before push: passed. +- The pushed product tree is the exact immutable tree already verified by task + `20260831-merge-upstream-main-7c3a91f2`: typecheck passed; 1,726 unit tests + passed with 3 staged-runtime skips; lint passed with zero errors; Vite build + passed; focused Electron E2E passed 13/13. No product file changed after that + verification, so the suites were not redundantly rerun for the network push. +- Remote verification: `ls-remote` and a fresh fetch both returned + `38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5`; divergence is `0 0`. + +## Follow-ups + +- None required for this push. The excluded Learning Player branch remains local + historical state and must not be merged unless ADR-005 is explicitly reversed. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260831-review-local-update-5b8c21.md b/.project-docs/30-worklog/tasks/20260831-review-local-update-5b8c21.md new file mode 100644 index 0000000..344ea34 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-review-local-update-5b8c21.md @@ -0,0 +1,77 @@ +# Task: Review updated local plugin integration + +## Identity + +- Task ID: 20260831-review-local-update-5b8c21 +- Mode: Feature +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Inspect the locally merged Marketplace client, Package Store, effective Plugin + resolver, Pi Agent Server, extension host, and Provider projection. +- Reassess whether the existing plugin-management lifecycle can carry + `pi-web-search` without adding a second management subsystem. +- Make no product-source or dependency changes. + +## Intent And Constraints + +- Treat the current `main` source as authoritative over the earlier isolated + network/search assessments, whose base predates the local Marketplace and + shared-Agent-Server merge. +- Preserve Electron Main ownership of package trust, Provider credentials, + proxy behavior, runtime authorization, and packaged closure. +- Distinguish a Marketplace Skill package from executable Pi extension code. + +## Outcome + +- Confirmed that the local merge now has an end-to-end Main-owned Marketplace + lifecycle: catalog/library client, signed immutable Package Store, project + enablement, Agent Skill assignment, effective snapshot, and worker resource + materialization. A second plugin-management subsystem is unnecessary. +- Confirmed that Release A artifact installation is deliberately limited to + `skill_only`: installed records and trusted metadata force that runtime kind, + archives accept only manifests plus static Skill text/image assets, and + executable/script components are rejected. +- Confirmed that the shared Pi Agent Server disables ambient extension discovery + and requires exactly one code-owned Makelore extension factory per logical + thread. Therefore `pi install npm:pi-web-search` or an unchanged Marketplace + artifact cannot currently activate `pi-web-search`. +- Confirmed that `pi-web-search` 1.3.1 is a third-party executable TypeScript Pi + extension listed on pi.dev. It supports Google Generative AI, OpenAI Responses, + OpenAI Codex Responses, and Anthropic Messages; the default Works-imported + account remains projected as `openai-completions`, so that default path would + be rejected by the extension even after loading. +- Recommended reusing the existing lifecycle while treating Web Search as a + reviewed, pinned, system-included capability (or adapting it behind the + Main-owned tool bridge), rather than opening Release A to arbitrary executable + Marketplace packages for a single integration. + +## Verification + +- Read current source across `electron/coding-plugins`, + `electron/coding-runtime/pi`, `resources/pi-agent-server.mjs`, Provider routes, + and the local AI proxy. +- Checked upstream `pi-web-search` 1.3.1 package metadata and source from pi.dev, + GitHub, and npm. +- Focused Vitest run passed: 5 files / 62 tests covering manifest parsing, + effective resolution, Marketplace client, composition, and the real shared + Agent Server process. +- The workspace fallback `pnpm` is 11.19.0 rather than the repository-pinned + 10.33.4 and aborted before changing dependencies; focused tests were therefore + run directly with the bundled Node executable and existing Vitest install. + +## Follow-ups + +- If implementation is requested, choose the trusted bundled-capability route + and define how a logical thread exposes its selected Provider/model to the + Main-owned search adapter. Do not add an executable Marketplace runtime kind + without a separate architecture/security decision. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260831-show-progress-messages-a14f9c2d.md b/.project-docs/30-worklog/tasks/20260831-show-progress-messages-a14f9c2d.md new file mode 100644 index 0000000..cfef58e --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-show-progress-messages-a14f9c2d.md @@ -0,0 +1,84 @@ +# Task: Show assistant progress messages + +## Identity + +- Task ID: 20260831-show-progress-messages-a14f9c2d +- Mode: Feature +- Branch: codex/20260831-show-progress-messages-a14f9c2d-show-progress-messages-a14f9c2d +- Worktree: /Users/inmanx/Documents/makelore-show-progress-messages-a14f9c2d +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Identify the protocol and UI classification of assistant text emitted before + or between tool calls, using the supplied screenshot only as a visual example. +- Verify that this stage/progress narration is projected and rendered in the + current Makelore Code timeline without exposing Pi-private wire types. +- Preserve per-item expand/collapse after a stage explanation has completed, + including after the enclosing turn has settled. +- Make every collapsed thinking, stage-explanation, and tool-output preview + start from its first visible block and line, including horizontally + overflowing text. + +## Intent And Constraints + +- Preserve the product-neutral `ConversationMessageNode` / Snapshot / Patch + contract and the Main-owned Pi boundary. +- Keep private reasoning distinct from user-visible assistant narration and + tool execution output. +- Do not modify the occupied primary worktree or the separate running + development worktree. + +## Outcome + +- Confirmed the raw message is an ordinary Pi assistant message whose turn ends + with `stopReason: toolUse`; Main normalizes it to an assistant + `ConversationMessageNode` with `stopReason: tool-use`. +- Confirmed the Renderer semantically projects non-final assistant text as + `assistant-commentary` / “过程说明”, rather than treating it as hidden + thinking or as a tool node. +- Confirmed active process groups open automatically and show a compact + one-line stage explanation with an expand control; settled process groups + retain the explanation behind their collapsed process summary while the + final assistant answer remains visible. +- Added explicit regression coverage proving a completed stage explanation + remains collapsed to its first line by default, retains its own expand + control, reveals the complete Markdown body, and can be collapsed again. +- Changed collapsed process preview selection to use the first displayable + block and first non-empty line for thinking, assistant commentary, and tool + output. Long lines retain their beginning and use a trailing ellipsis. +- Kept every collapsed preview viewport at horizontal offset zero on content + and size changes. Tail-only streaming updates no longer replace or replay an + unchanged head preview, while expanded content remains complete. + +## Verification + +- Installed the existing lockfile with pinned pnpm `10.33.4`; 1,015 packages + were reused from the local store and no manifest or lockfile changed. +- Focused Vitest passed: 3 files / 28 tests covering live Pi event projection, + durable session hydration, and Coding timeline process/commentary rendering. +- Follow-up focused Vitest passed: 1 file / 9 tests, including completed + commentary expand/collapse and stable head-preview assertions across + streaming updates. +- `pnpm run typecheck`: passed. +- Scoped ESLint for the changed Renderer, unit, and Electron E2E files: passed. +- `pnpm run build:vite`: passed; only the repository's existing dynamic-import + and bundle-size warnings were emitted. +- Focused Electron Playwright E2E passed: 1 file / 2 tests, including first-line + thinking display, head truncation, and a measured tool-preview + `scrollLeft` of zero. +- Inspected `electron/coding-runtime/pi/event-projector.ts`, + `electron/coding-runtime/pi/session-projector.ts`, + `shared/coding-conversation-contracts.ts`, and + `src/pages/Chat/CodingConversationTimeline.tsx` across the complete + classification path. + +## Follow-ups + +- None. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260831-start-dev-91c4e2a7.md b/.project-docs/30-worklog/tasks/20260831-start-dev-91c4e2a7.md new file mode 100644 index 0000000..14b3d09 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-start-dev-91c4e2a7.md @@ -0,0 +1,62 @@ +# Task: Start Makelore development environment + +## Identity + +- Task ID: 20260831-start-dev-91c4e2a7 +- Mode: Feature +- Branch: codex/20260831-start-dev-91c4e2a7-start-dev +- Worktree: /Users/inmanx/Documents/makelore-start-dev-91c4e2a7 +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Running + +## Scope + +- Start the pushed `main` product in an isolated worktree without disturbing + the concurrently occupied primary worktree. +- Use the repository-pinned pnpm version and frozen lockfile. +- Keep the Vite, Electron Main, Host API and Electron window processes running + for the user. + +## Intent And Constraints + +- Run the standard `pnpm run dev`; do not enable retired local Canvas semantics + or alter product source. +- Preserve the cloud-only Works Square V2 Canvas contract and current Main-owned + runtime boundaries. +- Retain task ownership while the development process is live so another task + cannot mutate this worktree underneath it. + +## Outcome + +- Created isolated worktree + `/Users/inmanx/Documents/makelore-start-dev-91c4e2a7` from pushed + `main@38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5`. +- Installed dependencies with exact `pnpm@10.33.4` and + `pnpm install --frozen-lockfile`. +- Started `pnpm run dev` in persistent terminal session `59768`. +- Vite is listening on IPv6 loopback `http://localhost:5173`; Electron Main's + Host API is listening on `http://127.0.0.1:13210`; Electron PID is `65509`. +- Startup selected the Works Square V2 cloud Canvas contract. The application + remains running. + +## Verification + +- Vite initial compilation completed in 1.679 seconds; Main, Preload and release + utility development bundles built successfully. +- `curl http://localhost:5173/`: HTTP 200. +- `curl http://[::1]:5173/`: HTTP 200. +- `curl http://127.0.0.1:13210/`: expected HTTP 401 authentication challenge. +- `lsof`: Node PID `65494` listens on `[::1]:5173`; Electron PID `65509` + listens on `127.0.0.1:13210`. +- The macOS login-item permission warning is non-fatal and does not affect the + running application. + +## Follow-ups + +- Stop terminal session `59768` when the user asks to stop the development app, + then complete and release this task ownership. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260831-start-platform-7c9e2a41.md b/.project-docs/30-worklog/tasks/20260831-start-platform-7c9e2a41.md new file mode 100644 index 0000000..33b4a97 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260831-start-platform-7c9e2a41.md @@ -0,0 +1,54 @@ +# Task: Start Makelore platform + +## Identity + +- Task ID: 20260831-start-platform-7c9e2a41 +- Mode: Feature +- Branch: codex/20260831-start-platform-7c9e2a41-start-platform-7c9e2a41 +- Worktree: /Users/inmanx/Documents/makelore-start-platform-7c9e2a41 +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Start the Makelore Electron development platform from an isolated linked + worktree based on the current `main` commit. +- Prepare dependencies only as required for startup; make no product-source or + dependency-version changes. + +## Intent And Constraints + +- Do not disturb the occupied primary worktree or its ready-for-integration + plugin review task. +- Use the repository-pinned pnpm `10.33.4` with `--frozen-lockfile`. +- Keep the development process running after verification. + +## Outcome + +- Installed the existing lockfile dependency graph in the isolated worktree + using pnpm `10.33.4`; all 1,015 packages were reused from the local store. +- Started `pnpm run dev`. Vite serves the Renderer at + `http://localhost:5173/`, Electron Main listens on Host API port `13210`, and + the Pi Agent Server reported `server.ready`. +- No product source, package manifest, or lockfile was changed. + +## Verification + +- `pnpm install --frozen-lockfile`: passed with pnpm `10.33.4`. +- Vite development build: ready; Main, Preload, and utility worker builds + completed. +- `curl http://localhost:5173/`: HTTP 200 over `::1`. +- Electron PID `98444` and Pi Agent Server PID `98504` remained alive after + startup. +- Non-blocking development warning: macOS denied changing the login item; + startup continued and launch-at-startup remained disabled. + +## Follow-ups + +- Keep the active development process running until the user stops it or asks + for a restart. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260901-close-main-sync-91d4e7c2.md b/.project-docs/30-worklog/tasks/20260901-close-main-sync-91d4e7c2.md new file mode 100644 index 0000000..76e2c39 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260901-close-main-sync-91d4e7c2.md @@ -0,0 +1,52 @@ +# Task: Close local main sync documentation gate + +## Identity + +- Task ID: 20260901-close-main-sync-91d4e7c2 +- Mode: Integration +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: e92b1d2f33094a8687966d76aef535fecd5e2632 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Close the Task Documentation Gate after the remote fast-forward and its + source task records are already present in the recorded base. +- Verify the final local `main` topology, task ownership boundaries, and the + absence of product changes introduced by the documentation closure. + +## Intent And Constraints + +- Own `main` and the repository Integration lock at base `e92b1d2`. +- Do not change product code, shared canonical memory, source task records, + remote refs, deployment state, or production activation gates. +- Keep the adopted review record byte-preserved and locally excluded from Git + tracking; do not delete, move, or commit it. + +## Outcome + +- Confirmed local `main` contains remote tip + `bf32fecd6013101485434b1977a73beb8b6a1fe3` and the nine fetched commits. +- Confirmed the only local commits after `origin/main` are task-documentation + closure records; no product file was changed locally after the fast-forward. +- No push, deployment, publication, or production operation was performed. + +## Verification + +- `origin/main` is an ancestor of local `main`. +- `git diff --check 38f85f6..bf32fec` passed for the fetched history. +- Before documentation closure, `HEAD` and `origin/main` both resolved to + `bf32fecd6013101485434b1977a73beb8b6a1fe3`, and the ahead/behind count was + `0 0`. +- No code tests were rerun because this closure adds only its own task record; + the fetched product history remains byte-identical to `origin/main`. + +## Follow-ups + +- Push the local documentation commits only if explicitly requested. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260901-diagnose-session-stall-c398db33.md b/.project-docs/30-worklog/tasks/20260901-diagnose-session-stall-c398db33.md new file mode 100644 index 0000000..4ff9c8f --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260901-diagnose-session-stall-c398db33.md @@ -0,0 +1,69 @@ +# Task: Diagnose in-app Coding session stall + +## Identity + +- Task ID: 20260901-diagnose-session-stall-c398db33 +- Mode: Feature +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: 8062b45103be45ae036167d3e9890c3ca9ba3e80 +- Owner: codex +- Status: Completed + +## Scope + +- Diagnose whether the visible Makelore Code run in project `肉鸽小游戏`, Agent + `小小明`, Conversation `新对话` is still doing useful work or is stuck. +- Correlate the supplied screenshot with the live Electron/Pi process, Main log, + project metadata, and persisted Pi session timeline. +- Make no product-code change and do not abort, recover, or otherwise mutate the + user's live Conversation. + +## Intent And Constraints + +- Treat the screenshot's earlier Token Point failures separately from the latest + `继续` turn; do not infer that a historical error is the current terminal cause. +- Preserve Main ownership of runtime state and the rule that accepted work is not + automatically replayed. +- Use only read-only runtime diagnostics. Do not expose credentials, raw upstream + request details, or unrelated Conversation content. + +## Outcome + +- Confirmed that the visible run is stuck in its terminal-settlement path rather + than continuing model or tool work. +- The latest turn began at 11:50:10 local time. All observed upstream requests for + that turn returned HTTP 200, its tools completed, and Pi persisted a final + assistant message with `stopReason: stop` at 11:55:38. +- The screenshot was captured at 12:02:14 while the UI still displayed + `处理中 12 分 3 秒` and the stop control. At 12:10:08 the Pi session and Main log + were still unchanged, the shared Agent Server remained alive but idle at 0% CPU, + and it held no TCP connection. +- The earlier `词元点数余额不足` rows are historical failed branches and did not + cause the latest run to wait. The effective failure boundary is after final + `message_end`: the run did not become idle, so `agent_settled` was either not + emitted/delivered or did not take effect in Main projection. +- No live run, process, project file, or product source was changed. + +## Verification + +- Correlated project/Agent/Conversation IDs from the local schema-v2 metadata. +- Compared screenshot mtime, Pi JSONL mtime, Main log mtime, and wall-clock time. +- Inspected the final persisted Pi message and the preceding completed tool results. +- Checked the shared Agent Server process state, CPU time, and open TCP sockets. +- Checked ADR-006 and the current event projector: `message_end` completes the + assistant node, while authoritative `agent_settled` is required to set the run + idle and release run ownership. + +## Follow-ups + +- If a fix is requested, reproduce a long real tool-use turn whose final assistant + message is persisted but settlement is missing, then harden the Agent Server/Main + terminal handshake without replaying the accepted prompt. +- Add regression coverage for final `message_end` followed by missing/delayed + `agent_settled`, including safe recovery and release of run/background ownership. + +## Promotion Candidates + +- None. This task records a live diagnosis only; any product behavior change needs a + separate implementation task and verification. diff --git a/.project-docs/30-worklog/tasks/20260901-finalize-all-local-7c3e9a2d.md b/.project-docs/30-worklog/tasks/20260901-finalize-all-local-7c3e9a2d.md new file mode 100644 index 0000000..e3811fc --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260901-finalize-all-local-7c3e9a2d.md @@ -0,0 +1,70 @@ +# Task: Finalize all-local integration + +## Identity + +- Task ID: 20260901-finalize-all-local-7c3e9a2d +- Mode: Integration +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: fac08e975de0446ede55882268155efbfadc58b7 +- Owner: codex +- Status: Completed + +## Scope + +- Certify the post-intake `main` tree after every semantically compatible local + source branch and task-owned record has been integrated. +- Confirm the user-selected exclusion of the obsolete Learning Player branch, + the canonical documentation promotion, and the running merged application. +- Run the task-aware project-document checks from a base that already contains + the immutable source records, then close and release the Integration Gate. + +## Intent And Constraints + +- Preserve ADR-005 and do not merge or modify + `codex/learning-player-release-fix@b1f51be`. +- Make no further product behavior change; the verified intake tree at + `fac08e9` is the finalization base. +- Keep `main` local. No push, deployment, publication, branch deletion, or + worktree deletion is authorized. + +## Outcome + +- Confirmed that the Plugin Services consolidation, Coding history/quota fix, + assistant progress-preview fix, all five formerly dirty source worktrees, + both documentation-only local branches, and the preserved plugin-review + record are represented by `main`. +- Confirmed that the only local branch not ancestral to `main` is + `codex/learning-player-release-fix@b1f51be`; it remains clean, unchanged, and + intentionally excluded by the user's explicit decision. ADR-005 remains the + accepted Learning product boundary. +- Canonical `README.md`, `current-state.md`, and `module-map.md` now describe + Project Configuration as the sole Plugin Services surface, full visible + active-branch history across Pi compaction, safe persistent quota feedback, + and first-line anchored collapsed process previews. +- The merged development application remains running from local `main`. + +## Verification + +- Direct ancestry checks passed for all eight integrated local source/document + branches; the Learning Player ancestry check returned false as required, and + the formerly excluded plugin-review record is now tracked. +- Intake verification from task `20260901-integrate-all-local-4d7a9c21` passed: + typecheck; 7 focused Vitest files / 64 tests; full unit suite of 1,732 tests + with 3 skips; lint with 0 errors; Renderer/Main/Preload/utility Vite build; + and 5 focused Electron E2E scenarios. +- Runtime probes from the intake task returned Vite HTTP 200 and the expected + unauthenticated Host API HTTP 401 challenge on ports 5173 and 13210. +- `check_project_docs.py`: passed. +- `check_doc_drift.py --task-id 20260901-finalize-all-local-7c3e9a2d`: + passed from the post-intake base with only this task's own record changed. +- `git diff --check` and `git diff --check origin/main..main`: passed. +- Final runtime probes again returned Vite HTTP 200 and Host API HTTP 401. + +## Follow-ups + +- None. Push remains a separate user-authorized operation. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260901-integrate-all-local-4d7a9c21.md b/.project-docs/30-worklog/tasks/20260901-integrate-all-local-4d7a9c21.md new file mode 100644 index 0000000..e2abe58 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260901-integrate-all-local-4d7a9c21.md @@ -0,0 +1,101 @@ +# Task: Integrate all local changes into main + +## Identity + +- Task ID: 20260901-integrate-all-local-4d7a9c21 +- Mode: Integration +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: 9ed70ba925a7e544f17bf5b418846df9d4145a2a +- Owner: codex +- Status: Ready for finalization + +## Scope + +- Audit every local branch, linked worktree, tracked modification, and + untracked task-owned file that is not already represented by `main`. +- Commit complete source-task changes in their owning worktrees, then integrate + all semantically compatible local product and documentation changes into + local `main`. +- Preserve the consolidated Plugin Services navigation, Coding history/quota + correction, and assistant progress-message behavior together when resolving + their overlapping Renderer and test files. +- Synchronize canonical product documentation, run repository-required + verification, and restart the merged development application. + +## Intent And Constraints + +- Preserve the current four-module, light-only product boundary, Main-owned + runtime/Host API architecture, `.makelore/project.json` authority, and the + accepted removal of the Learning course runtime/OpenMAIC player under + ADR-005. +- Source task records are immutable from this Integration Gate; source commits + must be completed in their owning worktrees before they are merged. +- Do not mechanically choose one side of a semantic conflict. The historical + `codex/learning-player-release-fix` branch is retained unchanged because it + would reverse ADR-005 and was explicitly excluded by the earlier integration + record; merging it requires a new explicit product-direction decision. +- Do not push `main`; this task is a local integration unless the user separately + authorizes a remote update. + +## Outcome + +- Committed the complete ready source-task work on its owning branches: + `d523b72` restores visible Coding history and safe quota feedback, + `8d878eb` preserves assistant progress previews, and `2c9f741`, `1f2e35c`, + and `82415ba` retain diagnosis/startup task records. +- Merged the two-commit Plugin Services consolidation, both Coding behavior + branches, and every compatible local documentation-only branch into `main`. + Their overlapping timeline and Electron E2E files merged without text + conflicts and passed combined verification. +- Preserved the previously excluded local plugin-integration review record as + tracked history. All source worktrees are clean. +- Restarted the merged development application from `main`; Vite is live at + `http://localhost:5173/` and Electron Main owns Host API port `13210`. +- Exactly one local branch remains outside `main`: + `codex/learning-player-release-fix@b1f51be`. Its five-file patch restores the + retired OpenMAIC Learning Player packaging supply chain and contradicts + ADR-005. On 2026-09-01 the user explicitly confirmed that this branch must + remain excluded, so ADR-005 stays authoritative and the branch is preserved + unchanged as historical local state. +- Promoted the accepted Plugin Services, complete visible history, quota + feedback, and first-line process-preview facts into `README.md`, + `current-state.md`, and `module-map.md`; no accepted ADR was reversed. + +## Verification + +- Focused Vitest: 7 files / 64 tests passed across Plugin Services, history, + quota classification, Pi projection, and combined timeline behavior. +- `pnpm run typecheck`: passed. +- `pnpm run lint:check`: passed with zero errors. It reported 12 warnings: the + repository's existing warnings plus duplicates from the nested historical + Learning worktree scanned by ESLint. +- `pnpm test`: passed, 212 regular files / 1,728 tests with 3 skips, plus the + isolated pressure file / 1 test (1,732 tests total). +- `pnpm run build:vite`: passed for Renderer, Electron Main, Preload, and the + release utility worker; existing dynamic-import, Browserslist-age, and large + chunk warnings remain non-fatal. +- Focused Electron E2E: 5/5 passed across Plugin Services discovery/library, + project enablement, Coding history, quota feedback, and process previews. +- Runtime probes: Vite returned HTTP 200; the Host API returned its expected + unauthenticated HTTP 401 challenge; Node PID 39837 and Electron PID 39847 + listen on ports 5173 and 13210 respectively. +- `check_project_docs.py`: passed. +- `git diff --check`: passed after canonical promotion. +- `check_doc_drift.py --task-id 20260901-integrate-all-local-4d7a9c21`: + correctly blocked this intake task because nine immutable source-task + records entered through the merges after its base. No source record was + edited; a successor Integration Gate will use this complete intake as its + base and perform the final task-aware certification. + +## Follow-ups + +- Open a fresh finalization Integration Gate based after these source-task + merges. This task intentionally introduced foreign immutable task records, + so the task-aware drift checker must certify the final tree from the + post-intake base rather than misattribute those records to canonical edits. + +## Promotion Candidates + +- Applied to `README.md`, `current-state.md`, and `module-map.md`; no remaining + promotion candidate is known. diff --git a/.project-docs/30-worklog/tasks/20260901-pull-main-6f2a9c1d.md b/.project-docs/30-worklog/tasks/20260901-pull-main-6f2a9c1d.md new file mode 100644 index 0000000..087c039 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260901-pull-main-6f2a9c1d.md @@ -0,0 +1,62 @@ +# Task: Pull remote and fast-forward main + +## Identity + +- Task ID: 20260901-pull-main-6f2a9c1d +- Mode: Integration +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: 38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5 +- Owner: codex +- Status: Transferred to Final Integration Gate + +## Scope + +- Fetch `origin` and fast-forward the local `main` branch from `38f85f6` to + the verified remote `origin/main` tip. +- Preserve the adopted untracked review task record and make no independent + product-source changes. + +## Intent And Constraints + +- Use a fast-forward-only merge so this task cannot synthesize an unreviewed + merge commit or resolve content conflicts implicitly. +- Keep all existing local task records intact and verify branch ancestry, + remote parity, and worktree state after the update. + +## Outcome + +- Fetched `origin` and fast-forwarded local `main` by nine commits, from + `38f85f6b5e4dc4e2c5e5b9f8f4506554cfd578f5` to + `bf32fecd6013101485434b1977a73beb8b6a1fe3`. +- The update used `git pull --ff-only origin main`; no conflict resolution or + additional merge commit was created. +- Preserved the adopted untracked review record + `20260831-review-local-update-5b8c21.md` unchanged. +- The task-aware drift check correctly identified the five immutable source + task records and product files introduced by the remote fast-forward as + changes after this task's recorded base. Those files were not edited or + re-authored by this task; final ownership transfers to a fresh Integration + task whose base already contains the remote history. + +## Verification + +- `git rev-parse HEAD` and `git rev-parse origin/main` both returned + `bf32fecd6013101485434b1977a73beb8b6a1fe3`. +- `git rev-list --left-right --count main...origin/main` returned `0 0`. +- `git merge-base --is-ancestor 38f85f6 HEAD` passed. +- `git diff --check 38f85f6..HEAD` passed. +- No code tests were rerun because this task performed an exact fast-forward of + the already-committed remote history and introduced no independent source + edits. +- The first task-aware documentation drift check was blocked by the imported + remote source records, as required by the ownership boundary. + +## Follow-ups + +- Close the documentation gate from a fresh Integration task based on the + synchronized `main` tip; do not push remote history unless explicitly asked. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260901-start-dev-3a8f2c91.md b/.project-docs/30-worklog/tasks/20260901-start-dev-3a8f2c91.md new file mode 100644 index 0000000..0129558 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260901-start-dev-3a8f2c91.md @@ -0,0 +1,78 @@ +# Task: Start current Makelore development environment + +## Identity + +- Task ID: 20260901-start-dev-3a8f2c91 +- Mode: Feature +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: c515e3bc75de845089ba182ae66917c84d2cfbca +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Prepare dependencies for the current local `main` checkout and start the + Makelore Electron development environment. +- Verify the Renderer, Electron Main Host API, and supporting local runtime are + alive, then keep the development process running for the user. + +## Intent And Constraints + +- Use the repository-pinned `pnpm@10.33.4` and `--frozen-lockfile`. +- Make no product-source, manifest, lockfile, dependency-version, account, or + external-service changes. +- Start from `/Users/inmanx/Documents/makelore`; do not reuse the older + development worktree or its stale task context. + +## Outcome + +- Installed the existing lockfile dependency graph with repository-pinned + `pnpm@10.33.4`; the lockfile was already current and no dependency version + changed. +- Started `pnpm run dev` from the current `main` checkout and retained the live + terminal session `4255`. +- Vite serves the Renderer on `http://localhost:5173/`, and Electron Main owns + the Host API listener on `127.0.0.1:13210`. +- Electron application PID `30847` and its helper processes remained alive + throughout the requested development session. +- Follow-up UI diagnosis confirmed that current `main` does not contain the + completed local Plugin navigation consolidation. Commits `b3f4166` and + `e237941` remain only on + `codex/20260831-consolidate-plugin-navigation-3d9a6c71-consolidate-plugin-navigation`. +- Stopped terminal session `4255`, Vite, Electron, and the Pi Agent Server at + the start of the user-requested all-local-changes integration so files can be + merged and verified from a quiescent worktree. + +## Verification + +- `pnpm install --frozen-lockfile`: passed with pnpm `10.33.4`. +- Vite reported ready and completed the Electron Main, Preload, and release + utility worker development builds. +- `curl http://localhost:5173/`: HTTP 200; Vite listens on IPv6 loopback + `[::1]:5173`. +- `lsof` confirmed Electron PID `30847` listening on `127.0.0.1:13210`. +- `package.json` and `pnpm-lock.yaml` remain unchanged. +- Non-blocking warnings: macOS denied changing the login item, and the local + Browserslist database reports that it is six months old. +- `git cherry main ` marked both Plugin navigation + commits with `+`; neither patch is present on `main`. +- The refreshed remote advertises only `main` at `bf32fec` and + `codex/performance-optimization`; no remote ref contains either Plugin + navigation commit. +- Current `main` still declares standalone `/plugin-marketplace`, + `/my-plugins`, and `/project-plugins` routes and the two Plugin sidebar + buttons. The unmerged branch removes those buttons and redirects the routes + into Project Configuration's Plugin Services tabs. + +## Follow-ups + +- Restart the development environment after the requested local integration is + complete and verified. +- If requested, integrate the two Plugin navigation commits onto current + `main`, reconcile them with the newer hosted-Plugin history, rerun focused + UI/E2E verification, and restart the development environment. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260902-build-unsigned-mac-9d7e4a2c.md b/.project-docs/30-worklog/tasks/20260902-build-unsigned-mac-9d7e4a2c.md new file mode 100644 index 0000000..9c0d65c --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-build-unsigned-mac-9d7e4a2c.md @@ -0,0 +1,70 @@ +# Task: Build unsigned macOS installer + +## Identity + +- Task ID: 20260902-build-unsigned-mac-9d7e4a2c +- Mode: Feature +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: 301c1496b0a3a1af3b9443a68a67de8dfea45749 +- Owner: codex +- Status: Ready for integration + +## Scope + +- Build a fresh unsigned macOS arm64 installer from the current `main` worktree. +- Produce a local-only DMG artifact under ignored `release/`; do not publish. +- Verify architecture, absence of a trusted code signature, and basic DMG contents. +- Diagnose the installed package's `CODING_RUNTIME_UNAVAILABLE` failure, fix the packaged Agent Server bootstrap, and rebuild a locally usable artifact. + +## Intent And Constraints + +- Use the exact pnpm version pinned by `packageManager` through Corepack. +- Stage the bundled Python, uv, and Pi runtime required by the packaged app. +- Disable macOS signing identity discovery and notarization explicitly. +- Limit the product fix to packaged Agent Server dependency resolution and a focused real-process regression test; do not change release configuration, app identity, backend contracts, or canonical project memory. +- Build only the current Apple Silicon (`arm64`) architecture because the user requested a package for local use on this Mac. + +## Outcome + +- The first unsigned installer opened but its Code module reported `CODING_RUNTIME_UNAVAILABLE`. The installed app log identified `ERR_MODULE_NOT_FOUND` for `@earendil-works/pi-ai` from `Contents/Resources/resources/pi-agent-server.mjs`. +- Root cause: Node 24 did not honor the parent URL passed to `import.meta.resolve`, so the packaged Agent Server resolved the package from the sibling product-resources tree instead of `Contents/Resources/pi-runtime/node_modules`. +- Updated `resources/pi-agent-server.mjs` to locate the package manifest from the configured runtime root with `findPackageJSON`, select its import entry, validate that the entry remains inside the package, and import that explicit file URL. +- Added a real-process regression test that copies the server resource outside the project module tree, reproducing the packaged directory relationship while using the staged runtime root. +- Rebuilt the corrected artifact as both `/Users/inmanx/Documents/makelore/release/Makelore-2.0.0-mac-arm64.dmg` and the identical, user-facing `/Users/inmanx/Documents/makelore/release/Makelore-2.0.0-mac-arm64-fixed.dmg`. +- The corrected installer is 315,653,270 bytes with SHA-256 `6d0216da6c30f7fed537041b37c69811b8e025af3e9cccf85a64c690e29ecb7b`. It supersedes the earlier unusable DMG and checksum recorded by this task. +- The final DMG-only electron-builder run exited successfully with `mac.identity: null`, `mac.notarize: false`, `--publish never`, and explicit `dmg`/`arm64` target selection. No Developer ID identity was used and no notarization or publication occurred. +- Release configuration, app identity, backend contracts, and canonical project-memory files were unchanged. + +## Verification + +- `corepack pnpm exec vitest run tests/unit/pi-agent-server-process-real.test.ts`: passed, 3/3 tests including the packaged-layout regression. +- `corepack pnpm run typecheck`: passed. +- `corepack pnpm run lint:check`: passed with 0 errors and 12 pre-existing warnings. +- `corepack pnpm test`: passed; 212 test files, 1,733 passed plus the separate pressure test, and 3 skipped. +- `corepack pnpm run build:vite`: passed; Vite reported only existing chunk/dynamic-import and Browserslist age warnings. +- `node scripts/bundle-pi-runtime.mjs --target darwin-arm64`: passed with 131 production packages and 7 runtime assets. +- `hdiutil verify release/Makelore-2.0.0-mac-arm64.dmg`: valid checksum for the corrected DMG. +- `file` and `lipo -archs` on the packaged executable: `Mach-O 64-bit executable arm64` / `arm64`. +- Effective builder config: `identity: null` and `notarize: false`; builder logged `skipped macOS code signing`. +- `codesign --verify` returned non-zero for the app and DMG, confirming no valid product/DMG signature. The unsigned Electron app retains upstream binary signature metadata, so this is not a signed or notarized distribution claim. +- Read-only DMG mount: passed; contains `Makelore.app`, the `/Applications` symlink, version `2.0.0`, and executable bundled uv, Python, and Pi runtime entries. +- `corepack pnpm run verify:artifact:pi -- --app-exe .../release/mac-arm64/Makelore.app/Contents/MacOS/Makelore --samples 1 --timeout-ms 10000`: top-level result `pass`; Pi `0.84.2`, target `darwin-arm64`, missing packages `[]`, missing assets `[]`, and the final executable runtime probe completed. The nested release-wide runtime report retains its pre-existing cross-platform and real-Provider waivers and therefore reports `partial-pass` rather than claiming a signed release gate. +- Direct JSONL initialize/shutdown against the unpacked corrected app's bundled Helper, Agent Server resource, and Pi runtime passed with exit code 0. +- Mounted the exact corrected DMG read-only and repeated the bundled Agent Server initialize/shutdown handshake directly from the mounted image: both responses succeeded, exit code 0, empty stderr. +- `corepack pnpm run smoke:pi:real ...` launched the packaged Electron Main and progressed through an active parent Agent Server and active child subagent. Its later macOS process-enumeration assertion remained unsupported (`processes.supported: false`), so this task does not claim that release-wide smoke gate as a full pass. +- `git diff --check`: passed. + +## Follow-ups + +- Replace the previously installed `/Applications/Makelore.app` with the corrected `-fixed.dmg` build before testing Code again; the old mounted installer volume still refers to the superseded artifact. +- This artifact is for local Apple Silicon use only. A signed/notarized macOS release, cross-platform evidence, real-Provider evidence, and macOS process-enumeration support remain outside this task. + +## Promotion Candidates + +- Target: `.project-docs/30-worklog/current-state.md` + Proposal: record that packaged Agent Server package resolution is anchored to the staged Pi runtime and that the unsigned arm64 local DMG passed an exact mounted-image initialize/shutdown handshake, without presenting it as signed-release readiness. + Evidence: `resources/pi-agent-server.mjs`, `tests/unit/pi-agent-server-process-real.test.ts`, and corrected artifact SHA-256 `6d0216da6c30f7fed537041b37c69811b8e025af3e9cccf85a64c690e29ecb7b`. + Future impact: future packaging changes should retain the runtime-root resolution regression test and distinguish local unsigned artifact usability from signed/notarized release readiness. + Semantic-conflict check: no conflict with the accepted packaging ADR because signed/notarized release readiness remains explicitly open. + Human confirmation required: no. diff --git a/.project-docs/30-worklog/tasks/20260902-check-session-after-bash-fix-a6d12f39.md b/.project-docs/30-worklog/tasks/20260902-check-session-after-bash-fix-a6d12f39.md new file mode 100644 index 0000000..324cab3 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-check-session-after-bash-fix-a6d12f39.md @@ -0,0 +1,84 @@ +# Task: Check latest session after Bash fix + +## Identity + +- Task ID: 20260902-check-session-after-bash-fix-a6d12f39 +- Mode: Feature +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: 774beee50867b1da3a25fda8fbff559f570d4e56 +- Owner: codex +- Status: Completed + +## Scope + +- Determine whether the newest visible Makelore Code turn after the write-leased + Bash batch fix is still making useful progress or is functionally stuck. +- Correlate the live UI with the target Pi JSONL timeline, Electron/Agent Server + logs, process activity, child processes, and sockets. +- Keep the diagnosis read-only: do not abort, recover, resend, or otherwise + mutate the current Conversation or project files. + +## Intent And Constraints + +- Distinguish normal model/tool latency from the previously fixed prepare-all + write-lease self-deadlock and from a missing terminal-settlement handshake. +- Treat the visible timer, model response, tool start/result, child-process + activity, and terminal settlement as separate evidence. +- Do not expose credentials, unrelated account data, or unrelated project + content in the task record or user-facing conclusion. + +## Outcome + +- Confirmed a user-visible stale-running state in project `肉鸽小游戏`, Agent + `小小明`, Conversation `新对话`. The UI advanced from 55 to 58 minutes of + processing and continued to show one `uv pip install` Bash call as executing. +- Confirmed that the displayed Bash call was not running or deadlocked. Its Pi + JSONL tool call began at 13:13:12 local time and recorded a successful result + at 13:13:33 with `requests` and `Pillow` installed. Later Bash and task-state + tools also succeeded, and Pi persisted the final assistant `stop` response at + 13:14:42. +- Electron Main stopped both idle logical workers and the parent Agent Server at + 13:14:55 with the intentional `background_sleep` reason. At inspection time + there was no Agent Server, Bash, `uv`, `tail`, or skill virtual-environment + child process, and the session JSONL had not changed since the final response. +- This is not a recurrence of the prepare-all write-lease deadlock. The durable + work completed; the Renderer retained a stale live Snapshot after background + sleep and continued animating an earlier tool call. +- The likely foreground-recovery gap is supported directly by the current code: + Main sends `lifecycle:sleep`, but the Renderer has no subscriber that invalidates + or refreshes Coding state. `selectConversation` also skips `getSnapshot` when + its cached entry remains `live`, so refocusing the existing window does not + hydrate the already-persisted final response. +- No UI action, Conversation mutation, process stop, retry, project-file change, + or source-code change was performed during diagnosis. + +## Verification + +- Read the live Electron accessibility tree twice without clicking or typing; + the timer increased while the same earlier Bash call remained focused. +- Correlated the exact target JSONL timeline with the current UI. The displayed + 120-second Bash call settled successfully in about 21 seconds, later tool + results succeeded, and the final assistant response has `stopReason: stop`. +- Correlated Main logs showing the final Provider stream at 13:14:42 and the + intentional `background_sleep` worker/Agent Server shutdown at 13:14:55. +- Inspected the live process table and target Agent Server PID; no runtime or + shell child remained while the UI reported processing. +- Inspected the Renderer store and lifecycle wiring: `lifecycle:sleep` is exposed + but not consumed, and a cached `live` Conversation bypasses Snapshot loading. + +## Follow-ups + +- If implementation is requested, invalidate/disconnect Coding event state on + `lifecycle:sleep` and force a target Snapshot refresh when the programming + window becomes visible again. Cover hidden-run completion, idle runtime sleep, + second-instance focus, persisted final hydration, and no accepted-mutation + replay in focused Renderer/Main tests. +- Immediate non-destructive recovery is to reload/restart the Renderer so it + hydrates the persisted final response. Do not rerun dependency installation; + that work already succeeded. + +## Promotion Candidates + +- None yet. Promote the foreground rehydration contract only after a focused fix + and regression tests are implemented and integrated. diff --git a/.project-docs/30-worklog/tasks/20260902-conversation-badge-a7c49e.md b/.project-docs/30-worklog/tasks/20260902-conversation-badge-a7c49e.md new file mode 100644 index 0000000..cb51662 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-conversation-badge-a7c49e.md @@ -0,0 +1,90 @@ +# Task: Limit conversation unread badge triggers + +## Identity + +- Task ID: 20260902-conversation-badge-a7c49e +- Mode: Feature +- Branch: codex/20260902-conversation-badge-a7c49e-conversation-badge-a7c49e +- Worktree: /Users/inmanx/Documents/makelore-conversation-badge-a7c49e +- Base commit: 301c1496b0a3a1af3b9443a68a67de8dfea45749 +- Owner: codex +- Status: Ready for integration + +## Scope + +- Limit the Makelore Code Conversation-list red unread marker to hidden + Conversations that newly require user interaction or whose current task has + reached a terminal result. +- Keep assistant streaming content, thinking, tool progress, and individual tool + failures visible in the timeline without treating them as new-message badge + triggers. +- Add focused Renderer-store coverage and an Electron production-seam regression, + and synchronize the current behavior in `README.md`. + +## Intent And Constraints + +- Preserve Conversation-local Snapshot/Patch reduction, generation/sequence + recovery, selected-Conversation read clearing, and lightweight hidden summaries. +- Do not change Electron Main, public contracts, runtime error semantics, + Conversation metadata storage, or the sidebar presentation itself. +- A new pending select/confirm/input/editor interaction must set the marker for a + hidden Conversation. A new completed, failed, or aborted run terminal must also + set it. +- Repeated updates to an already-pending interaction or an already-terminal run + must not recreate a marker the user has already cleared. +- Work in the isolated worktree because local `main` is owned by the completed + unsigned-macOS-package task; that task has no semantic or file overlap. + +## Outcome + +- Replaced the prior `assistant message.upsert` unread trigger with a transition + predicate over the target Conversation's previous and reduced Snapshots. +- The predicate recognizes only a newly pending interaction or a newly terminal + run. Terminal identity is compared by run id and terminal reason so unrelated + late patches do not recreate a cleared marker. +- Assistant stream start/update, thinking, tool lifecycle, and `tool.upsert` error + patches no longer mark a hidden Conversation unread. If the whole run later + settles, its completed/failed/aborted transition still marks it. +- Extended the controllable Electron EventSource fixture and added a real sidebar + assertion covering process failure, pending confirmation, read clearing, and + final task settlement. +- Updated `README.md` with the user-visible badge rule. No Main/runtime/API/storage + behavior changed. + +## Verification + +- Red phase: the focused store suite failed four assertions against the prior + assistant-message trigger, covering the unwanted process marker and the missing + interaction/failed/aborted terminal markers. +- `pnpm exec vitest run tests/unit/coding-conversations-store.test.tsx --maxWorkers=1`: + passed, 1 file / 28 tests. +- `pnpm exec vitest run tests/unit/coding-chat-panel.test.tsx --maxWorkers=1`: + passed, 1 file / 17 tests. +- `pnpm run typecheck`: passed. +- `pnpm run lint:check`: passed with 0 errors and 5 pre-existing warnings outside + the changed files; scoped ESLint on all changed TypeScript files passed cleanly. +- `pnpm test`: passed; functional stage 212 files / 1,735 passed / 3 skipped, + isolated pressure stage 1 file / 1 passed. +- `pnpm run build:vite`: passed for Renderer, Electron Main, Preload, and utility; + only existing Browserslist, dynamic-import, and chunk-size warnings remained. +- `pnpm exec playwright test tests/e2e/pi-coding-first-chat.spec.ts`: passed, 3/3. +- All project commands used the pinned pnpm 10.33.4. `git diff --check` passed. + +## Follow-ups + +- None. + +## Promotion Candidates + +- Target: `.project-docs/30-worklog/current-state.md` + Proposal: record that a hidden Code Conversation's red unread marker now signals + only newly pending user interaction or a newly terminal completed/failed/aborted + task, while assistant/tool process updates and individual tool failures remain + non-notifying. + Evidence: `src/stores/coding-conversations.ts`, focused 28-test store suite, and + the 3/3 Electron `pi-coding-first-chat` production-seam run. + Future impact: future patch types and sidebar changes should preserve the + distinction between process visibility and user-attention notification. + Semantic conflicts: none; ADR-006's Snapshot/Patch and per-Conversation isolation + boundaries are unchanged. + Human confirmation required: no. diff --git a/.project-docs/30-worklog/tasks/20260902-integrate-bash-tool-bridge-e81a5c6d.md b/.project-docs/30-worklog/tasks/20260902-integrate-bash-tool-bridge-e81a5c6d.md new file mode 100644 index 0000000..7453106 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-integrate-bash-tool-bridge-e81a5c6d.md @@ -0,0 +1,70 @@ +# Task: Integrate Bash tool bridge fix + +## Identity + +- Task ID: 20260902-integrate-bash-tool-bridge-e81a5c6d +- Mode: Integration +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: 6073bd6f4ce269b067b13e1eed0371b38dc2c054 +- Owner: codex +- Status: Completed + +## Scope + +- Merge completed feature task `20260902-fix-bash-tool-bridge-9c4e7a12` + (`49112b6`) into local `main`. +- Reconcile the write-leased Pi tool-batch rule with README, canonical current + state, and the evidence index. +- Re-run focused real-process coverage, typecheck, and the production Vite build + on the integrated tree, then restart the local Makelore platform. + +## Intent And Constraints + +- Preserve the feature task's bounded semantics: only tools that acquire the + project write lease are serialized; read-only Pi tool batches remain parallel. +- Do not replay or mutate the user's accepted, currently stuck Conversation as a + smoke test. Use the disposable real-process regression instead. +- Merge only to local `main`; do not push, package, deploy, or alter unrelated + user changes. +- Treat production build success as proof that packaging consumes the fixed + resources, not as permission to claim an installer artifact was produced. + +## Outcome + +- Merged source commit `49112b6` into local `main` as merge commit `55bf80e`. +- Integrated the bounded Pi tool-batch correction: built-in `bash`, `edit`, and + `write`, plus dynamic product tools that acquire the project write lease, are + sequential within one assistant response. Read-only batches retain parallel + execution. +- Updated README, canonical current state, and the evidence index with the exact + failure mechanism and verification boundary. A production build was verified; + no installer was created, pushed, deployed, or published. +- Stopped the old development process cleanly so its already-open Agent Server + could not keep serving the pre-fix resource. The platform restart is the final + operational handoff after the integration commit. + +## Verification + +- Source branch: the pre-fix real-process regression reproduced the second Bash + result as `fetch failed` under a 250 ms managed-extension HTTP idle timeout. +- Source branch: 3 focused files / 9 tests, 213 full files / 1,733 tests with 3 + skipped, typecheck, lint with 0 errors / 5 existing warnings, and the full + Renderer/Main/Preload/utility Vite production build passed. +- Integrated `main`: the 3 focused Pi process/extension/worker files passed all + 9 tests, `pnpm run typecheck` passed, and `pnpm run build:vite` passed for all + four production targets. +- The regression verifies that a timed-out first Bash does not block the second + Bash, the second returns `SECOND_OK`, and neither result contains `fetch failed`. + +## Follow-ups + +- A signed installer is unnecessary to correct this source-level deadlock. Build + an installer only when release validation or distribution is requested; the + normal packaged resource path already consumes the corrected Agent Server and + v5 extension bundle. + +## Promotion Candidates + +- None; the relevant runtime contract and evidence were promoted during this + integration task. diff --git a/.project-docs/30-worklog/tasks/20260902-integrate-session-settlement-6e41c8b2.md b/.project-docs/30-worklog/tasks/20260902-integrate-session-settlement-6e41c8b2.md new file mode 100644 index 0000000..c9fd405 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-integrate-session-settlement-6e41c8b2.md @@ -0,0 +1,74 @@ +# Task: Integrate stuck session settlement fix + +## Identity + +- Task ID: 20260902-integrate-session-settlement-6e41c8b2 +- Mode: Integration +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: dedbc85014e0c7e656464c9b3af26f9f4882bd04 +- Owner: codex +- Status: Completed + +## Scope + +- Integrate source commit `f1fd13a` from task + `20260901-fix-session-settlement-a47d2e91` into local `main`. +- Promote the verified missing-settlement recovery behavior into the integrated + project snapshot without changing the accepted ADR-006 runtime boundary. +- Re-run focused runtime tests, typecheck, and the Renderer/Main/Preload build on + the integrated tree, then confirm the development desktop process reloads. + +## Intent And Constraints + +- Preserve normal `agent_settled` authority, per-Conversation generation isolation, + exactly-once ownership cleanup, and sibling availability. +- Do not replay an accepted or uncertain Coding mutation and do not settle an + ordinary prompt solely because `message_end` was observed. +- Permit bounded fallback completion only from exact target-thread idle evidence; + contradictory terminal state must converge to a target-only protocol failure. +- Keep Pi wire/runtime state private to Electron Main and make no unrelated product, + protocol, authentication, provider, or project-data change. + +## Outcome + +- Committed the completed read-only live-stall diagnosis on `main` as `dedbc85` + and released its prior feature-task ownership cleanly. +- Merged source fix `f1fd13a` into `main` without conflicts as merge `e76a1e1`. +- Kept the source task record read-only on its feature branch; `main` contains the + integration task record and canonical promoted facts rather than a mutable copy + of another task's owned record. +- Promoted the bounded missing-settlement recovery into `current-state.md`, the + evidence index, and `README.md`; ADR-006 remains unchanged because the fix + restores its existing authority, isolation, and no-replay contract. +- Restarted the development desktop application from the integrated tree. The + new Electron Main process is listening on the Host API and the Renderer returns + HTTP 200 with the settlement-probe implementation present in the built bundle. + +## Verification + +- Source task evidence: 68 focused tests passed across 8 files; 1,732 full unit + tests passed and 3 skipped; typecheck, targeted/full lint, Vite build, and + `git diff --check` passed. +- Integrated tree: `vitest run tests/unit/pi-worker-pool.test.ts + tests/unit/pi-agent-server-process-real.test.ts --maxWorkers=2` passed 26 tests + across 2 files. +- Integrated tree: `pnpm run typecheck` passed. +- Integrated tree: `pnpm run build:vite` passed for Renderer, Main, Preload, and + utility bundles; only pre-existing Browserslist/import/chunk-size warnings + remained. +- Restart smoke: Renderer `http://localhost:5173/` returned HTTP 200; Electron + process `87870` listens on `127.0.0.1:13210`; the built Main bundle contains the + settlement-probe implementation. +- `check_project_docs.py`, task-aware `check_doc_drift.py`, `git diff --check`, + and the final Git cleanliness check passed. + +## Follow-ups + +- No additional code follow-up is required for this local integration. A future + packaged release should retain the existing Pi final-artifact and cross-platform + release gates. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260902-verify-latest-session-4d2ac781.md b/.project-docs/30-worklog/tasks/20260902-verify-latest-session-4d2ac781.md new file mode 100644 index 0000000..fcb1506 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-verify-latest-session-4d2ac781.md @@ -0,0 +1,79 @@ +# Task: Verify latest Coding session settlement + +## Identity + +- Task ID: 20260902-verify-latest-session-4d2ac781 +- Mode: Feature +- Branch: main +- Worktree: /Users/inmanx/Documents/makelore +- Base commit: dcc186877aa398e91a32278844d6481a78a4c360 +- Owner: codex +- Status: Completed + +## Scope + +- Determine whether the newest visible Makelore Code run in project `肉鸽小游戏`, + Agent `小小明`, Conversation `新对话` is still doing useful work or is stuck. +- Correlate the live UI with Electron/Pi logs, the persisted Pi session timeline, + process activity, child processes, sockets, and a native process sample. +- Make no live Conversation mutation: do not abort, recover, resend, or click any + session control. + +## Intent And Constraints + +- Distinguish the newly integrated missing-`agent_settled` recovery from a run that + has not reached terminal settlement because a tool call remains pending. +- Treat user-visible waiting, model activity, tool execution, and terminal + settlement as separate stages. +- Inspect only the current in-scope Conversation and avoid exposing unrelated + project or account data. + +## Outcome + +- Confirmed that the current run is functionally stuck, but not in the previously + fixed terminal-settlement path. +- The current user turn began at 11:35:13 local time. Upstream model requests + repeatedly completed with HTTP 200, including complete streams at 11:35:16, + 11:40:21, and 11:45:25, so the model gateway is responsive. +- Each assistant response requested two `bash` tools. Persisted tool results arrived + only after about 3 minutes 46 seconds or 5 minutes, commonly as `fetch failed`, + after which the model requested another pair. At 11:45:25 it retried even trivial + `echo`/`env` commands. +- The UI showed `处理中 10 分 26 秒` with the newest two Bash calls executing. No + matching shell, `find`, `echo`, or `env` child process existed; the Agent Server + was at 0% CPU and its Node event loop was idle. The per-command 90-second timeout + therefore did not bound the visible wait; an outer roughly five-minute failure + path controlled progress instead. +- The run never produced a terminal assistant `stop` or authoritative idle state, + so the missing-settlement probe correctly did not fire. The current failure is a + Bash tool-execution bridge/timeout-retry loop, not a recurrence of the merged + `agent_settled` defect. +- No application process, Conversation, project file, or product source was changed + by the diagnosis. + +## Verification + +- Read the live Makelore accessibility tree through Computer Use without performing + UI actions; confirmed the visible timer, failed and running Bash calls, and active + stop control. +- Correlated Main `ai-proxy` request/completion timestamps with the target Pi JSONL + timeline for Conversation `a464d8ff-1f5e-4cba-81ab-806d816f7d81`. +- Inspected the Agent Server process, recursive child-process set, TCP endpoint, and + a one-second native process sample. The process was alive but idle with no Bash + subprocess while the UI claimed two commands were executing. +- Observed one full five-minute boundary: at 11:45:22 the prior tool pair returned + and the model immediately continued, proving a slow retry loop rather than a + permanently dead process. + +## Follow-ups + +- If a fix is requested, trace the Bash tool bridge from Pi tool invocation through + its execution adapter, make per-command timeout authoritative even when process + creation or transport fails, and terminate repeated tool-failure loops with one + explicit target-only error rather than another five-minute retry. +- Immediate user recovery is to use the visible stop action; do not resend the same + request while this run still owns the Conversation. + +## Promotion Candidates + +- None recorded. diff --git a/.project-docs/30-worklog/tasks/20260903-complete-remote-merge-b41e7c92.md b/.project-docs/30-worklog/tasks/20260903-complete-remote-merge-b41e7c92.md new file mode 100644 index 0000000..ba2bb45 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260903-complete-remote-merge-b41e7c92.md @@ -0,0 +1,131 @@ +# Task: Complete remote main merge + +## Identity + +- Task ID: 20260903-complete-remote-merge-b41e7c92 +- Mode: Integration +- Branch: main +- Worktree: D:\Datas\OthersProjects\makelore +- Base commit: 7a37593e4be3c39a9782975de53f56a76dd53ef0 +- Owner: codex-root +- Status: Ready for Integration + +## Scope + +- Complete the already-started normal merge of fetched `origin/main` + `8e947b4f0e1ff0409e52a9d528ae3bb240ceb687` into local `main` + `7a37593e4be3c39a9782975de53f56a76dd53ef0`. +- Resolve all product, test, README, and canonical project-memory conflicts by + preserving both compatible intents: the reviewed unified `/plugins` workspace + and the remote Pi/runtime/Conversation fixes. +- Run integration-focused and repository-required verification, create the merge + commit, and close the project-documentation gate. Do not push. + +## Intent And Constraints + +- The user explicitly authorized takeover of the dirty root worktree and completion + of its existing merge. The prior integration owner was released with the bundled + task-context recovery command; this task now exclusively owns the worktree and + integration lock. +- Preserve normal two-parent history. Do not abort, rebase, reset, force-update, + stash, or discard staged/working content. +- `ML-PLUGIN-NAV-001` and its R4 zero-finding acceptance are the authority for the + Plugin conflict: keep one canonical `/plugins` page and one sidebar entry, exact + replace redirects, and deletion of the three superseded page implementations. +- Preserve remote packaged Pi runtime-root resolution, foreground Snapshot + reconciliation, attention-only Conversation markers, bounded missing-settlement + recovery, and serialized write-leased tool batches under ADR-006. +- Keep Catalog, Account Library, official Package Store, Device Packages, project + enablement, Agent assignment, runtime admission, and billing as independent + authorities. Keep selected-model native Web Search outside the Plugin projection. +- Preserve the three pre-existing untracked foreign task records exactly. Do not + package, install, deploy, publish, open a PR, or push. + +## Project Context Loaded + +- Concurrent Task Gate passed for this exact integration task, `main` worktree, + branch, base commit, and exclusive integration lock. +- Planning Gate passed after reading the startup memory, active record, integrated + snapshot, accepted decisions, architecture/domain/evidence/reflection/commitment + records, all planning peer scopes, both merge histories, the Plugin implementation + specification, source handoffs, and final R4 acceptance. +- All 212 peer records are readable. Twenty-nine remain registered as planning; + none has unresolved semantic ownership over this merge. One historical Data + Service scope names obsolete `.niancode` ownership, which is overridden for this + task by repository guidance and accepted ADR-006 rather than treated as current + authority. +- The remote Project Configuration Plugin Services design is superseded by the + later user-confirmed unified `/plugins` specification. The remote runtime and + Conversation changes are compatible and must be retained. + +## Outcome + +- Took over the existing root-worktree merge only after the user explicitly + authorized takeover, released the stale prior owner through the bundled + ownership command, and acquired the exclusive integration lock for this task. +- Resolved all 15 unmerged paths without aborting or rewriting history. The final + tree keeps the reviewed canonical `/plugins` page, sidebar entry, deterministic + legacy redirects, and source-qualified unified projection. The remote predecessor + Project Configuration Plugin Services component, helper, page implementations, + and tests remain retired rather than creating a second product surface. +- Preserved the remote Pi runtime-root resolver, complete active-branch history, + foreground Snapshot reconciliation, attention-only markers, exact Token Point + quota projection, missing-settlement recovery, and serialized write-leased tool + batches. The real-process conflict now verifies both the local two-extension + Device Package generation and the remote post-acceptance invalid-JSON failure. +- Reconciled README and canonical architecture/current-state/evidence/commitment + records semantically. The staged result is ready for a normal merge with parents + `7a37593e4be3c39a9782975de53f56a76dd53ef0` and + `8e947b4f0e1ff0409e52a9d528ae3bb240ceb687`. +- Corrected the bundled `maintain-project-docs` recovery path so explicit + `--adopt-existing` records the Git-index and working-tree fingerprints of known + foreign task records. Unchanged adopted records now survive a merge commit, + while any staged or unstaged mutation is blocked again. Registered the 24 + unchanged `MERGE_HEAD` records and three older untracked records only after the + user authorized this recovery. + +## Verification + +- `corepack pnpm --version` -> `10.33.4`, matching `packageManager`. +- The initial real-process run exposed stale `node_modules` junctions into a + different worktree and a missing physical `@earendil-works/pi-ai`; running + `corepack pnpm install --frozen-lockfile --config.confirmModulesPurge=false` + recreated the ignored dependency tree from the unchanged lockfile. +- `corepack pnpm run typecheck` -> passed. +- Focused Vitest after dependency repair -> 14 files / 175 tests passed. One first + combined run observed a cold timing sample of 2170 ms against a 2000 ms assertion; + the isolated semantic regression then passed in 1.48 s, and the later full suite + passed the same test without changing the threshold. +- `corepack pnpm test` -> 225 files / 1,858 passed / 2 conditional skips, followed + by the pressure file 1/1 passed. +- `corepack pnpm run lint:check` -> 0 errors and the five existing warnings in + `src/pages/Home/index.tsx` and `src/pages/Makelore/index.tsx`. +- `corepack pnpm run build:vite` -> Renderer, Main, Preload, and utility builds + passed; only existing Browserslist, mixed-import, and chunk-size warnings remain. +- Electron E2E -> unified Plugin workspace 3/3 passed in a clean grouped rerun; + Coding foreground/history/badge/runtime scenarios plus project Plugin behavior + passed 5/5. An earlier cross-file run passed 6/8 and timed out twice waiting for + auth initialization; Playwright trace showed no page/network/console error, and + both affected tests passed when rerun as their own three-test file. +- Repository conflict-marker search and `git diff --cached --check` -> passed. +- Bundled skill regression -> the temporary-repository adoption test passed, + including unchanged pre/post-commit acceptance and staged/untracked mutation + rejection; both modified scripts compiled, `task_context.py doctor` reported a + consistent registry, and `skill-creator` validation reported `Skill is valid!`. +- `check_doc_drift.py --task-id 20260903-complete-remote-merge-b41e7c92` -> + passed after exact adoption of the 27 unchanged foreign records. + +## Follow-ups + +- Do not push this merge unless the user separately requests it. +- The three pre-existing untracked foreign task records remain byte-for-byte + untouched and outside this task's ownership; the local recovery baseline only + proves that this integration task did not change them. +- Existing release gates remain open: exact-main installed-client Device Package + activation, signed/notarized macOS proof, refreshed Windows artifact proof, and + real selected-model Provider/Web Search acceptance are not claimed here. + +## Promotion Candidates + +- None. Accepted integration facts were applied directly to canonical project + memory under the exclusive Integration Gate. diff --git a/.project-docs/30-worklog/tasks/20260903-diagnose-recurring-stall-a83f5c21.md b/.project-docs/30-worklog/tasks/20260903-diagnose-recurring-stall-a83f5c21.md new file mode 100644 index 0000000..721d4a4 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260903-diagnose-recurring-stall-a83f5c21.md @@ -0,0 +1,107 @@ +# Task: Diagnose recurring Coding session stall + +## Identity + +- Task ID: 20260903-diagnose-recurring-stall-a83f5c21 +- Mode: Feature +- Branch: codex/20260903-diagnose-recurring-stall-a83f5c21-diagnose-recurring-stall +- Worktree: /Users/inmanx/Documents/makelore-diagnose-recurring-stall-a83f5c21 +- Base commit: 301c1496b0a3a1af3b9443a68a67de8dfea45749 +- Owner: codex +- Status: Completed + +## Scope + +- Diagnose the recurring visible `处理中` state in the installed macOS app for + project `肉鸽2.0`, including the active Pi logical thread that began around + 09:51 on 2026-09-03. +- Verify whether the installed artifact actually contains the previously integrated + terminal-settlement and Bash tool-bridge fixes. +- Correlate the screenshot with the installed Main log, persisted Conversation/Pi + session, target process state, and any active tool child before deciding whether + this is useful work, a tool wait, a Provider failure, or another settlement gap. + +## Intent And Constraints + +- Keep diagnosis-time live inspection read-only: do not abort the target run, + replay the accepted prompt, or mutate user project/session data. For the user's + later explicit launch request, replace the old installed process only by a + graceful quit after confirming the target had no remaining runtime/tool child; + do not force-kill it. +- Preserve ADR-006 target-only failure and no-replay semantics. +- Do not touch the occupied `main` worktree or its uncommitted packaging changes; + perform any source change only in this isolated worktree after evidence identifies + a concrete defect. + +## Outcome + +- Confirmed that this recurrence was not an active model or Bash wait. The target + Pi JSONL persisted an assistant terminal entry with `stopReason: "aborted"` + and `errorMessage: "Request was aborted"` at 09:52:24 local time. Electron + Main then completed the same Conversation with the intentional + `project_deactivated` reason at 09:52:33. The remaining Agent Server was idle + at 0% CPU with no tool child or network socket while Renderer still showed + `处理中`. +- Confirmed the installed app already contains the earlier Bash write-lease and + bounded terminal-settlement corrections. This incident instead repeated the + foreground recovery gap documented by task + `20260902-check-session-after-bash-fix-a6d12f39`: Renderer retained a cached + `live` Snapshot after Main had stopped the target, and selecting an already + selected Conversation skipped authoritative Snapshot hydration. +- Changed Conversation selection to always reconcile with Main. Existing live + state refreshes silently, while cold and invalidated targets keep their prior + loading/recovery behavior. Accepted or uncertain mutations are not replayed. +- Coding Chat now closes its old event stream on Main's `lifecycle:sleep`, + refreshes the selected Conversation when the window becomes visible or focused, + and refreshes a still-selected Conversation once when the programming view is + mounted or its project context changes. A project/Conversation selection key + prevents duplicate hydration caused by the selection render itself. +- Added store, Renderer, and Electron regressions that begin with a stale running + Snapshot and verify that foreground recovery displays the persisted aborted + terminal state without posting a prompt. + +## Verification + +- Correlated screenshot time, installed Main log, project registry, Conversation + metadata, target Pi JSONL, and live PID/process/socket state. The durable turn + ended before the visible timer continued; no running tool process remained. +- Installed `app.asar` contains `state_probe`, the bounded terminal-settlement + failure string, and `PROMPT_FAILED_AFTER_ACCEPTANCE`, confirming the prior fix + was packaged in the inspected app. +- Focused Vitest: `45 passed` across + `coding-conversations-store.test.tsx` and `coding-chat-panel.test.tsx`. +- Full Vitest: `1736 passed`, `3 skipped` across 213 test files, including the + single-worker pressure suite. +- Focused Electron E2E: + `foreground focus rehydrates a terminal Snapshot after lifecycle sleep` passed. +- TypeScript `tsc --noEmit`, scoped ESLint, production `build:vite`, and + `git diff --check` passed. Full `lint:check` passed with five pre-existing + warnings and zero errors. +- `check_project_docs.py` and task-aware `check_doc_drift.py` passed. +- The old installed app was gracefully quit after confirming no target Agent + Server or tool child remained. `pnpm run dev` then started the repaired + worktree successfully, with the Renderer on port 5173 and Electron using the + existing Makelore user-data directory. + +## Follow-ups + +- Integrate this feature branch after the occupied `main` packaging worktree is + available, then rebuild/reinstall the macOS artifact. The currently installed + `/Applications/Makelore.app` remains the older build; the repaired behavior is + available from this task worktree for development verification. + +## Promotion Candidates + +- Target: `README.md`, `20-architecture/data-flow.md`, and + `30-worklog/current-state.md` during Integration Mode. + Proposal: record the Renderer foreground rehydration contract: lifecycle sleep + closes the old Coding event stream, and view mount, project reselection, + visibility, or focus reconciles the selected Conversation from a Main-owned + Snapshot without replaying accepted mutations. + Evidence: focused store/Renderer tests, real IPC Electron E2E, and the two + installed-app stale-running incidents documented by this task and + `20260902-check-session-after-bash-fix-a6d12f39`. + Future impact: any new Coding navigation or lifecycle surface must preserve the + same terminal-state reconciliation and no-replay behavior. + Human confirmation: not required; this promotes implemented behavior without + changing ADR-006 ownership or product direction. diff --git a/.project-docs/30-worklog/tasks/20260903-finalize-main-integration-b7e2c4a9.md b/.project-docs/30-worklog/tasks/20260903-finalize-main-integration-b7e2c4a9.md new file mode 100644 index 0000000..83c83bd --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260903-finalize-main-integration-b7e2c4a9.md @@ -0,0 +1,65 @@ +# Task: Finalize latest main integration + +## Identity + +- Task ID: 20260903-finalize-main-integration-b7e2c4a9 +- Mode: Integration +- Branch: codex/20260903-integrate-latest-6f2c8e1a-integrate-latest +- Worktree: /Users/inmanx/Documents/makelore-integrate-latest-6f2c8e1a +- Base commit: cdbc262802434b6e51b6308cdbf7f94756346b2a +- Owner: codex +- Status: Completed + +## Scope + +- Treat verified integration checkpoint `cdbc262` as the immutable base after all + source merges and canonical reconciliation are already present. +- Re-run the task-aware project-document gate from that post-merge base, verify + `origin/main` has not advanced, fast-forward local `main`, and push normally to + `origin/main`. + +## Intent And Constraints + +- Do not change source-task records, product behavior, or accepted product + decisions in this final gate. +- Do not rewrite remote history or force-push. Abort promotion if remote ancestry + changes or local `main` cannot fast-forward cleanly. +- Keep retired OpenMAIC Learning Player source `b1f51be` excluded under ADR-005. +- Release integration ownership only after the final worktree is clean. + +## Outcome + +- The final-gate task was claimed at clean post-merge checkpoint `cdbc262`, so + source task records are part of its immutable base and remain read-only under + the project-doc ownership check. +- A fresh fetch confirmed `origin/main` remains at `28e1690`; the verified + checkpoint contains all remote commits and is 42 commits ahead / 0 behind. +- Local branch audit again found only `codex/learning-player-release-fix` outside + the integrated ancestry; that branch remains deliberately excluded by ADR-005. +- Local `main` fast-forwarded cleanly from `5d7a235` to `e1fddee`. A normal, + non-force push advanced `origin/main` from `28e1690` to `e1fddee`; the subsequent + fetch confirmed local `main`, the integration branch, and `origin/main` all + resolve to `e1fddeea158a6273fbdfe894aae2c239b13b9dd9`. + +## Verification + +- Inherited verified checkpoint results from task + `20260903-integrate-latest-6f2c8e1a`: focused Vitest 5 files / 70 tests; + Electron `pi-coding-first-chat` 4/4; full Vitest 223 files / 1,832 passed / + 3 skipped plus pressure 1/1; typecheck; lint with 0 errors / 5 existing + warnings; Renderer/Main/Preload/utility production build; `git diff --check`. +- Fresh `git fetch origin --prune`: passed; `origin/main` remains `28e1690`. +- `git rev-list --left-right --count origin/main...cdbc262`: `0 42`. +- `git merge --ff-only codex/20260903-integrate-latest-6f2c8e1a-integrate-latest` + in the main worktree: passed. +- `git push origin main`: passed, `28e1690..e1fddee main -> main`. +- Post-push `git fetch origin --prune` and ref comparison: local `main`, + integration branch, and `origin/main` all equal `e1fddee`; main worktree clean. + +## Follow-ups + +- None recorded. + +## Promotion Candidates + +- None. This task performs the final serialized promotion only. diff --git a/.project-docs/30-worklog/tasks/20260903-integrate-latest-6f2c8e1a.md b/.project-docs/30-worklog/tasks/20260903-integrate-latest-6f2c8e1a.md new file mode 100644 index 0000000..9b503a6 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260903-integrate-latest-6f2c8e1a.md @@ -0,0 +1,117 @@ +# Task: Integrate latest changes into main + +## Identity + +- Task ID: 20260903-integrate-latest-6f2c8e1a +- Mode: Integration +- Branch: codex/20260903-integrate-latest-6f2c8e1a-integrate-latest +- Worktree: /Users/inmanx/Documents/makelore-integrate-latest-6f2c8e1a +- Base commit: 301c1496b0a3a1af3b9443a68a67de8dfea45749 +- Owner: codex +- Status: Checkpointed for final gate + +## Scope + +- Merge fetched `origin/main` with the 34 local-only `main` commits without + dropping either side's product changes or task-scoped project memory. +- Integrate the three completed, worktree-owned change sets that remain + uncommitted: packaged Pi Agent Server resolution, Conversation unread-marker + semantics, and foreground authoritative Snapshot reconciliation. +- Reconcile accepted promotion candidates into canonical project memory, run + the repository's required verification, promote the verified integration tip + to local `main`, and push it to `origin/main`. +- Audit all local branches and worktrees; retain the explicitly excluded + historical Learning Player branch outside the merge. + +## Intent And Constraints + +- Preserve both divergent mainline histories with a normal merge; do not + rewrite or force-push remote history. +- Preserve ADR-005's retired Learning Player boundary and ADR-006's Pi-only, + Main-owned, no-mutation-replay architecture. +- Treat source task records and their promotion candidates as authoritative for + scope; resolve shared project-memory conflicts semantically rather than with + blanket `ours`/`theirs` rules. +- Keep generated installers, release output, caches, dependencies, secrets, and + local user data out of Git. +- Use pinned pnpm 10.33.4 and run typecheck, focused tests, the full unit suite, + lint, and the Vite/Electron production build before pushing. + +## Outcome + +- Fetched remote frontier `28e1690` and preserved it together with the 34 + local-only main commits through normal history merge `d25ed08`; no rebase, + history rewrite, or force-push was used. +- Committed each fully owned source worktree without altering its implementation: + packaged Pi runtime resolution as `5d7a235`, attention-only Conversation badges + as `f2c3755`, and foreground authoritative Snapshot reconciliation as `7aa118b`. +- Integrated those sources through merges `4babd6d`, `bbfe16d`, and `04320dd`. + Semantic conflict resolution retained Project Configuration as the only visible + Plugin Services navigation while including remote Model Tools, Device Packages, + and youth-facing Canvas behavior. The merged Electron EventSource/Snapshot + fixtures retain both attention-badge and foreground-reconciliation coverage; + the badge fixture now emits the authoritative worker generation so the newly + required foreground Snapshot refresh does not correctly discard its later + synthetic events as stale. +- Reconciled accepted promotion candidates into README, current state, architecture, + business rules, evidence, and commitments. The explicit staged-runtime package + resolver supersedes the earlier flag-only packaged resolver without overstating + the local unsigned macOS artifact as signed-release readiness. +- Audited all local branches. Only source `b1f51be` is not merged; it is the retired + OpenMAIC Learning Player implementation explicitly excluded by ADR-005. +- The task-aware drift check cannot treat task records introduced by source merge + commits as read-only: because this task was claimed before those merges, it reports + every newly merged source task record as a foreign edit even though their blobs were + not modified. The fully verified tree is therefore checkpointed here; final gate + task `20260903-finalize-main-integration-b7e2c4a9` will claim this exact clean HEAD + as its base, repeat the drift/completion gate, and perform the main push. + +## Verification + +- Planning and integration ownership gates passed. +- Fetched `origin/main`; divergence is 51 remote-only / 34 local-only commits + from merge base `bf32fecd6013101485434b1977a73beb8b6a1fe3`. +- All registered peer task records were reviewed; no semantic conflict exists + among the three pending source tasks. +- `corepack pnpm exec vitest run tests/unit/plugin-marketplace-pages.test.tsx + tests/unit/pi-managed-worker-opener.test.ts + tests/unit/pi-agent-server-process-real.test.ts --maxWorkers=1`: passed, + 3 files / 20 tests after the remote/local history merge. +- `corepack pnpm exec vitest run tests/unit/pi-agent-server-process-real.test.ts + --maxWorkers=1`: passed, 1 file / 5 tests after the runtime-root source merge. +- `corepack pnpm exec vitest run tests/unit/coding-conversations-store.test.tsx + tests/unit/coding-chat-panel.test.tsx --maxWorkers=1`: passed, + 2 files / 49 tests after the badge and foreground source merges. +- `corepack pnpm run typecheck`: passed on the fully merged source tree. +- Final focused Vitest: 5 files / 70 tests passed, covering Marketplace navigation, + managed-worker startup, packaged Agent Server, Conversation reducer, and Coding + Chat foreground behavior. +- `corepack pnpm exec playwright test tests/e2e/pi-coding-first-chat.spec.ts`: + passed, 4/4 after rebuilding the final Electron bundles. The first attempt used + stale pre-merge ignored build output and timed out during Electron launch; after + `build:vite`, one fixture generation mismatch was exposed and corrected before + the final all-green run. +- `corepack pnpm test`: passed; functional stage 223 files / 1,832 passed / 3 + skipped, isolated pressure stage 1 file / 1 passed. +- Final `corepack pnpm run typecheck`: passed. +- `corepack pnpm run lint:check`: passed with 0 errors and 5 pre-existing warnings. + An earlier concurrent lint run raced a Marketplace test's temporary-directory + teardown and returned `ENOENT`; the required standalone rerun passed. +- `corepack pnpm run build:vite`: passed for Renderer, Electron Main, Preload, and + utility outputs with only existing Browserslist, chunk-size, and dynamic-import + warnings. + +## Follow-ups + +- Final gate task `20260903-finalize-main-integration-b7e2c4a9` must start from this + clean checkpoint, verify remote ancestry has not advanced, pass project-doc drift, + update local `main`, and push normally to `origin/main`. +- Signed/notarized macOS release evidence, the actual corrected-app reinstall and + first-Conversation smoke, full macOS process enumeration, other architecture + gates, packaged Device Package acceptance, and real selected-model paid search + remain tracked external acceptance work; none blocks source integration. + +## Promotion Candidates + +- None. Accepted source-task promotion candidates were reconciled directly into + canonical memory by this integration task. diff --git a/.project-docs/40-domain/business-rules.md b/.project-docs/40-domain/business-rules.md index 272a945..62d3773 100644 --- a/.project-docs/40-domain/business-rules.md +++ b/.project-docs/40-domain/business-rules.md @@ -85,8 +85,11 @@ - Makelore Code 的唯一 production runtime 是精确 pin 的 Pi `0.84.2`。不得恢复 OpenCode fallback、RPC/SDK 双轨、兼容执行路径或 Renderer runtime 直连;产品公共合同必须保持 project/Agent/Conversation/Snapshot/Patch 中立,Pi wire 只属于 Main。 - project、Agent、Conversation 使用 `.makelore/project.json` 与 `.makelore/conversations.json` schema v2。Agent id、名称、原始 prompt、Skills 与 archive 状态必须稳定保存;不得从 `.niancode` 或 `.opencode` 读取或迁移项目元数据,也不得作为顺带清理删除这些用户内容。 - 每条 active/warm Conversation 在同一个长驻父 Agent Server 内绑定独立 Pi Runtime/Session/channel。首次本地 Conversation 创建和 Composer 编辑不得等待逻辑线程;未解析 Conversation 第一次选模必须先验证模型、持久化 resolved metadata,再 prepare。已解析且同账号的模型变化复用 target `set_model`,跨账号变化等 active run settled 后只重建目标逻辑线程。 +- 正式包中的父 Agent Server 必须从显式 staged `pi-runtime` manifest/root 定位 Pi 包及其导入入口,并验证入口仍位于目标包目录内;不得从脚本相邻资源目录、应用 `node_modules`、系统 npm 或网络下载回退解析。 - prompt、steer、follow-up、compact 等 mutation 必须先获得目标 Conversation 的 `202` acceptance/dedupe 结果。confirmation timeout 只表示 uncertain,不得自动重发,也不得释放 run permit、Agent Server/child process ownership 或 Main background lease;迟到 success/failure/exit/abort 必须单调、exactly-once 收敛。线程级失败只影响目标 Conversation;整个 Agent Server 退出时所有旧父 channel 一起 fail closed,但 Main/Renderer 继续存活且下次恢复只启动一个新 Server。 - Renderer 只消费 Snapshot-first 与 `patch-batch` SSE。每条 Conversation 的 generation/seq 独立;stale generation 丢弃,gap/reconnect 只恢复目标 Snapshot 并应用严格连续的缓冲 tail,不重放 mutation,也不改变乐观消息的 UI identity。 +- `lifecycle:sleep` 必须关闭旧 Coding SSE;编程视图挂载、项目上下文变化、页面重新可见或窗口 focus 必须静默刷新已选 Conversation 的 Main-owned Snapshot,即使 Conversation id 没有变化。该流程只协调权威状态,绝不重放 accepted/uncertain mutation。 +- 隐藏 Conversation 的红色注意标记只可由新的 pending interaction,或当前 run 新进入 completed、failed、aborted terminal 触发;助手流式文字、thinking、工具过程、单个工具失败以及已经见过的相同 interaction/terminal 更新不得创建或重新创建标记。 - top-level 逻辑 turn 并发上限 4、warm idle logical-thread LRU 上限 8、child 并发上限 4,child 使用 FIFO process budget 8;shared parent 逻辑线程不各占一个 process lease。单次 subagent dispatch 最多 8 个 child 且禁止递归;coding child 与 parent 共用同项目 write lease,父 abort/crash/generation 失效必须清理所有 child、permit 与 process lease。 - Provider Account、credential、custom header 和 proxy token 只可投影到选中父逻辑线程的内存 credential store 或选中 child 进程,不能进入 argv、catalog、Renderer 或 sibling thread。确定性的 Works user-context 缺失必须使缓存 gateway credential 过期、fail fast 且不重放 mutation,并向产品投影固定的 `CODING_PROVIDER_AUTH_REQUIRED`;不能把它解释为 Pi 崩溃。 - Provider/resource revision 在 idle 逻辑线程下一 prompt 前应用;running 线程使用当前 run snapshot,settled 后重建。同账号 refresh single-flight 且最多一次 auth refresh/reopen。真实 Provider 认证、endpoint/proxy/rate-limit、协议差异、真实并发和共享 Agent Server 内跨线程凭据隔离由用户明确豁免并接受风险,`realTurnVerified=false`,不得写成 Pass。 @@ -135,7 +138,7 @@ - Prompt Museum 客户端契约和页面完成不等于 Works Square 内容后台、审核数据或 CDN 已部署;上线前需以真实登录账号验证列表、详情、署名链接、分页、图片和 Prompt 回填。 - Learning 客户端集成不等于 Works Square 运营后台、对象存储或项目 API 已部署;上线前需先部署服务端合同,再以真实账号验证发布/下架、分页、远程 HTTPS README 图片(含 SVG 与失败隔离)、保存取消、缺失或错误 `Content-Length` 的 ZIP、摘要/签名错误以及 Windows/签名 macOS 安装包。 - 四模块入口策略上线前需确认 Works `module_access` migration/API 已部署,安装包包含对应客户端,并用真实账号逐一关闭四个模块验证卡片、根/深层/别名路由和独立 API 授权。 -- Makelore Code 当前只有 Windows x64 最终安装包和 WSL2/WSLg Linux 证据。macOS x64/arm64 与 native non-WSL Linux desktop/compositor 尚未验收;在补齐前不得宣称 cross-platform release-ready。真实 Provider 风险虽经用户明确豁免,但仍不得从 loopback/provider-shaped smoke 推断为真实 Provider Pass。 +- Makelore Code 已有 Windows x64 最终安装包证据、WSL2/WSLg Linux 证据,以及 macOS arm64 本地未签名 DMG 的 mounted-image Agent Server initialize/shutdown 证据。macOS arm64 的签名/公证与完整 process-enumeration gate、macOS x64、native non-WSL Linux desktop/compositor 仍未验收;在补齐前不得宣称 cross-platform release-ready。真实 Provider 风险虽经用户明确豁免,但仍不得从 loopback/provider-shaped smoke 推断为真实 Provider Pass。 ## Last Reviewed diff --git a/.project-docs/50-evidence/evidence-index.md b/.project-docs/50-evidence/evidence-index.md index 194bf65..8d470ab 100644 --- a/.project-docs/50-evidence/evidence-index.md +++ b/.project-docs/50-evidence/evidence-index.md @@ -5,6 +5,10 @@ Use this index for searchable, traceable evidence records. | Date | Topic | Status | Source | Detail | |---|---|---|---|---| | 2026-09-03 | Device Package packaged prepare 与全部已启用资源加载 | Integrated on local `main`; rebuilt installed-client activation pending | Prepare source `5a2f0eb6785b59d8b455ed5cb1d9773351ff895a`, activation source `17664c5fffcfe695653b4146503e645f54767c4b`, tasks `20260902-local-skill-install-fix-6b3e91a4` / `20260903-load-installed-resources-8f3c1a72`, verified candidate `bd377c9`, main promotion task `20260903-promote-installed-resources-main-5c8e1a72` | Packaged package inspection now uses the distributed physical Pi runtime instead of importing an incomplete `app.asar` graph; closed Device Package failures remain closed across the bridge. The parent Agent Server keeps its generated Makelore extension first, passes every further Main-selected installed/enabled extension through Pi `0.84.2` `additionalExtensionPaths`, retains every selected Skill path, and keeps ambient discovery off. Packaged prepare without commit passed for loose `SKILL.md`, npm, and Git/Ponytail; a real Agent Server loaded two external extensions and exposed both commands. Across the two source tasks, focused tests, 222 files / 1,815 full unit tests / 2 conditional skips, pressure, typecheck, scoped lint, production build, Windows packaging, and artifact/Pi verification passed. The currently installed 1.2.6 client was not replaced, so no live installed-client success is claimed. | +| 2026-09-03 | Packaged Pi runtime-root resolver and local unsigned macOS arm64 artifact | Integrated locally; exact mounted-image bootstrap passed; signed release gate remains open | Source `5d7a235`, merge `4babd6d`, task `20260902-build-unsigned-mac-9d7e4a2c` | Node 24 did not honor the parent URL previously passed to `import.meta.resolve`, so an installed Agent Server searched beside the product resource script instead of staged `pi-runtime`. The replacement uses `findPackageJSON` from the explicit runtime manifest, selects the package import entry, rejects path escape, and imports the exact file URL. Focused real-process, full unit, typecheck, lint and Vite build passed at source. The corrected local-only unsigned DMG is 315,653,270 bytes with SHA-256 `6d0216da6c30f7fed537041b37c69811b8e025af3e9cccf85a64c690e29ecb7b`; direct initialize/shutdown passed both unpacked and from a read-only mounted image. It is not signed, notarized, published, or complete cross-platform evidence. | +| 2026-09-03 | Makelore Code foreground Snapshot reconciliation and attention-only Conversation markers | Integrated locally; installed-app cause correlated and source regression suites passed | Foreground source `7aa118b` / merge `04320dd`, badge source `f2c3755` / merge `bbfe16d`, tasks `20260903-diagnose-recurring-stall-a83f5c21` and `20260902-conversation-badge-a7c49e` | The diagnosed installed run had already persisted an aborted terminal and Main had completed it with `project_deactivated`; no tool child or network work remained, while Renderer retained a cached `live` Snapshot. Coding now closes stale SSE on `lifecycle:sleep` and refreshes the selected Main-owned Snapshot on view/project context, visibility, and focus without replaying any mutation. Hidden red markers now arise only for a new pending interaction or a new completed/failed/aborted run, not assistant/thinking/tool progress or an individual tool failure. Source verification covered 45 focused foreground tests, 28 focused badge-store tests, full suites, typecheck, lint, production builds, and real Electron regressions. | +| 2026-09-02 | Makelore Code Pi write-leased tool-batch deadlock fix | Integrated locally; source and production-build verification passed; no installer produced | Source `49112b6`, merge `55bf80e`, source task `20260902-fix-bash-tool-bridge-9c4e7a12`, integration task `20260902-integrate-bash-tool-bridge-e81a5c6d` | Live evidence showed every assistant response emitting two Bash calls; Pi prepared both hooks before execution, so the first acquired the project write lease and the second waited before the first could run or release it. The five-minute delay was the managed-extension HTTP idle timeout, not a missing dependency. Built-in `bash`/`edit`/`write` and dynamically declared lease-requiring tools are now sequential within a batch while read-only batches remain parallel. The pre-fix real-process regression reproduced `fetch failed`; the source passed 3 focused files / 9 tests, 213 full files / 1,733 tests with 3 skipped, typecheck, lint with 0 errors / 5 existing warnings, and the Renderer/Main/Preload/utility production build. | +| 2026-09-02 | Makelore Code missing terminal-settlement recovery | Integrated locally; live pre-fix stall diagnosed and automated regression coverage passed | Source `f1fd13a`, merge `e76a1e1`, source task `20260901-fix-session-settlement-a47d2e91`, integration task `20260902-integrate-session-settlement-6e41c8b2` | Live evidence showed Pi had persisted its final assistant response while Main retained run ownership because no effective `agent_settled` arrived. The fix preserves normal settlement authority and no-replay semantics, reports post-accept prompt rejection, and after a bounded grace period probes only the target thread: exact idle evidence hydrates and settles once; contradictory state fails that target explicitly. The source passed 68 focused tests, 1,732 full tests / 3 skipped, typecheck, lint, and Vite build; the integrated tree repeated focused runtime/process tests, typecheck, and Vite build. | | 2026-09-01 | Makelore Code packaged Agent Server Pi AI resolver fix | Integrated on local `main`; rebuilt Windows artifact and installed-client acceptance pending | Source `7df245a`, source task `20260901-local-runtime-unavailable-8b42c7f1`, merge `9640255`, verified candidate `42ea83c`, main promotion task `20260901-promote-local-runtime-main-a7c4e291` | The installed Electron Node process reproduced `ERR_MODULE_NOT_FOUND` for the present `@earendil-works/pi-ai@0.84.2` because the optional `import.meta.resolve` parent URL was inactive and lookup began beside `resources/resources/pi-agent-server.mjs`. Enabling that capability only for the Main-owned Agent Server made the exact differential pass without changing the runtime closure. A production-shaped sibling-layout Electron test went red before and green after; 24 focused tests, 1,761 full unit tests / 2 conditional skips, typecheck, lint with 0 errors, and Renderer/Main/Preload/utility build passed. This does not claim that the currently installed old package is fixed or that a rebuilt final artifact has passed release proof. | | 2026-08-30 | MakeLore AI Design Living Form V2 hard cutover | Integrated locally with matching server source; production data cutover and Provider activation pending | Client source `b0b5a602b501308a23eb27e2f51a5169b9e46b1e`, server source `b5351d54f595ce8eb873593e462e4a556bea0b05`, integration task `20260830-integrate-marketplace-design-client-main-9d5f3b82`, ADR-007 | Replaces nested V1 Conversations, Briefs, editable provider Prompt, mutable Quote PATCH, and local semantic fallback with one current Direction, one Living Form/Specification authority, stable operations, immutable Quote confirmation, and Main-owned V2 transport. Client source passed typecheck, lint with zero errors, 1,425 unit tests, Vite/Electron build, and 2 Electron E2E tests. Server source passed 2,626 tests / 31 skipped under UTF-8 mode. No production database cutover, real paid Provider request, deployment, or publication occurred. | | 2026-08-30 | MakeLore curated Plugin Marketplace Release A | Integrated locally; XMA-01 live acceptance PASS; production trust activation HOLD | Source `40df677a31ff7651f962151eb84b925987781c03`, source task `20260828-plugin-marketplace-client-5f8b3d72`, integration task `20260830-integrate-plugin-marketplace-client-6e3b9d82` | R7 Standards/Spec passed with zero findings. XMA-01 passed 12/12 using real PostgreSQL, signed-in packaged MakeLore, Operations-published temporary-key packages, A/B account isolation, immutable install/update/freeze/rollback, project/Agent/Pi materialization, lifecycle invalidation, pricing-only ETag change, Data Service 10 tools/14 operations and zero Token Point transactions. Final Windows installer was 208,235,670 bytes with SHA-256 `738A2F6573C502281787F418EA666C6E62C287CA0EC13C6EB22369DAB0B3FFFA`. The official Ed25519 public key was not supplied, so the production trust store correctly remains fail closed; no deployment or production publication occurred. | diff --git a/.project-docs/80-commitments/commitments.md b/.project-docs/80-commitments/commitments.md index cd80e5b..a9d5e47 100644 --- a/.project-docs/80-commitments/commitments.md +++ b/.project-docs/80-commitments/commitments.md @@ -13,12 +13,12 @@ Track future-facing memory: promised follow-ups, unfinished loops, timed checks, | 2026-08-16 | 验收 Prompt Museum 与 Canvas 删除/重报价的真实服务端链路 | 发布包含 `26b52d7` Canvas 行为的安装包前 | 客户端/服务端集成 | Pending | 使用真实 Works 账号核对 Museum 列表/详情/分页/筛选/署名/CDN/Prompt 回填,并确认固定相对媒体路径可经 Main Bearer 代理、401 刷新、10 MiB/可信 raster MIME 边界后在 Renderer 展示;核对最终 Prompt/options 重报价和确认设计点;删除 Workspace 后确认软删除可见性、未提交任务取消/预留积分释放、已运行任务结算。保留 Main 错误脱敏和严格 DTO/HTTPS 投影,不以客户端回归替代服务端验收 | | 2026-08-16 | 验收 default-on Robot Guided Hotspot Binding 的 Windows/macOS 真实设备链路 | 下一份包含页面内热点连接行为的安装包发布前 | 客户端/硬件/服务端集成 | Pending | 核对精确出货固件与固定 Portal、六位码发行/消费语义;执行 Windows Robot 真机扫描/连接、签名 macOS x64/arm64 CoreLocation/CoreWLAN/worker/ASAR/Koffi smoke,以及真实 Host/native Electron 端到端配网+Binding;保留 `NIANCODE_AI_HARDWARE_GUIDED_HOTSPOT_BINDING=0` 回滚并记录支持矩阵 | | 2026-08-10 | 完成客户端提交到 App `play_url` 播放的真实生产整链验收 | source+built+contract 服务端协议、OSS immutable Release、CDN/Edge 与 App 消费链成组集成后 | 客户端/服务端集成 | Pending | 使用真实账号执行小游戏和小程序创建、客户端本地构建与同字节预检、双归档提交、服务端逐字节校验/不可变 Release 固化、运营批准、CDN 发布、App 播放与监控核对;如需不可绕过 runtime gate,另行设计可信 verifier | -| 2026-08-12 | Windows 发布流水线保留固定 npm/Pi 运行时与 Device Package 产物门禁 | 每次生成正式 Windows 安装器时 | 客户端发布 | Pending | 运行 `pnpm verify:publish-runtime`、`pnpm verify:artifact:win`、`pnpm verify:artifact:pi` 与 final packaged proof;启动最终 `resources/resources/pi-agent-server.mjs` 并确认它从相邻 `pi-runtime` 初始化。安装 exact-main 客户端后,用 Conversation 完成至少一个 npm/Git/loose-Skill prepare→confirm 流程,并以包含两个 extension 的已启用 Device Package generation 验证首个 Makelore bridge 与所有附加 extension/Skill 均生效、禁用项不加载、新/idle parent 刷新且 child 为空。当前安装的 1.2.6 客户端早于 `5a2f0eb` / `17664c5` 修复,不能作为通过证据;不得以 prepare-only 或 workspace real-process 测试替代安装后 activation。 | +| 2026-08-12 | Windows/macOS 发布流水线保留固定 npm/Pi 运行时与 Device Package 产物门禁 | 每次生成正式 Windows 或 macOS 安装器时 | 客户端发布 | Partial / Pending | 运行目标平台的 publish-runtime、artifact、Pi 与 final packaged proof;启动最终 Agent Server 并确认它从 staged `pi-runtime` manifest/root 初始化,再用安装后的首个 Conversation 确认不出现 runtime-unavailable 投影。安装 exact-main 客户端后,还需完成至少一个 npm/Git/loose-Skill prepare→confirm 流程,并以包含两个 extension 的已启用 Device Package generation 验证首个 Makelore bridge 与所有附加 extension/Skill 均生效、禁用项不加载、新/idle parent 刷新且 child 为空。源 `5d7a235` 已通过本地未签名 macOS arm64 DMG 的 read-only mounted-image initialize/shutdown,DMG SHA-256 为 `6d0216da6c30f7fed537041b37c69811b8e025af3e9cccf85a64c690e29ecb7b`;当前安装的 1.2.6 客户端早于 `5a2f0eb` / `17664c5` 修复。仍需签名/公证、完整 process proof、实际替换安装与首个 Conversation、新 Windows artifact 及安装后 Device Package activation;不得以 prepare-only 或 workspace real-process 测试替代。 | | 2026-08-10 | 删除客户端 `runtime_url` 兼容回退 | 一个客户端兼容版本结束,且服务端与存量数据稳定提供 `play_url` | 客户端 | Pending | 删除类型字段、读取分支和对应回归测试 | | 2026-08-11 | 部署并验收 AI 绘画多 Conversation 服务端契约 | 发布包含多会话客户端之前 | 客户端/服务端集成 | Pending | 确认迁移 `0033`、Conversation list/create/read/turn/confirm API、持久 Agent Session 与事件字段已上线 | | 2026-08-12 | 复核 AI 编程 Provider 错误与重试投影 | 升级 Pi、Works gateway 或上游 Provider 错误格式时 | 客户端/Pi/provider 集成 | Pending | 重新验证确定性 Works user-context 缺失仍会 expire credential、非重试 fail fast、固定脱敏为 `CODING_PROVIDER_AUTH_REQUIRED`,并且不会被后续 `agent_settled` 改写为 completed;不得把其他 401/429/5xx 扩大归类。 | | 2026-08-15 | 复核 AI 编程 Pi 上下文压缩事件契约 | 升级锁定的 Pi `0.84.2` 或模型上下文配置时 | 客户端/Pi 集成 | Pending | 运行长上下文与 manual compact smoke,确认新版本是否仍缺 `agent_settled`;保持 correlated compact RPC terminal、Snapshot completed 单调、普通 prompt 独立 settled、late result exactly-once 和 lease 清零。 | -| 2026-08-26 | 补齐 Makelore Code 跨平台最终产物验收 | 对外宣称 cross-platform release-ready 或发布对应平台安装包前 | 客户端发布 | Pending | 在 macOS x64、macOS arm64 与 native non-WSL Linux desktop/compositor 上执行最终 artifact、Pi closure、extension/subagent、4+4、SSE/UI/performance、abort/uncertainty/cleanup 验收。Windows Pass 与 WSL2/WSLg 证据不得替代这些平台结果;失败时回到对应实现修复,不恢复 OpenCode fallback。 | +| 2026-08-26 | 补齐 Makelore Code 跨平台最终产物验收 | 对外宣称 cross-platform release-ready 或发布对应平台安装包前 | 客户端发布 | Partial / Pending | macOS arm64 已有源 `5d7a235` 的本地未签名 DMG、Pi closure 与 mounted-image initialize/shutdown 证据,但 process enumeration 不支持,且未签名/公证、未完成安装后 UI/首会话及完整 extension/subagent、4+4、SSE/performance、abort/uncertainty/cleanup 门禁。继续补齐这些 arm64 门禁,并在 macOS x64 与 native non-WSL Linux desktop/compositor 执行完整验收;Windows 与 WSL2/WSLg 证据不得替代其他平台,失败时不恢复 OpenCode fallback。 | | 2026-08-13 | 将 AI 绘画素材选择 quick reply 升级为结构化 action/purpose | 新增第三种素材输入用途、调整本地化文案或重构 Design Message 协议时 | 客户端/服务端集成 | Pending | 用 `select_image_asset` + `image_reference | video_first_frame` 等稳定字段替代精确中文字符串匹配,并覆盖延迟 Brief 快照兼容 | | 2026-08-13 | 完成图生图客户端与服务端生产整链 smoke | 相匹配客户端与服务端部署后 | 客户端/服务端集成 | Pending | 用当前 Workspace 已完成作品与本地上传各执行一次图生图,核对 Asset 所有权、Quote、任务创建、结果展示与下载;不以本地单测替代付费 Provider 验收 | | 2026-08-13 | 发布并验收 Makelore 稳定更新链 | 权威版本对齐且 Windows/macOS 正式产物已签名/公证后 | 客户端发布 | Pending | 将 updater-role 产物 inactive-first 发布到 Works Square,核对 manifest/签名/架构后激活,并从旧稳定版执行发现、下载、重启、安装与版本确认 smoke;源码错误文案不替代该验收 | diff --git a/README.md b/README.md index 0c8cf62..533f85f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ Makelore 是一个面向软件、视觉创作、互动学习与智能机器人 ## 当前产品状态 - 桌面技术栈:Electron、React 19、Vite、TypeScript、Zustand、Tailwind CSS。 -- AI 编程核心对话运行时:Electron Main 按需启动一个长驻 Pi Agent Server,同一进程内为每条 Conversation 建立独立的 Runtime、Session、凭证视图、扩展上下文和 JSONL 通道,同时复用 Pi 模块与进程启动成本;Server 异常退出时所有旧通道统一失效,下一次恢复只重启一个 Server。Renderer 不直接启动或调用 Pi,也不读取其 wire 类型。项目与 Conversation 元数据先从本地读取,选中对话后才按需准备对应逻辑线程;输入框不等待运行时准备完成。子 Agent 仍使用独立的短生命周期进程,以保留并发配额、故障和写入边界;macOS 上两类后台进程都使用 Electron 自带的 Helper,不在 Dock 中创建额外应用图标。 +- AI 编程核心对话运行时:Electron Main 按需启动一个长驻 Pi Agent Server,同一进程内为每条 Conversation 建立独立的 Runtime、Session、凭证视图、扩展上下文和 JSONL 通道,同时复用 Pi 模块与进程启动成本;Server 异常退出时所有旧通道统一失效,下一次恢复只重启一个 Server。正式包中的 Agent Server 从明确传入的 `resources/pi-runtime` 根定位依赖清单与包入口,不从脚本所在目录或应用 `node_modules` 猜测解析。Renderer 不直接启动或调用 Pi,也不读取其 wire 类型。项目与 Conversation 元数据先从本地读取,选中对话后才按需准备对应逻辑线程;输入框不等待运行时准备完成。同一助手工具批次中,会取得项目写租约的 `bash`、`edit`、`write` 与动态产品工具按顺序执行,避免批次在命令启动前自锁;纯只读工具仍可并行。子 Agent 仍使用独立的短生命周期进程,以保留并发配额、故障和写入边界;macOS 上两类后台进程都使用 Electron 自带的 Helper,不在 Dock 中创建额外应用图标。 +- AI 编程历史与配额反馈:Pi 上下文压缩不删除用户可见的 active-branch 历史,压缩摘要保持 Main-private;Renderer 首次渲染最近 120 个节点,向上滚动时按 100 个节点追加更早内容并保持阅读位置。Main 精确识别 `token_point_balance_exhausted` / `词元点数余额不足`,只向 Renderer 投影不可重试的稳定配额错误和安全中文提示,重开会话后仍可见且不暴露上游原始错误或 request id。 - 桌面性能策略:应用窗口使用不透明浅色表面并默认保留硬件合成;仅在显式安全模式或短时间内重复 GPU 进程崩溃时启用软件渲染,并把故障原因保存在用户数据目录。启动关键路径只创建本地应用壳,认证、代理、同步、更新和遥测在首帧后延迟初始化;开发版可通过 `app:performance` 快照观察 GPU、进程、WebContents、事件循环与 Renderer Long Task 聚合指标。 - 后台生命周期:Main 统一维护模块活动状态与任务租约。隐藏窗口、离开模块和关闭开发浏览器会释放非必要连接;生成、Code 执行、下载与发布构建持有租约并在完成后释放。已写入 Pi 的 prompt 或上下文整理即使确认超过 10 秒,也会继续持有运行所有权与后台租约,直到迟到响应、权威事件、明确失败或用户清理使其收敛;此时同一 Conversation 不接受重叠 mutation,其他 Conversation 不受影响。各模块的后台连接、轮询和子进程必须通过同一生命周期入口登记。 - 共享开发浏览器:AI 编程右侧提供项目级浏览器,用户与 Agent 查看并调试同一实时页面、Console 和 Network,支持本地与公网开发地址。 @@ -32,7 +33,7 @@ Makelore 是一个面向软件、视觉创作、互动学习与智能机器人 - 插件工作台:Code 侧栏只有一个“插件”入口,`/plugins` 在同一列表中投影官方目录、账号 Library、本机 Device Packages 与当前项目状态;旧 Marketplace、My Plugins、Project Plugins URL 只做确定性筛选重定向。来源身份保持 `official:`、`local:`、`retained:` 分离;“免费获取”、官方设备交付、项目启用和伙伴分配仍分别写入既有 authority,不会自动推进下一步。任一来源失败不会清空其他来源;本机 Skill/Pi 插件仍只能通过对话安装,页面只管理已安装包并明确其本机全局生效范围。 - 官方插件运行架构:Renderer 只调用 Main-owned Marketplace facade;Main 负责账号、请求 deadline、签名/摘要校验、不可变 Release、current selection 与原子回滚。Pi parent worker 使用冻结的 effective snapshot,将每个有效 Skill 与已验证 Package Store root 成对传给 resource loader、Extension Host 和 CLI;`skill_only` 不依赖运行时 Policy,也不执行分发包中的任意代码。正式激活仍等待官方 Ed25519 公钥(production key activation HOLD);生产私钥只能来自部署 secret,测试使用注入的临时密钥。 - 原生 Web Search 是冻结 selected model/provider/credential 的 parent-only 模型工具;只有 Works `model_capabilities` 明确声明支持时才出现,使用当前模型的正常计费链,不依赖 Marketplace 获取、Release、Admission、Hosted Provider 或 `agent_browser` 回退。 -- 本机 Device Packages 只由 Conversation 工具发起安装,支持 npm、Git、绝对本地 Plugin 目录和 loose `SKILL.md`。Main 在预览和独立确认后提交不可变本机 generation,禁用生命周期脚本;新建/空闲 parent 会同时加载所有显式安装且当前启用的 Skill,以及必需 Makelore 扩展之外的全部 Pi extension,active parent 在 turn settled 后切换,child 不继承这些资源,也不会扫描 ambient 项目或用户目录。 +- 本机 Device Packages 只由 Conversation 工具发起安装,支持 npm、Git、绝对本地 Plugin 目录和 loose `SKILL.md`。Main 在预览和独立确认后提交不可变本机 generation,禁用生命周期脚本;确认界面明确提示 Pi extension 以桌面当前用户权限执行。新建/空闲 parent 会同时加载所有显式安装且当前启用的 Skill,以及必需 Makelore 扩展之外的全部 Pi extension,active parent 在 turn settled 后切换,child 不继承这些资源,也不会扫描 ambient 项目或用户目录;Renderer 不提供安装源选择器。 - 提示词博物馆:只陈列经过审核的作品预览、Prompt、分类以及作者/来源/许可证信息,支持搜索、使用场景/风格/主体筛选和详情抽屉;“使用此 Prompt”只把原文带回当前 Canvas 对话草稿,不自动发送、不构成社区。列表和详情数据由服务端提供,客户端不打包数据集;服务端字段契约见 [`docs/prompt-museum-server-contract.md`](docs/prompt-museum-server-contract.md)。 - 视觉系统:单一浅色主题,品牌蓝 `#3A5578`、星火橙 `#F26A3D`、白色画布与低饱和蓝灰层级。 - 字体系统:Renderer UI 内嵌 Inter Variable 与经过字符子集化的 Source Han Sans SC WOFF2,按字符范围统一中英文并保留系统中文字体 fallback;代码、路径和日志使用独立等宽字体。 @@ -74,7 +75,7 @@ pnpm run package:win pnpm run package:linux ``` -Windows 打包脚本会先准备目标架构所需的 Pi、Python 与 uv 运行时资源,产物写入忽略的 `release/` 目录。AI 学习不再携带独立播放器产物。Windows 正式包中的 Pi、Python 和 uv 均从安装目录解析;缺少本地资源时启动或产物验证会直接失败,不会回退到系统 Python、npm 或 npx 下载。Git、项目编译器和用户选择的浏览器仍属于项目/系统工具,不属于内置 Pi 运行时。macOS 与 Linux 的双架构产物需要分别完成对应架构的 staging 与产物验证后再发布。 +各平台打包脚本会先准备目标架构所需的 Pi、Python 与 uv 运行时资源,产物写入忽略的 `release/` 目录。AI 学习不再携带独立播放器产物。正式包中的 Pi、Python 和 uv 均从安装目录解析;Agent Server 以已 staged 的 `resources/pi-runtime/package.json` 为解析锚点,并校验目标包入口仍位于该包内部。缺少本地资源或合法入口时启动和产物验证会直接失败,不会回退到应用 `node_modules`、系统 Python、npm 或 npx 下载。Git、项目编译器和用户选择的浏览器仍属于项目/系统工具,不属于内置 Pi 运行时。macOS、Windows 与 Linux 的各架构产物需要分别完成对应架构的 staging 与产物验证后再发布。 Pi 正式包必须继续运行 `pnpm run verify:artifact:pi`、`pnpm run smoke:pi:real` 和 `pnpm run perf:pi:release`。这里的 `real` 表示从最终产品可执行文件启动最终 `resources/pi-runtime`,并使用受控的 Provider-shaped 回环服务验证会话、工具、中止、结算、重开、并发隔离、子 Agent 与退出;它不表示真实外部 Provider 已验证。各目标平台、证据字段、兼容边界与整版本回滚步骤见 [`docs/pi-runtime-release-runbook.md`](docs/pi-runtime-release-runbook.md)。 @@ -114,7 +115,7 @@ Pi 正式包必须继续运行 `pnpm run verify:artifact:pi`、`pnpm run smoke:p ### 项目内置编码技能 - 产品内置四个核心编码技能根:`agent-browser`(开发浏览器)、`frontend-slides`(项目演示)、`grilling`(方案质询)和 `planning-with-files`(项目规划),统一从 vendor-neutral 的 `resources/coding-skills/` 打包。`data-service`(开发数据)由固定的 `resources/coding-plugins/data-service/` 插件包持有,不在核心技能根中复制路径或定义。 -- Marketplace 插件 Skill(包括 Web Search)从已签名 Package Store Release 动态物化,不进入核心技能根,也不靠静态工具白名单;只有下一代 parent worker 的冻结 effective snapshot 同时满足安装、账号 Library、项目启用与伙伴分配时才生效,child worker 不继承 hosted tool。 +- Marketplace 官方插件 Skill 从已签名 Package Store Release 或受控内置定义动态物化,不进入核心技能根;只有 parent worker 的冻结 effective snapshot 满足对应账号、设备、项目与伙伴条件时才生效,child worker 不继承 hosted tool。所选模型的 Web Search 与 Conversation 安装的设备包使用各自独立的 Main-owned 合同,不属于这条 Marketplace 生命周期。 - 项目插件“启用”和把插件技能分配给智能体是两个独立动作:只有项目已启用 Data Service 时,未分配的 `data-service` 才可供新选择;禁用后,已有分配仍会显示并继续保存在智能体的 `skillIds`,但处于不可用且不生效的状态,重新启用后恢复生效。未启用的技能不进入该智能体的有效 Pi 资源集合。 - `data-service` 只在用户显式请求后触发:先检查并说明最小集合,用户确认后配置一次、复制 SDK 资产,再用本地预览执行 put/read-back;它不用于已发布作品。 - 创建项目智能体时,`agent-browser`、`grilling` 与 `planning-with-files` 默认启用;`frontend-slides` 作为专项能力可手动启用。用户可以在创建或编辑智能体时调整选择。最终选择写入项目智能体的 `skillIds`。 @@ -130,12 +131,13 @@ Pi 正式包必须继续运行 `pnpm run verify:artifact:pi`、`pnpm run smoke:p - “可用技能”入口使用模块图标;点击技能后先展示目录结构,再展示主文件 `SKILL.md` 原文,并支持返回技能列表。 - 一个智能体可以拥有多条互相独立的 Pi Session;智能体和对话元数据分别由 `.makelore/project.json` 与 `.makelore/conversations.json` 保存,稳定的智能体与对话 id 保持本地历史连续。 - 核心聊天页左侧把本地对话嵌套在展开的所属智能体下,并在该智能体子组中提供新建入口;首次选择没有对话的智能体时立即创建本地元数据,同时异步准备对应运行时。即使准备被阻塞或超时,输入框仍可编辑,草稿也不会丢失。 -- Conversation 历史按需从 Main-owned Snapshot 读取。公开 SSE 只交付 Snapshot 与按 Conversation、worker generation 分组的 `patch-batch`;Renderer 整批校验连续 seq 后在一次状态事务中顺序应用,缺口或畸形批次只恢复目标 Conversation,隐藏 Conversation 的流式更新不会提交选中时间线。 -- 核心时间线按每条用户消息组织为“用户消息、连续处理过程、结论回复”。Pi 的流式 thinking、工具、压缩、重试、通知和 `subagent.v1` 单个/并行/串行子任务共享一条轻量过程轨道;运行时显示“处理中”和活动指示,完成后按耗时(历史缺少计时时按项目数)收纳为可展开的“已处理”摘要。文字层级按结构化事件类型划分,不按文案关键词猜测:助手在工具调用前后直接对用户说的过程正文与最终结论都使用黑色;thinking、工具输入输出、压缩、子任务和运行状态使用浅灰辅助文字,仍在运行的灰色缩略文字带有低对比度 Shimmer 擦拭高光,结束后停止动画;错误状态保持红色。流式 thinking 与助手过程正文默认用一行高度展示最新内容,缩略态保持自然左对齐并始终追随最后一条非空内容:短内容从左侧开始,超出可视宽度时才在行内滚到最新尾部;两类文字都在原位更新且不做位移动画,助手过程正文也不使用 Shimmer。工具调用用一行摘要持续展示工具名称、最新输入或输出和运行状态,并可按流式批次轻量整行轮动。思考、助手过程正文和工具节点均可展开查看完整详情,工具的累计输出和浏览器附件留在对应的二级展开区而不形成独立气泡。结论回复按安全 Markdown 排版标题、列表、引用、代码和表格;可点击的 HTTP/HTTPS 与本地 HTML 链接统一使用无底色的橙色文字。左键通过 Main-owned 系统入口交给默认浏览器打开;右键可打开系统菜单,网页链接可复制地址,本地链接还可在访达或系统文件夹中显示。同一回复已经给出完整 HTML 路径时,对应的相对查看路径也会解析到该文件。内部 `turn-start`/`turn-end` 只维持事件顺序、不呈现在界面。时间线默认保留最近 120 个节点的渲染窗口,可按 100 个节点加载更早内容。 +- Conversation 历史按需从 Main-owned Snapshot 读取。Session hydration 沿完整 active branch 投影可见历史;公开 SSE 只交付 Snapshot 与按 Conversation、worker generation 分组的 `patch-batch`。Renderer 整批校验连续 seq 后在一次状态事务中顺序应用,缺口或畸形批次只恢复目标 Conversation,隐藏 Conversation 的流式更新不会提交选中时间线。Main 发出 `lifecycle:sleep` 时页面关闭旧事件流;视图挂载、项目上下文变化、窗口重新可见或获得焦点时,已选 Conversation 会静默重读权威 Snapshot,从而收敛后台已结束的任务,但绝不重放已接受或状态不确定的 mutation。时间线默认挂载最近 120 个节点,接近顶部时按 100 个节点自动追加更早内容并补偿新增高度,保持当前阅读锚点。 +- 核心时间线按每条用户消息组织为“用户消息、连续处理过程、结论回复”。Pi 的流式 thinking、工具、压缩、重试、通知和 `subagent.v1` 单个/并行/串行子任务共享一条轻量过程轨道;运行时显示“处理中”和活动指示,完成后按耗时(历史缺少计时时按项目数)收纳为可展开的“已处理”摘要。文字层级按结构化事件类型划分,不按文案关键词猜测:助手在工具调用前后直接对用户说的过程正文与最终结论都使用黑色;thinking、工具输入输出、压缩、子任务和运行状态使用浅灰辅助文字,仍在运行的灰色缩略文字带有低对比度 Shimmer 擦拭高光,结束后停止动画;错误状态保持红色。折叠的 thinking、助手过程正文和工具输出固定展示第一个可见块的首个非空行,横向偏移始终为零;尾部流式更新不会替换未变化的首行预览,展开后仍可查看完整内容。两类流式文字均在原位更新且不做位移动画,助手过程正文也不使用 Shimmer。工具调用用一行摘要持续展示工具名称、最新输入或输出和运行状态,并可按流式批次轻量整行轮动。思考、助手过程正文和工具节点均可展开查看完整详情,工具的累计输出和浏览器附件留在对应的二级展开区而不形成独立气泡。结论回复按安全 Markdown 排版标题、列表、引用、代码和表格;可点击的 HTTP/HTTPS 与本地 HTML 链接统一使用无底色的橙色文字。左键通过 Main-owned 系统入口交给默认浏览器打开;右键可打开系统菜单,网页链接可复制地址,本地链接还可在访达或系统文件夹中显示。同一回复已经给出完整 HTML 路径时,对应的相对查看路径也会解析到该文件。内部 `turn-start`/`turn-end` 只维持事件顺序、不呈现在界面。 - Composer 使用贴底的复合输入面板,支持文字、登录后的语音转写,以及粘贴或选择 PNG/JPEG/WebP/GIF 图片;每条消息最多 16 张图片、最多并行上传 4 张。语音录制通过现有 Main-owned Works Speech Host API 转成文字,并在项目或账号切换时立即停止;当前对话的模型与思考强度组合入口、语音及发送/中止入口保留在底部控制行,模型入口默认使用透明底色。模型与思考强度通过向上展开的分级菜单切换,只列出当前 Provider 或 Makelore 已验证模型档案实际支持的选项;Qwen3.6 Plus 使用混合思考开关,Qwen3.8 Max 暴露低/中/高三档并把产品“高”映射到服务端 `xhigh`。模型仅报告 `off` 时明确显示“不可调”,不伪造未知模型的推理档位。切换完成后静默刷新当前 Snapshot,只有真正恢复 worker 时才显示本地 Agent 重连提示。重复读取 Snapshot 不发布无变化的 worker 状态;Snapshot 与 SSE 批次竞态时,Renderer 会丢弃已覆盖事件并只应用连续的新尾部,避免形成伪重连循环。上下文接近模型限制或发生溢出时由 Pi 自动整理,Composer 不提供手动整理入口;自动整理的进度和结果仍显示在对话过程轨道中。图片在发送前只保留本地预览,点击发送时才经 Main-owned 有界二进制接口上传一次;Main 在落盘前核对 MIME 与最小图片签名,状态与事件只保存 attachment id,时间线按需读取二进制并创建临时 object URL,不保存重复 base64。智能体报告 `changed-file.v1` 后,输入框上方会展示当前轮次的文件数与增删行统计,并可展开查看 Main-owned 变更快照中的逐文件差异。 - Makelore 在应用侧按 Session 独立提交、跟踪和隔离运行状态,不使用“当前对话正在回复”的全局界面锁;同一 Session 的后续消息仍按顺序排队。最终产品中的 Pi 运行时会通过受控 Provider-shaped 回环 smoke 验证同一 Agent Server 内多个 Conversation 逻辑线程的重叠执行、状态隔离、凭证引用隔离、单线程关闭和 Server 崩溃后重启,以及独立子 Agent 进程的隔离与回收;真实外部 Provider 的并发、限流、协议兼容和凭证隔离仍是独立风险,未执行真实 Provider 验证时不得标记为 Pass。 - 首次发送会立即生成稳定的乐观用户消息;HTTP 202 只表示本地 Agent 已接收。后续失败不会删除已接受消息,不确定交付不会自动重发;准备失败可在目标 Conversation 上手动恢复。 -- 运行中的 Conversation 可把新消息作为 steer 引导当前回答或 follow-up 排到下一轮,并显示队列位置;队列只在 `agent_settled` 后释放,用户可中止当前运行。select/confirm/input/editor 交互在输入区上方回答,select 除预设选项外支持自由输入;交互提交或取消后立即从输入区移除,失效请求会就地提示并允许重试或取消。 +- 运行中的 Conversation 可把新消息作为 steer 引导当前回答或 follow-up 排到下一轮,并显示队列位置;队列正常在 `agent_settled` 后释放,用户可中止当前运行。若 Pi 已终结但该握手丢失,Main 会在 30 秒宽限后仅探测目标线程:权威 idle 状态会补齐持久化最终回复并 exactly-once 释放 ownership,矛盾状态则显式失败该目标,且不会重放已接受请求。select/confirm/input/editor 交互在输入区上方回答,select 除预设选项外支持自由输入;交互提交或取消后立即从输入区移除,失效请求会就地提示并允许重试或取消。 +- 隐藏 Conversation 的红色新消息标记只在出现新的待回答交互,或当前任务进入 completed、failed、aborted 终态时设置;助手流式文字、思考、工具过程和单个工具失败不会提前触发红点。 - 核心聊天页标题栏左侧只显示 Conversation 名称,必要的中止/恢复入口保留在右侧;不再展示 Pi 空闲/运行副标题。对话列表栏只显示项目名称和项目设置入口,不显示“Pi 本地对话”副标题;全局品牌 Logo 贴齐窗口最右边缘。归档、未读、整段 Conversation 分支和编程工具抽屉均不占用标题栏。从已持久化的 user 消息仍可“从这里创建新对话分支”,assistant 消息和未持久化消息不提供该动作;分支只创建新的 Conversation 历史,不表示文件回滚。Pi 的工具调用与浏览器附件继续在对应的对话过程节点中按需展开;分享、待办、全局运行时和 revert/unrevert 不属于该产品界面。 ## 运行内核与项目数据 diff --git a/electron/api/routes/ai-proxy.ts b/electron/api/routes/ai-proxy.ts index ce59208..3015793 100644 --- a/electron/api/routes/ai-proxy.ts +++ b/electron/api/routes/ai-proxy.ts @@ -86,7 +86,8 @@ function getForwardedOneApiStatus(status: number, bodyText: string): number { if (status === 502 && isAIGatewayUserContextMissing(bodyText)) { return 401; } - if (status === 429 && getAIGatewayErrorKind(bodyText) === 'quota_exhausted') { + if ((status === 403 || status === 429) + && getAIGatewayErrorKind(bodyText) === 'quota_exhausted') { return 402; } if (status === 429 && isAIGatewayUpstreamSaturated(bodyText)) { diff --git a/electron/coding-runtime/pi/event-projector.ts b/electron/coding-runtime/pi/event-projector.ts index 72ee270..f49e511 100644 --- a/electron/coding-runtime/pi/event-projector.ts +++ b/electron/coding-runtime/pi/event-projector.ts @@ -6,8 +6,8 @@ import type { ConversationToolNode, PublicUsage, } from '../contracts'; -import { isAIGatewayUserContextMissing } from '../../../shared/ai-gateway-error-details'; import type { PiRpcEvent } from './rpc-client'; +import { projectPiProviderFailure } from './provider-failure'; import { subagentDetailsOfResult } from '../subagent-protocol'; import { isProductToolName, @@ -45,17 +45,6 @@ function retryFailure(message: string) { }; } -function providerFailure(message: unknown) { - if (typeof message === 'string' && isAIGatewayUserContextMissing(message)) { - return { - code: 'CODING_PROVIDER_AUTH_REQUIRED' as const, - message: '模型服务身份上下文无效,请重试;若仍失败请重新登录。', - recoverable: true, - }; - } - return retryFailure('模型服务请求失败,请稍后重试。'); -} - function asRecord(value: unknown): Record | null { return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record @@ -294,7 +283,7 @@ export class PiEventProjector { ...run, status: 'error', terminalReason: 'failed', - error: providerFailure(event.finalError), + error: projectPiProviderFailure(event.finalError), }, }]; } @@ -485,7 +474,7 @@ export class PiEventProjector { ...snapshot.run, status: 'error', terminalReason: 'failed', - error: providerFailure(assistant.errorMessage), + error: projectPiProviderFailure(assistant.errorMessage), }, }]; } diff --git a/electron/coding-runtime/pi/extensions/makelore-runtime.ts b/electron/coding-runtime/pi/extensions/makelore-runtime.ts index 270f026..d8e6d65 100644 --- a/electron/coding-runtime/pi/extensions/makelore-runtime.ts +++ b/electron/coding-runtime/pi/extensions/makelore-runtime.ts @@ -180,12 +180,13 @@ export function createMakeloreRuntime(runtimeDefaults = {}) { return response.result; } - function registerProductTool(name, label, description, parameters) { + function registerProductTool(name, label, description, parameters, projectWriteLease = false) { pi.registerTool({ name, label, description, parameters, + ...(projectWriteLease ? { executionMode: 'sequential' } : {}), async execute(toolCallId, params, signal) { return await invokeProduct(toolCallId, name, params, signal); }, @@ -227,6 +228,7 @@ export function createMakeloreRuntime(runtimeDefaults = {}) { declaration.label, declaration.description, declaration.inputSchema, + dynamicLeaseTools.has(declaration.name), ); } } diff --git a/electron/coding-runtime/pi/provider-failure.ts b/electron/coding-runtime/pi/provider-failure.ts new file mode 100644 index 0000000..346a5e4 --- /dev/null +++ b/electron/coding-runtime/pi/provider-failure.ts @@ -0,0 +1,25 @@ +import type { CodingRuntimePublicError } from '../contracts'; +import { isAIGatewayUserContextMissing } from '../../../shared/ai-gateway-error-details'; +import { getAIGatewayErrorKind } from '../../../shared/ai-gateway-error-kind'; + +export function projectPiProviderFailure(message: unknown): CodingRuntimePublicError { + if (typeof message === 'string' && isAIGatewayUserContextMissing(message)) { + return { + code: 'CODING_PROVIDER_AUTH_REQUIRED', + message: '模型服务身份上下文无效,请重试;若仍失败请重新登录。', + recoverable: true, + }; + } + if (typeof message === 'string' && getAIGatewayErrorKind(message) === 'quota_exhausted') { + return { + code: 'CODING_PROVIDER_QUOTA_EXHAUSTED', + message: '词元点数余额不足,请充值后重试。', + recoverable: false, + }; + } + return { + code: 'CODING_RUNTIME_START_FAILED', + message: '模型服务请求失败,请稍后重试。', + recoverable: true, + }; +} diff --git a/electron/coding-runtime/pi/release-proof.ts b/electron/coding-runtime/pi/release-proof.ts index e2e7117..cfbbc15 100644 --- a/electron/coding-runtime/pi/release-proof.ts +++ b/electron/coding-runtime/pi/release-proof.ts @@ -1416,7 +1416,7 @@ export async function runFinalAsarExtensionProof(): Promise { this.recordProjectionFailure(event.conversationId, event.generation, error); }); diff --git a/electron/coding-runtime/pi/session-projector.ts b/electron/coding-runtime/pi/session-projector.ts index 70c611b..b27d7a7 100644 --- a/electron/coding-runtime/pi/session-projector.ts +++ b/electron/coding-runtime/pi/session-projector.ts @@ -14,6 +14,7 @@ import type { } from './event-projector'; import { isProductToolName, productToolDetailsOfResult } from '../product-tool-protocol'; import { subagentDetailsOfResult } from '../subagent-protocol'; +import { projectPiProviderFailure } from './provider-failure'; export interface PiSessionSnapshotInput { snapshot: ConversationSnapshot; @@ -106,23 +107,6 @@ function activePath(entriesValue: unknown, leafId: unknown): SessionEntryRecord[ return path.reverse(); } -function retainedTail(path: SessionEntryRecord[]): SessionEntryRecord[] { - const compactionIndex = path.findLastIndex((entry) => entry.type === 'compaction'); - if (compactionIndex < 0) return path; - const compaction = path[compactionIndex]; - const firstKeptEntryId = compaction.firstKeptEntryId; - if (typeof firstKeptEntryId !== 'string') unreadable(); - const firstKeptIndex = path.findIndex( - (entry, index) => index < compactionIndex && entry.id === firstKeptEntryId, - ); - if (firstKeptIndex < 0) unreadable(); - return [ - compaction, - ...path.slice(firstKeptIndex, compactionIndex), - ...path.slice(compactionIndex + 1), - ]; -} - async function contentBlocks( messageId: string, content: unknown, @@ -265,6 +249,19 @@ async function projectEntries( ...(stopReason ? { stopReason } : {}), }; nodes.push(node); + if (message.stopReason === 'error') { + const failure = projectPiProviderFailure(message.errorMessage); + if (failure.code === 'CODING_PROVIDER_AUTH_REQUIRED' + || failure.code === 'CODING_PROVIDER_QUOTA_EXHAUSTED') { + nodes.push({ + kind: 'notice', + id: `${messageId}:provider-error`, + code: failure.code, + level: 'error', + message: failure.message, + }); + } + } if (!Array.isArray(message.content)) continue; for (const value of message.content) { const block = asRecord(value); @@ -310,6 +307,16 @@ function reconcileLiveIds( liveNodes: ConversationNode[], ): ConversationNode[] { const used = new Set(); + const durableCompactions = durableNodes.filter((node) => node.kind === 'compaction'); + const liveCompactions = liveNodes.filter((node) => node.kind === 'compaction'); + const compactionMatches = new Map>(); + for ( + let durableIndex = durableCompactions.length - 1, liveIndex = liveCompactions.length - 1; + durableIndex >= 0 && liveIndex >= 0; + durableIndex -= 1, liveIndex -= 1 + ) { + compactionMatches.set(durableCompactions[durableIndex].id, liveCompactions[liveIndex]); + } return durableNodes.map((node) => { if (node.kind === 'message') { const live = liveNodes.find((candidate) => candidate.kind === 'message' @@ -338,10 +345,8 @@ function reconcileLiveIds( }; } if (node.kind === 'compaction') { - const live = liveNodes.findLast( - (candidate) => candidate.kind === 'compaction' && !used.has(candidate.id), - ); - if (!live || live.kind !== 'compaction') return node; + const live = compactionMatches.get(node.id); + if (!live) return node; used.add(live.id); return { ...node, id: live.id, runId: live.runId }; } @@ -375,7 +380,7 @@ export async function projectPiSessionSnapshot( const response = asRecord(input.entries); const state = asRecord(input.state); if (!response || !state) unreadable(); - const path = retainedTail(activePath(response.entries, response.leafId)); + const path = activePath(response.entries, response.leafId); const durableNodes = await projectEntries(path, input); const nodes = reconcileLiveIds(durableNodes, input.snapshot.nodes); return { diff --git a/electron/coding-runtime/pi/worker-pool.ts b/electron/coding-runtime/pi/worker-pool.ts index f507a92..c27d9c1 100644 --- a/electron/coding-runtime/pi/worker-pool.ts +++ b/electron/coding-runtime/pi/worker-pool.ts @@ -26,6 +26,45 @@ import { } from './telemetry'; import { logger } from '../../utils/logger'; +const SETTLEMENT_PROBE_INTERVAL_MS = 3_000; +const SETTLEMENT_PROBE_TIMEOUT_MS = 5_000; +const TERMINAL_SETTLEMENT_TIMEOUT_MS = 30_000; + +function recordValue(value: unknown): Record | null { + return value !== null && typeof value === 'object' && !Array.isArray(value) + ? value as Record + : null; +} + +function sessionIsAuthoritativelyIdle(value: unknown): boolean { + const state = recordValue(value); + return state?.isStreaming === false + && state.isCompacting === false + && state.pendingMessageCount === 0; +} + +function sessionIsTerminallyStalled(value: unknown): boolean { + const state = recordValue(value); + return state?.isStreaming === true + && state.isCompacting === false + && state.pendingMessageCount === 0 + && state.retryAttempt === 0; +} + +function isTerminalAssistantMessage(event: PiRpcEvent): boolean { + if (event.type !== 'message_end') return false; + const message = recordValue(event.message); + return message?.role === 'assistant' + && ['stop', 'length', 'error', 'aborted'].includes(String(message.stopReason)); +} + +function continuesAfterTerminalCandidate(event: PiRpcEvent): boolean { + return event.type === 'agent_start' + || event.type === 'auto_retry_start' + || event.type === 'compaction_start' + || (event.type === 'message_start' && recordValue(event.message)?.role === 'user'); +} + export interface PiConversationWorker { readonly id: string; readonly generation: number; @@ -122,6 +161,7 @@ export type PiWorkerPoolEvent = conversationId: string; generation: number; runId: string; + source: 'agent_settled' | 'compact_rpc' | 'state_probe'; } | { type: 'top-level.failed'; @@ -147,6 +187,9 @@ export interface PiWorkerPoolOptions { revisionCoordinator?: PiManagedInputRevisionCoordinator; now?: () => number; onTelemetry?: (event: PiRuntimeTelemetryEvent) => void; + settlementProbeIntervalMs?: number; + settlementProbeTimeoutMs?: number; + terminalSettlementTimeoutMs?: number; } export interface PiProcessLease { @@ -255,6 +298,20 @@ interface PendingTopLevelRun extends PiTopLevelRunInput { queuedAt?: number; } +interface ActiveTopLevelRun { + runId: string; + run: PendingTopLevelRun; + generation: number; + cold: boolean; + acceptedAt?: number; + confirmationStartedAt?: number; + confirmation: 'pending' | 'uncertain' | 'confirmed'; + cancelConfirmation?: () => void; + settlementProbeTimer?: ReturnType; + settlementProbeInFlight?: boolean; + terminalObservedAt?: number; +} + export class PiWorkerPool { private readonly openWorker: PiWorkerPoolOptions['openWorker']; private readonly maxRunning: number; @@ -264,20 +321,14 @@ export class PiWorkerPool { private readonly revisions: PiManagedInputRevisionCoordinator; private readonly now: () => number; private readonly onTelemetry: ((event: PiRuntimeTelemetryEvent) => void) | undefined; + private readonly settlementProbeIntervalMs: number; + private readonly settlementProbeTimeoutMs: number; + private readonly terminalSettlementTimeoutMs: number; private readonly workers = new Map(); private readonly prepareFlights = new Map>(); private readonly rebuildFlights = new Set>(); private readonly waitingRuns: PendingTopLevelRun[] = []; - private readonly activeRuns = new Map void; - }>(); + private readonly activeRuns = new Map(); private readonly generations = new Map(); private readonly listeners = new Set<(event: PiWorkerPoolEvent) => void>(); private readonly reclaimWaiters = new Set<() => void>(); @@ -297,12 +348,27 @@ export class PiWorkerPool { this.revisions = options.revisionCoordinator ?? new PiManagedInputRevisionCoordinator(); this.now = options.now ?? Date.now; this.onTelemetry = options.onTelemetry; + this.settlementProbeIntervalMs = options.settlementProbeIntervalMs + ?? SETTLEMENT_PROBE_INTERVAL_MS; + this.settlementProbeTimeoutMs = options.settlementProbeTimeoutMs + ?? SETTLEMENT_PROBE_TIMEOUT_MS; + this.terminalSettlementTimeoutMs = options.terminalSettlementTimeoutMs + ?? TERMINAL_SETTLEMENT_TIMEOUT_MS; if (!Number.isSafeInteger(this.maxRunning) || this.maxRunning <= 0) { throw new Error('maxRunning must be a positive safe integer'); } if (!Number.isSafeInteger(this.maxIdle) || this.maxIdle < 0) { throw new Error('maxIdle must be a non-negative safe integer'); } + for (const [name, value] of [ + ['settlementProbeIntervalMs', this.settlementProbeIntervalMs], + ['settlementProbeTimeoutMs', this.settlementProbeTimeoutMs], + ['terminalSettlementTimeoutMs', this.terminalSettlementTimeoutMs], + ] as const) { + if (!Number.isSafeInteger(value) || value <= 0) { + throw new Error(`${name} must be a positive safe integer`); + } + } } prepare(conversation: PrepareConversationInput): Promise { @@ -544,7 +610,7 @@ export class PiWorkerPool { failTopLevel(conversationId: string, runId: string, error: Error): void { const active = this.activeRuns.get(conversationId); if (active?.runId === runId) { - this.activeRuns.delete(conversationId); + this.removeActiveRun(conversationId); active.cancelConfirmation?.(); this.runningCount -= 1; const record = this.workers.get(conversationId); @@ -724,7 +790,7 @@ export class PiWorkerPool { const records = [...this.workers.values()]; this.workers.clear(); const activeRuns = [...this.activeRuns.values()]; - this.activeRuns.clear(); + for (const active of activeRuns) this.removeActiveRun(active.run.conversationId); this.runningCount = 0; for (const active of activeRuns) active.cancelConfirmation?.(); await Promise.all(records.map(async (record) => { @@ -778,13 +844,7 @@ export class PiWorkerPool { }); this.confirmTopLevel(current, run); if (run.command.type === 'compact') { - this.settleTopLevel(current); - this.emit({ - type: 'top-level.settled', - conversationId: run.conversationId, - generation: current.generation, - runId: run.runId, - }); + this.settleTopLevelAndNotify(current, 'compact_rpc'); } run.resolve(response); } catch (error) { @@ -799,7 +859,7 @@ export class PiWorkerPool { return; } if (active) { - this.activeRuns.delete(run.conversationId); + this.removeActiveRun(run.conversationId); active.cancelConfirmation?.(); this.runningCount -= 1; this.launchWaitingRuns(); @@ -834,6 +894,7 @@ export class PiWorkerPool { active.cold, ); record.acceptedPromptCount += 1; + this.scheduleSettlementProbe(record, active); } return true; } @@ -854,19 +915,13 @@ export class PiWorkerPool { runId: run.runId, }); if (run.command.type === 'compact') { - this.settleTopLevel(record); - this.emit({ - type: 'top-level.settled', - conversationId: run.conversationId, - generation: record.generation, - runId: run.runId, - }); + this.settleTopLevelAndNotify(record, 'compact_rpc'); } return; } if (result.error.code === 'PI_RPC_EXITED' || result.error.code === 'PI_RPC_PROTOCOL_ERROR') return; - this.activeRuns.delete(run.conversationId); + this.removeActiveRun(run.conversationId); active.cancelConfirmation = undefined; this.runningCount -= 1; if (this.workers.get(run.conversationId) === record && record.state !== 'crashed') { @@ -889,10 +944,10 @@ export class PiWorkerPool { }); } - private settleTopLevel(record: WorkerRecord): void { + private settleTopLevel(record: WorkerRecord): ActiveTopLevelRun | null { const conversationId = record.conversation.conversationId; const active = this.activeRuns.get(conversationId); - if (!active || active.generation !== record.generation) return; + if (!active || active.generation !== record.generation) return null; const cancelConfirmation = active.cancelConfirmation; if (active.confirmation !== 'confirmed') { this.confirmTopLevel(record, active.run); @@ -911,7 +966,7 @@ export class PiWorkerPool { active.cold, ); } - this.activeRuns.delete(conversationId); + this.removeActiveRun(conversationId); cancelConfirmation?.(); this.runningCount -= 1; record.state = 'idle'; @@ -932,6 +987,110 @@ export class PiWorkerPool { } this.launchWaitingRuns(); void this.trimIdleWorkers(); + return active; + } + + private settleTopLevelAndNotify( + record: WorkerRecord, + source: Extract['source'], + ): boolean { + const active = this.settleTopLevel(record); + if (!active) return false; + this.emit({ + type: 'top-level.settled', + conversationId: record.conversation.conversationId, + generation: record.generation, + runId: active.runId, + source, + }); + return true; + } + + private observeTopLevelEvent(record: WorkerRecord, event: PiRpcEvent): void { + const conversationId = record.conversation.conversationId; + const active = this.activeRuns.get(conversationId); + if (!active || active.generation !== record.generation) return; + if (continuesAfterTerminalCandidate(event)) { + active.terminalObservedAt = undefined; + return; + } + if (isTerminalAssistantMessage(event) + || (event.type === 'agent_end' && event.willRetry === false)) { + active.terminalObservedAt ??= this.now(); + this.scheduleSettlementProbe(record, active); + } + } + + private scheduleSettlementProbe(record: WorkerRecord, active: ActiveTopLevelRun): void { + if (this.shuttingDown + || active.run.command.type !== 'prompt' + || active.settlementProbeTimer + || active.settlementProbeInFlight + || this.activeRuns.get(record.conversation.conversationId) !== active) return; + active.settlementProbeTimer = setTimeout(() => { + active.settlementProbeTimer = undefined; + void this.probeTopLevelSettlement(record, active); + }, this.settlementProbeIntervalMs); + active.settlementProbeTimer.unref(); + } + + private async probeTopLevelSettlement( + record: WorkerRecord, + active: ActiveTopLevelRun, + ): Promise { + const conversationId = record.conversation.conversationId; + if (this.activeRuns.get(conversationId) !== active + || active.generation !== record.generation + || this.workers.get(conversationId) !== record + || record.state === 'crashed') return; + active.settlementProbeInFlight = true; + try { + const response = await record.worker.request({ type: 'get_state' }, { + retry: 'read-only-once', + timeoutMs: this.settlementProbeTimeoutMs, + }); + if (this.activeRuns.get(conversationId) !== active) return; + if (sessionIsAuthoritativelyIdle(response.data)) { + this.settleTopLevelAndNotify(record, 'state_probe'); + return; + } + if (this.terminalSettlementExpired(active) + && sessionIsTerminallyStalled(response.data)) { + this.handleInvalidation(record, this.settlementProtocolError(record)); + } + } catch { + if (this.activeRuns.get(conversationId) === active + && this.terminalSettlementExpired(active)) { + this.handleInvalidation(record, this.settlementProtocolError(record)); + } + } finally { + active.settlementProbeInFlight = false; + if (this.activeRuns.get(conversationId) === active) { + this.scheduleSettlementProbe(record, active); + } + } + } + + private terminalSettlementExpired(active: ActiveTopLevelRun): boolean { + return active.terminalObservedAt !== undefined + && this.now() - active.terminalObservedAt >= this.terminalSettlementTimeoutMs; + } + + private settlementProtocolError(record: WorkerRecord): PiProcessError { + return new PiProcessError( + 'PI_RPC_PROTOCOL_ERROR', + 'Pi prompt produced a terminal response but did not settle', + { generation: record.generation }, + ); + } + + private removeActiveRun(conversationId: string): ActiveTopLevelRun | undefined { + const active = this.activeRuns.get(conversationId); + if (!active) return undefined; + this.activeRuns.delete(conversationId); + if (active.settlementProbeTimer) clearTimeout(active.settlementProbeTimer); + active.settlementProbeTimer = undefined; + return active; } private async ensureFresh(record: WorkerRecord): Promise { @@ -1098,7 +1257,17 @@ export class PiWorkerPool { reconfigureAfterSettled: false, }; record.unsubscribeEvent = worker.subscribe((event) => { - if (event.type === 'agent_settled') this.settleTopLevel(record); + if (event.type === 'makelore_thread_error' + && event.code === 'PROMPT_FAILED_AFTER_ACCEPTANCE') { + this.handleInvalidation(record, new PiProcessError( + 'PI_RPC_PROTOCOL_ERROR', + 'Pi prompt failed after it was accepted', + { generation }, + )); + return; + } + this.observeTopLevelEvent(record, event); + if (event.type === 'agent_settled') this.settleTopLevelAndNotify(record, 'agent_settled'); this.emit({ type: 'worker.event', conversationId: conversation.conversationId, @@ -1190,7 +1359,7 @@ export class PiWorkerPool { const active = this.activeRuns.get(conversationId); if (active?.generation === record.generation) { - this.activeRuns.delete(conversationId); + this.removeActiveRun(conversationId); active.cancelConfirmation?.(); this.runningCount -= 1; } @@ -1218,7 +1387,7 @@ export class PiWorkerPool { private cancelConversationRuns(conversationId: string, error: Error): void { const active = this.activeRuns.get(conversationId); if (active) { - this.activeRuns.delete(conversationId); + this.removeActiveRun(conversationId); active.cancelConfirmation?.(); this.runningCount -= 1; } diff --git a/resources/pi-agent-server.mjs b/resources/pi-agent-server.mjs index 361f2ff..5d3a26b 100644 --- a/resources/pi-agent-server.mjs +++ b/resources/pi-agent-server.mjs @@ -1,5 +1,6 @@ import { randomUUID } from 'node:crypto'; import { readFile } from 'node:fs/promises'; +import { findPackageJSON } from 'node:module'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; @@ -14,12 +15,36 @@ const runtimeRoot = runtimeRootFromArgs(process.argv.slice(2)); const runtimeModule = (...segments) => pathToFileURL(path.join(runtimeRoot, ...segments)).href; const runtimePackageUrl = pathToFileURL(path.join(runtimeRoot, 'package.json')).href; +async function runtimePackageModule(packageName) { + const packageJsonPath = findPackageJSON(packageName, runtimePackageUrl); + if (!packageJsonPath) throw new Error(`Runtime package is unavailable: ${packageName}`); + const packageRoot = path.dirname(packageJsonPath); + const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8')); + const rootExport = packageJson.exports?.['.']; + const entry = ( + typeof rootExport === 'string' + ? rootExport + : rootExport?.import + ) ?? packageJson.module ?? packageJson.main; + if (typeof entry !== 'string' || !entry.trim()) { + throw new Error(`Runtime package import entry is unavailable: ${packageName}`); + } + const entryPath = path.resolve(packageRoot, entry); + const entryRelative = path.relative(packageRoot, entryPath); + if (!entryRelative || entryRelative === '..' || entryRelative.startsWith(`..${path.sep}`) + || path.isAbsolute(entryRelative)) { + throw new Error(`Runtime package import entry escapes its package: ${packageName}`); + } + return pathToFileURL(entryPath).href; +} + const outputGuard = await import(runtimeModule('dist', 'core', 'output-guard.js')); outputGuard.takeOverStdout(); +const piAiModule = await runtimePackageModule('@earendil-works/pi-ai'); const [pi, piAi, httpDispatcher, jsonEvents, jsonl, themeModule, shellModule] = await Promise.all([ import(runtimeModule('dist', 'index.js')), - import(import.meta.resolve('@earendil-works/pi-ai', runtimePackageUrl)), + import(piAiModule), import(runtimeModule('dist', 'core', 'http-dispatcher.js')), import(runtimeModule('dist', 'modes', 'json-event.js')), import(runtimeModule('dist', 'modes', 'rpc', 'jsonl.js')), @@ -46,6 +71,7 @@ function configureNetwork(settingsManager) { const SERVER_CHANNEL = '@makelore/server'; const PROTOCOL_VERSION = 1; +const PROJECT_WRITE_LEASE_TOOL_NAMES = new Set(['bash', 'edit', 'write']); const threads = new Map(); const openingThreads = new Set(); let shuttingDown = false; @@ -106,6 +132,14 @@ function referencedEnvironmentNames(value) { return names; } +function serializeProjectWriteLeaseTools(session) { + session.agent.state.tools = session.agent.state.tools.map((tool) => ( + PROJECT_WRITE_LEASE_TOOL_NAMES.has(tool.name) && tool.executionMode !== 'sequential' + ? { ...tool, executionMode: 'sequential' } + : tool + )); +} + async function credentialStoreFor(options, providerId) { const models = JSON.parse(await readFile(path.join(options.configDir, 'models.json'), 'utf8')); const provider = record(record(models)?.providers)?.[providerId]; @@ -370,6 +404,12 @@ class AgentThread { async rebind() { const session = this.runtime.session; + // Pi prepares every tool_call hook before starting a parallel tool batch. + // Makelore acquires its project write lease in that hook and releases it in + // tool_result, so two parallel mutation tools would otherwise wait on each + // other before either command can start. Mark only the built-in mutation + // tools sequential; read-only batches retain Pi's parallel execution. + serializeProjectWriteLeaseTools(session); await session.bindExtensions({ uiContext: this.createExtensionUiContext(), mode: 'rpc', @@ -428,7 +468,14 @@ class AgentThread { this.emit(success(id, 'prompt')); }, }).catch((error) => { - if (!accepted) this.emit(failure(id, 'prompt', error)); + if (!accepted) { + this.emit(failure(id, 'prompt', error)); + return; + } + this.emit({ + type: 'makelore_thread_error', + code: 'PROMPT_FAILED_AFTER_ACCEPTANCE', + }); }); return undefined; } @@ -454,6 +501,7 @@ class AgentThread { thinkingLevel: session.thinkingLevel, isStreaming: session.isStreaming, isCompacting: session.isCompacting, + retryAttempt: session.retryAttempt, steeringMode: session.steeringMode, followUpMode: session.followUpMode, sessionFile: session.sessionFile, diff --git a/shared/ai-gateway-error-kind.ts b/shared/ai-gateway-error-kind.ts index d512b7d..f16ca51 100644 --- a/shared/ai-gateway-error-kind.ts +++ b/shared/ai-gateway-error-kind.ts @@ -3,8 +3,10 @@ export type AIGatewayErrorKind = 'quota_exhausted' | 'authentication_invalid'; export function isTokenBalanceExhausted(message: string | null | undefined): boolean { const normalized = message?.trim().toLowerCase() ?? ''; return normalized.includes('token_balance_exhausted') + || normalized.includes('token_point_balance_exhausted') || normalized.includes('token balance exhausted') - || normalized.includes('balance exhausted'); + || normalized.includes('balance exhausted') + || normalized.includes('词元点数余额不足'); } export function getAIGatewayErrorKind(message: string | null | undefined): AIGatewayErrorKind | null { diff --git a/shared/coding-conversation-contracts.ts b/shared/coding-conversation-contracts.ts index 9238815..699d0cd 100644 --- a/shared/coding-conversation-contracts.ts +++ b/shared/coding-conversation-contracts.ts @@ -32,6 +32,7 @@ export type CodingRuntimeErrorCode = | 'CODING_RUNTIME_READY_TIMEOUT' | 'CODING_RUNTIME_PROTOCOL_ERROR' | 'CODING_PROVIDER_AUTH_REQUIRED' + | 'CODING_PROVIDER_QUOTA_EXHAUSTED' | 'CODING_MODEL_UNAVAILABLE' | 'CODING_SESSION_UNREADABLE' | 'CODING_STORAGE_WRITE_FAILED' diff --git a/shared/coding-conversation-reducer.ts b/shared/coding-conversation-reducer.ts index efd53f3..d1f1a70 100644 --- a/shared/coding-conversation-reducer.ts +++ b/shared/coding-conversation-reducer.ts @@ -60,6 +60,7 @@ const ERROR_CODES = new Set([ 'CODING_RUNTIME_READY_TIMEOUT', 'CODING_RUNTIME_PROTOCOL_ERROR', 'CODING_PROVIDER_AUTH_REQUIRED', + 'CODING_PROVIDER_QUOTA_EXHAUSTED', 'CODING_MODEL_UNAVAILABLE', 'CODING_SESSION_UNREADABLE', 'CODING_STORAGE_WRITE_FAILED', diff --git a/src/pages/Chat/CodingChatPanel.tsx b/src/pages/Chat/CodingChatPanel.tsx index 1770f85..35bdd7b 100644 --- a/src/pages/Chat/CodingChatPanel.tsx +++ b/src/pages/Chat/CodingChatPanel.tsx @@ -17,6 +17,7 @@ import { abortCodingConversation, forkCodingConversation, } from '@/lib/coding-conversations'; +import { subscribeHostEvent } from '@/lib/host-events'; import { codingConversationStore, selectCodingConversationDraft, @@ -148,6 +149,7 @@ export function CodingChatPanel({ >({}); const appliedNavigationDraftRef = useRef(null); const automaticCreationKeyRef = useRef(null); + const selectedConversationContextRef = useRef(null); const attachmentsRef = useRef(attachmentsByDraftKey); const uploadedAttachmentsRef = useRef(new Map()); const uploadFlightsRef = useRef(new Map>()); @@ -183,6 +185,11 @@ export function CodingChatPanel({ attachmentsRef.current = attachmentsByDraftKey; + const selectProjectConversation = useCallback((projectId: string, conversationId: string) => { + selectedConversationContextRef.current = `${projectId}:${conversationId}`; + return selectConversation(conversationId); + }, [selectConversation]); + const selectDraft = useCallback((state: CodingConversationStoreState) => ( targetConversationId ? selectCodingConversationDraft(targetConversationId)(state) @@ -236,6 +243,24 @@ export function CodingChatPanel({ useEffect(() => () => disconnectEvents(), [disconnectEvents]); + useEffect(() => subscribeHostEvent('lifecycle:sleep', () => { + disconnectEvents(); + }), [disconnectEvents]); + + useEffect(() => { + if (!targetConversationId) return undefined; + const refreshVisibleConversation = () => { + if (document.visibilityState !== 'visible') return; + void selectConversation(targetConversationId).catch(() => undefined); + }; + window.addEventListener('focus', refreshVisibleConversation); + document.addEventListener('visibilitychange', refreshVisibleConversation); + return () => { + window.removeEventListener('focus', refreshVisibleConversation); + document.removeEventListener('visibilitychange', refreshVisibleConversation); + }; + }, [selectConversation, targetConversationId]); + useEffect(() => () => { for (const attachments of Object.values(attachmentsRef.current)) { for (const attachment of attachments) URL.revokeObjectURL(attachment.previewUrl); @@ -251,15 +276,23 @@ export function CodingChatPanel({ useEffect(() => { if (!activeProject || !selectedAgent) { + selectedConversationContextRef.current = null; clearConversationSelection(); return; } - if (selectedConversation?.agentId === selectedAgent.id && !selectedConversation.archivedAt) return; + if (selectedConversation?.agentId === selectedAgent.id && !selectedConversation.archivedAt) { + const selectionContext = `${activeProject.id}:${selectedConversation.id}`; + if (selectedConversationContextRef.current !== selectionContext) { + void selectProjectConversation(activeProject.id, selectedConversation.id) + .catch(() => undefined); + } + return; + } const next = newestConversation(conversations, selectedAgent.id); if (next) { markConversationUnread(next.id, false); if (next.unread) void patchConversation(next.id, { unread: false }).catch(() => undefined); - void selectConversation(next.id).catch(() => undefined); + void selectProjectConversation(activeProject.id, next.id).catch(() => undefined); return; } const creationKey = `${activeProject.id}:${selectedAgent.id}`; @@ -271,7 +304,8 @@ export function CodingChatPanel({ const current = codingWorkspaceStore.getState(); if (current.activeProjectId === activeProject.id && current.selectedAgentId === selectedAgent.id) { - void selectConversation(conversation.id).catch(() => undefined); + void selectProjectConversation(activeProject.id, conversation.id) + .catch(() => undefined); } }) .catch(() => undefined); @@ -283,7 +317,7 @@ export function CodingChatPanel({ markConversationUnread, patchConversation, primeConversation, - selectConversation, + selectProjectConversation, selectedAgent, selectedConversation, ]); @@ -332,8 +366,8 @@ export function CodingChatPanel({ if (conversation.unread) { void patchConversation(conversation.id, { unread: false }).catch(() => undefined); } - void selectConversation(conversation.id).catch(() => undefined); - }, [activeProject, markConversationUnread, patchConversation, primeConversation, selectConversation]); + void selectProjectConversation(activeProject.id, conversation.id).catch(() => undefined); + }, [activeProject, markConversationUnread, patchConversation, primeConversation, selectProjectConversation]); const handleCreateConversation = useCallback(async () => { if (!activeProject || !selectedAgent || creatingAgentIds[selectedAgent.id]) return; @@ -344,14 +378,14 @@ export function CodingChatPanel({ primeConversation(createLocalConversationSnapshot(projectId, conversation)); const current = codingWorkspaceStore.getState(); if (current.activeProjectId === projectId && current.selectedAgentId === agentId) { - await selectConversation(conversation.id).catch(() => undefined); + await selectProjectConversation(projectId, conversation.id).catch(() => undefined); } }, [ activeProject, createConversation, creatingAgentIds, primeConversation, - selectConversation, + selectProjectConversation, selectedAgent, ]); @@ -369,9 +403,9 @@ export function CodingChatPanel({ if (workspace.activeProjectId === sourceProjectId && workspace.selectedAgentId === sourceAgentId && selectedId === sourceConversationId) { - await selectConversation(forked.id); + await selectProjectConversation(sourceProjectId, forked.id); } - }, [activeProject, primeConversation, selectConversation, selectedAgent, targetConversationId, upsertConversation]); + }, [activeProject, primeConversation, selectProjectConversation, selectedAgent, targetConversationId, upsertConversation]); const handleAddFiles = useCallback((files: File[]) => { if (!draftKey diff --git a/src/pages/Chat/CodingConversationTimeline.tsx b/src/pages/Chat/CodingConversationTimeline.tsx index 62690c1..e8db3cf 100644 --- a/src/pages/Chat/CodingConversationTimeline.tsx +++ b/src/pages/Chat/CodingConversationTimeline.tsx @@ -72,7 +72,6 @@ const WINDOW_STEP = 100; const LONG_OUTPUT_CHARS = 4_000; const STREAMING_THINKING_WINDOW_CHARS = 16_000; const PROGRESS_PREVIEW_MAX_CHARS = 360; -const PROGRESS_ROLL_CHAR_STEP = 6; const NODE_STATUS_LABELS: Record = { declared: '已声明', @@ -701,19 +700,19 @@ function toolActivityIcon(node: ConversationToolNode) { return