Compare commits

...
107 Commits
Author SHA1 Message Date
mesalogo e8d93b5bb6 release: prepare Desktop 0.13.15 recovery
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Carry the approved unpublished 0.13.14 release notes into 0.13.15 after
stabilizing the CI UI tests. Remove the duplicate unpublished entry and
update the bilingual feature matrices and recovery evidence.

Keep published Agent 0.13.4 unchanged. Its minimum Desktop requirement is
0.13.14; users should install this recovery release before updating Hosts.
Schema 47 still requires a pre-upgrade backup for rollback.

Release-note verification and nine focused tests passed. Require this
candidate main-branch validation and production build before tagging.

Release note: 承接监督回顾、待办布局、原生搜索和远程恢复更新;请先升级 Desktop 0.13.15,再更新 Host 至 Agent 0.13.4。
2026-09-26 02:05:59 +08:00
mesalogo cd27163660 test: stabilize asynchronous UI checks in release CI
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Control the native inventory retry response and wait for the complete
browser-close result instead of asserting on intermediate renders.
Run native note-layout input in a visible isolated Electron window,
scan the fixture dependencies up front, and report timeout explicitly.
Keep all behavior and geometry assertions and Electron sandboxing.

The three focused regressions, full typecheck, and lint pass locally.
Linux CI must confirm the release failures are resolved. No product,
Agent, version, or release-tag changes are included.
2026-09-26 01:53:04 +08:00
mesalogo 107dc974e3 merge: retain published Desktop 0.13.13 history
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
GoodBuddy Agent Release / Authorize Agent release (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agent Release / Build darwin arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build linux arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build linux x64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Sign cumulative Agent catalog (push) Canceled after 0s
GoodBuddy Agent Release / Publish Agent to GitHub and OSS (push) Canceled after 0s
Merge the published release preparation commit without rewriting either
branch. Keep the approved Desktop 0.13.14 and Agent 0.13.4 versions and
bilingual notes, and restore the historical release documentation.

The merged product source, tests, dependencies, and approved release notes
are identical to d23af99, which passed 5127 tests. Post-merge release-note
checks, nine focused tests, full typecheck, and lint passed.
2026-09-26 01:02:55 +08:00
mesalogo d23af99708 release: prepare Desktop 0.13.14 and Agent 0.13.4
Prepare the approved bilingual release notes for paged Supervisor reviews,
resizable to-dos, native direct-model searches, and remote recovery fixes.
Agent 0.13.4 requires Desktop 0.13.14; schema 47 requires a pre-upgrade
backup for rollback. Keep Desktop and Agent publication separate.

Include the existing validation fixture corrections and helper patches as
requested. Full validation passed 5127 tests with 74 skipped, typecheck,
lint, and release-note checks. Current-source Linux Host completion and
cleanup probes passed with two real model requests.

Release note: 改善监督回顾与续跑、待办布局和搜索;配套 Agent 修复远程完成状态并清理停用安装。请先升级桌面再更新 Host,升级前备份用户数据。
2026-09-26 00:59:27 +08:00
mesalogo ff746bdc1a fix: preserve native ripgrep capabilities for direct models
Replace the restricted workspace_rg parameter mapping with native args and optional cwd. Preserve rg output and exit codes, and reuse process cancellation and output_read pagination so large search results remain accessible. Ask remains read-only within the workspace; Execute uses current-account permissions.

Validation: focused regression 169 passed, 1 skipped; typecheck and lint passed. Real DeepSeek Ask and Execute pagination scenarios passed with six counted requests in the successful run; an earlier failed fixture run has no retained request count. Full-suite validation is not green: the recorded run had unrelated failures and the latest run timed out without a summary. The remote Agent does not use this local execution path.

Release note: 直连模型可使用 ripgrep 原生参数搜索文件并保留原生结果,大输出可通过 output_read 完整续读。Ask 保持工作区只读,Execute 使用当前账号权限。
2026-09-25 23:58:21 +08:00
mesalogo d4bc10c0f3 fix: use full width for supervisor recap and activity
Remove the extra reading and dashboard width caps so recap controls, results, and activity details fill the supervisor panel. Update layout assertions and UI documentation.

Release note: 修复监督者工作回顾与活动记录页面偏窄的问题,内容现在铺满页签下方的可用宽度。
2026-09-25 23:16:47 +08:00
mesalogo db5e43d7d5 fix: complete supervisor review coverage and execution workflow
Replace truncated review input with paged, resumable batches and retain extracted facts and source references through navigation summaries. Expose concurrency, timeouts, batch budgets and response capacity in supervisor settings.

Separate automatic supervision from review results, activity and settings; use a standard plan modal and show execution stages with saved batch details. Fix historical entity identity mapping, navigation output validation and arbitrary summary length failures.

Include focused regressions, isolated real-data validation scripts and updated design and verification records. Overall layout acceptance remains outstanding; this commit does not claim that all visual issues are resolved.

Release note: 监督回顾支持完整分页、分批保存和暂停后继续,可配置并发、超时及响应容量;修复实体引用、汇总校验和长摘要报错,并重新组织自动监督与活动记录入口。
2026-09-25 22:32:43 +08:00
mesalogo f68584b4e2 feat: resize todo panes and collapse note groups
Let users drag the to-do list/detail divider and remember its width. Note headings independently collapse task rows without clearing selected details. Use a 1px divider with a transparent drag target and compact spacing.

Validation: 115 focused tests including native Electron input; typecheck and lint passed.

Release note: Resize the Magic Notes to-do list with a thin draggable divider and collapse tasks by source note.
2026-09-24 16:17:16 +08:00
mesalogo 7adae97baa fix: restore remote conversation recovery and clean obsolete agents
Wait for model delivery acknowledgements before completing remote prompts and preserve completed outcomes when idle Runtime cleanup fails. Explicit messages after an unknown outcome now open a fresh binding with history instead of reusing the failed session; recovery remains attach-only.

Run obsolete payload cleanup through the current Agent after activation, retaining history and unresolved process ownership. Live legacy daemons and shared Runtime versions remain deferred. Includes focused regressions, local OpenCode simulation, and isolated Linux validation evidence. Typecheck and lint passed; the full test run timed out.

Release note: Remote conversations can continue after an uncertain result, and completed answers no longer fail solely because Runtime cleanup fails. Updates remove verified inactive Agent payloads and owned orphan processes while retaining active or uncertain installations and conversation history.
2026-09-24 10:39:28 +08:00
mesalogo 0ed1be1773 fix: widen project picker beyond the sidebar
Float the project picker at 380px with viewport bounds and preserve menu focus and dismissal. Align local package metadata and packaged release notes with Desktop 0.13.13.

Release note: 项目下拉列表可浮出侧栏,宽度调整为 380px,窄窗口自动收窄。
2026-09-24 00:34:08 +08:00
mesalogo d5f6dc6990 fix: hide queue attachment action for messages without attachments
Derive attachment presence from saved queue inputs and only show the preview action when attachments exist. Cover queue metadata and button visibility with regression tests.

Release note: Queued messages without attachments no longer show the View attachments button.
2026-09-23 23:44:08 +08:00
mesalogo c71c0adf9e fix: place supervision feedback below project overview
Order the task center as project overview, supervision feedback, then project tasks. Update the sidebar layout documentation to match.

Release note: 任务中心的监督反馈现位于项目概览下方、项目任务上方。
2026-09-23 23:11:33 +08:00
mesalogo 344008aaec fix: align note thumbnails with content edges
Remove extra top and left padding from the note record index so thumbnails align with the content area. Draw keyboard focus outlines inward to keep them visible within the scroll viewport, and cover alignment in the Electron layout regression test.

Release note: Note thumbnails now align with the content area, and keyboard focus outlines remain visible at the list edges.
2026-09-23 23:03:09 +08:00
mesalogo cebd513da8 fix: restore full workbar resize hit area
Keep the workbar divider's 9px hit target inside the sidebar so overflow clipping no longer makes it difficult to drag. Reserve matching space beside browser and terminal content, and remove it in browser fullscreen mode.

Validation: 68 sidebar tests, typecheck, lint, and Chromium hit tests passed. Full suite timed out after 10 minutes and reported three supervision test failures.

Release note: Make the divider between chat and the browser or terminal workbar easier to grab by restoring its full resize area.
2026-09-23 22:51:55 +08:00
mesalogo 2ca19ebf50 test: allow more time for large model bridge encoding
Give the 600 KiB request encoding round-trip test 15 seconds instead of the default 5 seconds after a CI timeout. Preserve the payload size, all assertions, global test timeout, and production behavior.

Validation: all nine model bridge contract tests and file-scoped ESLint passed. The full typecheck/lint command was cancelled before its completion could be confirmed.
2026-09-23 22:38:06 +08:00
mesalogo 7756231b3b release: prepare Desktop 0.13.13
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Prepare a Desktop-only patch for repeated activity-history reads during streaming and the supervision feedback sidebar layout. Keep Agent 0.13.3 and the database format unchanged; no Host update is required.

Update bilingual release notes, feature matrices, and validation evidence. Isolate this candidate from the original worktree and exclude its uncommitted supervision timeout tests. Validation passed 192 focused tests, an additional six-case Electron sidebar scenario, typecheck, lint, and release-note checks.

Release note: 减少流式活动历史的重复读取,修复监督反馈侧栏的按钮布局和目标名称显示;Agent 保持 0.13.3。
2026-09-23 22:02:55 +08:00
mesalogo b16fc900bb fix: restore supervision feedback sidebar layout
Replace unstyled full-width pin and refresh controls with shared icon buttons in the feedback header. Show conversation and task titles instead of raw IDs, with responsive spacing and keyboard focus coverage.

Release note: 修复任务中心监督反馈按钮样式和布局,显示会话或任务名称,并改善窄侧栏下的阅读与操作体验。
2026-09-23 21:52:36 +08:00
mesalogo bf8ff7f78e fix: avoid repeated activity history reads during streaming
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Reuse the last committed activity records when comparing streaming saves instead of reading the full history for every update. Invalidate comparisons on close, data clearing and commits from other SQLite connections; preserve rollback and retry behavior.

Validation: 3 related test files / 121 tests passed; typecheck and lint passed. Ten consecutive saves now perform at most one full-history scan while preserving the WAL write reduction. Installed-app physical disk throughput remains unverified.

Release note: Reduce repeated database reads while streaming activity updates in conversations with large activity histories.
2026-09-23 21:49:58 +08:00
mesalogo bb8c04af6c fix: use bundled OpenCode path in isolation tests
Cross-platform packages / Validate source (push) Canceled after 0s
GoodBuddy Agent Release / Authorize Agent release (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agent Release / Build darwin arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build linux arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build linux x64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Sign cumulative Agent catalog (push) Canceled after 0s
GoodBuddy Agent Release / Publish Agent to GitHub and OSS (push) Canceled after 0s
Linux release CI could not start OpenCode because the isolation test assumed a platform-specific npm executable name. Reuse the production runtime path resolver, which matches the installed bin/opencode.exe on all platforms.

Validation: nine focused tests, typecheck, lint, and bilingual release-note verification passed. Product behavior, versions, and release notes remain unchanged; main-branch CI must pass before tagging.
2026-09-23 20:09:05 +08:00
lofyerandGitHub 073aa45a30 Merge pull request #7 from mesalogo/jojocoding-patch-6
Update README.zh-CN.md
2026-09-23 19:29:05 +08:00
lofyerandGitHub 1cf74d3d2c Merge pull request #6 from mesalogo/jojocoding-patch-5
Update README.md
2026-09-23 19:28:44 +08:00
mesalogo 163e245b00 fix: stabilize release CI regression tests
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Address Desktop CI 35849362694 failures with isolated OpenCode config dependencies, cleared IPC lookup counts, Supervisor default-off expectations, and polling-safe UI fixtures and timeouts.

Record the failed candidate CI and follow-up validation: 14 focused tests, typecheck, and lint passed. Product code, versions, and bilingual release notes are unchanged. The revised candidate requires approval and successful main-branch CI before tagging.
2026-09-23 19:15:08 +08:00
mesalogo bf8ece0590 release: prepare Desktop 0.13.12 and Agent 0.13.3
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Complete the pending Supervisor changes with opt-in execution, incremental automatic reviews, and activity history. Preserve saved preferences and plans, skip model calls for unchanged input, and keep full replay and cancellation limitations explicit.

Update both release versions, bilingual notes, feature matrices, migration fixtures, and validation evidence. Clear stale browser errors after a successful close retry. Focused validation passed 690 tests across 35 files, plus notes verification, typecheck, and lint; full CI and native release acceptance remain pending. No local production build or packaging was run.

Release note: Enable Supervisor to review work, trace story graphs, and inspect manual or automatic activity without repeating model calls for unchanged input. Desktop 0.13.12 and Agent 0.13.3 also prepare idle remote Runtime cleanup and retirement of replaced Agents; update Desktop before the Host environment. Database upgrades require a pre-upgrade backup for rollback.
2026-09-23 18:20:23 +08:00
mesalogo 83a48a4079 fix: reclaim idle remote runtimes and retire replaced agents
Remote tasks could leave idle Runtime processes behind, and replaced Agent
installations lacked a drain-and-exit lifecycle. Keep the current Agent
resident, let displaced Agents finish existing work and reconnect for its
results, then retire them without restarting obsolete installations.

Reclaim Runtime processes when their last active task finishes, preserve
pending questions and shared active tasks, and restore conversation history
when the next turn starts. Remove the Remote Projects technical preview label
from settings, errors, tests, and current documentation; the feature remains
disabled by default.

Validation recorded during implementation: 213 lifecycle tests and 28 focused
UI/IPC tests passed, as did SettingsPanel and lint. Linux x64 real-Host checks
used 8 provider requests. Broader tests and final typecheck encountered
unrelated concurrent changes; details and coverage limits are documented.

Release note: 远程任务结束后自动释放空闲运行进程,下一轮对话恢复历史;更新后的旧 Agent 等待已有任务完成再退出,当前 Agent 保持常驻。远程项目移除“技术预览”标识,仍默认关闭。
2026-09-23 17:37:14 +08:00
mesalogo cdd573778c fix: show expandable project and conversation usage totals
Group usage into project or conversation totals with collapsed Runtime/model children. Keep model grouping flat and preserve expanded rows when usage refreshes. Include aggregation and interaction tests plus matching documentation.

Validation: 25 focused tests, typecheck, and lint passed. Full suite timed out after 200 seconds with four failures reported in ipc.test.ts and ApplicationMenu.test.tsx.

Release note: Project and conversation usage now show totals first, with model details available by expanding each row.
2026-09-23 16:33:13 +08:00
mesalogo 0265cfb762 fix: group and label note page controls
Move the record index toggle beside the back button and show text labels for both index and AI comment controls. Preserve pane visibility, keyboard focus, and narrow-window behavior; update tests and documentation.

Validation: 112 workspace tests and Electron layout regression passed; typecheck and lint passed. Full suite hit unrelated test failures and timed out.
2026-09-23 15:47:54 +08:00
mesalogo fb4b9bdd46 fix: float Git branch picker above workspace content
Render the searchable branch picker in the shared floating portal so opening it no longer pushes down changes or commit history. Clamp it to the viewport and retain search focus, outside dismissal, and Escape focus restoration.

Update existing migration fixtures for activity history and supervision schema changes. Focused suites passed: 149 storage tests, 41 UI tests, and the Electron overlay test; typecheck and lint passed. The full suite did not complete cleanly and reported failures outside this change.

Release note: The Git branch picker now opens over the workspace without displacing the file list or commit history.
2026-09-23 15:27:33 +08:00
mesalogo 984f16f178 fix: reduce activity history disk writes during streaming
Save only changed activity records instead of rewriting the entire history snapshot. Migrate existing history to schema 44 while preserving order and duplicate IDs.

Run unit tests with up to four workers and keep the configured integration phase serial. Include activity-history I/O, storage-upgrade, settings, and OpenCode isolation regression coverage.

Validation: 4 related test files / 123 tests passed; typecheck and lint passed.

Release note: Reduce disk writes while agent activity updates stream in, preserving existing activity history.
2026-09-23 14:54:21 +08:00
mesalogo 7d487f9326 feat: add supervisor workspace and traceable story graph
Extend the existing heartbeat workspace with scoped work reviews, result-specific story graphs, source inspection, and pinned conversation or task feedback. Connect confirmed continuation and local knowledge entity previews through the desktop APIs, and preserve result history and user corrections in schema 43.

Include focused regression tests, an isolated visual fixture, and implementation evidence. Full event-by-event replay, Experiment targets, no-change handling, and cancellation remain incomplete; this commit does not claim complete feature acceptance. Existing automatic plans remain active when the application entry is disabled.

Release note: Review work progress in the Supervisor workspace, trace graph events to saved sources, and open a matching review from pinned conversation or task feedback.
2026-09-23 13:49:13 +08:00
mesalogo 785b1ee468 fix: keep Mermaid diagram edges reachable and add PNG export
Fix oversized diagram alignment in the large-diagram viewer and provide complete PNG downloads independent of zoom and pan. Cover the full window and shared native drag handling, with regression checks for both themes and narrow windows.

Validation: 58 focused tests passed, including 9 Electron scenarios and 10 PNG downloads; typecheck and lint passed. Full-suite runs did not complete.

Release note: Mermaid 大图放大后可浏览全部边缘,并可从弹窗导出完整 PNG 图片。
2026-09-23 11:53:09 +08:00
mesalogo be84534e77 fix: simplify notifications and shorten display time
Use compact neutral notification cards with vertically aligned icons, text, and close controls. Dismiss success and info messages after two seconds; keep errors visible until dismissed.

Release note: Notifications now use a cleaner compact layout, and success and info messages disappear after two seconds.
2026-09-23 11:51:17 +08:00
mesalogo f7541203e4 fix: restore cached reply duration when switching conversations
Keep execution statistics by conversation and project so returning to a viewed conversation immediately restores its reply duration while refreshing in the background. Preserve late-result isolation and clear failed scopes.

Validation: 15 focused tests, typecheck and lint passed. Full suite: 4878 passed, 9 navigation settings assertion failures and 1 Agent package timeout outside this change.

Release note: Switching back to a viewed conversation now immediately displays its cached cumulative Agent reply duration while refreshing the statistics.
2026-09-23 01:00:18 +08:00
jojokeeandGitHub 24bde0c2e3 Update README.zh-CN.md 2026-09-22 09:51:43 +08:00
jojokeeandGitHub 4366bdc716 Update README.md
更新社群码
2026-09-22 09:50:42 +08:00
mesalogo 598c68f955 release: prepare Desktop 0.13.11 2026-09-22 09:13:56 +08:00
mesalogo e6a25b779c ci: use OSS transfer acceleration for uploads 2026-09-22 09:09:05 +08:00
mesalogo a0b35d1192 release: prepare Desktop 0.13.10 2026-09-21 22:53:44 +08:00
mesalogo 6c4c054180 fix: improve document parsing settings spacing 2026-09-21 16:11:51 +08:00
mesalogo 38d0923d79 fix: stop repeated history migration after restart
Desktop 0.13.8 still treated SQLite freelist pages left by normal chat and note writes as evidence of pending migration when a file-backed note existed. Migration detection now checks only the schema version and legacy note payloads, while the startup worker reuses its confirmed pending decision during retries so actual migrations can still finish compaction.

Release note: 修复已有笔记用户正常聊天后重启仍重复显示历史迁移的问题;已完成迁移的数据继续按新格式保存,普通写入产生的空闲页不再触发迁移。
2026-09-21 12:31:14 +08:00
mesalogo 8dce1d7007 feat: improve model connections and settings workflow
Model settings can now duplicate saved connections with encrypted API-key copying, while settings navigation stays usable across long forms and preserves the separate Platform Features tabs. The assistant storage startup worker now gates historical migration and compaction on actual pending legacy data, so ordinary chat free pages do not reopen the migration screen. Desktop and Agent release metadata is prepared for 0.13.8 and 0.13.2.

Release note: 改进模型连接复制、长表单设置导航和历史数据迁移判断;普通聊天退出并重开后不再重复显示迁移页面。
2026-09-21 09:19:30 +08:00
mesalogo 6739e8e902 fix: simplify task center status selection and rendering 2026-09-20 22:03:27 +08:00
mesalogo fe093bc15f ci: allow full desktop validation to finish
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
2026-09-20 14:15:27 +08:00
mesalogo 3cd6f44d7d ci: rehydrate Electron sandbox before validation
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
2026-09-20 12:20:01 +08:00
mesalogo 9e8c98290e release: prepare Desktop 0.13.7 and Agent 0.13.1
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Prepare the approved bilingual Desktop 0.13.7 and Agent 0.13.1 release
notes, version locks, feature matrices, and Agent release notes. Package both
OpenCode and Continue in the compound Agent release and publish native
execution checklists for remote projects.

Fix Linux source validation by configuring Electron's chrome-sandbox helper
and running the full test suite under Xvfb without disabling sandboxing.

Release note: Desktop 0.13.7 adds paged canvas notes, HTTP OCR, managed app
navigation, remote Continue and execution checklists, while preserving
conversation history and improving remote/runtime recovery. Agent 0.13.1
ships OpenCode and Continue together with checklist delivery.

Validation: npm test passed; npm run typecheck passed; npm run lint passed;
npm run release:notes:verify passed; 22 release workflow tests passed. No
local production build or package was run. LoongArch preview is not included.
2026-09-20 11:54:58 +08:00
mesalogo 9836a4cc9d fix: keep question order and streamline contextual help
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Keep interactive questions and answered cards at their original position in
conversation history, including remote reception, recovery, and reopening.
Preserve legacy history placement without a database migration.

Move supplementary guidance into shared accessible contextual-help popovers
across pages and settings. Add keyboard, modal, accessibility, and Electron
regressions, plus bounded opt-in live canvas-analysis validation.

Remove unused Agent, Runtime, and in-memory graph-search helpers, obsolete
mocks and browser copy; reuse the existing package-size formatter. Keep
production execution paths, protocol schemas, and released data migrations.

Validation: targeted cleanup tests, typecheck, lint, shared Linux Host
attach/read/re-attach, and added help/Electron checks passed. Final full-suite
verification passed 4,858 tests with 67 skipped and zero failures (412 files
passed, 9 skipped). Focused help checks passed 54 tests, including Electron
style comparisons and Chromium accessibility descriptions. Details and
validation boundaries are recorded in feature docs.

Release note: 修复交互式问答在回答后排到后续输出末尾的问题,重新打开会话仍保留提问位置。页面和设置中的补充说明改为标题或字段旁的按需帮助,支持鼠标与键盘操作。
2026-09-20 01:26:36 +08:00
mesalogo bf1d34e897 feat: configure canvas pages sent for note analysis
Canvas analysis previously sent every page. Add a Magic Notes setting for
1-8 canvas pages, defaulting to 1 for new and historical settings. Select
the first N pages in current canvas order before capture and use the same
text/image range for saved entries, drafts and canvas-source todos.

Keep full 50-page saving/export, expose the same setting through config
tools, and use the shared settings select with an inline help icon.
Include pagination, persistence, production IPC and isolated Electron
regressions, including text-only input and scaled page boundaries.

Validation: focused feature tests and isolated Electron regressions passed;
the final compact UI passed 14 tests. Typecheck and lint were reported
passing by the coordinating agent. No full-suite pass is claimed because
the completed full test command exposed no summary. No live model calls
were made for this commit preparation.

Release note: 魔法笔记新增“发送画布页数”设置,可选 1~8 页,默认 1 页,按当前画布顺序发送前 N 页的文字和图片;标题旁问号可查看说明,保存和导出完整画布不受影响。
2026-09-20 00:07:07 +08:00
mesalogo ecf09f4e95 docs: consolidate product PR workflow and draft update article
Keep feature inventory, user-focused prioritization, draft confirmation,
article templates, screenshot guidance and publication checks in one guide.
Use separate new-feature, improvement and bug-fix sections, with explicit
image, replacement and verification markers in drafts.

Archive the unreleased update article and its evidence review, retaining
pending screenshots and release details. Remove the split guide files and
update release-runbook references.

Validation: documentation structure, draft markers, 59 local links and
anchors, and staged whitespace checks passed. No product code changed;
product tests and builds were not run.
2026-09-19 23:45:28 +08:00
mesalogo a3b7a27045 fix: preserve drafts, canvas edits, and remote Continue tools
Keep paragraph formatting and checklist todos when removing canvas pages,
and preserve accepted undo/redo history after oversized input is rejected.
Merge restored queued text into the latest conversation draft and retain
image delivery when OCR returns image references and generated warnings only.

Deliver session MCP servers to bridged remote Continue Execute while keeping
Ask read-only and preserving local model-profile isolation. Add regression
coverage and update feature documentation. Correct stale conversation-limit
and forced-color assertions and the measured Electron layout test deadline.

Validation: 4,826 tests passed, 67 skipped; full typecheck and lint passed.
Current-source Linux x64 Execute/Ask/Execute MCP delivery passed, using 10
real text-provider requests and no image-provider requests. Image generation
itself was not exercised. Staged whitespace checks passed before commit.

Release note: 修复恢复队列时覆盖新输入、画布删页丢失清单格式及超限输入清空撤销历史的问题;图片 OCR 无正文时保留原图发送方式,远程 Continue Execute 正确保留会话 MCP 工具。
2026-09-19 23:09:48 +08:00
mesalogo 375b2159c8 fix: remove conversation list and message history limits
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
List and search all active conversations instead of only the latest 100. Preserve message history across consecutive sends instead of truncating it to 500 messages. Existing detail loading and context compression remain unchanged.

Validation: 7 focused tests, typecheck and lint passed. Full test run timed out after reporting failures in App completion visibility and WorkspacePrimitives forced-color contracts.

Release note: 会话列表和搜索不再限制为最近 100 个会话;发送消息不再截断超过 500 条的历史记录。
2026-09-19 21:34:42 +08:00
mesalogo b5d8204986 fix: remove graph extraction budget and unify image download buttons
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Graph extraction could exhaust its fixed 8192-token budget before returning
structured content. OpenAI requests now omit output-token limits, while
Anthropic uses the model connection limit required by its protocol.
Provider limits, timeout, cancellation and bounded response handling remain.

Use the shared accessible icon button for image downloads in chat and the
image viewer. Add graph request regression coverage and document the limits.

Release note: 知识图谱抽取不再额外限制为 8192 tokens;OpenAI 由模型服务决定输出上限,Anthropic 沿用模型连接配置。聊天和图片预览中的下载按钮统一为带提示的图标按钮。
2026-09-19 21:02:43 +08:00
mesalogo c98eb69cbd feat: refine notes workspace and add canvas zoom
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Simplify sidebar selection and note record framing, compact the empty composer, and emphasize note titles. Replace redundant AI comment headings with an accessible panel toggle.

Add view-only canvas zoom and fit width, correct PDF import/export icons, and allow image files up to 20 MiB. Include regression coverage and updated design documentation.

Release note: Notes have clearer navigation and a more compact writing area. Canvases support zoom and fit width, and image imports accept files up to 20 MiB.
2026-09-19 20:56:03 +08:00
mesalogo df89d96803 fix: remove redundant topbar runtime status
Keep model selection and switching feedback in the composer. Report automatic runtime status failures through notifications and move the send button accessible description into the composer.

Release note: Remove the duplicate top-right model/runtime badge while retaining switching feedback and error notifications.
2026-09-19 20:24:00 +08:00
mesalogo ec08109a09 fix: keep menus and notifications usable above modals
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Parsing test menus and application notifications could appear behind modal overlays or become unreachable while the background was inert. Share a stable top-layer portal that belongs to the owning or active modal, preserving notification timers, keyboard navigation and focus restoration.

Add unit and Electron regression coverage for settings, nested parsing results, application center, inference monitoring and native dialogs, with matching UI guidance and validation evidence.

Validation recorded in the preceding implementation: 4,793 tests passed and 67 skipped; typecheck, lint, isolated production build and Electron UI checks passed. Staged diff whitespace checks passed before this commit.

Release note: Menus and notifications remain visible and usable while settings or other dialogs are open, including keyboard navigation and notification dismissal.
2026-09-19 18:11:39 +08:00
mesalogo 6f06fce039 docs: record attachment UI behavior and OCR validation
Document the compact import cancellation control and progress-card layout. Record the prior full-suite results, focused attachment and Electron checks, and production probe review with their validation boundaries.

Release note: 附件查看结果、更多操作、移除和导入取消采用紧凑图标按钮,并提供悬停提示和键盘焦点说明,减少窄窗口中的布局拥挤。
2026-09-19 17:26:46 +08:00
mesalogo b1eb5d00d5 test: update app regression coverage 2026-09-19 16:53:43 +08:00
mesalogo a9b59ceb1c chore: include document probe and attachment UI refinements 2026-09-19 16:53:21 +08:00
mesalogo 935a8f56bf feat: add persistent document handling and protect note drafts
Add HTTP PaddleOCR-VL settings and shared document previews for chat and knowledge imports. Preserve attachment originals and parsed assets across drafts, messages and queues; support explicit image OCR, selecting document images into a conversation draft, and restoring queued input for editing.

Protect unsaved note bodies and titles during navigation and asynchronous updates. Match canvas analysis to source revisions, invalidate affected comments, preserve annotations during pagination, and provide a resizable record index with saved width preferences.

Existing OCR settings retain local behavior. Historical attachments without saved originals are not reconstructed. Advanced OCR options depend on the configured service; image sending depends on the effective model and Runtime.

Validation: full typecheck and lint passed; 477 focused tests passed across 12 files; staged whitespace checks passed. The full repository suite was not rerun for this commit.

Release note: Keep note drafts safe while switching workspaces, resize the record index, and revisit saved document attachments with OCR previews and selected images in chat.
2026-09-19 16:51:10 +08:00
mesalogo 17186b0c11 feat: add file-backed canvas notes
Magic Notes previously supported rich-text records without a paged drawing or
PDF annotation workflow. Add the integrated PeopleLib Fabric and Quill editor
with flowing text, pen/highlighter, object transforms, images, paper templates,
PDF backgrounds with native text extraction, and raster PDF export. Saves are
manual; undo/redo remains scoped to an annotation page or Quill mode.

Analyze drafts, saved canvases and canvas-source todos with page images and
text when the default model supports images, or an explicitly labelled
text-only fallback. Keep successful saves when automatic analysis fails and
prevent MCP plain-text replacement of canvas records.

Move entry bodies and binary assets into local note files with schema 38
migration and verified readback. SQLite retains metadata, indexes, revisions,
todos and comments. Backups must coordinate SQLite and the adjacent notes
directory while writes are paused or the app is closed; no one-click backup
action is included.

Show a continuous record stream with independent collapsible record index and
AI pane, real first-page thumbnails, and scroll-only record navigation. Open a
blank text/canvas composer by default and reset it after creation. Protect
unsaved drafts and establish normalized editor baselines after saving so clean
records do not prompt again when leaving.

Validation: latest recorded focused run passed 149 tests across five files;
typecheck, lint and git diff --check passed. The prior user-reported full run
passed 4,713 tests before later corrections and UI changes; it is not a full
validation of this final snapshot. Tests were not rerun for this commit, as
requested; independent systematic review follows the commit.

Release note: Create paged notes with handwriting, flowing text and PDF
annotations, browse records through thumbnails, and request AI comments with
an explicit text-only fallback. Existing notes migrate to local file storage;
backups now need both the database and notes directory.
2026-09-19 11:54:23 +08:00
mesalogo c7a93803c9 feat: show runtime checklists and support remote Continue
Display native OpenCode and Continue checklists alongside scheduled tasks, preserving request ownership, explicit clears and saved progress. Bundle both remote runtimes and support managed Continue installation, model routing, cancellation and restart continuation.

Validation: real local and Linux remote model checks, full Electron remote Continue flows, typecheck and lint passed. Full suite: 4707 passed, 67 skipped, one OpenCode compaction failure passed targeted rerun.

Release note: Display native execution checklists above conversations and support managed remote Continue projects with saved progress and cancellation recovery.
2026-09-19 10:47:27 +08:00
mesalogo 164e89253a fix: reduce repeated rendering during subagent progress
Reuse unchanged task and activity state, preserve activity nodes across reordering, and isolate historical tool rendering. Add regression coverage for progress, terminal states, copying, and component reuse.

File-level snapshot also includes existing runtime checklist and remote Continue UI changes in the selected files. Their remaining dependencies are still uncommitted in the worktree; this commit is not a standalone validated build.

Validation on the combined worktree: typecheck and lint passed; full tests had one packaging timeout that passed separately. Real-model UI runs verified parallel child tasks, progress, details, and persistence with existing internal-network certificate trust unchanged.

Release note: Reduce unnecessary UI updates while subagents stream progress and preserve activity list state when records reorder.
2026-09-19 01:39:17 +08:00
mesalogo a8d1dbc11f fix: give notes and todos dedicated workspace layouts
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Replace the crowded note sidebar with a card overview and full note detail. Keep tasks in a stable split view with source content and AI comments, compact header navigation, and search-adjacent status filters. Preserve drafts and return context.

Validation: 93 focused tests, typecheck, lint, and isolated Electron renderer checks passed. Full suite was interrupted; renderer probes used in-memory APIs.

Release note: Notes now open from a card overview, while todos keep a stable task list beside their details. Compact view and filter controls leave more room for reading and editing.
2026-09-18 21:22:58 +08:00
mesalogo 5aad0cf5f7 fix: use a compact single-column application center 2026-09-18 16:31:13 +08:00
mesalogo 9bd0dae865 fix: prevent knowledge search icons from overlapping text
Preserve icon spacing in graph and document search fields against shared input styles. Update scheduled approval tests to expand the collapsed project task list before interacting with task approvals.

Release note: Knowledge search fields now keep the search icon clear of placeholder and entered text.
2026-09-18 16:03:10 +08:00
mesalogo daa14d0b7e fix: keep project tasks compact with visible running counts
Collapse the project task list by default while retaining create and filter actions. Move the project name into the conversation statistics card and keep the running task count visible independently of list filters.

Validation: 60 sidebar tests, typecheck, and lint passed. The earlier full test run encountered an offline dependency package failure and timed out.

Release note: 项目任务默认收起,标题旁显示运行中任务数量;项目名称整合到当前会话统计卡片中。
2026-09-18 15:48:26 +08:00
mesalogo ca3c919a34 feat: unify app ordering and simplify app settings access 2026-09-18 15:29:00 +08:00
mesalogo bbae893c45 feat: add app management, local inference controls, and task statistics 2026-09-18 08:20:45 +08:00
mesalogo 6dd6297414 fix: prevent subagent progress content from being clipped
Constrain nested subagent grids so long tool summaries cannot widen progress and final output beyond the card. Preserve text wrapping and local scrolling for code and tables, and extend the layout regression test.

Release note: Subagent progress and results now stay within the card width, keeping long text and tool status controls readable.
2026-09-17 13:06:28 +08:00
mesalogo 04d9ea9eba fix: use monochrome brand icons for messaging channels
Show distinct WeChat, WeCom, and DingTalk marks in channel settings and the project dropdown. Bundle the SVGs locally and inherit the interface foreground color.

Release note: 远程通道设置和项目下拉列表统一使用微信、企业微信、钉钉各自的黑白品牌图标。
2026-09-17 13:01:39 +08:00
mesalogo 94852acf82 fix: keep consecutive requests running and clear finished stop state
Wait for OpenCode session cancellation before reusing a conversation, and replace the session after unconfirmed cancellation. Preserve terminal message state when stale streaming snapshots arrive so finished conversations return to Send.

Validated with full production Electron UI and the configured real text model: queued requests complete, cancellation followed by immediate resend completes, and Stop clears. Focused tests: 362 passed; typecheck and targeted lint passed. Live dispatch counting limitations are recorded in the feature evidence.

Release note: Consecutive OpenCode requests no longer inherit a previous cancellation, and finished conversations no longer retain the Stop button after stale updates.
2026-09-17 10:11:31 +08:00
mesalogo 3639566e1b fix: align magic note actions with conversation list menus
Move pin and whole-note deletion into hover and focus menus on note rows. Preserve editor drafts when operating on notes and reuse destructive confirmation with keyboard navigation.

Commit the eight affected files in full, including existing application-settings integration and UI design edits in those files. Validation was performed against the shared worktree; unrelated changes remain uncommitted.

Release note: 魔法笔记的置顶和删除移至列表项的更多菜单,与会话列表保持一致,并保留未保存的编辑内容。
2026-09-17 00:15:23 +08:00
mesalogo 1698d9fd3e fix: keep valid older agent packages usable after desktop updates
Accept package-declared OpenCode, Node and koffi versions instead of desktop release pins. Preserve signature, hash, protocol and package-internal version consistency checks.

Validate both cached 0.11.18 packages and Linux x64 installation, bootstrap, health and stop. Related regression suite: 326 passed, 1 skipped; unrelated full-workspace check failures are recorded in the remote Host design.

Release note: Desktop updates no longer reject intact older Agent packages solely because their bundled runtime versions differ.
2026-09-16 19:04:54 +08:00
mesalogo 441f14b5f7 docs: organize PR articles by date and version tag 2026-09-16 16:35:44 +08:00
mesalogo cc9ea7c718 fix: prevent send queue flicker when the agent is idle
Claim idle conversation inputs before publishing queue changes so immediately dispatched messages do not briefly appear as pending. Preserve busy conversation queueing and add a regression assertion for notification ordering.

Validation: 369 focused tests, typecheck, and changed-file lint passed. Full-suite runs timed out with failures in other modules; repository lint reports errors in the existing story-graph demo.

Release note: Fix a brief pending-queue flash when sending a message while the agent is idle.
2026-09-16 16:35:13 +08:00
mesalogo 7e11d1099a docs: add product PR workflow and article templates 2026-09-16 15:17:16 +08:00
mesalogo 0a8439a855 fix: allow larger note searches and recoverable MCP validation errors
Raise note_search limit to 100 while preserving the default and output budget. Return scoped-tool argument validation failures as tool errors so models can correct and retry instead of receiving MCP internal errors.

Validation: 64 focused tests, typecheck and changed-file lint passed. Full tests timed out with two heartbeat migration failures; full lint reports unrelated story-graph demo errors.

Release note: 笔记搜索单次最多支持 100 条结果;内置 MCP 参数错误会返回明确提示,便于模型纠正后重试。
2026-09-16 15:02:11 +08:00
mesalogo da23810205 fix: show task approvals in their task cards
Task Center previously separated task approvals from their tasks and showed an empty approval section. Associated approvals now appear with allow-once and deny actions inside task cards, and those tasks appear under Active. Only unassociated approvals retain a separate section; no approval section or badge is shown when there are no pending requests.

Release note: Review and handle pending approvals directly in their Task Center cards. Tasks awaiting approval appear under Active, while requests without a matching task remain separately accessible.
2026-09-16 14:31:28 +08:00
mesalogo cb245a6402 fix: simplify task center scopes and status filters
Task Center previously split unbound tasks into a global scope and tasks needing attention into a separate filter. Keep Current project and All projects, label unbound tasks, and restore legacy global scope preferences as all-projects without changing task data.

Keep Active, Paused, and Finished filters. Active includes idle, queued, running, waiting_approval, failed, and interrupted tasks; task details and project-scoped approvals remain available.

Release note: Task Center now offers Current project or All projects and groups tasks needing attention under Active, while retaining project labels and approval actions.
2026-09-16 14:05:36 +08:00
mesalogo e84d7bd185 feat: retain completed project activity until viewed
Keep successful background conversations in project activity until their chat is visible and loaded. Track completion in memory for the current client session; initial history, failures and cancellations do not create completion notices.

Include whole-file companion changes already present in App and shared UI: preserve the workspace beneath Settings, show inline reply-copy feedback, and keep project settings inside the project menu. Include their component tests and documentation; navigation demo source changes remain unstaged.

Validation: 77 focused activity tests, typecheck and lint passed; Electron component fixtures passed 112 checks. Full suite: 4365 passed, 3 failed, 67 skipped (offline inventory timeout and two heartbeat migration failures). Commit-time staged whitespace check passed; tests were not rerun.

Release note: 后台会话完成后保留已完成提示,进入对应聊天后自动清除,避免错过任务结果。
2026-09-16 12:51:58 +08:00
mesalogo 6c045bfe2e fix: preserve remote task success after recoverable tool errors
Align remote ACP tool recovery with local runtime behavior when the model responds after a tool failure and completes normally. Preserve original errors and derive recovery state from committed remote events so reconnecting safely replays partial terminal events.

Validation: 461 focused tests, typecheck, lint, and shared Linux Host recovery scenarios passed. Full-suite validation has two pre-existing heartbeat migration fixture failures involving the pinned column.

Release note: 修复远端 Agent 在读取等工具失败后已正常回复,任务仍被误判为失败的问题,并修复断线重连时的工具状态恢复。
2026-09-16 12:49:17 +08:00
mesalogo eaf71ce16f fix: refine workbar tab overflow controls
Keep the add button after the last tab in the scrollable strip. Show transparent floating scroll arrows on hover without reducing tab space, preserve keyboard and touch access, and reveal activated tabs.

Update tab interaction documentation and regression coverage. Exclude documentation demos from ESLint.

Validation: 66 focused tests, typecheck and lint passed. Full tests reported Agent package and runtime reuse failures before timeout; desktop visual verification remains pending.

Release note: Workbar tabs now keep the add button beside the last tab and reveal floating scroll arrows when hovering over an overflowing tab bar.
2026-09-16 00:07:39 +08:00
mesalogo f1d7197480 fix: open browser popup links in managed tabs
Links requesting a new window were silently blocked. Open them in managed browser tabs with the existing session restrictions, clean up script-closed tabs, and follow click-created tabs in subsequent direct and MCP browser tools.

Validation: 232 targeted tests and Windows Electron E2E passed; typecheck and lint passed. The full suite retains six failures outside the popup path.

Release note: Browser links that open a new page now open in a managed tab, and AI browser actions follow the new page after a click.
2026-09-15 23:55:52 +08:00
mesalogo 625182c3dc feat: pin conversations and float their action menus
Persist conversation pins across restarts and show pinned conversations first, including older history. Anchor action menus above the list without shifting neighboring rows, with keyboard dismissal and sidebar-close cleanup.

Add schema 36 migration, focused regression coverage, and feature documentation. File-level staging also includes existing workbar styles and browser contract adjustments in shared files.

Validation: 145 backend tests and 237 App tests passed; typecheck and lint passed. Full test run timed out with Runtime and Agent package failures; native Electron visual validation remains pending.

Release note: 会话菜单新增置顶和取消置顶,重启后仍保留;操作菜单现在悬浮显示,不再挤动下方会话。
2026-09-15 23:39:06 +08:00
mesalogo b16b0d10e7 fix: update workbar tab titles when switching language
Derive application and default terminal titles from the current locale while preserving renamed and unmarked legacy terminal titles. Add locale-switch and layout-restoration regression coverage.

File-level scope also includes existing browser popup selection and close handling in the sidebar, its regression test, and workbar documentation updates.

Validation: 74 focused tests passed; typecheck and lint passed. Full test runs timed out with failures in settings navigation, Agent packaging, and database migration tests. Electron verification remains outstanding.

Release note: Workbar application and new default terminal titles now follow the interface language, including after restoring a layout; custom and legacy terminal names are preserved.
2026-09-15 23:21:51 +08:00
mesalogo a383b2d11f fix: reduce conversation history memory use
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
Load lightweight conversation summaries and hydrate full history only when
needed, preserving search, copy, export, queued requests, and remote questions.
Keep unchanged image reconciliation messages stable so list refreshes no
longer resend the entire saved history.

Prepare Desktop 0.13.6 with the approved bilingual notes and aligned feature
matrices. Agent remains 0.13.0; no LoongArch preview or navigation demo edits
are included. Development validation passed 4301 tests with 67 skipped;
the user requested CI validation instead of repeating local release checks.

Release note: 历史会话按需加载,减少切回应用时的重复保存和内存占用,同时保留完整历史、搜索与续聊。
2026-09-15 20:33:47 +08:00
mesalogo f47049c40d Merge github/main README updates into release candidate
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
GoodBuddy Agent Release / Authorize Agent release (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agent Release / Build darwin arm64 compound package (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
GoodBuddy Agent Release / Build linux arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build linux x64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Sign cumulative Agent catalog (push) Canceled after 0s
GoodBuddy Agent Release / Publish Agent to GitHub and OSS (push) Canceled after 0s
Preserve the published English and Chinese README community QR-code updates. Desktop 0.13.5, Agent 0.13.0, approved bilingual release notes, and the validated product code remain unchanged.
2026-09-15 17:29:33 +08:00
mesalogo 4c18d8d9c6 chore: prepare Desktop 0.13.5 and Agent 0.13.0 releases
Record the approved bilingual release notes and align Desktop and Agent version metadata. Refresh both feature lists with image tools, PPTX OCR, Runtime process reuse, and the completed/to-implement status convention.

Agent 0.13.0 requires Desktop 0.13.5. Keep Node 24.19.0 and OpenCode 1.18.29 unchanged, preserve published release history, and exclude the LoongArch preview.

Validation: bilingual release notes verified; 4293 tests passed and 67 skipped; typecheck, lint, version consistency, and 63 documentation links passed. No new real model calls, Host reruns, local production build, or packaging. Full UI-driven image invocation and local/remote switching are not covered by the existing validation records.
2026-09-15 17:25:12 +08:00
mesalogo 8a63ac9ee8 fix: recognize PPTX images and release idle OCR memory
Image-only PPTX attachments failed with no indexable text even when a local
OCR model was configured. PPTX parsing now recognizes embedded PNG, JPEG
and WebP images through the existing local OCR path, preserves native text
and slide locators, and honors the selected parsing workflow.

OCR workers previously retained model sessions and WASM memory indefinitely
after successful parsing. Release the worker after 60 seconds with no active
or queued requests, reuse it for continuous work, and reload on demand.
Fast text/index modes still skip OCR. This does not render Office layouts,
recognize inherited master images, or change Agent execution permissions.

Validated four real PPTX attachments (40 pages), idle memory release and a
nine-page reimport after model reload on Windows x64 with PP-OCRv6 Tiny.
49 local OCR requests and zero text-model calls. All 4293 tests passed
with 67 skipped; typecheck, lint and the isolated Electron build passed.

Release note: 修复已配置本地 OCR 时图片型 PPTX 仍无法导入的问题,识别结果保留幻灯片定位。OCR 空闲 60 秒后自动释放模型运行内存,连续解析复用模型,下次使用时自动重新加载。
2026-09-15 16:58:28 +08:00
mesalogo 02e59427a1 docs: plan private app market and local model management
Record YAML application import/export, configurable market URLs, and future ShareServer distribution responsibilities. Define a local model management entry with managed-process and external-service boundaries; both remain planned capabilities.
2026-09-15 16:52:57 +08:00
mesalogo 34215c72a1 docs: define runtime checklist display scope
Specify read-only OpenCode and Continue checklists above conversations, including remote Agent delivery and validation requirements. Record native runtime test evidence and distinguish planned work from implemented behavior.
2026-09-15 15:59:13 +08:00
mesalogo f8d07419d2 feat: generate and edit conversation images across runtimes
Add model-controlled image invocation and read-only runtime assignments. Route image tools through Main so accepted operations retain their originating conversation when chat runtimes change, with saved references, cancellation, and manual recovery.

Keep Node hashing outside browser-shared contracts to restore production builds. Validation includes the production build, typecheck, lint, focused regressions, real Host transport checks, and two successful real image requests. Full UI-driven runtime switching acceptance remains documented as pending.

Release note: 开启图片模型的会话调用后,聊天助手可调用图片生成与编辑工具,Runtime 分配自动管理。图片成果保存在原会话,可继续修改,并独立显示生成状态和取消入口。
2026-09-15 15:51:52 +08:00
lofyerandGitHub d898f420fd Merge pull request #4 from mesalogo/jojocoding-patch-3
Update README.md
2026-09-15 08:20:55 +08:00
lofyerandGitHub c4492e4a92 Merge pull request #5 from mesalogo/jojocoding-patch-4
Update README.zh-CN.md
2026-09-15 08:20:34 +08:00
mesalogo e5aa9ed688 fix: reuse runtime processes across project sessions
Multiple projects could spawn duplicate heavyweight runtimes, while settings
changes and remote reconnection could cancel or strand native conversations.
Share compatible local OpenCode and DeepSeek Harness process owners and
Agent-owned ACP connections, retaining workspace routing, per-session modes,
model attribution, question ownership, targeted cancellation and idle cleanup.

Restore remote pending questions and terminal task projections across reconnect
and desktop restart, and preserve accepted ordinary requests during settings
replacement while cancelling expert-team parents with their replaced scheduler.
Extend regression coverage and Agent type checks. Record measured resource and
real-model validation results, including failures and limitations.

Also include the conversation media-generation design drafts (not an implemented
feature), documentation entry links and dedicated Linux test-directory guidance.
Desktop and Agent changes must be delivered together for shared process results.

Validation: typecheck and lint passed; staged content and documentation links
checked. Prior full regression and live-model evidence are recorded in progress
notes; no new full test suite or model calls were run for this commit.

Release note: 多项目会话现在复用兼容的 OpenCode 和 DeepSeek Harness 进程,减少重复启动并保持工作区、工具和取消隔离。修复远端问答在断线、重启及配置更新后的恢复与状态同步问题,Desktop 与 Agent 需配套更新。
2026-09-15 08:14:42 +08:00
mesalogo b755951ecd fix: offer recovery for blank desktop windows
React render failures now show a localized reload page, and renderer crashes offer native reload confirmation. Record renderer and load failures in existing diagnostics without automatic retry loops. Reload may lose unsaved input; the intermittent failure cause remains unconfirmed.

Release note: Recover from desktop rendering failures using a visible Reload action instead of being left without a recovery entry point.
2026-09-15 08:07:08 +08:00
mesalogo d4f1668c9b feat: paste local files into chat attachments
Copied local files previously required the attachment picker. The chat
composer now imports one or more supported files directly from paste,
sharing parsing progress, limits, and batch rollback with file selection.
Text and screenshot paste remain available, and failures preserve drafts.

Validation: prior 166 focused tests, typecheck, and lint passed; 28 targeted
tests passed again before commit. The prior full suite had 4214 passes,
67 skips, and 6 window-recovery failures; window tests passed 10/10 alone.
Native clipboard behavior was verified on Windows; macOS/Linux remain unverified.

Release note: 现在可在文件管理器中复制一个或多个支持的本地文件,直接粘贴到聊天输入框添加附件;可查看读取和解析进度,失败时保留草稿与已有附件。
2026-09-15 08:05:31 +08:00
mesalogo e1fe1cf10a fix: refresh magic notes after external updates
Subscribe the open Magic Notes workspace to committed database changes from desktop and Agent/MCP writes. Preserve selections and unsaved drafts during refresh and external deletion.

File-level commit includes existing runtime edits in shared files; their remaining dependencies are still in the working tree.

Release note: Magic Notes now updates automatically after Agent/MCP edits while preserving your selection and unsaved drafts.
2026-09-15 00:31:21 +08:00
mesalogo fbba2e3abf fix: place workbar toggle beside theme switch
Move the assistant workbar toggle from the floating content corner to the topbar immediately before the theme switch. Preserve toggle behavior and update layout tests and documentation.

Release note: The workbar toggle now stays beside the light/dark theme switch instead of floating over the content area.
2026-09-14 23:37:37 +08:00
mesalogo 511f50aa63 fix: keep remote recovery actions inside the project menu
Remove the duplicate recovery button beside New project and its extra layout column. Keep the collapsed failure status and the retry action on the affected project inside the dropdown.

Stage complete files as requested, including existing App snapshot regression coverage and remote runtime design updates in those files.

Validation: 29 ProjectSwitcher tests and the targeted App recovery test passed; typecheck and lint passed. Full test run timed out after 60 seconds.

Release note: Remote recovery retries now stay in the project dropdown, keeping the project header stable when a connection fails.
2026-09-14 22:16:13 +08:00
mesalogo 6c1e8105dd fix: keep project activity submenu height independent
Opening long conversation lists stretched and repositioned the project menu. Give the conversation submenu its own surface and positioning, aligned to the selected project row with independent viewport clamping and scrolling. Use compact navigation when neither side has room.

Validation: 13 component tests, typecheck and lint passed; 108 Electron fixture checks passed across 14 light/dark scenarios. Full test run timed out after 60 seconds.

Release note: Project activity menus now keep the project list stable when opening long conversation submenus.
2026-09-14 22:10:35 +08:00
jojokeeandGitHub 3b83c90d0f Update README.zh-CN.md
更新社群二维码
2026-09-14 16:43:20 +08:00
jojokeeandGitHub 327b06587b Update README.md
更新社群二维码
2026-09-14 16:42:30 +08:00
mesalogo de14a6157a fix: hide project activity summary when idle 2026-09-14 09:06:36 +08:00
mesalogo 81ae9e0324 chore: prepare Desktop 0.13.2 and Agent 0.11.25
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
GoodBuddy Agent Release / Authorize Agent release (push) Canceled after 0s
GoodBuddy Agent Release / Build darwin arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build linux arm64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Build linux x64 compound package (push) Canceled after 0s
GoodBuddy Agent Release / Sign cumulative Agent catalog (push) Canceled after 0s
GoodBuddy Agent Release / Publish Agent to GitHub and OSS (push) Canceled after 0s
Prepare bilingual notes and aligned feature lists for the schema-35 history repair, retained tool summaries, Windows DeepSeek workspace paths, and compact project activity navigation.

Agent 0.11.25 is a maintenance release with unchanged protocol and runtime pins. Its compound packages require Desktop 0.13.2. Desktop targets the standard six platforms without a LoongArch preview.

Make activity interaction tests wait for the loaded conversation and enabled send button, reset response mocks between cases, and wait for completed answer rendering. Accept bilingual maintenance-only Agent notes without inventing features or fixes.

Validation: 4,155 tests passed, 66 skipped; typecheck, lint, and release notes verification passed. The affected 20 tests passed three consecutive runs; all 227 App and Agent release-note tests passed. No local production build or packaging was run.
2026-09-14 01:18:42 +08:00
mesalogo f49d6678a3 fix: refine project activity spacing and conversation rows
Add 8px below the activity summary to separate it from new conversation. Remove redundant conversation-row arrows while keeping project submenu arrows, and update the UI documentation.

Validation: 11 ProjectActivity tests and typecheck passed. Lint was interrupted before completion.

Release note: 调整项目活动摘要与新建会话的间距,移除会话列表末尾的多余箭头,保留项目子菜单指示。
2026-09-14 00:40:10 +08:00
mesalogo 0d7ba55a50 fix: preserve tool details and prevent repeated history growth
Show bounded operation summaries for Continue, DeepSeek Harness and direct-model tools, retaining input details across incremental updates. Use one canonical workspace path for DeepSeek Host startup and ACP sessions so equivalent Windows paths reuse a working Runtime.

Compare persisted subagent tool fields by value, release terminal task caches, and avoid rebuilding caches for duplicate remote events. Upgrade to schema 35 to recompact repeated schema-34 tool blocks while preserving events and provenance. Include regression coverage, validation records and temporary-resource test exclusions.

Validation: 4155 tests passed, 66 skipped; typecheck and lint passed. Full desktop real-model validation covered all three tool-summary paths and both Windows path spellings. Storage validation evidence includes local and remote tool workloads and real database upgrade checks.

Release note: Tool records now show paths, commands and search details across runtimes. Fix DeepSeek Windows workspace failures and repeated subagent history growth; schema 35 reclaims redundant history data while preserving execution records, and older clients cannot reopen the upgraded database.
2026-09-14 00:34:51 +08:00
mesalogo c71da4b6ae fix: browse project activity through cascading menus
Replace the production activity dialog with anchored project and conversation menus, preserving exact conversation navigation and adding keyboard and narrow-window coverage. Include the interactive demo and design documentation.

The demo hides idle activity and removes conversation arrows; production still retains those two elements. Validation: typecheck, lint and 11 ProjectActivity tests passed. The full suite timed out after 240 seconds with an agent-package offline inventory test failure.
2026-09-13 23:06:20 +08:00
mesalogo ba6f6b8f22 fix: show operation details in OpenCode tool records
Preserve OpenCode operation titles in main-session tool summaries, including permission waits, failures and interrupted calls. Keep title trimming and length limits aligned with subagent records.

Update tool display documentation and include the activity-menu wording in UI-DESIGN.md. Validation: 132 focused tests passed; main-process typecheck and changed-file lint passed. Full-suite checks were affected by concurrent activity-menu changes and a test timeout.

Release note: OpenCode main-session tool records now show file paths, commands or operation descriptions beside tool names, matching subagent records.
2026-09-13 22:55:12 +08:00
610 changed files with 67482 additions and 9458 deletions
+9
View File
@@ -0,0 +1,9 @@
from pathlib import Path
import difflib
files = ['src/main/assistant/assistant-database.test.ts', 'src/main/assistant/activity-history-io.test.ts', 'src/main/assistant/assistant-storage-upgrade.test.ts', 'src/main/assistant/heartbeat-database.test.ts', 'src/main/magic-notes/magic-note-storage.test.ts']
patch = ''
for name in files:
old = Path(name).read_text(encoding='utf-8')
new = old.replace('DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;', 'DROP VIEW IF EXISTS supervision_review_current;\n DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;\n DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;\n DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;')
patch += ''.join(difflib.unified_diff(old.splitlines(True), new.splitlines(True), fromfile='a/'+name, tofile='b/'+name))
Path('.validation-fixtures.patch').write_text(patch, encoding='utf-8')
+4 -1
View File
@@ -228,12 +228,14 @@ jobs:
set -euo pipefail
output="$RUNNER_TEMP/goodbuddy-agent-output"
mkdir -p "$output"
continue_archive="$(node build/acquire-continue-runtime.cjs "$RUNNER_TEMP/goodbuddy-continue-runtime" "$AGENT_ARCH" "$AGENT_PLATFORM")"
node build/agent-package.cjs build \
--platform "$AGENT_PLATFORM" \
--arch "$AGENT_ARCH" \
--minimum-desktop-version "$(node -p "require('./package.json').version")" \
--node-archive "$RUNNER_TEMP/goodbuddy-agent-release-inputs/$NODE_ARCHIVE" \
--runtime-archive "$RUNNER_TEMP/goodbuddy-agent-release-inputs/$OPENCODE_ARCHIVE" \
--continue-archive "$continue_archive" \
--output "$output/goodbuddy-agent-$AGENT_VERSION-$AGENT_PLATFORM-$AGENT_ARCH.gbagent" \
> "$output/result.json"
node - "$output/result.json" "$AGENT_ARCH" "$AGENT_VERSION" <<'NODE'
@@ -484,6 +486,7 @@ jobs:
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
OSS_UPLOAD_ENDPOINT: https://oss-accelerate.aliyuncs.com
run: |
set -euo pipefail
export OSS_ACCESS_KEY_ID="$ALIBABA_CLOUD_ACCESS_KEY_ID"
@@ -497,7 +500,7 @@ jobs:
name="$(basename "$file")"
ossutil cp "$file" \
"oss://${OSS_BUCKET}/agent-releases/v${AGENT_VERSION}/${name}" \
--endpoint "$OSS_ENDPOINT" \
--endpoint "$OSS_UPLOAD_ENDPOINT" \
--region "$region" \
--update
done
+2
View File
@@ -305,11 +305,13 @@ jobs:
rm -rf "$directory"
mkdir -p "$directory"
agent_version="$(node -p "require('./agent-runtime-lock.json').agentVersion")"
continue_archive="$(node build/acquire-continue-runtime.cjs "$RUNNER_TEMP/goodbuddy-continue-runtime" "$AGENT_ARCH" "$AGENT_PLATFORM")"
node build/agent-ci-bundle.cjs \
--platform "$AGENT_PLATFORM" \
--arch "$AGENT_ARCH" \
--node-archive "$RUNNER_TEMP/goodbuddy-agent-node/$NODE_ARCHIVE" \
--opencode-archive "$RUNNER_TEMP/goodbuddy-opencode-runtime/$OPENCODE_ARCHIVE" \
--continue-archive "$continue_archive" \
--output-directory "$directory/bundle" \
--archive "$directory/goodbuddy-agent-$agent_version-$AGENT_PLATFORM-$AGENT_ARCH.gbagent" |
tee "$directory/result.json"
+17 -3
View File
@@ -19,7 +19,7 @@ jobs:
validate:
name: Validate source
runs-on: ubuntu-24.04
timeout-minutes: 20
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
@@ -40,9 +40,22 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Prepare sandboxed Electron tests
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends --yes xvfb xauth
sandbox="node_modules/electron/dist/chrome-sandbox"
if [[ ! -f "$sandbox" ]]; then
rm -rf node_modules/electron/dist
force_no_cache=true node node_modules/electron/install.js
fi
test -f "$sandbox"
sudo chown root:root "$sandbox"
sudo chmod 4755 "$sandbox"
- name: Run validators
run: |
npm test
xvfb-run --auto-servernum npm test
npm run typecheck
npm run lint
@@ -445,6 +458,7 @@ jobs:
env:
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
OSS_UPLOAD_ENDPOINT: https://oss-accelerate.aliyuncs.com
OSS_REGION: ${{ steps.oss-release.outputs.region }}
run: |
set -euo pipefail
@@ -459,7 +473,7 @@ jobs:
name="$(basename "$file")"
ossutil cp "$file" \
"oss://${OSS_BUCKET}/releases/${GITHUB_REF_NAME}/${name}" \
--endpoint "$OSS_ENDPOINT" \
--endpoint "$OSS_UPLOAD_ENDPOINT" \
--region "$OSS_REGION" \
--update
done
+258
View File
@@ -0,0 +1,258 @@
--- a/src/main/assistant/assistant-database.test.ts
+++ b/src/main/assistant/assistant-database.test.ts
@@ -174,7 +174,10 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 35;`)
legacy.close()
try {
@@ -325,7 +328,10 @@
DROP VIEW supervision_review_current;
DROP TABLE supervision_review_navigation; DROP TABLE supervision_review_batches;
DROP TABLE supervision_review_sources; DROP TABLE supervision_review_runs;
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 41;`)
legacy.close()
try {
@@ -1088,6 +1094,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 3;
`)
@@ -1251,6 +1260,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 26;
`)
@@ -1367,6 +1379,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 30;
`)
@@ -1445,6 +1460,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 30;
`)
@@ -1542,6 +1560,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 24;
`)
@@ -1589,6 +1610,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 24;
`)
@@ -1637,6 +1661,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 24;
`)
@@ -1681,6 +1708,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 24;
`)
@@ -1714,6 +1744,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 24;
`)
@@ -1744,6 +1777,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 5;
`)
@@ -1871,6 +1907,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 21;
COMMIT;
@@ -1967,6 +2006,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 9;
`)
@@ -2025,6 +2067,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 16`)
legacy.close()
@@ -2977,6 +3022,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 23`)
legacy.close()
@@ -3141,6 +3189,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 18;
`)
@@ -3654,6 +3705,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 31;
`)
--- a/src/main/assistant/activity-history-io.test.ts
+++ b/src/main/assistant/activity-history-io.test.ts
@@ -27,6 +27,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version=43`)
const legacySave = raw.prepare('UPDATE activity_history SET records_json = ?, legacy_history_may_be_incomplete = 1')
--- a/src/main/assistant/assistant-storage-upgrade.test.ts
+++ b/src/main/assistant/assistant-storage-upgrade.test.ts
@@ -101,7 +101,10 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 37`)
} finally { legacy.close() }
expect(hasPendingAssistantStorageUpgrade(path)).toBe(true)
@@ -419,7 +422,10 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = ${sourceVersion}; BEGIN`)
const insert = legacy.prepare(
`INSERT INTO task_events(
--- a/src/main/assistant/heartbeat-database.test.ts
+++ b/src/main/assistant/heartbeat-database.test.ts
@@ -82,6 +82,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 2`)
raw.close()
@@ -141,6 +144,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 20`)
raw.close()
--- a/src/main/magic-notes/magic-note-storage.test.ts
+++ b/src/main/magic-notes/magic-note-storage.test.ts
@@ -223,6 +223,9 @@
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
+ DROP VIEW IF EXISTS supervision_review_current;
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
PRAGMA user_version = 37`)
rmSync(join(directory, 'notes'), { recursive: true })
+5
View File
@@ -0,0 +1,5 @@
--- a/src/renderer/src/App.test.tsx
+++ b/src/renderer/src/App.test.tsx
@@ -13972 +13972 @@
- supervisionReview: { pageSize: 17, batchCharacters: 8000, batchMessages: 30, executionSeconds: 300 }
+ supervisionReview: { pageSize: 17, batchCharacters: 8000, batchMessages: 30, executionSeconds: 300, responseKiB: 1024 }
+6 -2
View File
@@ -31,7 +31,7 @@ risk or an explicit user requirement.
## Design Simplicity
Follow KISS and YAGNI. Use the smallest conventional design that completes the
real user workflow.
real user workflow. NO over-engineering or over-design.
- A demonstrated failure authorizes only the smallest proportional fix, not a
general recovery, compatibility, or security framework.
@@ -294,7 +294,7 @@ Repository-wide release invariants:
## Validation
Run all validators after source changes:
Run validators after source changes when function/fix done, not on every source change:
```text
npm test
@@ -339,6 +339,10 @@ validation are not substitutes for this development-time check.
- Keep all artifacts in GoodBuddy-owned test directories. Before stopping or
changing a remote process, verify that it belongs to the test run; never
modify unrelated Host files or processes.
- During remote testing, prefer dedicated test directories under `/root/tmp`
for temporary files. Avoid creating files under `/tmp` where possible, since
it may be memory-backed and consume additional RAM. Set `TMPDIR` to the
dedicated test directory when needed so test tools use it as well.
- If neither route is reachable, report the real-Host validation as blocked and
do not present the Agent work as complete or defer its first real validation
to release time.
+35 -2
View File
@@ -43,6 +43,31 @@ npm run typecheck
npm run lint
```
Vitest runs two ordered projects from `vitest.config.ts`. The `unit` project runs
first with up to four workers, bounded by Node's available CPU parallelism. Once
it finishes, the `integration` project runs with one worker. The serial group
contains all test files under `tests/` (including `*.electron.test.ts`),
`src/main/agent/**/*runtime*.test.ts`, and
`src/agent-daemon/private-endpoint.test.ts`. Other tests remain in the unit group.
Both projects inherit the React plugin, jsdom environment, renderer setup, and
existing exclusions; test-file isolation remains enabled.
This preserves serialization for the process/socket tests that previously timed
out on Linux with two workers (commit `93721d6`). `sequence.groupOrder` keeps the
two phases from overlapping. Existing file filters still work, for example
`npm test -- src/main/agent/opencode-runtime.test.ts`. To select a whole phase,
use `npm test -- --project unit` or `npm test -- --project integration`. To inspect
file selection without running tests, use `npx vitest list --filesOnly`.
For activity-history persistence, run
`npx vitest run src/main/assistant/activity-history-io.test.ts`. This checks both
WAL growth and repeated full-history reads: consecutive saves compare against
the last committed in-memory records, invalidated on close, data clearing or
another SQLite connection's commit. It also checks rollback and retry. The cache
does not change the database schema or the full-snapshot IPC contract. WAL size
and SQL scan counts do not measure physical disk throughput or conversation
message persistence; validate those separately when investigating live I/O.
监听模式:
```bash
@@ -156,7 +181,7 @@ npm run dist:linux:arm64
可安装的远端 Agent 或远端 Runtime payload。没有使用托管 SSH 时,无需下载任何额外
组件,缺少远端包也不能阻塞普通桌面打包或发布。
“设置 > 平台功能 > 远程项目(技术预览)”列出用户数据目录中已经下载或导入的
“设置 > 平台功能 > 远程项目”列出用户数据目录中已经下载或导入的
Linux x64、Linux arm64 和 macOS arm64 复合 Agent 包。首次打开和手动刷新会从“关于与更新”所选来源读取
小型签名目录,显示本地版本、在线最新兼容版本以及 `有更新 / 已是最新 / 未下载 /
校验失败`;此检查不会下载 `.gbagent`。只有用户点击“下载”或“更新到”后才传输包,
@@ -313,7 +338,7 @@ OSS 先写 `agent-releases/v<version>/` 不可变对象,GitHub Release 公开
`agent-catalog.json` 与 `agent-catalog.sig`,避免客户端观察到跨版本组合。Agent Release 必须使用
`--latest=false`,不得改变桌面 Release 的 Latest 标记。
### 远程 OpenCode Runtime 基础
### 远程 Runtime 工件
`remote-runtime-lock.json` 独立锁定首个远程 Runtime:OpenCode 1.18.29 的 Linux x64
baseline 与 arm64 官方包 integrity、`bin/opencode` 入口和固定 `acp` 参数。它不同于
@@ -321,6 +346,14 @@ baseline 与 arm64 官方包 integrity、`bin/opencode` 入口和固定 `acp`
的 Koffi 版本。Agent bundle 将 Koffi loader 作为 external module,并只携带目标
Linux 架构的 glibc/musl 原生 binding;构建与导入都会校验其 ELF 架构和 MIT 许可证。
lock 同时固定 `@continuedev/cli@1.5.47` 的 npm integrity。CN 工件使用
`lib/continue/dist/cn.js`,由 Agent 中的 HTTP-to-ACP helper 启动,不能传入原生 `acp`
参数。`remote-runtime:build` 与 `build/agent-package.cjs build` 接受
`--runtime-id continue`;默认 Agent 组包通过 `--continue-archive` 同时携带 OC/CN。
CN 的 JS payload 通过文件摘要与签名校验,执行它的 Node 仍来自 Agent 固定包。
默认 workflow 已接入联合组包;开发包与完整桌面到 Linux 的实测见
[CN 部署记录](./docs/features/remote-host/runtime-checklist-validation.md#cn-部署与生命周期)。
远程 Runtime 工件由以下命令独立管理:
```bash
+277 -58
View File
@@ -8,8 +8,11 @@ otherwise.
## Status
- [x] Available
- [ ] In development or planned, as described by the item
- [x] Completed
- [ ] To implement
Status describes implementation, not publication or test coverage. Validation
records are listed separately and do not introduce another feature status.
## Feature Matrix
@@ -21,7 +24,7 @@ otherwise.
packages target compatible x64 and arm64 environments, including UOS, Kylin,
Hygon, Zhaoxin, Kunpeng, and Phytium systems. This is not vendor certification.
LoongArch has a separate experimental loong64 preview outside standard
releases and automatic updates; no preview is planned for 0.13.1. See the
releases and automatic updates; it is built only on explicit request. See the
[preview boundaries](./docs/development/loongarch-preview-build.md).
- [x] **Configurable global shortcut**: Enable, disable, or record an Electron
accelerator under Platform Features / General. The default remains
@@ -38,14 +41,53 @@ otherwise.
Host heading and only the remote path on each project row.
- [x] **Cross-project conversation activity**: A shared activity entry lists
running conversations and conversations needing attention across projects,
including older conversations with live work. Open the associated
conversation directly; project selection also shows activity counts.
including older conversations with live work. Anchored project-to-conversation
menus provide keyboard navigation and exact conversation opening without a
full activity dialog. The summary hides when idle, long conversation submenus
scroll independently, and project selection continues to show activity counts.
Successful background conversations retain a completion notice during the
current client session until their chat is visible and loaded, not across restarts.
- [x] **Pinned conversations and complete saved history**: Pin common
conversations and retain their position after restart. Lists and search no
longer stop at 100 conversations, and consecutive sends no longer truncate
saved history beyond 500 messages. Model context limits still apply;
previously lost messages cannot be restored.
- [x] **On-demand contextual help**: Supplementary page and settings
explanations use shared title- or field-adjacent help controls with mouse
and keyboard access. Important operation consequences and errors remain visible.
- [x] **Compact conversation controls**: Composer options share a compact
settings panel while Runtime, mode, sending, and queue controls remain
available. Conversation search has an inline clear action that restores the
scoped list and returns focus to the input.
- [x] **On-demand conversation history**: Lists load lightweight summaries
rather than every conversation's process metadata. Opened and active
conversations retain full details; search, copy, export, continued requests,
and remote pending questions remain available. Unchanged image reconciliation
does not resend saved messages, and idle acknowledged details follow the
existing view-cache lifetime. See the
[history loading and retention rules](./docs/features/assistant-workbar/execution-history-storage.md#会话列表读取与前端保留).
- [x] **File, screenshot, window, and clipboard context**: Added to model
context only after explicit user selection.
context only after explicit user selection. Paste one or more supported local
files into the chat composer with `Ctrl+V` to add attachments, using the same
parsing progress and limits as the attachment button. Text and screenshot
paste remain available. See [attachment rules](./docs/features/document-processing/prd.md#321-聊天附件入口).
- [x] **Text recognition in image-based PPTX files**: With local OCR configured
and an OCR-enabled parsing workflow, embedded PNG, JPEG, and WebP images are
recognized alongside native text, retaining slide locations. OCR releases
model memory after 60 seconds without active or queued work and reloads on
demand; fast text/index modes still skip OCR. See the
[parsing behavior and real-file evidence](./docs/features/document-processing/chat-attachments-technical-design.md).
- [x] **HTTP OCR and persistent attachment previews**: Configure HTTP
PaddleOCR-VL under Settings > Document Parsing, shared by chat documents and
knowledge imports. Saves originals and extracted images and provides
text/image/detail previews, explicit image-to-text actions, and selecting
document images into a named conversation draft before sending. Ordinary chat
images use image input by default, subject to the effective model and Runtime.
This adds no Application Center entry; Local Inference Monitor manages local
services only. Real validation covers Windows, direct visual input, and managed
OpenCode on the shared Linux Host. Deployment-specific advanced OCR models and
other Runtime combinations need separate verification. See
[scope and evidence](./docs/features/document-processing/README.md).
- [x] **Per-file workspace diffs**: Changed files expose separate staged and
unstaged diffs, including deleted, renamed, and untracked files. Refresh
reloads the selected diff, browsed directory, and expanded directories; more than 50 changes
@@ -64,6 +106,8 @@ otherwise.
stash or discard. Managed SSH operations require Agent `0.11.23`.
- [x] **Rich responses**: Supports GitHub Flavored Markdown, LaTeX math,
constrained Mermaid diagrams, and static in-conversation HTML previews.
Mermaid's large-diagram viewer keeps all edges reachable when zoomed and
exports the complete diagram as PNG, independently of zoom and pan.
Complete HTML and HTML code blocks can be previewed in place after an Agent
reply finishes, with icon actions to reveal the source or open a full-screen
preview. The feature is on by default and can be disabled under Platform
@@ -74,28 +118,59 @@ otherwise.
citation metadata. Full-conversation copy uses the same validated
Preload/Main clipboard path.
- [x] **Assistant workbar, multiple terminals, and resizable layouts**: The
right workbar uses a persistent “+” capability catalog and application tabs.
toggle sits beside the theme switch. The right workbar uses a persistent “+”
capability catalog and application tabs.
Task Center and Workspace are permanent singleton tabs; browser tabs are
independent instances. Task Center can show the current project, global
tasks, or all projects, and retains the selected scope with the layout.
independent instances. Task Center can show the current project or all projects,
and retains the selected scope with the layout. Tasks without a project appear
under all projects with an “Unbound project” label.
Users can open multiple independent terminals for the current local or
managed SSH project with bounded output, resizing, termination, and explicit
reconnection. Closing a terminal tab ends its Shell; restarting the app
restores only an ended tab description and never restarts the Shell. The
main sidebar and Magic Notes list support pointer and keyboard resizing;
the notes list can be hidden, remembers its layout, and stacks at narrow
widths.
main sidebar and Magic Notes editor's AI pane support pointer and keyboard
resizing. The AI pane remembers its visibility and width, and stacks below
the editor at narrow widths.
- [ ] **Project Agent Space** (planned): Unifies roles, knowledge, Skills/MCP,
models, approval policy, budgets, and timeouts in a Project, with reusable
templates.
- [ ] **Application navigation and system-tool shortcuts** (design only):
Plans fixed system entries, configurable built-in application visibility,
and footer shortcuts into the existing workbar. These are not implemented
interface changes. See the [design](./docs/features/application-tool-navigation/README.md).
- [x] **Application center and navigation**:
Clicking the bottom App Center opens a lightweight upward anchored popup with enabled apps,
regardless of pinning or opening history, and no modal backdrop. Clicking an app row closes
the popup; Local Inference Monitor opens a separate modal preserving the workspace, while other apps
open their main content pages. Manage Apps opens searchable cards with settings details;
layout and interaction rules are defined in the [UI design](./docs/features/application-tool-navigation/ui-design.md).
All four application cards support arrow and drag reordering regardless of enablement or pinning.
Cards, the launcher, and the filtered sidebar share one persisted order. Knowledge
is always enabled and pinned, with Open and reorder controls. Supervisor, Notes,
and Local Inference Monitor have enablement and pinning settings. Supervisor uses
the existing Heartbeat identity and defaults to off when no preference is saved;
explicit saved choices are preserved. Disabling it retains plans and history,
blocks new reviews, and hides sidebar feedback; in-flight work may finish.
Enabling the app does not create automatic plans. Older application orders retain
their relative order when missing defaults are added.
Durable settings saves, including configuration-tool writes, synchronize through change events;
reopening the center locks edits until refresh completes, and newer snapshots supersede stale reads.
The `local-inference` modal shows a single service list without task history or external
connections. Embedding shows its independent process CPU and working-set memory;
ASR/OCR explain shared-process attribution limits. ASR readiness is unknown rather
than inferred from active requests. Supported service controls retain impact confirmation;
failed operations refresh the list and require a new confirmation. TTS remains unavailable.
Cancelling inference remains active in service-stop impact until worker
acknowledgement or exit. Implementation is connected to production App;
acceptance records distinguish full-App checks, component fixtures, real
local-engine checks, and remaining platform/package coverage. Historical
failures are not the current full-suite result. See the
[validation progress](./docs/features/application-tool-navigation/progress.md).
- [ ] **Privately deployable application marketplace** (planned): Extends
application management with an organization-owned catalog and application
distribution. Current management covers built-in apps only, not marketplace
browsing or installation. See
[FR-15](./docs/features/application-tool-navigation/prd.md#fr-15-私有化市场与-yaml-交换后续).
- [ ] **Additional assistant workbar and execution-space capabilities**
(planned): Builds on the current workbar and multiple terminals with
supervision, unified Runtime monitoring, managed processes, safe static HTML
preview, target-pinnable workspace/browser/artifact instances, bottom
broader execution supervision, unified Runtime monitoring, managed processes,
target-pinnable workspace/browser/artifact instances, bottom
docking, and separate windows. Task Center remains the singleton Task index;
attachments and knowledge remain in the conversation composer, while memory,
when implemented, and historical execution context belong to the associated Task. See
@@ -116,7 +191,10 @@ otherwise.
The first upgrade automatically converts existing history and reclaims disk
space, with visible progress and quit/retry support. Chats, execution details,
results, and remote event deduplication are preserved; older clients cannot
reopen the upgraded database. See
reopen the upgraded database. The schema-35 repair released in 0.13.2 also handles
repeated tool blocks written after upgrading to 0.13.1, without deleting events.
Terminal tasks release their write caches; duplicate remote replay does not
rebuild a released cache. See
[storage and upgrade behavior](./docs/features/assistant-workbar/execution-history-storage.md).
- [x] **Direct model Runtime**: Supports question answering, knowledge
@@ -129,6 +207,22 @@ otherwise.
take priority. Missing editing support does not block text-based generation:
the affected reply shows a quiet footer explaining that reference images were
not used. See [image generation](./docs/features/image-generation/README.md).
- [x] **Image tools in the current conversation**:
Enable conversation access for an image model to let tool-capable chat models
generate images and edit uploads or earlier artifacts in Execute, without a
separate conversation or manually switching to an image model. Direct text
models, local OpenCode, Continue, DeepSeek Harness, and managed remote OpenCode
use the shared service; Runtime assignment is derived from model settings.
Requests may incur provider charges and editing requires provider support.
See the [implementation and validation records](./docs/features/conversation-media-generation/progress.md).
- [x] **Cross-project Runtime process reuse**: Compatible local OpenCode and
DeepSeek Harness configurations share heavyweight processes; managed OpenCode
sessions share a process on the same Host. Project adapters and session
workspaces, models, tools, questions, and cancellation stay independently
routed. Remote pending questions and task status recover after reconnect or
Desktop restart without resending accepted prompts. Requires Desktop `0.13.5`
and Agent `0.13.0` together for the new remote behavior. See the
[design and measured validation](./docs/features/assistant-workbar/runtime-process-reuse-technical-design.md).
- [x] **Direct model programming agent**: Local direct text models can run the
platform Shell in Execute mode and delegate one level of programming
Subagent work while inheriting the parent request's mode, model, workspace,
@@ -149,7 +243,7 @@ otherwise.
credential without starting a throwaway server; the explicit settings test,
native inventory still performs full startup and health checks, then shares
that execution Runtime with the first real request instead of cold-starting
another server. Execution Runtimes remain reusable per project, while local
another server. Execution Runtime adapters remain reusable per project, while local
OpenCode configuration dependencies and content-addressed Skill snapshots are
shared globally across projects. Session data, tool output, and request-scoped
MCP state remain isolated. GoodBuddy-managed local OpenCode keeps
@@ -173,8 +267,8 @@ otherwise.
first; invalid paths fail at actual launch. On managed Linux ARM Hosts,
Runtime activation reuses the registry and manifest verified during setup
instead of rehashing or rechecking the complete OpenCode binary.
- [x] **Managed SSH OpenCode loop (technical preview)**: Controlled by the
separate Remote Projects (Technical Preview) tab under Settings / Platform
- [x] **Managed SSH OpenCode and Continue**: Controlled by the
separate Remote Projects tab under Settings / Platform
Features and disabled by default. Disabling it does not affect local
projects, ordinary desktop capabilities, or desktop releases. When enabled,
users can manage SSH Hosts with pinned Host Keys, browse bounded remote
@@ -186,6 +280,10 @@ otherwise.
provider/tool rounds, Runtime processes, a stable model ledger, and a bounded
semantic transcript over a private Unix socket and ACP v5. Work continues on
the Host after Desktop exit, network loss, or local-process termination.
The current Agent may remain resident; a superseded Agent drains active work
before exiting. Idle Runtime processes are reclaimed after tasks finish,
while shared processes retain other active tasks, including pending questions.
Subsequent turns recreate the Runtime and restore conversation history.
Reconnection attaches only to the original controller, binding, and
operation, then atomically merges provenance into the original conversation
without resending the Prompt. Model profiles and API keys enter Agent memory
@@ -197,7 +295,7 @@ otherwise.
state for Linux x64, Linux arm64, and macOS arm64. Intel Macs are not supported.
Downloads occur only after a user action, and
packages can also be imported or exported offline. Each compound `.gbagent`
package contains the Agent, fixed Node, and the compatible OpenCode Runtime
package contains the Agent, fixed Node, and compatible OpenCode and Continue Runtimes
maintained by the desktop source. Online sources follow the GitHub/Beijing
OSS choice in About and Updates. The cumulative signed catalog binds minimum
Desktop version, Agent protocol, platform, architecture, size, SHA-256, and fixed URL.
@@ -211,7 +309,8 @@ otherwise.
SSH only for that architecture. Projects store only Host, remote path,
Runtime selection, and mode. Current Host identity is read when a
Workspace/Runtime is first used, then reused by other projects in the same
process. Managed SSH conversations expose only supported OpenCode choices.
process. Managed SSH conversations expose supported OpenCode and Continue choices;
Continue requires the updated compound package and matching Desktop.
Multiple projects and conversations can run concurrently on one Host;
Agent-owned prompts are not stopped at a fixed model-call count or
prompt-wide output-token total, and Runtime output uses transport
@@ -223,7 +322,7 @@ otherwise.
failure, Agent `SIGKILL`/restart, and recovery from a reopened Desktop SQLite
database. Successful tool START/END events appear exactly once, with no
Prompt, provider, or tool replay observed. The current Agent source lock is
`0.11.24`, while the current Desktop release candidate is `0.13.1`; formal
`0.13.4`, while the current Desktop release candidate is `0.13.15`; formal
publication status follows the separate Agent and Desktop
release channels. Previous macOS validation covered native package installation,
detached lifecycle, Attach, real Ask/Execute, and cancellation of tools in
@@ -259,7 +358,7 @@ otherwise.
demand and run a fixed `attach-or-bootstrap`; registered health,
capabilities, and prompt startup do not scan the full payload. See the
[design](./docs/features/remote-host/environment-provisioning-technical-design.md).
- [ ] **Real-Host acceptance for SSH Host environment provisioning**: The
- **SSH Host environment provisioning validation records**: The
current source passed isolated Linux x64 package installation, Ask/Execute,
native subagent external writes, reconnection, and stop/bootstrap.
The complete Host-card acquisition matrix across GitHub, Beijing mirror,
@@ -277,7 +376,9 @@ otherwise.
permissions. Image input follows the selected model connection's declared
capability: text models reject images before Host or model invocation, while
image-capable models receive bounded inline JPEG/PNG content through a
temporary Attachment Store.
temporary Attachment Store. On Windows, Host startup and ACP sessions share
one canonical workspace path, so equivalent path spellings reuse the same
working Runtime instead of failing session creation.
- [x] **DSH npm plugin marketplace**: Disabled by default and searches public
npm `dsh-plugin` packages only after explicit user enablement. It uses the
bundled npm to install exact versions with ordinary lifecycle scripts and
@@ -291,15 +392,15 @@ otherwise.
paths available to the current local or SSH account, including paths outside
the workspace and native subagent work.
- [x] **Efficient direct-model workspace tools**: Direct models use bundled
ripgrep for compact file discovery and content search, read large UTF-8 files
by line, and apply multi-file patches in Execute. Ask can search and read but
cannot patch or run commands; packaged ripgrep does not require a system install.
Actionable read/search errors let the model correct its arguments and
continue. Partial search results remain available with an explicit
incomplete-coverage warning.
ripgrep with native arguments for file discovery and content search, read large
UTF-8 files by line, and apply multi-file patches in Execute. Ask searches stay
read-only within the workspace; Execute searches use current-account permissions.
No system ripgrep install is required. Search preserves native output and exit
codes, with complete large results available through `output_read`; actionable
argument errors let the model correct its request and continue.
- [x] **Long context and paged tool output**: Message history, long replies,
and parsed document text no longer use the previous fixed truncation limits.
Direct models can continue reading stored command and Subagent output in
Direct models can continue reading stored search, command, and Subagent output in
pages; the selected model's context window and bounded transports still apply.
- [x] **Native Runtime interaction routing**: OpenCode and Continue questions
support choices, yes/no, free-text answers, and skipping through the existing
@@ -308,13 +409,19 @@ otherwise.
and a newly started managed Runtime;
this does not extend question support to arbitrary ACP services.
Successful answers and skips retain the original questions and answers
across rounds and local conversation reloads; answers discarded by older
at their original position across rounds and local conversation reloads; answers discarded by older
versions cannot be recovered.
Concurrent questions wait in order, duplicate events preserve drafts, and
failed submissions remain retryable. Cancelling a pending managed SSH
question allows another message in the same conversation.
Execute permission confirmations are handled automatically rather than
waiting for another approval. See the [interaction boundaries](./docs/features/assistant-workbar/runtime-interactions.md).
- [x] **Native execution checklists**: OpenCode and Continue update a
read-only checklist above the conversation, with progress saved in history.
Explicit clears and remote replay retain request ownership; cancellation
does not mark unfinished items complete. Remote delivery requires the matching
Agent package. See the
[checklist contract](./docs/features/assistant-workbar/runtime-checklist-technical-design.md).
- [x] **Experts and Subagents**: Supports explicit experts, team analysis, and
up to three experts running in parallel. Experts inherit the parent
Ask/Execute mode and can use enabled local direct-model tools; Ask remains
@@ -452,7 +559,7 @@ otherwise.
installation validation on Windows x64. Managed Python archive validation
follows the target filesystem: Linux preserves valid case-distinct paths,
while every target still rejects exact duplicates and unsafe entries.
- [ ] **Local tool-execution environment release acceptance**: All six
- **Local tool-execution environment validation records**: All six
platform/architecture OSS mirror objects have passed public byte, size, and
SHA-256 verification. Managed Python remains an on-demand download and does
not add license files to the Desktop package. Each standard package job uses
@@ -502,7 +609,7 @@ otherwise.
index, or modify remote content. Bounded cited snippets are retained locally
with their conversations. See the
[external knowledge-base PRD](./docs/features/knowledge-base/external-knowledge-prd.md).
- [ ] **External knowledge-base acceptance**: All three providers have passed
- **External knowledge-base validation records**: All three providers have passed
real service, local HTTP MCP, and production-IPC short-answer checks; Dify
additionally passed the complete App composer-to-answer-and-citation path.
FastGPT/RAGFlow desktop-specific parameters, complex multi-library answers,
@@ -510,6 +617,8 @@ otherwise.
[validation evidence and remaining work](./docs/features/knowledge-base/progress.md).
- [x] **Knowledge graph**: Supports rule-based, model-based, and hybrid
extraction plus entity, relationship, alias, and evidence maintenance.
Model extraction adds no fixed 8192-token output cap: OpenAI follows provider
limits, and Anthropic uses the model connection's output setting.
- [x] **Embedding configuration and retrieval**: Configures compatible
Embeddings endpoints and uses them for semantic retrieval.
- [x] **Embedding diagnostics and indexing jobs**: Provides real embedding
@@ -530,12 +639,53 @@ otherwise.
demand or before every response, show zero-result, degraded, failed, and
cancelled states, and expose cited context or safely open its source.
- [x] **Magic Notes**: A local-first notes and todo workbench with scope
management, editing, filtering, and controlled AI comments. The todo view
displays the source entry inside the selected item's details, without
management, editing, filtering, and controlled AI comments. Notes open from a
responsive card overview. Detail displays a continuous stream with independently
collapsible left index and right AI pane. The index defaults to 168px and supports
pointer and keyboard resizing from 140px to 320px, with saved width preferences;
narrow layouts retain the fixed-width drawer without a resize handle.
Real first-page thumbnails scroll to
records without replacing editors. Successful saves establish the editor's
normalized baseline; only unsaved changes require confirmation on leaving. Opening
a note shows a blank text/canvas composer above the history, and creation resets it
for the next record. Explicit editing updates a saved record. The separate To-dos view offers search, status filters,
grouped tasks and direct completion, with instructions, source entries and AI
comments in a right detail pane beside a stable task list. Narrow views offer
on-demand detail with an explicit return to the list. Wide views support a
draggable list/detail divider with a saved width; source-note groups collapse
independently without clearing the selected task's details. A content-sized switch sits
beside New note in the header; status filters follow search. Source-note
navigation returns to the same task context, without
showing the previous item's source after a selection change. Failed reads
use application notifications, and missing sources have an explicit fallback. The left
use application notifications with inline retry, and missing sources have an explicit fallback. The left
navigation can show the incomplete-todo count, and create, save, and comment
results use application-wide notifications.
results use application-wide notifications. Agent/MCP note writes automatically
refresh the open workbench while preserving selection and unsaved drafts.
- [x] **Paged canvas notes**: The integrated PeopleLib Fabric + Quill editor
combines flowing body text with pen/highlighter, object selection/transforms,
floating text, images and paper templates. Editors and read-only viewers support
25%-300% view zoom, 100% reset and responsive Fit width, without changing saved
content or export dimensions. Import PDFs as page backgrounds
with native text extraction, then export a raster PDF of text and annotations;
exported PDFs do not retain a searchable text layer. Manual saves store entry
bodies and binary assets in local note files; SQLite retains metadata, indexes,
revisions, todos and comments. Backups must coordinate SQLite and the notes
directory; there is no one-click backup action. Undo/redo
is per annotation page or Quill mode, not global document history; there is no
infinite canvas or PNG download button. Drafts, saved entries and canvas-source
todos use the default model's image-input capability for page images plus text,
or an explicitly labelled text-only fallback. Canvas pages to send selects the
first 1-8 pages in current order (default 1) for both text and images; saving and
exporting still support 50 pages. Purely visual entries/drafts
require an image-capable model. Immediate-comment mode offers manual canvas
analysis; after-save automatic analysis failures do not block or undo saving.
Text-only comments survive unchanged extracted text; visual comments are
invalidated by layout changes and reanalyzed in after-save-auto mode. MCP
rejects plain-text replacement of canvas entries. Implementation is complete;
final validation status is tracked in the [feature progress record](./docs/features/magic-notes/progress.md).
The first upgrade migrates stored bodies into files and raises the database
schema to 39 together with attachment changes. Older clients cannot reopen
that database; rollback requires a pre-upgrade backup.
- [ ] **MCP Server Control Plane** (planned): Unified MCP lifecycle, health
checks, reconnection, schema cache, isolation, approval, and audit.
- [ ] **Traceable note excerpts and AI editing** (planned): Collects sourced
@@ -553,12 +703,17 @@ otherwise.
without a persistent legacy-cache truncation warning; previously lost
history is not restored. Token usage is grouped by Runtime and
model and normalizes the different OpenAI-compatible and Anthropic Messages
cache-reporting semantics when showing cache hit rate. Activity is grouped
cache-reporting semantics when showing cache hit rate. Project and conversation
usage groups show collapsed totals by default and expand to Runtime/model child
rows; model grouping remains flat. Activity is grouped
by conversation and collapsed by default so long histories do not fill the
page.
- [x] **Compact conversation tool records**: Expand individual tool records
to read or copy results, errors, and input parameters. Conversation and
child-task progress use concise rows without repeated Runtime summaries.
OpenCode, Continue, DeepSeek Harness, and direct models show available file
paths, commands, or search summaries and retain them through completion and
conversation reloads.
- [x] **Task and custom-task experience**: Each product-level Task belongs to
one Conversation, while one Conversation can contain multiple Tasks. The
left conversation list exposes Task children through a leading expand
@@ -580,21 +735,38 @@ otherwise.
Run Now actions. See the
[Task Center PRD](./docs/features/task-and-job/task-center-prd.md) and
[Scheduled Task PRD](./docs/features/task-and-job/scheduled-task-prd.md).
Status counts and filters identify running tasks and those needing attention;
task approvals remain actionable in their task cards.
- [x] **Memory and Smart Heartbeat**: Provides periodic review, suggested
memories, insights, follow-up tasks, and auditable run history.
- [x] **Improved Smart Heartbeat entry and scope**: Smart Heartbeat / Heartbeat
Plans is the sole authoritative full-configuration entry. Plans can be
- [x] **Automatic supervision settings and scope**: Supervisor / Automatic supervision is
the sole authoritative plan configuration, automatic report, and suggestion entry. Daily or weekly plans can be
created and edited for Global or one or more selected Projects. Legacy
single-project settings migrate without loss, and project-level memory and
action output must explicitly target a Project in scope. Task Center and
Settings no longer duplicate the form. Partition-aware review, candidate
generation, and recall triggers remain to be designed. See the
[Smart Heartbeat PRD](./docs/features/smart-heartbeat/prd.md).
- [ ] **General supervision** (planned): Uses a fixed supervision section to
observe user-selected conversations, Tasks, automations, or experiments and
provide evidence-backed comments and requests for human intervention,
without automatically speaking, approving tools, or switching to Execute.
See the
- [x] **Supervisor reviews, story graphs, and activity**: Review a selected scope
and period, read dated supervision results and history in Work review, inspect saved result graphs and sources, confirm or revise entities,
and preview local knowledge entity writes. Sidebar feedback follows or pins a
Conversation or Task and opens its matching result. It shows target names with
compact pin/refresh buttons and preserves long-title readability and keyboard
focus in narrow sidebars. Activity combines heartbeat
and downstream review stages, including failures and links to older results.
Automatic stages process only new, changed, or unprocessed source portions;
no-change checks skip model calls. Manual review pages through the selected history
and saves batches with their facts and sources. Runs can pause and resume from
saved progress; settings expose concurrency, timeouts, batch sizes, execution
budgets, and response capacity. Activity shows execution stages and saved batch
details. Work review and activity use the available panel width.
Calls remain read-only without tools and may incur model charges. See the
[implementation and evidence](./docs/features/conversation-supervision/progress.md).
- [ ] **Further supervision capabilities** (planned): Full event-by-event replay,
Experiment targets, chronological manual-edit audits, event
triggers, and broader execution observation remain incomplete. Current automatic
review respects input budgets and the rolling window; deleting reports does not
reset processing progress or rebuild graphs. See the
[Conversation Supervision PRD](./docs/features/conversation-supervision/prd.md).
- [ ] **Batch runs and comparison lab** (planned): Compares model, Prompt,
role, and workflow configurations in batches and summarizes quality,
@@ -662,6 +834,9 @@ otherwise.
(default) or the mirror. Manual checks, startup checks, and the download page
use the same choice and read only fixed trusted release indexes; GoodBuddy
does not automatically download or install updates.
- [x] **Window recovery controls**: Rendering errors show a localized Reload
action and renderer crashes offer a native recovery confirmation. Reload may
lose unsaved input. See [window recovery](./docs/development/window-recovery.md).
- [x] **In-app feedback**: About and Updates can submit problems, suggestions,
or experience feedback with an optional email and one screenshot.
Diagnostics are not uploaded by default; users can explicitly attach a
@@ -676,15 +851,48 @@ otherwise.
### Open source, builds, and releases
- Current source candidates are Desktop `0.13.1` and Agent `0.11.24`, with
OpenCode pinned to `1.18.29`. Publication status follows the independent
- The current Desktop candidate is `0.13.15`; Agent `0.13.4` is published, with
OpenCode pinned to `1.18.29` and Continue to `1.5.47`. Publication status follows the independent
Desktop and Agent release channels.
- Candidate acceptance remains separate from version preparation. The current
remote-question source has passed real Linux x64 SSH and Electron question
tests with a deterministic loopback model, not the complete production
Main/Preload path with a real provider. That real-model path and candidate
native CI/package validation remain outstanding; see the
[remote-question evidence](./docs/features/assistant-workbar/progress.md).
- Desktop `0.13.15` carries forward the unpublished `0.13.14` changes to paged Supervisor reviews and resume behavior,
native ripgrep searches, to-do layouts, and explicit remote follow-up after an
uncertain result. Database schema 47 requires a complete pre-upgrade backup
for rollback to older clients.
- Agent `0.13.3` reclaims idle Runtimes and retires displaced Agents after existing
work drains. Its packages require Desktop `0.13.12`;
update Desktop first, then the Host environment. Node remains `24.19.0`.
- Agent `0.13.4` waits for model delivery acknowledgements before completion,
preserves completed results when idle Runtime cleanup fails, and removes verified
inactive Agent payloads and owned orphan processes after environment updates.
History and active or uncertain installations are retained. Its independent
packages require Desktop `0.13.14` or later. Since `0.13.14` did not publish,
upgrade to Desktop `0.13.15` first, then update the Host.
- Release preparation requires the full test suite, notes verification,
typecheck, and lint. Candidate main-branch CI, its production build, native
packaging, and publication verification remain separate acceptance steps.
No local production build, packaging, or LoongArch preview is requested.
Current validation status is recorded in the
[recovery preparation record](./docs/development/release-preparation-0.13.15.md).
- Desktop `0.13.12` and Agent `0.13.3` have completed their native release jobs;
this does not replace development-time real-Host scenario coverage.
- Validation records remain separate from implementation status. Current-source
storage validation covers real local and Linux x64 Host tool workloads,
concurrent projects/conversations, cancellation, and lossless database migration.
Runtime process reuse has real Windows full-App and Linux x64 Host evidence;
PPTX OCR and idle-memory release have real Windows file evidence. Image tools
have real provider generation/editing and Host transport evidence. Full
UI-driven natural-language invocation and local/remote switching are not
covered by those records. The candidate still requires main CI and
native release packaging. See the [Runtime evidence](./docs/features/assistant-workbar/progress.md)
and [image-tool evidence](./docs/features/conversation-media-generation/progress.md).
- Before release preparation, `9836a4c` passed 4,858 local tests with 67 skipped,
full typecheck and lint; real-provider canvas analysis remains unverified.
Its three native Agent CI builds passed. Desktop CI run `35485936807`
passed 4,875 tests but failed eight Electron tests before UI assertions because
the Linux sandbox helper was not configured. The candidate configures the
helper and runs tests under Xvfb without disabling sandboxing. Candidate-local
validation, main-branch CI and native tag packaging are tracked separately;
no local production build or package is part of release preparation.
- [x] **0BSD open-source license**: Original code can be freely used, copied,
modified, distributed, and commercialized. Third-party components and
resources retain their own licenses.
@@ -707,6 +915,17 @@ otherwise.
### Open interfaces, team collaboration, and remote execution
- [x] **Remote Continue delivery**: Current source supports a
managed CN 1.5.47 package, Agent HTTP-to-ACP adaptation, model routing, project
selection, and native checklists. Linux x64 development packages passed real
model, cancellation and reconnection checks. Default packages include OC/CN;
Windows desktop-to-Linux checks cover installation, checklist updates, long lists,
cancellation and restart recovery. Execute session MCP delivery passed real
text-model Host checks with a substituted image service, not real image generation.
Published Agent `0.13.4` requires Desktop `0.13.14` or later; publication and native
platform acceptance remain separate from Linux development evidence. See the
[validation record](./docs/features/remote-host/runtime-checklist-validation.md).
- [x] **Remote task delegation**: Enabled only after the user explicitly
configures an endpoint and token, uses HTTP(S) according to the global
private-network compatibility setting, and writes results to a persistent
@@ -715,7 +934,7 @@ otherwise.
artifact API with scoped, expiring, rate-limited, and revocable tokens.
- [ ] **GoodBuddy Team Hub** (planned): An optional service for organizations,
RBAC, project sharing, remote Agents, policy distribution, and tenant audit.
- [ ] **SSH Host and remote execution-space release acceptance**: Host CRUD,
- **SSH Host and remote execution-space validation records**: Host CRUD,
Host Key, encrypted credentials, Project UI, Workspace, OpenCode ACP v5,
Agent-owned Prompt/gateway/transcript, read-only Ask, full-account Execute,
cancellation, exact detached-Agent reconnection, and release-only
+56 -26
View File
@@ -6,48 +6,60 @@
## 状态说明
- [x] 已提供
- [ ] 开发中或规划中,具体以条目标注为准
- [x] 已完成
- [ ] 待实现
状态表示功能实现情况,不等同于版本已发布或全部测试已执行。验证记录单独列出,不作为第三种功能状态。
## 功能总表
### 桌面基础、工作空间与上下文
- [x] **跨平台桌面应用**:支持 Windows、macOS、Linux,以及 `x64`、`arm64` 发布目标。
- [x] **信创软硬件覆盖**:正式 Linux 安装包覆盖国产 `x64` 与 `arm64` 架构,可用于统信 UOS、银河麒麟及海光、兆芯、鲲鹏、飞腾等兼容环境。龙芯 LoongArch 另行提供 `loong64` 实验预览版,不属于正式发布矩阵,也未纳入自动更新;`0.13.1` 不构建该预览。这里的覆盖范围不等同于厂商或整机兼容认证,具体状态与限制见[龙芯预览版构建说明](./docs/development/loongarch-preview-build.md)。
- [x] **信创软硬件覆盖**:正式 Linux 安装包覆盖国产 `x64` 与 `arm64` 架构,可用于统信 UOS、银河麒麟及海光、兆芯、鲲鹏、飞腾等兼容环境。龙芯 LoongArch 另行提供 `loong64` 实验预览版,不属于正式发布矩阵,也未纳入自动更新;仅在明确要求时构建。这里的覆盖范围不等同于厂商或整机兼容认证,具体状态与限制见[龙芯预览版构建说明](./docs/development/loongarch-preview-build.md)。
- [x] **可配置全局快捷唤起**:在“平台功能 / 通用设置”中启停或录制 Electron accelerator;默认保留 `CommandOrControl+Shift+Space`,冲突或保存失败时继续使用上一组已注册快捷键,并显示可处理的状态。
- [x] **Projects、独立对话与会话分支**:按项目隔离上下文,管理会话、附件和 Git 工作区变更;本地会话可在稳定状态下复制当前聊天内容到独立分支,分支持续显示来源徽标且不复制 Task、队列或成果归属;项目选择器区分本地、托管 SSH 与远程消息通道项目,托管 SSH 项目按 Host 分组并在 Host 标题显示真实 Agent 连接状态,项目行只保留远端路径。
- [x] **跨项目会话活动**:统一入口汇总各项目正在运行和需要处理的对话,保留仍有活动的较早会话,可直接打开对应对话;项目选择器也显示活动数量。
- [x] **跨项目会话活动**:统一入口汇总各项目正在运行和需要处理的对话,保留仍有活动的较早会话。锚定的“项目 → 会话”菜单支持键盘导航和精确打开对话,不再使用整块活动弹窗;空闲时隐藏摘要,长会话子菜单独立滚动,项目选择器继续显示活动数量。
后台会话成功完成后,在本次客户端运行期间保留完成提示,直到对应聊天可见且加载完成;提示不跨重启保留。
- [x] **会话置顶与完整历史**:常用会话可置顶,重启后保留。列表和搜索不再限于最近 100 个会话,连续发送不再截断超过 500 条的已保存历史;模型上下文限制仍有效,已丢失的历史无法恢复。
- [x] **按需上下文帮助**:页面和设置中的补充说明使用标题或字段旁的共享帮助入口,支持鼠标与键盘;关键操作后果和错误提示保持可见。
- [x] **紧凑会话控件**:输入选项集中在紧凑设置面板,Runtime、工作模式、发送和队列操作保持可用;会话搜索提供内嵌清除按钮,清除后恢复当前范围列表并把焦点返回输入框。
- [x] **文件、截图、窗口、剪贴板上下文**:用户明确选择后才加入模型上下文。
- [x] **会话历史按需加载**:列表只加载轻量摘要,不再一次读入全部会话的过程元数据;已打开和活动会话保留完整详情,搜索、复制、导出、续聊和远程待答问题保持可用。图片合并不会重新发送未变化消息,已确认保存的空闲详情随现有视图缓存释放。详见[历史读取与保留规则](./docs/features/assistant-workbar/execution-history-storage.md#会话列表读取与前端保留)。
- [x] **文件、截图、窗口、剪贴板上下文**:用户明确选择后才加入模型上下文。聊天输入框支持 `Ctrl+V` 粘贴一个或多个本地支持文件并自动添加为附件,沿用附件按钮的解析进度与限制;普通文本和截图仍可直接粘贴。详见[聊天附件规则](./docs/features/document-processing/prd.md#321-聊天附件入口)。
- [x] **图片型 PPTX 文字识别**:配置本地 OCR 并选择启用 OCR 的解析方式后,可识别内嵌 PNG、JPEG 和 WebP 图片,保留原生文字与幻灯片定位。OCR 在没有活动或排队任务时空闲 60 秒自动释放模型内存,下次按需重载;快速文本/索引模式仍跳过 OCR。详见[解析行为与真实文件验证](./docs/features/document-processing/chat-attachments-technical-design.md)。
- [x] **HTTP OCR 与附件持久预览**:在“设置 / 文档解析”配置 HTTP PaddleOCR-VL,供聊天文档和知识库导入共用。保存原件与插图,提供正文/图片/详情预览、显式图片文字提取,以及选图加入指定会话草稿。普通图片默认走图片输入,发送与入队检查实际模型和 Runtime。Windows、直连视觉输入和共享 Linux Host 托管 OpenCode 已有真实验证;部署高级 OCR 模型及其他 Runtime 组合需分别测试。本机推理监控只管理本地服务,不新增应用中心入口,详见[范围与证据](./docs/features/document-processing/README.md)。
- [x] **工作区单文件差异**:变更文件可分别查看已暂存和未暂存差异,覆盖删除、重命名和未跟踪文件;刷新会重读当前差异、当前浏览目录和已展开目录,超过 50 项变更可继续加载。
- [x] **工作区文件与 Git 管理**:支持文件和目录的新建、重命名、移动、删除、属性查看,以及本地/远程分支搜索与切换、新建分支、显式 Fetch、提交历史和单文件提交差异。工具按文件/Git 视图分组,分支搜索使用统一紧凑表单,子目录保留文字导航而根目录不再显示孤立图标。提交历史贴底,首次展开占 Git 内容区一半,支持调整比例、独立滚动和统一刷新;切换项目自动检测 Git,不沿用其他项目状态。移动限于工作区内,删除须确认,分支冲突不自动 stash 或丢弃改动;托管 SSH 操作需要 Agent `0.11.23`。
- [x] **富文本回答**:支持 GitHub Flavored Markdown、LaTeX 数学公式、受控 Mermaid 图表和会话内 HTML 静态预览。Agent 完成回复后,完整 HTML 与 HTML 代码块可在原位置预览,并通过图标按钮查看源码或打开全屏预览;该能力默认开启,可在“平台功能 / 通用设置”关闭,预览不执行脚本、联网、表单提交或窗口操作。
- [x] **富文本回答**:支持 GitHub Flavored Markdown、LaTeX 数学公式、受控 Mermaid 图表和会话内 HTML 静态预览。Mermaid 大图放大后可滚动到各个边缘,并可导出完整 PNG,不受当前缩放和拖动位置影响。Agent 完成回复后,完整 HTML 与 HTML 代码块可在原位置预览,并通过图标按钮查看源码或打开全屏预览;该能力默认开启,可在“平台功能 / 通用设置”关闭,预览不执行脚本、联网、表单提交或窗口操作。
- [x] **AI 回复与完整会话复制**:已完成的 AI 回复可从消息底部复制不含推理、工具日志和引用元数据的 Markdown 原文;完整会话复制复用同一条经过 Preload/Main 校验的剪贴板路径。
- [x] **助手工作栏、多终端与可调布局**:右侧工作栏使用持久“+”能力目录和应用 Tab;任务中心与工作区为不可关闭的单实例,浏览器可打开多个独立实例。任务中心可切换当前项目、全局任务和全部项目,范围随布局保存。用户可为当前本机或托管 SSH 项目打开多个独立终端,获得有界输出、调整尺寸、结束与显式重连。关闭终端 Tab 会结束 Shell,应用重启只恢复已结束的 Tab 描述,不自动重启 Shell。主侧栏和魔法笔记列表支持鼠标与键盘调宽,魔法笔记列表可隐藏并记住布局,窄宽度下自动堆叠。
- [x] **助手工作栏、多终端与可调布局**:工作栏开关位于明暗主题按钮旁;右侧工作栏使用持久“+”能力目录和应用 Tab,任务中心与工作区为不可关闭的单实例,浏览器可打开多个独立实例。任务中心可切换当前项目和所有项目,范围随布局保存;无项目任务在所有项目中标注“未绑定项目”。用户可为当前本机或托管 SSH 项目打开多个独立终端,获得有界输出、调整尺寸、结束与显式重连。关闭终端 Tab 会结束 Shell,应用重启只恢复已结束的 Tab 描述,不自动重启 Shell。主侧栏和魔法笔记编辑器的 AI 评论栏支持鼠标与键盘调宽;AI 评论栏记住显示状态及宽度,窄窗口下移至编辑器底部。
- [ ] **项目 Agent Space**(规划中):在 Project 中统一角色、知识、Skills/MCP、模型、审批策略、预算和超时,并支持模板复用。
- [ ] **应用导航与系统工具快捷入口**(仅设计):规划固定系统入口、内置应用显隐和打开现有工作栏的底部工具按钮;尚未实现新界面。详见[设计文档](./docs/features/application-tool-navigation/README.md)。
- [ ] **通用助手工作栏与执行空间后续能力**(规划中):在现有工作栏和多终端基础上继续加入监督、统一 Runtime、受管进程、静态安全 HTML 预览、可固定目标的工作区/浏览器/成果实例、底部停靠与独立窗口。Task Center 保持 Task 的单例索引,附件与知识库继续由会话输入区管理,未来的记忆与历史执行上下文归入关联 Task。详见 [Feature PRD](./docs/features/assistant-workbar/prd.md)。
- [x] **应用中心与导航**:底部应用中心向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻筛选。点击应用行关闭菜单,本机推理监控打开保留底层工作区的独立 Modal,其余应用打开主内容区;底部“管理应用”打开可搜索卡片及设置详情,布局与交互见 [UI 设计](./docs/features/application-tool-navigation/ui-design.md)。四张应用卡片均支持上下移动和拖动排序,不受启用或常驻状态限制;卡片、菜单和经过过滤的侧栏共用一份保存顺序。知识库始终启用并常驻,提供“打开”和排序;监督者、魔法笔记和本机推理监控可设置启用与常驻。监督者沿用智能心跳身份,缺失启用设置时默认关闭,已明确保存的选择保留;关闭后保留计划和历史,拒绝新回顾并隐藏侧栏反馈,在途运行可完成。启用应用不会自动创建计划。旧应用顺序补齐默认项并保留已有相对顺序。设置持久化后的事件同步各入口,包括工具配置写入;重开中心刷新期间锁定修改,新快照覆盖迟到的旧读取。`local-inference` Modal 直接显示服务列表,不展示任务历史或外部连接;向量服务显示独立进程 CPU 与工作集内存,ASR/OCR 说明共享进程无法独立统计的原因。ASR 就绪状态未知,不从活动请求推断运行中。受支持的服务操作保留影响确认,失败后刷新列表并要求重新确认;TTS 显示不可用。取消中的推理在 Worker 确认或退出前仍计入活动任务及服务停止影响。实现已接入生产 App;验收记录分别列出完整 App、组件 fixture、真实本地引擎和剩余平台/安装包覆盖,不以历史失败代替当前全量结果,详见[验证进度](./docs/features/application-tool-navigation/progress.md)。
- [ ] **私有化应用市场**(规划中):在应用管理基础上提供组织自己的目录与应用分发。当前只管理内置应用,尚无市场浏览或安装能力,详见 [FR-15](./docs/features/application-tool-navigation/prd.md#fr-15-私有化市场与-yaml-交换后续)。
- [ ] **通用助手工作栏与执行空间后续能力**(规划中):在现有工作栏和多终端基础上继续加入更完整的执行监督、统一 Runtime 监控、受管进程、可固定目标的工作区/浏览器/成果实例、底部停靠与独立窗口。Task Center 保持 Task 的单例索引,附件与知识库继续由会话输入区管理,未来的记忆与历史执行上下文归入关联 Task。详见 [Feature PRD](./docs/features/assistant-workbar/prd.md)。
- [ ] **ShareServer Office 协同编辑**(设计中):通过可选 ShareServer 集成 ONLYOFFICE Docs,在助手工作栏中以文件名打开多个文档 Tab,支持 DOCX、XLSX、PPTX 的人工编辑、保存、撤销、源文件冲突保护和后续 AI 选区修改。Office 正文会由所选 ShareServer 和编辑引擎处理;Desktop 不内置或启动 Document Server,未配置服务时不宣称离线编辑。详见[设计文档](./docs/features/office-document-editing/README.md)。
### Agent Runtime 与模型连接
- [x] **子任务历史去重与空间回收**:本机与托管 SSH 子任务事件只存变化,不再反复保存完整进度。首次升级自动转换旧记录并回收空间,显示进度且支持退出重试;保留聊天、执行详情、结果和远程事件去重。旧客户端不能打开升级后的数据库,具体规则见[执行记录存储与升级回收](./docs/features/assistant-workbar/execution-history-storage.md)。
- [x] **子任务历史去重与空间回收**:本机与托管 SSH 子任务事件只存变化,不再反复保存完整进度。首次升级自动转换旧记录并回收空间,显示进度且支持退出重试;保留聊天、执行详情、结果和远程事件去重。已随 0.13.2 发布的 schema 35 补修还会整理升级到 0.13.1 后重复写入的工具块,不删除事件;终态及时释放写入缓存,远程重复重放不重建已释放的缓存。旧客户端不能打开升级后的数据库,具体规则见[执行记录存储与升级回收](./docs/features/assistant-workbar/execution-history-storage.md)。
- [x] **直连模型 Runtime**:支持问答、知识总结、受控工具执行、图像生成,以及通过支持 OpenAI 兼容图片编辑接口的服务商进行参考图编辑。连续请求自动复用最近一次成功生成的图片和文字历史,已保存会话重开后仍可继续修改,本轮显式附图优先;上游明确不支持编辑时继续按文字生成,仅在回复底部提示未使用参考图。编辑使用 multipart 上传图片,结果仍只接受经过校验的内联图片,不下载服务商返回的图片 URL。详见[图片生成与连续修改](./docs/features/image-generation/README.md)。
- [x] **当前会话图片工具**:为图片模型开启会话调用后,支持工具调用的聊天模型可在 Execute 中生成图片、修改上传图片或历史成果,无需另开会话或手动切换图片模型。直连文本模型、本地 OpenCode、Continue、DeepSeek Harness 和托管远程 OpenCode 复用统一服务,Runtime 分配由模型设置派生;图片请求可能产生费用,编辑需服务商支持。详见[实现与验证记录](./docs/features/conversation-media-generation/progress.md)。
- [x] **跨项目 Runtime 进程复用**:兼容配置的本地 OpenCode 和 DeepSeek Harness 共享重型进程,托管 OpenCode 会话在同一 Host 上复用进程。项目适配器及会话的工作区、模型、工具、问答和取消仍独立路由;远程待答问题和任务状态可在重连或桌面重启后恢复,不重发已接受请求。新的远程行为需要 Desktop `0.13.5` 与 Agent `0.13.0` 配套更新,详见[设计与实测](./docs/features/assistant-workbar/runtime-process-reuse-technical-design.md)。
- [x] **直连模型编程 Agent**:本机直连文本模型可在 Execute 模式运行平台 Shell,并可按父请求模式、模型、工作区和能力范围委派一层编程 Subagent;OpenCode、Continue、DeepSeek Harness 和托管 SSH 不重复注入这两个工具。Windows 本机命令与真实模型“修改、测试、修复、复核”闭环已通过,macOS 与 Linux 真机命令仍待对应平台验收。
- [x] **高效直连模型工作区工具**:直连模型使用随包 ripgrep 紧凑地发现文件和搜索内容,按行分页读取大型 UTF-8 文件,并在 Execute 中应用多文件补丁。Ask 可以搜索和读取,但不能应用补丁或运行命令;用户无需另行安装 ripgrep。可处理的读取或搜索错误允许模型调整参数后继续,部分搜索结果保留并明确提示覆盖不完整。
- [x] **长上下文与工具输出分页**:历史消息、长回复和文档解析文本不再采用旧的固定截断限制。直连模型可分页续读已保存的命令和 Subagent 输出;所选模型的上下文窗口及传输边界仍然有效。
- [x] **高效直连模型工作区工具**:直连模型使用随包 ripgrep 的原生参数发现文件和搜索内容,按行分页读取大型 UTF-8 文件,并在 Execute 中应用多文件补丁。Ask 限于工作区只读搜索和读取,Execute 按当前账号权限搜索;用户无需另行安装 ripgrep。搜索保留原生输出与退出码,大结果通过 `output_read` 完整续读,可处理的参数错误允许模型调整后继续。
- [x] **长上下文与工具输出分页**:历史消息、长回复和文档解析文本不再采用旧的固定截断限制。直连模型可分页续读已保存的搜索、命令和 Subagent 输出;所选模型的上下文窗口及传输边界仍然有效。
- [x] **OpenCode 与 Continue**:使用隔离子进程、环境变量白名单、统一配置、取消、启动与控制请求时限、有界流式输出和活动记录;共享进程回收逻辑保留 Windows 完整进程树终止,并对采用独立进程组的 POSIX 子进程执行组回收。聊天状态检查使用一次性探测 Runtime 并在返回后立即回收,不进入按模型与项目隔离的执行 Runtime 缓存;本机 GoodBuddy 管理的 OpenCode 在该被动检查中只确认所选路径和模型凭据,不启动用后即毁的 Server,设置中的显式连接测试和原生能力清单仍执行完整启动及健康检查,原生能力清单随后与首次实际请求复用同一个执行 Runtime,不再重复冷启动;执行 Runtime 仍按项目复用,但 OpenCode 配置依赖和按内容摘要生成的 Skill 快照在所有本机项目间全局复用,会话数据、工具输出及请求级 MCP 状态继续隔离。本机 GoodBuddy 管理的 OpenCode 只对同一会话内的请求保持顺序,不同会话即使属于同一项目也可并行,不同项目同样可并行;请求级动态 MCP 通过默认通配禁用和当前请求精确放行保持隔离。托管的本机和远端 OpenCode 跳过界面未使用的自动 Git 快照,避免同步差异计算拖住会话,文件工具、子代理与工作区 Git 差异保留;Continue 持续排空不用展示的宿主 stdout,防止控制台输出填满管道。这些调整随新启动的 Runtime 生效,不改写外部 OpenCode Server 配置。OpenCode 与 Continue 不再按固定工具调用数或活动数中止单次运行,已观察到的工具与 Subagent 活动全部保留在本地会话,不再在原生任务继续执行时丢弃后续详情。交互提问只由前台对话回答,定时任务、远程通道和委派等后台执行遇到提问时会立即失败并提示改为前台运行,避免无限等待。本机 OpenCode 启动时直接使用已选择的绝对路径,不预先检查文件或运行 `--version`,路径失效时由实际启动返回错误;托管 Linux ARM Host 激活 Runtime 时复用准备阶段已验证的 registry 和 manifest,不重新哈希或检查完整 OpenCode 二进制。
- [x] **托管 SSH OpenCode 闭环(技术预览)**:该能力由“设置 > 平台功能”中的独立“远程项目(技术预览)”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。Ask 在 Runtime 工具边界保持只读,Execute 使用所选 SSH 账号的完整权限。Agent 持有 Prompt、模型和工具轮次、Runtime 进程及语义 transcript,Desktop 断开后重新 Attach 原操作而不重发 Prompt;模型凭据仅在当前操作内存中使用,不写入 Renderer、SSH 参数、环境或磁盘。设置页按平台和架构显示包状态,用户显式下载、更新或导入/导出包含 Agent、固定 Node 和 OpenCode 的签名 `.gbagent`;三个目标共用一个签名 catalog,继续校验签名和 SHA-256。首次发布混合 catalog 前需先升级 Desktop,旧版 Linux-only 目录读取器无法解析包含 Darwin 的目录。macOS 原生安装、生命周期、Attach、真实 Ask/Execute 和独立进程组工具取消已在真实 Host 验证;这不等于新版本已经发布。详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以 Agent 与 Desktop 的独立发布渠道为准。
- [x] **托管 SSH OpenCode 与 Continue**:该能力由“设置 > 平台功能”中的独立“远程项目”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。Ask 在 Runtime 工具边界保持只读,Execute 使用所选 SSH 账号的完整权限。Agent 持有 Prompt、模型和工具轮次、Runtime 进程及语义 transcript,Desktop 断开后重新 Attach 原操作而不重发 Prompt;模型凭据仅在当前操作内存中使用,不写入 Renderer、SSH 参数、环境或磁盘。当前 Agent 可以常驻,更新替换的旧 Agent 排空活动任务后退出。任务完成后回收空闲 Runtime,共享进程仍有任务或待答问题时保留;下一轮重建 Runtime 并恢复对话历史。设置页按平台和架构显示包状态,用户显式下载、更新或导入/导出包含 Agent、固定 Node、OpenCode 和 Continue 的签名 `.gbagent`;三个目标共用一个签名 catalog,继续校验签名和 SHA-256。Continue 需要更新后的联合包与配套 Desktop。首次发布混合 catalog 前需先升级 Desktop,旧版 Linux-only 目录读取器无法解析包含 Darwin 的目录。历史 macOS OpenCode 原生安装、生命周期、Attach、真实 Ask/Execute 和独立进程组工具取消已在真实 Host 验证,不代表 Continue 的同等实机覆盖。Agent `0.13.4` 已发布,当前 Desktop 候选为 `0.13.15`;详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以独立发布渠道为准。
- [x] **SSH Host 手动环境准备源码链路**:Host Key、认证和系统探针成功后先保存 Host,并只读探测共享 Agent/Runtime;保存 Host、打开项目都不自动安装。Host 卡片只有一个按版本事实显示“安装远程环境”“更新远程环境”或“重新安装”的主按钮,次级 SegmentedControl 选择默认且不持久化的“自动”、Host 下载或 GoodBuddy 传输;“版本匹配”badge 不等同环境健康。自动模式只在 operation/prepare 前探测并择一,显式选择保持有效,任何 prepare、commit 或 adoption 失败都不跨 acquisition 自动 fallback。两种方式把同一签名 compound `.gbagent` 交付到固定 staging 后,共用 control-plane prepare、commit、Agent activate/health、Runtime activate、finalize 与显式 cleanup。GoodBuddy 路径可在同一次操作下载并验证缺失候选、缓存并取得 lease,再有界流式 SFTP 上传一个归档和其中已验证的 bootstrap Node,不把约 294 MiB 整包读入 Main `Buffer`;Host 在解包时完成一次完整 payload 校验。未完成操作只记录暂存 cleanup 所需的 operation ID;下次更新尽力清理旧暂存后重新 prepare,不保存远端 metadata 副本,也不让 cleanup 失败阻塞新更新或回滚健康环境。已有项目在实际使用 Workspace/Runtime 时按需解析 Host current identity 并执行固定 `attach-or-bootstrap`,注册后的 health、capabilities 和 prompt 启动不扫描完整 payload。详见 [设计说明](./docs/features/remote-host/environment-provisioning-technical-design.md)。
- [ ] **SSH Host 环境准备真实 Host 验收**:当前源码已在隔离 Linux x64 环境通过包安装、Ask/Execute、原生子代理工作区外写入、重连及 stop/bootstrap。Host 卡片的完整 GitHub、北京镜像、Linux x64/arm64、取消和离线 GoodBuddy 传输矩阵仍待验收,候选 CI/原生打包与系统休眠唤醒也尚未验证;部分开发证据不代表已经发布。
- **SSH Host 环境准备验证记录**:当前源码已在隔离 Linux x64 环境通过包安装、Ask/Execute、原生子代理工作区外写入、重连及 stop/bootstrap。现有记录未覆盖 Host 卡片的完整 GitHub、北京镜像、Linux x64/arm64、取消和离线 GoodBuddy 传输矩阵,候选 CI/原生打包与系统休眠唤醒也尚未验证;开发记录不代表版本已经发布。
- [x] **远程工作区与长任务更新**:Agent `0.11.23` 新增远程文件/Git 管理和可选模型限额,随包 Runtime 不再施加固定十分钟 Prompt 时限;无限请求时长仍保留独立连接超时。请先升级 Desktop 至 `0.12.11`,再下载并更新 Host 上的 Agent。
- [x] **远程原生问答与取消修复**:Agent `0.11.24` 修复远程原生问答转交和取消待答后同一会话续发,需要先升级 Desktop 至 `0.13.0`。
- [x] **DeepSeek Harness(预览)**:使用 GoodBuddy 固定 Host 和 OpenAI 兼容模型连接;优先使用管理员提供的连接,否则跟随兼容的默认模型或首个兼容连接,无需单独重复选择,设置页显示实际管理员或回退模型来源。Ask 只允许调用 Host 中真实注册的 `read`、`skill` 以及 Main 管理的 Web Search/Fetch 代理,拒绝插件同名冒充,Execute 放行全部已启用内置及插件工具,并以当前用户权限运行。图像输入跟随所选模型连接的能力声明,文本模型在 Host 或模型调用前拒绝图片,图片模型通过有界内联内容和临时 Attachment Store 接收 JPEG/PNG。
- [x] **DeepSeek Harness(预览)**:使用 GoodBuddy 固定 Host 和 OpenAI 兼容模型连接;优先使用管理员提供的连接,否则跟随兼容的默认模型或首个兼容连接,无需单独重复选择,设置页显示实际管理员或回退模型来源。Ask 只允许调用 Host 中真实注册的 `read`、`skill` 以及 Main 管理的 Web Search/Fetch 代理,拒绝插件同名冒充,Execute 放行全部已启用内置及插件工具,并以当前用户权限运行。图像输入跟随所选模型连接的能力声明,文本模型在 Host 或模型调用前拒绝图片,图片模型通过有界内联内容和临时 Attachment Store 接收 JPEG/PNG。Windows Host 启动和 ACP 会话共用规范工作区路径,同目录不同写法可复用正常 Runtime,不再因路径不一致而创建会话失败。
- [x] **DSH npm 插件市场**:市场默认关闭,由用户显式开启后搜索公共 npm 的 `dsh-plugin` 包,使用捆绑 npm 执行精确版本安装和普通 lifecycle scripts,并支持启停、JSON 配置、移除、失败启动自动停用和离线管理已安装插件;关闭市场只隐藏目录与管理界面,不改变已有插件的启停状态,第三方代码不受 Ask 初始化隔离。
- [x] **Ask 与 Execute 工作模式**:Ask 保持只读;Execute 是用户对当前本机或 SSH 账号可用工具、进程、网络和可写路径的完整授权,包括工作区外路径及原生子代理工作。
- [x] **Runtime 原生交互转交**:OpenCode 与 Continue 的选择、yes/no、自由文本回答和跳过使用现有问答卡片,本机与托管 SSH OpenCode 同时转交属于当前请求的子会话提问。托管 SSH 需要 Desktop `0.13.0`、Agent `0.11.24` 和新启动的托管 Runtime,不扩展到任意 ACP 服务。回答或跳过成功后保留原问题与答案,支持多轮记录和本地会话重载;旧版本已丢弃的答案不能恢复。并行问题按顺序等待回答,重复事件保留草稿,提交失败可重试;取消远程待答后可在同一会话继续发送。Execute 权限确认自动处理,不等待第二次审批;具体支持范围见[交互边界](./docs/features/assistant-workbar/runtime-interactions.md)。
- [x] **Runtime 原生交互转交**:OpenCode 与 Continue 的选择、yes/no、自由文本回答和跳过使用现有问答卡片,本机与托管 SSH OpenCode 同时转交属于当前请求的子会话提问。托管 SSH OpenCode 问答需要 Desktop `0.13.0`、Agent `0.11.24` 和新启动的托管 Runtime,不扩展到任意 ACP 服务。回答或跳过成功后在原位置保留问题与答案,支持多轮记录和本地会话重载;旧版本已丢弃的答案不能恢复。并行问题按顺序等待回答,重复事件保留草稿,提交失败可重试;取消远程待答后可在同一会话继续发送。Execute 权限确认自动处理,不等待第二次审批;具体支持范围见[交互边界](./docs/features/assistant-workbar/runtime-interactions.md)。
- [x] **原生执行清单**:OpenCode 和 Continue 在对话顶部更新只读执行清单,进度随会话保存。显式清空和远程重放保留请求归属,取消不把未完成条目标成完成;远程交付需要配套 Agent,详见[清单契约](./docs/features/assistant-workbar/runtime-checklist-technical-design.md)。
- [x] **专家与 Subagent**:支持显式专家、团队分析和最多三个专家并行分析。专家继承父请求 Ask/Execute 模式,可使用已启用的本机直连模型工具,Ask 仍只读;这些工具不是远程 OpenCode 子会话。聊天先展示可逐项展开的专家完整输出,再在其下展示总 Agent 的综合结果,并随会话保存。
- [x] **OpenCode 子代理过程与最终结果**:子代理卡片按顺序显示文字、推理和工具过程,最终结果独立展示。远程实时过程需要 Agent `0.11.20`,旧包仍显示最终结果但不补造缺失过程。
- [x] **OpenCode 事件连接回收**:聊天与原生上下文压缩在结束事件迭代前关闭各自订阅,覆盖完成、失败、取消和消费方提前结束,不取消其他并行会话。
@@ -70,33 +82,42 @@
- [x] **Skills 按需接入**:可分配给直连模型、OpenCode、Continue 和 DeepSeek Harness,并使用有界资源和受控 Runtime 边界。
- [x] **本机工具执行环境源码链路**:在“能力与工具 > 工具执行环境”中为本机 Skills 与 stdio MCP 选择 GoodBuddy 托管 Node.js、按需安装的托管 Python,或经过真实验证的自定义解释器;提供独立的原生地址/OSS 镜像选择、诊断、安装进度、取消和删除。新的本机 Runtime 与 stdio MCP 获得不可变 PATH 快照,不修改普通终端、系统环境或远程 Host。Windows x64 托管 Node 与原生地址 Python 已通过真实安装验证;托管 Python 归档按目标文件系统验证,Linux 保留大小写不同的合法路径,所有目标仍拒绝完全重复路径和不安全条目。
- [ ] **本机工具执行环境发布验收**:六个平台/架构的 OSS 镜像对象已完成字节、大小和 SHA-256 公开回读验证。托管 Python 保持按需下载,不向 Desktop 发行包额外带入许可证文件。每个标准打包任务使用目标架构原生 Runner,并在打包前真实安装托管 Python,验证 SSL、pip 与 venv。不可变的 `v0.12.0` 尝试在发布前通过 Windows 与 macOS,但暴露了 Linux TAR 大小写处理问题;`v0.12.1` 在同步 Agent 发布时于原生打包前取消。已发布的 `v0.12.2` 随后通过全部六个原生打包任务及其托管 Python 安装探针。真实 Skill/MCP、自定义解释器和运行中进程协调仍是待完成验收。
- **本机工具执行环境验证记录**:六个平台/架构的 OSS 镜像对象已完成字节、大小和 SHA-256 公开回读验证。托管 Python 保持按需下载,不向 Desktop 发行包额外带入许可证文件。每个标准打包任务使用目标架构原生 Runner,并在打包前真实安装托管 Python,验证 SSL、pip 与 venv。不可变的 `v0.12.0` 尝试在发布前通过 Windows 与 macOS,但暴露了 Linux TAR 大小写处理问题;`v0.12.1` 在同步 Agent 发布时于原生打包前取消。已发布的 `v0.12.2` 随后通过全部六个原生打包任务及其托管 Python 安装探针。现有记录未覆盖真实 Skill/MCP、自定义解释器和运行中进程协调。
- [x] **内置 MCP 按需接入**:知识库、魔法笔记、GoodBuddy 配置与内置浏览器 MCP 可分别启停,并可分配给直连模型、GoodBuddy 管理的 OpenCode 和 Continue;DeepSeek Harness 在设置中明确显示为暂不支持。内置 MCP 仅通过当前请求的短期本机权限提供,Ask / Execute 读写边界不受用户配置放宽。
- [x] **MCP Tools**:显式启用的自定义 MCP 可按 Runtime 分配给直连模型、GoodBuddy 管理的 OpenCode、Continue Agent Execute 和 DeepSeek Harness,并仅在 Execute 加载;Agent 子进程只获得按请求签发的本机回环权限,MCP 地址、命令和凭据保留在 Main,动态工具仍经过发现、执行记录与权限边界。直连模型与 Harness 的健康调用复用当前请求已发现的工具清单,不在每次调用前重复发现。
- [x] **MCP Prompts 与 Resources 元数据**:MCP 测试仅在 Server 声明对应能力时发现有界的 Prompt、参数与 Resource 元数据,不读取 Resource 内容;Runtime 支持的 Prompt 可填入聊天草稿后继续编辑。OpenCode 可报告实验性 Resource 清单,Continue 当前版本明确不支持 Resources。
- [x] **本地知识库**:支持文件、目录和网页导入、SQLite FTS5 检索及来源追溯。知识范围按对话保存,新对话默认不选择知识库;创建与检索高级参数默认折叠。文档区分可用、处理中和失败,支持打开来源、失败重试及移除来源前确认。
- [x] **外部知识库接入**:在知识库页面管理 Dify、FastGPT 和 RAGFlow 实例,通过目录或手工 ID 验证并绑定远端知识库,保留 Provider 专属检索配置和引用;对话支持按需检索及每次回答前检索。外部系统只负责知识检索,不接入其 App、Chat、Workflow 或 Agent,也不批量同步、本地索引或修改远端内容。被回答引用的有界检索片段随会话保存在本机。详见[外部知识库接入 PRD](./docs/features/knowledge-base/external-knowledge-prd.md)。
- [ ] **外部知识库验收**:三家 Provider 已通过真实服务、本机 HTTP MCP 和生产 IPC 短答案检查,Dify 另通过完整 App 输入区到回答及引用的链路。FastGPT/RAGFlow 桌面专属参数、复杂多库问答和远程回答前检索仍待验证,详见[验证证据与剩余工作](./docs/features/knowledge-base/progress.md)。
- **外部知识库验证记录**:三家 Provider 已通过真实服务、本机 HTTP MCP 和生产 IPC 短答案检查,Dify 另通过完整 App 输入区到回答及引用的链路。现有记录未覆盖 FastGPT/RAGFlow 桌面专属参数、复杂多库问答和远程回答前检索,详见[验证记录](./docs/features/knowledge-base/progress.md)。
- [x] **知识图谱**:支持规则、模型和混合抽取,以及实体、关系、别名和证据维护。
模型抽取不再额外限制为 8192 tokens;OpenAI 使用服务商上限,Anthropic 使用模型连接的输出设置。
- [x] **向量模型配置与检索**:可配置兼容 Embeddings 接口并用于语义检索。
- [x] **向量诊断与索引任务**:提供真实向量生成诊断、按文档重建进度、取消、失败状态与重启后结果恢复;每篇成功文档立即可用于检索。
- [x] **混合检索测试台**:支持全文、中文词组、向量和图谱通道诊断,可调 Top K、阈值、权重、本地或学习型重排及上下文预算。
- [x] **分块、维护与评估**:支持固定、结构化和父子分块,分块维护、可取消重建及双语检索评估。
- [x] **受控知识本体**:每个知识库可定义实体、关系、别名和端点约束,保留证据偏移、置信度和抽取来源,并显式提示图谱重建。
- [x] **强制检索与引用上下文**:对话可按需或每次先检索,显示零结果、降级、失败与取消状态,并可查看引用上下文或安全打开来源。
- [x] **魔法笔记 / Magic Notes**:提供本地优先的笔记与待办工作台、范围管理、编辑、筛选和受控 AI 评论;来源记录直接显示在所选待办的详情中,切换事项不会混入上一项的来源内容,读取失败使用应用通知,来源缺失有明确提示。左侧入口可按设置显示未完成待办数量,创建、保存和评论结果使用统一应用通知。
- [x] **魔法笔记 / Magic Notes**:提供本地优先的笔记与待办工作台、范围管理、编辑、筛选和受控 AI 评论。笔记从自适应卡片总览打开;独立待办页提供搜索、状态筛选、分组列表和直接勾选,宽屏列表稳定在左侧,任务说明、来源记录及 AI 评论在右侧同一区域显示,窄屏按需显示详情并提供返回列表。紧凑页签放在页头新建按钮左侧,筛选放在搜索右侧。打开原笔记再返回可恢复任务上下文;切换任务不会混入上一项的来源内容,读取失败使用应用通知并提供就地重试,来源缺失有明确提示。左侧入口可按设置显示未完成待办数量,创建、保存和评论结果使用统一应用通知。Agent / MCP 写入后,已打开的工作台自动更新,并保留当前选择与未保存草稿。
- 待办宽屏列表与详情之间可拖动分隔条并记住宽度;来源笔记分组可独立折叠,不清除当前选中任务的详情。
- [x] **连续记录工作区**:笔记详情连续展示全部记录,左侧索引默认 168px,可拖动右边界或用键盘在 140–320px 内调宽,并记住偏好;索引与右侧 AI 评论独立收起。每条画布记录显示真实首页缩略图、页数和时间,文字记录显示摘要;点击索引只滚动定位,保留当前编辑器和草稿。打开笔记就在标题下方显示空白输入区,保留文字/画布选择和保存操作;新增保存后清空,供下一条记录使用,旧记录通过编辑入口修改。空白输入区和保存后未再修改的记录返回时不提示;保存失败和真实修改仍受保护。窄屏索引使用固定宽度折叠抽屉,不显示调宽分隔条,AI 评论移至底部。
- [x] **分页画布笔记**:已集成 PeopleLib Fabric + Quill 编辑器,支持跨页正文、笔迹、高亮、对象选择及变换、浮动文字、图片和纸张模板。PDF 可作为分页底版导入并提取原生文字;正文和批注可导出为栅格 PDF,导出文件不保留可搜索文字层。用户手动保存后,记录正文与二进制资源存入本地笔记文件,SQLite 保留元数据、索引、修订、待办和评论。备份须协调保存 SQLite 与笔记目录,产品无一键备份入口。撤销/重做限当前批注页或 Quill 模式,不提供全局文档撤销、无限画布或 PNG 下载按钮。草稿、已保存记录和来源为画布的待办按默认模型图像输入能力使用页面图像加文字,或明确标注仅文字降级;“发送画布页数”可选 1~8,默认 1,按当前顺序发送前 N 页,文字和图片范围一致,保存与导出仍支持 50 页。纯视觉记录/草稿需要支持图像输入的模型。即时评论模式提供手动画布分析,保存后自动分析失败不阻断或回滚保存。提取文字不变时保留文字评论;布局变化使视觉评论失效,并在保存后自动评论模式下重新分析。MCP 禁止以纯文字覆写画布记录。实现已完成,最终验证状态见[功能进展](./docs/features/magic-notes/progress.md)。
编辑与只读画布均支持 25%–300% 视图缩放、100% 还原及随视口调整的“适应宽度”,不改变保存内容或导出尺寸。
首次升级将历史正文迁移到文件,并随附件变更把数据库升级至 schema 39;旧客户端不能再打开,回退需使用升级前备份。
- [ ] **MCP Server Control Plane**(规划中):统一 MCP 生命周期、健康检查、重连、Schema 缓存、隔离、审批和审计。
- [ ] **可追溯笔记摘录与 AI 编辑**(规划中):从对话、知识和网页收集带来源的摘录,并提供需确认的总结、改写和整理操作。
### 工作管理、长期协作与工作流
- [x] **任务、活动与成果**:集中管理任务状态、审计活动和独立成果文件;普通聊天回复只保留在会话中,不再自动复制到成果栏,已有重复聊天 Markdown 从成果列表隐藏但不物理删除。运行记录改由 Main SQLite 保存,不再受旧 Renderer 的 500 条、单详情 4,000 字符或约 2 MB 上限影响,页面按批继续显示;不再常驻旧缓存截断警告,但不会恢复此前已丢失的历史。Token 用量按 Runtime 与模型归类,并针对 OpenAI 兼容与 Anthropic Messages 的不同上报口径归一化展示缓存命中率;活动按会话分组并默认收起,避免长历史占满页面。
- [x] **紧凑会话工具记录**:逐项展开工具记录即可查看和复制结果、错误及输入参数;会话与子任务进度使用简洁行,减少重复 Runtime 摘要。
- [x] **任务、活动与成果**:集中管理任务状态、审计活动和独立成果文件;普通聊天回复只保留在会话中,不再自动复制到成果栏,已有重复聊天 Markdown 从成果列表隐藏但不物理删除。运行记录改由 Main SQLite 保存,不再受旧 Renderer 的 500 条、单详情 4,000 字符或约 2 MB 上限影响,页面按批继续显示;不再常驻旧缓存截断警告,但不会恢复此前已丢失的历史。Token 用量按 Runtime 与模型归类,并针对 OpenAI 兼容与 Anthropic Messages 的不同上报口径归一化展示缓存命中率;用量按项目或会话分组时默认收起并显示汇总,展开后显示 Runtime / 模型子行,按模型分组时保持平铺;活动按会话分组并默认收起,避免长历史占满页面。
- [x] **紧凑会话工具记录**:逐项展开工具记录即可查看和复制结果、错误及输入参数;会话与子任务进度使用简洁行,减少重复 Runtime 摘要。OpenCode、Continue、DeepSeek Harness 和直连模型可显示已有文件路径、命令或搜索摘要,并在工具完成和会话重载后保留。
- [x] **Task 与定制任务体验**:每个产品级 Task 只关联一条 Conversation,一条 Conversation 可承载多个 Task;左侧会话列表通过行首展开按钮显示带共享状态点的 Task 子项,父会话行不重复任务标签,UI 只展示到 Task,不暴露 Job/Run 层级。新建定制任务可关联当前或新 Conversation,默认 Execute 并沿用 Runtime、工具和审批边界;重复触发复用同一 Task,文本结果回写 Conversation,独立文件和图片保留为成果。普通消息与到期 Scheduled Task 共用 Conversation 级持久发送队列,同一会话一次只执行一项;当前回复期间仍可继续发送,队列按顺序续跑,并允许删除或“立即中断并插入”。Task Center 继续作为完整索引,不建设独立 Automation Center。当前计划触发支持单次、每日和每周;高级时区、Cron、事件触发与重试治理仍按 PRD 逐步实现。详见 [Task Center PRD](./docs/features/task-and-job/task-center-prd.md) 和 [Scheduled Task PRD](./docs/features/task-and-job/scheduled-task-prd.md)。
- [x] **记忆与智能心跳**:当前提供周期回顾、建议记忆、洞察、后续任务和可审计运行轨迹。
- [x] **定时消息沿用会话**:定时消息使用会话当前历史、Runtime、工作模式和已保存的知识检索设置;任务详情显示实际运行模式,当前 occurrence 未结束时禁止重复“立即运行”。
- [x] **智能心跳入口与范围改善**:将“智能心跳 > 心跳计划”作为完整配置的唯一权威入口,支持创建和编辑 Global 或指定一个、多个 Project 的计划;旧单项目配置无损迁移,项目级记忆与行动输出必须显式指定范围内的 Project。Task Center 和设置不再复制心跳表单;按分区回顾、候选生成和唤起条件仍待设计。详见 [智能心跳 PRD](./docs/features/smart-heartbeat/prd.md)。
- [ ] **通用监督**(规划中):通过固定监督栏目观察用户选择的会话、任务、自动化或实验对象,提供带证据的评论与人工介入请求,但不自动发言、批准工具或切换 Execute。详见 [会话监督 PRD](./docs/features/conversation-supervision/prd.md)。
任务中心通过状态数量和筛选定位运行中及待处理任务,审批操作保留在对应任务卡片中。
- [x] **自动监督设置与范围**:“监督者 > 自动监督”统一管理计划、自动监督报告和建议,支持创建和编辑 Global 或指定一个、多个 Project 的每日、每周计划;工作回顾阅读带日期的统一监督结果及历史。旧单项目配置无损迁移,项目级记忆与行动输出必须显式指定范围内的 Project。Task Center 和设置中心不再复制心跳表单;按分区回顾、候选生成和唤起条件仍待设计。详见 [智能心跳 PRD](./docs/features/smart-heartbeat/prd.md)。
- [x] **监督者回顾、故事线图谱与活动**:按范围和时间回顾工作,查看保存结果的图谱及来源,确认或修订实体,预览写入本地知识库实体。侧栏可跟随或固定 Conversation、Task,并打开匹配结果。活动合并心跳报告与下游监督的阶段状态,保留失败和旧结果入口。自动阶段只处理新增、修改及未处理的来源片段,无变化时跳过模型;手动回顾重新整理有界历史。回顾只读且禁止工具,模型调用可能产生费用,详见[实施与验证记录](./docs/features/conversation-supervision/progress.md)。
- 手动回顾分页处理所选历史,并按批保存事实与来源;支持暂停和从保存进度继续。设置提供并发、超时、批次大小、执行预算和响应容量,活动记录显示实际阶段与已保存批次详情;工作回顾和活动记录使用面板可用全宽。
- [ ] **监督后续能力**(规划中):逐事件完整回放、Experiment 目标、人工修改时间序列审计、事件触发及更完整的执行观察仍未完成。当前自动回顾受输入预算和滚动窗口限制,删除报告不会重置处理进度或重建图谱。详见 [会话监督 PRD](./docs/features/conversation-supervision/prd.md)。
- [ ] **批量运行与对比实验室**(规划中):对模型、Prompt、角色和工作流配置执行批量对比,汇总质量、耗时、Token、费用、失败率和成果差异。
- [ ] **时态记忆与事实冲突检测**(规划中):为记忆和知识图谱增加有效期、当前事实、过期与矛盾检测、事实核验及证据回溯。
- [ ] **可视化受控工作流**(规划中):提供版本化 DAG、条件分支、审批、取消和恢复,执行节点继续经过 Main Runtime 边界。
@@ -116,13 +137,20 @@
- [x] **可选本地语音模型管理**:应用不内置模型权重;提供校验下载、进度与取消、来源链接、ZIP 或本地目录导入、切换和删除。
- [x] **本地录音与离线转写**:采集麦克风音频并使用已选择的本地模型离线转写,支持停止、取消、状态反馈和资源释放。
- [x] **版本检查与镜像节点**:在“关于与更新”中选择 GitHub(默认)或镜像节点;手动检查、启动时检查和下载页使用同一选择,并只读取固定可信的发布索引,不自动下载或安装。
- [x] **窗口恢复入口**:渲染失败时显示本地化的重新加载页面,渲染进程崩溃时提供原生恢复确认;重新加载可能丢失未保存输入。详见[窗口恢复说明](./docs/development/window-recovery.md)。
- [x] **应用内反馈**:在“关于与更新”中直接提交问题、建议或体验反馈,支持可选邮箱和单张截图;默认不上传诊断,用户可主动附加有界的最近桌面诊断摘要。失败后保留草稿、诊断选择和同一请求编号;不会附加对话、Prompt、凭据、文件内容、路径、Provider 原始响应或远端 Agent 日志。
- [x] **内网兼容**:允许应用内 HTTP 与无效、自签名或过期的 HTTPS 证书;反馈、微信凭据与媒体等固定平台服务仍保持严格证书校验。
### 开源、构建与发布
- 当前源码候选为 Desktop `0.13.1`、Agent `0.11.24`,OpenCode 固定为 `1.18.29`;正式发布状态以 Desktop 与 Agent 的独立发布渠道为准。
- 候选验收与版本准备分别记录。当前远程问答源码已通过真实 Linux x64 SSH 与 Electron 问答测试,但模型为确定性 loopback 服务,不是完整生产 Main/Preload 配合真实服务商;该真实模型链路和候选原生 CI/打包验证仍待完成,详见[远程问答证据](./docs/features/assistant-workbar/progress.md)。
- 当前 Desktop 候选为 `0.13.15`,Agent `0.13.4` 已发布,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。
- Desktop `0.13.15` 承接未发布的 `0.13.14`,改善监督者分页回顾与续跑、ripgrep 原生搜索、待办布局和远程结果未知后的显式续聊。数据库升级至 schema 47,回退旧客户端需使用升级前完整备份。
- Agent `0.13.3` 回收空闲 Runtime,并使被替换的 Agent 等已有工作结束后退出;复合包要求 Desktop `0.13.12`,请先升级 Desktop,再更新 Host 环境。Node 保持 `24.19.0`。
- Agent `0.13.4` 在完成前等待模型交付确认,空闲 Runtime 回收失败不改变已完成结果,并在环境更新后清理已确认停用的旧程序及所属孤儿进程;保留历史、活动及状态不明的安装。独立包要求 Desktop `0.13.14` 或更新版本;因 `0.13.14` 未发布,请先升级至 Desktop `0.13.15`,再更新 Host。
- 发布准备要求全量测试、发布说明校验、类型检查及 lint;候选主分支 CI、生产构建、原生打包及公开发布核验为独立验收步骤。本轮不做本地生产构建、打包或龙芯预览,当前验证状态见[恢复版准备记录](./docs/development/release-preparation-0.13.15.md)。
- Desktop `0.13.12` 和 Agent `0.13.3` 已完成原生发布流程,但不替代开发期真实 Host 场景覆盖。
- 验证记录与功能状态分开。存储已有本地及 Linux x64 Host 真实工具任务、多项目多会话、取消和无损迁移验证;进程复用有 Windows 完整 App 与 Linux x64 Host 证据,PPTX OCR 与空闲内存释放有 Windows 真实文件证据。图片工具已有真实服务商生成/编辑及 Host 传输证据,这些记录未覆盖完整界面自然语言调用和本地/远程切换。候选仍需主分支 CI 及原生发布打包,详见[Runtime 验证记录](./docs/features/assistant-workbar/progress.md)与[图片工具验证记录](./docs/features/conversation-media-generation/progress.md)。
- 发布准备前,`9836a4c` 已通过本地 4,858 项测试(67 项跳过)、完整类型检查和 lint;真实服务商画布分析仍未验证。该提交的三个原生 Agent CI 构建通过;桌面 CI `35485936807` 为 4,875 项通过、8 项 Electron 测试失败,均因 Linux 沙箱辅助程序未配置,在页面断言前退出。候选配置该程序并在 Xvfb 下运行测试,不关闭沙箱;候选本地验证、主分支 CI 与原生标签打包分别记录,不在发布准备中运行本地生产构建或打包。
- [x] **0BSD 开源许可**:原创代码可自由使用、复制、修改、分发和商用;第三方组件和资源仍遵循各自许可证。
- [x] **可复现依赖安装与源码构建**:使用锁定依赖、Node.js 24 和统一的测试、类型检查、Lint、生产构建命令。
- [x] **六平台原生发布矩阵**:Windows、macOS、Linux 的 `x64`、`arm64` 目标由原生 Runner 构建,并提供发布清单和 SHA-256 哈希。Windows 提供 NSIS 与 portable ZIP,macOS 仅 DMG,Linux 提供 AppImage、DEB 和 RPM,共 12 个安装包、20 个发布资产。
@@ -131,10 +159,12 @@
### 开放接口、团队协作与远程执行
- [x] **远程 Continue 交付**:已接入 CN 1.5.47 受管包、Agent HTTP-to-ACP 适配、模型路由、项目选择和原生清单。默认包同时携带 OC/CN;Windows 桌面到 Linux x64 已通过安装、真实模型清单更新、长清单、取消及重启恢复验证。Execute 会话 MCP 已通过真实文本模型 Host 往返,图片服务使用替代接收端,不代表真实生图验证。已发布 Agent `0.13.4` 需要 Desktop `0.13.14` 或更新版本;正式发布及原生平台验收与 Linux 开发证据分开,详见[验证记录](./docs/features/remote-host/runtime-checklist-validation.md)。
- [x] **远程任务委派**:仅在用户显式配置端点和令牌后启用,按全局内网兼容模式使用 HTTP(S),结果进入持久化发件箱。
- [ ] **Headless Runtime API**(规划中):提供本机优先的任务、事件、状态和成果 API,以及有范围、有效期、限流和撤销能力的令牌。
- [ ] **GoodBuddy Team Hub**(规划中):以可选服务提供组织、RBAC、项目共享、远程 Agent、策略下发和租户审计。
- [ ] **SSH 主机与远程执行空间发布验收**:主机 CRUD、Host Key、加密凭据、Project UI、Workspace、OpenCode ACP v5、Agent-owned Prompt/gateway/transcript、Ask 只读、Execute 完整账号权限、取消、detached Agent 精确重连和 release-only 双架构资源校验已经接线。Linux x64 当前源码已用真实模型与工具通过 detach、进程结束、relay 丢失、并发、取消、Provider 异常、Agent 重启和 Desktop SQLite 恢复矩阵;当前源码测试签名 Agent `0.11.17` 又通过两个并发 Ask 读取、一次 Execute 写入并读回,以及 6 次带自定义 Header 和 Body 的真实受管网关模型请求,未发现 Provider 请求重放,隔离 Agent state 中也没有 API Key 或自定义请求值。公开 signing key registry 已供应。控制面直连源码不等待新 installer-bearing 包,但仍需公开核对当前 Linux x64/arm64 正式签名工件矩阵,并完成 GitHub/北京镜像、双架构和离线 GoodBuddy 传输的发布验收;门槛未满足时不宣称该路径已发布或通过正式发布验收。
- **SSH 主机与远程执行空间验证记录**:主机 CRUD、Host Key、加密凭据、Project UI、Workspace、OpenCode ACP v5、Agent-owned Prompt/gateway/transcript、Ask 只读、Execute 完整账号权限、取消、detached Agent 精确重连和 release-only 双架构资源校验已经接线。Linux x64 当前源码已用真实模型与工具通过 detach、进程结束、relay 丢失、并发、取消、Provider 异常、Agent 重启和 Desktop SQLite 恢复矩阵;当前源码测试签名 Agent `0.11.17` 又通过两个并发 Ask 读取、一次 Execute 写入并读回,以及 6 次带自定义 Header 和 Body 的真实受管网关模型请求,未发现 Provider 请求重放,隔离 Agent state 中也没有 API Key 或自定义请求值。公开 signing key registry 已供应。控制面直连源码不等待新 installer-bearing 包;现有记录未覆盖当前 Linux x64/arm64 正式签名工件的完整公开核对及 GitHub/北京镜像、双架构和离线 GoodBuddy 传输矩阵,发布结果另行记录。
- [ ] **多云远程沙盒 Agent**(规划中):通过云厂商 API 和 SSH Agent 管理专用 Linux 沙盒;凭据留在 Main 进程,高风险控制面操作单独确认。
## 规划原则
+13 -2
View File
@@ -53,7 +53,7 @@ current limitations.
| Area | Available in GoodBuddy |
| --- | --- |
| Agent Runtimes | Direct model connections, OpenCode, Continue, and the preview DeepSeek Harness |
| Data and execution | Local SQLite storage, read-only `Ask`, full-account `Execute`, and recorded tool activity |
| Data and execution | Local SQLite and note-file storage, read-only `Ask`, full-account `Execute`, and recorded tool activity |
| Knowledge | File, folder, and web imports with full-text, Chinese phrase, vector, and knowledge graph retrieval |
| Desktop releases | Official Windows, macOS, and Linux builds for `x64` and `arm64`, plus an experimental LoongArch preview |
| Integrations | Local or hosted model endpoints, custom MCP servers, WeChat ClawBot, WeCom, and DingTalk |
@@ -68,6 +68,15 @@ current limitations.
### Magic Notes and Smart Heartbeat
Magic Notes displays a continuous record stream, with a narrow left index and
independent right AI comments. Both panes can collapse; thumbnails scroll to a
record while preserving drafts. It supports rich-text entries and paged canvases with flowing text,
handwriting, highlights, images and PDF backgrounds. Import PDF text and export
annotated pages as a raster PDF. Canvas saves are manual; AI comments use the
default model's image-input capability, with an explicit text-only fallback when
images are unsupported. See the [Magic Notes documentation](./docs/features/magic-notes/README.md)
for behavior and final validation status.
![GoodBuddy Magic Notes](./docs/screenshots/GoodBuddy_MFSGeK0NoT.gif)
![GoodBuddy Smart Heartbeat](./docs/screenshots/smart-heartbeat.png)
@@ -156,7 +165,9 @@ npm run lint
Scan the QR code to join the GoodBuddy WeChat group:
<img width="1250" height="1194" alt="9627ce196b0de42f123c33dd9dea1568" src="https://github.com/user-attachments/assets/e806543f-6d8a-4c5c-ba44-19e74dac9a78" />
<img width="1250" height="1743" alt="8d6cd5dc3f347a0f3f1cf3844071b4d3" src="https://github.com/user-attachments/assets/1c92c1b8-d1f7-4270-be2c-b9543beadc11" />
+10 -2
View File
@@ -38,7 +38,7 @@ GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时
| 范围 | GoodBuddy 提供的能力 |
| --- | --- |
| Agent Runtime | 直连模型、OpenCode、Continue 和预览版 DeepSeek Harness |
| 数据与执行 | 本地 SQLite、只读 `Ask`、使用当前账号完整权限的 `Execute`、工具调用记录 |
| 数据与执行 | 本地 SQLite 与笔记文件存储、只读 `Ask`、使用当前账号完整权限的 `Execute`、工具调用记录 |
| 知识库 | 导入文件、目录和网页,通过全文、中文词组、向量和知识图谱检索 |
| 桌面版本 | Windows、macOS、Linux 的 `x64` 与 `arm64` 正式版本,以及龙芯 LoongArch 实验预览版 |
| 外部连接 | 本机或内网模型服务、自定义 MCP、微信 ClawBot、企业微信和钉钉 |
@@ -53,6 +53,12 @@ GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时
### 魔法笔记与智能心跳
魔法笔记连续展示全部记录,左侧窄栏索引与右侧 AI 评论可独立收起,点击缩略图滚动定位并保留草稿。
支持富文本记录和分页画布,可混排跨页正文、手写笔迹、高亮、图片与 PDF 底版,
导入 PDF 时提取原生文字,并将批注页面导出为栅格 PDF。画布由用户手动保存;AI 评论按
默认模型的图像输入能力分析,不支持图像时明确提示仅文字降级。具体行为与最终验证状态见
[魔法笔记文档](./docs/features/magic-notes/README.md)。
![GoodBuddy 魔法笔记](./docs/screenshots/GoodBuddy_MFSGeK0NoT.gif)
![GoodBuddy 智能心跳](./docs/screenshots/smart-heartbeat.png)
@@ -128,7 +134,9 @@ npm run lint
扫描二维码加入 GoodBuddy 微信群:
<img width="1250" height="1194" alt="9627ce196b0de42f123c33dd9dea1568" src="https://github.com/user-attachments/assets/ff87dfac-d72f-47b7-ac9a-c88b0b4c96d9" />
<img width="1250" height="1743" alt="8d6cd5dc3f347a0f3f1cf3844071b4d3" src="https://github.com/user-attachments/assets/e2cd4874-0b6c-40d4-8867-0a9e843a7cb5" />
## 开源许可
+127 -45
View File
@@ -35,6 +35,7 @@
- 加载、空内容、无结果、失败、只读和权限不足是不同状态,必须分别呈现。
- 可撤销操作优先提供撤销,不可撤销操作必须在执行前说明影响范围。
- 不依赖颜色单独表达状态或风险。
- 当前模型或 Runtime 及切换进度集中显示在会话输入区,顶栏不重复显示可用性标识。切换失败、自动状态检查失败或返回不可用时,通过应用通知说明原因;发送按钮保留关联的无障碍状态说明。
### 2.5 默认支持浅色与深色主题
@@ -145,8 +146,9 @@
| 变体 | 最大内容宽度 | 适用场景 | 页面映射 |
| --- | --- | --- | --- |
| `reading` | `820px` | 连续阅读、单列编辑、对话撰写 | 聊天正文与输入区 |
| `standard` | `960px` | 常规列表、设置与表单 | 最近对话、设置 |
| `dashboard` | `1040px` | 指标、卡片网格、宽表格与审计数据 | 智能心跳、活动记录 |
| `standard` | `960px` | 常规列表与表单 | 最近对话 |
| `dashboard` | `1040px` | 指标、卡片网格、宽表格与审计数据 | 活动记录 |
| `supervisor` | `1440px` | 选择列表、图谱画布、来源详情三栏工作台 | 监督者(含智能心跳设置) |
| `master-detail` | 可用空间内流式铺开 | 左侧选择、右侧编辑或预览 | 知识库 |
共同规则:
@@ -176,7 +178,8 @@
`PageHeader` 统一接收以下内容:
- `title`:必填,简短名词或任务名称。
- `description`:可选,一行说明当前页面能做什么,不重复标题。
- `description`:可选,需要常显的简短上下文,不重复标题或帮助内容。
- `help`:可选,补充用途和操作说明,通过标题同行的 `InlineHelp` 按需展开,规则见 6.14。
- `scope`:涉及范围的数据页面必填,使用 `ScopeBadge`。
- `primaryAction`:可选,页面唯一主操作。
- `secondaryActions`:可选,最多两个直接显示,其余进入更多菜单。
@@ -193,7 +196,7 @@
### 6.1 PageTabs
用于同一一级页面内的同级内容面板,例如智能心跳的“运行概览”和“心跳计划”。
用于同一一级页面内的同级内容面板,例如监督者的“工作回顾”和“故事线图谱”。
- 使用 `tablist`、`tab` 和 `tabpanel` 语义,当前项使用 `aria-selected="true"`。
- 一级页面之间的导航由应用主导航承担,不复用 `PageTabs`。
@@ -283,9 +286,12 @@
- 浅色侧栏使用冰蓝灰表面,与白色主内容画布形成明确边界;深色侧栏使用比主画布略亮的蓝灰表面。
- 一级导航与最近会话之间、最近会话与底部账户区之间必须有可见结构分隔线。浅色主题使用 `--border-default`,深色主题可在可辨前提下使用 `--border-subtle`。
- 当前导航项和当前会话必须同时使用至少三种信号中的两种:强调背景、可见边框、图标或文字强调。浅色主题的当前项优先使用更完整的蓝色选中表面和较高字重。
- 当前导航项和当前会话使用一致的淡蓝选中背景、强调色文字和中等字重,不额外描边或添加侧边标记;导航图标随选中状态使用强调色,悬停时保留选中状态。浅深主题遵循相同规则,强制颜色模式保留系统选中边框。
- 固定导航行高至少 `36px`、行间距 `4px`,图标统一为 `16px`;导航与最近会话分隔线上下各留 `16px`。普通待办和待处理建议数量使用中性色徽标,不使用危险红色;徽标高度不得改变导航行高。搜索框获得内部焦点时显示完整焦点环。
- 未选中项保持平整,不为每一行添加卡片边框或阴影。悬停反馈不得强于选中状态。
- 项目选择器下方承载跨项目活动汇总,空闲时隐藏;项目计数与活动对话框遵守
- 项目选择器顶行保留项目切换与新建按钮;项目设置统一从下拉列表对应行的管理按钮进入,关闭后焦点返回项目选择器。
- 项目下拉列表通过 `FloatingPortal` 浮在侧栏与会话区上方,默认宽度 `380px`,不受侧栏裁剪;距窗口边缘至少 `16px`,窄窗口自动收窄,长列表内部滚动。浮层锚定项目选择按钮,窗口、滚动和尺寸变化后重新定位。
- 项目选择器下方仅在有运行中、待处理或已完成未查看会话时显示跨项目活动汇总,无活动时隐藏;项目计数与项目、会话级联菜单遵守
[项目活动汇总 UI 规则](./docs/features/application-tool-navigation/ui-design.md#10-项目活动汇总)。
活动数量不改变项目菜单的稳定分组和顺序。
- 桌面停靠状态下,主侧栏宽度允许在 `220px` 至 `420px` 范围内通过指针或键盘调整,
@@ -314,9 +320,11 @@
接受用户本机选择的 PNG、JPEG 或 WebP 内联数据,宽高不超过 `4096 × 4096`、总像素不
超过 1600 万且大小不超过 `512 KB`;不得读取远程 Logo URL。名称或副标题溢出品牌区时
单行截断,不能撑宽或挤压侧栏。
- 侧栏底部固定显示文件、终端、浏览器和资源监控四个系统工具图标,设置按钮独立靠右。工具
图标只负责打开或聚焦助手工作栏面板,不在侧栏复制工具内容。已有稳定设置入口时,不在顶栏
重复提供同一入口。
- 侧栏底部提供应用中心入口,设置按钮独立靠右。点击应用中心向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻状态筛选;点击行关闭菜单,知识库、智能心跳和魔法笔记在主内容区打开页面。本机推理监控从菜单、常驻入口或管理 Modal 打开时均使用独立 Modal,保留底层工作区。菜单底部“管理应用”打开既有整窗管理 Modal,共同应用设置沿用设置 Modal。
四个内置应用的管理列表、菜单和侧栏共用保存顺序,菜单按启用过滤,侧栏按启用且常驻过滤。知识库、智能心跳始终启用并常驻,可调整位置;管理 Modal 提供“打开”和排序,不提供启用、常驻开关或通用空设置,标签为“始终显示”(英文 `Always shown`)。原工作区配置及心跳单条计划 enabled 保持不变。只有魔法笔记、本机推理监控可修改启用和常驻,取消常驻不关闭功能。当前对话、运行记录和设置保持固定系统位置,不参与应用排序。
设置持久化后的事件同步各入口及工具配置写入;重开中心刷新期间锁定修改,新快照不被旧读取覆盖。文件、终端、浏览器和资源监控只通过右侧工作栏访问,不在左侧复制入口。
管理 Modal 始终使用紧凑单列横向列表,纵向对应保存顺序;左侧为图标、名称及简短用途,右侧为打开、可用的设置及上下移动操作,行间使用弱分隔线,不加卡片外框。窄窗口操作换行且不溢出,保留共享键盘焦点环。可选应用关闭时禁用“打开”。启用、常驻位于紧凑设置详情;四行均提供上下移动及拖动排序,不受启用或常驻影响,箭头按完整顺序判断边界。搜索时禁用拖动,箭头仍按完整顺序移动,不丢失未命中项。尺寸、返回与滚动重置规则见[应用中心 UI 设计](./docs/features/application-tool-navigation/ui-design.md)。已有稳定设置入口时,不在顶栏重复提供同一入口。
- 本机推理监控 Modal 复用整窗 Portal、背景隔离和焦点循环;停止、重启及释放使用其上方的紧凑影响确认。Escape 只关闭最上层,操作提交期间禁止关闭及重复提交;焦点恢复与尺寸见[本机推理监控 UI 规则](./docs/features/application-tool-navigation/ui-design.md#91-本机推理管理)。
### 6.9 助手工作栏
@@ -334,10 +342,10 @@
- Tab 行只显示已打开实例,行末始终提供“+”普通按钮;点击后在内容区打开应用选择页。
“+”不使用 `tab` 语义,也不能因为终端或其他应用已经打开而消失。
- 应用注册表分别声明单实例或多实例、默认打开、必须存在、可关闭和排序策略。在工作栏“+”目录中,
单实例重复选择时聚焦已有 Tab;浏览器和终端每次选择创建独立实例。主侧栏系统工具快捷
入口按当前上下文聚焦最近使用的匹配实例,额外实例仍从“+”目录创建。
单实例重复选择时聚焦已有 Tab;浏览器和终端每次选择创建独立实例。选择已有 Tab 只激活
该实例,保持其目标和历史;主侧栏不再提供按当前上下文匹配实例的重复工具入口。
- Task Center 是默认跟随当前项目的单实例索引,不使用多实例模式。用户可在同一 Tab 内切换
“当前项目”“全局任务”和“全部项目”;范围变化不得创建第二个 Task Center。每个 Task 只
“当前项目”和“所有项目”;无项目任务在“所有项目”中标注“未绑定项目”。范围变化不得创建第二个 Task Center。每个 Task 只
关联一条 Conversation,一条 Conversation 可以关联多个 Task;列表不得复制会话内容,也
不得把 Job/Run 提升为可导航 UI 对象。
- 各栏目读取当前会话或项目的对应内容;当前内容不可用时仍保留栏目入口并说明原因。
@@ -373,7 +381,9 @@
遮罩或隔离其内容。分栏计算排除最左侧主导航,主工作区与工作栏采用相同的 `300px`
最小宽度;拖动保存两者在可用分栏宽度中的比例,任一侧到达最小值后都不得继续侵占。
所有可容纳两侧最小值的尺寸均支持指针和键盘调整,并须保留全部栏目入口。
- 工作栏开关固定在窗口关闭按钮正下方,同一个按钮负责展开与收起并持续暴露状态;图标中的
- 工作栏分隔条须保留完整的 `9px` 拖拽命中区域,位于侧栏内部,不能被容器裁剪;栏目内容
和原生浏览器视口须避开该区域。浏览器全屏时隐藏分隔条并取消这段预留空间。
- 工作栏开关固定在顶栏 `.topbar__actions` 内、紧邻浅色/深色主题按钮之前,同一个按钮负责展开与收起并持续暴露状态;图标中的
箭头方向必须随当前动作切换,收起状态提示展开,展开状态提示收起。按钮保持普通图标按钮
视觉,不通过持续高亮表达展开状态。工作栏内部不再重复关闭按钮;当前会话没有 Task 时,
不得为了放置该开关而保留一条空上下文栏。
@@ -397,6 +407,9 @@
顶栏,不能保留一条未被遮罩的窗口控制色块。关闭操作由 Modal 自身提供。
- 整窗固定遮罩通过 `createPortal(..., document.body)` 挂载,避免页面层叠上下文影响;
保留 React Context、关闭与焦点管理,表单样式不得依赖已移出的页面祖先选择器。
- 共享锚定菜单 `AnchoredMenu`、同行帮助 `InlineHelp` 和应用通知共用 `FloatingPortal` 的非模态 `popover="manual"`,由浏览器 top layer
绘制,避免被 Modal 或滚动容器遮挡。菜单和帮助挂在触发按钮所属 Modal 内,通知跟随当前活动
Modal,无 Modal 时挂在 `body`;不能只提高 `z-index` 或解除背景的 `inert`。
- 原生浏览器 `WebContentsView` 不受网页 `z-index` 控制。应用级 Modal 打开时统一释放
浏览器 viewport;非模态对话框、菜单、局部确认或应用通知与浏览器区域相交时也释放。
浮层关闭后只恢复当前活动实例的 viewport,不关闭、重建或刷新网页。接线与测试见
@@ -415,7 +428,7 @@
不再重复显示状态文字;完整状态详情继续通过可访问说明和工具提示提供。
- 浅色与深色切换属于持续可用的窗口级操作,直接显示太阳或月亮图标,并通过可访问名称说明将切换到的主题。选择必须持久化,切换不得改变布局。
- 顶栏只直接显示当前任务所需的高频操作。已有侧栏账户设置入口时,不再重复显示 Runtime/设置入口;使用帮助优先放在相关操作附近,而不是为单个帮助项创建“更多”菜单。
- 助手工作栏开关固定在上下文栏最右侧并与窗口关闭按钮纵向对齐;展开后该开关保持原位并
- 助手工作栏开关固定在顶栏 `.topbar__actions` 内、紧邻浅色/深色主题按钮之前;展开后该开关保持原位并
负责收起,不在工作栏标题区域增加第二个关闭入口。
- 只有存在至少两个无法由稳定入口承载的低频全局操作时才增加全局菜单,不为了容纳一个冗余入口而显示省略号按钮。
- 窄窗口下优先压缩状态标签并保留图标按钮,不隐藏窗口控制、当前范围或进行中的风险状态。
@@ -436,14 +449,18 @@
尚未完成而禁用该入口。选择入口只展开流程,验证成功前不提交实际值;不得自动采用第一个
候选。取消保持原值,验证错误在对应操作附近显示。
- 项目选择器属于富信息单选菜单。收起时保持当前项目名称紧凑可见;展开后每项至少显示项目名称,并按数据可用性补充目录、远程来源或其他能帮助辨认项目的信息。托管 SSH 项目在“远程项目”内按已保存 SSH Host 做一层轻量分组,Host 标题旁显示真实 Agent 连接状态;项目副标题只显示远端路径,不重复“托管 SSH”类型。
- 项目菜单区分“本地项目 / 远程项目 / 远程通道”,不把消息通道命名为远程项目。“远程项目(技术预览)”关闭时完整移除远程项目分组,而不是显示不可操作的残留选项;本地项目和远程通道继续可用。Agent 连接状态使用“未连接 / 连接中 / 就绪 / 异常”等文字标签,读取状态不得发起连接,且不能只依赖图标或颜色;新增项目来源时不得退回仅列名称的原生 `select`。
- 项目菜单区分“本地项目 / 远程项目 / 远程通道”,不把消息通道命名为远程项目。“远程项目”关闭时完整移除远程项目分组,而不是显示不可操作的残留选项;本地项目和远程通道继续可用。Agent 连接状态使用“未连接 / 连接中 / 就绪 / 异常”等文字标签,读取状态不得发起连接,且不能只依赖图标或颜色;新增项目来源时不得退回仅列名称的原生 `select`。
### 6.12 应用通知与就地反馈
应用级通知统一进入全局通知视口,页面不得自行复制通知卡片或在内容流中长期堆放短期消息。
- 右上角通知使用紧凑的中性表面、弱边框和轻阴影,最大宽度为 `360px`,窄窗口保留两侧各 `16px`。状态色仅用于图标,不铺满卡片;正文直接呈现,通用状态标题仅供屏幕阅读器读取。图标、正文与关闭按钮统一垂直居中,不使用各自的上下偏移。
- 异步操作成功、无需立即处理的信息,以及不属于某个字段的异步失败,使用应用级 `success`、`info` 或 `error` 通知。
- 成功和信息通知默认在约 4.5 秒后自动消失;错误通知保持可见,直到用户关闭或同一去重键的更新替换它。
- 成功和信息通知默认在约 2 秒后自动消失;错误通知保持可见,直到用户关闭或同一去重键的更新替换它。
- 通知出现不抢焦点;Modal 打开时,通知关闭按钮纳入当前 Modal 的 Tab 循环。聚焦通知时
Escape 返回原控件或当前 Modal 的可用控件,不关闭 Modal;关闭通知后同样恢复焦点。
打开、关闭或切换 Modal 不重建通知,不重置自动消失计时。
- 同一语义和文案的重复通知应去重。通知正文必须有长度上限,不包含凭据、私人内容或未脱敏的提供商响应。
- 字段校验、破坏性确认、操作进度、阻塞整个页面的状态,以及需要就地重试或修正的错误保留在相关控件附近。
- 就地错误必须与对应字段或操作建立程序化关联;全局错误使用 `alert` 和 assertive 实时区域,成功与信息使用 `status` 和 polite 实时区域。
@@ -462,6 +479,28 @@ Switch 用于在两个持久状态之间立即切换,例如启用能力、开
- 涉及联网、上传、电脑控制或其他外部影响的 Switch,附近必须持续说明数据去向、权限范围或风险,不能只靠设置名称表达影响。
- 自动化测试应按 `switch` 角色查询二元开关,按 `checkbox` 角色查询多选或确认项,防止视觉迁移后语义回退。
### 6.14 InlineHelp 同行帮助
页面、区块和字段的补充用途、格式及操作说明统一收进标题或标签旁的 `InlineHelp` 问号按钮。
`PageHeader` 与 `SettingsSectionHeader` 通过 `help` 接收这类内容;`description` 保留需要常显的上下文,两处不重复说明。描述缺省或为空时不渲染段落,不保留空行。
当前范围、状态、字段错误、操作进度、风险、费用和影响用户决策的限制仍就地可见。混合文案先拆分,再将补充说明移入帮助。
- 标题或标签与问号使用共享 `inline-help-label` 同行排列,不为帮助另占说明行;字段标签通过 `htmlFor` 关联控件,帮助按钮放在 `label` 外,避免点击帮助触发输入或开关。
- 悬停或键盘聚焦按钮时展开;指针可移入浮层阅读。点击(含 Enter、Space 激活)固定展开,再次点击关闭;未固定时,指针和焦点均离开后关闭。
- Escape、点击外部或焦点移到外部关闭帮助。Escape 优先关闭帮助,不同时关闭所属 Modal;展开和关闭不主动转移焦点,不建立焦点陷阱。
- 按钮提供与标题或标签对应的可访问名称、`aria-expanded`,展开时通过 `aria-controls`、`aria-describedby` 关联 `role="tooltip"` 内容。提供稳定帮助 ID 时,关闭状态保留同 ID 的隐藏说明,展开时由浮层替换,确保字段的 `aria-describedby` 始终有唯一目标。持久标签与必要提示不得依赖浮层。
- SSH 密码字段常显认证用途和验证成功后加密保存的说明;Main 处理及不写入命令行、日志、普通设置的细节收进同行帮助。应用常驻说明与关闭后果使用独立翻译键,关闭后果保持常显,不按标点拆分翻译文本。
- 浮层复用 `FloatingPortal`,遵循 6.10 的 Modal 归属规则。宽度最多 `360px`,四周保留 `16px` 视口边距;空间不足时翻转或调整位置,长内容在浮层内滚动,窗口及滚动变化后重新定位。
- 问号使用共享 `28px × 28px` 点击区域、焦点环与主题令牌。不得另做原生 `title` 长说明或页面专属帮助浮层。
### 6.15 表单 Modal
- 复用 `styles.css` 中的 `custom-task-dialog__surface`、`custom-task-dialog__header`、`custom-task-dialog__content` 和 `custom-task-dialog__actions`,与定制任务、SSH Host 等现有弹窗使用同一结构;不为单个业务另建 Modal 框架。
- 标题放在 `header` 左侧,右侧只放共享 `icon-button` 的 X 关闭按钮,使用本地化 `aria-label` 和工具提示。取消不放在标题下方或表单顶部。
- `footer` 使用共享操作区,按钮整体右对齐,次要取消在前、主要保存在后。放弃修改确认沿用同一操作区,继续编辑在前、危险操作在后。
- header、滚动 body、footer 是 surface 的三个直接子区域。沿用共享 Grid 的 `auto minmax(0, 1fr) auto`,只有 body 滚动,短窗口仍可见标题、关闭和提交操作;窄窗口沿用共享外边距、内边距和主题令牌。
- X、取消、Escape 和遮罩关闭共用业务关闭检查,不绕过未保存确认或提交锁定。确认中的 Escape 或 X 返回编辑;继续复用 `activateModalFocus`、`trapTabFocus`,保留背景隔离、初始焦点和关闭后的焦点恢复。保存失败保留草稿及可恢复错误。
## 7. 交互状态
所有可交互组件必须实现:
@@ -605,8 +644,9 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
右边缘与用户气泡右边缘对齐;两侧为头像保留独立安全边距,滚动条出现或工作栏缩放不得改变对齐。
- 对话标题和当前项目范围位于 `PageHeader` 或对话上下文区,不在消息流中重复。
- 已结束且包含正文的助手回复在底部提供带可访问名称和工具提示的复制图标;复制 Markdown
原文,不包含推理过程、工具日志或引用元数据,结果通过应用通知反馈。流式回复完成前不显示
复制入口。
原文,不包含推理过程、工具日志或引用元数据。复制成功后,当前按钮显示使用 `--success`
的绿色对号,可访问名称和工具提示同步表示已复制,3 秒后恢复复制图标;重复复制成功重新
计时,不显示成功通知。失败仍通过应用错误通知反馈。流式回复完成前不显示复制入口。
- 默认开启“会话 HTML 渲染”时,已完成 Agent 回复中的完整 HTML 与 `html` / `htm`
代码块在原位置显示隔离静态预览;用户消息、推理过程和流式中的回复不得执行 HTML。
每个预览持续标注脚本和网络已禁用,并在标题右侧复用与 Mermaid 图表相同的图标按钮组:
@@ -614,6 +654,9 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
全屏预览遵循统一 Modal 的背景隔离、初始焦点、Tab 循环、Escape 关闭和焦点恢复规则,
并沿用同一套清理与 Sandbox/CSP 边界。关闭平台功能后必须显示 HTML 源码,不能隐藏内容
或保留旧预览。
- Mermaid 大图支持缩放和拖动;图形超过画布时,四个边缘都必须能通过滚动到达,不能因居中而裁掉顶部或左侧。
弹窗工具栏提供“导出 PNG”,保存完整图形,不受当前缩放和滚动位置影响;背景使用当前主题的画布底色。
导出按原图四周留出 `16px`,以 2 倍分辨率绘制,任一边最多 `4096px`。导出期间禁用重复点击,失败在按钮旁提示重试。
- 本地会话的更多操作提供“在新会话中继续”。创建前必须先持久化当前稳定消息;有活动运行
或待发送内容时不可创建。新会话复制聊天内容与当前 Runtime/知识检索选择,重置压缩状态,
使用新的会话和消息 ID,且不复制 Task、队列、运行、记忆或成果归属。
@@ -629,16 +672,19 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- OpenCode 原生 Task 调用在本地项目和托管 SSH 项目中统一解析为子 Agent 状态卡,不显示为普通工具卡。状态卡持续显示 Agent 类型、任务说明、运行状态、完成输出或失败原因;身份徽标、Agent 名称和状态位于首层,任务说明作为主信息,Runtime 来源作为次级元数据,输出与错误在同一卡片内按需展开。父 Agent 的推理不冒充子 Agent 过程,必须与状态卡保持明确间距。状态不能只靠颜色表达。推理、子 Agent、工具和正文统一按事件首次出现顺序进入消息块;增量更新只更新原位置,Task 临时工具块重分类后必须原位替换,不能把子 Agent 卡统一提升到消息顶部。同一调用的增量事件按稳定调用 ID 更新,不能因 ACP 首帧尚无参数而永久保留重复工具卡或活动记录。一次请求内已观察到的子 Agent 均保留在消息与活动记录中;父请求取消、失败或意外中断时仍处于活动状态的卡片必须结算为对应终态,父请求成功结束但子 Agent 未报告终态时必须显示为失败而不是推断为完成。
- Agent 回复进行中锁定模型、专家角色、工作模式和 Runtime 定制选择器,并关闭已打开的上下文菜单;回复结束或停止后再恢复选择,避免界面状态与本次运行实际使用的上下文不一致。
- 普通工具在主会话和子 Agent 过程中使用同一轻量折叠记录:不显示外层“工具执行”标题、计数、背景和边框,保留无障碍区域名称。记录纵向左对齐,头部按内容收宽且不超过消息宽度;展开箭头在左,工具名作为主标题,有额外信息的单行摘要弱化显示。右侧以绿色对勾显示成功完成,以图标显示运行和待执行状态,提供悬停文字和无障碍名称;失败和可恢复状态保留文字。仅重复工具名和执行状态的已知 Runtime 模板摘要在头部与详情中均隐藏;刷新重试、审批等额外提示保留。默认折叠,状态和输出更新不改变用户的展开选择;折叠时露出一行错误,展开详情使用消息可用宽度、浅背景和左侧细线,按错误、结果、独立折叠的输入参数阅读。详情保留原始文本,完整 JSON 输入可缩进显示,复制仍使用收到的原文,并通过应用通知反馈。详情工具栏只保留复制按钮,文本默认自动换行,长内容在固定最大高度内滚动,不提供换行切换和展开全部按钮。
- OpenCode 主会话和子 Agent 的工具记录均保留 Runtime 提供的操作标题作为摘要,显示文件路径、命令或操作描述;本地 SDK 标题去除首尾空白后最多保留 240 字符,没有标题时沿用工具名回退规则。主会话工具失败或中断时保留已收到的标题;远端 ACP 继续在记录名称中显示其提供的标题。
- 工具取消后保留“已取消”文字,避免与失败图标混淆。
- Continue、DeepSeek Harness 和直连模型的工具名后显示单行操作摘要,内容提取、长度与完成态保留规则见[工具操作摘要](./docs/features/assistant-workbar/runtime-interactions.md#工具操作摘要)。
- Agent 回复进行中不能禁用普通消息发送。新消息与到期 Scheduled Task 共用 Conversation 级待发送队列。队列位于 Composer 容器之外、与输入框等宽并保持独立间距;有待发送项时直接显示无标题栏的极简列表,空队列不保留占位。每项固定为一行,按进入顺序显示轻量来源图标和截断摘要;普通消息使用消息图标,Scheduled Task 统一使用淡出时钟图标。
- 待发送项默认在当前回复结束后顺序执行。每项提供明确的“立即中断并插入”(空闲时为“立即运行”)和删除操作;前者先取消当前 Conversation 的活动执行,再将所选项提升为下一项,不允许同一 Conversation 并发写入时间线。
- 队列区域向上增长并设置有界滚动高度,不提供展开、收起或冗余计数标题。宽容器中的操作采用弱化的行内样式,不渲染成强调卡片或胶囊;窄容器中保留来源、摘要和图标操作并隐藏冗长按钮文字。区域、来源和每个操作必须有可读无障碍名称。队列异步错误进入应用通知,不在 Composer 内复制通知样式。
- 支持上下文状态的 Runtime 在输入区下方复用同一紧凑用量条;文案必须区分“本次模型调用”和“压缩后对话估算”。手动压缩仅在当前 Runtime 明确支持且没有活动回复时显示,作为元信息区左下角的浮动次操作,不参与输入区高度计算;元信息区始终预留稳定高度,切换 Runtime 不得让输入框上下位移。元信息区与窗口底部只保留紧凑安全留白,不形成额外空白区。进行中禁用重复操作,结果通过应用通知反馈。
- 已选择的工作模式在触发按钮中只显示 `Ask` 或 `Execute`;完整中文含义和说明保留在菜单选项、可访问名称及输入区下方的模式说明中。
- 宽度大于 `700px` 时,主工具栏的添加内容、Runtime、模式、会话选项与发送保持同一行。窄输入区按容器宽度换行,保留发送及停止入口;选项浮层不得超出输入区宽度,不再常驻独立的 Runtime 专属功能行。
- 输入框原生支持 `Ctrl+V`:文本直接进入草稿,图片转换为本次消息附件。文件选择由上传按钮承担,不再提供独立“读取剪贴板”按钮;默认工具栏也不提供“截取当前屏幕”和“选择应用窗口”入口,避免与系统粘贴、文件选择和后续工具执行重复。
- “Enter 发送 · Shift+Enter 换行 · Ctrl+V 粘贴图片或文本”等输入操作提示放在空输入框内部,作为主占位文案的次级行;不得在输入框下方单独占用第二行。输入框下方只保留一行当前模式、安全边界或全局快捷键说明。
- 输入框原生支持 `Ctrl+V`:文本直接进入草稿,系统截图和从文件管理器复制的一个或多个支持文件加入本次消息附件。文件粘贴与上传按钮共用读取、解析进度和数量限制;读取期间阻止重复导入及发送,错误就地关联输入框和附件按钮。具体类型、大小及批次失败规则见[聊天附件要求](./docs/features/document-processing/prd.md#321-聊天附件入口)。默认工具栏不提供独立“读取剪贴板”“截取当前屏幕”和“选择应用窗口”入口。
- “Enter 发送 · Shift+Enter 换行 · Ctrl+V 粘贴文件、图片或文本”等输入操作提示放在空输入框内部,作为主占位文案的次级行;不得在输入框下方单独占用第二行。输入框下方只保留一行当前模式、安全边界或全局快捷键说明。
- 输入操作提示不能替代表单的可访问名称,输入框始终保留持久的程序化标签。
- 草稿与消息中的附件操作使用紧凑图标按钮,并提供悬停和键盘焦点提示;文件名、解析状态与操作行的布局遵循[附件操作规则](./docs/features/document-processing/ui-design.md#12-附件操作菜单)。
- 空对话展示可执行的起始建议,发送失败保留输入并提供重试。
- 消息流的滚动范围必须使用已渲染消息的真实高度。长会话可以按批次只挂载最近消息并由用户
加载更早内容,但不得用固定的消息占位高度替代实际布局,避免推理、工具列表或长正文被
@@ -667,12 +713,17 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
### 13.4 智能心跳
- 使用 `dashboard` 壳层。
- 顶部先呈现运行状态、实际范围和主操作,再呈现指标、建议、历史与配置。
- 监督者入口使用 `supervisor` 壳层;智能心跳设置位于同一壳层内。
- 顶层页签为“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”,使用一套共享 PageTabs,窄窗口单行横向滚动。页签下不重复同名标题,也不保留空标题容器;所选回顾使用“监督结果”标题,历史结果不得标为最新。
- 工作回顾只展示统一监督结果、历史选择和紧凑的新回顾控件;结果保留自身范围、覆盖区间和生成时间,图谱读取同一结果 ID。自动监督集中展示当前计划、运行概览、指标、趋势、自动监督报告、记忆与行动建议及心跳运行审计;活动记录只展示统一执行活动、进度和详情。每页保留所属数据的一处刷新入口,范围与时间选择仅作用于新回顾请求。详细布局与阅读规则见[监督者 UI 设计](docs/features/conversation-supervision/ui-design.md#应用入口)。
- 监督者保留统一页面壳层;阅读与活动子面板分别复用 `--content-reading` 和 `--content-dashboard`,在壳层内左对齐。各面板的工具栏、历史导航、状态和结果共享宽度,不在全宽卡片内部再次居中窄正文。活动筛选按内容紧邻排列,阶段以文字状态行呈现。
- 范围明确区分 Global 与指定的一个或多个 Project;Global 与指定项目互斥,多项目选择使用
Checkbox,不能依靠进入页面时的当前项目推断。
- 状态卡片使用统一状态令牌,不只依赖颜色。
- 保留“运行概览 / 待处理建议 / 报告与记录 / 心跳计划”四个同级页面。
- 活动记录先显示运行状态、时间、范围及实际阶段,恢复操作紧邻进度。原始错误默认折叠,展开后在有界面板内滚动并可选择复制;配置归入次要详情。任务层级只展示可查询的记录,缺失阶段明确标注,不能用剩余来源为零推断发布成功。具体阶段与分页规则见[监督者活动设计](docs/features/conversation-supervision/ui-design.md#活动)。
- 自动监督的当前状态下显示计划列表,仅保留列表的一个创建按钮;无计划时使用列表的未配置说明,不叠加第二个空态或创建入口。创建与编辑复用同一份表单及共享 Modal 焦点、背景隔离和遮罩样式;未保存关闭须确认,保存失败保留草稿,编辑暂停计划不恢复运行。“执行记录”进入按该计划 ID 筛选的活动页,活动页不重复显示心跳审计列表。
- 设置只展示模型超时与并发、分页、分批和执行预算,不含计划、报告、审计或嵌套页签,不显示整页全局徽标或独立刷新行。
- 监督者的设置、帮助、空态及错误文案不得向终端用户展示内部路线图、交付进度或设计与实现缺项;只说明当前行为、适用范围和可执行的下一步。不得因此夸大为提供商全局限流或完整语义识别;真实的运行失败、超时、暂停、来源失效及恢复操作必须保留。
- 智能心跳菜单入口是完整配置的权威位置;任务中心和设置中心不得复制同一 CRUD 表单。
- 分区记忆协同与唤起条件尚未实现,不得显示占位入口或平行的记忆类型。
@@ -680,14 +731,19 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- 保留现有助手工作栏入口,首期在窄栏内适度完善,不先扩张成新的独立一级页面。
- 只展示 Task;普通 Conversation、Job、Run、工具步骤、Subagent 和心跳事项不独立占行。
- 每项显示名称、关联 Conversation、Global 或 Project 范围、Ask/Execute、状态、最近进展、
最近真实活动时间及需要关注信息。
- 范围固定为当前项目,不提供全局或所有项目入口;切换会话不筛掉本项目其他会话的任务。
- 顶部紧凑显示当前会话消息总数和 Agent 累计回复时间,按每轮请求区间累加,包含轮内工具和
审批等待,不计轮间空闲。历史缺失显示不完整提示,远程缺少可靠证据时不虚构用时。
- 每项显示名称、关联 Conversation、所属项目、Ask/Execute、状态、最近进展、
最近真实活动时间、累计执行时间及需要关注信息。任务用时汇总历次执行请求,不按父任务生命周期计算。
- 点击列表项打开关联 Conversation 并定位该 Task,不显示第二份内容载体。
- 需要关注、进行中、已暂停和已结束使用共享 `SegmentedControl`;窄栏不足时单行滚动。
- 项目任务标题右侧依次为“+”新建和筛选图标;图标按钮提供可访问名称,筛选按钮提供展开状态。
状态筛选默认收起且为全部;展开后用共享 `SegmentedControl` 提供全部、进行中、暂停、已结束,
窄栏不足时单行滚动。收起保留条件,非全部时图标高亮并显示条件。状态归类遵循[任务中心 PRD](docs/features/task-and-job/task-center-prd.md#5-交互)。
- 完整消息留在 Conversation;长错误、工具、审批和成果按 Task 关联到 Runtime、活动记录和
成果查看器,不撑高列表,也不显示 Job/Run 树。
- “新建定制任务”使用共享 Modal,明确选择当前或新 Conversation。聊天入口默认当前
Conversation,Task Center 入口默认新 Conversation;选择必须持续可见。
- “新建定制任务”使用共享 Modal,默认立即执行和当前 Conversation,可选择本项目其他可用
Conversation 或新建会话,选择必须持续可见。内容必填,名称可选并从内容生成;定时提供单次、每日和每周。
- 定时消息创建 Modal 使用关联 Conversation 的 Runtime 与 Ask/Execute 模式,不再提供独立
模式开关。未运行计划的详情显示会话当前模式,已运行任务显示该次执行记录的模式;
不得用旧计划字段表示下一次执行权限。不支持工具时不得静默降级。
@@ -698,32 +754,52 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- 默认视图按“项目 → 任务或会话 → 活动详情”组织,项目范围持续可见,任务或会话详情可以折叠。
- 任务或会话的综合状态以最近一次顶层请求对应的最终 Agent 结果为准;最终结果尚未产生时使用该请求的当前状态。中间工具或子专家的失败、取消和中断保留在活动详情中,但不得覆盖最终成功状态。
- 活动时间线按项目分组、按任务或会话建立横向轨道,所有轨道共享同一执行顺序并按事件时间排列,以带身份名称的节点表达用户、主 Agent、子专家、工具、审批、状态和并行关系。节点内使用 `U / G / S / T / A` 拉丁字母简称,节点下显示完整身份或名称;选择节点后显示所属会话、不可变范围快照和完整详情。
- 用量统计与活动记录分离,支持按项目、会话和模型切换统计维度,宽表格在独立容器内横向滚动。
- 用量统计与活动记录分离,支持按项目、会话和模型切换统计维度;按项目或会话分组时默认收起并显示汇总,展开后显示 Runtime / 模型子行,按模型分组时保持平铺。宽表格在独立容器内横向滚动。
- 活动状态筛选使用 `SegmentedControl`,不与页面页签混合。清空历史遵循破坏性操作政策。
### 13.7 魔法笔记
- “笔记 / 待办”属于同一工作台内的同级内容面板,使用 `PageTabs` 的 `segmented` 视觉变体,与模型设置的分段控件保持同一外观。
- 页签切换保留 `tablist`、`tab` 和 `tabpanel` 语义;待办状态仍使用独立的 `SegmentedControl`,不得与内容页签合并。
- 笔记编辑器保留 Quill 原生列表快捷输入:输入 `-`、`*`、`1.`、`[ ]` 或 `[x]` 后按空格,分别转换为项目符号、编号或待办清单。快捷输入只生成现有 Delta 富文本,不引入 Markdown 源码模式、第二套持久化格式或第三方 Markdown 插件;编辑区附近持续提供简短提示。
- 左侧笔记与待办列表在双栏布局中允许在 `180px` 至 `360px` 范围内通过指针或键盘调整,
也可从页面标题区隐藏和恢复;宽度与显示状态均记住用户选择。分隔条保留 `separator`
语义及方向键、Home、End 操作,窄窗口堆叠布局不显示拖拽分隔条。
- 左侧笔记列表依次显示标题、最多两行内容预览,以及同一行的记录数和本地化更新时间。记录数靠左、更新时间靠右,窄宽度下均保持单行省略,不挤压标题或扩大列表栏。
- 笔记总览与待办使用一个普通按钮切换,放在页头“新建笔记”按钮左边。按钮显示目标“切换到待办”或“切换到笔记”及对应图标,按内容宽度显示,不拉伸;窄窗口操作组可换行到标题下方,保留控件高度。
- 切换按钮不使用页签语义,切换后焦点回到同一个按钮;内容 section 持续提供当前列表的 `aria-label`。切换沿用草稿保护入口,待办状态仍使用独立的 `SegmentedControl`。
- 文字记录编辑器保留 Quill 原生列表快捷输入:输入 `-`、`*`、`1.`、`[ ]` 或 `[x]` 后按空格,分别转换为项目符号、编号或待办清单。快捷输入只生成现有 Delta 富文本,不引入 Markdown 源码模式、第二套文字记录持久化格式或第三方 Markdown 插件;新建文字记录的操作提示收进类型选择旁的 `InlineHelp`。
- 新记录可选择“文字 / 画布”;已保存画布提供分页阅读和“展开编辑画布”。画布使用 PeopleLib 的 Fabric 分页批注与 Quill 跨页正文,支持纸张模板、增删页、笔迹、高亮、整对象橡皮、选择及移动缩放旋转、浮动文字、图片和 PDF 底版。画布有固定页面边界;已保存记录、待办来源和编辑态默认容纳完整当前页及工具栏,高度随当前页实际显示尺寸变化,纵向由外层笔记流滚动。多页仍属于一条记录,通过页码翻页;窄窗口工具栏换行,纸张仅在自身视口内横向滚动,不撑宽外层页面。
- 画布通过“保存记录 / 保存修改”手动持久化,离开或切换记录类型前沿用未保存草稿确认。撤销/重做作用于当前批注页或 Quill 正文模式,增删页和 PDF 导入不属于全局撤销历史。删除非空批注页或 PDF 页需要在五秒内再次点击确认。
- 画布编辑与只读工具栏均提供缩小、当前百分比、放大和“适应宽度”。默认 100%,范围 25%–300%;加减每次调整 25 个百分点,点击百分比还原 100%。画布区域内 Ctrl/Command + 滚轮连续缩放,普通滚轮和 Shift 横滚保持原有方向与边界传递。适应宽度按实际视口计算,窗口或分栏调整后自动更新;翻页保留固定比例或适应宽度策略。缩放仅影响显示,整页高度与横向滚动范围随之变化,不新增内层纵向滚动,也不改变正文分页、保存内容、导出尺寸或未保存状态。控件沿用画布的主题、焦点环与提示,窄屏整组换行,适应宽度通过 `aria-pressed` 和选中样式表达状态。
- “导入 PDF”保留页面并提取原生文字供搜索与 AI 使用,扫描页不自动 OCR。“导出 PDF”将底版、正文和批注逐页合成为栅格 PDF,不保留可搜索文字层;当前 UI 无 PNG 下载按钮,页面 PNG 捕获仅作为程序接口提供。
- 画布新建与编辑态不显示常驻分析提示。操作放在编辑区右上角:新建与类型选择同排,顺序为“分析画布草稿、保存记录”;编辑放在记录头部,顺序为“分析画布草稿、取消、保存修改”。保存为最右侧主按钮,窄屏允许换行;底部不重复操作,新建不增加取消按钮。`immediate` 仍通过按钮手动分析,不随笔画自动请求;`after-save-auto` 在手动保存后分析。设置读取或页面捕获失败不阻断保存,保存成功后通过应用通知说明自动分析失败,可稍后重新分析;模型分析失败也不回滚已保存内容。
- 魔法笔记设置提供“发送画布页数”单选框,复用设置表单 `.field` 控件样式,范围 1~8,默认 1。标题同行的问号使用 `InlineHelp`,下拉框通过稳定的帮助 ID 关联展开的说明,交互遵循 6.14。说明包含当前页序前 N 页、不足时发送全部页、数量指单个画布页数而非记录数,以及完整保存与导出不受影响。修改后沿用设置保存反馈,下次分析生效。
- 草稿、已保存画布记录及来源为画布的待办,按默认模型的 `supportsImageInput` 使用所选前 N 页图像与文字,或只发送同一范围文字并标注“仅文字分析(未使用画布图像)”。所选页只有视觉内容的记录/草稿遇到文字模型时提示切换支持图像输入的默认模型,不声称已理解笔迹或布局。待办分析同时保留任务标题、说明和所选来源画布页的上下文。
- 画布文字评论在提取的 `plainText` 不变时保留;图像评论随画布内容或布局变化失效,在保存后自动评论模式下重新分析。草稿内容变化清除旧草稿分析,评论区显示实际使用的输入模式。存储和判定细节见[画布技术设计](./docs/features/magic-notes/technical-design.md#canvas-analysis-and-comments)。
- 默认进入笔记总览,不自动打开第一篇笔记。笔记以自适应卡片网格平铺,卡片显示标题、最多两行内容预览、记录数和本地化更新时间。点击笔记进入详情,详情不保留内部左列表;“返回总览”恢复搜索、滚动位置和入口焦点。新建入口在笔记总览与待办页可见,创建成功后进入笔记详情。
- 待办页是独立任务视图:搜索任务标题、说明或来源笔记,搜索框右侧同一工具栏放置“全部 / 未完成 / 已完成”筛选,显示匹配数与总数;任务按来源笔记分组。列表独立滚动,页头、搜索和筛选保持可见,窄屏工具栏空间不足时换行。直接勾选完成或恢复未完成,不自动选择任务;状态变化后按当前筛选更新列表。
- 待办内容容器宽于 `700px` 时,任务列表稳定在左侧,右侧显示当前任务说明、来源记录和 AI 评论;未选择时右侧提示选择任务。点击任务只更新右侧,不改变列表宽度、行位置或滚动位置,再次点击取消选择。AI 不增加第三栏。容器不超过 `700px` 时,按需详情替换同一内容区域,并提供“返回待办列表”;列表保持挂载,返回恢复滚动和任务行焦点,不将列表与详情上下堆叠。任务按钮提供 `aria-expanded` 和 `aria-controls`。
- 待办左右栏分割线支持拖拽,并记住列表宽度;键盘左右方向键每次调整 `16px`,Home/End 调整至边界。列表默认 `320px`、最小 `240px`,详情至少保留 `300px`;窄屏隐藏分割线,临时缩窄窗口不覆盖已保存宽度。每个来源笔记标题可独立收起或展开待办列表,保留标题与数量,通过 `aria-expanded` 和 `aria-controls` 表明状态;收起不清除右侧当前详情。
- 待办 AI 评论保留方向选择、流式输出、重新分析和已有结果;来源加载失败通过应用通知提示,并提供就地重试。从待办打开原笔记后,“返回待办”恢复任务选择、搜索、筛选和列表滚动;宽屏焦点返回任务行,窄屏返回待办详情中的返回按钮。
- 笔记详情主区按新到旧连续展示全部记录。打开笔记即在标题下、历史记录上显示空白输入区,保留文字/画布类型选择和保存操作,标题栏与索引均不提供“新增记录”按钮。新增保存成功后重建空白输入区,保留所选类型;自动分析结束也不转入旧记录编辑。旧记录通过显式编辑入口修改,保存使用同一 ID 和返回的修订号。点击索引或当前笔记内的 AI 来源只按 entry ID 滚动定位,不卸载编辑器、不清草稿、不询问离开。空白输入区离开不提示,返回总览、打开其他笔记或改为编辑另一条记录时仍保护未保存修改。
- 详情页的一级标题显示当前笔记已保存的标题,不显示模块说明;全局范围、返回导航和索引开关保持可见。原标题输入框使用正文大小,保留原有编辑、失焦保存与键盘操作,不形成第二个醒目标题。新建文字记录的输入区初始约四行,随内容自然增高,保留原有调整大小与滚动方式;此高度不作用于旧记录编辑器或画布。笔记内部列表、记录流、AI 区域和文字编辑区在精细指针设备上使用细原生滚动条,轨道透明;强制颜色与粗指针设备保留系统默认。说明、摘要和输入提示使用 `--font-body` 与 `--text-secondary`,时间和短标签保留 `--font-caption`。
- 左侧记录索引默认 `168px`,可拖动右边界在 `140–320px` 内调整栏宽,缩略图随列宽等比适配。开关独立位于详情标题左侧,提供 `aria-controls`、展开状态和动作提示;收起后完全移除索引列、分隔条、间隙、边框及轨道,不占宽度,隐藏内容不可聚焦。右侧 AI 评论独立显示、滚动和收起,不与索引共享页签。每条记录一个导航项:画布显示真实首页缩略图及总页数,文字显示摘要,两者均显示类型和时间。两侧分别记住桌面展开状态和宽度,共用原有布局偏好;宽布局分隔条共用样式及 `separator` 语义,方向键每次调整 `16px`,Home、End 跳到当前上下限。两侧宽度按可用空间协调限制,为主区保留至少 `300px`,窗口缩小时不覆盖保存的偏好。内容容器不超过 `800px` 时索引默认隐藏,由同一标题栏按钮开闭标题栏下方固定 `168px` 的抽屉,不显示调宽分隔条;标题栏自身保留控件高度,不保留轨道。选择记录或按 Escape 后关闭,Escape 将焦点返回开关。AI 移至底部,高度不超过可用布局的 40% 且最多 `280px`,不显示分隔条。
AI 评论的唯一显隐入口位于页面右上角,使用紧凑面板图标按钮;展开时应用共享选中样式,图标随显示或隐藏动作切换,并提供对应的工具提示、可访问名称、`aria-expanded` 和 `aria-controls`。评论面板内部不再显示重复的“AI 评论”标题或隐藏按钮行,保留区域的可访问名称、评论方向选择及各记录的分析操作。
- 笔记详情的内部记录索引使用透明底色,与主内容表面连续,仅保留细调宽分隔线。每条索引记录保留 `1px` 的 `--border-subtle` 淡边框,不加卡片底色或阴影,悬停使用轻微次级底色;选中记录使用淡蓝背景和左侧 `3px` 蓝色内侧标记,悬停时保留选中态。缩略图保留纸张边界,键盘焦点环与强制颜色模式选中边框保持可见。窄屏抽屉使用不透明的主内容表面,避免下层正文透出;深色主题沿用语义令牌。
- 笔记详情的标题、正文和记录流不再包裹额外的大外框,记录流容器不加内边距;缩略图列表顶部与正文区顶部对齐,左边界与索引栏左边界对齐,仅在右侧和底部保留 `8px` 留白,键盘焦点环向内绘制以避免滚动容器裁切。每条已保存记录使用一层 `1px` 的 `--border-subtle` 淡边框和 `--radius-control` 圆角,保留透明底色与记录间留白,不加阴影或头部下划线。记录编辑时保留输入区边框、页面安全边距及 AI 区域分隔。此规则仅作用于笔记详情,不改变卡片总览与待办独立布局。
- 画布新建、编辑和只读态统一保留 `12px` 内容间距;详情中已保存画布去掉预览外框和工具栏底色、下划线,保留灰色画布衬底与白色纸张,编辑态保留编辑器边框。记录头部操作按内容宽度排列,空间不足时自然换行。文字新建的保存区与编辑器属于同一卡片,空白编辑区使用最小高度,不随容器百分比高度拉长。文字工具栏图标、菜单和原生滚动条须跟随主题。
- 整篇笔记的置顶、取消置顶和删除放在卡片的“更多”菜单,标题编辑区不重复提供入口。按钮在悬停、键盘聚焦或菜单展开时显示,触屏设备常显;菜单沿用会话列表样式,通过 Portal 避免滚动区域裁切,支持方向键、Home、End 和 Escape。删除在菜单内二次确认并说明笔记标题及全部记录;打开菜单不进入详情。
- 左侧“魔法笔记”入口可显示未完成待办数量。数量为 0 时隐藏徽标,超过 99 时显示 `99+`,可访问名称仍说明精确数量;勾选、取消勾选以及 Agent / MCP 对笔记清单的写入都必须刷新数量。
- 未完成待办徽标的高度不得超过主导航图标;数量从 0 变为可见或再次隐藏时,主导航项行高和相邻菜单位置必须保持不变。
- 在待办面板勾选或取消勾选后,当前缓存中的源笔记正文必须立即同步对应清单状态,不得依赖离开工作区后重新加载。
- 当前记录草稿非空时,切换笔记、待办或内容面板必须先在编辑器旁就地确认;继续编辑时保留草稿并恢复编辑焦点,只有明确选择放弃后才切换。
- Agent / MCP 或其他入口成功写入笔记后,已打开的工作台自动更新笔记列表、当前详情、待办及来源记录,不增加刷新按钮。保留仍存在的笔记与待办选择、搜索和筛选,以及未保存的标题、输入区和记录编辑草稿;普通后台刷新不重建编辑器。
- 外部删除当前笔记时,无草稿则返回总览;有草稿则保留编辑区并提示先复制草稿。外部删除正在编辑的记录时,同样保留该编辑器并明确提示记录已删除;保存失败不得清空草稿。
- 当前记录草稿非空或标题尚未保存时,返回总览必须先就地确认;编辑另一条记录等会丢弃正文草稿的操作沿用同一保护。继续编辑时保留草稿并恢复编辑焦点,只有明确选择放弃后才切换。
- 保存成功后,以该修订的编辑器初始化并完成 flush 后的内容作为修改比较基线;没有新编辑时返回不再提示。编辑器归一化不算用户修改,后续输入、画布变更和保存失败仍保留离开保护;同修订的后台刷新不替换已建立的基线。
- 创建、保存、更新、删除和 AI 评论完成等短期结果进入应用级通知,不在编辑区或列表上方堆放页内通知。
- 标题或正文校验、删除确认、同步进度和可就地恢复的错误仍靠近对应编辑器或操作呈现。
### 13.8 设置中心
- 全页设置使用固定标题区、左侧分类导航和独立滚动的内容区。右上角关闭按钮是离开设置中心的稳定入口。
- 设置中心以整窗遮罩上的大 Modal 打开,保留底层工作区、会话草稿和助手工作栏,不切换一级页面。固定标题区和右上角关闭按钮,左侧分类与右侧内容各自滚动。关闭后返回原工作区并恢复入口焦点。
- 新建项目与项目设置弹窗固定标题、关闭按钮和底部操作区,仅表单内容滚动;滚动条不进入标题区,关闭按钮保留至少 40 × 40px 的点击区域。
- 项目弹窗通过 Portal 挂载到 `document.body`,避免侧栏内的弹窗与主窗口标题栏拖动区域重叠时,Electron 截获关闭按钮的鼠标输入。
- 左侧分类导航在宽屏使用 `220px`,中等窗口使用 `196px`,窄窗口转为横向滚动;纵向滚动条仅在内容溢出时占用右侧空间,不在左侧创建镜像预留,选项与左侧可见边界保持默认内距。分类标题使用正文级字号,分类说明使用辅助字号;右侧内容区在可用空间内流式伸缩,最大宽度使用 `standard` 壳层的 `960px`,不得以页面专属较窄宽度压缩表单。
- 全页设置标题区与双栏内容使用共享 `--page-gutter`,标题、分类导航和内容区在同一页面边距基线上;标题继续使用标准 `PageHeader` 的底部分隔线与下内距。固定标题和双独立滚动区域不改变这些一级页面壳层规则;模态设置保留自身外框与标题边界。
- Modal 最大尺寸为 `1200px × 900px`,常规外边距为 `24px`;窗口宽度不超过 `1020px` 或高度不超过 `720px` 时接近全屏,保留 `16px` 外边距。左侧分类宽度依次为 `220px`、`180px`(宽度不超过 `1020px`)、`120px`(不超过 `720px`);极窄窗口隐藏分类辅助说明,仍保留左右布局。内容区在可用空间内伸缩,最大宽度为 `960px`。
- 设置 Modal 通过 Portal 挂载到 `document.body`,复用共享背景隔离与焦点恢复机制。打开后聚焦关闭按钮,Tab 在弹窗内循环;Escape 先交由嵌套弹窗处理,设置自身的 Escape 与关闭按钮共用未保存确认,确认出现后再按 Escape 返回编辑。点击遮罩不关闭;保存、测试和清理数据期间禁止关闭。每次打开重新挂载设置,模型和消息通道等直达入口使用本次指定分类,普通入口默认打开 Agent Runtime。
- 设置中心不显示全局操作页脚,避免重复关闭入口和没有功能意义的整宽分隔线。
- 所有分类使用共享的 `SettingsCategoryHeader` 呈现分类标题、说明、错误与操作,不得在内容卡片内复制分类标题或创建页面专属操作栏。左侧分类名称与说明来自同一份分类定义,新增分类时不得分别维护导航和内容标题。
- 当前分类存在“保存”或“测试”等未提交配置操作时,统一放在分类页头右侧;主保存操作在最右侧,测试等次操作排列在其左侧。
@@ -737,18 +813,24 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- “平台功能 / 通用设置”同时提供默认开启的“在会话中渲染 HTML”持久 Switch。说明必须
明确该能力只生成禁用脚本、联网、表单提交和窗口操作的静态预览;保存成功后当前会话立即
更新,保存失败时保留最后确认状态。
- “平台功能”使用共享 `PageTabs` 区分“通用设置”“远程项目(技术预览)”和“魔法笔记”,默认进入通用设置。“远程项目”页将持久 Switch 与用户数据目录中的 Linux x64、Linux arm64、macOS arm64 Agent 包清单分成独立卡片;每张卡片与下载进度按平台和架构区分,不能把两个 arm64 目标合并。清单延迟到首次打开该页签时由 Main 完整校验,并读取所选更新源的小型签名目录,显示本地版本、在线最新兼容版本以及“有更新”“已是最新”“未下载”“校验失败”。打开页签或刷新不得自动下载 `.gbagent`;只有用户点击带目标版本的下载/更新按钮后才传输包。签名目录不可用时继续显示并允许导出已验证本地包,提供就地重试错误;清单不混入 Host 已安装状态、缓存路径或敏感签名细节。下载和导入期间保留上下文进度,成功进入全局通知视口。“魔法笔记”页提供默认开启的“显示未完成待办数量”持久 Switch,关闭后只隐藏左侧数量徽标,不改变待办数据或魔法笔记入口。全局模型下载源使用 `fieldset`、持久 `legend` 与整行可点击的原生 Radio 卡片;选中状态同时依靠 Radio、边框和背景表达,读取失败时不得用默认值伪装为已保存选择。
- “关于与更新”的更新源位于“启动时检查新版本”开关下方,常规宽度下将标签、原生单选下拉框和用途说明放在同一行,并复用设置表单的统一控件样式;关闭启动检查后仍可选择更新源,手动检查继续使用当前选择。选项显示“GitHub(默认)”和中性的“镜像节点”。该选择同时控制手动检查、启动时检查、下载页和 Agent 包在线下载,不显示底层服务商名称。
- “平台功能”使用共享 `PageTabs` 区分“通用设置”和“远程项目”,默认进入通用设置。“远程项目”页将持久 Switch 与用户数据目录中的 Linux x64、Linux arm64、macOS arm64 Agent 包清单分成独立卡片;每张卡片与下载进度按平台和架构区分,不能把两个 arm64 目标合并。清单延迟到首次打开该页签时由 Main 完整校验,并读取所选更新源的小型签名目录,显示本地版本、在线最新兼容版本以及“有更新”“已是最新”“未下载”“校验失败”。打开页签或刷新不得自动下载 `.gbagent`;只有用户点击带目标版本的下载/更新按钮后才传输包。签名目录不可用时继续显示并允许导出已验证本地包,提供就地重试错误;清单不混入 Host 已安装状态、缓存路径或敏感签名细节。下载和导入期间保留上下文进度,成功进入全局通知视口。全局模型下载源使用 `fieldset`、持久 `legend` 与整行可点击的原生 Radio 卡片;选中状态同时依靠 Radio、边框和背景表达,读取失败时不得用默认值伪装为已保存选择。
- “关于与更新”的更新源位于“启动时检查新版本”开关下方,常规宽度下将标签、同行帮助和原生单选下拉框放在同一行,用途说明收进 `InlineHelp`,并复用设置表单的统一控件样式;关闭启动检查后仍可选择更新源,手动检查继续使用当前选择。选项显示“GitHub(默认)”和中性的“镜像节点”。该选择同时控制手动检查、启动时检查、下载页和 Agent 包在线下载,不显示底层服务商名称。
- “关于与更新”在版本信息之后提供应用内反馈卡片。反馈对话框使用原生字段、明确隐私说明和一张可选截图;另提供默认关闭的原生 Checkbox,让用户主动附加最近桌面诊断摘要。Checkbox 附近必须持续说明摘要只含时间、组件、阶段、稳定错误码、错误类型和固定短消息,不含对话、Prompt、凭据、文件内容、路径、Provider 原始响应或远端 Agent 日志。勾选时为诊断摘要预留描述字符预算,超长草稿必须保留并显示字段错误,不得静默截断。失败时保留草稿、勾选状态和请求编号并提供就地重试,成功时在对话框内保留可复制编号而不重复发送全局通知;对话框遵循统一 Modal 的背景隔离、初始焦点、Tab 循环、Escape、焦点恢复和窄窗口布局。
- Agent Runtime 分类页头的“保存设置”同时保存 Runtime 基础配置与 Runtime 原生定制,不在原生定制卡片内提供第二个保存入口。原生定制存在未保存更改时持续显示状态和撤销入口;切换设置分类或 Runtime 不丢弃草稿,关闭设置中心前必须先保存或撤销。
- Agent Runtime 页面在低层程序与配置覆盖之外提供“能力与默认配置”区域。能力清单使用共享 `PageTabs`,按 Agents、Tools、Commands、Skills、MCP、Rules、Prompts、Resources、LSP、Formatters 和上下文 11 类单行滚动展示,一次只呈现当前分类的 `tabpanel`;清单只显示 Runtime 自有能力,不混入 GoodBuddy 分配的 Skills、临时 MCP 或 Continue 预设。Tools 必须独立于 Commands、LSP 和 Formatters,显示工具类型、来源及 Ask/Execute 可用性;清单状态必须区分完整、部分、不可用、仅连接和不支持,不能用进程连通性冒充清单可读。
- “能力与默认配置”只显示一个模块标题,刷新入口位于该标题右侧,能力状态压缩为一行并排在默认 Agent 或 Continue 预设编辑器之前;不得再复制“Runtime 原生能力”等同义标题、说明或状态结论。刷新只更新能力快照,不覆盖未保存的原生定制草稿。
- OpenCode 的默认 Agent 使用原生下拉选择;Continue 预设编辑器允许管理名称、说明、启用的 Rules 以及 Prompt 名称、说明和正文,并可展开查看原生 Rules 与启用预设 Rules 的最终合并顺序。持久启停仍使用共享 Switch,添加与删除使用明确按钮和可访问名称。
- 魔法笔记设置统一从应用中心对应行进入;笔记页和全局设置不再提供重复入口。“显示未完成待办数量”持久 Switch 只控制左侧数量徽标,不改变待办数据。旧入口开关绑定业务启用值,迁为“启用应用”并保留原值,常驻使用独立配置。默认开启仅适用于未配置布尔值,只有独立显隐偏好才映射到常驻。此迁移规则见[应用导航 PRD](./docs/features/application-tool-navigation/prd.md#fr-11-应用导航设置)。
- MCP 设置按“内置 MCP / 直连模型 / 自定义 MCP / 电脑控制”四个同级 `PageTabs` 组织。联网搜索保留在直连模型;内置浏览器归入内置 MCP,提供持久启停和 Runtime 分配,并明确说明其操作 GoodBuddy 隔离浏览器、不控制客户端已安装的浏览器,开启后可由已分配 Runtime 的 Execute 直接使用,不逐次询问。该开关只控制 Agent 工具访问,关闭时不得禁用浏览器工作栏中的用户手动导航、返回、刷新、停止加载、交互或关闭操作。尚未生效的命名浏览器配置不得显示在界面中,“电脑控制”只显示实际操作客户端电脑的能力。内置 MCP 卡片与 Skills 一样提供持久启停和 Runtime 分配;直连模型、GoodBuddy 管理的 OpenCode 与 Continue 默认选中且可调整,DeepSeek Harness 必须以置灰、未选择和“暂不支持”文案持续显示,不能呈现为可保存的分配。魔法笔记 MCP 的自身启停与平台功能依赖分别显示,依赖未开启时保留用户配置并说明当前不会加载。
- MCP Server 测试结果在同一展开卡片中分组显示 Tools、Prompts 和 Resources 的支持状态、数量与有界元数据;Prompt 参数标明必填项,Resource 只显示 URI、名称、类型和说明,不读取或渲染 Resource 内容。
- 内置 MCP 中的“图片生成与编辑”是自动管理例外:该行始终可见,标记“内置 · 自动管理”,不提供独立启停、分配保存、移除或凭据编辑。直连 Model、OpenCode、Continue、DeepSeek Harness 的分配只由已保存图片模型的“允许 AI 在会话中调用”设置推导;至少一个开启则全部勾选,没有开启模型则全部取消。使用禁用的原生 Checkbox、Runtime 标签及关联说明,保留可聚焦的“前往图片模型设置”导航;远程适用范围通过 GoodBuddy Agent 说明持续可见。Ask、离线、无工具能力或调用错误不改变勾选。图片模型默认关闭会话调用;默认生图模型独立于普通默认连接,关闭或删除后保留失效选择并提示改选,不自动回退。
### 13.9 文档解析设置
现有本地解析与本轮 HTTP 扩展的实现边界见[文档处理进度](./docs/features/document-processing/progress.md)。
跨场景入口、附件菜单、共享预览及选图返回规则由[文档处理 UI 设计](./docs/features/document-processing/ui-design.md)维护;
当前接线与存储行为见[生产实现说明](./docs/features/document-processing/implementation.md)。文档解析属于全局设置,不新增应用中心成员;本机推理监控只管理本地服务。
- 设置中心新增独立的“文档解析”分类,统一管理聊天附件、知识库导入以及后续文档审阅场景使用的提取、转换和 OCR 策略。OCR 不作为普通对话模型出现在“模型连接”中。
- 分类页头说明文档解析的跨场景作用,右侧依次显示“测试解析”和“保存设置”;保存位于最右侧。测试必须选择真实文件并执行实际解析,不能只检查模型文件或接口连通性。
- 页面首先显示原生解析、文档转换和 OCR 的运行状态,并明确当前可处理格式、回退能力与不可用原因。部分能力未配置时使用“部分可用”状态,不得把原生文本解析一并标记为失败。
@@ -762,9 +844,9 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- PDF 先读取文本层。仅当页面无有效文本、乱码比例过高或用户选择“始终 OCR”时渲染该页并识别;不得因为单页需要 OCR 而丢弃其他页面已经提取的可靠文本。
- DOCX、XLSX、PPTX 优先保留段落、单元格、公式、备注等原生语义。转换为 PDF 用于补充版面、页码、图表和图片理解,不作为唯一中间格式。
- DOC、XLS、PPT 等旧格式通过受控转换 Provider 生成新式 Office 文档和 PDF。转换子进程必须禁用宏和网络,限制输入、输出、内存、超时与临时目录,并在关闭或取消时清理。
- OCR 来源使用“本地模型 / 远程服务”互斥选择。选择本地后显示模型下载、模型下拉选择和本地运行参数;选择远程后显示 MinerU、PaddleOCR-VL 等服务连接配置。未实现的远程服务入口保持可读但禁用,不再增加与来源选择重复的“隐私与云端处理”授权区。
- OCR 来源使用“本地模型 / 远程服务”互斥选择,远程为 HTTP PaddleOCR-VL;MinerU 不在本轮范围。来源切换保留两套配置,未保存时禁止测试。来源配置、保存和测试规则见[文档解析设置设计](./docs/features/document-processing/ui-design.md#2-文档解析设置),不另加逐文件授权区。
- 用户配置并保存远程 OCR 服务即表示选择该处理路径,不再逐场景重复询问。界面仍须明确显示当前服务名称、处理范围和远程属性,API 密钥只保存在主进程加密设置中,未选中远程服务时不得上传文档。
- 解析结果使用统一文档结构,至少保留文档标题、来源格式、页码或工作表定位、正文块、置信度、处理方式和警告。聊天附件对结果做有界截断,知识库使用完整结果分块和索引。
- 解析结果使用统一文档结构,保留文档标题、来源格式、页码或工作表定位、正文块、处理方式和警告;置信度仅在解析器提供时显示,缺失不补造。聊天文档附件保留完整解析文本,超过暂存容量时报错;知识库使用完整结果分块和索引,具体要求见[文档处理 PRD](./docs/features/document-processing/prd.md#32-场景工作流)。普通图片默认走图片输入,显式 OCR、原图保存和文档选图遵循该功能的规划规则。
- 测试结果显示文件类型、页数、实际工作流、提取字数、OCR 页数、耗时和警告。测试文件不得自动进入聊天上下文或知识库。
## 14. 文案规则
@@ -814,7 +896,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- [ ] 将输入快捷键与附件提示置于空输入框内部,输入区下方保持单行说明。
- [ ] 最近对话迁移到 `standard`,统一搜索、范围、时间和删除行为。
- [ ] 知识库迁移到 `master-detail`,清除内联浅色样式并补齐窄窗口单面板流程。
- [x] 智能心跳使用 `dashboard`,保留概览、建议、轨迹和计划,并在计划中支持 Global / 多 Project 范围。
- [x] 监督者使用 `supervisor`,工作回顾读取统一监督结果;自动监督包含 Global / 多 Project 计划、概览、自动监督报告、建议与审计;活动记录显示统一执行,设置只保留算法参数。
- [x] 在现有工作栏中完善任务中心,统一范围、状态、最近进展和筛选,不新建平行任务平台。
- [ ] 活动记录迁移到 `dashboard`,统一导航、筛选和表格行为。
- [ ] 设置中心使用共享分类定义与 `SettingsCategoryHeader`,将保存与测试操作统一放到分类页头右侧,并把成功反馈接入应用通知。
+1 -1
View File
@@ -1,6 +1,6 @@
{
"formatVersion": 1,
"agentVersion": "0.11.24",
"agentVersion": "0.13.4",
"protocol": {
"major": 2,
"minor": 0
+18
View File
@@ -0,0 +1,18 @@
const { createHash } = require('node:crypto')
const { mkdirSync, readFileSync } = require('node:fs')
const { join, resolve } = require('node:path')
const { execFileSync } = require('node:child_process')
const { readRemoteRuntimeLock, resolveLockedRuntimeInput } = require('./remote-runtime-bundle.cjs')
const [directory, architecture, platform = 'linux'] = process.argv.slice(2)
if (!directory) throw new Error('Continue archive destination is required')
const input = resolveLockedRuntimeInput(readRemoteRuntimeLock(), architecture, platform, 'continue')
const destination = resolve(directory)
mkdirSync(destination, { recursive: true })
execFileSync('npm', ['pack', `${input.packageName}@${input.version}`, '--ignore-scripts',
'--registry=https://registry.npmjs.org', '--pack-destination', destination], { stdio: 'ignore' })
const archive = join(destination, input.archive)
if (`sha512-${createHash('sha512').update(readFileSync(archive)).digest('base64')}` !== input.integrity) {
throw new Error('Continue archive integrity mismatch')
}
process.stdout.write(`${archive}\n`)
+22 -4
View File
@@ -152,8 +152,8 @@ function assertPackageDescriptor(descriptor) {
descriptor.files.length > 50_000 ||
typeof descriptor.remoteRuntime !== 'object' ||
descriptor.remoteRuntime === null ||
descriptor.remoteRuntime.runtimeId !== 'opencode' ||
descriptor.remoteRuntime.provider !== 'opencode' ||
!['opencode', 'continue'].includes(descriptor.remoteRuntime.runtimeId) ||
descriptor.remoteRuntime.provider !== descriptor.remoteRuntime.runtimeId ||
!semanticVersionPattern.test(
descriptor.remoteRuntime.version ?? ''
) ||
@@ -164,6 +164,7 @@ function assertPackageDescriptor(descriptor) {
throw new Error('Agent package descriptor is invalid')
}
assertProtocol(descriptor.agentProtocol, 'Agent protocol')
assertAdditionalRuntimes(descriptor)
assertProtocol(
descriptor.remoteRuntime.protocol,
'Remote Runtime protocol'
@@ -592,8 +593,8 @@ function assertCatalog(catalog) {
entry.formatVersion !== 1 ||
entry.product !== 'GoodBuddy' ||
entry.component !== 'agent' ||
entry.remoteRuntime?.runtimeId !== 'opencode' ||
entry.remoteRuntime?.provider !== 'opencode' ||
!['opencode', 'continue'].includes(entry.remoteRuntime?.runtimeId) ||
entry.remoteRuntime?.provider !== entry.remoteRuntime?.runtimeId ||
!semanticVersionPattern.test(
entry.remoteRuntime?.version ?? ''
) ||
@@ -604,6 +605,7 @@ function assertCatalog(catalog) {
throw new Error('Agent catalog entry is invalid')
}
assertProtocol(entry.agentProtocol, 'Agent protocol')
assertAdditionalRuntimes(entry)
assertProtocol(
entry.remoteRuntime.protocol,
'Remote Runtime protocol'
@@ -634,6 +636,18 @@ function readVerifiedCatalog(catalogPath, signaturePath, registry) {
return catalog
}
function assertAdditionalRuntimes(value) {
if (value.additionalRuntimes === undefined) return
if (!Array.isArray(value.additionalRuntimes) || value.additionalRuntimes.length > 1) throw new Error('Invalid additional Runtimes')
for (const runtime of value.additionalRuntimes) {
if (!runtime || !['opencode', 'continue'].includes(runtime.runtimeId) || runtime.runtimeId === value.remoteRuntime.runtimeId ||
runtime.provider !== runtime.runtimeId || !semanticVersionPattern.test(runtime.version ?? '') || !/^sha256:[a-f0-9]{64}$/u.test(runtime.bundleDigest ?? '')) {
throw new Error('Invalid additional Runtime identity')
}
assertProtocol(runtime.protocol, 'Additional Runtime protocol')
}
}
function parseVersion(value) {
const match =
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?$/u
@@ -710,6 +724,10 @@ function createCatalog(options) {
...(options.darwinArm64Package ? [readPackageMetadata(options.darwinArm64Package, registry)] : [])
]
const [first, second] = packages
const additionalIdentity = descriptor => canonicalJson((descriptor.additionalRuntimes ?? []).map(({ bundleDigest: _digest, ...identity }) => { void _digest; return identity }))
if (packages.some(value => additionalIdentity(value.descriptor) !== additionalIdentity(first.descriptor))) {
throw new Error('Agent package matrix has inconsistent additional Runtimes')
}
const {
bundleDigest: _firstRuntimeDigest,
...firstRuntimeIdentity
+9
View File
@@ -38,6 +38,7 @@ function parseArguments(argv) {
'--platform',
'--node-archive',
'--opencode-archive',
'--continue-archive',
'--output-directory',
'--archive'
].includes(argument)
@@ -63,6 +64,7 @@ function parseArguments(argv) {
for (const key of [
'nodearchive',
'opencodearchive',
'continuearchive',
'outputdirectory',
'archive'
]) {
@@ -77,6 +79,7 @@ function parseArguments(argv) {
platform,
nodeArchive: resolve(options.nodearchive),
opencodeArchive: resolve(options.opencodearchive),
continueArchive: resolve(options.continuearchive),
outputDirectory: resolve(options.outputdirectory),
archive: resolve(options.archive)
}
@@ -160,6 +163,11 @@ async function buildCiAgentBundle(options) {
registry: signing.registry,
testSigningIdentity: identity
})
const continueRuntime = buildRuntimeBundle({
platform, runtimeId: 'continue', lock: runtimeLock, projectRoot, architecture: options.arch,
runtimeArchive: options.continueArchive, outputRoot: runtimeRoot,
registry: signing.registry, testSigningIdentity: identity
})
const assemble = (archive) =>
assembleAgentPackage({
platform,
@@ -170,6 +178,7 @@ async function buildCiAgentBundle(options) {
output: archive,
agentBundle: options.outputDirectory,
runtimeBundle: runtime.bundleDirectory,
additionalRuntimeBundles: [continueRuntime.bundleDirectory],
agentLock: lock,
runtimeLock,
registry: signing.registry,
+43 -6
View File
@@ -142,7 +142,7 @@ function packageFileMode(path) {
if (path.startsWith('agent/')) {
return expectedAgentManifestMode(path.slice('agent/'.length))
}
return /\/bin\/opencode$/u.test(path) ? '0755' : '0644'
return /(?:\/bin\/opencode|\/lib\/continue\/dist\/cn\.js)$/u.test(path) ? '0755' : '0644'
}
function stagePackagePayload(options) {
@@ -153,10 +153,13 @@ function stagePackagePayload(options) {
join(
staging,
'runtime',
'opencode',
options.runtimeId ?? 'opencode',
basename(options.runtimeBundle)
)
)
for (const runtime of options.additionalRuntimes ?? []) {
copyTree(runtime.bundleDirectory, join(staging, 'runtime', runtime.manifest.runtimeId, basename(runtime.bundleDirectory)))
}
for (const [value, destination] of [
[
options.registry,
@@ -420,13 +423,22 @@ function assembleAgentPackage(options) {
join(tmpdir(), `goodbuddy-agent-package-${target}-`)
)
try {
const additionalRuntimes = (options.additionalRuntimeBundles ?? []).map(directory => verifyRuntimeBundle(directory, {
platform, projectRoot, architecture, registry, lock: runtimeLock,
verificationEnvironment: options.testSigningIdentity ? 'test' : 'production'
}))
if (new Set([runtime, ...additionalRuntimes].map(value => value.manifest.runtimeId)).size !== additionalRuntimes.length + 1) {
throw new Error('Duplicate packaged Runtime')
}
stagePackagePayload({
staging,
registry,
agentLock,
runtimeLock,
agentBundle: resolve(options.agentBundle),
runtimeBundle: runtime.bundleDirectory
runtimeBundle: runtime.bundleDirectory,
runtimeId: runtime.manifest.runtimeId,
additionalRuntimes
})
const payload = payloadFiles(staging)
const initial = {
@@ -447,6 +459,10 @@ function assembleAgentPackage(options) {
bundleDigest: runtime.manifest.bundleDigest,
protocol: runtime.manifest.protocol
},
...(additionalRuntimes.length ? { additionalRuntimes: additionalRuntimes.map(({ manifest }) => ({
runtimeId: manifest.runtimeId, provider: manifest.provider, version: manifest.runtimeVersion,
bundleDigest: manifest.bundleDigest, protocol: manifest.protocol
})) } : {}),
contentDigest: `sha256:${'0'.repeat(64)}`,
files: payload.files
}
@@ -510,6 +526,7 @@ function buildAgentPackage(options) {
: { signingIdentity })
})
const runtime = buildRuntimeBundle({
runtimeId: options.runtimeId,
platform: options.platform ?? 'linux',
projectRoot,
architecture: options.architecture,
@@ -521,6 +538,16 @@ function buildAgentPackage(options) {
? { testSigningIdentity: options.testSigningIdentity }
: { signingIdentity })
})
const additionalRuntimeBundles = []
if (!options.runtimeId || options.runtimeId === 'opencode') {
if (!options.continueArchive) throw new Error('Default Agent package requires the locked Continue archive')
additionalRuntimeBundles.push(buildRuntimeBundle({
runtimeId: 'continue', platform: options.platform ?? 'linux', projectRoot,
architecture: options.architecture, runtimeArchive: options.continueArchive, outputRoot: runtimeRoot,
registry, ...(options.runtimeLock ? { lock: options.runtimeLock } : {}),
...(options.testSigningIdentity ? { testSigningIdentity: options.testSigningIdentity } : { signingIdentity })
}).bundleDirectory)
}
return assembleAgentPackage({
...options,
projectRoot,
@@ -529,7 +556,8 @@ function buildAgentPackage(options) {
? {}
: { signingIdentity }),
agentBundle,
runtimeBundle: runtime.bundleDirectory
runtimeBundle: runtime.bundleDirectory,
additionalRuntimeBundles
})
} finally {
rmSync(agentRoot, { recursive: true, force: true })
@@ -555,6 +583,9 @@ function parseArguments(argv) {
'--minimum-desktop-version',
'--node-archive',
'--runtime-archive',
'--runtime-id',
'--continue-archive',
'--continue-bundle',
'--agent-bundle',
'--runtime-bundle',
'--output'
@@ -579,11 +610,15 @@ function parseArguments(argv) {
throw new Error('Agent package arguments are incomplete')
}
const common = {
runtimeId: options.runtimeId ?? 'opencode',
platform: options.platform ?? 'linux',
architecture: options.arch,
minimumDesktopVersion: options.minimumDesktopVersion,
output: resolve(options.output)
}
if (!['opencode', 'continue'].includes(common.runtimeId)) {
throw new Error('Unsupported Agent package Runtime')
}
targetName(common.architecture, common.platform)
if (command === 'build') {
if (!options.nodeArchive || !options.runtimeArchive) {
@@ -593,7 +628,8 @@ function parseArguments(argv) {
command,
...common,
nodeArchive: resolve(options.nodeArchive),
runtimeArchive: resolve(options.runtimeArchive)
runtimeArchive: resolve(options.runtimeArchive),
continueArchive: options.continueArchive ? resolve(options.continueArchive) : undefined
}
}
if (!options.agentBundle || !options.runtimeBundle) {
@@ -603,7 +639,8 @@ function parseArguments(argv) {
command,
...common,
agentBundle: resolve(options.agentBundle),
runtimeBundle: resolve(options.runtimeBundle)
runtimeBundle: resolve(options.runtimeBundle),
additionalRuntimeBundles: options.continueBundle ? [resolve(options.continueBundle)] : []
}
}
+68 -32
View File
@@ -49,6 +49,17 @@ const profileDigests = Object.freeze({
"sha256:ac7696abe504bbea444d8ad80dd8faa96437e6dbb27ed987ce46e1045f3dd365",
acp: "sha256:0e3764ab897258bc0234162357c2eefc3faeb9918080ae81b60e2084ed7996a6",
});
function runtimeProfileDigests(runtimeId) {
if (runtimeId === 'opencode') return profileDigests;
if (runtimeId !== 'continue') throw new Error('Unsupported Runtime');
return {
adapter: `sha256:${sha256('GoodBuddy Continue HTTP ACP facade v1')}`,
acp: `sha256:${sha256(canonicalJson({ protocolVersion: 1, capabilities: {
loadSession: true, mcpCapabilities: { http: true }, promptCapabilities: { image: true },
sessionCapabilities: { resume: {}, close: {} }
} }))}`
};
}
const fixedLimits = Object.freeze({
// Zero leaves prompt duration to its requested deadline and cancellation.
maximumPromptRuntimeMilliseconds: 0,
@@ -365,9 +376,9 @@ function validateManifestShape(manifest) {
if (
manifest?.formatVersion !== 2 ||
manifest.product !== "GoodBuddy" ||
manifest.runtimeId !== "opencode" ||
manifest.runtimeVersion !== "1.18.29" ||
manifest.provider !== "opencode" ||
!['opencode', 'continue'].includes(manifest.runtimeId) ||
manifest.runtimeVersion !== (manifest.runtimeId === 'continue' ? '1.5.47' : '1.18.29') ||
manifest.provider !== manifest.runtimeId ||
!(
manifest.platform === "linux" ||
(manifest.platform === "darwin" && manifest.architecture === "arm64")
@@ -396,10 +407,10 @@ function validateManifestShape(manifest) {
throw new Error("Runtime manifest limits contract is invalid");
}
if (
manifest.entrypoint?.identity !== "opencode-acp" ||
manifest.entrypoint.path !== "bin/opencode" ||
manifest.entrypoint?.identity !== `${manifest.runtimeId}-acp` ||
manifest.entrypoint.path !== (manifest.runtimeId === 'continue' ? 'lib/continue/dist/cn.js' : 'bin/opencode') ||
!/^[a-f0-9]{64}$/u.test(manifest.entrypoint.sha256) ||
JSON.stringify(manifest.entrypoint.argvPrefix) !== JSON.stringify(["acp"])
JSON.stringify(manifest.entrypoint.argvPrefix) !== JSON.stringify(manifest.runtimeId === 'continue' ? [] : ['acp'])
) {
throw new Error("Runtime manifest entrypoint is invalid");
}
@@ -436,13 +447,14 @@ function assertManifestMatchesLock(
architecture,
platform = "linux",
) {
const expected = lock.runtimes.opencode;
const expected = lock.runtimes[manifest.runtimeId];
if (!expected) throw new Error('Runtime is not locked');
const target =
expected.targets[
platform === "linux" ? architecture : targetName(architecture, platform)
];
if (
manifest.runtimeId !== "opencode" ||
manifest.runtimeId !== expected.provider ||
manifest.runtimeVersion !== expected.version ||
manifest.provider !== expected.provider ||
manifest.platform !== platform ||
@@ -457,8 +469,8 @@ function assertManifestMatchesLock(
JSON.stringify(expected.allowedEnvironmentNames) ||
manifest.protocol?.major !== expected.protocol.major ||
manifest.protocol?.minor !== expected.protocol.minor ||
manifest.adapterDigest !== profileDigests.adapter ||
manifest.acpCapabilitiesDigest !== profileDigests.acp
manifest.adapterDigest !== runtimeProfileDigests(manifest.runtimeId).adapter ||
manifest.acpCapabilitiesDigest !== runtimeProfileDigests(manifest.runtimeId).acp
) {
throw new Error(
"Runtime manifest does not match the locked OpenCode profile",
@@ -572,7 +584,7 @@ function verifyBundleDirectory(bundleDirectoryInput, options = {}) {
) {
throw new Error("Runtime entrypoint payload is missing");
}
assertElfArchitecture(
if (manifest.runtimeId === 'opencode') assertElfArchitecture(
join(bundleDirectory, ...manifest.entrypoint.path.split("/")),
architecture,
platform,
@@ -584,8 +596,8 @@ function verifyBundleDirectory(bundleDirectoryInput, options = {}) {
};
}
function lockedArchive(lock, architecture, archivePath, platform = "linux") {
const input = resolveLockedRuntimeInput(lock, architecture, platform);
function lockedArchive(lock, architecture, archivePath, platform = "linux", runtimeId = 'opencode') {
const input = resolveLockedRuntimeInput(lock, architecture, platform, runtimeId);
if (!archivePath || !existsSync(archivePath)) {
throw new Error(`Locked OpenCode archive is required: ${input.archive}`);
}
@@ -603,14 +615,15 @@ function lockedArchive(lock, architecture, archivePath, platform = "linux") {
`Runtime archive integrity mismatch for ${targetName(architecture)}`,
);
}
return lock.runtimes.opencode.targets[
return lock.runtimes[runtimeId].targets[
platform === "linux" ? architecture : targetName(architecture, platform)
];
}
function resolveLockedRuntimeInput(lock, architecture, platform = "linux") {
function resolveLockedRuntimeInput(lock, architecture, platform = "linux", runtimeId = 'opencode') {
targetName(architecture, platform);
const runtime = lock.runtimes.opencode;
const runtime = lock.runtimes[runtimeId];
if (!runtime) throw new Error('Runtime is not locked');
const target =
runtime.targets[
platform === "linux" ? architecture : targetName(architecture, platform)
@@ -619,7 +632,7 @@ function resolveLockedRuntimeInput(lock, architecture, platform = "linux") {
packageName: target.package,
version: runtime.version,
integrity: target.integrity,
archive: `${target.package}-${runtime.version}.tgz`,
archive: `${target.package.replace('@', '').replace('/', '-')}-${runtime.version}.tgz`,
};
}
@@ -636,24 +649,24 @@ function createManifest(bundleDirectory, metadata) {
path,
size: statSync(filePath).size,
sha256: sha256FileSync(filePath),
mode: path === "bin/opencode" ? "0755" : "0644",
mode: path === metadata.runtime.entrypoint ? "0755" : "0644",
};
});
const entrypoint = files.find((file) => file.path === "bin/opencode");
const entrypoint = files.find((file) => file.path === metadata.runtime.entrypoint);
if (!entrypoint) {
throw new Error("Runtime entrypoint is missing");
}
const initial = {
formatVersion: 2,
product: "GoodBuddy",
runtimeId: "opencode",
runtimeId: metadata.runtime.provider,
runtimeVersion: metadata.runtime.version,
provider: metadata.runtime.provider,
platform: metadata.platform ?? "linux",
architecture: metadata.architecture,
signingKeyId: metadata.signingKeyId,
bundleDigest: `sha256:${"0".repeat(64)}`,
adapterDigest: profileDigests.adapter,
adapterDigest: runtimeProfileDigests(metadata.runtime.provider).adapter,
sourcePackage: metadata.sourcePackage,
entrypoint: {
identity: metadata.runtime.entrypointIdentity,
@@ -664,15 +677,15 @@ function createManifest(bundleDirectory, metadata) {
files,
licenses: [
{
package: "opencode-ai",
package: metadata.runtime.provider === 'continue' ? '@continuedev/cli' : 'opencode-ai',
version: metadata.runtime.version,
spdx: "MIT",
path: "licenses/opencode-MIT.txt",
spdx: metadata.runtime.provider === 'continue' ? 'Apache-2.0' : 'MIT',
path: metadata.runtime.provider === 'continue' ? 'licenses/continue-Apache-2.0.txt' : 'licenses/opencode-MIT.txt',
},
],
allowedEnvironmentNames: metadata.runtime.allowedEnvironmentNames,
protocol: metadata.runtime.protocol,
acpCapabilitiesDigest: profileDigests.acp,
acpCapabilitiesDigest: runtimeProfileDigests(metadata.runtime.provider).acp,
limits: fixedLimits,
};
return {
@@ -685,11 +698,11 @@ function defaultOutputRoot(projectRoot = root) {
return join(projectRoot, ".remote-runtime-resources");
}
function bundleDirectory(outputRoot, architecture, digest, platform = "linux") {
function bundleDirectory(outputRoot, architecture, digest, platform = "linux", runtimeId = 'opencode') {
return join(
outputRoot,
targetName(architecture, platform),
"opencode",
runtimeId,
digest.slice("sha256:".length),
);
}
@@ -699,12 +712,14 @@ function buildRuntimeBundle(options) {
const architecture = options.architecture;
const platform = options.platform ?? "linux";
const lock = options.lock ?? readRemoteRuntimeLock(projectRoot);
const runtime = lock.runtimes.opencode;
const runtimeId = options.runtimeId ?? 'opencode';
const runtime = lock.runtimes[runtimeId];
const sourcePackage = lockedArchive(
lock,
architecture,
options.runtimeArchive,
platform,
runtimeId,
);
const registry = options.registry ?? readTrustedKeyRegistry(projectRoot);
const signingIdentity =
@@ -730,6 +745,7 @@ function buildRuntimeBundle(options) {
sync: true,
strict: true,
filter(path) {
if (runtimeId === 'continue') return path === 'package/package.json' || path.startsWith('package/dist/') || path === 'package/LICENSE';
return (
path === "package/package.json" || path === "package/bin/opencode"
);
@@ -753,6 +769,15 @@ function buildRuntimeBundle(options) {
"OpenCode source package version does not match the lock",
);
}
if (runtimeId === 'continue') {
mkdirSync(join(staging, 'lib'), { mode: 0o700 });
cpSync(join(extracted, 'package'), join(staging, 'lib', 'continue'), { recursive: true });
for (const file of listFiles(staging)) chmodSync(file, 0o644);
chmodSync(join(staging, runtime.entrypoint), 0o755);
mkdirSync(join(staging, 'licenses'), { mode: 0o700 });
// The pinned CN package declares Apache-2.0 but omits the license text.
copyFileSync(join(projectRoot, 'node_modules', '@agentclientprotocol', 'sdk', 'LICENSE'), join(staging, 'licenses', 'continue-Apache-2.0.txt'));
} else {
mkdirSync(join(staging, "bin"), { mode: 0o700 });
copyFileSync(
join(extracted, "package", "bin", "opencode"),
@@ -780,6 +805,7 @@ function buildRuntimeBundle(options) {
join(staging, "licenses", "opencode-MIT.txt"),
);
chmodSync(join(staging, "licenses", "opencode-MIT.txt"), 0o644);
}
const manifest = createManifest(staging, {
platform,
architecture,
@@ -811,6 +837,7 @@ function buildRuntimeBundle(options) {
architecture,
manifest.bundleDigest,
platform,
runtimeId,
);
mkdirSync(dirname(destination), { recursive: true, mode: 0o700 });
if (existsSync(destination)) {
@@ -849,6 +876,8 @@ function buildRuntimeBundle(options) {
function createRuntimeArchive(bundleDirectoryInput, archivePath) {
const directory = resolve(bundleDirectoryInput);
const manifest = readJson(join(directory, manifestFileName), 'Runtime manifest');
const modes = new Map(manifest.files.map(file => [file.path, parseInt(file.mode, 8)]));
const entries = listFiles(directory)
.map((filePath) => posixRelative(directory, filePath))
.sort(compareUtf8);
@@ -862,6 +891,9 @@ function createRuntimeArchive(bundleDirectoryInput, archivePath) {
portable: true,
noPax: true,
mtime: new Date(0),
onWriteEntry(entry) {
if (entry.stat) entry.stat.mode = modes.get(entry.path) ?? 0o644;
},
},
entries,
);
@@ -896,6 +928,8 @@ function importRuntimeArchive(archivePath, options) {
outputRoot,
architecture,
verified.manifest.bundleDigest,
verified.manifest.platform,
verified.manifest.runtimeId,
);
mkdirSync(dirname(destination), { recursive: true, mode: 0o700 });
if (existsSync(destination)) {
@@ -917,8 +951,8 @@ function importRuntimeArchive(archivePath, options) {
}
}
function findOnlyBundle(outputRoot, architecture) {
const runtimeRoot = join(outputRoot, targetName(architecture), "opencode");
function findOnlyBundle(outputRoot, architecture, runtimeId = 'opencode') {
const runtimeRoot = join(outputRoot, targetName(architecture), runtimeId);
const entries = existsSync(runtimeRoot)
? readdirSync(runtimeRoot, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
@@ -954,6 +988,7 @@ function parseArguments(argv) {
const allowed = new Set([
"--arch",
"--runtime-archive",
"--runtime-id",
"--archive",
"--output-root",
"--bundle",
@@ -997,6 +1032,7 @@ function main(argv = process.argv.slice(2)) {
if (options.command === "build") {
const verified = buildRuntimeBundle({
architecture: options.arch,
runtimeId: options.runtimeId,
runtimeArchive: resolve(options.runtimeArchive ?? ""),
outputRoot,
});
@@ -1006,7 +1042,7 @@ function main(argv = process.argv.slice(2)) {
root,
"dist",
"remote-runtime-bundles",
`goodbuddy-opencode-${targetName(options.arch)}.tar`,
`goodbuddy-${options.runtimeId ?? 'opencode'}-${targetName(options.arch)}.tar`,
);
createRuntimeArchive(verified.bundleDirectory, archivePath);
console.log(
@@ -1027,7 +1063,7 @@ function main(argv = process.argv.slice(2)) {
verifyBundleDirectory(
options.bundle
? resolve(options.bundle)
: findOnlyBundle(outputRoot, options.arch),
: findOnlyBundle(outputRoot, options.arch, options.runtimeId),
{ architecture: options.arch },
);
console.log(`Remote Runtime bundle verified: ${targetName(options.arch)}`);
@@ -425,9 +425,8 @@ Renderer 只通过显式 IPC 列出、加入、删除、提升、释放和接收
```text
任务中心
├─ 需要关注
├─ 进行中
├─ 已暂停
├─ 暂停
└─ 已结束
└─ 打开任务自身
@@ -0,0 +1,101 @@
# 本机专用推理架构与任务控制评估
## 记录范围与状态
评估日期:2026-09-18。本文记录 ASR、OCR、内置 embedding 的执行边界、任务控制缺口和后续优化依据,供语音输入、文档解析、知识检索与索引共同使用。
现状依据本次工作区源码核对,包含尚未提交的变更,不等同于已发布版本或实机验收结论。本次仅整理文档,未运行引擎性能测试、复现故障或实施迁移。下文“建议”是评估方向,“待办”均未在本次实施,也没有约定交付日期。
产品 Task、Job、Run 的定义沿用 [Task 与 Job 领域模型](../features/task-and-job/task-and-job-model.md)。本文的“推理任务”指一次引擎请求及其执行记录,不自动等同于产品 Task,不据此新增产品对象、持久化日志或全局调度器。本机推理管理的现有界面和接入清单见[应用导航技术设计](../features/application-tool-navigation/technical-design.md#111-本机推理任务与执行服务);本文负责跨功能架构评估及优化待办。
## 当前执行边界
| 能力 | 源码现状 | 所有权与释放 | 评估建议 |
| --- | --- | --- | --- |
| ASR | 录音结果就绪后提交完整 PCM;Main 创建 `node:worker_threads` Worker,以 sherpa-onnx WASM 执行离线识别;服务一次只接受一个请求 | 每请求创建识别器;超时或 abort 调用并等待 `worker.terminate()`;正常结果路径的清理确认存在缺口,见 TC-4 | 保留请求级 Worker,适合间歇语音输入;常驻化需流式需求或测量支持 |
| OCR | Main 排队后发往主窗口 Renderer;Renderer 复用 Web Worker,运行 `ppu-paddle-ocr/web` 与 `onnxruntime-web` WASM;文档串行处理,PDF 逐页处理 | Renderer 模块持有 Worker;活动请求取消或单页超时会终止 Worker;无活动和排队请求时空闲 60 秒释放 | 当前保留 Web Worker;独立于 UI 的持续任务可评估专用后台 Renderer 加 Worker |
| 内置 embedding | Main 按模型目录共享引用计数 Broker;transport 使用 `utilityProcess.fork`,Granite tokenizer 和 ONNX session 首次请求懒加载并复用 | 多使用方共享进程;最后一个 provider 释放时 shutdown;也支持显式启停,停止等待 child exit | 保留共享 utilityProcess;优先核对取消后的容量回收与加载失败重试 |
### ASR 的证据与边界
[App.tsx](../../src/renderer/src/App.tsx) 在 `recording.result` 完成后调用 `speech.transcribe`;[录音模块](../../src/renderer/src/speech-recognition.ts)负责采样与重采样。[SpeechTranscriptionService](../../src/main/speech/speech-transcription-service.ts) 的 `active.size` 拒绝并发识别,`runSherpaTranscription` 为每次请求创建 Worker,转移音频缓冲区,使用 `createOfflineRecognizer` 同步 decode。Worker 创建后的识别超时为 120 秒,不包含此前模型解析的等待时间。
[package.json](../../package.json) 固定 `sherpa-onnx` 为 1.13.4;本次同时核对已安装包的 `index.js`,其入口加载 `sherpa-onnx-wasm-nodejs.js`。因此这里记录的是 Node Worker 内的 WASM 路径。Node Worker 与 Main 共享 OS 进程,线程隔离不能视为独立进程故障边界。
Worker 先 `postMessage`,再在 `finally` 中调用 `stream.free()` 和 `recognizer.free()`。Main 收到结果后以 `finish(..., false)` 结算、移除监听和超时,不等待 Worker exit;随后 `active` 删除请求,推理记录转为终态。这证明结果完成与资源清理确认之间存在记账间隙,但尚未证明实际泄漏或清理耗时。
### OCR 的浏览器依赖
[OCR Worker](../../src/renderer/src/document-ocr-worker.ts) 显式选择 WASM provider、`canvas-native` 图像处理,当前 `ort.env.wasm.numThreads = 1`。PDF 路径使用 `pdfjs-dist`、`OffscreenCanvas` 和 `convertToBlob`。本次核对的依赖链为 `ppu-paddle-ocr/web/platform.web.js` 到 `ppu-ocv/canvas-web`;后者的 `platform/web.js` 使用 `createImageBitmap` 解码图片。包依赖以 [package-lock.json](../../package-lock.json) 为准,依赖升级后需重新核对此结论。
这些能力与浏览器执行环境有关。直接迁到 Node utilityProcess,需要另行验证图像解码、Canvas、PDF 渲染及打包资源路径,不能只替换进程创建 API 就宣称等价。
[DocumentOcrBroker](../../src/main/document-ocr-broker.ts) 最多保留 4 个 pending 请求,一次派发一个;总超时从派发时开始,不覆盖 Main 排队等待。其预算为 `min(10 分钟, max(单页预算, 60 秒 + 单页预算 × 页数))`。[Renderer bridge](../../src/renderer/src/document-ocr-bridge.ts) 另有串行 Promise 队列,Worker 就绪超时为 120 秒,就绪后按单页预算计时,并在进度消息到达时重置单页计时器。Main 的 60 秒启动预算与 Renderer 的 120 秒就绪等待未对齐,短文档冷启动时 Main 有机会先判超时。
[Main 初始化](../../src/main/index.ts) 将 Broker 绑定到 `mainWindow`;[Renderer 入口](../../src/renderer/src/main.tsx) 安装 bridge。Broker 派发前仅检查窗口销毁等条件,没有等待 bridge 就绪的握手,也没有在该派发链路中处理 bridge 就绪丢失。Renderer 内的 Worker `ready` 只确认模型初始化,不能证明 Main 发请求时 Renderer 已安装 IPC 监听。窗口重载、Renderer 崩溃及启动时监听尚未安装的影响应按 TC-3 复现。
来源请求可以取消 OCR;已派发请求取消时 Main 发送取消消息并立即结算,然后派发下一项,未等待 Renderer 清理确认。Renderer 对当前活动请求的取消会终止共享 Worker。管理页因此没有将该操作作为安全的单任务取消能力;手动释放也会拒绝活动或排队请求。
### Embedding 的共享执行与取消
[Main provider 工厂](../../src/main/index.ts) 按模型目录维护 `sharedEmbeddingBrokers` 及使用计数;[transport](../../src/main/knowledge/embedding-utility-transport.ts) 以 spawn 确认进程启动。[Granite 引擎](../../src/main/knowledge/granite-embedding-engine.ts) 缓存 `loadTask`,在首次请求时创建 tokenizer 和 WASM ONNX session。进程已启动不等于模型已加载;当前没有为模型加载完成提供独立服务状态。
[Broker](../../src/main/knowledge/embedding-inference-broker.ts) 默认请求超时为 120 秒,计时在 transport 就绪后建立 pending 时开始。调用方取消或超时会先返回错误,同时发送 cancel,保留 pending 和推理执行记录,等待结果、`CANCELLED` 或进程退出。保留容量避免把仍在计算的任务当作已释放。
[执行端](../../src/main/knowledge/embedding-inference-worker.ts) 收到 cancel 后设置 AbortSignal;Granite 在加载、输入构造和 `session.run` 前后检查取消,但没有将可抢占的取消句柄传入 `session.run`。若该调用一直不返回,取消检查和确认就无法完成。Broker 的 `cancelPending` 会清除原计时器,当前没有针对该 pending 的后续有界清理升级,因此持续挂起可一直占用在途容量。此为源码可达的条件风险,真实引擎挂起尚未复现。
`loadTask ??= this.loadFiles(...)` 也会缓存被拒绝的加载 Promise;同一引擎实例后续请求复用同一个失败,不能通过再次请求重新加载。重建进程会建立新引擎;是否增加实例内重试及其清理方式,按 TC-2 验证后确定。
## 架构建议与触发条件
ASR 继续按录音请求创建 Worker。对低频、间歇输入,每次释放模型能避免长期占用;本次没有冷启动与常驻成本数据,不能量化收益。只有连续流式识别成为真实需求,或高频请求测出重复加载已影响目标延迟,才评估常驻 utilityProcess。届时需比较加载成本、暖请求延迟、空闲工作集和取消时延,并重新设计音频流与识别器复用;单纯常驻不会把当前离线 API 变成流式识别。
OCR 保留 Web Worker。如果文档任务需要在主 UI 重载或不可用时继续执行,优先评估由 Main 持有的专用后台 Renderer 加 Worker,以保留浏览器图像/PDF 能力。该方案仍是候选,需要完成以下设计与实机验证后才可决定迁移:
- 保持现有 sandbox、context isolation、禁用 Node integration 的边界;通过专用 preload 暴露必要方法,并沿用可信 sender 与输入校验。当前 IPC 若限定主窗口 sender,需要明确新增宿主的受信身份及响应归属。
- 分别确认后台 Renderer 的 IPC 监听就绪与 Worker 模型就绪;定义重载、崩溃、启动失败及应用退出时 pending、队列和模型的处置,防止静默丢请求或自动重放。
- Main 持有后台宿主生命周期;管理 UI 关闭不销毁执行宿主,服务释放和应用退出才按约定清理。验证隐藏 Renderer 的调度、计时器和 Worker 执行,不根据窗口不可见推断运行不受影响。
- 用实际 OS PID、Electron 进程指标和崩溃影响验证隔离程度。单独的 BrowserWindow/WebContents 不能直接作为独立 OS 进程或独立服务资源统计的证据;若与其他 Renderer 共享 PID,必须继续报告共享范围。
Embedding 保留共享 utilityProcess。查询与索引共享模型和进程的现有方式具备复用基础,优化先处理取消回收和失败重试。单请求取消继续尽量只影响该请求;进程终止属于服务级操作,必须说明其他使用方的结果。不得把每次请求超时直接等同于可以无条件杀死共享进程。
## 任务控制覆盖与缺口
[共享契约](../../src/shared/local-inference-contracts.ts)将服务状态与任务状态分开;[LocalInferenceService](../../src/main/local-inference-service.ts) 在 Main 内存登记请求,保存活动项及最近 100 条终态记录。任务的 `running` 当前包含排队、模型加载与执行,未提供统一细分阶段。该登记器是观测和控制入口,各引擎仍负责实际队列与清理。
| 控制维度 | 当前覆盖 | 后续评估和验收要求 |
| --- | --- | --- |
| 状态 | 任务有 `running/cancelling/completed/cancelled/failed`;服务另有启动、运行、停止、未知和错误状态;ASR 模型可用时服务仍为 unknown | 区分调用方已返回、执行仍活动、资源清理已确认;进程 spawn、模型 ready、请求完成不能互相替代。先增加必要证据,不预建完整阶段状态机 |
| 所有权 | ASR 请求持有 Worker;OCR Renderer 持有共享 Worker;embedding 按目录共享 Broker 并引用计数 | 控制记录注明真实来源与宿主;缺失项目、会话关联时不猜测归属。产品 Task 的取消不能凭名称去停止整个推理服务 |
| 取消 | ASR terminate;OCR 来源取消会终止当前 Worker;embedding 协作取消并保留执行记录 | 验证取消与自然完成竞态、重复取消及清理结果。单请求取消与共享服务停止分别定义影响,不用 UI 隐藏代替取消 |
| 超时与容量 | ASR Worker 120 秒;OCR 两端预算与排队语义不同;embedding 默认请求 120 秒且保留取消后容量 | 分别说明排队、启动、推理、清理的计时起点和上限;调用方超时后仍在执行的工作需要有界处置,不得提前释放容量制造超额并发 |
| 停止与清理 | 服务操作按 serviceId 串行,期间拒绝新增请求;停止校验当前活动 taskId 确认清单;向量停止等待 exit,重启不重放请求 | 清理失败保持可观察的活动/错误信息;窗口或宿主丢失需使受影响请求得到明确结果;ASR 和 OCR 的结果/清理间隙分别核对 |
| 可观测性 | 有来源、状态、时间、可选模型和错误;向量有 PID 资源数据;管理 Modal 不显示任务历史 | 测量排队、加载、执行、取消请求到执行结束、清理确认的耗时与在途数;日志不记录音频、图片或向量原文,未知指标不补零 |
本文的覆盖要求用于后续问题复现和最小修复,不构成引入统一常驻服务框架、持久化执行副本或自动恢复协议的决定。范围为桌面本机引擎;远程 Agent、外部向量端点和未接入的 TTS 不因此获得同样控制能力。
## 资源观测口径
[LocalInferencePage](../../src/renderer/src/LocalInferencePage.tsx) 在启用、文档可见且 Modal 内容未被 hidden/inert 隔离时每 5 秒读取快照,跳过重叠请求,关闭时清理轮询。该刷新间隔不能用于证明短请求的峰值或清理完成时刻。
[向量资源采样器](../../src/main/inference-process-resources.ts) 按 transport 当前 PID 匹配 `app.getAppMetrics()` 的 Utility 条目,用累计 CPU 秒差除以单调时间间隔。100% 表示一个逻辑核心,可超过 100%;首次采样、PID/创建时间变化、间隔超过 15 秒或读数异常时重建基线。工作集从 KiB 转为字节,UI 显示 MiB。
向量工作集包含运行时、模型、临时缓冲区及并发使用方,不能当作模型权重大小或单任务内存。ASR Node Worker 和 OCR Web Worker 没有当前可独立归属的 OS 进程工作集;宿主进程总量不能直接填入某个服务行。未来 OCR 后台宿主也必须先核实 PID 与共享情况,再决定指标范围。显存当前未采集。
## 优化待办与验收
优先级表示建议处理顺序:P1 为执行正确性和回收问题,P2 为测量及有条件的架构选型。全部待办待开展;源码核对已完成,故障复现、修复和实机验收分别记录,不以文档完成替代实现完成。
| ID | 优先级与事项 | 依赖及验收条件 |
| --- | --- | --- |
| TC-1 | P1:embedding 取消/超时后的有界回收 | 先覆盖 `session.run` 延迟返回及不返回,观察 caller、pending、执行记录与容量。定义取消后的清理等待上限及升级条件,数值依据测量确定。普通取消不终止其他共享任务;确需服务级终止时明确受影响使用方及失败结果,等实际退出再释放容量,且不重放请求。模拟用于稳定触发条件,最终以真实引擎取消和停止路径补验 |
| TC-2 | P1:embedding 加载失败重试 | 覆盖首次读模型或建 session 失败、随后恢复依赖的场景;选择清除失败缓存或显式重建引擎的最小方案。验收后续请求能够重新加载,并发请求不重复加载,失败中已创建的资源被清理,销毁后不再复活;不能把一次成功创建进程当作重试通过 |
| TC-3 | P1:OCR 宿主就绪、丢失与超时预算 | 保留当前 Web Worker 即可先验证,无须等待迁移。覆盖监听安装前派发、主 Renderer 重载/崩溃、冷启动超过 Main 启动预算及排队取消。明确 bridge 就绪与 Worker ready 的区别;受影响请求有明确结果,队列能继续使用,没有静默丢失或重放;两端预算、起算点和取消后下一项派发与清理顺序一致 |
| TC-4 | P1:ASR 结果与清理记账 | 覆盖结果先到、延迟退出、清理异常以及取消/超时与结果竞争。选择延后请求结算或单独保留清理记录的最小方案;验收资源仍活动时不会被报告为已释放,也不提前放开实际单请求容量。正常完成及异常路径均验证 Worker 最终退出 |
| TC-5 | P2:统一测量记录与资源归属核验 | 可与 P1 复现并行,为 TC-6/TC-7 提供依据。记录冷/暖加载时间、端到端延迟、排队时长、取消至执行结束/资源回收时长、空闲与峰值工作集、CPU 时间和样本数;按 PID 对照 OS 工具,分别报告引擎、宿主与应用总量。重启后不沿用旧 PID 基线,不以 5 秒 UI 轮询代替专门测量 |
| TC-6 | P2、条件待定:OCR 后台 Renderer 加 Worker | 依赖明确的 UI 独立执行需求、TC-3 生命周期结论及 TC-5 数据。候选验证需包含图片、多页 PDF、UI 重载时执行、后台宿主崩溃、空闲释放、应用退出、preload/IPC sender 边界和各支持平台打包资源。证实任务连续性及实际 PID 隔离后,再记录采用或保留现状的结论 |
| TC-7 | P2、条件待定:ASR 常驻 utilityProcess | 依赖流式/高频场景成立,或 TC-5 证明重复加载达到需优化程度,并完成 TC-4。对比请求级 Worker 与候选的冷启动、暖请求、空闲内存和取消成本;若为流式,验证连续音频、结果提交和停止语义。没有测量或实际需求时继续保留请求级 Worker |
测量记录应包含 OS/架构、Electron 与依赖版本、模型版本、硬件、输入时长/页数/token 与批量大小、冷暖条件、请求频率和并发使用方。使用固定可重复输入,报告样本数、分布及异常,不填入未经测量的性能收益。后续验收覆盖受影响的 Windows、macOS、Linux 构建;尚未测的平台明确保留为待验。
本次证据为源码和已安装依赖入口检查。后续执行待办时,在所属功能进度文档记录命令、环境、真实路径结果及遗留项,并引用 TC 编号;若迁移条件未成立,记录保留现状的原因即可。
@@ -24,7 +24,7 @@
1. “平台功能”保留现有一级设置分类,在分类内部增加共享 `PageTabs`:
- **通用设置**
- **魔法笔记**
- **远程项目**
2. “通用设置”首个功能为“模型下载源”。
3. 首期提供两个互斥选项:
- `modelscope`,用户文案为 **ModelScope**,默认值。
@@ -49,7 +49,7 @@
下载使用新来源。
9. 当前来源失败时,不自动重试另一个来源。错误可以提供“前往通用设置更换下载源”,由
用户明确选择。
10. 现有魔法笔记设置只移动到“魔法笔记”页签,不改变保存、启用或评论行为。
10. 魔法笔记设置由应用中心与笔记页共享,不在全局设置中重复提供入口。
---
@@ -60,7 +60,7 @@
“平台功能”现已使用两个二级页签:
- “通用设置”承载全局模型下载源。
- “魔法笔记”承载显示入口、AI 评论方式和 AI 评论形式。
- “远程项目”承载远程项目开关与 Agent 包管理。
页签复用共享 `PageTabs`,默认打开“通用设置”,不会把当前页签写入应用设置。
@@ -99,7 +99,7 @@
- 切换来源时不影响已安装、已选择或正在使用的模型。
- 下载失败时知道实际使用了哪个来源。
- 所选来源没有模型时得到明确说明,并可以主动前往设置切换。
- 在“平台功能”中快速区分通用设置与魔法笔记。
- 在“平台功能”的“通用设置”中选择模型下载源。
### 3.2 产品目标
@@ -166,10 +166,9 @@
└─ 平台功能
├─ 通用设置
│ └─ 模型下载源
└─ 魔法笔记
├─ 显示魔法笔记入口
├─ AI 评论方式
└─ AI 评论形式
└─ 远程项目
├─ 远程项目开关
└─ Agent 包管理
```
分类页只有一个一级标题“平台功能”。页签标题不重复渲染为第二个页面标题;每个面板内部
@@ -186,7 +185,7 @@
onChange={setActiveSection}
tabs={[
{ id: 'general', label: '通用设置' },
{ id: 'magic-notes', label: '魔法笔记' }
{ id: 'remote-projects', label: '远程项目' }
]}
value={activeSection}
variant="segmented"
@@ -214,7 +213,7 @@
未来新增页签时:
- 必须与“通用设置”和“魔法笔记”处于同级。
- 必须与“通用设置”和“远程项目”处于同级。
- 页签表示完整、独立的功能设置域。
- 通用设置只放跨功能的全局行为,不成为任意设置的杂物区。
- 页签超过可读数量时重新组织信息架构,不允许多行堆叠。
@@ -230,7 +229,7 @@
平台功能
管理通用平台行为与可选工作区能力
[ 通用设置 ] [ 魔法笔记 ]
[ 通用设置 ] [ 远程项目 ]
┌ 模型下载源 ────────────────────────────────────────────┐
│ 选择 GoodBuddy 托管本地模型后续下载使用的平台。 │
@@ -304,28 +303,11 @@
---
## 8. 魔法笔记页签
## 8. 魔法笔记设置边界
现有魔法笔记卡完整移动到“魔法笔记”面板:
- 显示魔法笔记入口。
- AI 评论方式。
- AI 评论形式。
保持以下行为不变:
- 开关继续使用共享 Switch 和 `role="switch"`。
- 评论方式和形式继续使用 `SegmentedControl`。
- 每项继续即时保存。
- 保存失败保留最后确认设置。
- `onMagicNotesEnabledChange` 继续更新应用导航入口。
页签重构不能:
- 重置现有设置。
- 在切换页签时保存或改变值。
- 因魔法笔记入口关闭而隐藏“魔法笔记”设置页签。
- 把页签选择误当成启用开关。
全局设置不再提供魔法笔记页签或跳转入口。应用中心与笔记页继续共享同一表单和配置,
保留已有启用、待办数量显示、评论方式、评论形式及业务数据。共同设置的交互与同步规则见
[应用导航 UI 设计](../features/application-tool-navigation/ui-design.md)。
---
@@ -899,7 +881,7 @@ Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源
- 默认选中“通用设置”Tab。
- Tab 使用 `tablist`、`tab`、`tabpanel` 和 `aria-selected`。
- 方向键、Home、End 和焦点恢复正确。
- 切换到魔法笔记不改变任何设置。
- 切换平台功能页签不改变任何设置,全局设置不显示魔法笔记入口。
- Radio Group 具有持久 Label。
- 选择来源时保存一次,保存期间防止重复提交。
- 保存失败恢复原值并保留可重试错误。
@@ -954,7 +936,7 @@ Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源
- 将应用设置版本升级并迁移旧版本。
- 在“平台功能”中增加 `PageTabs`。
- 增加“通用设置”与模型下载源 Radio Group。
- 把现有魔法笔记卡移动到“魔法笔记”面板。
- 魔法笔记卡已迁入应用共同设置,全局页签已移除,见第 8 节。
### 阶段 2:共享目录和来源解析(已完成)
@@ -983,14 +965,14 @@ Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源
### 19.1 产品
- “平台功能”显示“通用设置”和“魔法笔记”两个可访问页签。
- “平台功能”显示“通用设置”和“远程项目”两个可访问页签。
- 默认打开“通用设置”。
- “通用设置”提供 ModelScope 和 Hugging Face,默认 ModelScope。
- 来源选择说明影响范围,并明确已安装模型、ZIP、Ollama 和应用更新不受影响。
- 切换来源不会下载、删除、切换或重新验证任何已安装模型。
- 当前来源没有模型时,在点击下载前即可看到不可用原因。
- 下载失败不会自动请求另一来源。
- 魔法笔记的现有设置和入口行为保持不变。
- 魔法笔记设置由应用中心与笔记页共享,全局设置不再提供笔记入口;已有配置值和业务数据保持不变。
### 19.2 数据与下载
+44
View File
@@ -0,0 +1,44 @@
# Runtime Storage And Background Reads
SQLite owns persisted task, event and conversation evidence. The desktop business
connection is long-lived. WAL permits readers alongside a writer; synchronous SQL
still blocks the thread that executes it. Memory caches are disposable derived data.
Startup storage upgrades run in `assistant-storage-worker` before the business
connection opens. Runtime readers must not invoke initialization, migrations or
recovery. `SubagentProgressStorage` caches event encoding state, not task statistics.
## Runtime Read Design
Execution statistics use one lazily started worker with a read-only connection.
The worker performs the existing evidence query inside a short read transaction.
Main supplies the current reply lease IDs and receives only scalar summaries.
There is no worker pool or duplicate persisted summary. The worker is terminated
when its owning database closes; errors reject reads, without a synchronous Main
fallback. In-memory test databases use the synchronous query because independent
connections cannot access the same private in-memory database.
Derived caches must specify scope, capacity, lifetime and invalidation. Statistics
keep at most eight scopes for 30 seconds. SQLite `data_version` detects other
connections' commits; `total_changes()` detects writes on the owning connection.
Reads inside an existing transaction clear and bypass the cache, so rolled-back
values cannot escape. Closing the connection clears its cache. Live leases bypass
the cache because elapsed time changes without a database write.
Read transactions end before replies are posted. Long-lived read transactions
would prevent WAL checkpoints from reclaiming old pages. Each reader has its own
SQLite page cache; adding workers increases memory usage and requires evidence of
parallel query demand. New consumers should reuse the same lifecycle and read-only
rules, without introducing a general query protocol before a second use case exists.
## Renderer Scheduling
Hidden task panels use React `Activity` to preserve state while scheduling hidden
render work below visible updates. Panel factories run inside that boundary.
Terminal and browser panels retain their existing mounted effects and sessions.
Statistics refresh only when the task center and document are visible. Results
must remain scoped and late responses from a previous selection must be discarded.
The [task statistics design](../features/task-and-job/technical-design.md) owns the
query contract and evidence rules. Tests and timings demonstrate only their fixture
conditions; they do not establish a latency guarantee for arbitrary retained history.
+8
View File
@@ -37,6 +37,9 @@
覆盖正常转换、无变化记录、重复打开、内容修订、失败事务和退出续跑。数据库大小问题
必须同时验证新增写入的增长量、旧记录可还原性以及文件实际缩小,而不只检查 schema。
对象比较必须覆盖真实生产形状和 JSON 往返,尤其是可选字段值为 `undefined` 时与字段缺省
的差异。再次修复已发布迁移产生的数据时,提高目标 schema,并覆盖已升级用户的入口;
不能只处理尚未迁移的旧库。
### 真实旧库
@@ -52,6 +55,9 @@
源 schema 与目标 schema。确认原件和备份未被改写。
6. 运行 `npm test`、`npm run typecheck`、`npm run lint`;生产构建变化按开发验证规则
检查,发布准备则遵循[发布手册](./release-runbook.md),不重复本地打包。
7. 用真实模型模拟正常用户任务,允许主代理与子代理按需调用工具、修改独立测试项目、
运行测试和处理失败;只读纯文字模型调用不能证明工具事件增长正常。记录实际提供商
请求数与统计范围,不把 Runtime 用量上报数当成全部 HTTP 请求数。
## 发布与维护
@@ -65,3 +71,5 @@
表示,并回收空间。具体规则见
[执行记录存储与升级回收](../features/assistant-workbar/execution-history-storage.md),
数据保留与真实旧库验证见[工作栏进度](../features/assistant-workbar/progress.md)。
- 当前未发布源码:schema 35 同时整理旧快照和 schema 34 的重复工具块,工具字段按
JSON 可保存的值比较,不因 `undefined` 被省略而误判变化。
@@ -0,0 +1,137 @@
# Desktop 0.13.12 and Agent 0.13.3 Preparation
Prepared on 2026-09-23 on `main`. This record describes a local candidate, not a
published release. Desktop notes are in `resources/release-notes.json`; Agent
notes are in `resources/agent-release-notes/0.13.3.md`. Both are bilingual drafts
awaiting approval with the exact candidate commit before tagging.
## Scope
- Desktop baseline: `v0.13.11`, including all nine subsequent commits through
`83a48a4` and the reviewed working-tree changes.
- Agent baseline: `agent-v0.13.2`. The Agent changes reclaim idle Runtimes and
drain displaced Agents; the Desktop Continue path restores conversation
history when reopening a Runtime.
- Desktop package and both root lockfile versions are `0.13.12`.
`agent-runtime-lock.json` is `0.13.3`. Agent package tooling derives the minimum
Desktop version from `package.json`. Node, OpenCode, and Continue locks are
unchanged at `24.19.0`, `1.18.29`, and `1.5.47`.
- Both feature matrices were reviewed and updated for versions, Supervisor
default-off behavior, implemented reviews/graphs/activity/incremental input,
remaining supervision work, and release acceptance limits.
## Local Validation
`npm run release:notes:verify`, `npm run typecheck`, and `npm run lint` passed
after the final source changes. Focused runs cover 35 distinct test files and
690 passing tests; 403 tests were excluded by the IPC/App name filter.
| Group | Files | Passing Tests |
| --- | ---: | ---: |
| Supervisor, settings, bridge, and release-note suites passing in the initial run | 14 | 157 |
| Database, migration, activity I/O, note storage, menu, and sidebar suites after fixes | 6 | 209 |
| IPC/App filtered by `supervision\|Supervisor\|heartbeat\|application enable\|graph navigation` | 2 | 16 |
| Agent CLI, daemon, Runtime backend, Desktop ACP, transport, and connection manager | 7 | 207 |
| Usage, duration, workspace, Markdown, and Supervisor Electron layout | 6 | 101 |
The initial focused run had 25 failures: 22 synthetic legacy database fixtures
retained schema-46 objects, two menu assertions assumed Supervisor was enabled,
and one browser close retry left its earlier error visible. Fixtures now remove
the new objects before simulating older versions, menu tests follow the saved
enabled preference, and a successful browser close clears its scoped error.
All affected suites passed on rerun. Production migration behavior was not
changed to accommodate synthetic fixtures.
The Electron layout test uses production components with isolated fixtures,
including 1440, 1024, and 390 pixel widths and light/dark themes. It is not a
packaged-app probe or a real-user database acceptance test.
No real model requests were made during this preparation. Existing development
evidence records eight requests on the shared Linux x64 Host for remote lifecycle
checks and three for incremental review. The remote evidence does not repeat
Continue real-Host validation, the complete signed installation flow, or the
native architecture matrix. See
[remote lifecycle evidence](../features/remote-host/technical-design.md#生命周期验证2026-09-23)
and [incremental review evidence](../features/conversation-supervision/progress.md#2026-09-23-自动增量验证).
## Pending Release Work
The user requested no further full local suite. Full main-branch CI validation
and its production build remain required before tagging; native Desktop and
Agent packaging and publication verification remain pending. No local production
build or package was run. No push, tag, remote synchronization, or publication
was performed during preparation. Approval must identify the exact candidate
commit and both bilingual note files before either release tag is created.
## Approved Candidate CI Attempt
The user approved `bf8ece05902509411454b27b742ebfec930005ad` and both bilingual
note files. On 2026-09-23 that exact commit was fast-forwarded to `main` on
`origin` and `github`; both remote refs were verified. GitHub SSH stalled, so
the GitHub push used HTTPS with the existing GitHub CLI keyring credentials.
No persistent Git configuration was changed. GitHub reported the account's
existing pull-request-rule bypass for this requested direct push.
- [Desktop main CI](https://github.com/mesalogo/goodbuddy/actions/runs/35849362694)
failed in the full suite: 4 files failed, 418 passed, 13 skipped; 10 tests
failed, 4,997 passed, 43 skipped. Type checks, lint, and the production build
did not execute after the test failure.
- [Agent main CI](https://github.com/mesalogo/goodbuddy/actions/runs/35849362799)
passed source validation and native Linux x64, Linux arm64, and Darwin arm64
deterministic compound-package verification with ephemeral test signatures.
- Neither release tag was created or pushed. No Desktop or Agent publication
workflow was started, and no production release assets were verified.
The follow-up changes are limited to tests. Two IPC tests clear the startup
settings-read count before measuring interactive request lookups. The settings
failure UI test now expects Supervisor to remain disabled. OpenCode isolation
tests prepare their real configuration dependencies in a dedicated temporary
directory instead of relying on a prior local package preparation; connection
failures now include their diagnostic detail. The large activity-list and
multi-page navigation and external knowledge binding tests have explicit
15-second budgets; the binding test also waits up to five seconds for its
resulting heading.
Its snapshot fixture now selects by library ID, and its instance list remains
available during polling instead of assuming a fixed number of asynchronous
reads. Both mocks reset between tests; the binding test waits for the
selected-library read.
These budgets address timeouts observed in the full CI run without removing
assertions or changing product behavior.
The approved source and bilingual notes remain unchanged. These test changes
need a revised candidate commit and approval before another release attempt;
the failed commit must not be tagged. No local production build, package,
external model request, or LoongArch build was performed.
Follow-up validation: the seven selected IPC/UI regression cases and all seven
OpenCode isolation cases passed locally. Type checks and lint passed. Full
remote CI and the production build still require a new approved commit; these
focused results do not satisfy that release gate.
## Linux Executable Path Follow-up
On 2026-09-23, local `main` was fast-forwarded from `163e245` to
`073aa45a30efe99fab7a92f8aee2a36087db9c2a` after inspecting the four intervening
commits. They update only the community QR codes in the two README files.
The user authorized retaining these changes, fixing CI, pushing both remotes,
and publishing Desktop `v0.13.12` and Agent `agent-v0.13.3` after successful
candidate CI without another approval round. No LoongArch preview is requested.
[Desktop CI 35854792610](https://github.com/mesalogo/goodbuddy/actions/runs/35854792610)
reported 5,004 passing tests, three failures, and 43 skipped tests. All three
failures were in the real OpenCode isolation test. It constructed
`opencode-ai/bin/opencode` on Linux, but OpenCode 1.18.29 installs its npm
executable as `bin/opencode.exe` on every platform. The test now reuses
`resolveBundledRuntimePaths`, the existing production resolver, instead of
constructing a platform-specific filename. Production Runtime code and both
release-note files are unchanged.
Local follow-up validation passed all nine tests in
`opencode-selection-isolation.test.ts` and `bundled-runtimes.test.ts`, including
the existing Linux development-path assertion, plus full type checks and lint.
Release-note and package/lockfile version validation passed. The isolation
tests made two requests to their local mock endpoint and zero real model
requests. No full local suite, production build, package, or real-Host check
was run for this test-only fix; it changes neither deployed Agent code nor
the Desktop-to-Agent production path. Successful Linux main-branch CI and
its production build remain required before tagging.
@@ -0,0 +1,37 @@
# Desktop 0.13.13 Preparation
Prepared on 2026-09-23 from `b16fc90`, relative to published Desktop `v0.13.12`.
This is a Desktop-only release; Agent remains at published `0.13.3`.
No LoongArch preview is requested.
## Scope and isolation
- `bf8ff7f` reduces repeated activity-history reads during streaming saves.
- `b16fc90` restores compact supervision feedback controls and target names.
- The candidate is prepared in a separate worktree. The user's uncommitted
supervision timeout tests in the original worktree are explicitly excluded.
- Desktop and both root lockfile versions are `0.13.13`. Both feature matrices
and bilingual release notes were reviewed against this range.
- Database schema and Agent/Runtime contracts are unchanged. The activity
comparison cache belongs to Desktop persistence; it does not modify the
deployed Agent, model bridge, or remote Runtime lifecycle.
## Validation
- Six focused files passed 192 tests: activity-history I/O, AssistantDatabase,
storage upgrades, sidebar interactions, SupervisionCard, and the Supervisor
Electron layout fixture.
- Re-running `tests/supervisor-layout.electron.test.ts` with
`GOODBUDDY_SUPERVISOR_SIDEBAR=1` passed its additional sidebar scenario:
480, 300, and 200 pixel widths in both themes, no horizontal overflow,
compact 34px buttons, visible keyboard focus, and no raw target ID in body text.
- `npm run release:notes:verify`, `npm run typecheck`, and `npm run lint` passed.
- Ten consecutive activity saves performed one full-history scan. WAL and SQL
counters are not installed-application physical disk throughput measurements.
- No real model requests, local full-suite rerun, production build, packaging,
or packaged-app probe were performed. UI validation uses production
components with isolated fixtures, not a real user database.
The candidate must pass complete main-branch CI and its production build before
an unused annotated `v0.13.13` tag is pushed to both remotes. Native six-platform
packaging and public GitHub/OSS verification remain subsequent release steps.
@@ -0,0 +1,64 @@
# Desktop 0.13.14 / Agent 0.13.4 发布准备
后续状态:Desktop 标签流程 `36165805814` 在测试阶段失败,未进入构建或发布;
Agent 流程 `36165805884` 成功且 0.13.4 已公开。两个历史标签均保留,
桌面恢复版见 [0.13.15 准备记录](./release-preparation-0.13.15.md)。
以下为原候选准备时的范围与证据,不代表其桌面发布成功。
## 范围与审批
- 用户确认从 0.13.13 升至 0.13.14,现有七个测试修改及三个验证辅助文件一起提交。
- 不构建实验性 LoongArch 预览,不运行本地生产构建、打包或安装包启动探针。
- 准备起点为 `ff746bdc1add6a12227ccb61a7794f654aaaa8ba`。
- 最近可达稳定标签为 `v0.13.12`。已公开发布的 `v0.13.13` 指向
`7756231`,其发布准备提交不在当前 main 祖先链上;两项产品修复已包含,
0.13.13 的包内发布说明也已保留。需同时核对两个范围,不重复宣传已发布修复,
不移动标签或重写历史。
- 用户随后确认 Agent 也末位加一,目标为 0.13.4。已批准联合发布的双语说明,
以及验证通过后一起提交本地候选;最终候选 SHA 仍须另行确认。
- Agent 范围为 `agent-v0.13.3..HEAD`,生产改动来自 `7adae97`。
- 用户批准提交并推送两个远端、等待 CI;标签仍待精确提交确认。
- Fetch 确认两个远端 main 均为 `7756231b3bdfa148f1b2702e4ff3fc3bd0cb057e`。
用户批准以普通合并保留双方历史,保留本次版本和已批准说明,
补回历史发布文档;不强推、不改动已有标签。
- 本地候选提交为 `d23af99`。普通合并解决后,仅新增或修改文档;
产品源码、测试、依赖、版本文件及两份已批准说明与该候选完全一致。
合并后 `v0.13.13` 恢复为可达发布基线,先前祖先链差异仅记录准备起点状态。
## 交付与数据边界
- 本轮联合准备 Desktop 0.13.14 和独立 Agent 0.13.4,不覆盖历史 Agent 包。
- 桌面包含远程结果未知后显式发送新消息的恢复修复;Agent 完成确认、
空闲回收失败处理和旧安装清理随 0.13.4 交付。
- Agent 发布流程读取本次 package.json 作为最低 Desktop 版本,因此需先升级
Desktop 至 0.13.14,再下载并更新 Host。Node、OpenCode、Continue 锁定版本不变。
- 两个候选分别使用 `v0.13.14`、`agent-v0.13.4`,Agent 不替代 Desktop Latest。
- 数据库从 schema 46 升至 47。升级前需完整备份用户数据,包括数据库和
相邻 notes/ 目录;旧客户端不能打开升级后的数据库。
- 已检查中英文功能矩阵,更新当前版本、监督回顾、待办能力及独立 Agent
交付边界。其他平台和功能的既有未验收事项继续保留。
## 验证
- 首次 `npm test`:5,125 项通过、74 项跳过、2 项失败。失败均为版本已更新而
说明尚未获批写入导致的当前版本缺失,未修改断言规避。
- 联合说明写入后的 `npm test`:435 个文件通过、10 个文件跳过;
**5,127 项通过、74 项跳过,0 失败**,耗时 685.49 秒。
- `npm run typecheck`、`npm run lint`:首轮与联合候选复验均通过。
- `npm run release:notes:verify`:联合说明写入后通过;Desktop 三处版本一致,
当前说明仅一项,Agent lock 与独立双语标题一致。
- 合并后复验:发布说明校验、9 项发布说明测试、完整 typecheck、lint、
`git diff --check` 通过。因合并仅恢复文档,未重复运行完整测试和 Host 探针。
- 当前源码 Linux x64 完成/历史恢复探针通过:两轮 Ask 均完成,Runtime 重建后
保留会话历史,每轮结束及最终退出时所属进程数均为 0。
- 隔离旧安装清理探针通过:真实 CLI 回收所属孤儿进程、删除对应旧 payload,
保留存活旧 daemon、活动工作、历史及 current/candidate,重复清理无重复删除。
- 本轮新增真实模型调用 **2 次**,模拟模型调用 0 次;清理探针模型调用 0 次。
- 两个探针从当前源码重新生成临时测试脚本,仅操作共享 Linux x64 Host 的独立
`/root/tmp/gb-lifecycle-*` 与 `/root/tmp/gb-upgrade-*` 测试目录,不替换共享安装。
探针沿用历史测试身份版本,不是正式签名 0.13.4 包安装验收,也不代替
Linux arm64、macOS arm64 原生 CI 或完整 Host 更新界面验证。
- 复现命令沿用[远程 Host 技术设计](../features/remote-host/technical-design.md)
中 lifecycle-host 和 upgrade-cleanup 脚本;本轮生命周期参数为
`driver --completion-only`,使用真实模型而非 `--fake-model`。
- 候选主分支 CI、生产构建、六平台原生打包及公开发布验证:尚未执行。
@@ -0,0 +1,40 @@
# Desktop 0.13.15 恢复版准备
## 发布范围
- 以已发布的 `v0.13.13` 为用户更新基线,完整承接未发布 `v0.13.14` 的产品变更。
历史准备及 Host 验证见 [0.13.14 记录](./release-preparation-0.13.14.md)。
- 0.13.14 标签流程 `36165805814` 在测试阶段失败,生产构建、打包与发布均未执行;
GitHub Release API 返回 404。不可变标签保留,包内条目改为 0.13.15,
不保留重复的未发布说明。
- Agent 0.13.4 流程 `36165805884` 成功,三个包已公开,最低 Desktop 要求仍为
0.13.14。恢复版满足该最低要求;不重发 Agent、不更改其签名目录或标签。
- 用户批准 0.13.15 标题及升级提示调整,其余双语正文沿用已批准内容;
要求先推送候选,等 CI 全量验证与生产构建成功后再打标签。
- 不构建 LoongArch,不运行本地生产构建、打包或安装包启动探针。
- 中英文功能矩阵已复核,同步恢复版本和 Agent 已发布状态,保留其他未验收边界。
## CI 修复
`cd27163` 仅改测试和质量规范,不改变产品、Agent、数据库或外部契约:
- 原生能力清单测试控制第二次响应,等待重试已开始再展开控件,并分别校验响应前后状态。
- 浏览器关闭测试在同一个 `waitFor` 中检查两次调用、页签移除和错误清除。
- 笔记布局测试显式扫描 fixture 依赖,使用可见的隔离 Electron 窗口执行原生输入,
保留 sandbox/contextIsolation,明确检查超时、信号和退出码后才读取结果。
对应 CI 证据:标签流程有 1 项失败(原生能力按钮未找到);
main 流程 `36164698647` 有 2 项失败(关闭错误残留、布局测试约 90 秒后缺少结果文件)。
两次失败点不同,不能仅凭本地通过认定 CI 已修复。
## 验证
- 本地三个定向用例通过,Electron 布局约 3.46 秒,全部几何和交互断言保留。
- 修复提交完整 typecheck、lint 通过。
- 本轮没有重复本地全量测试,以新候选 Linux CI 的完整测试结果为准;
先前 5,127 项本地通过仅为历史记录。
- 无新真实模型调用,未改变 Agent 或 Runtime 生产源码,无需重复 Host 开发探针。
- 恢复候选的说明校验、9 项发布说明测试和 `git diff --check` 通过;
三处桌面版本一致。逐项比较确认只替换版本及已批准升级提示,
0.13.14 无重复条目,Agent lock 仍为 0.13.4。
- 主分支 CI、生产构建和标签发布结果待后续核对。
+24 -2
View File
@@ -10,6 +10,23 @@ from the authoritative GitHub, OSS, workflow, and Git state, then use the
smallest existing idempotent rerun or a new immutable version as specified
below.
## Optional PR Article
Product publicity is an optional companion to a release, not a release blocker
or a replacement for the approved bilingual release notes. When requested,
follow the [PR article workflow](../pr-articles/README.md) and choose either the
[featured article](../pr-articles/README.md#重点稿模板) or
[regular update](../pr-articles/README.md#常规与综合稿模板) template based on user value,
not version size or commit count.
Prepare the draft and real screenshots against the candidate commit using the
[isolated screenshot procedure](../pr-articles/README.md#截图执行步骤). This does
not waive the build, packaging, or launch restrictions below. Keep missing
materials in the article's review record without delaying the software release.
Before external publication, verify the final version, download links, upgrade
notes, and screenshot accuracy. Preparing materials does not authorize posting
to a public channel; follow the user's explicit publication instruction.
## Release Packaging
- Unless the user explicitly requests it, never run a local production build,
@@ -27,6 +44,11 @@ below.
- `.github/workflows/packages.yml` is the canonical cross-platform packaging
workflow. It validates and builds `out` once, then packages on six native
runners: Windows, macOS, and Linux, each for x64 and arm64.
- Linux source validation configures the installed Electron `chrome-sandbox`
helper with root ownership and mode `4755`, rehydrates the Electron
distribution without cache when the helper is missing, then runs the full
test suite under Xvfb. Keep Electron sandboxing enabled; do not skip UI tests
or add `--no-sandbox` to work around runner setup failures.
- The experimental Linux LoongArch (`loong64`) preview is built separately
through `build/loongarch-cross` by following
`docs/development/loongarch-preview-build.md`. It is not part of
@@ -62,7 +84,7 @@ below.
Host.
- Each independently published Linux x64, Linux arm64, or macOS arm64 `.gbagent` contains the
Agent daemon, pinned Node, and the desktop-maintained compatible OpenCode
Runtime. Build all three targets on native GitHub Actions runners from one
and Continue Runtimes. Build all three targets on native GitHub Actions runners from one
immutable `agent-v<agentVersion>` tag.
- Keep the GoodBuddy Agent source, shared protocol/contracts, runtime lock,
bundle tooling, and tests in this repository so a desktop commit identifies
@@ -72,7 +94,7 @@ below.
- `.github/workflows/agents.yml` is the branch and pull-request build
verification workflow for GoodBuddy Agent. It must build Linux x64 and
arm64 on native runners from the checked-out commit, acquire the locked
official Node archive and locked OpenCode npm archive, verify every locked
official Node archive and locked OpenCode and Continue npm archives, verify every locked
digest, use only an ephemeral in-memory test signing identity, and verify the
resulting compound package and deterministic archive. It must not read
production signing secrets, publish installable release artifacts, or modify
+54
View File
@@ -0,0 +1,54 @@
# Desktop Window Recovery
This is recovery hardening for intermittent blank windows, not an established
diagnosis of their cause.
- `src/renderer/src/main.tsx` wraps the locale provider, App and storage-upgrade
page in `AppErrorBoundary`. A React render/lifecycle failure replaces the tree
with a blocking recovery page and an explicit Reload button. It uses the saved
UI language (including system preference resolution), shared theme tokens and
primary-button styling, without depending on the failed application providers.
- Reload refreshes the current document, retaining its URL and storage-upgrade
query. It can lose unsaved input. There are no automatic React retries.
- `src/main/window.ts` listens for `render-process-gone`. Abnormal exits open one
native dialog at a time, using the OS locale because renderer language storage
is unavailable to Main. Reload occurs only on explicit confirmation. Cancel
leaves the window unchanged; persistent failures require quitting and reopening
GoodBuddy. Clean exits, shutdown and destroyed windows do not trigger recovery.
- Main-frame load failures are logged; aborted navigation and subframe failures
are ignored. Initial load promises are caught, without automatic load retries
or a new load-failure UI.
- Native crashes and main-frame load failures use the existing bounded desktop
diagnostics store (`desktop.renderer.gone`, `desktop.renderer.load-failed`,
stage `renderer`). Persistent records retain fixed summaries rather than raw
errors. Crash reason/exit code and React errors/component stacks go to their
respective process consoles. There is no new diagnostic IPC.
The boundary does not catch module evaluation/startup failures before React
mounts, event-handler exceptions, asynchronous rejections, a hung renderer, or
main-process failure. Reload does not establish that a repeated underlying error
has been fixed. Agent runtimes and remote execution are unchanged.
Targeted regressions are in `AppErrorBoundary.test.tsx`, `window.test.ts` and
`desktop-diagnostics.test.ts`. Electron API mocks verify decision paths but do
not establish native dialog appearance or full application recovery.
Validation on Windows, 2026-09-15:
- Targeted Vitest run: 20 tests passed across the three files above.
- `npm test`: 359 files passed, 9 skipped; 4,225 tests passed, 67 skipped.
- `npm run typecheck`, `npm run lint` and `git diff --check` passed.
- An isolated Electron fixture bundled the current boundary, shared styles and
production window module. Both languages and themes at requested content sizes
1280x800, 960x720, 720x640 and 640x420 passed visibility, focus and horizontal
overflow checks. Windows scaling rounded actual widths down by one CSS pixel.
The 16-image overview and Chinese/light and English/dark short-window originals
were visually reviewed; no clipping or focus-style defect was found.
- A native Space key activated the recovery button and reloaded the document.
Two actual renderer crashes verified confirmed reload with URL retention and
cancellation without retry. Only native dialog responses were mocked; native
dialog appearance, full App business-state recovery and other OSes were not
verified. No model calls were made by this fixture.
- The first temporary Electron driver timed out during ESM startup. Removing its
top-level readiness await fixed the driver; the subsequent run passed. This
was a test-driver failure, not evidence of the reported application root cause.
@@ -1,20 +1,30 @@
# 应用导航与系统工具入口
本功能整理 GoodBuddy 主侧栏的入口层级:系统入口保持固定,内置应用通过最底部应用中心
访问,并可常驻主导航;系统工具由右侧工作栏 Tab 承载。应用商店是后续功能,首期不提供
本功能整理 GoodBuddy 主侧栏的入口层级:系统入口保持固定,四个内置应用共用可调整的保存顺序,知识库始终启用并常驻;
智能心跳、魔法笔记和本机推理监控可通过应用管理控制启用和常驻。底部应用中心向上展开轻量锚定菜单,列出已启用应用;底部“管理应用”打开既有管理 Modal。系统工具由右侧工作栏 Tab 承载。应用商店是后续功能,首期不提供
商店入口、推荐位或安装流程。
当前分期见 [PRD 第 8 节](./prd.md#8-分期),实施步骤见[技术设计第 11 节](./technical-design.md#11-实施顺序)。
本轮更新实施计划与范围,旧版功能要求、验收和配套设计仍需按新计划同步,此项列为实现前的第一步。
本轮 A–D 已接入生产源码:应用菜单、应用管理 Modal、共享应用设置、持久化偏好及本机推理监控。菜单不按打开历史或常驻状态筛选,无模态遮罩;点击应用行关闭菜单,知识库、智能心跳和魔法笔记打开主内容区页面。本机推理监控从菜单、常驻入口或管理 Modal 打开时均使用独立 Modal,保留底层工作区。管理列表、菜单和侧栏按同一保存顺序排列,侧栏按启用和常驻过滤。知识库在管理 Modal 中提供“打开”和排序,不提供启用、常驻开关或通用空设置;原工作区配置及心跳单条计划 enabled 保持原义。验证范围及集成缺口见[实施进度](./progress.md)。
新增计划包括 [私有化市场与 YAML 交换](./prd.md#fr-15-私有化市场与-yaml-交换后续):
通过市场 URL 获取应用,支持 `.yml` 导入导出,未来由 ShareServer 提供私有目录与分发。
[本机模型管理入口](./prd.md#fr-16-本机模型管理入口新增计划项)已独立于商店接入,作为
可常驻左侧的“本机推理监控”(英文 Local Inference Monitor)内置应用,直接查看语音、OCR、向量等本机服务状态与可用资源指标,不显示任务历史。向量服务显示独立进程的 CPU 和工作集内存;ASR、OCR 与宿主共享进程,逐行说明无法独立统计的原因。
本轮已接入 `local-inference` 管理 Modal、类型化 IPC、真实请求登记及引擎管理;实际支持清单见
[技术设计](./technical-design.md#111-本机推理任务与执行服务),市场仍为后续需求。
相关验收见 [User Stories](./user-stories.md#7-后续需求场景)。
项目活动汇总独立于上述应用中心计划,范围见 [FR-14](./prd.md#fr-14-项目活动汇总)。
计数规则由[活动逻辑](./logic-design.md#9-项目活动汇总)定义,入口与弹窗见
计数规则由[活动逻辑](./logic-design.md#9-项目活动汇总)定义,入口与级联菜单见
[活动界面](./ui-design.md#10-项目活动汇总),数据加载与会话跳转见
[技术设计](./technical-design.md#12-项目活动汇总)。此处“活动”指当前运行或待处理会话,
与用于审计的“运行记录”不同;已完成未读面板属于后续范围,本次未实现。
[技术设计](./technical-design.md#12-项目活动汇总)。此处“活动”包括当前运行、待处理及本次客户端
会话内已完成但未查看的会话;完成提示在实际进入聊天后清除,不累计历史完成总数。
## 文档导航
现有 `heartbeat` 入口承载监督者工作回顾与故事线图谱,可管理启用、常驻和排序。应用默认关闭;完整执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。未来市场复用同一应用身份,不增加平级重复入口,目标定义见[监督者产品设计](../conversation-supervision/supervisor-prd.md)。
| 文档 | 权威职责 |
| --- | --- |
| [PRD](./prd.md) | 用户问题、产品边界、功能要求与产品验收 |
@@ -22,32 +32,58 @@
| [功能逻辑设计](./logic-design.md) | 分类规则、入口状态、优先级与失败结果 |
| [UI 设计](./ui-design.md) | 侧栏信息架构、控件、文案、键盘和响应式行为 |
| [技术设计](./technical-design.md) | 现有模块映射、状态契约、持久化和实施顺序 |
| [实施进度](./progress.md) | 应用中心、设置及项目活动级联的实现与验证记录 |
| [应用中心交互 Demo](./navigation-demo.html) | 对比六个候选方案;配套 CSS 和 JS 仅服务原型 |
Demo 可直接在浏览器中打开,支持方案切换、浅深主题、添加、移除、排序和商店模拟。
六个方案共用本次页面会话内的应用配置,刷新后恢复默认。商店、资源指标和工具内容均为
演示数据,不执行真实安装或本机操作。窄屏上下排列侧栏与输入区,桌面展示完整应用布局。
这些方案用于讨论最底部应用中心;其中商店页、商店应用安装与 F 方案属于未来交互参考,
不纳入首期实现或验收。首期产品只提供内置应用;Demo 暂保留远期示例以便后续讨论。
Demo 可直接在支持原生 dialog、popover 的现代浏览器中打开,无需依赖或构建。
六个方案共用本次页面会话内的配置,刷新后全部恢复默认,不持久化或修改真实设置。
A–E 仅展示内置应用,没有商店入口、推荐或安装操作;F 单独演示未来私有市场。
模型服务、资源指标和工具内容均为演示数据,不执行真实安装、模型加载或进程操作。
窄屏上下排列侧栏与主内容,应用页面隐藏输入区,对话页面显示输入区。
Demo 的模型管理和 F 方案均使用模拟数据;正式本机推理监控 Modal 的交付范围以技术清单和实施进度为准,旧页面原型不作为 Modal 验收依据。
底部输入区共用一个简化候选布局:附件、语音、Runtime、模式和发送位于主操作行,专家、
知识范围及 Runtime 专属选项通过“选项”展开,非默认选择显示为可移除标签。可体验文件名
附件、模拟语音、模板填入、原生命令选择、8 秒模拟回复、发送排队、立即插入、删除、停止,
以及上下文详情和模拟压缩。文件只读取名称,语音使用固定示例文本,不录音、不上传、不调用 AI。
这是输入区设计讨论用的原型,尚未改变正式产品或替代现有 UI 规范。
按最新讨论,Demo 已移除左侧系统工具快捷区:文件、终端、浏览器和资源监控只在右侧
工作栏 Tab 中演示,不列入应用中心。上方正式文档仍保留原设计,待方案确定后同步。
左上角项目选择器下方仅在有运行中或待处理会话时显示活动摘要,全部空闲时隐藏。点击摘要打开项目
列表,悬停或点击项目向右展开会话子菜单,点击会话才切换项目与对话。子菜单与项目列表
相接,项目行保留子菜单箭头,会话行只显示名称与状态。支持上下方向键选择、右键进入、左键或 Esc 返回,顶层 Esc 关闭;窄屏在同一浮层内
进入会话层,并提供返回按钮。页面上方可切换“有待处理、仅运行中、仅当前项目运行、全部
空闲”四种场景。项目与会话均为演示数据;该级联交互已接入正式产品活动入口,范围与
验证记录见[实施进度](./progress.md)。
Demo 已移除左侧系统工具快捷区:文件、终端、浏览器和资源监控只在右侧
工作栏 Tab 中演示,不列入应用中心。旧原型仍直接打开整窗 Modal,其中使用可搜索应用列表。
Demo 待同步更正:本节仅描述旧原型,不是生产行为或验收依据。原型直接打开 Modal、知识库的启停设置,以及心跳默认启用和关闭仅隐藏入口的说明均已过时;原型的常驻排序也不代表生产中四个应用的共享顺序。生产规则见上述说明及 UI 设计。Demo 已同步“本机推理监控”名称及其默认不常驻状态。
六个方案共用“我的应用”区:通过 + 或“全部应用”选择常驻应用,编辑时可拖动或使用
上下箭头排序、移除入口。未常驻的已安装应用仍可从“全部应用”打开。商店包含“项目管理”
和“阅读空间”应用示例,支持仅安装或安装并常驻左侧菜单,常驻不表示后台运行。
常驻应用在本次页面会话内跨方案保持一致;“恢复默认应用”重置安装与常驻配置。
移除入口不会卸载应用或关闭当前页面,刷新后恢复默认。
上下箭头排序、移除入口。内置应用默认全部启用;魔法笔记、知识库、智能心跳默认常驻,计划预览“本机推理监控”默认不常驻。
取消常驻仅移除导航捷径,仍可从应用中心打开;关闭应用隐藏业务入口,应用中心保留设置与
重新开启入口,并记住常驻偏好。当前应用页关闭后显示不可用状态,重新启用从应用中心卡片设置进入。
知识库关闭时同步隐藏侧栏入口、会话选项中的知识范围和检索方式、已选知识标签;重新开启
恢复原来的选择。以上操作不删除数据,也不表示停止后台任务或模型服务。
“恢复默认应用”重置应用安装、启用、常驻和笔记设置,返回对话页;模型服务模拟状态保留至刷新。
魔法笔记提供应用中心“设置”、笔记页面“笔记设置”和全局设置演示入口,均打开同一设置
面板,共用本次页面会话内的配置。面板沿用现有选项:显示未完成待办数量、AI 评论方式
通用应用设置统一从应用中心卡片进入,应用页面和全局设置不提供重复入口,分别控制
启用与常驻。魔法笔记面板另含显示未完成待办数量、AI 评论方式
(即时/保存后自动/保存后手动)、AI 评论形式(长评 + 要点/长评/要点)。现有“显示
魔法笔记入口”在原型中与“常驻左侧菜单”统一,不额外增加功能启停开关。侧栏待办数为
固定的 3 项演示数据。修改即时生效于原型,不调用 AI 或写入真实应用设置。
魔法笔记入口”不在原型中重复提供。正式迁移保留其业务启用值,常驻使用独立配置。启用、常驻、待办数量显示默认开启;
评论方式默认即时,评论形式默认长评 + 要点。侧栏待办数为固定的 3 项演示数据。
修改即时生效于原型,不调用 AI 或写入真实应用设置。
此 Demo 中的任务、进度、使用方与服务内存为旧方案模拟内容;正式 Modal 已收窄为服务状态列表,见 [FR-16](./prd.md#fr-16-本机模型管理入口新增计划项)。
引擎和管理方式收进详情;受管进程模拟启动、停止、重启,进程内模型模拟加载与释放。
服务停止或重启后任务标为中断,重新启动不自动恢复任务;任务级取消仍待实际引擎接入。
影响使用方的操作先显示就地确认,取消保留原状态;隐藏或关闭管理应用不改变服务状态。
F 提供私有市场 URL、模拟获取目录、模拟不可达与重试,不发起网络请求或切换公共来源。
目录包含项目管理和阅读空间示例,可模拟安装或安装并常驻。YAML 文本支持导入预览和导出
预览,明确标为 mock schema,仅识别示例中的七个扁平字段,尚非通用 YAML 解析器。
导入列出依赖、外部资源和同标识冲突:缺少依赖或资源时不允许安装;内置应用不允许覆盖;
已安装扩展须显式确认更新定义,保留启用与常驻偏好。导入新应用在目录中以标识显示,原始名称
保留在定义及导出预览中。修改输入会使旧预览失效,取消不修改安装状态。
导出仅显示定义文本,不写文件,不包含凭据、会话、笔记、知识数据、程序包或模型权重。
| 方案 | 讨论重点 |
| --- | --- |
@@ -56,7 +92,7 @@ Demo 可直接在浏览器中打开,支持方案切换、浅深主题、添加
| C:应用预览 | 用卡片说明应用用途 |
| D:搜索启动台 | 搜索打开能力,支持 Ctrl / Command K、方向键与 Enter |
| E:常驻管理 | 已常驻应用优先排列,可取消常驻或打开 |
| F:商店发现(未来) | 默认展开模拟商店,不纳入首期 |
| F:私有市场预览(未来) | URL 模拟获取、YAML 导入导出与显式模拟安装,不纳入首期 |
D 参考 [Raycast](https://www.raycast.com/) 的搜索启动与扩展发现方式。早期方案参考过
Notion 收藏和 VS Code Profiles,当前版本已移除场景工具包与快捷区。
@@ -68,7 +104,7 @@ Notion 收藏和 VS Code Profiles,当前版本已移除场景工具包与快
| 术语 | 定义 |
| --- | --- |
| 系统入口 | GoodBuddy 工作流程或审计所必需的固定入口,用户不能卸载、隐藏或改变类型 |
| 应用 | 在主内容区打开完整一级页面的功能,可显示在“当前对话”所在的主导航区 |
| 应用 | 可显示在主导航区的功能;知识库、智能心跳和魔法笔记打开一级页面,本机推理监控打开保留底层工作区的 Modal |
| 系统工具 | GoodBuddy 自带且不可卸载的辅助能力,包括文件、终端、浏览器和资源监控,由右侧助手工作栏 Tab 承载 |
| 插件应用 | 未来由插件提供的一级页面入口 |
| 插件工具 | 未来由插件提供的辅助面板或命令入口 |
@@ -81,3 +117,4 @@ Notion 收藏和 VS Code Profiles,当前版本已移除场景工具包与快
- [助手工作栏多终端页签 PRD](../assistant-workbar/terminal-tabs-prd.md)
- [智能心跳 PRD](../smart-heartbeat/prd.md)
- [知识库功能文档](../knowledge-base/README.md)
- [文档解析入口与职责](../document-processing/ui-design.md#11-入口与职责):全局 OCR 配置、附件与知识文档操作的目标分工;不新增应用中心成员,本机推理监控不管理 HTTP OCR 服务。
@@ -4,9 +4,9 @@
| 项目 | 内容 |
| --- | --- |
| 状态 | 设计中 |
| 版本 | 0.2 |
| 日期 | 2026-09-08 |
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
| 版本 | 0.4 |
| 日期 | 2026-09-17 |
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
| 关联 User Stories | [User Stories](./user-stories.md) |
@@ -15,9 +15,11 @@
1. “当前对话”“运行记录”和“设置”始终是系统入口。
2. 运行记录不进入工具注册表、应用显隐设置或插件市场。
3. 文件浏览器、终端、浏览器和资源监控始终是系统工具,不能卸载或被插件替换。
4. 左下角只提供快捷入口,工具内容仍由助手工作栏承载。
5. 隐藏应用不等于停用功能,也不删除、迁移或清理业务数据。
6. 快捷入口不能扩大执行空间、文件、PTY、网络或浏览器权限。
4. 左下角提供应用中心和独立固定设置入口;文件、终端、浏览器、资源监控只通过右侧工作栏 Tab 和“+”访问,不进入应用中心。
5. 四个内置应用共享保存顺序。知识库始终启用并常驻,位置可调整;智能心跳、魔法笔记、本机推理监控可独立设置启用和常驻。取消常驻不关闭功能,关闭不删除数据或导航偏好。
6. 入口不能扩大执行空间、文件、PTY、网络或浏览器权限。
7. 应用中心始终可达,点击向上展开无模态遮罩的锚定菜单,只列已启用应用,不使用打开历史或常驻过滤。菜单底部“管理应用”打开既有 Modal,关闭的可选应用在其中仍可搜索、配置和重新开启。
8. 所有入口和新请求的功能判断读取同一份保存成功的权威设置;保存中的目标值不提前生效。
## 2. 入口模型
@@ -25,14 +27,21 @@
type NavigationContribution =
| {
type: 'system-entry'
id: 'chat' | 'activity' | 'settings'
id: 'chat' | 'activity' | 'application-center' | 'settings'
placement: 'primary-start' | 'primary-end' | 'footer'
}
| {
type: 'application'
id: 'magic-notes' | 'knowledge' | 'heartbeat'
id: 'knowledge'
source: 'builtin'
visible: boolean
alwaysShown: true
}
| {
type: 'application'
id: 'magic-notes' | 'heartbeat' | 'local-inference'
source: 'builtin'
enabled: boolean
pinned: boolean
}
| {
type: 'system-tool'
@@ -41,7 +50,8 @@ type NavigationContribution =
}
```
该模型只覆盖首期入口。插件贡献模型随插件 Host、权限和生命周期另行设计。
该模型是首期的逻辑投影,启用值来自各应用的唯一业务配置,常驻及顺序来自导航偏好。
应用清单为魔法笔记、知识库、智能心跳、本机推理监控,打开无需安装。市场不加入本轮清单。
## 3. 主导航组合规则
@@ -49,35 +59,60 @@ type NavigationContribution =
```text
当前对话
可见的内置应用
应用(按保存顺序;知识库始终显示,可选应用仅显示启用且常驻项)
运行记录
```
当前对话和运行记录不参与排序。首期内置应用默认顺序为魔法笔记、知识库、智能心跳;显隐
设置只从中移除对应项,不改变固定入口的位置。
当前对话、运行记录和设置不参与应用排序。默认应用顺序为知识库、智能心跳、魔法笔记、本机推理监控;魔法笔记默认常驻,本机推理监控默认不常驻。过滤可选项不留下空白。
常驻、取消常驻和排序按设备保存,重启后恢复。排序只发生在应用区,不移动固定系统入口。
管理列表、应用菜单和主导航共用完整应用顺序,分别按搜索、启用、启用且常驻过滤;过滤不删除顺序记录,重新开启或常驻时按保存顺序恢复。
每个管理行均可排序,包括关闭或未常驻的应用。上下移动以完整保存顺序中的相邻应用为目标,拖动使用同一移动规则。搜索时禁用拖动,箭头仍按完整顺序移动,保留未命中应用及其相对顺序。
### 3.1 应用可见性与业务状态
| 导航可见 | 功能启用 | 结果 |
| --- | --- | --- |
| 是 | 是 | 显示入口并允许进入 |
| 否 | 是 | 隐藏入口,后台和数据保持原状 |
| 是 | 否 | 不显示不可用入口;设置页保留启用路径 |
| 否 | 否 | 不显示入口,保留符合该功能自身规则的数据 |
下表适用于智能心跳、魔法笔记和本机推理监控。知识库始终可用,管理 Modal 中提供“打开”和排序,保留原工作区实际配置,不增加启用、常驻开关或通用空设置页。心跳应用的默认值与执行条件见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。
“功能启用”由各应用现有产品设置负责。例如魔法笔记启停仍使用原设置;导航偏好不能覆盖它。
| 常驻偏好 | 功能启用 | 结果 |
| --- | --- | --- |
| 是 | 是 | 主导航显示,可从主导航或应用中心打开 |
| 否 | 是 | 主导航隐藏,仍可从应用中心打开,后台和数据保持原状 |
| 是 | 否 | 主导航、应用菜单和业务入口隐藏,管理 Modal 保留设置,记住常驻及顺序 |
| 否 | 否 | 主导航、应用菜单和业务入口隐藏,管理 Modal 保留设置,记住未常驻状态 |
管理 Modal 浏览和搜索包含关闭的可选应用,搜索不修改启用或常驻。轻量菜单仅显示已启用项,点击行先关闭菜单;本机推理监控打开独立 Modal,其余应用请求主内容区导航。管理 Modal 的“打开”和常驻入口遵守同一规则。打开已启用应用不自动常驻;
取消当前应用的常驻不关闭当前页面或本机推理监控 Modal。关闭应用后不能从旧入口继续执行新的业务操作。
停用时仍允许管理常驻偏好,界面说明重新开启后才会显示入口。
### 3.2 关闭应用与恢复
关闭与恢复适用于可选应用,知识库页面、会话控件及关联不受影响。智能心跳关闭后的调度、手动请求、侧栏与在途运行按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)处理,保留单条计划 enabled。
| 对象 | 关闭后的结果 | 重新开启后的结果 |
| --- | --- | --- |
| 当前已打开应用页或本机推理监控 Modal | 原位置显示“应用已关闭”的不可用状态;保留内容状态及草稿,不强制跳转。重新启用从应用中心对应行的设置进入,监控 Modal 需先关闭 | 恢复可用性;仍打开时恢复内容,已关闭或离开则不自动打开 |
| 已在执行的请求及后台操作 | 沿用功能自身的取消、完成和清理生命周期;本次应用开关不新增取消策略 | 不自动恢复、中断后重跑或补发任务 |
| 数据、常驻及顺序 | 保留原值,业务入口按启用状态过滤 | 按保存的常驻及顺序恢复入口,不把未常驻应用自动常驻 |
知识请求及单个知识库实例停用、配置修改或删除继续遵守知识库自身规则,没有应用级禁用判断。
### 3.3 魔法笔记设置迁移
魔法笔记设置迁入应用中心对应行的设置页;笔记页面和全局设置均不提供重复入口,
编辑同一配置。启用、常驻、显示未完成待办数量分别控制功能、导航和徽标,不保留重复的
“显示魔法笔记入口”开关。
未配置的魔法笔记、本机推理监控启用及魔法笔记相关布尔项默认开启;心跳应用默认关闭。魔法笔记和心跳默认常驻,本机推理监控默认不常驻;已启用应用仍可从应用菜单及管理 Modal 打开。已有保存的 `true` 和 `false` 均保留。
AI 评论方式和评论形式继续为单选,沿用现有默认值“即时”和“长评 + 要点”,不能解释为
全部模式同时启用。迁移保留既有单选值及业务数据。关闭数量显示只隐藏徽标,不修改待办。
## 4. 系统工具打开规则
### 4.1 通用流程
```text
用户激活工具按钮
-> 校验按钮仍可用
-> 向助手工作栏发送打开意图
-> 工作栏按应用实例策略查找或创建实例
-> 展开工作栏
-> 激活目标实例
用户展开右侧工作栏
-> 选择已有 Tab,或从“+”目录选择工具
-> 已有 Tab 按实例 ID 激活;目录按单实例或多实例策略创建或激活
-> 把焦点移入面板标题或首个主要控件
```
@@ -85,31 +120,27 @@ type NavigationContribution =
### 4.2 决策表
| 工具 | 匹配实例存在 | 匹配实例不存在 | 上下文不可用 |
| 工具 | 选择已有 Tab | 从“+”目录选择 | 上下文不可用 |
| --- | --- | --- | --- |
| 文件 | 激活不可关闭的单实例并保留其跟随或固定状态 | 仅在迁移或布局修复时补回 `workspace` | 打开面板并显示目录修复入口 |
| 终端 | 激活当前执行空间最近的 `starting` 或 `running` 实例 | 创建一个绑定当前执行空间的实例 | 没有项目时使用本机 Home |
| 浏览器 | 激活绑定当前 Conversation 的最近使用实例 | 创建绑定当前 Conversation 的新实例 | 激活最近使用实例;不存在时创建待绑定实例 |
| 资源监控 | 激活 `resources` 单实例 | 创建本机 `resources` 单实例 | 部分指标不可用时显示原因并保留其余指标 |
| 文件 | 激活不可关闭单实例并保留跟随或固定状态 | 聚焦 `workspace`;仅迁移或布局修复时补回 | 跟随模式无有效项目目录或默认工作区时显示修复入口 |
| 终端 | 激活选中的确切实例,保留执行空间和历史状态 | 每次创建绑定当前执行空间的额外实例 | 新建时无项目使用本机 Home;SSH 失败保留远端目标与诊断 |
| 浏览器 | 激活选中的确切实例,保留绑定目标和页面 | 每次创建绑定当前 Conversation 的额外实例 | 新建时无 Conversation 创建待绑定空实例 |
| 资源监控 | 激活本机 `resources` 单实例 | 已存在则聚焦,否则创建 | 部分指标不可用时显示原因并保留其余指标 |
文件保持单一实例原有绑定。浏览器快捷入口按当前 Conversation 匹配最近使用实例,而工作栏
“+”始终新建浏览器实例。固定目标失效时显示该目标的修复状态,不回退到当前项目、默认
工作区或当前会话。
`closing`、`exited`、`interrupted` 和 `failed` 终端不属于可复用实例。当前执行空间只有这些
终端时,快捷按钮创建新实例并保留原 Tab 的历史状态。用户需要额外的并行终端时使用工作栏
“+”或终端面板中的既有创建命令。
选择已有 Tab 不按当前项目或 Conversation 查找替代实例,也不重绑目标。文件的跟随模式
仍按工作栏自身规则更新上下文;固定目标失效时显示该目标的修复状态,不静默回退。
终端 `closing`、`exited`、`interrupted`、`failed` Tab 保留原状态,选择它们不自动重建。
终端和浏览器通过“+”新建时不按目标去重,不恢复旧快捷按钮的最近匹配实例复用行为。
### 4.3 选中与状态
工具按钮满足以下条件时显示选中:助手工作栏已展开,且当前活动实例的 `appId` 与工具映射
一致。只有实例存在但未激活时不显示选中。
工作栏按确切实例 ID 表达 Tab 选中状态,同类型其他实例不能同时显示为选中。“+”是普通
按钮,不是 Tab。主侧栏和应用中心不显示工具选中状态或工具徽标。
徽标优先级为:等待用户处理 > 错误 > 运行中 > 无徽标。同一按钮只显示一个视觉徽标,
徽标优先级为:等待用户处理 > 错误 > 运行中 > 无徽标。同一 Tab 只显示一个视觉徽标,
可访问名称包含完整状态。
资源监控首期不在后台采样,因此不显示负载告警徽标。只有资源面板是工作栏当前活动实例且
工作栏已展开时,资源按钮才显示选中状态。
资源监控首期不在后台采样,因此不显示负载告警徽标。后台事件不自动展开工作栏或切换 Tab。
### 4.4 资源采样状态
@@ -134,44 +165,74 @@ warming/loading/live/partial/error -> inactive
## 5. 设置保存规则
应用导航偏好与应用业务启停分开保存。Switch 每次切换立即提交完整目标集合;请求完成前禁用
三个应用显隐 Switch,下一次操作基于最新确认结果提交,防止并发更新互相覆盖。
应用导航偏好与业务启停使用独立字段,通过同一应用设置保存链路提交。常驻、排序、启用和
魔法笔记设置更新串行执行;前一请求确认前,所有入口中会修改这组设置的控件暂不可操作。
每次只提交本次变更,Main 在最新已保存设置上合并并原子写入;顺序提交完整目标顺序。
持久化成功后由 Store 发布完整权威快照,应用中心、主导航、当前应用页、会话和所有设置入口同步采用,工具配置写入也走同一通知链路。每次重开中心先刷新并锁定修改;较新的设置事件使在途旧读取及旧保存响应失效,不能覆盖新快照。
保存失败时:
- 主导航和 Switch 恢复最后一次成功保存的偏好。
- 设置行显示可重试错误。
- 不回滚或修改各应用的业务状态。
- 所有界面保留最后一次确认的配置,包括启用、常驻、顺序、徽标和会话入口;不提前运行关闭或恢复副作用。
- 发起操作的设置行或排序区域显示就地可重试错误,保留焦点及编辑上下文,不重复显示同一错误通知。
- 若响应中断导致保存结果未知,先重读权威设置再开放修改;不能把本地旧值当成回滚成功,也不自动重发变更。
## 6. 与需求的对应关系
| 逻辑 | PRD | User Stories |
| --- | --- | --- |
| 固定入口不变量 | FR-1、FR-2 | US-A1、US-A2 |
| 应用显隐和失败恢复 | FR-3、FR-4、FR-11、FR-12 | US-B1、US-B2、US-B4、US-B5 |
| 工具打开决策表 | FR-5 至 FR-10 | US-C1 至 US-C7 |
| 应用常驻和失败恢复 | FR-3、FR-4、FR-11、FR-12 | US-B1、US-B2、US-B4、US-B5 |
| 应用中心、常驻顺序 | FR-3、FR-4、FR-5、FR-11、FR-12;PRD 第 8 节 | US-B6、US-B7 |
| 可选应用启用恢复、知识库固定可用 | FR-3、FR-4、FR-12;PRD 第 8 节补充要求 | US-B8 |
| 魔法笔记迁移及共同配置 | FR-11、FR-12;PRD 第 8 节补充要求 | US-B9 |
| 工具打开决策表 | FR-6 至 FR-10 | US-C1 至 US-C7 |
| 资源采样状态 | FR-9 | US-C6 |
| 无障碍与窄窗口 | FR-13 | US-D1、US-D2 |
| 活动聚合、完成通知与查看清除 | FR-14 | US-E1、US-E2、US-E3 |
| 后续私有市场与 YAML | FR-15 | US-F1、US-F2 |
| 服务状态、资源可用性、服务生命周期 | FR-16 | US-F3、US-F5;US-F4 入口已移除 |
稳定 ID 保留原编号。US-B6 为应用中心,US-B7 为顺序,US-B8 为启用联动,US-B9 为笔记迁移;
US-F3 为服务概览,US-F4 为已移除的任务取消入口,US-F5 为服务生命周期。PRD 旧 FR-5 至 FR-10 和
User Stories 已同步上述入口规则,工具实例行为沿用右侧工作栏契约。
## 7. 逻辑完整性评估
首期规则已覆盖固定入口、应用显隐、保存失败、系统工具匹配与显式多实例差异、本机 Home、
托管 SSH、无活动会话、目录失效、资源部分可用、采样停止、权限边界、键盘和窄窗口。其他
工具面板内部的取消、超时、
连接恢复和固定目标失效继续由助手工作栏负责,本功能不复制这些状态机。
首期覆盖应用中心访问、启用与常驻组合、顺序恢复、默认值与旧值迁移、共同设置、保存失败、
知识库固定入口、心跳应用启停、可选应用已打开页面恢复,以及右侧实例选择、本机 Home、托管 SSH、无会话、
目录失效、资源部分可用与采样停止。工具内部取消、超时、连接恢复及业务后台生命周期由
相应功能负责,本功能不复制状态机。底部入口打开轻量菜单,“管理应用”才进入与设置相同的整窗 Modal。实现与验证的剩余范围见[实施进度](./progress.md)。
## 8. 后续产品决策
- 插件工具上线后,快捷栏允许固定的最大数量及溢出规则。
- 插件应用是否允许用户排序,以及固定系统入口之间允许的排序范围。
后续市场 URL、YAML 交换、ShareServer 目录与分发、插件注册及安装不进入首期。其协议、
扩展身份与安装生命周期由市场功能另行确定;内置应用常驻及排序不依赖这些决策。
这些决策不影响首期四个系统工具的固定入口和内置应用显隐实现。
### 8.1 本机推理管理
FR-16 独立于市场排期,作为可常驻的管理 Modal。所有打开入口只改变 Modal 打开状态,不改变底层工作区、项目、会话或草稿;关闭时卸载 Modal,服务生命周期独立。US-F3 只有服务列表,展示名称、状态、引擎、模型及错误。共享服务只展示一次;资源按独立服务进程归属,CPU 预热与指标不可用分别给出原因,不填 `0`,不将宿主进程占用分摊给共享线程。采样与单位见[技术设计](./technical-design.md)。ASR 没有 Worker 就绪观测,显示按需运行且状态未知;活动请求不覆盖服务状态。OCR 状态来自 Renderer Worker,向量状态来自 Broker。
US-F4 的 Modal 单任务取消入口已移除。后端取消行为保留:Worker 确认或退出前仍计入活动任务和停止影响,即使调用方已收到取消或超时错误;任务先完成则保留真实完成结果,不通过终止共享服务模拟单任务取消。
| 服务管理方式 | 可提供操作 | 结果要求(US-F5) |
| --- | --- | --- |
| GoodBuddy 受管子进程 | 引擎已支持的启动、停止、重启 | 共用同一服务实例;操作成功由真实进程和服务就绪状态确认 |
| 外部服务 | 在模型与连接设置中管理 | 不列入本机服务,不推断本机归属 |
| 进程内模型 | 引擎实际支持的加载、释放 | 不显示虚构 PID 或进程启动、停止状态 |
停止、重启或释放均先打开紧凑的嵌套影响确认,列出任务和影响;无已登记活动任务时说明执行前再次核对。取消确认不改变运行状态。
确认存在时禁止关闭底层管理 Modal;提交期间两层均不能关闭,重复操作被拒绝。未提交时 Escape 只关闭确认,确认关闭后才可用 Escape 关闭管理 Modal。焦点规则由 [UI 设计](./ui-design.md#91-本机推理管理)定义。
实际操作失败关闭旧确认、显示错误并刷新快照;刷新期间或失败时禁用服务操作。再次操作重新取得当前任务清单并要求确认,避免重复提交旧清单。
再次启动不重放已中断任务;关闭管理 Modal、关闭管理应用或取消常驻不停止服务,卸载模型文件为独立操作。
当前向量服务允许确认影响后停止,并拒绝确认清单未包含的新任务;OCR 仅空闲时允许释放。
ASR 按请求加载与释放,支持独立取消;TTS 尚无执行服务。具体适配边界以
[技术清单](./technical-design.md#111-本机推理任务与执行服务)为准。活动请求仅在影响确认中展示,主列表不展示历史。应用退出清理沿用各引擎原有路径。
## 9. 项目活动汇总
对应 [FR-14](./prd.md#fr-14-项目活动汇总),按 Conversation ID 聚合,每条会话最多一行。
数据来源为活动会话 ID、会话中的流式消息及待回答/待审批请求,以及全部可见的运行中或
等待处理 Task;不能只取当前项目、当前搜索结果或每条会话的最新 Task。
等待处理 Task,以及当前应用会话内的已完成未查看集合;不能只取当前项目、当前搜索结果或每条会话的最新 Task。
| 优先级(高到低) | 条件 | 汇总类别 |
| --- | --- | --- |
@@ -179,10 +240,30 @@ warming/loading/live/partial/error -> inactive
| 待审批 | 消息有待审批请求 | 待处理 |
| Task 等待处理 | Task 为 `waiting_approval`,尚无更具体的消息状态 | 待处理 |
| 运行中 | 活动会话 ID、`streaming` 消息或运行中 Task | 运行中 |
| 已完成 | 会话在已完成未查看集合中,且无更高优先级实时状态 | 已完成 |
同一会话只采用最高优先级状态;Task 的 `waiting_approval` 也可能对应提问,不能直接
标成待审批。“待处理”和“运行中”数量互斥,全局总数为两者之和;项目计数使用相同结果。
标成待审批。“待处理”“运行中”和“已完成”数量互斥,全局总数为三者之和;项目计数使用相同结果。
项目归属优先取会话元数据,缺少会话时取 Task 元数据;无项目归属的活动只计入全局汇总。
历史排队项、仅待调度的 Task、终态 Task 和未读标记本身不构成活动。活动结束且没有其他
运行或等待条件时移除该行,不能因历史任务或未读状态继续计数。
历史排队项、仅待调度的 Task、终态 Task 和普通未读标记本身不构成活动。完成通知规则如下:
- 本地运行收到成功 `done`,或当前活动运行从持久化快照确认成功完成时,记录会话完成通知。
- 后台顶层 Task 只有在本次应用会话中已观察到该任务,随后状态转为 `completed` 时才记录通知;
初次载入或首次出现的已完成历史任务只建立基线,子任务不单独产生通知。
- 失败、取消不产生完成通知。实时待处理或运行中状态优先;开始新运行时清除旧完成通知,
已观察 Task 转为 `running` 或 `waiting_approval` 时也清除对应会话的旧通知。
- 通知只保存在 Renderer 内存,不跨重载或重启恢复,也不从普通未读状态推导。
- 仅当目标会话实际成为当前聊天、设置未打开且文档未隐藏时清除通知;普通侧栏导航和活动菜单
导航采用同一查看条件。已在查看的会话完成后不保留通知,恢复可见聊天时也清除通知。
- 展开活动项目、查看菜单、设置覆盖下保留挂载的聊天、隐藏文档以及取消设置离开都不算查看。
没有实时状态且没有已完成未查看通知时才移除活动行。
级联菜单先按项目归属聚合活动行,沿用本地、SSH Host、通道的稳定项目顺序;缺少项目
元数据的归属组及“未归属项目”组放在已知项目之后。无归属组只用于访问这些会话,不增加
项目计数。每个项目内依次列待处理、运行中、已完成会话,同类别保留来源顺序。
展开项目只改变菜单层级,不导航。选中项目的活动行全部移除后返回项目列表;全局无活动行后
关闭菜单并隐藏摘要入口。后台更新保留仍存在的焦点项,删除焦点项时将焦点
移至当前层可用项。关闭菜单后,新的活动只恢复摘要,不会自动重新打开菜单。
@@ -5,7 +5,43 @@
--accent: #086fc7; --accent-subtle: #e8f4ff; --accent-selected: #a7cfea;
--font-family-ui: "Inter Variable", "PingFang SC", "Microsoft YaHei UI", "Noto Sans SC Variable", system-ui, sans-serif;
--radius-control: 10px; --radius-card: 14px;
--warning: #865200;
}
[data-theme="dark"] { --warning: #f2bd66; }
.project-lab { max-width: 1440px; margin: 0 auto 12px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; color: var(--text-secondary); font-size: 12px; }
.project-lab select { font: inherit; color: var(--text-primary); background: var(--surface-raised); padding: 4px 8px; border: 1px solid var(--border-default); border-radius: var(--radius-control); }
.project-entry { position: relative; margin-bottom: 12px; }
#project-trigger { width: 100%; display: flex; align-items: center; gap: 8px; text-align: left; border: 1px solid var(--border-default); }
#project-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.project-waiting { color: var(--warning); font-weight: 600; }
#activity-trigger { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin: -8px 0 12px; padding: 4px 8px; color: var(--text-secondary); font-size: 11px; text-align: left; }
#project-activity { position: fixed; inset: auto; margin: 0; padding: 0; border: 1px solid var(--border-default); border-radius: var(--radius-control); background: var(--surface-raised); color: var(--text-primary); max-width: calc(100vw - 32px); }
#project-activity:popover-open { display: flex; align-items: stretch; }
#project-activity > section { width: 264px; max-width: calc(100vw - 34px); max-height: min(480px, calc(100dvh - 32px)); overflow: auto; padding: 8px; }
#project-activity h3 { margin: 8px; font-size: 11px; font-weight: 500; color: var(--text-secondary); overflow-wrap: anywhere; }
#activity-project-list > p { padding: 8px; }
#activity-project-list button { display: grid; grid-template-columns: minmax(0, 1fr) auto; width: 100%; gap: 4px 8px; text-align: left; }
#activity-project-list button > span:first-child { overflow-wrap: anywhere; }
#activity-project-list small { grid-column: 1; font-size: 11px; color: var(--text-secondary); }
#activity-project-list button > span:last-child { grid-column: 2; grid-row: 1 / 3; align-self: center; }
#activity-project-list [aria-expanded="true"] { background: var(--accent-subtle); color: var(--accent); }
#activity-sessions { border-left: 1px solid var(--border-default); }
#activity-back { display: none; font-size: 12px; color: var(--accent); }
#project-activity.activity-left { flex-direction: row-reverse; }
.activity-left #activity-sessions { border-left: 0; border-right: 1px solid var(--border-default); }
.activity-compact.has-submenu #activity-projects { display: none; }
.activity-compact #activity-back { display: block; }
.activity-compact #activity-sessions { border: 0; }
#project-menu { position: fixed; inset: auto; margin: 0; width: min(300px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow: auto; padding: 8px; border: 1px solid var(--border-default); border-radius: var(--radius-control); background: var(--surface-raised); color: var(--text-primary); }
#project-list h3, #activity-list h3 { margin: 8px; font-size: 11px; font-weight: 500; color: var(--text-secondary); }
#project-list button { width: 100%; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; text-align: left; }
.project-row-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
#project-list [aria-current="true"] { background: var(--accent-subtle); color: var(--accent); }
#project-list small { width: 100%; display: flex; gap: 12px; font-size: 11px; color: var(--text-secondary); }
#activity-list button { display: grid; grid-template-columns: minmax(0, 1fr); width: 100%; text-align: left; padding: 12px; }
#activity-list strong { font-weight: 500; overflow-wrap: anywhere; }
#activity-list small { grid-column: 1; color: var(--text-secondary); overflow-wrap: anywhere; }
.history button small { display: block; font-size: 11px; color: var(--text-secondary); }
[data-theme="dark"] { color-scheme: dark; --surface-canvas: #07131f; --surface-raised: #102335; --surface-subtle: #162c40; --text-primary: #edf7ff; --text-secondary: #b5c6d5; --border-default: #29445a; --accent: #72c5ff; --accent-subtle: #123b5b; --accent-selected: #28668f; }
* { box-sizing: border-box; }
body { margin: 0; padding: 24px 32px; font: 13px/1.6 var(--font-family-ui); background: var(--surface-subtle); color: var(--text-primary); }
@@ -86,6 +122,27 @@ dialog[data-variant="grid"] #catalog-items { display: grid; grid-template-column
.sidebar > * { flex-shrink: 0; }.sidebar .history { flex: 1 1 80px; min-height: 0; }.sidebar .shortcuts { flex-shrink: 1; min-height: 80px; }
.sidebar .resident-apps { margin-top: 12px; min-height: 96px; flex-shrink: 1; display: flex; flex-direction: column; max-height: 240px; }.resident-apps .section-label { margin-bottom: 4px; flex-shrink: 0; }#resident-list { overflow: auto; min-height: 0; }.resident-row { display: flex; align-items: center; }.resident-row > button { display: flex; align-items: center; gap: 10px; padding: 8px 10px; flex: 1; min-width: 0; text-align: left; }.resident-row > button span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }.resident-row [aria-current="page"] { background: var(--accent-subtle); color: var(--accent); }.resident-row[draggable="true"] { cursor: grab; }.resident-row .edit-actions { flex-shrink: 0; }.all-apps { display: flex; justify-content: space-between; padding: 6px 10px; font-size: 11px; color: var(--text-secondary); flex-shrink: 0; }.empty-apps { padding: 8px 10px; font-size: 11px; color: var(--text-secondary); }.app-placement { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }.app-placement button { font-size: 11px; padding: 6px 8px; }.app-placement button[aria-pressed="true"] { border: 1px solid var(--accent-selected); }.app-catalog-row { align-items: flex-start; }.app-catalog-row > svg { margin-top: 4px; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.workspace { min-height: 0; }
.workspace-header { flex: 0 0 auto; flex-wrap: wrap; gap: 8px; align-items: center; }
#page-label { overflow-wrap: anywhere; }
.conversation { min-height: 0; overflow: auto; }
.conversation.app-page { justify-content: flex-start; padding: 24px; max-width: 960px; gap: 12px; }
.app-page h1 { font-size: 24px; }
.model-list { width: 100%; }
.model-service { padding: 16px 0; border-bottom: 1px solid var(--border-default); }
.model-service p, .model-service details { color: var(--text-secondary); font-size: 12px; }
.model-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
.model-actions button, .inline-confirm button { border: 1px solid var(--border-default); }
.inline-confirm { padding: 12px; margin: 8px 0; background: var(--accent-subtle); border: 1px solid var(--accent-selected); border-radius: var(--radius-control); }
#market-preview { padding: 12px 0; border-bottom: 1px solid var(--border-default); }
#market-preview label { display: block; margin: 8px 0; }
#market-preview textarea, #market-preview select { width: 100%; padding: 8px; font: inherit; background: var(--surface-subtle); color: var(--text-primary); border: 1px solid var(--border-default); border-radius: var(--radius-control); }
#market-preview textarea { resize: vertical; }
#market-preview p, #import-preview { font-size: 12px; overflow-wrap: anywhere; }
#market-preview summary, .model-service summary { cursor: pointer; }
.note-segments, .center-switch, .tool-tabs { flex: 0 0 auto; }
.center-app-info { min-width: 0; overflow-wrap: anywhere; }
dialog, dialog::backdrop { -webkit-app-region: no-drag; }
.launcher-trigger { display: flex; align-items: center; gap: 8px; width: 100%; padding: 12px 8px; border: 1px solid var(--border-default); background: var(--surface-raised); margin-bottom: 12px; font-size: 12px; }.launcher-trigger kbd { margin-left: auto; font: 10px var(--font-family-ui); color: var(--text-secondary); white-space: nowrap; }
[data-variant="launcher"] .shortcut-list { grid-template-columns: repeat(4, minmax(0, 1fr)); }[data-variant="launcher"] .shortcut-button { flex-direction: column; padding: 8px 0; }[data-variant="launcher"] .icon-tile { height: 24px; }[data-variant="launcher"] .store-link { border-top: 1px solid var(--border-default); border-radius: 0; }
[data-variant="favorites"] .section-label { padding-left: 0; }[data-variant="favorites"] .shortcut-list { gap: 0; }[data-variant="favorites"] .shortcut-button { justify-content: flex-start; padding: 8px 12px; }[data-variant="favorites"] .item-name { font-size: 12px; }[data-variant="favorites"] .shortcut-item { display: flex; align-items: center; }[data-variant="favorites"] .edit-actions { margin-left: auto; }[data-variant="favorites"] .store-link { margin-top: 12px; border: 1px dashed var(--border-default); }.count { padding: 0 6px; color: var(--text-secondary); }
@@ -94,4 +151,4 @@ dialog[data-variant="grid"] #catalog-items { display: grid; grid-template-column
.catalog-pack { padding: 20px 0; border-bottom: 1px solid var(--border-default); }.catalog-pack h3 { margin: 8px 0; font-size: 18px; font-weight: 550; }.pack-art { display: grid; place-items: center; float: right; width: 56px; height: 56px; border: 1px solid var(--accent-selected); background: var(--accent-subtle); color: var(--accent); border-radius: var(--radius-card); }.pack-art svg { width: 28px; height: 28px; }.pack-includes { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0; }.pack-includes span { padding: 4px 8px; font-size: 11px; background: var(--surface-subtle); border-radius: var(--radius-control); }.pack-includes svg { width: 14px; height: 14px; }.catalog-pack > button { border: 1px solid var(--accent-selected); color: var(--accent); background: var(--accent-subtle); }
dialog[data-mode="launch"] { width: min(640px, calc(100% - 32px)); }dialog[data-mode="launch"] .catalog-row:focus-within { background: var(--accent-subtle); }.catalog-row { padding-left: 8px; padding-right: 8px; }
@media (max-width: 1080px) { body { padding: 16px; }.app { position: relative; }.workbar { position: absolute; right: 0; top: 52px; bottom: 0; width: min(320px, 100%); box-shadow: -8px 0 24px var(--border-default); }.lab-header { flex-wrap: wrap; gap: 12px; }.design-note > span:last-child { display: none; }.conversation { padding: 32px 24px; } }
@media (max-width: 640px) { body { padding: 12px; }.lab-header > div:first-child { flex: 1; }.variants { order: 3; width: 100%; }.variants button { flex: 1; padding: 8px 4px; font-size: 12px; }.design-note { align-items: flex-start; gap: 12px; }.app { height: auto; min-height: 0; }.app .sidebar { width: 100%; flex: 0 0 auto; border: 0; padding-top: 12px; }.brand { padding-bottom: 12px; }.app .history { display: none; }.app .workspace { display: flex; min-height: 320px; border-top: 1px solid var(--border-default); }.app .workspace .composer { display: block; }.composer-area { margin: 12px 12px 4px; margin-top: auto; }.composer-toolbar { gap: 2px; }.composer-select select { max-width: 108px; font-size: 11px; }.composer-toolbar button { font-size: 11px; padding: 6px; }.composer-toolbar .composer-icon { width: 28px; }.composer-meta { flex-wrap: wrap; gap: 0; }.shortcut-list { max-height: 300px; overflow: auto; }.lab-footer { align-items: flex-start; }.lab-footer button { white-space: nowrap; }.workbar { top: 0; width: 100%; }.design-note p { font-size: 11px; } }
@media (max-width: 640px) { body { padding: 12px; }.lab-header > div:first-child { flex: 1; }.variants { order: 3; width: 100%; }.variants button { flex: 1; padding: 8px 4px; font-size: 12px; }.design-note { align-items: flex-start; gap: 12px; }.app { height: auto; min-height: 0; }.app .sidebar { width: 100%; flex: 0 0 auto; border: 0; padding-top: 12px; }.brand { padding-bottom: 12px; }.app .history { display: none; }.app .workspace { display: flex; min-height: 320px; border-top: 1px solid var(--border-default); }.app .workspace .conversation { display: flex; overflow: visible; padding: 16px; }.app .workspace .composer { display: block; }.composer-area { margin: 12px 12px 4px; margin-top: auto; }.composer-toolbar { gap: 2px; }.composer-select select { max-width: 108px; font-size: 11px; }.composer-toolbar button { font-size: 11px; padding: 6px; }.composer-toolbar .composer-icon { width: 28px; }.composer-meta { flex-wrap: wrap; gap: 0; }.shortcut-list { max-height: 300px; overflow: auto; }.lab-footer { align-items: flex-start; }.lab-footer button { white-space: nowrap; }.workbar { top: 0; width: 100%; }.design-note p { font-size: 11px; } }
@@ -16,14 +16,19 @@
<button data-variant="cards" aria-pressed="false">C · 应用预览</button>
<button data-variant="launcher" aria-pressed="false">D · 搜索启动台</button>
<button data-variant="favorites" aria-pressed="false">E · 常驻管理</button>
<button data-variant="scenes" aria-pressed="false">F · 商店发现</button>
<button data-variant="scenes" aria-pressed="false">F · 私有市场预览</button>
</div>
<button id="theme">切换深色</button>
</header>
<div class="design-note"><span id="variant-label">A / 紧凑 Dock</span><p id="variant-description"></p><span>交互原型 · 商店与指标均为演示数据</span></div>
<div class="design-note"><span id="variant-label">A / 紧凑 Dock</span><p id="variant-description"></p><span>交互原型 · 全部状态仅在内存中,刷新重置</span></div>
<div class="project-lab"><label>项目活动演示 <select id="project-scenario"><option value="waiting">有待处理</option><option value="running">仅运行中</option><option value="current">仅当前项目运行</option><option value="idle">全部空闲</option></select></label><span>有活动时显示摘要,悬停或点击项目向右展开会话。</span></div>
<main class="app" data-variant="dock">
<aside class="sidebar" aria-label="主侧栏">
<div class="brand"><span class="brand-mark">GB</span><strong>GoodBuddy</strong><span class="muted">个人空间</span></div>
<div class="project-entry"><button id="project-trigger" popovertarget="project-menu" aria-expanded="false" aria-controls="project-menu"><span id="project-name">GoodBuddy</span><span class="muted" aria-hidden="true">⌄</span></button></div>
<button id="activity-trigger" popovertarget="project-activity" aria-expanded="false" aria-controls="project-activity"><span id="activity-summary"></span><span aria-hidden="true">⌄</span></button>
<div id="project-menu" popover aria-label="切换项目"><div id="project-list"></div></div>
<div id="project-activity" popover aria-label="所有项目活动"><section id="activity-projects"><h3>所有项目活动</h3><div id="activity-project-list"></div></section><section id="activity-sessions" hidden><button id="activity-back">‹ 返回项目列表</button><h3 id="activity-project-title"></h3><div id="activity-list"></div></section></div>
<button class="new-chat" data-page="当前对话"><span>+</span> 新对话</button>
<nav class="navigation" aria-label="应用导航">
<button class="selected" data-page="当前对话"><span data-icon="chat"></span>当前对话</button>
@@ -58,8 +63,8 @@
<div id="composer-options" popover aria-label="会话选项">
<header><strong>会话选项</strong><button popovertarget="composer-options" popovertargetaction="hide" aria-label="关闭会话选项">×</button></header>
<label class="option-row">专家<select id="composer-expert"><option value="">默认助手</option><option>产品顾问</option><option>代码审查专家</option></select></label>
<fieldset><legend>知识范围</legend><label><input type="checkbox" data-knowledge="产品文档"> 产品文档</label><label><input type="checkbox" data-knowledge="团队知识"> 团队知识</label></fieldset>
<label class="option-row">检索方式<select id="composer-retrieval"><option value="auto">自动检索</option><option value="always">每次检索</option></select></label>
<fieldset data-knowledge-control><legend>知识范围</legend><label><input type="checkbox" data-knowledge="产品文档"> 产品文档</label><label><input type="checkbox" data-knowledge="团队知识"> 团队知识</label></fieldset>
<label class="option-row" data-knowledge-control>检索方式<select id="composer-retrieval"><option value="auto">自动检索</option><option value="always">每次检索</option></select></label>
<div id="runtime-options"><label class="option-row"><span id="agent-label">Agent</span><select id="composer-agent"><option value="">默认</option><option value="plan">Plan</option><option value="review">Review</option></select></label><label class="option-row">快捷操作<select id="composer-command"><option value="">无</option><option value="prompt">填入总结模板</option><option value="command">原生命令 /init</option></select></label></div>
<p class="muted">非默认选项会显示在输入框上方。</p>
</div>
@@ -72,7 +77,16 @@
</main>
<footer class="lab-footer"><span id="design-tradeoff"></span><button id="reset">恢复默认应用</button></footer>
<div class="reference-note">设计参考:<a href="https://www.raycast.com/" target="_blank" rel="noreferrer">Raycast · 搜索与扩展</a><a href="https://www.notion.com/help/intro-to-workspaces" target="_blank" rel="noreferrer">Notion · 收藏与折叠分区</a><a href="https://code.visualstudio.com/docs/configure/profiles" target="_blank" rel="noreferrer">VS Code · Profiles</a><span>借鉴交互思路,保留 GoodBuddy 的视觉与入口分类。</span></div>
<dialog id="catalog" aria-labelledby="catalog-title"><button id="back-to-apps" hidden>‹ 返回应用中心</button><header><div><span class="eyebrow">GOODBUDDY COLLECTION</span><h2 id="catalog-title">应用中心</h2></div><button id="close-catalog" aria-label="关闭">×</button></header><p class="muted" id="catalog-description"></p><div class="center-switch" role="group" aria-label="应用中心视图"><button data-catalog="apps">我的应用</button><button data-catalog="store">商店</button></div><input id="search" type="search" aria-label="搜索应用" placeholder="搜索应用…"><div id="catalog-items"></div><section id="app-settings" aria-label="魔法笔记设置" hidden></section><footer>常驻只表示菜单入口,不表示后台运行。安装与内容均为演示。</footer></dialog>
<dialog id="catalog" aria-labelledby="catalog-title"><button id="back-to-apps" hidden>‹ 返回应用中心</button><header><div><span class="eyebrow">GOODBUDDY COLLECTION</span><h2 id="catalog-title">应用中心</h2></div><button id="close-catalog" aria-label="关闭">×</button></header><p class="muted" id="catalog-description"></p><div class="center-switch" role="group" aria-label="应用中心视图" hidden><button data-catalog="apps">我的应用</button><button data-catalog="store">未来私有市场</button></div><section id="market-preview" hidden>
<label>私有市场 URL<input id="market-url" type="url" value="https://market.example.invalid" aria-describedby="market-status"></label><button id="market-fetch">模拟获取目录</button><button id="market-fail">模拟不可达</button><p id="market-status" role="status">未来预览,不发送网络请求;来源仅用于市场。</p>
<details><summary>YAML 导入 / 导出预览(mock schema)</summary><p class="muted">仅支持下列扁平示例字段,非正式 Schema。支持 .yml / .yaml 的文本预览;不包含凭据或业务数据,不替代程序包或模型权重。</p><label>应用定义<textarea id="yaml-input" rows="9" spellcheck="false">schema: mock-v1
id: reading
name: 阅读空间
version: 1.0
entry: main
dependencies: builtin:knowledge
resources: bundled</textarea></label><button id="yaml-preview">预览导入</button><label>导出应用<select id="yaml-app"></select></label><button id="yaml-export">预览导出 YAML</button><pre id="yaml-output" tabindex="0" hidden></pre><div id="import-preview" hidden></div></details>
</section><input id="search" type="search" aria-label="搜索应用" placeholder="搜索应用…"><div id="catalog-items"></div><section id="app-settings" aria-label="应用设置" hidden></section><footer>取消常驻仅隐藏捷径;关闭应用隐藏业务入口,保留数据与后台状态。此处全部为模拟。</footer></dialog>
<div id="toast" role="status" aria-live="polite"></div>
</body>
</html>
@@ -14,6 +14,7 @@ const items = [
{ id: 'notes', name: '魔法笔记', icon: 'note', detail: '记录想法、整理待办', summary: '把刚才的灵感留在这里', builtin: true },
{ id: 'knowledge', name: '知识库', icon: 'book', detail: '整理资料、检索知识', summary: '下一次提问,从已有知识开始', builtin: true },
{ id: 'heartbeat', name: '智能心跳', icon: 'activity', detail: '管理定时任务与主动提醒', summary: '让例行任务按计划发生', builtin: true },
{ id: 'models', name: '本机推理监控', icon: 'layers', detail: '语音、OCR、向量任务统一管理 · 计划预览', summary: '查看本机推理任务、使用方和资源占用', builtin: true },
{ id: 'projects', name: '项目管理', icon: 'layers', detail: '跟踪项目、任务与进度', summary: '把一个想法变成可跟进的项目', builtin: false },
{ id: 'reading', name: '阅读空间', icon: 'book', detail: '集中阅读、批注与摘录', summary: '给长文和深度阅读留一个空间', builtin: false }
];
@@ -23,12 +24,131 @@ const variants = {
cards: ['C / 应用预览', '入口展示内置应用图标,展开后用卡片说明每个应用的用途。', '适合通过内容理解应用,卡片不代表后台持续运行。'],
launcher: ['D / 搜索启动台', '最底部就是搜索入口,也可以按 Ctrl / ⌘ K 打开。', '输入名称,用方向键与 Enter 打开已安装应用。'],
favorites: ['E / 常驻管理', '从底部管理哪些应用常驻菜单,未常驻应用也可直接打开。', '常驻应用优先排列;入口显隐不影响安装和运行状态。'],
scenes: ['F / 商店发现', '底部入口强调应用发现,展开后查看商店应用与安装位置。', '先模拟安装,再决定是否常驻;我的应用始终可切换访问。']
scenes: ['F / 私有市场预览', '未来实验:私有市场 URL、模拟目录获取与 YAML 交换。', '不联网、不写入文件;mock schema 不代表正式协议。']
};
const $ = selector => document.querySelector(selector);
const demoProjects = [
{ id: 'goodbuddy', name: 'GoodBuddy', group: '本地', conversations: ['优化项目导航', '检查构建问题'] },
{ id: 'website', name: '官网改版', group: '本地', conversations: ['发布首页'] },
{ id: 'remote', name: '客户方案与交付文档工作空间', group: 'SSH', conversations: ['整理报价'] },
{ id: 'channel', name: '团队助手', group: '通道', conversations: ['本周工作计划'] }
];
let currentProject = 'goodbuddy';
let projectActivity = [];
let expandedActivityProject = '';
function renderProjects() {
const scenario = $('#project-scenario').value;
const activity = demoProjects.flatMap(project => project.conversations.map((name, index) => ({ project, name, index,
state: scenario === 'idle' || project.id === 'channel' || (scenario === 'current' && project.id !== currentProject) ? '' : scenario === 'waiting' && project.id === 'website' ? '等待审批' : '运行中'
}))).filter(row => row.state);
projectActivity = activity;
const waiting = activity.filter(row => row.state !== '运行中').length;
const running = activity.length - waiting;
const project = demoProjects.find(entry => entry.id === currentProject);
$('#project-name').textContent = project.name;
$('#project-trigger').setAttribute('aria-label', `切换项目,当前项目:${project.name}`);
$('#activity-trigger').hidden = !activity.length;
if (!activity.length && $('#project-activity').matches(':popover-open')) $('#project-activity').hidePopover();
$('#activity-summary').innerHTML = `${running ? `运行中 ${running}` : ''}${waiting && running ? ' · ' : ''}${waiting ? `<span class="project-waiting">待处理 ${waiting}</span>` : ''}`;
$('#activity-trigger').setAttribute('aria-label', `所有项目活动:${$('#activity-summary').textContent}`);
$('#project-list').innerHTML = ['本地', 'SSH', '通道'].map(group => `<section><h3>${group}</h3>${demoProjects.filter(entry => entry.group === group).map(entry => {
const rows = activity.filter(row => row.project.id === entry.id);
const pending = rows.filter(row => row.state !== '运行中').length;
const active = rows.length - pending;
return `<button data-project="${entry.id}" ${entry.id === currentProject ? 'aria-current="true"' : ''}><span class="project-row-name">${entry.name}</span>${entry.id === currentProject ? '<span aria-hidden="true">✓</span>' : ''}${rows.length ? `<small>${pending ? `<span class="project-waiting">待处理 ${pending}</span>` : ''}${active ? `<span>运行中 ${active}</span>` : ''}</small>` : ''}</button>`;
}).join('')}</section>`).join('');
$('#activity-project-list').innerHTML = demoProjects.map(entry => {
const rows = activity.filter(row => row.project.id === entry.id);
if (!rows.length) return '';
const pending = rows.filter(row => row.state !== '运行中').length;
return `<button data-activity-project="${entry.id}" aria-expanded="false" aria-controls="activity-sessions"><span>${entry.name}</span><small>${pending ? `<span class="project-waiting">待处理 ${pending}</span>` : `运行中 ${rows.length}`}</small><span aria-hidden="true">›</span></button>`;
}).join('') || '<p class="muted">暂无运行中或待处理会话。</p>';
expandActivityProject('');
$('.history').innerHTML = `<span class="eyebrow">当前项目对话</span>${project.conversations.map((name, index) => {
const row = activity.find(entry => entry.project.id === currentProject && entry.index === index);
return `<button data-conversation="${currentProject}" data-conversation-index="${index}">${name}${row ? `<small>${row.state}</small>` : ''}</button>`;
}).join('')}`;
}
$('#project-scenario').onchange = renderProjects;
const projectMenu = $('#project-menu');
function positionProjectMenu() {
const rect = $('#project-trigger').getBoundingClientRect();
projectMenu.style.left = `${Math.max(16, Math.min(rect.left, innerWidth - projectMenu.offsetWidth - 16))}px`;
projectMenu.style.top = `${Math.max(16, Math.min(rect.bottom + 8, window.innerHeight - projectMenu.offsetHeight - 16))}px`;
}
projectMenu.addEventListener('toggle', event => {
$('#project-trigger').setAttribute('aria-expanded', String(event.newState === 'open'));
if (event.newState === 'open') positionProjectMenu();
});
window.addEventListener('resize', () => { if (projectMenu.matches(':popover-open')) positionProjectMenu(); });
window.addEventListener('scroll', () => { if (projectMenu.matches(':popover-open')) positionProjectMenu(); }, true);
function positionActivityMenu() {
const panel = $('#project-activity');
const rect = $('#activity-trigger').getBoundingClientRect();
panel.classList.toggle('activity-compact', innerWidth < 620);
panel.classList.toggle('activity-left', innerWidth >= 620 && rect.left + panel.offsetWidth > innerWidth - 16 && rect.right >= panel.offsetWidth + 16);
const left = panel.classList.contains('activity-left') ? rect.right - panel.offsetWidth : rect.left;
panel.style.left = `${Math.max(16, Math.min(left, innerWidth - panel.offsetWidth - 16))}px`;
panel.style.top = `${Math.max(16, Math.min(rect.bottom + 4, window.innerHeight - panel.offsetHeight - 16))}px`;
}
function expandActivityProject(id, focus = false) {
expandedActivityProject = id;
$('#activity-sessions').hidden = !id;
$('#project-activity').classList.toggle('has-submenu', Boolean(id));
document.querySelectorAll('[data-activity-project]').forEach(button => button.setAttribute('aria-expanded', String(button.dataset.activityProject === id)));
if (id) {
$('#activity-project-title').textContent = demoProjects.find(project => project.id === id).name;
$('#activity-list').innerHTML = projectActivity.filter(row => row.project.id === id).sort((a, b) => Number(a.state === '运行中') - Number(b.state === '运行中')).map(row => `<button data-conversation="${id}" data-conversation-index="${row.index}"><strong>${row.name}</strong><small>${row.state}</small></button>`).join('');
}
if ($('#project-activity').matches(':popover-open')) positionActivityMenu();
if (focus) $('#activity-list button')?.focus();
}
$('#project-activity').addEventListener('toggle', event => {
$('#activity-trigger').setAttribute('aria-expanded', String(event.newState === 'open'));
if (event.newState === 'open') { expandActivityProject(''); positionActivityMenu(); }
});
$('#activity-project-list').addEventListener('pointerover', event => {
const button = event.target.closest('[data-activity-project]');
if (event.pointerType === 'mouse' && innerWidth >= 620 && button && button.dataset.activityProject !== expandedActivityProject) expandActivityProject(button.dataset.activityProject);
});
$('#activity-project-list').addEventListener('click', event => {
const button = event.target.closest('[data-activity-project]');
if (button) expandActivityProject(button.dataset.activityProject, true);
});
$('#activity-back').onclick = () => {
const id = expandedActivityProject;
expandActivityProject('');
$(`[data-activity-project="${id}"]`)?.focus();
};
$('#project-activity').addEventListener('keydown', event => {
const button = event.target.closest('[data-activity-project]');
if (event.key === 'ArrowRight' && button) { event.preventDefault(); expandActivityProject(button.dataset.activityProject, true); }
if ((event.key === 'ArrowLeft' || event.key === 'Escape') && expandedActivityProject) { event.preventDefault(); event.stopPropagation(); $('#activity-back').click(); }
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
const buttons = [...document.querySelectorAll(button ? '[data-activity-project]' : '#activity-list button')];
const index = buttons.indexOf(document.activeElement);
if (index >= 0) { event.preventDefault(); buttons[(index + (event.key === 'ArrowDown' ? 1 : -1) + buttons.length) % buttons.length].focus(); }
}
});
window.addEventListener('resize', () => { if ($('#project-activity').matches(':popover-open')) positionActivityMenu(); });
window.addEventListener('scroll', () => { if ($('#project-activity').matches(':popover-open')) positionActivityMenu(); }, true);
renderProjects();
let variant = 'dock';
let catalogMode = 'apps';
let residentApps = ['notes', 'knowledge'];
let residentApps = items.filter(item => item.builtin && item.id !== 'models').map(item => item.id);
let enabledApps = new Set(items.filter(item => item.builtin).map(item => item.id));
let currentPage = '当前对话';
let settingsApp = 'notes';
let marketReady = false;
let importDraft = null;
const definitions = new Map();
const modelServices = [
{ id: 'ocr', name: 'OCR 文字识别', purpose: '文档入库 #12 · 正在识别第 8 / 24 页', engine: 'PaddleOCR worker', management: '受管进程', running: true, usage: '420 MB', users: '知识库 / 文档入库任务 #12' },
{ id: 'speech', name: '语音识别', purpose: '语音输入 #18 · 正在转写', engine: '本机语音识别引擎', management: '受管进程', running: true, usage: '310 MB', users: '当前会话 / 语音输入 #18' },
{ id: 'tts', name: '语音合成', purpose: '回复朗读 #19 · 正在合成', engine: '本机语音合成引擎', management: '进程内', running: true, usage: '180 MB', users: '当前会话 / 回复朗读 #19' },
{ id: 'embedding', name: '向量生成', purpose: '文档向量化 #7 · 已处理 32 / 80 个片段', engine: 'BGE-small / ONNX Runtime', management: '进程内', running: true, usage: '128 MB', users: '知识库 / 文档向量化任务 #7' }
];
let modelConfirmation = null;
let editingApps = false;
let draggedApp = '';
let installed = new Set(items.filter(item => item.builtin).map(item => item.id));
@@ -37,23 +157,28 @@ let settingsOpen = false;
const noteSettings = { showCount: true, mode: 'immediate', format: 'combined' };
const noteModes = { immediate: '即时', 'after-save-auto': '保存后自动', 'after-save-manual': '保存后手动' };
const noteFormats = { combined: '长评 + 要点', narrative: '长评', structured: '要点' };
function renderNoteSettings() {
$('#app-settings').innerHTML = `<div class="note-setting-row"><div><strong>常驻左侧菜单</strong><p>对应“显示魔法笔记入口”,移除后仍可从应用中心打开。</p></div><button class="demo-switch" role="switch" aria-label="常驻左侧菜单" aria-checked="${residentApps.includes('notes')}" data-note-toggle="resident"></button></div>
function renderAppSettings() {
const item = items.find(entry => entry.id === settingsApp);
$('#app-settings').innerHTML = `<div class="note-setting-row"><div><strong>启用应用</strong><p>关闭后隐藏业务入口,保留常驻偏好、数据和后台状态。可在此重新开启。</p></div><button class="demo-switch" role="switch" aria-label="启用${item.name}" aria-checked="${enabledApps.has(item.id)}" data-app-toggle="enabled"></button></div>
<div class="note-setting-row"><div><strong>常驻左侧菜单</strong><p>仅控制主导航捷径;应用关闭时记住此偏好。</p></div><button class="demo-switch" role="switch" aria-label="常驻左侧菜单" aria-checked="${residentApps.includes(item.id)}" data-app-toggle="pinned"></button></div>`;
if (settingsApp === 'notes') $('#app-settings').innerHTML += `
<div class="note-setting-row"><div><strong>显示未完成待办数量</strong><p>在左侧入口显示数量,超过 99 项显示 99+。本 Demo 使用 3 项示例。</p></div><button class="demo-switch" role="switch" aria-label="显示未完成待办数量" aria-checked="${noteSettings.showCount}" data-note-toggle="showCount"></button></div>
<fieldset class="note-setting-options"><legend>AI 评论方式</legend><div class="note-segments" role="group" aria-label="AI 评论方式">${Object.entries(noteModes).map(([value, label]) => `<button data-note-mode="${value}" aria-pressed="${noteSettings.mode === value}">${label}</button>`).join('')}</div><p>即时:按回车并停止输入 5 秒后评论未保存草稿。保存后自动:保存后评论。保存后手动:点击 AI 分析后评论。</p></fieldset>
<fieldset class="note-setting-options"><legend>AI 评论形式</legend><div class="note-segments" role="group" aria-label="AI 评论形式">${Object.entries(noteFormats).map(([value, label]) => `<button data-note-format="${value}" aria-pressed="${noteSettings.format === value}">${label}</button>`).join('')}</div><p>默认同时生成流式长评和结构化要点,也可只保留其中一种。</p></fieldset>
<div class="note-settings-preview"><span class="eyebrow">当前配置预览</span><p>${noteModes[noteSettings.mode]} · ${noteFormats[noteSettings.format]}</p><small>选择后即时保留于本次 Demo 会话,不调用 AI、不修改真实设置。</small></div>`;
if ($('#note-settings-summary')) $('#note-settings-summary').textContent = `当前设置:${noteModes[noteSettings.mode]} · ${noteFormats[noteSettings.format]}。待办数量${noteSettings.showCount ? '显示' : '隐藏'}。`;
}
function showNoteSettings() {
function showAppSettings(id = 'notes') {
settingsApp = id;
const item = items.find(entry => entry.id === id);
if (!$('#catalog').open) { catalogMode = 'apps'; $('#search').value = ''; }
settingsOpen = true;
$('#catalog').dataset.variant = variant;
$('#catalog-title').textContent = '魔法笔记设置';
$('#catalog-description').textContent = '调整入口与 AI 评论行为。应用中心和笔记页面共享这份演示配置。';
$('#catalog-title').textContent = `${item.name}设置`;
$('#catalog-description').textContent = '应用中心、应用页面和全局设置共用本次演示配置。';
$('#back-to-apps').hidden = false; $('#app-settings').hidden = false;
for (const selector of ['.center-switch', '#search', '#catalog-items']) $(selector).hidden = true;
renderNoteSettings();
for (const selector of ['.center-switch', '#search', '#catalog-items', '#market-preview']) $(selector).hidden = true;
renderAppSettings();
if (!$('#catalog').open) $('#catalog').showModal();
$('#back-to-apps').focus();
}
@@ -63,54 +188,73 @@ function notify(text) {
}
function renderApps() {
$('#edit-apps').textContent = editingApps ? '完成' : '编辑';
$('#resident-list').innerHTML = residentApps.map((id, index) => {
$('#resident-list').innerHTML = residentApps.filter(id => enabledApps.has(id) && (variant === 'scenes' || items.find(item => item.id === id).builtin)).map(id => {
const index = residentApps.indexOf(id);
const item = items.find(entry => entry.id === id);
return `<div class="resident-row" data-resident="${id}" draggable="${editingApps}"><button data-open="${id}" ${$('#page-label').textContent === item.name ? 'aria-current="page"' : ''}>${icon(item.icon)}<span>${item.name}</span>${id === 'notes' && noteSettings.showCount ? '<small class="note-count" aria-label="3 项未完成待办(示例)">3</small>' : ''}</button>${editingApps ? `<div class="edit-actions"><button data-move="${id}" data-direction="-1" aria-label="上移${item.name}" ${index === 0 ? 'disabled' : ''}>↑</button><button data-pin="${id}" aria-label="移除${item.name}">×</button><button data-move="${id}" data-direction="1" aria-label="下移${item.name}" ${index === residentApps.length - 1 ? 'disabled' : ''}>↓</button></div>` : ''}</div>`;
}).join('') || '<p class="empty-apps">点击 +,选择常驻应用。</p>';
const subtitles = { dock: '', grid: `${installed.size} 个已安装应用`, cards: '笔记 · 知识 · 自动化', launcher: 'Ctrl / ⌘ K', favorites: `${residentApps.length} 个应用常驻菜单`, scenes: '发现你的下一个好应用' };
const subtitles = { dock: '', grid: '内置应用', cards: '笔记 · 知识 · 自动化 · 模型', launcher: 'Ctrl / ⌘ K', favorites: '管理常驻偏好', scenes: '未来私有市场 · 模拟' };
$('#center-entry').innerHTML = `<button id="open-center" aria-haspopup="dialog"><span class="brand-mark">${variant === 'launcher' ? icon('search') : 'GB'}</span><span><strong>${variant === 'launcher' ? '搜索应用…' : '应用中心'}</strong>${subtitles[variant] ? `<small>${subtitles[variant]}</small>` : ''}</span><span class="entry-arrow">${variant === 'scenes' ? '↗' : '⌃'}</span></button>`;
}
function renderCatalog() {
const query = $('#search').value.trim().toLowerCase();
const matches = items.filter(item => (catalogMode === 'store' ? !item.builtin : installed.has(item.id)) && `${item.name}${item.detail}`.toLowerCase().includes(query));
const matches = items.filter(item => (catalogMode === 'store' ? marketReady && !item.builtin : installed.has(item.id) && (variant === 'scenes' || item.builtin)) && `${item.name}${item.detail}`.toLowerCase().includes(query));
if (variant === 'favorites') matches.sort((a, b) => Number(residentApps.includes(b.id)) - Number(residentApps.includes(a.id)));
$('#catalog-items').innerHTML = matches.map(item => {
const ready = installed.has(item.id); const pinned = residentApps.includes(item.id);
return `<article class="center-app">${icon(item.icon)}<div class="center-app-info"><strong>${item.name}</strong><p>${item.builtin ? '内置应用' : '商店应用 · 示例'}</p><p class="app-detail">${variant === 'cards' ? item.summary : item.detail}</p></div><div class="center-actions">${ready ? `<button data-open="${item.id}">打开</button>${item.id === 'notes' ? '<button data-note-settings aria-label="魔法笔记设置">设置</button>' : ''}<button data-pin="${item.id}" aria-pressed="${pinned}">${pinned ? '取消常驻' : '常驻左侧'}</button>` : `<button data-install="${item.id}">模拟安装</button><button data-install-pin="${item.id}">安装并常驻</button>`}</div></article>`;
}).join('') || '<p class="empty-apps">没有匹配的应用。可以换个关键词,或切换到商店。</p>';
return `<article class="center-app">${icon(item.icon)}<div class="center-app-info"><strong>${item.name}</strong><p>${item.builtin ? '内置应用' : '私有市场 · 示例'}${item.id === 'models' ? ' · 计划预览' : ''}${ready ? enabledApps.has(item.id) ? ' · 已启用' : ' · 已关闭' : ''}</p><p class="app-detail">${variant === 'cards' ? item.summary : item.detail}</p></div><div class="center-actions">${ready ? `${enabledApps.has(item.id) ? `<button data-open="${item.id}">打开</button>` : ''}<button data-app-settings="${item.id}">${enabledApps.has(item.id) ? '设置' : '设置 / 重新开启'}</button><button data-pin="${item.id}" aria-pressed="${pinned}">${pinned ? '取消常驻' : '常驻左侧'}</button>` : `<button data-install="${item.id}">模拟安装</button><button data-install-pin="${item.id}">模拟安装并常驻</button>`}</div></article>`;
}).join('') || `<p class="empty-apps">${catalogMode === 'store' && !marketReady ? '请先模拟获取私有市场目录。' : '没有匹配的应用,请换个关键词。'}</p>`;
$('#yaml-app').innerHTML = items.filter(item => installed.has(item.id)).map(item => `<option value="${item.id}">${item.name}</option>`).join('');
document.querySelectorAll('.center-switch button').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.catalog === catalogMode)));
}
function openCatalog(mode = 'apps') {
importDraft = null; $('#import-preview').hidden = true;
if (variant !== 'scenes') mode = 'apps';
settingsOpen = false; $('#back-to-apps').hidden = true; $('#app-settings').hidden = true;
for (const selector of ['.center-switch', '#search', '#catalog-items']) $(selector).hidden = false;
$('.center-switch').hidden = variant !== 'scenes';
$('#market-preview').hidden = mode !== 'store';
catalogMode = mode; $('#catalog').dataset.variant = variant;
$('#catalog-title').textContent = mode === 'store' ? '发现新应用' : '应用中心';
$('#catalog-description').textContent = mode === 'store' ? '安装你需要的应用,自主决定是否常驻左侧菜单。' : '打开应用,或把常用应用留在左侧。系统工具请使用右侧工作栏。';
$('#catalog-title').textContent = mode === 'store' ? '私有市场 · 未来预览' : '应用中心';
$('#catalog-description').textContent = mode === 'store' ? '目录和安装均为模拟,未接入 ShareServer。导入不会启动模型或任务。' : '内置应用无需安装;启用与常驻分别设置。系统工具请使用右侧工作栏。';
$('#search').value = ''; renderCatalog();
if (!$('#catalog').open) $('#catalog').showModal();
$('#search').focus();
}
function openPage(name) {
if ($('#catalog').open) $('#catalog').close();
function openPage(name, keepSettings = false) {
currentPage = name;
modelConfirmation = null;
if ($('#catalog').open && !keepSettings) $('#catalog').close();
$('#page-label').textContent = name;
$('#page-content').replaceChildren();
const title = document.createElement('h1'); title.textContent = name;
const description = document.createElement('p'); description.className = 'muted'; description.textContent = '主内容区页面预览。右侧工作栏保持当前工具状态。';
$('#page-content').append(title, description);
$('#page-actions').innerHTML = name === '魔法笔记' ? `<button data-note-settings>${icon('settings')} 笔记设置</button>` : '';
const app = items.find(item => item.name === name);
$('.composer-area').hidden = Boolean(app) || name === '设置' || name === '运行记录';
$('#page-content').classList.toggle('app-page', $('.composer-area').hidden);
for (const panel of ['#composer-options', '#context-details']) if ($(panel).matches(':popover-open')) $(panel).hidePopover();
$('#page-actions').replaceChildren();
if (app && !enabledApps.has(app.id)) {
description.textContent = '此应用已关闭,当前页面不可用。已有数据和选择保留;此操作不停止后台任务或模型服务。';
renderApps(); return;
}
if (app?.id === 'models') renderModels();
if (name === '魔法笔记' || name === '设置') {
const summary = document.createElement('p'); summary.id = 'note-settings-summary'; summary.className = 'muted'; $('#page-content').append(summary);
if (name === '设置') { const button = document.createElement('button'); button.dataset.noteSettings = ''; button.textContent = '魔法笔记设置'; $('#page-content').append(button); }
renderNoteSettings();
renderAppSettings();
}
document.querySelectorAll('.navigation button').forEach(button => button.classList.toggle('selected', button.dataset.page === name));
renderApps(); notify(`已打开${name}`);
renderApps(); if (!keepSettings) notify(`已打开${name}`);
}
function selectVariant(name) {
variant = name; $('.app').dataset.variant = name;
document.querySelectorAll('button[data-variant]').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.variant === name)));
[$('#variant-label').textContent, $('#variant-description').textContent, $('#design-tradeoff').textContent] = variants[name];
history.replaceState(null, '', `#${name}`); renderApps();
if ($('#catalog').open) openCatalog();
const pageApp = items.find(item => item.name === currentPage);
if (name !== 'scenes' && pageApp && !pageApp.builtin) openPage('当前对话');
}
function selectTool(id) {
const content = {
@@ -122,21 +266,123 @@ function selectTool(id) {
$('#tool-content').innerHTML = content[id]; $('#tool-content').setAttribute('aria-labelledby', `tab-${id}`);
document.querySelectorAll('[data-tool]').forEach(button => { button.setAttribute('aria-selected', String(button.dataset.tool === id)); button.tabIndex = button.dataset.tool === id ? 0 : -1; });
}
function renderModels() {
$('#page-content').innerHTML = `<h1>本机推理监控</h1><p class="muted">统一查看语音、OCR、向量等本机推理任务与执行服务。任务进度、引擎及占用均为模拟数据。</p><h2>推理任务与服务</h2><p class="muted">下方操作作用于执行服务;启动或加载服务不会自动恢复已中断的任务。</p><div class="model-list">${modelServices.map(service => {
const external = service.management === '外部管理';
const active = service.management === '进程内' ? '已加载' : '运行中';
const actions = external ? ['config'] : service.management === '进程内' ? [service.running ? 'release' : 'load'] : service.running ? ['stop', 'restart'] : ['start'];
return `<article class="model-service"><strong>${service.name}</strong><p>${service.interrupted ? '任务已中断,需由原应用重新发起' : service.purpose}</p><p>执行服务:${service.running ? active : '未运行'} · 内存:${service.running ? service.usage : '已释放(模拟)'}</p><p>使用方:${service.interrupted ? '暂无活动使用方' : service.users}</p><div class="model-actions">${actions.map(action => `<button data-model="${service.id}" data-model-action="${action}">${modelActionLabels[action]}</button>`).join('')}</div>${modelConfirmation?.id === service.id ? `<div class="inline-confirm" role="group" aria-label="推理服务操作确认"><p>${modelActionLabels[modelConfirmation.action]} ${service.name} 将影响 ${service.interrupted ? '该执行服务,当前没有活动任务' : service.users}。仅模拟状态变化。</p><button data-model-cancel>取消</button><button data-model-confirm>确认${modelActionLabels[modelConfirmation.action]}</button></div>` : ''}<details><summary>引擎与错误详情</summary><p>${service.engine} · ${service.management}</p><p>最近错误:无(示例)。操作仅更新本页内存状态。</p></details></article>`;
}).join('')}</div>`;
}
const modelActionLabels = { start: '启动', stop: '停止', restart: '重启', load: '加载', release: '释放', config: '连接配置' };
function applyModelAction(id, action) {
const service = modelServices.find(entry => entry.id === id);
service.running = !['stop', 'release'].includes(action);
if (['stop', 'release', 'restart'].includes(action)) service.interrupted = true;
modelConfirmation = null;
renderModels();
$(`[data-model="${id}"]`)?.focus();
notify(`已模拟${modelActionLabels[action]} ${service.name}`);
}
function previewImport() {
importDraft = null;
const preview = $('#import-preview'); preview.hidden = false; preview.replaceChildren();
const values = {};
const fields = ['schema', 'id', 'name', 'version', 'entry', 'dependencies', 'resources'];
for (const line of $('#yaml-input').value.split(/\r?\n/).filter(line => line.trim() && !line.trim().startsWith('#'))) {
const match = line.match(/^([a-z]+):\s*(.+)$/);
if (!match || !fields.includes(match[1]) || Object.hasOwn(values, match[1])) { preview.textContent = '仅支持示例中的七个扁平字段,不接受重复或未知字段;请修正后重新预览。'; return; }
values[match[1]] = match[2].trim();
}
if (fields.some(field => !values[field]) || values.schema !== 'mock-v1' || !/^[a-z][a-z0-9-]*$/.test(values.id) || values.entry !== 'main') { preview.textContent = '请填写完整 mock-v1 示例,id 使用小写字母、数字或连字符,entry 为 main。'; return; }
const existing = items.find(item => item.id === values.id);
const dependencies = values.dependencies === 'none' ? [] : values.dependencies.split(',').map(value => value.trim());
const missing = dependencies.filter(value => !value.startsWith('builtin:') || !installed.has(value.slice(8)) || !enabledApps.has(value.slice(8)));
const blocked = existing?.builtin || missing.length || values.resources !== 'bundled';
const conflict = installed.has(values.id);
const text = document.createElement('p');
text.textContent = `${values.name} · ${values.id} · v${values.version} · 主内容区入口。依赖:${values.dependencies}。${missing.length ? `缺失或关闭:${missing.join('、')}。` : '依赖已具备(模拟)。'}资源:${values.resources}。${values.resources !== 'bundled' ? '需补充外部资源,不能安装为可用。' : ''}${existing?.builtin ? '与内置应用冲突,不允许覆盖。' : conflict ? '同标识冲突:需显式更新定义,保留启用与常驻偏好。' : '没有同标识冲突。'}`;
preview.append(text);
const cancel = document.createElement('button'); cancel.textContent = '取消'; cancel.onclick = () => { importDraft = null; preview.hidden = true; $('#yaml-preview').focus(); }; preview.append(cancel);
if (!blocked) {
importDraft = values;
const confirm = document.createElement('button'); confirm.id = 'import-confirm'; confirm.textContent = conflict ? '确认模拟更新定义' : '确认模拟安装';
confirm.onclick = () => {
if (!importDraft) return;
const draft = importDraft;
const item = items.find(entry => entry.id === draft.id);
if (!item) items.push({ id: draft.id, name: draft.id, icon: 'layers', detail: 'YAML 导入 · 未来示例', summary: '导入的应用定义预览', builtin: false });
// Imported text stays in textContent/textarea; catalog markup uses the validated identifier.
definitions.set(draft.id, { ...draft });
if (!installed.has(draft.id)) { installed.add(draft.id); enabledApps.add(draft.id); }
importDraft = null; preview.hidden = true; renderCatalog(); renderApps(); $('#yaml-preview').focus(); notify('已模拟安装 / 更新定义,未启动模型或任务');
};
preview.append(confirm);
}
}
$('#market-fetch').onclick = () => {
const valid = /^https?:\/\/[^\s/]+(?:\/[^\s]*)?$/.test($('#market-url').value.trim());
marketReady = valid;
$('#market-status').textContent = valid ? `已模拟获取目录:${$('#market-url').value.trim()},无网络请求。` : '请输入 http(s) 市场 URL 后重试。';
renderCatalog();
};
$('#market-fail').onclick = () => { marketReady = false; $('#market-status').textContent = '模拟来源不可达,请点击模拟获取目录重试。未切换公共市场,已安装应用仍可使用。'; renderCatalog(); };
$('#market-url').oninput = () => { marketReady = false; $('#market-status').textContent = '来源已修改,请重新模拟获取目录。'; renderCatalog(); };
$('#yaml-preview').onclick = previewImport;
$('#yaml-input').oninput = () => { importDraft = null; $('#import-preview').hidden = true; };
$('#yaml-export').onclick = () => {
const item = items.find(entry => entry.id === $('#yaml-app').value);
const definition = definitions.get(item.id) || { schema: 'mock-v1', id: item.id, name: item.name, version: '1.0', entry: 'main', dependencies: 'none', resources: 'bundled' };
$('#yaml-output').hidden = false;
$('#yaml-output').textContent = '# Mock schema only; no credentials or business data\n' + Object.entries(definition).map(([key, value]) => `${key}: ${value}`).join('\n');
};
document.addEventListener('click', event => {
const button = event.target.closest('button'); if (!button) return;
const data = button.dataset;
if (data.project || data.conversation) {
currentProject = data.project || data.conversation;
if (projectMenu.matches(':popover-open')) projectMenu.hidePopover();
if ($('#project-activity').matches(':popover-open')) $('#project-activity').hidePopover();
renderProjects();
const project = demoProjects.find(entry => entry.id === currentProject);
openPage(data.conversation ? project.conversations[Number(data.conversationIndex)] : '当前对话');
$('#page-label').textContent = `${project.name} / ${data.conversation ? project.conversations[Number(data.conversationIndex)] : '当前对话'}`;
$('#project-trigger').focus();
}
if (data.variant) selectVariant(data.variant);
if (data.catalog) openCatalog(data.catalog);
if (data.page) openPage(data.page);
if (data.open) openPage(items.find(item => item.id === data.open).name);
if (data.tool) selectTool(data.tool);
if (button.hasAttribute('data-note-settings')) showNoteSettings();
if (data.appSettings) showAppSettings(data.appSettings);
if (data.appToggle) {
const id = settingsApp;
if (data.appToggle === 'enabled') enabledApps.has(id) ? enabledApps.delete(id) : enabledApps.add(id);
else residentApps = residentApps.includes(id) ? residentApps.filter(entry => entry !== id) : [...residentApps, id];
renderApps(); renderComposer();
if (currentPage === items.find(item => item.id === id).name) {
openPage(currentPage, true);
}
renderAppSettings(); $(`[data-app-toggle="${data.appToggle}"]`).focus();
}
if (data.modelAction) {
if (data.modelAction === 'config') {
const row = button.closest('.model-service');
let config = row.querySelector('.model-config');
if (!config) { config = document.createElement('p'); config.className = 'model-config'; config.textContent = '连接配置预览:Ollama · http://localhost:11434 · 外部管理。仅展示示例,不连接或保存配置。'; row.append(config); }
} else {
const service = modelServices.find(entry => entry.id === data.model);
if (service.running && service.users) { modelConfirmation = { id: data.model, action: data.modelAction }; renderModels(); $('[data-model-cancel]').focus(); }
else applyModelAction(data.model, data.modelAction);
}
}
if (button.hasAttribute('data-model-cancel')) { const id = modelConfirmation.id; modelConfirmation = null; renderModels(); $(`[data-model="${id}"]`).focus(); }
if (button.hasAttribute('data-model-confirm') && modelConfirmation) applyModelAction(modelConfirmation.id, modelConfirmation.action);
if (data.noteToggle || data.noteMode || data.noteFormat) {
if (data.noteToggle === 'resident') residentApps = residentApps.includes('notes') ? residentApps.filter(id => id !== 'notes') : [...residentApps, 'notes'];
if (data.noteToggle === 'showCount') noteSettings.showCount = !noteSettings.showCount;
if (data.noteMode) noteSettings.mode = data.noteMode;
if (data.noteFormat) noteSettings.format = data.noteFormat;
renderApps(); renderNoteSettings();
renderApps(); renderAppSettings();
const selector = data.noteToggle ? `[data-note-toggle="${data.noteToggle}"]` : data.noteMode ? `[data-note-mode="${data.noteMode}"]` : `[data-note-format="${data.noteFormat}"]`;
$(selector).focus();
}
@@ -144,7 +390,7 @@ document.addEventListener('click', event => {
if (button.id === 'edit-apps') { editingApps = !editingApps; renderApps(); $('#edit-apps').focus(); }
if (data.pin || data.install || data.installPin) {
const id = data.pin || data.install || data.installPin;
if (data.install || data.installPin) installed.add(id);
if (data.install || data.installPin) { importDraft = null; $('#import-preview').hidden = true; installed.add(id); enabledApps.add(id); notify('已模拟安装,未启动任务或模型'); }
if (data.pin || data.installPin) residentApps = residentApps.includes(id) ? residentApps.filter(entry => entry !== id) : [...residentApps, id];
renderApps();
if ($('#catalog').open) { renderCatalog(); ($(`#catalog [data-pin="${id}"]`) || $('#search')).focus(); }
@@ -166,7 +412,15 @@ $('#resident-list').addEventListener('drop', event => {
$('#resident-list').addEventListener('dragend', () => { draggedApp = ''; });
$('#search').addEventListener('input', renderCatalog);
$('#close-catalog').onclick = () => $('#catalog').close();
$('#back-to-apps').onclick = () => { const query = $('#search').value; openCatalog(catalogMode); $('#search').value = query; renderCatalog(); ($('#catalog [data-note-settings]') || $('#search')).focus(); };
$('#back-to-apps').onclick = () => { const query = $('#search').value; openCatalog(catalogMode); $('#search').value = query; renderCatalog(); ($(`#catalog [data-app-settings="${settingsApp}"]`) || $('#search')).focus(); };
$('#app-settings').addEventListener('keydown', event => {
const group = event.target.closest('.note-segments');
if (!group || !['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
event.preventDefault();
const buttons = [...group.querySelectorAll('button')];
const index = buttons.indexOf(event.target);
buttons[event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1 : (index + (event.key === 'ArrowRight' ? 1 : -1) + buttons.length) % buttons.length].click();
});
function toggleWorkbar(visible) { $('.workbar').hidden = !visible; $('#toggle-workbar').setAttribute('aria-expanded', String(visible)); }
$('#toggle-workbar').onclick = () => toggleWorkbar($('.workbar').hidden);
$('#close-tool').onclick = () => { toggleWorkbar(false); $('#toggle-workbar').focus(); };
@@ -184,7 +438,7 @@ document.addEventListener('keydown', event => {
if (event.key === 'Enter' && document.activeElement === $('#search')) { event.preventDefault(); actions[0].click(); }
});
$('#theme').onclick = () => { const dark = document.documentElement.dataset.theme !== 'dark'; document.documentElement.dataset.theme = dark ? 'dark' : 'light'; $('#theme').textContent = dark ? '切换浅色' : '切换深色'; };
$('#reset').onclick = () => { installed = new Set(items.filter(item => item.builtin).map(item => item.id)); residentApps = ['notes', 'knowledge']; editingApps = false; renderApps(); notify('应用安装与常驻配置已恢复默认'); };
$('#reset').onclick = () => { installed = new Set(items.filter(item => item.builtin).map(item => item.id)); residentApps = [...installed].filter(id => id !== 'models'); enabledApps = new Set(installed); Object.assign(noteSettings, { showCount: true, mode: 'immediate', format: 'combined' }); editingApps = false; definitions.clear(); importDraft = null; $('#import-preview').hidden = true; renderApps(); renderComposer(); openPage('当前对话'); notify('应用启用、常驻和笔记设置已恢复默认'); };
let demoRunning = false;
let demoRecording = false;
let demoAttachments = [];
@@ -201,8 +455,11 @@ function renderComposer() {
const select = $(`#${id}`);
if (select.value) addTag(select.selectedOptions[0].textContent, () => { select.value = ''; });
}
document.querySelectorAll('[data-knowledge]:checked').forEach(input => addTag(input.dataset.knowledge, () => { input.checked = false; }));
if ($('#composer-retrieval').value === 'always') addTag('每次检索', () => { $('#composer-retrieval').value = 'auto'; });
document.querySelectorAll('[data-knowledge-control]').forEach(element => { element.hidden = !enabledApps.has('knowledge'); });
if (enabledApps.has('knowledge')) {
document.querySelectorAll('[data-knowledge]:checked').forEach(input => addTag(input.dataset.knowledge, () => { input.checked = false; }));
if ($('#composer-retrieval').value === 'always') addTag('每次检索', () => { $('#composer-retrieval').value = 'auto'; });
}
tags.hidden = !tags.childElementCount;
$('#stop').hidden = !demoRunning;
$('#send').disabled = !$('#message').value.trim() && $('#composer-command').value !== 'command';
+110 -65
View File
@@ -4,20 +4,19 @@
| 项目 | 内容 |
| --- | --- |
| 状态 | 设计中 |
| 版本 | 0.2 |
| 日期 | 2026-09-08 |
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
| 版本 | 0.4 |
| 日期 | 2026-09-17 |
| 适用产品 | GoodBuddy 桌面端 |
| 关联设计 | [统一界面设计系统](../../../UI-DESIGN.md)、[助手工作栏 PRD](../assistant-workbar/prd.md) |
## 1. 背景
计划修订说明:当前实施范围以[第 8 节](#8-分期)为准。本文其余章节仍包含 v0.2 的
左下角系统工具方案;启动实现前须同步功能要求、验收及配套设计,旧快捷工具条不再列入实施。
本版已同步应用中心方案,分期见[第 8 节](#8-分期)。左下角用于访问和管理应用,系统工具保留在右侧工作栏。
主侧栏当前把“当前对话”“魔法笔记”“知识库”“智能心跳”和“运行记录”放在同一导航组,
底部长期显示“GB 本地工作区”和平台架构。底部信息不能反映用户可以执行的操作,文件、终端
和浏览器则需要先找到右侧助手工作栏后才能打开。
原主侧栏把“当前对话”“魔法笔记”“知识库”“智能心跳”和“运行记录”放在同一导航组,
底部显示“GB 本地工作区”和平台架构。当前已将底部改为应用中心及独立设置入口,文件、终端
和浏览器继续从右侧助手工作栏打开。
后续插件可能提供完整页面,也可能只提供辅助面板或命令。若不先区分系统入口、应用和工具,
插件接入后会同时争用主导航、侧栏底部和助手工作栏,用户也无法判断入口会切换页面还是打开
@@ -25,16 +24,17 @@
## 2. 产品定义
### 2.1 三类入口
### 2.1 入口分类
| 类型 | 首期成员 | 默认位置 | 用户控制范围 |
| --- | --- | --- | --- |
| 系统入口 | 当前对话、运行记录、设置 | 主导航固定位置或侧栏底部 | 不可隐藏、卸载或改变类型 |
| 应用 | 魔法笔记、知识库、智能心跳 | 主导航应用区 | 可控制是否显示;隐藏不删除数据 |
| 系统工具 | 文件浏览器、终端、浏览器、资源监控 | 侧栏底部快捷工具栏 | 首期固定提供,不可卸载 |
| 始终显示的内置应用 | 知识库 | 主导航应用区及应用中心 | 始终启用并常驻,可排序,不提供启用或常驻开关;保留工作区实际配置 |
| 可选内置应用 | 智能心跳、魔法笔记、本机推理监控 | 应用中心及主导航常驻区 | 独立控制启用、常驻与排序;保留数据 |
| 系统工具 | 文件浏览器、终端、浏览器、资源监控 | 右侧助手工作栏 Tab 和“+”目录 | 自带且不可卸载,按工作栏规则管理实例 |
运行记录是系统审计页面,不属于工具。文件浏览器、终端、浏览器和资源监控是系统工具;它们
的快捷入口位于左下角,实际界面继续由右侧助手工作栏承载。
的入口与实际界面均由右侧助手工作栏承载,不列入应用中心。
### 2.2 后续插件类型
@@ -43,10 +43,10 @@
## 3. 目标
- 用户从侧栏底部一次点击即可打开文件、终端、浏览器或资源监控。
- 主导航只承载完整页面,辅助工具不占用一级页面位置。
- 用户从侧栏底部进入应用中心,查找、打开和配置内置应用。
- 主导航承载应用入口,本机推理监控打开 Modal,其余内置应用打开完整页面;辅助工具由工作栏承载。
- 运行记录始终可发现,不受应用显示设置和插件状态影响。
- 内置应用可以从一个设置位置控制主导航可见性,隐藏后仍可恢复。
- 四个内置应用均支持排序;智能心跳、魔法笔记和本机推理监控可独立设置启用与常驻,知识库始终显示,未常驻的已启用应用仍可从应用菜单打开。
- 为插件应用和插件工具保留明确的入口契约,不提前建设插件安装能力。
## 4. 非目标
@@ -56,7 +56,7 @@
- 不把文件树、终端或浏览器重新实现到左侧栏。
- 不改变助手工作栏的面板实例、停靠、执行空间和安全边界。
- 不允许隐藏运行记录、当前对话或设置。
- 不因隐藏应用而删除魔法笔记、知识库或智能心跳数据。
- 不因隐藏可选应用而删除业务数据。
- 不把“显示在侧边栏”解释为停止智能心跳计划或后台任务。
- 不提供任意本机程序启动器。
- 首期资源监控不采集远程主机、GPU、显存或网络吞吐,不提供后台告警和历史持久化。
@@ -75,42 +75,43 @@
### FR-3 内置应用区
魔法笔记、知识库和智能心跳显示在当前对话与运行记录之间。应用入口打开对应一级页面,保留
现有徽标、范围和页面状态。用户可以在设置中控制其主导航可见性。
主导航在当前对话之后按共享保存顺序显示应用,知识库始终显示,可选应用仅显示已启用且常驻项;运行记录位于应用区之后。四个应用均可排序。本机推理监控入口打开独立 Modal,其余应用打开对应一级页面,保留
现有徽标、范围和页面状态。管理 Modal 列出全部内置应用,知识库只提供“打开”和排序。
### FR-4 隐藏应用
隐藏应用只移除主导航入口,不删除数据、不修改业务启停状态,也不终止正在进行的操作。显隐
Switch 更新成功后主导航立即生效;更新失败时恢复原状态并允许重试。用户可从设置恢复入口。
取消常驻只移除主导航捷径,不删除数据、不修改启用状态,也不终止正在进行的操作。
关闭可选应用则同步移除业务入口,保留常驻偏好;知识库不受这些偏好影响。心跳关闭后的执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。
配置保存成功后各入口使用同一已确认状态,失败时保持原状态并允许重试。
### FR-5 系统工具快捷栏
### FR-5 应用中心入口
侧栏底部提供文件浏览器、终端、浏览器和资源监控四个图标按钮,并保留独立设置按钮。每个
按钮具有可见工具提示和可访问名称。首期系统工具顺序固定为文件、终端、浏览器、资源监控。
侧栏底部提供应用中心和独立设置入口。点击应用中心向上展开锚定按钮的轻量菜单,无模态遮罩。菜单列出已启用应用,不按是否打开过、历史记录或常驻状态筛选;点击应用行关闭菜单,本机推理监控打开 Modal,其余应用在主内容区打开页面。
菜单底部“管理应用”关闭菜单并打开既有管理 Modal,在其中浏览、搜索全部内置应用,为已关闭的可选应用提供设置与重新开启入口。无搜索结果时显示空状态并可清除搜索。
首期不展示商店、安装、推荐或系统工具清单。入口具有可访问名称,Escape 关闭后恢复触发位置的焦点。
### FR-6 文件浏览器入口
点击文件按钮时展开助手工作栏,并聚焦默认存在且不可关闭的“工作区”单实例。该实例保持
在右侧选择“工作区”Tab 时聚焦默认存在且不可关闭的单实例。该实例保持
“跟随当前上下文”或“固定到指定对象”状态;迁移或布局修复发现实例缺失时补回,并默认跟随
当前项目。没有项目时使用已配置的默认工作区。跟随上下文所需的路径不可用时,面板显示修复
入口;已有有效固定目标不受当前项目和默认工作区状态影响。
入口;已有有效固定目标不受当前项目和默认工作区状态影响。固定目标失效时修复该目标,不静默回退到其他目录。
### FR-7 终端入口
点击终端按钮时展开助手工作栏。当前执行空间有正在启动或运行中的终端时聚焦最近使用的实例;
没有时创建一个绑定当前执行空间的新终端,已退出、已中断或失败的历史 Tab 保持不变。没有
项目时当前执行空间为本机 Home。创建额外并行终端继续使用工作栏现有的“+”目录入口。
选择已有终端 Tab 仅激活该实例,保持目标、会话和历史状态。通过工作栏“+”选择终端时
创建绑定当前执行空间的新实例,不按目标复用已有实例;无项目时使用本机 Home。
远程项目的新实例绑定其托管 SSH 执行空间和权威目录。创建失败保留当前主页面与其他实例。
### FR-8 浏览器入口
点击浏览器按钮时展开助手工作栏,并优先聚焦绑定当前 Conversation 的最近使用浏览器实例;
没有匹配实例时创建一个。没有活动 Conversation 时聚焦最近使用的浏览器实例;完全不存在时
创建待绑定空实例。该快捷入口不会因连续点击创建重复实例;用户通过工作栏“+”选择浏览器时
则始终创建新实例。入口不打开系统默认浏览器,也不创建旁路 WebView。
选择已有浏览器 Tab 保留其绑定目标、页面和导航状态;通过工作栏“+”选择浏览器时创建新实例,
有活动 Conversation 时绑定该会话,否则创建待绑定空实例。同一 Conversation 的实例共享隔离
登录上下文,页面与导航状态独立。入口不打开系统默认浏览器,也不创建旁路 WebView。
### FR-9 资源监控入口
点击资源监控按钮时展开助手工作栏,并打开或聚焦单实例的本机“资源监控”面板。面板显示
通过右侧工作栏“+”打开或聚焦单实例的本机“资源监控”,已有面板直接选择其 Tab。面板显示
整机 CPU 使用率、内存使用量与总量、磁盘读写速率,以及 GoodBuddy 桌面进程的 CPU、内存
和进程数摘要。指标不可用时显示具体项目和原因,不能显示为 `0`。
@@ -119,36 +120,43 @@ Renderer 内存中最多保留最近 5 分钟。任一条件不满足时停止
也不据此产生侧栏告警。首次采样及停止后恢复的首次采样只建立 CPU 和磁盘 IO 速率基线,
不显示伪造的 `0` 值。
### FR-10 快捷入口状态
### FR-10 工作栏入口状态
工具面板已激活时,对应按钮显示选中状态;面板存在但未激活时保持普通状态。运行中、错误或
工具面板已激活时,对应 Tab 显示选中状态;面板存在但未激活时保持普通状态。运行中、错误或
等待用户处理可以显示徽标,但必须有可访问文字,且不得自动抢占当前面板。
### FR-11 应用导航设置
设置页提供“应用与工具”管理入口。首期“应用”区域列出三个内置应用并提供“显示在侧边栏”
Switch,切换后立即持久化。系统工具区域列出四项内置固定能力,不提供虚假的卸载或停用按钮。
管理 Modal 的四个应用行均提供上下移动和拖动排序,不受启用或常驻状态限制;管理列表、菜单和侧栏共用一份顺序。箭头边界按完整顺序计算,搜索过滤不丢失隐藏项。智能心跳、魔法笔记和本机推理监控另提供应用设置、常驻管理;知识库提供“打开”和排序,不添加通用空设置页或开关。原工作区配置入口及心跳单条计划 enabled 保持不变。设置中“启用应用”与“常驻左侧菜单”分别保存,
修改即时提交;请求期间串行处理并禁用会修改同一配置的控件,失败就地反馈。
魔法笔记设置统一从应用中心对应行进入,笔记页与全局设置不再提供重复入口。持久化成功后的设置变更同步覆盖工具配置写入;每次重开中心先刷新并锁定修改,较新的确认快照优先于迟到的读取结果。
### FR-12 偏好持久化
应用可见性按设备持久化。升级时保留当前行为:已有可用内置应用继续显示;魔法笔记原有功能
启停设置保持原意,不由新的导航可见性覆盖。
四个应用的顺序及可选应用启用、常驻按设备持久化,重启后恢复。默认顺序为知识库、智能心跳、魔法笔记、本机推理监控。心跳应用默认关闭,其余应用默认启用;心跳和魔法笔记默认常驻,本机推理监控默认不常驻;
魔法笔记待办数量默认显示,评论方式与形式沿用现有默认值。迁移保留已保存的启用、常驻和排序选择,
旧魔法笔记入口开关绑定业务启用值,迁为“启用应用”并保留原值;常驻使用独立配置,
只有已发布的独立显隐偏好才映射到常驻,不能由常驻推导业务启停。旧可选应用顺序补入知识库、智能心跳默认位置并保留已有相对顺序,具体补齐规则见[技术设计](./technical-design.md#43-持久化与-ipc)。`heartbeatEnabled` 的缺失值与保存值按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)处理;不读取未发布草案中的 `knowledgeEnabled`。
### FR-13 窄窗口行为
窄窗口下主侧栏仍按现有抽屉方式显示。系统工具按钮保持单行且不与设置按钮重叠;空间不足时
收起文字和非必要分隔,不隐藏四个系统工具或设置。
窄窗口下主侧栏仍按现有抽屉方式显示,应用中心和设置保持可操作且不重叠。应用列表可滚动,
搜索、设置及键盘排序不依赖拖拽;工具沿用右侧工作栏响应式规则,不搬到左侧。
### FR-14 项目活动汇总
用户在任一项目中都能查看跨项目的运行中与待处理会话,并从活动列表直接进入所选会话。
没有活动时隐藏汇总入口;项目菜单保留原有分组与顺序,并显示各项目的活动数量。
用户在任一项目中都能查看跨项目的待处理、运行中与已完成未查看会话,并从活动列表直接进入所选会话。
汇总入口仅在有上述活动行时显示在项目选择器下方,无活动行时整行隐藏。点击后先显示有活动会话的项目,
悬停或点击项目展开会话子菜单,点击会话进入该条对话。项目菜单保留原有分组与顺序,并显示
各项目的活动数量;级联菜单内的项目顺序不随各类活动数量变化。
汇总不受当前项目、搜索条件或最近历史条数限制。计数与排除规则见
[活动逻辑](./logic-design.md#9-项目活动汇总),交互见[活动界面](./ui-design.md#10-项目活动汇总)。
验收覆盖跨项目后台运行、同会话多任务去重、提问与审批优先、较早的仍在运行或等待处理的
会话可见,以及从带搜索条件的页面或有未保存修改的设置页跳转。结束后仅有未读标记的
会话不保留在活动汇总中;已完成未读面板不属于本项交付。
会话可见,以及从带搜索条件的页面或有未保存修改的设置页跳转。成功完成后保留“已完成”
通知,直到用户实际查看该会话;从活动菜单或普通侧栏进入均可清除。设置覆盖下的聊天及
隐藏文档不算查看。完成通知仅保留在当前应用会话内,初始任务历史不补发通知,失败和取消
不产生完成通知;普通未读标记本身不构成活动。
## 6. 权限与安全
@@ -165,49 +173,46 @@ Switch,切换后立即持久化。系统工具区域列出四项内置固定
- 主导航能明确区分固定系统入口和可配置内置应用。
- 运行记录在所有应用显隐组合下保持可见并可打开。
- 左下角不再用“本地工作区”与系统架构占据主要空间。
- 文件、终端、浏览器和资源监控均可从左下角打开正确的工作栏面板,且不产生第二套内容界面。
- 连续点击系统工具不会无意创建重复的浏览器或终端实例;工作区始终复用不可关闭的固定实例。
用户通过工作栏“+”可以显式创建额外浏览器或终端实例。
- 应用菜单只列已启用应用,点击行关闭菜单;本机推理监控打开保留底层工作区的 Modal,其余应用进入主内容区。“管理应用”打开可搜索的既有 Modal。四个应用均可排序,知识库始终可用,三个可选应用可管理启用与常驻,首期无商店入口。
- 系统工具仅从右侧 Tab 与“+”访问;选择 Tab 不创建或改绑实例,“+”可显式创建额外浏览器或终端。
- 资源监控以 2 秒间隔更新,保留不超过 150 个样本;面板不活动、工作栏收起或窗口隐藏、
最小化后停止请求新样本。
- CPU、内存、磁盘 IO 或 GoodBuddy 指标不可用时,页面区分部分可用与整体失败。
- 隐藏内置应用不删除数据、不停止后台计划,并能从设置恢复;保存失败时入口保持原状。
- 取消常驻不删除数据、不停止后台计划;关闭应用按统一规则联动业务入口,保存失败时各处保持原状。
- 魔法笔记设置迁移保留已有选择,应用中心与笔记页共用同一配置。
- 键盘、屏幕阅读器、浅色主题、深色主题和窄窗口均可完成相同操作。
## 8. 分期
### 首期
- 将侧栏最底部改为内置应用中心入口,设置按钮保持独立;具体列表、宫格或卡片样式从
Demo 中收敛确定。
- 应用中心提供魔法笔记、知识库、智能心跳的浏览、搜索与打开,不要求安装。
- 支持应用常驻主导航、取消常驻和排序;未常驻应用仍可从应用中心打开,隐藏不删除数据或停止后台任务。
- 应用中心和应用页面就近提供已有功能设置,共享真实设置来源;已有全局设置入口定位到同一界面。
- 侧栏最底部应用中心打开向上锚定的轻量菜单,无模态遮罩;底部“管理应用”进入与设置相同的整窗 Modal 和可搜索应用列表,设置按钮保持独立。
- 应用中心提供魔法笔记、知识库、智能心跳及本轮追加的本机推理监控入口,不要求安装。本机推理监控 Modal 与服务单独实现和验证。
- 四个应用共享排序;支持智能心跳、魔法笔记、本机推理监控常驻主导航及取消常驻,知识库始终显示。已启用未常驻应用仍可从菜单打开,取消常驻不删除数据或停止后台任务。
- 通用应用设置统一从应用中心对应行进入;应用页面保留实际业务配置,本机推理监控保留“模型与连接设置”。
魔法笔记优先接入待办数量、AI 评论方式和评论形式,入口显示与常驻控制统一,避免重复开关。
- 文件、终端、浏览器保留在右侧工作栏 Tab,不在左下角复制入口,也不纳入应用中心。
- 原计划的本机资源监控仍作为系统工具交付项,在右侧增加单实例面板;与应用中心分别实现和验证。
- 保持运行记录为固定系统入口。
首期验收需覆盖:无商店入口时可以访问全部内置应用;取消常驻后仍可重新打开和配置;
首期验收需覆盖:无商店入口时可从菜单访问已启用应用,并从管理 Modal 恢复关闭的可选应用;取消常驻后仍可重新打开和配置;
不同设置入口状态一致;常驻排序重启后保留;右侧工具切换不改变当前应用页面。
本节是范围调整,Demo 交互通过不代表正式功能已经实现。
### 补充要求:魔法笔记迁移与入口联动
- 将现有魔法笔记设置迁入应用中心的应用设置界面,笔记页面内的设置入口访问同一份配置。
魔法笔记可常驻主导航;原全局设置位置如保留入口,应定位到共同设置界面。
- 将现有魔法笔记设置迁入应用中心对应行的设置界面,移除笔记页面内的重复入口。
魔法笔记可常驻主导航;移除原全局设置入口,保留已有配置值和业务数据。
- 魔法笔记相关布尔开关默认全部开启,包括入口显示(常驻)和未完成待办数量显示。
AI 评论方式、评论形式是单选项,沿用现有默认值,不把“全部开启”解释为同时选中多个模式。
新默认值适用于未配置状态,迁移时保留用户已明确保存的选择。
- 应用关闭后的入口联动必须覆盖主导航和会话页面,不能只更新应用中心卡片或一个菜单。
知识库关闭后,左侧知识库菜单及会话中的知识库入口按钮同步移除;重新开启后按配置恢复。
- “取消常驻”只移除主导航捷径;“关闭应用”移除该应用在主导航、会话等业务界面的入口。
应用中心保留管理和重新开启入口。原型中仅有常驻控制,正式设计需补齐这两种状态,避免混用。
- 入口变化不删除已有笔记或知识数据。知识库已打开页面、会话已关联知识以及后台任务的处理,
在详细逻辑同步时分别定义,不能仅凭隐藏按钮推断功能或后台行为已经停用。
- 知识库侧栏入口始终存在且可调整位置;会话知识范围、检索方式、标签及请求行为继续遵守知识库自身规则。心跳入口按启用和常驻显示,单条计划 enabled 仍由心跳页面管理。
- 可选应用“取消常驻”只移除主导航捷径;“关闭应用”移除该应用在业务界面的入口。
应用中心保留管理和重新开启入口,重新开启沿用常驻偏好,不自动导航。
- 入口变化不删除已有数据。关闭后已打开的可选应用页显示不可用状态及恢复设置入口。
已发出的请求和后台任务沿用各功能的生命周期规则,关闭入口不新增强制取消或重放行为。
补充验收:从任一配置入口关闭知识库,主导航和当前会话按钮立即同步移除,切换会话、
重启后也不重新出现;重新开启后恢复相应入口。验证魔法笔记未配置时的默认开启,以及
补充验收:关闭全部可选应用后,知识库侧栏入口仍可用,知识会话入口及心跳单条计划状态不变;管理 Modal 的知识库行提供“打开”和排序,不能启停或取消常驻。心跳执行边界按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)验收。验证魔法笔记未配置时的默认开启,以及
已有关闭选择在迁移后保留。设置保存失败时各入口保持原状态,不能出现互相矛盾的显示。
### 后续:应用商店与扩展
@@ -219,3 +224,43 @@ Switch,切换后立即持久化。系统工具区域列出四项内置固定
现有 Demo 的商店页、项目管理和阅读空间安装流程仅保留为未来交互参考,不纳入首期验收,
也不作为已有商店能力的说明。首期实现不预建插件安装服务或远程目录协议。
### FR-15 私有化市场与 YAML 交换(后续)
市场面向组织私有化部署。用户配置市场 URL 后即可获取该来源的应用目录与详情,并按需获取
应用;需要认证的来源复用其登录流程。市场不可达时说明原因并允许重试,不影响已安装应用
和内置应用使用,也不自动切换到公共市场。市场 URL 只影响应用市场,不改变模型、桌面更新
或 Runtime 的下载源。市场 URL 按设备保存,重启后保留;认证失效时提示重新认证。
应用支持以 `.yml` 导入、导出,兼容 `.yaml` 扩展名,用于组织内部共享、备份和迁移。
导出内容为应用定义及可迁移配置,包含格式版本、应用标识、应用版本、入口声明和依赖说明;
凭据、会话、笔记及知识库业务数据不随应用定义导出。导入前展示应用摘要、缺失依赖和与已有
应用的冲突;同标识应用由用户选择更新或取消,不静默覆盖本机配置。成功后展示实际版本与可用状态;
获取、校验或更新失败保留原已安装版本与配置,并显示可重试原因。
YAML 描述文件不能代替模型权重或插件程序包。有外部依赖时应明确资源引用;离线导入仅在
所需资源已具备时可完成使用,否则列出需要补充的资源,不显示为可运行。导入应用不会自动
启动模型或执行任务。
未来 ShareServer 提供私有市场目录、应用定义的导入导出、发布和版本资源分发;桌面端负责
配置来源、获取目录、本地导入导出及应用安装管理。服务端职责归属见
[ShareServer 总体设计](../../architecture/share-network-architecture.md)。本节记录后续需求,
尚未确定 API、YAML Schema 或服务端交付版本,不纳入应用中心首期。
### FR-16 本机模型管理入口(新增计划项)
应用中心提供“本机推理监控”内置管理应用,可选择常驻左侧。从常驻入口、应用菜单或管理 Modal 的“打开”均进入同一独立管理 Modal;来源菜单或管理 Modal 关闭,底层工作区、当前项目、会话及草稿保持不变。该入口统一
查看本机语音识别、OCR、向量生成等服务的运行状态与可用资源指标。
Modal 只有服务列表,显示名称、简短状态、引擎、模型、资源和错误,不显示任务历史、任务页签、单任务取消或外部连接列表。独立向量进程显示 CPU 与工作集内存,并标明进程范围;CPU 预热、采样失败和共享进程 worker 无法独立统计时说明原因,不能用零或整个应用进程占用代替服务指标。活动请求可能仍在排队或加载,不能据此宣称服务正在运行。ASR 当前仅显示按需运行且状态未知;OCR 和向量服务使用各自执行模块的状态。
服务行保留实际支持的启动、停止、重启或模型加载、释放,工具栏保留刷新和模型设置入口。停止后重新启动不自动重放已中断任务。
停止、重启或释放前,在管理 Modal 上方打开紧凑影响确认,展示正在使用该服务的任务和影响;没有已登记活动任务时也说明执行前会再次核对。取消时保留运行状态,
失败时关闭旧确认、显示原因并刷新状态,再次操作须重新确认当前影响。多个应用共用同一模型服务时只展示同一个服务,不重复
启动进程。隐藏管理入口、关闭管理 Modal 或取消应用常驻均不停止模型;卸载模型文件是独立操作。
确认打开时不能关闭底层管理 Modal;Escape 只关闭最上层,提交期间禁止关闭及重复执行。关闭后恢复可用入口焦点,具体规则见 [UI 设计](./ui-design.md#91-本机推理管理)。
资源监控仍是只读系统工具,本机推理监控聚焦本地服务状态。该项独立于商店接入生产源码,支持 OCR 空闲加载与释放、内置向量进程启动/停止/重启;TTS 显示不可用。后端保留请求登记及原有取消能力,供执行路径和服务影响校验使用。各引擎边界见
[技术清单](./technical-design.md#111-本机推理任务与执行服务),验证范围见[实施进度](./progress.md)。
原首期资源监控的 GoodBuddy 桌面进程统计范围不因此扩大。
@@ -0,0 +1,375 @@
# 应用导航实施进度
2026-09-23 文档校正:当前 `heartbeat` 已作为可选应用管理,默认值与执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行),设置与导航契约见[技术设计](./technical-design.md#4-应用导航偏好)。下方日期记录保留当时实现与验证结果;其中“心跳始终显示”“无应用级开关”和旧字段删除说明不再代表当前行为。本次仅核对源码并更新文档,未重新执行功能测试。
## 2026-09-18:窄幅单列应用列表
对应 FR-11、US-B6、US-B7、US-D1。管理概览改为紧凑单列横向列表,概览与详情共用 `640px` 最大宽度;左侧显示图标、名称、状态与可换行用途,右侧放置打开、可用的设置及成组排序箭头。行间使用弱分隔线,窄窗口操作换行。保留四应用保存顺序、箭头和拖动规则、知识库与智能心跳始终显示、本机推理监控默认不常驻。现行尺寸与交互见 [UI 设计](./ui-design.md#4-应用中心与共同设置),下方历史记录保留当时布局。
验证:ApplicationCenter、ApplicationMenu、WorkspacePrimitives 共 54 项测试通过;`npm run typecheck`、`npm run lint`、`git diff --check` 通过。全量 `npm test` 超过 60 秒未取得汇总,不记为通过。
Windows Electron fixture 重新编译当前组件、生产 CSS 与中文翻译,使用真实 ApplicationSettingsStore 及临时 IPC;浅深主题分别检查 1280、960、720、640、375px 目标窗口,实际 CSS 视口因缩放约多 1px。计算布局均为单列且无横向溢出,桌面 Modal 宽度为 640px,四行共八个箭头且均可拖动。原生键盘排序、磁盘顺序读取、详情返回及关闭后的焦点恢复通过。证据为临时目录 `application-rows-Hl3X55/result.json`。截图捕获持续报 `UnknownVizError`,禁用硬件加速后仍失败,因此未完成截图审阅;此 fixture 不作为完整 App、安装包或原生指针拖放验收。
中文改动按 deai-writing 自查清单复核。未修改模型、OCR、任务中心或 Agent 执行逻辑,未调用模型,未创建提交。
## 2026-09-18:收拢通用应用设置入口
对应 FR-11、US-B9。移除魔法笔记页面及本机推理监控停用状态的通用“应用设置”按钮,启用、常驻和笔记评论配置统一从应用中心卡片进入。保留管理应用、设置详情返回导航、模型与连接设置及原业务控件。删除仅用于页面快捷入口的 Context、跳转回调和初始详情参数;停用时仍保留页面挂载状态。现行入口规则见 [UI 设计](./ui-design.md#41-共同应用设置页),Demo 已移除页面、停用恢复及全局设置中的重复快捷入口。
验证:App 入口与状态同步定向测试 14 项通过;ApplicationCenter、ApplicationMenu、LocalInferencePage、SettingsPanel 共 128 项通过。首次包含完整 App 文件的运行出现两项既有任务审批失败,以及新增笔记测试未启用 fixture 的失败;修正 fixture 后相关定向测试通过。两项审批失败与上次记录一致,本轮未修改。类型检查、全仓 lint、Demo 语法及 diff 检查通过。本轮未运行全量测试或 Electron 窗口验收,未调用模型,未创建提交;保留已有四应用排序、监控名称和默认不常驻改动。
## 2026-09-18:四应用共享顺序
对应 FR-3、FR-11、FR-12、US-B7。四张管理卡片均提供上下移动和拖动,包含关闭、未常驻以及始终显示的应用;卡片、菜单、侧栏共用 `applicationNavigation.order`。知识库和智能心跳保留无开关的始终启用、常驻行为,位置可调整,标签改为“始终显示”/`Always shown`。当前对话、运行记录、设置仍保持系统位置。搜索中箭头按完整顺序移动,拖动禁用。保留此前 `260px` 自适应网格、本机推理监控名称及默认不常驻。
权威设置返回及写入 schema 接受四个 ID 各一次,拒绝重复、未知和缺失项。仅存储读取补齐旧可选应用顺序及部分默认值,保留已有顺序、显式布尔值和无关设置;规则见[持久化与 IPC](./technical-design.md#43-持久化与-ipc)。没有新增存储版本、应用启用字段或第二份导航状态。
验证:`npm test -- src/main/application-settings-store.test.ts src/shared/goodbuddy-config-contracts.test.ts src/main/goodbuddy-config-service.test.ts src/renderer/src/ApplicationCenter.test.tsx src/renderer/src/ApplicationMenu.test.tsx src/renderer/src/LocalInferencePage.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx` 共 127 项通过;App 排序保存、菜单与侧栏同步、外部事件、重开及入口行为的 9 项定向测试通过。包含完整 `App.test.tsx` 的八文件运行共 390 项通过、2 项失败,失败项是任务中心审批测试在折叠列表中找不到任务行(`App.test.tsx` 的 scheduled approvals 场景),本次没有修改该行为。`npm run typecheck`、`npm run lint` 和 `git diff --check` 通过。全量 `npm test` 运行约七分钟仍未得到汇总,已停止本次测试进程;日志复现上述两项失败,不能宣称全量通过。
Windows Electron fixture 使用当前 ApplicationCenter、生产 CSS/中文翻译、真实 ApplicationSettingsStore 和临时 IPC,从旧两项顺序读取后,移动知识库以及关闭且未常驻的本机推理监控,并用新 Store 实例确认磁盘结果。浅深主题各检查 1280、960、640、375px 目标宽度,实际 CSS 视口因缩放多约 1px;均显示八个排序箭头、四个可拖动卡片,无横向溢出,列数分别为三、三、二、一。详情不含排序,返回搜索焦点和重开顺序通过。最终证据目录为临时 `all-app-order-t0hDya`;已查看桌面浅色及窄屏深色截图。此项是组件与存储验证,未验收完整生产 Main/Preload 启动或安装包,原生指针拖放由组件事件测试之外另行验收。
中文文档运行 deai-writing 扫描,无阻断项;对本次改动的复核候选人工检查,保留明确的产品状态与验收限制。未调用模型,未修改模型管理、GPU 指标或 Agent 执行路径,未创建提交。下方记录保留各次实施时的行为,旧“固定位置”“仅常驻项排序”规则已由本条及现行设计替代。
## 2026-09-18:卡片排序与自适应列数
对应 FR-11、US-B7、US-D1、US-D2。上下移动从设置详情移到常驻可选应用卡片,箭头名称及提示包含应用名;首尾、唯一常驻项和保存锁定时禁用对应操作。拖动也仅允许常驻可选应用,固定应用及未常驻项不参与排序。卡片改用 `auto-fit/minmax`,最小宽度 `260px`,Modal 最大宽度仍为 `960px`;启用、常驻及笔记设置继续使用原详情表单。保留本机推理监控名称及默认不常驻改动。
Windows Electron 组件 fixture 使用当前 ApplicationCenter、生产 CSS/中文 i18n、真实 ApplicationSettingsStore 及临时 IPC。浅深主题各检查 1280、960、640、375px 目标窗口宽度,实际 CSS 视口受缩放约多 1px;分别为三、三、二、一列,均无横向溢出。检查排序持久化、详情无排序及返回搜索焦点,并人工查看桌面浅色、窄屏深色截图。证据位于临时目录 `cards-order-fixture-tucKjv`。完整 App 配合已有 Main/Preload 的两次启动尝试未产生窗口,此次 fixture 不作为完整 App 或安装包验收。
`npm run typecheck`、`npm run lint`、`git diff --check` 通过;`npm test -- src/renderer/src/ApplicationCenter.test.tsx src/renderer/src/ApplicationMenu.test.tsx src/renderer/src/LocalInferencePage.test.tsx src/main/application-settings-store.test.ts` 共 66 项通过,覆盖固定/未常驻项、边界、保存锁定、搜索排序及设置入口。全量 `npm test` 在 60 秒后超时,未取得完整结果。中文改动按 deai-writing 自查清单人工复核。未调用模型、修改模型管理或 GPU 指标,未创建提交。
## 2026-09-18:本机推理监控默认不常驻
对应 FR-11、US-B 的默认值与迁移场景。共享导航默认值改为本机推理监控不常驻;新安装和旧设置缺失整个导航配置时采用此值,应用仍默认启用,可从应用菜单及管理 Modal 打开。默认可选顺序及知识库、智能心跳的固定入口规则保持不变。沿用存储版本 12,已有常驻 `true`/`false` 均保留;旧默认值与主动固定没有来源标记,无法区分,不强制取消已有常驻。Demo 初始状态和恢复默认同步。
验证:设置存储、应用中心、菜单、推理 Modal 共 62 项测试通过,App 入口及设置同步 8 项定向测试通过;`npm run typecheck`、`npm run lint`、Demo 的 `node --check` 和 `git diff --check` 通过。全量 `npm test` 在 60 秒后超时,未取得完整结果。中文改动按 deai-writing 自查清单人工复核。本次未启动 Electron 或调用模型,不涉及 GoodBuddy Agent 或远程执行路径;未创建提交,保留此前未提交的名称调整。
## 2026-09-18:统一名称为本机推理监控
对应 FR-16、US-F3。导航、应用中心、应用设置、Modal 标题及关闭按钮统一使用“本机推理监控”,英文应用名为 Local Inference Monitor。现行功能文档与 Demo 名称同步;`local-inference`、API、组件名及服务行为保持不变,下方历史记录保留当时称谓。
验证:应用菜单、应用中心、Modal 的 27 项测试及 App 入口、设置同步相关的 8 项测试通过;`npm run typecheck`、修改源码 ESLint、Demo 的 `node --check` 和 `git diff --check` 通过。Demo 被 ESLint 既有规则排除。中文改动按 deai-writing 自查清单人工复核,仅同步名称及本条验证事实。本次未运行全量测试或启动 Electron 验收窗口排版。
## 2026-09-18:独立推理进程资源采样
对应 FR-16、US-F3。向量 utility process 已按 PID 接入 Electron 真实 CPU 和工作集内存,服务行显示进程范围、PID、百分比及 MiB。ASR 为主进程线程,OCR 为渲染进程 Web Worker,分别显示不能独立统计的原因。采样口径及基线规则见[技术设计](./technical-design.md)。服务 Modal 继续保持单一列表。
本轮验证:资源采样器、Main 服务、向量 Broker、Modal 共 39 项定向测试通过;IPC 与 OCR Bridge 另有 130 项通过,共 169 项。类型检查、修改范围 ESLint、`git diff --check` 通过。全仓 lint 仅报告未修改的 story-graph Demo 的 22 个 `document` 错误;`npm test` 超过 60 秒未完成,不记为通过。中文功能文档 deai-writing 扫描阻断项为 0,复核项中的采样限制和验证边界按事实保留。
真实 Windows Electron 验证直接调用生产 `createEmbeddingUtilityTransport` 和采样器,使用临时 CPU 工作负载:PID 5892 的工作集从 57,769,984 字节变为 63,090,688 字节,区间 CPU 为 98.71%;退出后返回不可用,重启得到 PID 20532 并重新预热。进程刚 spawn 时指标可能尚未登记,返回等待数据。此验证没有加载推理模型,没有调用模型 API,也没有重新验收完整 App 窗口;macOS、Linux 尚未实机验证。改动仅涉及桌面本机服务监视,不影响 GoodBuddy Agent 或远程执行。
## 2026-09-18:本机推理服务监视简化
对应 FR-16、US-F3、US-F5;US-F4 的 Modal 单任务取消入口退役。`LocalInferencePage` 直接显示服务列表,移除任务页签、历史、外部连接和长说明,保留刷新、模型设置及受支持的服务操作。现行定义见 [UI 设计](./ui-design.md#91-本机推理管理)。
当时没有服务级 CPU/内存采样,统一显示“暂不可用”;这一限制已由上方资源采样实现修正。ASR 模型可用时返回 `unknown`,登记活动请求不再将状态覆盖为 `running`。OCR/向量仍使用各自执行模块的状态。操作失败关闭旧确认并刷新,重试时重新确认当前影响;后端任务清单校验和 OCR 忙碌保护保留。
验证结果:
- 定向运行 LocalInferencePage、Main 服务、向量 Broker、OCR Bridge 和 IPC,5 个文件共 165 项通过;App 的本机推理入口和停用恢复场景 4 项通过。
- `npm run typecheck` 和修改范围 ESLint 通过。`npm run lint` 仍报告无关 `docs/features/story-graph/demo.js` 的 22 个 `document` 错误。
- `npm test` 首次超过 120 秒,再次超过 600 秒,均未完成。第二次报告的 App 失败来自旧页签断言,已改为服务列表并定向复验通过;不将全量测试记为通过。
- 中文功能文档按 deai-writing 扫描,阻断项为 0;复核项中的状态限制和操作清单按产品事实保留。
本轮未启动真实 Electron 或调用模型,未重新验证原生窗口排版。改动仅影响桌面本机服务监视,不涉及 GoodBuddy Agent 或远程执行路径。未创建提交,下方历史记录保留当时的 UI 与验证边界。
## 2026-09-17:应用卡片与紧凑详情
对应 US-B6 至 US-B9、US-F3 至 US-F5。已核对 `ApplicationCenter`、`LocalInferencePage`
及其样式:管理改为桌面双列、窄窗口单列卡片,固定应用仅有打开;可选应用提供打开和设置,
关闭时禁用打开。启用、常驻及上下移动位于最大 `640px` 的详情,卡片拖动排序保留。
详情使用紧凑返回标题栏,进入及返回时滚动归顶。本机推理使用内容高度 Modal,列表状态与操作
分区并响应式排列。现行规则见 [UI 设计](./ui-design.md#4-应用中心与共同设置)。
本轮实现验证结果由实现方提供,本次仅更新文档,未重跑产品测试:
- 59 项组件测试及 8 项选定 App 测试通过;`npm run typecheck`、修改范围 ESLint 和
`git diff --check` 通过。
- 真实 Electron 使用当前 Renderer、已有 Main/Preload、真实 IPC 和隔离配置,四种尺寸、浅深
主题共 32 个视图无横向溢出;导航、焦点、Escape、背景隔离及详情滚动重置已检查。
- 实机任务列表为空态;有任务及长名称由组件测试覆盖。模型调用为 0,未重跑全量测试,
未创建提交;此前全量测试与全仓 lint 的失败记录仍保留,不将整体验收标为完成。
实机报告已核对:`C:/Users/jiang/AppData/Local/Temp/opencode/application-cards-review-cUiVAc/result.json`,
截图位于同目录。下方记录保留各轮当时的布局及验证范围,旧单列管理行和旧 Modal 尺寸不作为现行定义。
## 2026-09-17:本机推理改为独立 Modal
对应 FR-3、FR-5、FR-16、US-B1、US-B6、US-F3 至 US-F5。已核对当前 `App.tsx`、
`LocalInferencePage.tsx`、`local-inference.css` 和 `dialog-focus.ts`:常驻入口、应用菜单及管理
Modal 的“打开”均打开独立本机推理 Modal,保留底层工作区;其他内置应用仍打开主内容区页面。
停止、重启和释放使用嵌套紧凑影响确认,复用背景隔离、焦点循环与恢复,按当前层处理 Escape,
操作提交期间禁止关闭及重复执行。具体行为见 [UI 设计](./ui-design.md#91-本机推理管理)。
以下结果由实现代理提供,本次文档更新未重新运行代码测试:
- 180 项定向测试通过。
- `npm run typecheck` 与修改范围的 ESLint 通过。
- 完整 App 测试运行超时,未取得完整通过结果。
- 全仓 lint 仍有 `docs/features/story-graph/demo.js` 的既有 22 个 `document` 全局声明错误。
随后完成真实 Electron 验证:当前 Renderer 源码配合当日已有 Main/Preload 构建,使用隔离配置,
未模拟 IPC。三个入口均保留同一知识库工作区节点;背景隔离、Escape、焦点恢复及窄窗口侧栏
开关回退通过。使用已有本机资产完成 OCR 模型加载与确认释放,推理请求和模型下载均为零。
浅深主题及窄窗口未出现横向溢出,释放确认框约为 480 × 182 CSS 像素,内边距 16px、按钮高 36px。
修正窄窗口关闭按钮换行后再次通过实机检查和 43 项相关测试。证据位于临时目录
`inference-modal-ui-8KJFgQ/result.json` 及同目录截图;未验证重新打包后的应用。
`npm test` 在 240 秒边界停止,未取得全量通过结果;独立复跑心跳数据库测试为 6 项通过、
2 项失败,均为已有的 `duplicate column name: pinned`。下方旧页面截图不作为 Modal 验收证据。
本轮已同步源码与文档,未修改 Demo,未创建提交。
下方保留各轮当时的实现与验证记录,旧主内容区页面及无 props 描述不作为当前 Modal 的行为定义。
## 2026-09-17:生产界面密度与响应式复核
对应 US-B5 至 US-B9、US-F3。已审阅 `abcd-production-08rUQA` 中 A、B、D 的原生产截图。
应用管理从全宽纵向块改为最大 `960px` 的内容高度 Modal:名称旁标明固定/可选状态,桌面操作
同行,窄屏自然换行且排序箭头成组。共同设置统一标签、说明和开关对齐;本机推理改为弱分隔列表。
菜单只调整共享字体和图标层级。展示规则见 [UI 设计](./ui-design.md#4-应用中心与共同设置)。
最终源码验证:
- 应用中心、应用菜单、本机推理、设置面板和共享 UI 的 5 个定向文件共 148 项通过。
- `npm run typecheck`、改动 Renderer 文件的定向 ESLint、`git diff --check` 通过。
- `npm run build:bundle` 成功;最后一处布局修正后,`npx electron-vite build --logLevel error` 再次成功。
- 全仓 `npm run lint` 仍有 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误,未改动该文件。
- 完整 `npm test` 用时 `648.80s`:4,454 项通过、67 项跳过、2 项失败。失败仍为心跳数据库
迁移的 `duplicate column name: pinned`,位于未改动的 `assistant-database.ts:9988`;全量测试
未通过。完整日志位于临时目录 `abcd-polish-tests-VxXZ34/test.log`。
真实 Electron 使用仓库 package 入口、当前生产 Main/Preload/App 和独立用户目录,未替换 IPC。
最终截图与测量位于临时目录 `abcd-production-m9I4Vy`:`1200×775` 下四行约 `75px` 高,管理
内容区 `scrollHeight` 与 `clientHeight` 均为 `420px`,操作同行;`520×740`、`375×667` 下
管理、共同设置和推理内容无横向溢出。已审阅桌面与窄屏浅深色截图、375px 管理和设置截图。
管理行按钮为 `34px`/`36px`,Switch 标签点击区高 `36px`,评论选项保留共享紧凑分段控件。
生产验证覆盖菜单 End/Escape、Modal 焦点保持、页签方向键,以及 Space 切换常驻后重载 Renderer
确认保存。运行报告为同目录 `result.json`,截图为 `A-menu*.png`、`B-manage*.png`、
`C-settings*.png`、`D-inference*.png` 和 `D-tasks.png`。
此次为界面调整,未改后端生命周期;隔离目录没有安装模型,模型请求为 0,仅检查真实服务快照与任务空态。
日志仍有既有字体 CSP 拦截,未捕获 JavaScript 异常。未验证模型启停、安装包或整个项目的功能验收。
工作区原有 Runtime 与 story-graph 修改保留,未创建提交。
## 2026-09-17:轻量应用菜单与两个固定应用
对应 FR-3、FR-5、FR-11、FR-12、US-B5 至 US-B8、US-D1/US-D2。当前生产源码的底部应用中心向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻状态筛选。点击应用行关闭菜单并打开主内容区;底部“管理应用”打开既有管理 Modal。
[ApplicationMenu](../../../src/renderer/src/ApplicationMenu.tsx) 与 [AnchoredMenu](../../../src/renderer/src/AnchoredMenu.tsx) 承担菜单及键盘、定位;[App](../../../src/renderer/src/App.tsx) 固定依次显示知识库、智能心跳,再显示已启用且常驻的可选应用。[管理 Modal](../../../src/renderer/src/ApplicationCenter.tsx) 中两个固定应用仅提供“打开”,不提供启用、常驻、排序或通用空设置;原工作区配置及心跳单条计划 enabled 不变。
[设置契约](../../../src/shared/application-settings-contracts.ts) 的可选偏好仅包含 `magic-notes`、`local-inference`。未发布草案中的 `knowledgeEnabled`、`heartbeatEnabled` 已删除,不增加迁移或兼容读取。[Store](../../../src/main/application-settings-store.ts) 持久化后发布权威快照的链路保留,包含配置工具写入;重开刷新期间锁定修改,较新快照使迟到的旧读取和保存响应失效。
本节收录本轮提供的最新验证结果;此次文档编辑未重新运行产品测试或模型请求:
- `npm run typecheck` 通过,`npx eslint src` 通过。
- 完整 `npm test` 已结束:4,448 项通过、67 项跳过、2 项失败。两项仍为心跳数据库迁移的 `duplicate column name: pinned`,相关迁移未改动;全量测试未通过。
- 全仓 `npm run lint` 仍有 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误,属于无关文档 Demo;全仓 lint 未通过。
- Electron fixture 共 8 个主题/尺寸场景通过,覆盖原生键盘、焦点、菜单几何、原生视图遮挡及实际 `ApplicationSettingsStore` 持久化。此证据验证组件 fixture 和真实设置存储,不是完整生产 App 的端到端验收,也不是安装包验收。
完整生产 App 补充验证:`npm run build:bundle` 成功后,以隔离用户目录启动仓库 package 入口,
使用实际 `out/main/index.js`、生产 Preload 和 App,未替换 IPC handlers。已验证上拉菜单、
“管理应用”Modal、两个固定应用仅有“打开”、笔记常驻切换,以及刷新 Renderer 和磁盘配置的
持久化结果;本机推理页通过真实注册 IPC 取得 ASR、OCR、向量和 TTS 状态。已审阅菜单与管理截图。
证据位于临时目录 `abcd-production-08rUQA` 的 `result.json`、`A-menu.png`、`B-manage.png`、
`D-inference.png` 和 `runtime.log`。生产启动日志有字体被 CSP 阻止的消息,未捕获 JavaScript 异常。
此轮未重启整个应用验证持久化;隔离目录没有安装模型,未执行模型启停或模型请求。
A–D 已接入源码并通过上述生产 App 冒烟检查;不将全量测试、安装包及整个计划标为通过。
D 的真实 ASR、OCR、向量引擎证据及最小 Main 接线限制保留在下方原记录中,本次状态读取不扩大
其模型操作验证范围。本机资源监控仍待实现。Demo 的直接 Modal 和知识库/心跳可选设置已过时,本轮未编辑 Demo 文件。
下方为历史记录,保留当时实现、测试结果及限制,旧应用级开关和旧入口描述不作为现行行为定义。
## 2026-09-16:知识库固定入口与设置同步更正
本节更正下方早期记录的现行行为,历史实现和验证记录原文保留,不作为最新修订已重新通过验证的证据。
A–D 的应用中心 Modal、可选应用共同设置、常驻排序及本机推理均已接入生产源码,整体验收边界仍见各次记录。
本次文档核对的源码证据:
- [共享设置契约](../../../src/shared/application-settings-contracts.ts) 将可编辑 ID 限为魔法笔记、智能心跳、本机推理;知识库不参与启用、常驻或排序。版本 12 为未发布分支格式,`knowledgeEnabled` 已移除,无兼容旧草案要求。
- [应用中心](../../../src/renderer/src/ApplicationCenter.tsx) 的知识库行只有“打开”;[App](../../../src/renderer/src/App.tsx) 在首个可选槽位后插入固定知识库,再过滤可选应用。知识库始终显示,原工作区实际配置保留;旧的禁用知识库请求守卫说明已从现行设计中删除。
- [设置 Store](../../../src/main/application-settings-store.ts) 持久化成功后发布 `onChanged`;[IPC](../../../src/main/ipc.ts) 转发完整快照,覆盖[配置工具服务](../../../src/main/goodbuddy-config-service.ts) 的写入。App 重开中心时刷新并锁定修改,较新的事件使在途旧读取和保存响应失效。
- [向量 Broker](../../../src/main/knowledge/embedding-inference-broker.ts) 区分调用方返回与 Worker 执行结束;取消或超时后,执行记录在 Worker 确认或退出前仍占用在途名额并计入活动任务和服务停止影响。
本次仅更新文档并核对源码、链接和文本一致性,未运行产品测试或实机验证,也未发起模型请求。
README 已标明 Demo 中知识库启停、常驻及通用设置描述待独立同步;本次未修改 Demo 文件。
## 2026-09-16:D 本机推理生产接入
对应 FR-16、US-F3 至 US-F5。`LocalInferencePage` 默认导出、无 props,已与中心代理的
`local-inference` 页面入口对接,未修改 App 生产入口或应用启用契约。真实支持范围见
[技术清单](./technical-design.md#111-本机推理任务与执行服务)。ASR、OCR、向量生产请求均登记
生命周期,服务操作和任务取消独立。内置向量 Broker 按模型目录共享;OCR 直接控制持有模型的
Renderer Worker。没有新增任务持久化、虚构进度、资源值或 PID。
验证证据:
- 原始 Vitest 配置运行 App、IPC、应用中心、设置存储、推理服务、ASR、OCR、向量 Broker、
Worker 和引擎,共 12 个文件、452 项通过。完整 App 测试使用真实页面模块,没有 resolve 替身。
- 收尾新增 OCR 加载失败恢复用例后,11 个定向文件 202 项通过;加上已通过的完整 App 251 项,
当前覆盖合计 453 项。最终 `npm run typecheck`、D 源码及测试定向 ESLint、`git diff --check`
均通过;diff 检查仅有工作区已有换行符提示。验证生成的临时 bundle 已清理。
- Windows Electron 隔离用户目录,加载当前页面与生产 CSS、生产 Preload、实际引擎模块,
通过临时最小 Main IPC 接线运行已安装的 SenseVoice、PaddleOCR、Granite 模型,未修改模型配置。
最终一轮:3 次向量请求(含完成/取消竞态)、1 次 ASR 静音采样请求、1 次 OCR 图片请求。
向量返回 384 维;UI 停止、启动后再次推理成功;隐藏窗口保持服务运行;OCR 识别 `HELLO`,
UI 加载与释放成功;1080px 和 520px 窗口无横向溢出。未调用外部付费模型。
- 实机取消竞态中执行方自然完成,管理页正确保留完成结果;取消确认和共享进程不被终止的分支
另由 Broker 回归测试验证。停止失败、启动失败重试、新任务影响清单变化由定向测试覆盖。
- 真实向量验证发现浏览器 ONNX `/wasm` 入口无法 fetch 本地资源;Main 改为 Node 条件入口后
实机推理通过,具体执行错误可回传任务列表。
限制与阻塞:本轮不是打包安装包验收,也未驱动完整生产 App 的所有入口与原生浏览器视图。
实机测试 Main 使用最小接线,完整生产 IPC 注册和 App 接入由上述原始测试及类型检查覆盖。
全量 `npm test` 在 200 秒预算内未结束;已复现心跳迁移两项 `duplicate column name: pinned`,
独立重跑仍失败,和应用设置 v12 期望值无关,未修改该数据库迁移。全仓 lint 仍被并行工作目录
`docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误阻塞。不将全量测试或 lint
标为通过。D 不修改 GoodBuddy Agent 或远程执行链路,无远程主机验证要求;没有创建提交。
本节取代下方早期记录中“缺少 LocalInferencePage”的当前阻塞描述,早期验证范围仍保留供追溯。
## 2026-09-16:应用中心、共同设置与知识联动
对应 US-A2、US-B5 至 US-B9。当前源码接入左下角应用中心和独立设置按钮;中心使用设置 Modal
壳层,包含搜索、打开、常驻、拖动及上下移动。四个稳定 ID 为 `magic-notes`、`knowledge`、
`heartbeat`、`local-inference`。应用启用、常驻和顺序独立持久化,固定入口及右侧工具不参与排序。
`ApplicationSettingsStore` 升为版本 12,保留已有 `false` 和评论单选值,为缺失项补默认值。
笔记页与中心使用 `ApplicationSettingsView`;全局笔记入口已移除。保存期间共用锁,失败后读取
确认值,无法确认时锁定修改并允许重新读取。关闭当前应用保留页面挂载及草稿,显示恢复入口。
知识关闭后移除会话知识控件与摘要,保留各会话关联;Main 交互请求入口在授予知识能力及
检索前裁剪新请求范围,不取消已开始的请求。队列派发重新经过同一入口,本机和托管 SSH
分支消费同一个请求对象。未修改 Skills、MCP、专家或本机推理页面与后端实现。
验证记录:
- 合并定向运行 App、IPC、配置与 Renderer 共 10 个 suite,599 项全部通过;随后新增本机推理
主内容区入口用例,完整 App suite 251 项通过,合计覆盖 600 项;
App 仍使用下述临时页面契约替身,其余生产代码未替换。
- `ApplicationCenter.test.tsx`、设置、笔记、更新设置、共享界面与浏览器遮挡及配置契约的
定向回归共 227 项通过;随后增加的保存失败、缺失笔记配置回归与配置契约共 39 项通过。
- `ipc.test.ts` 和中心回归共 127 项通过,包含新请求禁用、在飞请求继续及重新开启的范围恢复。
- `App.test.tsx` 共 251 项通过;因独立负责的 `LocalInferencePage.tsx` 尚未到位,使用临时
Vitest resolve 插件提供无 props 空组件,仅用于验证 App 壳层,未替代或写入生产页面。
- Windows Electron 使用当前中心组件、生产 CSS、真实 `ApplicationSettingsStore` 与临时
最小 IPC 桥接验证常驻、排序、启用及重新读取持久化结果。浅深主题各验证 `1200 × 850` 和
`680 × 560` 窗口,无水平溢出;搜索初始焦点、Escape、关闭焦点恢复及背景隔离通过。
已审阅窄浅色和宽深色截图;证据位于本轮临时目录的 `application-center-*.png`。
- 全量 `npm test` 的 JSON 报告为 4,153 项通过、4 项失败、67 项跳过,另有 suite 加载失败。
其中应用配置契约旧快照已修正并定向通过;其余失败包括心跳旧库迁移两项、工作栏终端标题一项。
终端标题用例随后单独复跑通过,未修改其实现。
App suite 因缺少 `LocalInferencePage` 无法在原始配置下加载,不能将全量检查记为通过。
- `npm run typecheck` 当前仅报告缺少 `./LocalInferencePage`;`npm run lint` 当前仅报告
并行工作目录 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误。
本轮修改的生产源文件定向 Lint 通过。
剩余集成验证:接入另一实现负责的默认导出 `LocalInferencePage` 后重跑原始 App 测试与类型
检查;完整应用的原生浏览器遮挡和共享 Linux Host 真实请求尚未实测。本轮真实模型调用为 0。
本节不宣称本机推理后端、管理页或整个计划已交付,也没有生成提交。
## 2026-09-16:设置改为大 Modal
对应 [US-A2](./user-stories.md#us-a2-从固定入口进入设置)。设置入口保留底层工作区,关闭后卸载
设置内容;布局、关闭与分类直达规则见 [设置中心 UI 规范](../../../UI-DESIGN.md#138-设置中心)。
`App.test.tsx`、`SettingsPanel.test.tsx`、`WorkspacePrimitives.test.tsx` 和
`browser-viewport-occlusion.test.ts` 定向回归共 389 项通过。`npm run lint` 通过。
全量 `npm test` 完成时为 4,354 项通过、9 项失败、67 项跳过;失败涉及本地 Runtime 复用超时、
心跳旧库迁移、远程恢复及 ProjectSwitcher。共享工作区存在并行修改,最终 `npm run typecheck`
报错位于非本次修改的 `use-unviewed-completions.test.tsx`,未将全量检查记为通过。
Windows Electron 使用临时用户目录、当前 Renderer 源码和已有 Main/Preload 输出,验证窗口
尺寸 `1440 × 960`、`1024 × 700`、`680 × 560`(系统缩放使实际 CSS 高宽略有偏差)。
浅色与深色检查通过:整窗背景隔离、左右分栏、无内容横向溢出、遮罩点击不关闭、原生 Tab 与
Escape、关闭后保留原知识库组件。截图保存在本轮临时验证目录;没有运行打包构建,也未单独
驱动原生 `WebContentsView`,其遮挡规则通过共享检测测试验证。本次未修改 GoodBuddy Agent
或桌面到 Agent 的执行路径,未额外发起真实模型请求。
## 2026-09-16:已完成保留至查看
对应 [FR-14](./prd.md#fr-14-项目活动汇总)、[US-E3](./user-stories.md#us-e3-完成后保留至查看)。
已核对当前工作区 `use-unviewed-completions.ts`、`conversation-activity.ts` 和 `App.tsx` 的
完成通知接入:Renderer 会话内集合、后台 Task 转换、本地及持久化活动运行成功标记、
三类计数,以及聊天、设置和文档可见性条件。行为定义见[活动逻辑](./logic-design.md#9-项目活动汇总)。
活动汇总、完成通知 Hook、项目活动组件、项目选择器及 App 活动集成定向回归共 77 项通过。
覆盖前台完成、后台完成、持久化完成补齐、失败和取消、普通会话入口及级联菜单进入、
设置覆盖聊天、窗口隐藏、历史完成不提醒、查看后刷新不重复提示和下一轮完成。
实际聊天详情加载完成后才清除提示。最终 `npm run typecheck` 与 `npm run lint` 通过。
全量 `npm test` 用 900 秒预算运行,762.20 秒结束:4,365 项通过、3 项失败、67 项跳过。
失败为 `agent-package.test.ts` 离线依赖清单安装内部 60 秒超时,以及
`heartbeat-database.test.ts` 两项迁移的 `duplicate column name: pinned`。
共享工作区存在并行修改,此项未改动这些文件,不将全量检查记为通过。
Windows Electron 43.2.0 / Chromium 150 的当前组件 fixture 验证浅深主题、1280×800 和
375×667、混合状态及仅完成状态,共 8 个场景、112 项检查通过,8 张 PNG 已审阅。
检查包含完成图标、三类计数、原生方向键与 Space、进入回调及移除后的空闲隐藏。
证据为临时目录 `activity-completion-0916-results.json` 和 `activity-completion-0916-review.md`。
此项使用受控活动数据,未验证完整 Main/Preload 真实模型端到端;模型及知识库请求为 0。
本次只修改 Renderer 展示与状态跟踪,本地和远程均消费已有事件/Task 更新,不改变 Agent 执行路径。
## 2026-09-14:菜单独立高度修正
生产菜单移除共用双栏表面,会话层独立定位到选中项目行。打开较长会话列表时,项目层的位置、
宽度与高度保持不变;会话层独立限制窗口边界和滚动。两侧均无法容纳子菜单时使用同层返回交互。
`npx vitest run src/renderer/src/ProjectActivity.test.tsx` 通过 13 项,类型检查与 Lint 通过。
`npm test` 运行 60 秒后超时,未取得完整结果。
最终源码的 Electron 43.2.0 / Chromium 150 组件 fixture 使用生产样式与中文 i18n、模拟活动数据,
深浅主题共 14 个场景、108 条检查通过,覆盖短/60 条会话、两层独立滚动、底部避让、向左展开、
375px 窄屏及 640px 两侧空间不足。桌面项目层打开前后矩形一致,短窗口会话层底部保留 16px。
此项验证实际组件排版和交互,不代表完整 Main/Preload 端到端验证;未修改远程执行路径。
## 2026-09-13:生产活动入口
对应 [FR-14](./prd.md#fr-14-项目活动汇总)、[US-E1 / US-E2](./user-stories.md#6-epic-e项目活动级联)。
本页只记录项目活动入口,不表示应用中心计划已交付。
`App` 已接入 `ProjectActivity` 常驻摘要和锚定级联菜单。会话和项目计数继续使用现有
`deriveConversationActivity` 结果,点击会话继续调用 `openActivityConversation`。未增加
持久化字段、IPC 或 Runtime 状态。项目选择器分组及计数保持原有行为。
组件与 App 定向回归通过 32 项,覆盖空闲入口、稳定项目及 SSH Host 顺序、待处理优先、
跨项目精确跳转、设置离开检查、实时状态清理、键盘返回、外部关闭和窄侧栏焦点。
Windows Electron 视觉 fixture 使用当前 `ProjectActivity`、生产样式和 i18n,活动数据为
受控样例。浅深主题分别验证 `1280×800`、`960×720`、`720×640`、`640×420`、`375×600`;
原生键盘验证 Enter、上下及右方向键、End、两级 Escape、Tab 退出,另测左侧回退和空闲入口。
此项证明组件的实际 Chromium 排版与交互,不代表完整生产 Main/Preload 的端到端验证。
浏览器遮挡复用共享菜单相交机制,组件测试检查相交和非相交结果;本轮未单独驱动原生
`WebContentsView` 可见性。远程 Agent 执行路径未改动,未发出模型或知识库请求。
提交前复核:`npm run typecheck`、`npm run lint` 通过;单独运行
`npx vitest run src/renderer/src/ProjectActivity.test.tsx`,11 项通过。
`npm test` 在 240 秒后超时,超时前 `tests/agent-package.test.ts` 的
`verifies and installs offline dependency inventories larger than one MiB` 用例失败,未取得完整结果。
Demo 已按最新讨论隐藏空闲摘要并移除会话行箭头;生产空闲摘要在下述非驻留修正中对齐。
后续界面修正已移除生产会话行箭头,并为活动摘要与下方新建会话增加 `8px` 间距。
## 2026-09-14:摘要非驻留修正
生产 `ProjectActivity` 在无活动时返回空内容,活动归零时关闭级联菜单;新活动只恢复摘要。
组件回归 11 项通过,覆盖初始空闲、活动归零关闭菜单及活动恢复后不自动展开。
App 的 `project activity integration` 定向回归 18 项通过,类型检查与 Lint 通过。
本轮 `npm test` 在 60 秒后超时,未取得全量结果;未重新执行 Electron 视觉验证。
## 2026-09-14:0.13.2 候选验证
正式发布准备时再次复现 App 问答用例的时序失败。活动摘要改为常驻后,等待该入口出现
不再意味着会话初始化和 Runtime 状态读取已经完成;测试有时点击仍被禁用的发送按钮,
留下未消费的一次性问答 mock,继而影响后续用例。
测试现等待已加载的会话和可用发送按钮,并在每个用例前重置问答及审批响应 mock。
答案重载用例等待提交后的界面状态,而不只等待 API 被调用。未修改生产交互、
未增加固定延时,也未跳过失败用例。相关 20 项连续三轮通过,完整 App 与 Agent
发布说明检查共 227 项通过。
最终候选 `npm test` 通过 4,155 项、跳过 66 项、失败 0 项;`npm run typecheck`、
`npm run lint` 和 `npm run release:notes:verify` 均通过。发布准备未执行本地生产构建、
打包或安装包启动探针,原生包验收由主分支与标签 CI 完成。
@@ -4,16 +4,16 @@
| 项目 | 内容 |
| --- | --- |
| 状态 | 设计中 |
| 版本 | 0.2 |
| 日期 | 2026-09-08 |
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
| 版本 | 0.4 |
| 日期 | 2026-09-17 |
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
| 功能逻辑 | [功能逻辑设计](./logic-design.md) |
| UI 设计 | [UI 设计](./ui-design.md) |
## 1. 现状
主页面没有 URL Router,由 `App.tsx` 中的 `WorkspaceView` 和 keep-alive 状态切换:
主页面没有 URL Router,由 `App.tsx` 中的 `WorkspaceView` 和 keep-alive 状态切换;该类型也包含由独立状态打开 Modal 的入口:
```ts
type WorkspaceView =
@@ -21,6 +21,7 @@ type WorkspaceView =
| 'magic-notes'
| 'knowledge'
| 'heartbeat'
| 'local-inference'
| 'activity'
| 'settings'
```
@@ -28,90 +29,72 @@ type WorkspaceView =
文件、浏览器和终端已经由 `RightAssistantSidebar`、`WorkbarShell` 与 `workbar-contracts.ts`
承载。工作区是不可关闭的单实例应用,浏览器和终端是多实例应用。浏览器多实例的跨进程
实现以[浏览器多实例技术设计](../assistant-workbar/browser-tabs-technical-design.md)为准。代码中尚无资源指标采集和资源监控
面板。现有侧栏底部 `user-card` 整行打开设置,并显示“本地工作区”和平台架构。
面板。侧栏底部已替换为应用中心与独立设置按钮。
本次改动增加入口编排、从主侧栏到工作栏的命令通道,以及本机资源监控的 Main/Preload/
Renderer 最小实现。文件、终端和浏览器后端保持不变。
应用中心、四应用共享排序、可选应用常驻及启用联动、共同设置页和本机推理监控已接入生产源码;本机资源监控的
Main/Preload/Renderer 实现仍待交付。系统工具只使用右侧工作栏已有 Tab 和“+”链路。
行为由[功能逻辑](./logic-design.md)定义,验证范围见[实施进度](./progress.md)。
## 2. 模块边界
| 模块 | 责任 |
| --- | --- |
| `App.tsx` | 组合主导航、系统工具栏、设置入口和页面离开检查 |
| `SidebarSystemTools`(新增) | 渲染四个系统工具按钮并发送打开意图 |
| `RightAssistantSidebar.tsx` | 接收打开意图,调用工作栏实例创建或激活逻辑 |
| `App.tsx` | 组合主导航、应用中心、固定设置,持有确认配置及页面离开检查 |
| `ApplicationMenu.tsx` / `AnchoredMenu.tsx` | 底部向上锚定的已启用应用菜单;关闭后导航,或打开既有管理 Modal;共享几何定位与键盘行为 |
| `ApplicationCenter.tsx` | 浏览、搜索内置清单,调用既有页面导航及共同设置入口,编辑常驻和顺序;复用设置 Modal 样式和焦点工具 |
| `ApplicationSettingsView` | 位于 `ApplicationCenter.tsx`,由应用中心对应行进入的应用配置表单 |
| `RightAssistantSidebar.tsx` | 保留右侧工具 Tab 和“+”目录调用链路 |
| `WorkbarShell.tsx` | 继续负责实例策略、Tab、目录和焦点行为 |
| `workbar-contracts.ts` | 继续作为工作栏应用 ID 和实例布局的权威契约 |
| `resource-monitor-contracts.ts`(新增) | 定义本机资源快照、可用性和错误码 |
| `resource-monitor-service.ts`(新增) | 在 Main 中采集并规范化本机与 GoodBuddy 指标 |
| `ResourceMonitorPanel.tsx`(新增) | 按需轮询、保留有界历史并渲染资源面板 |
| `application-settings-contracts.ts` | 增加应用导航偏好 schema 和默认值 |
| `application-settings-store.ts` | 迁移并原子保存应用导航偏好 |
| `PlatformFeaturesSettingsSection` 或独立设置 Section | 编辑“显示在侧边栏”偏好 |
| `application-settings-contracts.ts` | 定义常驻、顺序及应用启用字段;保留笔记既有配置键 |
| `application-settings-store.ts` | 迁移默认值,串行合并更新并原子保存,返回完整确认快照 |
| `PlatformFeaturesSettingsSection` | 移除全局笔记设置页签、跳转入口及专用回调,保留通用设置和远程项目 |
不要在 `SidebarSystemTools` 中读取文件、创建 PTY、操作浏览器或采集系统指标;该组件只发送
有界的工具 ID。
应用中心只编排应用导航和配置,不读取文件、创建 PTY、操作浏览器或采集系统指标。
不增加左侧工具组件、左侧工具状态镜像或主侧栏到工作栏的专用命令通道。
## 3. Renderer 契约
### 3.1 系统工具 ID
### 3.1 内置应用清单
```ts
export const sidebarSystemToolIds = [
'workspace',
'terminal',
'browser',
'resources'
] as const
type BuiltInApplicationId = 'magic-notes' | 'knowledge' | 'heartbeat' | 'local-inference'
export type SidebarSystemToolId = (typeof sidebarSystemToolIds)[number]
```
该类型可以放在 Renderer 内共享模块。首期不暴露 IPC,因为动作只编排已有工作栏 Renderer
状态;实际能力调用继续走现有 Preload API。
### 3.2 打开意图
`App` 向 `RightAssistantSidebar` 传入单调递增的请求或命令对象:
```ts
type WorkbarOpenIntent = {
requestId: number
appId: 'workspace' | 'terminal' | 'browser' | 'resources'
reason: 'sidebar-system-tool'
context?: { conversationId?: string; projectId?: string }
type BuiltInApplicationDefinition = {
id: BuiltInApplicationId
source: 'builtin'
titleKey: string
descriptionKey: string
view: BuiltInApplicationId
}
```
`requestId` 使连续点击同一工具仍能被消费。不要仅设置 `assistantSidebarTab`,因为用户可能已
关闭对应实例,且终端需要按执行空间寻找实例。
全部应用 ID 在 `application-settings-contracts.ts` 的 `BuiltInApplicationId` 定义;`EditableApplicationId` 包含 `magic-notes`、`heartbeat`、`local-inference`。Renderer 的 `applicationDefinitions` 映射图标、国际化键和可选应用启用键,知识库始终可用。
`App.tsx` 默认导入 `LocalInferencePage`,传入 `onClose`、`enabled` 和 `restoreFocus`。`local-inference` 保留为入口 ID,`setView` 在提交工作区切换前将其转为 `localInferenceOpen = true` 并返回,不更新 `view` 或 KeepAlive 缓存;组件按该独立状态挂载及卸载。
静态清单复用既有应用页面和国际化键。管理 Modal 的搜索只过滤该清单,包含未常驻和关闭的应用,不请求
远程目录。启用、常驻和排序从确认配置派生,不在清单中持久化第二份状态。
建议由 `RightAssistantSidebar` 在消费意图后通过现有 `WorkbarShell` 控制接口完成。控制器必须
接收上下文和调用原因,不能只按 `appId` 查找第一个实例:
### 3.2 应用与设置导航
```ts
type WorkbarController = {
openOrActivate(intent: WorkbarOpenIntent): Promise<WorkbarTabInstance>
}
```
应用中心调用既有 `navigateFromSidebar` 或同一页面导航函数,沿用未保存离开检查和焦点
策略。打开前检查确认的启用值;未常驻但已启用的应用不附带常驻写入。本机推理监控从常驻入口、菜单及管理 Modal 均经 `setView('local-inference')` 打开独立 Modal,保留底层工作区与设置草稿;其他应用继续页面导航。
`ApplicationMenu` 按四个应用共用的持久化顺序派生清单,仅按启用过滤,不读取常驻或打开历史。应用行先关闭菜单再导航;底部“管理应用”关闭菜单、恢复锚点焦点并打开既有 `ApplicationCenter` Modal。
应用中心对应行的设置操作仅接受 `EditableApplicationId`;应用页和全局设置不提供重复入口。知识库的管理行提供“打开”和排序,原工作区实际配置保留,不添加通用空设置页。
共同页复用设置 Modal 容器、配置读取及更新方法,不建立独立配置存储或重复表单。
若当前 `WorkbarShell` 没有命令式入口,先增加窄的回调或受控请求 Props,不引入全局事件总线。
### 3.3 工作栏选择与创建
工作区意图始终激活注册表中的不可关闭单实例;仅在迁移或布局损坏时补回。浏览器意图按
`conversationId` 激活最近使用的匹配实例,没有匹配实例时创建一个。工作栏“+”不走该去重
路径,选择浏览器时始终创建新实例。
已有 Tab 仅按实例 ID 激活,保留目标和历史状态。不得按当前项目、执行空间或 Conversation
查找最近匹配实例替代用户选择,也不在选中时自动重建退出或失败终端。
### 3.3 终端匹配
工作栏“+”目录选择终端时每次创建新实例;侧栏系统工具按钮执行“进入当前终端”,采用:
1. 解析当前项目对应的 `ExecutionSpaceSelection`。
2. 在现有终端实例中按目标 identity 查找 `starting` 或 `running` 状态。
3. 存在多个匹配实例时激活最近使用的实例。
4. 没有匹配实例时调用现有终端创建流程。
`closing`、`exited`、`interrupted` 和 `failed` 实例保留为历史 Tab,不阻止快捷按钮创建新终端。
工作栏“+”继续按原规则创建额外终端,不受快捷按钮去重影响。
“+”选择终端每次调用既有创建流程,解析当前 `ExecutionSpaceSelection`,绑定本机或托管
SSH 项目的权威目录;无项目时使用本机 Home。选择浏览器每次创建额外实例,绑定当前
Conversation,无 Conversation 时创建待绑定空实例。同一 Conversation 的浏览器页面独立,
登录上下文共享规则继续由工作栏负责。文件按单实例聚焦,缺失时仅由布局修复补回。
选择现有文件 Tab 保留跟随或固定模式,固定目标失效时不切换目标。
### 3.4 资源监控工作栏应用
@@ -125,8 +108,8 @@ type WorkbarController = {
}
```
该应用不加入升级后的默认打开实例。侧栏资源按钮与工作栏“+”目录都通过既有单实例规则创建
或聚焦它。
该应用不加入升级后的默认打开实例,仅通过右侧已有 Tab 或“+”目录访问,使用既有单实例
规则创建或聚焦。左侧应用清单不包含 `resources`。
## 4. 应用导航偏好
@@ -135,31 +118,35 @@ type WorkbarController = {
在 `ApplicationSettings` 增加独立字段:
```ts
type BuiltInApplicationId = 'magic-notes' | 'knowledge' | 'heartbeat'
type ApplicationNavigationSettings = {
hiddenBuiltInApplicationIds: BuiltInApplicationId[]
order: BuiltInApplicationId[]
pinned: Record<EditableApplicationId, boolean>
}
```
默认值为空数组,保持升级前所有可用入口可见。ID 数组可以避免为每个应用增加一个顶层布尔
字段;Zod schema 必须去重、限制数量并拒绝未知内置
ID。
首期不保存排序,因为内置应用顺序固定。插件偏好在插件系统确定稳定 identity 和卸载保留
规则后另行扩展,不提前混入当前 schema。
`applicationNavigation` 保存四个应用的完整顺序和三个可选应用的常驻布尔值。默认顺序为
`knowledge`、`heartbeat`、`magic-notes`、`local-inference`;心跳和魔法笔记常驻默认 `true`,本机推理监控默认 `false`。
权威返回 schema 和更新 schema 均要求四个 ID 各出现一次,拒绝重复、未知和缺失 ID;`pinned` 包含三个可选应用,缺失 `heartbeat` 时补 `true`,显式布尔值保留。Renderer 直接读取该顺序,再按各入口的可见性过滤。知识库始终启用、常驻,无可写开关键。
按[排序规则](./logic-design.md#3-主导航组合规则)提交完整顺序;不持久化可推导的可见 ID 列表。
首期不预建插件字段,也不为未发布的旧隐藏 ID 草案增加迁移读取器。
### 4.2 与现有开关组合
入口可见条件为:
可选应用侧栏入口可见条件如下,知识库按保存位置始终显示:
```ts
const visible = featureEnabled && !hiddenBuiltInApplicationIds.includes(id)
const visible = featureEnabled && applicationNavigation.pinned[id]
```
`magicNotesEnabled` 继续控制功能启停。知识库和智能心跳若未来增加业务启停开关,也按同一组合
规则处理。功能停用时设置页保留显隐偏好但禁用显隐 Switch,并提供原功能设置入口。导航设置
不能写回现有开关。
`magicNotesEnabled` 保留为笔记启用的唯一权威键,`localInferenceEnabled` 为本机推理监控应用启用键,默认 `true`,不在导航配置再复制 enabled map。`heartbeatEnabled` 控制心跳与监督者应用;Renderer 仅将 `settings?.heartbeatEnabled === true` 视为启用,设置未加载时也不开放入口。默认值及运行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行),Main 与侧栏接入见[监督者运行路径](../conversation-supervision/technical-design.md#2-运行路径)。关闭可选应用时保留常驻控件和恢复入口。
笔记配置使用 `magicNotesShowIncompleteTodoCount`、`magicNoteCommentMode`、
`magicNoteCommentFormat` 和 `magicNoteCanvasPageCount`,应用中心与笔记页使用同一 schema 和更新链路。
画布发送页数默认 1、范围 1~8,具体选择规则见[画布分析契约](../magic-notes/technical-design.md#canvas-analysis-and-comments)。未配置的笔记
布尔值(含 `magicNotesEnabled`)默认 `true`;迁移必须用字段是否存在区分缺失与显式 `false`,
不能用 `value || true`。已有单选值保留,缺失时分别使用 `immediate` 和 `combined`。
若旧“显示入口”实际绑定 `magicNotesEnabled`,保留其启用含义和原值;独立常驻使用新字段,
不得直接把旧业务启用键改名为 pinned。只为确认已发布的独立显隐偏好增加对应迁移。
### 4.3 持久化与 IPC
@@ -173,61 +160,73 @@ Renderer
-> application-settings.json
```
更新继续由 Main 校验可信 sender、Zod 输入和原子写入。设置版本迁移只补充默认空数组,不
修改已有 `magicNotesEnabled` 或其他平台设置。
Main 继续校验可信 sender、Zod 输入,复用 `ApplicationSettingsStore` 的更新队列,读取最新
确认值后合并本次 patch、原子写入并返回完整 `ApplicationSettings`。写入失败不发布目标值。
存储版本 12 补充可选应用导航和启用字段,为缺失字段补默认值;保留已有
`true`、`false`、单选值和无关设置,不迁移或清理笔记、知识及会话业务数据。
新安装和升级时缺失整个 `applicationNavigation` 均使用共享默认值,本机推理监控启用但不常驻;本次不增加存储版本。已保存的常驻值均保留,无法区分旧默认 `true` 与用户主动固定,不强制改为 `false`。
仅存储读取允许补齐旧数据:缺失的 `knowledge`、`heartbeat` 按此顺序前置,再保留已有 ID 的相对顺序,最后按默认顺序追加缺失的可选应用。旧 `[local-inference, magic-notes]` 因此变为 `[knowledge, heartbeat, local-inference, magic-notes]`;完整四项顺序原样保留。缺失或空顺序采用默认顺序,部分 `pinned` 映射仅补缺失键,显式 `false` 保留。重复或未知 ID 仍按现有损坏文件处理规则处理,不静默去重或丢弃。版本 11 迁移时写入归一化结果;版本 12 读取时归一化,下一次设置保存时写入完整结果。IPC 和配置工具写入不接受这些部分数据。
版本 12 属于未发布的分支格式,原草案 `knowledgeEnabled` 已移除,不增加兼容读取器。`heartbeatEnabled` 是当前应用启用字段,由 Store 归一化缺失值并保留显式保存值。
## 5. 侧栏底部重构
现有 `user-card` 是一个覆盖整行的设置按钮。实现时替换为容器和相邻独立按钮:
原 `user-card` 整行设置按钮已替换为容器和相邻独立按钮:
```tsx
<div className="sidebar-footer">
<SidebarSystemTools onOpenTool={openSystemTool} />
<button onClick={toggleApplicationMenu} aria-haspopup="menu" aria-expanded={applicationMenuOpen} ...>
{t('navigation.applicationCenter')}
</button>
<button aria-label={t('navigation.settings')} ...>
<Settings aria-hidden="true" />
</button>
</div>
```
不能在设置按钮内部嵌套工具按钮。设置按钮继续调用 `navigateFromSidebar('settings', trigger)`,
保留设置预加载、焦点恢复和未保存离开确认。
应用中心和设置为相邻独立按钮。设置按钮继续调用 `navigateFromSidebar('settings', trigger)`,
保留设置预加载、焦点恢复和未保存离开确认。该调用只打开设置 Modal,不更新当前工作区或
KeepAlive 缓存;`settingsOpen` 独立管理打开状态。关闭后卸载设置内容,下次按入口指定的分类
和消息通道重新挂载,避免缓存上次分类覆盖本次直达目标。底层工作区继续挂载,普通关闭不跳回聊天。
系统工具按钮调用统一 `openSystemTool`:
应用菜单使用 `AnchoredMenu` 的 body Portal,向上锚定,无模态遮罩或背景隔离;`menu` 语义纳入共享浏览器 viewport 相交遮挡。管理 Modal 及共同设置沿用原 Portal、焦点隔离、恢复及 Escape 机制。
不在主侧栏复制工具入口。工作栏仍由窗口右上方固定开关展开和关闭,关闭后焦点返回该开关。
1. 设置工作栏打开意图。
2. 展开工作栏。
3. 由工作栏创建或激活实例。
4. 按现有策略把焦点移入目标面板。
## 6. 应用可用性与知识请求
工作栏仍由窗口右上方的固定开关关闭,关闭后的焦点返回该开关。
Renderer 从同一确认快照派生可选应用主导航、中心状态和页面可用性,依赖只包含相应
字段。keep-alive 页挂载不代表应用仍启用;禁用后在原位置渲染不可用状态,
保留页面草稿及业务数据,阻止新的页面操作。重新启用不触发导航或业务重放。
## 6. 工作栏状态与快捷按钮
`ApplicationAvailability` 隐藏并设置业务内容为 inert,保留挂载的页面实例;重新启用从
应用中心对应行的设置进入。笔记页面直接消费确认的评论配置,
不因修改评论方式而重建编辑器。
`RightAssistantSidebar` 应向 `App` 暴露当前活动实例的 `appId` 和必要的有界状态摘要,或由
现有 `onTabChange` 扩展得到。主侧栏不读取终端输出、文件内容、URL 或活动详情。
知识库不经过 `ApplicationAvailability`;会话知识按钮、范围、检索方式和标签继续遵守原知识库规则。心跳页面受应用可用性控制,单条计划 enabled 保持不变。
请求构建没有应用级禁用知识库的裁剪或守卫;本机、托管 SSH 和排队请求沿用原知识上下文链路。
单个知识库实例的配置、停用、删除及取消清理仍由知识库功能负责。
```ts
type SidebarSystemToolState = {
activeToolId?: SidebarSystemToolId
statusByToolId?: Partial<Record<SidebarSystemToolId, 'attention' | 'error' | 'running'>>
}
```
## 7. 串行保存与确认快照
首期可以只实现 `activeToolId`,没有可靠聚合来源时不显示状态徽标。不能根据局部事件猜测错误
或运行状态。
中心与应用页的启用、常驻、排序及笔记配置共用一个 Renderer 更新入口及 pending
状态。任一提交未完成时,所有入口中修改这组设置的控件暂时禁用;下一次操作基于最新确认
快照生成 patch,不提交整个过期 `ApplicationSettings` 覆盖无关字段。Main 队列仍负责串行合并。
## 7. 应用显隐更新
应用显隐 Switch 复用平台功能设置的即时更新模式。请求开始后禁用三个应用显隐 Switch,只
允许一个完整目标集合处于提交中。Main 返回成功结果后更新 App 中的平台设置快照,主导航
随之变化。失败时恢复控件原值,在对应设置行显示可重试错误;主导航继续读取最后一次成功
保存的设置。
`ApplicationSettingsStore.update` 在持久化成功后通过 `onChanged` 发布完整快照;IPC 转发
`applicationSettingsChanged`,Preload 提供 `updates.onSettingsChanged`。工具配置服务调用同一 Store,
因此配置工具写入也同步界面,保存失败不发事件。`App` 收到事件后推进读取修订号并替换快照,
迟到的旧读取及旧保存响应不能覆盖新状态。每次重开中心都刷新配置,刷新期间锁定修改,失败后
保留锁和重试入口;控件不先乐观生效。
明确保存失败时保留最后确认值,就地显示可重试错误与编辑上下文。若 IPC 响应中断而写入
结果未知,通过既有设置读取重新取得权威快照再解锁;读取失败继续显示错误并允许重试读取,
不自动重发变更或写回旧配置。无须引入持久化回滚日志或独立导航快照文件。
## 8. 插件兼容边界
首期不增加插件注册表、插件 schema 或插件持久化字段,只保留系统 ID 不向插件开放的产品边界。
插件市场设计需要单独确定 Host、权限、签名、生命周期和稳定 identity;不得直接复用 DeepSeek
Harness 的 npm 插件市场。
首期不增加市场 URL、YAML 导入导出、ShareServer API、插件注册表或安装服务。FR-15 的
私有市场协议、YAML Schema、资源分发与扩展生命周期另行设计;系统 ID 不向插件开放。
不得直接复用 DeepSeek Harness 的 npm 插件市场。后续依赖见第 11 节,首期不预建占位界面。
## 9. 本机资源监控
@@ -333,23 +332,28 @@ resourceMonitor.getLocalSnapshot(input: {
### 10.1 Renderer 测试
- 固定入口顺序不受三个应用显隐组合影响。
- 运行记录和设置没有显隐控件。
- 四个系统工具按钮具有名称、工具提示、键盘行为和选中状态。
- 文件复用不可关闭单实例;浏览器和终端按当前上下文聚焦匹配实例,工作栏“+”允许显式多实例。
- 工具按钮展开工作栏但不改变当前主页面。
- 显隐更新成功后主导航立即变化,失败时 Switch 和主导航恢复原状态。
- 窄窗口下工具按钮不重叠,焦点恢复正确。
- 固定入口不受应用启用、常驻和顺序组合影响,应用中心及设置始终可达;左侧没有重复系统工具。
- US-B6:底部菜单向上锚定且无遮罩,只列已启用应用,不受常驻或打开历史影响;点击行关闭菜单并导航,“管理应用”进入既有可搜索 Modal,包含关闭及未常驻应用。
- US-F3/US-F5:三个入口均打开本机推理监控 Modal 并保留底层工作区;覆盖嵌套确认的焦点循环、逐层 Escape、提交期间禁止关闭与重复执行、关闭后的入口及后备焦点恢复,以及停用内容后的键盘焦点。
- US-B7:常驻、取消常驻、键盘及拖动排序成功后同步,重新开启及重启保留原偏好和顺序。
- US-B8:知识库始终启用且常驻,对应行提供打开与排序,不提供启用或常驻开关;心跳应用启停按监督者逻辑验收,单条计划 enabled 不变;可选应用关闭后的不可用页保留草稿,重新开启不自动导航或重放。
- US-B9:应用中心对应行进入笔记表单,管理启用、常驻、待办数量、评论方式及形式;笔记页和全局设置无重复入口。
- US-B5:所有入口共享 pending,保存失败保留相同确认值;重开刷新期间锁定修改,较新事件使旧读取失效;结果未知时重读,不自动重发。
- 文件复用不可关闭单实例;选择任一已有终端或浏览器 Tab 保留确切目标,“+”每次创建额外实例。
- 已退出、失败的终端 Tab 不因选中而重建;工具切换不改变主页面,后台状态不抢占 Tab。
- 窄窗口、键盘排序、共享 Switch、Tab 语义及浮层焦点恢复正确;中心和设置不被原生浏览器遮挡。
- 资源面板只在可见活动时轮询,跳过重叠请求并把历史限制为 150 项。
- 窗口隐藏、最小化和恢复时停止轮询并重新建立区间基线。
- 资源部分可用、全部失败和重新进入活动状态均显示正确结果。
### 10.2 Main 与契约测试
- 新设置字段默认值、合法 ID、重复 ID、数量上限和未知 ID 校验。
- 旧版本设置迁移后默认显示所有既有应用。
- 更新导航偏好不修改魔法笔记启停等无关字段。
- 原子写入失败时保留最后一次有效设置。
- 顺序包含四个应用 ID 各一次;公开写入拒绝重复、缺失、未知 ID 及无效常驻字段;旧存储顺序按迁移规则补齐,不为未发布的 `knowledgeEnabled` 添加迁移。
- 未配置的心跳应用默认关闭,其余应用启用及笔记布尔项默认开启,本机推理监控默认不常驻;已保存的启用、常驻值和评论单选值经迁移及重启保留。
- 更新常驻或顺序不修改启用、计划或业务数据;笔记既有配置键保持语义。
- 串行 patch 在 Main 最新快照上合并,原子写入失败不发布目标状态;响应丢失后重读实际结果。
- Store 持久化后才发布变更,工具配置写入使用同一通知链路;知识库不受可选应用偏好影响。
- 保存启用失败不会改变业务判断;会话关联和历史知识引用不因入口隐藏被清空。
- 资源快照拒绝 NaN、Infinity、负数字节数、越界百分比和未知错误码。
- 采集超时、权限不足和磁盘 IO 不支持时返回有界部分结果。
- 超时采集尚未实际结束时保持单飞,不累积新的后台采集任务。
@@ -369,40 +373,130 @@ resourceMonitor.getLocalSnapshot(input: {
- 浅色、深色、`220px` 与 `420px` 侧栏宽度。
- Windows、macOS 和 Linux 的真实 CPU、内存与磁盘 IO 采集;不支持项显示准确原因。
FR-16 单独覆盖 US-F3 概览去重、未知指标、US-F4 单任务取消与完成竞态、US-F5 服务
实际启动/停止/重启或加载/释放、使用方确认、外部管理限制及失败真实状态;用接入的真实引擎
验证共享服务不被单任务取消终止,重启不重放。设计文档检查或模拟服务不算生产路径验证。
## 11. 实施顺序
实施范围以 [PRD 分期](./prd.md#8-分期)为准。本节替代旧实施顺序;前文关于四个左侧
系统工具按钮、仅持久化隐藏 ID 的契约需在实现前同步,不能直接按旧规格开发。
实施范围以 [PRD 分期](./prd.md#8-分期)及最新评审决定为准,逻辑映射见
[需求对应](./logic-design.md#6-与需求的对应关系)。既有 FR/US 编号保持稳定;新增故事使用
US-B6 应用中心、US-B7 顺序、US-B8 启用联动、US-B9 笔记迁移,以及 US-F3 概览、
US-F4 取消、US-F5 服务生命周期。PRD、User Stories、配套设计与根 UI 规范已同步入口及持久化规则。
1. 同步 PRD 功能要求、User Stories、逻辑、UI 与技术契约,收敛首期应用中心样式。
以下为实施阶段划分,应用中心及本机推理监控已接入源码,验证状态以进度记录为准。
1. 底部入口使用向上锚定的轻量菜单,通过“管理应用”进入既有整窗 Modal;核对已发布配置的迁移来源。
2. 接入现有内置应用页面与最底部应用中心入口,保留独立设置和固定运行记录入口。
3. 为常驻显隐和排序设计最小持久化契约,保留已发布的相关设置与用户数据,按保存结果更新导航。
4. 接入应用设置的共同界面与既有设置更新路径,先验证魔法笔记多入口状态一致性。
5. 验证右侧现有文件、终端、浏览器 Tab;移除计划中的左下角工具快捷条和专用于该快捷条的意图接口工作。
3. 实现常驻、完整顺序及启用契约,迁移时保留明确旧值,通过串行保存与确认快照同步所有界面。
4. 迁移魔法笔记共同设置,保留知识库始终可用及原工作区配置、心跳单条计划 enabled,验证可选应用关闭和重新开启。
5. 验证右侧现有文件、终端、浏览器 Tab 的确切目标与“+”额外实例行为,不实现左侧重复工具条。
6. 单独实现资源快照契约、Main 采集服务、Preload API 与右侧 `resources` 单实例面板。
7. 验证键盘、焦点、浅深主题、窄窗口、设置失败、重启持久化和跨平台行为。
首期使用内置应用清单,不依赖远程商店、插件注册或安装服务。商店目录、推荐、安装与
更新机制在后续独立设计中确定;Demo 中的模拟商店不接入首期产品界面。
本轮 `local-inference` 已接入管理 Modal、类型化 IPC 与真实引擎管理,支持范围见 11.1;
实机和自动化验证范围见[实施进度](./progress.md)。
后续设计输入见 [FR-15](./prd.md#fr-15-私有化市场与-yaml-交换后续) 与
[FR-16](./prd.md#fr-16-本机模型管理入口新增计划项):
- 私有市场的 YAML Schema 应由桌面本地导入与 ShareServer 发布共用,具体协议与字段在
市场功能设计中确定。市场 URL 按设备保存,认证信息沿用受保护凭据存储;导出仅包含
可迁移应用定义与非敏感配置。
- 程序包与模型资源单独分发。受管下载声明不可变版本、预期字节数和 SHA-256,下载开始后
固定来源,失败不静默切源;下载地址与重定向范围由 Main 验证,不接受 Renderer 任意覆盖。
市场来源配置不覆盖现有模型、应用更新或 Runtime 下载源设置。
- ShareServer 的市场发布与分发实现由其独立仓库承接,本仓库先记录依赖,不将桌面需求
记录标为服务端已实现。
### 11.1 本机推理任务与执行服务
ASR、OCR 与内置向量的跨功能执行边界、任务控制缺口及有条件的优化方向,见[本机专用推理架构与任务控制评估](../../architecture/local-inference-task-control.md)。该评估区分源码现状、建议与待办,未将常驻化或后台宿主迁移列为已采用实现。
`LocalInferencePage` 默认导出,接收必需的 `onClose` 及可选的 `enabled`、`restoreFocus`,通过 `createPortal(..., document.body)` 渲染管理 Modal,复用 `PageHeader` 和共享
`activateModalFocus`/`trapTabFocus`。`ImpactConfirmation` 使用独立 Portal 和紧凑确认样式;共享焦点工具隔离下层 Modal,并在关闭时恢复可用触发元素或指定后备焦点。`operation` ref 防止重复执行,`busy` 和 `control` 控制关闭与 Escape,交互细节见 [UI 设计](./ui-design.md#91-本机推理管理)。`local-inference-contracts.ts` 定义快照、操作与取消输入;Main 注册四个
`local-inference:*` 通道,复用可信 sender 校验和 Zod 输入校验。Preload 只暴露读取、服务操作、
任务取消及打开既有设置的方法,不接收 PID、命令或模型路径。
| 引擎 | 生产接入与可用操作 | 明确限制 |
| --- | --- | --- |
| ASR / sherpa-onnx | `SpeechTranscriptionService` 登记真实请求;执行路径保留独立 Worker 请求取消 | 每次请求加载、结束释放;未提供 Worker 就绪观测,模型可用时状态为 unknown,不从活动请求推断 running |
| OCR / PaddleOCR | `DocumentOcrBroker` 登记文档请求;`document-ocr-bridge` 提供实际 Worker 状态、加载及空闲释放 | 有活动或排队请求时拒绝释放;不把终止共享 Worker 暴露为单任务取消 |
| 内置向量 / Granite | 按模型目录复用引用计数 Broker;启动、停止、重启;取消消息等待 Worker 结果或取消确认 | 启动确认进程 spawn,模型在首个请求时加载;不把进程启动称为模型已加载 |
| TTS | 显示当前没有执行服务 | 不提供虚假任务或控制 |
| 外部向量连接 | 快照契约保留配置,Modal 不显示;通过既有模型设置管理 | 本页不主动探测,不推断本机归属,不控制外部进程 |
`LocalInferenceService` 在 Main 内存中登记真实 ASR、OCR、向量请求,保留活动请求和最近
100 条终态记录。只保存能力来源、时间、模型(执行路径有提供时)、状态和错误,不保存音频、
图片或向量输入文本。运行阶段包括排队、模型加载及执行,没有统一阶段进度;来源未提供项目或
会话对象时不补造对象关联。Modal 仅使用活动请求生成影响确认,不展示任务历史或单任务取消。
向量执行端通过 `utilityProcess.fork` 创建独立进程。每个 transport 持有自己的资源采样器,按当前 `child.pid` 从 `app.getAppMetrics()` 匹配 Utility 条目。服务快照的 `resources.scope` 为 `service-process`,携带 PID、采样时间及可用指标;无进程或无法采集时为 `unavailable` 并给出原因。Broker 清除退出的 transport,重启使用新采样器,不复用旧进程指标。
Modal 可见且启用时每 5 秒读取一次快照,跳过重叠请求。CPU 使用累计 CPU 秒数之差除以单调时钟间隔,100% 表示一个逻辑核心,可超过 100%;首次读取、PID 或创建时间变化、采样间隔超过 15 秒、计数回退及采样失败后重新建立基线。15 秒阈值为定时器延迟和跳过重叠请求留出余量。工作集由 Electron 的 KiB 转为契约中的字节,界面显示 MiB。缺少 CPU 时间或内存读数时只标记该指标不可用,不补零。数值为整个向量服务进程的占用,包含运行时和并发使用方,不是单任务或模型权重大小。
ASR 使用主进程中的 `node:worker_threads`;OCR 使用渲染进程中的 Web Worker,两者均没有独立进程工作集,逐行标明无法独立统计 CPU/内存。没有将主进程或整个 Renderer 的读数归属给服务。显存未采集,本机资源监控工作栏应用仍未实现。
服务操作按 serviceId 串行,操作期间拒绝新增该服务请求;停止前校验所有当前活动 taskId
都在用户确认清单内,新增使用方要求刷新后重新确认。向量停止等待实际 child exit;失败保留
可观测状态和错误。Renderer 操作失败时关闭旧确认并重新读取快照,刷新期间或失败时禁用服务操作;再次操作须重新确认当前活动请求。重启不重放中断请求。任务取消与服务停止分开,完成竞态保留执行方结果。
向量 Broker 将调用方 Promise 与 Worker 执行记录分开。调用方取消或超时可先返回错误,
执行记录在 Worker 确认、返回结果或进程退出前仍为活动项,保留在途名额并计入服务停止影响;
停止等待超时也不能提前移除记录。请求取消与自然完成竞争时采用执行方结果。
OCR 模型可用性由 Main 校验,Worker 运行状态由实际持有它的 Renderer 模块提供。手动加载
同样复用既有 Worker,加载超时恢复为可重试错误;空闲 60 秒自动释放。Modal 启用、文档可见且内容未被 hidden/inert 隔离时每 5 秒刷新,跳过未结束的请求;嵌套确认期间暂停新快照读取。
关闭 Modal 时卸载组件并清理轮询,窗口隐藏、关闭管理应用或取消常驻不会触发服务停止。退出整个进程继续
沿用原有应用清理路径。模型文件卸载仍在模型设置中独立进行。
向量 Main 引擎使用 `onnxruntime-web` 的 Node 条件入口,避免浏览器 `/wasm` 入口通过 fetch
加载本地文件失败。执行方的具体错误随任务返回;Renderer 不把失败展示为零指标或恢复成功。
## 12. 项目活动汇总
对应 [FR-14](./prd.md#fr-14-项目活动汇总),按[活动逻辑](./logic-design.md#9-项目活动汇总)
派生状态,界面行为见[活动界面](./ui-design.md#10-项目活动汇总)。
- `conversation-activity.ts` 从 `App.tsx` 的会话、`activeConversationIds`、全部可见
`assistantTasks` 和项目元数据派生会话行、全局及项目计数,不新增持久化活动副本。
`ProjectActivity` 渲染汇总与弹窗,`ProjectSwitcher` 复用项目计数。
`assistantTasks`、`completedConversationIds` 和项目元数据派生会话行、全局及项目计数,不新增持久化活动副本。
`ProjectActivity` 在有活动时渲染汇总与项目、会话级联菜单,`ProjectSwitcher` 复用项目计数。
- `use-unviewed-completions.ts` 在 Renderer 内存维护完成集合和上一轮 Task 状态;已观察到的
顶层 Task 转为 `completed` 时加入集合,初始历史不生成通知。`App` 的本地 `done` 事件和
活动运行持久化快照的成功终态调用 `markConversationCompleted`,失败、取消不调用。
新运行及 Task 恢复运行或等待处理时清除旧通知。具体优先级和通知生命周期以活动逻辑为准。
- `App` 仅在 `view === 'chat' && !settingsOpen && !applicationCenterOpen` 时向 Hook 传入当前会话 ID;Hook 监听
`visibilitychange`,文档不为 `hidden` 时才按当前会话清除通知。这使普通侧栏、活动菜单
导航和可见性恢复共用查看规则,KeepAlive 挂载本身不代表查看。集合不写入 SQLite 或设置。
- 聚合状态新增 `completed`,全局和项目计数包含 `attention`、`running`、`completed`,
同一 Conversation ID 只按最高优先级计数;活动行为空才隐藏摘要。
- `App` 向 `ProjectActivity` 传入项目元数据、活动行、侧栏可见状态与既有精确跳转回调。
菜单按项目 ID 分组并记忆化派生集合;排序仅影响展示,不改聚合状态或持久化数据。
- 两层菜单在同一 body Portal 内相接,桌面会话层使用独立 fixed 定位,不参与项目层高度计算。
项目层锚定摘要,会话层锚定选中项目行并独立限制窗口边界。布局在打开、层级变化、实时数据更新、窗口缩放和
滚动时按锚点重算;窄窗口隐藏项目面板并保留返回操作。菜单不调用 Modal 焦点隔离,
捕获阶段消费 Escape,防止触发窄侧栏的文档级监听;Tab 关闭后交还正常焦点顺序。
菜单关闭与焦点恢复在导航回调之前同步完成。
- `role="menu"` 使浮层直接进入共享 `browser-viewport-occlusion.ts` 相交检测。未增加
浏览器实例的特殊隐藏条件,也不关闭或重建浏览器会话。
- 持久化快照刷新时,同一条仍在流式输出的消息保留本地待审批、待回答信息,持久化终态
则清除这些信息。响应成功后对应 Task 立即退出等待状态;响应期间到达的新问题、审批或
终态不能被旧响应覆盖。
- `AssistantDatabase.listTasks()` 保留最近历史窗口(默认 100 条),额外包含窗口外当前
运行或等待处理的可见 Task;`activeVisibleTaskSelect` 与 `toTask` 的有效状态保持一致,
待调度运行规范化为 `queued`,不因旧 Task 状态被误纳入实时集合。
- `listConversations()` 保留最近 100 条活动会话,同时包含上述 Task 关联的较早活动会话,
以及含 `streaming` 消息的较早活动会话。两类列表去重后仍按原时间顺序返回,结果允许超过
历史窗口大小,避免后台工作被最近记录挤出。
- `listConversations()` 的范围与排序遵守
[会话列表读取与前端保留](../assistant-workbar/execution-history-storage.md#会话列表读取与前端保留),
包含上述 Task 关联及含 `streaming` 消息的较早活动会话,避免后台工作被最近记录挤出。
`listTasks()` 仍按原时间顺序返回;列表结果允许超过历史窗口大小。
- `openActivityConversation` 按确切 Conversation ID 导航;内存中缺失时刷新持久化会话
列表并合并,再查找同一 ID。刷新失败或会话已不存在时显示通知,不回退到项目最新会话。
设置离开检查通过后才提交项目、会话及界面清理状态。
- 远程恢复成功提示沿用独立的自动消失计时器;活动汇总更新不重置或替代该计时器。
完成通知的定向验证应覆盖本地成功、持久化活动运行成功、后台 Task 状态转换、初始历史、
失败和取消、实时状态优先、三类计数、普通侧栏查看、设置覆盖、隐藏文档与恢复可见、无活动行隐藏。
验证记录见[项目活动级联实施进度](./progress.md);源码核对不能替代真实 Electron 路径验证。
@@ -4,130 +4,152 @@
| 项目 | 内容 |
| --- | --- |
| 状态 | 设计中 |
| 版本 | 0.2 |
| 日期 | 2026-09-08 |
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
| 版本 | 0.4 |
| 日期 | 2026-09-17 |
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
| 功能逻辑 | [功能逻辑设计](./logic-design.md) |
| 全局规范 | [GoodBuddy 统一界面设计系统](../../../UI-DESIGN.md) |
本文定义入口、设置和轻量资源监控界面。文件、终端和浏览器的内容布局继续遵守助手工作栏
文档。
本文定义已接入的应用中心、共同设置和本机推理监控界面,以及待实现的右侧资源监控界面。
行为以[功能逻辑](./logic-design.md)为准,需求保留 FR-1 至 FR-16 与既有 US 编号。
根 UI 规范已同步应用中心与工具入口规则;共享控件、主题、设置 Modal 与工作栏布局遵守根规范。
## 1. 侧栏信息架构
```text
品牌
项目选择
项目活动汇总(有活动时)
项目活动汇总(仅有活动时显示)
新对话
搜索
当前对话
魔法笔记 3
知识库
智能心跳 1
应用(按保存顺序;知识库始终显示,可选应用仅显示启用且常驻项)
运行记录
最近对话
...
────────────────────────
[文件] [终端] [浏览器] [资源] [设置]
[应用中心] [设置]
```
“当前对话”和“运行记录”属于固定系统入口。三个内置应用位于二者之间。侧栏底部不再显示
“本地工作区”和 `platform · arch`;排障信息移至关于或诊断页面。
“当前对话”和“运行记录”属于固定系统入口,常驻应用位于二者之间。首期应用清单为魔法笔记、
知识库、智能心跳及本轮追加的本机推理监控,应用中心与设置始终可见。侧栏底部不再显示“本地工作区”和 `platform · arch`;
排障信息移至关于或诊断页面。左侧不再提供文件、终端、浏览器、资源监控的重复入口。
## 2. 底部系统工具栏
## 2. 底部应用中心入口
底部使用连续平整表面和顶部分隔线,不做成账户卡片或嵌套卡片。按钮顺序固定:
底部使用连续平整表面和顶部分隔线,应用中心在前,固定设置靠右,二者为独立按钮:
| 位置 | 图标 | 可见工具提示 | 点击结果 |
| --- | --- | --- | --- |
| 1 | `FolderOpen` | 文件 | 聚焦不可关闭的工作区面板 |
| 2 | `SquareTerminal` | 终端 | 打开或聚焦当前执行空间终端 |
| 3 | `Globe` | 浏览器 | 聚焦当前 Conversation 的匹配浏览器实例 |
| 4 | `Activity` | 资源监控 | 打开或聚焦本机资源监控面板 |
| 末位 | `Settings` | 设置 | 打开设置页面 |
| 1 | `Grid2X2` | 应用中心 | 向上展开已启用应用菜单,底部“管理应用”进入管理 Modal |
| 末位 | `Settings` | 设置 | 在当前工作区上打开设置 Modal |
图标使用项目现有 Lucide 库。每个按钮采用统一的 `34px × 34px` 点击区域,内部图标建议
`16px`。工具栏使用 `8px` 内边距和 `4px` 间隔;设置按钮通过自动空白靠右,不添加第二条
视觉分隔线。
图标使用项目现有 Lucide 库。应用中心保留文字,设置图标采用统一的 `34px × 34px` 点击区域。
底部使用共享间距和焦点样式,设置通过弹性空白靠右;不得把整个容器做成设置按钮。
### 2.1 状态
- 默认:透明背景,图标使用次级文字色。
- 悬停:使用弱表面色,不能强于当前选中状态。
- 选中:使用强调选中背景,并提高图标对比度。
- 展开:应用中心按钮显示展开反馈,不能当作当前应用的选中状态。
- 键盘焦点:显示统一焦点环,不能被侧栏边缘裁切。
- 处理中:创建或激活请求未完成时暂时禁用对应按钮,保留尺寸并提供进行中名称。
- 徽标:只用于等待处理、错误或活动状态,不显示无意义的常驻绿色圆点。
- 加载:读取应用配置时保留入口,中心内显示加载状态;失败提供重试,不用默认值冒充已保存配置。
选中状态表达“对应工作栏面板正在显示”,不表达功能已安装或权限已授予。
应用中心入口不承载工具运行徽标。全部可选应用关闭时菜单仍显示知识库及“管理应用”,可进入 Modal 重新开启可选应用。
## 3. 主导航应用区
应用入口沿用现有 `nav-item` 行,不使用应用卡片。图标、名称和徽标保持单行:
```text
[图标] 魔法笔记 3
[图标] 知识库
[图标] 智能心跳 1
[图标] 魔法笔记 3
[图标] 本机推理监控
```
隐藏某个应用后,其余应用向上补位。运行记录始终位于最后一个可见应用之后,不因应用显隐
改变样式或归类为工具。
取消常驻或关闭可选应用后不留下空白。知识库始终启用并显示;心跳按启用和常驻显示,位置遵守共享应用顺序。运行记录始终位于应用区之后。
四个应用均在管理行上通过拖动及“上移”“下移”按钮排序;首尾无目标时禁用对应按钮。两种方式提交完整应用顺序,保存成功后更新,重启保留。当前对话、运行记录和底部系统入口不参与排序。
设置页打开期间,主侧栏显示最后一次成功保存的导航状态。Switch 更新成功后导航立即变化,
失败时保持原导航。
取消常驻不关闭当前页面或本机推理监控 Modal。重新常驻按已保存顺序恢复,关闭应用保留常驻与顺序偏好。
主侧栏始终读取最后确认配置,不展示尚未保存成功的排序或开关目标值。
## 4. 应用与工具设置
## 4. 应用中心与共同设置
设置中心新增“应用与工具”分类,采用 `standard` PageShell:
底部点击打开轻量菜单,锚定按钮向上展开,无模态遮罩、背景隔离或焦点循环。菜单按共享保存顺序列出已启用应用,不按打开历史或常驻状态过滤。点击应用行关闭菜单,本机推理监控打开独立 Modal,知识库、智能心跳和魔法笔记在主内容区打开页面。分隔线下的“管理应用”关闭菜单并打开既有管理 Modal。
菜单通过共享 `AnchoredMenu` 挂载到 body,宽度最多 `280px`,距窗口边缘至少 `16px`,与按钮上缘间隔 `8px`;短窗口限制高度并内部滚动,滚动、窗口和内容尺寸变化时重算位置。原生浏览器相交遮挡复用共享机制。
管理 Modal 沿用设置 Modal 的表面、遮罩和焦点规则,提供全部内置应用的名称及用途搜索、打开,以及可选应用的常驻和设置:
应用管理始终采用紧凑单列横向列表,从上到下对应保存顺序;行间使用 `--border-subtle` 弱分隔线,不加卡片外框。概览与设置详情共用 `640px` 最大宽度,窄窗口保留两侧各 `16px` 外边距,高度随内容增长并受窗口限制。
每行左侧显示图标、名称、状态和简短用途,右侧对齐打开、可用的设置及上下移动操作;空间不足时操作组换到下一行并保持右对齐,组内也可换行,上下箭头保持成组。名称旁区分
“始终显示”(英文 `Always shown`)和“可选 · 已启用/已关闭”。名称与用途可自然换行,不截断用途说明;正文与控件均不可横向溢出。
```text
应用与工具
管理主导航中的应用。系统工具由 GoodBuddy 提供并固定在侧栏底部。
应用
魔法笔记 笔记、待办和评论 [显示在侧边栏 开]
知识库 管理和检索知识 [显示在侧边栏 开]
智能心跳 查看计划、建议和运行历史 [显示在侧边栏 开]
系统工具
文件 打开当前工作区 内置
终端 进入当前执行空间 内置
浏览器 打开 GoodBuddy 受控浏览器 内置
资源监控 查看本机 CPU、内存和磁盘 IO 内置
系统入口
当前对话、运行记录和设置位置固定,不参与上述配置。
应用中心
[搜索内置应用]
知识库 始终显示 [打开] [上移] [下移]
智能心跳 可选 · 已关闭 [打开(禁用)] [设置] [上移] [下移]
魔法笔记 可选 · 已启用 [打开] [设置] [上移] [下移]
本机推理监控 可选 · 已启用 [打开] [设置] [上移] [下移]
```
应用显隐使用共享 Switch,标签必须是“显示在侧边栏”,不能缩写为“启用”。切换时立即保存,
请求期间禁用三个应用显隐 Switch;失败后恢复原值,并在对应设置行显示可重试错误。系统工具
使用紧凑只读列表,不渲染禁用的卸载、停用或固定按钮。
已启用但未常驻的应用可以直接打开,打开不自动常驻。关闭的应用仍能被搜索和配置,
保留禁用的“打开”按钮,可通过“设置”重新开启。启用、常驻放在设置详情中;每行右侧提供成组的上下箭头,与打开、设置操作按空间换行。箭头使用共享图标按钮,名称和提示包含方向及应用名;目标取完整保存顺序中的相邻应用,仅首尾无目标或修改锁定时禁用对应箭头。无搜索词且未锁定修改时,所有行均支持拖动排序,包括关闭或未常驻的应用;搜索时禁用拖动,箭头仍按完整保存顺序操作,未命中的应用保留在顺序中。无搜索结果显示清除搜索入口。
首期不展示商店页签、推荐、安装按钮、市场 URL 或 YAML 操作,系统工具也不在此列出。
功能已停用时,对应应用行显示“功能已停用”和设置入口,保留显隐偏好但暂时禁用 Switch。
### 4.1 共同应用设置页
若某应用自身支持业务启停,应在该应用原有设置中保留“启用功能”开关,并用说明区分:
智能心跳、魔法笔记、本机推理监控的通用应用设置统一从应用中心对应行的“设置”进入;应用页面不再提供重复快捷入口。本机推理监控保留“模型与连接设置”。知识库在管理 Modal 中提供“打开”和排序,不提供启用、常驻开关或通用空设置;原工作区实际配置入口保留。
共同页遵守[设置中心 UI 规范](../../../UI-DESIGN.md#138-设置中心),不为每个入口建立一套设置。
全局设置移除魔法笔记页签及跳转入口。旧“显示魔法笔记入口”绑定业务启用值,迁为“启用应用”并保留原值;
“常驻左侧菜单”使用独立配置,只有已发布的独立显隐偏好才映射到常驻。
- “显示在侧边栏”只控制入口。
- “启用功能”控制该功能是否运行。
| 控件 | 表达含义 |
| --- | --- |
| 启用应用 | 控制业务可用性及主导航、会话等业务入口 |
| 常驻左侧菜单 | 仅控制主导航捷径,关闭应用时保留该偏好,并说明开启后才显示 |
| 显示未完成待办数量 | 仅控制魔法笔记导航徽标 |
| AI 评论方式 | 即时/保存后自动/保存后手动,单选 |
| AI 评论形式 | 长评 + 要点/长评/要点,单选 |
二元配置使用共享 Switch 和 `role="switch"`;评论选项使用共享单选控件。本机推理监控
默认启用但不常驻;心跳应用默认关闭,常驻偏好默认开启,其余未配置布尔项默认开启。评论默认值沿用既有值,已有保存的选择保留。启用与常驻不可合并。
设置详情最大宽度 `640px`,紧凑标题栏包含返回图标、应用名称、用途及关闭按钮。进入详情和
返回列表时正文滚动位置重置到顶部;进入详情聚焦关闭按钮,返回后聚焦搜索框。
共同设置表单使用共享 Switch,相关字段分组,组间使用弱分隔线;说明沿用正文字号。
评论分段控件按内容占宽,窄窗口中标签与选项上下排列;详情不提供排序控件。
智能心跳单条计划 CRUD 及 enabled 仍由其业务页面负责,应用级开关及执行条件见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。
所有入口共享保存中状态。串行保存期间禁用相关启用、常驻、排序及笔记配置控件,保留
最后确认值;持久化成功后的设置事件同步中心、侧栏、会话、应用页及其他设置入口,包括工具配置写入。重开中心刷新期间锁定修改,迟到的旧读取不能覆盖更新快照。失败在发起操作
附近显示可重试错误并保留焦点;结果未知时显示“正在确认保存结果”,重读成功后再开放修改。
### 4.2 关闭后的页面与会话
关闭当前可选应用成功后,原页面或本机推理监控 Modal 显示“应用已关闭”,重新启用通过应用中心对应行的设置完成;本机推理监控需先关闭 Modal。保留草稿和内容状态,
暂停该页面的新业务操作。重新开启后恢复可用性,不自动跳到已离开的应用页或重放任务。
知识库不显示应用关闭状态;会话知识控件、历史引用和工作区配置遵守原功能规则。心跳关闭时的侧栏行为见[监督者 UI 设计](../conversation-supervision/ui-design.md#会话侧栏)。可选应用的已有请求和后台任务继续按功能生命周期展示,详细边界见[关闭应用与恢复](./logic-design.md#32-关闭应用与恢复)。
## 5. 助手工作栏反馈
点击系统工具后,右侧工作栏按现有布局回流打开。主内容页面保持不变,侧栏不渲染工具内容。
系统工具仅从右侧工作栏已有 Tab 或“+”目录进入,工作栏按现有布局回流,主内容页面保持不变。
- 文件:激活默认存在且不可关闭的“工作区”Tab,焦点进入目录区域或错误修复入口。
- 终端:激活匹配终端,焦点进入终端输入区;新建期间保留按钮宽度和进度状态。
- 浏览器:激活绑定当前 Conversation 的最近使用“浏览器”Tab;没有匹配实例时创建一个,
没有 Conversation 时按产品规则复用最近实例或进入待绑定空状态。
- 终端:选择已有 Tab 激活该确切终端并保留目标与历史状态;“+”每次创建额外终端,按当前
项目选择本机或托管 SSH,无项目时使用本机 Home。新建期间显示进度,失败保留诊断。
- 浏览器:选择已有 Tab 保留其 Conversation、页面及导航状态;“+”每次创建额外浏览器,
无活动 Conversation 时新建待绑定空实例。不会按当前会话改选其他 Tab 或重绑已有实例。
- 资源监控:激活“资源监控”Tab,焦点进入指标摘要;首个样本加载后开始绘制历史。
后台事件可以更新徽标,不能自动展开工作栏或切换当前 Tab。由用户点击快捷按钮触发的打开
属于显式操作,可以展开工作栏并切换 Tab。
文件和资源监控在“+”中按单实例规则聚焦或创建,终端和浏览器不按目标去重。
后台事件可以更新 Tab 徽标,不能自动展开工作栏或切换当前 Tab。
### 5.1 资源监控面板
@@ -167,7 +189,9 @@ CPU 4% · 内存 620 MB · 桌面进程 7
| 对象 | 文案 |
| --- | --- |
| 设置分类 | 应用与工具 |
| 底部入口 | 应用中心 |
| 菜单底部管理入口 | 管理应用 |
| 应用设置开关 | 启用应用 |
| 文件系统工具 | 文件 |
| 终端系统工具 | 终端 |
| 浏览器系统工具 | 浏览器 |
@@ -175,7 +199,8 @@ CPU 4% · 内存 620 MB · 桌面进程 7
| 资源系统工具 | 资源监控 |
| 资源范围 | 本机 |
| 指标缺失 | 不可用 |
| 应用显隐开关 | 显示在侧边栏 |
| 应用常驻开关 | 常驻左侧菜单 |
| 关闭的应用页 | 应用已关闭 |
| 内置来源 | 内置 |
不再使用“本地工作区”作为侧栏底部身份名称。项目和默认工作区的路径继续由项目选择器、页面
@@ -183,41 +208,101 @@ CPU 4% · 内存 620 MB · 桌面进程 7
## 7. 键盘和无障碍
- 底部按钮均为独立 `button`,不能嵌套在整行设置按钮中。
- Tab 顺序为文件、终端、浏览器、资源监控、设置。
- 底部应用中心和设置均为独立 `button`,Tab 顺序与视觉顺序一致,不嵌套按钮。
- 图标按钮通过 `aria-label` 提供名称,工具提示不能作为唯一可访问名称。
- 工作栏打开后按现有焦点策略聚焦目标面板;用户通过固定工作栏开关关闭时,焦点返回该开关。
- 选中按钮使用 `aria-pressed` 或等价的可访问状态,不能只依靠背景色。
- 徽标的数量或状态合并进按钮可访问名称,例如“终端,1 个等待处理”。
- 应用显隐 Switch 支持 Space 切换,保存失败后恢复原值并保留焦点。
- 应用中心按钮提供 `aria-haspopup="menu"`、`aria-expanded` 和控制关系;菜单使用 `menu`/`menuitem`,打开后聚焦首个可用项,上下方向键循环选择,Home、End 到首尾,Enter、Space 激活。
- Escape 关闭菜单并恢复入口焦点;Tab 关闭后继续正常焦点顺序,点击外部、焦点离开或再次点击入口也关闭。菜单不隔离背景,不增加遮罩。
- “管理应用”打开的 Modal 使用共享 Portal、整窗遮罩、原生浏览器遮挡、背景隔离和焦点循环,初始焦点进入搜索。行操作与详情控件按正常 Tab 顺序访问,保留共享可见焦点环,不伪装成单个菜单项;详情切换焦点见共同应用设置页。
- 工作栏使用 `tablist`、`tab`、`tabpanel`、`aria-selected`、游标焦点和方向键、Home、End。
“+”使用普通按钮语义,同类型 Tab 仍按实例独立标记。
- 徽标数量或状态合并进可访问名称;Switch 支持 Space,失败保留焦点。排序按钮名称包含
应用名,操作结果通过 polite 状态区播报;拖动不是唯一排序方式。
## 8. 响应式
- 侧栏宽度 `220px` 至 `420px` 时,底部按钮尺寸不随宽度缩放。
- 窄窗口继续使用现有侧栏抽屉;工具栏不转为多行。
- 空间不足时缩小设置按钮前的弹性空白,不隐藏四个系统工具和设置。
- 侧栏宽度 `220px` 至 `420px` 时,应用中心与设置均可操作,不随宽度缩小点击区域。
- 窄窗口继续使用现有侧栏抽屉,底部入口不重叠;应用菜单向上展开并限制窗口边界、内部滚动。管理 Modal 的长名称可换行,搜索、设置与重新开启入口始终可达。
- 右侧 Tab 单行横向滚动,“+”持续可见;工作栏保持停靠回流,不遮罩主页面。
- 工具提示不得超出窗口;靠近底边时显示在按钮上方。
- 粗指针设备不依赖悬停,按钮的可访问名称和选中反馈保持有效。
### 会话置顶与操作菜单
会话行的“更多会话操作”提供“置顶会话/取消置顶”,本地与消息通道会话均可使用。
置顶会话显示带“已置顶”可访问名称的图标;当前项目和搜索过滤仍生效,过滤后的列表先显示
置顶会话,两组内各按更新时间倒序。取消置顶恢复时间排序,不改变会话更新时间。
置顶状态重启后保留;保存成功后更新图标和顺序,失败保留原状态并显示应用错误通知。
存储尚未就绪或置顶请求进行中时禁用置顶操作。
操作菜单通过 body Portal 浮在会话列表上方,不撑开会话行。菜单锚定更多按钮,默认位于
按钮下方,随滚动、窗口及菜单尺寸变化重新定位,距窗口边缘至少 `8px`;空间不足时限制
宽高并允许菜单内部滚动。使用共享表面色、边框、圆角与浮层阴影。
入口提供 `aria-haspopup="menu"`、展开状态和控制关系;菜单使用 `menu`/`menuitem`。
打开后聚焦首个可用按钮,上下方向键循环移动,Home/End 到首尾;Escape 关闭并返回
触发按钮,点击外部或焦点离开时关闭。本地会话继续提供分支、重命名和删除确认,复制与
导出也适用于消息通道会话。持久化与刷新规则见
[会话置顶存储](../assistant-workbar/execution-history-storage.md#会话置顶)。
## 9. 未来插件入口
插件市场交付后另行定义插件区域、固定数量、溢出和权限界面。系统入口与四个系统工具的位置
保持保留,不向插件开放。
FR-15、US-F1/US-F2 的市场 URL、YAML 导入导出及 ShareServer 分发在后续设计中定义。
首期中心只浏览本地内置清单,不显示未来市场的空壳界面。
### 9.1 本机推理管理
FR-16 的 `local-inference` 入口已纳入本轮应用清单,可常驻主导航。从常驻入口、应用菜单和管理 Modal 的“打开”均进入同一本机推理监控 Modal,关闭来源菜单或管理 Modal,保留底层工作区、项目、会话和草稿。其他内置应用仍打开主内容区页面。
Modal 与真实执行模块已接入;支持清单及验证范围分别见技术设计和实施进度。
复用 `custom-task-dialog` 的整窗 body Portal、遮罩、背景隔离及浏览器遮挡机制。主 Modal 最大宽度 `960px`,高度随内容增长,最大高度为 `min(720px, 100dvh - 2 × --space-6)`;紧凑标题栏和关闭按钮固定在顶部,正文独立滚动。直接显示服务列表,没有页签或任务历史。工具栏显示“每 5 秒刷新 · CPU 100% = 一个逻辑核心”、模型与连接设置、刷新状态,窄屏可换行。服务行显示“服务进程 PID … · CPU …% · 工作集 … MiB”,预热时显示“正在建立采样基线”;共享进程的 worker 在该行显示无法独立统计的原因。
初始焦点进入“关闭本机推理监控”,Tab/Shift+Tab 在当前 Modal 内循环。没有影响确认且没有操作提交时,关闭按钮或 Escape 关闭 Modal;点击遮罩不关闭。关闭后优先恢复仍可用的原触发元素,入口已移除时回到应用中心,窄侧栏已隐藏时回到侧栏开关。
服务采用弱分隔列表;名称使用区块标题字号,状态及引擎元数据使用
辅助字号,状态与名称同组并与操作区分开,原因与操作说明使用正文字号。桌面操作独立位于右侧;
窗口宽度不超过 `720px` 时移到正文下方并左对齐,保留完整状态和原因。
- US-F3 每行显示服务名、简短状态、引擎、模型及错误。ASR 可用时显示“按需运行(状态未知)”;OCR 和向量使用执行模块的状态。TTS 显示不可用,外部连接不列入本机服务。
- US-F4 不再提供单任务取消入口;活动任务仅在服务操作的影响确认中出现。
- US-F5 服务行右侧保留实际支持的启动、停止、重启或模型加载、释放。
- 停止、重启、释放前打开最大宽度 `480px` 的紧凑嵌套确认,列出正在使用的任务与影响;无已登记任务时说明执行前再次核对。初始焦点进入“保留运行状态”,最终危险按钮写明“确认停止服务/重启服务/释放模型”;取消保留运行状态。
确认使用独立 body Portal,隔离底层管理 Modal,Tab/Shift+Tab 只在确认中循环;Escape 只关闭确认,关闭后恢复原服务操作按钮,按钮不可用时回到管理 Modal。确认存在时禁用主 Modal 关闭按钮;提交期间禁用确认和取消按钮,两层 Escape 均不能关闭,也不能重复执行。失败关闭旧确认,在服务列表显示错误并刷新;再次操作须重新确认当前影响。成功只关闭确认。
操作失败呈现实际状态和可重试错误,无法确认则显示未知。服务恢复不自动重放任务。
关闭 Modal、关闭管理应用或取消常驻不停止服务,模型文件卸载另行操作。
服务列表提供首次加载、无服务、加载失败重试及保留旧快照的刷新失败状态;刷新期间或失败时禁用服务操作。
OCR 忙碌或模型不可用时禁用加载与释放,并在执行模块再次检查。
文档解析与 HTTP OCR 的配置归属见[文档处理入口设计](../document-processing/ui-design.md#11-入口与职责),
不因接入 HTTP 而扩展本机服务列表。“模型与连接设置”现直达 `model`,OCR 服务行的
“文档解析设置”直达 `document-parsing`,关闭设置返回原工作区;实施与验证见
[文档处理进度](../document-processing/progress.md#2026-09-19-实现与验证)。
页面不显示无来源的进度条或资源数值。US-B6 至 US-B9 的应用中心、顺序、启用联动、笔记共同设置映射见
[需求对应](./logic-design.md#6-与需求的对应关系)。
## 10. 项目活动汇总
对应 [FR-14](./prd.md#fr-14-项目活动汇总),状态与计数遵守
[活动逻辑](./logic-design.md#9-项目活动汇总)。
- 全局汇总按钮位于项目选择器下方,上行标明“全项目活动”,下行显示待处理与运行中数量;
箭头跨两行居中,无活动时整个按钮隐藏。
- 全局汇总按钮仅在有活动行时显示在项目选择器下方,紧凑显示待处理、运行中与已完成数量,无活动行时整行隐藏,
右侧显示展开箭头,与下方新建会话保留 `8px` 间距。可访问名称明确数量属于“全项目活动”。
- 项目菜单在项目行显示同口径数量,保留“本地项目 / 远程项目 / 远程通道”分组和 SSH Host
层级,不因活动状态重排项目。数量为零时不显示标记,连接状态仍独立显示。
项目名独占一行,状态数量排列在名称下方,避免窄侧栏中数量挤占名称宽度。
- 点击汇总打开活动对话框,先列“待处理”,再列“运行中”,空分组隐藏。每行显示项目名称、
会话标题及明确状态文字,不展开 Task、Job 或 Run 列表,也不自动切换用户正在查看的会话。
- 点击一行先关闭对话框、释放 Modal 焦点隔离,再请求进入该条会话;允许离开当前页面后,
- 点击汇总在按钮下方打开项目菜单,只列有活动会话的项目,每项显示项目名和同口径计数。
桌面鼠标悬停或点击项目展开右侧会话菜单,右侧空间不足且左侧可容纳时向左展开。两个面板
相接,各自拥有边框、圆角与阴影;会话层对齐所选项目行,底部空间不足时仅上移会话层,
不改变项目层的位置或高度。指针离开项目行进入会话层时不收起。展开项目不切换当前会话。
- 会话层顶部显示项目名,每行显示完整可换行的会话标题与明确状态文字,待处理会话排在
运行中会话之前,已完成未查看会话排在最后并显示“已完成”。项目行保留子菜单箭头,会话行不显示箭头。不展开 Task、Job 或 Run 列表。
- 点击会话先关闭菜单并恢复入口焦点,再请求进入该条会话;允许离开当前页面后,
选择所属项目、清除搜索和该会话未读标记、清除任务选择与会话操作菜单,窄窗口同时关闭侧栏。
设置页沿用未保存修改的离开检查,取消离开时保留原页面。
- 对话框沿用全局 Modal 规范,支持 Tab 循环、Escape、关闭按钮、点击遮罩关闭及焦点恢复;
窄窗口限制宽高并在内容区滚动。活动全部结束时关闭对话框并隐藏入口。
设置页沿用未保存修改的离开检查,取消离开时保留原页面和完成通知。完成通知在目标聊天
实际可见后清除,从普通侧栏进入也相同;设置覆盖下的聊天或隐藏文档不触发清除。
- 菜单挂载到 body,使用 `menu`、`menuitem`、`aria-haspopup`、`aria-expanded` 与控制关系,
不隔离背景、不增加遮罩。原生浏览器相交遮挡沿用共享机制。
- 打开后焦点进入首个项目;上下方向键循环选择当前层项目或会话,Home、End 到首尾,
Enter、Space 激活,右方向键进入会话层。左方向键或 Escape 返回项目层并恢复对应项目
焦点;顶层 Escape 关闭并恢复汇总焦点,不继续关闭窄侧栏。Tab 关闭菜单后继续正常焦点
顺序;点击外部、焦点离开、再次点击入口或隐藏侧栏也关闭菜单。
- 单面板宽度为 `264px`,窗口小于 `620px` 或项目菜单两侧均无法容纳子菜单时,就地进入会话层并显示“返回项目列表”,
触控无需悬停。浮层距窗口边缘至少 `16px`,每层最大高度为 `480px`,短窗口进一步限制
高度并独立滚动。成功完成但尚未查看的会话继续保留;所有活动行移除后,关闭已打开菜单并隐藏摘要。
@@ -4,9 +4,9 @@
| 项目 | 内容 |
| --- | --- |
| 状态 | 设计中 |
| 版本 | 0.2 |
| 日期 | 2026-09-08 |
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
| 版本 | 0.4 |
| 日期 | 2026-09-17 |
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
| 关联逻辑 | [功能逻辑设计](./logic-design.md) |
@@ -29,47 +29,50 @@
验收:
- Given 用户隐藏任意内置应用
- Given 用户隐藏任意可选内置应用
- When 用户查看主导航
- Then “运行记录”仍显示在应用区之后
- And 设置中没有隐藏、卸载或固定运行记录的选项
### US-A2 从固定入口进入设置
作为用户,我希望设置入口始终位于侧栏底部,以便快捷工具状态变化时仍能进入配置。
作为用户,我希望设置入口始终位于侧栏底部,以便应用状态变化时仍能进入配置。
验收:
- Given 任意主页面和任意侧栏宽度
- When 用户查看侧栏底部
- Then 设置按钮保持可见且点击区域不与工具按钮重叠
- Then 设置与应用中心为始终可见的独立按钮,点击区域不重叠
- And 点击后继续遵守未保存内容的离开确认
## 3. Epic B:管理应用入口
本节的启用、常驻和共同设置适用于智能心跳、魔法笔记、本机推理监控;四个应用均可排序,知识库始终启用并常驻。心跳的默认关闭及执行边界按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)验收,保留既有故事 ID。
### US-B1 识别完整应用
作为用户,我希望魔法笔记、知识库和智能心跳显示在主导航,以便知道它们会打开完整页面。
验收:
- Given 对应功能已启用且应用设置为显示
- Given 始终显示的应用,或已启用且常驻的可选应用
- When 用户打开主侧栏
- Then 应用位于“当前对话”和“运行记录”之间
- And 点击应用在主内容区打开对应一级页面
- And 原有待办或建议徽标继续显示
- And 点击知识库、智能心跳或魔法笔记在主内容区打开对应一级页面;本机推理监控打开保留底层工作区的 Modal,验收见 US-F3
- And 原有待办或建议徽标按已保存的显示设置继续显示
- And 当前对话之后按保存顺序显示应用;知识库始终显示,可调整位置,无启用或常驻开关;心跳按启用及常驻过滤
### US-B2 隐藏低频应用
### US-B2 取消低频应用常驻
作为用户,我希望隐藏不常用的内置应用,以便缩短主导航,但保留已有数据。
验收:
- Given 应用已有内容
- When 用户关闭该应用的“显示在侧边栏”Switch
- Then 主导航不再显示该应用
- Given 应用已启用且已有内容
- When 用户关闭该应用的“常驻左侧菜单”Switch 并保存成功
- Then 主导航不再显示该应用,当前已打开页面保持不变
- And 应用数据、计划和业务启停状态不变
- And 用户可从同一设置位置恢复显示
- And 用户可从应用中心打开该应用,或从同一设置位置恢复常驻
### US-B4 保留功能启停语义
@@ -78,35 +81,133 @@
验收:
- Given 魔法笔记功能已启用
- When 用户只隐藏其侧栏入口
- When 用户只取消其侧栏常驻并保存成功
- Then 笔记及待办处理能力继续按原设置工作
- And 再次显示时恢复原页面和数据
- And 重新常驻只恢复入口,不自动导航;再次打开时保留原页面状态和数据
### US-B5 恢复显隐保存失败
### US-B5 保持设置保存一致
作为用户,我希望入口设置保存失败时继续使用原导航,以免界面状态与实际设置不一致。
作为用户,我希望应用设置保存期间和失败后各入口保持一致,以免界面与实际配置不符。
验收:
- Given 应用显隐更新尚未完成
- When 用户切换“显示在侧边栏”Switch
- Then 三个应用显隐 Switch 在请求期间暂时不可操作
- And 更新成功后主导航立即变化
- And 更新失败时 Switch 和主导航恢复原状态并显示可重试错误
- Given 配置已加载且没有待确认的更新
- When 用户修改启用、常驻、顺序或魔法笔记配置
- Then 更新串行保存,在请求确认前,应用中心、应用页及全局设置中修改这组配置的所有控件均暂不可操作
- And 各处保留最后确认值,不提前改变导航、业务可用性、徽标或会话入口
- When 保存成功
- Then 各入口同时采用确认配置,后续修改基于该配置提交
- And 工具配置写入持久化成功后也同步各入口;较新的确认快照不被迟到的读取或保存响应覆盖
- When 关闭后重新打开应用中心
- Then 刷新最新配置期间锁定修改,读取失败保留重试入口,不允许用旧快照提交
- When 保存明确失败
- Then 各入口仍使用相同的最后确认配置,操作处显示可重试错误,保留焦点和编辑上下文,不重复通知
- Given 响应中断,无法确认保存结果
- When 用户查看设置
- Then 显示正在确认保存结果,重读权威配置成功后才恢复修改;读取失败可重试读取,不自动重发变更或写回旧值
### US-B6 从应用中心访问内置应用
作为用户,我希望从固定应用中心找到应用,以便未常驻或已关闭的应用仍有访问和恢复入口。
验收:
- Given 用户位于任意一级页面,包括全部可选应用均已关闭的情况
- When 点击侧栏底部应用中心
- Then 向上打开轻量锚定菜单,无模态遮罩;按保存顺序列出知识库和已启用的可选应用,不按打开历史或常驻筛选
- When 点击菜单中的应用行
- Then 菜单关闭;知识库、智能心跳和魔法笔记经既有离开检查在主内容区打开,本机推理监控打开保留底层工作区的 Modal,不自动常驻
- When 点击菜单底部“管理应用”
- Then 菜单关闭并打开既有管理 Modal,可按名称或用途搜索全部内置应用,包含未常驻及已关闭的可选应用
- And 知识库行提供“打开”和排序,没有通用设置、启用或常驻开关;心跳行可管理启用和常驻,单条计划 enabled 不变
- And 首期没有商店、推荐、安装、市场 URL、YAML 操作或系统工具入口
- Given 应用已启用但未常驻
- When 用户选择打开
- Then 本机推理监控打开 Modal,底层内容无需离开;其余应用经既有未保存内容离开检查进入页面,不自动常驻
- Given 应用已关闭
- When 用户在管理 Modal 中找到该应用
- Then 显示关闭状态,保留禁用的“打开”按钮,可通过“设置”进入共同设置重新开启
- Given 搜索无匹配结果
- Then 显示无结果及清除搜索入口,搜索不修改启用和常驻
- Given 配置正在加载或读取失败
- Then 中心保留加载状态或重试入口,不用默认值冒充已保存配置
### US-B7 保存常驻及应用顺序
作为用户,我希望常驻和顺序按设备保存,以便重启后仍使用熟悉的导航。
验收:
- Given 首次使用且未配置导航偏好
- Then 默认顺序为知识库、智能心跳、魔法笔记、本机推理监控;心跳默认关闭且保留默认常驻偏好,魔法笔记默认常驻,本机推理监控默认启用但不常驻,仍可从应用菜单打开;侧栏只按启用与常驻过滤可选项
- When 用户修改常驻,或通过拖动、键盘可操作的上移和下移按钮排序并保存成功
- Then 管理列表从上到下、菜单和侧栏按同一保存顺序显示,知识库和智能心跳可移动;当前对话、运行记录及底部系统入口位置不变,重启后恢复保存结果
- And 每行均提供上下移动,以完整保存顺序中的相邻应用为目标;未常驻及关闭应用仍可排序,设置详情不提供排序控件
- And 搜索时箭头仍按完整顺序移动,保留未命中应用,拖动禁用;清除搜索后显示完整保存结果
- Given 旧设置仅保存本机推理监控、魔法笔记的顺序
- When 升级后读取设置
- Then 知识库、智能心跳补在前面,两个可选应用保留原有相对顺序和常驻值
- Given 应用已关闭或取消常驻
- When 用户重新开启或常驻
- Then 按保存顺序恢复入口;关闭或取消常驻不删除顺序,已关闭应用仍可管理常驻偏好
- Given 保存失败
- Then 按 US-B5 保留原常驻和顺序,在操作处提供重试并保留键盘焦点
- Given 没有可见的可选应用或仅有一个
- Then 固定入口和应用中心仍可用,没有移动目标的排序按钮禁用,不产生空白占位项
### US-B8 独立控制启用与常驻
作为用户,我希望关闭应用时保留数据和导航偏好,并能明确知道哪些业务操作已不可用。
验收:
- Given 应用已启用
- When 仅取消常驻并保存成功
- Then 业务能力和后台操作保持原状,仍可从应用中心打开
- Given 知识库已有会话关联,智能心跳已有启用或暂停的计划
- When 用户关闭全部可选应用或修改其常驻与顺序
- Then 知识库始终启用,侧栏位置稳定,页面可打开;知识控件及关联保持可用,请求遵守知识库自身规则,心跳单条计划 enabled 原值不变
- And 心跳应用关闭后显示不可用状态,可从应用中心设置重新开启;新运行、在途运行及侧栏查询按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)验收
- Given 关闭的应用页面或本机推理监控 Modal 仍打开
- Then 原位置显示“应用已关闭”,保留草稿及页面状态,阻止新业务操作而不强制跳转;用户从应用中心对应行的设置重新启用,监控 Modal 需先关闭
- When 用户重新开启并保存成功
- Then 仍停留的页面恢复可用;已离开时不自动导航,不重放或补发旧请求和任务
- And 保留原常驻及顺序,未常驻应用不自动常驻
- Given 启用变更保存失败
- Then 按 US-B5 保留原业务状态和入口,不执行关闭或恢复副作用
### US-B9 迁移魔法笔记共同设置
作为魔法笔记用户,我希望在应用中心统一管理笔记设置,并在升级后保留已有选择。
验收:
- Given 用户从应用中心的魔法笔记行进入设置
- Then 显示笔记设置表单;笔记页面不提供重复入口,也不保留“显示魔法笔记入口”开关
- And 全局设置不再显示魔法笔记页签或跳转入口,已有配置值和业务数据保持不变
- And 启用、常驻、显示未完成待办数量分别控制业务、导航和徽标;隐藏数量不修改待办
- Given 升级前已有明确保存的布尔值和评论选择
- When 迁移配置并重启
- Then 保留已有 `true`、`false`、评论单选值及业务数据;缺失导航配置时,本机推理监控默认不常驻,魔法笔记默认常驻;未配置的应用启用和笔记相关布尔项默认 `true`
- And 旧入口开关若实际控制笔记启用,保留其启用含义和原值,不将其改作常驻
- Given AI 评论方式或评论形式未配置
- Then 分别默认“即时”和“长评 + 要点”,均为单选,不同时启用所有模式
- When 任一入口修改配置并保存成功
- Then 应用中心、主导航、笔记页、会话及其他设置入口同步采用确认值;保存中和失败均遵守 US-B5
## 4. Epic C:使用系统工具
### US-C1 一键查看文件
作为本地工作用户,我希望点击左下角文件按钮后直接查看当前工作区文件。
作为本地工作用户,我希望从右侧工作栏查看文件,并保留工作区面板的目标模式。
验收:
- Given 当前项目有有效根目录
- When 用户点击文件按钮
- Then 助手工作栏展开
- And 聚焦默认存在且不可关闭的工作区单实例
- And 文件按钮通过选中状态表明该面板正在显示
- When 用户展开右侧工作栏并选择已有工作区 Tab,或从“+”选择文件
- Then 聚焦默认存在且不可关闭的工作区单实例,不创建重复实例;仅迁移或布局修复时补回缺失实例
- And 保留跟随或固定模式,跟随模式继续按工作栏规则更新上下文
- And 仅该实例 Tab 显示选中状态,焦点进入目录区域
### US-C2 处理无效工作区
@@ -116,67 +217,62 @@
- Given 工作区面板处于跟随模式
- And 当前项目和默认工作区均不可用
- When 用户点击文件按钮
- When 用户在右侧工作栏选择工作区 Tab 或从“+”选择文件
- Then 工作区面板仍打开
- And 面板说明目录不可用并提供选择或修复目录的入口
- And 应用不静默改用其他目录
- Given 工作区面板固定的目标已失效,即使当前项目或默认目录可用
- When 用户选择工作区 Tab
- Then 保留固定目标并显示该目标的修复状态,不回退到其他目录或切换为跟随模式
### US-C3 快速进入终端
作为本地工作用户,我希望点击终端按钮后进入当前执行空间的终端。
作为本地工作用户,我希望选择确切终端继续查看,并能通过“+”创建额外终端。
验收:
- Given 已有终端 Tab,包括绑定其他执行空间的终端
- When 用户选择其中一个 Tab
- Then 仅激活该确切实例,保留执行空间、目录及历史,不按当前项目查找最近匹配实例或重新绑定
- Given 已选 Tab 正在关闭、已退出、已中断或失败
- Then 保留其真实状态,不因选中而重建
- Given 当前执行空间已有正在启动或运行中的终端
- When 用户点击终端按钮
- Then 工作栏聚焦最近使用的匹配终端,不创建重复实例
- Given 当前执行空间没有终端
- When 用户点击终端按钮
- Then 创建一个绑定该执行空间的新终端
- When 用户从右侧工作栏“+”选择终端
- Then 每次创建绑定当前执行空间的额外实例,不复用或按目标去重
- Given 当前没有项目
- When 用户点击终端按钮
- Then 创建或聚焦使用本机 Home 的终端
- Given 当前执行空间只有已退出、已中断或失败的终端
- When 用户点击终端按钮
- Then 创建新终端并保留原终端的历史状态
- When 用户从“+”选择终端
- Then 创建使用本机 Home 的新终端,已有终端保持原目标和历史
### US-C4 使用远程项目终端
作为远程项目用户,我希望同一个终端按钮进入远程项目目录,以免误在本机执行命令。
作为远程项目用户,我希望新建终端绑定远程项目目录,以免误在本机执行命令。
验收:
- Given 当前项目绑定托管 SSH 执行空间
- When 用户点击终端按钮
- Then 创建或聚焦绑定该远程项目及其权威目录的终端
- When 用户从右侧工作栏“+”选择终端
- Then 创建绑定该远程项目及其权威目录的额外终端
- And 面板持续显示远程主机和工作目录
- And 连接失败时保留重连与诊断信息
- And 连接失败时保留远端目标、重连与诊断信息,不回退为本机终端
- When 切换当前项目后重新选择该终端 Tab
- Then 仍激活原远程实例,不重绑到新项目
### US-C5 打开受控浏览器
作为用户,我希望浏览器按钮打开 GoodBuddy 受控浏览器,以便查看与 Agent 相同的会话。
作为用户,我希望从右侧工作栏使用 GoodBuddy 受控浏览器,并按需创建独立页面。
验收:
- Given 浏览器面板跟随当前上下文且当前会话已有浏览器会话
- When 用户点击浏览器按钮
- Then 工作栏聚焦绑定当前 Conversation 的最近使用浏览器实例
- And 不打开系统默认浏览器或无意创建重复实例
- Given 现有浏览器实例都绑定到其他 Conversation
- When 用户点击浏览器按钮
- Then 工作栏创建并聚焦绑定当前 Conversation 的新浏览器实例
- Given 当前没有活动 Conversation
- And 已存在浏览器实例
- When 用户点击浏览器按钮
- Then 工作栏聚焦最近使用的浏览器实例并保持其目标
- Given 当前没有活动 Conversation
- And 不存在浏览器实例
- When 用户点击浏览器按钮
- Then 创建待选择目标的空实例
- Given 用户需要同一 Conversation 的另一个独立页面
- Given 已有浏览器 Tab,当前 Conversation 与其目标不同或没有活动 Conversation
- When 用户选择该 Tab
- Then 激活该确切实例,保留绑定目标、页面及导航状态,不重绑、重建或改选最近匹配实例
- Given 当前有活动 Conversation,无论是否已有匹配实例
- When 用户从工作栏“+”选择浏览器
- Then 每次创建拥有独立页面和导航状态的新实例
- And 同一 Conversation 内共享隔离登录上下文
- Then 每次创建绑定当前 Conversation 的额外实例,页面和导航状态独立,同一 Conversation 内共享隔离登录上下文
- Given 当前没有活动 Conversation,无论是否已有浏览器实例
- When 用户从“+”选择浏览器
- Then 创建待选择目标的空实例,不复用已有实例
- And 以上操作均使用 GoodBuddy 受控浏览器,不打开系统默认浏览器
### US-C6 查看本机资源
@@ -186,17 +282,26 @@
验收:
- Given 资源监控面板尚未打开
- When 用户点击左下角资源监控按钮
- When 用户从右侧工作栏“+”选择资源监控
- Then 助手工作栏创建并激活单实例资源监控面板
- And 页面显示整机 CPU、内存、磁盘读写及 GoodBuddy 桌面进程摘要
- And 页面每 2 秒更新一次,并用图表保留最近 5 分钟
- And 可见活动期间每 2 秒采样一次,前次请求未结束时跳过新请求,图表最多保留 150 个真实时间样本,连续采样约 5 分钟
- Given 资源监控实例已存在
- When 用户选择其 Tab 或再次从“+”选择资源监控
- Then 激活同一本机单实例,不新建重复面板
- When 用户切换到其他工作栏 Tab、收起工作栏、隐藏窗口或最小化窗口
- Then 资源监控停止请求新样本
- When 用户再次显示窗口并返回资源监控
- Then 首次采样建立区间指标基线,下一次采样开始显示 CPU 和磁盘 IO 速率
- And 内存等瞬时指标可立即显示,基线未建立时不显示虚构零值
- Given 某项指标在当前平台不可用
- When 页面收到部分结果
- Then 该指标显示“不可用”及原因,其他指标继续更新
- Given 本次采样全部失败或超时
- Then 显示失败原因和重试入口,保留已有历史并标明最后更新时间,不把旧值当作实时数据
- And 失败或暂停采样期间保留真实时间缺口,不补零或伪造中间样本
- When 可见活动期间自动重试或用户手动重试成功
- Then 按真实采样结果恢复更新,较晚返回的失活前旧请求不覆盖当前结果
### US-C7 从任意应用打开工具
@@ -205,32 +310,194 @@
验收:
- Given 用户位于任意一级页面
- When 用户点击文件、终端、浏览器或资源监控
- When 用户展开右侧工作栏,选择已有工具 Tab 或通过“+”选择文件、终端、浏览器或资源监控
- Then 主页面保持不变
- And 右侧工作栏打开对应面板
- And 工具不会覆盖主内容或伪装成一级页面
- And 创建失败时工作栏保持展开并显示对应错误,主页面和其他实例不被清空
- And 后台事件不自动展开工作栏或切换当前 Tab,左侧和应用中心不复制工具入口或选中状态
## 5. Epic D:可访问性和布局
### US-D1 使用键盘打开系统工具
### US-D1 使用键盘管理应用与访问工具
作为键盘用户,我希望按顺序聚焦并打开左下角工具,以便不依赖鼠标。
作为键盘用户,我希望浏览和管理应用,并通过右侧工作栏访问工具,以便不依赖鼠标。
验收:
- 每个系统工具和设置都是独立按钮
- Tab 顺序与视觉顺序一致
- Enter 和 Space 均可激活按钮
- 每个纯图标按钮都有可访问名称和工具提示
- 焦点样式不被侧栏边界裁切
- Given 用户使用键盘访问侧栏底部
- Then 应用中心和设置是独立按钮,Tab 顺序与视觉顺序一致,Enter 和 Space 可激活,图标按钮有可访问名称和工具提示
- When 打开应用中心
- Then 入口提供菜单展开状态和控制关系,焦点进入首个可用菜单项;上下方向键循环移动,Home、End 到首尾,Enter、Space 激活
- When 按 Escape 关闭菜单
- Then 焦点返回应用中心入口;Tab 关闭菜单后继续正常焦点顺序,点击外部或焦点离开也关闭
- When 选择“管理应用”
- Then 关闭菜单并进入既有 Modal,焦点进入搜索;每行的打开、设置、排序及详情中的启用、常驻控件按正常 Tab 顺序可达,不伪装成单个菜单项;进入详情及返回时滚动归顶,返回后聚焦搜索
- When 通过键盘修改常驻或顺序
- Then Switch 使用共享开关语义并支持 Space,上移和下移按钮名称包含应用名,结果通过 polite 状态区播报,失败保留焦点
- Given 用户进入右侧工作栏
- Then Tab 选择、游标焦点、方向键及 Home、End 由工作栏负责,使用 `tablist`、`tab`、`tabpanel` 和 `aria-selected`,按确切实例表达选中状态
- And “+”为普通按钮,工具状态纳入 Tab 可访问名称;激活后焦点进入面板,关闭工作栏后返回固定工作栏开关
- And 焦点环不被边界裁切,应用中心及设置沿用共享浮层和原生浏览器遮挡处理
### US-D2 在窄窗口使用快捷栏
### US-D2 在窄窗口访问应用中心与工作栏
作为小屏用户,我希望侧栏抽屉中的工具按钮保持可操作。
作为小屏用户,我希望应用管理和工具实例在窄窗口中仍可操作。
验收:
- 工具按钮保持单行且不重叠
- 不因宽度不足隐藏文件、终端、浏览器、资源监控或设置
- 触控点击区域符合统一界面规范
- 关闭侧栏抽屉后焦点返回原触发按钮
- Given 侧栏宽度为 `220px` 至 `420px`,或窄窗口使用侧栏抽屉
- Then 底部应用中心和设置不重叠,不因宽度不足隐藏入口或缩小点击区域
- When 打开应用菜单或通过“管理应用”进入 Modal
- Then 菜单向上锚定且限制在窗口内,可内部滚动;Modal 中长名称可换行,搜索、常驻、排序、设置及重新开启入口均可达
- And 触控不依赖悬停,工具提示保持在窗口内,点击区域符合统一规范
- When 关闭侧栏抽屉
- Then 焦点返回原触发按钮
- Given 右侧工作栏有多个工具实例
- Then Tab 单行横向滚动且“+”持续可见,工作栏停靠回流,不遮罩主页面;布局和 Tab 交互仍由工作栏负责
## 6. Epic E:项目活动级联
### US-E1 从摘要进入确切会话
对应 [FR-14](./prd.md#fr-14-项目活动汇总)。用户在当前项目工作时,可从项目选择器下方
查看跨项目活动,先定位项目,再进入需要处理的会话。
- Given 多个项目存在运行中和待处理会话,当前搜索仅匹配当前项目
- When 打开全项目活动并悬停或点击另一个项目
- Then 项目顺序保持稳定,该项目会话按待处理优先展示,当前页面和会话保持原选择
- When 点击会话
- Then 进入该确切 Conversation ID,清除搜索、任务选择和会话菜单,窄窗口关闭侧栏
- Given 设置页有未保存修改
- Then 会话跳转仍需通过既有离开检查,取消离开保留设置草稿和原项目
- Given 用户选择的确切 Conversation ID 未在内存中或已被删除
- When 请求进入该会话
- Then 刷新持久化会话列表后仍只查找同一 ID;刷新失败或会话不存在时显示通知并保留原页面和选择,不回退到项目最新会话或其他会话
### US-E2 键盘、窄窗口与实时结束
- Given 菜单已打开
- Then 上下方向键与 Home、End 选择当前层,右方向键或 Enter 进入会话层,左方向键或
Escape 返回;再次 Escape 关闭活动菜单并恢复摘要焦点,窄侧栏保持打开
- Given 窗口小于 `620px`
- Then 点击项目后仅显示会话层,可通过返回按钮回到项目列表;长列表可滚动到最后一项
- Given 鼠标从项目行移向相接的会话面板
- Then 子菜单保持打开,点击会话正常导航
- Given 当前项目已无待处理、运行中或已完成未查看会话
- Then 返回项目层并恢复可用焦点;全局无活动时整行隐藏摘要并关闭菜单,新活动到来时只恢复摘要
- When 点击外部、焦点离开、按 Tab 或隐藏侧栏
- Then 菜单关闭,后台新活动不自动重新展开
### US-E3 完成后保留至查看
对应 [FR-14](./prd.md#fr-14-项目活动汇总),具体状态规则见[活动逻辑](./logic-design.md#9-项目活动汇总)。
- Given 用户正在其他会话或应用中工作
- When 本次应用会话内的运行成功完成,或已观察到的后台任务转为完成
- Then 活动会话保留为“已完成”,全局与项目的已完成计数同步更新
- When 从活动菜单或普通侧栏实际进入该会话的可见聊天
- Then 清除其完成通知;没有其他活动行时隐藏摘要并关闭菜单
- Given 聊天被设置覆盖或文档隐藏
- When 对应运行完成
- Then 完成通知保留,直到设置关闭且目标聊天在可见文档中显示;取消设置离开不清除通知
- Given 目标聊天已可见且未被设置覆盖
- When 运行成功完成
- Then 不保留该会话的完成通知
- Given 初始加载已完成任务历史,或任务失败、取消
- Then 不产生新的完成通知;重载或重启也不恢复上一应用会话的通知
- Given 同一会话仍有待处理或运行中工作
- Then 按实时状态优先展示,只计入一个类别
## 7. 后续需求场景
US-F1、US-F2 为后续市场计划;US-F3 至 US-F5 的本机推理监控已接入源码,当前支持边界见技术设计,验证范围见实施进度。
### US-F1 连接组织应用市场
对应 [FR-15](./prd.md#fr-15-私有化市场与-yaml-交换后续)。
- Given 组织已部署提供应用市场的 ShareServer
- When 用户配置市场 URL,并在来源要求时完成登录
- Then 可以浏览该来源的应用目录、详情及可获取版本
- And 市场 URL 按设备保存,重启后恢复;认证信息使用受保护凭据存储,不作为可导出配置
- Given 市场不可达、URL 无效、登录过期或认证失败
- When 用户访问该来源
- Then 显示具体原因及修改地址、重新登录或重试入口,已安装和内置应用仍可打开,不静默切换公共市场或其他来源
- Given 用户修改市场来源或开始获取应用资源
- Then 市场配置不改变模型、应用更新及 Runtime 等独立下载源;已开始的下载保持原来源,失败不静默切源
### US-F2 导入导出应用定义
对应 FR-15。
- Given 用户需要在私有部署间迁移应用
- When 导出应用为 YAML,并在另一台设备选择 `.yml` 或 `.yaml` 文件导入
- Then 预览应用身份、版本、可迁移配置和依赖,凭据与业务数据不包含在导出文件中
- When 用户确认有效定义且所需依赖已满足,导入成功
- Then 应用目录显示导入的身份、版本和配置,可按其启用状态打开或配置,不自动启动模型或任务
- Given 同标识应用已存在
- When 用户查看导入预览
- Then 可选择更新或取消,取消后原应用与配置保持不变
- When 更新成功
- Then 显示已确认的新定义和配置
- When 更新失败
- Then 显示具体原因及重试入口,保留原应用定义、版本和配置,不留下部分更新
- Given 文件格式无效、版本不支持或离线环境缺失程序包或模型资源
- Then 显示具体问题与补充要求,不将应用误报为可运行,也不自动启动模型或任务
### US-F3 查看本机推理服务与资源
对应 [FR-16](./prd.md#fr-16-本机模型管理入口新增计划项)。
- Given 用户使用依赖本机模型的应用
- When 从常驻入口、应用菜单或管理 Modal 的“打开”进入“本机推理监控”
- Then 均打开同一管理 Modal,来源菜单或管理 Modal 关闭,底层工作区、当前项目、会话和未提交草稿保持不变
- And 直接显示服务列表,不出现任务页签、任务历史或外部连接;知识库、智能心跳和魔法笔记仍打开主内容区页面
- And 初始焦点进入关闭按钮,背景不可交互,Tab/Shift+Tab 在 Modal 内循环;标题与关闭按钮保持可见,正文独立滚动
- Given 没有影响确认且没有操作提交
- When 点击关闭按钮或按 Escape
- Then 关闭并卸载管理 Modal,底层工作区继续显示,模型服务不停止;点击遮罩不关闭
- And 焦点恢复到仍可用的原触发元素;原入口已移除时回到应用中心,窄侧栏已隐藏时回到侧栏开关
- When 查看服务列表
- Then 显示服务名、模型、引擎、简短状态及错误;共享服务只展示一次
- Given 向量服务独立进程正在运行
- Then 显示带 PID 和进程范围的 CPU、工作集内存;CPU 首次采样显示预热状态
- Given 服务 worker 与宿主共享进程,或进程已退出、采样失败
- Then 在服务行显示不能独立统计或当前不可用的原因,不展示虚构零值或借用整个应用进程指标
- Given ASR 有活动请求但没有 Worker 就绪观测
- Then 显示“按需运行(状态未知)”,不推断服务正在运行
### US-F4 单任务取消入口(已移除)
保留故事 ID。FR-16 已收窄为服务监视,Modal 不再提供任务取消按钮。执行路径的原有取消行为和活动请求登记继续用于服务影响校验,见[逻辑设计](./logic-design.md#81-本机推理管理)。
### US-F5 管理实际执行服务生命周期
对应 FR-16;当前引擎支持清单见[技术设计](./technical-design.md#111-本机推理任务与执行服务)。
ASR 取消独立 Worker,向量停止核对所有活动请求,OCR 有活动或排队请求时拒绝释放。
运行阶段包括排队、加载和执行;当前未接入统一进度及来源项目对象关联。资源指标按服务进程统计,见 US-F3。
- Given 多个应用共用一个由 GoodBuddy 受管子进程承载的模型服务
- When 用户启动、停止或重启服务
- Then 仅提供引擎实际支持的操作,按同一服务串行处理;重复请求不重复创建进程或并发执行同一服务的重启
- And 以真实进程退出和服务就绪确认结果,不将命令已发送当作操作成功
- Given 用户准备停止、重启或释放服务或模型
- When 用户发起操作
- Then 在管理 Modal 上方打开紧凑影响确认,列出使用方、任务及影响;无已登记活动任务时说明执行前再次核对,取消后不发送操作指令且运行状态不变
- And 初始焦点进入“保留运行状态”,Tab/Shift+Tab 在确认内循环,底层管理 Modal 不可交互;Escape 只关闭确认并恢复服务操作按钮焦点,按钮不可用时回到管理 Modal
- When 确认提交尚未完成
- Then 禁止重复执行,确认和取消按钮均禁用,两层 Modal 都不能通过关闭按钮或 Escape 关闭
- When 操作成功
- Then 只关闭影响确认,管理 Modal 保持打开
- Given 执行前出现确认清单未包含的新任务
- Then 拒绝执行,关闭旧确认并显示错误,自动刷新状态;刷新期间或失败时禁用服务操作,再次发起操作须确认新的影响清单
- Given 停止失败,或重启时停止成功但启动失败
- Then 显示实际状态、原因和可重试错误,不假报已停止或已恢复;无法确认时显示未知
- When 服务再次启动或重启成功
- Then 已中断任务不自动重放,由来源应用显式重新发起
- Given 模型连接由外部程序管理
- Then 在既有模型与连接设置中查看,不将其列入本机服务或提供停止、重启
- Given 模型在进程内运行
- Then 仅提供引擎实际支持的加载、释放,不显示虚构 PID、进程数或进程启动停止状态
- When 用户关闭管理 Modal、关闭管理应用或取消常驻
- Then 模型服务运行状态不变,取消常驻不关闭当前 Modal,卸载模型文件为独立操作
+12 -4
View File
@@ -10,8 +10,16 @@
| [多终端页签 PRD](./terminal-tabs-prd.md) | 工作栏应用的固定、可关闭、单实例和多实例策略,以及多终端生命周期 |
| [浏览器多实例技术设计](./browser-tabs-technical-design.md) | 浏览器 Tab 身份、Conversation 绑定、IPC、BrowserService 和 MCP 路由 |
| [实现与验证进度](./progress.md) | 工作区与 Runtime 相关改动的实现事实、验证证据与剩余阻塞 |
| [执行记录存储与升级回收](./execution-history-storage.md) | 子任务过程差量存储、远程去重、旧库转换、空间回收及升级界面 |
| [Runtime 交互边界](./runtime-interactions.md) | 原生权限、结构化问答、后台拒绝、终端交互、托管 Runtime 并行与输出及消息底部请求状态的适配范围 |
| [执行记录存储与升级回收](./execution-history-storage.md) | 子任务差量存储、远程去重、旧库转换、会话置顶、摘要与完整历史按需读取、内存引用释放 |
| [Runtime 交互边界](./runtime-interactions.md) | 原生权限、结构化问答、后台拒绝、终端交互、并行与输出、消息请求状态及资源生命周期规则 |
| [Runtime 进程复用技术设计](./runtime-process-reuse-technical-design.md) | 跨 Runtime 资源调研、锁定协议证据、进程与会话拆分、远端模型桥、实施顺序和性能验收 |
| [Runtime 执行清单技术设计](./runtime-checklist-technical-design.md) | OC/CN 原生清单提取、远程 Agent 传递复用、消息恢复与真实部署验收;尚未实现 |
当前尚未拆出独立 User Stories、功能逻辑和 UI 设计文档;相关内容仍保留在两份现有 PRD
中。浏览器多实例涉及跨进程身份和工具路由,单独使用技术设计文档维护。
当前尚未拆出独立 User Stories、功能逻辑和 UI 设计文档;产品场景与验收保留在现有 PRD,
Runtime 资源状态规则由交互边界文档维护。浏览器多实例和 Runtime 进程复用涉及不同的
跨进程身份与工具路由,分别使用技术设计文档维护。
执行清单的需求、场景和顶部布局见 [PRD](./prd.md#131-runtime-执行清单),状态规则见
[Runtime 交互边界](./runtime-interactions.md#runtime-执行清单)。本目录拥有该功能;
[Task 与 Job](../task-and-job/README.md)拥有产品任务领域,
[远程主机](../remote-host/technical-design.md#执行清单接入范围)拥有远程部署与连接边界。
@@ -153,9 +153,38 @@ Conversation 归属,不新建租约或把页面重新归属到子级对话。
单次工具取消信号、Tab 使用租约信号和请求信号,取消一次工具调用不终止后续调用。
该阶段 MCP transport、endpoint 和 token 生命周期不变,只扩展服务端 capability 内容和内部
路由。工具调用使用当前请求绑定的 Tab,只有上述显式导航恢复会更换绑定。
路由。工具调用使用当前请求绑定的 Tab;显式导航恢复和下述点击开窗会更换绑定。
### 6.2 Agent 多 Tab 阶段
### 6.2 Page-created tabs
`target="_blank"` and `window.open` create managed `WebContentsView` tabs through Electron's
`setWindowOpenHandler.createWindow`. Chromium retains the original request and opener relationship,
including a blank window followed by a script-assigned location. These tabs share the Conversation
partition, proxy and permission restrictions. HTTP(S) URL checks remain in the session and filtering
proxy; `about:blank` is permitted only as the initial empty popup. Unsupported URL schemes remain denied.
BrowserService reserves capacity before creating the native view, assigns a new Tab/workbar identity,
and owns initialization, the Driver and cleanup. Initialization failure or Conversation disposal releases
the popup. Closing the opener leaves an already created popup alive. A popup's `window.close()` releases
its service slot and usage leases; Renderer removes its workbar instance and returns to the opener when
it is still available. No separate unmanaged BrowserWindow is created.
Live state includes `openerTabId`. A popup from the visible browser is selected even when the active chat
has switched to a different Conversation. Other workbar panels and viewport occlusion rules retain their
existing behavior. The existing workbar capacity notification still applies when all 32 slots are occupied.
During `browser_click`, the service collects popup registrations from that Tab and waits for their
initialization. The result identifies the last created popup. BrowserModelTools binds subsequent tools to
that Tab and asks the model to obtain new references with `browser_snapshot`; the MCP gateway also replaces
its capability binding and usage lease. Switching visible tabs does not alter this binding. Closing the
popup retains the existing explicit `browser_navigate` recovery behavior. A page may still be loading
when the click returns. Popups requested asynchronously after the click has completed are displayed as
managed tabs, but are not attributed to that completed tool call.
The remote ACP Runtime exposes its separately scoped image MCP server, not this desktop browser gateway;
this change does not add browser access to the deployed GoodBuddy Agent.
### 6.3 Agent 多 Tab 阶段
只有明确需要 Agent 管理多个页面时,才增加:
@@ -0,0 +1,57 @@
{
"date": "2026-09-17",
"status": "passed-with-counting-limitation",
"sourceBase": "3639566e1b52296c6a9bc409bcfcecb86c905e30",
"source": "Working tree including consecutive-request cancellation and terminal snapshot fixes",
"environment": {
"platform": "Windows x64",
"runtime": "OpenCode",
"mode": "ask",
"model": "Existing configured default text model",
"upstreamProtocol": "http",
"fullProductionApp": true,
"isolatedProfile": true,
"nativeInput": true
},
"checks": {
"twoQueuedRequestsCompleteAndPersist": true,
"cancelAfterExternalDispatch": true,
"cancelToResendMs": 25,
"resendCompletesAndPersists": true,
"cancellationPersists": true,
"stopAbsentAfterBothScenarios": true,
"sendEnabledWithUnsentProbe": true,
"terminalScreenshotsReviewed": true
},
"requests": {
"instrumentedExternalDispatches": 4,
"http200": 3,
"cancelledBeforeResponseHeaders": 1,
"observedRetries": 0,
"toolExecutions": 0,
"initialUninstrumentedCompletedGenerations": 2,
"totalUiSubmissions": 6,
"totalExternalDispatches": null
},
"limitations": [
"Initial Main counting hook missed child-process HTTP traffic; total dispatches across attempts are unknown.",
"Successful rerun used a transparent Main relay to the unchanged configured HTTP upstream for dispatch counting.",
"Seven tool definitions remained exposed by production Ask, with zero observed tool executions.",
"Upstream proxy retries and cancelled request outcome at the upstream are not observable.",
"No deterministic stale-event ordering, remote Host, or restart verification in this live run."
],
"validation": {
"focusedTestsPassed": 362,
"typecheck": "passed",
"productionBundle": "passed"
},
"cleanup": {
"ownedProcessesStopped": true,
"isolatedProfileRemoved": true,
"temporaryBuildRemoved": true,
"sensitiveCopiesRemoved": true,
"originalSettingsUnchanged": true,
"targetSourceHashesUnchanged": true
},
"localArtifacts": "consecutive-ui-0917/{final-evidence.json,scenarios.json,report.json,normal-terminal.png,cancel-resend-terminal.png} under the approved temporary directory"
}
@@ -19,7 +19,9 @@
- `messages.metadata_json` 继续保存供会话显示的完整子任务过程。
- `task_events` 的 `subagent` 内容由共享 `SubagentProgressStorage` 编码。
`progressUpdates` 只保存文字追加、变更的块,或确有删除/重排时的完整重置。
完全相同的过程使用空更新数组。其他事件内容不改变。
完全相同的过程使用空更新数组。工具字段按值比较,缺省字段与 `undefined` 等价,
不因 JSON 省略空字段而重复写入整个工具结果;空字符串与实际输出/错误变化仍保留。
其他事件内容不改变。
- 每个任务内按事件 ID 顺序、按 `childTaskId` 独立还原过程。事件 ID、顺序、状态、
最终输出、错误、时间及远程来源字段均保留,不按天数裁剪或删除旧事件。
- Runtime、Agent transcript、Preload 和 Renderer 的公共 `SubagentEvent` 仍传完整
@@ -27,27 +29,100 @@
- 远程消息投影与事件插入仍共用 SQLite 事务。重放时还原已保存内容进行等值校验,
相同来源重复事件不重复投影,不同内容继续报冲突。内存过程缓存通过已提交事件 ID
核对;事务回滚或重新打开后从数据库重建,不保存独立快照或回收日志。
- 存储层最多缓存 16 个任务;收到任务完成、错误、取消或中断的终态记录后立即移除该任务
的缓存引用,不影响其他会话。若事务随后回滚或仍收到该任务的后续事件,按已提交记录重建。
释放引用不等于进程 RSS 立即下降,垃圾回收和 Runtime 自身会话缓存另按生命周期回收。
## 活动历史连续保存
活动历史在桌面 `AssistantDatabase` 中按变化记录写入,保留记录顺序和重复 ID 的独立位置。
连续保存使用最近一次成功提交的记录作为比较基准,不再逐次读取整份历史。关闭数据库、
清空数据或检测到其他 SQLite 连接提交后重新读取;事务失败不更新比较缓存,重试仍基于已提交内容。
本次优化不改变数据库 schema 或完整快照 IPC,也不改变 Agent、Runtime 和远程模型桥。
本机与远程任务的桌面活动记录共用此保存路径,Host 不需要更新。
WAL 增长与 SQL 全历史扫描次数的定向验证见 [BUILD.md](../../../BUILD.md);
这些指标不等于安装后应用的物理磁盘吞吐测量。
## 会话列表读取与前端保留
列表读取与数据库存储是两个不同层次。`messages.metadata_json` 继续保留完整过程,
本次读取优化不修改 schema、迁移或用户数据:
- Renderer 启动及焦点、会话、队列刷新使用 `conversations.listSummaries(detailIds)`。
普通未打开会话只返回标题、项目、置顶状态、上下文设置和消息数量/首条角色,不解析或跨 IPC
传输正文、工具、子任务及图片操作元数据。
- 当前会话、现有 KeepAlive 视图,以及 Main 仍持有请求、数据库中仍有活动 Task 或
streaming 消息的会话返回完整消息。列表包含全部 active 会话,不限制数量,
按置顶优先、组内更新时间倒序返回;仅置顶不会加载完整历史。
- 打开历史通过 `conversations.get(id)` 读取全部消息,不使用会截断历史的分页上限。
加载时明确显示进度;失败保留会话与输入,提供原地重试。发送、队列派发和压缩上下文
必须先确认完整历史,发送时不按消息条数截断;复制与导出临时读取完整内容,不把未打开会话留入视图缓存。
- 搜索由 Main 在 SQLite 中读取标题与正文,不读取过程元数据,保持原来的 Unicode
大小写匹配和字面子串规则。范围包含全部 active 会话;前端同时匹配尚未
落库的消息。搜索状态只保留命中的 ID,不额外持有整份历史对象。
- KeepAlive 淘汰后,在现有保存周期中释放已经确认落库且空闲的详情及持久化比较引用。
未保存消息、正在执行的会话、待答/待审批内容不被淘汰。读取中的请求按会话合并;
晚到摘要不能覆盖刚打开的历史或新到的后台审批,摘要附带的实时新增消息仍增量保存。
- `messageSummary` 仅为只读列表标记,不进入持久化 header。保存继续使用已有逐消息
增量接口,空消息数组不代表删除历史。原有完整 `list()` 与数据库 `getConversation()`
接口保留供其他调用方使用。
- 图片状态通过同一合并函数处理。本地更新与 Main 图片操作按原有时间优先级合并,
未变化消息返回原对象,避免每次列表刷新后把无变化的整个历史当作脏消息再次保存。
- 本机和托管 SSH 的 Renderer/Main 读取、持久化与恢复投影共用以上实现。Agent 的
transcript、原生 Session、权限及模型桥协议未改变;完整列表、摘要和详情都附带
Main 当前恢复请求的问题投影。
此优化不等于工具折叠内容的延迟渲染,也不保证操作系统立即归还全部 Private Commit。
实际测量与适用边界见[实现与验证进度](./progress.md)。
## 会话置顶
界面行为见[会话置顶与操作菜单](../application-tool-navigation/ui-design.md#会话置顶与操作菜单)。
`conversations.pinned` 是桌面数据库维护的会话属性,本地和消息通道会话共用,默认未置顶。
完整快照与摘要均返回 `pinned`,旧快照缺省按未置顶处理。写入只通过
`conversations.setPinned({ conversationId, pinned })`,经 Preload、可信发送方校验和严格
Schema 校验后更新活动会话,再发布会话变更通知;不存在的会话报错。该写入不修改时间戳、
消息或其他会话字段,普通自动保存 header 排除 `pinned`,不会覆盖置顶状态。
尚未落库的本地草稿先等待保存队列并保存,再设置置顶;消息通道会话直接调用专用接口。
Renderer 合并时独立接收 Main 的置顶属性,即使本地正文更新时间更晚也不忽略它。
请求修订号和进行中标记防止较早发出的列表/详情响应覆盖刚更新的置顶状态。
此属性不修改 Runtime 或远端 Agent 协议。
## 旧库升级
启动检查只根据旧 schema 或仍未转换的笔记正文决定是否进入升级流程;正常写入、更新
与删除产生的 SQLite 空闲页不是旧格式证据。迁移后新增数据使用当前格式,不因正常使用
再次迁移。同一次启动已进入的迁移流程可重试压缩;若正文已转换、仅压缩中断,退出后
重开不单凭空闲页重新进入迁移,剩余页由 SQLite 后续写入复用。不新增持久化迁移标记。
桌面在打开业务数据库之前启动独立 Node 工作线程,使用同一套编码和还原函数转换旧库。
按任务有序读取,每批最多 32 条,提交后报告进度;SQLite 保留事务原子性及正常 WAL
自动 checkpoint,每个任务结束时截断 WAL。读取缓存为 64 MiB,避免 1 MiB 级历史
快照在过小缓存中反复读取;不以关闭事务或同步落盘换取速度。
转换可以混合读取旧完整记录与已经转换的记录,因此退出、磁盘写入失败后可以重新扫描
并继续,不建立额外的恢复状态表。最后运行 SQLite `VACUUM` 回收已释放的空间,而非
转换可以混合读取旧完整记录与已经转换的记录,也会重新编码 schema 34 中重复的工具块
更新。因此退出、磁盘写入失败后可以重新扫描并继续;已是相同 JSON 的记录不重复更新,
不建立额外的恢复状态表。最后运行 SQLite `VACUUM` 回收已释放的空间,而非
在转换前复制整份膨胀内容。回收仍需要额外可用磁盘空间,失败保留已提交转换并允许重试。
业务初始化随后将数据库升级为 schema 34,并建立子任务事件的部分索引。
子任务整理对应 schema 35;schema 34 用户也会执行该整理,成功后后续启动直接跳过。
继续复用 schema 34 的子任务事件部分索引和同一差量表示。当前业务初始化随后在事务中
升级为 schema 36,新增 `pinned INTEGER NOT NULL DEFAULT 0 CHECK(pinned IN (0, 1))`,
已有会话默认未置顶,保留原消息与时间戳;失败回滚本次字段迁移。
低于该 schema 的旧版客户端不能重新打开已经升级的数据库。需要回退客户端时,必须使用
升级前备份,不要修改 `user_version` 强行绕过版本检查。
## 验证要求
- 使用可还原的合成过程验证文字追加、内容修订、块重排、清空和多次打开。
- 工具 fixture 必须含生产对象会携带的 `undefined` 可选字段,并经过实际 JSON 写入/重读。
同时验证旧完整事件和 schema 34 差量事件的整理、混合重试及无变化记录不被重写。
- 验证远程重复事件、冲突事件,以及消息更新失败时事件和过程缓存的事务回滚。
- 在真实旧库的已核验备份副本上运行 portable 的正常启动入口,核对事件逐条等价、
其他用户表内容、数据库完整性和实际文件大小,不能只统计空闲页或只测试内存 fixture。
- 同时验证真实本地 Runtime 和共享 Linux Host 的桌面持久化路径。具体证据见
- 同时验证真实本地 Runtime 和共享 Linux Host 的桌面持久化路径。真实场景必须让主代理
与子代理按正常任务需要读取、修改文件和执行测试,不能用“子代理不使用工具”的纯文字
场景代替工具存储验收;记录实际请求数、有效变化、重复工具块及还原结果。具体证据见
[实现与验证进度](./progress.md)。
+116 -21
View File
@@ -101,12 +101,14 @@ Runtime
### 3.2 当前上下文只提供默认值
任务中心只有一个工作栏实例,默认跟随当前项目,不跟随当前 Conversation。用户可以在同一
实例内显式切换到全局任务或全部项目,但不能为不同项目打开多个任务中心 Tab。其他可绑定
任务中心只有一个工作栏实例,任务列表固定跟随当前项目,不随当前 Conversation 筛选;顶部
统计跟随当前 Conversation。不提供全局或所有项目入口。其他可绑定
目标的能力使用当前会话、项目、Runtime 和主机帮助新面板实例初始定位,但这些上下文不是
使用门槛:
- 监督默认选择当前会话,用户可以改选其他 Conversation、Task 或实验对象。
当前监督卡片已支持 Conversation/Task 跟随与固定,绑定保存在 tasks 实例的 `targetRef`;绑定只影响监督卡片,任务列表继续跟随当前项目。实现范围与验证见[监督者进度](../conversation-supervision/progress.md)。实验对象仍属后续工作。
- Runtime 默认跟随当前会话或 Task;执行事件可以查看,但 Job/Run 不作为独立选择对象。
- 终端创建时固定到当前项目执行空间,由项目配置自动决定本机或托管 SSH;没有当前项目时
使用本机 Home。
@@ -217,8 +219,8 @@ Runtime
- 应用注册表分别声明实例策略、默认打开、必须存在、是否可关闭和是否允许排序。`single` 或
`multiple` 只控制实例数量;`required` 控制布局修复,不能兼作关闭或排序策略。
- 在工作栏“+”目录中重复选择单实例应用时聚焦已有实例;选择浏览器、终端等多实例应用时
每次创建新实例。主侧栏系统工具快捷入口按当前上下文聚焦匹配实例,不能因连续点击无意
创建重复实例。
每次创建新实例。选择已有 Tab 只激活该实例,保留其目标与历史。主侧栏应用中心不重复
提供文件、终端、浏览器或资源监控入口。
- 支持方向键、Home、End、Enter、Space、可关闭面板的关闭命令和正确焦点恢复。不可关闭
实例不渲染关闭按钮,也不响应关闭快捷命令。
- 徽标显示未解决数量、等待审批或失败状态,并同时提供文字或可访问名称。
@@ -230,7 +232,7 @@ Runtime
从当前聊天右栏迁移时,Task Center 和工作区作为默认打开、单实例且不可关闭的基础应用保留;
浏览器保留一个默认打开实例,但注册为可关闭的多实例应用;成果保持默认打开、单实例且可
关闭;终端不默认打开,由用户通过“+”创建,并从首期开始支持多实例。Tab 必须单行横向滚动,
不能自动隐藏或缩写到不可辨认,“+”不能随滚动消失。Task Center 继续作为 Task 的现有
不能自动隐藏或缩写到不可辨认,“+”紧随最后一个 Tab 并随标签滚动,溢出时两端提供固定滚动箭头。Task Center 继续作为 Task 的现有
入口;审批定位到所属 Task 或 Runtime。智能心跳不作为工作栏页签,其报告、建议、历史和
完整配置统一归属“智能心跳”菜单入口。当前阶段不新增独立自动化中心。
@@ -245,7 +247,7 @@ Runtime
项目切换器和导航整体向下推移。
- 对话内容与输入区始终保留对称的左右安全边距,工作栏边界不得覆盖或裁切输入框、发送
按钮及主工作区右侧边距。
- 唯一展开与收起开关固定在窗口关闭按钮正下方。工作栏内部不重复关闭入口。开关图标中的
- 唯一展开与收起开关固定在顶栏操作区、紧邻浅色/深色主题按钮之前。工作栏内部不重复关闭入口。开关图标中的
箭头方向随当前动作切换,按钮不以持续高亮表达展开状态;当前会话没有 Task 时不渲染空
Task 上下文栏。
- “新建任务”入口位于工作栏“任务索引”标题后方,不在会话 Task 上下文栏重复出现;索引
@@ -260,25 +262,26 @@ Runtime
Task Center 保留为工作栏中的稳定入口,并在现有基础上适度完善:
- 只索引 Task;每个 Task 只关联一条 Conversation,一条 Conversation 可以关联多个 Task。
- 显示名称、关联 Conversation、Global 或 Project 范围、Ask/Execute、状态、最近进展、
真实活动时间和需要关注信息。
- 顶部显示当前会话消息总数和 Agent 累计回复时间;列表显示本项目任务的名称、关联 Conversation、
所属项目、Ask/Execute、状态、最近进展、真实活动时间、累计执行时间和需要关注信息。
- 点击 Task 打开其关联 Conversation 并定位 Task。
- 完整消息留在 Conversation;工具、Subagent、审批、错误和成果按 Task 关联到 Runtime、
活动记录和成果查看器中,不在窄栏复制,也不呈现 Job/Run 树。
- 智能心跳的报告、建议、历史和配置不进入任务中心。
任务中心是应用级单例,默认存在且不可关闭。它的数据范围默认跟随当前项目,并在标题或范围
控件中明确显示项目;用户可以切换到“全局任务”或“全部项目”,该选择只改变同一实例的列表
范围,不创建第二个任务中心 Tab。切换回“跟随当前项目”后,项目变化立即刷新列表。后台状态
任务中心是应用级单例,默认存在且不可关闭。当前项目是唯一范围,标题明确显示项目;切换
项目刷新任务和审批,同项目切换会话只更新顶部统计。无项目时提示选择项目。后台状态
可以更新徽标和排序,但不能抢占用户当前工作。
当前实现已按活动项目过滤 Scheduled Task 和审批,并提供“当前项目”“全局任务”“全部项目”
范围控件。
回复时间按每轮请求区间累加,包含轮内工具和审批等待、不计轮间空闲;任务用时汇总历次执行
请求,不按父任务生命周期计算。缺失历史有提示,远程缺少可靠证据时不虚构时间。
项目任务标题右侧先显示“+”新建按钮,再显示筛选图标;状态筛选默认收起、默认全部。
主侧栏最近会话为关联 Task 提供轻量入口:父会话行只显示行首展开按钮,Task 子项使用
任务图标和本地化摘要,展开层级只到 Task;点击 Task 子项打开同一 Conversation 并定位。
新建定制任务使用 Modal 选择当前或新
Conversation,默认 Execute,并持续显示 Runtime、Project、工作目录、工具和审批摘要。
新建定制任务使用共享 Modal,默认立即执行和当前 Conversation,可选本项目其他可用会话或
新会话,以及单次定时、每日或每周。内容必填,名称可留空从内容生成;工作模式使用关联
会话的执行时配置,未设置时使用项目默认值,不在创建表单另设模式。
详细产品边界以 [Task Center PRD](../task-and-job/task-center-prd.md) 和
[Task 与 Job 统一领域模型](../task-and-job/task-and-job-model.md) 为准。
@@ -416,8 +419,9 @@ Renderer 不接收任意系统 PID 控制能力。控制动作引用 Main 签发
不自动 force、reset、discard 或 stash,不因浏览分支而自动 Fetch。
分支触发器复用共享模型选择按钮,带分支图标和展开箭头;Fetch 使用共享次按钮,二者在
紧凑工作栏内保持同一行,长分支名省略并提供完整名称提示。
展开的分支选择面板不超过 360px 或工作栏可用宽度;搜索使用共享表单标签、输入框与主题样式,
分支列表独立限高滚动,创建按钮以紧凑次操作放在列表下方。Escape 收起面板并将焦点返回分支触发器。
分支选择面板浮在内容上方,展开时不挤占变更列表和提交历史;宽度不超过 360px,并限制在窗口内。
搜索使用共享表单标签、输入框与主题样式,分支列表独立限高滚动,创建按钮以紧凑次操作放在列表下方。
点击面板外或焦点移出时收起;Escape 收起面板并将焦点返回分支触发器。
- FR-W5:未提交更改支持列表和目录树切换;两种视图均可打开单文件 Diff。
- FR-W6:Git 工作区底部提供默认收起的提交历史区,独立滚动,可调整高度。
历史标题默认停在面板底部,首次展开向上占用当前 Git 内容区的一半高度;收起后标题回到底部。
@@ -457,6 +461,12 @@ Renderer 不接收任意系统 PID 控制能力。控制动作引用 Main 签发
### 7.7 浏览器
Page requests made with `target="_blank"` or `window.open` open managed browser tabs in the same
Conversation. A popup from the visible page becomes visible in the workbar; closing it from page script
removes that popup tab. When an Agent click opens a new tab, subsequent browser tools follow that page
and obtain fresh element references. Detailed lifecycle and routing rules are defined in
[Page-created tabs](./browser-tabs-technical-design.md#62-page-created-tabs).
浏览器是可关闭的多实例应用。升级后的默认布局保留一个浏览器实例;用户从“+”目录选择
浏览器时每次创建新的 Browser Tab。每个实例必须显示可区分的标题和绑定 Conversation,
不能让多个工作栏 Tab 只是同一页面的重复控制入口。
@@ -475,7 +485,7 @@ Renderer 不接收任意系统 PID 控制能力。控制动作引用 Main 签发
新建或重新创建的空白标签页尚无已提交 URL 时禁用刷新;只输入地址草稿不会启用刷新。
首次导航期间仍可停止加载。操作错误仅在当前面板本次打开期间显示,切换面板、关闭当前
页签或收起工作栏后清除,已离开面板的异步失败不得重新显示旧提示。
页签或收起工作栏后清除;关闭失败后重试成功也清除原错误,已离开面板的异步失败不得重新显示旧提示。
同一 Conversation 下的 Browser Tab 共享隔离存储和代理上下文,但每个 Tab 拥有独立页面、
导航状态、操作队列和元素引用空间。模型请求开始时绑定一个确定的 Browser Tab;用户切换
@@ -644,7 +654,7 @@ type WorkbarPanelInstance = {
### 9.1 设置入口
设置中心“平台功能”提供独立“远程项目(技术预览)”页签及开关,默认关闭。关闭时不显示
设置中心“平台功能”提供独立“远程项目”页签及开关,默认关闭。关闭时不显示
“主机与远程执行”分类、托管 SSH 项目创建选项和已保存远程项目,Main IPC 同时拒绝新的
SSH/远程项目操作;已有 Host、项目和凭据保持不变。若当前项目已经是远程项目,则切回
项目列表中的第一个普通
@@ -937,6 +947,55 @@ Electron、ChildProcess、PTY、SSH Client、Socket 或文件句柄。
应用通知;预览失败、终端断线、Host Key 变化、监督证据失效等需要本地恢复的错误留在面板
内。同一事件不得同时重复显示为面板警告和应用通知。
### 13.1 Runtime 执行清单
本节为待实施设计。用户需要在对话顶部查看 Agent 本轮列出的工作项及完成情况,避免在
工具记录中反复查找。这里的“执行清单”由 Runtime 更新,术语边界见
[Task 与 Job 统一领域模型](../task-and-job/task-and-job-model.md#17-runtime-执行清单)。
- **FR-CL1**:接入 OpenCode(OC)的 `todowrite` 与 Continue(CN)的 `Checklist`,
范围包括本地及远程 GoodBuddy Agent 上运行这两个 Runtime。远程 CN 的部署和正常请求
路径也属于交付范围,当前可用程度见[进度](./progress.md#2026-09-15-runtime-执行清单设计未实现)。
- **FR-CL2**:顶部展示本轮最新有效清单,更新按整份替换处理,支持明确清空。每轮请求、
会话、项目、执行空间与 Runtime 的清单分别归属;普通聊天续轮不能沿用旧清单冒充本轮进度。
- **FR-CL3**:清单只读,用户不能手动勾选。请求完成、失败或取消保留清单实际状态,
不自动完成工作项;原生工具调用继续进入消息与活动记录。
- **FR-CL4**:重新打开会话可恢复已保存的清单及请求结果,远程重连遵循已有 Agent 恢复
流程,不重新发送模型或工具请求。没有原生清单的请求不显示空占位。
ACP 仅是传输及远端协议,不分类为 Runtime。DeepSeek Harness(DS)真实默认环境无
todo/plan,本次不为其增加工具。也不增加 plan 工作模式、计划文档、计划编辑器或定时任务
能力;只有格式已确认的原生 plan entries 可以适配清单。
#### 顶部布局
执行清单位于聊天内容区顶部、已有 Task 上下文栏下方、消息流上方;没有 Task 时仍可独立
显示。它不占用窗口标题栏操作区,也不替换 Task 摘要、任务索引或右侧 Runtime 栏目。
布局遵循 [UI-DESIGN](../../../UI-DESIGN.md#610-应用顶栏与全局操作)。
默认折叠为一行“执行清单 · 已完成 m/n”,有进行中条目时附简短摘要;展开后按原生顺序
显示内容、状态及原生提供的优先级。行内状态使用图标和“待处理 / 进行中 / 已完成 / 已取消”文字,
不用可勾选 Checkbox,也不根据位置推测优先级或进行中状态。请求结束后的摘要附真实
请求结果,例如“已取消”,不改变条目状态。后台更新不强制展开、不抢焦点。
展开按钮提供 `aria-expanded` 和 `aria-controls`,支持 Enter/Space,折叠后焦点留在
按钮;实时区域只播报摘要变化。复用全局颜色、间距和字体令牌,浅深主题均可读。窄窗口
摘要截短、展开内容换行并有界滚动,保留完成计数、请求结果与输入区;200% 缩放时仍可
操作。没有清单或收到有效空清单时整行隐藏,历史内容仍按消息归属查看。
#### 场景验收
| 场景 | Given / When / Then | 需求 |
| --- | --- | --- |
| US-CL1,连续更新 | OC/CN 本轮产生清单并修改、重排或删除条目;顶部显示最新完整内容,工具活动仍可查 | FR-CL1–FR-CL3 |
| US-CL2,请求隔离 | 同会话开始下一请求、切换 Runtime 或并行使用另一会话;旧轮迟到事件不覆盖当前清单,历史按原归属保留 | FR-CL2、FR-CL4 |
| US-CL3,失败与取消 | CN 更新失败或请求被取消;失败候选不替换上次有效清单,取消不把未完成项勾完;有效空清单清除顶部 | FR-CL2、FR-CL3 |
| US-CL4,本地与远程恢复 | 重新打开本地会话或重连共享 Linux Host 上的 OC/CN 请求;已有记录按原请求恢复,重复事件不重复展示,不重新调用模型或工具 | FR-CL1、FR-CL4 |
| US-CL5,无清单与无障碍 | DS 默认请求或普通 Markdown/list 工具输出没有已确认清单;顶部不出现伪清单;有清单时键盘、窄窗口及浅深主题可用 | FR-CL1–FR-CL3 |
行为规则见 [Runtime 交互边界](./runtime-interactions.md#runtime-执行清单),接入与验证见
[技术设计](./runtime-checklist-technical-design.md)。
## 14. 性能与资源边界
- 工作栏关闭或面板实例关闭时停止对应非必要画面和高频日志推送,但保留 Main 中的受管运行。
@@ -949,6 +1008,41 @@ Electron、ChildProcess、PTY、SSH Client、Socket 或文件句柄。
用户级 GoodBuddy Agent Daemon。依赖 Main 模型网关、审批或本机工具的 Run 不能被描述为
可以无人值守继续。
### 14.1 Runtime 资源复用
2026-09-14 调研确认:多个项目可能各自保留一个重型 Runtime,状态或原生清单读取也会
影响其启动与保留。用户需要多项目/多会话自然并行工作,而不是为避免内存增长关闭工具、
子代理或删除历史。以下为新需求,**不表示 Desktop 0.13.2 / Agent 0.11.25 已实现**。
- **FR-RR1**:支持原生多会话的 Runtime,在同一账号和兼容启动配置下跨项目复用重型
进程;不得仅因项目目录或会话不同重复启动。等价的默认和显式选择应得到相同复用结果。
不兼容的 Runtime、Host、模型连接或插件配置可以分开,不要求全产品只有一个进程。
- **FR-RR2**:仅查看被动状态不启动重型 Runtime。显式连接测试、原生清单和执行请求
复用兼容的已启动实例;尚未握手时不能显示为已经验证连接。
- **FR-RR3**:共享进程仍保持各会话的目录、历史、模型用量、工具、权限和业务问答归属。
同一会话顺序执行,不同会话并行;Ask 保持只读,Execute 不增加第二层审批。
- **FR-RR4**:请求结束、会话释放与进程退出分别处理。取消一个普通请求、关闭工作栏
或释放一个会话不得误停其他健康会话;无会话的空闲重型实例及时释放。正常续聊保留
原生上下文,应用退出、配置替换和真实进程故障仍有明确的终态与有界回收。
- **FR-RR5**:托管 SSH 独立实现并验证复用,保留 Agent 对活动 Prompt 的所有权、
断线续接、事件去重和模型调用结果不确定性规则。不能以本机通过代替远端交付。
- **FR-RR6**:对不具备原生多会话 Host 的 Continue 保留正确的请求隔离;直连模型不
新增独立模型进程。优化应有前后性能证据,不以删历史、禁用工具、全局串行化或
推迟正常用户操作来换取进程/内存指标。
产品场景验收:
| 场景 | Given / When / Then |
| --- | --- |
| US-RR1,首次与热启动 | 同一有效配置的两个项目,分别查看状态、打开清单并开始四个对话;状态不冷启动,后续共用一个可多会话的重型 Runtime |
| US-RR2,并行与取消 | 两个项目有同名不同内容文件,四个会话进行真实开发;目录及成果正确,取消其中一个后其余三个继续完成 |
| US-RR3,续聊与释放 | 一轮任务完成后继续原会话,再释放其中一个;原生上下文可续用、其他会话不受影响,数据库完整历史仍可查看 |
| US-RR4,边界与远程 | 切换不兼容配置、查看 Continue/直连状态或中断 SSH;不串配置、不伪造多会话能力,Agent 已接受工作按现有归属恢复 |
资源状态规则由 [Runtime 交互边界](./runtime-interactions.md#runtime-资源生命周期)
定义;锁定版本研究、实现选择与逐项验收由
[进程复用技术设计](./runtime-process-reuse-technical-design.md)定义。
## 15. 无障碍与响应式
- 能力目录、所有面板实例、目标选择器、终端控制和进程操作可用键盘完成。
@@ -973,7 +1067,8 @@ Electron、ChildProcess、PTY、SSH Client、Socket 或文件句柄。
- 建立实例级跟随、固定和失效目标语义。
- 保留现有任务中心、工作区、浏览器和成果行为;附件与知识库继续由 Composer 管理。
- 将 Task Center 明确为 Task 的单例索引,并补齐范围、状态、最近进展、需要关注和直接打开 Conversation。
- 审批在所属任务或 Runtime 中持续可见,不新增独立审批面板。
- 审批在所属任务或 Runtime 中持续可见;任务中心只为未关联任务的审批保留按需显示的处理
区域,具体规则见 [Task Center 交互](../task-and-job/task-center-prd.md#5-交互)。
- 智能心跳菜单入口承接完整配置和范围后,再从任务中心移除重复表单;不得移除任务中心本身。
### 阶段 1:成果与 Runtime 可观测性
@@ -1091,7 +1186,7 @@ Electron、ChildProcess、PTY、SSH Client、Socket 或文件句柄。
- [ ] 宽、中、窄窗口均可访问完整能力目录和用户打开的面板实例。
- [ ] 工作栏在宽、中、窄窗口展开时都推动主工作区回流,不覆盖、遮罩或隔离主内容;唯一
开关始终固定在窗口关闭按钮正下方。
开关始终固定在顶栏操作区、紧邻浅色/深色主题按钮之前。
- [ ] 仅使用键盘可以选择能力、面板实例、目标、执行空间和控制动作。
- [ ] 状态不只依赖颜色,徽标具有文字或可访问名称。
- [ ] 终端、HTML、监督证据和高频日志具有可访问替代或降噪行为。
+936 -2
View File
@@ -1,5 +1,913 @@
# 工作栏实现与验证进度
## 2026-09-21 重复历史迁移补修(Desktop 0.13.9 候选)
- 0.13.8 的检查仍在已有笔记时把 `freelist_count > 0` 当作迁移依据;正常聊天更新
也会释放页。此前无笔记的回归提前返回,漏掉这一场景,不能证明该版本已修复。
- 启动检查现在只依据旧 schema 或旧笔记 payload;空闲页只用于已进入迁移流程的
压缩步骤。同一启动流程的 Worker 重试显式复用已确认的迁移决定,不持久化额外标记。
若正文已转换但仅压缩中断,退出后重新启动不因剩余空闲页再次迁移,SQLite 可复用这些页。
- 新回归先复现旧实现错误,再覆盖真实 SQLite 的旧笔记迁移、新建与编辑笔记、
聊天释放页、连续三次重开、内容一致性和完整性;断言没有进度回调或 VACUUM。
- 此次仅修改 Desktop 启动流程;新写入格式、schema、Agent 和远程协议不变。
- 迁移、Magic Notes、数据库专项复跑 **135 项通过**(30 秒单测上限);此前一个
listTasks 测试超时,独立复跑通过。加强后的迁移与笔记专项 **32 项通过**。
全量测试此前被取消,候选全量验证和 CI 尚待完成。
## 2026-09-19 问答位置持久化
- Main 在收到问题时保存消息位置块,而不是等回答成功才追加;前台及重启恢复的实时待答
快照、带来源的语义事件投影共用同一去重函数。答案写入不移动位置块,旧消息不补造历史
顺序;规则见[Runtime 交互边界](./runtime-interactions.md#运行与失败)。未改变 schema。
- `npm exec -- vitest run src/main/assistant/assistant-database.test.ts src/main/ipc.test.ts`
通过 227 项,覆盖真实 SQLite 的问题/正文/工具交错、重复和重放、回答/跳过、完成及
重新打开,以及三个设置重载入口的并行远程问题恢复。
`npm exec -- tsc --noEmit -p tsconfig.node.json` 通过。
- 共享 Linux x64 Host 使用已保存凭据与固定 Host Key,经 LAN 路由运行当前源码 Agent、
Desktop Runtime 适配器和 Main SQLite 投影,使用真实 OpenCode `1.18.29`。首轮暴露实时
问题没有语义来源的实际分支,使用 1 次真实文本模型请求后停止;补齐 Main 接收入口后,
最终一轮 2 次请求,验证回答前位置块已经落库,回答后为 `tool → question → text`,
完成重开后消息完全一致。合计真实文本模型调用 **3 次**,每次最多 1024 输出 token,
每轮有限调用预算与 120 秒请求超时;图片模型调用 **0 次**。
- 临时复验脚本为既有批准临时目录中的 `question-order-host-build.cjs`、
`question-order-host-run.cjs`,最终日志为 `question-order-host-live.log`。
两轮均停止测试 daemon,所属进程剩余 0,移除本轮专属 Host 目录及上传文件。
该记录只覆盖 Main/Agent 路径,不代替 Renderer 视觉验收或最终全量验证。
## 2026-09-19 远程 CN 完整桌面验证
默认联合开发包已包含 OC/CN,并通过桌面导入、验签、传输至共享 Linux x64、安装及
CN 项目创建。真实输入框发送产生清单更新,顶部和消息保存一致;退出重启后恢复完成
与待办状态。修复实测发现的 CN 冷会话续发及重复序号问题,复验重启续发、点击停止和
取消后再次发送通过。证据、调用口径及平台边界见
[远程记录](../remote-host/runtime-checklist-validation.md#2026-09-19-联合包与完整桌面验收)。
任务与清单分别展开,容器高度有界,键盘可进入清单滚动区;状态图标不再影响文字
基线。此前真实 Electron 视觉矩阵覆盖同时展开、长列表、窄窗口、浅深主题及 200% 缩放。
本轮实际桌面截图进一步核对真实远程清单和取消状态。开发包未发布,非 x64 平台未实测。
远程 CN 真实长清单为 55 项、5,601 字符,展示与落库一致;清空后退出重启仍保持空数组。
本轮 Agent gateway 账本记录 16 次模型请求,其中 15 次完成、1 次取消结果未确认。
测试 Host 目录、所属进程及本地隔离 profile 已清理。全量 4707 passed、67 skipped、
1 failed;本地 OC 压缩失败项与远程 ACP、项目选择、清单组件专项复验共 136 passed。
最终 typecheck、lint 和差异检查通过。
## 2026-09-18 Remote Continue Checklist
CL-P1、CL-P3 的远程 CN 源码已接入受管包、HTTP 宿主到 ACP 的适配、模型桥、项目
校验和聊天选择。CN 原生成功 Checklist 经既有 semantic transcript 传递统一清单,
不增加数据库或重连协议。修复聊天页把远程 CN 选择恢复为 OC、隐藏 Continue 菜单,
以及复合包验包默认读取 OC payload 的遗漏。
Linux x64 当前源码与开发包通过验包、prepare/commit、Agent bootstrap/health、CN
激活、真实模型清单新增/清空、未 ACK 断线重放、取消和同对话 Ask 续发。本轮新增
17 个外部模型 HTTP 请求;精确分项、临时探针、已清理的 Host 目录及剩余验收见
[远程执行清单验证记录](../remote-host/runtime-checklist-validation.md#cn-部署与生命周期)。
本阶段使用单 Runtime 开发包;后续联合包与完整桌面验证见上方 2026-09-19 记录。
远程相关测试 27 文件、374 项通过;最终项目选择、顶部组件和安装管理 41 项、App
远程选择与继承 3 项、CN 复合包安装专项通过。typecheck 和 lint 通过。
全量测试完成一轮:4665 passed、67 skipped、6 failed。其中 CN 新增包测试的文件名
错误已修正并复验;其余 5 项为 ActivityPanel/activity-store 的引用复用断言,本轮未
修改这些文件。最终 typecheck、lint 和专项结果见上述远程记录。
并行工作区更新后,ActivityPanel/activity-store 的 39 项专项复验全部通过。
## 2026-09-18 Local Runtime Checklist Backend
CL-P1 and the backend portion of CL-P2 are implemented. Shared contracts use
`runtime-checklist.ts`, `AgentEvent.type = checklist`, and message field
`runtimeChecklist`. Local OC validates parent Session, submitted message identity,
assistant parent identity and native tool arguments. CN extracts before truncation,
commits successful calls once, excludes startup history and retains explicit clears.
Message metadata serialization, reconstruction, remote replay deduplication and
terminalization preserve the field without a database migration. Existing IPC
transport already forwards these events. Renderer and `acp-remote-runtime` were
outside this backend change; no shared Linux Host acceptance is claimed here.
The focused backend suite passed 275 tests across shared checklist, OC/CN Runtime,
host adapter, permissions, real-binary lifecycle and assistant database tests.
Coverage includes OC reused Sessions and stale/child updates, native event ordering,
CN long lists and omitted success arguments, failure preservation, history dedupe,
empty lists, database reopen and remote event replay/terminalization.
`npm run typecheck` and `npm run lint` passed on the final validation run.
The full `npm test` run completed with 4604 passed, 67 skipped and 9 failed:
one Agent offline-package inventory test exceeded 60 seconds; eight failures were
in concurrently changing Magic Notes renderer tests (including PDF `DOMMatrix`
availability and canvas UI expectations). These files were not modified here.
Real model evidence on Windows, using production local adapters and isolated
temporary profiles:
| Runtime | Version | Submitted tasks | Actual model HTTP POSTs | Observed result |
| --- | --- | --- | --- | --- |
| CN | 1.5.47 | 1 | 3 | Two successful Checklist calls, two structured snapshots; A pending then completed, B pending |
| OC investigation | 1.18.29 | 2 | 6 | Native tools succeeded; revealed todo.updated precedes running input, initially no projected snapshot |
| OC corrected ordering | 1.18.29 | 1 | 3 | Two native updates projected; done retained |
| OC final sortable message ID | 1.18.29 | 1 | 3 | Two native updates projected; assistant parent IDs matched; done retained |
Total: **15 actual external model HTTP POSTs**, all HTTP 200, **5 submitted tasks**.
The unavailable old OC server probe and two isolated startup attempts dispatched
zero model requests. Startup attempts were blocked by the probe's fetch wrapper
not accepting Request objects; correcting that wrapper required no product change.
The proxy allowed only the exact native checklist tool, with a per-run request cap.
No business-file tools ran. Saved production settings were read through isolated
credential handling; the existing explicit test connection was used in memory
because its endpoint/model tuple differed from the saved connection. Credentials
were not printed, original settings were unchanged, and isolated run data was removed.
The real probes verified adapter extraction and OC Runtime events, not desktop
rendering. CN Runtime event forwarding and persistence were verified by tests.
Long lists, clears, cancellation boundaries and history replay were tested with
fixtures/database tests, not exhaustively exercised through real models. Temporary
probe sources: `cn-todo-probe.ts` / `cn-todo-run.cjs` under the approved OpenCode
temporary directory; each run printed its dispatch count and structured results.
## 2026-09-17 连续请求取消与停止状态
- 复现并修复 OpenCode 迟到的会话取消中断下一条请求:同一次运行复用一个有界 abort,
等待取消结束后释放会话锁;取消失败时移除对应绑定。规则见
[本机 OpenCode](./runtime-process-reuse-technical-design.md#4-本机-opencode)。
- 复现并修复旧 streaming 快照覆盖本地终态后停止按钮残留。完成、取消两种回归在
修复前失败,修复后通过;连续两条消息结束后按钮恢复。规则见
[消息底部请求状态](./runtime-interactions.md#消息底部请求状态)。
- App 257 项测试通过;最终 OpenCode Runtime、权限与真实 lifecycle 105 项通过。
lifecycle 使用真实 OpenCode 二进制与 SDK、本地模型 fixture,验证延迟取消后同会话
下一条正常完成及 SSE 回收。该轮外部真实模型调用 0 次;后续完整 App 实测见下文。
- `npm test`:4454 项通过、67 项跳过、2 项失败;失败均为 heartbeat 迁移的
`duplicate column name: pinned`。`npm run typecheck`、修改文件定向 ESLint、
`git diff --check` 通过;全库 lint 因范围外 `docs/features/story-graph/demo.js`
的 22 项 `document` 未定义错误失败。
- 托管 SSH 经 `ManagedRemoteAcpRuntime` / `AgentOwnedAcpPrompt` 使用独立 ACP
取消流程,不复用此 HTTP abort 实现;本次未改 Agent 或远程协议,未做共享 Host 实测。
### Real-model follow-up
Current-source production Main, Preload, and Renderer were built into dedicated
temporary output and launched in an isolated Electron profile on Windows. Native
input submitted two queued OpenCode Ask requests, then cancelled another request
after external HTTP dispatch and clicked resend within 25 ms. Both queued answers
and the resend completed and were persisted. The cancellation was also persisted.
After each scenario, the real DOM had no Stop button; Send was enabled after
entering an unsent readiness probe. Both terminal screenshots were reviewed.
The instrumented run dispatched exactly four external text-model HTTP requests:
three returned HTTP 200 and one was cancelled before response headers. There were
no observed retries or tool executions. A transparent Main relay forwarded to the
unchanged configured HTTP upstream to count child-process requests; model answers
and events were not simulated. An initial driver attempt completed two additional
real generations before its counting hook was found not to cover child traffic.
The total external dispatch count across both attempts is therefore unknown.
This verifies the observed full-App flows, not deterministic stale-event ordering,
remote Hosts, or restart behavior. Isolated credentials, profile, build, and owned
processes were cleaned up; original settings and target source hashes were unchanged.
The follow-up focused suite passed all 362 tests, and `npm run typecheck` passed.
Redacted evidence: [real-model report](./consecutive-requests-live-2026-09-17.json).
## 2026-09-15 Managed browser popups
Implemented [page-created tabs](./browser-tabs-technical-design.md#62-page-created-tabs): native
Electron popup creation now registers a managed Tab, and click results update direct-tool and MCP
bindings. Renderer selects a visible opener's popup across chat switches and removes script-closed
popup instances. Existing worktree changes were retained.
Validation against the current source:
- `npx vitest run src/main/browser src/main/agent/knowledge-mcp-gateway.test.ts src/main/agent/model-tool-provider.test.ts src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/contracts.test.ts`: 10 files, 232 tests passed.
- `node build/run-browser-tabs-electron-e2e.cjs`: passed twice after making the test wait for the shown
native view's animation frames before sending mouse input. Real Chromium verified `target=_blank`,
`window.open(url)`, blank-then-location, script close, opener close, shared partition, unsupported URL
denial, and subsequent direct/MCP snapshots on the popup. No fixed click delay was added to production.
- `npm run typecheck` and `npm run lint`: passed. `git diff --check`: no whitespace errors.
- `npm test`: 4319 passed, 67 skipped, 6 failed across 5 files. Failures were Agent offline dependency
installation timeout, local Runtime reuse timeout, diagnostic-log writer expectations, two heartbeat
migration fixtures with `duplicate column name: pinned`, and an App external knowledge binding test
missing the `Remote handbook` heading. The full suite was not green; none of these failures exercise
the popup registration or binding path.
The Electron test uses production BrowserService, CDP and MCP with a local HTTP fixture; the workbar
selection/removal path is covered by the actual Renderer component test. A complete installed-App
visual test and macOS/Linux native popup checks were not run. No external model calls were made for
the popup validation. Remote ACP exposes only its scoped image MCP server (`imageMcpServers`), so the
deployed Agent has no separate affected browser implementation and no remote Host change was needed.
## 2026-09-15 会话置顶与浮动操作菜单
已核对当前源码差异:置顶经专用 IPC 写入 SQLite,schema 36 迁移保留已有会话;会话操作
改为 body Portal 菜单。规则分别见[会话置顶](./execution-history-storage.md#会话置顶)和
[菜单界面](../application-tool-navigation/ui-design.md#会话置顶与操作菜单)。
本次任务提供的已验证聚焦测试结果为 backend **135 项通过**、renderer **283 项通过**。
本轮仅更新文档,未重新运行测试;任务未提供这两组的完整命令或原始日志路径。
已核对 `assistant-database.test.ts` 与 `App.test.tsx` 中相关回归,覆盖持久化与重开、
schema 35 升级、最近 100 条之外的置顶、自动保存隔离、草稿先保存、通道会话、失败保留
状态、旧刷新竞态,以及 Portal、窗口边界、键盘与外部关闭。
原生 Electron 视觉与完整应用交互尚未验证,不能据组件测试声明实际菜单排版、原生浏览器
遮挡或跨主题/窗口尺寸视觉验收通过;本轮也未运行全量测试、类型检查、lint 或构建。
## 2026-09-15 工作栏标签标题国际化修复
实现与兼容规则见[多终端页签 PRD 第 10 节](./terminal-tabs-prd.md#10-实例session-与持久化)。
任务中心、工作区、成果和带默认编号的终端标题由 Sidebar 按当前语言派生;重命名清除默认
编号。浏览器标题保持原行为,未修改工作栏壳层。本次只涉及桌面标题显示与布局描述,不改变
本机或远程 PTY、Runtime、Agent 协议或桌面到 Agent 的执行路径。
聚焦验证:
- `npx vitest run src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/workbar-contracts.test.ts src/renderer/src/WorkbarShell.test.tsx`:3 个文件、74 项通过。
- Sidebar 新增 6 项行为回归,覆盖中英切换且不改写布局、默认终端编号持久化与跨语言恢复、
重命名后恢复,以及中文默认名、英文默认名和自定义名三种无标记旧终端。测试使用真实
Sidebar、WorkbarShell 和布局 Schema,终端面板使用测试替身,未启动 Shell。
- `npx eslint src/renderer/src/RightAssistantSidebar.tsx src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/workbar-contracts.ts`:通过。
- 首轮测试有一处新增浏览器文案断言错误,修正为现有“未绑定会话”后通过。按用户要求未运行
全量测试、全量 typecheck、全量 lint 或构建;本轮未进行 Electron 实机验证。
## 2026-09-15 Desktop 0.13.6 发布准备
用户批准两项性能修复的中英文更新说明,指定 Desktop 0.13.6,不发布 Agent 或
LoongArch 预览,并明确排除工作区另外四个应用导航设计/演示文件。
`package.json` 与 lockfile 的两处根版本一致,更新说明校验通过。
双语功能矩阵已核对并补充按需历史行为;Agent 保持 0.13.0。
用户随后要求直接交由 CI 发布,本地独立候选的全量验收命令被取消,未记为通过;
开发阶段的完整测试及实测结果保留在下一节。候选发布不再进行本地生产构建或
打包,主分支 CI 与六平台标签工作流仍为发布验收入口。最终提交、标签与公开
产物状态须以远端实际结果核对,不以版本号变更视为已发布。
## 2026-09-15 会话列表与图片合并性能修复
本轮针对用户报告的 0.13.5 卡顿与高内存,不修改正在运行的正式应用或数据库。
只读检查发现最近会话正文约 1.14 MiB,消息 metadata 约 119.3 MiB;`f8d0741`
的图片合并还会重建无变化消息,使焦点刷新后再次保存历史。实现规则见
[会话列表读取与前端保留](./execution-history-storage.md#会话列表读取与前端保留)。
已实现并验证:
- 图片合并的三个分支共用保留引用的函数。1,600 条无图片历史引用不变;App 使用
JSON 往返形状刷新后,保存的消息数为 0,较新的图片操作、成果关联和流式正文仍保留。
- 启动、焦点/队列刷新、活动跳转与自定义任务入口均使用摘要或按 ID 读取。
搜索保留中文、Unicode 大小写和字面 `%_` 匹配,包含仍显示但已落到最近 100 条以外的
会话;真实 SQLite 中 501 条历史仍可完整取回,header-only 保存不删除消息。
- 打开、复制、导出、发送及压缩上下文保留完整历史语义。详情读取失败可原地重试;
旧摘要不能覆盖晚到的后台审批或刚加载的历史。搜索只保存命中 ID,复制/导出不把
未打开详情留入缓存,空闲详情在已确认保存后随现有 KeepAlive 生命周期释放。
- 三路 simplify 审查覆盖全部本轮源码,修正了两处残留全量读取、搜索保留大对象、
较早会话搜索遗漏及后台审批竞态。保留已发布完整列表接口,无 schema/Agent 协议迁移。
- 定向 App、SQLite、IPC/恢复及图片合并测试:**443 通过**。最终 `npm test`:
**4,301 通过、67 跳过、0 失败**,约 736.46 秒。`npm run typecheck` 和所有本轮
源码文件的 ESLint 通过。全仓 `npm run lint` 仍被本轮范围外的
`application-tool-navigation/navigation-demo.js:360` 的 `no-unused-expressions`
阻塞,未修改该用户编辑;不能记为全仓 lint 通过。
- 以 `f47049c40d757a38296d06d281b87fb6a5fe1c8c` 和本轮 14 个源码/测试文件冻结
before/after,两个生产 Electron 构建均成功;验证后哈希核对无源码漂移。
- 隔离生产 Electron 的 100 会话/1,600 消息试验:首次摘要读取 0 条消息,仅当前会话
详情读取 16 条;搜索第 100 个会话的正文不预载其 metadata,点击后再读取完整 16 条。
已检查实际 UI 截图,不是只调用数据库或 Mock。
- 另一个隔离 App 正常 UI 打开未加载的旧会话并发送,Main 请求携带原有 2 条历史;
**1 次真实直连文本调用**,HTTP 200,最终完整保存准确标记 `HISTORY_RENDERER_7391`。
初始等待使用错误 DOM class 超时,随后以保存结果核对成功,未重复发送。
- Linux x64 共享 Host 在本轮独立目录构建、安装并激活当前源码 Agent/OpenCode,
真实 SQLite 摘要测试通过;**2 次真实无工具文本调用**均完成且 delivered=1,
第二轮复用同一原生 Session 并准确记住首轮标记。测试 Agent 停止后独立 `/proc`
检查无该目录所属进程。此检查验证当前源码部署与 Session 续接,不冒充远端 UI
断线恢复全流程;桌面恢复问题投影由本轮真实 SQLite IPC 回归覆盖。
本轮真实模型调用共 **3 次文本、0 次图片**,无工具请求,不新增图片生成费用。
准备阶段的错误 fixture provider/图片默认字段、独立 profile 缺少原安全存储的
加密上下文、Host 源码包漏带 LICENSE 均只修验证脚本;失败记录保留,不计为产品修复。
所有脚本与原始证据位于忽略目录
`.agent-resources/conversation-performance-20260915/run-KX7bIz/`。
### 完整 App 配对内存测量
最终采用三组顺序执行的 before/after 冷启动,共六个独立 profile。每组使用相同
100 会话/1,600 消息,正文 1.20 MiB、完整 reasoning metadata 119.69 MiB;
执行三次焦点刷新、搜索未打开正文并导航 11 次。使用直连 Runtime 的无网络配置,
不启动 OC/DSH、不调用模型;测试与构建已完成后再开始这组测量。
下表是各三次结果的中位数,内存为 Windows Private Commit,单位 MiB:
| 指标 | before | after |
| --- | ---: | ---: |
| 初次传入 Renderer 的完整消息数 | 1,600 | 16 |
| 三次无变化刷新保存的消息数 | 4,800 | 0 |
| Main 焦点列表读取耗时(ms,不是端到端帧延迟) | 207.28 | 8.05 |
| 启动稳定点 App / Renderer | 803.42 / 284.77 | 682.36 / 155.73 |
| 三次刷新后 App / Renderer | 1,640.43 / 703.91 | 661.34 / 154.16 |
| 导航及缓存释放后 App / Renderer | 895.27 / 557.09 | 574.65 / 248.95 |
| App 采样峰值 | 1,788.71 | 694.35 |
每次 10 个进程样本,定时器间隔 5 秒,另有阶段采样,不代表连续峰值。
本数据集采样峰值中位数下降 **61.18%**;before 三次峰值分别为
2,506.29 / 1,788.71 / 1,766.39,after 为 694.35 / 693.61 / 719.27。
它测的是这两项历史读取/重复保存修复,不含重型 Runtime 的原生内存,不能推广为
正式用户整个 App 必然下降相同比例,也不是长时间空闲泄漏证明。
六个最终 profile 都保留 1,600 条消息,按 ID/角色/状态/正文/完整 reasoning
计算的 SHA-256 一致,`quick_check` 均通过;六个 App 均正常退出(code 0)。
汇总见 `summary.json`,单轮为 `before-2` 至 `after-4` 的 report/acceptance。
保留但不纳入三组对照的早期 `before-1`/`after-1`:after 的驱动在清除搜索后未等待
延迟筛选收敛,导航失败;退出时还记录 `PostQueuedCompletionStatus` 无效句柄。
后续为两个版本都补上可见列表等待,并等本轮采样子进程完成后再正常退出;
未唯一定位早期退出错误,也未据此修改产品代码。最终六轮完成后的监督脚本仍因
向已退出 CDP 调用 browser close 返回错误;随后不附带 CDP 独立关闭命名浏览器成功。
OS 按本轮目录核对无遗留 Electron/Node 进程,原始失败日志未覆盖。
文档检查覆盖本轮六份文档、81 个相对文件链接,无缺失;未逐一校验所有历史锚点。
修改未提交、未推送、未发布。
## 2026-09-15 Runtime 执行清单设计(未实现)
对应 FR-CL1–FR-CL4。用户本轮明确只完善设计与进展文档,不改代码;
[设计与验收](./runtime-checklist-technical-design.md)不代表功能已经接入或发布。
此前调研已通过真实模型完成以下本地测试;本次文档更新未重新运行测试:
| Runtime | 已确认的本地研究结果 | 调用口径 |
| --- | --- | --- |
| OC 1.18.29 | `todo.updated` 与 `session.todo` 为 `content/status/priority` 完整替换,无 item ID | 3 个真实模型轮次 |
| CN 1.5.47 | `Checklist.checklist` 为 Markdown `- [ ]` / `- [x]` 完整替换,成功后才提交 | 3 次真实 HTTP 调用 |
| DS | 真实默认环境无 todo/plan;本次不加工具 | 1 次真实模型 HTTP 调用,返回 200 |
上述口径不合并计算为同一种调用次数,也不算 GoodBuddy 顶部清单端到端通过。本轮新增
模型调用为 0,未运行代码测试或远程部署验证。
本轮只读源码检查确认 OC 普通工具输入及 CN 宿主工具输入存在 4,000 字符截断;
Agent 已传递带 Session、binding/operation 归属的通知,桌面已有 ACP plan/tool 消费,
但 plan entries 当前降为状态文本。这些链路可供设计复用,尚未验证清单端到端接入。
| 部署 | 已有能力与本功能可用程度 | 剩余工作 |
| --- | --- | --- |
| 本地 OC/CN | 原生清单格式已有上述实测结论;顶部清单未实现 | CL-P1、CL-P2、CL-P4 待实施及验证 |
| 远程 OC | 已有托管 Agent 安装与请求路径,其历史验证见远程文档;本功能没有远程实测 | 原生 entries/tool 格式、完整传递及 CL-P3、CL-P4 待验证 |
| 远程 CN | 当前受管安装、项目校验与 Runtime 创建只支持 OC,不能声明 CN 已可部署使用 | CL-P3 需补齐 CN 真实部署路径,再验收 CL-P4;当前本功能未验证 |
未决技术点:远程 OC/CN 实际发送的原生清单格式与等价性、CN 原生清空表示及 Markdown
接受规则、现有消息/事件字段的最小扩展位置、CN 受管工件与启动/模型桥接入方式。
产品范围与隔离规则已明确,不因这些待办缩减远程 CN 范围或增加 DS 工具、plan 模式与
文档功能。共享 Linux Host 的清单及重连验收全部待验证。
文档检查:已按 `deai-writing` 审校新增内容;新技术设计扫描为 0 个阻断项、12 个复核项,
复核项为必要的实现边界和验收枚举,按原意保留。新增相对链接与章节锚点已人工核对。
`git diff --check` 通过,仅输出工作区 LF/CRLF 提示;未修改本轮范围外的既有改动。
## 2026-09-14 Runtime 进程复用(实施中)
对应 FR-RR1–FR-RR6,设计及完整验收清单见
[Runtime 进程复用技术设计](./runtime-process-reuse-technical-design.md)。
用户已授权实施,本次复用改动尚未整体提交或发布,不包含在正式 Desktop 0.13.2 /
Agent 0.11.25 中。最终验证结果见本节末尾,早期失败及中间状态按时间保留。
- P0–P2:Main 已组合 `LocalRuntimeRegistry`,OpenCode Server 和 DSH Utility 按兼容
配置共享,工作区通过轻量适配器传入 Session。无会话 owner 空闲 60 秒回收;
适配器淘汰不结束其他会话。DSH 被动状态不再启动 Host,取消超时先释放目标 Session,
同时回收对应 Main 工具资源;配置替换期间的原生问题继续交给原请求的 Runtime。
- 首轮 Windows 完整 UI 使用当时构建的生产 Main/Preload/Renderer 和隔离 profile,
OpenCode、DSH 各两个项目、四个并行会话,通过输入区和发送按钮完成实际开发。
OpenCode 工具数为 7、7、6、8;DSH 为 9、13、11、11。独立运行各项目测试合计
60 项通过。两类 Runtime 分别共用一个重型进程,本轮未使用原生子代理。
- 首轮 UI 提供方请求共 55 次 HTTP 200:OpenCode 23、DSH 32。其中包含驱动选择器
失误引发的一条额外默认项目 Ask,工具为 0,不把它计为开发任务验收。
同轮 OpenCode 单进程 Private Commit 约 882.88 MiB,工作集约 629.51 MiB;
Electron 自身指标另存,不将两者混为整个 App 的测量结论。后续重复运行和空闲
证据见下文,不把这一单点与正式用户库直接比较。
- P3:生产 Agent 已接入单 ACP decoder/握手、Session → binding/operation 路由、
逐 operation Unix 模型桥及空闲回收。共享 stdio 与 Prompt 输入/期限分离;
Session 取消结算可如实保留 `processTree: running`。未增加持久进程池账本。
- 共享 Linux x64 Host 已通过现有加密凭据与固定 Host identity 连接。使用当前源码、
固定 Node 24.19.0 / OpenCode 1.18.29 构建隔离测试包,走真实 prepare/commit、
activate、attach 和 Agent-owned Prompt,不改写已有 Host 安装。
- Linux 首轮五条 Prompt 共 22 次已完成且已交付模型调用;两个 Execute 分别在正确
项目完成修改,各通过 8 项测试,四个会话只启动一个原生进程。但 Ask 的搜索被原
`kind === read` 规则误拒,原生子代理工具因权限请求未转发而等待,首轮验收失败。
修正采用原生 Session 工具规则;原生子代理只继承父拒绝规则、不继承允许规则这一点
已按固定版本源码核实,子 Session 同样设置当前模式,并保留原生显式限制。
- 下一轮测试脚本在 transcript 尚有分页时按已完成状态提前结束,误报缺少项目 marker,
随后 cleanup 中止了其他三个请求。这是验证驱动缺陷,产品自身已有 `hasMore` 处理;
驱动已修正为读完全部分页、等待全部并行结果。该轮共 14 次 dispatch,
其中 11 次完成、3 次结果不确定,失败记录保留,不将其计为通过。
- 重复测试包构建曾遇到 Host ENOSPC。只清理本轮已确认可重建的 bundle、Runtime
staging 和重复 `.gbagent`,保留测试 home、workspace、SQLite 及报告,继续验证。
- 后续两轮 Linux 各 7 条 Prompt、35/34 次 dispatch,四个 Ask/Execute 与原生子代理
续接均通过,分别取得 483/467 条子会话事件;取消没有关闭共享 owner。重连驱动先后
漏发 `controller/resume`、错读 `agent/capabilities.generation`,导致两次后续验收
失败;均已修正,保留失败报告。35 次调用一轮包含 1 次结果不确定,34 次一轮全部完成。
- 最终当前源码 Linux 生命周期复验:2 条 Prompt、5 次完成且已交付调用,共享一个
owner。目标返回 `cancelled / processTree: running`;另一会话跨真实 SSH 断线、
controller resume 和重新 attach 继续执行,3 个工具返回正确项目 marker。
全部 binding 释放后等待 65 秒,owner 数为 0;停止隔离 Agent 后仍为 0。
- 原生 Compact 单独复验:两项目各先完成 3 工具请求,再并行压缩原生 Session。
两次压缩分别归到各自的新 operation,各 1 次已交付调用,前后为同一个 owner;
总计 4 条 Prompt、8 次完成且已交付调用。释放会话并等待 65 秒后 owner 为 0,
Agent 停止后仍为 0。该结果没有用普通聊天返回“已压缩”代替原生 Compact。
- 第二轮 Windows 完整 UI 重开同一隔离 profile,四项目八条续接开发请求全部完成,
OpenCode 包含原生子代理,DSH 使用已有原生工具。独立项目测试合计 77 项通过。
两个 Runtime 仍各一个重型进程。另有四条实际慢工具请求:前两条 30 秒测试在点击
停止前已结束,不作为取消通过;后两条 120 秒测试中目标被正常 Stop 取消,另一项目
的测试继续完成,DSH PID 未变化。第二个 App 共 12 条任务,11 完成、1 取消;
100 次 HTTP 200(OpenCode 24、DSH 76),无失败请求。
- 第二轮任务结束后的 631.573 秒采样共 121 点,提供方请求计数始终为 100,
DB 始终 2,928,640 字节、WAL 始终 4,268,352 字节。期间查看了项目和截图,属于
**无新任务/模型请求**,不是完全无人操作的 UI。原生 Session 有意保留,不要求其
Runtime 退出;首个 App 自行退出时的进程消失不计为空闲计时验收。
| 进程 | Private Commit 起止(MiB) | 范围(MiB) | 区间 CPU 秒 |
| --- | ---: | ---: | ---: |
| OpenCode | 826.34 → 825.05 | 824.36–830.82 | 12.03125 |
| DSH Utility | 98.73 → 98.73 | 98.73–98.73 | 0 |
| Main | 184.16 → 185.05 | 182.90–186.77 | 0.953125 |
| Renderer | 142.80 → 136.22 | 135.00–142.80 | 0.6875 |
| GPU | 120.48 → 138.88 | 120.48–173.52 | 0.0625 |
Private Commit 不是独占物理内存,工作集相加会重复计算共享页。这组测量未发现两个
Runtime 在该空闲区间持续增长,但不是整个 App 无泄漏证明,也不是旧新版本对照。
- 当前 Linux 六个 ACP/bridge/owner 测试文件为 82 通过、0 跳过,包含 Windows 跳过的
Unix broker 并发及旧路由排队拒绝回归。本机完整回归曾为 4,170 通过、67 跳过;
首轮唯一失败来自旧测试把 `failed + running` 当成非法,修正后仍保留 `unknown`
的拒绝断言,并新增共享失败/取消接受用例。
- 标准 `typecheck` 原先没有把部分 Agent 源码作为检查入口。已增加
`tsconfig.agent.json`,并修正暴露的 ACP resume capability、spawn stdio 及几个局部
类型问题;扩展类型检查、26 项受影响 Agent 回归及 lint 通过,之后已再次进行上述
当前源码 Host 生命周期与压缩验证。
- 三个只读审查分别检查复用、质量/兼容来源和效率。修复了被动状态刷新不断重置
60 秒空闲计时的问题;状态检查不取得执行 owner,Native Session 映射成为唯一会话
保留依据,不再另存重复 conversation Set。最后本机定向回归 98 通过、3 跳过,
含真实 OpenCode 与 DSH ACP 组合;扩展类型检查通过。该补修仅涉及本机 registry,
不改变 Agent/远程路径。当时全量 `npm test` 为 **4,171 通过、67 跳过、0 失败**
(356 文件通过、9 跳过,609.57 秒);`npm run typecheck`、`npm run lint`、
`npm run build` 均通过。构建仍报告既有 chunk 大小/混合导入警告,不影响成功结果。
- 保留 exclusive raw ACP 是已有公开字节流路径,不是为本分支中间形态保留的兼容层。
新的共享取消/失败结果需要配套 Desktop/Agent,不声称旧 Desktop 已支持新结果。
原生插件两处祖先遍历可局部提取,但没有已证实故障或性能收益,本次不为此扩展改动。
- 本轮真实模型调用合计 **273 次**:本机两轮 UI 155 次,Linux 六轮
**118 次 dispatch**(29 条 Prompt,114 次完成、4 次结果不确定)。
包含所有失败轮次和误发默认 Ask,不重复计算最新报告别名;本机 HTTP 200 不等于
Agent ledger 的完整交付证明。原生确定性协议测试不计入真实提供方调用。
补充的 owner 成本对照使用同一当前源码、同一 Windows 机器、全新空工作区,分别经
生产 factory 创建两个独占实例或两个共享适配器。每种组合重复 3 轮,每轮显式连接后
再做 5 次双工作区连接检查;共启动 18 个真实 OpenCode/DSH Utility,全部退出,
**真实模型和本机合成模型请求均为 0**。测量前全量测试已结束,没有同时运行测试套件。
| Runtime / owner 方式 | 进程数 | Private Commit 中位数及范围(MiB) | 双工作区首次 ready 中位数及范围(ms) |
| --- | ---: | ---: | ---: |
| OpenCode 两个独占实例 | 2 | 1,401.17(1,396.15–1,517.91) | 1,195.00(1,184.14–1,213.49) |
| OpenCode 一个共享 owner | 1 | 777.59(667.04–802.78) | 1,135.71(1,130.32–1,140.81) |
| DSH 两个独占实例 | 2 | 171.62(170.79–173.50) | 167.06(163.36–167.78) |
| DSH 一个共享 owner | 1 | 86.64(86.18–87.29) | 155.31(155.28–157.79) |
这是**进程所有权方式**的对照,不是旧版 App 对新版 App、等规模历史或真实开发负载
对照;没有测模型首字节。DSH 后续连接检查读取已缓存握手,不能当作热 Prompt 延迟。
最后一个共享 DSH 在无 Session 时每 5 秒读取状态,持续 65 秒,原 Host 已退出且没有
重启,证明状态轮询不会续期空闲 owner。探针早期的类型及 CJS/ESM 打包错误均发生在
模型调用前;改为正确 ESM 依赖解析并加入启动前语法检查后完成测量,未修改产品以适应驱动。
清理时,完整 UI 的“关闭窗口”按现有产品行为隐藏到托盘,并不退出 App。随后只对已无
活动任务且重新核对启动命令的隔离 App 进程树进行清理;这不作为正常托盘退出验收。
命名浏览器会话已关闭,本机按本轮启动路径筛选的进程数为 0。最终通过固定身份 SSH
只读核对本轮 Linux 路径下的 `/proc` 进程和各测试安装 owner 数据库,两者均为 0;
保留测试 profile、工作区、数据库和报告,未删除用户历史或停止用户 Runtime。
此时尚未完成的完整 UI、十项目和问答恢复验收,后续结果见以下继续验收记录。
旧新版本全 App 对照、完整共享能力组合及 Continue/直连资源分解仍需独立完成,
不能将 P0–P4/V1–V8 整体标为完成。
本机证据位于忽略目录 `.agent-resources/runtime-reuse-implementation-20260914/`,
包括 UI 聚合指标、截图、Host 聚合报告及保留的失败轮次。这些不是 checkout 自带资产,
不包含可提交的模型或 SSH 凭据。
### 继续验收:远端完整 UI 与模式切换
- 用户要求继续后,已从完整生产 Electron UI 启用远程项目、保存两个 SSH 项目并在
各项目创建两条会话。仅在测试 SSH 命令入口将 `HOME` 指向既有隔离安装;项目保存、
Host/Agent/Runtime 发现、Main Runtime factory、传输、协议、模型和工具均保持生产实现,
未伪造 capability、Prompt、事件或权限结果。
- 四条正常开发任务均完成,共用一个原生 owner。分别在各自工作区实现 words/prices,
独立运行两项目测试为 12 + 13 = **25 项通过**。实际重叠峰值为 2 条请求,
不是四条同时运行;四个会话完成后,共享的原生 owner 仍然存活。
- 新增真实模型调用 **21 次**,分别为 5、5、5、6,均 completed 且已交付。
与上一段 273 次相加,累计 **294 次**;没有把旧 Agent ledger 的 13 次基线重复计入。
- 随后在原价格会话中从 Execute 切到 Ask 并发送原生业务问答请求,实际失败,UI
显示“没有可安全附加的远端 Agent 请求,且恢复不会重放任务”。Agent 仍把 workMode
固定在 binding 首次启动值,拒绝新模式;此次在模型 dispatch 前失败,新增调用为 0。
此问题不能由先前“不同 binding 使用不同模式”的通过结果排除。
- 当前修正只允许共享 Session 的后续准备变更模式,独占进程仍保持原契约;
`AgentOwnedAcpPrompt.start` 显式接收当前请求模式,原生 Session 路由与 ACP 备用权限
决策均使用该模式,不再捕获构造时模式。48 项定向回归和扩展类型检查、lint 通过,
包含 Execute → Ask → Execute 保留 Session 与原生父/子问答。
后续当前源码 Linux 包和完整 UI 复验已通过,见下一节;48 项是当时的定向结果,
不是后续全部修正后的全量结果。
- 首轮 UI 通过正常 `app.quit()` 进入生产退出清理,本机测试进程退出;
隔离 Agent 随后按 installation identity 停止,`/proc` 与 owner 数据库均为 0。
首轮报告、失败任务和工作区保留。再次构建前 Host 仅余约 251 MiB,已明确限定只回收
更早停止的测试安装软件及重复测试包,不清理状态、历史或工作区。
### 继续验收:断线、取消、模式续接与 Desktop 重启
- 四个原生问题同时等待时切断实际 SSH,复现“ACP channel does not exist”,继而出现
重复注册 binary channel。诊断确认 Agent-owned Prompt 可能没有 raw ACP journal 行,
replay 却对不存在的行 ACK 0。修正只在已有 raw cursor 时确认 journal,不放宽正序号、
channel/epoch 或 installation 检查。真实临时 SQLite 回归先失败再通过;94 项后端/
journal/channel 测试、当前源码 Linux 六文件 86 项通过,失败报告保留。
- 保留旧 Desktop profile 并更换隔离 Agent 后,旧安装的恢复曾无限重试阻塞项目。
attach-only 路径现在在打开 channel 前明确报告安装身份不匹配,不重放 Prompt。
同一 Agent 的新连接则保留 Runtime,对已有 binding 旋转 transport generation,
新 channel 使用实际连接 generation;不同 daemon 或倒退身份仍按原规则拒绝/替换。
- 完整生产 Electron UI 再次同时等待四个问题,实际断线重连后原表单和 Session 均保留,
没有新增模型调用或 owner。四会话尚在等待时,第五个新 Session 可以执行文件读写;
该 Session 的 Execute → Ask → Execute 三轮均完成,各 2 个工具、3 次模型调用,
验证实际写入、只读读取、再次写入,不以模型自述代替文件结果。
- 取消一个 120 秒原生命令时,其余三个待答会话仍可各自回答并完成。取消后立即在原
对话显式发送新请求也完成,使用新的原生 Session,仍共用原 owner。已确认取消通过
专用错误进入终态;未确认的 Stop 保留原恢复逻辑,不把网络中断等同于取消成功。
曾出现 Task 已取消但 assistant message 仍 streaming,改用既有数据库终态事务
同时更新消息、任务和终态事件。最终真实 UI、Desktop SQLite、Agent 状态一致,
streaming message 和 active Task 均为 0,共享进程未被误杀。
- Desktop 重启前的首次尝试因测试观察 SSH 挂起,实际上未退出,已明确作废该证据。
驱动加有界观察超时和 `finally` 退出后,真实重启复现两个缺陷:live question 没有
transcript provenance 却被当作持久事件拒绝;同项目串行恢复让第二个待答 Session
无法附加。现由 Main 并行恢复任务,live question 使用已有待答 Map 和活动请求租约,
只投影到实时会话快照,不伪造 provenance,不写新的问题日志。
- 修正后的真实正常退出/重开恢复两个同时待答的 Session,后一个先回答并完成,
前一个独立 Stop。两端终态分别 completed/cancelled,UI 无残留活动或恢复提示,
仍为同一个原生 owner。到该轮累计真实提供方调用 **348 次**:
273 + 首轮远端 UI 21 + 旧隔离安装诊断 6 + 当前隔离安装 48。
48 次全部完成并交付;更早 273 次中的 4 次不确定仍保留,Prompt 取消不等同于
模型调用交付不确定。后续审查复验的增量另记,不覆盖这一时间点。
- 重启修正后的全量回归为 **4,186 通过、67 跳过、1 失败**,唯一失败是
`ActivityPanel` 的 501 条历史显示测试;该文件单独重跑 **16 项全通过**。
不将单文件重跑写成全量通过,原全量报告保留。此前 4,171 通过的报告早于这些修正。
### 继续验收:共享能力与配置退役审查
- OpenCode 生产 factory、真实二进制测试覆盖 2/10 项目,每项目两 Session、两轮原生
文件读取并逐项目原生 Compact,manager 容量设为 1,仍只启动一个 Server。
DSH 实际 Host/ACP composition 同样覆盖 2/10 项目、每项目两 Session、两轮原生
文件读写和平台 Shell,一个 Host。二者使用确定性模型,新增真实提供方调用为 0;
这是原生组合测试,不是十项目完整 Electron 性能测试。
- DSH 补充双项目共享组合:原生 Skill、实际 stdio MCP、Host 插件清单、不同尺寸的
inline 图片和 Execute → Ask 均通过。两项目使用不同 Main ToolProvider 和正确的
conversation context,Ask 不调用先前 MCP,图片原始字节分别匹配。该文件当前
**9 通过、3 跳过**;Host 在测试进程内实际运行,不能表述为完整 Utility/UI 能力矩阵。
- 完整 61 文件复查分别检查复用、质量/兼容来源和效率。没有新增仅服务分支中间形态的
兼容层;raw ACP 独占和 Continue 单请求 Host 保留其已有契约。
复查发现重启后的回答/跳过未保存到 remote-authoritative assistant message,以及
manager reset 后旧 controller 拒绝所属问题的回答。修正复用原消息 `answeredQuestions`
metadata 和既有 question owner,不新增 schema、恢复日志或重复历史。
- 真实 UI 复验又确认设置保存入口仍主动取消全部请求,不能以 controller 单测通过
宣称生产配置切换完整。Runtime 设置、原生定制和能力更新现在只激活/退役配置,
不批量 abort 或清除待答确认;已接受请求继续使用原配置。用户 Stop、清除数据和
App 退出的原有取消/分离规则不变。真实 SQLite 的三入口回归覆盖并行恢复、反序回答、
跳过、重新打开答案和终态;manager reset 回归覆盖旧问题可回答、新工作仍拒绝。
最新 **152 项 IPC/controller/manager 测试、typecheck、lint 和生产构建通过**;
这次补修的最终真实 UI 复验及全量回归仍在进行。
- 后续真实 UI 的设置保存不再取消原生待答 Prompt;一次连接 readiness 超时后,
重新启动 Desktop 附加同一请求并回答 `KeepYes`,原 owner 未更换,任务 completed,
原消息的 `answeredQuestions` 已落库。再一次正常退出/重开后,“问题与回答”仍显示
正确答案和完成状态,截图已检查。该时点累计真实调用 **352 次**,相对 348 新增
当前 Agent 3 次及误选默认项目的本机 Ask 1 次;后者未使用问题工具,不计问答验收。
当前 Agent ledger 共 51 次,均 completed 且已交付。正常退出 Desktop 后按已核对的
隔离 installation 停止 Agent,所属 `/proc` 进程和 active owner 记录均为 0。
- 复查设置更新的专家团队分支发现,既有调度器会取消剩余专家,但父请求可能用替换后的
模型继续综合。仅对活动团队父请求保留配置替换取消,普通原生请求不受影响;
**156 项 IPC/controller/manager/SubagentService 回归通过**。此分支属于 Main 专家
调度,不是 Agent 原生子代理;远端普通 Prompt 的 `teamMode` 为 false,先前验证的
SSH 问答路径不改变。
- 工作区随后出现其他任务的新提交和 Magic Notes API 修改。整仓类型检查曾因其
`DesktopApi.magicNotes.onChanged` 与 App 测试 mock 不匹配失败,没有覆盖这些并行
改动。冻结 candidate 的 typecheck、lint、生产构建通过,前后性能对照
使用该隔离源码,不滚入后来的功能变更;其已带入的按钮位置调整见下节范围说明。
- 随后完成 baseline/candidate 两个独立生产构建、相同 seed 历史、三次冷启动及
无新任务十分钟的全 App 对照,结果见下节。驱动校准失败不计性能样本,
测量期间未并发全量测试;既有 owner-only 数据不替代此结果。
### 2026-09-15:前后完整 App 对照与剩余 Runtime 检查
对照基线为 `de14a6157a6c34f1e3ef22619eca374a50a83706`。通过隔离源码构建 baseline
和包含本任务改动的 candidate,使用同一 Windows 机器、同一依赖、同一 seed
SQLite(四项目、八会话、320 条等量历史消息)和相同工作区 marker。后续其他工作区
提交不滚入该冻结对照;这里的冷启动是新的 App/profile/Runtime 进程,不清 OS 文件缓存。
截图及源码复核发现,candidate 的 `App.tsx` 同时带入了 `fbba2e3` 的工作栏按钮
移到主题切换按钮旁的调整。用户明确要求保留,工作区及冻结 candidate 均未剔除,
也不为此补跑。以下结果是**包含该 UI 差异的完整 App 实测对照**,不是严格的
纯 Runtime 单变量实验;此前“只含本任务改动”的范围描述不准确,现予以更正。
- 每组 3 次冷启动,各 55 条请求、110 次确定性 loopback 模型请求。每次冷启动包含
八会话一轮工具请求和五条热请求,最后一次另加两轮八会话续接。每轮等待八个真实
Native Prompt 同时到达提供方再释放响应,随后实际原生 `read` 读取各项目 marker。
全部目录结果正确,无全局 Prompt 串行化。该对照新增真实提供方调用为 **0**;
自然开发、写入、Shell 和真实模型验收仍以前文证据为准。
- baseline 稳态为 3 个 OpenCode Server、2 个 DSH Host;三轮分别累计启动 3/3/3 个
OpenCode 和 4/4/6 个 DSH(包含被动状态临时 Host)。candidate 每轮只启动一个
OpenCode、一个 DSH;六次 App 正常退出后,本次登记的全部 Runtime 进程均退出。
- 早期驱动的必填模型字段、Windows 子进程继承输出管道、Runtime 未就绪时点击以及
读到半份 JSON 报告导致的失败均保留,不纳入以下样本。最终报告原子替换,发送前等待
实际启用状态并核对 Main 已接收请求;不重放失败/不确定的真实模型请求。
| 指标 | baseline,中位数(范围) | candidate,中位数(范围) | 样本口径 |
| --- | ---: | ---: | --- |
| 全 App 采样峰值 Private Commit,MiB | 3,543.18(3,485.58–3,589.01) | 2,040.11(1,698.68–2,085.82) | 每组 3 个 App,分别取采样峰值 |
| Runtime 采样峰值 Private Commit,MiB | 2,843.18(2,831.00–2,854.82) | 1,338.93(1,229.31–1,347.63) | 同上,仅 Runtime 进程 |
| bootstrap 到首个模型请求到达,ms | 4,519(4,464–4,529) | 3,573(3,519–4,040) | 每组 3 次,包含 UI 启动与操作,不是独立握手计时 |
| OpenCode 热请求总时长,ms | 225(198–286) | 200(187–308) | 每组 9 次 |
| OpenCode 首个可见事件,ms | 139(118–201) | 124(102–187) | 文本/推理/工具任一事件,不是模型首字节 |
| DSH 热请求总时长,ms | 66.5(54–80) | 83(62–104) | 每组 6 次 |
| DSH 首个可见事件,ms | 32(28–39) | 38(30–45) | 同上 |
这批包含上述 UI 差异的样本中,全 App 采样峰值中位数降低 **42.42%**,不能将全部
差值单独归因于 Runtime 复用。DSH 的热请求中位数反而增加 16.5 ms,
没有隐去该回退,也没有据此增加未经测量的队列优化。Private Commit 不是独占物理
RAM,工作集求和会重复统计共享页;5 秒采样不等于捕获了瞬时峰值。两组使用相同
测试观察代码,数据不是未插桩 App 或生产大库的性能承诺。
最后一次各有 **124 个**无新任务空闲样本,覆盖 **610.588 / 610.544 秒**,最大间隔
5.149 / 5.068 秒。两组模型请求均固定为 58,active Task 为 0,DB 固定
2,007,040 字节、WAL 固定 4,128,272 字节、事件数固定为 276。
| 空闲组 | baseline Private Commit 起止,MiB / CPU 秒 | candidate Private Commit 起止,MiB / CPU 秒 |
| --- | ---: | ---: |
| 全部 OpenCode | 2,433.02 → 2,382.73 / 19.359375 | 1,007.88 → 973.12 / 11.609375 |
| 全部 DSH | 180.00 → 179.19 / 0.09375 | 86.92 → 87.62 / 0 |
| Main | 211.00 → 200.34 / 4.9375 | 205.33 → 197.70 / 4.4375 |
| Renderer | 183.32 → 137.40 / 0.265625 | 157.90 → 127.23 / 0.4375 |
Session 有意保留;DSH 尾端有 0.70 MiB 增量,不把这一段描述为所有点都下降,
也不从十分钟数据宣称整个 App 已无泄漏。聚合报告同时保留工作集、堆、句柄和逐进程 CPU。
原始数据位于忽略目录 `matched-app-WBCdJL/{before,after}-{10,11,12}`,汇总为 `summary.json`;
这些是本机验证资产,不进入版本库。
其后才运行剩余 Runtime 检查,避免干扰对照:
- OpenCode 双项目真实 Server 组合通过原生 Skill、真实 stdio MCP 和不同 inline 图片,
两轮八次 MCP 调用均用请求 ID 核对原 grant 归属,读取结果仍匹配各自目录;十项目
测试仍通过。DSH 的对应共享组合也通过。四文件合计 **114 通过、4 跳过**,
跳过的是 3 个显式启用的真实 DSH 模型测试和 1 个真实图像生成测试,不把这些记为通过。
- 直连模型的会话 release/dispose 与实际 Shell 完整输出分页检查通过:删除目标会话
缓存并释放对应 ToolProvider;dispose 先释放所有已知会话,再释放 provider。实际命令
结束后输出可分页,dispose 后句柄失效。没有新增模型进程池、历史字节 LRU 或持久缓存,
此处也没有做直连模型整体 retained-heap 泄漏证明。
- Continue 使用实际 1.5.47 包和生产 Utility adapter,3 轮各两个并发请求,另 5 次热请求。
11 次本机确定性提供方请求全部返回预期内容,11 个请求级 Utility 均退出,真实模型调用
为 0。准备耗时中位数:冷准备 **333.93 ms**(326.06–381.94),同 adapter 缓存
**0.0031 ms**(0.0010–0.0054),新 adapter 命中磁盘产物仍 **312.97 ms**
(276.08–334.63),各 3 次。11 次原生 HTTP ready 中位数 **936.96 ms**
(925.60–959.27),首个提供方请求从 run 起算 **980 ms**(969–1,013),总时长
**1,353 ms**(1,314–1,373)。准备在 run 前单独测量,不能把它再说成总时长已包含。
跨 adapter 重复准备没有消失;本次保留现有检查与缓存,不为约 0.31 秒的准备成本
修改单 Session Host 或新增常驻池。
- 最终整仓 `npm test -- --reporter=json --outputFile=<本机证据目录>/full-validation-final.json`
为 **4,204 通过、67 跳过、0 失败**,耗时约 **733.80 秒**。包括最后加强的
OpenCode 逐请求图片互斥断言、共享能力组合、设置退役及专家父请求回归;
先前失败的 `ActivityPanel` 本次在全量中 **16 项通过**。早期失败报告仍保留,
不将跳过的真实提供方或平台专用测试计为通过。
- 全量结束后再次运行 `npm run typecheck`、`npm run lint` 和 `git diff --check`,
全部通过,先前并行 Magic Notes mock 不匹配已不再出现。10 个相关文档的
**140 个相对文件链接**均可解析,未独立复验标题锚点。全量测试进程及按本次测试
路径筛选的本机验证进程均已退出;Linux 隔离安装的清理证据见前文。
- 最终门禁及报告修订没有新增真实提供方调用,累计仍为 **352 次**。按钮位置调整及
其他并行工作均保留;本次未执行 commit、push、tag 或发布。性能对照仍使用原冻结
样本,不把整仓后来通过的回归结果描述成已重新测量该版本的性能。
验收边界仍需保留:性能对照是 Windows 四项目确定性读取,不是大库自然开发或十项目
完整 UI 性能报告;十项目为实际 Native composition 测试。未测所有第三方插件的组合,
未证明整个 App/直连模型无泄漏,macOS/Linux arm64 原生运行仍需可用平台或 CI。
### 2026-09-15:追加真实模型复验与资源观察
用户要求继续使用真实模型,并监控数据库及内存。此次将 `e1fe1cf` 与当时工作区改动
冻结到独立源码目录,构建真实 Main/Preload/Renderer,使用新的隔离 profile 和四个专用
项目,预置 320 条合成历史。按钮位置调整保留;没有把正式用户历史送入测试。
原始证据位于忽略目录 `.agent-resources/runtime-live-20260915/run-tQ1xtk/`,
不是 checkout 自带资产。本轮只调整测试驱动,没有修改 Runtime 产品源码。
- 独立生产构建通过。冻结快照的类型检查暴露并行附件功能的 App 测试 mock 缺少
`getFilePath` / `importFiles`,未覆盖或撤销那部分工作;这一失败不被早先整仓通过
的结果覆盖。初次构建取证脚本误用 Preload 扩展名、同步 esbuild 不支持插件等驱动
错误均发生在模型调用前,生产构建与 Runtime 未因此修改。
- 显式启用 DSH 的三个真实模型专项,全部通过:自定义 Header、Ask 下 Main 代理的
Web Search/Fetch、真实 npm 插件在 Ask 被拒绝且在 Execute 执行。共 **7 次**
实际模型请求;该次按测试名选择,仅运行这三项,不把其余九项的过滤跳过算作通过。
- 完整 App 从输入区发出四项目八条开发任务,实际读写模块、添加边界测试并执行
`node --test`。项目选择器曾因模糊名称匹配到路径中的 `live-2` 而选错项目,
驱动在发送前被会话身份检查挡住,改用已验证的精确项目文本;没有重复发送已接受任务。
- 第一轮六条任务完成、两条 DSH 以 `pi-ai stream idle timeout after 300000ms`
失败。一次未观察到上游响应状态,另一条有 HTTP 200 但任务仍超时,不能把 HTTP 200
当作交付成功。保留失败与数据库后,正常退出测试 App,将测试转发层改为独立
`undici.request` 流式转发,再显式重跑这两条,均完成。由于同时重启了 App,
不能据此断言已经唯一定位到转发器或提供方;本轮未盲目修改 Runtime 来规避失败。
- 四项目独立执行测试分别 **16、17、18、19 项通过,共 70 项**。每类 Runtime 的
项目请求存在重叠,使用同组 owner;本轮开发请求重叠峰值为 OpenCode 4、DSH 2。
两类 Runtime 分批发送,不声称八条同时运行。
后续真实原生子代理读取模块及测试,父请求执行测试完成;DSH 两个真实慢命令重叠时,
只 Stop 其中一个,另一项目继续完成,DSH Host PID 未变。
- 直连与 Continue 从同一完整 App 会话各发出一次无工具短请求,分别持久化
`LIVE_DIRECT_OK` / `LIVE_CONTINUE_OK`。当前已配置的图像模型另发出一次生成请求,
实际得到白底蓝色圆形图片并可在生产预览中打开;不是配置检查,也没有读取提供方
返回的图片 URL。这验证当前配置路径,不冒充运行了绑定指定服务商的可选测试。
- 此时 App 共 **16 条 Prompt:13 completed、2 failed、1 cancelled**,
**67 次模型请求**;加上 DSH 专项 7 次,本机新增 **74 次真实调用(73 次文本、
1 次图像)**。计数包含失败及显式重跑。早期累计 352 次暂增至 426 次,
后续 Linux 增量另记;本机 HTTP 计数不等于 Agent ledger 的交付证明。
- 真实子代理的 **153 条事件**,实际保存 **70,668 字节**,等价完整快照
**495,934 字节**,该事件负载减少 **85.75%**。使用生产 codec 逐条还原及重新编码
校验通过;6 次工具块 upsert 中无变化重复写入为 **0**,数据库 `quick_check` 为
`ok`。这是同批事件的编码差额,不是整个数据库文件缩小 85.75%,也没有删历史。
- 模型结束后连续 **610.857 秒、123 个样本**,最大采样间隔 **5.292 秒**。
请求数固定为 16,模型调用数固定为 67,活动任务和 streaming 消息为 0;
DB 固定 **1,765,376 字节**、WAL 固定 **4,140,632 字节**,消息数 355、
事件数 663、事件 JSON 负载 284,577 字节均不变。
- 本轮全 App 采样峰值 Private Commit 为 **1,728.79 MiB**,OpenCode + DSH
合计采样峰值 **997.66 MiB**。没有同负载的旧版真实模型样本,不能把它与前文
确定性对照相减生成新的“节省百分比”,也不把采样峰值称为瞬时峰值或独占物理 RAM。
- App 收到正常 quit 后回收了本轮登记的全部 Runtime,OS 核验最终 Main、OpenCode、
DSH 和 Continue Utility 均已消失。退出探针曾因等待 `will-quit` 误报超时:
生产实现实际在 `before-quit` 清理后调用 `app.exit(0)`。该驱动失败记录保留,
后续探针改从独立父进程观察退出,不修改产品退出路径或伪造原始报告中的 phase。
关闭后 WAL 为 0,DB 为 **3,125,248 字节**,属于正常 checkpoint,不是空闲增长;
已保存图片内容为 **1,351,526 字节**,不能把图片占用与事件差量节省混为一谈。
| 空闲进程 | Private Commit 起止(MiB) | 范围(MiB) | 区间 CPU 秒 | 句柄起止 |
| --- | ---: | ---: | ---: | ---: |
| 全 App | 1,345.77 → 1,345.95 | 逐进程样本保留 | 分进程见下 | 分进程见下 |
| OpenCode | 750.42 → 746.00 | 741.62–750.42 | 5.984375 | 333 → 333 |
| DSH | 89.86 → 89.86 | 89.86–89.93 | 0.015625 | 364 → 364 |
| Main | 196.61 → 200.37 | 193.13–205.09 | 5.703125 | 1,137 → 1,137 |
| Renderer | 120.66 → 121.51 | 120.64–122.67 | 0.15625 | 337 → 335 |
Main 堆使用量为 67.27 → 69.44 MiB;Main 与 Renderer 有小幅增量,不能表述为
“所有进程都下降”。采样包含测试观察器累积记录的分配,这一窗口未见数据库增长或
Runtime 句柄积累,但不构成整个 App 无泄漏证明。
当前源码 Linux 复验使用 `/root/tmp` 下的专用目录。旧 `/tmp` 测试 Node/依赖缓存
已不存在,初次准备因失效链接失败,未发出模型请求。随后只替换本轮创建的失效链接,
核验锁定 Node 24.19.0 的 SHA-256,并安装锁定依赖,完成当前源码 Agent/Runtime
组包、隔离安装及 OpenCode 1.18.29 激活,不修改旧安装或无关 Host 文件。
- 两项目各 Ask/Execute 一会话,共四会话使用同一个进程 owner、一个原生 OpenCode PID。
Ask 实际读取,Execute 实际修改和运行测试,目录 marker 均正确。后续原生子代理
产生 **497 条**子会话事件,同一 Session 续接完成;两项目独立测试各 **8 项通过**。
- 两个真实 90 秒慢命令期间,目标 Prompt 明确结算为
`cancelled / processTree: running`。另一会话经真实 attach 传输断开、
controller resume 和重新附加后完成,原 Session 与 owner 均保留。
本轮走生产 SSH/ACP/Agent/模型桥,不是再次运行完整远程 Electron UI 或 Desktop 重启。
- 共 **7 条 Prompt:6 completed、1 cancelled**,**36 次模型请求全部 completed
且已交付**,逐 operation 归账。取消 Prompt 不等于模型交付不确定。释放全部 binding,
等待 65 秒后 owner 为 0;随后停止本轮隔离 Agent,独立 `/proc` 与 owner 表核验均为 0。
- Host 共 71 次资源快照,未出现采样错误;所属进程私有驻留页合计采样峰值
**1,033.00 MiB**,其中 OpenCode 峰值 **771.60 MiB**。这是 Linux 私有驻留页,
不是 Windows Private Commit;也不是 Linux 十分钟空闲或旧新版本对照。
退出后 semantic-prompts、model-calls、runtime-owners 数据库分别为
**1,634,304 / 36,864 / 24,576 字节**,WAL 均为 0。
- 本轮最终新增 **110 次模型请求:109 次文本、1 次图像**,包括本机失败和显式重跑;
加上此前 352 次,累计 **462 次**。其中本机一次未观察到上游响应状态,不能把全部
110 次写成成功交付。实际开发项目测试合计 **86 项通过(Windows 70 + Linux 16)**。
- 收尾时当前工作区 `npm run typecheck`、`npm run lint` 和差异检查通过。
238 个 Runtime/Agent/远程适配器文件与冻结快照的文本内容一致,原始字节差异来自
checkout 的 CRLF/LF;Main、Preload、Renderer 入口产物哈希未变化。并行附件测试
mock 的缺项已在当前工作区消失,不回写冻结快照,也不掩盖其当时的类型检查失败。
10 个相关文档的 140 个相对文件链接有效,标题锚点未独立复验。
本轮没有产品源码修复,未再次运行全仓 `npm test`;前文 4,204 项通过的报告
早于后来的并行附件/窗口改动,不能将其当成这些改动的完整回归结果。
- 本机命名浏览器会话及所有本轮测试进程均已关闭;保留隔离工作区、数据库、生成图片、
失败诊断及汇总报告,没有清理正式用户历史,也没有 commit、push、tag 或发布。
## 2026-09-14 Runtime 资源调研与方案(未实施)
- 按用户要求完成文档与源码调研,方案见
[Runtime 进程复用技术设计](./runtime-process-reuse-technical-design.md),对应
FR-RR1–FR-RR6。当前工作仅文档和本机忽略目录内的隔离探针,未修改 Runtime/Agent
源码、停止用户 Runtime、清理用户数据或准备新发布。
- 源码基线 `de14a6157a6c34f1e3ef22619eca374a50a83706`,保留此前活动 UI 修复。
manager/controller、OpenCode、Continue、DeepSeek、直连、远程 ACP、Agent backend /
owner、模型桥和原生插件均已检查。此前同基线生命周期定向回归为 11 文件、
346 通过/1 跳过;不是本方案实施后的产品验收。
- 已安装 0.13.2 的 09:10:12–09:15:12 五分钟只读采样:数据库约增加 2.72 MiB,
3,367 条新增子代理事件约 1.49 MiB、平均约 465 字节,93 个工具 upsert 中未变化的
upsert 为 0;App 相关进程 Private Commit 约 3,229–3,866 MiB,CPU 平均约为
16 核整机的 3.15%。后续一分钟又完成两个任务,不能作为真正空闲或泄漏结论。
- 两个 OpenCode Server 属于当前 App,启动工作区不同。其中一个原生库 0 Session /
0 Message、约 527 MiB Private Commit。源码确认按工作区保留实例、清单可启动执行
Runtime、无时间型空闲回收;没有独立调用轨迹证明该空 Server 最早由清单启动。
- 首轮两个无真实模型探针中 OpenCode 通过,DSH 驱动误用工具内部 `filePath` 而不是
原生 schema 的 `file_path`,断言失败。修正驱动参数后两项通过,未调整产品代码。
- 随后完整三探针最终通过,Vitest 3 passed / 0 failed,39.20 秒:
- 生产 OpenCode launcher 单次 spawn,两个目录四个原生 Session,文件路由与单会话
删除后的其他会话存活通过;没有调用生产 `Runtime.run`。
- 真实 DSH Host composition、Agent factory、文件和 PowerShell 工具,两个不同 cwd
的 Agent 并行读写各自测试文件;生产控制面拒绝第二工作区也得到断言确认。
这不是 Electron Utility/ACP 生产多工作区测试。
- 一个 Windows 原生 `opencode acp`、一条 ACP connection、两个工作区 Session,
并行完成两个合成 Prompt;`chat.headers` 的 sessionID 与两个 HTTP 请求归属一致。
尚未经过 Agent backend、生产模型桥、真实工具/子代理或 Linux Host。
- 本次研究真实模型调用 **0 次**;最终 ACP 探针向本机确定性服务发出 **2 次 HTTP
请求**。测试服务、原生进程和专用工作区在 finally 中关闭/删除,不操作用户运行中的
Runtime 或其临时数据库。
- 复现命令:`npx vitest run --config .agent-resources/runtime-reuse-research-20260914/vitest.config.ts`。
`probe.test.ts`、`opencode-result.json`、`dsh-result.json`、`acp-result.json` 保留在
同一忽略目录,仅为本机证据,不作为他人 checkout 后必然存在的测试资产。
- 未实施项及生产验收见技术设计 P0–P4/V1–V8;本次未进行新的 Linux Host 验证、
真实模型开发任务或修复前后内存对照,不将原生能力探针描述成修复完成。
- 文档核验覆盖 10 个改动文档、131 个相对链接和其中 44 个标题锚点,全部通过;
FR-RR1–FR-RR6 的定义唯一且有验收引用,`git diff --check` 通过。研究用 OpenCode
二进制剩余进程数为 0。产品源码未改动,因此本轮未重跑全仓 test/typecheck/lint。
本页下方 schema 35 段落保留当时的失败与未发布记录。其后已随 Desktop `0.13.2`
(发布提交 `81ae9e0`)正式交付,Agent `0.11.25` 同期为维护发布;新的进程复用方案
不包含在这两个已发布版本内。
## 2026-09-14 多 Runtime 工具操作摘要
显示与事件合并规则见[工具操作摘要](./runtime-interactions.md#工具操作摘要)。Continue、
DeepSeek Harness 和直连模型已接入共享摘要函数,完成事件缺少输入时保留已有摘要。
Windows 完整 App 使用当前生产 Main、Preload、Renderer 和隔离配置,模型为
`deepseek-v4-flash`。从真实输入区输入并点击发送读取测试文件的请求,校验码只存于
文件中。三条成功会话都返回正确校验码,工具完成后仍显示路径;原生点击展开可查看
输入与输出,离开后重新打开会话、重新加载 Renderer 后再次打开均保留摘要和详情。
六张对话区截图已实际打开审阅。
| Runtime | 成功模式 | 提交数(含失败重跑) | 模型 HTTP dispatch | 工具执行 |
| --- | --- | ---: | ---: | ---: |
| 直连模型 | Ask | 1 | 3 | 3 |
| Continue | Execute | 2 | 4 | 3 |
| DeepSeek Harness | Execute | 3 | 3 | 2 |
| 合计 | | 6 | 10 | 8 |
计数来自 Main 和真实 Utility Host 的 HTTP dispatch,未注入答案或工具事件。Continue
首轮 Ask 仅提供配置工具,未能读取文件;改用 Execute 成功。DS 前两次因 Windows
工作区路径写法与 Host `realpath` 比较不一致而返回 ACP `-32602`,均未调用模型;
同目录改写路径仍复用缓存 Runtime,改用全新规范路径目录后成功。该路径问题随后由
[DS 工作区路径修复](../deepseek-harness/progress.md#2026-09-14-windows-工作区路径修复)
单独处理,新增验证计数见该记录,不计入上表。驱动的包解析、
DOM 定位及卡片比较器失败已记录,修正比较器后的复验没有追加模型请求。
本机临时 `runtime-summary-live/report.json` 保留逐次结果和产物哈希;源码基线为
`c71da4b6ae1d7228ff968d39c63db205caf61339` 加本次工作树改动,Main SHA-256 为
`2153d72a947ab6f5eab22eab28d7a3f2f86a47740d5223a3e1547f199fa7de84`。
测试进程、隔离配置、数据库、工作区和构建均已清理,正式配置字节未改变;报告和截图
保留在本机临时目录,不作为长期可用的仓库资源。请求使用已配置 HTTPS,沿用生产
证书策略,本轮未验证严格证书校验。未测试 Main 重启或远程 Agent。
专项回归 210 项通过、1 项跳过。完整 `npm test` 首轮 4154 项通过、66 项跳过,另有
6 个 `.agent-resources` 内的本地 `node:test` 文件被 Vitest 误收集导致套件失败。
使用 `npm test -- --exclude "**/.agent-resources/**"` 完整重跑后,353 个文件通过、
9 个跳过,4154 项通过、66 项跳过、0 失败,耗时 646.04 秒。`npm run typecheck`、
`npm run lint` 和 `git diff --check` 通过。
## 2026-09-13–14 schema 35 工具重复写入补修(未发布)
- 0.13.1 的历史库回收成功,但其真实模型验收限制子代理不使用工具,遗漏了生产工具对象
的 `undefined` 可选字段。JSON 重读省略这些键,原深比较误判未变化的工具块。正常开发
任务复现同批 928 条事件的 7,787 次重复工具块写入;仅归一化空字段的对照将负载从
10,467,622 降为 452,391 字节,逐条还原一致。该对照不是产品修复验收。
- 当前修复按共享工具 schema 的字段值比较,不序列化整份工具输出进行比较。空字符串、
输出修订、状态和错误变化仍保留。schema 35 使用同一编码器重整旧完整事件及 schema 34
已存差量,跳过 JSON 未变化的行;不会删除事件、会话、工具结果或来源字段。
- 存储缓存仍最多 16 个任务,终态及时移除缓存引用;回滚后按已提交记录重建,其他任务
不受影响。远程先按来源主键判断是否重放,重复事件不重建已经释放的写入缓存;
`INSERT OR IGNORE`、冲突校验及消息投影事务仍保留。
- Windows 当前源码加便携版内置 OpenCode 完成正常 CSV 项目开发,主/子代理实际读取、
改代码、补测试和执行命令;独立 59 项测试及 CLI 通过。698 条子代理事件共
368,575 字节,重复工具块为 0,与归一化对照字节数完全相同,每条事件还原一致。
- 多项目实测使用两个独立 OpenCode 实例、每项目两个会话,两轮共 8 次 Execute。
第一轮四个开发任务全部完成;第二轮复用会话,主动取消一个正在使用工具的子代理任务,
其余三个完成并通过测试。2,841 条子代理事件共 1,305,340 字节,重复工具块为 0,
全部逐条还原一致。每轮结束后缓存任务数、打开的 SSE 响应流均为 0。
隔离 Main 测试进程在显式 GC 后的堆占用为 12.94 → 12.91 MiB;
这证明本轮未持续积累,不是整个 Electron UI/Runtime 的 RSS 上限或无限负载保证。
- 共享 Linux x64 Host 使用当前桌面源码经真实 SSH、Agent、ACP 和模型 gateway 完成
Ask 与正常工具 Execute。912 条子代理事件共 405,360 字节,重复工具块为 0,
逐条还原及重复重放通过,远程 Python 项目测试通过。修复位于共享桌面层,不修改 Agent
或公开事件协议。两次早期测试驱动错误中断操作,修正瞬态事件处理后才完成上述验收;
不能将早期失败写成产品通过。
- 本轮实际提供商请求为 121 次:本地开发 26,多会话 79,远程账本 16(成功验收 14,
早期驱动中断 2 次结果未知、未交付)。本地计数器只记录 HTTP 状态,远程按测试
request/operation 的 Agent ledger 统计,不记录凭据、正文或请求头。另有先前诊断的
24 次请求,不计入这 121 次。已核对本轮本机和远程测试工作区无残留 Runtime 进程。
- 活动 schema 34 库通过 SQLite online backup 取得一致性备份,并校验独立测试副本。
正常启动升级成功,2,898,984,960 → 607,055,872 字节(2.70 GiB → 578.93 MiB,
回收 79.06%)。完整比较 424,148 条原事件的内容、ID、时间及来源一致。
隔离启动未复制模型配置,既有启动修复调整一条项目/会话 Runtime 选择及一个在途任务、
消息和活动状态,另追加一个状态事件;不是迁移删除或改写历史内容。
- 同一真实备份的另一个副本直接运行实际构建的升级 Worker,在处理 64 条后取消,
重启后完成 316,664 条子代理记录;全量事件再次还原一致。全部非事件表逐表内容一致,
包括 150 个会话、1,915 条消息、914 个任务和 136 个成果;`integrity_check=ok`。
原运行库和校验备份没有被测试改写。schema 35 副本再次进入实际 Worker 时直接完成,
进度事件数为 0,不重复整理。
- 最终专项 99 项、typecheck、lint 及生产开发构建通过。首轮全量产品用例 4,154 通过、
66 跳过,但 6 个临时 Node 测试项目被 Vitest 错误发现;配置已排除 `.agent-resources/`,
独立项目仍单独运行自己的 Node 测试。随后显式双 worker 全量为 4,148 通过、66 跳过、
`App.test.tsx` 6 项时序失败;恢复仓库默认单 worker 后该文件 220 项全部通过,
未修改 UI 代码。最终默认配置全量为 4,151 通过、66 跳过、3 个问答 UI 测试失败,
失败项与双 worker 不同;本轮全量回归尚未全绿。随后仅调整测试等待和 mock 隔离的
尝试仍有不稳定失败,已完整撤回这些额外 UI 测试改动,不把单次重跑通过当作稳定修复。
存储专项、真实工具任务、并发内存和真实数据迁移证据均通过;UI 测试问题仍需单独处理,
本次未提交、未发布,也未替换用户正在运行的 0.13.1。
## 2026-09-13 Desktop 0.13.1 执行记录去重与大库升级
- 实现与兼容边界见[执行记录存储与升级回收](./execution-history-storage.md),通用开发
@@ -31,7 +939,8 @@
实包开发验证通过。0.13.1 版本与发布说明变更后的本地最终验证被用户主动取消,按要求
不再追加本地测试,候选的最终全量测试、类型、lint 与生产构建由 main CI 判定。
- 用户批准 0.13.1 中英文说明及仅标准六平台发布;Agent 保持 0.11.24。
截至本记录尚未提交或打标签,准确候选提交、main CI 和原生发布结果待发布流程确认。
已发布提交 `4937720` 和不可变标签 `v0.13.1`,main CI 与六平台发布通过;
后续真实工具场景的遗漏及补修见本页 schema 35 记录。
## 2026-09-13 原生浏览器与应用浮层修复
@@ -370,13 +1279,38 @@ src/agent-daemon/runtime-composition.test.ts src/main/ipc.test.ts` 为 179 项
测试应用与具名浏览器会话已退出,所属 Electron/Node 进程及 fixture 监听端口均为 0;
临时截图和隔离 profile 保留在本机测试目录,不进入仓库。
## 2026-09-16 任务卡内审批
- 任务中心按 [Task Center 交互规则](../task-and-job/task-center-prd.md#5-交互) 展示卡内审批,
未关联审批保留处理入口;没有审批时不渲染空审批区域。
- 当前定时运行从队列 `taskId` 记录到 `ActiveRun`,审批事件再写入消息的临时审批数据;
历史消息使用明确的 `message.task.id`。Sidebar 按任务 ID 匹配当前范围内顶层任务,
并核对会话 ID 和项目 ID,不按会话、标题、时间或计划猜测运行归属。
- `RightAssistantSidebar.resize.test.tsx` 的 54 项测试通过,覆盖项目范围、状态筛选、
卡内处理、无审批、普通会话审批及关联不一致。App 使用 `-t "approval|scheduled instructions"`
定向运行的 14 项测试通过,验证历史与当前定时审批的映射、允许/拒绝接口调用及成功移除。
- `npm run typecheck`、`npm run lint` 通过。本次仅修改 Renderer 展示与临时关联数据,
本机和远程审批继续使用原有接口;未改 Main、Agent 或存储协议。未进行真实 Electron
窗口和模型调用验收;按本次要求未运行全量测试及无关数据库迁移测试。
## 2026-09-16 任务中心范围简化
- 任务中心仅保留“当前项目”和“所有项目”;无项目任务在后者中标注“未绑定项目”。具体规则见
[Task Center 范围](../task-and-job/task-center-prd.md#4-范围)。
- Renderer 将旧布局中的 `global` 恢复为 `all-projects` 并保存新值;共享解析契约、后端与任务
数据不变。App 仍只向任务中心传入 schedule 来源的顶层任务。
- `npx vitest run src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/workbar-contracts.test.ts`
通过 60 项;`npm run typecheck` 和 `npm run lint` 通过。未进行真实 Electron 窗口验收。
- `npm test` 在 200 秒后超时,未取得整库结果;单独复跑 `heartbeat-database.test.ts` 为 6 项通过、
2 项失败,均在未修改的数据库迁移代码中报 `duplicate column name: pinned`。
## 2026-09-10 固定基础页签与浏览器多实例
- 工作栏注册表已分别声明实例策略、默认上下文、默认打开、必须存在、可关闭和排序属性。
任务中心与工作区保持默认存在且不可关闭;浏览器改为当前 Conversation 上下文的多实例。
布局加载按 `required` 补回缺失的基础页签,并修复失效活动实例。
- 任务中心增加“当前项目”“全局任务”“全部项目”范围,默认跟随活动项目;任务和审批按
Conversation 所属项目过滤,范围模式随工作栏布局持久化。
Conversation 所属项目过滤,范围模式随工作栏布局持久化。范围选项已于 2026-09-16 简化,见上方记录。
- 浏览器服务已拆分 Conversation Context 与 Browser Tab。每个 Tab 拥有独立页面、Driver、
操作队列、状态和元素引用空间,同一 Conversation 的 Tab 共享 partition、代理和 Cookie。
- Renderer 使用工作栏实例 UUID 原子创建或恢复 Browser Tab,并以 UUID viewport token 防止
@@ -0,0 +1,142 @@
# Runtime 执行清单技术设计
状态(2026-09-19):本地与远程 OC/CN 提取、统一事件、消息保存和顶部展示已实现。
默认开发组包同时携带 OC/CN,远程 CN 已通过 Windows 桌面到 Linux x64 的真实模型
更新、取消、重启恢复及续发验证。验证范围与调用数见
[远程记录](../remote-host/runtime-checklist-validation.md#2026-09-19-联合包与完整桌面验收)。
需求、场景与顶部布局由 [PRD](./prd.md#131-runtime-执行清单)
维护,替换、隔离和终态规则由 [Runtime 交互边界](./runtime-interactions.md#runtime-执行清单)
维护。研究证据与交付状态见[进度](./progress.md#2026-09-15-runtime-执行清单设计未实现)。
## 1. 原生数据与提取位置
Implemented local behavior:
- OC sends a native-sortable `messageID` with both prompt and command submissions.
Only assistant messages whose `parentID` matches that request can own checklist
calls. Parent Session identity is checked independently of child-session routing.
OC 1.18.29 emits `pending`, `todo.updated`, then `running` with complete arguments.
The adapter retains native snapshots only while an owned call is active and
submits a snapshot once its validated items match that call's original input.
Calls and pending snapshots are request-local; no `session.todo` polling is used.
- CN extracts the exact `Checklist.checklist` argument in the patched native start
callback and in native history before display truncation. Candidates survive
success frames without input. A completed call commits once per call ID; failed
calls and startup history do not commit. Ordinary tool events retain their limits.
- CN accepts blank lines and `- [ ]`, `- [x]`, `- [X]` entries with nonempty content.
Empty or whitespace-only strings clear the list. Other nonempty lines invalidate
the entire update. Native 1.5.47 accepts a string without Markdown validation;
these are GoodBuddy's explicit projection rules, not additional native guarantees.
| Runtime / 路径 | 已确认格式与设计接入 |
| --- | --- |
| 本地 OC 1.18.29 | `todo.updated` 与 `session.todo` 返回完整条目列表,字段为 `content/status/priority`,无 item ID;以原生更新事件驱动本轮整份替换 |
| 本地 CN 1.5.47 | 精确原生工具 `Checklist` 的参数 `checklist` 是 Markdown 勾选列表,`- [ ]` / `- [x]` 表示未完成/完成;按一次成功调用提交完整替换 |
| 远程 OC/CN | OC 1.18.29 通过原生 todo.updated,CN 1.5.47 通过成功 Checklist 参数进入 Agent transcript;Linux x64 清单与重放已实测,CN 同时验证取消后续发,见[远程记录](../remote-host/runtime-checklist-validation.md) |
`opencode-runtime.ts` 当前将普通工具 input 经 `boundedToolDetail(..., 4_000)` 转成
展示字符串。接入应在 SDK 原始事件层消费 `todo.updated`;如需核对 `todowrite` 参数,
必须在该截断之前处理。`session.todo` 是 Session 当前快照,读取结果必须与请求生命周期
关联;不在每轮开始时无条件读旧快照充当新清单,也不轮询代替事件。
CN 的提取位置在 `continue-host-adapter.ts`:宿主产生工具事件、HTTP 状态合并及输入
校验均存在 4,000 字符展示限制,不能等到 `continue-runtime.ts` 收到截断字符串后再解析。
宿主应在最早的原始参数层提取 `checklist`,按工具调用 ID 保留候选,结合最终成功状态
提交;成功帧省略参数时使用同次调用的候选,失败不得提交。普通工具事件继续沿现有路径
流转。超出 4,000 字符的清单需要独立验证完整性,但不扩大所有普通工具输入的展示上限。
Markdown 解析只适用于已确认的 CN `Checklist.checklist`,不扫描助手正文或其他工具
输出。空数组在统一事件中明确表示清除;CN 空字符串或仅空白的字符串表示清空。
解析规则见上文;无效输入保留旧清单,不能误当清空。
## 2. 统一事件与消息归属
The shared contract is `src/shared/runtime-checklist.ts`: `runtimeChecklistSchema`
and `RuntimeChecklist { source: 'opencode' | 'continue', items }`. Each item has
`content`, `status: pending | in_progress | completed | cancelled`, and optional
`priority: low | medium | high`. `AgentEvent` carries
`{ type: 'checklist', requestId, checklist }`; conversation messages carry optional
`runtimeChecklist`. Empty arrays are valid replacements. Existing IPC event
transport forwards them without a separate channel or tool-input parsing.
本地 Renderer 按请求映射更新助手消息并沿既有保存入口落库;远程 Main 使用既有事务
投影更新消息。顶部读取所属请求消息的最新有效状态。
同一请求若同时观察到原生清单事件与其工具记录,只以前者更新清单,后者保留活动;不能
提交两次替换。原生父 Session、请求开始/结束边界需共同校验,子 Session 或旧请求事件
不得通过当前活动请求映射到新轮。
The existing message metadata serializer and every database message reconstruction
retain `runtimeChecklist`, including remote event projection and terminalization.
No SQLite schema migration or separate checklist store is needed.
历史清单与所属助手消息一起保存。
远端沿用事件提交、消息投影和 ACK 的既有事务及去重规则;相关存储边界见
[执行记录存储](./execution-history-storage.md)。实施前核对本地会话与远程任务实际落库
字段,选择能复用的最小表示。不新建清单数据库、独立快照系统、恢复日志或持久化服务。
旧消息没有结构化清单时保持缺省,不从已截断工具字符串或状态文案补造历史。
## 3. 远程 Agent 与 ACP 复用
远程传递复用以下实现:
1. `AgentAcpConnection` 按原生 `sessionId` 分派 `sessionUpdate`;
`AgentOwnedAcpPrompt` 校验 Session 后,以 `bindingId/operationId` 将通知写入
既有 semantic transcript。
2. `ProtocolRemoteRuntimeChannel` 已有 transcript 读取/ACK、身份核对,以及 ACP
channel 恢复与重放;`AcpRemoteRuntime` 消费 `session-update` 并转换桌面事件。
3. OC 插件转发原生 `todo.updated`,CN 宿主将成功清单写入 `_meta.goodbuddyChecklist`;
`AcpRemoteRuntime` 校验归属和结构后产生统一清单事件。标准 OC `plan.entries` 也有
映射,但 OC 1.18.29 实测走插件事件。Markdown、URI 和普通状态文字不生成清单。
优先复用上述通知和 transcript,不另建远端清单通道。对实际 OC/CN 部署确认过的
`plan.entries`,或已确认等价的 `plan_update` items,校验条目字段和状态后转成统一
清单事件。普通 `status`、plan markdown/URI、`plan_removed` 文案以及泛化工具名均不
触发清单更新;在没有确认对应原生清单身份前,`plan_removed` 也不能清空顶部。
若远程原生输出走精确清单工具,须确认原始参数、调用身份与成功状态确实贯穿 Agent,
再按同一提取规则接入。是否能直接复用原生 entries,或需补充最小 Agent 适配,取决于
真实格式证据,不能根据 ACP 类型定义推断 OC/CN 一定会发送。ACP 是协议,Runtime 身份
仍为 OC 或 CN。
受管安装、校验、项目创建与聊天选择已接入 CN;Agent 在 Host 运行固定 CN HTTP 宿主,
复用本地适配器和已有模型桥、请求归属、取消及恢复。开发包与 Linux 验证、联合分发和
完整桌面验收的剩余范围见[远程记录](../remote-host/runtime-checklist-validation.md)。
重连按[既有 ACP 与断线规则](../remote-host/technical-design.md#acp-与断线)执行。
已接受 Prompt 使用原 binding/operation 恢复,清单从已提交消息和其后的既有事件恢复;
重复事件不重复展示。连接丢失不表示条目完成,不为恢复清单重新发送 Prompt、模型请求或
工具调用,也不新增独立重连机制。
## 4. 顶部布局
`ConversationTaskStrip` 与 `RuntimeChecklistStrip` 共用顶部容器,任务在上、清单在下,
各自展开和折叠。没有 Task 时清单仍可显示;远程会话也保留清单入口。总高度不超过
45vh,各自展开内容不超过 20vh 并内部滚动,标题不收缩。清单内容区可用键盘聚焦和滚动。
状态文字与条目首行按文字基线对齐,状态图标单独居中。CN 不推断进行中或优先级。
新轮先选择本轮消息,再读取清单,不回溯寻找旧轮非空清单。后台更新保持展开状态和焦点。
## 5. 实施与验收
| 阶段 | 必须完成的工作 | 对应需求 |
| --- | --- | --- |
| CL-P1 | 原始层提取、严格格式校验、统一事件及请求归属;保留工具活动 | FR-CL1–FR-CL3 |
| CL-P2 | 消息投影和既有存储恢复、顶部只读布局、空数组清除及终态展示 | FR-CL2–FR-CL4 |
| CL-P3 | 确认远程 OC 原生格式,补齐远程 CN 真实部署与事件接入,复用 Agent 传递和恢复 | FR-CL1、FR-CL4 |
| CL-P4 | 本地与共享 Linux 真实 Host 逐项验收,记录版本、结果、调用口径与剩余阻塞 | FR-CL1–FR-CL4 |
以下为产品验收范围;已执行项和未覆盖项分别记录在进度与远程验证记录中:
- 本地 OC/CN 各经桌面正常发送路径产生并连续修改清单,检查新增、修改、重排、删除、
重复文本、有效清空及超过 4,000 字符的完整内容;CN 成功才替换,失败保留前值。
- 同会话连续两轮、并行会话、切换 Runtime/项目/执行空间、旧轮迟到事件和子会话事件
不串数据;完成、失败、取消不改写条目状态。默认 DS、任意 status/Markdown/list
工具不生成伪清单,工具活动仍可查。
- 本地会话重新打开及应用重启恢复历史;顶部折叠、键盘操作、浅深主题、窄窗口和 200%
缩放符合 PRD,保存与重放后的空清单不会恢复旧内容。
- 在共享 Linux x64 真实 Host 使用开发中的 Agent 与实际部署的 OC/CN,分别从远程项目
完成原生清单更新、取消、断线续接及历史恢复。沿用
[Agent 开发验证规则](../../../AGENTS.md#goodbuddy-agent-development-validation),
LAN/VPN 是同一机器的两条路径,不能计作两台 Host。
- 逐个记录 OC/CN 的安装、启动、模型请求、清单传递、顶部显示、取消和重连结果;基础
Runtime 成功不等于清单通过。任一步未执行写“待验证”,被外部条件阻塞则记录原因,
不以 mock、协议 fixture 或本地成功代替远程验收。
@@ -10,9 +10,10 @@
| DeepSeek Harness | 固定 Host 的 Execute 文件与命令工具不询问目录权限;若收到 ACP 权限请求,活跃 Execute 直接选一次性允许,缺少该选项时使用 Runtime 提供的允许选项,Ask 拒绝,不等待额外 authorizer | 固定 Host 当前未提供通用原生业务问答服务,不把普通模型正文误识别成待答协议。模型以普通回复提问时,用户通过下一条消息回答 |
托管 SSH OpenCode 的权限、原生问题事件与回复链路由
[远程主机技术设计](../remote-host/technical-design.md)定义。本次候选的远端业务问答需要
[远程主机技术设计](../remote-host/technical-design.md)定义。该远端业务问答基线需要
Desktop `0.13.0`、Agent `0.11.24` 和新启动的托管 Runtime;请先升级 Desktop,再更新
Host 环境。任意第三方 ACP 服务及旧 Agent 不因此获得通用问答接口,候选不表示已经发布。
Host 环境。2026-09-14 的已发布 Desktop `0.13.2` / Agent `0.11.25` 已包含此前问答修复,
但不包含本文末尾尚未发布的进程复用改动。任意第三方 ACP 服务及旧 Agent 不因此获得通用问答接口。
## 运行与失败
@@ -28,12 +29,25 @@ Host 环境。任意第三方 ACP 服务及旧 Agent 不因此获得通用问答
- 回答或跳过成功后,会话保留只读的“问题与回答”,显示原问题及对应的选项答案、
自定义文本或“已跳过”。同一任务的多轮问答逐次保留,随本地会话保存,重新打开后仍可查看。
提交失败时保留输入与重试入口,不生成已回答记录;取消未回答的问题不记为主动跳过。
- 新消息在提问到达时,将只含 `questionId` 的问题位置块插入当时的消息块序列;
待答和回答后的卡片使用同一位置,后续正文、工具输出不再排到问题前面。重复问题不追加
或移动位置块。位置块不保存待答表单;旧会话没有位置块时保留原有问答展示,不推测或
回填历史顺序。该变更复用既有消息元数据,不升级数据库 schema。
- 提交响应晚于任务结束时仍保存已提交的问答,但不覆盖完成或失败状态;晚于下一轮提问时
不清除新的待答卡片。旧版会话未保存的问题与答案无法从“已提交”状态文案恢复。
- 待答队列仅保存在当前桌面会话内存中,不写入会话快照。刷新仍在运行的消息时保留整个
- 待答队列仅保存在当前桌面会话内存中,不写入持久会话快照。刷新仍在运行的消息时保留整个
本地队列;完成、失败、取消事件或持久化终态快照清除整个队列。迟到的提交结果可以保存
已回答记录,但不会恢复表单或覆盖终态。本机 Runtime 不恢复重启前未提交的问题;托管 SSH
重新附加同一存活 Agent Prompt 时,由 Agent 当前待答列表补发问题,已回答的历史事件不补发。
- 未发布的进程复用源码在 Desktop 重启后由 Main 并行附加同项目的存活任务,将待答
列表和 Stop 所需的请求 ID 投影到实时 `activeRequest`,不保存该字段或重新发送任务。
回答/跳过成功由 Main 写回原远程助理消息的既有问答历史,Renderer 不覆盖远程正文或终态。
Main 在前台与恢复请求收到实时问题时立即保存位置块;实时待答快照没有语义事件来源,
不伪造来源或追加 transcript。带来源的提问事件仍通过既有事务投影并按问题 ID 去重。
Runtime 设置、定制和能力更新让旧请求在原配置上结束,已有问题仍可回答;新请求使用
新配置。专家团队例外:既有专家调度器在设置替换时取消子任务,团队父请求也随之停止,
不使用替换后的模型汇总旧结果。详细实现见
[恢复与终态投影](./runtime-process-reuse-technical-design.md#63-恢复与终态投影)。
- 后台任务收到结构化提问时,通过现有 Main 路径明确失败并提示改为前台对话,不永久等待。
- 请求结束或取消后清除对应待答映射;过期回答明确报错,不转交给后来的请求。
- 任意终端程序的密码、OAuth 登录、许可证确认或 stdin 提示不自动回复 `yes`。这类程序应
@@ -57,6 +71,21 @@ Host 环境。任意第三方 ACP 服务及旧 Agent 不因此获得通用问答
- 远端最终启动配置由[远程主机技术设计](../remote-host/technical-design.md)定义,
模型桥生成最终配置时同样保留上述快照策略。
## 工具操作摘要
工具记录沿用 [UI 设计系统](../../../UI-DESIGN.md)的折叠布局。Continue、DeepSeek Harness
和直连模型共用 `toolOperationSummary`:优先使用有效原生标题,否则从参数提取命令、
文件路径、搜索条件或 URL,合并空白后最多显示 240 字符。无法识别时保留原有工具名
回退规则;文件正文、整份参数 JSON 不进入标题,参数详情仍可展开查看。
Continue 在宿主截断输入前提取摘要,流式完成与失败事件使用合并后的输入和摘要。
DeepSeek Harness 按调用 ID 保留名称、输入和摘要,结束事件省略输入时仍能显示操作内容。
直连模型从原始参数生成一次摘要,在待执行、执行中和完成时复用;审批失败、拒绝和
执行失败、刷新重试仍保留已有提示。
本机 OpenCode 沿用原生操作标题。远端 ACP 独立使用原生标题作为记录名称,不经过上述
本地转换,本次没有修改 Agent、远程桥或协议。
## 消息底部请求状态
- 状态属于当前请求,不与顶部 Runtime 的连接中、就绪、不可用混为一谈。提交后先显示
@@ -69,9 +98,86 @@ Host 环境。任意第三方 ACP 服务及旧 Agent 不因此获得通用问答
正文、推理或工具进展会清除旧状态;子任务更新不覆盖父请求的重试提示。
- 完成、失败、取消后保留真实终态,迟到的状态事件不能把请求恢复成处理中。底部状态点
为静态,不凭请求尚未结束暗示仍有进展;会话列表闪点和发送、停止按钮保持原有行为。
持久化快照中的同一条消息仍为生成中时,也不能覆盖本地已收到的终态,即使快照的
会话更新时间更晚;否则连续发送或刷新会让已结束的请求重新显示停止按钮。
- Continue、DeepSeek Harness 和远端 ACP 保留其已接入的处理与工具事件,不伪造未接入
的原生重试状态。直连模型的重试等待与请求开始文案见
[直连模型 UI 设计](../direct-model-agent/ui-design.md#6-错误与通知);本次不改变任何
Runtime 的重试上限、请求重放或取消策略。
实现与真实 Runtime 验证证据见[进度记录](./progress.md)。
## Runtime 执行清单
本节为未实现的状态设计,对应 [FR-CL1–FR-CL4、US-CL1–US-CL5](./prd.md#131-runtime-执行清单)。
原始格式和传递边界由[技术设计](./runtime-checklist-technical-design.md)维护。
清单属于一次请求及其助手消息。沿用项目、执行空间、Conversation、Runtime 选择和
原生 Session 到请求的既有映射;远端还需核对 binding/operation。不能仅以原生 Session
或 Conversation 为键更新顶部,因为同一会话会连续执行多轮请求。
| 输入或操作 | 清单与顶部行为 |
| --- | --- |
| 本轮尚无有效清单 | 隐藏顶部清单,不复制上一轮内容 |
| 收到本轮有效完整清单 | 按原生顺序整体替换,包括修改、删除、重排和重复文本;不按条目文本合并 |
| 收到有效空数组 | 记录明确清空并隐藏;空数组与缺少清单字段不同 |
| CN 调用进行中 | 候选只按该请求的调用 ID 暂存;成功后才提交完整替换 |
| CN 调用失败、取消或格式无效 | 丢弃该候选,保留上次有效清单,工具记录保留实际结果 |
| 请求完成、失败、取消或中断 | 冻结最新已提交清单,并显示请求真实结果;不推断条目完成,迟到输入不改写已结算清单 |
| 同一会话开始下一轮 | 顶部转向新请求,上一轮清单留在原消息;旧轮事件不得归入新轮 |
| 切换会话、项目、执行空间或 Runtime | 只读取目标归属的数据;原请求在原归属结算,不把原清单标为新 Runtime 的进度 |
| 查看历史或重连 | 从原消息及既有事件恢复;按原请求和远程来源顺序去重,不能用最新原生 Session 快照覆盖历史轮次 |
条目状态支持 `pending`、`in_progress`、`completed`、`cancelled`。OC 原生提供的 priority 保留;
CN 未勾选项映射为 `pending`,已勾选项为 `completed`,不推断 `in_progress` 或 priority。
OC 无稳定 item ID,视图索引仅服务本次完整列表渲染,不生成跨更新的条目身份。
清单内容不创建、调度或完成产品 Task,也不改变底部请求状态。清单更新与工具活动可以
同时保留,但顶部计数只来自已确认的结构化清单,不能反向解析普通状态文本、任意
Markdown 或名称含 `list` 的工具。子会话清单不覆盖父请求顶部。
以上产品状态规则已明确;远程 CN 接入、原生格式确认及历史字段选择仍属技术待办,见
[进度记录](./progress.md#2026-09-15-runtime-执行清单设计未实现)。
## Runtime 资源生命周期
本节对应 [FR-RR1–FR-RR6、US-RR1–US-RR4](./prd.md#141-runtime-资源复用),
定义进程复用的行为规则。当前源码已接入本机共享 owner 和 Agent 共享 ACP;
完整验收状态以[进度记录](./progress.md#2026-09-14-runtime-进程复用实施中)为准,不表示已发布。
具体 owner、兼容键和协议实现见[技术设计](./runtime-process-reuse-technical-design.md)。
资源有三个独立维度:进程是否启动/退役,会话是否仍持有原生上下文,请求是否在途。
“任务完成”“当前没有选中该项目”“工作栏关闭”均不能直接推导进程可以销毁。
| 触发 | 请求与会话行为 | 进程行为 | 对应需求/场景 |
| --- | --- | --- | --- |
| 被动状态读取 | 不取得新执行会话,已有状态可读取 | 不冷启动,不延长无会话 owner 的空闲期限;不为探测替换服务中的进程 | RR2 / US-RR1 |
| 显式测试/原生清单 | 取得临时检查引用;清单带明确工作区 | 与执行共用启动 Promise 和 owner;结束只释放检查引用 | RR1、RR2 / US-RR1 |
| 首次或并行 Prompt | 取得对应 execution space + conversation 的绑定 | 同兼容组复用,非兼容组分别启动 | RR1、RR3 / US-RR1、RR2 |
| 普通请求完成 | 释放请求订阅、临时 token、问题和 callback,保留可续聊 Session | 不因为一个请求完成关闭整个进程 | RR3、RR4 / US-RR3 |
| 取消一个请求 | 取消其模型/工具调用并结算,其他会话继续 | 不以 kill 共享进程代替正常会话取消 | RR3、RR4 / US-RR2 |
| 关闭面板/切换项目 | 停止非必要 UI 推送;不取消后台任务或抹掉会话所有权 | 不触发隐式关闭 | RR4 / US-RR3 |
| 显式释放/删除会话 | 释放该 Session 与会话工具资源;删除历史仍只由原有明确删除操作决定 | 其他引用仍在则保留 | RR4 / US-RR3 |
| 无会话、无请求、无检查引用 | 不存在需要保留的原生上下文 | 进入短空闲等待,再关闭;新使用者到来取消等待 | RR4 / US-RR1、RR3 |
| 配置替换 | 已接受请求使用旧配置结算,新请求使用新配置 | 旧 owner 排空后退役;不积累只被历史 ID 持有的旧配置进程 | RR1、RR4 / US-RR4 |
| 进程故障/不可响应 | 只给该 owner 下受影响请求明确中断;未知结果不自动重放 | 有界终止失效 owner,不伪造逐会话故障隔离 | RR3、RR4 / US-RR2、RR4 |
| SSH detach | 保留 Agent 已接受 Prompt 及既有恢复归属 | 关闭传输不关闭 Agent 或活动 Runtime | RR5 / US-RR4 |
表中 RRn 是 PRD 稳定需求 ID `FR-RRn` 的简写。
不变量:
1. 一条请求始终使用接受时的 execution space、工作模式、配置和归属;不能读取后来
切换的 UI 选择,也不以进程 cwd 表示所有会话的 cwd。
2. 同一会话串行,跨会话不增加全局 Prompt 队列。权限、子代理、问题、用量和最终清理
必须沿同一 Session/请求归属,不因共享进程放宽 Ask 或限制 Execute。
3. owner 的退出条件依据所有使用者,不依据某个 adapter 的空闲状态。
未启动对象的 dispose 不产生冷启动;一个等待者取消不取消其他等待者的初始化。
4. 正常会话仍持有原生上下文时,不用任意 TTL 清掉它来声称“空闲内存已回收”。
无会话 owner 的短空闲等待为 60 秒;
不增加用户配置项。它是进程引用回收,不是聊天历史保留期限。
5. 持久历史不因请求结束、进程 idle、配置变化或测试 cleanup 被删除。
原生进程确实重启后的续聊沿现有历史重建规则,不新增第二套持久会话快照。
已明确普通并行、取消、释放、配置替换及断线行为。各项实际通过的场景及尚未完成的
组合/性能验证以[进度记录](./progress.md#2026-09-14-runtime-进程复用实施中)为准。
@@ -0,0 +1,392 @@
# Runtime 进程复用技术设计
## 1. 状态与结论
| 项目 | 内容 |
| --- | --- |
| 状态 | 已接入本机与 Agent 生产源码,Windows / Linux x64 验证结果及边界已记录,未发布 |
| 日期 | 2026-09-15 |
| 调研源码 | `de14a6157a6c34f1e3ef22619eca374a50a83706`;相关 Runtime 与 Desktop 0.13.2 一致 |
| 需求 | [工作栏 PRD §14.1,FR-RR1–FR-RR6](./prd.md#141-runtime-资源复用) |
| 行为规则 | [Runtime 资源生命周期](./runtime-interactions.md#runtime-资源生命周期) |
| 验证事实 | [实施与验证记录](./progress.md#2026-09-14-runtime-进程复用实施中),原始调研记录保留在同页 |
**修复对象是进程所有权过细,不是工具太多,也不是历史记录应该删除。** 本机 OpenCode
和 DeepSeek Harness 应在兼容启动配置内共享重型进程,工作区与会话上下文仍各自独立。
远端 OpenCode 必须同时调整 Agent 进程所有权、ACP 连接和模型桥,不能把桌面修复当作远端
修复。Continue 的锁定版本是单会话 Host,不做未经验证的常驻进程池;直连模型没有独立
模型进程,应检查对象、历史和工具资源的生命周期。
“一个进程”指一组兼容启动配置对应一个 Runtime Server/Host,不是把不同 Runtime、
不同 SSH 账号、不同插件组合强行塞入一个进程。正常工具命令产生的短期子进程不属于重复
Server,也不能通过关闭 Shell、子代理或 MCP 来达成进程数指标。
## 2. 修复前证据与缺陷
### 2.1 已测现象和解释边界
Windows 已安装 0.13.2 的只读采样发现两个属于同一 App 的 OpenCode Server:一个原生
数据库为 0 Session / 0 Message,约 527 MiB Private Commit;另一个已有会话并执行请求。
两个 Server 的启动工作区不同。源码能解释“清单查询也可启动按工作区缓存的 Runtime”,
但没有取得能独立证明空 Server 最初由哪一个 UI 操作启动的调用链记录。
该缺陷不等于整个 App 内存的唯一来源。五分钟活跃采样的 App 相关进程 Private Commit
约 3.15–3.78 GiB,CPU 平均占整机约 3.15%;后续一分钟包含新任务,不是空闲回收试验。
数据库五分钟增长约 2.72 MiB,新增子代理负载约 1.49 MiB,重复工具 upsert 为 0。
schema 35 的存储修复与本方案独立,不能回退或删历史来改善指标。
同次源数据检查还发现最近 100 个会话的消息 metadata 合计约 107.75 MiB,
`listConversations()` 会读入这些会话的全部消息详情。它是 Renderer 大对象加载的明确
检查方向,不是堆快照已经证明的泄漏。进程复用不能代替历史按需加载的独立性能工作。
### 2.2 调研基线的源码归属
| 位置 | 已确认行为 | 修复含义 |
| --- | --- | --- |
| [SelectedRuntimeManager](../../../src/main/agent/selected-runtime-manager.ts) | 缓存键包含原始 selection 与 execution-space identity;默认 8 个缓存、16 个保留实例;只在容量压力下淘汰 | 轻量工作区适配器和重型进程不能继续按同一键、同一数量计费 |
| [AgentRuntimeController](../../../src/main/agent/runtime-controller.ts) | `canRetire` 同时要求无请求、无 owned conversation;普通完成不移除会话所有权 | 不能把“已完成请求”和“已释放原生会话”混为一谈 |
| [本机 OpenCode](../../../src/main/agent/opencode-runtime.ts) | 对象私有 Server/client/初始化 Promise;已有多会话映射;多个入口使用固定 `defaultWorkspace` | 仅删除 manager 的 workspace 键会把后续操作送进错误目录 |
| [DeepSeek Runtime](../../../src/main/agent/deepseek-harness-runtime.ts) | 每对象一个 Utility Host;`getStatus()` 调用 `getState()` 启动 Host | manager 的临时状态实例会额外启动、探测并销毁 Host |
| [Harness Host](../../../src/main/deepseek-harness-host.ts)、[控制面](../../../src/main/agent/goodbuddy-harness-control-plane.ts) | 根 Provider 有 cwd 默认值;控制面拒绝与 Host workspace 不同的 `newSession.cwd` | 限制主要在 GoodBuddy 创建入口,不是原生工具缺少会话 cwd |
| [Continue Host Adapter](../../../src/main/agent/continue-host-adapter.ts) | 每次 run 启动临时 Host,最终 `/exit` 与进程树回收;准备结果已在 adapter 内缓存 | 不重复添加已有缓存;研究跨 adapter 重复准备,不直接共享活动 Host |
| [直连模型](../../../src/main/agent/model-runtime.ts) | Main 内 HTTP;历史 Map 按 50 个会话限制,其他归属及工具资源依赖 release/dispose | 没有可合并的常驻模型子进程,按对象和资源检查 |
| [远端 ACP](../../../src/main/agent/acp-remote-runtime.ts)、[Agent backend](../../../src/agent-daemon/runtime-acp-backend.ts) | 会话取得 binding/channel;backend 按 binding 拥有进程并拒绝跨 binding 复用 process identity | 必须拆开会话绑定与进程所有权 |
| [Agent-owned Prompt](../../../src/agent-daemon/agent-owned-acp-prompt.ts)、[模型桥](../../../src/agent-daemon/model-bridge-helper.ts) | owner 一个 ACP connection/session/当前 operation;helper 固定模型和工作模式并串行 dispatch | 共享进程还要求会话路由、请求归属和取消隔离 |
原始 selection 的 `auto`、默认 profile 和显式 profile 可能解析为同一有效配置。当前
已确认“先按原始 selection 建键、后解析配置”的顺序,但尚未证明全部 UI 调用会实际生成
等价重复实例。实施时从 [runtime-selection](../../../src/main/agent/runtime-selection.ts)
和调用者验证,不把可能性记为另一次生产复现。
### 2.3 锁定依赖与协议基础
以仓库 lock 和本机已安装代码为准,不用最新网页替代锁定版本:
| 依赖 | 基线 | 本次核验 |
| --- | --- | --- |
| OpenCode binary / SDK / plugin | 1.18.29 | HTTP API 接受 directory;ACP 接受多个 Session;`chat.headers` 有 sessionID |
| ACP SDK | 0.25.1 | 一条连接关联多个 request ID,session notification/permission 带 sessionId |
| DeepSeek Harness 包族 | 0.1.2-rc.1 | Agent factory/scoped context、Session header.cwd、真实文件与 Shell 工具 |
| Cordis | 4.0.2 | Host 根组件与 Agent scope 的生命周期 |
| Continue CLI | 1.5.47 | 实际受管补丁入口的单 `M.session`、QuizService 和权限状态 |
已通过的三个隔离探针:
1. **OpenCode HTTP**:调用当前生产 Server launcher,只有一次 spawn;同一个 SDK client
在两个目录创建四个 Session,读取各自的同名文件;删除一个 Session 后其余三个仍可取回。
没有调用 `Runtime.run`、模型或活动取消。
2. **DeepSeek 工具**:当前 Host composition 和真实 Agent factory 在同一 Node 测试进程中
创建两个不同 cwd 的原生 Agent;并行执行真正的 `read`、`write` 和 PowerShell,
同名相对路径各自命中正确目录。另确认生产控制面仍拒绝第二工作区。没有启动 Electron
Utility、ACP Prompt 或模型流,不能据此声称生产多工作区已经可用。
3. **原生 ACP 与模型归属**:Windows 上一个 1.18.29 `opencode acp` 进程、一条 ACP
connection、两个工作区 Session;分别选模型并发出并行 Prompt。本机确定性 HTTP
服务收到两次请求,原生 `chat.headers` 插件传来的 sessionID 分别匹配两个 Session。
不经过已发布 Agent backend/helper,没有测试真实工具、子代理、Linux 或断线恢复。
三个探针真实模型调用均为 **0**,其中第三个有 **2 次本机合成提供方请求**。
它们证明方案有原生基础,不替代后文的真实开发任务验收。
特别纠正:`dsh-tool-fs` 将 `exec.agent.session.header.cwd` 传给 `fs.resolve`;
`dsh-tool-bash` / `dsh-tool-pwsh` 也从 Session cwd 补全相对 workdir。
`dsh-fs-local` / 本地 Shell 的根 cwd 是非会话调用的默认值,不是工作区隔离边界。
不需要为这个已具备的能力新建一套“每工作区 Filesystem/Shell Provider”。
官方背景资料:[OpenCode Server](https://opencode.ai/docs/server/)、
[DeepSeek Harness](https://www.deepseek.com/harness/en/)、
[Continue CLI](https://docs.continue.dev/cli/quickstart)。
网页只用于核对产品入口;上述版本结论来自本地锁定代码和探针。
## 3. 选定的结构
### 3.1 两层对象,不建立通用进程池框架
```text
SelectedRuntimeManager
-> 轻量 Runtime adapter(有效 selection + execution space)
-> OpenCode process owner / Harness Host owner(兼容启动配置)
-> Session binding(execution space + conversationId)
-> 当前 requestId / 原生 sessionId / 工作目录
```
- 保留 Main 已解析、校验的 execution space;adapter 每次将其显式交给共享 owner。
目录不是 Renderer 新增的任意启动参数,也不依赖 `process.chdir` 或进程级可变 cwd。
- owner 持有唯一启动 Promise、client/connection、子进程、会话映射与关闭逻辑。两个
工作区同时冷启动时复用同一 Promise;一个调用者取消等待不取消其他使用者的启动。
- adapter 的 `dispose` 只释放其引用,不直接关闭同组进程。会话映射由 owner 持有,避免
adapter 缓存淘汰丢掉原生续接。会话操作通过 binding 查目录,不从“当前选中项目”反查。
- 设置重建沿用现有 reset/drain:新请求取得新配置,旧请求持有启动时的配置直到结算。
旧配置不再有在途使用者后释放旧绑定和进程,不能被历史会话 ID 永久钉住。
IPC 的 Runtime 设置、定制及能力更新不再主动取消全部请求或清空审批;controller
退役仍允许回答其已拥有的问题,但拒绝新请求和未知问题。
既有专家调度器仍在配置替换时取消专家子任务,团队父请求一起停止,防止用新模型
汇总旧 generation 的部分结果;不为该单独生命周期新建通用退役框架。
- 8/16 的旧上限不应继续限制轻量项目 adapter 数量。若保留重型 owner 的容量保护,
应统计实际进程配置组及退役进程;不靠提高常数掩盖“一项目一进程”。
- 只用进程内 Map、现有 controller、Promise 和普通定时器。会话历史仍以现有 SQLite /
Agent transcript 为准,不增加数据库表、迁移、持久化池快照、恢复日志或新配额。
### 3.2 兼容键与上下文
先通过 `applyRuntimeSelection` 等现有解析取得有效配置,再生成进程键;公开的 selection
仍按现有语义保存,不能把“跟随默认”永久改写成某个显式 profile。
| 范围 | 参与进程兼容性 | 留在会话/请求 |
| --- | --- | --- |
| 本机 OpenCode | 本机账号、二进制身份、有效模型连接/配置、启动环境、原生配置和插件组合 | 目录、conversation/session、Prompt、模式、当前权限和问答、请求级 MCP/知识 token |
| 本机 Harness | Host/依赖版本、有效模型连接、图片能力、凭据引用、启动环境、启用插件及配置、启动 Skill 集合 | cwd、conversation/session、请求模式、Main Web/MCP 代理与用量归属 |
| SSH OpenCode | 已有 Host/SSH 身份、Agent/Runtime installation identity、模型协议/连接、原生插件与启动配置 | Workspace、binding/session、operation、请求凭据和账本归属 |
第一轮只合并同一有效模型连接及等价默认选择,不顺便支持任意不同模型连接共享:
本机 OpenCode 配置使用固定 provider ID,Harness 用量也读取 Host config,跨 profile
混用会引入另一项真实风险。不同 profile ID 不通过比较或序列化明文 API Key 来合并;
密钥、Header 和插件配置更新通过现有设置失效路径使旧 owner 退役。
调研基线的 SSH launch profile 把 workMode 写入原生启动配置。当前实现把工作模式
放入 Session/当前 Prompt 路由,并设置原生 Session 工具规则,详见 §6.1;
不能仅从兼容键中删除 workMode,也不向用户增加逐工具审批。
## 4. 本机 OpenCode
在 [opencode-runtime.ts](../../../src/main/agent/opencode-runtime.ts) 中抽出重型 owner,
由 [create-runtime](../../../src/main/agent/create-runtime.ts) 的 Main composition 复用。
保留工作区 adapter,逐项替换隐式 `options.defaultWorkspace`:
1. create/get/delete Session、Prompt、历史初始化和压缩。
2. 原生能力、工具、文件、Skill、MCP 查询的 directory。
3. SSE 订阅、父子 Session 归属、待答 question 的 directory 与原生 ID。
4. 权限更新、abort、问题答复/拒绝、MCP 临时注册及最终清理。
会话绑定记录规范化绝对目录及原生 sessionId。同一 conversation 的请求继续串行,
不同 conversation 并行,不引入覆盖整个 Server 的 Prompt 队列。
取消或失败时,每次请求只发送一次会话级 abort,并在该调用结束后才释放会话锁,避免
迟到的 abort 中断下一条请求。abort 使用既有控制请求超时;失败或超时后移除对应绑定,
下一条请求创建新 Session。这也适用于配置外部 OpenCode HTTP Server 的路径;托管 SSH
使用独立的 ACP 取消路径。
现有 `snapshot: false` 必须保留。
`mcpMutationTail` 目前属于 Runtime 对象;共享后同一原生目录的变更必须由同一 owner
串行协调。注册名称、工具可见性、knowledge token 和清理仍按请求区分,不能用一个全局
“当前 MCP 列表”覆盖同目录或跨目录的其他请求。探针未覆盖动态 MCP,这是实施验收项。
第一轮保留现有按请求消费事件流和结束清理的方式,扩展目录及 Session 过滤即可。
只有测出重复 SSE 消费是瓶颈时才另做 owner 级事件分发,不同时引入第二套事件总线。
健康检查也读取 owner 的总 activeRequests,不能从一个空闲 adapter 判断整个 Server
无任务后重启。异常退出使该 owner 的全部绑定失效,明确结算受影响请求,不重放已执行工具。
## 5. DeepSeek Harness
### 5.1 共享 Host 的最小改法
- 将 `HarnessState`、Utility 启动 Promise、ACP connection 和事件路由提升到兼容配置
owner;Runtime adapter 保留请求工作区。
- 每个新 Session 在 Main 解析 `realpath`,Host 校验绝对路径格式,
不再要求它等于 Host 启动工作区;`meta.cwd` 传入已有 Agent factory。
不依赖 Windows 大小写字符串猜测,也不取消路径存在性校验。
- 保留一个本地 Filesystem、Subprocess 和平台 Shell Provider。Session 使用原生 cwd
路由;非 Session 的清单查询显式传入查询工作区,不能回退到第一个项目。
- 保留 `setup(agentCtx)` 注册 Skill 和工具视图、每 Session 的 Ask definition 身份校验、
`sessionId + requestId` 准备及事件路由。Main 的 Web/MCP callback 按对应请求找
ToolProvider,不挂到某个最先创建的 adapter 上。
- 第一轮保持同 profile、图片能力和插件组合共享,`queueUsage` 的模型配置因此仍一致。
给用量加跨会话断言,不借本次改造扩大跨模型 Host 能力。
第三方插件的初始化可能依赖配置中的路径,不能因为内置工具支持 cwd 就声称所有插件
天然多工作区安全。已启用插件组合属于进程配置;对实际产品支持的插件验证它使用
Agent scope/Session cwd。若某插件确实有项目级初始化状态,将该状态显式作为不兼容
启动配置,而不是默认给所有工作区各起一套 Provider 或禁止 Execute 工具。
### 5.2 状态与取消
被动 `getStatus` 改为已有 owner 快照或本地配置/Host 资源检查,不调用 `getState()`。
没有启动过时不能写“握手已验证”,可表示资源已安装、尚未连接。
状态检查不取得执行引用,也不清除或重置无会话 owner 的空闲计时;owner 已回收时,
临时配置检查对象用完即释放,不将其登记成新的执行 owner。
用户显式连接测试与原生清单可启动共享 owner;已有 owner 时只做有界控制检查,最终
释放的是检查引用,不能在 `finally` dispose 掉仍服务其他会话的 Host。
当前取消超时会 terminate 整个 Utility。共享后先取消并释放目标 Session 的 Agent /
工具资源,不因一个请求结算较慢就杀掉仍健康服务其他 Session 的 Host。若真实控制面
已失去响应,保留整个 Host 的有界终止,向受影响会话分别报告中断;不得把它说成
“取消完全不影响其他任务”。同步插件卡死的进程级故障无法在同一 JS 进程中强行隔离,
不为此新增通用恢复框架。普通长工具取消与整 Host 故障必须分开测试。
## 6. SSH OpenCode 与 Agent
本节定义当前源码的共享进程改造;当前已发布版本仍见
[远程技术设计](../remote-host/technical-design.md)。不能删除或绕过其持久 binding、
operation、transcript、ACK 和模型 dispatch 结果不确定性规则。
### 6.1 连接与所有权
将 `RuntimeAcpBackend` 的进程 owner 与 binding 分开:
```text
Agent 当前 Runtime installation + 兼容配置
-> 一个 process owner / 一条 ACP ClientSideConnection
-> sessionId -> binding -> 当前 operation / 原始 Prompt Promise
```
- 一条 stdio 流只交给一套 ACP decoder/connection,不能让几个 ClientSideConnection
竞争读取同一 stdout。消息和 permission 按 sessionId 分派给 binding 的当前 operation。
- `AgentAcpConnection` 统一连接级握手、stdio 解码与退出监听,Prompt
生命周期仍留在 binding。同一 Session 串行,其他 Session 不等待它完成。
- `closeAcpChannel`、取消和 release 只释放所属 binding/Session;以活动 operation
判断共享进程是否空闲,最后一个任务结束即回收,不因历史 binding 保留进程。
待答问题仍占用活动 operation。关闭 Session 时等待原生 `closeSession` 和原始 Prompt
结算,不等待排在 backend 控制队列后面的完成回调。只有原生控制面不响应时才有界
终止共享进程,并向其余请求报告中断,不声称这类故障可以逐 Session 隔离。
- terminal 与物理进程状态分开:取消/失败的 Session 也可返回
`processTree: running`,表示进程继续服务其他 Session,不能伪报进程树为空。
Desktop 与 Agent 须使用配套的新契约;原始字节透传 binding 仍保留独占进程规则。
- 共享 stdio owner 不使用单 Prompt 的累积输入和墙钟期限管理整进程;单次写入、
待写队列、输出背压继续有界,Prompt 输入与截止时间由 binding 管理。
- SSH attach 仍不拥有活动 Prompt,断线不能取消或重发 Agent 已接受的工作。
历史 binding 的语义和原生 sessionId 保留;内存进程索引从当前安装和已有绑定得出,
不增加第二份持久进程恢复账本。
- 同一 PID 的多个 binding 不代表同一权限或同一 operation。Host/installation identity
变化仍使用已有失效和退役规则,不把不同 Host、账号或 Runtime generation 合并。
原生 `chat.message` 在模型请求前设置该 Session 的工具规则:Execute 允许工具;
Ask 只开放原生只读、搜索和读取网络内容的工具,不开放 Shell/编辑/写入。
OpenCode 1.18.29 的 ACP 不转发未注册子 Session 的权限请求,且原生子代理只继承父
Session 的拒绝规则、不继承允许规则,因此子 Session 也按根 Prompt 设置规则,再
保留原生子代理的显式限制。不能只把进程启动配置统一设成 `ask`,否则子代理工具会等待。
### 6.2 模型桥归属
选用锁定 OpenCode 已有的 **`chat.headers` 会话标记**,扩展当前受管
[原生插件](../../../src/agent-daemon/opencode-subagent-plugin.ts),不靠解析正文、猜测
“当前最新 Prompt”或重写用户模型名来路由:
1. 现有 Agent prepare/start 顺序在 helper 的进程内登记 Session/根 Session 与
binding、operation;插件从原生 sessionID 及已建立的父子关系取得精确归属。
2. 在请求生成时固定所属 operation,将有界内部归属 Header 加到 loopback 模型请求。
归属必须覆盖原生子代理、自动/手动压缩,不能等 HTTP 到达后才绑定“目前活动的请求”。
3. helper 检查它是否仍属于已登记的 Prompt,选择对应现有 Agent gateway/凭据上下文。
转发真实提供方前剥离内部 Header;不将其当作密钥或新增认证体系。
4. 当前 helper 的单 socket/全局 dispatchBusy 改成按 operation 隔离的派发状态;
每个 operation 的现有轮次、顺序、字节和结果不确定性规则不变,取消 A 只关闭 A 的
提供方流,不关闭 B 的 socket、response 或待处理请求。
5. Prompt 终态撤销对应映射及凭据;迟到请求不能借用下一轮 operation。保留
`snapshot: false`、关闭不消费的 title 请求和现有父子问题转交。
普通 ACP 双 Session 的 Header 路由已有探针证据;子代理、压缩和终态竞态的实际
验证结果以进度记录为准。若锁定版本某条实际调用路径不触发 hook,先定位那条原生路径,
不得悄悄用全局串行 Prompt 或一个共享凭据槽冒充并行复用。
内部桥字段如需调整,按既有受管 Agent/Runtime 版本配套交付,不移动发布标签,不给
旧 Agent 宣称具备新行为。
### 6.3 恢复与终态投影
- Agent-owned 语义 Prompt 不一定产生原始 ACP journal 行。binary replay 仍校验现有
cursor 边界,但不存在 raw cursor 时不向 journal ACK 0;不虚建记录来满足 replay。
- 同一 Agent 断线恢复后,保留的 Session binding 使用新 transport generation,
Runtime 可以同时接纳新 Session。安装身份已经更换的 attach-only 请求明确失败,
不能无限重试阻塞项目,也不能借此重发旧 Prompt。
- Desktop 重启时,同项目的可恢复任务并行附加,不能等待一个待答 Prompt 结束才附加
第二个。live `question`/`question-resolved` 不属于有 provenance 的 transcript,
使用既有 pending Map,清除时只影响对应 request/question。
- `conversations.list` 将恢复租约的 request/message ID 和待答列表投影为可选
`activeRequest`,Renderer 合并表单并使 Stop 指向真实请求。保存 header/消息时不保存
这个实时字段。回答/跳过成功由 Main 按 task → assistant message 归属更新既有
`answeredQuestions`,不写另一份问题日志,不覆盖流式正文、已完成或失败状态。
- 明确确认的取消使用现有消息/任务终态事务一并结算,避免 Task cancelled 而消息仍
streaming。仍无法确认的 Stop 保持可恢复;连接中断本身不是已取消或已失败的证据。
## 7. Continue、直连模型与相邻内存问题
### 7.1 Continue
保留每请求 Host 的隔离和最终清理。1.5.47 的单 Session、QuizService/权限队列意味着
“一个 HTTP Server”不等于可安全承载多会话;这一例外有锁定源码依据。
`getPreparedHost()` 已缓存本 adapter 的准备 Promise,磁盘也有按内容确认的补丁产物。
先分别测量读取/哈希/补丁准备、Utility 启动、HTTP ready、首个提供方请求和退出耗时。
若确认跨 adapter 重复准备明显,再将只读准备结果与启动 Promise 按已验证的 CLI
identity/补丁版本放到 Main composition 复用;失败仍允许重试。不得复用临时 config、
token、global dir、活动 Session 或权限/问题队列。设置替换及窗口状态刷新也纳入测量。
不在本次给 Continue 加多 Session 补丁或常驻池。将来只有锁定版本真正支持多 Session
且生产桥验证通过,才替换这一实现。
### 7.2 直连模型及历史详情
- 不新增模型 Utility 或进程池。检查 `conversations`、`knownConversationIds`、
summary、ToolProvider 与 DirectModelProcessService 的实际引用与 release 调用链。
- 活动请求、会话可续读的有界输出和数据库历史是不同资源。请求结束回收临时闭包/
callback;会话释放才结束其可续读输出和工具资源,不在工具刚完成时删掉用户可查看结果。
- 当前 50 个会话的 history 限制不是字节上限。先采堆与 retained size,再决定可从
SQLite 重建的历史缓存是否使用字节约束和 LRU,不能先增加一套持久历史副本。
- Renderer 整包 metadata 加载是独立于进程复用的性能工作。现有实现改为列表摘要、
打开会话时读取完整历史,不截断已保存消息;搜索、复制、续聊、后台活动和缓存释放
规则见[会话列表读取与前端保留](./execution-history-storage.md#会话列表读取与前端保留)。
不将这项后续修改的收益计入原进程复用对照结果。
- 旧 Runtime 磁盘缓存可单独评估版本清理策略,不在本次研究或进程 idle 时删除安装、
用户工作区、数据库、备份或历史。
## 8. 内部实施顺序
| 阶段 | 具体工作 | 必须产出的证据 |
| --- | --- | --- |
| P0 | 固化本次原生探针为聚焦回归;补有效 selection/进程计数和状态启动计数 | 修复前能复现重复启动,失败原因不依赖真实用户配置 |
| P1 | Main composition、兼容键、owner 与 adapter 生命周期;本机 OpenCode 全入口目录化 | 真实 UI 两项目四会话同一 Server,文件/工具/问答/取消不串线 |
| P2 | DeepSeek 共享 Utility、会话 cwd、被动状态、请求级代理及取消收敛 | 实际 Utility/ACP 两项目四会话,原生工具与插件验证,状态刷新无额外 Host |
| P3 | Agent process/session owner、ACP 单连接路由、Header 模型桥、权限和终态清理 | 开发中的真实 Linux x64 Host,含多会话工具、断线/重连和结果不确定性 |
| P4 | 全 Runtime 资源回归;Continue 冷启动分解与有证据的准备复用;直连对象释放检查 | 相同输入的前后进程/内存/CPU/磁盘/延迟报告;没有残留测试进程 |
这是依赖顺序,不是发布一半实现的许可。涉及的正常生产 UI 路径、远端专用修复和下节
验收全部完成后,才能称为 Runtime 复用修复。Continue 测量若没有显示重复准备成本,
明确记录不改;不要为凑齐阶段增加代码。历史 UI 分页是后续独立工作。
## 9. 验收与性能比较
### 9.1 必测场景
| 编号 | 对应需求 | 场景与通过条件 |
| --- | --- | --- |
| V1 | FR-RR1、FR-RR2 | 全新 profile 仅查看状态、切换设置,OpenCode/DSH 重型启动次数为 0;已有 Host 时刷新不新建;清单与首个真实请求并发只有一次初始化 |
| V2 | FR-RR1、FR-RR3 | 同一有效模型连接,两个独立项目各两会话同时自然开发,OpenCode/DSH 各一重型进程;各项目同名不同内容文件、命令 cwd、Git/测试结果都正确 |
| V3 | FR-RR3 | 第二轮复用同一批会话;Ask/Execute 混合,Ask 写入被边界拒绝,Execute 不增审批;Skill、MCP、图片能力、用量和业务问答不串归属 |
| V4 | FR-RR3、FR-RR4 | 一会话取消真实长工具/子代理时另外三会话完成并通过独立项目测试;分别检查请求 abort、SSE、问题映射和工具句柄释放 |
| V5 | FR-RR4 | 关闭面板不取消工作;释放一个会话不关闭共享进程;无会话 owner 空闲后退出;App 退出后本机受管进程无残留,远端按已有 detach 规则保留 Agent-owned 工作 |
| V6 | FR-RR1、FR-RR4 | 等价默认/显式选择复用;不同实际连接/插件配置不串用;配置更新期间旧请求完成,旧 generation 不永久积累;超过八个项目不因轻量 adapter 报重型容量满 |
| V7 | FR-RR5 | 当前源码 Desktop → SSH → 当前开发 Agent → 原生 Runtime,两个项目/四会话;断线、重连、取消、重复 ACK/事件、迟到模型请求不重复调用或跨 operation 归账 |
| V8 | FR-RR6 | Continue 并发请求仍各自正确清理;直连 HTTP 不生出额外模型进程;会话 release 后工具/分页输出资源释放;性能采样不靠删除历史或禁用工具达标 |
V2/V4 必须是正常开发任务,模型可自然调用读写、Shell、测试和已支持的原生子代理;
不是要求模型“不用工具”后回复一行。验证产出应用本身可运行、独立测试通过,模型口头
说完成不算完成。每个 Runtime 只验证它已经支持的能力,不伪造 DSH/Continue 子代理。
### 9.2 采样口径
- 使用同一源码变更前后、同一机器、隔离 profile 与等规模历史副本;不改写用户运行库。
覆盖至少三次冷启动、五次热请求,以及三轮多项目任务和至少十分钟**确无新任务**的
空闲观察。分别记录中位数、范围与实际样本数,不从一次采样推断泄漏已消失。
- 记录 Main、Renderer、Runtime 和工具子进程的 Private Commit、工作集/RSS、堆、
CPU、PID/启动次数、会话数、活动请求、SSE/ACP 订阅与残留句柄。Windows 工作集求和
会重复计算共享页,Private Commit 也不等于独占物理 RAM。
- 同时报首轮准备、HTTP/ACP ready、首个模型请求、首次可见输出和总耗时。降低进程数
但把不同会话全局串行化,不能通过验收。
- 数据库/WAL 字节与新增事件负载分开;进程 I/O 包含管道,不能称为物理磁盘读写。
继续检查未变化工具 upsert 为 0,旧历史读取/搜索/展开正常。
- 核心硬指标是每个兼容组一重型进程、无额外被动启动、无跨会话错误、无结束后持续
增加的活动句柄、无重复提供方 dispatch。没有证据前不承诺“固定省 527 MiB”或
“整个 App 降到某个 RAM 上限”;原生 Session 仍保留时必须单独说明其常驻成本。
### 9.3 源码与平台验证
定向覆盖 manager/controller、各 Runtime lifecycle、Harness Host/control plane、
ACP backend/owner、模型桥与插件,随后执行仓库要求的 `npm test`、`npm run typecheck`、
`npm run lint`;生产构建改动在开发阶段按仓库规则构建验证。
凡改变 Agent 或桌面到 Agent 路径,必须在开发期间按
[真实 Host 验证规则](../remote-host/technical-design.md#agent-开发期间的真实-host-验证)
使用当前源码的共享 Linux x64 Host 验证。Windows 原生 ACP 探针、旧版本 Host、mock、
CI 和将来发布测试都不能替代它。Windows PowerShell 与 Linux Bash 的工作目录及取消
分别验证;macOS/Linux arm64 生命周期与原生依赖由可用原生平台和 CI 补齐并明确报告。
当前源码的真实 Linux Host、完整生产 UI 和最终整仓门禁结果均保留在进度记录;
各次失败、修正、模型调用数及未测平台也一并记录,不以本机单测或原生协议探针
替代完整产品验收。完整 App 性能样本保留用户要求的按钮位置调整,属于包含该 UI
差异的实测对照,不是纯 Runtime 单变量实验。
@@ -90,7 +90,8 @@ SSH 连接池,但尚不存在工作栏应用实例模型、终端 IPC、终端
工作栏 Tab 是应用的一次打开实例。每个实例具有独立 ID、标题、应用类型和状态。Tab 行只
显示已经打开的实例,不把全部能力同时渲染为 Tab。
实例持久化字段只有 `id`、`appId`、`title` 和可选 `targetRef`。`targetRef` 使用公开引用:
实例持久化字段为 `id`、`appId`、`title`,以及可选的 `targetRef`、`defaultTitleNumber`。
默认标题编号的保存和兼容规则见第 10 节。`targetRef` 使用公开引用:
终端接受 `local` 或 `projectId`,浏览器接受 `conversationId`。Main 签发的 Browser Tab ID、
Terminal Session ID、元素引用、凭据、路径和运行状态不进入工作栏布局。
@@ -178,9 +179,13 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
- 已打开实例位于一个语义化 `tablist` 中。
- 每个实例使用独立 `tab` 和 `tabpanel`。
- “+”固定在 Tab 行末尾,但位于 `tablist` 之外。
- Tab 过多时实例区域单行横向滚动,“+”保持可见;不换行、不自动隐藏实例。
- Tab 过多时实例区域单行横向滚动,不换行、不自动隐藏实例。“+”紧随最后一个标签并随标签一起滚动,滚到末尾时可见。
- 标签全部可见时隐藏左右滚动按钮;溢出时左右箭头透明浮动于工作区两端,不占标签布局宽度。鼠标移入标签栏时显示箭头,移出后隐藏;键盘聚焦箭头时显示,无悬停能力的设备保持可见。悬停箭头时显示轻微底色。点击滚动约一屏,边界方向禁用且不拦截下方标签的指针操作。切换或新增激活标签时将其滚入可见区域。未激活标签使用低对比度灰蓝底色和细边框,激活标签保留亮底与强调色顶线。
- 终端 Tab 默认使用递增标题,例如“终端 1”“终端 2”;状态栏持续显示项目、Host、
工作目录和 Shell。用户可以重命名终端。
- 任务中心、工作区、成果及新建终端的默认标题随界面语言切换,恢复布局时也使用当前语言。
终端自定义名称保持原文;旧布局中的终端名称保持原值,即使看起来像“终端 1”。浏览器
标题沿用现有行为,不在本次标题国际化范围内。
- 当前 Tab 同时使用连续内容表面、选中边框、顶部强调线、图标和较高字重,不能只靠文字颜色。
- Tab、关闭按钮和“+”入口沿用共享控件圆角;终端面板外框和终端顶部工具栏按钮使用直角,
保持紧凑的停靠式终端界面。
@@ -345,6 +350,7 @@ type WorkbarTabInstance = {
id: string
appId: WorkbarAppDefinition['id']
title: string
defaultTitleNumber?: number
targetRef?:
| { type: 'local' }
| { type: 'project'; projectId: string }
@@ -362,6 +368,8 @@ type WorkbarLayoutPreferences = {
```
- 持久化工作栏打开状态、Tab 顺序、活动实例、应用 ID、自定义标题、公开目标引用和宽度比例。
- `taskScope` 中的 `global` 仅保留旧布局解析兼容;UI 恢复时映射为 `all-projects`,此后只保存
`current-project` 或 `all-projects`。范围规则见 [Task Center PRD](../task-and-job/task-center-prd.md#4-范围)。
- 加载布局时根据 `required` 补回缺失实例,并拒绝重复的单实例应用;应用策略不写入实例
数据,避免持久化状态覆盖注册表。
- `reorderable` 已进入共享契约;当前 Tab 壳层尚未提供拖放或键盘重排,不能把声明属性视为
@@ -373,6 +381,12 @@ type WorkbarLayoutPreferences = {
Shell。
- 运行期间切换项目不会改变已经创建的终端目标。用户需要新目标时通过“+”创建新终端。
新建终端保存正整数 `defaultTitleNumber`,沿用同一目标已有终端数量加一的编号规则。
Sidebar 在渲染时按当前语言派生应用和默认终端标题,再交给 Tab 壳层与终端面板;切换语言
不改写持久化标题。用户重命名时清除此字段并保存输入的 `title`。缺少标记的旧终端直接显示
原 `title`,不通过名称模式推断默认标题。布局存储键保持 `goodbuddy.workbar-layout.v1`;
旧版本使用严格 Schema,不能读取带新增字段的布局,本变更不提供降级兼容。
## 11. 生命周期与关闭语义
| 用户或系统动作 | Tab | Shell Session |
@@ -548,7 +562,7 @@ Schema、假数据终端或不可用入口的中间产品。
- [x] 连续选择浏览器会创建页面、导航状态和元素引用相互独立的浏览器 Tab。
- [x] 连续选择终端会创建多个互相独立的终端 Tab。
- [ ] 关闭可关闭实例、切换和重排实例后,“+”仍保持可用。
- [ ] Tab 溢出时单行横向滚动,不隐藏“+”或把 Tab 换成下拉框。
- [ ] Tab 溢出时单行横向滚动,两端箭头固定,“+”跟随最后一个 Tab,滚到末尾可见,不把 Tab 换成下拉框。
- [x] 关闭可关闭的活动 Tab 后激活正确的相邻 Tab 并恢复焦点。
- [x] 当前 Tab 的背景、边框、顶部指示和字重清晰可辨;关闭按钮仅在悬停、焦点或无悬停
设备上出现,且不引发布局位移。
@@ -0,0 +1,37 @@
# 会话媒体生成
实现状态:已完成。纳入 Desktop 0.13.5 / Agent 0.13.0 候选,尚未发布。日期:2026-09-15。实际验证范围单独见[进度记录](./progress.md)。
用户添加图片模型后,可以开启“允许 AI 在会话中调用”。支持工具调用的聊天模型通过 GoodBuddy 生成图片、编辑上传图片和历史成果。切换聊天模型或 Agent Runtime 后仍可发起生成和编辑;已提交操作固定生成模型,结果保存到原会话。
本目录负责普通 LLM 调用媒体模型的产品行为、调用入口和生命周期。现有直连图片协议及连续修改仍由[图片生成](../image-generation/README.md)维护;本设计不表示现有功能已改变。
## 范围
图片生成与编辑覆盖直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness,以及 GoodBuddy Agent 远程路径上的适用 Runtime。复用已有图片协议和各 Runtime 的内部工具或 MCP 注入,由 GoodBuddy 自动管理;用户无需创建 Skill、搭建 MCP server 或重复配置凭据。
使用稳定工具 `generate_image` 完成生成与编辑。全局默认和本次指定模型足够,不新增会话级选择器或独立生成表单。无工具能力的聊天模型明确提示限制,可使用已有直连图片工作流。视频不在本设计范围;完整需求以[产品需求](./prd.md)为准。
图片能力在现有内置 MCP 列表持续可见,Runtime 分配以只读复选框展示,由图片模型的会话调用设置自动管理;没有开启的模型时全部取消勾选,仍保留该行和模型设置入口。分配规则见[行为规则](./logic-design.md),展示与说明见[界面设计](./ui-design.md)。
## 文档导航
| 文档 | 权威职责 |
| --- | --- |
| [产品需求](./prd.md) | 范围、需求编号与产品验收 |
| [用户场景](./user-stories.md) | 用户操作及场景验收 |
| [行为规则](./logic-design.md) | 选择优先级、切换规则、状态和失败处理 |
| [界面设计](./ui-design.md) | 配置入口、会话操作、结果卡片和反馈 |
| [技术设计](./technical-design.md) | 工具注入、Main 服务、存储与 Runtime 接入 |
| [进度记录](./progress.md) | 当前代码事实、剩余工作与验证证据 |
## 术语与依赖
聊天模型负责理解请求、调用工具和组织回答;生成模型负责图片生成与编辑;Agent Runtime 承载聊天模型及工具执行。工具提供结构化调用入口,使用说明随工具提供,不要求用户管理 Skill。
“生成操作”指一次图片生成或编辑及其结果记录,不自动创建任务中心的 Task,也不引入新的通用 Job 调度系统。领域边界遵循 [Task 与 Job 模型](../task-and-job/task-and-job-model.md)。
- [模型连接](../model-connections/README.md):连接、凭据、协议及请求定制。
- [直连文本 Agent](../direct-model-agent/README.md):文本工具循环与上下文。
- [远程主机](../remote-host/README.md):远程 Runtime 与桌面通信。
- [统一界面规则](../../../UI-DESIGN.md):控件、反馈和可访问性。
@@ -0,0 +1,78 @@
# 行为规则
状态:设计草案。此文是模型选择、切换与生成状态的权威定义,范围见[产品需求](./prd.md)。L-1 至 L-5 编号保留,以下规则替代原视频、会话级选择及通用查询恢复规则。
## 能力与调用时点
L-1(FR-1、FR-3、FR-7、FR-9;US-01、US-08、US-11、US-15 至 US-19):自动生成与编辑要求图片连接允许会话调用、协议支持所需操作、聊天模型支持工具调用且当前请求处于 Execute。直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness 及 GoodBuddy Agent 远程适用 Runtime 均须接入同一图片能力,内部工具或 MCP 注入由 GoodBuddy 管理。
允许调用的模型目录取自已保存且“允许 AI 在会话中调用”开启的图片模型配置。目录非空时,图片生成与编辑能力的全部支持 Runtime 分配自动勾选;目录为空时全部取消勾选。模型配置是唯一事实来源,不保存独立可变的能力启用或分配设置,用户不能操作分配复选框。
| 配置或运行变化 | 分配与目录结果 |
| --- | --- |
| 首次开启或重新开启一个图片模型 | 自动勾选全部支持 Runtime,目录包含该模型 |
| 关闭或删除一个模型,仍有其他开启模型 | 分配保持勾选,从目录移除该模型 |
| 最后一个开启模型被关闭或删除,或从未开启任何模型 | 全部分配取消勾选,目录为空;能力行、说明和模型设置入口仍可见 |
| Runtime 离线、聊天模型无工具能力、Ask、配置或执行错误 | 保持按模型开关推导的勾选和目录,另行显示调用限制或错误 |
配置保存成功后同步刷新分配展示和允许模型目录;保存失败时保留最后已保存配置推导的状态。分配不表示运行就绪,也不保证每个图片模型支持编辑;协议及实际提交校验仍适用。
请求开始和聊天模型、Runtime、配置切换后刷新可用能力;进程复用也须刷新。运行中的聊天回合保留原工具协议,Main 在实际提交前检查最新配置及工作模式,已关闭的连接不能接受新操作。无工具模型说明限制并提示已有直连图片工作流;Ask 不提交生成或编辑,不自动切换模式。Runtime 接入尚未完成属于实施缺口,不能作为减少支持范围的规则。
## 模型和素材选择
L-2(FR-2、FR-5;US-02、US-03、US-05、US-07、US-14、US-15):先确定新建或编辑,再选择图片模型。
| 操作 | 选择优先级,由高到低 |
| --- | --- |
| 新建图片 | 用户本次明确指定 > 全局默认 |
| 编辑历史成果 | 用户本次明确指定 > 原成果模型 > 全局默认 |
| 编辑上传图片(无来源模型) | 用户本次明确指定 > 全局默认 |
只有较高优先级未设置时才查看下一项。选定模型被删除、关闭或不支持编辑时提示改选,不静默跳过。未设默认时提示本次指定或去设置默认后重新发送;名称歧义先澄清,LLM 不得编造模型 ID。不持久化会话级生成模型选择。
显式附件或点选成果优先。“刚才那张”等指代仅在对象唯一时解析;多义指代先澄清。新建不相关图片不附带旧图。编辑能力不足或原图缺失时保留输入并提示,不把编辑自动降为文生图;用户可以明确要求按文字重新生成。已有直连图片路径的行为仍由[图片技术设计](../image-generation/technical-design.md)定义。
## 切换规则
L-3(FR-4;US-03 至 US-07、US-11、US-15):Main 接受提交时固定原会话、触发消息、实际图片模型、当次连接参数、生成参数及素材引用。未提交请求在实际提交时解析默认值并校验显式选择;提交后不重选模型。图片请求由 Main 持有,独立于聊天 Runtime 生命周期。
| 变化 | 未提交操作 | 已提交操作与成果 |
| --- | --- | --- |
| 切换聊天模型 | 新回合使用新模型及可用工具,无工具能力时说明限制 | 图片模型不变,操作独立继续 |
| 切换本地或远程 Runtime | 新回合注入图片工具,可发起生成和编辑 | 不依赖旧进程,不向新进程补发旧工具响应,不重放生成 |
| 更改全局默认或本次指定 | 新操作按 L-2 解析 | 保持提交时模型 |
| 关闭或删除连接 | 拒绝新操作并提示改选 | 已发请求使用当次配置尽力完成 |
| 更改地址、凭据或参数 | 新操作使用新配置 | 当前操作不混用新端点或凭据 |
| 离开或重开会话 | 使用当前请求及全局配置 | 只更新原会话,历史成果仍可引用 |
| 远程断连 | 未被 Main 接收的请求不能声称已提交 | 桌面仍运行时由 Main 继续管理已接收操作 |
“停止回复”只结束文字回合;“取消生成”单独处理图片操作。切到 Ask 不撤销先前 Execute 已提交的操作,也不允许新增生成。运行时切换不自动唤起新的 LLM 请求。
## 操作状态
L-4(FR-6、FR-8;US-04、US-09、US-13):复用现有操作与成果反馈,状态只表达实际知道的事实。没有上游百分比时显示状态及等待时间,不虚构进度。
| 状态表达 | 条件与处理 |
| --- | --- |
| 进行中 | 正在发送或等待响应;仅有上游确认时才说明已接受生成 |
| 保存中 | 已取得图片,Main 正在校验并保存 |
| 已完成 | 图片及会话引用已保存,可重新读取 |
| 取消中 | 已请求终止,尚无明确结果 |
| 已停止等待/已取消 | 本地中断只说明“已停止等待,上游可能继续生成”;上游确认停止才说明“生成已取消” |
| 失败 | 有明确的请求或保存失败原因,保留输入和错误上下文 |
| 结果未确认 | 响应丢失、超时或重启后无法确认结果,不当作上游失败或取消 |
同一操作的通知更新同一记录。取消后仍取得图片时,在原卡片保存并说明“取消未生效,已生成”,不恢复旧工具回合。超时不等于上游取消,不承诺退款。
人工重新生成创建新操作,沿用原模型与输入并重新校验;改选模型时在新卡片显示实际选择。失败、结果未确认及保存失败均不自动重发收费请求,不设置独立保存重试入口。应用退出后不承诺继续本地执行,重启时未完成记录显示结果未确认,不建立跨重启查询恢复或通用恢复框架。
## 历史成果
L-5(FR-5、FR-8;US-07、US-12、US-14、US-15):历史上下文提供成果引用、提示词及实际图片模型信息。只有具备视觉输入能力的聊天模型才接收适用的图片内容;无视觉模型可根据用户文字将原图引用交给编辑工具,但不能声称看过图片。上传图片和历史成果均使用已有可持久化素材引用。
删除会话沿用已有确认流程,说明活动生成的取消及无法保证上游停止的情况;删除后的晚到结果不能重建会话。
## 完整性与实施验证
规则覆盖配置、新建、上传图与历史图编辑、模型与 Runtime 切换、Ask、无工具或无视觉模型、取消、失败、重启和会话归属。没有待定产品范围;各 Runtime 工具注入、远程往返及取消传播的具体接法需在实施时核验,按[技术设计](./technical-design.md)完成真实路径验收。视频、跨设备同步和应用退出后的本地执行不在范围内。
@@ -0,0 +1,37 @@
# 产品需求
状态:设计草案。本文描述目标行为,不作为现有能力说明。
## 用户问题与范围
用户希望在文字会话中直接提出“画一张海报”“把上传图片的背景改成蓝色”“继续修改刚才的图片”,由聊天助手调用已配置的图片模型。切换聊天模型或 Runtime 后,应能继续生成、引用和编辑图片。
范围包括图片模型调用开关、全局默认与本次指定、文生图、已有协议支持的上传图片和历史图片编辑、成果保存、取消及状态反馈。支持直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness,以及 GoodBuddy Agent 远程路径上的适用 Runtime;各路径的生成、编辑和切换均属于验收范围。
GoodBuddy 自动管理内部工具或 MCP 注入,复用模型连接与凭据。用户无需创建 Skill、搭建 MCP server 或填写第二套凭据。自动调用要求聊天模型支持工具调用;无工具能力时明确说明限制,用户可使用已有直连图片工作流。
不包含视频生成、模型训练、自动采购服务、通用文本指令执行器、批量渲染平台或应用退出后继续本地执行的承诺。不新增图片协议、会话级生成模型选择器、独立生成表单或通用恢复框架,也不安排视频交付计划。
## 功能需求
保留 FR-1 至 FR-9 编号。原需求中的视频部分移出范围,FR-2 的会话级选择由全局默认和本次指定替代,FR-7 的新增显式入口由已有直连图片工作流替代;下表为当前定义。
| 编号 | 要求 | 产品验收 |
| --- | --- | --- |
| FR-1 | 图片模型提供“允许 AI 在会话中调用”开关;新增和已有连接默认关闭,由用户主动开启;模型配置是图片能力启用与 Runtime 分配的唯一事实来源 | 至少一个模型开启时自动勾选全部支持 Runtime;全部关闭或删除时全部取消勾选;关闭或删除其中一个后若仍有开启模型,保持勾选并更新允许调用的模型目录;被关闭模型拒绝新操作,历史成果保留 |
| FR-2 | 支持全局默认和本次明确指定;编辑可沿用原成果模型 | 按[选择规则](./logic-design.md)解析并显示实际模型;失效或有歧义时提示,不静默换服务 |
| FR-3 | 在支持工具调用的聊天模型下,以自然语言生成和编辑图片 | 上传图片、历史图片及文生图均可经真实服务处理,保存后在原会话展示,活动记录可见 |
| FR-4 | 支持聊天模型、Runtime 及生成模型切换 | 切换后可发起新的生成和编辑;已提交操作固定原模型和原会话,不重复提交 |
| FR-5 | 支持上传素材、历史成果引用与连续编辑 | 重开会话及切换 Runtime 后可引用已保存原图;缺失素材或能力不足时保留输入并提示处理方式 |
| FR-6 | 生成操作独立显示状态,支持取消及人工重新生成 | 离开会话不取消;保存成功才完成;不虚构进度,不自动重试收费请求;重启后未完成操作显示结果未确认 |
| FR-7 | 遵循工作模式及模型能力边界 | Ask 不提交生成或编辑;无工具模型说明限制,可使用已有直连图片工作流,无需新增表单 |
| FR-8 | 图片作为会话成果保存 | 可预览、导出和重新打开,来源模型可追溯;无视觉聊天模型只接收文字事实及引用,不编造观察 |
| FR-9 | 本地及 GoodBuddy Agent 远程适用 Runtime 均支持图片工具;GoodBuddy 管理的图片生成与编辑能力及 Runtime 分配持续可见 | 分别完成真实生成、编辑、切换验收;分配复选框只读,不可手动切换;无开启模型时仍显示该行、未勾选状态、自动管理说明及模型设置入口;无独立能力启停、分配、移除或凭据编辑设置,用户无需维护 Skill 或 MCP server |
分配表示模型配置允许的调用范围。聊天模型不支持工具、Ask、Runtime 离线、配置错误或执行失败均单独说明,不取消配置分配的勾选;不保存可独立修改的能力启用或 Runtime 分配状态。具体推导与刷新规则由 L-1 定义。
## 产品验收
以[用户场景](./user-stories.md)验收。各 Runtime 分别记录真实工具往返、生成、上传图编辑、历史图编辑及切换结果,不能用单一路径成功代替其他路径。配置检查或工具注入成功不等于生成成功,成果必须可在会话中重新打开。
Runtime 接入细节和真实 Host 验证属于实施工作,未验证时如实记录未完成,不据此缩减 FR-9 的支持范围。
@@ -0,0 +1,83 @@
# 进度记录
## 2026-09-15
### 真实图片请求与构建修复
已使用现有 `gpt-image2` 连接(模型 `sub-gpt-image-2`)经生产 `ImageGenerationService` 发起两次真实图片请求:生成白底蓝色圆形,再上传该成果将圆形改成红色。两次均 HTTP 200,保存并重新解码成功,导出图片目视检查符合要求。请求数为 2,自动重试为 0,用户保存的配置未修改。请求尺寸为 1024×1024,供应商实际返回两张 1254×1254 PNG;未对结果重采样。证据保存在本机临时目录 `image-bounded-oiOPRi/`。此项验证 Main 图片服务与真实供应商,尚不替代真实聊天模型经各 Runtime 自行调用工具及完整界面切换验收。
用户报告 Renderer 生产构建失败,定位到 `remote-image-tool-contracts.ts` 的 `node:crypto` 被共享契约间接引入浏览器。已将 MCP 名称哈希移至仅 Main 与 Agent 引用的 `remote-image-tool-node.ts`,共享消息编解码使用标准 `TextEncoder`/`TextDecoder`,无新增依赖或 polyfill。修复后 `npm run build`(含类型检查、Main、Preload、Renderer 构建)、`npm run lint` 通过,4 个相关测试文件共 89 项通过。此前仅运行类型检查和测试未发现这一构建问题,不能作为生产构建通过证据。
### 实施与集成复验
工作区已有模型会话调用开关、默认图片模型、配置派生的四种 Runtime 只读分配、Main 图片服务、上传与历史成果引用、操作状态及取消/重新生成 IPC。本地 Model、OpenCode、Continue、DeepSeek Harness 和受管远程 OpenCode 均已接入同一服务。操作复用消息 metadata 和成果存储,未新增任务表或独立分配设置。
本轮修复远程保留会话在图片目录清空后未刷新 MCP,以及 Continue 准备自定义 MCP 失败时未释放本次图片工具令牌的问题。图片卡片更新保持原顺序;取消提示区分请求取消、停止等待和取消未生效后的结果保存。失败卡片提供模型设置与素材重选入口,无工具能力及 Ask 有相应提示。长名称和提示词自动换行,窄窗口及 200% 缩放下恢复按钮可换行。
验证证据如下;此前小节中的超时和未验证记录仅代表各次检查时点:
| 验证 | 结果与边界 |
| --- | --- |
| `npm test` | 364 个文件通过、9 个跳过;4,267 项通过、67 项跳过。后续恢复入口及样式修改另有 269 项定向测试通过,未再运行全量测试 |
| `npm run typecheck`、`npm run lint` | 最后一次 Renderer 修改后通过 |
| 真实 Linux Host | 最终 Agent 构建通过固定主机身份连接,使用实际远程 OpenCode;Ask、Execute 各 1 次真实文本请求均 HTTP 200 并完成,真实图片请求 0 次 |
| Host 图片工具往返 | 最终运行含 7 次受控图片请求、15 次受控文本请求;生成、上传和历史图编辑、目录刷新、Ask 隐藏、并发会话归属、停止回复和断连后 Main 继续保存均通过。图片服务返回固定内联 PNG,不构成真实生图证明 |
| Electron 界面 | 当前生产组件、样式、字体及双语文案,使用隔离 fixture API;初轮 94 项检查,恢复入口完成后 40 项检查通过。覆盖明暗主题、四种窗口尺寸、键盘导航及 200% 缩放;没有修改用户配置 |
最终 Host 运行退出码为 0,残留自有进程为 0,隔离目录和上传文件已清理。此前受控复验另有 7 次图片请求和 15 次文本请求,因此本轮合计真实文本请求 2 次、真实图片请求 0 次、受控图片请求 14 次、受控文本请求 30 次。此前历史测试记录不计入此合计。
本机临时证据位于 `C:/Users/jiang/AppData/Local/Temp/opencode/`:`image-review-full-tests-20260915.stdout.log`、`image-host-final-real-20260915.stdout.log`、`image-host-review-20260915.stdout.log` 及 `image-visual-0915/`。这些是开发检查产物,不随产品发布。
当时尚未完成真实图片供应商生成与编辑;后续验证见上方记录。完整 Electron Main/Preload 路径中的自然语言触发及本地/远程双向切换验收仍待完成,FR-3、FR-9 尚未完成全部产品验收。
隔离的真实图片验证脚本离线校验通过后,已按用户要求执行上述两次真实请求。该连接的会话调用仅在验证内存中启用,不修改保存配置。此前 `deai-writing` 文档扫描阻断项为 0,25 个候选经语境复核保留产品边界、状态限制和历史证据;`git diff --check` 通过。
### Renderer 与设置复查
本轮在已有未提交实现上修复图片操作卡片:状态事件原位更新,保留同一消息中多次生成的卡片顺序;取消提示区分正在请求取消、已停止等待和取消未生效但图片已保存。按钮通过 `aria-describedby` 关联就地操作错误,中英文提示同步更新。补充组件回归,并在现有 App 测试中检查离开原会话、结果晚到、返回及切换 Runtime 后继续修改时的卡片顺序。
已检查模型设置、RuntimeSettingsStore、CapabilityService 和共享设置契约。现有实现从已保存模型开关派生四种 Runtime 的只读分配,不持久化独立分配;关闭或删除默认图片模型后保留失效默认。相关测试本轮通过,未改动这些生产契约。共享工具目录测试原先仅排除浏览器工具,现补充图片受管能力例外,确认 `generate_image` 保持写操作且不重复进入直连模型设置分组。
验证记录:
- `npx vitest run src/renderer/src/ImageOperationStatus.test.tsx src/renderer/src/App.test.tsx src/renderer/src/SettingsPanel.test.tsx src/main/runtime-settings-store.test.ts src/main/capabilities/capability-service.test.ts`:5 个文件、451 项通过。
- 修正目录测试后,`npx vitest run src/shared/builtin-model-tools.test.ts src/renderer/src/ImageOperationStatus.test.tsx`:2 个文件、5 项通过。
- `npm run typecheck`、`npm run lint` 通过。
- `npm test` 在 120 秒上限被终止;期间报告的共享工具目录测试失败已单独修正并复验,未取得全量测试通过结论。
- 未调用真实文本或图片服务,请求数均为 0;未执行 Electron 视觉验收或真实 Host 验证。本轮没有修改 `src/main/agent` 或 `src/agent-daemon`。各 Runtime 的真实生成、编辑、取消及切换验收仍待完成。
### 此前文档修订记录
本日补充:已按最新明确接受的要求修订 FR-1、FR-9、L-1、界面与技术设计,并新增 US-16 至 US-19。只读分配及模型配置派生规则见[行为规则](./logic-design.md),受管行、说明与无障碍要求见[界面设计](./ui-design.md)。已读取根目录与 features 的 AGENTS、根 UI-DESIGN.md 及本目录七份现有文档,保留此前未提交修订;本次未修改目录外文件或应用代码,相关行为仍待实现与产品验收。
本日更正:此前将其他 Runtime 自动调用排除在交付范围之外的记录不符合用户要求,现由本条替代。七份文档已统一为图片生成和编辑,支持范围及验收以[产品需求](./prd.md)和[用户场景](./user-stories.md)为准,不再保留分期规则。视频移出范围,US-10 保留编号并明确标注;其他被替代要求已记录编号变更。
本次只修改本目录设计文档,未检查或改变应用实现。2026-09-14 记录仅为当日历史证据,不表示已重新验证。图片工具、Main 请求生命周期、各本地及 GoodBuddy Agent 远程适用 Runtime 的生成、编辑和切换均仍需实施与实际验证;具体注入及桥接核验属于实施工作,不缩减支持范围。
本次文档验证:7 份文档的 38 个相对 Markdown 链接均可解析;一致性审阅覆盖模型配置唯一来源、只读分配、空目录保留行、运行状态独立及完整 Runtime 范围,未发现分期或缩减支持范围的现行规则。`deai-writing` 扫描阻断项为 0,25 个复核候选涉及设计状态、职责与必要行为限制,逐条按语境保留,并通读检查结构及前后规则。`git diff --check -- docs/features/conversation-media-generation` 通过;本次七份文件均已被 Git 跟踪,检查覆盖其工作区差异。该命令不覆盖未跟踪文件,不能沿用此前“目录未跟踪”的结论。未运行应用测试或真实生成请求,未声称功能完成或重新验证此前代码。
## 2026-09-14(历史记录)
以下保留当日检查与原设计记录,其中视频、保存重试等旧范围已被 2026-09-15 更正取代,不作为当前实施要求。
当前阶段:设计草案已落盘,功能未实现。本次只新增设计文档与相关入口链接。
代码与文档检查确认以下基础能力:
| 事实 | 证据 |
| --- | --- |
| 已有直连图片生成、编辑及历史成果引用 | [图片技术设计](../image-generation/technical-design.md)、`src/main/agent/model-runtime.ts` |
| 模型连接及 Main 凭据存储已存在 | `src/shared/contracts.ts`、`src/main/runtime-settings-store.ts` |
| 已有 Skill 发现、Runtime 分配及工具分发 | `src/main/capabilities/capability-service.ts`、`src/main/agent/model-tool-provider.ts` |
| 会话、消息 metadata 和成果已有 SQLite 存储 | `src/main/assistant/assistant-database.ts`、`src/shared/assistant-contracts.ts` |
| 生图尚未成为普通 LLM 的内置生成工具 | 检查 `src/shared/builtin-model-tools.ts` 及工具分发路径 |
| 本次检查未发现第一方视频生成协议实现 | 检查模型能力契约、Runtime 及现有功能文档;不据此推断外部 MCP 的能力 |
剩余工作对应[技术设计实施顺序](./technical-design.md):模型调用设置与选择、图片服务及会话入口、各 Runtime 接入、视频真实服务与成果交付均待实施。视频协议选择和远程工具桥接验证待完成。
本次未修改应用代码,未运行应用测试或真实生成请求。文档验证记录如下:
- 相对 Markdown 链接检查:7 份新文档及图片功能入口共 33 个链接,失效链接为 0。
- `deai-writing` 扫描:阻断项为 0;人工复核保留设计状态、能力边界和禁止行为等必要限定,以及真实并列的验收项。
- 独立一致性审阅发现“重新生成”与“重试保存”的操作身份表述不一致,已在 L-4 和 US-09 中统一:前者创建新操作,后者更新原操作。
- `git diff --check` 通过。上述检查只验证文档,不构成功能验收。
@@ -0,0 +1,86 @@
# 技术设计
状态:工作区实现已接通,真实图片服务与完整产品验收未完成,证据见[进度记录](./progress.md)。内部契约实现 [FR-1 至 FR-9](./prd.md)及[行为规则 L-1 至 L-5](./logic-design.md),不作为外部公开 API。
## 调用结构
```mermaid
flowchart TD
Direct[直连文本 Model Runtime] --> Tool[generate_image]
Local[本地 OpenCode / Continue / DeepSeek Harness] --> Injection[GoodBuddy 管理的内部 MCP 注入]
Remote[GoodBuddy Agent 远程适用 Runtime] --> Bridge[现有 Agent 通信与工具桥接]
Bridge --> Injection
Injection --> Tool
Tool --> Main[Main 图片服务]
Main --> Profile[现有模型连接与凭据]
Main --> Adapter[现有图片生成与编辑协议]
Main --> Artifact[原会话操作记录与图片成果]
```
Main 持有图片请求、取消句柄及结果保存职责,生命周期独立于聊天 Runtime controller。切换或释放聊天进程不释放已提交图片操作。Renderer 的取消等操作经明确 preload/IPC 方法进入 Main,沿用共享契约和发送方校验。凭据只在 Main 解密,不写入工具说明、参数、输出或远程 Runtime 配置。
`ImageGenerationService` 复用 `ModelAgentRuntime.generateImage` 的图片请求和校验,在 Main 保存操作及成果。工具路径按 L-2 明确区分新建和编辑,不自动将缺少素材或不支持的编辑降为文生图;已有直连图片路径保持原上下文规则。
## 工具与注入
稳定图片工具 `generate_image` 同时负责生成和编辑。当前输入为 `prompt`、可选 `modelProfileId`、`intent`(`create` 或 `edit`)、`sourceArtifactIds` 和可选 `quality`;类型及编辑素材约束由 `src/shared/image-generation-contracts.ts` 校验。每次请求沿用已有协议的单图生成和尺寸行为,没有新增数量或尺寸参数。原会话、触发消息和工具调用关联由运行上下文绑定,LLM 不能自由指定结果写入位置或任意文件路径。
每个新回合提供可调用图片模型的脱敏 ID、名称及生成、编辑能力,模型切换、配置变化或进程复用时刷新。Main 提交前重新校验工作模式、连接开关、所需能力及素材。优先由工具描述承载模型选择、图片引用和结果使用说明,不把 Skill 管理作为依赖。
直连文本 Model Runtime 使用内置工具契约及分发;外部 Runtime 复用内部 MCP 注入,由 GoodBuddy 自动注册、配置和管理生命周期,远程路径经现有 Agent 工具桥接调用同一服务。内置 MCP 列表中的图片行只展示这项受管能力,不代表直连 Model 也通过 MCP 调用。无需用户创建 Skill、另建 MCP server 或复制凭据,不按模型连接复制工具或 Skill。
Main 从已保存图片 profile 的会话调用开关推导允许模型目录,并以目录是否非空推导全部支持 Runtime 的分配。Renderer 只读取派生分配;该受管行不接入通用 MCP 的启停、分配、删除或凭据编辑写入接口。模型配置保存后刷新展示及注入目录,进程复用和下一回合重新读取;运行中的回合及已提交操作继续遵循 L-1、L-3。配置错误、Runtime 在线状态、聊天模型工具能力与执行结果独立于派生分配,不因这些状态移除允许目录中的模型或取消勾选。
不预建能力发现、查询或取消工具目录。只有实际 Runtime 协议要求独立发现、查询或取消交互,且现有工具描述、响应及 UI 操作不能满足时,才增加所需的最小适配,并记录协议依据;这些适配仍调用同一 Main 图片服务,不形成新的媒体平台。取消默认从会话卡片进入 Main。
工具等待遵循现有超时约束,返回操作 ID、实际模型、真实状态及完成后的成果 ID,不把 Base64 塞入工具历史。原回合仍有效时通过合法工具响应返回结果;回合结束或 Runtime 切换后,Main 继续写原会话,下一回合读取操作摘要及成果引用,不补发旧响应、不自动唤起 LLM 或重发图片请求。
## 配置与持久化
| 对象 | 最小数据与职责 |
| --- | --- |
| 图片模型连接 | 复用 settings/profile,增加会话调用开关;现有连接缺少开关时解释为关闭 |
| 图片能力与 Runtime 分配 | 从图片模型配置派生,只读展示;不新增独立可变的 capability enabled、assignment 设置或持久化副本 |
| 全局偏好 | 默认图片 profile ID,复用应用设置;本次指定只属于请求,不新增会话级选择 |
| 生成操作 | 优先使用现有消息 metadata,保存操作 ID、原会话/消息、工具关联、状态、实际模型 ID 与名称、提示词、有效参数、素材引用、时间、错误及成果 ID |
| 素材与成果 | 复用附件和 artifact 持久化,保存上传图片、生成图片及编辑来源关系,不能只存于 Runtime 内存 |
活动请求在 Main 内存持有提交时连接参数,持久记录不复制凭据。应用重启后未完成操作显示结果未确认,不建立跨重启查询恢复、凭据历史、通用恢复日志或自动重试。取消和晚到结果按 L-4 更新同一记录;人工重新生成使用新操作 ID。
不新增独立任务表、视频字段或通用调度器,操作不自动成为 Task/Job。若实施涉及已发布数据,按[迁移规范](../../development/database-migrations.md)处理实际兼容需求,不预建迁移或恢复机制。
Main 仅在图片和会话引用持久化成功后发布完成事件。素材必须是当前会话可访问的附件或成果,沿用已有项目和会话边界;删除会话后的晚到结果不重建会话。
会话刷新共用保留对象引用的图片状态合并:无图片或图片状态未变化时,不重建消息并触发
整段历史重新保存。未打开历史的读取与缓存释放遵循
[会话列表读取与前端保留](../assistant-workbar/execution-history-storage.md#会话列表读取与前端保留),
不改变图片操作的 Main 所有权、成果关联或时间优先级。
## 图片协议
沿用[图片技术设计](../image-generation/technical-design.md)的已有生成、编辑协议及素材限制。接收并校验有界内联图片,不下载 Provider 返回的图片 URL。沿用现有成果存储限制,实施时核对编码开销与超限反馈,不以配置检查成功代替真实生成验收。视频协议、下载、播放及交付计划均不属于本设计。
## Runtime 接入
| 路径 | 设计接入点 | 必须完成的验证 |
| --- | --- | --- |
| 直连文本 Model Runtime | 现有内置工具契约及工具分发转入 Main 图片服务 | 自然语言生成、上传图编辑、历史图编辑及切换 |
| 本地 OpenCode | 现有内部 MCP 注入转入同一服务 | 实际工具往返、能力刷新、生成编辑及切换 |
| 本地 Continue | 现有内部 MCP 注入转入同一服务 | 实际工具往返、能力刷新、生成编辑及切换 |
| 本地 DeepSeek Harness | 现有内部 MCP 注入转入同一服务 | 实际工具往返、能力刷新、生成编辑及切换 |
| GoodBuddy Agent 远程适用 Runtime | 复用桌面与 Agent 通信及工具桥接,将请求绑定原会话,由 Main 调用图片模型 | 逐一验证实际支持的远程 Runtime,覆盖真实 Host 工具往返、生成编辑、本地与远程双向切换、断连及成果归属 |
| 无工具能力的聊天模型 | 说明自动调用限制,可使用已有直连图片工作流 | 不解析普通文字伪装工具调用,不新增表单;Ask 不提交 |
本地 OpenCode、Continue 和 DeepSeek Harness 经内部 MCP gateway 注入;直连 Model 使用内置工具分发。远程通过 `main-image-tool-session.ts`、Agent 协议和 `image-tool-mcp.ts` 回到同一 Main 服务。当前受管远程 Runtime 包括 OpenCode 和 Continue;Continue 模型桥的会话 MCP 交付及 Ask 边界见[远程 Runtime](../remote-host/technical-design.md#runtime)。保留会话在目录清空时也刷新 MCP,Continue 准备失败时释放本次分配的令牌。工具发现、无计费测试接收端往返或 Skill 注入成功不能代替真实生成与编辑。
Main 已接收的请求在桌面仍运行时独立于远程连接继续;未确认是否接收的请求不自动重放。聊天回合停止等待与用户取消图片是不同动作,须验证现有取消传播不会误杀 Main 图片请求。
## 实施与验证
实施工作包括配置与选择、Main 图片服务提取、各本地及远程 Runtime 工具适配、成果与状态 UI。它们共同完成本图片功能,不按 Runtime 划分交付范围。
针对 L-1 至 L-5 验证选择优先级、失效配置、上传与历史素材、无视觉模型、Ask、会话归属、重复通知、取消及晚到结果。检查切换聊天模型、图片默认模型、本地与远程 Runtime 后的新生成和编辑,以及旧操作不重复提交。回归已有直连图片工作流。
按 US-16 至 US-19 验证从零到一个开启模型、多个中关闭或删除一个、最后一个关闭或删除、重新开启,以及配置错误和 Runtime 离线时的派生分配、目录刷新、行可见性与只读交互。重开设置和应用后均从模型配置推导,不恢复独立分配值;辅助技术可读取标签、勾选状态和说明,键盘可到达模型设置导航。
源代码实现后运行 `npm test`、`npm run typecheck`、`npm run lint`,并逐路径执行[用户场景](./user-stories.md)的真实产品验收,记录模型、Runtime、请求数量和结果。涉及 Agent 的实现按[远程主机验证要求](../remote-host/technical-design.md)在真实测试 Host 验证。实施和集成验证状态由[进度记录](./progress.md)维护。
@@ -0,0 +1,41 @@
# 界面设计
状态:工作区已有实现及 Electron 组件验收,完整应用验收见[进度记录](./progress.md)。遵循[统一界面设计](../../../UI-DESIGN.md),实现 [FR-1 至 FR-9](./prd.md),行为以[行为规则](./logic-design.md)为准。
## 模型配置
在现有图片模型连接表单提供“允许 AI 在会话中调用”。说明文字:“开启后,支持工具调用的聊天助手可使用此模型生成和编辑图片,编辑能力取决于图片协议。结果保存在发起请求的会话中。”复用共享 Switch,提供 `role="switch"`、键盘操作及可见焦点。
开启后提供“设为默认生图模型”,列表显示可调用和默认标识。关闭或删除默认连接时提示默认选择不可用,保留失效选择供改选,不自动指定其他模型。用户无需创建 Skill、搭建 MCP server 或重复填写 API 凭据;内部注入由 GoodBuddy 管理,不作为配置步骤展示。
## 能力与 Runtime 分配
复用设置中心“MCP / 内置 MCP”的列表与分配布局,增加“图片生成与编辑”行,标记“内置 · 自动管理”。这是 GoodBuddy 管理能力的展示入口,直连 Model 通过内部工具调用,外部 Runtime 通过内部 MCP 接入;不显示为用户另行添加的 MCP 或 Skill,也不混入 Runtime 原生能力清单。
该行按 L-1 展示直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness 及 GoodBuddy Agent 远程适用 Runtime 的只读分配复选框。此图片能力采用用户已接受的自动管理规则,不沿用其他内置 MCP 的手动启停、可调分配或 DeepSeek Harness“暂不支持”状态。没有开启模型时仍保留整行及全部未勾选项,不能因目录为空隐藏。
持续显示说明“由图片模型的「允许 AI 在会话中调用」设置自动管理。”,并提供“前往图片模型设置”导航。该行不提供独立启用开关、保存分配、移除、编辑连接或凭据入口。运行就绪、无工具能力、配置或调用错误单独显示,不改变分配勾选。
分配复用共享 Checkbox 的方形视觉和原生语义,以 `checked` 表示派生值并使用 `disabled` 禁止鼠标、标签点击或键盘切换;仅在共享控件已有受支持的只读实现时采用该实现,不给原生 Checkbox 添加无效的 `readonly` 或自行添加 `aria-readonly`。每项保留 Runtime 标签和可访问名称,以 `aria-describedby` 关联自动管理说明。说明放在禁用控件之外,保持可读及辅助技术可访问;导航保持正常 Tab 顺序和可见焦点。窄窗口下分配项换行,保留标签、勾选状态、说明和导航。
## 会话入口
在现有聊天输入区以自然语言发起图片生成或编辑,本次指定模型也通过请求表达。复用上传附件和历史成果引用入口,输入区显示素材缩略图或文件名并允许移除。不新增会话级图片模型选择器、独立生成表单或视频入口。
未设默认时,助手提示本次指定可用模型,或到设置选择默认后重新发送;名称歧义先澄清。没有图片连接时提供现有模型设置入口。模型不支持编辑或素材缺失时保留输入,提示更换模型或重新选择素材。
直连文本、本地各 Runtime 和 GoodBuddy Agent 远程适用 Runtime 使用一致的图片操作与成果反馈。切换后可继续生成及编辑历史图;工具调用异常时显示具体原因,不宣称已提交。接入未完成的开发状态记录在进度文档,不能作为产品支持范围。
不支持工具调用时提示“当前聊天模型无法自动调用图片工具,可使用已有直连图片工作流”。Ask 下提示“生成或编辑图片需要 Execute 模式”,由用户切换,不自动切换。无视觉能力时说明无法直接查看图片,不妨碍按用户文字调用编辑工具。
## 图片卡片
复用现有图片成果展示,卡片就近显示实际模型、操作状态、成果或错误。提示词、适用参数和来源素材放在详情中;不显示密钥、认证头或供应商私有地址。活动记录与卡片使用同一操作标识。
完成后可预览、下载和“继续修改”。继续修改将成果加入输入区,用户填写修改要求;切换 Runtime 或重开会话后仍可使用。离开会话不取消操作,结果只出现在原会话。
进行中提供“取消生成”,与“停止回复”分开。取消、晚到结果和结果未确认的文案遵循 L-4。失败提供适用的“重新生成”“重新选择模型”或“重新选择素材”;重新生成由用户明确触发,并说明会发起新的模型请求。没有独立“重试保存”按钮。
“重新选择模型”进入已有模型连接设置;改变模型后,用户在新请求中明确指定。“重新选择素材”聚焦输入区并打开已有附件选择器,保留草稿和已有附件;空草稿填入原提示词及模型名称。历史成果仍使用“继续修改”加入引用并聚焦输入区。上述操作不自动发送请求,“重新生成”继续使用原操作的模型和输入。
卡片错误不重复弹全局通知;模型设置保存等非上下文反馈使用应用通知。状态使用文字,不能只靠颜色;状态变化通过适度的 `aria-live` 提示,避免逐秒播报等待时间。窄窗口操作可换行,图片不超过消息区宽度,模型长名称截断时保留完整可访问名称。复用的菜单关闭后焦点回到触发控件。
@@ -0,0 +1,34 @@
# 用户场景
状态:设计草案。Given、When、Then 分别为前提、操作和预期结果。范围以[产品需求](./prd.md)为准,行为以[行为规则](./logic-design.md)为准。
## 场景验收
适用的自动调用场景须在直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness 和 GoodBuddy Agent 远程适用 Runtime 分别验证,前提为 Execute 且聊天模型支持工具调用。切换验收同时检查原操作归属和目标 Runtime 的新生成、编辑能力。
| 编号 | 需求 | Given | When | Then |
| --- | --- | --- | --- | --- |
| US-01 | FR-1、FR-3、FR-9 | 已添加图片模型、开启会话调用并设为默认 | 在文字会话中要求画海报 | 自动调用并显示活动,原会话出现图片及实际模型;无需创建 Skill、MCP server 或重复凭据 |
| US-02 | FR-2 | 有两个可调用图片模型,未选默认 | 请求生成图片 | 提示本次指定模型或到设置选择默认后重新发送;名称歧义先澄清,不按列表顺序提交 |
| US-03 | FR-4、FR-5 | 聊天模型 A 已调用图片模型生成图片 | 切换聊天模型 B,要求修改该图 | B 获得成果引用和可用工具,编辑模型按规则选择 |
| US-04 | FR-4、FR-6、FR-9 | 生成操作已提交 | 切换 Runtime 并打开其他会话,再请求生成或编辑 | 原操作继续且只写回原会话;目标 Runtime 可提交新操作,不重放旧调用 |
| US-05 | FR-2、FR-4 | 当前操作使用图片模型 X | 将全局默认改为 Y,再请求新建图片 | 旧操作仍用 X,新操作用 Y;本次明确指定可覆盖默认 |
| US-06 | FR-1、FR-2、FR-4 | 默认或本次指定模型为 X | X 被关闭或删除后请求生成 | 显示 X 不可用并提示改选,历史图仍可打开,不静默换模型 |
| US-07 | FR-5、FR-8 | 已保存图片并重新打开会话或切换 Runtime | 引用原图,指定另一个支持编辑的模型 | 新模型接收原图;素材缺失时要求重新选择,不能假装已编辑 |
| US-08 | FR-7 | 当前聊天模型不支持工具调用,或处于 Ask | 请求生成或编辑 | 无工具能力时说明可使用已有直连图片工作流;Ask 下不提交并提示需要 Execute,不自动切换模式 |
| US-09 | FR-6 | 操作正在生成或保存,或已经失败 | 取消,或明确要求重新生成 | 如实显示取消结果;人工重新生成创建新操作,不自动重复收费请求,不提供独立保存重试入口 |
| US-11 | FR-4、FR-9 | 使用 GoodBuddy Agent 远程适用 Runtime | 生成及编辑图片,切换本地后继续编辑,再切回远程生成 | 工具往返可用,成果可继续引用;活动归原会话,凭据留在 Main;远程断连不取消 Main 已接收的操作 |
| US-12 | FR-5、FR-7、FR-8 | 聊天模型无视觉能力但支持工具 | 请求分析图片或根据文字修改图片 | 说明无法直接查看图片;可传原图给编辑工具,不编造视觉观察 |
| US-13 | FR-6 | 图片操作尚未取得可保存结果 | 退出并重开应用 | 未完成记录显示结果未确认,不承诺后台继续,不自动查询恢复或重新提交 |
| US-14 | FR-5 | 会话有多张图片 | 要求“修改第二张”或“新画一张无关图片” | 前者引用指定成果,后者不附带旧图;指代不清先澄清 |
| US-15 | FR-3、FR-5、FR-9 | 上传图片并选择支持编辑的图片模型 | 要求修改背景,再切换 Runtime 继续修改成果 | 上传原图及后续成果被正确引用,编辑结果保存在各自发起会话;不降为纯文生图 |
| US-16 | FR-1、FR-9 | 尚无开启会话调用的图片模型 | 查看内置 MCP 列表,再开启一个图片模型并保存 | 图片生成与编辑行一直可见且标记“内置 · 自动管理”;全部支持 Runtime 从未勾选变为勾选,目录包含该模型;重开设置或应用仍按模型配置显示 |
| US-17 | FR-1、FR-9 | 至少两个图片模型开启会话调用 | 关闭或删除其中一个并保存 | 全部支持 Runtime 保持勾选,允许目录移除该模型、保留其他开启模型;新操作不能调用已关闭或删除模型 |
| US-18 | FR-1、FR-9 | 仅剩一个开启会话调用的图片模型 | 关闭或删除该模型,再重新开启或添加并开启一个模型 | 目录为空时全部取消勾选,整行和说明、模型设置导航仍可见;再次开启后自动恢复全部勾选,无需另行分配 |
| US-19 | FR-1、FR-7、FR-9 | 至少一个图片模型开启会话调用 | 尝试用鼠标或键盘切换分配,或切换到无工具聊天模型、Ask、离线 Runtime,或遇到配置或执行错误 | 分配不可手动修改,运行限制与错误不取消勾选;无独立启用、分配保存、移除或凭据编辑操作;自动管理说明持续可读,辅助技术可识别勾选和禁用状态,键盘可通过“前往图片模型设置”导航修改源配置 |
## 编号变更
US-10(图生视频)移出本图片设计范围,编号保留,不安排实施。US-02、US-05、US-06 的会话选择预期由全局默认和本次指定替代;US-08 使用已有直连图片工作流;US-09 取消独立保存重试要求;US-13 的视频查询恢复由图片中断状态说明替代。其余编号保持,US-15 补充上传图片编辑的显式验收。
US-16 至 US-19 补充自动管理分配验收,设置展示不依赖 Execute 或当前聊天模型支持工具;自动生成与编辑仍遵循本节的调用前提。
@@ -1,11 +1,25 @@
# 会话监督
# 监督者与会话监督
会话监督定义对长会话、工具执行和异常状态的观察、判断与介入边界。
监督者统一工作回顾、会话监督与故事线图谱,以智能心跳作为定时唤醒机制。现有智能心跳页面承载监督者工作回顾入口,手动回顾、定时结果投影、来源原文、实体/关系确认、继续讨论、知识库预览提交和侧栏反馈已经接入。历史回顾定位到对应结果图谱;侧栏按会话或任务查询,并支持固定目标。同 scope 的跨 run 实体复用依赖模型显式选择 Main 提供的候选引用,Main 处理 UUID 和旧文本主键的精确映射,具体规则和验证边界见[实施进度](./progress.md)。
## 文档导航
主页面包含工作回顾、故事线图谱、自动监督、活动记录和设置。工作回顾阅读统一监督结果并选择历史,图谱定位同一结果;自动监督管理计划、概览、自动监督报告、记忆与行动建议和心跳审计。活动记录展示统一执行、进度及详情,可按计划 ID 筛选;设置只保留算法参数。当前布局以 [UI 设计](./ui-design.md#应用入口)为准。活动读取实际手动回顾及心跳执行,合并展示同一次心跳的报告与监督回顾阶段;状态规则、历史缺失和保留期限见[活动记录](./logic-design.md#活动记录)。
自动心跳报告和监督回顾已接入按来源版本及正文位置的增量处理,无变化时跳过模型。手动历史回顾继续重新整理所选区间;进度、失败重试和删除边界见[自动增量与手动重看](./logic-design.md#自动增量与手动重看)。
| 文档 | 权威职责 |
| --- | --- |
| [会话监督 PRD](./prd.md) | 监督目标、证据、介入方式和产品验收 |
| [监督者应用产品设计](./supervisor-prd.md) | 应用定位、心跳分工、知识库关系与目标范围 |
| [逻辑设计](./logic-design.md) | 整体架构图、图谱对象关系图、状态规则与需求映射 |
| [UI 设计](./ui-design.md) | 应用导航、工作回顾、侧栏反馈及页面状态 |
| [User Stories](./user-stories.md) | 监督者与故事线图谱的用户操作和验收场景 |
| [技术设计](./technical-design.md) | Main/Renderer 分层、数据模型、IPC、安全边界和实施顺序 |
| [回顾分块调度设计](./review-scheduling-design.md) | 第 0 节定义生产分页、批次持久化、续跑、当前设置及剩余边界;其余章节保留改造前基线、目标设计和原型证据 |
| [实施进度](./progress.md) | 已验证实现、验证证据和当前边界 |
| [故事线图谱设计](../story-graph/prd.md) | 图谱对象、环绕时间轴与实体交互 |
当前尚无独立 User Stories、功能逻辑、UI、技术设计和进度文档。
生产入口已接通完整来源分页、叶子持久化、预算暂停、活动页继续及批次详情;设置可调页大小、批次正文/消息上限、软预算、两个模型超时和共享并发。实际合同及限制见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。真实 DeepSeek 验证已覆盖一个完整的 27 条消息会话;较早的 129 条消息实验仍未完成。两次实验共 5 次付费请求,详情见进度。原始 AbortError 原因尚未由日志确定。
实施状态以代码和[实施进度](./progress.md)为准。逻辑待定项见[逻辑设计](./logic-design.md),架构边界见[技术设计](./technical-design.md)。现有心跳行为见[智能心跳](../smart-heartbeat/README.md),原型范围见[Story Graph 演示](../story-graph/README.md)。
@@ -0,0 +1,177 @@
# 监督者逻辑设计
日期:2026-09-23。状态:部分规则已接入产品,验证范围见[实施进度](./progress.md)。图中的连线表示逻辑依赖与数据流,不代表所有目标接口均已实现。
本文承接[监督者产品设计](./supervisor-prd.md)、[故事线图谱设计](../story-graph/prd.md)和[User Stories](./user-stories.md),定义触发、整理、展示与用户后续操作之间的关系。既有会话监督的证据和介入边界继续由[会话监督 PRD](./prd.md)约束。
## 整体逻辑架构
报告、监督叶子与导航整理共用监督专用 FIFO 并发池,普通聊天不入池;监督服务外层 run 仍串行。报告在排队前冻结超时,获槽后领取,实际模型请求前校验并刷新租约;报告提交或无变化后先释放槽位,再执行监督。监督超时在 run 创建时保存,节点获槽并解析 Runtime 后计时。阶段超时、租约、传输时限、后台检查与活动轮询各自独立,取值及生效规则见[当前设置合同](./review-scheduling-design.md#当前设置合同)。
完整回顾的生产接线已增加分页来源清单、逐批输出/offset 事务和导航节点。预算耗尽显示 `paused`,模型或保存失败显示 `failed`;两者均保留此前成功批次,可继续同一 run。存在未读来源、覆盖空洞或未成功导航时不能标 `completed`。手动位置与自动 checkpoint 分开,自动位置只在最终发布事务推进。来源变更目前要求重新回顾,模型配置跨重启固定等差异见[生产接线与剩余边界](./review-scheduling-design.md#0-生产接线与剩余边界)。真实模型验证覆盖 27 条消息的完整会话,较大样本失败单独记录。
```mermaid
flowchart TB
subgraph Entry["入口与触发"]
App["监督者应用"]
Scope["关注范围、时间区间与整理策略"]
Manual["用户主动回顾"]
Heartbeat["智能心跳:按计划唤醒"]
end
subgraph Sources["已有内容与状态"]
Conversation["会话记录"]
Notes["魔法笔记"]
Work["任务状态与执行记录"]
Knowledge["现有知识库资料与检索引用"]
end
subgraph Supervisor["监督者 Agent"]
Collect["收集范围内的有界可见证据"]
Review["理解进展与变化,判断是否需要整理"]
Result["带来源的整理结果"]
Quiet["记录检查结果,保持安静"]
end
subgraph Outputs["结果与展示"]
Recap["工作回顾:阶段决定、变化、未解决事项"]
Feedback["监督反馈:聊天右侧展示"]
Graph["故事线图谱:事件、实体、关系与演变"]
end
subgraph Actions["用户后续操作"]
Verify["核对来源、确认或纠正整理结果"]
Continue["预览所选上下文后继续讨论"]
Save["预览并确认新建或补充本地知识条目"]
end
App --> Scope
App --> Manual
Scope --> Collect
Manual --> Collect
Heartbeat --> Collect
Conversation --> Collect
Notes --> Collect
Work --> Collect
Knowledge --> Collect
Collect --> Review
Review -->|需要整理| Result
Review -->|无有效新变化| Quiet
Result --> Recap
Result --> Feedback
Result --> Graph
Recap -->|定位阶段| Graph
Feedback -->|定位相关对象| Graph
Recap --> Verify
Graph --> Verify
Verify -->|更新确认状态或用户修订| Result
Graph --> Continue
Continue --> Conversation
Graph --> Save
Save -->|仅写入可写的本地条目| Knowledge
```
心跳负责唤醒,监督者负责判断;模型不因被称为 Agent 而自动获得工具权限。收集证据由应用按所选范围提供,现有心跳不具备的知识库或笔记接入属于后续工作。
工作回顾、监督反馈和图谱引用同一次整理的结论与来源。展示可以裁剪为阶段摘要、会话卡片或图形,但不能分别维护相互矛盾的结论。一次整理可以没有图谱变更,也可以有整理结果但无需主动通知。
结果定位以结果 ID 为准。历史选择不能回退到其他项目的最新图谱;侧栏需同时匹配目标来源和范围,没有匹配结果时显示空态。固定目标失效时保留目标并显示读取错误,用户可取消固定。跨 run 仅允许引用同 scope 的 Main 候选实体,模型局部 ID 和同名均不构成身份。模型候选引用与存储身份分离;历史文本主键按 Main 精确映射保留,格式错误的旧模型身份字段只产生新实体,不猜测合并对象。每次结果保留当次描述,自动更新不得改写已确认字段或其他结果;人工修改仅更新当前对象与明确选定的结果。
应用维护来源正文;监督者维护其产生的阶段总结、事件关联、实体描述与关系。图谱是这些对象的视图。图中“纠正”只更新整理内容或关系,修改来源正文仍需进入对应应用的编辑流程。
## 图谱对象关系
```mermaid
flowchart LR
Story["故事线"]
Event["时间事件"]
Change["事件与实体的关联:提出、补充、验证、修订"]
Entity["知识实体"]
Relation["知识关系:类型、理由、确认状态"]
Summary["阶段总结"]
Interval["时间区间与来源范围"]
Reference["来源引用:所属对象、位置、可用版本信息"]
Original["原内容:会话、笔记、本地资料或已保存外部片段"]
Story -->|组织| Event
Story -->|关联,可跨故事共享| Entity
Story -->|按阶段回顾| Summary
Summary -->|限定覆盖范围| Interval
Summary -->|归纳范围内事件| Event
Summary -->|概括当时的认识| Entity
Event -->|一个事件可影响多个实体| Change
Change -->|一个实体可经历多个事件| Entity
Entity -->|关系起点| Relation
Relation -->|关系终点| Entity
Event -->|原始依据| Reference
Change -->|变化依据| Reference
Relation -->|关联依据| Reference
Entity -->|支撑资料| Reference
Summary -->|结论依据| Reference
Reference -->|定位,不复制正文| Original
```
这里的关联对象用于说明关系自身也有类型和依据,不指定存储表。知识实体与来源为多对多关系;实体可以被多个故事引用,不能仅凭同名就合并。来源可用的版本信息沿用原内容能力,不要求为此复制完整来源历史。
外圈时间轴呈现事件及阶段标记,内部平铺实体;时间到实体的连线对应事件关联,实体间连线对应知识关系。时间轴上的同日聚合点只负责显示密集事件,不形成新的知识实体。
## 应用启停与执行
以下规则对应 FR-S1、FR-S4、FR-S5 和 US-S14,适用于监督者与智能心跳共用的 `heartbeat` 应用。
- 新安装及设置缺失 `heartbeatEnabled` 时按 `false` 处理;已明确保存的 `true`、`false` 均保留。
- 关闭应用后不领取新的定时心跳运行,拒绝新的手动心跳和监督回顾请求,也不启动心跳完成后的自动监督回顾。侧栏不挂载监督面板,不发起该面板的查询。
- 启用应用后,只有已保存且启用的心跳计划才参与自动调度;没有此类计划时不产生自动工作。启动或启用应用不自动创建、补种或启用计划,用户仍可主动发起回顾。
- 关闭前已经领取并在执行的运行可以完成并保存结果,关闭应用不强制取消;若完成时应用已关闭,不再追加自动监督回顾。已有计划的 `enabled`、历史结果及导航偏好保留。
- 应用开关不影响普通任务的现有定时调度。取消常驻只改变导航入口,不改变应用启用或计划状态。
## 状态与处理规则
下表包含目标规则;当前活动执行状态以本节后的“活动记录”为准,取消及部分完成尚未接入。
| 规则 | 条件或状态 | 处理与用户可见结果 | 需求与场景 |
| --- | --- | --- | --- |
| L1 范围 | 手动或定时触发 | 按本次所选范围、时间和输入预算收集证据;启用应用不扩大来源范围 | FR-S3、S4;US-S01 |
| L2 检查 | 没有有效新变化 | 记录检查结果,保留旧回顾,不重复通知;用户主动查看时仍可读取原结果 | FR-S4、S5;US-S03 |
| L3 整理 | 产生新的阶段认识或关联 | 回顾、反馈和图谱引用同一整理结果;是否主动反馈按反馈策略决定 | FR-S2、S5;US-S02、S04 |
| L4 失败 | 取消、失败或部分来源不可用 | 保留上次成功结果;显示当前运行状态、已覆盖与遗漏范围,支持重试;部分结果不标为完整 | FR-S6;US-S12 |
| L5 纠正 | 用户修改描述或确认关系 | 后续自动整理提出补充或修订建议,不静默覆盖用户内容;移除关系可撤销且不删除来源 | FR-S6、S9、FR-G4;US-S06、S10 |
| L6 历史 | 选择过去时刻或时间区间 | 使用当时已有的事件与认识;来源旧版不可恢复时提示,不以当前原文冒充旧版 | FR-S9、FR-G3、G5;US-S05、S07、S13 |
| L7 目标 | 侧栏跟随或固定 | 跟随时显示当前会话,固定时保持所选目标;切换聊天不改变固定目标 | FR-S5;US-S25 |
| L8 沉淀 | 用户选择写入知识库 | 先预览并确认,再新建或补充本地条目;已有条目提供打开入口,外部引用保持只读 | FR-S7、S8;US-S08、S09 |
| L9 继续 | 用户选择继续讨论 | 预览并确认上下文后进入会话;新内容在后续整理时纳入,不立即作为已确认知识 | FR-S3、S5;US-S11 |
| L10 展示 | 改变布局或输入方式 | 保留范围、回放时刻及选择;图形和文字列表访问相同对象与来源 | FR-G2、G5、G6;US-S15 |
整理运行状态与内容确认状态分开:一次运行成功不意味着自动归纳已经得到用户确认。手动重试创建新运行;心跳继续沿用原有运行领取和重试规则。
## 活动记录
对应 FR-S10、US-S26。监督运行在收集证据之前保存 `running`,结果事务成功后转为 `completed`;收集、模型、校验或保存失败转为 `failed`,保留错误及结束时间。重开应用将没有完成的监督运行标为失败,旧成功结果保持可读。
心跳活动按真实运行 ID 关联下游监督。报告阶段使用 `claimed / completed / failed / skipped / no_change`,活动将 `claimed` 显示为运行中。报告已结束而投影尚在执行时,整条活动仍为运行中;任一阶段失败,整条活动显示失败。两阶段都没有新输入时显示“无变化(未调用模型)”;任一阶段保存新结果且没有失败时显示完成。没有下游运行记录时明确显示“无执行记录”。
旧监督成功记录继续显示;旧心跳与监督之间未保存关联时分别呈现,不用时间接近猜测关系。旧心跳未保存的执行范围显示不可用,不从当前计划反推。此前未保存的手动运行与失败无法恢复。心跳报告沿用计划的历史保留和删除规则;监督结果独立保留,原心跳被清理后仍可读取监督记录。活动不是永久审计库。
当前活动没有 `cancelled` 或 `partial` 状态,也不提供取消按钮。实体和关系修改只有当前状态及结果内容,没有按时间追加的人工修改记录。
收集、叶子整理、导航合并和发布分别展示。阶段推进只依据服务记录,最终完成只依据结果事务;“六批已保存、剩余零来源”仍可处于合并失败或发布失败。继续保留成功叶子和导航节点,缺失阶段不得靠错误文本或来源数猜测。导航摘要允许省略事实数组,叶子缺少事实数组仍失败;导航不得生成替代叶子事实的非空数组。阶段及输出合同见[生产调度](./review-scheduling-design.md#已接入的调度与存储)。
### 自动增量与手动重看
自动心跳报告和下游监督分别记录已成功处理的来源版本及正文位置。首次自动运行只读取计划范围和回顾窗口内的来源;后续运行在同样边界内处理新增、修订或上次预算未覆盖的片段。项目列表顺序不改变自动进度;不同项目集合和 Global 分开处理。失败、未送入模型或结果事务未提交的输入保留待处理状态。
两个阶段都在调用模型前检查输入。没有待处理来源时记录 `no_change`,保留旧报告和图谱,不产生新结果或重复事件。心跳报告无变化仍进入受应用开关控制的下游检查,允许监督阶段重试之前的失败。已确认记忆、已有实体说明及最近摘要仅作为背景,不单独触发自动模型调用。
工作回顾的手动操作重新整理所选区间,沿用有界历史收集,不读取或推进自动进度。自动监督计划列表中的“立即运行”重新生成心跳报告,其下游监督仍按自动进度处理。删除报告或计划不重置来源进度;重看历史、删除报告和重建图谱是不同操作。当前没有清空或重建图谱功能。
计划 Modal 的新建草稿不产生持久化或执行副作用,显式保存时创建并启用;编辑沿用已有 enabled。保存失败保留草稿,提交期间禁止关闭,未保存关闭须确认。活动筛选只改变读取范围,不触发执行;同名同范围计划仍按 configId 区分,筛选在分页前应用,结果跳转继续按 resultId 定位。
规则冲突时,先满足所选范围与既有观察边界,再保留用户修订和来源事实,最后应用自动归纳和展示策略。新增证据与已确认结论冲突时显示待核对差异,不能自动断言旧观点失效。
## 完整性与待定项
本设计覆盖触发、证据输入、静默检查、正常整理、取消与失败、跨视图共享结果、用户纠正、知识库写入及历史回放。它补充产品逻辑,不代表接口、持久化或迁移方案已经完成。
仍待产品决策:事件触发与阶段识别条件、反馈频率与静默判定、实体合并与拆分的交互、时间轴比例和集中度含义。应用启停按[应用启停与执行](#应用启停与执行)处理;既有入口偏好、计划与运行记录的保留要求见 FR-S1、S2 和 US-S14。
来源旧版本和完整历史回放仍受原应用的保存能力限制。增量进度与同 scope 实体 ID 复用的实现见[技术设计](./technical-design.md#自动增量收集)。
@@ -13,6 +13,8 @@
## 1. 背景
本 PRD 继续定义会话监督能力。统一应用“监督者”提供工作回顾、故事线图谱、自动监督、活动记录与设置五个页签;实际能力及执行记录要求见[监督者应用产品设计](./supervisor-prd.md)。本文其余执行观察与介入目标仍按各节范围实施。
GoodBuddy 的魔法笔记已经提供一种有价值的交互:用户持续写作,AI 在右侧以长评、建议和
警告进行评论,用户可以选择综合、扩展、润色、质疑或发散方向。该能力是内容分析,不是
会话监督:
@@ -252,7 +254,7 @@ type SupervisorDecision = {
### 13.1 工作栏监督栏目评论流
监督是助手工作栏中固定且始终可访问的栏目,不是只在聊天页面出现的附属面板。栏目默认
监督应用启用后,助手工作栏提供监督栏目;关闭时不挂载或查询监督面板,见[应用启停与执行](./logic-design.md#应用启停与执行)。栏目默认
跟随当前会话,用户也可以固定到其他普通 Conversation、Task 或 Experiment。
切换页面不会改变固定目标;目标失效时必须显示修复状态,不能静默回到当前会话。
@@ -0,0 +1,422 @@
# 监督者实施进度
日期:2026-09-24。
当前记录以已验证生产行为为准。监督者尚未覆盖全部 user stories。
## 已验证
### 2026-09-24 长摘要与内容区对齐
针对全局七天回顾的 `summary max 2000` 报错,代码确认导航输出 schema 的 `max(2000)`、服务层对叶子及导航的二次 2000 字符检查,以及模型提示词中的同值要求。另发现监督旧摘要通过 SQL `substr` 和 JavaScript `slice` 截断。以上监督路径限制已移除;生成内容和事实集合的合同见[生产调度合同](./review-scheduling-design.md#已接入的调度与存储)。未读取本次 16:03 失败的原始模型响应,不能据此声明该运行的具体失败节点。
生产工厂回归使用隔离 SQLite 和离线 Runtime 流,验证 14,000 字符的叶子及导航摘要完整进入合并输入、存储、历史读取,发布失败后以已保存节点恢复且不再请求模型,叶子长描述仍进入最终图谱。单次超过 100,000 字节的响应明确失败,保留续跑位置。共享契约另覆盖原字段及数组数量边界,仍拒绝导航事实和叶子缺字段;旧身份兼容、跨范围拒绝和六叶合并恢复测试继续通过。IPC 暂停/继续测试改用 3,600 字符摘要,并修正已有 `persistedId` 提示词断言为当前 `candidateRef` 合同。
布局采用共享阅读、dashboard 宽度,范围、工具栏、历史和结果的对齐规则见[UI 设计](./ui-design.md#应用入口)。真实 Electron 加载生产 React 组件,使用隔离 fixture 验证 2000、1440、1024、390px 的浅深主题,覆盖长摘要、活动、运行中、失败共 32 组,并保存 8 张正文末尾截图。无页面横向溢出,正文末尾保留,宽屏阶段行低于 40px,刷新紧邻筛选。另运行既有活动分支,验证原生键盘展开、完整错误滚动、六个叶子及 12 批分页。已人工查看宽窄屏、浅深主题、运行/失败、正文末尾及批次树截图。
截图分别保存在本机临时目录 `opencode/supervision-content-layout` 与 `opencode/supervision-content-activity`。复现命令为设置 `GOODBUDDY_SUPERVISOR_CONTENT_LAYOUT=1` 或 `GOODBUDDY_SUPERVISOR_ACTIVITY=1` 后运行 `npx vitest run tests/supervisor-layout.electron.test.ts`,可用 `GOODBUDDY_SUPERVISOR_ARTIFACTS` 指定保留目录。模型调用 0 次,未改写用户数据库、运行全量测试或提交。
验证记录:首轮服务、分块、历史和工作区 6 文件 68 项通过;补充共享契约、生产工厂、活动存储/preload、心跳中心及工作区共 8 文件 112 项通过。`npx vitest run src/main/ipc.test.ts -t 'supervis'` 为 17 项通过;修正测试等待的 TypeScript 类型后,暂停/继续单项再次通过。最终 `npm run typecheck`、本轮修改 TS/TSX/MJS 的定向 ESLint 和 `git diff --check` 通过。中文变更按 `deai-writing` 清单人工审校。
### 2026-09-24 活动阶段、批次层级与导航输出修复
对应 FR-S4、FR-S6、FR-S10、US-S26。活动记录改为紧凑运行摘要、实际阶段、恢复操作、折叠错误、按需批次层级和次要配置详情。阶段存入既有运行 JSON;叶子与导航分别校验,叶子缺数组仍失败。当前字段与缺失数据规则见[生产调度合同](./review-scheduling-design.md#已接入的调度与存储),布局见[活动设计](./ui-design.md#活动)。
只读检查 portable 原库的最新失败运行 `f6800335-5765-4163-9f55-a30c8ab18a94`,确认 32 个来源、剩余 0、6 个成功批次、4,515 字符和 1 个已保存导航节点。错误仅包含 `events / entities / entityChanges / relations` 四个缺失数组。旧代码把导航响应交给叶子 schema,且提示词同时要求“只返回摘要字段”和“事实数组必须为空”,合同矛盾已确认。原始失败响应和阶段没有保存,因此该次失败发生在导航解析仍属有数据支持的推断,不能声称取回了原响应。
使用 SQLite 一致性备份和关联笔记副本执行 `scripts/supervision-navigation-replay.ts`。仅私有副本初始化;第一次因缺少关联笔记停止,补齐后完成。生产工厂继续同一 run,复用已存导航节点,4 次离线响应完成剩余合并及发布,6 个叶子逐项未变。回放文本来自该 run 已保存的成功导航,只保留 summary、changeDigest、openItems,以验证缺数组的合法导航形状;它不验证新摘要质量,也不是原失败响应回放。原库只读,原失败 run 前后相同,付费模型调用 0 次。
本轮验证:
- `npx vitest run src/main/assistant/supervision-production.test.ts src/main/assistant/supervision-review.test.ts src/main/assistant/supervisor-service.test.ts src/main/assistant/supervision-activity.test.ts src/preload/supervision-activity.test.ts src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:8 文件、116 项通过。包含六叶零剩余后的合并失败与继续、发布失败后无模型继续、缺数组叶子拒绝、未知来源/实体拒绝、实际阶段呈现、旧阶段缺失、错误与配置折叠、树分页及读取失败重试。
- `npm run typecheck` 和本轮变更 TS、TSX、MJS 文件的定向 ESLint 通过;未运行全量测试。
- `GOODBUDDY_SUPERVISOR_ACTIVITY=1` 下运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过。真实 Electron 加载生产 React 组件,以多项目、多会话的隔离数据覆盖 1440×1000、390×1000 和浅深主题,分别截取运行摘要、错误展开、批次树,共 12 张截图。检查原生 Enter 展开、240px 错误滚动、全部六个已保存叶子及 12 批跨页读取,无横向溢出和控制台错误。失败运行摘要高度为桌面 366px、窄屏 476px,两主题一致;已人工查看截图。
私有数据库、回放报告及截图位于 `C:/Users/jiang/AppData/Local/Temp/opencode/activity-incident-20260924/`,截图测量文件为 `screenshots/review-activity-measurements.json`。收尾检查修正成功返回 coverage 沿用合并前阶段的问题,3 个生产服务测试文件共 33 项复验通过,活动与返回结果均使用发布阶段投影。`git diff --check` 通过,仅提示现存行尾转换。中文文档经 deai-writing 扫描与人工复核,本轮新增段落无阻断项;全文件扫描的 7 项阻断均在既有非本轮段落,未改动其内容。
历史缺失 phase 仍显示未记录;树只列成功批次,没有持久化的单任务失败/在途状态,项目和会话数量仅针对本页。收集与发布仍为同步 SQLite 阶段,不保证轮询可见。未修改 Agent、远程 Runtime、传输或凭据逻辑;原暂存及未暂存修改保留,未暂存、提交或替换 portable 构建。
### 2026-09-24 工作回顾与自动报告分区
对应 FR-S2、US-S22 至 US-S24。工作回顾仅阅读统一监督结果,历史选择和精确结果图谱入口移到正文前;手动新回顾控件保持内联,390px 下使用双列。结果常显生成时间、覆盖区间和范围,正文按原始段落及已有结构化字段展示,限制阅读宽度。新请求配置与运行状态不改写旧结果。自动报告、记忆与行动建议全部归入自动监督,保留展开、确认/忽略、带入对话、标记完成和每次追加 20 条;移除独立最近报告副本。五页签当前布局以 [UI 设计](./ui-design.md#应用入口)为准,下方各轮记录保留为历史证据。
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/SupervisorActivity.test.tsx`:66 项通过。覆盖非空结果与空自动报告不矛盾、完整建议操作、长内容展开与分页保留、历史精确 ID、变更新请求范围不重标旧结果、运行中保留正文和活动入口。
- `npx vitest run src/renderer/src/App.test.tsx -t 'graph navigation opens the pinned sidebar result|keeps heartbeat plans independent|saves Supervisor timeouts'`:3 项通过,284 项按名称跳过。校正已有设置保存测试的旧字段选择器,保留实际 App 接线验证。
- `npm run typecheck` 与本轮修改的 Renderer、测试及 Electron driver 定向 ESLint 通过。中文 UI 文档经 deai-writing 扫描,无阻断项;复核项涉及既有状态与实现边界,保留事实限定。
- 设置 `GOODBUDDY_SUPERVISOR_RECAP=1` 后运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过。现有生产组件 fixture 覆盖 1440×1100、390×1100 的工作回顾与自动监督,含两类结果均非空、自动报告为空、两者均空,共 12 个页面场景;另验证历史结果运行中保持可见及活动跳转。每页一处刷新、横向溢出及正文宽度断言通过。
截图与 `recap-measurements.json` 保存在 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-recap/`。已人工查看桌面与窄屏正文、运行中的历史结果、自动报告展开、建议操作和独立空态截图。此次验证使用隔离 fixture,未读取用户数据库,模型调用 0 次,未运行全量测试;未修改 Main、Preload、数据库、Agent 或远程 Runtime。保留已有暂存和未暂存修改,未暂存或提交。
### 2026-09-24 自动监督概览归属修正
对应 FR-S2、US-S22 至 US-S24。当前状态、范围与刷新、成功率、记忆、洞察、行动指标、报告趋势和心跳运行审计已整体移到“自动监督”,与计划列表同页。当前状态下只保留计划列表的创建按钮和未配置说明,点击直接打开原 Modal。活动页只显示统一监督活动、进度、详情及结果入口,计划 ID 筛选、清除、分页和精确结果跳转沿用原路径;活动筛选不改变自动监督概览。当前归属见 [UI 设计](./ui-design.md#应用入口),下方旧方案的截图与测试记录保留为历史证据。
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorActivity.test.tsx`:48 项通过。覆盖空/非空时整块归属、唯一创建入口、独立刷新回调、原 Modal 的 X/footer/关闭保护、计划操作、审计分页、同名计划筛选和结果跳转。
- `npx vitest run src/renderer/src/App.test.tsx -t 'keeps heartbeat plans independent of the active project'`:1 项通过,286 项按名称跳过,保留实际 App 的跨项目计划列表与暂停计划 Modal 保存验证。
- `npm run typecheck`、本轮修改的 `HeartbeatCenter.tsx`、`HeartbeatCenter.test.tsx`、`supervisor-layout-driver.mjs` 定向 ESLint 通过;`git diff --check` 无空白错误,仅提示现存行尾转换。
- 设置 `GOODBUDDY_SUPERVISOR_AUTOMATIC_OVERVIEW=1` 和产物目录后运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过,约 10 秒,仅运行本轮范围。现有生产组件 fixture 覆盖浅色 1440×1100、390×1100,空/非空计划与自动监督/活动共 8 个页面场景;检查整块归属、唯一创建按钮、横向溢出、原 Modal 打开关闭及精确计划活动筛选。
截图和 `automatic-overview-measurements.json` 位于 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-automatic-overview-20260924/`。已查看桌面、窄屏空/非空计划、活动及滚动后的趋势和运行审计截图;桌面指标四列、窄屏单列,内容无横向裁切,活动页无重复心跳列表。本轮中文修改按 deai-writing 清单人工审校。未运行全量测试,模型调用 0 次,未读取用户数据库;未修改 Agent/远程 Runtime、Modal 实现或共享样式,保留已有暂存和未暂存修改,未暂存或提交。
### 2026-09-24 真实失败运行身份修复
只读定位到 `dist/GoodBuddy-windows-x64/data/assistant.sqlite` 中的运行 `db9902ab-22c8-4aae-86ae-7850bfc07b76`。Roaming 库不含该运行。原运行是全局手动回顾,范围为 2026-09-17 14:33:02 至 09-24 14:33:02,开始于 14:33:02,失败于 14:44:52(本机 UTC+8)。保存配置为分页 50、批次文本量 8000、消息数 20、执行预算 300 秒、模型超时 240 秒、并发 1;清单 32 个来源,成功批次 0。
原错误指向 `entities[0].persistedId` 的 UUID 校验。原始模型响应未保存,诊断文件没有该次响应,无法确认原错误值及 710 秒耗时的模型、排队、重试分解。只读候选检查发现 15 个旧实体均使用文本主键;隔离真实调用再次证明,即使模型选择提供的候选,旧主键仍会触发严格 UUID 校验。修复采用候选短引用、Main UUID 别名及旧键精确映射,详细规则见[技术设计](./technical-design.md)。
通过 SQLite 一致性备份复制数据库和关联笔记文件,仅隔离副本运行初始化及生产工厂 `createProductionSupervisorService`、`ModelAgentRuntime`、批次事务和结果保存。首次隔离启动因缺少笔记文件停止,付费调用 0 次;补齐副本后开始模型验证。使用 `.env.deepseek.local` 的 `deepseek-flash`,总计 3 次 HTTP 请求,无自动付费重试或模型回退:
| 验证 | 本机开始时间 | HTTP | 响应耗时 | 结果 |
| --- | --- | --- | --- | --- |
| 原运行首叶提取 | 14:53:57 | 200 | 5.994 秒 | 首次暴露旧键校验失败,响应私存;修复后缓存复验并保存 |
| 真实来源有界回顾 | 14:58:25 | 200 | 4.445 秒 | 2 个来源、402 字符、1 批,完成并发布 2 个实体 |
| 同一来源候选复用 | 14:58:30 | 200 | 4.703 秒 | 完成并复用 2 个既有实体 |
缓存复验要求请求除 Runtime 当前时间外一致,没有新 HTTP。隔离副本中原失败 run 保存 1 批、3 个精确匹配的原文片段、490 字符及 3 个完整来源位置后主动暂停;32 个来源还剩 29 个,不冒充原全局回顾已完成。两个有界回顾分别完成结果发布,实体总数保持 15。手动回顾不写自动检查用的 `review_checkpoints`。只读复核原 run、来源清单及批次逐项未变;副本完整性检查通过,原有 1 个受保护实体保持不变。
私有产物在 `C:/Users/jiang/AppData/Local/Temp/opencode/supervision-incident-real-20260924/`:`incident.private.json`、`candidates.private.json`、3 份请求与 SSE 响应、`live-metrics.json`、`audit.json` 和副本 `assistant.sqlite`。原库只读打开,无初始化、迁移或业务写入;原失败状态及原有数据保留。验证脚本为 `scripts/supervision-incident-{inspect,launch,audit}.cjs` 与 `scripts/supervision-incident-live.ts`,重复付费启动受检查限制。
定向 7 文件共 47 项测试、`npm run typecheck`、本轮改动文件 ESLint 和 `git diff --check` 通过,后者仅提示现存行尾转换。回归包含真实旧键形状、严格 UUID 别名、空值和占位符的新事实保留、未知候选/跨 scope 拒绝、确认字段及图谱端点保护、失败后成功叶子不重算,以及中英文可展开错误详情。活动页显示本地化失败原因,技术错误保留在可选择文本的折叠详情;进度与设置不再直接展示 UTF-16、Unicode 或共享池术语。新增中文文档按 deai-writing 清单人工复核。未运行全量测试,没有提交、暂存或构建替换应用。此修复属于桌面监督身份边界,未修改 Agent 或远程 Runtime 执行实现;正在运行的 portable 和 D 盘安装版均需包含新源码的构建并重启后才会生效。
### 2026-09-24 计划 Modal 标题与操作区统一
对应 FR-S2、US-S22 至 US-S24。`HeartbeatSettings` 复用既有 `custom-task-dialog__header/content/actions` 和按钮样式,标题在左、带本地化可访问名称及工具提示的 X 在右,取消与保存位于右对齐 footer。采用共享 Grid 的固定标题、滚动正文、固定操作区,没有新增 Modal 框架或页面专属 CSS。共享规范见根目录 [表单 Modal](../../../UI-DESIGN.md#615-表单-modal),计划交互见[自动监督设置](./ui-design.md#自动监督设置)。
本轮验证:
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/CustomTaskDialog.test.tsx`:50 项通过。覆盖中英文创建/编辑结构、X/取消/Escape/遮罩的干净关闭与未保存确认、Tab 循环、焦点恢复、提交锁定、失败草稿及暂停计划保存重试。
- `npx vitest run src/renderer/src/App.test.tsx -t 'keeps heartbeat plans independent of the active project'`:1 项通过,286 项按名称跳过;验证实际 App 的暂停计划编辑提交、Portal 和背景隔离恢复。
- `npm run typecheck` 通过;本轮修改的 Renderer、翻译和 Electron driver 文件定向 ESLint 通过;`git diff --check` 无空白错误。
- `GOODBUDDY_SUPERVISOR_PLAN_MODAL=1`、`GOODBUDDY_SUPERVISOR_ARTIFACTS=C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-plan-modal-standard` 下运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过,约 13 秒。创建/编辑 × 浅/深色 × 1440×1100、390×1100、390×480 共 12 个截图场景,测量无横向溢出、标题与底部按钮不随正文滚动,关闭及底部操作可命中;原生 Shift+Tab、Enter、Escape 验证焦点循环、取消确认和返回编辑。首次两次运行暴露测试驱动的 Enter 字符事件缺失及窗口焦点不稳定,补充字符事件和输入前显式聚焦后通过。
截图与 `plan-modal-measurements.json` 保存在上述产物目录。已查看宽窗口、390px 单列及短窗口的浅深主题创建/编辑截图,X 保持右上、取消与保存保持右下,短窗口只滚动正文。fixture 使用现有五页签和生产 React 组件,未读取用户数据库;模型调用 0 次。未运行全量测试,保留原暂存区与工作区改动,未提交或推送。
### 2026-09-24 监督者文案整理
设置帮助删除“提供商 RPM、TPM、Retry-After 控制及费用预算尚未实现”,保留执行软预算到期后保存正在处理的批次、暂停和从活动记录继续的说明。活动说明删除人工修改审计缺失提示;计划帮助改为选择每日或每周时间、保存并启用后运行。图谱说明实体状态以所选回顾结果为准,事实详情提示展开批次并对照来源核对模型内容。中英文同步,不承诺全局限流或完整语义识别。
已检查监督者设置、帮助、空态、错误展示及相关测试。真实运行错误、失败、超时、暂停和来源失效仍按原路径显示,保留恢复操作。根目录 UI-DESIGN 的既有监督者章节记录文案原则,UI 设计和用户故事同步;内部技术设计与验证记录仍保留事实边界。
本轮验证:
- `npx vitest run src/renderer/src/SupervisionReviewSettings.test.tsx src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:4 文件、57 项通过。新增中英文用例覆盖帮助文案、监督并发范围、来源核对提示,以及真实 HTTP 429 / Retry-After 错误、失败状态和继续入口的保留。
- `npx eslint src/renderer/src/SupervisionReviewSettings.tsx src/renderer/src/SupervisionReviewSettings.test.tsx src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/i18n/locales/zh-CN/heartbeat.ts src/renderer/src/i18n/locales/en-US/heartbeat.ts`:通过。`git diff --check` 通过,仅有现存行尾转换提示。
- 中文 UI 文档扫描无阻断项,9 项复核提示均涉及实际行为边界,按事实保留;新增规则和技术记录另经人工审校。未运行全量测试、类型检查、Electron 或真实模型调用,本轮模型调用 0 次。
本轮仅修改监督者 Renderer、i18n、相关测试和文档;Main、Shared 只读核对。保留已有暂存及未暂存修改,未暂存、提交或推送。
### 2026-09-24 persistedId 并行修复记录
此段记录被上文真实旧库修复替代的早期方案。当时静态核对并行 Main 改动:新实体示例省略 `persistedId`,空字符串和 `null` 视为省略,其他格式错误值仍拒绝。该方案未处理真实候选中的旧文本主键,不能证明用户故障已解决。当前合同见[技术设计](./technical-design.md)。原始失败响应的具体坏值仍未知。
用户提供的并行验证结果为 29 项定向测试及另 1 项 IPC 测试通过;两次真实 DeepSeek 请求分别验证创建和复用,实体数保持 2,实体 ID 稳定。本轮仅记录并静态核对修复,没有复跑这些测试或模型请求;此证据不证明全部事实的语义识别,也不替代此前完整会话实验的范围与计数。
### 2026-09-24 五页签与计划 Modal 修正
对应 FR-S2、US-S22 至 US-S24。当前导航为工作回顾、故事线图谱、自动监督、活动记录、设置。计划列表移入自动监督,原有表单移入共享 Modal;设置只保留算法参数。保存失败保留草稿,未保存关闭确认,暂停计划编辑后仍暂停;计划执行记录按 configId 进入活动筛选,数据库在分页前过滤。报告、建议、概览、审计及精确结果跳转按[当前 UI 设计](./ui-design.md#应用入口)归属。
- 六文件定向组最终 61 项通过:HeartbeatCenter、SupervisorWorkspace、SupervisorActivity、SupervisionReviewSettings、Main supervision-activity、Preload supervision-activity。包含计划创建/编辑、失败重试、放弃确认、焦点循环与恢复、同名计划筛选、报告/审计分页,以及真实 SQLite 分页前过滤。新增换计划重置分页和忽略旧响应回归,筛选与清除保留键盘焦点。
- App 筛选 3 项通过:跨项目计划列表与暂停计划 Modal 保存、设置保存后重开、精确结果跳转。IPC 筛选 2 项通过,验证活动 configId 经真实 handler/SQLite 过滤并拒绝空 ID;其余 App/IPC 测试未运行。
- 最终 `npm run typecheck`、`npm run lint` 和 `git diff --check` 通过;按要求未运行全量测试。中文 UI 文档审校无阻断项,复核提示涉及执行限制和未实现边界,按事实保留。
- Electron 生产组件 fixture 测试 1 项通过,约 66 秒,覆盖 1440/1024/390px、浅深主题、五页签归属、Modal 焦点与放弃、390×720 短窗口保存按钮命中、计划活动过滤和原生方向键。首次运行被终端 60 秒上限中断,延长后通过。
截图位于 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-five-tabs/`,包括 `menu-plans-light-390.png`、`plan-modal-dark-390-short.png`、各页签浅深主题截图及 `measurements.json`。人工检查计划、Modal 和设置截图,无横向裁切;短窗口长表单内部滚动。fixture 复用实际生产组件与已有模拟数据,未读取用户数据库,未调用模型。此次不修改 Agent/远程 Runtime,无需远程执行验证。
开始检查时 HEAD 为 `7adae97`,监督改动仍在暂存区与工作区,未发现上轮计划提交;本轮保留其他 dirty 修改,未提交、amend 或推送。
### 2026-09-24 菜单归属调整
此段记录上轮四页签方案,已由上文五页签修正替代。当时设置包含自动监督计划、模型超时与并发、回顾算法三个纵向区块;报告与建议迁入工作回顾,运行概览和心跳审计迁入活动记录。以下保留当时验证证据,当前交互以[应用入口](./ui-design.md#应用入口)为准。
本轮验证:
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/SupervisionReviewSettings.test.tsx`:50 项通过,覆盖双语菜单归属、设置无嵌套导航、计划操作、建议确认/忽略、报告及审计分页、页签切换保留展开状态、快捷按钮焦点、图谱来源与持久进度。
- App 按测试名称筛选运行 4 项并通过:页面读取失败重试、侧栏结果跳转与 keepalive、计划独立于当前项目、模型及回顾参数保存后重开。其余 App 测试未运行。
- `npm run typecheck`、本轮修改的 TS/TSX/MJS 文件 ESLint、`git diff --check` 通过;未运行全量测试。
- `npx vitest run tests/supervisor-layout.electron.test.ts`:最终 1 项通过,约 51 秒。保留原图谱、异常状态、设置及活动的 1440/1024/390px 验证,新增非空菜单在 1440/390px 浅深主题的 12 个页面场景、区块归属、报告全宽、设置无范围徽标/刷新行和原生方向键导航检查。人工查看截图后修正了报告遗留双栏宽度,并补全 fixture 下次运行时间。一次复验受终端 60 秒限制中断,延长执行上限后通过。
截图及测量保存在 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-menu-20260924/`,菜单截图以 `menu-` 开头,`measurements.json` 包含布局结果。Electron 使用生产 React 组件和隔离 fixture,未读取用户库、调用模型或验证真实模型产出。本轮模型调用 0 次,无提交或推送。
### 2026-09-24 完整会话生产验证与收尾
生产链路已通过一个独立完整会话的真实模型测试;较早的 129 条消息实验仍为未完成,不能据本次成功改写其状态。按用户要求,没有重发此前的两个请求,也没有重新计算其已保存批次。
只读选择器 `scripts/supervision-select-readonly.cjs` 在 `readOnly`、`query_only` 和读取事务下选择另一条完整会话,筛选条件为 21..40 条 user/assistant 消息、8,001..14,000 码点,并排除旧目标 ID。命中会话共 27 条消息、9,677 码点;选取的是整条会话,没有截取其头尾。仅该会话进入外部临时快照及隔离产品库,原用户库未初始化、迁移或写入。
首次请求输出有效 JSON,提供商以 `stop` 结束,但使用输入的 `locator.source` 作为引用,未使用带范围的 fragment ID。生产校验因此拒收。修正为:引用若是本节点提供的 source token,且只匹配一个输入片段,就映射到该片段 ID;同源多片歧义或未知来源仍拒绝。映射不扩展引用范围,不把片段升级为整条消息。针对唯一映射、歧义拒绝补了确定性测试。
原始 SSE 已保存在外部临时目录。继续测试时通过同一生产工厂、`SupervisorService` 和 `ModelAgentRuntime` 重放该响应,校验相同来源快照、运行配置及请求字节数;这是一次本地响应复核,没有新增付费请求。之后只为未调用的第二叶子和导航节点请求 DeepSeek。最终与每条原消息逐字比较,并检查码点区间连续性。
| 实测项 | 结果 |
| --- | --- |
| 完整覆盖 | 27 / 27 条消息,9,677 / 9,677 码点,27 个片段;重建差异及剩余来源均为 0 |
| 生产产物 | 2 个叶子、1 个导航合并、1 个完整发布结果 |
| 叶子记录 | 19 个事件、15 个实体、8 个变化、11 个关系,共 53 条;摘要未替换叶子 |
| HTTP | 本会话共 3 次 HTTP 200:两个叶子和一个导航;另有一次缓存响应复核,HTTP 为 0 |
| 本轮总付费请求 | 较早失败实验 2 次 + 本会话 3 次 = 5 次;无重复付费请求、无自动重试、无回退模型 |
| 配置 | `deepseek-flash`;页大小 17;默认正文批次 8000、默认消息上限 20;模型超时 180 秒、软预算 600 秒、接纳并发 2 |
| 耗时 | 首次 20,164ms,缓存复核及剩余执行 19,584ms;二者不含人工修正间隔 |
| 手动隔离 | 自动 checkpoint 为 0;SQLite integrity_check 为 ok |
产物目录为系统临时目录 `opencode/review-production-complete-20260924`;来源快照在 `opencode/review-production-selected-20260924`。计数见 `metrics.json` 和只读审计入口,响应 `.private.sse` 仅供本机诊断,未进入仓库。模型输入包含完整选中正文,但未进行独立语义召回评估;“覆盖完成”不表示模型识别了每个事实。
最终确定性验证:12 文件定向组 136 项通过,覆盖服务/存储、迁移、设置、Preload 和页面;生产 IPC 筛选 22 项通过,其余 128 项未运行。完整 App 的设置保存及重开 1 项、AssistantDatabase 的监督历史筛选 4 项通过。Electron 使用生产组件,在 1440/1024/390px、浅深主题通过布局测试。首次视觉测试发现脚本仍只选中第一个设置表单,改为测量整个设置面板的可见控件后通过;两个表单同名保存按钮也已分别命名。
收尾新增用例确认 schema 46 已提交的 2,000 码点前缀不重读,只处理后续 1,300 码点;来源变化保留旧批次并要求新 run,后续自动检查不会永久卡在旧版本。来源上下文按已保存 messageId 和片段返回,避免同时间消息误定位或用当前正文冒充历史片段。`npm run typecheck`、本轮修改及新增 TS/TSX/CJS/MJS 文件的定向 ESLint 均通过。没有运行全量测试、提交或推送。
最终定向命令:
```text
npx vitest run src/main/assistant/supervision-review.test.ts src/main/assistant/supervisor-service.test.ts src/main/assistant/supervision-history.test.ts src/main/assistant/supervision-activity.test.ts src/main/assistant/incremental-review.test.ts src/main/assistant/supervision-model-pool.test.ts src/main/application-settings-store.test.ts src/preload/supervision-activity.test.ts src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/SupervisionReviewSettings.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx
npx vitest run src/main/ipc.test.ts -t "supervision|heartbeat enabled gates|admits .*reports before claiming"
npx vitest run src/renderer/src/App.test.tsx -t "saves Supervisor timeouts and concurrency"
npx vitest run tests/supervisor-layout.electron.test.ts
npm run typecheck
```
### 2026-09-24 生产接线与真实模型失败
本节记录首轮生产接线及较大样本失败;最终补充证据见上一节。下方同日的原型、设计及旧 collector 记录是更早阶段证据。
已接线:Main 手动 IPC 与心跳回调共用 `createProductionSupervisorService`;schema 47 保存来源清单、配置、叶子和导航节点;稳定键分页、项目/会话轮转、Unicode 片段、逐批事务、失败/预算后继续、严格发布判定已进入生产服务。活动及 Preload 增加暂停、继续、分页批次详情;设置存储、App 回调和中英文表单已接通页大小、字符/消息批次上限及软预算。应用仍默认关闭。实现合同和未完成项见[生产接线与剩余边界](./review-scheduling-design.md#0-生产接线与剩余边界)。
确定性验证使用隔离 SQLite、生产 IPC 和可控 Runtime,先于真实请求执行。覆盖超过 20 会话/20 消息的 506 条来源、Unicode 精确重建、手动/自动位置隔离、失败后重开、冻结配置、事务回滚、暂停 signal 到 Runtime、成功批次不重算及历史结果来源。后续增加项目轮转、分页大小不改变语义块、发布失败后复用导航节点、Preload 方法和 UI 操作验证。
真实验证入口为 `scripts/supervision-production-live.cjs` / `.ts`,使用与 Main 相同的生产工厂及 `ModelAgentRuntime`,不使用 `review-algorithm.mjs`。输入为已有只读快照中的完整目标会话,截止时间为 2026-09-23 16:09:45.919 UTC,共 129 条消息;逐消息集合 SHA-256 与原探测清单一致。只将选中会话导入临时产品库,没有复制整份用户库,未打开或改写原库及用户设置。
| 实测项 | 结果 |
| --- | --- |
| 配置 | DeepSeek `.env` 中的 `deepseek-flash`;页大小 17、正文批次 16000、消息上限 50、并发接纳 2、模型超时 180 秒、软预算 600 秒 |
| HTTP | 2 次,均 200;无付费重试、无模型回退、无工具 |
| 结果 | 第 1 批校验并保存,第 2 次输出 JSON 无效;逻辑 run 为 failed,没有完整结果 |
| 来源清单 | 129 条消息,54,979 个 Unicode 码点 |
| 已持久化覆盖 | 16,000 码点;28 条完整消息及 1 条部分消息,共 29 个片段;101 个来源未完成 |
| 已保留输出 | 10 个事件、9 个实体、9 个变化、9 个关系,共 37 条结构化记录 |
| 完整发布 / 自动 checkpoint | 均为 0;此次为手动运行,失败批次没有推进位置 |
| 耗时 | 56,775ms;HTTP 测量仅记录响应头延迟,不能当作完整模型耗时 |
| 完整性 | 临时库 `PRAGMA integrity_check` 为 ok |
私有产物位于系统临时目录 `opencode/review-production-20260924`。`metrics.json` 与只读命令 `node scripts/supervision-production-audit.cjs <外部临时目录>/assistant.sqlite` 可核对上述计数;不在仓库保存正文或凭据。失败响应正文没有保存,尚不能确定 JSON 无效是格式问题还是输出截断;不补造原因。按用户要求未重发失败付费请求,**该 129 条消息样本没有完成真实模型全会话覆盖验收**。16000 字符测试上限也不能视为经过可靠性验证的推荐默认。
已完成的定向命令包括:
```text
npx vitest run src/main/ipc.test.ts -t "production supervision IPC|collects supervision within the exact UI timeRange"
npx vitest run src/main/assistant/supervision-review.test.ts src/main/assistant/incremental-review.test.ts src/main/assistant/supervision-history.test.ts src/main/assistant/supervision-model-pool.test.ts src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisionReviewSettings.test.tsx src/preload/supervision-activity.test.ts
npx vitest run src/main/assistant/assistant-database.test.ts -t supervision
```
前述 IPC 3 项、七文件 45 项及数据库历史 4 项通过。更早一次十文件定向运行 116 项通过,2 项因两个表单保存按钮同名失败;已分别命名并复查相关文件。类型检查、最终定向 lint 和收尾验证在本节后续记录。未运行全量测试、提交或推送,保留已有 dirty 改动。此次没有修改 Agent、远程 Runtime 协议或其取消实现;监督父 signal 使用既有 Runtime 接口,本轮不声称完成远程真实验证。
### 2026-09-24 DeepSeek 全目标会话隔离原型
使用 `.env.deepseek.local` 的 `DEEPSEEK_BASE_URL`、`DEEPSEEK_MODEL`、`DEEPSEEK_API_KEY`;配置模型及 12 次响应的实际模型均为 `deepseek-flash`。直接调用配置端点,没有 GPT 回退、自动付费重试或工具调用。原型复用 `SupervisionModelPool`,实验并发参数设为 2,实测峰值为 2;没有修改应用设置。单请求超时 180 秒,输出预留 16,384 token,12 次均以 `stop` 结束。
启动前核对旧探测的 PID、创建时间、启动记录及命令路径,终止已取消的全会话 GPT 探测。旧尝试记录 10 次已插桩 HTTP 及 2 次未插桩调用,不计入下表的 DeepSeek 12 次。原始消息复用 `review-full-20260924/sources.private.json`,核验冻结消息集合的 SHA-256 及逐消息重建;未沿用旧 GPT 事实或导航。该快照来自既有 `readOnly`、`query_only` 采集入口,本轮没有打开原库,也未初始化、迁移或写入原库。
| 测量 | 结果 |
| --- | --- |
| 完整目标范围 | 1 个会话,129 / 129 条 user/assistant 消息,8 页;截至 2026-09-23 16:09:45.919 UTC 的冻结快照 |
| 正文覆盖 | 54,979 / 54,979 个 Unicode 码点;本样本 UTF-16 单元数相同;135 个片段、7 个叶子批次,每批正文不超过 8,000 UTF-16 单元 |
| 来源核验 | 重建差异 0、覆盖空洞 0、引用码点区间错误 0 |
| 持久化结果 | 354 条叶子事实、503 条引用;其中一个重复短引文保留 2 个明确标为歧义的候选位置,没有猜选唯一位置 |
| 导航 | 18 组完整索引 354 条事实,每条一次;生成导航前后叶子内容散列一致 |
| 真实调用 | 7 次叶子提取 + 2 次独立检查点选择 + 1 次导航 + 2 次语义评分,共 12 次 HTTP 200 |
| 首批速度 | 并发两请求分别 20.376 / 9.236 秒,首轮 20.420 秒;先保存 16,000 码点并正确返回 partial |
| 叶子速度 | 单请求 9.236..20.376 秒;七请求耗时之和 107.334 秒,含并发重叠,不能当作墙钟耗时 |
| 全实验时间 | 四次执行的进程内耗时合计 111.955 秒;首次请求至末次响应约 333.869 秒,包含检查、修改及人工重启间隔;另两次离线验证合计约 38ms |
| token | 提供商报告输入 188,377、输出 56,009;其中叶子提取为 34,771 / 35,852,其余用于评估及导航;未换算货币费用 |
| 续跑与无变化 | 三个已保存叶子在校验失败后复用;失败批次的已有响应重新本地校验,未再次请求。最终重启复用七批,无变化检查 4.362ms、模型调用 0;独立离线验证 19.826ms、模型调用 0 |
目标会话在旧探测后继续增长,先前记录的 119、123 条与本轮 129 条属于不同读取时点,不能混为同一分母。本轮覆盖完整目标会话,不等于覆盖旧全局区间的 830 条消息、63 个会话,也未测试任务、记忆和知识引用的全量采集。
独立检查点选择请求只看到两半完整原文,没有看到叶子结果,也未注入指定引文。模型返回 125 个检查点,超过要求的每半 24..32 个;排除 2 个原消息中不存在的逐字引文后,评分采用 123 个。第二半的消息回执虽有 64 项,但 ID 集合不匹配,因此仅声称全部 129 条已提交给评估器,不以模型回执证明它逐条读懂。
| 独立检查类别 | 有效检查点 | 模型判为保留 | 遗漏 / 失真 |
| --- | --- | --- | --- |
| 决策 | 46 | 46 | 0 / 0 |
| 修正 | 9 | 9 | 0 / 0 |
| 否定约束 | 1 | 1 | 0 / 0 |
| 未解决事项 | 5 | 5 | 0 / 0 |
| 助手陈述归属 | 62 | 62 | 0 / 0 |
模型评分未报告不受引用支持的事实;离线核验 123 项均有原消息的引用,118 项在所引用叶子中保留完整逐字检查引文,另 5 项依赖语义比较。评估器报告导航短摘要遗漏 69 项叶子细节,相关叶子仍完整保留。评估独立性仅指先从原文选择检查点、再比较输出,提取与评分使用同一模型;缺少人工金标准,否定类仅一项,跨两半的修正关系没有单独审计。这些数字不构成全部事实召回率或“无语义遗漏”的证明。
实际出现两次本地校验失败:重复引文导致叶子拒收,以及评估输出违反检查点数量与引用合同。成功批次在失败后仍可读取;原型为重复引文保存全部候选定位,对无效评估引文明确排除并计数。原始响应保留在临时目录,三次缓存读取不产生 HTTP。确定性测试另外覆盖批次失败、截断、取消和预算暂停后的恢复,未刻意制造提供商失败来增加付费调用。
执行入口为 `scripts/review-deepseek.cjs` / `scripts/review-deepseek.mjs`,共享原型为 `scripts/review-algorithm.mjs`。私有正文、来源、模型响应及 SQLite 均位于系统临时目录;仓库仅包含脚本、合成测试和不含正文的测量记录。产物目录:`C:/Users/jiang/AppData/Local/Temp/opencode/review-deepseek-20260924`。可公开计数见 `metrics.json`、`coverage.json`;私有依据见 `batches.sqlite`、`facts.private.json`、`navigation.private.json`、`gold-*.validated.private.json`、`evaluation-*.private.json`。
复现命令如下。`first`、`finish`、`verify` 启动独立进程,须待上一次退出再运行下一条;`status` 仅读取计数。现有输出目录会复用已保存产物;更换输出目录重跑会新增最多 12 次付费调用。
```powershell
$source = "$env:TEMP/opencode/review-full-20260924"
$output = "$env:TEMP/opencode/review-deepseek-20260924"
node scripts/review-deepseek.cjs first $source $output .env.deepseek.local 2
node scripts/review-deepseek.cjs status $output
node scripts/review-deepseek.cjs finish $source $output .env.deepseek.local 2
node scripts/review-deepseek.cjs verify $source $output .env.deepseek.local 2
node --test scripts/review-algorithm.test.mjs
npx vitest run src/main/assistant/supervision-model-pool.test.ts
npx eslint scripts/review-algorithm.mjs scripts/review-algorithm.test.mjs scripts/review-deepseek.cjs scripts/review-deepseek.mjs
```
10 项原型测试、3 项共享池测试及上述定向 ESLint 通过;没有运行全量测试、提交或推送。测试覆盖分页同序键、Unicode 重建和引用位置、空洞、预算暂停、失败后重开、无变化零调用、来源版本变化、输出拒收、取消以及导航不删除冲突叶子。该原型未接生产 IPC/UI、自动 checkpoint、全局多项目调度或多层汇总;真实请求绕过产品 ModelRuntime。生产实现仍存在下节记录的采集遗漏,未来实现边界见[分块调度设计](./review-scheduling-design.md#12-隔离原型的设计结论)。
### 2026-09-24 模型阶段超时、并发与设计边界
- 本次静态核对当前新代码:报告 `heartbeatReportTimeoutSeconds`、监督 `supervisorOrganizeTimeoutSeconds` 已接入共享校验、设置存储、App/设置表单及 Main 摘要器;整数 30..600 秒、默认 240。报告在等待池前冻结值,监督在摘要器入池前读取值,均在获槽及 Runtime 解析后才计时。完整控制及未覆盖环节见[控制清单](./review-scheduling-design.md#当前超时与调度控制清单已实现)。
- `supervisorModelConcurrency` 已接入同一设置链路,整数 1..4、默认 1;报告与监督共用 FIFO 池,普通聊天排除,降限不终止在途项。报告获槽后才领取,每次到期领取最多一条;租约为 `max(300, 报告超时 + 60)` 秒,实际请求开始前按运行 ID、owner、attempt、claimed 和未过期条件校验并刷新。报告提交或无变化后先释放槽位,再进入下游监督。尚无提供商全局并发、RPM、TPM 或监督层 Retry-After 控制。
- 本轮协作提供的新增验证记录:24 项并发相关测试、33 项租约相关测试及 typecheck、lint 通过。本次文档修订仅核对实现,没有重新执行这些测试;记录未附精确过滤命令,不补造命令或与此前批次相加。
- 实现代理报告:87 项测试、App/IPC 聚焦用例、另 19 项取消测试,以及 typecheck、lint 通过。本次文档修订未复跑这些命令,不将数量相加当作独立用例总数,也不推断全量测试已通过。
- 实现代理报告的真实远程取消验证耗时 30.75 秒,清理后本次拥有的进程为 0。该证据验证相应 Runtime 取消/清理路径,不证明完整分页调度、父级取消、批次持久化或跨重启续跑已经实现。
- 真实用户数据探测结果见下节。两次原始失败耗时与旧 240 秒时限一致,但缺少 abort 原因记录,尚不能证明证据规模是超时根因。
- [回顾分块调度设计](./review-scheduling-design.md)已按用户授权加入完整分页、保留叶子事实/来源、成功批次及位置持久化、预算/失败后继续和严格完成判定。上述调度、存储合同、状态及 UI 仍待实现;当前有界单次成功不等于整个时间区间完整覆盖。
### 2026-09-24 真实会话覆盖与模型小样本
原始数据库以 `readOnly` 和 `query_only` 打开,只读事务内分析,未初始化或迁移。用户所述安装路径不存在;实际安装位于 `D:\Program Files (x86)\GoodBuddy`,数据位于用户配置目录。统计使用当前数据库内 9 月 16 至 23 日记录,并非还原该次运行时的数据库快照。
| 测量 | 结果 |
| --- | --- |
| 区间内会话消息 | 830 条、63 个会话、5 个项目 |
| 手动采集器返回 | 202 条消息、70,491 个 UTF-16 单元 |
| 实际进入当前摘要器 | 100 条消息、17,714 个 UTF-16 单元;覆盖 9 个会话 |
| 目标会话 | 区间内 80 条中仅 20 条进入摘要器;正文覆盖 3,693 / 33,247,即 11.11% |
| 离线完整分块审计 | 123 条消息分为 127 块,重建差异为 0;未对这些块执行模型提取 |
| 只读采集耗时 | 手动 182ms、单批增量 375ms、整个探测 805ms |
整条消息遗漏发生在 SQL 数量限制及服务取前 100 项处;本样本进入摘要器的消息未再发生正文裁剪。100 条来源均缺少 `locator.messageId`,现有会话定位不能代替逐消息覆盖凭据。完整分页与消息定位仍为待实现项。
隔离模型实验使用两条已保存的真实样本,共 814 个 UTF-16 单元,经 `createDefaultModelRuntime` 调用 `gpt-6-astra`。3 次 HTTP 请求均为 200,未付费重试;两次提取耗时 34.665 / 35.413 秒,导航概述 6.703 秒,实验总耗时 76.813 秒。模型输入与原消息逐字一致,要求保留的两处引用及消息引用均核验通过,导航引用两项已保留事实;无变化复查耗时 5.99ms,模型请求为 0。原应用设置在完成时逐字节未变。
两处引文已在提示中明确指定,因此该实验仅验证小样本的引文保留、引用正确性与保留产物,不能证明自由提取完整性、全会话无失真或未来调度器性能。实验采用自定义单来源准入及直接模型 Runtime,未经过完整生产 IPC/UI。原库两次失败运行分别耗时 240.211 / 240.216 秒,监督结果仍为 0;样本成功不等于原问题已修复。
复核产物保存在本机临时目录 `opencode/review-fidelity-20260923-current`:`verification-current/coverage.json`、`live-resolved/live-metrics.json`、`verification-current/verification.json`。执行入口为私有临时脚本 `resume-supervision-probe.cjs` 和 `verify-supervision-probe.cjs`;核验退出码为 0。启动器等待曾超时,最终产物记录三次请求完成且进程随后退出,未因此重发请求。此前依赖加载失败均发生在请求发送前。会话正文及配置副本未加入仓库。
### 既有实现
- 共享监督者契约已接入 Main 服务,模型请求包含系统指令;证据总字符预算为 48,000,输出仍限制为 100KB,并校验本次证据集内的来源引用、输出局部实体 ID 和关系端点。手动回顾当前收集会话、任务、消息已有的知识引用和已确认记忆;记忆使用 `memory` 来源类型。
- SQLite schema 43 包含监督运行、结果、来源、故事线、事件、实体、实体关联和关系表,并保存每次结果的实体与关系内容。来源保存本地 library/document/chunk 或外部 locator 元数据;持久化与升级规则见[技术设计](./technical-design.md)。
- 初版监督者入口只有一个页面页头和四项顶层页签,原有计划、概览、建议及报告当时保留在设置页内部。2026-09-24 已按上文菜单调整迁移内容;手动运行仍通过 Supervisor 通道,未绑定自动计划时也可用。
- 工作回顾显示结果自身冻结的 scope/time range。故事线视图使用真实数据库结果,外围事件按实际时间逆时针排列并保留缺口,事件实体连线和来源过滤来自 Main 查询投影,图形不可用时仍可通过事件/实体/关系列表操作。
- `HeartbeatService` 在定时或手动心跳成功落库后调用同一个 `SupervisorService`,按心跳范围和回顾窗口自动保存监督结果;投影失败不会改写已经成功的心跳状态。
- 监督图谱提供 Main/Preload IPC 的来源读取、实体确认/修订/撤销和关系确认/移除;关系移除保存 `revoked` 状态,不删除来源,后续自动 run 不恢复相同身份的关系。
- 右侧助手栏已读取监督反馈,支持来源查看、继续讨论预览与确认发送,以及本地知识库实体写入预览和确认提交。
- 本地会话来源可按会话 ID 和发生时间读取上下文;没有当前会话时,继续讨论操作明确禁用。
- 应用中心已增加监督者开关,继续复用 `heartbeat` 导航 ID;关闭后已打开页面显示关闭状态并禁用工作区操作,不删除既有心跳计划或历史数据。导航显示同时遵守 enabled 和 pinned 语义。默认值及执行边界以[应用启停与执行](./logic-design.md#应用启停与执行)为准。
## 验证证据
### 2026-09-23 Desktop 0.13.13 侧栏布局回归
- 固定目标与刷新改用标题旁的共享图标按钮;目标显示会话/任务名称,缺失名称使用未命名文案。
对应 US-S25,行为见[会话侧栏](./ui-design.md#会话侧栏),未改变监督运行或取消逻辑。
- `RightAssistantSidebar.resize.test.tsx` 66 项和 `SupervisionCard.test.tsx` 4 项通过。
`tests/supervisor-layout.electron.test.ts` 的常规工作区场景通过,额外设置
`GOODBUDDY_SUPERVISOR_SIDEBAR=1` 后的侧栏场景也通过:480/300/200px、
浅深主题、长标题、34px 按钮、键盘焦点与无横向溢出,共 6 种布局。
- 完整 typecheck、lint 通过,真实模型调用 0 次。使用隔离组件 fixture,不代表真实
用户数据库端到端验收;发布验证范围见[候选记录](../../development/release-preparation-0.13.13.md)。
### 既有实现验证
- `npx vitest run src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx`:17 个测试通过,覆盖统一页签、无计划手动回顾、加载状态、图谱真实连线与来源过滤。
- `npx vitest run src/main/assistant/supervisor-service.test.ts src/main/assistant/assistant-database.test.ts src/main/application-settings-store.test.ts src/renderer/src/ApplicationCenter.test.tsx`:166 个测试通过。
- `npm run typecheck`:通过。
- `npm run lint`:通过。
- `git diff --check`:通过;仅有现有文件的行尾转换提示。
### 2026-09-23 页面与视觉验证
- 生产页面按 [UI 设计](./ui-design.md) 使用三栏工作台、真实实体名称、逆时针事件环、实际关联连线、选中强调与来源详情。颜色复用现有图谱令牌,未修改全局主题。密集数据按批显示,列表保留全部返回记录;事件浏览不还原历史实体状态。
- 设置只保留一个一级标题,缺少监督者 bridge 不再回退旧概览;无计划手动回顾、项目/时间范围、来源关联过滤均有组件回归。来源异步读取期间切换事件时,迟到正文不会出现在无关联事件下。
- `npx vitest run tests/supervisor-layout.electron.test.ts src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/main/assistant/supervisor-service.test.ts src/main/assistant/assistant-database.test.ts`:134 项通过。Main 测试使用隔离数据库和注入摘要器;视觉入口渲染生产 React 组件,未读取真实用户数据或调用付费模型。
- Electron 实测 1440、1024、390px 与浅深主题,页面、壳层和内容容器均无横向溢出;1440px 三栏实测为 216 / 863 / 280px。窄屏画布内部滚动保留可读字号,缩放后 SVG 文字不低于 11px;另验证 9 个容器临界宽度、长标题、80 个同时间事件、40 个实体、来源详情、设置表单、加载/空/错误/缺 bridge 状态。
- 截图与 `measurements.json` 输出到本次指定的临时目录 `opencode/supervisor-visual`。代表文件为 `selected-source-1440.png`、`light-1024.png`、`light-390.png`、`dark-1440.png`、`dense-selected.png`、`selected-source-390.png`、`settings-plans-390.png`。可复用的验证和交互预览入口见 UI 设计末段。
- 全量 `npm test`:416 个文件通过、9 个跳过、1 个失败;4902 项通过、67 项跳过、1 项失败。失败为 `local-runtime-reuse.test.ts` 的十项目 OpenCode 压缩断言(`compacted: false`),单独复跑该文件的 2 项均通过。本次未改动 Runtime 实现,未将这次全量运行记为全绿。
- 页面收尾后重新执行 `npm run typecheck`、`npm run lint` 通过。App 已在上述全量测试中通过;一次包含 App 的额外定向命令超过终端 120 秒限制,随后缩小到上述 5 个相关文件完成验证。
## 当前边界
### 2026-09-24 算法设置文档补充
已静态核对共享设置合同、设置存储及隔离原型参数,补充[设置配置清单](./review-scheduling-design.md#4-设置配置清单)和 [UI 交互目标](./ui-design.md#算法配置设置目标)。本次仅更新文档;新增分页、分块、预算、续跑及额度控件均未实现,原型值不作为产品默认。未运行代码、测试、探测或模型调用,未修改应用配置和用户数据库。
### 2026-09-23 Main 与侧栏定向修复
- 已修复:继续讨论不再把数组写入要求字符串的 `serializedContexts`;请求省略 Runtime 时复用会话选择。真实 SQLite 入队后,生产队列解析器成功派发请求。
- 已修复:每次结果独立分配实体和来源 UUID,映射事件、关系、实体变化及来源引用。两次重叠 run 和跨项目相同模型 ID 均有 SQLite 回归,用户修订的名称和说明保留,所有结果均可读取自己的来源及 locator。schema 41 升级并重复打开后保留旧记录和确认状态。
- 已修复:知识库预览与提交均检查本地库及实体所属库;侧栏确认框展示实际返回内容,取消与写入失败后可重新预览。测试覆盖真实本地实体读写,外部只读及提交前归属变化由注入状态验证。
- 定向验证:`npx vitest run src/main/assistant/assistant-database.test.ts src/main/ipc.test.ts src/renderer/src/RightAssistantSidebar.resize.test.tsx src/main/assistant/supervisor-service.test.ts -t 'supervision|shares bounded evidence'`,4 个文件中选中的 9 项通过,其余 289 项未运行。本轮未运行全量测试、typecheck、lint 或真实模型请求;前文历史验证不能代替本轮验证。
- 当时未修复的双边时间范围及记忆背景语义已由下节定向修复覆盖。跨 run 实体识别由本轮候选身份实现补齐;旧实现已经覆盖或忽略的历史内容无法从现有记录自动还原。
### 2026-09-23 第 4 问题:双边时间范围
- 已接通真实 `supervisionRun` IPC handler → collector → SQLite → 摘要输入 → 结果保存。监督者显式传递请求时间区间,不再扩大到一小时;消息与会话在 LIMIT 前按闭区间筛选,任务按创建或完成时间筛选。所选项目复用存在且 active 的检查;普通 Heartbeat 不传明确区间时仍使用原下界行为。
- 记忆证据使用真实 `updated_at`,locator 保留 `created_at`/`updated_at` 并标明当前背景;摘要指令禁止据此推断历史内容。任务的当前状态也不作为历史状态快照。实现规则与剩余容量边界见[技术设计](./technical-design.md)。
- 定向验证:`npx vitest run src/main/ipc.test.ts src/main/assistant/heartbeat-database.test.ts -t 'collects supervision within the exact UI timeRange|builds one bounded snapshot across only the selected projects'`,2 个文件中选中的 3 项通过,133 项未运行。两项新 IPC 测试分别覆盖 global/projects:15 分钟精确范围与端点、25 条区间后消息及 21 个区间后会话不挤掉区间消息、旧任务期间完成、期内创建但期后完成的任务选用创建时间、记忆非采集时刻、archived 项目拒绝。已有 Heartbeat 项目快照用例通过。
- 首两次定向运行因新增测试 fixture 缺少 `description`、消息 `state` 失败,补齐实际契约后通过。本轮所改 tracked 文件的 `git diff --check` 通过,仅有行尾转换提示。验证使用真实隔离 SQLite 与生产 IPC/collector,模型 Runtime 为测试替身;未运行 UI/Electron、全量测试、typecheck 或 lint,真实模型调用为 0。未改 Renderer/CSS、数据库 schema、远端 Agent/Runtime,不涉及独立远程执行逻辑;未提交。
### 2026-09-23 结果定位、目标过滤与候选身份
- 对应 US-S02、US-S05、US-S10、US-S13、US-S25:overview 返回稳定结果/故事线 ID;Main 校验 graph 请求归属,并按结果读取当次事件、对象与来源。历史选择和图谱共享结果定位,请求序号阻止迟到响应覆盖新选择。
- 侧栏 overview 通过类型化 Conversation/Task 目标查询;SQLite 在 LIMIT 前同时校验匹配来源和项目范围。无匹配结果不展示全局第一条。卡片展示结果范围和时间区间,目标切换清空来源与预览并忽略旧请求。
- tasks 工作栏实例新增可持久化的会话/任务监督绑定;固定、切换聊天、取消固定均连接到实际卡片查询,任务列表仍使用原项目范围。继续讨论预览展示真实发送目标,回调显式传会话 ID,Main 复用该会话配置。固定任务找不到关联会话时不启用发送。
- 模型只能用 `persistedId` 引用 Main 提供的同 scope 候选 UUID;服务与保存事务分别校验。未显式引用候选的实体和所有来源继续分配新 UUID。SQLite 回归覆盖重复 run 复用、不同项目拒绝、裸模型 ID 不合并、确认/修订字段及关系理由保护、移除关系后不自动恢复。
- schema 43 升级保存现有可定位的结果成员,后续 run 不改写旧结果内容;测试从 schema 42 升级并重复打开,比较历史图谱与来源,包括没有直接来源引用、但两端实体归属明确的关系。已有 schema 41 保留数据用例继续通过。缺失归属、曾被覆盖的描述仍无法重建。
- 定向测试共覆盖 32 个不同用例,最终均通过。主命令为 `npx vitest run src/main/assistant/supervision-history.test.ts src/main/assistant/assistant-database.test.ts src/main/assistant/supervisor-service.test.ts src/main/ipc.test.ts src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/SupervisionCard.test.tsx src/renderer/src/RightAssistantSidebar.resize.test.tsx src/renderer/src/HeartbeatCenter.test.tsx -t '[sS]upervis'`:29 项通过,2 项失败来自新增同步 IPC 拒绝断言误用 `.rejects`。修正后仅复跑 `src/main/ipc.test.ts src/shared/workbar-contracts.test.ts -t '[sS]upervis'`,5 项通过,包含新增工作栏绑定契约用例。期间修正了旧侧栏 fixture 缺少目标、迁移比较未忽略成员顺序和图谱按钮查询歧义。
- 收尾增加同来源但项目范围不匹配的过滤断言,以及无直接来源引用的关系迁移断言;仅复跑 `src/main/assistant/supervision-history.test.ts src/main/assistant/assistant-database.test.ts -t supervision`,5 项通过,其余 103 项未运行。
- `npm run typecheck` 运行一次,通过。对本轮涉及的 TS/TSX 执行一次定向 ESLint,0 error、1 条 ref 清理 warning;调整后仅复查该文件及新增契约测试,迁移收尾只复查数据库与对应测试,均为 0 error、0 warning。未重复检查整个 App。`git diff --check` 通过,仅有现有行尾转换提示。
- 本轮未跑全量测试或 App 整文件,未启动 Electron 或调用真实模型;模型请求数为 0。验证包含生产 IPC/collector 与真实隔离 SQLite,以及真实 React 组件的异步交互测试,不能据此声称已完成真实模型或完整桌面端到端验收。未修改 Agent/远程执行协议或 Runtime 生命周期;此次改动在桌面 Main 的监督者输入、存储和查询层生效。全部 dirty 改动保留,未提交。
### 2026-09-23 侧栏结果直达图谱
- 对应 US-S25:SupervisionCard 有匹配结果时提供中英文“在图谱中查看 / View in graph”原生按钮。App 通过显式回调接收 resultId,遵守现有离页检查并打开 heartbeat 路由,向 HeartbeatCenter / SupervisorWorkspace 传递类型化导航请求;没有新增页面或 window 事件。沿用 schema 43 与现有 graph IPC,未改 Main、Preload 或存储。
- 图谱页签、键盘焦点和历史结果选择共用现有页面状态;同一结果再次点击、不同结果切换及 keepalive 再进入均重新定位,固定监督目标保持。导航切换立即清空旧图谱和详情;overview、graph、来源及操作的迟到响应不会改写新选择。overview 没有返回指定结果时仍按该 ID 请求图谱;失败可重试原 ID,不回退默认最新结果。
- 新增 7 个定向用例,最终均通过。首轮命令:`npx vitest run src/renderer/src/SupervisionCard.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/App.test.tsx -t 'graph navigation'`,3 项通过、2 项因测试使用旧文案失败。修正为实际“工作栏”“历史结果”文案并补充两项竞态测试后,仅运行 `src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/App.test.tsx -t 'graph navigation'`,5 项通过、296 项未运行。请求状态收尾后仅复跑 SupervisorWorkspace 的同一筛选,4 项通过、11 项未运行。
- 测试覆盖真实 React 组件与 App 路由接线:首次只请求侧栏结果、固定目标、同结果重复点击、不同结果、keepalive 页面实例不变、历史选择同步、页签方向键及焦点、中英文/空态入口、旧 overview/graph/来源迟到、指定 ID 不在 overview 和失败重试。IPC 在这些 UI 测试中为替身,未声称真实桌面端到端验收。
- 本轮涉及的 10 个 TS/TSX 文件定向 ESLint 通过,0 error、0 warning。`npx tsc --noEmit -p tsconfig.node.json` 与 `npx tsc --noEmit -p tsconfig.web.json` 各一次有效源码检查通过;此前附加 `--incremental false` 的启动因 composite 配置报 TS6379,未执行源码检查。`git diff --check` 通过,仅有现有行尾提示。
- 未运行 App 整文件、全量测试、全量 lint、Agent typecheck、Electron 或真实模型请求;模型调用为 0。此次仅修改桌面 Renderer 导航和异步展示,不影响 Agent/远程 Runtime。保留全部其他 dirty 改动,未提交。
### 2026-09-23 自动监督设置
- 对应 US-S22 至 US-S24:当时设置直接显示完整自动监督配置,并保留运行概览、建议、报告与记录次要面板;2026-09-24 已迁移这些面板并移除内部导航。界面统一中英文命名,保留内部 heartbeat 标识和已有内容。无计划时明确显示仅支持手动回顾,填写草稿不触发保存或运行。
- 核对共享 recurrence 合同及实际调度器后,仅展示已支持的每日、每周指定时间;页面明确说明不支持分钟间隔。编辑保留原时区和暂停状态,计划列表显示范围、周期、时区、回顾窗口与保留期限,继续提供编辑、暂停、恢复、运行及确认删除。
- 已核对 App、Preload、Main 和 HeartbeatService 的既有 CRUD 接线,本轮未修改调度器或创建默认计划。保留工作区原有默认关闭及 Main 启用检查改动。
- 定向测试:`HeartbeatCenter.test.tsx` 23 项、`application-settings-store.test.ts` 50 项通过;`heartbeat-recurrence.test.ts`、`heartbeat-service.test.ts`、`heartbeat-database.test.ts` 合计 17 项通过;`ipc.test.ts -t 'heartbeat enabled gates'` 4 项通过、128 项未运行。覆盖中英文直接入口、每日/每周提交、多项目、无效窗口、失败保留草稿、时区及暂停状态、各项计划操作,以及默认关闭、无计划不运行和启停行为。首轮整文件运行发现原有顶层图谱测试未清理 DOM,补充文件级 cleanup 后通过。
- `tests/supervisor-layout.electron.test.ts` 通过:生产 React 组件的自动监督设置在 1440、1024、390px 和浅深主题下测量无横向溢出,周频表单全部控件边界可达,默认配置页及未配置说明存在。该测试使用隔离 fixture;未将其表述为真实模型或用户数据库端到端验收。
- `npm run typecheck` 通过;本轮 9 个 TS/TSX 文件及 Electron driver 的定向 ESLint 通过。按要求未运行全量测试,真实模型调用 0 次;没有 Agent 或远程 Runtime 改动,未提交或推送。
### 2026-09-23 活动页签
- 对应 FR-S10、US-S26:schema 45 复用运行表保存监督开始、完成和失败,并按真实心跳 ID 合并报告和下游监督。手动失败、报告完成但下游运行中/失败、回调失败、应用重开后的未完成记录均有真实 SQLite 测试。没有新的执行引擎或取消协议。
- 类型化活动 IPC/Preload 已接入生产页面;关闭应用不查询,页签或路由离开停止刷新并忽略迟到响应。每页 50 条,串行轮询,读取失败可重试。历史结果通过 resultId 定位,保留现有自动监督设置。
- `npx vitest run src/main/assistant/supervision-activity.test.ts src/main/assistant/supervisor-service.test.ts src/main/assistant/heartbeat-service.test.ts src/preload/supervision-activity.test.ts src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:7 个文件、58 项通过。
- `npx vitest run src/main/assistant/supervision-activity.test.ts src/main/assistant/heartbeat-database.test.ts src/main/assistant/supervision-history.test.ts`:3 个文件、14 项通过,包含 schema 44 升级及重开保留。
- `npx vitest run src/main/ipc.test.ts -t 'supervision|heartbeat enabled gates'`:8 项通过、124 项未运行。新增活动查询首次运行暴露 UNION 排序列缺少别名,修正后真实 SQLite 和 IPC 均通过。
- `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过,新增活动页 1440、1024、390px 浅深主题测量,长错误、元数据和操作无横向溢出,短窗口页签保持高度。测试使用生产组件和隔离 fixture,没有读取用户库。
- `npm run typecheck` 通过;定向 ESLint 通过。未跑全量测试或真实模型,模型调用 0 次。没有修改 Agent/远程 Runtime;工作区其他改动保留,未提交或推送。
- App 定向命令 `npx vitest run src/renderer/src/App.test.tsx -t 'Supervisor|graph navigation'`:5 项通过、281 项未运行。刷新互斥及焦点收尾后,`SupervisorActivity.test.tsx`、`HeartbeatCenter.test.tsx`、`supervisor-service.test.ts` 合计 31 项通过;22 个相关 TS/TSX/MJS 文件的定向 ESLint 为 0 error、0 warning。
- 结束时间收尾:手动心跳使用真实报告完成时间,下游失败单独记录结束时间。`npx vitest run src/main/assistant/supervision-activity.test.ts src/main/assistant/heartbeat-service.test.ts src/main/assistant/heartbeat-database.test.ts src/main/assistant/supervision-history.test.ts`:4 个文件、21 项通过,新增受控时钟用例区分 10:00 开始、10:02 报告完成、10:03 下游失败。随后 Node typecheck 和这 3 个改动源码/测试文件的 ESLint 通过。
- 已存在的手动失败无法补回;旧心跳没有冻结范围和关联 ID,保留缺失提示。心跳仍按原保留期限清理,监督结果独立保存。取消和人工修改时间序列审计未实现;`no_change` 的后续接入见下节。
### 其他边界
- 新批次来源读取按 messageId 返回已保存片段及版本;旧历史缺少消息 ID 时保留会话/时间定位。本地知识引用通过现有 `KnowledgeService` 解析文档/分块,失效时显示不可用;外部引用只使用已保存 locator,不调用远端全库。
- 继续讨论和知识库写入使用现有本地会话队列与知识库实体接口,并要求先预览、再由用户确认提交。侧栏已支持结果直达图谱,尚无可编辑的上下文预览。
- 关系移除保留撤销状态及原始来源;界面没有提供恢复入口。
- 候选集最多 100 个实体,复用依赖模型显式选择;不会自动合并历史重复实体或跨 scope 合并。结果级历史内容已保存,事件滑块仍不重建逐事件的实体演变。Experiment 和完整图谱回放尚未实现;批次暂停/继续已接通。
## 2026-09-23 自动增量验证
对应 FR-S4、FR-S10、US-S27。自动心跳报告与下游监督均已接入来源版本和处理位置;无变化跳过模型并保存 `no_change`。消息修订、任务状态、知识引用变化分别参与增量判定,已确认记忆只作背景。实现、预算和删除语义见[自动增量收集](./technical-design.md#自动增量收集)。
- schema 46 增加最小来源进度与消息版本,扩展已有心跳状态 CHECK。来源片段进度与对应结果原子保存,模型或事务失败不推进。未送入模型的来源及正文余段保留待处理资格。
- `incremental-review.test.ts` 使用真实 SQLite 和生产服务,覆盖重开数据库、重复零调用、消息修订后复用实体 ID、105 个长来源分批读完、模型及结果事务失败、任务状态、范围排序、记忆背景、知识引用、报告保存失败和 schema 45 有数据升级。
- 真实模型使用已有加密默认文本配置,经 `RuntimeSettingsStore` 和 Electron `safeStorage` 读取隔离副本,生产 `createDefaultModelRuntime`、`HeartbeatService`、`SupervisorService` 和 SQLite 执行。`tests/incremental-review-live.electron.test.ts` 于本日通过:首次自动运行 2 次 HTTP 请求,重复自动运行 0 次,修改消息后的监督回顾 1 次,再次监督检查 0 次,总计 **3 次真实文本请求**。工具与附件均为 0。
- 真实输出通过结构、来源及实体引用校验,修改后的 Atlas 实体至少复用一个原持久化 ID;无变化检查前后图谱相同。测试检查原设置字节未变、外键与数据库完整性。隔离数据库、配置快照和加密 Local State 随测试目录删除,未改用户计划或数据库。
- 实时测试命令:设置 `GB_REVIEW_LIVE_SETTINGS` 为已有运行时配置路径后,执行 `npx vitest run tests/incremental-review-live.electron.test.ts`。无该环境变量时跳过;每次测试最多 3 个真实请求,不记录凭据。
- 最终聚焦验证:10 个服务、SQLite、Preload 和 Renderer 测试文件共 76 项通过;`npx vitest run src/main/ipc.test.ts -t 'supervision|heartbeat|application enable'` 另有 9 项通过,124 项按过滤条件跳过。`npm run typecheck`(Node、Agent、Web)和本次改动的 19 个 TypeScript 文件 scoped ESLint 均通过。
真实测试覆盖来源较短的单次自动回顾、重复检查及修改后的实体复用。长正文和大量来源预算由确定性 SQLite 回归验证;没有执行全量测试或真实用户数据库升级。事件驱动、清空或重建图谱和语义近似去重不在本次实现范围。
- 知识正文条目与监督实体仍是两个模型,当前不会把监督实体当作知识正文条目。
@@ -0,0 +1,348 @@
# 监督回顾分块调度设计
日期:2026-09-24。状态:生产分块、持久化续跑、设置及活动入口已接线;27 条消息的完整会话通过真实模型验证,较早的 129 条消息实验仍未完成。当前实现以第 0 节为准,第 1 节保留改造前基线,第 2 至 11 节保留目标设计,第 12 节为早期隔离原型证据。
本文针对全局七天 `supervision:run` 出现 `AbortError` 的反馈,设计完整分页、证据分块、模型任务调度和逐层汇总。用户已明确要求预算耗尽或失败后保留成功批次及位置,重启后可继续,不重复计算成功叶子。生产接线复用 `SupervisorService`、现有数据库及 `review_checkpoints`;不另建定时调度器。schema 47 的实现与目标差异见第 0 节。
产品范围仍由[监督者产品设计](./supervisor-prd.md)定义;本设计对应 FR-S4、FR-S6、FR-S10 及 US-S12、US-S26、US-S27。既有规则见[逻辑设计](./logic-design.md#自动增量与手动重看),实现架构见[技术设计](./technical-design.md),已验证事实见[实施进度](./progress.md)。第 0 节以当前代码为准,其余目标须逐项核对实现状态;大范围回顾仍需独立性能验证。
## 0. 生产接线与剩余边界
Main 的手动 IPC 和心跳完成回调均使用 `createProductionSupervisorService`,该工厂连接 `SupervisorService`、`SupervisionReviewStore`、共享模型池及实际 ask Runtime。生产路径不再调用旧的 20 会话 × 20 消息 collector,也不使用旧服务分支的 100 条 / 48,000 字符裁剪。旧的有界服务注入方式仍供原有测试和实验脚本使用;心跳报告本身继续使用原报告预算。
### 已接入的调度与存储
schema 47 增加运行配置、来源清单、成功批次、导航节点四张表和一个实时来源视图。迁移只创建新结构,保留此前迁移、结果及用户数据。来源清单通过 `(project_id, conversation_id, sequence, source)` 稳定键分页,SQL 事务冻结范围内的来源身份、版本、长度及自动起点;清单不保存正文。项目和会话由来源页共同枚举,不另设全量 JavaScript 目录数组。正文随后按主键和有界 `substr` 读取。
派发按项目及项目内会话轮转,每个会话同一轮至多一个批次,会话内按 sequence/source 排序。批次跨数据库页累计;长消息未装入部分保留待处理。每片最多 8,000 UTF-16 单元、每批最多 50 片,均为封块条件,不是范围上限。位置采用零基、左闭右开的 Unicode 码点;消息 locator 保存 messageId、conversationId、projectId、role、sequence、source、revision、start、end、length。知识引用另保留原 locator 及所属消息,任务使用当前状态和区间内创建/完成时间。
成功叶子的输出、原文片段和连续 offset 在同一事务提交。失败或软预算到期保留此前批次。继续操作使用同一 run ID、时间范围和分块配置;重启后可继续。手动运行不读写自动 checkpoint。自动运行先恢复同 scope 的未完成 run;仅在全部来源连续覆盖、导航完成、最终发布事务成功时写入 `stage=supervisor` 的 checkpoint。空范围无需模型。
叶子输出仍使用现有事件、实体、变化和关系契约,并校验引用与候选身份。会话、项目、scope 使用二叉递归合并导航摘要,单子透传,总共 N 个叶子至多 N-1 次合并。已成功导航节点按孩子 ID 保存,发布失败后可复用。叶子、导航及最终结果的生成文本不设字段字符上限,事实数组不设独立数量上限;导航节点仍不得生成替代叶子的事件或实体。发布事务逐页读取叶子,保存既有历史/图谱结构,保持历史定位、来源及用户确认状态;完整叶子同时通过独立批次接口分页读取。
单次 Runtime 响应继续在接收过程中限制为 100,000 字节,防止响应缓冲持续增长。超过边界时停止接收,明确提示本批次未保存、已保存批次可继续使用;不裁剪后冒充成功结果。该边界与摘要字符数不同,不增加摘要长度设置。监督旧摘要读取、导航输入、保存和恢复均保留全文。身份格式、来源归属、字段类型与导航空事实数组的校验继续生效;原有数据库字段及已保存结果无需迁移。
导航使用独立的严格输出 schema,要求 `summary`、`changeDigest`、`openItems`;四个事实数组允许省略或为空,非空数组、错误类型和额外字段仍拒绝。验证通过后才在内部补空数组,以复用现有导航存储形状。叶子继续要求四个数组齐全,不将缺字段当作成功;来源、事件实体、变化实体和关系端点均校验。继续时还会核对已保存叶子的引用,导航不覆盖其独立事实。
`supervision:pause` 将父 signal 传入排队及在途模型;取消后的输出不提交。`supervision:resume` 恢复已保存 run;`supervision:batches` 默认每页 10 批、最多 20 批,返回完整叶子及来源。活动返回已保存批次、码点数、剩余来源、当前阶段、排队或执行中的模型任务数及已保存配置。软预算到期会等待已派发批次各自结束,随后显示 `paused`;完成判定不依赖摘要措辞。
当前阶段以 `phase` 写入既有 `supervision_review_runs.state_json`,取值为 `collecting / extracting / summarizing / saving`;无新表或迁移。它记录服务最后进入的阶段,失败和重启后保留;完成仍由 `supervision_runs.status` 的发布事务决定。活动的 `navigationNodes` 从已保存导航表计数,`inFlight` 仍来自进程内计数。旧运行没有 phase 时不回填推测值。清单和最终发布为同步事务,短阶段不保证被轮询看到,清单事务失败也不会留下部分阶段记录。批次接口只有成功记录,没有逐个失败/在途任务或项目、会话聚合状态;界面仅按分页返回的真实归属组织详情。
### 当前设置合同
| 字段 | 默认 | 整数范围 | 生效规则 |
| --- | --- | --- | --- |
| `supervisionReview.pageSize` | 50 | 1..200 | 每次来源元数据页条数;不改变最终集合或批次边界 |
| `supervisionReview.batchCharacters` | 8000 | 1000..16000 | 每批新正文 UTF-16 单元,不含提示、候选及 JSON |
| `supervisionReview.batchMessages` | 20 | 1..50 | 每批不同消息数;任务等无消息 ID 的来源单独计数 |
| `supervisionReview.executionSeconds` | 300 | 30..3600 | 每次执行软预算,继续后重新计时;到期停止新增工作 |
| `supervisorOrganizeTimeoutSeconds` | 240 | 30..600 | 创建回顾时冻结,节点获槽并解析 Runtime 后计时 |
| `heartbeatReportTimeoutSeconds` | 240 | 30..600 | 维持报告发起时冻结及既有租约规则 |
| `supervisorModelConcurrency` | 1 | 1..4 | 回顾保存接纳并发;共享池上限仍随应用设置实时变化,实际执行取较小值 |
应用仍默认关闭。新参数由共享 Zod 合同、ApplicationSettingsStore、App 设置回调和 Renderer 表单保存;没有放置尚未生效的高级字段。
### 未完成与实测限制
- 真实验证覆盖一个 27 条消息的完整会话;较早的 129 条消息实验在第二次输出 JSON 无效后停止。两实验合计 5 次付费请求,没有重复付费或回退。范围及缓存复核说明见[进度](./progress.md#2026-09-24-完整会话生产验证与收尾)。
- 来源被修改或删除时保留旧批次并明确失败,标记需要新建回顾,活动页停止提供无效的继续按钮;下一次自动检查可创建新 run,不会永久重复该失败。尚未实现仅使受影响叶子和父节点失效后重算。清单记录冻结版本,不保存全库正文快照。
- 分块配置、超时、接纳并发及算法版本已保存。实际默认 Runtime、模型及候选背景仍在执行时解析;未实现跨重启模型/提示词版本固定,也未实现共享池排空后切换冻结并发的目标规则。
- 元数据清单和最终发布仍是同步 SQLite 事务,缺少大库延迟验证及可中断来源读取。批次详情已分页,既有历史图谱查询仍返回整份结果;大图谱的响应大小尚未改造。
- 没有 token/费用预算、提供商 RPM/TPM、Retry-After 冷却、执行硬期限、独立清理期限或逐层可配置超时。默认正文预算不保证任意模型上下文容量;超长输出、非法 JSON 和引用错误会失败,保留已成功批次。
- 片段以码点边界切分,尚未增加句界优先和重叠背景。事实保留指保留模型已产出的结构化记录,不证明语义无遗漏;生产契约校验引用 token,不要求原型的逐字引文和 reviewedTokens 回执。提供过的 locator.source 仅在本节点唯一对应一个片段时可作为引用别名;歧义及未知来源拒绝。
## 1. 改造前代码事实
下列旧草案行号仅作定位参考,工作区代码更新可能使其偏移;以所列函数和下方本次核对的控制清单为准。
| 环节 | 当前行为与代码依据 | 对调度设计的影响 |
| --- | --- | --- |
| 手动入口 | [ipc.ts:7917](../../../src/main/ipc.ts#L7917) 校验应用启用和维护状态,等待 `SupervisorService.run()` 返回 | 当前没有提前返回 runId 的开始接口,也没有监督取消接口 |
| 运行串行 | [supervisor-service.ts:124](../../../src/main/assistant/supervisor-service.ts#L124) 用一个 `pending` Promise 串行执行整个服务的请求,出队后才创建运行 | 当前不是按项目并发;排队时间不在摘要定时器内,也尚无排队活动记录 |
| 单次摘要 | [supervisor-service.ts:135](../../../src/main/assistant/supervisor-service.ts#L135) 最多取 100 条证据,每条最多 8,000 个 JS 字符,总正文最多 48,000,再调用一次 `summarize` | 没有分块队列或递归汇总;48,000 不包括实体候选、系统指令、旧摘要和 JSON 开销 |
| 模型超时 | [ipc.ts](../../../src/main/ipc.ts) 的监督摘要器读取 `supervisorOrganizeTimeoutSeconds`,默认 240 秒,超时带明确原因 abort;流结束检查 signal 和 `done`,finally 释放会话 | 新控制已实现,完整清单见下节;不能据此反推原始 AbortError |
| 底层模型请求 | [model-runtime.ts:206](../../../src/main/agent/model-runtime.ts#L206) 默认请求超时十分钟;[createRequestSignal](../../../src/main/agent/model-runtime.ts#L584) 传播上层 signal 并设置底层超时;[retryModelStream](../../../src/main/agent/model-runtime.ts#L1891) 对部分网络失败重试 | 上层默认四分钟可以先到,当前已可配置;调度层不能再盲目叠加网络重试。注入 Runtime 的实际类型和配置仍须运行时核实 |
| 手动采集 | [ipc.ts:7120](../../../src/main/ipc.ts#L7120) 传入精确时间范围;[buildHeartbeatInput](../../../src/main/assistant/assistant-database.ts#L8168) 在时间和范围过滤后最多取 20 个会话、每会话最后 20 条消息、100 个任务、100 条记忆 | SQL 层未入选的证据在服务分块前已经遗漏,单改 `summarize` 无法补齐 |
| 自动采集 | [collectIncrementalReview](../../../src/main/assistant/assistant-database.ts#L8333) 按阶段、scope、来源版本和位置过滤,再按发生时间和来源 ID 排序;最多 100 个来源,各取后续 2,000 个 Unicode 字符 | 已有片段定位与进度能力;查询需要扩展运行内位置,避免提交前反复读到同一片段 |
| 自动预算 | [ipc.ts:7115](../../../src/main/ipc.ts#L7115) 监督新证据预算 44,000 个 JS 字符;[heartbeat-service.ts:227](../../../src/main/assistant/heartbeat-service.ts#L227) 报告预算 12,000;[reviewBackground](../../../src/main/assistant/assistant-database.ts#L8419) 最多四条各 500 字符记忆,旧摘要最多 2,000 字符 | 自动路径与手动路径的证据集合、预算和游标语义不同 |
| 输出及身份 | [supervisor-service.ts:71](../../../src/main/assistant/supervisor-service.ts#L71) 检查 100KB 输出与来源引用;[同文件:167](../../../src/main/assistant/supervisor-service.ts#L167) 校验候选身份;[共享输出契约](../../../src/shared/supervision-contracts.ts#L62) 限制每对象 20 个来源、40 个事件、40 个实体、80 个变化和 80 个关系 | 多个合法子输出拼接后不一定合法;汇总必须同时约束字节、数量与引用 |
| 保存 | [saveSupervisionResult](../../../src/main/assistant/assistant-database.ts#L8531) 一个事务保存运行状态、结果、来源正文片段、图谱快照及进度,来源重新分配 UUID | 可复用一次结果事务;当前 `coverage_json` 只有证据条数,不能表达完整覆盖 |
| 自动入口 | [heartbeat-service.ts:220](../../../src/main/assistant/heartbeat-service.ts#L220) 先处理报告,再经 [ipc.ts:1914](../../../src/main/ipc.ts#L1914) 调同一监督服务;报告 `no_change` 仍检查监督阶段 | 报告和监督是独立消费阶段,报告成功不能替监督推进游标 |
| 活动 | [活动契约](../../../src/shared/supervision-contracts.ts#L27) 没有 `partial`、`cancelled` 或子任务计数;[数据库活动投影](../../../src/main/assistant/assistant-database.ts#L8501) 按 heartbeat run ID 合并;[SupervisorActivity](../../../src/renderer/src/SupervisorActivity.tsx#L21) 仅在激活时轮询 | 展示新进度需要明确扩展契约,不能只添加状态文案 |
初稿阶段没有检查真实运行日志或用户数据库。后续只读探测记录了两次约 240 秒的失败,但没有复现该次请求或取得明确 abort 原因,尚不能确定是四分钟定时器或证据规模导致超时。诊断记录应包含运行 ID、阶段、耗时、输入规模、错误类型和 abort 原因;不记录证据正文、模型凭据或完整提示词。
当前监督输出先累积字符串、再检查 100KB;它不像心跳摘要器那样在流中检查字节数。报告和监督均将池 signal 与局部超时 signal 合并;清除本地数据会取消池内等待和在途工作,IPC 释放时 dispose 池。该生命周期取消不等于已有用户取消入口或分块 run 父级取消。以上都是静态实现差异,不是本次真实日志结论。
### 是否截断丢数据
手动回顾会遗漏本次分析输入:SQL 的数量限制,以及服务的 `.slice(0, 100)`、单条和总字符裁剪均会排除内容;任务和记忆排在会话证据之后,还会竞争余量。未入选内容没有自动续读机制。原始消息并未因此从数据库删除,但结果不能代表七天全部记录,裁剪后的来源也不能冒充完整消息。
自动回顾的未读片段仍保留待处理资格。[supervisor-service.ts:174](../../../src/main/assistant/supervisor-service.ts#L174) 只将完整进入摘要输入的片段 checkpoint 交给保存事务,模型或保存失败不推进。这里的“完整”指采集片段,并非整条消息。来源若在后续执行前离开滚动窗口,就不会被自动补读,因此“不会误标已处理”不等于“未来必定覆盖”。
目标是不再静默裁剪,把单次上限改成分页、分块和暂缓派发的依据,直至冻结范围内全部项目、会话、消息及其他入选来源处理完毕。模型提取仍可能遗漏原文含义,但已产出的叶子事实、事件、冲突及来源必须完整保留、可分页读取。摘要只用于导航,不能替代叶子结果;“原文已处理”也不等于模型识别了全部事实。
### 当前超时与调度控制清单(已实现)
以下是当前代码事实,不是后文拟议的多层调度预算。设置暴露两个阶段超时和一个监督并发数。
| 控制 | 实际值、读取时点与范围 | 代码依据与边界 |
| --- | --- | --- |
| 心跳报告模型阶段 | `heartbeatReportTimeoutSeconds`,整数 30..600 秒,默认 240;`runNow` / `processDue` 在等待池之前读取并冻结,获得槽位后才领取数据库任务 | [共享设置](../../../src/shared/application-settings-contracts.ts)、[设置存储](../../../src/main/application-settings-store.ts)、[HeartbeatService](../../../src/main/assistant/heartbeat-service.ts);手动及自动均适用,排队期间修改设置不改变该次报告值;到期领取每次最多一条 |
| 监督整理模型阶段 | `supervisorOrganizeTimeoutSeconds`,整数 30..600 秒,默认 240;出队、采集完成后,在摘要器开始时读取 | [IPC 摘要器](../../../src/main/ipc.ts);后续监督阶段可读到新值,正在执行阶段不变;不覆盖排队、采集、Runtime 解析、保存或 finally 清理 |
| 监督模型并发 | `supervisorModelConcurrency`,整数 1..4,默认 1;报告与监督整理共用一个 Main 内 FIFO 池 | [SupervisionModelPool](../../../src/main/assistant/supervision-model-pool.ts)、[IPC 接线](../../../src/main/ipc.ts);普通聊天不入池。升限可派发等待项,降限不打断在途项,等占用降到新上限以下再派发;监督服务外层 run 仍串行 |
| 报告计时与失败 | 获得槽位、领取、采集和 Runtime 解析完成后,在实际请求开始前续租并启动计时;timeout 原因优先于下层 abort,流结束检查 signal、`done` 及空输出;流中限制 100KB | [IPC 报告摘要器](../../../src/main/ipc.ts);失败不保存报告及其 checkpoint,finally 清 timer、释放会话;报告槽位覆盖领取与采集,但排队和这些步骤不消耗模型时限 |
| 监督计时与失败 | 摘要器读取设置后等待池,获得槽位并解析 Runtime 后启动计时;保留明确 timeout 原因及非 timeout 的提供商错误,流结束检查 signal 和 `done` | [IPC 监督摘要器](../../../src/main/ipc.ts)、[SupervisorService](../../../src/main/assistant/supervisor-service.ts);排队不消耗模型时限,失败不保存结果及 checkpoint;100KB 仍在服务收齐后校验,无监督用户取消入口 |
| 直连模型传输 | 产品默认固定 600 秒的独立请求超时,本页两个阶段超时设置不修改它;内部 `requestTimeoutMs` 可由 Runtime 构造配置 | [model-runtime.ts](../../../src/main/agent/model-runtime.ts) 的 `createRequestSignal` / `fetchWithTimeout`;传播上层 signal,不是整次回顾预算;其他 Runtime/提供商可能更早失败 |
| 直连传输重试 | 最多 3 次重试,间隔 500 / 1,000 / 2,000ms,仅符合条件的网络错误且尚未有阻止重试的流事件时重试 | 同文件 `retryModelStream`;等待也受上层 signal 约束,不重新获得报告/监督阶段时限,不是用户设置或调度叶子重试 |
| 报告领取租约 | `max(300, reportTimeoutSeconds + 60)` 秒,实际范围 300..660 秒;模型请求实际开始前按同一冻结值刷新 | [HeartbeatService](../../../src/main/assistant/heartbeat-service.ts) / [renewHeartbeatLease](../../../src/main/assistant/assistant-database.ts);刷新须匹配运行 ID、owner、attempt、claimed 状态且尚未过期,否则拒绝启动。300 是下限;数据库默认仍为 300 秒,租约不是取消定时器,不覆盖下游监督 |
| 阶段交接 | 报告保存成功或 `no_change` 后释放报告槽位,再进入下游监督;finally 的重复释放幂等 | [HeartbeatService](../../../src/main/assistant/heartbeat-service.ts);默认并发 1 时不会持有报告槽位等待监督取得同一个槽位 |
| 心跳失败重领 | 最多 3 次尝试,失败退避 60 秒、300 秒;过期租约按领取条件重领 | [AssistantDatabase](../../../src/main/assistant/assistant-database.ts) 的 `claimDueHeartbeats` / `failHeartbeatRun`;与 HTTP 重试及未来成功批次续跑不同 |
| 后台 tick | 30 秒一次,启动时也检查;`heartbeatTickRunning` 防重入 | [ipc.ts](../../../src/main/ipc.ts) 的 `runDueHeartbeats`;是到期检查周期,不是阶段超时或分钟级计划设置 |
| 计划迟到 | 到期超过 2 小时记为 skipped 并计算下次时间 | [claimDueHeartbeats](../../../src/main/assistant/assistant-database.ts);这是漏跑处理窗口,不是模型超时 |
| 活动轮询 | 有运行中记录时读取结束 2 秒后刷新,否则 10 秒;串行,离页停止 | [UI 设计](./ui-design.md#活动);只读展示,不终止任务、不触发模型 |
| 提供商限流 | 尚无跨聊天与监督的提供商全局并发、RPM 或 TPM 调度限制,也无监督层 Retry-After 冷却 | 当前池只限制监督工作;普通聊天仍可能与之竞争同一上游额度。已核对直连文本 [model-runtime.ts](../../../src/main/agent/model-runtime.ts) 的 HTTP 失败分支和 `retryModelStream`:网络错误重试不等于按 429 或 Retry-After 退避;其他 Runtime 的内部策略需分别核实 |
阶段超时以各自读取设置时的值为准;并发数更新则影响池的后续派发。两个时限不包含数据采集和保存,不能相加视为整次 run 的最大耗时;清理也可能使实际返回晚于计时值。当前 FIFO 无排队超时或持久化等待记录,也没有按项目公平调度。池的生命周期取消不证明完整分块调度及所有子任务清理已实现。原始错误没有关联日志证据,仍不能确定是否来自旧四分钟定时器、传输、提供商或其他 Runtime。
## 2. 执行单位与范围
一次 run 对应一个冻结的 scope、时间区间和最终结果。项目、会话和消息块只是 run 内部的分组,不能各自创建故事线或把全局请求改写成多个项目 scope,否则既有实体身份将被分散。Global 在启动时分页解析并保存符合条件的项目集合及目录读取边界;后续新增项目留给下一次运行。正文按读取时版本处理,不持有跨模型调用的数据库事务,也不建立全库来源快照;成功批次仍须保存事实及其对应原文片段和定位。
手动新回顾重新读取所选区间,使用该 run 的持久化位置,不读取或写入自动 checkpoint;继续操作恢复同一逻辑 run。自动模式从 `stage=supervisor` 的已提交位置开始。批次输出及其恢复位置一起持久化,自动消费 checkpoint 只在最终完整结果提交时推进。两种位置分开,避免其他自动运行跳过尚未发布的事实;同 scope 有未完成自动 run 时先恢复它,不重复创建相同工作。
采集器按项目轮转、项目内按会话轮转;会话内遵循消息 sequence,同序时用消息 ID,任务和知识引用有各自的来源顺序。每轮先给各组一个块,再给仍有余量的组第二个块。分页在范围与时间过滤后执行,使用稳定排序键,不再使用“最新 20 个会话 × 最后 20 条消息”的预筛选。项目、会话目录也须分页,不能先加载全库正文再切片。页内只读元数据及即将派发的正文。
自动查询的排序与现有实现不同,需定向验证旧 checkpoint 仍能复用。项目目录、会话目录、消息页和其他来源页均保存恢复位置、稳定排序边界及来源版本;内存只缓存有界在飞批次。预算、失败或退出不删除已提交批次。续跑使用原冻结区间,不让滚动窗口移动丢掉该 run 尚未读取的尾部;来源删除或版本不符必须显式处理,不能当作已覆盖。只有目录和全部来源页耗尽、无空洞且汇总提交成功,才完成该 run。
任务按其实际项目归组;无项目任务仅在 Global 的独立组内处理。知识引用按所属消息分组,保留项目、会话、引用位置及本地或外部 locator。当前手动 collector 未输出这些完整分组字段,自动知识引用 locator 也需补所属消息元数据;这是采集接口的拟议改动。
## 3. 消息分块与背景
优先将一个会话中连续的短消息合成一个叶子任务;项目或会话改变就结束该块。单条长消息按段落或句界切分,找不到适当边界时按字符位置切分,后半段必须留在队列中。结构化任务、引用 JSON 保留完整字段及对象身份。超长说明按字段分段,游标仍映射到采集器原始 body 的位置;若暂未实现这种映射,则整条装入一个符合预算的块,装不下时明确失败,避免以重新序列化后的下标写入旧 checkpoint。
每个原文片段在运行内用 `source + revision + start + end` 标识,另保存 `projectId`、`conversationId`、`messageId`、角色、发生时间和原始 locator。消息来源的业务 `sourceId` 继续是会话 ID,不能用它代替消息身份。范围统一采用零基、左闭右开的 Unicode 码点位置,与 SQLite `substr` 的位置规则转换;JS 字符预算按 UTF-16 code unit 计算。中文、emoji、代理对和组合字符需有测试,不能用 JS `slice` 的下标直接推进 SQLite 位置。
允许加入前一片尾部或前一轮对话作为理解背景。背景注明原来源及位置,与本块的新证据分开传递;重叠区域计入模型输入预算,但不计新增字符、不推进 checkpoint、不独立产生事件。若结论只引用背景而没有本轮新证据,不把它作为新增变化输出。范围外的旧上下文也须标为背景,不能据此创建区间内事件。
已有摘要、同 scope 实体说明和已确认记忆同样属于背景。记忆是当前内容,任务状态也是当前状态,不推断历史快照。只有这些背景改变时,自动运行仍是 `no_change`。长消息完整分析后的累计覆盖是新证据区间的并集,不是包含重叠后的输入长度之和。
## 4. 设置配置清单
本节是监督者算法配置的权威清单,对应 FR-S2、FR-S4、FR-S6、FR-S10。目标是在“监督者 > 设置”开放有实际调优用途的参数,普通项常显,高级项按需展开;交互见 [UI 设计](./ui-design.md#算法配置设置目标)。除明确标为已实现的三个字段外,均为规划,不代表已有设置合同或控件。未测参数的产品默认值与数值范围均为待定,下表只给设计校验规则;原型参数单独列出,不作为推荐默认值。
### 普通配置
| 设置 | 单位、默认值与状态 | 用途与约束 |
| --- | --- | --- |
| 消息每页条数 | 条/数据库页;待实现,默认值与范围待定 | 正整数;只控制一次数据库 fetch 的消息数,不是总接纳消息上限、每次模型调用消息数或活动列表页大小 |
| 监督模型并发数 | `supervisorModelConcurrency`;已实现,整数 1..4,默认 1 | 报告和监督共用;未来叶子、会话、项目及全局汇总也入同一池,不将项目并发与叶子并发相乘;外层 run 仍串行 |
| 心跳报告模型超时 | `heartbeatReportTimeoutSeconds`;已实现,整数 30..600 秒,默认 240 | 报告模型阶段;当前读取时点及租约规则见第 1 节 |
| 监督整理模型超时 | `supervisorOrganizeTimeoutSeconds`;已实现,整数 30..600 秒,默认 240 | 当前单次整理;未来作为未单独覆盖的节点超时,仍须按层测量,不声称现值适合所有节点 |
| 单次执行软预算 | 秒/次执行;待实现,默认值与范围待定 | 到期停止读页、接纳和派发;已在飞节点在各自期限及硬期限内结束,校验后保存批次,再暂停。继续同一 run 可重新获得一次执行预算 |
消息页必须按稳定键持续读取至冻结范围耗尽,可跨多次执行继续。增大或减小页大小只改变数据库读取批量,不改变来源资格、最终待覆盖消息集合或语义分块边界。块可跨数据库页,但不能跨本设计规定的会话/项目边界;一条消息也可拆成多个片段。页面耗尽、批次封块和执行预算耗尽是不同事件,不能用页数或单轮接纳上限代替完整覆盖判定。
### 高级配置
下列分组均待实现。计数、字符、token、字节和时间分别校验,不使用一个“批量大小”兼任多个含义;数值上限需通过内存与真实模型测量后写入共享合同。
| 分组 | 拟开放参数及单位 | 设计校验、依赖与到限行为 |
| --- | --- | --- |
| 来源分页 | 项目目录每页项目数、会话目录每页会话数、任务/引用等来源每页记录数 | 各为正整数,与消息每页条数独立;只改变 fetch 大小,不限制范围总量。正文读取还受内存接纳预算限制,不能一次装入多条无界长消息 |
| 叶子分块 | 每批不同消息数、每批片段数、每批新增正文字符数(UTF-16 单元) | 各为正整数;任一到限即封块,未装入内容继续排队。拆分消息按消息 ID 去重计数,片段另计;字符切分必须保留完整 Unicode 码点 |
| 上下文与重叠 | 相邻片段重叠字符数、旧摘要字符预算、记忆条数及单条字符预算 | 非负整数,重叠可为零;均计入完整输入预算,不计新增正文及覆盖。输入预算不足时优先缩减可选背景,不能通过缩减新证据后丢弃余段来满足预算 |
| 模型输入 | 每节点完整输入 token 上限、模型上下文容量、无 tokenizer 时的保守估算系数 | 输入包含系统指令、元数据、JSON、实体候选、背景与重叠。上下文容量优先读取实际模型配置,手工值不得超过已知能力;估算必须标明,不能冒充实测 token |
| 模型输出 | 叶子与汇总各自的输出预留 token、输出字节预算 | 输入上限加输出预留不得超过模型上下文;预留还受提供商输出上限约束。用户预算不得放宽生产输出合同;超限拆小任务或显式失败,不截断 JSON、事实或引用 |
| 摘要卡 | 叶子/中间导航卡的目标 token、字节预算 | 卡片只用于导航,完整事实独立保存;须能在父节点输入预算内放入至少两个孩子及必要元数据,否则拒绝该组合 |
| 实体候选 | 每组候选数、候选描述字符/token 预算 | 数量与正文均有界,UUID 和名称保留;合法孩子身份继承规则不变,缩小候选预算不授权按同名合并 |
| 汇总结构 | 每次合并孩子数(扇入)、最大汇总深度 | 扇入至少两个,单子透传;层级必须严格收缩,触及深度限制显式失败,保留叶子。产品默认及上限待测 |
| 内存接纳 | 待执行块数、每轮新增字符/token、采集与在飞内容合计字节预算 | 约束内存和本轮接纳量,包含元数据、背景、摘要卡;到限暂缓读取/接纳并持久化成功批次。不得当作整次 run 的消息总额 |
| 来源与排队 | 单页来源读取超时、队列等待超时(秒) | 分开计时;读取超时不能变成空页,排队超时保留恢复记录。同步 SQLite 查询须验证真实中断能力;等待不消耗模型时限 |
| 节点时限 | 叶子、会话汇总、项目汇总、跨项目汇总模型超时(秒,可继承监督整理值) | 包含 Runtime 内部重试,各自获槽并解析 Runtime 后开始;显式覆盖需校验实际 Runtime 能力,不能自动沿用现有字段的数值范围 |
| 阶段预算 | 单项目处理、全局汇总与最终发布前的阶段预算(秒) | 与单节点超时分开;到限停止新增工作并保存可恢复状态,缺失项目不能用空摘要补齐;发布事务保持原子性 |
| 执行硬期限与清理 | 本次执行硬期限、独立清理期限(秒) | 硬期限不得早于软预算;到期向在飞节点传播取消,拒绝迟到提交,保留此前批次。清理单独计时和报告,不承诺实际返回时间等于硬期限 |
| 调用与用量 | 每次执行及逻辑 run 累计模型调用数、token 预算;费用预算仅在价格和用量可可靠换算时开放 | 派发前预留,完成后核算;逻辑调用与 HTTP 尝试分别计数,重试也计入用量。累计预算续跑不归零,到限暂停;费用不可计算时显示不可用,不猜价格 |
| 失败与续跑 | 自动续跑开关、最大自动尝试次数、重试退避起始/上限(秒) | 仅失败或未提交节点可重试;成功批次复用。次数有界、退避上限不小于起始值;初版不新增节点网络重试,复用 Runtime 策略,后续根据失败证据再开放对应字段 |
| 上游额度 | 同额度组并发数、RPM(请求/分钟)、TPM(token/分钟)、429 冷却等待预算(秒) | 额度组按实际提供商账号/连接/模型共享规则确定;生效并发取监督池与额度组限制的较小值。遵守 Retry-After,等待预算不足则暂停,不能提前重发;冷却不占模型槽或报告租约 |
上游额度设置必须标明实际纳入的请求路径。仅监督接线时标为“监督请求额度”,不能称为提供商全局限额;覆盖普通聊天及相关 Runtime 后才能称为应用内共享额度,其他进程或客户端的消耗仍不可控。跨连接共享同一账号额度时需归入同组,不能按连接各放一份完整额度;未知共享关系不猜测。未支持的 Runtime 不显示可保存却不生效的控件。
### 配置冻结与续跑
目标设计中,保存设置仅影响新建 run;创建时保存解析后的有效配置及算法/提示词/输出合同版本,整个 run(含排队、节点和跨重启继续)使用同一份配置。共享模型池不能为不同 run 各创建独立上限:不同并发配置的执行须等待当前报告及监督工作排空,再采用该执行冻结的值;恢复旧 run 同样使用其原值,不修改已保存的新设置。这是待实现的生效规则调整;第 1 节记录的当前阶段读取与并发即时影响后续派发行为保持事实描述,不提前改称已冻结整个 run。
默认“继续”沿用原配置,不能因应用设置已保存新值而重切剩余片段。性能字段(分页条数、并发、等待/执行时限、速率与退避)不改变已保存事实的有效性,但本设计仍只在新 run 使用新值,不隐式修改恢复中的配置。语义字段(消息/片段/字符分块、背景重叠、模型与提示词、候选选择、输入输出预算、汇总结构)会影响产物;需要采用新值时新建 run,不将旧叶子按新规则冒充同一版本结果。
恢复时复查来源 revision、算法/提示词/输出合同版本及依赖。来源变更使该来源叶子及依赖汇总失效,其他有效批次保留;仅汇总规则失效可保留可验证的叶子、重做受影响汇总,叶子语义或合同不兼容则相应叶子及上层都需重做。软件升级后无法按原版本继续时明确说明并提供重新回顾,禁止静默套用新版本。这里只要求最小有效配置与依赖记录,不新增通用历史版本执行引擎。
累计调用/token 预算、成功批次和恢复位置须随 run 保存;普通恢复不能重置累计额度,也不能自动删除未完成 run 的唯一输出。后续若增加显式调整预算的续跑操作,需单独定义其生效范围与记录;当前清单不将保存设置视为该操作。
### 固定正确性条件
以下不提供关闭开关或可降低的阈值:冻结范围内全部目录与来源页耗尽、同 revision 连续无空洞覆盖、来源及精确位置校验、叶子事实与 provenance 保留、输出结构/身份校验、取消后的迟到提交拦截,以及最终发布与自动 checkpoint 的原子提交。覆盖率不得配置成低于完整覆盖的“成功阈值”;任何预算都不能授权静默截断或将未读内容标为完成。批次持久化与恢复所需记录不是可选性能开关。
### 原型参数依据
静态核对 [review-algorithm.mjs](../../../scripts/review-algorithm.mjs) 和 [review-deepseek.mjs](../../../scripts/review-deepseek.mjs):`pages` 默认 17 条/页,`chunks` 默认 8,000 UTF-16 单元/叶子且跨页累计;没有单批消息数/片段数上限、句界切分或重叠参数。`extract` 默认宽度 2、调用上限 `Infinity`,DeepSeek 入口仅接受并发 1..2,首次执行最多两次叶子调用,实验总 HTTP 上限 12(包含评估),单请求 180 秒,输出 16,384 token、200,000 字节校验上限。这些是脚本值和实验边界,未验证为产品默认或合法范围;尤其不能用原型 200,000 字节替换生产 100KB 合同,也不能采用无限调用作为产品默认。
没有可用 tokenizer 时用保守字符估算,并将计数明确标为估算值。上线前按模型、中文长文本和大量实体测量输入 token、首字延迟、总耗时、输出大小及调用数,再调整参数。证据数量大但每条很短时,也要计入元数据和引用 token 开销。
一次执行接纳量不限制整个逻辑 run 的范围。内存片段、元数据和摘要卡总量应有上界;达到上界就持久化已完成批次并暂缓派发,不能删来源。现有全局 100 条/48,000 字符裁剪须改为每节点校验及分页结果。存储不足明确暂停/失败,保留已保存内容及未完成状态,不通过删除叶子事实满足容量限制。
### 未来超时与清理清单(均未实现)
以下是调度设计项,不是现有设置字段或已接通的分块取消能力。没有测量依据的数值保持未定。
| 层次 | 拟议范围和到期行为 | 参数状态 |
| --- | --- | --- |
| 叶子模型 | 单块执行含底层重试;超时停止该节点,保留其他成功批次,恢复只重试未成功部分 | 未定 |
| 会话汇总 | 有界卡片合并;失败保留所有叶子事实及引用 | 未定 |
| 项目汇总 | 单项目各合并节点及项目阶段预算;不得用失败项目的缺省摘要替代完整结果 | 未定 |
| 跨项目汇总 | 全局合并节点及最终发布预算;缺任一项目时不能标完成 | 未定 |
| 来源读取 | 项目/会话/消息分页、任务及引用读取;读取超时不是空页或无变化 | 未定;同步 SQLite 查询不能仅靠 JS timer 中断,实现时需验证 |
| 排队等待 | 区分等待与执行时间;到期移出待执行队列,保留恢复记录,不创建假完成结果 | 未定 |
| 批次接纳预算 | 块数、字符、token、内存到限后暂停接纳;成功输出及连续位置先落库 | 未定 |
| 单次执行与整次 run | 软预算停止派发并保存进度,硬期限取消在飞调用;逻辑 run 可跨执行继续,累计调用/token 预算不重置 | 默认与范围待测;费用预算以可可靠换算为开放前提,不能以预算耗尽视为完成 |
| 提供商速率与冷却 | 按上方额度组与实际接线范围限制并发、RPM、TPM;冷却不占执行槽,不领取报告租约,预算不足时保留待恢复工作 | 未实现;先核实各 Runtime 已有重试与错误信息,避免重复退避;未覆盖聊天时只称监督请求额度 |
| 用户取消与进程关闭 | 父 signal 到全部节点,停止读页/派发/发布,保留已提交批次,忽略迟到完成 | 待接类型化 IPC 和状态;关闭应用开关仍按既有规则,非取消 |
| 清理期限 | 清 timer、监听器和 controller 注册,等待在飞调用终止,释放临时会话,核验本次拥有的远程进程已退出 | 独立期限未定;不得误停共享或其他运行资源,清理失败单独记录 |
有效节点期限不得超过本次执行剩余硬期限;软预算只停止新增派发,不立即取消已在飞调用。暂停、错误和用户取消分别记录原因;自动恢复与重试按配置有界执行,不能形成无限付费循环。上述清单的设置交互等实现后再开放,目前算法控制 UI 只显示报告/监督两个超时及共享监督并发数。
## 5. 队列及汇总算法
```text
run(request):
create or resume durable run with frozen scope, time range, runtime and effective configuration
wait for existing supervisor run queue and compatible shared pool configuration
load successful batches and page/source positions; read next bounded page
if automatic, all eligible pages exhausted and no new evidence: save no_change; return
admit bounded leaf tasks, round-robin by project and conversation
run admitted leaves in a shared worker pool
validate and persist each successful batch with facts, provenance and resume positions
if budget expires, a task fails or cancellation arrives: stop; retain batches; mark incomplete
continue paging all project/conversation/message/source groups until exhausted
reduce within conversation, then project, then requested scope
pass through singleton groups; reduce oversized groups in bounded rounds
validate final output, identities, original references and coverage
persist successful reduction nodes without replacing leaf facts
if coverage is exhaustive and gap-free: publish result and automatic checkpoints atomically
return completed only after publication; otherwise return resumable incomplete status
```
采集与叶子执行可交错,但入队和在飞任务有界,不使用对全部来源的 `Promise.all`。每批完成后持久化,再接纳下一批;时间不足就暂停。首次派发前用有界探测判断是否直通;穷尽范围后确实只有一个叶子时直接生成最终输出,通过校验后只调用一次模型。因预算只接纳一个叶子时仍是未完成 run。空范围及自动 `no_change` 必须在全部相关目录/来源页检查完毕后判定,不调用模型、不把预算未读误作无变化。
叶子有多个时输出受限摘要卡及结构化事实,Main 保存卡片 token 到原文片段的映射。会话只有一个卡片时透传,项目只有一个会话摘要时透传,全局只有一个项目摘要时透传。不得为了层级外观额外调用模型。边界必须标清项目和会话,跨项目汇总仍使用原请求 scope 的候选实体集。
每次汇总只读取符合输入预算的一组有界卡片、少量背景和所需实体候选,不重新附上所有原文、所有中间摘要或全部候选描述。若一个大组有 N 个卡片,划成至少两个孩子的组,单个余项透传;下一轮卡片数必须严格小于 N。以卡片数量作终止度量,同时限制每张输出卡大小和最大深度。输入预算连两张卡都放不下时,重新缩小叶子卡输出目标或明确失败;禁止用同一内容反复“再总结一次”形成不收敛循环。
24 个叶子使用二叉合并时至多产生 23 个合并节点;分组层级不额外生成单子节点调用。最终合并节点直接生成最终结果,不再调用独立的润色模型。小任务直通和此调用上界都应有确定性测试。上述次数按摘要器逻辑调用计,底层 HTTP 重试应单独统计。
单节点 JSON 继续遵守事件、实体、关系和引用上限,整个 run 的事实结果使用分页读取,不能把最终 40 个事件等单输出上限套在叶子事实总集上。归纳层可以合并导航条目,但必须保留被合并的叶子 ID、事实及冲突;不能以父摘要覆盖孩子。一个对象展开后超过 20 个来源时,以分页关联读取完整引用或显式拒绝该节点,不能截断。完整叶子事实及 provenance 的存储和查询合同属于本设计的必要实现项,不再作为可选后续工作。
## 6. 来源与实体合并
来源归属由 Main 管理。模型只返回本节点收到的引用 token;中间卡片 token 由 Main 展开为原始来源 token,校验引用属于本 run、对应版本与范围,并存在于已保存片段集合。每个成功批次持久化叶子事实、原始引用映射、版本、范围和有界原文片段,恢复时不依赖内存映射。中间摘要只作导航;父摘要未列出的事实仍能从叶子结果分页访问。覆盖依据成功片段,不以最终摘要引用数推算,不复制完整用户库。
重叠输入的同一原文区间只登记一次;不同版本或不同片段保持可区分。不同 run 的来源 UUID 继续重新分配,底层 `source`、revision、位置和业务 `sourceId` 用于追溯,不能拿结果内 UUID 当增量身份。引用去重与实体合并是两件事。
实体采用两类明确身份:已有实体用允许候选中的 `persistedId`,本轮新实体先用带叶子命名空间的局部 ID。多个叶子引用同一合法 `persistedId` 时,Main 合成一个身份并改写事件和关系端点,再汇总描述及冲突,最终输出中的 persistedId 保持唯一。父节点可继承孩子已校验的身份,Main 同时校验它属于本轮原始候选集;候选描述的筛选不应使已确认合法的孩子身份丢失。
新实体只在有界汇总中显式给出“这些局部 ID 指同一概念”的映射后合并,并校验 ID 均来自孩子输出;同名、相同裸模型 ID 或相似描述都不是自动合并规则。保留未能判断的独立实体,接受候选集有界带来的漏复用风险,不新增语义检索服务。合并后从实体到事件、变化、关系及来源的引用必须全部重写,去掉合并导致的重复关系,并校验自关系是否仍有意义。
实体候选始终来自原请求的同 scope 故事线。不得将项目分组变成跨 scope 身份复用,也不批量修复历史重复实体。保存事务再次检查候选归属和撤销状态,沿用 [assistant-database.ts:8564](../../../src/main/assistant/assistant-database.ts#L8564) 的保护;已确认、已修订字段及已撤销关系不被自动结果覆盖。候选在执行中被撤销时整次保存失败,游标保持原值,下一次重新读取候选。
## 7. 超时、取消和部分失败
一个 run 持有父 controller,节点持有独立的超时 controller;父取消传到全部在飞节点。取消后停止采集、派发和结果提交,清除定时器并释放临时 Runtime 会话。节点超时只触发本轮失败,不取消其他运行或共享 Runtime。节点采用独立 requestId/conversationId;冻结默认 Runtime 选择,不因证据来自某个项目就切换到该项目远程执行环境。
当前监督通过无 projectId 的 [resolveRequestRuntime](../../../src/main/ipc.ts#L1492) 获取默认 Runtime。此设计只改变桌面侧编排,不要求新增 Agent 调度协议。后续实现若改动共享 Runtime 的取消或释放逻辑,必须检查 `gbagent` 是否共用该实现,并按仓库要求验证实际受影响的远程路径,不能据桌面测试推断远端行为。
摘要器应在流式输出期间检查字节上限,并要求完成事件;超时、用户取消、关闭进程、提供商失败、非法 JSON 和引用错误保留可区分的原因。调度层初版不重试节点网络错误,复用 Runtime 已有重试;模型格式错误也不无限“修 JSON”。先用小块降低失败成本,再根据实际失败数据决定是否值得增加一次有界纠正调用。
取消需要 Main 注册与执行生命周期连接,并扩展类型化 Preload/IPC;只让 Renderer 忽略响应不构成取消。取消先于最终发布事务开始则拒绝发布,保留此前批次;仅最终完整结果已经同步提交时,取消才返回“已完成”。单个批次已提交不能使 run 完成。关闭监督应用继续遵循现有规则:停止新领取,允许已开始的运行结束,不等同于用户取消。
| 情况 | 本轮结果与进度 | 用户可见结果 |
| --- | --- | --- |
| 叶子或汇总失败 | 保留已持久化成功批次和恢复位置,不发布完整结果、不推进自动消费 checkpoint | run 失败但可恢复;仅重做失败/未提交节点,旧完整结果仍可读 |
| 用户取消或执行硬期限到达 | 停止在飞调用和发布,保留已提交批次 | 显示取消或预算暂停及可恢复范围,不标 `completed`;具体新状态需扩展合同 |
| 执行软预算到达 | 停止读页、接纳和派发,在飞节点在节点及硬期限内结束并保存成功批次 | 未完成时显示预算暂停,可继续;若已满足全部完成条件则可正常发布 |
| 批次或发布事务失败 | 该事务回滚;此前成功批次不回滚 | 从最后持久化位置恢复;发布失败不重算已有合法叶子 |
| 达到采集预算,已接纳任务全部成功 | 保存有限覆盖批次及位置,仍有未读内容 | 显示“未完成,可继续”,不以 `completed` 加提示代替真实状态 |
| 有来源读取失败或运行中被删除 | 初版按本轮失败处理,不将读取失败当作没有新证据 | 显示来源不可用的数量和范围,后续重新查询 |
| 自动无新来源 | 保存 `no_change`,无新结果或图谱事件 | 保留旧结果,模型调用数为零 |
成功批次持久化是用户已授权的续跑要求。最小记录包含 run 冻结范围、目录/页位置、来源 revision 与连续 offset、已校验叶子输出及引用、必要的汇总依赖和成功输出。输出与对应位置同事务提交,以 run 和来源区间等稳定身份防止重复保存;不持久化每次进度动画或完整提示词。崩溃前未提交的在飞批次允许重算,已提交成功批次必须复用。表结构及保留/清理实现仍待设计落地,不能声称现有 schema 46 已支持此能力。
## 8. 游标与覆盖提交
同一来源片段可能乱序完成。恢复位置只能是同一 revision 从已提交 offset 开始、成功持久化区间连续覆盖的最远位置,不能取最大 end 跨过空洞;空洞后的成功批次单独保留,恢复时跳过其已完成工作。背景、仅读取内容和失败片段不推进。自动消费 checkpoint 在完整发布事务中推进,现有 `MAX` 合并位置的调用方仍须满足连续覆盖条件。
同一来源读到不同 revision 时不拼接版本;标明该来源及依赖汇总需重新处理,保留其他未受影响的成功批次。若最后读取之后来源修改,提交的仍是确实分析过的旧 revision,下一次查询因版本不符处理新版本。已保存旧事实及片段保留版本标识,不冒充当前内容;不还原未保存的历史正文,也不引入全库快照。
建议扩展 `coverage_json`:是否读完冻结范围、已处理新字符和片段数、覆盖项目/会话数、是否仍有来源、停止原因及恢复引用。精确片段继续用 locator,恢复记录关联批次输出,不仅保存一个总计数。总数未知时显示“仍有未读内容”,不用动态发现数充当固定分母。结果读取合同需显式暴露 coverage;当前数据库查询了 coverage,但共享结果 schema 未返回它。
手动和自动均须跨重启继续同一未完成逻辑 run。Main 根据持久化 run ID 读取冻结范围、成功输出及位置,复查来源版本,恢复剩余页和未成功汇总;Renderer 不能自报已处理 offset。手动继续不污染自动位置;自动恢复先于创建同 scope 新 run。恢复后的单次执行重新获得执行预算,但不丢失既有进度。
持久化续跑及手动“继续”入口是本设计的验收要求,不能以建议缩小范围替代。恢复记录的保留期限、主动放弃后的清理规则和具体 schema 尚未确定;不得在 run 未完成时自动删除唯一成功输出或继续所需位置。显式放弃可以终止后续工作,但不能标为完整覆盖。
## 9. 心跳接线与重复调用
手动 `supervision:run` 和心跳完成回调继续进入同一个服务及同一运行内队列。外层按计划唤醒仍归 `HeartbeatService`,不能让各项目、会话和叶子再调用 `heartbeat.runNow`、创建计划或再次进入 `supervision:run`。队列等待中的自动请求到执行时重新查询 checkpoint,从而看到前一个 run 的已提交结果。
初版保留既有报告语义:每次心跳最多执行一次原报告阶段,再执行一次分块监督;报告含记忆和任务建议,不能直接用监督 summary 替代而悄悄删除这些产物。报告阶段无新证据时零次模型调用,仍允许监督补读或重试;报告已经提交而监督失败,下次报告仍按自己的 checkpoint 判定,无变化则不再付费。设置中的“立即运行”目前会主动重做报告,此行为不与自动无变化混淆。
因此本设计消除的是分块引入的重复报告调用,不声称将现有两类模型输出合为一次调用。一个叶子的自动首次运行在两阶段都有新输入时仍为“报告 1 次 + 监督 1 次”;多叶时为“报告至多 1 次 + 叶子数 + 必要汇总数”,另计底层重试。报告只读报告阶段的有界输入,不能宣称它覆盖监督处理的全部片段。
若后续决定让心跳只触发监督、报告从同一结构化结果投影,必须先定义记忆建议和任务建议的共同输出契约、各自成功语义及旧报告展示,另行修改 FR-S4 和 US-S27;本轮不把该产品调整藏在调度重构里。也不使用多阶段事务将两个结果强行绑定。
报告先获共享槽位再领取租约,并在实际模型请求开始前刷新;报告完成或无变化提交后释放槽位,再执行下游监督。领取与续租条件见当前控制清单。未来整次执行预算不能套到报告租约,也不能在报告完成前等待整棵监督树。现有 tick 防重入不替代共享池上限;监督过长会延迟到期检查,拟通过有界执行和持久化续跑控制,不另建后台调度服务。
## 10. 活动与结果展示
沿用[现有活动设计](./ui-design.md)及[全局界面规范](../../../UI-DESIGN.md),活动仍是一条手动逻辑 run 或合并后的心跳 run。拟显示采集中、整理会话、汇总项目、生成回顾、保存中,以及已发现块数、成功块数、在飞数和新字符数。持久化成功批次提供可恢复进度,实时在飞数只存内存。发现未结束时显示“已处理 N 块,继续读取”,不用不可靠百分比。摘要、项目导航可展开分页叶子事实及完整来源。
计数在 `supervision:activity` 读取时与持久化运行信息合并,保持每页 50 条及串行轮询,不持久化每次计数更新。当前出队前没有 run 记录;未来排队/恢复操作需能定位逻辑 run。重启后从成功批次重建已完成计数,不恢复虚构在飞状态;待恢复记录不能被现有未完成记录处理逻辑误判为全部作废。
结束后以持久化状态为准:只有冻结范围穷尽、片段无空洞、必要汇总成功并发布结果,才能显示 `completed`。预算暂停或失败保留部分产物,显示“未完成,可继续”、实际覆盖和原因,不显示完成徽标。需要扩展活动/结果状态、coverage、恢复入口及中英文文案和 UI 测试;不能从摘要措辞猜状态。旧结果 coverage 缺失时显示“覆盖详情不可用”,不推断已读完。
取消按钮仅在 Main 接线可用时加入。拟增加暂停/未完成/可恢复及取消的明确状态语义,最终枚举和 schema 尚未实现;禁止用现有 `completed` 冒充部分成功。活动读取不调用模型,离页停止轮询,迟到响应不改写选择。旧完整结果与本次部分产物、失败原因分开展示,继续入口恢复原 run,不暗中重做全区间。
## 11. 验证方案与本次检查
实现时先用隔离 SQLite、生产采集与保存函数、可控摘要器和时钟验证调度规则,不修改用户数据库。以下生产端到端验收仍待执行;隔离原型已覆盖的子项及其限制见第 12 节和实施进度:
| 场景 | 必须验证的结果 |
| --- | --- |
| 全局七天、多项目、超过一页项目/会话、每会话超过 20 消息 | 过滤在分页前;逐页耗尽项目、会话、消息及其他来源,无静默裁剪;每个片段有来源定位 |
| 一条超长消息及中文、emoji | 多片段覆盖连续、无意外缺口;Unicode 位置与 JS/token 预算分开;重叠不增加进度和事件 |
| 小任务、自动无变化 | 小任务只有一次监督模型调用;背景变化单独触发时零次;不调用空的会话/项目汇总层 |
| 24 叶子测试样本、多级汇总、超大候选及引用 | 报告及所有节点共用配置的监督池上限;每个节点满足预算;节点数严格收缩;二叉合并调用至多 23;引用展开超限显式处理,样本数不作为产品预算 |
| 某叶失败、根汇总失败、保存失败及进程重启 | 已提交成功批次仍可读取;恢复不重新调用成功叶子;只重做未提交/失效依赖;完整发布与自动 checkpoint 原子提交 |
| 预算结束、乱序完成及同源多个片段 | 批次输出及连续恢复位置原子提交;空洞后成功输出保留但不提前推进;同一冻结 run 续读尾部,绝不标 completed |
| 叶子事实超过最终输出上限 | 所有已产出叶子事实和 provenance 分页可读;摘要遗漏、合并或缩短不删事实、冲突或来源 |
| 来源修改、删除及窗口滑出 | 版本不混用;下一次处理新版本;窗口外未读不假装已完成,也不擅自扩大范围 |
| 两叶同一 persistedId、同名新实体、关系端点变化 | 显式身份可复用;局部 ID 命名空间隔离;同名不自动合并;人工修订和撤销状态受保护 |
| 节点超时、run 超时、取消及迟到完成 | signal 到达实际 Runtime,停止派发与提交,释放临时会话;原因可区分,其他请求不受影响 |
| 报告与叶子/汇总竞争槽位、运行中调低并发、上游 429 | 共用监督池且不重入占槽;排队不消耗模型时限、不提前领取报告;未来冷却遵守提供商信息且不重复叠加 Runtime 重试;普通聊天与监督共享额度的行为按实际接线验证 |
| 手动与自动先后入队、报告成功而监督失败 | 后续自动请求重读已提交位置;手动不改自动位置;每次心跳报告最多一次,监督重试不重复已成功且无变化的报告输入 |
| 活动及继续入口 | 合并活动仍关联同一逻辑 run;部分覆盖不伪装完整;重启后从持久化输出重建进度;继续失败明确原因,手动恢复不改自动位置 |
| 配置与分页 | 改变消息/目录页大小不改变最终入选集合和语义块;分别触发消息数、片段数、字符与 token 上限,余段仍被覆盖;跨字段无效组合不能保存 |
| 配置生效与恢复 | 保存和恢复默认值只影响新 run;进行中与重启恢复沿用冻结配置;语义或来源版本失效只重做相应依赖,累计额度不被续跑重置;软预算与硬期限行为分开验证 |
实现后的定向测试应覆盖 `supervisor-service`、`incremental-review`、`supervision-activity`、相关 IPC 和 Renderer 场景,并检查所有参数上界及调用计数。真实模型性能验证另行执行,使用脱敏固定长文本,限制请求次数,记录不同输入规模的耗时和 token,不以模型替身测试宣称超时已解决。涉及 Runtime 生命周期变化时追加对应真实 Runtime 路径验证。
初稿只做静态核对。后续只读用户数据探测及 DeepSeek 隔离原型的测量、复现命令、范围和限制统一记录在[实施进度](./progress.md)。这些证据不证明生产分块调度已实现;原始 AbortError 仍缺乏明确 abort 原因的日志证据。
## 12. 隔离原型的设计结论
`scripts/review-algorithm.mjs` 实现了可执行的消息分页、码点分块、逐批 SQLite 保存、按已保存片段恢复、完整覆盖判断及叶子导航校验。`scripts/review-deepseek.mjs` 用配置中的 DeepSeek 完成整条目标会话验证;具体计数和耗时只在[实施进度](./progress.md#2026-09-24-deepseek-全目标会话隔离原型)维护。原型按已授权的续跑要求使用最小批次表,没有修改产品数据库。
来源覆盖、事实保留和短摘要须分别验收。覆盖以完整冻结消息集合及成功批次的区间并集判断;事实以精确来源引用和独立语义检查判断;导航必须索引全部叶子,生成前后核验叶子内容未变。真实实验中导航摘要确实省略了仍保留在叶子中的细节,因此产品查询和 UI 必须能访问叶子,不能只存最终 summary。模型的 reviewedTokens 回执仅参与结构校验,不能证明事实召回。
重复引文是已出现的定位歧义。原型保留同一已提交片段内全部精确匹配区间,标记 ambiguous 和候选数;这些位置都能重建原引文,但不冒充已确定的唯一发生位置,也不复制事实。生产接口需要携带该歧义或要求更长引文,禁止无提示地选第一次出现。来源中不存在的引文仍拒收。
成功叶子与覆盖区间一起提交,另一批校验失败不回滚已提交结果;重开时只处理缺失批次。测试脚本额外保存原始提供商响应,以便校验器修正后本地复核,不因此增加付费请求。该实验缓存不是新增的产品响应日志设计。无变化路径必须遍历所选批次并核验完整覆盖后返回零调用,不能因本轮预算耗尽就返回 no_change。
本次每叶 8,000 UTF-16 单元、并发 2、输出预留 16,384 token 只是在该模型和单会话样本上验证过的实验参数,不直接定为产品默认值。原型加载完整冻结来源,尚未满足生产有界内存采集;没有句界切分和上下文重叠,也未实现项目轮转、多层有界汇总、生产发布事务、自动 checkpoint 或继续 UI。导航关联保持空数组,跨项目身份和关系合并未作真实验证。第 11 节中的生产端到端场景仍需分别落实。
@@ -0,0 +1,60 @@
# 监督者应用产品设计
日期:2026-09-23。状态:工作回顾、图谱、活动和设置已接入;其余目标能力以[实施进度](./progress.md)为准。
本文定义监督者应用的定位、功能边界与知识库关系。[会话监督 PRD](./prd.md)继续负责对执行过程的观察、证据与介入边界;本文将该能力纳入更完整的工作回顾应用。
## 产品定位
监督者是持续关注指定工作范围的智能 Agent。用户可以主动要求回顾,也可以通过智能心跳定期唤醒它。它根据可见记录判断是否需要反馈、整理阶段或更新知识关联,没有值得反馈的变化时保持安静。
| 名称 | 职责 |
| --- | --- |
| 监督者 | 应用名称,负责观察、回顾和解释工作进展 |
| 智能心跳 | 按计划唤醒监督者的机制,保留计划与运行管理 |
| 监督反馈 | 会话右侧的阶段回顾、问题提示与证据入口 |
| 故事线图谱(Story Graph) | 呈现事件、知识实体及其随时间发展的关系 |
FR-S1:将现有智能心跳入口整合、升级为监督者,不增加另一个平级重复应用。当前通过应用中心管理;未来应用市场提供同一应用的发现与启用入口。市场建设不应成为监督者落地的前置条件。
应用默认关闭,用户显式启用后才接受新的回顾工作;关闭时保留计划和历史。默认值、已保存选择、自动运行条件及在途运行边界由[应用启停与执行](./logic-design.md#应用启停与执行)统一定义。
FR-S2:应用包含“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”五个主视图。工作回顾提供统一监督结果阅读、历史选择和手动新回顾,结果显示自身范围与日期;自动监督集中展示计划状态、范围、周期及下次执行时间,以及运行概览、指标、趋势、自动监督报告、待处理记忆与行动建议和心跳审计,创建和编辑使用同一份 Modal 表单;活动记录展示统一实际执行、进度、详情及结果入口,支持按计划 ID 筛选;设置只管理超时、并发、分页、分批及执行预算,不嵌套次级菜单。计划保存失败保留草稿,关闭未保存修改须确认;编辑已暂停计划保持暂停,创建须显式保存并启用。原有运行记录及应用、存储标识保留。
当前设置还支持分别调整报告与监督整理的模型超时,以及两者共用的监督并发数。并发设置不限制普通聊天,也不保证避免上游限流;排队不消耗模型阶段时限,降低并发不打断在途请求。取值及生效规则由[当前控制清单](./review-scheduling-design.md#当前超时与调度控制清单已实现)定义。
FR-S10:活动同时覆盖手动监督回顾、定时心跳及用户立即运行的心跳。每条记录显示触发方式、范围、开始与结束时间、运行状态、失败原因和已有结果入口。同一次心跳的报告和下游监督回顾合为一条活动,报告保存成功不能掩盖下游仍在运行、暂停或失败。活动读取不调用模型,关闭应用后不查询活动。新批次运行支持暂停及继续,保留成功批次;人工修改的时间序列审计尚未实现,历史缺失不得猜测补齐。
FR-S2:分页只控制数据库读取批量,不能限制最终处理范围。当前已开放来源页大小、每批正文/消息上限、执行软预算;这些配置在新建回顾时保存,继续沿用。当前字段和未来高级参数的边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界),交互见 [UI 设计](./ui-design.md#算法配置设置目标)。完整覆盖、来源校验与事实保留不作为可关闭选项。
## 范围与触发
FR-S3:首次启用时选择项目、会话及可用来源,生成前显示范围和时间区间。应用启用不等于默认观察全部内容。已有心跳的 Global、单项目和多项目范围语义由[智能心跳 PRD](../smart-heartbeat/prd.md)定义,统一应用时保留。
FR-S4:支持手动回顾,并复用已有定时检查机制。自动报告和监督回顾分别只处理所选范围、回顾窗口内新增、修订或尚未处理的来源片段;没有待处理输入时跳过模型及结果生成。失败输入保留重试资格,预算遗漏不得记为已处理。手动回顾重新整理所选历史区间。具体规则见[自动增量与手动重看](./logic-design.md#自动增量与手动重看)。事件触发和阶段自动判定仍待设计。
FR-S5:会话右侧在跟随模式下展示当前会话的监督反馈;固定到 Conversation、Task 或 Experiment 时保持所选目标并显示其名称,切换聊天不改变监督目标。跨会话总结在完整应用中展示。反馈包含结论、来源和“在图谱中查看”定位入口。用户主动选择“发到当前会话”才加入聊天消息,同一结果不重复触发侧栏和全局通知。
FR-S6:监督者可提出整理与修改建议;已有会话监督的观察边界继续生效。自动更新图谱不能静默覆盖用户修改的描述、已确认的关系或已有知识库正文。取消整理后保留上次结果;失败说明受影响范围并允许重试,部分结果不得显示为完整回顾。
FR-S4、FR-S6、FR-S10 的验收要求:完整分页处理冻结范围内的入选来源;叶子事实和来源完整保留,摘要用于导航。预算耗尽或失败后保存成功批次与位置,重启后继续,不重复计算仍有效的成功批次;未读完或汇总未成功不能标完成。该续跑要求已进入生产接线,当前实现、实测范围与剩余差异见[回顾分块调度](./review-scheduling-design.md#0-生产接线与剩余边界)。
## 与知识库的关系
FR-S7:知识库管理资料、索引、检索和可复用知识;监督者管理工作事件、阶段总结与演变关系。故事线图谱共用现有来源,引用知识库对象,不复制一套资料正文,也不取代已有知识库图谱。
一个知识实体可以关联多份资料,一份资料也可支撑多个实体。图谱的关系与阶段记录由监督者维护,原始内容由所属会话、笔记或知识库维护。新增功能的概念实体与现有图谱实体是否共用标识,需结合现有模型制定技术方案。
FR-S8:本地知识库提供双向定位:资料详情可进入相关故事线图谱,图谱详情可返回资料及引用位置。已有本地条目显示“打开知识库条目”;新归纳提供“新建条目”或“补充已有条目”,先预览内容和引用再保存。外部引用沿用现有能力,打开已保存的有界片段及可用定位信息,不承诺重新打开远端原文;缺少位置时明确提示。外部知识库保持只读;不得因图谱整合而增加对 Dify、FastGPT、RAGFlow 的写入或批量同步。
FR-S9:来源事实、自动归纳、待核对关联和用户确认内容分别标注。移除关联不删除原文,纠正操作可撤销。来源失效时显示不可用状态;回放历史时使用当时已有的归纳与依据,无法恢复旧版原文时明确提示,不能以当前版本冒充历史内容。
## 落地范围
触发、整理与结果共享规则,以及整体架构和图谱对象关系见[逻辑设计](./logic-design.md)。
首个完整流程为:启用监督者、选择范围、生成回顾、进入图谱、核对来源、携带选定上下文继续讨论。知识库双向定位与内容补充随后接入同一流程。图谱关系辅助检索需单独验证效果,不作为首版默认检索策略。
当前工作回顾和图谱读取生产结果,活动展示实际执行与无变化检查、暂停和继续入口,自动监督设置复用已有计划。完整图谱回放及应用市场尚未实现,验收证据见[实施进度](./progress.md)。
具体 UI 与验收见[UI 设计](./ui-design.md)和[User Stories](./user-stories.md)。图谱对象与操作由[故事线图谱设计](../story-graph/prd.md)定义。已实现的实体身份和增量处理方式见[技术设计](./technical-design.md),来源旧版本能力仍受原应用限制。
@@ -0,0 +1,299 @@
# 监督者技术设计
## 文档信息
| 项目 | 内容 |
| --- | --- |
| 状态 | 生产分页、持久化批次、暂停/继续及设置已接线;27 条消息完整会话通过真实模型验证,较大样本仍未完成;完整历史回放未实现 |
| 日期 | 2026-09-23 |
| 产品设计 | [监督者应用产品设计](./supervisor-prd.md) |
| 行为规则 | [监督者逻辑设计](./logic-design.md) |
| 界面设计 | [监督者 UI 设计](./ui-design.md) |
| 实施进度 | SQLite schema 47;当前算法、设置和验收边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界) |
本文回答如何在现有 GoodBuddy 桌面端中实现监督者。它不改变产品范围,也不把模拟 Demo 当作生产数据模型。
生产入口由 `supervision-production.ts` 组装服务、SQLite、共享池和 ask Runtime。`supervision-review-store.ts` 保存冻结来源清单,按有界页和片段供 `SupervisorService` 派发;叶子及连续位置先提交,导航和完整结果之后发布。消息已有知识引用保留本地或外部 locator,已确认记忆通过独立背景输入提供。不会检索外部全库。详细存储、调度及当前限制统一见[分块调度第 0 节](./review-scheduling-design.md#0-生产接线与剩余边界)。
模型实体 `id` 只在单次输出内有效。Main 从同一 scope 的故事线提供最多 100 个未撤销实体候选,生产提示只暴露 `candidateRef`(如 `known_1`)、名称和说明。模型显式选择候选后,Main 转换为严格 UUID 类型的内部 `persistedId`。服务校验候选集成员和重复映射,保存事务再次按实体主键校验故事线归属及撤销状态。没有有效候选引用时分配新 UUID,不按名称或裸模型 ID 合并。来源仍按每次结果分配 UUID,保留原始 `source_id` 和 locator。
新实体省略 `candidateRef`,不要求模型生成数据库 ID。未知非空 `candidateRef`、冲突身份和候选集之外的 UUID 均拒绝。旧输出中的 `persistedId` 仅在 UUID 合法且属于候选集时复用;空值、占位符和格式错误值不能证明已有身份,保留为独立新实体。局部 ID 或同名不会触发合并。
真实旧库存在 `goodbuddy` 等文本实体主键。候选读取为这些键生成稳定 UUID 别名,并在 Main 内保留 `storageId` 到原键的映射;模型看不到这两个字段。别名使用 scope 和原键的 SHA-256 摘要构造 UUID v8,保存时按精确映射查询原实体,保留历史图谱引用和人工确认字段,不迁移或重写原主键。新实体仍由 Main 分配随机 UUID。此兼容仅处理已经存在的旧库键,模型返回同样的文本不能直接复用实体。实测见[真实失败运行修复](./progress.md#2026-09-24-真实失败运行身份修复)。
schema 42 为实体、实体变化和关系增加 `source_reference_ids_json`,保存映射后的来源 ID。升级只补列,旧记录、ID、确认状态及 locator 保留;此前未保存的引用使用空数组,已被覆盖或忽略的历史内容无法据此恢复。
schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、说明、确认状态、关系理由及本次来源引用。后续 run 只更新 `automatic` 的当前实体与关系,已确认、修订或移除状态受保护。历史结果读取自身内容;人工操作带 `resultId` 时先校验成员归属,在事务内更新当前对象及所选结果,其他结果不变。移除关系记录 `revoked`,防止后续相同端点和类型的自动关系恢复它。
升级从现有事件实体关联、实体变化和来源引用恢复结果成员,不猜测名称匹配;旧数据缺失的归属或已经覆盖的内容无法还原,原对象仍保留。新结果即使实体没有事件或来源,也会保存完整成员。结果内容与运行、事件、来源在同一事务提交。
`overview({ target? })` 返回稳定的 `id`、`storyLineId` 及来源 ID。指定 Conversation/Task 时,Main 读取目标实际项目,查询同时要求目标来源匹配、结果 scope 为 global 或包含该项目;LIMIT 在过滤之后。卡片展示结果真实范围,global 结果不会伪装成单会话摘要。`graph({ resultId, storyLineId? })` 校验二者归属,只返回该结果的事件、对象和来源;显式 ID 无效时不回退到最新结果。继续讨论按结果主键读取,并验证来源属于该结果。
工作回顾保存所选结果,切换或刷新使用请求序号忽略迟到图谱。侧栏按目标重建卡片,清空来源和预览,并忽略旧请求;工作栏 tasks 实例的 `targetRef` 可保存 conversation/task 固定目标,取消固定后恢复跟随当前选择。该绑定只控制监督卡片,不改变任务列表的项目范围。继续讨论预览显示实际目标名称和 ID,发送回调显式携带该会话 ID,由 Main 使用该会话的 Runtime 与项目。
侧栏通过 `onOpenSupervisionGraph(resultId)` 交给 App 导航;App 遵守现有离页检查,向 HeartbeatCenter / SupervisorWorkspace 传递类型化 `graphNavigation`,复用 `heartbeat` 路由及 keepalive 页面。每次点击创建新的导航请求,打开 graph 页签并更新历史选择共用的 resultId。overview 未包含该 ID 时仍直接请求其 graph;旧 overview、graph、来源和操作响应通过同一请求序号失效,不覆盖新结果。没有新增 window 事件、页面或持久化字段。
继续讨论入队时,无附件请求省略 `serializedContexts`;请求未指定 Runtime 时使用会话保存的选择。知识库预览与提交均检查目标库存在且为本地可写库,更新实体还检查其实际所属库。侧栏确认框展示 Main 返回的实体字段、目标库和来源正文,取消或写入失败后可以重新预览。
监督来源清单将 UI 时间区间归一化为 UTC,先筛选再按稳定键分页。项目必须存在且 active;消息按闭区间筛选,任务按区间内创建或完成时间筛选。手动和自动监督共用该路径,自动清单另读取 supervisor checkpoint;手动心跳报告保留原有 collector。
已确认记忆作为当前背景,不参与新增正文覆盖。最多四条、每条 500 字符,时间来自真实更新时间;模型不能把背景引用为本批新增证据。旧结果继续保留原来源类型和 locator。监督正文上限现用于分批,余段继续处理;心跳报告的有界输入语义没有改变。
## 模型阶段超时与调度
监督整理超时由 run 创建时冻结;共享模型池仍采用实时设置上限。`supervision:pause` 取消该 run 的排队和在途工作,`supervision:resume` 继续已保存批次;监督整理输出在流中按 `supervisionReview.responseKiB` 检查响应容量,默认 1024 KiB,可在设置页调整为 100 至 16384 KiB。该容量每次请求读取当前设置,继续旧运行也采用新值;超限保留成功批次并提示调高后继续。生成摘要及事实内容不设独立短字符限制,监督旧摘要读取保留全文,具体边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。以下报告领取和租约规则继续适用。
手动和自动监督均在桌面 Main 调用生产工厂,Runtime 解析请求只有 `workMode: ask`,不携带 SSH 项目或执行空间。摘要校验、合并和持久化在 Main 完成;本次取消内容长度限制不改变 `gbagent`、远端 Runtime 启动或桌面到 Agent 协议。
`ApplicationSettingsStore` 持久化报告和整理模型超时,均为 30..600 整数秒,默认 240。报告排队前冻结,监督创建 run 时冻结;计时从获槽及 Runtime 解析后开始,覆盖模型执行及其内部重试。流结束校验 signal 和完成事件,finally 释放临时会话,心跳报告保持原有 100KB 上限,监督整理使用上文的可调响应容量。采集、排队、保存和清理不计入模型超时。
`supervisorModelConcurrency` 为 1 至 4 的整数,默认 1。报告和监督整理共用 Main 内 `SupervisionModelPool` FIFO 池,普通聊天不入池;降低上限不终止在途请求。报告槽位从领取前持有至报告完成或无变化提交后,在下游监督开始前释放,避免并发 1 时相互等待。监督服务外层运行仍串行;该池不是完整分块调度器,也不提供跨聊天与监督的提供商全局并发、RPM、TPM 或 Retry-After 控制。
报告租约为 `max(300, reportTimeoutSeconds + 60)` 秒,300 是下限;每次到期领取最多一条。Runtime 解析后、模型请求开始前按冻结超时刷新租约,数据库要求运行 ID、owner、attempt、claimed 状态和未过期条件均匹配,否则拒绝启动模型。直连模型独立的 600 秒传输超时、网络重试、心跳重领及轮询不属于用户阶段时限。设置 UI 提供两个超时、共享并发及分页/分批/软预算字段。
schema 47 的四张批次相关表使用外键随监督运行清理,未完成运行不新增自动过期删除规则。启动时原有 running 记录会标 failed,但已保存批次和配置保留,活动页仍可继续。发布逐页读取叶子并复用现有结果/来源/图谱写入,在同一事务更新完成状态和自动 checkpoint。
当前设置及冻结范围见[当前设置合同](./review-scheduling-design.md#当前设置合同)。未实现的高级参数继续保留在目标清单,不显示为可保存控件;没有兼容原型数据库的读取器。
## 自动增量收集
本节的 100 来源/2,000 码点批次查询现用于原心跳报告及旧服务测试;生产监督使用 schema 47 清单及批次位置,自动 checkpoint 只在完整发布时提交。已有 schema 46 指纹算法和 checkpoint 仍复用,不重置用户已处理位置。
对应 FR-S4、FR-S10 和 US-S27,行为定义见[自动增量与手动重看](./logic-design.md#自动增量与手动重看)。schema 46 在现有数据库增加 `review_checkpoints`,主键为阶段、规范化 scope 和来源标识,保存版本指纹、已处理位置和来源长度。来源标识区分消息 ID、任务 ID 和消息内知识引用位置,不使用消息时间戳作为身份。
消息正文写入时由 SQLite trigger 更新轻量 `review_revision`;范围、标题、角色和时间共同参与版本指纹。任务标题、状态、创建和完成时间,以及知识引用内容或定位变化分别使对应来源待处理。工具执行元数据更新不会把未修改的消息正文再次标为新增。删除来源时清理其进度。原始消息不复制到进度表;自动查询先排除已处理版本,再读取待处理片段,不重新载入每个会话的历史正文。
原心跳增量查询按来源时间和 ID 排序,每轮最多 100 个来源、每个最多 2,000 码点,正文预算 12,000 UTF-16 单元。该报告路径的余段留待后续检查,窗口外来源不自动补读。生产监督另用冻结清单和持久化批次,未读尾部不会因继续时的滚动窗口变化而消失;旧监督分支的 44,000/48,000 字符上限已退出生产接线。
只有实际送入模型、通过结果校验且成功落库的完整片段推进位置。心跳报告及其进度、监督结果及其进度分别在各自 SQLite 事务提交。监督服务串行执行,后续自动请求在前次保存后重新收集。报告成功而监督失败时,下一次自动检查跳过已成功的报告输入,但重试监督输入;无变化不创建报告、来源或图谱事件。
已有同 scope 故事线的 scope 表示和实体 ID 保留;比较项目集合时忽略排序。模型接收最近摘要和同 scope 最多 100 个未撤销实体的候选引用、名称、说明,显式选择候选才复用,身份转换按上文执行。不同范围不合并,已有重复实体不自动修复。知识引用继续以独立来源保存 locator;当前记忆和旧摘要不作为新事件。
迁移在事务中扩展心跳计划及运行表的状态 CHECK,保留列、索引、ID、计划、报告与引用;消息版本列使用默认值,无需遍历或重写旧正文。进度初始为空,首次自动运行从配置窗口开始。报告保留期限覆盖 `no_change` 运行,但不删除来源进度;删除报告不触发重新消费。
## 活动读取与执行持久化
对应 FR-S10、US-S26。schema 45 在既有 `supervision_runs` 增加可空 `heartbeat_run_id` 和唯一索引,在 `heartbeat_runs` 增加执行范围、下游投影状态、错误及结束时间字段。迁移保留原记录,不补造旧关联。监督服务通过 store 的 start/fail 接线记录执行,保存结果事务更新同一 run;不增加调度器。心跳在执行前记录冻结范围和投影进度,报告事务仍保持原有含义,下游失败单独保存。手动心跳在生产路径使用实际结束时间,活动等待下游结束后才显示整次执行的结束时间。
`supervision:activity` 沿用可信 sender 与 Zod 输入校验,接受 `limit`(1 至 100,默认 50)、`offset`(0 至 100000)及可选 `configId`。应用未启用时返回空列表且不访问活动表。未筛选时 SQL 合并心跳及未关联的监督记录;筛选时在 LIMIT/OFFSET 前按 `heartbeat_runs.config_id` 精确匹配,排除独立手动回顾,不依赖计划名称或范围。关联查询使用运行 ID;返回状态、阶段、范围、时间、错误、摘要和 resultId,不调用模型。保留规则和历史缺失见[活动记录](./logic-design.md#活动记录)。
Preload 暴露类型化 `supervision.activity()`。`overview({ resultId })` 支持按 ID 读取历史回顾,供活动跳转使用。Renderer 只在活动页签和 heartbeat 路由同时激活时串行定时读取,每次有界分页;effect 清理撤销定时器并使旧响应失效。App 关闭应用时卸载页面;读取没有执行副作用。该改动位于桌面 Main/Renderer,未修改 Agent 或远程 Runtime 协议。
活动进度现在包含既有运行 JSON 中的持久 `phase` 及导航表计数 `navigationNodes`。`SupervisionReviewStages` 将实际阶段与监督运行状态组合呈现;`SupervisionBatchDetails` 按既有 10 批分页结果组织项目、会话和批次,新增批数变化时刷新已打开页。导航和叶子使用不同的严格输出 schema,继续仍复用成功叶子与导航节点。字段语义、旧记录缺失和同步阶段限制由[生产调度合同](./review-scheduling-design.md#已接入的调度与存储)统一定义;未增加任务调度器、数据库表或远程协议。
## 1. 总体方案
监督者作为 Main 进程中的一个领域服务,复用现有智能心跳的计划、运行领取、租约、重试和有界模型调用。图谱是监督者整理结果的查询视图,知识库继续拥有资料正文、索引和检索。
```mermaid
flowchart LR
UI["Renderer\n监督者页面 / 图谱 / 侧栏"]
IPC["Preload + IPC\n类型化输入输出"]
SVC["SupervisorService\n范围、运行、结果、用户确认"]
HB["HeartbeatService\n计划、领取、租约、重试"]
EVIDENCE["EvidenceCollector\n按范围读取有界证据"]
MODEL["BoundedSummarizer\n只读 JSON 输出"]
STORE["AssistantDatabase\nSQLite 事务与查询"]
KB["KnowledgeService\n本地资料、引用、检索"]
CONV["会话 / 笔记 / 任务"]
UI --> IPC --> SVC
HB --> SVC
SVC --> EVIDENCE
EVIDENCE --> CONV
EVIDENCE --> KB
SVC --> MODEL
SVC --> STORE
SVC --> UI
SVC -->|确认后补充本地条目| KB
```
职责边界如下:
| 模块 | 负责 | 不负责 |
| --- | --- | --- |
| `HeartbeatService` | 计划、到期运行、租约、重试、已有报告兼容 | 图谱实体合并、用户确认、知识库写入编排 |
| `SupervisorService` | 触发统一、证据收集、整理结果、图谱查询和修订 | 自己实现调度器、复制来源正文 |
| `EvidenceCollector` | 按冻结范围读取会话、笔记、任务、知识库引用 | 越过范围读取全部数据、调用工具 |
| `SupervisorSummarizer` | 以有界输入调用文本模型并解析结构化输出 | 执行工具、直接修改知识库 |
| `AssistantDatabase` | 事务、外键、分页、状态和来源定位持久化 | 生成模型内容 |
| `KnowledgeService` | 资料和本地知识条目的维护、检索及引用 | 管理监督者事件和关系 |
| Renderer | 展示和发起用户操作 | 访问 SQLite、决定权限或拼接 SQL |
## 2. 运行路径
应用启停遵守[逻辑规则](./logic-design.md#应用启停与执行)。`ApplicationSettingsStore` 对新设置及缺失的 `heartbeatEnabled` 使用 `false`,保留显式布尔值。Main 在心跳 tick 调用 `processDue()` 前、`heartbeatsRunNow` 与 `supervisionRun` IPC 入口、心跳完成后的监督回顾回调中读取已保存设置,只有值为 `true` 才继续。此检查不取消已领取运行,也不改变普通任务调度链路。Renderer 由 App 将同一启用状态传入 `RightAssistantSidebar`,控制监督面板挂载。
`HeartbeatCenter` 的五个顶层页签直接选择内容,无内部页签。overview 只由 `SupervisorWorkspace` 读取 `supervision.overview` 的统一结果,历史选择和图谱共享 resultId;新请求控件不改变所选结果的 scope/timeRange。`HeartbeatSections` 仅在 plans 输出内容,包含 `HeartbeatSettings`、范围与刷新、运行概览、指标、趋势、自动监督报告、记忆与行动建议及审计;activity 只渲染 `SupervisorActivity`,settings 只渲染两个算法参数表单。运行中的手动请求保留既有结果,活动入口清空计划筛选并聚焦 activity。计划列表放在当前状态下,复用唯一创建按钮及未配置说明。计划表单在原组件内通过 body Portal 显示,复用 `custom-task-dialog` 和 `activateModalFocus` / `trapTabFocus`,没有复制表单或嵌套 form。关闭确认、提交锁定及失败草稿由本地状态管理;保存继续调用 App 原有心跳 CRUD 回调。
计划“执行记录”将 `activityPlanId` 交给活动查询,并切换、聚焦 activity 页签。更换计划时清空旧列表、重置 offset 并清理旧轮询,保留筛选控件的键盘焦点;该筛选不传入自动监督概览,返回 plans 时仍显示全部计划的统计和心跳审计。报告展开和追加条数保存在同一组件内,切换顶层页签仍保留;图谱继续由 `SupervisorWorkspace` 持有结果选择,活动结果跳转保留精确 resultId。此次仅调整桌面 UI 和只读活动查询,不修改 schema、Agent 或远程 Runtime。
计划 CRUD 沿用 App → Preload → Main 接线。`heartbeatRecurrenceSchema` 与 `computeNextHeartbeatRun` 仅支持 daily/weekly;本轮不增加分钟间隔合同或调度分支。编辑草稿保存已有 `timezone` 和 `enabled`,新建仅在显式提交时使用本机时区创建启用计划。UI 命名统一为自动监督,内部 ID、翻译键、数据库及 IPC 名称保留。
手动回顾和心跳回顾必须经过同一个 `SupervisorService.run()`,只在触发来源上区分。这样可以保证两者使用相同的范围校验、输入预算、输出契约、结果状态和来源处理。
```mermaid
sequenceDiagram
participant T as 手动/智能心跳
participant S as SupervisorService
participant D as AssistantDatabase
participant C as EvidenceCollector
participant M as 文本模型
participant R as Renderer
T->>S: run(scope, timeRange, trigger)
S->>D: 创建 run,冻结范围与时间
S->>C: collect(frozenScope)
C-->>S: 有界证据 + source references
S->>M: 结构化整理请求,禁止工具
M-->>S: validated result
S->>D: 事务写入 result、events、entities、relations、references
D-->>S: completed
S-->>R: 状态和定位信息
```
运行状态与内容确认状态分开保存:
- 当前监督运行状态:`running`、`paused`、`completed`、`failed`、`no_change`。心跳及合并活动的映射见[活动记录](./logic-design.md#活动记录)。
- 内容状态:来源事实、自动归纳、待核对、用户确认、用户修订、已移除关系。
- 运行成功只表示模型结果已按契约保存,不表示所有实体和关系已经被用户确认。
- 失败保留上次完整结果和本次已保存批次;暂停不生成完整结果,继续入口恢复同一 run。
## 3. 数据模型
首版使用现有 `AssistantDatabase` 和同一 SQLite 数据库。新增表保持领域对象独立,但不复制会话、笔记或知识库正文。
```mermaid
erDiagram
supervision_runs ||--o{ supervision_results : produces
supervision_results ||--o{ story_events : contains
story_lines ||--o{ story_events : organizes
story_lines }o--o{ knowledge_entities : references
story_events }o--o{ knowledge_entities : changes
knowledge_entities ||--o{ knowledge_relations : source
knowledge_entities ||--o{ knowledge_relations : target
supervision_results ||--o{ source_references : cites
story_events ||--o{ source_references : cites
knowledge_entities ||--o{ source_references : cites
knowledge_relations ||--o{ source_references : cites
```
建议的最小持久化对象:
| 对象 | 关键字段 | 说明 |
| --- | --- | --- |
| `supervision_runs` | `id`, `trigger`, `scope_json`, `time_range_json`, `status`, `error`, `started_at`, `completed_at` | 一次手动或心跳整理;保存冻结范围,不从当前配置回推历史 |
| `supervision_results` | `id`, `run_id`, `summary`, `change_digest`, `coverage_json`, `graph_snapshot_json` | 工作回顾、监督反馈和图谱共同读取的结果版本 |
| `story_lines` | `id`, `scope_json`, `title`, `created_at`, `updated_at` | 用户选择的工作故事线,不等同于 Project |
| `story_events` | `id`, `story_line_id`, `result_id`, `occurred_at`, `title`, `description`, `event_type`, `confidence` | 时间轴上的事件;同日聚合只在查询或 UI 层处理 |
| `knowledge_entities` | `id`, `canonical_label`, `description`, `state`, `confirmation_state`, `updated_at` | 可被多条故事线引用的持续认识,不与知识库文档一一对应 |
| `entity_changes` | `event_id`, `entity_id`, `change_type`, `description`, `confirmation_state` | 表达提出、补充、验证和修订,避免覆盖实体正文 |
| `knowledge_relations` | `id`, `from_entity_id`, `to_entity_id`, `relation_type`, `reason`, `confirmation_state` | 关系独立拥有状态和来源;移除关系不删除来源 |
| `source_references` | `id`, `owner_type`, `owner_id`, `source_type`, `source_id`, `locator_json`, `availability` | 只保存来源标识和有界定位,不复制正文 |
外键和索引要求:
- 新增监督者对象使用 UUID;已有文本实体主键按上文兼容映射保留。实体、来源和关系通过稳定 ID 关联,不能只按名称合并。
- `supervision_runs` 按 `created_at`、`scope` 查询;事件按 `story_line_id, occurred_at` 查询。
- 关系对 `(from_entity_id, to_entity_id, relation_type)` 建唯一约束,允许同一实体对存在不同关系类型。
- 删除故事线只删除其组织关系,不删除共享实体、来源或知识库资料;删除知识库资料后来源标为不可用。
- 整理结果、实体变化、关系变化和来源引用在同一 SQLite 事务中提交,提交失败全部回滚。
- 只保存整理结果自身的对象内容与有界来源,不建立原会话、记忆或知识库的完整历史快照和独立向量索引。
叶子模型输出使用严格共享契约,包含 `events`、`entities`、`entityChanges`、`relations`、`summary`、`changeDigest`、`openItems`,事实项保留 `sourceReferenceIds`。Main 校验数量、字符、枚举、ID 所属范围和引用存在性,拒绝未知字段后才保存。导航输出另用[导航合同](./review-scheduling-design.md#已接入的调度与存储),允许省略空事实数组;该规则不适用于叶子。
## 4. Main、IPC 与 Renderer
新增能力沿用现有边界:Renderer 只传业务输入,Main 重新校验项目、故事线、时间范围、来源和权限。
建议的 IPC 分组:
| 通道 | 用途 |
| --- | --- |
| `supervision:overview` | 工作回顾、运行状态、未解决事项和最近结果 |
| `supervision:run` | 手动开始一次整理,完成后返回包含 runId 的结果 |
| `supervision:activity` | 有界读取真实执行和阶段状态 |
| `supervision:graph` | 按故事线、时间区间和回放时刻分页读取图谱 |
| `supervision:object` | 读取事件、实体、关系及来源详情 |
| `supervision:confirm` | 确认、修订或移除自动关系和实体变化 |
| `supervision:continue-context` | 生成继续讨论前的上下文预览 |
| `supervision:knowledge-preview` | 预览新建或补充本地知识条目 |
| `supervision:knowledge-commit` | 用户确认后调用知识库服务写入 |
IPC 返回图谱查询结果时使用分页和有界字段;详情中的来源正文复用现有会话、笔记和知识库读取路径。不要把完整图谱一次性发送给 Renderer,也不要把模型原始输出暴露为可执行内容。
侧栏反馈通过已有应用状态刷新机制读取结果。首版可以在手动运行完成后主动刷新,心跳完成后发布一个不含私人正文的变更通知,Renderer 再按权限读取摘要;不新增系统通知正文通道。
## 5. 安全与一致性
- 监督者模型调用固定为 `ask` 语义,输入来自 `EvidenceCollector` 的有界快照,工具授权始终拒绝。
- `Global`、Project、多项目和固定 Conversation/Task/Experiment 目标在 Main 冻结;模型不能生成或改变范围。
- 每个来源引用必须属于本次冻结范围,跨范围引用、伪造项目 ID 和不存在的来源 ID 直接使结果失败。
- 心跳运行沿用现有 `heartbeat_runs` 的领取、租约和重试机制;新增监督运行按心跳运行 ID 关联,活动据此合并阶段。没有新增取消协议。
- 同一配置同时触发手动和定时运行时,沿用现有幂等键和运行领取规则;已完成结果不能重复写入。
- 用户确认或修订后的实体、关系不被后续整理静默覆盖;新证据以补充或冲突待核对状态写入。
- 外部知识库只读。图谱可以引用保存到本地的外部片段和定位信息,不调用远端写入、不承诺打开远端原文。
- 来源正文仍由原应用拥有。监督者删除关系、故事线或运行记录时不得删除会话、笔记、任务或知识库资料。
## 6. 实施顺序
### 阶段 A:先把现有心跳变成统一运行入口
- 抽出 `HeartbeatSummarizer` 的有界输入和结构化输出校验,使 `SupervisorService` 可以复用。
- 增加手动运行的统一服务入口,但先只生成现有回顾结果,不改变心跳页面行为。
- 为运行状态、范围冻结、失败保留旧结果补充 Main 单元测试。
### 阶段 B:接入证据引用和阶段回顾
- 实现 `EvidenceCollector`,接入会话、任务、消息已有知识引用和记忆;魔法笔记仍未接入。
- 建立 `supervision_runs`、`supervision_results` 和 `supervision_sources`,保存来源 locator,并通过 `KnowledgeService` 解析本地文档/分块。
- 在现有智能心跳页面增加监督者工作回顾入口;不先改应用名称和导航 ID。
### 阶段 C:实现最小故事线图谱
- 建立事件、实体、实体变化和关系表及查询服务。
- 先完成平铺环绕时间轴、节点详情、来源展开、回放和列表模式;圆形视图继续作为后续布局。
- 同一实体通过稳定 ID 连接多个事件,禁止按每次事件复制实体。
- 用真实整理结果替换 Demo 的模拟数据,并保留 Demo 作为视觉回归 fixture。
### 阶段 D:接入用户确认和知识库双向定位
- 增加关系确认、修订、撤销和实体拆分的最小交互。
- 从图谱返回本地会话资料及引用位置;知识库文档定位依赖来源中存在文档/分块元数据。
- 新建或补充本地条目必须经过预览和确认;外部知识库保持只读。
### 阶段 E:侧栏反馈与增量整理
- 将结果摘要投影到现有右侧助手栏,并沿用当前会话生命周期。
- 心跳只负责触发,监督者根据新证据决定有无变化和是否反馈。
- 在运行去重、用户修订保护和来源失效提示稳定后,再评估更复杂的跨目标固定策略、事件唤醒和自动阶段识别。
## 7. 验证策略
首版每层验证一条可追踪链路:
1. `EvidenceCollector` 测试范围裁剪、字符预算、来源定位和不可用来源。
2. `SupervisorService` 测试模型输出解析、引用完整性、重复运行、取消、失败和部分结果。
3. `AssistantDatabase` 测试迁移、事务回滚、外键、分页、关系移除不删来源。
4. IPC 测试不可信输入、越权 Project/Source ID、确认状态和知识库写入前置预览。
5. Renderer 测试从反馈进入图谱、点击实体高亮多个时间点、回放不显示未来结果,以及窄屏列表访问。
6. 使用一组固定脱敏 fixture 验证“监督反馈 → 实体 → 多个事件 → 来源 → 继续讨论”的完整路径。
首个生产验收不要求模型自动发现所有关系。必须先证明来源可追溯、失败可解释、用户修订不会丢失,以及图谱与工作回顾读取同一结果。
## 8. 暂不实现的内容
- 独立的图数据库、向量图谱或新的知识库正文存储。
- 让监督者拥有通用 Agent 工具执行能力。
- 自动把所有聊天消息变成事件或节点。
- 无依据的实体自动合并、旧观点自动失效和静默覆盖用户确认。
- 应用市场建设作为监督者开发前置依赖。
- 在没有增量和去重证据前实现事件触发、自动阶段识别和全量后台重算。
## 当前剩余差距
- 实体与关系 revoke 保存状态;界面没有 undo 或恢复入口。
- 跨 run identity 依赖同 scope 候选 UUID 的显式引用,不自动合并历史重复实体;模型漏选候选时会创建新身份。
- 结果图谱使用真实事件实体连线,侧栏已按固定目标过滤并支持结果直达图谱;逐事件状态回放和 Experiment 尚未实现。
- 用户暂停已接到排队/在途 signal,并保留已保存批次;源版本变更后的局部重算、完整模型配置冻结及其他限制见分块调度第 0 节。
- 监督知识实体与知识库正文条目仍是不同对象,知识正文只能通过现有条目定位和预览接口处理。
@@ -0,0 +1,104 @@
# 监督者 UI 设计
日期:2026-09-16。状态:目标设计,遵循根目录 [UI-DESIGN.md](../../../UI-DESIGN.md)。需求对应[监督者应用产品设计](./supervisor-prd.md) FR-S1 至 FR-S9。
## 应用入口
应用中心中的智能心跳入口升级为“监督者”,沿用既有启用、常驻与排序能力。未来应用市场使用同一应用身份。应用已启用时,停用单条自动心跳计划仍可手动回顾;关闭应用后,已打开页面显示关闭状态并禁用工作区操作。默认值及运行边界见[应用启停与执行](./logic-design.md#应用启停与执行)。
当前应用页只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”(英文活动标签为 Activity records)。自动监督独立管理计划;设置只显示模型超时与并发、回顾算法,不含计划、报告、审计或内部页签,也不显示整页全局徽标或独立刷新行。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。
工作回顾只读取统一监督结果。顶部为紧凑的“新回顾”范围、时间、发起与刷新控件,不新增 Modal;简短帮助说明选择仅作用于新请求。历史结果选择与图谱入口位于正文之前,即使只有一条结果也显示选择器。新回顾工具栏、状态、历史选择和结果面板铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设阅读宽度上限;结果内部按面板内边距排版,不再单独居中窄正文。正文使用“监督结果”标题,常显生成日期、真实范围和覆盖区间,历史结果不标为最新。工作总结按原有空行分段并保留换行;本次变化和未解决事项直接使用已有字段,不补写结论或截断正文。390px 下新请求字段与操作各占两列,结果元数据可换行。
新回顾运行中保留已显示结果及其日期、范围,禁用重复提交,并显示简短状态和活动记录入口;该入口清除计划筛选并聚焦活动页签。工作回顾不读取自动监督报告,也不显示自动报告空态、记忆与行动建议或第二行全局范围与刷新。无监督结果时显示手动回顾入口;自动报告是否为空不影响此判断。
## 活动
文案遵循根目录 [监督者文案规则](../../../UI-DESIGN.md#134-智能心跳):不把内部路线图、交付状态或实现缺项展示给用户。活动说明只描述手动回顾、自动监督执行及自动刷新;真实错误、失败和暂停状态仍展示,并保留恢复操作。事实详情提示展开批次并对照原文核对模型内容,不承诺完整语义识别;图谱说明实体状态以所选回顾结果为准。
对应 FR-S10、US-S26。活动按开始时间倒序,每页 50 条,提供较新/更早记录和刷新。列表保留触发方式、执行范围、开始/结束时间、实际状态、阶段状态、错误及已保存摘要。没有结果时不显示结果按钮;有监督结果时可分别进入该结果的工作回顾和故事线图谱,焦点移到目标页签。超过最近 20 条的结果仍按 ID 读取,不能回退最新结果。
活动页只展示统一监督活动、进度、详情及结果入口,不在列表下方重复显示自动监督概览或心跳审计。计划“执行记录”打开此页并按精确 ID 筛选,筛选器显示所选计划并提供清除;监督查询在分页前过滤。同名计划仍按 ID 区分;没有匹配记录时显示筛选无结果,不回退全部数据。自动报告正文和建议处理位于自动监督。
自动监督页依次展示范围与“刷新自动监督”、当前状态及计划列表、运行指标、报告趋势、待确认记忆与行动建议、“自动监督报告”历史、心跳报告阶段审计。刷新统一更新此页数据。报告与运行历史每次追加 20 条,报告可展开全部摘要和洞察,切换页签保留展开与分页位置;不再单独复制最近报告。建议保留确认、忽略、带入对话、标记完成及长内容展开。活动的计划筛选不改变自动监督的统计范围。当前状态下复用计划列表的唯一创建按钮,无计划时只显示列表的未配置说明,不叠加第二个计划空态或创建入口;点击直接打开现有 Modal。报告为空时明确写“暂无自动监督报告”,与工作回顾的结果状态区分。
加载、空记录和读取失败分开显示;读取失败保留已显示记录并提供就地重试。活动页面激活时,存在运行中记录则在上次读取结束 2 秒后刷新,否则 10 秒后刷新;每次最多一个请求。切换顶层页签、离开监督者或关闭应用后停止定时器,迟到响应不更新界面。错误后等待用户重试,不持续重发失败查询。
活动列表与工具栏铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设仪表盘宽度上限。范围、计划筛选、清除筛选和刷新紧邻排列,空间不足时换行。列表使用共享表面、边框、状态颜色和按钮。每条运行首先显示类型、状态、开始时间和范围;“收集来源 / 整理批次 / 合并摘要 / 发布回顾”紧随其后,宽窗口使用按内容排布的紧凑状态行,窄于 720px 时两列,不为每个阶段铺设独立色块卡片。阶段读取已保存位置和真实运行状态,剩余来源为零不表示发布成功。旧运行没有阶段字段时显示“未记录”,不猜测失败位置;已提交来源清单可确认收集结束。无变化运行不显示新结果跳转,后续无需执行的阶段单独标注。
阶段下显示已保存批数、已读字符数、剩余来源和已保存导航合并数,运行中另显示排队或在途数。字符进度按码点统计,界面不展示 Unicode 术语。恢复操作先于详情:运行中可暂停,失败或暂停后沿用同一运行继续;已记录合并、发布阶段时分别显示“继续合并摘要”“重试发布”。成功叶子保留。来源失效时显示新建回顾提示,不提供无效继续。原始错误默认折叠,展开后的完整文本最多占 240px 高度,可滚动、选择复制和键盘聚焦;错误与恢复操作位于批次树之前。结束时间、覆盖区间、报告状态和已有摘要收进“运行详情与配置”,已保存配置在其中再次折叠。
“已保留事实与来源”展开后才查询既有批次接口,每页 10 批,按本页持久记录组成项目、会话、批次层级。项目使用可用名称,会话优先使用保存来源的标题;只有已保存批次标记“已保存”,不创建未落库的待执行、在途或失败叶子,也不把本页数量当作项目总量。批次摘要收起时最多两行,展开后保留完整摘要、事件、实体、变化、关系及原文片段。来源显示消息 ID、码点范围及版本;长原文局部滚动。原生 details/summary 保留键盘展开和可见焦点,轮询不主动展开用户收起的会话。加载、空批次和读取失败分开显示,失败可重试;换页失败不显示上一页批次,切换 run 后旧响应失效。暂停状态为“未完成,可继续”,手动回顾软预算暂停后在工作回顾页提示去活动继续,不显示完成通知。
## 自动监督设置
2026-09-23 当前已实现“模型超时与并发”表单:心跳报告与监督者整理分别设置,均为 30 至 600 的整数秒,默认 240;监督模型并发数为 1 至 4 的整数,默认 1。保存真实字段 `heartbeatReportTimeoutSeconds`、`supervisorOrganizeTimeoutSeconds`、`supervisorModelConcurrency`;无效输入不能提交,设置加载/保存中或锁定时禁用,失败保留草稿并提供重试。超时以阶段读取设置的时点为准,不改变已冻结的值;并发数影响后续派发,降低上限不打断在途请求。
并发说明明确报告和监督整理共用上限,排队不计入模型超时;普通聊天不受此设置限制,但仍与监督竞争上游额度。表单不宣称提供商全局并发、RPM、TPM 或 Retry-After 限流已实现。
模型超时不包含数据收集与保存,直连模型另有独立 600 秒传输超时。“回顾算法”表单提供“每次读取来源条数”“每批文本量”“每批消息数”和“单次整理时长”,显示默认值及合法范围,使用“保存回顾算法”独立提交。文本量内部仍按 UTF-16 单元计算,界面不要求用户理解编码单位。保存经 App 的现有设置回调进入共享设置存储,加载/保存/锁定时禁用,失败保留草稿。取值以[当前设置合同](./review-scheduling-design.md#当前设置合同)为准。帮助说明执行时间达到软预算后,正在处理的批次完成并保存,再暂停,可从活动记录继续;已保存配置用“同时处理任务数”解释并发,说明实际并发还受当前监督设置限制。
对应 US-S22 至 US-S24。“自动监督”先显示计划列表,点击创建或编辑才打开 Modal;复用原表单,不保留页面内副本。表单展示名称、项目范围、每日或每周频率、指定时间、回顾窗口(1 至 720 小时)和历史保留(1 至 365 天);每周显示星期选择。时区常显,新建使用本机时区,编辑保留计划原时区。列表上方帮助说明选择每日或每周时间、保存并启用后自动运行。
无计划时显示“尚未配置自动监督,目前仅支持手动回顾”;全部暂停时另行说明。加载或首次读取失败不显示未配置状态。打开页面、Modal 或填写字段均不创建计划,只有点击“保存并启用计划”才提交创建。已有计划显示范围、周期、时区、回顾窗口、保留期限和启停状态,保留编辑、暂停、恢复、立即运行、执行记录和确认删除;暂停时不显示待执行的下次时间,编辑保存不改变暂停状态。
Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复用 header、body、footer:标题左侧、带本地化名称和工具提示的 X 右侧;底部取消次按钮、保存主按钮整体右对齐。标题和操作区保持可见,仅表单内容滚动。表单使用独立字段容器,不嵌套设置表单;窄窗口表单单列,保留共享窗口安全边距。
沿用整窗 Portal、共享遮罩、背景 inert、焦点循环与关闭后焦点恢复;初始焦点进入名称。X、Escape、取消或点击遮罩关闭时,有未保存修改先确认;确认初始焦点为继续编辑,Escape 或 X 只返回编辑,点击遮罩不放弃修改。保存期间禁止关闭和重复提交,失败在表单内显示并保留草稿。
界面及可访问名称统一使用“自动监督 / 回顾”,保留内部 heartbeat 标识及已有计划名称、报告正文。表单继续使用共享控件和容器响应式布局,建议及审计无需先进入即可配置计划。
### 算法配置设置目标
以下高级分组为目标交互;已接通的四项算法控件以上节为准。其余参数引用[设置配置清单](./review-scheduling-design.md#4-设置配置清单),不在 UI 文档另设数值,不增加一级页面。
普通配置常显消息每页条数、监督并发、报告/整理模型超时和单次执行软预算。高级配置按“来源与分块”“上下文与输出”“汇总与内存”“超时与续跑”“上游额度”分组。消息页字段的常显帮助为:“每次从数据库读取的消息数;会持续读完所选范围,不限制回顾总消息数,也不决定每次发给模型的消息数。”模型批次消息数、片段数和新增字符数分别标注,避免统称“批量大小”。
字段显示单位、已确定的默认值和合法范围;补充帮助说明调大后的内存、等待或模型用量影响。允许继承的节点超时显示“继承监督整理值”及解析后的秒数;依赖模型容量的输入/输出预算显示所用模型、容量及 token 是否为估算。跨字段校验覆盖输入与输出预留之和、扇入可容纳性、软预算与硬期限顺序、退避起始与上限;错误附在字段并关联可访问说明,提交时聚焦首个错误,Main 仍作权威校验。
规划中的“保存配置”持久化后只用于新建运行,常显“已开始和待继续的回顾沿用启动时配置”。“恢复本组默认值”只修改该组草稿,经保存才生效,不开始回顾、不修改已有计划;取消编辑恢复已保存值。运行详情展示本次实际配置,继续操作沿用它;想采用新算法配置时使用“重新回顾”,说明会重新计算。当前已实现字段仍按上文的阶段读取和共享池生效方式工作,整次 run 冻结接线完成后再统一调整提示。
初次加载、读取失败、编辑、保存中、保存失败和已保存分别呈现;读取失败不填假默认值,保存失败保留草稿并允许重试。保存成功使用应用通知,字段错误就地显示,不重复通知。窄窗口按单列排列,折叠标题可键盘操作,单位和关键生效说明不藏进悬浮帮助。
只在真实 Main/存储/运行路径接通后显示相应可编辑字段;尚未实现的配置留在设计文档,不放置永久禁用的假控件。已接通但因加载、保存或依赖缺失而临时不可编辑时说明实际原因。上游额度明确显示适用连接/模型和是否包含普通聊天;未实现跨路径共享时不得标为提供商全局限制。覆盖完整性、来源校验及事实保留不提供开关。
## 工作回顾
当前页面提供全局或单项目范围,以及 1、7、30 天时间区间选择。没有自动计划也能手动回顾;成功结果显示自身冻结的范围、时间和未解决事项,可选择历史结果或进入图谱。来源筛选由本次回顾范围和时间限制;图谱详情只列出所选事件的关联来源,实体或关系列出相关事件的来源。独立的来源类型选择器尚未实现。
选择历史结果后,图谱读取该结果的成员和内容,标题显示其回顾区间和生成时间。切换时清空旧图谱和详情并显示加载状态,迟到请求不得替换新选择。确认、修订和移除反馈更新当前所选结果,后续自动回顾不改写旧结果。
手动“回顾当前进展”生成中禁用重复提交。该操作新建回顾,活动页“继续”恢复原 run;两者不得混淆。失败保留上次结果、本次范围和成功批次。手动操作不推进自动位置,删除报告也不重置自动位置。
## 会话侧栏
监督反馈标题右侧使用共享紧凑图标按钮固定/取消固定目标与刷新回顾;固定状态通过
`aria-pressed` 和按钮高亮表达。目标正文显示会话或任务名称,名称缺失时使用“未命名会话”
或“未命名任务”,原始 ID 仅作为悬停补充。无目标时禁用固定和刷新,读取中禁用刷新;
窄侧栏中的长标题换行,按钮保留可见键盘焦点。
仅在应用已启用时挂载监督面板及固定目标控件;关闭或设置尚未加载时不挂载,也不发起面板查询。任务列表仍按原规则显示。
任务中心从上到下依次显示项目概览(含当前会话统计)、监督反馈、项目任务。
右侧通用助手工作栏中的监督卡片展示“监督反馈”,沿用现有[工作栏设计](../assistant-workbar/prd.md)的布局和执行边界。当前支持 Conversation/Task:有选中任务时跟随任务,否则跟随会话;“固定监督目标”保存当前目标,切换聊天后保持,取消固定恢复跟随。卡片显示目标、结果真实范围、回顾区间和摘要,可刷新并查看匹配来源;无目标或无匹配结果显示空态。Experiment 尚未接入。
有匹配结果时提供“在图谱中查看”,打开现有监督者页面的图谱页签并读取该结果;无匹配结果不显示入口。按钮保留原生键盘操作,进入后焦点落在图谱页签,继续使用共享 PageTabs 的方向键导航。固定目标不因跳转改变;再次点击相同或不同结果均重新定位,包括页面缓存后再次进入。结果不可用时显示错误并重试原 ID,不展示默认最新结果。
当前“继续讨论”预览实际发送会话名称、ID 和上下文,确认后发送到该会话;固定任务使用其关联会话,无法确定发送会话时禁用。切换目标使旧来源请求和预览失效。编辑预览中的引用仍属后续工作。
## 故事线图谱
使用 `supervisor` 页面壳层,最大内容宽度为 `1440px`。当前生产图谱左侧为紧凑的事件、实体和关系列表,中间为 SVG 画布,右侧为详情与来源。桌面列宽为 `216px minmax(0, 1fr) 280px`,画布获得主要空间。外圈事件按发生时间排序后等距逆时针排列,不表示实际时间间隔;起止之间保留缺口和方向箭头。内部显示真实实体名称,实线仅连接实际事件与实体,虚线仅连接实际实体关系。
实体及其影响连线复用现有 `--graph-node-*-border` 令牌;颜色用于辅助辨认实体,不代表故事分类或确认状态。选中项加粗并突出连线,其余节点降低图形强调度但保留可读文字。详情分为名称与说明、图谱关联、确认操作和来源,来源正文按需读取。图例常显,读图说明可展开。
画布每批最多显示 8 个事件、6 个实体;同时间事件分开排布。列表保留全部返回记录和完整名称,选择实体会定位到真实关联事件所在批次,选择关系会优先显示两端实体。滑块明确命名“事件浏览”,只切换当前事件,不还原实体历史状态,也不宣称完整历史回放。当前接口返回单条故事线,不复制 Demo 中的三条模拟故事。后续视图规划见[图谱设计](../story-graph/prd.md)。
按实际内容容器宽度响应:不超过 `1190px` 时变为 `184px` 列表加画布,详情移到下方;不超过 `860px` 时变为单列,画布优先,列表限制高度并独立滚动。SVG 最小宽度为 `660px`,窄屏只在画布内部横向滚动,页面本身不横向溢出。范围、时间和操作可换行。节点、关系与事件均可从列表选择,画布节点支持 Enter、Space 和可见焦点。加载、无数据、读取失败和服务不可用各自呈现,错误提供可行的恢复说明或重试入口。
隔离视觉验证使用 `tests/support/supervisor-layout-regression.tsx` 渲染生产 React 组件;所有输入标为模拟 fixture,不读取用户数据库或调用模型。`tests/supervisor-layout.electron.test.ts` 启动 Vite 与 Electron,通过 `executeJavaScript` 测量布局、`capturePage` 截图,覆盖 1440、1024、390px、浅深主题、长名称、密集事件和异常状态。可通过 `GOODBUDDY_SUPERVISOR_ARTIFACTS` 保留截图;交互预览入口为 `node tests/support/supervisor-preview.mjs`。
设置 `GOODBUDDY_SUPERVISOR_PLAN_MODAL=1` 可只验证五页签 fixture 中的计划 Modal:创建和编辑各覆盖浅深主题、1440×1100、390×1100、390×480,检查关闭按钮、底部按钮对齐、正文滚动、鼠标命中和原生键盘操作。截图及测量写入同一产物目录。
设置 `GOODBUDDY_SUPERVISOR_RECAP=1` 可验证本次工作回顾分区:1440×1100 和 390×1100 下覆盖监督结果与自动报告均有内容、仅有监督结果、两者均空,另检查历史结果运行中仍可阅读以及活动入口。该分支检查每页一处刷新、自动报告和建议的归属、正文段落与宽度,并截取自动页建议、展开报告和审计区块。
@@ -0,0 +1,570 @@
# 监督者 User Stories
日期:2026-09-22。本文定义监督者从应用入口到故事线图谱的用户场景、界面行为和验收条件。
当前代码已经交付智能心跳页面和运行链路;监督者工作回顾、故事线图谱、会话侧栏反馈和知识库双向定位仍是目标设计。因此每条故事标注了当前状态:
- **已实现**:当前生产界面或 Main/Preload 链路已有可验证行为。
- **待实现**:产品目标,不能用现有智能心跳页面或 Story Graph Demo 代替验收。
- **沿用**:已有能力在监督者统一入口中必须继续保留。
## 用户角色
| 角色 | 目标 |
| --- | --- |
| 工作使用者 | 回到工作现场,理解进展、决定和未解决事项,并继续工作 |
| 知识整理者 | 核对来源,修正自动归纳,把可靠认识补充到知识库 |
| 监督者配置者 | 决定观察范围、运行时间和反馈方式 |
| 受限访问者 | 只能看到已授权范围内的会话、任务、笔记和知识库引用 |
## 入口与范围
### US-S01 启用后知道监督者观察什么
**状态:待实现。**
作为工作使用者,我希望在第一次使用监督者时明确它将观察哪些内容,避免启用应用后默认读取全部工作。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 用户第一次打开监督者 | 选择 Global 或一个、多个 Project,并选择可用来源和时间区间 | 页面在生成前显示完整范围、来源和时间区间 |
| 用户没有选择来源 | 点击生成回顾 | 不开始整理,显示选择来源的入口和原因 |
| 当前项目已打开 | 打开范围选择 | 当前项目可以作为建议值,但不能覆盖已保存范围 |
| 用户切换当前项目 | 返回监督者 | 已保存的范围不改变 |
**界面行为**
- 首次状态显示“选择关注范围”,不显示看似已有的空回顾。
- 范围摘要持续显示在页面标题或范围徽标中。
- Global、指定项目和来源选择使用持久标签;不能只用颜色表示范围。
- 生成按钮在范围未完成时禁用,并把错误关联到对应字段。
### US-S14 从应用中心进入唯一的监督者
**状态:应用入口与启停已接入,完整验证范围见[实施进度](./progress.md)。**
作为用户,我希望监督者只有一个稳定入口,不需要在智能心跳、任务中心和设置中心之间寻找重复配置。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 用户打开应用中心 | 启用应用后选择智能心跳入口 | 进入监督者统一页面,未来显示为监督者名称 |
| 用户已有智能心跳计划 | 应用入口升级 | 计划、运行记录、报告、建议和启用偏好仍可访问 |
| 用户打开任务中心或设置中心 | 查找心跳配置 | 不出现第二套计划 CRUD;需要时只提供进入监督者的链接 |
| 新安装或设置缺失应用启用值 | 启动应用 | 监督者关闭;已有显式开启或关闭选择保留 |
| 应用已启用但没有已保存且启用的计划 | 等待自动调度 | 不自动创建计划或运行回顾 |
| 已有心跳运行被领取并正在执行 | 关闭应用 | 该运行可完成并保存;不再接受新运行或追加自动监督回顾,侧栏不挂载或查询监督面板;普通任务定时调度不变 |
以上启停验收遵守[应用启停与执行](./logic-design.md#应用启停与执行)。
**界面行为**
- 应用身份、排序和常驻行为保持一致,不能生成第二个平级应用。
- 迁移期间如果仍显示“智能心跳”,页面应明确说明它是监督者的自动唤醒能力。
- 旧数据为空时进入有意义的首次配置状态,而不是报错。
### US-S21 选择一个或多个项目作为观察范围
**状态:已实现。**
作为监督者配置者,我希望心跳计划明确属于 Global 或选定项目。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 用户编辑心跳计划 | 选择 Global | 计划显示 Global,不能同时选择项目 |
| 用户选择指定项目 | 勾选一个或多个活动项目并保存 | 计划保存为一份多项目计划,不为每个项目复制 Run 或报告 |
| 用户没有勾选项目 | 尝试保存指定项目计划 | 保存按钮不可用或显示字段错误 |
| 项目已归档或删除 | 查看或保存旧计划 | 显示不可用项目;不能默默扩大或改写范围 |
**界面行为**
- 使用分段控件选择 Global/指定项目,项目使用 Checkbox。
- 计划卡片显示 Global 或完整项目摘要;项目过多时显示数量并保留可访问的完整名称。
- 当前项目切换不会重新解释计划卡片的范围。
### US-S22 配置自动监督
**状态:已实现。**
作为监督者配置者,我希望配置回顾时间、周期、回顾窗口和保留期限。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 用户打开自动监督 | 查看计划列表并点击创建或编辑 | Modal 显示名称、项目范围、每日/每周频率、指定时间、时区、回顾窗口和保留期限;页面说明选择每日或每周时间、保存并启用后自动运行 |
| 应用已启用但没有计划 | 打开自动监督或填写 Modal 字段 | 显示“尚未配置自动监督,目前仅支持手动回顾”,不自动保存、创建计划或发起运行 |
| 用户修改计划草稿 | 保存失败或关闭 Modal | 失败保留输入并可重试;Escape、取消和遮罩关闭均先确认放弃,继续编辑保留草稿 |
| 已有计划处于暂停状态 | 编辑并保存 | 更新计划字段,保持暂停;只有显式恢复才启用 |
| 两条计划名称相同 | 点击其中一条的执行记录 | 打开统一活动记录并按所选计划的精确 ID 筛选;可清除筛选,不影响自动监督页的概览和审计 |
| 用户完成配置 | 点击保存并启用计划 | 保存明确选择的频率、时间、范围和期限,参与既有调度 |
| 用户编辑已有计划 | 修改字段并保存 | 计划卡片显示新配置、下次运行和上次状态 |
| 已有计划暂停且时区与本机不同 | 编辑并保存 | 保留已暂停状态及原时区,不隐式恢复或改变执行时间 |
| 用户开始编辑 | 点击取消编辑 | 草稿丢弃,恢复编辑前的计划值 |
| 保存失败 | 提交表单 | 草稿保留,错误就地显示,用户可以修正后重试 |
**界面行为**
- 表单标签始终可见,不能用 placeholder 代替。
- 每次操作期间禁用重复提交和相互冲突的按钮。
- 周期选择为 weekly 时才显示星期字段。
- 新建使用本机时区,编辑保留原计划时区,表单与计划摘要常显时区;范围和状态不能只依赖颜色。
### US-S23 暂停、恢复或立即运行计划
**状态:已实现。**
作为用户,我希望临时暂停自动回顾,也能随时手动运行一次。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 计划正在运行 | 点击暂停 | 计划停止下一次自动运行,已有历史和结果保留 |
| 计划已暂停 | 点击恢复 | 计划恢复并显示下一次运行时间 |
| 计划可运行 | 点击立即运行 | 创建一条标记为手动触发的 Run,并在完成后刷新结果 |
| Run 正在执行 | 再次点击运行或暂停 | 禁止重复操作,按钮显示进行中状态 |
## 工作回顾和监督反馈
### US-S02 隔几天回来能继续上次工作
**状态:待实现;当前智能心跳的运行概览和最新报告是可复用基础。**
作为工作使用者,我希望打开监督者就知道上次形成了什么决定、哪些内容改变、还有什么没有解决。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 已有成功的阶段回顾 | 打开工作回顾 | 默认显示最近成功结果、覆盖范围和生成时间 |
| 回顾包含决定、变化和未解决事项 | 点击其中一项 | 展开依据,并提供“在图谱中查看”入口 |
| 没有成功回顾 | 打开工作回顾 | 显示首次生成入口,不伪装成已有总结 |
**界面行为**
- 页面顶部固定显示当前范围、来源和时间区间。
- 工作回顾是监督者的主视图之一,与故事线图谱使用 PageTabs 切换。
- 内容卡片显示“自动归纳”“待核对”或“用户确认”等状态。
- 点击图谱入口时带入当前阶段、实体或事件的定位,不打开无选择的全局图谱。
### US-S24 手动回顾当前进展
**状态:待实现;当前智能心跳已有“立即运行一次心跳”。**
作为工作使用者,我希望主动检查当前范围,而不必等待下一次计划运行。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 范围有效且有来源 | 点击“回顾当前进展” | 页面显示整理中状态、进度说明和取消操作 |
| 整理完成并有新变化 | 返回结果 | 工作回顾、监督反馈和图谱引用同一次结果 |
| 没有有效新变化 | 整理完成 | 显示检查时间和“暂无新进展”,不重复推送旧内容 |
| 用户点击取消 | 整理仍在运行 | 显示已取消,保留上一次成功结果 |
**界面行为**
- 运行按钮在整理期间禁用,取消后可以重试。
- “无新进展”与“失败”使用不同状态和文案。
- 结果必须显示本次冻结的范围,不能因用户后来切换项目而改变解释。
### US-S03 自动检查不过度打扰
**状态:待实现;当前智能心跳已实现计划运行、静默之外的私有结果保存和运行审计。**
作为用户,我希望定时检查有变化时才打扰我,没有新信息时保持安静。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 计划到期 | 智能心跳唤醒监督者 | 监督者只读取冻结范围内的有界内容 |
| 没有有效新变化 | 运行完成 | 保存检查记录,不发送重复侧栏反馈或全局通知 |
| 有新的阶段认识 | 运行完成 | 更新工作回顾和图谱,并按反馈设置显示监督反馈 |
| 运行失败 | 运行结束 | 显示失败状态和重试入口,不把失败当作成功回顾 |
**界面行为**
- 心跳负责唤醒,监督者负责判断是否需要反馈。
- 自动结果不在系统通知中暴露私人正文;用户在应用内查看详情。
- 同一结果不能同时作为重复的侧栏卡片和全局通知推送。
### US-S25 查看和处理监督反馈
**状态:Conversation/Task 过滤、固定/跟随、来源、确认发送和侧栏结果直达图谱已接入;Experiment 仍待实现。验证见[进度](./progress.md)。**
作为工作使用者,我希望在当前会话旁边看到与当前工作有关的简短反馈,并能进入完整回顾。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 监督栏处于跟随模式 | 用户切换会话或任务 | 面板只显示包含该目标来源且范围匹配的结果,同时标出结果真实范围;无结果显示空态 |
| 旧目标请求仍在读取 | 用户切换目标 | 旧摘要、来源和预览不出现在新目标下 |
| 监督栏固定到 Conversation、Task 或 Experiment | 用户切换聊天 | 面板继续显示固定目标,并显示目标名称 |
| 反馈卡片有来源 | 点击“查看依据” | 展开来源摘要和可用定位信息 |
| 用户点击“在图谱中查看” | 进入图谱 | 图谱定位到反馈对应阶段或实体 |
| 用户点击“发到当前会话” | 确认发送 | 才将反馈作为消息加入会话;默认不自动发言 |
**界面行为**
- 卡片显示目标、阶段、结论、未解决事项和来源。
- 跨会话总结明确显示覆盖范围,不能伪装成当前会话观察。
- 固定/跟随模式沿用通用助手工作栏的选择和返回行为。
## 故事线图谱
### US-S04 从时间事件查看发生了什么
**状态:待实现;当前 Demo 仅有模拟数据。**
作为工作使用者,我希望知道某个时间点发生了什么,以及它影响了哪些认识。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 图谱显示多个时间事件 | 点击外圈时间点 | 相关知识实体和实际连线高亮,其余内容降低显著度 |
| 同一天事件过密 | 点击聚合点 | 显示事件数量和事件列表,可选择具体事件 |
| 用户选择具体事件 | 查看右侧详情 | 显示事件摘要、所属故事、发生时间、影响实体和来源入口 |
| 来源可用 | 点击来源 | 打开所属会话、笔记或知识库条目的对应位置 |
**界面行为**
- 外圈时间轴从起点到终点逆时针排列,持续显示方向和当前时间范围。
- 外圈保留缺口,不能让用户误以为时间循环。
- 时间到实体的连线表示事件影响实体;它与实体之间的知识关系使用不同文字说明。
### US-S05 追溯同一个知识实体的变化
**状态:待实现。**
作为知识整理者,我希望看到一个认识如何被提出、补充、验证或修订。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 一个知识实体关联多个时间事件 | 点击内部实体 | 多个时间点和连线高亮,右侧先显示当前理解 |
| 事件类型分别为提出、补充、验证或修订 | 展开演变 | 每次变化显示类型、时间、摘要和依据 |
| 新方案只是增加选择 | 查看演变 | 旧方案不被自动标记为失效 |
| 正在回放过去时刻 | 查看实体 | 只显示截至该时刻已形成的理解,不泄漏未来结论 |
**界面行为**
- 一个实体只显示一个稳定节点,不能把每次变化复制成同名节点。
- 用户能从当前理解切换到演变列表和来源列表。
- 用户确认过的描述和关系显示确认状态,自动新增内容显示待核对状态。
### US-S06 查看知识关系为什么成立
**状态:待实现。**
作为知识整理者,我希望知道两个实体为什么被连接,而不是只看到一条线。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 图谱存在实体间关系 | 点击关系线 | 右侧显示关系类型、关联理由、确认状态和两端依据 |
| 关系由系统推测 | 查看关系详情 | 明确标记“待核对” |
| 用户确认关系 | 点击确认 | 关系进入用户确认状态,并保留确认操作记录 |
| 用户认为关系错误 | 修改类型或移除 | 图谱更新,原始来源不删除,移除可撤销 |
**界面行为**
- 关系类型必须有文字名称,颜色只能辅助区分。
- 时间到实体的关系和跨故事实体关系在详情中分别说明。
- 后续自动整理只能提出补充或修订建议,不能静默覆盖用户确认内容。
### US-S10 纠正错误关系而不删除来源
**状态:待实现。**
作为知识整理者,我希望修正系统错误合并的概念或错误关系,同时保留原始记录和撤销能力。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 自动归纳把两个概念错误连接 | 在关系详情中选择修改或移除 | 图谱立即反映修正,并显示待保存或已保存状态 |
| 用户移除一条关系 | 查看来源或知识库 | 原始来源和知识库正文仍然存在 |
| 用户完成修正 | 后续自动整理 | 系统可以提出新的证据或差异,但不能静默恢复被移除关系 |
| 用户点击撤销 | 修正仍可撤销 | 关系恢复到修正前状态,来源不发生变化 |
**界面行为**
- 破坏性关系操作使用明确确认,并说明只改变图谱组织。
- 用户修改、系统归纳和来源事实显示不同状态。
- 修正失败时保留当前详情和未提交意图,不能显示为已保存。
### US-S07 按时间段查看阶段总结
**状态:待实现。**
作为工作使用者,我希望复盘一段过程,而不是逐条翻阅原始记录。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 图谱有多个事件 | 拖选起止时间或使用时间范围控件 | 页面显示选中范围和阶段总结入口 |
| 用户打开阶段总结 | 查看结果 | 总结只覆盖该范围,分别列出决定、变化和未解决事项 |
| 用户拖动回放控制 | 回到过去时刻 | 图谱只显示当时已经发生的事件和已经形成的认识 |
| 用户切换圆形/平铺 | 保留当前范围 | 回放时刻、选择和范围继续保留 |
**界面行为**
- 平铺环绕视图是默认图谱视图;圆形视图用于观察故事聚合。
- 回放状态必须持续显示“截至某时刻”,避免误读当前结论。
- 时间轴密集时先聚合数量,展开后才能选择单个事件。
### US-S15 在列表、窄屏和键盘中完成同样操作
**状态:待实现。**
作为无法方便操作图形的用户,我希望通过文字列表和键盘完成相同的查看和追溯任务。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 用户使用窄屏 | 打开故事线图谱 | 故事列表收为选择入口,详情进入可返回的独立区域 |
| 用户不使用鼠标 | 用 Tab、方向键和 Enter 选择对象 | 能选择时间事件、实体和关系,并查看相同详情 |
| 用户缩放或平移图谱 | 返回详情或列表 | 当前选择不丢失 |
| 图形无法加载 | 使用文字列表 | 仍能浏览对象、关系、来源和回到知识库的入口 |
**界面行为**
- 焦点始终可见,弹出详情关闭后返回触发它的控件。
- 文字列表和图形共享同一选择状态,不维护两份内容。
- 缩放、平移和颜色不能成为获取信息的唯一方式。
## 来源与知识库
### US-S08 回到来源核对依据
**状态:待实现,沿用现有知识库的本地条目和外部片段边界。**
作为知识整理者,我希望从总结或图谱回到原始依据。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 引用的是本地知识库条目 | 点击来源 | 打开原条目及引用位置,并可返回图谱 |
| 引用的是会话或魔法笔记 | 点击来源 | 打开所属内容,并显示可用的消息、条目或位置 |
| 引用的是外部知识库片段 | 点击来源 | 打开已保存的有界片段和可用定位信息 |
| 原来源已失效或没有位置 | 点击来源 | 明确显示不可用或缺少定位,不伪造远端原文跳转 |
### US-S09 把新认识整理进本地知识库
**状态:待实现。**
作为知识整理者,我希望把可靠的新认识放入现有知识库,而不是建立第二份正文存储。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 候选归纳尚无对应条目 | 选择“新建条目” | 显示正文预览、来源和目标位置,确认后才保存 |
| 已有本地条目可以补充 | 选择“补充已有条目” | 显示新增内容与原内容的差异,确认后修改原条目 |
| 用户查看的是外部只读资料 | 查找写入操作 | 不显示写回或远端同步按钮 |
| 同一内容已被保存 | 再次整理 | 提示已有条目,避免重复创建 |
**界面行为**
- 图谱保存实体、关系和来源定位,不复制知识库正文。
- 保存失败保留编辑预览,不能把候选显示为已写入。
- 现有资料使用“打开知识库条目”,已有条目不显示“存入知识库”。
## 建议、任务与继续工作
### US-S16 审核心跳产生的记忆建议
**状态:已实现,沿用智能心跳页面。**
作为知识整理者,我希望逐条确认或忽略记忆候选。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 存在待确认记忆 | 打开待处理建议 | 显示类型、内容、置信度和显著性 |
| 内容较长 | 点击展开 | 显示完整内容,再次点击可以收起 |
| 用户确认 | 点击确认记忆 | 状态变为已确认,计数和指标更新 |
| 用户不接受 | 点击忽略 | 候选不再作为待处理项,不改写来源 |
### US-S17 处理后续任务建议
**状态:已实现,沿用智能心跳页面。**
作为工作使用者,我希望把回顾产生的行动建议带入对话或标记完成。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 存在未完成的后续任务 | 点击“带入对话处理” | 将任务交给当前会话处理入口,不自动执行工具 |
| 用户完成任务 | 点击标记完成 | 任务状态更新,行动转化指标刷新 |
| 用户不再需要任务 | 点击忽略建议 | 任务标记为取消或不再待处理 |
| 任务已经完成或取消 | 再次打开建议 | 不显示需要处理的操作按钮 |
### US-S11 带着上下文继续讨论
**状态:待实现。**
作为工作使用者,我希望围绕已有知识继续讨论,而不用重新交代背景。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 用户选中知识实体或阶段总结 | 点击“继续讨论” | 显示将携带的摘要、时间范围和来源列表 |
| 用户移除部分上下文并确认 | 进入会话 | 会话只携带剩余上下文,并明确来自监督者 |
| 新讨论产生内容 | 下一次整理 | 新内容可以关联到原实体或提出待核对的新关系 |
| 用户没有确认上下文 | 关闭预览 | 不创建会话消息,也不改变图谱确认状态 |
## 历史、失败与权限
### US-S18 查看报告和运行审计
**状态:已实现。**
作为监督者配置者,我希望区分报告内容和产生报告的运行记录。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 已有报告 | 打开报告与记录 | 按时间显示摘要、亮点和建议数量 |
| 用户点击展开报告 | 展开一项 | 显示完整摘要和亮点,可再次收起 |
| 用户查看运行记录 | 打开审计列表 | 显示手动/计划触发、时间、状态、尝试次数和错误 |
| 记录超过首屏数量 | 点击加载更多 | 追加历史,不替换当前已展开内容 |
**界面行为**
- 报告和 Run 审计是两个可区分的区域。
- `claimed`、完成、失败和跳过使用文字和图标共同表达。
- 失败信息直接显示在对应 Run 下,不能用空报告代替。
### US-S19 加载、刷新和首次空状态可区分
**状态:已实现;监督者页面必须沿用。**
作为用户,我希望知道页面是在加载、真的没有数据,还是读取失败。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 页面首次读取尚未完成 | 打开页面 | 显示加载状态和忙碌语义,不显示创建按钮造成误导 |
| 首次读取失败 | 页面返回错误 | 显示错误原因和重试按钮 |
| 已有数据显示但刷新失败 | 点击刷新后失败 | 保留旧数据,同时显示刷新失败提示和重试入口 |
| 读取成功但没有数据 | 完成加载 | 显示首次配置或生成回顾的空状态 |
### US-S12 新一次失败不覆盖旧结果
**状态:待实现;当前智能心跳已有失败 Run 保留历史结果的基础行为。**
作为工作使用者,我希望一次模型、来源或网络失败不会抹掉上一次可用回顾。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 已有成功回顾 | 新一次整理失败 | 旧回顾继续可读,新运行显示失败原因和覆盖范围 |
| 新一次整理被取消 | 用户查看工作回顾 | 显示已取消,不把部分内容标成完整回顾 |
| 只有部分来源可用 | 整理完成 | 显示已覆盖和遗漏范围,结果标记为部分完成 |
| 用户点击重试 | 重新运行 | 创建新的运行记录,不把旧失败记录改写为成功 |
### US-S13 历史回放不冒充当前来源
**状态:待实现。**
作为知识整理者,我希望回看过去时看到当时的认识,知道原始来源是否已经变化。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 来源后来被修改 | 回放旧阶段 | 仍显示当时保存的归纳和可用历史依据 |
| 旧版原文无法恢复 | 点击来源 | 明确提示历史版本不可用,并显示当前可用信息的时间,不称其为旧原文 |
| 当前结论与过去不同 | 回放过去 | 不把当前结论注入过去的图谱或阶段总结 |
### US-S20 遵守范围和访问边界
**状态:已实现于智能心跳输入与工具禁用边界;监督者目标行为待实现。**
作为受限访问者,我希望监督者只处理授权范围内的内容。
**Given / When / Then**
| Given | When | Then |
| --- | --- | --- |
| 计划是 Global | 运行回顾 | 只读取允许访问的有界会话、任务和 Global 已确认记忆 |
| 计划绑定项目 | 运行回顾 | 只读取选中项目内容及允许共享的 Global 记忆 |
| 模型尝试调用工具 | 整理期间 | 调用被拒绝,运行失败原因可审计 |
| 模型输出项目外的记忆或任务 | 校验输出 | 结果被拒绝,不写入越权内容 |
**界面行为**
- 页面显示本次运行的冻结范围。
- 权限不足、来源不可用和没有新内容使用不同状态。
- 监督者的 Agent 身份不自动获得工具执行、批准或切换 Execute 的能力。
## 活动记录
### US-S26 查看实际执行活动
对应 FR-S10,当前已接入。
| Given | When | Then |
| --- | --- | --- |
| 用户手动开始监督回顾 | 打开活动 | 显示冻结范围、开始时间和运行中状态;失败保留错误,成功提供结果入口 |
| 定时或立即运行心跳已保存报告 | 下游监督仍在运行或失败 | 一条活动分别显示阶段状态,整体不能显示完成 |
| 活动包含较早的成功结果 | 点击查看回顾或在图谱中查看 | 打开该 resultId,超过最近结果列表仍正确定位 |
| 活动读取失败 | 点击重试 | 保留可用记录并重新读取,不调用模型 |
| 活动有在途请求 | 离开页签、页面或关闭应用 | 停止后续刷新,迟到响应不更新界面;关闭后不查询活动 |
| 旧记录缺少范围或运行关联 | 查看活动 | 明确显示缺失,不根据当前计划或相近时间推断 |
| 六个来源批次已保存,剩余来源为零 | 导航合并失败后打开活动 | 已记录的合并阶段显示失败,发布保持未完成;继续复用成功叶子和导航节点 |
| 已保存批次跨多个项目和会话 | 展开事实并翻页 | 按本页真实归属分层,标明本页数量;原文可读,失败叶子不伪造为已保存 |
| 原始错误是多行校验 JSON | 展开错误详情 | 默认折叠,展开后有界滚动,完整文本可选择复制;恢复操作和阶段仍在其前面 |
取消和人工修改时间序列审计不属于当前已实现范围;实际状态及保留规则见[活动记录](./logic-design.md#活动记录)。
### US-S27 自动增量与历史重看
对应 FR-S4、FR-S10。
| Given | When | Then |
| --- | --- | --- |
| 自动报告和监督已成功处理窗口内来源 | 同范围再次自动检查且来源未修改 | 两阶段均不调用模型,活动显示无变化,不新增报告和图谱事件 |
| 消息正文、知识引用或任务状态有修改 | 再次自动检查 | 只处理修改版本及未读片段,结合已有摘要和实体说明,允许复用已有实体 ID |
| 来源多于单次预算或正文较长 | 一次结果成功保存后再次检查 | 继续处理窗口内遗漏或剩余片段,已送入且保存成功的部分不重复处理 |
| 模型、校验或保存失败 | 再次检查相同输入 | 失败阶段仍可重试;已成功的另一个阶段不重复付费 |
| 来源已经自动处理 | 用户手动回顾所选区间 | 重新整理有界历史,保留自动进度;删除报告不等同重看或重建 |
| 只有已确认记忆或背景摘要变化 | 自动检查 | 背景不单独触发模型调用 |
## 验收优先级与依赖
首个可用版本应先打通以下路径:
> 应用入口 → 选择范围 → 手动回顾 → 工作回顾 → 选择知识实体 → 查看多个时间事件 → 打开来源 → 预览并继续讨论
建议实施顺序:
1. 顶层有工作回顾、故事线图谱、自动监督、活动记录、设置五项。自动监督保留计划 CRUD 与范围校验,创建和编辑使用同一 Modal 表单;运行概览、指标、趋势、自动监督报告、建议和心跳审计与计划列表同页,无计划时只有一处未配置说明和一个创建按钮。工作回顾只阅读统一监督结果,新请求的范围和运行状态不改变旧结果的日期、范围及正文;自动报告为空时不会在成功结果下显示“没有报告”。历史选择位于正文前,图谱打开所选结果的精确 ID。活动记录只保留统一执行、进度和详情。设置只显示算法参数,没有计划、报告、审计、第二套页签、整页范围徽标或独立刷新行。
2. 增加监督者统一的工作回顾和共享结果模型。
3. 接入最小故事线图谱:时间事件、知识实体、实体变化、来源和时间回放。
4. 接入文字列表、窄屏和键盘操作,以及失败、取消和部分来源状态。
5. 接入来源双向定位、继续讨论预览和本地知识库新建/补充。
6. 侧栏监督反馈和自动增量已接入;事件驱动及语义层面的主动反馈判定仍待实现。
详细状态规则见[逻辑设计](./logic-design.md),页面结构和共享组件见[UI 设计](./ui-design.md),现有智能心跳的已实现范围见[智能心跳 PRD](../smart-heartbeat/prd.md)。
+6
View File
@@ -8,5 +8,11 @@
| 文档 | 权威职责 |
| --- | --- |
| [技术设计](./technical-design.md) | Runtime 产品与技术方案、协议、插件、打包、测试和验收 |
| [实现进度](./progress.md) | Windows 工作区路径修复及验证记录 |
现有文档同时包含部分 PRD、功能逻辑和进度内容,后续修改该 Runtime 时再按标准职责拆分。
跨项目共享 Host 和状态检查不冷启动的实现设计,由
[Runtime 进程复用技术设计](../assistant-workbar/runtime-process-reuse-technical-design.md)
统一维护;本 Runtime 的生命周期见[技术设计 §9](./technical-design.md#9-runtime-生命周期),
未发布实现的验收状态见[工作栏进度](../assistant-workbar/progress.md#2026-09-14-runtime-进程复用实施中)。
@@ -0,0 +1,45 @@
# DeepSeek Harness 实现进度
## 2026-09-14 Windows 工作区路径修复
本机 Runtime 的 Host 启动与 `newSession.cwd` 已共用初始化时解析的 `realpath`,
规则见[技术设计](./technical-design.md#52-受控组合不启动用户-profile)。修复此前
`C:/...\workspace` 写法触发 ACP `-32602`,以及随后换成同目录规范写法仍复用
错误 Runtime 的问题。Host 严格比较与 execution-space 缓存规则保持原样。
新增 Windows 真目录回归,连接真实 ACP、Host 和控制面,分别用混合分隔符及规范
路径创建会话,断言共用同一 Runtime、只启动一次 Host、两次均完成。该自动化回归
使用内存模型;外部真实模型验证单独由完整 App 完成。
Windows 完整 App 使用当前源码构建的 Main、Preload、Renderer 和新建隔离配置。
正式配置只读,模型凭据由隔离配置中的 safeStorage 解密;真实 composer 输入请求,
由 `deepseek-v4-flash` 在 Ask 模式读取 `summary-probe.txt`,校验码未放入 prompt。
| 目录写法 | Composer 提交 | 真实模型 HTTP dispatch | 文件读取 | 结果 |
| --- | ---: | ---: | ---: | --- |
| Windows 混合分隔符 | 1 | 2 | 1 | 仅返回文件中校验码,任务完成 |
| 同目录规范路径 | 1 | 2 | 1 | 仅返回文件中校验码,任务完成 |
| 合计 | 2 | 4 | 2 | 两条会话均通过 |
两轮 dispatch 来自同一 DS Utility Host 进程,切换目录写法未新增 Host;ACP 会话
ID 不同。两条工具卡片均显示路径摘要,原生点击展开后可查看输入与输出;重载
Renderer 并重新选择项目后,已保存会话的摘要、详情和完成状态仍通过检查。四张
局部截图已逐张打开审阅。驱动曾返回不可克隆的监听器函数,以及在重载后默认项目
等待工具卡片超时;修正定位后通过,这两次驱动错误没有增加模型请求。
本机临时 `ds-windows-path-live/report.json`、`dispatch.jsonl` 与 `source.json`
记录结果、逐请求计数和构建哈希,原 `runtime-summary-live` 报告未覆盖。
本次 App 正常退出,退出前记录的 5 个所属进程均已停止;隔离 profile、数据库、
工作区、临时构建和私有日志已清理。正式模型配置与启动前逐字节一致,报告和截图
保留在本机临时目录,不作为长期可用的仓库资源。
测试沿用生产 HTTPS 与证书处理,未单独验证严格证书校验或 Main 重启。
专项 7 个测试文件通过,91 项通过、3 项跳过。`npm run typecheck` 与
`npm run lint` 通过。最初两个假时钟测试因异步目录解析尚未结束就推进启动时钟而
超时;调整为等待 launcher 启动后,DS Runtime 的 34 项测试全部通过。
最终全量 `vitest run`(`npm test` 的脚本入口)为 353 个文件通过、9 个跳过,
4155 项通过、66 项跳过、0 失败,耗时 602.00 秒。`git diff --check` 通过。
远端核查:Main 的 SSH 分支创建托管远程 Runtime,最终使用 `AcpRemoteRuntime`;
`create-runtime.ts` 的本机 DS 入口拒绝 SSH execution space。本次未改远端代码,
未调用远端模型,也未进行 Linux Host 实测。
@@ -121,6 +121,11 @@ GoodBuddy 使用自己固定的 Harness Host 入口和只读组合模板,不
模型名称、服务地址、工作区、Skills、MCP schema 和已启用插件的规范化入口通过严格校验的 Main 配置传给 Host。API Key 只通过受控凭据通道按需提供,不写入 YAML、命令行、Renderer 或日志。插件配置只来自 GoodBuddy 受管状态,不合并用户 profile 或全局补丁。
Main 在创建 Session 前解析该请求工作区的 `realpath`,把绝对路径传给 ACP
`newSession.cwd`。Host 不再要求 Session cwd 等于启动 cwd,原生 Agent header.cwd
驱动文件与 Shell 工具;启动 cwd 仅为默认值。远端 SSH 由 `ManagedRemoteAcpRuntime` /
`AcpRemoteRuntime` 单独处理,不经过本机 DS Runtime 的目录解析。
### 5.3 双层内部控制面
Harness 子进程内控制面不能取代 Main 控制面,Main 控制面也不能代替进程内的 Session/Tool 适配层:
@@ -307,6 +312,7 @@ Harness Control Plane 只发送 GoodBuddy 能稳定解释的字段:
- `text`:已提交的助手文本分片。
- `reasoning`:可选的有界推理摘要分片。
- `tool`:工具 ID、名称、状态和有界输入/输出摘要。
- 操作标题与增量完成事件的参数保留遵循[工具操作摘要规则](../assistant-workbar/runtime-interactions.md#工具操作摘要),本机适配器按调用 ID 合并,详情上限保持不变。
- `model-usage`:模型、提供方、输入、输出和缓存 Token。
- `done`:停止原因和 Session ID。
@@ -316,18 +322,18 @@ Harness Control Plane 只发送 GoodBuddy 能稳定解释的字段:
### 9.1 进程模型
- 每个活动的 DeepSeek Harness Runtime 实例拥有一个 `utilityProcess`。
- 一个进程可以承载多个 Harness Session。
- Main `LocalRuntimeRegistry` 按兼容模型及 Host 配置共享重型实例;
每组拥有一个 `utilityProcess`,不同工作区使用轻量适配器及独立 Session cwd。
- 同一 GoodBuddy 对话的 Prompt 串行执行。
- 不同对话可以并行,但受全局并发上限控制。
- Runtime 设置变化时创建新实例,旧实例等待在途请求结束或在宽限期后被取消。
- Runtime 设置变化时旧配置停止接收新请求,在途请求结算后释放旧实例。
### 9.2 会话映射
Main 保存内存映射:
```text
GoodBuddy conversationId -> Harness sessionId + process generation
GoodBuddy conversationId -> Harness sessionId + workspace + process generation
```
- 首次请求创建 Session。
@@ -339,7 +345,8 @@ GoodBuddy conversationId -> Harness sessionId + process generation
### 9.3 取消与有界控制操作
- 用户取消时立即发送 `session/cancel`。
- 取消等待有界,超时后关闭连接并终止整个 Harness 进程。
- 取消等待有界,超时先释放目标 Session 的 Agent、工具和 Main 代理资源;
只有该控制操作也不响应时才终止整个 Harness 进程。
- 初始化、握手、请求准备、权限回传和关闭分别使用独立控制超时。
- 生产 Prompt 不设置固定墙钟总时限,由 Harness 自身协议终态或用户取消结束。测试可以
显式注入短 Prompt 时限验证取消传播,超时错误与用户取消仍不能被宽泛 catch 抹平。
@@ -347,7 +354,10 @@ GoodBuddy conversationId -> Harness sessionId + process generation
### 9.4 释放与退出
- 原生能力清单由 `SelectedRuntimeManager` 缓存的执行 Runtime 提供,与随后的对话请求共用同一实例,不再为清单单独启动并销毁一次 Host;该实例遵循执行缓存的空闲淘汰、设置变更 reset 和退出 dispose 规则。
- 原生清单与执行使用共享 owner,查询携带工作区。轻量适配器被缓存淘汰不结束原生
Session;无会话、无请求的 owner 空闲 60 秒退出,再次使用按需启动。
- `getStatus()` 只读取已有状态和配置,不启动 Host,也不重置空闲回收计时;显式 `testConnection()` 才启动
并验证连接,临时检查适配器的 dispose 不终止其他会话。
- 删除或释放对话时调用 `goodbuddy/session/release`。
- Runtime dispose 时先拒绝新请求,再取消所有 Session。
- Harness Control Plane 完成 Agent、工具和会话清理,Host 完成 Cordis Fiber 与子进程的反向清理。
@@ -356,6 +366,17 @@ GoodBuddy conversationId -> Harness sessionId + process generation
- 应用退出时中止正在运行的 npm 插件安装并终止其完整进程树,不能让 lifecycle script 在 GoodBuddy 退出后继续运行。
- 应用退出不得因 Harness 清理无限阻塞。
### 9.5 跨项目复用
锁定 0.1.2-rc.1 的文件和 Bash/PowerShell 工具已按原生 Session header.cwd 路由,
Provider 的根 cwd 只是默认值。不为每工作区复制本地 Provider;
Main 的 Web/MCP ToolProvider 按工作区保存,最后一个对应 Session 释放时 dispose。
共享 Utility、逐 Session cwd、被动状态、Main 工具代理和取消升级统一由
[Runtime 进程复用技术设计](../assistant-workbar/runtime-process-reuse-technical-design.md#5-deepseek-harness)
定义,证据及剩余验收见[工作栏进度](../assistant-workbar/progress.md#2026-09-14-runtime-进程复用实施中)。
本节描述当前未发布源码,不将新行为归入已发布的 Desktop 0.13.2。
## 10. 权限与主机执行
### 10.1 模式映射
+3 -3
View File
@@ -22,9 +22,9 @@
| --- | --- |
| 直连模型 | Runtime provider 为 `model`、由 GoodBuddy 直接调用模型协议的文本模型 |
| 进程执行 | GoodBuddy 在当前执行空间启动一个前台 Shell 命令并返回有界结果 |
| 工作区搜索 | 通过安装包内置 ripgrep 搜索内容或列出文件,并返回紧凑结果 |
| 工作区搜索 | 通过安装包内置 ripgrep 搜索内容或列出文件,接受原生参数并返回原生输出及退出码 |
| 工作区补丁 | 通过 `*** Begin Patch` 格式新增、修改或删除工作区文本文件 |
| 输出续读 | 通过 `output_read` 分页读取当前会话保留的进程或 Subagent 输出 |
| 输出续读 | 通过 `output_read` 分页读取当前会话保留的搜索、进程或 Subagent 输出 |
| 平台 Shell | Windows 上的 PowerShell,macOS/Linux 上的 Bash 或 POSIX Sh |
| 编程 Subagent | 由直连模型临时委派、使用直连模型 Runtime 执行一个有界子任务的执行者 |
| 父请求 | 发起进程调用或 Subagent 委派的当前直连模型请求 |
@@ -43,7 +43,7 @@ Task、Job、Subjob、Run 和 Subagent 的对象关系以
- OpenCode、Continue 和 DeepSeek Harness 继续使用各自原生执行与委派能力。
- Ask 可搜索和分页读取工作区,但不允许补丁写入或进程执行;Subagent 若在 Ask 中使用,
只能继承 Ask 的只读能力。
- 进程和 Subagent 的长输出返回预览与续读位置,完整内容保留到所属会话或 Runtime 释放。
- 搜索、进程和 Subagent 的长输出返回预览与续读位置,完整内容保留到所属会话或 Runtime 释放。
Ask 和 Execute 均可续读当前会话已取得的输出,详见 [输出契约](./technical-design.md#43-输出边界)。
- Execute 表示用户授权当前执行空间账号的完整能力,不增加第二套工具审批或权限档位。
- 首版跨平台指桌面本机 Windows、macOS 和 Linux。托管 SSH 项目当前继续使用远端
@@ -26,8 +26,8 @@
3 次;用户取消和已显示的部分输出禁止重放。
12. Ask 可调用 `workspace_rg` 和 `workspace_read_text`,但不能调用
`workspace_apply_patch`;Execute 可调用三者。
13. 工作区搜索固定使用随包 ripgrep 的结构化输出,不把任意 ripgrep 参数转成命令执行。
14. FR-7 / US-A6:预览之外的进程和 Subagent 输出保留在所属会话下,分页续读不启动新命令。
13. 搜索直接使用随包 rg 的原生参数和输出;Ask 的参数检查保持工作区内只读,Execute 使用账号权限。
14. FR-7 / US-A6:预览之外的搜索、进程和 Subagent 输出保留在所属会话下,分页续读不启动新命令。
会话释放先停止活动工作,再删除输出;子会话结束不删除父会话拥有的 Subagent 结果。
## 2. 状态维度
@@ -58,7 +58,7 @@
FR-4 / FR-12 / US-B0:Ask 工作区搜索和读取不依赖知识库、联网搜索或委派开关,不需要
切到 Execute 或额外批准。MCP 服务器失败也不改变本机只读工具的可用性。
`output_read` 在直连模型拥有进程或 Subagent 服务时提供,Ask/Execute 均可用,不取决于
`output_read` 在直连模型拥有搜索、进程或 Subagent 服务时提供,Ask/Execute 均可用,不取决于
本轮 Shell 可用性、是否允许再次委派或委派深度。读取只接受当前会话的句柄;其他 Runtime
不注册该工具,调用边界同样拒绝。
+5 -4
View File
@@ -193,16 +193,17 @@ Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完
### FR-12 高效工作区工具
- `workspace_rg` 同时支持内容搜索和文件列表,遵循项目忽略规则,并以
`path:line:column:text` 紧凑格式返回匹配。
- `workspace_rg` 接受原生 rg 参数和可选工作目录,支持内容搜索、上下文、文件列表及
自选输出格式,保留原生输出与退出码。默认遵循项目忽略规则。
- `workspace_read_text` 接受起始行和行数,大文件通过续读完成,不因超过旧的整文件上限而
完全不可读;超长单行可使用返回的字节位置继续读取。
- `workspace_apply_patch` 支持一个补丁新增、修改和删除多个 UTF-8 文件;执行前验证全部
路径和 hunk,执行失败时准确报告已完成文件。
- 搜索和读取在 Ask/Execute 均可用;补丁只在 Execute 可用,Main 调用边界再次检查模式。
- `workspace_rg` 使用固定参数调用随包 ripgrep,不向模型开放 `--pre` 或任意命令参数。
- `workspace_rg` 直接调用随包 ripgrep;Ask 限于工作区内只读搜索,Execute 使用当前账号权限。
具体参数与模式边界见[工具契约](./technical-design.md#工具输入与执行)。
- 工作区工具不限制文件总大小、补丁总大小、搜索表达式长度、glob 数量或模型请求的结果
数量。单次返回按上下文容量截断或分页;读取结果提供续读位置,搜索结果提示模型缩小查询。
数量。单次返回按上下文容量截断或分页;读取结果提供续读位置,搜索完整输出通过 `output_read` 续读。
## 6. 产品验收
@@ -18,6 +18,31 @@ DeepSeek Harness 和托管 SSH 路径不注入这些工具。既有功能的 Win
全量项目验证已通过;本次分页验证记录见下方 2026-09-10 条目。macOS 与 Linux 真机验证
仍需由对应平台完成。
## 2026-09-25 原生 ripgrep 参数与完整输出
- FR-12:`workspace_rg` 使用 `{ args: string[], cwd?: string }`,直接启动随包 rg,
保留原生 stdout、stderr 和退出码。此实现替代下方历史记录中的 JSON 解析与列号转换。
- 搜索复用进程服务的取消、进程树清理和会话输出存储;超过旧 4 MiB 上限的结果及超长行
可通过 `output_read` 完整续读。Ask 保留工作区只读边界,Execute 使用当前账号权限。
- Windows 真实随包 rg、生产 Provider 与 Runtime 接线聚焦回归:6 个文件,
`163 passed, 1 skipped`;覆盖原生参数、退出码 1/2、Ask 部分结果、长输出续读、
会话归属与清理。`npm run typecheck`、`npm run lint` 通过。
- 全量 `npm test`:`5091 passed, 72 skipped, 25 failed`,另有一个文件未发现测试套件,
合计 7 个测试文件失败。失败涉及监督视图与旧 schema 夹具、监督设置 UI 断言和
`scripts/review-algorithm.test.mjs`;本次未修改这些路径。全量开始后的最终搜索调整
已由上述 6 文件聚焦回归复验,不能把本次整库验证记为通过。
- 补充真实 DeepSeek 模型验收:Ask、Execute 两个场景均通过。模型通过原生参数调用
随包 rg,再通过 `output_read` 找回 110 KiB 超长单行末尾的随机标记;断言工具完成事件
进入 Runtime 活动。成功运行精确为 6 次上游请求(每个模式 3 次)、2 次搜索和 2 次续读。
首次测试因夹具未配置 rg 路径失败,控制台未保留请求计数,因此不推算整轮调用总数。
此验证覆盖真实 Runtime、Provider 和 rg,不作为完整桌面 UI 或 macOS/Linux 真机验证。
- 最终聚焦回归:6 个文件,`169 passed, 1 skipped`;Typecheck、Lint 通过。
最新全量运行在 600 秒执行窗口后超时,日志停留在附件布局 Electron 测试,未生成最终
汇总,因此整库验证仍不能记为通过。
- 已检查 `src/agent-daemon`、`src/main/remote-agent` 和远端 Runtime 打包入口:
它们不导入本机 rg 执行器、ModelToolProvider 或本机进程服务;远端工作区不提供该搜索
工具。因此本次不改变 gbagent 生产路径,不需要远端修复或 Host 验证。
## 2026-09-10 提交审查后的桌面 UI 验收
- Windows 隔离配置使用本轮最终开发构建,通过普通输入框发送触发完整
@@ -29,6 +29,11 @@
## 2. 总体架构
直连模型在 Main 内执行 HTTP,本身没有独立模型进程。跨 Runtime 资源调研中的直连
部分仅检查历史缓存、会话引用和工具资源释放,不为它新增进程池;分析与验证范围
见 [Runtime 资源技术设计 §7.2](../assistant-workbar/runtime-process-reuse-technical-design.md#72-直连模型及历史详情)。
以下当前功能不因此改变,进程工具产生的子进程仍按各自会话生命周期管理。
```text
ModelAgentRuntime
└─ ModelToolProvider
@@ -74,8 +79,10 @@ type BuiltinModelToolSummary = {
### 工具输入与执行
- `workspace_rg` 接受 `pattern`、相对目录、glob、固定字符串、忽略大小写、文件列表模式和
最大结果数。Main 固定追加 `--no-config`,内容搜索读取 `--json`,对模型返回紧凑文本。
- `workspace_rg` 接受 `{ args: string[], cwd?: string }`,直接启动随包 rg,不经过 Shell。
默认参数为 `--no-config --color=never --line-number`,随后传入原生 args;cwd 默认工作区。
保留原生 stdout、stderr 和退出码,不强制 JSON、不裁剪匹配行。完整输出复用进程服务的
临时文件与 `output_read`;搜索不另设时限,取消和会话释放终止进程树。
- `workspace_read_text` 接受 `path`、从 1 开始的 `offset` 和 `limit`,通过现有
`WorkspaceAccess.readText` 字节分页组装带行号结果;`offsetBytes` 提供原始字节续读,
覆盖跨多个页面的超长单行。
@@ -84,7 +91,7 @@ type BuiltinModelToolSummary = {
- Ask 只注册前两个只读工具;Execute 注册全部三个工具。旧的目录列表和整文件覆盖不再进入
新工具清单。
- 输入和目标文件不设固定总量上限。单次工具结果仍遵守直连模型上下文边界;读取返回续读
位置,搜索截断时要求模型缩小路径、glob 或表达式。
位置,搜索预览省略的内容通过 `output_read` 续读。
### 读取与搜索失败恢复
@@ -97,19 +104,16 @@ Runtime 向模型返回 `{ ok: false, recoverable: true, error, nextAction }`
提示不得原样重复失败参数。取消、远端断线、失效工作区、缺失 rg 可执行文件及未分类的内部
错误继续向外传播,不转为可恢复结果。
读取和搜索仍只接受工作区内相对路径,绝对路径即使指向工作区内文件也拒绝;原有越界及
符号链接检查继续生效。仅当本机直连模型 Execute 的 `process_execute` 实际可用时,恢复
提示才建议用它访问工作区外文件。此处理不改变 Windows 用户权限、UAC 或文件 ACL,
也不提升进程权限;Ask 仍保持只读。
`workspace_read_text` 仍只接受工作区内相对路径。搜索在 Ask 中检查 cwd、搜索路径、模式文件
和忽略文件位于工作区内,并阻止 `--pre`、`--hostname-bin`、`--follow/-L`、`--search-zip/-z`,
避免只读调用启动外部命令或跟随链接越界。Execute 原样接受 rg 参数及账号可访问的路径,
不增加逐工具授权。两种模式均禁用外部 rg 配置,不提升系统权限。
ripgrep 结果按退出状态处理:
- exit 1 保持正常无匹配结果。
- exit 2 有 stdout 且不是 regex/glob 编译错误时,保留已取得的匹配或文件列表,附加
`incomplete search coverage` 警告及有界 stderr。零匹配只代表已搜索部分,不能据此断言
整个目标不存在匹配;输出截断提示仍独立保留。
- exit 2 无 stdout,或出现无效 regex/glob 编译错误时,搜索层抛错,再由 Provider 转为
上述可恢复结果;模型可修正表达式,字面搜索可使用 `fixedStrings`。
- exit 2 返回原生 stderr 和已有 stdout,表示错误或搜索覆盖不完整;模型可修改参数继续。
无效正则同样返回 exit 2;字面搜索使用 `-F`。
该契约属于桌面 `ModelAgentRuntime → ModelToolProvider` 内置工具路径,复用浏览器标签页
关闭和元素引用失效已有的可恢复错误机制;浏览器分别提示 `browser_navigate` 或
@@ -198,7 +202,7 @@ type ProcessExecuteResult = {
页面在字符边界结束;若请求大小容不下首个完整字符,最多扩展到 4 字节,保证续读前进。
位于 UTF-8 延续字节的输入 cursor、负数、越界和未知字段均拒绝。
Provider 根据 `process:` 或 `subagent:` 前缀选择服务,以当前 `conversationId` 校验所有者,
Provider 根据 `rg:`、`process:` 或 `subagent:` 前缀选择服务,以当前 `conversationId` 校验所有者,
不接受模型另传 owner。Ask/Execute 均可用,工具总数为此预留一个槽位;不要求本轮能够启动
命令或再次委派。分页结果直接 JSON 序列化,不进行二次文本裁剪,以免 cursor 与内容不一致。
32 KiB 的页面即使全部需要 JSON 转义,也能保留在现有工具结果容量内。
@@ -53,6 +53,8 @@ Job、Subjob 和 Run 继续是内部对象,不创建树形 UI、独立路由
### 3.1 标题与摘要
正常执行状态的操作摘要遵循[工具操作摘要规则](../assistant-workbar/runtime-interactions.md#工具操作摘要),从原始调用参数提取并在完成后保留;异常状态继续显示原有提示。
```text
进程执行
PowerShell · packages/app · npm test
+36 -3
View File
@@ -1,14 +1,47 @@
# 文档处理
文档处理负责聊天附件和知识库导入中的文本提取、格式转换与本地 OCR。
文档处理负责聊天附件和知识库导入中的文本提取、本地 OCR、HTTP PaddleOCR-VL、原件与
解析产物保存,以及正文、图片、详情预览。格式转换仍为规划能力。
2026-09-19 已接入生产链路;验证层次、样例和未覆盖项见[实施进度](./progress.md#2026-09-19-实现与验证)。
## 文档导航
| 文档 | 权威职责 |
| --- | --- |
| [文档解析与本地 OCR PRD](./prd.md) | 产品范围、解析策略、OCR 模型管理和验收 |
| [文档解析与本地 OCR PRD](./prd.md) | 产品范围、解析策略、OCR 模型管理,以及规划中的解析资源持久化、配置预览行为和验收 |
| [聊天附件技术说明](./chat-attachments-technical-design.md) | 文件选择与粘贴的进程链路、IPC、PPTX 图片 OCR、Worker 生命周期和验证证据 |
| [HTTP OCR User Stories](./user-stories.md) | HTTP 接入、纯图片路由与资源保存的用户验收 |
| [HTTP OCR 逻辑设计](./logic-design.md) | 输入路由、来源选择、页码、资源归属与失败规则 |
| [文档解析与附件 UI 设计](./ui-design.md) | 设置、共享预览、聊天与知识库选图、附件状态、响应式、无障碍及完整验收矩阵 |
| [HTTP PaddleOCR-VL 技术设计](./http-paddleocr-vl-technical-design.md) | 计划接入点、选项契约、资产归属和实现顺序 |
| [实施进度与验证](./progress.md) | 实测命令、调用计数、部署差异和未验证能力 |
| [生产实现说明](./implementation.md) | 当前存储、IPC、迁移、生命周期、Runtime 校验与设计差异 |
| [改动文件清单](./changed-paths.md) | 本轮实施涉及的完整文件路径,区分并行工作区改动 |
当前尚无独立 User Stories、功能逻辑、UI、技术设计和进度文档。
User Stories、逻辑设计和进度文档覆盖 HTTP OCR 扩展、纯图片路由及资源持久化,本地 OCR
既有实现仍由 PRD 及聊天附件技术说明维护。界面行为由 UI 设计维护并遵循根目录
[UI 设计系统](../../../UI-DESIGN.md)。完整目标包含文档选图加入会话并发送,不以实施顺序
缩减产品范围。具体生产实现和验证证据分别由实施说明与进度文档维护。
全局配置、本机推理监控、聊天附件和知识文档的入口分工,以及草稿、历史与队列的菜单规则,
统一见 [入口与职责](./ui-design.md#11-入口与职责)和[附件操作菜单](./ui-design.md#12-附件操作菜单)。
首次菜单核对保留在[历史记录](./progress.md#菜单核对),当前接线见生产实现说明。
本文档中的“内联图片”指服务在 JSON 中返回的 Base64 图片字节;“图片键”指
Markdown 引用及 `markdown.images` 中的页内键,不是可直接写入磁盘的路径;“重组”指
对已解析页面调用 `/restructure-pages`,不等于已经验证跨页表格合并质量。
“会话资源”是应用 userData 下的文件资产及数据库归属引用,不是 Runtime 项目文件;
服务返回“内联图片”也不表示产品将图片字节长期存入数据库。
“待添加选择”仅指预览内尚未提交的勾选;“已在草稿”指目标会话实际持有的附件引用。
“文档插图附件”是显式复制到会话资源目录的独立图片,保留来源说明,不依赖知识库原结果
继续存在。“提取文字”是图片附件的一种发送方式,原图仍保留,不表示原图同时发给模型。
“纯图片附件”指直接添加到聊天的图片,与 PDF、Office 解析提取的“文档插图”分开。
普通图片默认直接作为模型图片输入,明确提取文字才调用配置的 OCR;不支持图片输入时的
提示和选择、原图长期保存见 [图片路由](./prd.md#322-纯图片与文档的路由规划);验证范围见进度。
约定的 userData 目录、既有 knowledge 原件与规划派生资源的区别统一见
[结果与资源](./http-paddleocr-vl-technical-design.md#结果与资源)。
Office 文件的浏览器内人工编辑、AI 选区修改、保存和撤销不属于文档解析职责,由
[Office 协同编辑](../office-document-editing/README.md)负责。文档处理仍负责内容提取、OCR、
@@ -0,0 +1,101 @@
# 本轮文档处理改动文件
本清单只列文档处理实施涉及的文件。工作区内并行的魔法笔记、画布及其测试改动不计入本轮;
共享文件中的并行改动保持原样,未提交或推送。
## Main
```text
src/main/assistant/assistant-database.ts
src/main/context-manager.ts
src/main/context-manager.test.ts
src/main/conversation-attachment-storage.ts
src/main/conversation-attachment-storage.test.ts
src/main/document-extracted-text.ts
src/main/document-extracted-text.test.ts
src/main/document-parsing-service.ts
src/main/document-parsing-service.test.ts
src/main/document-parsing-settings-store.ts
src/main/document-parsing-settings-store.test.ts
src/main/document-result-storage.ts
src/main/http-document-ocr.ts
src/main/http-document-ocr.test.ts
src/main/http-document-ocr.integration.test.ts
src/main/render-ocr-pdf.ts
src/main/index.ts
src/main/ipc.ts
src/main/ipc.test.ts
src/main/knowledge/document-parser.ts
src/main/knowledge/knowledge-database.ts
src/main/knowledge/knowledge-database.test.ts
src/main/knowledge/knowledge-service.ts
src/main/knowledge/external/external-knowledge-service.test.ts
```
## Shared 与 Preload
```text
src/shared/attachment-limits.ts
src/shared/assistant-contracts.ts
src/shared/contracts.ts
src/shared/document-parsing-contracts.ts
src/shared/document-result-contracts.ts
src/shared/ipc-channels.ts
src/shared/local-inference-contracts.ts
src/preload/index.ts
src/preload/preload-sandbox.test.ts
```
## Renderer
```text
src/renderer/src/App.tsx
src/renderer/src/App.test.tsx
src/renderer/src/AttachmentActions.tsx
src/renderer/src/AttachmentActions.test.tsx
src/renderer/src/AttachmentCapabilityNotice.tsx
src/renderer/src/AttachmentResultButton.tsx
src/renderer/src/ChatTimeline.tsx
src/renderer/src/ConversationInputQueue.tsx
src/renderer/src/DocumentConversationContext.ts
src/renderer/src/DocumentParsingSettingsSection.tsx
src/renderer/src/DocumentParsingSettingsSection.test.tsx
src/renderer/src/DocumentResultPreview.tsx
src/renderer/src/DocumentResultPreview.test.tsx
src/renderer/src/HttpOcrSettings.tsx
src/renderer/src/KnowledgeWorkspace.tsx
src/renderer/src/LocalInferencePage.tsx
src/renderer/src/PendingDocumentImports.tsx
src/renderer/src/QueuedAttachmentsDialog.tsx
src/renderer/src/SettingsPanel.tsx
src/renderer/src/styles.css
src/renderer/src/i18n/locales/en-US/app.ts
src/renderer/src/i18n/locales/zh-CN/app.ts
```
## 脚本与文档
```text
tests/attachment-layout.electron.test.ts
tests/support/attachment-layout-regression.tsx
scripts/document-parsing-production-probe.ts
scripts/document-table-fixture.mjs
FEATURES.md
FEATURES.zh-CN.md
UI-DESIGN.md
docs/features/application-tool-navigation/ui-design.md
docs/features/document-processing/README.md
docs/features/document-processing/prd.md
docs/features/document-processing/user-stories.md
docs/features/document-processing/logic-design.md
docs/features/document-processing/ui-design.md
docs/features/document-processing/http-paddleocr-vl-technical-design.md
docs/features/document-processing/chat-attachments-technical-design.md
docs/features/document-processing/implementation.md
docs/features/document-processing/progress.md
docs/features/document-processing/changed-paths.md
docs/quality/recurring-defects.md
```
`scripts/paddleocr-vl-probe.mjs` 和导航 README 原有未提交内容保留,本轮未修改。
临时 Electron 驱动、隔离构建、合成文件和原始报告未加入产品源码,证据目录索引见进度文档。
@@ -0,0 +1,163 @@
# 聊天附件技术说明
产品规则见 [PRD 的聊天附件入口](./prd.md#321-聊天附件入口),输入区布局与反馈遵循
[UI 设计系统](../../../UI-DESIGN.md)。本文说明本地文件选择、文件粘贴和截图粘贴的实现。
## 文件导入链路
`App.tsx` 在 textarea 的 `paste` 事件中同步读取 `clipboardData.files`。没有文件时
保留浏览器默认文本粘贴。有文件时阻止默认插入,并通过 preload 的
`context.getFilePath(file)` 调用 Electron `webUtils.getPathForFile`。
有本地路径的文件调用 `context.importFiles(paths)`,经 `context:import-files` IPC 到
Main。IPC 先验证可信 sender,再使用共享 `contextImportFilesSchema` 校验输入。
没有本地路径的截图沿用 `context.addPastedImage`;无路径的非图片文件和混有无路径项目
的本地文件批次显示可处理的错误,不静默丢弃项目。
`ContextManager.selectFiles` 只负责文件对话框,再调用与粘贴共用的 `importFiles`。
这个方法完成路径规范化、格式及大小检查、文本读取、图片转换、文档解析与上下文暂存。
任一文件失败时删除本批次已经暂存的项目,保留之前的附件。文件系统错误不向 Renderer
暴露绝对路径。
两种文件入口均发送 `context:file-selection-progress`。Renderer 共用正在导入状态、
附件上限处理和附件清理逻辑,解析期间禁用附件按钮并阻止发送。成功结果加入发起操作的
会话草稿;发送时通过已有 `contextIds` 和 `ContextManager.enrichRequest` 生成请求上下文。
本次变更不修改 Agent、远端协议或 Runtime 的附件消费实现。
## 2026-09-19 生产更新
HTTP 来源、文件资产、持久化草稿、显式图片 OCR、选图、历史预览和队列恢复已接入。
当前行为以[生产实现说明](./implementation.md)为准;本文件后续“纯图片现状与规划边界”
及“完整附件流程设计”保留 2026-09-18 的实施前分析,不能作为当前未实现清单。
历史 Electron 记录仍按原日期保留,本轮证据见[进度](./progress.md#2026-09-19-实现与验证)。
## 纯图片现状与规划边界
当前 `ContextManager` 将本地图片、粘贴图片和消息通道图片交给 `storeImage`,通过
`encodeBoundedJpeg` 转成 JPEG 暂存在内存;`enrichRequest` 将其放入 `images`,不调用
`DocumentParsingService`。`serializeForQueue` 保存上下文内容,`restoreFromQueue` 可恢复
队列图片,但这些机制未实现原始图片的独立文件保存。
图片能力由有效模型和 Runtime 决定。例如 `ModelRuntime` 在未启用图片输入时拒绝图片请求;
图片工具绑定路径还可能只保留工具引用并告知聊天模型无法看图,不能由附件已添加推断模型
已看到图片。现有路径没有统一的“提取图片文字或切换视觉模型”交互,本文不声称已经实现。
已确认的目标规则见 [纯图片路由需求](./prd.md#322-纯图片与文档的路由规划),原图与解析
文档分开保存的目录及生命周期见 [资源规划](./http-paddleocr-vl-technical-design.md#结果与资源)。
显式图片 OCR、原图保留、草稿引用保护和长期预览均待实现,以下历史验证不覆盖这些能力。
## 完整附件流程设计
本节为待实现范围,与下述既有验证记录分开。设置与预览的布局、交互及验收由
[UI 设计](./ui-design.md)维护,发送模式、幂等、限制及删除规则见
[选图与发送](./logic-design.md#选图与发送),进程契约和资源提交见
[HTTP 技术设计](./http-paddleocr-vl-technical-design.md#选图与附件请求)。
需要在现有聊天链路中补齐:
- `App.tsx` 的会话草稿附件增加读取持久化资源、逐附件解析状态、预览、重试和取消。
当前 `attachmentsByConversation` 与内存 contexts 不构成跨重启持久化。
- 共享附件契约保存资源引用、原始名称、来源和发送方式。图片 OCR 成功只改变该草稿的
发送方式与显示名,原图仍可打开;消息与队列保留接受时的文字/图片选择。
- 文档预览添加选图与普通导入汇入同一草稿和请求消费路径。选图采用整批提交及来源幂等,
不能直接套用新增文件按剩余位置截取的 UI 逻辑。父文档全文只送一次,图片分别计数。
- 数量及容量值统一读取已有附件规则;Main 对草稿、入队和实际执行分别核验能力及有效引用,
UI 不凭模型名称或图片工具引用推断已支持视觉。
- 草稿、发送和队列先保存会话资产与 DB 引用,再报告接受;从知识库复制的图片属于会话,
不依赖源库文档继续存在。移除父文档、图片、队列或会话各自只释放对应引用。
- `ContextFileSelectionProgress` 当前只有读取/解析阶段,需增加阶段、操作 ID 和取消接线;
首次批次失败仍回滚本批,新结果失败则保留旧附件。重启不自动恢复中断的 HTTP 推理。
新增图片发送路径仍需核对实际 Runtime 消费,包括远端 Agent;前文“本次变更不修改 Agent”
描述的是已完成文件粘贴工作,不是对本节未来实现范围的保证。生产实现需按仓库规则验证
受影响的远端路径,不能用历史 Windows 文件粘贴测试替代。
## PPTX 图片识别与 OCR 生命周期
产品策略与限制分别见 [PPTX 图片流程](./prd.md#75-pptx-图片流程)、
[OCR 空闲资源回收](./prd.md#76-ocr-空闲资源回收)。
`DocumentParsingService` 在非快速工作流中使用 `knowledge/pptx-parser.ts` 读取
演示文稿顺序、幻灯片文字、图片关系和内嵌图片。Office ZIP 读取复用
`document-parser.ts` 的条目数量、单条目及总展开大小检查;不读取工作区中的关联文件。
每张引用图片通过现有 `DocumentOcrBroker` 的图片请求契约,进入生产
`document-ocr-bridge.ts` 和 WASM Worker。结果合并时保留原生文字,附上幻灯片页码、
图片定位及 OCR 置信度,诊断中的 OCR 页数按页码去重。
Bridge 用待完成请求计数判断队列是否空闲,正常完成后安排一次空闲计时;
新请求和 `terminateWorker` 均清除计时。到期通过 `Worker.terminate()` 释放整个推理环境,
而不是只清空 JavaScript 引用或反复触发垃圾回收。此机制由 PDF 与 PPTX 共用,
不增加 IPC、持久化设置或模型安装状态。
解析与回收发生在 Desktop 的 Main/Renderer,`gbagent` 不运行此 OCR Worker。
对于 PDF、Office 文档,远程 Runtime 消费的仍是 Desktop 已提取的附件文本,没有单独的 Agent 解析器或
需要同步修改的远程 OCR 生命周期。
## Electron 验证
2026-09-15 在 Windows 的 Electron 43.2.0 上,从当前源码构建隔离输出并启动完整应用,
使用独立用户数据目录,保持 `sandbox: true`、`contextIsolation: true`。
已实际验证:
- Windows `Shell.Application` 的文件 `copy` verb 生成系统剪贴板后,原生粘贴事件提供
`File` 和有效路径,TXT 出现在聊天附件区。
- `CF_HDROP` 同时携带 TXT、DOCX 时,两个文件经生产 preload、IPC 和解析器成为附件;
DOCX 发出 `reading`、`parsing` 进度,包含正确文件名及 `2 / 2` 批次序号。
- 普通文本原生粘贴进入草稿,后续文件粘贴保持文字不变。
- ZIP 经生产导入路径报出不支持类型,已添加附件仍在。
- 系统图片剪贴板产生的 `image.png` 没有本地路径,仍通过截图入口生成图片附件。
以上使用原生 `webContents.paste()`,未伪造 DOM 粘贴事件或替换附件实现。Windows Shell
复制测试没有操纵资源管理器窗口中的键盘。产品未增加 Win32 剪贴板读取或兼容代码。
探测程序的原生调用仅用于构造文件剪贴板和保存、恢复测试前的剪贴板对象;最终探测的
`OleSetClipboard`、`OleFlushClipboard` 均返回成功。早期一次探测的恢复调用返回错误,
无法追溯确认该次剪贴板是否完整恢复。
macOS、Linux 文件管理器的实际剪贴板行为尚未在本次环境验证。没有发起模型调用;附件
进入发送请求的 `contextIds` 由 Renderer 回归测试验证,完整文档内容进入请求由生产
`ContextManager` 的真实文件解析测试验证。
## 自动检查
聚焦用例位于 `src/main/context-manager.test.ts`、`src/main/ipc.test.ts`、
`src/preload/context-files.test.ts`、`src/shared/context-contracts.test.ts` 和
`src/renderer/src/App.test.tsx`,覆盖多文件、进度、重复粘贴、发送上下文、错误与草稿保留、
大小及数量限制、批次回滚、可信 sender、输入契约和截图/文本回归。
```text
npx vitest run src/main/context-manager.test.ts src/main/ipc.test.ts src/preload/context-files.test.ts src/shared/context-contracts.test.ts src/renderer/src/App.test.tsx
npm test
npm run typecheck
npm run lint
```
本次最终拆分检查通过 6 项附件相关 Renderer 用例和 160 项 Main、IPC、preload、契约及
沙箱用例,类型检查与 lint 通过。完整 `npm test` 运行结果为 4214 项通过、67 项跳过、
6 项窗口恢复用例失败;这些用例属于并行修改的 `window.test.ts`,随后该文件单独复跑
10 项全部通过。本次没有再次完整重跑全仓测试。
## 2026-09-15 PPTX OCR 与空闲回收验证
在 Windows x64、Electron 43.2.0 上,从本次源码构建独立输出,使用独立用户数据目录和
本机已安装的 PP-OCRv6 Tiny 模型完成真实验证。测试仅替换系统文件选择对话框的返回路径,
保留生产 UI、preload、IPC、`ContextManager`、解析服务、OCR Broker 与 WASM Worker,
未替换解析或推理实现。
- 从“添加附件”一次导入 4 份图片型 PPTX,共 40 页;4 个附件均成功显示,连续处理期间
仅创建 1 个 OCR Worker。
- 队列空闲后观察到 Worker 被终止。每 5 秒读取 Electron `app.getAppMetrics()`:
Renderer 工作集采样高点为 733,492 KiB(约 716 MiB),回收后为 162,716 KiB
(约 159 MiB),启动基线约 163 MiB。该结果是本次设备与文件上的测量,不是固定内存承诺。
- 回收后,通过生产 `context.importFiles` 再导入一份 9 页 PPTX;新建第 2 个 Worker,
9 页均返回非空文字,共 2,462 字符,附件成功生成。
- 本次合计 49 次本地图片 OCR 请求,文本模型调用 0 次;没有发送聊天消息,也没有将文件
交给云端模型。未修改源文件。未在 macOS、Linux 或其他 OCR 模型档位上做本次实测。
新增聚焦回归覆盖 PPTX 关系与顺序、各场景路由、原生文字合并、快速模式、页数上限、
OCR 错误和取消;Worker 回归覆盖空闲到期、连续请求复用、队列保护、关闭清理与立即取消。
空闲回收后重新加载也在真实模型路径上验证,不能只用模型配置检查代替。
最终检查:`npm test` 4,293 项通过、67 项跳过;`npm run typecheck`、
`npm run lint` 通过。当前源码的隔离 Electron 生产构建通过;文档的 10 个相对文件链接
均有效。测试应用已关闭,包含附件草稿的独立用户数据目录已删除,原文件和原模型安装未改动。
@@ -0,0 +1,266 @@
# HTTP PaddleOCR-VL 技术设计
本设计实现 [PRD 的 HTTP 扩展](./prd.md#13-http-paddleocr-vl-扩展规划)和
[逻辑规则](./logic-design.md)。下文保留实施前的接口分析与目标约定,当前文件布局、
迁移编号、IPC 及生命周期以[生产实现说明](./implementation.md)为准;
接口证据包含 2026-09-18 探索和[2026-09-19 生产验证](./progress.md#2026-09-19-实现与验证)。
## 现有接入点
`src/main/document-parsing-service.ts` 的 `DocumentParsingService` 统一处理聊天、知识库、
成果导入及诊断。PDF 使用原生文本质量选择待 OCR 页;当前 `Map<pageNumber, section>`
只保留每页一个 OCR section。PPTX 在原生文字外识别直接引用的内嵌图片。
本地 OCR 经 `DocumentOcrBroker`、Renderer bridge 和 PP-OCRv6 WASM Worker 运行,
服务中的 PDF、PPTX 分支先检查已安装模型。`documentOcrSectionSchema.confidence`
目前必填,但 `ParsedSection.confidence` 已可选。`ParsedDocument` 尚无图片资源字段。
这些差异需显式修改,不能只替换一次网络请求。
计划在 Desktop Main 中增加一个小型 HTTP Provider,由解析服务按来源调用;本地 Broker
保留原路径。HTTP、认证、图片解码和持久化全部留在 Main,Renderer 只收状态、预览和
受管资源标识。现有 Desktop 文档解析后向 Runtime 传文本的边界继续适用;普通图片已有
独立图片请求路径。`gbagent` 无独立 OCR Worker,HTTP OCR 本身不要求 Agent 部署变更。
图片输入支持须核对本次有效模型配置及本地、远端 Runtime 实际消费路径,不作全 Runtime 保证。
## 配置与请求
计划扩展 `document-parsing-contracts.ts` 和 `DocumentParsingSettingsStore`:来源默认
`local`,HTTP 类型固定为 `paddleocr-vl`,保存用户输入的 base URL、Main 加密凭据和
类型化高级选项。设置缺少新字段时读为原有本地行为;不能把本次实测地址写为默认值。
旧设置兼容有已保存用户配置这一具体需求。设置写入仍经共享 Zod 与可信 sender 校验。
认证契约为无需认证或 Bearer API Key,不新增任意请求头编辑器。Main 从加密设置读取
密钥并构造 Authorization;Renderer 只收 configured 状态,空密钥输入表示保持现值,
清除使用显式操作。切换来源或地址不删除凭据。当前探测未验证认证部署,不能把该计划写成
已通过服务测试;实现须覆盖无认证、有效 Bearer、401 及凭据未配置的真实入口。
设置保存保留本地与 HTTP 两组值,只切换活动来源。高级布尔值采用省略/true/false,
过滤模式及自定义标签值分开保存,发送时按 L-H2 生成 `markdownIgnoreLabels`。服务默认
不展开成客户端猜测值,结果记录显式请求选项及服务返回的有效值(若有)。连接检查和
测试解析要求无未保存修改;正式解析直接走 Provider,不调用 health 或 test 作为前置。
| 接口 | 计划用途与请求 |
| --- | --- |
| `GET /health` | 连通性检查,不证明推理能力 |
| `GET /openapi.json` | 诊断部署声明的字段、类型与默认值;不动态生成任意网络调用 |
| `POST /layout-parsing` | `file` 为 Base64;`fileType: 0` 是 PDF,`1` 是图片;固定 `returnMarkdownImages: true`、`visualize: false` |
| `POST /restructure-pages` | 发送 `pages: [{ prunedResult, markdownImages }]`,其中 `markdownImages` 来自页面的 `markdown.images`;显式设置 `mergeTables`、`relevelTitles` |
默认按选中页渲染图片请求,保留准确输入页号;全页高保真可在核验页数及请求限制后使用
整份 PDF。OpenAPI 未声明页范围参数,不能发送一个猜测的 `pageNumbers` 参数并假定生效。
单页图片策略需要独立实现 HTTP 用的渲染入口,不能为使用旧 Worker 而要求安装 OCR 模型。
每次任务冻结配置,沿用原文件大小、页数、文本输出与取消边界;HTTP 增加有界响应读取和
独立请求超时。多页请求超时不能冒称单页耗时。HTTP 状态、服务 `errorCode`、任务取消及
图片解码错误分别保留,错误正文脱敏后限制长度。没有自动重试或 Provider 轮换。
## 高级参数
下表是接口字段映射,产品可配置性是规划;“声明”不代表部署模型可用。
空值语义遵循 L-H2。探测未请求 `visualize: true`,调试渲染图片不作为正文插图。
| 选项 | 类型或服务 schema 默认 | 本次证据 |
| --- | --- | --- |
| `markdownIgnoreLabels` | 可空字符串数组;schema 未列默认标签集合 | 省略、`[]`、显式排除 `text/header/footer/number` 均实测有效 |
| `useDocOrientationClassify`、`useDocUnwarping` | 可空 boolean | 仅声明,未启用验证 |
| `useLayoutDetection` | 可空 boolean | 默认结果有版面标签;未比较开关 |
| `useChartRecognition`、`useSealRecognition`、`useOcrForImageBlock` | 可空 boolean | 仅声明,未比较开关,不能推断对应模型已安装 |
| `prettifyMarkdown`、`showFormulaNumber` | boolean,默认 true / false | 使用默认值;格式开关及公式编号效果未验证 |
| `formatBlockContent`、`mergeLayoutBlocks` | 可空 boolean | 仅声明 |
| `layoutThreshold`、`layoutNms`、`layoutUnclipRatio`、`layoutMergeBboxesMode` | 数字、boolean、数组或对象等,以 schema 为准 | 仅声明 |
| `layoutShapeMode` | `rect/quad/poly/auto`,默认 auto | 未比较 |
| `promptLabel`、`repetitionPenalty`、`temperature`、`topP` | 可空字符串或数字 | 仅声明,不承诺调参收益 |
| `minPixels`、`maxPixels`、`maxNewTokens` | 可空正整数 | 仅声明,需另验有效边界 |
| `vlmExtraArgs` | 可空 object | 任意参数对象编辑不在约定范围;不影响已约定类型化高级选项的实现 |
| `restructurePages` | layout 请求中默认 false | 未测试该一体化入口,使用独立重组接口 |
| `mergeTables`、`relevelTitles` | 默认 true | 独立重组接口成组 false / true 请求成功,但结果相同 |
| `concatenatePages` | 仅独立重组请求,默认 false | 未启用验证 |
| `outputFormats` | 可空字符串数组,如 `docx` | 仅声明,导出不在本次范围 |
已知服务默认排除集合为 `number`、`footnote`、`header`、`header_image`、`footer`、
`footer_image`、`aside_text`;本次样例只实证页眉、页脚和页码的默认过滤。脚注、页眉图、
页脚图、侧栏未构造,不把完整集合误写成 OpenAPI 明示默认或全部通过测试。
## 结果与资源
`result.layoutParsingResults[]` 包含 `prunedResult`、`markdown.text`、`markdown.images`。
此部署的 `prunedResult` 没有 `page_index`,`page_count` 为 null;使用输入页映射和响应
数量检查。字段缺失是本次真实差异,无需猜测版本或伪造置信度。内部 Provider 结果计划将
confidence 改为可选,保持本地返回原值;多块内容组成逐页 section 并保留块到源页的关联,
避免当前按页 Map 丢失块。跨页输出另带原始页集合,不覆盖逐页来源事实。
插图实测为裸 Base64 JPEG,Markdown 使用 `<img src="imgs/…jpg">`。同一键可在不同页
出现,因此资源 ID 由客户端生成,Provider 图片键仅作为页内关联。计划给 `ParsedDocument`
增加图片集合,记录页号、页内键、MIME、尺寸及受管资源标识;解析暂存阶段携带有界字节,
由调用方提交所有权。只解码内联数据,验证格式及尺寸,不请求返回 URL。
现有存储不能直接宣称已覆盖该需求:
| 现有机制 | 计划复用与需补齐之处 |
| --- | --- |
| `AssistantDatabase.createImageArtifact` | 图片目前存 `artifacts.inline_content` data URI,`createInlineArtifact` 限制编码后 5 MiB。聊天解析文档改用 userData 下的文件资产与数据库引用,不复用该内联字节存储,也不借用图片生成 operation 或最多 8 项的消息生成图片列表。 |
| `deleteLocalConversation` | 当前直接删除的任务成果仅限 markdown;新 OCR 图片必须按新增文档所有者关系显式清理,不能假设删会话自动删图。项目删除已有项目成果清理路径,需合并验证。 |
| `KnowledgeService` 的 `managedRoot` | 现有受管原文件位于 knowledge/库 ID/来源 ID 下,reference 模式保留外部原文件路径。规划的 knowledge-assets 单独保存文档级解析产物,两种模式均不将派生图片写入用户原文件旁。 |
| `KnowledgeDatabase` 的文档、chunk metadata | 计划保存文档图片清单与页级引用,分块只放关联 ID,不复制 Base64;删除文档、删除来源、同步替换时一并处理图片。reference 来源删除当前不清理 managedRoot,需为派生图片补齐清理。 |
按 FR-H8 至 FR-H12 和 FR-I3,以下为约定的 `userData` 资源目录规划,不放入 Runtime 项目
工作目录。只有 `knowledge/<kbId>/<sourceId>/` 受管原文件布局已存在,其余目录待实现;
这里仅列文档与附件相关资源,不是 userData 的完整文件清单。
```text
<userData>/
conversation-assets/<conversationId>/
images/<attachmentId>/
original.<ext>
documents/<attachmentId>/
original.<ext>
parsed.md
manifest.json
images/<imageId>.<ext>
knowledge/<kbId>/<sourceId>/
<managed-original-files>
knowledge-assets/<kbId>/<documentId>/
parsed.md
manifest.json
images/<imageId>.<ext>
temp/document-parsing/<operationId>/
<staged-input-and-results>
```
会话附件原文件统一使用 `original.<ext>`,扩展名与保存字节的格式一致;原始显示名称、MIME、尺寸
及会话、消息、附件归属由数据库维护,不依赖磁盘文件名表达。文件导入保留输入原始字节;
剪贴板或截图没有源文件时保留该入口取得的原始图像,不能把请求转码副本称为外部原文件。
纯图片不要求 `parsed.md` 或 `manifest.json`。显式 OCR 后才保存关联的文档解析产物,
通过数据库引用原图片附件,无需重复复制原图。请求用的缩放、转码副本不替换 `original.<ext>`。
文档清单只描述结果需要的页码、页内图片键、受管图片 ID 和正文
引用映射,不建立版本历史或完整配置快照;数据库作为归属与引用的判断依据。随结果记录
解析时所选 Provider 和生效选项,凭据仍只在加密设置中,查看历史结果不重新读取当前配置解析。
结果契约至少增加 resultId、完整程度(完整/部分/仅图片)、页/section 到资源的映射、
来源页集合、缺页缺图诊断和上述解析元数据。任务运行状态与结果完整程度分开,避免失败
重解析覆盖旧成功结果。无文本但有图片可持久化为仅图片结果;知识库不生成空 chunk,
聊天不生成可发送的空 text context。原生、本地和 HTTP 结果使用同一预览契约,图片
集合可以为空;原生解析器不因字段存在就被宣称具有新增提图能力。
当前聊天解析文本仅在 contexts 和发送队列中使用,不能据此宣称已长期保存。
规划中草稿原文件、文本和图片先由应用暂存承接;撤销附件、批次失败或取消时仅清理本次
无有效引用的资源。`temp/document-parsing/<operationId>` 用于解析任务,不能作为一律可删
的判断依据;可恢复草稿仍引用的文件须保留,或先转入稳定资源并更新引用,再清理暂存。
启动、退出清理同样检查引用。发送被接受及队列记录持久化前须完成资源持久化及会话归属,队列保存可恢复的附件引用,
恢复发送不依赖内存 context ID 或外部原路径。持久化失败则不报告发送已接受,保留草稿供重试。
取消生成不回收已归属会话的附件。删除会话时检查有效引用,仅清理独占资源;项目删除路径
也需验证同一规则。采用暂存、现有事务和按数据库引用核对清理,不新增恢复引擎。
测试解析使用任务临时目录,结束预览且无有效引用后清理。知识库正文及 chunks 继续存数据库,
`knowledge-assets` 的 `parsed.md` 为解析正文文件,随同一次结果提交保持一致,不独立修改
或替代数据库中的检索内容。图片归知识库文档所有,不依赖聊天存活;managed 与 reference
派生文件均存该目录,reference 外部原件保持原路径,现有 managed 原件目录不迁移。
重解析或同步先在本次暂存目录完成新结果与必要资源校验,成功提交替换后清理无引用旧资源;
失败或取消只删除新暂存,保留旧正文、chunks、图片及关联。聊天草稿重解析同样成功后替换
草稿引用;历史消息和队列保持已接受结果,再解析时创建新草稿附件。若同一旧结果仍被消息
或队列引用,新结果使用新的资源 ID 和目录,不覆盖原文件字节;这是有效引用保护,不是
产品版本历史。旧结果没有有效引用后即可清理。
Markdown 提交前将 HTML 与 Markdown 图片引用改为受管资源引用,预览通过 Main 读取。
文件资产不套用内联成果的 5 MiB 编码上限;实际文件与响应容量需在实施时明确,超限须报错,
不静默压缩或丢弃。模型上下文仍按现有规则检查,不把图片文件全部计作提示词。跨页重组先传回页内图片
映射,重组后再次验证引用和源页。按 FR-I5,持久化图片集合与请求图片集合分开;后者须有
明确选中项,并校验有效模型、Runtime、数量及容量限制。选图按下节提交独立会话副本,
数量和发送容量复用既有附件限制,不能遍历已保存图片全部发送。知识库图片文字提取与索引按
知识库工作流路由,不依赖聊天模型视觉能力;图片向量索引不在本轮范围。
涉及已发布数据库结构时,实施前遵循
[数据库迁移规则](../../development/database-migrations.md),本文不预先编造迁移编号。
## 选图与附件请求
实现 L-I3 至 L-I5、L-H9 和 [UI 第 4、5 节](./ui-design.md#4-文档插图加入会话)。
以下为需增加的契约内容,名称按实现模块风格确定,不是现有可用 IPC。
| 契约 | Main 行为与持久化 |
| --- | --- |
| 获取解析结果 | 输入归属对象 ID 与 resultId,返回正文、定位、图片描述、诊断和无凭据配置;不重新解析,不一次把全部图片 Base64 发给 Renderer |
| 读取图片或打开原件 | 输入受管资源 ID,Main 解析已登记路径;按需提供预览图/原图或用现有系统打开入口;Renderer 不提交任意磁盘路径 |
| 添加选图 | 输入目标 conversationId、sourceResultId 和去重 imageIds;核对目标可编辑、来源存在及共享限制,复制后提交草稿引用,返回新增和已存在附件 |
| 提取图片文字 | 输入图片附件 ID,使用已保存 OCR 设置启动可取消任务,成功保存结果后更新草稿的发送方式,保留原图与 provenance |
| 修改发送方式/移除附件 | 只修改目标草稿;切回图片校验能力,移除解除草稿引用并清理无引用资产,不按来源 ID 级联删除 |
| 进度/取消/结束预览 | 通过 operationId 关联任务与所属会话或测试;扩展当前只有 reading/parsing 的进度契约以表示请求、校验、保存与取消,不伪造百分比 |
共享 Zod、可信 sender 和显式 preload 方法继续使用现有边界。结果读取和归属查找用
数据库主键或索引,不扫描全部会话/全部知识库文档。UI 的目标选择由 App 通过现有项目、
会话导航和 `startNewConversation` 管理;KnowledgeWorkspace 接收回调,不自行创建
会话 store。已有 `onUseInChat` 会修改检索选择,不能用它代替图片添加回调。
文档插图复制到 `conversation-assets/<conversationId>/images/<attachmentId>/original.<ext>`,
由 Main 从已验证的源文件复制,禁止 Renderer 回传 Base64 再导入。附件数据库字段记录
原名称、显示名称所需信息、MIME、尺寸、原始字节数、resourceId、发送方式,以及来源类型、
源文档/附件 ID、resultId、imageId、文档名、页码/定位。provenance 的可读信息在源删除后
仍保留,源 ID 仅用于仍有效时跳转。跨库复制不建立删除级联外键,不做跨会话物理去重。
添加前在 Main 先去掉同一草稿已经引用的来源图片,再计算全批数量和请求容量;重复调用
沿用已有附件 ID。草稿操作串行提交并用数据库唯一性约束保证幂等,不以禁用按钮作为唯一
保证。文件复制到本次暂存后移入新附件目录,再在事务内登记文件与草稿归属;失败删除
本批新文件,不删除已有副本。重启只核对无数据库引用的暂存或孤立新目录,已登记草稿
资源继续保留,不增加写前日志或恢复引擎。
当前限制来源是 `src/shared/contracts.ts` 的 `contextIds.max(8)`、导入数组上限及
`maximumPastedImageBytes`,Main 的 `ContextManager` 另有 8/16/12 MiB 常量;实现时将
这些同义值集中到共享附件契约供现有导入、选图、草稿与发送复用,保持已发布数值。源文件
限制仍区分图片与文档,不能套用生成成果 5 MiB 编码限制或发明“文档最多选几张”。
Provider 响应和解码资源的有界读取属于解析限制,不等同发送附件数量。超过任何限制均给
具体原因;选图整批拒绝,不复用 App 当前对新增文件 `slice` 接受剩余位置的行为。
`ContextManager` 对持久化图片生成既有有界请求副本,原图保持不变;提取文字模式只构造
text context。解析父文档只拼入一次全文,图片副本仅携带简短来源及图像。草稿、消息和
队列保存附件资源引用与发送方式,不能仅序列化会过期的内存 contextId。执行从这些资源
恢复 context 并重新检查真实模型/Runtime 能力,完整校验后才接受发送或入队。
仅图片父文档计入消息附件数并保留原件引用,但不创建空文本 context;发送前核对同批已有
来自该结果的选图或提取文字,提示及恢复操作遵循 L-I5。
队列开始执行及重启恢复再次检查实际能力;失配沿用可恢复队列错误,保持资源及内容,
提供恢复到草稿修正。恢复时先提交草稿引用再删队列引用,失败保留队列;用户修改提取文字
方式再发送产生新的队列输入。取消生成保留已发送附件,删除队列只释放该项引用。分支
会话复制已发送附件时也必须建立有效归属引用或独立副本,不能只复制原目录字符串;不改变
既有分支产品范围。会话和项目删除使用同一按有效引用清理路径。
图片能力检查必须落在 Main/Runtime 请求边界,并在 UI 展示同一结果。Desktop 直连模型、
本地 Runtime 与托管 SSH 的图片消费须分别核对;仅工具持有引用不算模型收到图。HTTP
OCR 位于 Desktop 不自动证明选图发送不涉及 Agent;若实现改动远端附件协议或消费路径,
必须按根 AGENTS 的共享 Linux Host 规则实测,记录具体 Runtime/模型组合及模型收到的输入。
## 预览实现
共享预览组件只接收受管结果、目标草稿状态与动作回调。设置、聊天 Modal 和知识库详情
负责外壳,沿用 [UI 容器映射](./ui-design.md#1-容器与共享组件)。PageTabs 的 idPrefix
按实例区分,详情滚动、页码定位和勾选状态在同一预览内保留,切换对象时清除旧对象选择。
现有 `MarkdownRenderer` 使用 `skipHtml`,`renderHtml` 只处理完整 HTML 或围栏,不能
直接显示混合 Markdown 中的 OCR `<img>`、`<table>`。需为解析预览增加受限内容适配:
用结构化 HTML 解析将已支持表格元素及受管图片节点映射到既有排版组件,移除脚本、事件
属性和外部资源,不开启全局任意 HTML 渲染。Markdown 与 HTML 图片统一通过清单解析
imageId,缺失引用换成诊断文字;HTML 表格保留行列及合并关系。页码链接使用结果的
sourcePages,不从文件名猜测。继续沿用应用既有静态内容安全边界,不引入新的文档授权流程。
图片按可见内容加载,缩略图与原字节分开;组件关闭释放本次预览 URL。结果读取失败只
替换相应面板并提供重试,不清掉其他正文或草稿。测试任务目录在关闭结果后清理;未关闭
预览期间不能因为 HTTP 任务已完成就删除图片。测试重跑成功后才替换当前预览,失败
保留上次结果并标明该结果属于上次测试。测试不建立草稿、会话或知识库引用。
取消贯穿渲染、Provider、重组和保存,在资源提交前检查取消;迟到响应不写入当前对象。
保存事务已成功后的取消按完成结算,不把已提交结果删除后报告“取消成功”。退出后未完成
任务只标中断并允许手动重试;数据库引用是清理依据,任务目录名不是删除许可。
## 实施与验证顺序
以下是同一完整功能的依赖顺序,不定义缩减版交付。验收范围以
[完整矩阵](./ui-design.md#8-完整验收矩阵)为准。
1. 按已完成的 UI 设计实现来源设置、Main HTTP Provider 和可选 confidence,验证无本地模型的真实解析入口、保存前禁止测试及凭据保留。
2. 补文件结果契约、纯图片与文档分目录、聊天与知识库归属、发送入队及删除;验证原图保留、重启重开、草稿引用保护、外部原文件移除、队列恢复、同名图片键、reference 来源删除、重解析失败和测试清理。
3. 接入类型化高级选项及逐页 PDF/PPTX 路由;以真实开关样例验证高级模型能力。
4. 补能触发合并的连续表格样例与来源映射,再接入可选跨页重组。不能以本次两组输出相同的请求作为该步骤完成依据。
5. 按 L-I1 至 L-I5 接入有效图片能力、不支持提示、显式提取文字及选图入草稿和发送;覆盖成功后的文本模式、父文档正文去重、独立移除、目标选择/新建、添加幂等、共享限额、来源删除后副本持久和本地/远端 Runtime 消费。
6. 从设置、聊天和知识库真实入口验收共享预览、完整/部分/仅图片状态、取消与重启、队列修正、资产清理;验证键盘、两主题、窄容器和原生浏览器遮挡。只有这些产品路径均完成,才可将对应需求记为已实现。
产品验收覆盖聊天、知识库和测试解析、HTTP 错误、缺图、取消和重开。已运行的层次、
全仓测试及尚未覆盖的场景见进度。原独立探测脚本及历史记录保留,不与本轮调用数混算。
@@ -0,0 +1,116 @@
# 文档处理生产实现
2026-09-19。本文描述当前源码,验收证据单独见 [progress.md](./progress.md#2026-09-19-实现与验证)。
需求 ID 沿用 [PRD](./prd.md),界面原则沿用 [UI 设计](./ui-design.md)。
## 解析与设置
`DocumentParsingService` 统一服务聊天文档、知识库、成果导入和设置测试。`HttpDocumentOcr`
在 Main 调用 PaddleOCR-VL,`render-ocr-pdf.ts` 使用 PDF.js 和本机 Canvas 渲染选中页,
不需要本地 OCR 权重。来源未配置时继续原有本地行为;产品没有测试地址默认值。
设置文件版本为 4,读取版本 1、2、3 时保留原有设置。认证支持无需认证和 Bearer API Key,
复用 Electron safeStorage,公开快照只显示是否配置。空密钥保持现值,显式清除才删除;
任务同时固定设置与凭据读取对象,中途保存不会更换在飞请求的地址或密钥。
快速文本/索引不上传。自动 PDF 只渲染需要 OCR 的页,高保真可直接发送全 PDF。
跨页整理开启时,连续待 OCR 页组成一个 PDF 请求;不连续页分组,避免把不相邻页面合表。
PPTX 仍按原生文本和直接引用的内嵌图片处理,定位保留幻灯片号与图片序号。
普通聊天图片直传;“提取图片文字”独立调用已保存来源,忽略快速工作流的自动跳过设置。
HTTP 请求固定返回 Markdown 图片、关闭调试可视化。高级布尔值保留省略、true、false;
过滤分别保存服务默认、全部保留、自定义及其排除集合。自定义隐藏后不清空未知标签。
页面处理、图表/印章/图片块识别、Markdown 格式、跨页整理和类型化版面/生成参数可配置。
部署模型能力需以用户样例确认,连接检查只执行 health 和 OpenAPI。
响应流上限 32 MiB,单张解码图上限 12 MiB、4000 万像素,文档文字上限 500 万字符。
请求超时独立于本地单页超时;HTTP 状态和服务错误码保留,凭据和响应正文不出现在错误中。
不下载服务返回的图片 URL,不自动重试或切换 Provider。
图片按来源页和页内键解码,以 UUID 替换 Markdown 与 HTML img 引用。同名键跨页不会覆盖。
跨页整理的变化输出作为一个带完整 `sourcePages` 的分组正文;图片按实际返回字节匹配原页。
无法唯一匹配时保留原逐页结果并警告。严格索引/高保真遇缺图或重组失败拒绝替换。
服务无置信度时字段缺省,详情显示“未提供”。
## 文件与引用
```text
<userData>/
conversation-attachments.sqlite
conversation-assets/<conversationId>/
images/<resourceId>/original.<ext>
documents/<resourceId>/original.<ext>
documents/<resourceId>/parsed.md
documents/<resourceId>/manifest.json
documents/<resourceId>/images/<imageId>
knowledge/<libraryId>/<sourceId>/<managed-original>
knowledge-assets/<libraryId>/<documentId>/<resultId>/
parsed.md
manifest.json
images/<imageId>
temp/document-parsing/<resourceId>/
```
会话资产的 `request.json` 保存已转换的请求内容,用于恢复内存 context;原件字节单独保存,
缩放和 JPEG 请求副本不覆盖原图。提取图片文件使用 UUID 无扩展名,MIME 在清单记录。
图片显式 OCR 后生成解析文件。切回原图保留已有结果,并重新进入图片能力校验。
HTTP OCR、显式图片提取及结果完整程度共用 `hasExtractedDocumentText`;图片引用与生成的
缺图警告只作预览/诊断,不算正文。无正文的显式提取失败并保留原草稿;有有效插图的文档
结果仍可保存为仅图片,不因缺图警告改判为可发送文本。
`ConversationAttachmentStorage` 的专用 SQLite schema 1 保存描述、路径与
`draft/message/queue/parsing` 引用,不在这些描述字段内存入原图 Base64。纯图片无需解析清单。
Main 先保存资产,再保存草稿或接受队列;消息、分支建立独立引用。无有效引用才回收文件。
会话删除、项目删除及清空数据均核对引用,启动回收已无归属的记录和孤立 UUID 目录。
清理失败保留无引用数据库记录供下次重试,不回滚已接受的草稿。
助手库 schema 39 标记新附件元数据,阻止旧客户端将其当旧格式读取;没有批量改写历史附件。
知识库 schema 13 新增 `metadata.parsedResultId` 表达式索引,结果按主键定位,不扫描全部文档。
知识库新结果先写入独立 resultId 目录,数据库正文/chunks 发布成功后替换引用,再清理旧目录。
原件继续遵循 managed/reference 原有布局,reference 派生文件不写入外部目录。
旧正文重解析为空索引时保留旧结果;首次仅图片结果可预览,但显示无可索引文字。
会话图片从文档结果复制为独立原图文件,保存文档名、页码、resultId、imageId。
源文档重解析或删除不影响会话副本。添加批次串行,重复来源在同一草稿内返回已有项;
提交前重新读取目标草稿,数量/容量不符整批回滚。限额集中在 `attachment-limits.ts`:
每消息 8 项、内存 context 16 项和 12 MiB;原文件上限仍为图片 12 MiB、文档 20 MiB。
重解析与发送方式改变采用新文件资源替换草稿位置,旧消息/队列保持原 resourceId。
`attachmentId` 保留同一草稿附件身份,context 的 `id/resourceId` 随新产物更换,原始名称和来源保持。
图片 OCR 后再次切回原图会保留原 OCR 结果的副本,用户无需重跑 OCR 才能查看。
## 任务与队列
文件导入提供 operationId、读取/解析/保存阶段和取消。HTTP AbortSignal 贯穿页渲染、识别、
重组与提交检查。首次失败回滚本批附件,已保存原件用独立解析失败记录供重试,不作为可发送
草稿项。用户取消清理本批暂存;退出或进程中断后,启动将未完成记录标为中断,不自动请求 OCR。
旧结果重解析失败时保留旧草稿或知识索引。
队列保存接受时的附件和发送方式。入队、分发、实际执行分别检查图片能力。
分发失败保留队列及可恢复错误,队列预览只读;“恢复到草稿”先合并并保存附件引用,
再移除队列项,文字用空行追加,超限保持原队列。Renderer 请求恢复时不传入旧草稿文本,
收到队列正文后通过原会话的函数式状态更新追加到最新草稿,保留等待期间的编辑和清空。
文件附件的启动恢复不再把提前保存的用户消息误认为 Runtime 已接受并删除队列。
## 界面与边界
设置页头的测试菜单选择聊天或知识库场景,未保存禁止测试/连接检查。测试中锁定设置退出,
可取消。诊断关闭释放测试资源,没有添加到会话入口。
`DocumentResultPreview` 共用于设置测试、聊天 Modal、知识文档详情。正文支持 Markdown、
受限 HTML 表格和已登记图片;脚本、事件属性、外部资源不渲染。图片按可见区域加载 640px
缩略图,点击大图才读原图。正文/图片定位与 PageTabs 使用结果页码;详情展示保存时配置。
知识库返回列表保留查询和位置。历史附件可复制到当前草稿重新解析,不修改原消息。
附件名称、解析状态及大小与操作行分开排列;查看结果和更多操作复用 32px 图标按钮,
提供悬停与键盘焦点提示。草稿、历史和队列共用相同操作组件,菜单及 Modal 的焦点恢复
继续由共享机制处理。具体布局见 [附件操作菜单](./ui-design.md#12-附件操作菜单)。
目标会话选择显示项目与名称;新建沿用 App 的项目限制和空会话复用,保持预览。
选择、已在草稿、移除、添加、前往会话分别处理。当前没有可编辑目标时需先选择或创建。
草稿图片可提示模型/Runtime 能力问题,但允许保留;Main 才是发送与入队校验边界。
本机推理监控的模型入口直达 `model`,OCR 行直达 `document-parsing`,HTTP 服务不加入本机服务列表。
OCR 和资产管理运行于 Desktop。直连模型及托管 OpenCode 已验证实际图片输入;Agent 未增加 OCR
解析器或新附件传输协议。当前桌面消费已有图片协议;Continue、DeepSeek Harness 的本次真实视觉
调用未执行,不能由设置声明推断这些组合已经过本轮验收。
@@ -0,0 +1,175 @@
# HTTP OCR 逻辑设计
需求来源为 [PRD](./prd.md#13-http-paddleocr-vl-扩展规划),场景验收见
[User Stories](./user-stories.md)。当前生产机制及与设计的差异见[实现说明](./implementation.md),
逐项验证见[进度](./progress.md#2026-09-19-实现与验证)。以下规则保留需求追溯职责。
## 输入路由
L-I1(FR-I1、FR-I2、FR-I4;US-I1、US-I2、US-I3、US-I6):先区分用户操作与输入类型,
再判断实际生效的模型及 Runtime 能力。显式提取图片文字使用已保存 OCR 配置,优先于普通
图片直传分支,也不受文档快速工作流的隐式跳过规则影响。
| 输入或操作 | 处理 |
| --- | --- |
| 普通聊天图片,实际支持图片输入 | 作为图片请求发送,不调用 OCR |
| 普通聊天图片,实际不支持或能力未确认 | 明确提示提取文字或切换视觉模型,不静默 OCR,不声称已发送图片 |
| 明确提取图片文字 | 调用配置的 OCR;模型视觉能力不影响此操作 |
| PDF、Office | 沿用文档原生解析与按需 OCR 工作流 |
| 知识库图片文字提取及索引 | 由知识库工作流决定,不读取聊天模型视觉开关作为路由依据 |
L-I2(FR-I3、FR-I5;US-I1、US-I4、US-I7):原图与请求副本分开,缩放、转码不覆盖原图。
纯图片无解析正文及清单前置条件;显式 OCR 产物关联回原附件。文档图片保存与模型请求选择
分别处理,只有明确选入请求且满足实际能力、数量和容量限制的图片才进入模型输入。
L-I3(FR-I2、FR-I3;US-I3、US-I11):附件身份、原始名称与来源不因 OCR 改变,发送方式
单独记录为图片或提取文字。只有 OCR 返回非空可靠文字且结果保存成功,才切到提取文字;
显示名派生为“原文件名 · 提取文字”。提取文字模式只拼入文本,不同时传原图或把解析中
提取的其他图片自动附加。失败、取消、空文字保持原方式;用户可显式切回图片并重新校验
能力。图片引用及解析器生成的缺图警告不算提取文字,即使同时保存了其他有效图片也不例外。
重试采用当前已保存 OCR 设置,开始时说明来源;旧结果在新结果成功前保持可用。
## 选图与发送
L-I4(FR-I5;US-I7、US-I8、US-I9):预览待添加选择是短期 UI 状态,目标草稿的附件引用
才是“已附加”的依据。进入预览默认零选择,切页签保留选择,关闭清空未添加选择。已附加
项不能靠取消勾选移除,必须明确执行移除操作。测试预览不提供添加入口。
添加提交包含明确的目标会话及来源结果 imageId 集合,提交时冻结目标,不根据异步完成时
活动会话改归属。Main 确认目标可编辑、源资源仍存在、数量与容量可接受后复制到会话所有
的稳定文件资产,再保存草稿引用;任一复制或保存失败回滚本批新副本,保留原选择与草稿。
无有效目标时必须先选择或新建会话,不隐式创建或采用第一个会话。活动回复不阻止下一条
草稿编辑,但仍遵循已有 Runtime/模型选择锁定规则。
以“目标草稿 + 来源结果 ID + imageId”判断重复添加;来源结果 ID 只标识实际解析产物,
不引入结果版本历史。同一草稿存在该图片引用时返回已有附件,不重复占用容量或位置。
移除后可再次添加;发送后新草稿可再次添加。重解析生成新结果和图片 ID,不自动替换
已附加副本。父文档与副本没有级联删除关系,来源关联用于说明及跳转,不作为副本存活条件。
L-I5(FR-I1、FR-I5、FR-H9;US-I2、US-I8、US-I10):选图和发送沿用 PRD 第 3.2.1 节
现有附件限制。父文档和每张图片分别占一个附件位置;现有基线为每消息 8 个附件、Main
16 个上下文项目及 12 MiB 暂存内容上限。实现从同一共享契约读取这些值,不在预览、IPC
或 Provider 中定义独立的“选图上限”。图片副本使用既有图片请求转码路径,原始资产大小
与请求副本大小分开计算,不能把尚未选中的全部插图计入本次发送容量。
选图批次先去掉已附加项再整批校验;超限全部拒绝并保留选择,不沿用文件粘贴入口的截取
剩余位置行为。发送前重新读取完整草稿并校验,不依赖添加时的剩余位置。文本父附件只
拼入一次,不为每个图片副本重复加入父文档正文;模型正文仅附简短来源信息及图片输入。
仅图片父文档仍作为来源附件保留并计入附件数量,但不生成空 text context;同批至少有一张
从该结果选出的图片(或其显式 OCR 文字)才可携带它发送,否则提示选择图片或移除该文档。
能力判定取本次实际模型与实际 Runtime 消费路径的交集。添加时显示能力问题但允许保存
待修复草稿,发送和入队接受前必须通过校验;未知能力不能当作支持。切换模型/Runtime、
恢复队列及真正开始执行时重新校验。队列能力失配转为可恢复失败并保留资源,用户可恢复
到草稿修正或切换到支持的配置后重试;队列内容不会静默转文字或丢图。不以工具拿到图片
引用代替聊天模型收到图片的证据,也不因模型支持视觉而隐式调用 OCR。
## 来源与选项
L-H1(FR-H1、FR-H2;US-H1、US-H2、US-H8):每次解析开始时读取并冻结已保存的来源、
地址和选项。选择 HTTP 后只检查 HTTP 配置;本地模型状态不再是 HTTP 的前置条件。
HTTP 失败不触发本地下载或切换。快速工作流优先于 OCR 设置,仍不运行 OCR。
L-H5(FR-H11、FR-H12;US-H12):来源切换保留各自配置,测试要求先保存;解析结果记录
本次 Provider 与生效选项,不含凭据。查看已存结果不触发解析,修改配置不改写旧结果。
配置为设备级共享值;项目、会话和知识库范围决定结果归属,不创建独立 OCR 配置。应用中心
的本机推理监控启用、常驻与窗口开关不决定 OCR 来源或可用性。HTTP 来源不依赖本地监控
入口和模型加载状态。菜单及设置直达返回规则见 [入口与职责](./ui-design.md#11-入口与职责)
(FR-H1、FR-H12、FR-I5;US-H16)。
L-H2(FR-H4;US-H4、US-H9):高级布尔项使用“服务默认 / 开启 / 关闭”语义。
服务默认省略字段,不将未知默认值写成 `false`。`markdownIgnoreLabels` 使用三种状态:
省略表示服务默认,空数组表示不排除标签,非空数组表示明确排除的标签集合。保存设置
不得把空数组归一为缺省。任务内选项不随用户中途修改设置变化。
自定义过滤的正向“保留”开关与排除集合取反映射。首次进入自定义且无旧值时从全部保留
开始,明确显示此初始值;不猜测服务默认。已有自定义排除列表在切换模式后保留,未知或
不常用标签也必须呈现并保留,不能保存时丢弃。默认保存结果引用的图片;过滤使图片不再
被正文引用时才可不提交对应图片。过滤设置不控制模型自动选图。
## 页面与图片
L-H3(FR-H2、FR-H5、FR-H7;US-H2、US-H5、US-H8):页面定位来自输入任务的原始页号。
单页图片请求可确定其 PDF 页号或 PPTX 幻灯片号;整份 PDF 请求在核对返回数量和顺序后
映射到原始页号,不依赖此次部署缺失的 `page_index`。不连续 OCR 页不能直接跨页合表。
重组输出保留 `sourcePages` 集合,无法恢复对应关系时报告失败,不能猜测唯一页号。
L-H4(FR-H3;US-H3、US-H7):以“文档 + 原始页 + 页内图片键”关联图片。只处理有界、
可解码的内联图片,不下载服务返回 URL。Markdown 中的 `![]()` 和 HTML `img src` 均须
映射到保存后的受管资源;表格 HTML 需要既有安全渲染处理。模型正文与向量索引不混入
Base64。筛除页眉图等内容时,对应图片只有仍被结果引用才需要提交。
## 结果与失败
| 条件 | 处理规则 | 关联 |
| --- | --- | --- |
| 原生文本可靠且未要求 OCR | 直接使用原生结果,不探测 HTTP | FR-H2 / US-H2 |
| HTTP 与图片保存完成 | 提交逐页内容、图片引用和诊断;置信度缺失就省略 | FR-H3、FR-H7 / US-H1、US-H3 |
| 自动聊天 OCR 失败且有可靠原生文字 | 沿用既有原生文字回退,并标明未完成 OCR 页 | FR-H6 / US-H6 |
| 自动聊天已取得文字但部分图片失败 | 保留文字和具体缺图警告;缺图引用改成文字说明 | FR-H3、FR-H6 / US-H7 |
| 自动聊天跨页重组失败,但逐页结果可用 | 保留原逐页正文、图片及页码,显示重组失败和部分结果;不使用映射不明的重组内容 | FR-H5、FR-H6 / US-H5、US-H6 |
| 知识库完整索引或高保真缺页、缺图、重组失败 | 本次失败,不替换已有成功结果;允许重试 | FR-H3、FR-H5、FR-H6 / US-H3、US-H5、US-H7 |
| 用户取消解析或应用关闭 | 中止客户端请求,不启动后续任务,仅清理本次无有效引用的临时资源;草稿、已发送、已入队引用的资源保留 | FR-H6、FR-H9 / US-H6、US-H10、US-I5 |
| 无文字但有已保存的有效插图 | 结果为仅图片,可预览并按 L-I4 选择;聊天父文档无可发送文本,知识库不生成空 chunk,不标为文本索引成功 | FR-H3、FR-H7、FR-I5 / US-H3、US-I7 |
| 无文字且无有效图片 | 失败,保留原件供重试,不能标为空成功 | FR-H6 / US-H15 |
请求状态按“准备、请求、校验、保存、完成”推进,提交前可进入失败或取消;资源及引用已
提交后按完成结算,迟到取消不删除结果。
HTTP 中止不保证服务端 GPU 推理立刻停止;本部署没有探测服务端取消接口。
L-H8(FR-H6、FR-H9、FR-H12;US-H6、US-H14、US-H15):任务运行状态与结果完整程度
分开。已保存结果可为完整、部分或仅图片,当前任务另有运行、失败、取消或中断状态。
打开旧结果时可以同时显示本次重解析失败,不能用单一 status 覆盖旧结果。
自动聊天允许的可靠文本回退必须将缺页/缺图范围和警告随文本传给模型;知识库完整索引
及高保真严格失败。快速索引只按所选原生策略完成,不因用户明确禁用 OCR 而假称漏页
已经识别。仅图片的知识库结果可保存供预览和选图,但显示“无可索引文字”,不算索引成功。
已有可检索结果重解析后变成仅图片时,不以空索引替换旧结果,本次报告无可索引文字并保留
旧结果;首次导入没有旧结果时可保留图片资产和该状态。
首次批次导入失败或取消沿用批次回滚,失败摘要保留可重试信息但不是草稿附件。重解析
失败不删除已有附件。任务取消通过同一 operationId/AbortSignal 传播,取消后的迟到响应
不提交结果;应用退出停止后续请求,重启将未完成任务显示为中断,不恢复 HTTP 调用。
有受管原件则手动重试;原件尚未保存则重新选择。重启仅根据数据库现有引用清理无主暂存,
无需记录逐页持久运行日志。
## 资源归属
L-H6(FR-H8、FR-H9、FR-I3;US-H10、US-H11、US-I4、US-I5):草稿资源暂存于应用目录;
发送被接受及队列记录持久化前,文档完成原文件、解析正文、图片的持久化及会话关联,纯图片
完成原图持久化及关联。失败保留草稿供重试,不标为已发送。
恢复队列读取持久化附件引用;取消生成不删除已提交资源。移除草稿附件、取消解析或批次失败
只清理本次无有效引用的资源。启动、退出或定时清理不得一律删除临时目录;可恢复草稿引用
的文件须继续可用。删除会话只释放其归属和引用,无其他有效引用时才删除文件。
L-H7(FR-H10、FR-H11;US-H11、US-H13):知识库文档独立持有正文、chunks 和图片关系,
两种来源模式的派生资源均归应用管理。删除文档或来源时按有效引用清理;外部原文件不属于
派生资源清理范围。聊天和知识库重解析均先完成新结果及必要资源保存,再替换引用;失败或
取消清理新暂存,旧结果保持可用。旧资源仅在替换成功且无引用时回收。
已发送消息及已接受队列的正文、选图、发送方式和资源引用冻结。历史消息“再次解析”产生
新草稿附件,不修改消息;草稿重解析成功仅更新草稿引用,其他有效引用保持原结果。修改队列
附件须先恢复整条输入到草稿,再重解析或改变发送方式,不额外复制一条仍待发送的输入。队列
恢复到草稿时先保存草稿引用再移除队列引用,失败保持队列。队列删除只释放该项引用;
取消正在生成的回复保留已发送输入。会话删除和项目删除均使用同一引用清理规则。
目标已有草稿时,将恢复文字以空行追加、附件按来源去重合并,提交前校验合并后的数量与
容量;超限则保留队列及原草稿,提示先发送或整理草稿,不覆盖已有输入。
恢复等待期间允许继续编辑;文字追加到恢复完成时该会话的最新草稿,不覆盖期间的输入,
也不复活已清空的旧文字。切换会话不会改变原恢复目标。
L-H9(FR-I5、FR-H9、FR-H10;US-I8、US-I9):从聊天或知识库选出的图片在添加成功时
已是会话独立副本,其来源名称、定位和源 ID 随附件保存。来源删除或重解析不删除副本,
来源跳转失效不妨碍打开原图和发送。移除父文档只解除父文档引用,移除副本只解除该图片
引用。草稿、消息、队列、分支等已有有效引用共同决定是否清理文件;不为跨库引用建立
全局去重或资源保活服务。知识库与聊天原资产各自清理,复制时失败只回滚本批新资源。
## 完整性检查
来源选择、缺省值、正向过滤、混合页、图片键、完整/部分/仅图片结果、失败取消、原图保存、
显式 OCR 发送方式、选图幂等、目标选择、共享限额、发送入队、重解析及清理均已有确定规则。
组件和布局见 [UI 设计](./ui-design.md),逐项验收见其完整矩阵;这些范围没有留待后续产品
决策的选图缺口。
实际直连和托管 OpenCode 图片消费、扫描 PDF 与跨页合表示例已取得真实证据,见进度。
其他 Runtime、本地 OCR 各平台和部署高级模型的本轮验证范围仍需分别核对。
模型声明与接口成功不替代效果测试;跨页整理默认关闭,失败明确报告。
+164 -31
View File
@@ -22,13 +22,20 @@ GoodBuddy 需要用同一条可信文档解析链路服务以下场景:
- XLSX 工作表 XML 与共享字符串;
- PPTX 幻灯片文字。
聊天文档附件、知识库和测试解析通过统一的 `DocumentParsingService` 使用已保存的工作流。
扫描 PDF 和 PPTX 幻灯片内嵌图片可使用已安装、已校验的本地 OCR 模型识别;
PPTX 的当前能力和边界见 [PPTX 图片流程](#75-pptx-图片流程)。
现有局限:
- 纯扫描 PDF 没有文本层时无法提取内容;
- DOC、XLS、PPT 等旧版二进制 Office 格式不支持;
- Office 解析主要提取文字,不能完整保留表格、公式、图表和版面;
- 聊天附件和知识库直接调用底层解析函数,缺少可配置的统一工作流;
- 没有本地 OCR 模型状态、真实解析测试和按场景策略。
- DOCX、XLSX 内嵌图片尚未接入 OCR;
- Office 格式转换尚未接入;HTTP PaddleOCR-VL 已接入,部署高级模型能力需要使用文件测试。
聊天文档原件、解析正文和提取图片现保存为会话文件资产,数据库记录草稿、消息和队列引用。
已有历史附件若未保存原件,不会被升级自动补齐。第 13 节保持需求定义,当前实现与证据分别见
[生产实现](./implementation.md)和[进度](./progress.md#2026-09-19-实现与验证)。
## 3. 产品原则
@@ -45,17 +52,54 @@ PDF 不是所有文档唯一的中间格式。解析应同时保留:
| 场景 | 默认预设 | 行为 |
| --- | --- | --- |
| 聊天附件 | 自动解析 | 优先快速提取,文本不足时按需 OCR,完整解析文本加入当前请求 |
| 聊天文档附件(PDF、Office 等) | 自动解析 | 优先快速提取,文本不足时按需 OCR,完整解析文本加入当前请求 |
| 知识库导入 | 完整索引 | 完整解析、按页或工作表定位、按需 OCR、分块与索引 |
| 扫描文档 | OCR | 页面渲染、文字识别、置信度与定位保留 |
| 表格分析 | 语义优先 | 单元格和值优先,PDF 或图片补充图表与打印布局 |
| 高保真审阅 | 视觉增强 | 原生解析、页面渲染、OCR 或视觉理解合并 |
PDF、DOCX、PPTX 和 XLSX 的解析结果保留章节定位及全文,不在附件入库时截断为
256 KiB,也不以富化后 Prompt 的 1 MiB 上限阻止发送。本地选择与消息通道附件使用
256 KiB,也不以富化后 Prompt 的 1 MiB 上限阻止发送。本地选择、文件粘贴与消息通道附件使用
同一文本保存路径;原文件大小、解析器和 12 MiB 附件暂存容量仍按现有规则检查,超限时
明确报错,不能用截断后的内容冒充全文。模型上下文窗口仍由所选 Runtime 处理。
#### 3.2.1 聊天附件入口
用户可点击附件按钮选择文件,也可在文件管理器中复制一个或多个文件后,在聊天输入框
按 `Ctrl+V` 添加。macOS 使用系统粘贴快捷键 `Command+V`。粘贴文件不替换草稿文字,
不自动发送;普通文本仍直接插入光标位置,系统截图仍作为图片附件添加。
本地文件选择与粘贴使用相同的格式及容量规则:
| 内容 | 支持范围 | 原文件大小上限 |
| --- | --- | --- |
| 文本、代码和配置 | C/C++、CSS、CSV、Go、HTML、Java、JS/JSX、JSON、日志、Markdown、Python、Rust、SQL、TS/TSX、TXT、XML、YAML | 256 KiB |
| 图片 | JPEG、PNG、WebP | 12 MiB |
| 文档 | PDF、DOCX、PPTX、XLSX | 20 MiB |
每条消息最多 8 个附件,Main 最多暂存 16 个上下文项目,合计不超过 12 MiB。
图片转换和文档解析后的内容计入暂存容量。一次粘贴超过 8 个文件时拒绝本次粘贴并提示;
批次本身未超限、但加上已有附件超过 8 个时,只保留剩余位置可容纳的附件并提示。
文件读取和解析期间显示文件名、处理阶段及批次序号,阻止重复文件导入和发送。
不支持的扩展名、目录、超限文件、读取或解析失败会使本批次回滚;已有附件与草稿保留,
错误显示在输入区。剪贴板中的虚拟文档若没有本地路径,提示先保存到本地后添加;
无本地路径的截图继续使用图片粘贴入口。
#### 3.2.2 纯图片与文档的路由(规划)
普通聊天图片默认作为图片输入发送,跳过文档解析与 OCR;是否可发送取决于本次实际生效的
模型和 Runtime 图片输入能力。下表定义提示、显式 OCR 操作和原图持久化的验收要求,
各 Runtime 的真实验证范围见进度文档。
| ID | 产品要求与验收 |
| --- | --- |
| FR-I1 | 普通图片附件默认直接送入支持图片输入的模型,不自动 OCR。当前模型或 Runtime 不支持时明确提示,用户可选择“提取图片文字”或切换支持图片输入的模型;不得静默转成 OCR 文本并声称已发送原图。 |
| FR-I2 | 用户明确选择“提取图片文字”时,使用文档解析中已保存的 OCR 来源与选项,即使聊天模型支持图片输入也执行 OCR。成功后附件显示“原文件名 · 提取文字”和“仅发送提取文字,原图保留”,本次仅发送文字;可显式改回发送原图并重新校验能力。失败、取消或未提取到文字保持原发送方式及草稿,不隐式切换 Provider。 |
| FR-I3 | 纯图片附件长期保留原始图片及其会话、消息、附件归属,重启后可查看。请求可按实际格式或容量限制生成缩放、转码副本,但不得覆盖原图。纯图片不强制生成 parsed.md 或解析清单;显式 OCR 才产生关联的解析结果。持久化完成后才接受发送或保存队列引用,删除按有效引用清理,草稿引用的资源也须保留。 |
| FR-I4 | PDF、Office 的原生解析、按需 OCR 与纯图片直传分开。知识库中的图片文字提取和索引按知识库工作流决定,不随聊天模型视觉能力改变;这不代表当前已支持独立图片导入或图片向量检索。 |
| FR-I5 | 聊天与知识库解析预览提供图片多选,默认零选择;“添加所选图片”将图片复制为明确目标会话草稿的独立视觉附件,保留文档、页码及图片来源,不自动发送或附加全部图片。区分待添加勾选与已在草稿,重复添加同一来源图片幂等。父文档文本仅加入一次,父文档与已复制图片独立移除;知识库删除或重解析不影响会话副本。无有效会话时提供选择或新建会话流程。添加与发送共用既有附件数量、容量及有效模型/Runtime 校验,超限整批拒绝并保留选择,不静默丢图或抽样。 |
### 3.3 本地优先
- 文本层和本地 OCR 均在设备上处理;
@@ -87,18 +131,20 @@ OCR 模型区沿用语音模型管理模式:
- “打开模型仓库”直接显示在 OCR 模型卡片右上角,并使用当前下载源对应仓库;
- 模型操作即时生效,解析策略仍通过分类页头的“保存设置”提交。
### 4.1 第一阶段字段
### 4.1 设置字段
- 聊天附件预设:`auto`、`fast-text`、`high-fidelity`;
- 知识库预设:`complete-index`、`fast-index`、`high-fidelity`;
- PDF OCR 策略:`auto`、`always`、`disabled`;
- OCR 来源:第一阶段固定为 `local`,远程服务入口禁用;
- OCR 来源:`local` 或 HTTP PaddleOCR-VL,旧设置仍使用本地;
- 本地 OCR 模型:`pp-ocrv6-tiny`、`pp-ocrv6-small`、`pp-ocrv6-medium`;
- 单文档最大页数;
- OCR 并发数;
- 单页超时。
OCR 来源使用互斥选择。本地模型选中后才显示模型下拉列表、按需下载、导入和本地 OCR 参数;远程服务计划接入 MinerU、PaddleOCR-VL 等接口,第一阶段保持可读但禁用。来源选择本身就是用户的明确决策,不再显示额外的“隐私与云端处理”授权区。
OCR 来源使用互斥选择。本地模型选中后才显示模型下拉列表、按需下载、导入和本地 OCR 参数;
远程显示 PaddleOCR-VL 地址、认证及高级选项。两套配置分别保留,来源选择本身就是用户的
明确决策,不再显示额外的“隐私与云端处理”授权区。MinerU 不在本次范围。
## 5. 架构
@@ -113,7 +159,7 @@ OCR 来源使用互斥选择。本地模型选中后才显示模型下拉列表
└─ ParsedDocument merger
```
`DocumentParsingService` 是唯一场景入口:
`DocumentParsingService` 是文档解析的统一场景入口;普通聊天图片直传不经过此接口:
```ts
type DocumentParsingPurpose = 'chat-attachment' | 'knowledge-index'
@@ -132,7 +178,8 @@ type DocumentParsingService = {
## 6. 统一结果
第一阶段兼容现有 `ParsedDocument`,并逐步扩展:
现有 `ParsedDocument` 文本结构如下;第 13 节要求补齐图片、解析配置与持久化引用,具体
契约由 [HTTP 技术设计](./http-paddleocr-vl-technical-design.md#结果与资源)维护:
```ts
type ParsedDocument = {
@@ -254,6 +301,32 @@ ZIP 根目录包含模型文件和 `goodbuddy-model.json`。清单格式为 `goo
受密码保护、损坏或超限的 PDF 不得进入 OCR。
### 7.5 PPTX 图片流程
自动解析、完整索引和高保真工作流保留幻灯片原生文字,同时对幻灯片直接引用的
内嵌 PNG、JPEG、WebP 图片运行本地 OCR。即使幻灯片已有标题或正文,也不会因此
跳过图片中的文字。快速文本、快速索引工作流不运行 OCR;图片型 PPTX 在这些模式下
明确提示当前工作流未启用 OCR。
按演示文稿中的幻灯片顺序返回结果,图片识别结果标注 `幻灯片 N · 图片 M`,
保留页码和识别置信度。OCR 页数限制计算含图片的幻灯片数,而不是图片总数;
图片顺序处理并复用已加载模型,沿用现有超时、取消和输出限制。
这不是 Office 渲染或转换:不识别母版、版式中继承的图片,不重建裁剪、旋转或遮挡后的
完整画面,不把演讲者备注当作幻灯片正文,也不下载外链图片。其他内嵌图片格式在
需要 OCR 时明确报错。自动聊天解析可在 OCR 失败时保留可靠原生文字并附带警告;
图片型文件没有可靠原生文字时仍失败,知识库和高保真工作流不以此回退掩盖 OCR 失败。
### 7.6 OCR 空闲资源回收
OCR 队列清空且没有正在处理的请求后,保留 Worker 60 秒,以复用连续文档的模型。
期间收到新请求立即取消回收计时;请求完成、队列再次空闲后重新计时。空闲到期后
销毁 Worker,释放其模型会话和 WASM 运行环境;下一次请求按需重新加载已安装模型。
回收不会删除模型文件或已经解析的附件文字,不打断正在识别、初始化或排队的任务。
取消、超时和服务关闭仍按原有路径立即清理。操作系统显示的进程内存不保证立刻恢复到
启动值,不能仅凭一次内存快照判断是否发生泄漏。
## 8. Office 与转换
本节只定义 Office 文件的语义解析和格式转换,不提供可视化编辑器、编辑会话、选区、撤销或
@@ -311,33 +384,20 @@ DOC、XLS、PPT 通过 `DocumentConversionProvider` 转换:
`auto` 工作流可以从 OCR 回退到可靠的原生文本,但不能把空结果标记为成功。知识库导入失败时保留来源和可重试上下文。
## 11. 实施阶段
## 11. 实施范围与顺序
### 阶段一
本地 OCR 基线与已验证能力见第 2、7 节及聊天附件技术说明。第 8 节的旧 Office 转换和
扩展语义解析保留为独立规划,不因 HTTP 接入而承诺已经实现。
- 新增文档解析设置分类和持久化契约;
- 建立 `DocumentParsingService`,供聊天和知识库共用;
- 将无文本 PDF 识别为可触发 OCR 的明确状态;
- 接入 PP-OCRv6 Tiny、Small、Medium 的双来源下载、校验、ZIP 离线迁移、删除与
WASM Worker;
- 实现真实文件测试和六平台验证入口。
### 阶段二
- 增强 DOCX、XLSX、PPTX 语义结构;
- 实现按页混合文本层与 OCR;
- 增加版面、表格和阅读顺序。
### 阶段三
- 增加 LibreOffice 和 API 转换 Provider;
- 支持 DOC、XLS、PPT;
- 增加 MinerU、PaddleOCR-VL 等远程 OCR 服务连接配置;
- 增加高保真工作流和解析结果预览。
第 13 节及 FR-I1 至 FR-I5 是本次约定的完整功能范围:HTTP Provider、设置与高级选项、
三入口预览、文档和原图持久化、显式图片 OCR、文档选图加入会话及发送、失败恢复与清理。
实施依赖顺序见 [技术设计](./http-paddleocr-vl-technical-design.md#实施与验证顺序),不构成
分批产品交付或推迟选图发送的许可。产品完成须覆盖 [完整验收矩阵](./ui-design.md#8-完整验收矩阵)。
## 12. 验收
- 同一份扫描 PDF 可从聊天附件和知识库得到一致的逐页文本;
- 图片型 PPTX 在自动解析下使用已配置的本地 OCR,并保留幻灯片来源定位;
- 文本型 PDF 在 `auto` 模式下不运行 OCR;
- 本地 OCR 在六个平台和两种架构上完全离线运行;
- 模型文件损坏时拒绝加载并显示可恢复错误;
@@ -348,6 +408,79 @@ DOC、XLS、PPT 通过 `DocumentConversionProvider` 转换:
- 语音和 OCR 模型可在联网设备导出 ZIP,并在离线设备导入后完成真实推理;
- 路径穿越、未知条目、错误模型 ID、篡改文件和超限 ZIP 均被拒绝;
- 超页数、超时、取消和关闭不会留下运行任务;
- OCR 空闲 60 秒后释放 Worker,连续请求复用模型,回收后的下一次解析可重新加载模型完成;
- 测试解析不会创建聊天消息或知识库文档;
- 选择本地模型时没有任何文档上传;
- 文档中的提示词不会改变系统、模式或工具权限。
## 13. HTTP PaddleOCR-VL 扩展(规划)
状态:已接入生产解析、设置、预览及资源流程。部署实测、验收层次与未覆盖项见
[进度](./progress.md#2026-09-19-实现与验证),行为规则见 [逻辑设计](./logic-design.md)。
用户需要使用已有内网 OCR 服务解析扫描件,并保留插图、表格和来源页码;文档中的页眉、
页脚有时包含合同编号或说明,不能只提供服务默认过滤结果。
| ID | 产品要求与验收 |
| --- | --- |
| FR-H1 | 在文档解析设置中明确选择本地 OCR 或 HTTP PaddleOCR-VL,保存服务地址和所需认证。HTTP 路径无需安装本地 OCR 模型;已有配置继续选本地来源。 |
| FR-H2 | 聊天文档附件、知识库导入和测试解析使用同一来源及解析设置。PDF 原生文本优先、快速模式禁用 OCR、高保真模式全页 OCR 的既有规则继续适用。PPTX 仍按直接内嵌图片识别并保留幻灯片定位。纯图片路由及显式 OCR 遵循 FR-I1、FR-I2。 |
| FR-H3 | 默认保存 HTTP 解析返回、被结果引用的插图字节及页码、Markdown 关联,重开后可查看;同名图片键仅在页内有效,跨页不得覆盖。支持 Markdown 图片及 HTML img、table 的预览。测试产物不进入聊天或知识库。图片缺失、超限或无法保存必须显示具体警告或失败,不能将带断图的结果表示为完整成功。 |
| FR-H4 | 高级设置允许保留或排除页眉、页脚、页码、脚注等标签,也允许恢复服务默认值。配置方向分类、去扭曲、版面检测、图表、印章、图片块 OCR 和 Markdown 格式选项时,应展示部署是否仅声明接口、是否经过真实样例验证。 |
| FR-H5 | 可选跨页表格合并与标题层级调整。启用后保留原始页面来源和图片引用;没有观察到合并效果时不得宣传为已支持跨页表格恢复。 |
| FR-H6 | HTTP 错误、超时、取消和图片持久化失败可诊断、可重试。取消后不再发起后续页或重组请求,不自动切换服务或本地模型。无文字且无有效图片时失败;仅有图片时明确标为仅图片结果,可预览和选择图片但不能发送空文本附件。部分解析不能标成完整成功。 |
| FR-H7 | 测试解析能核对文字、表格、图片数量、页码、过滤设置与耗时。服务没有置信度时显示“未提供”,不补成 1 或 0。 |
| FR-H8 | 已发送的聊天文档附件须长期保存原文件、解析文本和提取图片;应用重启后能从历史附件重新查看,成功持久化后移动或删除外部原文件不影响查看。资源属于会话,存放于应用 userData 管理的文件资源中,数据库保存归属和引用关系,不依赖 Runtime 项目工作目录。 |
| FR-H9 | 未发送文档附件使用应用管理的暂存资源;移除、导入失败或取消时仅清理本次无有效引用的暂存,保留其他附件和草稿。发送被接受及队列记录持久化之前完成会话归属和资源持久化,恢复队列不能依赖已丢失的内存 contexts 或外部原路径。删除会话清理其独占资源,仍被草稿或其他有效对象引用的资源须保留;不得按临时目录名在启动或退出时一律删除。 |
| FR-H10 | 知识库解析文本与 chunks 继续入库;图片归知识库文档所有,独立于聊天会话。managed 与 reference 来源的解析派生文件均由应用管理,不写入外部原文件目录。重解析或同步只有在新结果及必要资源成功保存后才替换旧结果;失败或取消保留旧结果,删除文档或来源时清理无引用的派生资源。 |
| FR-H11 | 每次解析使用启动时已保存的 Provider 和生效选项,并随结果记录供详情查看,不保存凭据副本。修改配置不改写已保存结果;草稿或知识库再次解析时才采用新配置,成功才替换。历史消息和已入队输入保持已接受内容,重新解析通过添加到草稿完成,不改写原消息或队列。失败不得损坏旧文本、图片或来源关联。 |
| FR-H12 | 设置中的来源切换保留本地与远程各自配置,高级选项默认折叠;测试前须保存设置。测试、聊天附件和知识库详情共用正文、图片、详情预览,支持页码互跳、打开原件、查看保存时配置、失败及部分结果。聊天卡显示解析进度、取消、重试及完整程度;界面支持窄容器、键盘和辅助技术。具体交互由 UI 设计维护。 |
### 13.1 范围与存储边界
本轮已实现配置、HTTP 适配器、数据库引用、共享预览和会话选图链路。
具体文件布局、schema 与已验证行为由[生产实现说明](./implementation.md)维护。
FR-H8 至 FR-H12 的资源与预览要求针对已解析文档及其产物,也适用于本地或原生解析结果,
不要求所有附件都生成解析文件。纯图片遵循 FR-I1 至 FR-I3;图片保存以解析器实际返回的
内容为准,不据此承诺本地解析新增插图提取能力。
聊天文档的原文件、解析文本和图片是应用持有的文件资产,数据库负责关联会话、消息、附件
和资源。目录与文件名规划由
[技术设计的结果与资源](./http-paddleocr-vl-technical-design.md#结果与资源)维护。历史数据若未保存
这些内容,不能显示为已有完整解析资源;本轮不要求恢复缺失历史内容或批量重解析。
不接入 MinerU、旧 Office 转换、
DOCX/XLSX 图片 OCR、图片向量检索、服务安装管理或 DOCX 导出。服务接受 PNG 的探测结果
也不改变聊天截图目前作为图片附件的行为。HTTP 上传路径不因 Ask/Execute 模式改变;
沿用第 3.3 节的来源选择与 Main 凭据边界,不增加逐文档授权。
不增加完整配置快照、结果版本历史或通用恢复系统;仅记录本次结果需要的来源、选项和资源关系。
### 13.2 配置与预览行为
界面的信息架构、控件、文案、状态、响应式和无障碍由 [UI 设计](./ui-design.md)唯一维护。
来源与保存规则见其第 2 节,共享正文/图片/详情预览见第 3 节,文档选图加入会话见第 4 节,
纯图片 OCR 与文档卡片见第 5 节,知识库交互见第 6 节。测试资产在结束预览后清理,不提供
加入聊天或知识库操作;正式文档中的图片使用 FR-I5 的显式选择路径。
### 13.3 用户验收
以下为产品验收条件;已执行范围与尚未执行项在进度中逐项注明:
| 场景 | 可观察结果 | 需求 |
| --- | --- | --- |
| 发送扫描文档后关闭应用,移动或删除外部原文件,再启动并打开历史附件 | 可打开应用保存的原文件,查看原解析正文、表格、图片和页码,无需重新 OCR | FR-H3、FR-H8 |
| 文档消息进入发送队列后重启,再恢复发送 | 附件与原解析内容仍可用,不要求重新选择外部文件;取消生成不删除已保存附件 | FR-H8、FR-H9 |
| 草稿移除附件、取消解析或批次导入失败 | 本次未提交资源被清理;先前附件、草稿和已发送内容仍可用 | FR-H9 |
| 删除一段含文档的会话,再查看其他会话或知识库中的关联文档 | 被删会话独占资源被清理,其他有效引用的原文件与图片仍能打开 | FR-H9、FR-H10 |
| 两页返回同名图片键,并使用 HTML img 和 table | 两页图片各自对应正确页码,表格可读,正文引用不串页、不出现未说明的断图 | FR-H3 |
| 切换本地与远程来源,修改页眉选项并测试 | 各来源配置保留;未保存时提示保存;保存后测试采用新值,三个结果视图可相互核对 | FR-H1、FR-H4、FR-H7、FR-H12 |
| 已保存结果后修改 Provider 或过滤项,再查看旧结果 | 旧正文与图片不变,详情仍显示原解析所用来源与选项;草稿或知识库再次解析成功后才替换,历史消息重解析进入新草稿 | FR-H11 |
| 对已有知识库文档重解析,发生服务错误、缺图或取消 | 旧正文、chunks、图片及页码仍可使用;重试成功后显示新结果并清理无引用旧资源 | FR-H6、FR-H10 |
| 分别导入 managed、reference 来源,随后删除来源 | 派生图片均在应用管理范围;删除后无引用派生资源清理,外部原文件不被派生资源清理误删 | FR-H10 |
| 从聊天文档选两张插图并重复点击添加,再移除父文档 | 草稿只增加两份独立图片附件;父文档在场时正文仅一次,移除后图片仍可发送 | FR-I5 |
| 从知识库选图,选择或新建会话后添加;随后重解析或删除源文档 | 仅明确选定会话的草稿变化,会话图片及已发送来源说明仍可用 | FR-I5、FR-H10 |
| 视觉模型配合不支持图片的 Runtime,或恢复队列时能力已变化 | 阻止该请求并保留附件,提供切换、提取文字或移除的恢复路径,不静默降级 | FR-I1、FR-I5 |
| 图片提取文字成功、失败、取消及再次改回原图 | 成功后明确仅发文字且原图可开;其余状态不丢原图,改回后重新校验图片能力 | FR-I2、FR-I3 |
完整验收覆盖见 [UI 验收矩阵](./ui-design.md#8-完整验收矩阵)。清理还需核对应用资源目录和
数据库引用;仅凭界面中看不到附件不能证明文件已删除。
@@ -0,0 +1,360 @@
# HTTP PaddleOCR-VL 实施与验证
## 状态
当前状态以 [2026-09-19 实现与验证](#2026-09-19-实现与验证)、[附件操作布局复验](#2026-09-19-附件操作布局复验)、[全局浮层复验](#2026-09-19-全局浮层复验)及[空正文与草稿恢复修正](#2026-09-19-空正文与草稿恢复修正)为准。
以下至“菜单核对检查”的内容是实施前历史记录,保留原日期与原调用数。
2026-09-18:完成规划文档和独立 HTTP 探索脚本。FR-H1 至 FR-H12 均未完成生产实现,
现有设置、解析器、数据库、界面与 Agent 未因本次任务改变。下一步实现顺序见
[技术设计](./http-paddleocr-vl-technical-design.md#实施与验证顺序)。
同日按用户确认补充 [PRD](./prd.md#131-范围与存储边界) 的会话文件资源、数据库关联、
生命周期及配置预览验收,并修正技术设计中复用聊天内联图片存储的建议。本轮仅修改文档,
未新增服务调用;此前探测记录和下述调用计数保持原意。当前聊天解析文本仍用于临时 contexts
与队列,长期附件保存、重启预览和队列资源恢复均待实现与产品验收。
2026-09-18 图片路由文档核对:新增 FR-I1 至 FR-I5 及对应场景、逻辑,目录规划见
[结果与资源](./http-paddleocr-vl-technical-design.md#结果与资源)。源码核对确认
`ContextManager.storeImage/enrichRequest` 已走图片请求且会转码 JPEG,队列序列化保存上下文;
这不等于原图文件持久化。`ModelRuntime` 有图片能力检查,IPC 的图片工具路径另有引用处理,
尚不能宣称所有 Runtime 已提供统一的不支持提示与 OCR 选择。`DocumentParsingService`
当前单独处理 PDF、PPTX,其他格式转原生解析;显式纯图片 OCR 入口仍待实现。
`KnowledgeService.managedRoot` 由 Main 配为 userData/knowledge,受管原件按库、来源保存。
本次仅更新既有文档,保留原有探索脚本和历史证据,未调用 OCR 或聊天模型服务。
2026-09-18 完整交互设计:新增 [UI 设计](./ui-design.md),同步 FR-I5、图片 OCR 发送方式、
历史/队列输入冻结及对应故事、逻辑、技术契约。设置、三入口预览、文档选图到会话、无有效
会话时的选择/新建、独立副本、幂等与移除、共享附件限制及完整验收矩阵均已写入设计。
这些是文档完成项,FR-H1 至 FR-H12、FR-I1 至 FR-I5 的新增生产能力仍未完成。
源码核对:设置实际使用 `SettingsCategoryHeader` 和 Portal 诊断框;`App.tsx` 提供聊天
reading 与知识库 master-detail 壳层,KnowledgeWorkspace 不再嵌套壳层;共享
`PageTabs`、`SegmentedControl`、焦点辅助函数可复用。现有 MarkdownRenderer 的
`skipHtml` 不能直接覆盖 OCR 混合 HTML 图表,技术设计已明确适配工作。附件 8 项、Main
16 项/12 MiB 规则已存在但部分常量仍分散,设计要求集中复用,没有新增选图配额。
剩余实现与验证包括 HTTP 和认证、高级选项及有效跨页样例、资产与数据库引用、预览与
选图生产接线、本地/远端实际图片输入、取消重启和删除清理。按
[技术实施顺序](./http-paddleocr-vl-technical-design.md#实施与验证顺序)推进,不代表允许
分批缩减产品范围。本轮未修改产品代码或探索脚本,未新增网络请求或模型调用。
## 菜单核对
2026-09-19 对照当前工作区源码检查入口,未运行 Electron;以下为源码事实,不是新增功能验收。
目标规则由 [UI 设计](./ui-design.md#11-入口与职责)维护。
| 入口 | 当前实现与证据 | 与完整目标的差距 |
| --- | --- | --- |
| 设置 → 文档解析 | [分类定义](../../../src/renderer/src/settings-categories.ts)包含 `document-parsing`;[解析设置](../../../src/renderer/src/DocumentParsingSettingsSection.tsx)提供本地模型管理、场景测试及纯文本诊断预览 | HTTP 配置、正文/图片/详情共享预览尚未接入;当前没有可用远程来源切换控件 |
| 应用中心 → 本机推理监控 | [应用定义](../../../src/renderer/src/ApplicationCenter.tsx)仍为四项;[监控](../../../src/renderer/src/LocalInferencePage.tsx)显示本地服务并加载/释放 OCR | 不承担远程 OCR 配置或管理;关闭监控入口不等于关闭解析能力 |
| 监控 → 模型与连接设置 | [IPC](../../../src/main/ipc.ts)的 `localInferenceOpenSettings` 只发出无分类的 `settingsOpen`;[App](../../../src/renderer/src/App.tsx)仅打开设置,[SettingsPanel](../../../src/renderer/src/SettingsPanel.tsx)默认分类为 `runtime` | 文案与默认落点不一致,未直达模型或文档解析;本轮仅记录,产品跳转未修复 |
| 聊天附件 | App 的附件按钮直接选文件;当前卡片显示名称、容量、缩略图或图标、文本 tooltip 和移除 | 尚无逐附件更多菜单、显式 OCR、解析结果预览、重解析或文档选图 |
| 知识库文档 | [DocumentsView](../../../src/renderer/src/KnowledgeWorkspace.tsx)提供任务、打开来源、失败文档重试和分块管理;来源行另有同步,库页头另有“用于当前对话” | 解析文档详情、成功文档重解析及选图须新增,不能把分块管理 Modal 当作已有完整解析预览 |
| 新建选图目标会话 | App 的 `startNewConversation` 返回布尔值,拒绝消息通道项目,并在复用空会话或新建后切到聊天 | 预览保留、目标 ID 取得及返回确认仍需接线;直接调用不满足 US-I9 |
本轮更正文档中“既有文档详情”的描述,补齐菜单矩阵及 US-H16 / A-15;队列修改统一先
恢复到草稿,显式图片 OCR 的上传说明与快速模式区别保持一致。根 UI 规范与双语功能清单
同步标明本轮范围;本地 OCR 仍为现有能力,HTTP 和新附件流程仍为规划。
未修改产品代码、探索脚本或既有应用排序,OCR、文本及视觉模型调用均为 0。
## 2026-09-19 实现与验证
HTTP PaddleOCR-VL 已接入 `DocumentParsingService` 的生产 PDF/PPTX 路径,设置支持来源切换、
Bearer 加密凭据、过滤、三态参数、跨页整理及连接诊断。新增会话文件资产、数据库归属、
可恢复草稿、历史预览、图片 OCR、选图独立副本、队列查看/恢复和解析中断记录。
知识库 managed/reference 派生资源单独保存,重解析成功才替换正文、chunks 和资源引用。
具体机制及设计差异见 [implementation.md](./implementation.md)。
### 验证层次
本轮 Windows x64、Electron 43.2.0、Node 24.18.0,使用当前工作区源码构建到
`node_modules/.document-e2e-out`,不覆盖其他运行中的 `out`,不改正式 profile。
测试保留 context isolation、sandbox 和禁用 Node integration,未改可信 sender 校验。
完整 App 的文件选择对话框由驱动返回合成 fixture;其余设置、解析、存储、预览和输入框
使用生产组件、preload 与 IPC。知识库批量生命周期部分通过生产 preload 调用,单独标注。
原始报告位于获准临时目录 `C:/Users/jiang/AppData/Local/Temp/opencode`。下表目录为该目录
的子目录;这些是本机取证路径,不是产品默认值。源码没有写入测试端点。
| 场景 | 层次及结果 | 证据目录 |
| --- | --- | --- |
| 自动/高保真扫描 PDF | 生产服务直调,无 OCR Broker 和本地权重;两页正文、两张内联插图,自动模式逐页渲染 | `document-production-XERonK` |
| 设置、测试、聊天附件、选图、显式 OCR | 完整 App;保存 HTTP 设置,HTML 表格、图片、测试隔离、文档选图、父附件独立移除、普通图片提取文字 | `document-app-GXKuZv` |
| 草稿及 OCR 结果重启 | 完整 App;两份草稿恢复、解析正文重开;重启没有 OCR 请求 | `document-app-MWn5Zn`、`document-app-GXKuZv` 的 restart 报告 |
| 实际输入框发送 | 完整 App;文档选图和 OCR 文本从 Composer 发送,回答 `Yellow`,草稿清空,历史预览存在 | `document-app-fuFTEY` |
| 已发送附件重启 | 完整 App;历史 OCR 正文包含合成金额,未重新解析 | `document-app-fuFTEY/restart-report.json` |
| managed/reference 导入、失败重解析、成功替换、删来源 | 当前 App 的生产 preload/IPC;两种模式各有两图四 chunks,故障注入为关闭的本机端口;失败保留 resultId/chunks,成功更换,删除不损坏会话图片 | `document-app-CBANUC`,前次 `document-app-n65dt4` |
| 解析中断恢复 | 完整 App 导入;驱动在 HTTP 发出前暂停并终止应用,重启显示中断,手动重试成功;未把暂停请求算作 OCR 调用 | `document-app-YmDUR9` |
| 队列能力失配、重启、恢复草稿 | 生产 IPC;首次暂扣 Renderer dispatch 以固定未执行队列状态,重启恢复真实分发;模型能力设为不支持图片,队列保留;附件恢复且文字合并,不重复排队 | `document-app-YyagTN` |
| 直连实际图片消费 | 当前生产 Main/IPC/ModelRuntime;观察 Provider 请求包含图片并收到 `Yellow` | `document-app-VmNZIG` |
| 托管 OpenCode 实际图片消费 | 当前 Desktop 至共享 Linux x64 Host;使用已有 Host 凭据和固定主机密钥,独立 `/root/tmp/goodbuddy-document-*` 目录;透传代理只计数与核对图片,Provider 返回 `Yellow` | `document-app-gzC5ZV` |
| 托管 OpenCode 文档选图发送 | 生产导入、持久化选图、移除父文档后发图;Provider 请求有图,回答 `Yellow`,测试目录回收 | 最终 `document-app-iZSxV8`,前次 `document-app-zqnBBt` |
| 跨页表格效果 | 生产解析服务;两页连续设备发票表从两个 HTML table 变为一个,32 个条目各出现一次;分组来源页为 `[1,2]` | `document-production-Twr0N4/report.json`、`table-check.json` |
| 图片文件与引用回收 | 真实 SQLite/文件测试;原字节、重启、共享消息引用、队列与草稿独立、数据库无原图 Base64、解析中断原件保留 | `conversation-attachment-storage.test.ts` |
| HTTP 认证及失败 | 真实 loopback HTTP fixture;生产客户端 Bearer、401、取消和 health/OpenAPI;公网/内网认证部署未测 | `http-document-ocr.integration.test.ts` |
HTTP OCR 本身只运行在 Desktop。未修改 Agent daemon、传输协议或远端 OCR 实现;远端验证
使用当前 Desktop 接入既有 Agent 图片链路,不将此次结果记作新 Agent 包发布或安装验收。
### 界面取证
`document-app-GXKuZv` 的视觉复验覆盖浅/深主题、约 1279×800、959×720、719×640、
680×561 CSS 像素及 200% 缩放下约 639×400。原生最小窗口限制使请求的 640×420 实际为
680×561,不能写作实际验证了 640×420。十组测量中页签未收缩、主体可滚动、操作区可见,
无横向溢出。原生 `Right` 和 `Escape` 通过;最初驱动用 `ArrowRight`,未产生 Electron
原生键事件,修正后复验。已打开审阅浅色窄窗口和深色 200% PNG。
诊断初次完整 App 截图发现 HTTP 被显示为“本地 OCR”、附件按钮继承固定 28px 宽度,
均已修正。测试菜单统一到分类页头,诊断关闭恢复测试按钮焦点。知识库使用详情/返回列表,
普通历史和队列分别只读;队列修改入口为“恢复到草稿”。
隔离生产构建启动有 4 条打包字体 data URI 被现有 CSP 阻止的控制台记录,本轮未修改字体或
CSP。截图使用系统字体回退可读;此项不能算作无控制台告警。
### 自动检查
最终完整 `npm test`:399 个文件通过、9 个跳过;4,779 项通过、67 项跳过,耗时 816.55 秒。
最新 `npm run typecheck`、`npm run lint` 通过。隔离 `electron-vite build` 通过,保留既有
workspace-management 动静态导入警告。没有提交或推送。
首次全仓运行有 9 项失败,其中 8 项是新增参数/schema 后的旧断言,1 项为并行画布导航的
Quill 加载失败;最终完整运行均已通过。中途一轮超过 240 秒工具期限,未产生完成报告,
不计为通过。其他聚焦失败包括新增测试缺少 cleanup、旧附件失效行为断言、诊断焦点返回,
均保留修正后的回归。
队列验证发现提前保存用户消息导致重启删除未执行队列的缺陷,已修正并复验。另有测试驱动
误读升级页首次 load、未等待菜单挂载、文本换行比较器使用了转义字符串等失败;这些没有
被记作产品已通过,最终队列证据采用 `document-app-YyagTN`。
### 调用计数与边界
按保存报告可核对 **28 次远程 layout、1 次 restructure、0 次远程 health/OpenAPI GET**。
另有一次表格测试被工具超时终止:设置已进入第二次 layout 阶段,可以确认它包含 2 次
layout,是否已经发出 restructure 缺少 dispatch 日志。故本轮已知 layout 合计 **30 次**;
restructure 可核对 1 次,另最多 1 次状态不明。未将 2026-09-18 探测计入本轮。
模型请求报告中可观察到 7 次 Provider dispatch,包含早期驱动提前退出/超时的 3 次。
`document-app-zMeC7X` 的远端调用不经过 Desktop fetch,另从 Agent 的操作 ID 记录确认
1 次已完成;完整 Composer 发送 `document-app-fuFTEY` 使用独立 Runtime,桌面 fetch 计数
不覆盖其内部 Provider 请求,已收到真实 `Yellow`,该次至少 1 次调用。
因此本轮可精确核对 **8 次模型请求,另有至少 1 次完整 UI Runtime 请求**;早期队列驱动
超时且缺少报告的轮次不能给出完整 Provider attempt 数。后续驱动在 dispatch 时单独写计数,
不以输入框提交数代替实际调用数。
未执行 macOS、Linux Desktop/arm64、本地 OCR 各档位、Continue/DeepSeek Harness 的本轮真实
图片消费;未验证方向分类、去扭曲、印章、图表和公式选项的效果,也未验证真实磁盘满场景。
跨页表格结论只覆盖上述连续表格,标题重定级没有独立有效样例。完整 App 的原生浏览器
遮挡没有在本轮单独实测,继续使用共享 Modal 遮挡机制及其既有回归。
上述未测项保留在验收矩阵,不能将 A-01 至 A-15 的全部分支写成已通过。
## 2026-09-19 附件操作布局复验
对应 FR-H12。检查实际工作区后保留已有 OCR、存储、预览与队列实现,修正草稿、历史消息
及队列预览的操作布局。查看结果与更多操作改为同排 32px 图标按钮,草稿移除使用同尺寸
叉号;保留含附件名的可访问名称、悬停提示和键盘聚焦说明。文件名省略,状态与大小可换行。
导入中的取消按钮也改为图标,进度卡使用三列,避免取消文字挤入固定宽度按钮。
文件名核对覆盖 `ContextManager` 导入、显式 OCR、重解析、`ConversationAttachmentStorage`
和结果清单。源码将 UUID 用于资源标识,显示名来自导入文件名;已有存储测试验证原名称在
重启后保留。本轮没有截图对应的原文件及记录,无法确认该 UUID 名称的来源,未作猜测性改名。
`AttachmentActions.test.tsx` 验证图标名称、预览关闭和菜单 Escape 焦点恢复、菜单方向键
及队列只读动作。`tests/attachment-layout.electron.test.ts` 加载生产 `ChatMessageRow`
与样式,在 Windows Electron 中验证浅/深主题、1000/720/360px 宽度及 200% 缩放;两个
操作均为 32px、同排,长文件名截断,卡片高度低于 110px,无页面横向溢出。原生 Tab、Space
打开菜单及 Escape 返回通过。此用例替代设置快照接口,不调用 OCR,不作为完整 App 解析验收。
新增 Electron 用例初次受系统缩放的亚像素取整影响,测得根容器宽 360.57px;驱动固定
device scale factor 后复验。驱动 Enter 未产生按钮激活,改用原生 Space 后菜单流程通过。
这些驱动失败未记作产品通过,也未通过修改页面溢出样式绕过测量。
定向 10 文件、116 项测试通过,覆盖 HTTP 客户端及 loopback、解析服务、设置、会话文件
存储、ContextManager、预览、附件操作及共享 UI。App 附件筛选的 3 项测试通过,包含导入
进度中的图标取消入口;新增 Electron 用例单独通过。最终
`npm run typecheck`、`npm run lint` 通过。`npm test` 完整运行 885.06 秒:400 个文件
通过、9 个跳过;4,782 项通过、67 项跳过。该运行启动时尚未创建新增 Electron 用例,
因此其通过结果单独记录;导入取消入口的最终改动由随后 App 定向测试覆盖。
日志仍有既有 jsdom Canvas 提示和测试临时仓库的换行提示,没有测试失败。
本轮未修改 Main、Preload、Agent 或 Runtime 请求链路,未发起真实 OCR、文本或视觉模型
请求。前轮实际调用与未测验收项仍以上节记录为准,不能由本次界面回归推断全部高级选项、
Runtime 组合和跨平台验收已完成。没有提交或推送。
复核前轮 `document-app-GXKuZv/report.json`、`document-app-fuFTEY/restart-report.json`
及 `document-app-iZSxV8/report.json`:设置和显式图片 OCR、重启后无 OCR 读取、远端图片
消费记录均为 passed。文档扫描无阻断项;复核候选为字段清单和验证边界,保留其事实限定。
## 复现
脚本为 [`scripts/paddleocr-vl-probe.mjs`](../../../scripts/paddleocr-vl-probe.mjs),
使用 Node 内置 fetch 和已安装的 `@napi-rs/canvas`,不新增依赖。`--endpoint`、`--out`
必填,无产品地址默认值;`--out` 必须是已存在的临时或 Git 忽略目录,由调用者选择。
脚本在其中创建独占子目录,不覆盖历史运行。中文 fixture 需 Microsoft YaHei 或 Noto Sans
CJK SC;缺字体导致文字未绘制时中文断言会失败,先检查 `fixture.png`。
本次实际执行命令(地址仅是获准使用的测试目标):
实测时脚本名为 `.cjs`;交付前按仓库脚本惯例改为 `.mjs`,只调整模块导入,复现时使用
下列最终路径。请求、fixture 和断言逻辑未变。
```powershell
node scripts/paddleocr-vl-probe.mjs --endpoint http://10.7.0.152:8080 --out C:/Users/jiang/AppData/Local/Temp/opencode --mode discover
node scripts/paddleocr-vl-probe.mjs --endpoint http://10.7.0.152:8080 --out C:/Users/jiang/AppData/Local/Temp/opencode --mode full --timeout-ms 60000
```
`discover` 执行 health、OpenAPI 共 2 个 GET;`full` 另执行 4 次 layout 请求和 2 次
restructure 请求。无自动重试,每请求默认超时 60 秒、最多 120 秒,响应流上限 32 MiB。
完整运行至多 8 个请求,默认最坏请求等待预算 480 秒;失败停止后续调用并写 report。
不读取用户资料、不使用凭据、不跟随重定向、不获取返回的图片 URL。
输出包括合成 PNG、两页纯图像 PDF、OpenAPI、选项 schema、各请求选项、逐页 Markdown、
摘要、解码后的插图与 `report.json`。摘要记录 HTTP 状态、耗时、页标签、图片格式、尺寸、
字节数、SHA-256 和页内引用对应关系,不输出 Base64 或返回 URL。Markdown 保留服务原文,
图片本地文件名映射在 `*-summary.json`;它不是已改写资源路径的产品预览。
## 实测结果
环境:Windows x64,Node 24.18.0。fixture 为 1200 × 1600 像素合成报告,包括中英文、
页眉页脚、页码、风景插画和两列表格;PDF 两页均为嵌入 JPEG,无原生文本层。与先前仅文字
PNG、文本 PDF 探测不同,本次验证了扫描型输入和插图输出。
最终运行目录:`C:/Users/jiang/AppData/Local/Temp/opencode/paddleocr-vl-L8LJIm`,
运行时间为 `2026-09-18T05:04:33.802Z`。23 项断言全部通过,8 个请求均 HTTP 200。
| 请求 | 选项差异 | 耗时 | 结果 |
| --- | --- | --- | --- |
| PNG 默认 | 省略 ignore labels,图片 true,可视化 false | 1439 ms | 804 字符,中文及金额正确;页眉页脚不进入 Markdown |
| PNG 保留标签 | `markdownIgnoreLabels: []` | 937 ms | 875 字符,页眉页脚可见 |
| PNG 排除正文 | `['text','header','footer','number']` | 931 ms | 696 字符,金额正文被排除;表格和插图保留 |
| 两页 PDF | 保留标签 | 2490 ms | 每页 875 / 847 字符,Page 1 / Page 2 顺序正确,均含表格和插图 |
| 重组关闭 | `mergeTables: false, relevelTitles: false` | 146 ms | 两页文字、图片引用保留 |
| 重组开启 | 两项均 true | 167 ms | 两页文字、图片引用保留,与关闭组 Markdown 相同 |
PNG 插图为裸 Base64 JPEG,解码后 899 × 482、23,708 字节;PDF 两页分别为
899 × 482、25,849 字节和 898 × 481、25,585 字节。未观察到 URL 或 data URI 图片;
这两种返回形式的分支尚无真实服务验收。Markdown 插图是 HTML `<img>`,表格是 HTML
`<table>`,不能只按 Markdown `![]()` 链接提取资源。
本次 `prunedResult` 没有 `page_index`,`page_count` 是 null。第一个 full 探测曾把
零起始 page_index 当断言,因此退出非零;服务请求本身全部成功。该运行同时发现默认
sans-serif fixture 没有显示中文。修正字体并将页码检查改为合成页标记顺序后完整复测。
第一次 full 的两页图片键相同,证实不能直接用 Provider 图片键作为文档级唯一标识。
## 调用计数
本轮实际发送:1 次 discovery 加 2 次 full,共 **6 个 GET、8 次 layout 推理请求、4 次
restructure 请求**;layout 输入共 10 页图像(每轮 3 张 PNG 加 2 页 PDF)。重组接口内部
是否另调用模型无法从客户端确认,以上计数为 HTTP 调用数。文本模型调用 0 次。
最初一次终端内联 Node 命令因 shell 转义在发请求前语法失败,不计为 HTTP 请求。
此前对话的探测记录另有 3 次成功 layout 请求(首次 PowerShell 图片请求、Node 双语 PNG、
两页文本 PDF),不包含在上述本轮计数;合并已知历史则为 11 次 layout、4 次 restructure,
历史 GET 数未追溯。
## 能力边界
已验证 FR-H3/FR-H4/FR-H7 对应的服务基础事实:图片字节、页内链接、表格结构、中英文、
页眉页脚过滤及有界报告。未完成这些需求的产品持久化、界面与端到端验收。
重组请求成功仅验证接口形状及图片传递。fixture 中虽然有“continued”表格,但开关前后
输出相同,不能证明真实跨页合并或标题重定级生效。方向分类、去扭曲、图表、印章、图片块
OCR、公式编号、输出格式等只发现 schema,未主动启用探测,详见
[参数矩阵](./http-paddleocr-vl-technical-design.md#高级参数)。
未测认证、HTTP 异常返回、服务端取消、大文件、并发、其他操作系统、原生/扫描混合页的
产品选页策略、PPTX HTTP 路径和图片生命周期。单次样例耗时不构成吞吐或质量基准。
测试产物留在独占临时目录供复核,未写入聊天或知识库。
## 检查
已运行脚本 `--help`、语法检查及上述真实 full 探测。最终 `.mjs` 的 `--help`、
`node --check`、`npx eslint scripts/paddleocr-vl-probe.mjs` 均通过;27 个相对文件链接及
锚点检查通过,`git diff --check` 无空白错误。
文档扫描的 2 个阻断候选均位于既有本地 OCR 文档(PRD 的按幻灯片计数、聊天技术说明的
Worker 整体回收),是具体实现对比,保留原文。本次新增内容没有阻断项;复核候选主要是
已测/未测边界、字段清单和生命周期规则,人工审阅后保留事实限制。
没有生产功能变更,不运行整个产品测试集。
2026-09-18 PRD 补充检查:7 份 Markdown 的 30 个相对文件链接及锚点通过,含未跟踪文档的
空白与冲突标记检查通过,相关 `git diff --check` 无空白错误。使用 deai-writing 扫描并人工
复核,新增内容无阻断项;既有的 2 处实现对比按上述理由保留。复核候选为参数清单、验收条件
和实现边界,未删除这些必要限定。本轮未运行产品测试或服务探测,未修改产品代码。
2026-09-18 图片路由补充检查:7 份 Markdown 的 36 个相对链接及锚点通过,含未跟踪文档的
空白与冲突标记检查通过,`git diff --check` 无空白错误。deai-writing 扫描仍为既有 2 个
阻断候选,按上述实现对比理由保留;79 个复核候选经通读核对,主要为参数枚举、验收和
能力限制,新增内容无阻断项。本轮未运行产品测试、真实服务调用或提交。
2026-09-18 完整 UI 文档检查:8 份 Markdown 的 58 个相对链接(其中 30 个含锚点)均通过;
17 项需求、27 个用户场景、14 条逻辑规则的 ID 引用有效,每项需求均有场景与逻辑追溯。
检查包含未跟踪文档的行尾空白、文件末尾换行及冲突标记,`git diff --check` 通过。
目标目录中的缩减交付和选图待定措辞已核对修正;技术实现顺序和未实测能力边界保留。
使用 `python -X utf8` 运行 deai-writing 的 `deai_scan.py docs/features/document-processing --ext .md`,
新增内容无阻断项;既有 2 处实现对比按此前记录的理由保留。人工复核参数清单、需求条件、
状态规则及否定句候选,保留防止误发、丢图、误删和夸大验证范围的明确限制。通读核对 UI
容器、选择/附加状态、原图/文字发送方式、资源归属和失败替换规则,未将设计计作生产完成。
本轮 OCR 请求、文本/视觉模型调用均为 0;未运行产品测试、构建或远端 Host 验证,未提交。
2026-09-19 菜单核对检查:文档处理目录及关联根文档、导航文档共 13 份 Markdown,185 个
相对链接和其中 70 个锚点均通过;含未跟踪文档的行尾空白、末尾换行和冲突标记检查通过。
`git diff --check` 通过。deai-writing 扫描文档处理目录仍为既有 2 个阻断候选,按前述具体
实现对比理由保留;新增内容无阻断项,复核项保留必要的能力边界和菜单状态限制。关联文档
新增段落经人工审校;本轮没有产品代码变更,未运行产品测试或实机菜单验收。
## 2026-09-19 全局浮层复验
修复前源码在 Electron 中复现:“测试解析”的 `AnchoredMenu` 层级为 129,低于设置
遮罩的 130;通知层级为 75,且仍在被设为 `inert` 的应用壳层内。两者的鼠标命中均失败。
共享修复及后续约束见[复发缺陷规则](../../quality/recurring-defects.md#modal-内的菜单与通知被遮挡或隔离)。
- `FloatingPortal.test.tsx` 覆盖 Modal 切换时保持同一通知宿主和内容、焦点循环及菜单归属。
- `overlay-layering.electron.test.ts` 使用生产组件与受控 IPC,验证浅/深主题下的
1280×800、960×720、720×640、640×420;包含通知在 Modal 前/后出现、点击关闭、
Tab/Shift+Tab/Escape、解析场景菜单与二级结果、应用中心、本机推理监控和原生 `dialog`。
- 完整 App 使用当前 Main、Preload、Renderer 的独立构建及空白 profile,从侧栏设置入口
修改最大页数为 99,确认生产读取值及成功通知;原生鼠标点击“测试解析”菜单后,生产 IPC
解析本地 TXT,结果展示 70 字符,Escape 逐层关闭并恢复焦点。仅文件选择器返回合成文件;
未替换解析、保存或通知。模型及 OCR 服务调用均为 0。
- 已通过图片读取工具逐张审阅 fixture 的 16 张 `light/dark-宽度-menu/notification.png`,
以及完整 App 的保存通知、测试菜单和解析结果三张 PNG;文案、主题、菜单与通知层级正常。
临时证据位于批准目录下的 `overlay-baseline-0919`、`overlay-layering-0919` 和
`overlay-full-app-0919-r2`。完整 App 首轮因驱动选择器错误失败,修正后通过;仍记录到
4 条既有 data-font CSP 警告。本轮实际 UI 环境为 Windows x64、Electron 43.2.0,
未验证 macOS、Linux 或辅助技术的实际语音播报。
本轮临时 profile、基线缓存和独立构建已清理,保留脚本、脱敏报告、源码/产物哈希及截图。
最终 `npm test`:403 个文件通过、9 个跳过,4,793 项通过、67 项跳过,耗时 909.69 秒。
`npm run typecheck`、`npm run lint` 及隔离目录的 Electron Vite 生产构建通过;
相关文档的 48 个相对文件链接有效,`git diff --check` 通过。
## 2026-09-19 空正文与草稿恢复修正
对应 L-I3、L-H7、L-H8。修复前已复现:无识别文字、有效插图与缺图警告并存时,
警告被当作正文,显式提取后请求没有原图;队列恢复等待期间输入的文字也会被旧草稿覆盖。
实现和判定归属见[生产实现说明](./implementation.md),未增加持久状态或修改数据 schema。
- 解析、结果完整程度和 HTTP Provider 共用提取正文判定。8 个解析/存储/共享 UI 测试文件
203 项通过,其中 loopback HTTP 回归覆盖缺图警告、正文存在与否及保存后的 `images-only`。
- 生产 `App` 的 6 项回归通过,覆盖空草稿、已有草稿、恢复期间编辑、清空、失败及切换会话。
IPC 响应由测试控制完成时机,不能称为完整桌面到 Main 的新增端到端验收。
- 当前源码在隔离 Electron 中通过真实 `nativeImage`、HTTP 客户端、附件 SQLite/文件存储、
`reparseDraft` 与 `enrichRequest` 复验:仅图片及图片加缺图警告均拒绝文字切换,
草稿仍引用原附件,发往 Runtime 的请求保留一张图片;真实正文控制组仍切为文字发送。
三次请求均发往本机合成 OCR 服务,外部 OCR/文本/图像 Provider 请求为 0。
- 本轮 OCR 判断和草稿合并仍位于 Desktop,共用同一 Main 请求构造与 Renderer 草稿机制;
没有远端 OCR 分支、Agent 协议或模型桥变更,不据此扩大远端视觉模型验收结论。
验证用的临时 fixture、Electron profile 和数据库已清理。首次 OCR 探针因临时目录的模块
解析失败,调整启动器的依赖路径后通过;新会话切换测试最初遗漏全局队列读取,修正
fixture 后通过。上述均为验证工具问题,不修改产品行为来满足错误 fixture。
合并画布与 Continue 修正后的 `npm test` 完整结束:405 个文件通过、9 个跳过,
4,826 项通过、67 项跳过,耗时 982.48 秒,无失败。最终 `npm run typecheck`、
`npm run lint` 及助手数据库 101 项单独复验均通过。会话列表测试更新为无 100 条截断的
既定行为,共享高对比度测试检查全部 forced-colors 块;没有为旧断言恢复产品限制。
隐藏 Electron 窗口的双帧等待实测令布局场景耗时约 63 秒,因此只把测试子进程期限从
60 秒改为 90 秒,并对自有临时目录增加有界删除重试;完整布局断言未减少。
@@ -0,0 +1,315 @@
# 文档解析与附件 UI 设计
本文定义 [PRD](./prd.md) FR-H1 至 FR-H12、FR-I1 至 FR-I5 的完整交互,状态决策以
[逻辑设计](./logic-design.md)为准。当前实现细节见[生产实现](./implementation.md),验证
状态记在 [进度](./progress.md)。配置、解析、预览、选图、发送和资源清理属于同一验收范围。
## 1. 容器与共享组件
遵循根目录 [UI 设计系统](../../../UI-DESIGN.md),保留当前应用容器结构。下表依据
2026-09-18 源码核对,标为新增的内容需要实现,不能当作现有 API。
| 位置 | 现有结构及设计用法 |
| --- | --- |
| 设置中心 | `SettingsPanel.tsx` 整窗 Modal 内的 `DocumentParsingSettingsSection.tsx`;继续使用 `SettingsCategoryHeader`、`settings-section`、`field`,不套第二个 PageShell 或一级标题 |
| 聊天 | `App.tsx` 的 `PageShell variant="reading"`;附件卡位于原 Composer 内,待发送队列仍在 Composer 外,不在助手工作栏增加附件栏目 |
| 知识库 | `App.tsx` 已提供 `PageShell variant="master-detail"`,`KnowledgeWorkspace.tsx` 内含库导航与 DocumentsView;需在文档区域新增解析详情及返回列表,不再嵌套页面壳层或新建知识库级页签 |
| 解析预览 | 新增共享内容视图,分别嵌入设置诊断 Modal、聊天附件 Modal 和知识库文档详情;共享数据和交互,外层容器负责标题、范围、关闭与滚动 |
| 页签与选择 | `WorkspacePrimitives.tsx` 的 `PageTabs variant="segmented"` 承载正文、图片、详情;来源及过滤模式使用 `SegmentedControl`,不能用它替换页签语义 |
| 浮层 | Modal 沿用 body Portal、`activateModalFocus` 与 `trapTabFocus`;测试及附件菜单复用 `AnchoredMenu`,由共享 `FloatingPortal` 挂入所属 Modal 并在 top layer 绘制。通知遵循根 UI 规范 |
| 图文内容 | 复用 `MarkdownRenderer.tsx` 的排版、表格滚动和静态 HTML 处理边界;补齐受管图片与 OCR HTML 表格适配,现有 Markdown 渲染器并未直接支持这些资源 |
| 状态与反馈 | `ScopeBadge`、`EmptyState`、共享按钮、`toggle-row` Switch、应用 `onNotify`;图片多选用 Checkbox,失败重试就地显示 |
全部颜色、间距、字体、边框、圆角和动效使用根设计令牌。连续正文使用阅读字号,图片格不
加装饰阴影。长文件名截断并提供完整名称,不能挤走状态、关闭和发送。图片和解析结果不是
生成图片成果,不使用生图操作卡或生图重试入口。
### 1.1 入口与职责
本表规定完整功能的入口和配置归属,源码现状及证据统一见
[菜单核对记录](./progress.md#菜单核对)。应用中心的四应用清单、启用、常驻和排序继续遵循
[应用导航](../application-tool-navigation/README.md),文档解析不新增第五个应用或右侧工作栏栏目。
| 用户任务 | 主入口与路由 | 范围及目标行为 |
| --- | --- | --- |
| 配置解析与 OCR | 侧栏底部“设置” → “文档解析”(`document-parsing`) | 设备级共享配置,分别保留聊天与知识库工作流;HTTP 地址、凭据和高级选项只在此维护,不随当前项目或会话切换 |
| 管理本地 OCR 文件 | 同一“文档解析”分类的本地模型区 | 下载、校验、ZIP、删除和目录操作;全局模型下载源仍在“平台功能 / 通用设置”,与 OCR 来源选择分开 |
| 查看或释放本地 OCR 内存 | “应用中心” → “本机推理监控”(`local-inference` Modal),或其常驻入口 | 只管理本机执行服务;应用启用及常驻控制入口,不控制解析功能;HTTP 服务不出现在本机服务列表,也不提供远程启停 |
| 添加文件或图片 | “当前对话” → “添加附件”,或输入框粘贴 | 先加入当前草稿,再按输入类型处理;上传按钮继续直接选文件,不改成先选 OCR 的菜单 |
| 查看聊天解析结果 | 草稿或历史消息的附件卡 | 规划中的共享预览;来源消息与目标草稿分别显示,历史输入只读 |
| 查看知识文档结果 | “知识库” → 选库 → “文档与来源”中的文档操作 | 规划新增“查看解析结果”和文档详情;返回恢复库、列表筛选和位置;不改变活动会话 |
| 把文档插图用于聊天 | 聊天或知识文档预览 → “图片” → “添加所选图片” | 明确目标会话草稿后复制;设置测试预览无此操作;已有“用于当前对话”仍负责知识库检索范围,不代替选图 |
从解析失败或未配置提示提供“前往文档解析设置”时,须直达 `document-parsing` 分类并保留
来源预览、目标和草稿;关闭设置返回原位置。跳转沿用设置中心离开保护,不复制配置表单。
保存后只更新后续解析采用的配置,不自动重试、重解析或发送。本机推理监控现有“模型与连接
设置”不是分类直达入口,不将它作为 HTTP OCR 配置路径。
### 1.2 附件操作菜单
下表规定交互。卡片提供查看入口,移除保留独立按钮;次操作放在卡片“更多”菜单,
菜单项包含附件名称以区分多个文件。普通图片的“提取图片文字”可作为就近次操作,不能和
菜单形成两套状态或重复执行。菜单复用 `AnchoredMenu` 的定位、键盘和焦点恢复。
草稿、历史消息和队列预览中的“查看解析结果”“更多附件操作”使用共享 `icon-button`
视觉,图标分别为文档查阅和省略号,点击区域为 `32px × 32px`,在独立操作行内排列。
按钮提供含文件名的可访问名称、悬停提示及键盘焦点提示;草稿移除和导入取消使用同尺寸
叉号。导入卡按进度图标、文件信息、取消按钮排列,进度条独占下一行。
文件名保持单行省略,悬停可读完整名称;解析状态、发送方式和大小在元数据行按空间换行,
不将次操作拉伸成整行文字按钮。布局以卡片所在容器宽度为准,沿用主题令牌和共享焦点环。
| 对象 | 查看操作 | 可改变内容的操作 |
| --- | --- | --- |
| 草稿文档 | 查看解析结果、打开原文件 | 使用当前设置重新解析、移除;图片多选只在预览中进行 |
| 草稿图片,发送原图 | 打开原图;已有 OCR 结果时可查看 | 提取图片文字、移除 |
| 草稿图片,发送提取文字 | 查看解析结果、打开原图 | 改为发送原图、使用当前设置重新提取文字、移除 |
| 历史消息附件 | 查看已保存结果、打开原件 | 需要重解析或改变发送方式时先添加到当前可编辑草稿;不修改原消息 |
| 已入队附件 | 仅有附件时显示“查看附件”,查看入队时冻结的结果与发送方式;无附件时隐藏入口 | 修改整条输入使用“恢复到草稿”,按 L-H7 合并;不直接修改队列附件 |
| 知识文档 | 查看解析结果、打开来源、管理分块 | 使用当前设置重新解析;失败时使用重试,不再并列一个同义重解析按钮;来源同步仍属于来源级操作 |
| 设置测试结果 | 查看正文、图片、详情及原件 | 重新测试、关闭;没有加入草稿、知识库或发送操作 |
操作依据对象、资源和任务状态显示。解析中仅保留可用旧结果的查看、进度和取消,不允许重复
解析;缺少结果或原件时说明缺失原因并给出重选入口。已有历史数据不显示能恢复完整资源的
假菜单。没有受管解析资产的外部知识库结果沿用其已有查看能力,不提供本地重解析或选图。
尚未接入生产链路的动作不先加入应用菜单或附件菜单充当可用功能。
## 2. 文档解析设置
### 2.1 信息顺序与保存
分类页头为“文档解析”,说明“配置聊天文档附件和知识库导入使用的解析方式”。右侧依次为
“测试解析”“保存设置”,仅保存为主按钮。现有两处场景测试按钮合并到页头测试入口,入口
展开“聊天附件 / 知识库导入”选择后打开文件对话框;场景预设仍各自编辑。
内容依次为运行状态、使用场景、OCR 来源与对应配置、默认折叠的高级设置。原生解析、OCR、
转换分别显示状态;远程 OCR 未配置不把原生解析标成不可用,旧 Office 转换沿用现状说明。
切换来源不发网络请求,不清空隐藏配置,也不改变已保存来源。显示“有未保存的修改”,
所有字段、过滤项和来源切换参与设置中心已有离开保护。
未保存时禁用测试和连接检查,旁边持续说明“请先保存设置,再测试解析”,通过
`aria-describedby` 关联;不得使用旧配置测试。保存期间防止重复操作,失败保留输入并在
分类页头显示原因。保存成功走应用通知,测试成功直接打开结果并呈现状态,不再同时弹
“测试成功”通知。取消文件选择不清除上次结果、不创建任务。
### 2.2 来源配置
“OCR 来源”使用共享分段控件,选项为“本地模型”“远程服务”,下方显示已保存来源与当前
编辑值是否不同。聊天和知识库分别保留工作流选择,不增加每会话 Provider 配置副本。
本地区域保留已有模型档位、下载源、安装校验、取消下载、ZIP 导入导出、删除及打开目录。
本地模型缺失只影响需要本地 OCR 的任务。切到远程后不要求先安装本地模型。
远程区域固定服务类型“HTTP PaddleOCR-VL”,包含有持久标签的服务地址、认证方式、密钥
输入和“已保存凭据 / 未配置凭据”。仅显示已实现的认证方式,技术契约见
[配置与请求](./http-paddleocr-vl-technical-design.md#配置与请求)。更改地址、临时选择无需
认证或切回本地都不清除已保存密钥;清除使用显式操作。密钥不回填明文。
配置旁持续说明“需要 OCR 的页面或图片将发送到此服务;快速文本模式不自动上传,主动提取图片文字仍使用此服务”。不增加
逐文件授权。“检查连接”是远程区域的次操作,成功文案为“服务可连接,解析能力需用文件
测试”;显示检查时间,不把 health 成功呈现为图表、印章等模型已可用。
### 2.3 高级设置
高级设置用原生 `details/summary` 默认折叠,按用途分组,不铺开 API 字段墙。
| 分组 | 控件与文案 | 生效语义 |
| --- | --- | --- |
| 内容保留 | 过滤模式“服务默认 / 全部保留 / 自定义”;自定义内为“保留页眉文字、页眉图片、页脚文字、页脚图片、页码、脚注、侧栏文字”正向 Switch | “保留”开启代表对应标签不进入排除列表;服务默认与空列表不同,见 L-H2 |
| 页面处理 | 方向分类、页面去扭曲、版面检测 | 各项原生选择框“服务默认 / 开启 / 关闭”,三态不能伪装成二态 Switch |
| 内容识别 | 图表识别、印章识别、图片块文字识别 | 同上;持续显示“接口已声明,尚未实测”或带测试条件的验证信息 |
| Markdown 格式 | 美化 Markdown、显示公式编号、格式化块内容、合并版面块 | 同上,不把未知服务默认显示成关闭 |
| 跨页整理 | “合并跨页表格”“调整标题层级”共享 Switch,默认关闭 | 明确会增加重组请求,保留源页集合;结果无变化如实报告 |
| 解析限制 | 最大页数、超时与现有适用的并发参数 | 显示单位和共享契约允许范围;本地单页超时与 HTTP 请求超时分开,不对未实现参数展示可保存控件 |
过滤项在文字和图片两个小组排列,标签使用正向“保留”。自定义从未编辑过时以全部保留
初始化并标明这一行为;已有自定义值切换模式后保留,不能根据未知的服务默认猜开关位置。
技术或已有配置含其他排除标签时以带名称的补充行显示并可恢复保留,例如 `text`;不能保存
一次表单就丢掉未列在常用组的排除项。“恢复服务默认”改变草稿,保存后才生效。
正文引用的插图默认保存,不设置“自动发送全部插图”。保留页眉图片等内容过滤与保存已引用
图片是两件事:被过滤且未引用的图片无需保存。高级能力证据仅描述相应部署和样例;没有证据
仍可由用户明确配置并测试,错误指出关联选项,完整实现范围包含这些已约定能力。
## 3. 共享解析预览
### 3.1 框架与正文
头部显示文件名、来源范围、结果状态、“打开原文件”和关闭或返回。设置测试标为“测试结果”,
聊天标明会话,知识库显示库名与 `ScopeBadge`。下一行固定正文、图片、详情三个 PageTabs,
页签及直接包装层 `flex: 0 0 auto`,仅当前面板滚动。查看已存结果不发 OCR 请求。
正文面板按来源页或章节呈现 Markdown、HTML 表格及受管图片。页面导航显示真实页号;
PPTX 显示“幻灯片 N”,没有分页的原生文档显示工作表或标题定位,不生成假页码。点击图片
旁的“第 N 页”或“查看正文位置”切到正文并聚焦对应标题;正文图片的“在图片中查看”反向
定位图片卡。跨页表格显示全部来源页链接,不把合表结果归给单一页。
“打开原文件”经 Main 打开保存的文件。聊天使用会话原件;知识库 managed 使用受管原件,
reference 使用原路径。reference 原件失效时显示“原文件不可用”,已保存正文与图片仍可看。
测试原件只在测试预览生命周期内使用,不承诺关闭预览后持久保留。旧附件没有解析资产时显示
“此附件未保存解析结果”,提供重新添加文件入口,不显示空白成功或自动补跑 OCR。
### 3.2 图片与详情
图片面板显示“已保存 N 张”及缺失数量,按原始页、页内顺序排列。每卡含缩略图、可读名称、
页码或定位、格式、尺寸和打开大图操作;图片使用 contain,不裁掉图表坐标轴。大图沿用共享
整窗浮层规则,Escape 只关闭顶层并返回图片卡,不能误关闭整个设置。
正文中的缺图替换为具体说明,例如“第 3 页图片 2 未保存:服务仅返回链接”,图片面板保留
不可选择的缺失项及原因。没有插图时显示“此结果没有提取图片”,说明原生解析器可能只提取
文字;加载中、读取失败、没有图片和筛选无结果分别呈现。仅有图片的结果显示“仅图片,无
可发送正文”,仍可预览并选择图片,不把空正文附件当成已就绪文本。
详情显示解析时间、实际工作流、Provider、无凭据的服务标识、原格式、总页数、OCR 页数、
文字量、图片保存情况、耗时、生效选项、服务默认项以及按页警告。服务未提供置信度显示
“未提供”。配置取自这次结果,不取当前设置;无法得知服务默认实际值时显示“服务默认,
实际值未提供”。跨页整理分别显示请求状态、是否观察到变化和来源映射,HTTP 200 不等于
“合并成功”。详情内不展示密钥、授权头、Base64 或原始错误响应。
### 3.3 测试与重解析
测试运行显示文件、场景、阶段、已完成页数(可确定时)和取消;未知进度使用不定进度条,
不模拟百分比。测试期间设置禁止关闭,用户可先取消,结算后再关闭。测试失败就地保留原因
和“重试”;有部分产物时可以查看,但不能标为成功。关闭测试预览后释放无引用测试资源。
测试预览不提供“添加到会话”,防止诊断文件进入用户草稿或知识库。
“使用当前设置重新解析”是聊天草稿和知识库文档的次操作,点击前显示将使用的已保存来源。
历史消息和队列冻结已接受的输入;其预览只读。历史消息使用“添加到当前草稿重新解析”,
产生草稿附件而不改写已发送正文;队列修改使用“恢复到草稿”,避免原队列和修改稿重复发送。
重解析期间旧结果仍可查看,顶部显示“正在重新解析,
当前显示上次结果”;成功才替换,失败或取消保留旧结果及单独的本次失败说明。
## 4. 文档插图加入会话
### 4.1 选择与添加
聊天和知识库预览的图片卡提供原生 Checkbox,标签含文档名、页码和图片序号;默认零选择,
打开预览不自动选图。点缩略图只放大,不勾选。页签切换保留本次勾选,关闭预览清除尚未
添加的选择;已添加状态由目标草稿查询得到,不用 Checkbox 冒充已附加状态。
底部操作条显示目标会话及项目、待添加数量、已在草稿数量和“添加所选图片”。仅对未附加
图片显示可勾选项;已附加卡显示“已在此会话草稿”与“从草稿移除”。取消勾选只取消本次
待添加选择,不删除已经附加的图片。无选择时禁用添加并说明原因,无默认全选行为。
添加只将选中的图片复制为目标草稿中的视觉附件,不发送消息、不附带整份知识库正文、不
自动启用知识库,也不重跑 OCR。成功后清除已提交勾选,显示已附加状态,提供“前往会话”
并通过一次应用通知反馈;失败保留勾选和原草稿,操作条就地显示原因,不重复通知。
同一来源结果的同一 imageId 加入同一草稿是幂等操作。点击两次或从正文和图片面板重复
添加都只产生一个附件;已发送后在新一条草稿中可再次选择。重新解析生成的新图片不按
文件名、页内键或视觉相似度与旧图片合并,也不自动替换草稿中的旧副本。
### 4.2 目标会话
从聊天当前草稿打开时,目标为该草稿;从历史附件打开时,目标为当前可编辑草稿,头部明确
区分来源消息与目标。知识库有有效活动会话时显示“添加到:会话名”,并保留其草稿文字。
浏览知识库不会自动改变活动会话或把知识库归属当作目标项目。
没有可编辑活动会话、会话已删除或当前为不可编辑消息通道时,保留图片选择,操作条显示
“选择会话后添加”,提供“选择会话”和“新建会话”。选择复用应用的项目与最近会话导航信息,
新建沿用现有 `startNewConversation` 的项目限制与空会话复用规则;该函数当前会切到聊天,
实现时须补齐目标会话返回和预览恢复接线,不能直接调用后假定选择仍在。需要先选可创建项目时在同一流程
显示原因和项目入口。新建或选择后回到预览核对目标,用户点击添加才复制,不自动发消息。
取消目标选择保留图片选择,不创建空附件。
提交时冻结显示的目标 conversationId。异步期间切换到其他会话不改变归属,完成提示带原
目标名称;目标已删除则失败并清理本次复制。重新打开预览按当前目标重新计算已附加状态。
### 4.3 发送与移除
选出图片的卡片显示“文档名 · 第 N 页 · 图片 M”,副标题为“文档插图 · 图片输入”。来源
跳转在原结果仍存在时可用;原结果删除后仍显示来源文字,副本照常打开与发送。
当前草稿本来含父文档时,父文档只提供一次解析文本,选出的图片分别占普通图片附件位置;
不为每张图片再次拼入父文档全文。移除父文档不级联删除已复制图片,反馈“已移除文档,
所选图片仍在草稿中”;移除图片不删父文档、不改知识库。移除只释放对应草稿引用,已发送
或队列仍引用的资源保留。数量按父文档和每个图片附件分别计数。
添加前和发送前均核对共享附件数量、容量和本次模型与 Runtime 图片能力,详见
[L-I3 至 L-I5](./logic-design.md#选图与发送)。超限拒绝整个本次选图批次并保留选择,列出
当前占用和可调整项,不能只取前几张。能力不匹配不丢图:草稿可保留图片,发送阻止并给出
“切换模型或 Runtime”“提取图片文字”“移除图片”。切换入口返回后保留草稿并重新校验。
活动回复期间沿用已有模型选择锁定规则,图片仍可进入下一条草稿,不能借预览绕开该锁定。
能力失配的队列项沿用队列的可恢复错误呈现,提供“恢复到草稿”供修改。恢复合并保留原
草稿,超限时队列与草稿均不改变并提示先整理草稿;具体合并规则见 L-H7。
## 5. 普通图片与文档附件状态
普通图片默认显示原图缩略图、“图片输入”及“提取图片文字”次操作。是否允许发送由实际
生效模型和 Runtime 共同决定,未知能力显示“尚未确认图片输入能力”,不能当作支持。
支持视觉也允许明确 OCR。OCR 中显示“正在提取文字”和取消,成功后同一附件变为
“原文件名 · 提取文字”,持续显示“仅发送提取文字,原图保留”,可查看正文和打开原图。
原始文件名不被磁盘重命名;来源与发送方式独立保存。“改为发送原图”恢复图片模式并重新
校验能力,不需要重新 OCR。失败、取消或没有文字时保持原先发送方式,不声称转换完成。
文档选出的图片执行 OCR 时采用相同交互,并保留文档、页码、图片序号来源。
| 附件状态 | 显示及操作 | 发送条件 |
| --- | --- | --- |
| 读取、解析、校验、保存中 | 文件名、阶段、批次序号、已知页数、取消 | 本批完成前阻止发送及重复导入,草稿文字可继续编辑 |
| 完整结果 | “解析完成”、页数、文字量、已保存图片数;查看解析结果、打开原件、移除 | 持久化和能力校验通过 |
| 部分结果 | “部分解析:第 N 页 OCR 失败”等持续提示;查看、重试、移除 | 仅逻辑允许的自动聊天结果可发送,发送内容携带不完整说明;不弹重复确认 |
| 仅图片结果 | “仅图片,无可发送正文”;查看和选择图片 | 选择至少一张该结果的图片或其提取文字后可发送;父文档保留来源并占附件位置,但不生成空文本上下文;也可移除父文档后单独发图片 |
| 失败或取消 | 具体原因或“已取消”;重试、移除 | 不可发送该项;批次回滚后保留失败摘要和重新选择入口 |
| 中断后重启 | “解析已中断”;从已保存原件重试,原件未保存则重新选择 | 不自动恢复 HTTP 推理,不把未完成状态变成成功 |
| 旧附件缺少资源 | “此附件未保存解析结果”;重新添加文件 | 不将旧元数据当成新的可恢复资源 |
导入仍遵守既有批次回滚:失败或取消时移除本批新增的未提交附件,保留之前草稿。失败摘要
不是可发送卡片。已添加附件重解析失败则保留其旧卡片,不适用首次导入回滚。取消停止后续
客户端请求;文案不承诺服务端 GPU 任务立即停止。取消生成与取消解析不同,前者不删除
已提交附件。入队保存失败保留草稿,队列显示不得早于资源持久化完成。
## 6. 知识库与生命周期呈现
DocumentsView 的文档操作增加“查看解析结果”,在新增文档详情区域呈现共享预览,保留来源、
同步、任务和 chunk 管理入口。图片预览不建立图片向量索引。正文页码与现有 chunk 来源
定位相连,图片选择不依赖该库是否已启用聊天检索。
完整索引或高保真缺页、缺图、重组失败时,本次任务标失败;旧文档仍可检索并显示“上次
结果仍可用”。首次导入失败没有可用旧结果时显示失败与重试,不能出现已索引成功标记。
删除文档或来源沿用危险操作确认,说明知识库派生资源会清理;已经复制到会话的图片不受
影响。删除聊天会话遵循会话删除流程,不在图片预览再实现平行删除系统。
草稿和队列恢复后仍能打开原图与解析结果。移除队列项仅释放该项引用,取消生成保留消息
附件;资源清理结果不能仅凭卡片消失验收,文件及数据库检查见验收矩阵。
## 7. 响应式与无障碍
按容器可用宽度响应,助手工作栏展开后不能只看窗口宽度。宽布局图片格多列,窄布局单列,
操作条换行;知识库按根规范在窄窗口转为列表和详情单面板,并提供返回列表。设置继续使用
既有左右分类 Modal;预览 Modal 使用共享尺寸与外边距,不另设全屏页面。正文表格独立
横向滚动,不撑宽 Composer 或 Modal。200% 缩放时目标会话、警告、取消和发送仍可达。
PageTabs 保留 tablist、tab、tabpanel、aria-selected、游标焦点和方向键/Home/End。
Switch 用 `role="switch"`,图片选择保留 checkbox 语义;图片卡内选择与放大为独立焦点。
表单字段有持久标签,错误用 aria-describedby 关联;进度使用 polite 实时区域,只播报
阶段和有意义的页数变化,失败使用 alert。图片替代文字描述来源,不凭空生成内容描述。
Modal 初始聚焦关闭按钮,确认删除初始聚焦取消。Tab 循环、背景隔离、顶层 Escape 和焦点
返回使用既有辅助函数;触发卡片被删除时返回附件区或文档列表的稳定入口。整窗遮罩纳入
共享 no-drag 选择器,原生浏览器 viewport 使用既有遮挡机制,不靠提高 z-index。
主题和 reduced-motion 遵循全局设置,不仅凭颜色区别部分、失败与完整结果。
## 8. 完整验收矩阵
以下为产品验收要求,是否通过及验证层次以进度记录为准。场景细节见
[User Stories](./user-stories.md),实现验证路径见 [技术设计](./http-paddleocr-vl-technical-design.md#实施与验证顺序)。
| 用例 | 覆盖与可观察结果 | 追溯 |
| --- | --- | --- |
| A-01 配置与升级 | 旧配置继续本地;来回切换保留两套配置及凭据;不安装本地模型即可用 HTTP;保存失败、未保存测试和离开保护可恢复 | FR-H1、H11、H12;US-H1、H8、H12 |
| A-02 工作流 | 聊天/知识库混合 PDF、图片型 PPTX、快速模式、高保真及纯图片分别走约定路径,保留真实页码 | FR-H2、I1、I4;US-H2、H8、I1、I6 |
| A-03 高级参数 | 页眉页脚正向开关、服务默认、空排除列表、其他标签往返不丢失;声明与实测分开;未知选项或模型缺失明确报错 | FR-H4、H7;US-H4、H9 |
| A-04 跨页整理 | 用能触发合并的样例验证表格、标题和 sourcePages;无变化、映射失败均如实显示,不把请求成功当质量通过 | FR-H5;US-H5 |
| A-05 预览 | 三入口共用正文/图片/详情;同名图片键不串页;HTML img/table、页码互跳、原件打开、历史配置、置信度缺省正确 | FR-H3、H7、H11、H12;US-H3、H12、H14 |
| A-06 失败与取消 | 401/429/5xx、超时、损坏/超限文件、URL 图片、缺页缺图、磁盘保存失败;自动聊天部分与严格工作流失败不同;取消停止后续请求且可重试 | FR-H3、H6、H9;US-H6、H7、H15 |
| A-07 显式 OCR | 支持视觉仍可提取文字;成功改名并明确仅发文字、原图可开;失败/取消/空文字保持原方式;改回原图重验能力 | FR-I1、I2、I3;US-I2、I3、I11 |
| A-08 文档选图 | 零默认选择、勾选与已附加区分、重复添加幂等;父文档文字仅一次;父文档/图片独立移除;重新解析不替换已复制图片 | FR-I5;US-I7、I8 |
| A-09 知识库选图 | 有活动会话、无会话、不可编辑通道、选择/新建取消、异步切会话;只加入明确目标草稿且不自动发消息或启用检索 | FR-I5、H10;US-I9 |
| A-10 能力与限制 | 模型支持但 Runtime 不支持、未知能力、切换后失配、队列恢复失配;普通附件与选图共用数量/容量检查,整批超限不静默截取 | FR-I1、I5;US-I2、I10 |
| A-11 持久化 | 发送/入队/草稿后重启,外部原文件移走,正文/原图/图片仍可开;解析中重启显示中断;入队失败保留草稿 | FR-H8、H9、I3;US-H10、H15、I4、I5 |
| A-12 替换与清理 | managed/reference 重解析失败保留旧 chunks;成功替换;移除草稿、队列、会话、项目、库来源均核对文件和 DB;KB 删除不损坏会话副本,其他有效引用保留 | FR-H9、H10、H11、I5;US-H11、H13、I8、I9 |
| A-13 测试隔离 | 测试不创建会话/知识记录,不提供选图入草稿;取消文件选择无副作用,关闭预览清理测试资产 | FR-H7、H12;US-H14 |
| A-14 界面质量 | 浅/深主题、宽/中/窄窗口与工作栏压缩、200% 缩放、纯键盘、嵌套 Modal、浏览器 viewport 遮挡、短窗口页签不收缩 | FR-H12、I5;US-H14、I12 |
| A-15 菜单与返回 | 全局解析配置与本机监控分离;附件菜单随草稿/历史/队列状态变化;设置直达返回保留选择;知识库“用于当前对话”不替代选图,新建目标后恢复预览 | FR-H1、H11、H12、I2、I5;US-H16、I9 |
跨平台、真实本地模型和远端 Runtime 的执行证据按现有仓库规则记录。Mock、静态稿和
健康检查均不能替代完整产品入口验收。
@@ -0,0 +1,44 @@
# HTTP OCR 用户场景
本文件覆盖 [HTTP 扩展](./prd.md#13-http-paddleocr-vl-扩展规划) FR-H1 至 FR-H12 及
[图片路由](./prd.md#322-纯图片与文档的路由规划) FR-I1 至 FR-I5。当前实现及证据见
[生产实现](./implementation.md)和[进度](./progress.md#2026-09-19-实现与验证),未执行的场景不视为通过。
| ID | Given / When / Then |
| --- | --- |
| US-H1 | 给定设备没有本地 OCR 模型,用户保存可用 HTTP 服务并测试扫描 PDF,应返回逐页文字和图片;切回本地来源时再检查本地模型。对应 FR-H1、FR-H7。 |
| US-H2 | 给定包含文本页和扫描页的 PDF,用户以自动聊天或完整索引方式导入,仅需要 OCR 的页进入所选 Provider,结果保留原始页码;快速模式不上传,高保真按全页处理。对应 FR-H2。 |
| US-H3 | 给定两页有相同图片键的文档,解析完成并重开聊天或知识库时,两页图片均可查看且来源正确;删除一份文档不损坏另一份的图片。对应 FR-H3。 |
| US-H4 | 给定页眉包含合同编号,用户选择保留页眉页脚后再次测试,能看到原先被默认过滤的内容;恢复服务默认或设置空排除列表的含义不同且可辨认。对应 FR-H4。 |
| US-H5 | 给定两页连续表格,用户启用重组后可检查表格结果及来源页集合;合并错误、无变化或无法关联页码时,测试信息如实显示。对应 FR-H5、FR-H7。 |
| US-H6 | 给定 HTTP 返回 401、429、5xx 或超时,导入提示对应错误;用户修正设置后可重试。解析期间取消则停止后续请求并清理本次未提交图片,已有草稿与索引保留。对应 FR-H6。 |
| US-H7 | 给定服务只返回图片 URL,客户端不访问该 URL,明确报告图片未保存;自动聊天可按逻辑规则保留文字及警告,知识库完整索引和高保真不可将其当作完整结果。对应 FR-H3、FR-H6。 |
| US-H8 | 给定现有本地设置和图片型 PPTX,升级后默认仍走本地;主动选择 HTTP 后识别直接内嵌图片并标注“幻灯片 N · 图片 M”,不把图片响应顺序当作幻灯片号。对应 FR-H1、FR-H2。 |
| US-H9 | 给定高级开关在 OpenAPI 中存在但对应模型未安装,启用测试时报告服务错误和选项名称,不以连接成功代替能力验证,也不静默忽略用户设置。对应 FR-H4、FR-H7。 |
| US-H10 | 给定文档附件已发送或被发送队列接受,用户关闭应用并删除外部原文件,再启动后可打开保存的原文件、正文及图片,队列可恢复附件内容;取消生成不会删除附件。对应 FR-H8、FR-H9。 |
| US-H11 | 给定草稿、已发送会话和知识库分别持有文档,移除草稿或取消导入只清理本次暂存;删除会话只清理无其他引用的资源,知识库图片仍可查看。对应 FR-H9、FR-H10。 |
| US-H12 | 给定本地与远程均有配置,切换来源后配置仍在;修改高级选项时须保存后才能测试,结果可切换正文、图片和详情视图。再改配置并打开历史附件时,旧结果及其所用选项不变。保存失败保留草稿,关闭或切分类遵循未保存保护。对应 FR-H11、FR-H12。 |
| US-H13 | 给定已有聊天草稿解析结果或 managed/reference 知识库文档,重解析失败或取消后旧正文和图片仍可查看,知识库旧 chunks 仍可检索;成功后才替换。历史消息及已入队内容不会被替换,再解析产生新草稿。删除知识库来源清理无引用派生资源,不误删外部原文件。对应 FR-H10、FR-H11。 |
| US-H14 | 给定测试、聊天、知识库中同一类解析结果,用户查看正文、图片和详情,页码及图片可互相定位,HTML 表格可读,缺失置信度显示未提供;打开原件遵循各自归属。测试不提供选图入会话,关闭预览后清理测试资源,reference 原件缺失仍可看保存的正文和图片。对应 FR-H3、FR-H7、FR-H12。 |
| US-H15 | 给定导入或保存中断、磁盘写入失败、无文字或部分 OCR 失败,界面区分失败、中断、仅图片及部分结果;重启可从保存的原件重试,未保存则提示重选。自动聊天可靠文本可带警告发送,完整索引和高保真不替换旧结果;失败批次回滚且原草稿保留。对应 FR-H6、FR-H9、FR-H12。 |
| US-H16 | 给定从聊天或知识库预览进入“前往文档解析设置”,应直达同一全局分类,关闭后保留原选择、目标和草稿;保存不自动重解析或发送。关闭本机推理监控应用不禁用解析,HTTP 来源无需加载本地模型。知识库“用于当前对话”继续选择检索范围,“添加所选图片”只复制图片;新建目标后回到预览,明确点击添加才改变附件。历史附件不可就地改写,队列修改须先恢复到草稿。对应 FR-H1、FR-H11、FR-H12、FR-I2、FR-I5。 |
## 图片与草稿验收
| ID | Given / When / Then |
| --- | --- |
| US-I1 | 给定本次模型和 Runtime 支持图片输入,用户添加普通图片并发送,模型收到图片输入,不调用 OCR;如为请求生成缩放或转码副本,历史附件仍可打开原图。对应 FR-I1、FR-I3。 |
| US-I2 | 给定实际模型或 Runtime 不支持图片输入,用户发送图片时收到明确提示,可选择提取文字或切换视觉模型;未作选择前不自动 OCR,也不声称模型已看到原图。切换后按实际能力重新判断。对应 FR-I1。 |
| US-I3 | 给定聊天模型支持视觉,用户仍明确选择“提取图片文字”,系统调用已保存 OCR;成功后附件显示“原文件名 · 提取文字”和“仅发送提取文字,原图保留”,请求只有提取文字。失败时原图、草稿及原发送方式保留,允许重试。对应 FR-I2。 |
| US-I4 | 给定纯图片消息已发送或入队,关闭应用并移除外部原文件后重开,原图仍可查看,队列可恢复;没有执行 OCR 的图片不要求存在解析正文或清单。对应 FR-I3。 |
| US-I5 | 给定可恢复草稿引用暂存附件,应用退出再启动时不因临时目录清理丢失附件;移除该引用后仅清理无其他引用的资源。对应 FR-H9、FR-I3。 |
| US-I6 | 给定同一知识库工作流,切换聊天模型是否支持视觉不会改变图片文字提取和索引策略;PDF、Office 仍遵循文档解析规则。该场景需在相应导入格式实现后验收。对应 FR-I4。 |
| US-I7 | 给定文档已保存多张提取图片,打开预览为零选择,不发模型请求。勾选图片后点击添加,目标会话草稿出现带文档名、页码、图片序号的视觉附件;关闭前未添加的勾选不算附件,重新打开按草稿显示已附加状态。重复点击只添加一次,不自动全量发送。对应 FR-I5。 |
| US-I8 | 给定草稿含父文档及从中选出的图片,发送时父文档全文只加入一次,图片分别作为图片输入;移除父文档后图片仍在,移除图片不影响文档。重解析父文档不会替换已复制图片,已发送图片及来源信息重启可用。同一图片移除后可再加,发送后可加到新草稿。对应 FR-I5、FR-H9。 |
| US-I9 | 给定知识库预览有选择,存在可编辑活动会话时显示其名称并添加到该草稿;无会话或当前通道不可编辑时,可选择或新建会话,取消保持选择。添加中切换会话不改变目标,目标被删除则失败且不留下新副本。添加不启用知识检索、不带整篇文档、不自动发消息;源文档重解析或删除后会话图片仍可用。对应 FR-I5、FR-H10。 |
| US-I10 | 给定草稿已有附件,选图批次超过共享数量或容量上限时整批拒绝且保留选择和草稿;重复项不重复占位。添加后切换到不支持图片的模型或 Runtime,发送明确阻止并允许切换、提取文字或移除。队列重启后实际能力不匹配同样保留资源并提供恢复到草稿修正,不静默丢图。对应 FR-I1、FR-I5。 |
| US-I11 | 给定普通图片或文档插图正在 OCR,取消、失败或空文字不会改变发送方式;成功后可打开保留的原图并“改为发送原图”,按新能力重新校验。改显示名不改原始文件名,插图仍保留文档和页码来源;重试按当前保存配置且不破坏旧结果。对应 FR-I2、FR-I3、FR-I5。 |
| US-I12 | 给定窄输入区、知识库单面板或嵌套预览,用户仅用键盘可切页签、勾选、放大、添加、取消和关闭;Escape 只处理顶层,关闭后焦点恢复。浅深主题、200% 缩放、工作栏展开及原生浏览器同时显示时,目标、状态和主操作仍可见可用。对应 FR-H12、FR-I5。 |
首次配置、成功解析、失败重试和取消均须从产品入口验收;独立脚本结果只证明指定部署的
接口行为。图片保留不等于后续文本模型会自动接收图片字节,也不等于知识库支持图片检索。
+1
View File
@@ -27,6 +27,7 @@
## 相关功能
- [会话媒体生成设计](../conversation-media-generation/README.md):规划普通 LLM 通过工具调用图片、视频模型及模型切换行为,尚未实现。
- [模型连接](../model-connections/README.md):地址、模型 ID、凭据与请求定制。
- [直连文本 Agent](../direct-model-agent/README.md):文本模型的工具和上下文压缩,
不等于图片模型支持 Responses 图片工具。
+9
View File
@@ -10,6 +10,8 @@ FastGPT/RAGFlow 完整 App 与专属配置、真实远程预检索仍待验收
## 文档导航
后续与监督者的关系:故事线图谱引用现有知识库资料,补充工作事件与知识演变视图;计划支持双向定位和预览后补充本地条目,不复制资料正文、不改变外部知识库只读边界。该集成尚未实现,权威定义见[监督者产品设计](../conversation-supervision/supervisor-prd.md) FR-S7 至 FR-S9。
| 文档 | 权威职责 |
| --- | --- |
| [知识库检索与分块增强 PRD](./prd.md) | 检索、分块、诊断和产品验收 |
@@ -29,6 +31,13 @@ FastGPT/RAGFlow 完整 App 与专属配置、真实远程预检索仍待验收
| [2026-09-13 产品验证摘要](./external-knowledge-validation-2026-09-13.json) | 生产服务与本机 HTTP MCP、Dify 桌面、凭据重开、调用计数、最终自动检查状态及远程阻断的脱敏证据 |
| [2026-09-13 UI 与真实生成复验](./external-knowledge-follow-up-2026-09-13.json) | 后续 UI 修正、三家生产 IPC 生成、Dify 完整 App 回答与引用、视觉 fixture 及本轮请求计数 |
## 模型图谱抽取
图谱抽取不设置独立的输出 token 预算。OpenAI Chat Completions 和 Responses
请求省略输出上限参数,由模型服务决定;Anthropic 必填的 `max_tokens` 沿用默认文本
模型连接的最大输出配置,未配置时使用应用共享协议默认值,不再固定为 8192。
服务端仍可能因自身限制返回长度截断;既有超时、取消、响应大小边界和失败诊断保持不变。
## 术语
- **外部实例**:一个由用户配置的 Dify、FastGPT 或 RAGFlow 服务地址及其认证信息。
+8
View File
@@ -16,6 +16,14 @@
## 验证证据
### 2026-09-20 清理孤立的内存图谱搜索
- 删除仅剩测试调用的 `searchGraph`、专属评分/遍历辅助函数、搜索上限及测试 fixture。
实际检索仍通过 `KnowledgeDatabase.hybridSearchWithDiagnostics` 与 `graphSearchScored`;
图谱抽取、归一化、合并、数据库结构和已发布迁移均保持不变。
- `graph-extractor.test.ts` 与 `knowledge-database.test.ts` 合计 55 项通过,保留合并时
丢弃失去端点关系的回归;没有调用外部模型。
### 2026-09-13 知识库布局与自动名称修复
- 创建类型的共享分段控件被长表单压缩,修复共享不收缩规则,不添加页面专属高度。
+79
View File
@@ -0,0 +1,79 @@
# Magic Notes
Magic Notes is the local notes, checklist and AI-comment workbench. Desktop
actions and authorized Agent/MCP tools use the same desktop store: SQLite for
metadata and indexes, and local files for entry bodies and binary assets.
The open workbench subscribes to successful writes so users can see external
changes without leaving the page or clicking a refresh button.
The workbench opens on a responsive note-card overview. Opening a note replaces
the overview with a continuous record stream, a narrow left index and independent
right AI comments. Both side panes can collapse. Index items show one real
first-page canvas thumbnail with page count, or a text summary; clicking scrolls
without replacing the editor or losing a draft. Opening a note shows a blank composer
above the history, with text/canvas selection and Save. Creating a record resets
that composer; editing a saved record requires its explicit Edit action. The separate To-dos view
keeps search, status filters and grouped tasks on one page, with direct completion
and a stable left list/right detail layout for instructions, source content and AI
comments. The divider can be dragged and remembers its width; each note group
can independently collapse its tasks while retaining its title and count.
Narrow containers use on-demand detail with an explicit return to the
mounted list. One content-sized button switches to the other view in the page
header immediately before New note; status filters sit beside search and wrap
when needed. Note detail removes the surrounding frame and stream padding while
keeping editor borders and the AI divider. Returning from a source note restores the task
context and protects unsaved editor changes. A successful save followed by no
further edits does not prompt again on return. Narrow note layouts use an index
drawer and bounded bottom AI pane.
## Documents
- [Technical design](./technical-design.md): file storage, schema 38 migration,
backup boundaries, database notifications, process boundaries, canvas and AI
contracts, refresh lifecycle and regression coverage.
- [Progress](./progress.md): implementation and validation evidence.
- [UI design system](../../../UI-DESIGN.md#137-魔法笔记): authoritative workbench
interaction rules, including selection and draft preservation.
- [Canvas integration](../../../src/renderer/src/magic-canvas/README.md): React
controller API, PeopleLib core limits, asset adaptation and rendering lifecycle.
An entry is a saved rich-text or paged-canvas record within a note. Its body lives
in `notes/<noteId>/entries/<entryId>.json` beside note-local binary assets;
`note.json` is a derived membership manifest. SQLite retains metadata, indexes,
revisions, todos and comments. API reads hydrate assets back into the existing
content format. A todo is a checklist item derived from rich text
or the canvas Quill flow. An unsaved draft belongs to the open editor; it is not
written to storage by a background refresh.
Backups must coordinate SQLite and the adjacent `notes/` tree while note writes
are paused or the app is closed. A database-only backup cannot restore these
bodies. There is no product one-click backup action; see the
[backup contract](./technical-design.md#backup-and-restore).
## Canvas Scope
The integrated PeopleLib editor uses Fabric for paged annotations and Quill for
body text flowing across pages. It supports paper templates, pen/highlighter,
object erasing and selection/transforms, floating text, images, PDF backgrounds
with native text extraction, and raster PDF export. Saves are manual. Undo/redo
belongs to the current annotation page or Quill body mode; it is not a global
document history. There is no infinite canvas or PNG download button.
Editors and saved viewers support view zoom and responsive Fit width without
changing saved content or export dimensions; see the
[canvas zoom reference](../../../src/renderer/src/magic-canvas/README.md#view-zoom).
Drafts, saved entries and todos sourced from canvases support AI comments using
the default model's image-input capability. The Canvas pages to send setting
selects the first 1-8 pages in current canvas order, defaulting to 1. It counts
pages in one canvas, not entries, and leaves full-canvas saving and export intact.
Image-capable models receive complete captures and extracted text for that prefix.
Text-only models receive the same page range as an explicitly labelled
text fallback; a purely visual canvas entry or draft reports that an image-capable
default model is required. Canvas comments are requested manually in `immediate`
mode, or after a manual save in `after-save-auto` mode. Analysis failure does not
undo a successful save. Detailed retention rules are in the
[AI contract](./technical-design.md#canvas-analysis-and-comments).
Canvas implementation is complete and final validation is in progress. The
[progress record](./progress.md) distinguishes confirmed production-path evidence
from the pending final repository checks.
+734
View File
@@ -0,0 +1,734 @@
# Magic Notes Progress
## 2026-09-24: Resizable Todo List and Collapsible Note Groups
- Todo list/detail uses a 1px divider with a transparent 9px pointer target and
8px content padding on each side. Dragging and keyboard resizing persist the
list width; narrow layouts retain the saved desktop preference.
- Source-note headings independently collapse their task rows while retaining
the count and selected detail. Buttons support native keyboard activation.
- Focused workspace, disclosure and Electron layout tests passed: 3 files,
115 tests. Native Electron input verified dragging, keyboard expansion,
width restoration after reload and narrow-window transitions. Typecheck and
lint passed. A full-suite validation attempt was cancelled; no full-suite
success is claimed for this change.
## 2026-09-20: Shared Inline Help
The canvas page-count question mark now uses `InlineHelp`, superseding the native
`title` mechanism recorded below. The overview description, comment-direction
help and new text-entry hints also use the shared component. Interaction rules
are maintained in [the UI design system](../../../UI-DESIGN.md#614-inlinehelp-同行帮助).
The final verification ran `npm test -- --reporter=default --reporter=json
--outputFile.json=<temporary-report>`: **412 files passed, 9 skipped; 4,858 tests
passed, 67 skipped, no failures**, in 1,016.17 seconds. The JSON report confirms
`success: true`. `npm run typecheck` and `npm run lint` passed. The focused help,
application, SSH, Skills and MCP run also passed **6 files / 54 tests**. SSH password
help now keeps authentication and encrypted-storage consequences visible; Skills
and MCP headers no longer retain an empty description paragraph. Pin help and
disable consequences use separate Chinese and English translation keys.
`tests/inline-help.electron.test.ts` renders production `ApplicationCenter` /
`ApplicationSettingsView`, `SettingsPanel`, retrieval and activity components with
fixture data. Native Electron input verified hover, focus, click pinning, Escape
isolation, outside dismissal and Modal ownership. Chromium AX checks confirmed
unique description IDs and accessible text before, during and after expansion.
Light/dark checks cover 1280x800 and 640x480 consumer views, plus 1000x700 and
360x320 shared-component views. Computed styles and captured screenshots confirm
that canvas-count and pin help match model settings: 28px buttons, 14px icons,
4px label gaps, zero center offset, identical button colors/margins and tooltip
typography/padding/borders in each theme. No CSS difference was reproduced, so
this pass adds comparison assertions without changing styles.
The focused run retained 28 screenshots and `evidence.json` under the temporary
`inline-help-final` artifact directory selected by `GOODBUDDY_HELP_ARTIFACTS`.
This validates real Electron rendering with fixture services, not a packaged
full App or live provider. Help actions triggered zero consumer operations and
zero model calls. The full suite also passed both production canvas-analysis
Electron cases with substituted model output; live-provider checks remain opt-in.
## 2026-09-19: Compact Page Count Setting
- The page-count select now uses the existing `.field` settings control styling.
A title-adjacent `CircleHelp` icon uses the shared `icon-button` and native
`title` hint; the full description is screen-reader-only and remains associated
with both controls through `aria-describedby`. The compact row uses flex and
keeps the label and help icon together with inline-flex, including at narrow
widths. Control styling and the native hint mechanism remain shared. Page
selection and persistence behavior are unchanged.
- `npx vitest run src/renderer/src/ApplicationCenter.test.tsx` passed 14 tests.
Focused ESLint and a temporary TypeScript configuration rooted at
`ApplicationCenter.tsx` with its imported dependencies passed. No full suite,
full typecheck, Electron/live-model test or model request was run in this UI pass.
- The commit preparation had only inspected status, diffs and history. The index
remained empty and HEAD remained `ecf09f4`; no staging or commit was performed.
The later same-line adjustment passed the same 14-test UI suite before commit.
- The coordinating agent subsequently reported full typecheck and lint passing.
The first full test command timed out at 200 seconds; the second completed but
exposed only the RUN banner without a summary. No full-suite pass is claimed.
## 2026-09-19: Canvas Pages to Send
- Implemented the page-count preference and input selection described in
[Canvas Analysis and Comments](./technical-design.md#canvas-analysis-and-comments).
Application settings and configuration tools persist the same field; historical
missing values read as 1. All three production analysis handlers use the saved
count for text and images. Saving retains the complete canvas.
- Focused verification passed **15 files / 271 tests**:
`npx vitest run src/main/application-settings-store.test.ts src/main/magic-notes/magic-note-analyzer.test.ts src/main/magic-notes/canvas-ipc.integration.test.ts src/renderer/src/ApplicationCenter.test.tsx src/renderer/src/MagicNotesWorkspace.test.tsx src/renderer/src/MagicCanvasEditor.test.tsx src/renderer/src/MagicCanvasThumbnail.test.tsx src/renderer/src/magic-canvas src/shared/goodbuddy-config-contracts.test.ts src/shared/magic-note-pages.test.ts src/main/goodbuddy-config-service.test.ts src/renderer/src/UpdateSettingsSection.test.tsx`.
Coverage includes 1/8 boundaries, invalid counts, missing historical fields,
restart persistence, settings events, config-tool writes, 50-page source
preservation, current page-array order, selected PDF/object/flow text, missing
flow text and selected pages with no readable text.
- `npx vitest run tests/magic-notes-analysis.electron.test.ts` passed **2 tests**,
one per image capability. The real settings form changes the default from 1 to
2 through production preload/IPC and the file-backed settings store. Workspace,
Quill and Fabric then analyze saved entries, edited saves, new drafts, new saves
and canvas-source todos through registered IPC/analyzer/SQLite. Explicit page
breaks and automatic overflow both exclude later-page text. Browsing page three
at 75% zoom still sends pages one and two. This exposed scaled DOMRect rounding
at column starts; the corrected boundary retains each page's first character.
- `npx vitest run src/renderer/src/App.test.tsx -t application` passed **5 tests**
with 279 unrelated cases filtered out. Focused ESLint and `git diff --check`
passed. Full `npm test`, typecheck and lint were not run, per user request.
- Electron uses an isolated feature window, real application code and temporary
data, with DOM actions/Quill edits and substituted model output. It does not
validate a packaged full App, physical input devices or a live provider.
Real model calls: **0**. Analysis uses desktop `createDefaultModelRuntime`;
deployed gbagent and desktop-to-Agent execution are unaffected. Existing user
changes were retained; no commits or user-data writes were made by this task.
## 2026-09-19: Canvas Content and History Preservation
- Page-break deletion now removes only the embed, retaining existing paragraph
terminators and checklist/heading formats. Mid-line insertion retains Quill's
native paragraph split rather than guessing which newline is disposable.
- Oversized input is rejected before the canvas-local History records it, then
reverted with its inverse Delta. Accepted undo/redo and user selections survive;
normal rich-text editors retain their original History module.
- `npx vitest run tests/canvas-core.electron.test.ts src/renderer/src/magic-canvas`
passed 35 tests across 5 files. Electron exercised actual Quill selection and history, then production
SQLite/file create, update and reopen retained the same checklist todo.
This uses Quill API edits in an isolated Electron window, not physical keyboard
input or the full App/IPC save flow.
- Focused ESLint for the four changed source/test files and `git diff --check`
passed. The integrated full suite passed 4,826 tests with 67 skipped
(405 files passed, 9 skipped); full typecheck and lint passed.
The existing hidden-window layout scenario measured about 63 seconds, so its
child deadline is now 90 seconds with bounded temporary-directory cleanup
retries. All geometry, input and persistence assertions remain intact and passed.
No model calls, persistence/schema changes, remote Runtime changes or commits.
## 2026-09-19: Canvas Image Import Size
- Raised the canvas image file limit to 20 MiB inclusive and updated its error
message and derived restoration bound. The authoritative limit is in the
[canvas integration reference](../../../src/renderer/src/magic-canvas/README.md#editor-limits).
- Real Electron exercised File input, FileReader, Fabric insertion, flush, model
conversion and remount: 2 MiB + 1 byte and exactly 20 MiB were retained;
20 MiB + 1 byte was rejected with the updated message. Fixtures use a valid
one-pixel PNG padded to each byte boundary, not a large decoded pixel image.
- Production preload/registered IPC handlers with mocked Electron transport and
real SQLite/files passed 20 MiB create, update and database reopen checks.
Shared canvas validation and file persistence impose no smaller byte budget.
Rich-text attachment budgets and the PDF import limit are unchanged.
- Validation: `npx vitest run tests/canvas-core.electron.test.ts src/main/magic-notes/canvas-ipc.integration.test.ts src/main/magic-notes/magic-note-storage.test.ts src/main/magic-notes/rich-content.test.ts src/renderer/src/magic-canvas`
passed 66 tests in 8 files. `npm run typecheck` and `npm run lint` passed.
The full suite and a combined native Electron IPC save scenario were not run.
## 2026-09-19: Resizable Record Index
- The record index defaults to 168px and supports pointer resizing from its right
edge and keyboard resizing between 140px and 320px. Index and AI separators share
event handlers, styles and accessibility semantics. The existing layout storage
key now includes `indexPaneWidth`; constrained window sizes do not overwrite the
saved desktop width. Layout behavior is defined in the
[technical design](./technical-design.md).
- Collapsing removes both the column and its 9px separator while retaining the
header toggle. At widths up to 800px the existing fixed 168px drawer remains,
without an index separator. Thumbnail images retain contain sizing.
- Focused verification: 147 tests passed across `MagicNotesWorkspace.test.tsx`,
`MagicCanvasThumbnail.test.tsx` and `WorkspacePrimitives.test.tsx`. Coverage includes
pointer bounds/cancel, unrelated pointer IDs, keyboard limits, persistence,
independent collapse and narrow layouts. `npm run typecheck`, `npm run lint`
and `git diff --check` passed. No full test suite was run for this change.
- `npx vitest run tests/magic-notes-layout.electron.test.ts` passed with current
production Workspace/CSS/i18n in Electron and fixture note IPC. Native mouse and
keyboard input verified default/min/max widths, both pane handles, resizing with
AI open and closed, a 329px stream increase when a 320px index is hidden, and
actual localStorage restoration after page reload. Content sizes were 1280x800,
820x720 and 720x640; the constrained desktop stream remained at least 300px,
narrow drawer width was 168px, and no document horizontal overflow occurred.
This verifies renderer geometry and browser layout preferences, not the full App
shell or note database persistence. No model calls were made.
## 2026-09-19: Final Full-Suite Verification of Six Fixes
- Ran `npm test` with a 1,200,000 ms command timeout: **4,756 passed,
11 failed, 67 skipped**; files: **395 passed, 1 failed, 9 skipped**.
Started at 12:59:14 and completed in 754.02 seconds. This was a shared,
concurrently changing worktree, not a frozen source snapshot.
- All full-run failures were in `src/main/magic-notes/canvas-ipc.integration.test.ts`.
`TypeError: contextManager.cancelImport is not a function` occurred during
disposal at test line 91 and reopen at line 77 (cases at 198, 238 and 265).
Evidence: the concurrent attachment-import diff adds `ContextManager.cancelImport`
and calls it unconditionally from the IPC disposer; the notes fixture supplied
only `clear`. The output mapped the IPC frame to line 8942; the inspected current
source had the actual call at line 8967.
- Added only the missing no-op `cancelImport` to the notes IPC fixture and the
equivalent environment fixture in `tests/support/magic-notes-analysis-main.ts`.
The latter was exposed by the first focused rerun: **397 passed, 1 failed,
0 skipped**, with the same disposal error reported at
`tests/magic-notes-analysis.electron.test.ts:56`. No production code was changed
in this verification pass.
- Final five-file focused command: `npx vitest run
src/main/magic-notes/canvas-ipc.integration.test.ts
src/renderer/src/MagicNotesWorkspace.test.tsx src/renderer/src/App.test.tsx
tests/magic-notes-analysis.electron.test.ts tests/magic-notes-navigation.electron.test.ts`.
Result: **396 passed, 2 failed, 0 skipped** in 121.12 seconds; files:
**4 passed, 1 failed**. Notes Workspace (108), IPC (11) and both Electron probes
(2) all passed, totaling **121 notes tests**.
- The remaining failures are App project-activity cases acknowledging foreground
and background local success from persisted messages, at
`src/renderer/src/App.test.tsx:1430` (missing completion text) and `:1418`
(still running instead of completed). Standalone `npx vitest run
src/renderer/src/App.test.tsx` reproduced **275 passed, 2 failed, 0 skipped**
in 58.44 seconds. Evidence of the unrelated attachment interaction: test line
1394 takes `saveLocal.mock.calls.at(-1)` and lines 1408-1411 build completion
messages from that call; the concurrent App diff adds an attachment-save queue
calling `saveLocal` with `messages: []` at line 2741, including when send clears
attachments at line 7620. These tests do not enter notes. App and attachment
files were left unchanged by this verification pass.
- Focused ESLint passed for both adjusted fixtures. The previously reported full
typecheck/lint results were not rerun. `git diff --check` passed after the
documentation update (only LF-to-CRLF warnings). The full suite
was not repeated after fixture fixes; this is not an all-green full-suite claim.
Live model calls in this pass: 0. No commits. Earlier progress sections retained.
## 2026-09-19: App Leave Guard and Title Drafts
- App navigation registers the workspace's existing canvas-flush and dirty-draft
confirmation through `onBeforeLeave`. Sidebar, launcher and conversation jumps
share the gate. Settings closes after its own leave approval before notes asks
for confirmation. Cache limits and continuous-record/index behavior are unchanged.
- Non-title responses preserve dirty titles; title-save responses acknowledge only
the submitted input and retain later edits.
- Focused suites passed: `MagicNotesWorkspace.test.tsx` (99 tests) and `App.test.tsx`
(277 tests). The expanded settings-to-notes confirmation cases also passed.
`npm run typecheck`, `npm run lint` and `git diff --check` passed.
- `tests/magic-notes-navigation.electron.test.ts` passed with actual Windows Electron,
Workspace, Quill and canvas components. It checks AI-time title input, pending
canvas flow input, Continue editing and confirmed discard. API responses are
controlled fixtures; this is not a packaged full-App/persistent-IPC probe.
Model calls: 0. Full repository test suite was not run.
## 2026-09-19: Analysis Source Revisions
- Main/shared/DB implement the source revision and comment invalidation rules in
[Canvas Analysis and Comments](./technical-design.md#canvas-analysis-and-comments).
Canvas todo writeback rejects stale sources even without pre-existing comments;
affected saved todo comments are cleared with a revision increment and stable ID.
- Shared `magicNoteCanvasAnalysisText` preserves analyzer page-number semantics.
Regression coverage includes blank-page insertion with unchanged plain text,
geometry changes, missing/stale capture revisions and source edits during runs.
- Focused Vitest: 5 files, 162 tests passed (database, storage, rich content,
analyzer and production preload/IPC integration). `npm run typecheck` and focused
ESLint passed for the initial Main/shared changes.
- Workspace now passes capture revisions, save-response revisions selected by the
exact created/edited entry ID, and selected todo source revisions. It uses the
shared page-numbered analysis text for automatic text-comment analysis decisions.
Draft analysis is unchanged. Regression coverage includes an external refresh
completing during capture without pairing the old image with a newer revision.
- UI adaptation validation: Workspace (108 tests), production IPC integration
(11 tests), both Electron probes (2 tests), and App (277 tests) passed, 398 total.
Workspace fixtures validate every analysis request with the production schemas.
`npm run typecheck`, `npm run lint` and `git diff --check` passed.
- `tests/magic-notes-analysis.electron.test.ts` runs actual Workspace/Quill/Fabric
captures through production preload, registered Electron IPC, source revision
checks, analyzer and SQLite. Saved-entry analysis, edit/create automatic analysis
and source-canvas todo analysis all persist `canvas-images` comments. Only model
output and unrelated environment services are substituted; live model calls: 0.
The earlier navigation/title Electron regression also passes. No full suite run.
## 2026-09-19: Default Composer
- Opening a note now shows a blank text/canvas composer below its title and above
the continuous history. Removed the New record button, locale keys and creation
visibility state. A successful create resets the editor key and content while
retaining its type; automatic analysis does not enter editing. Existing records
still require explicit Edit and retain normalized dirty baselines and revision keys.
- The index toggle stands directly before the title. Header height now explicitly
supplies the narrow drawer offset without relying on the removed button.
This supersedes the explicit-create and post-create-edit behavior recorded below.
- Focused validation: 5 files, 149 tests passed (`MagicNotesWorkspace`, both editors,
`MagicCanvasThumbnail`, `WorkspacePrimitives`). `npm run typecheck`, `npm run lint`
and `git diff --check` passed. No full test suite was run.
- Temp Electron validation rebuilt current Workspace/PageShell, Quill, Fabric, CSS,
production preload and registered IPC, backed by AssistantDatabase and note files.
Text/canvas create-save-reopen, blank composer return without confirmation, real
editor changes requiring confirmation, thumbnail scroll preserving the top draft,
explicit text/canvas edit-save and database close/reopen all passed. Automatic
analysis capture-before-reset is covered by the focused regression; model calls: 0.
- Light/dark checks at actual content widths 1287, 947, 547 and 362px passed document
overflow, continuous history, narrow drawer width, placement below the title and
close-on-navigation assertions. Reviewed wide light and narrow dark drawer PNGs.
Evidence: `C:/Users/jiang/AppData/Local/Temp/opencode/composer-default-Tq8Ui8/`;
runner: `node C:/Users/jiang/AppData/Local/Temp/opencode/run-composer-default.mjs`.
- Probe boundaries: isolated feature window, not the full App shell; Quill API input
and DOM button clicks, not native keyboard coverage. Early probe failures came
from inactive canvas text-tool input, an unconstrained fixture parent, and measuring
the inner navigation width instead of the drawer border box. Corrected probes
passed without further production changes. No commits or real user-data writes.
## 2026-09-19: Independent Layout Review
- Rechecked the action-placement changes in Electron using production PageShell,
Workspace, Fabric, Quill and CSS. The earlier bare-workspace screenshots omitted
PageShell gutters; no extra stream padding was added to compensate for that fixture.
- Fixed narrow record headers forcing each action onto a separate full-width row,
touching canvas inner/outer borders in create/read mode, and missing spacing below
the Records / AI tabs. Canvas create/edit/read inner frames now share 12px insets.
- Fixed Quill percentage heights stretching an empty text composer beyond the
visible area. The text save footer and validation now belong to the same composer
card. Save handlers, analysis rules and draft-switch semantics are unchanged.
- Text toolbar SVGs and picker menus use theme colors; native scrollbars in the
notes workspace follow light/dark mode.
- Final Temp probe: `canvas-layout-review.mjs final`, with captures prefixed
`canvas-layout-final-` and measurements in `canvas-layout-final-metrics.json`.
Checked create/edit/read for canvas and text at 1300x950, 960x800, 560x800 and
375x720 content sizes in both themes (48 combinations), plus AI-pane captures.
Assertions passed for document/main horizontal overflow, canvas action bounds
and equal heights, independent side scrolling, reachable text Save, and side
collapse/reopen. Inspected final wide, medium, narrow and deep-narrow dark images.
- Focused validation: `npx vitest run src/renderer/src/MagicNotesWorkspace.test.tsx
src/renderer/src/MagicNoteEditor.test.tsx src/renderer/src/MagicCanvasEditor.test.tsx`
passed 102 tests. `npm run typecheck` and `npm run lint` passed. No full suite run.
- Scope: actual Windows Electron renderer with an in-memory DesktopApi fixture,
not a packaged full-App or persistent-IPC validation. No model calls, runtime or
remote changes, commits, or user-data writes.
## 2026-09-19: Canvas Action Placement
- Canvas creation places Analyze and Save at the right of the type selector;
editing places Analyze, Cancel and Save in the record header. Save remains the
primary action. Canvas hints and footer actions were removed, including unused
English/Chinese locale keys. Text actions and existing handlers are unchanged.
- Focused workspace/editor tests: 2 files, 93 passed. Typecheck and lint passed.
Coverage includes all three comment modes, top placement, order, no duplicate
footer, cancellation, saving and manual/automatic analysis. No full suite run.
- A Temp Electron/Vite probe uses production Workspace, Fabric, Quill and CSS
with an in-memory DesktopApi fixture. Creation and editing were checked at
1300px and 560px window widths for top placement, visible Save and no horizontal
document overflow. Screenshots are `canvas-actions-{create,edit}-{wide,narrow}.png`
in the Temp opencode directory. No real model calls were made.
## 2026-09-19: Review Corrections
- Fixed local creation selecting an Agent's concurrent append. The database
returns the exact `createdEntryId` alongside detail, typed through shared API
and preload. The workspace uses that ID for selection, analysis and subsequent
edits. Existing Agent/MCP note projections retain their public shape.
- Text-edit save/cancel now use the shared analysis cleanup: pending timers and
queued content are removed, late comments are ignored, and abandoned settings
preparation cannot dispatch a draft request. The composer content is independent.
- Local and external deletion reconcile the actual selected ID when no dirty
editor must be retained. A later append no longer changes the fallback selection.
- Thumbnail abort immediately invokes core destruction, whose synchronous phase
cancels PDF/Fabric work. Final cleanup awaits the same promise and removes the
temporary surface. Regression tests cover both unmount and content replacement
while capture is pending, and verify the next queued thumbnail completes.
- Post-fix focused run: **13 files, 257 passed, 0 failed**. Coverage includes
`MagicNotesWorkspace`, `MagicCanvasThumbnail`, editor/content/core/model tests,
production canvas IPC integration, note storage, assistant database, MCP gateway
and preload note events. `npm run typecheck` and `npm run lint` passed.
- The user reported a full-suite result of **4,713 passed, 0 failed before these
review corrections**. That result does not validate the fixes above. No full
suite or Electron probe was rerun in this correction pass. Model calls: 0.
## 2026-09-19: Record Index Header Toggle Correction
- Moved the record index toggle immediately before New record in the detail
header, retaining its accessible name, title, controls and expanded state.
Collapsing hides the whole index and removes its grid column and border width.
Narrow layouts use the same toggle for a 168px overlay below the header, with
no reserved rail. This supersedes the 40px rail described in the earlier run.
- Focused validation: `npx vitest run src/renderer/src/MagicNotesWorkspace.test.tsx
src/renderer/src/WorkspacePrimitives.test.tsx` passed 130/130 tests.
`npm run typecheck` and `npm run lint` passed. No full suite was run.
- Actual Electron geometry checks compiled the current Workspace and production
CSS with fixture note data, covering window widths 1300, 960, 700 and 375px with
AI both open and closed. Wide stream widths increased by exactly 168px on
collapse; narrow stream widths stayed at 687.43 and 362.29px. Hidden index
geometry was zero, closed streams began at the layout's left edge, and no
horizontal overflow occurred. The header toggle remained hit-testable directly
before New record. Native Tab skipped hidden records, programmatic focus on
hidden records failed, Enter reopened the index, and narrow Escape restored
toggle focus. AI widths were unchanged by index toggling.
- Evidence: `C:/Users/jiang/AppData/Local/Temp/opencode/record-index-results-Xvgnsw/results.json`.
Reproduce with `node C:/Users/jiang/AppData/Local/Temp/opencode/run-record-index-geometry.mjs`.
This checks an isolated Electron renderer, not the full App shell or storage
path. Model calls: 0.
## 2026-09-19: Continuous Stream and Save Baseline Correction
- Restored all entries to the continuous stream, including while composing. The
168px left index and independent right AI pane collapse separately. Index and
AI source clicks scroll without discarding drafts or remounting the editor.
At widths up to 800px the index uses a 40px rail with a 168px drawer; AI retains
its bounded bottom layout. This supersedes the single-record interaction below.
- Dirty checks now compare against the editor's initialized content. Canvas waits
for its initial flush before enabling input or reporting readiness; text reports
Quill's initialized Delta. Save/analysis revisions remount with a fresh baseline,
while unchanged background revisions retain it. Failed writes do not advance it.
- The actual saved formatted-text callback had the same values but a different
Delta field order (`insert, attributes` in storage; `attributes, insert` from
Quill), reproducing the old false dirty comparison. Saved PDF content matched
both the readiness callback and subsequent flush in the final probe.
- Final focused validation: 15 files / 222 tests passed, covering Workspace, both
editors, thumbnail/content, shared primitives, canvas core/model, analyzer, file
storage, production canvas IPC and canvas text. `npm run typecheck` and
`npm run lint` passed.
- The full suite completed with 4,730 passed, 1 failed and 67 skipped (713.99s).
Its sole failure was the shared contrast test finding the first dark descendant
rule instead of the root token block. The test now matches the exact root
selector; all 36 shared primitive tests passed afterward, also included in the
final 222-test run. The full suite was not repeated after that test fix and the
final editor initialization regression additions.
- Actual Electron 43.2.0 validation used built renderer assets over `file://`, the
production preload, IPC handlers, AssistantDatabase and note files. It verified
three simultaneous entries, real thumbnail pixels, scroll-only draft retention,
independent collapse, text/canvas create and update followed by a clean return,
a new post-save edit requiring confirmation, and a real revision conflict keeping
the failed draft. A two-page PDF survived database reopen and three consecutive
saves in the same editing context, with unchanged PDF bytes, advancing revisions
and no prompt on return. Six entries remained mounted through the layout checks.
- Layout captures covered light/dark at content widths 1287, 947, 547 and 362px,
with no document horizontal overflow. Narrow drawer opening, 168px geometry,
record navigation and automatic closing passed. Temporary evidence is under
`C:/Users/jiang/AppData/Local/Temp/opencode/canvas-sqlite-N9japl/`; reproduction:
`node C:/Users/jiang/AppData/Local/Temp/opencode/run-canvas-sqlite.mjs continuous-notes.html`.
The earlier development-server attempt hit Vite PDF dependency optimization;
the built-asset run passed. This validates the production feature path in an
isolated Electron window, not the full App shell or a packaged distribution.
- `node tests/magic-canvas-wheel.electron.mjs` passed all 34 wheel and full-page
height checks. No storage/core wheel changes or model calls were needed.
## 2026-09-19: Single-Record Workspace (Superseded)
- Note detail mounts one selected record or an explicitly opened new-entry editor.
Saving a new entry selects it; further saves update the same ID and use revisions
returned by saves and automatic comments. Record/AI source switches share draft
confirmation, and clean external changes refresh without replacing dirty drafts.
- A collapsible Records / AI comments region defaults to Records. Each canvas
record has one real first-page thumbnail with total page count; text records show
summaries. Visible thumbnails render serially and capture only page zero at 240px.
The main editor and side region scroll independently; narrow layouts retain the
bounded bottom region. UI contract: [design system](../../../UI-DESIGN.md#137-魔法笔记).
- Focused validation: 9 files / 142 tests passed across workspace, canvas editor,
content, shared primitives, canvas core/model and canvas IPC integration.
`npm run typecheck` and `npm run lint` passed. No full `npm test` run in this task.
- Actual Electron renderer probe used production Workspace, Quill, Fabric and CSS
with an in-memory DesktopApi fixture. It verified one main entry, real thumbnail
pixels, one thumbnail for a two-page entry, record switching, independent scroll,
collapse/reopen, AI switching, discard/cancel, AI and todo source selection,
returning to todo context, and preserving canvas drafts through external updates
and deletion. One create followed by three updates included two
canvas updates and continuing to edit a newly created record. At 547px content
width the document scroll width remained 547px. This is renderer validation,
not a complete App-shell or persistent-IPC end-to-end run.
- Reproducible local probe: `C:/Users/jiang/AppData/Local/Temp/opencode/run-single-entry-probe.mjs`;
screenshot: `single-entry-narrow.png` in the same temporary directory.
- Existing `node tests/magic-canvas-wheel.electron.mjs` passed all 34 Electron
checks for wheel behavior and full-page editor/viewer height at 900px and 360px.
Storage, wheel and page-height behavior were retained. Model calls: 0.
## 2026-09-19: Full-Page Canvas Height
- Removed fixed saved-viewer and composer heights. Natural layout includes the
current paper, toolbar and padding, with outer vertical scrolling and inner
horizontal scrolling. Different page sizes still paginate within one entry.
- `node tests/magic-canvas-wheel.electron.mjs`: 34 checks passed in Electron
43.2.0, including editor/saved-viewer geometry at 900px and 360px, portrait to
landscape pagination, no inner vertical or outer horizontal overflow, and
native outer vertical scrolling. This mounts the production core with product
CSS and representative wrappers, not the complete App shell. The original
wheel fixture has an explicit test-only height constraint to retain both-axis
coverage; edge tests wait for the compositor after programmatic scrolling.
- Focused canvas/editor/content/workspace tests: 7 files, 95 tests passed.
Full typecheck and lint passed; the final probe edit also passed focused lint.
The full test suite was not rerun for this height-only change. Model calls: 0.
## 2026-09-19: File Storage and Schema 38
- Replaced SQLite body payloads with file-backed rich-text and canvas entries.
SQLite retains metadata, membership, indexes, revisions, todos and comments;
hydrated API content stays compatible. File layout, write/repair semantics,
migration and backup requirements are defined in the
[storage contract](./technical-design.md#file-storage-and-writes).
- The startup worker converts one entry per transaction and verifies hydrated
readback before clearing the legacy payload. Reclamation checks the freelist
on every attempt, including retries after conversion has finished. Committed
saves/deletes/resets remain successful when manifest or GC cleanup fails;
failures are logged and cleanup is retried on later access or reopen.
- Storage implementation handoff reports 135/135 focused tests passed across
`magic-note-storage`, `assistant-database`, `assistant-storage-upgrade` and
`canvas-ipc.integration`, with full `npm run typecheck` and `npm run lint`
passing after the review fixes. Filesystem rename/rm failure injection uses
real SQLite and files. A 1 MiB PDF retained its binary mtime across 20 updates
while SQLite growth stayed below 2 MiB; the migration regression verified
more than 2 MiB of physical shrink and restored SQLite plus the notes tree.
- Session validation reports the real Electron -> IPC -> SQLite/files path
successfully created text, image and canvas/PDF entries, completed three
consecutive updates and reopened persisted data after process restart.
Migration also passed with an on-disk schema 37 fixture. This was a constructed
fixture, not a user's historical database; no real user database was migrated.
Actual model calls for this storage validation: 0.
- Backups require coordinated SQLite and `notes/` copies while writes are paused
or the app is closed. No product one-click backup was added.
- Final full test run: 4,705 passed, 3 failed, 67 skipped (760.91 seconds).
All three failures were in `acp-remote-runtime.test.ts`, which was being changed
concurrently: a launch-response schema mismatch and two channel-close recovery
assertions. That file subsequently passed all 100 tests on a targeted rerun.
No note-storage test failed; this does not claim a second all-green full run.
## 2026-09-19: Canvas Wheel Navigation
- The shared canvas viewport explicitly handles horizontal and vertical wheel
input in annotation, flow-text and read-only modes. Shift with vertical-only
input scrolls horizontally. Ctrl input was initially left to the browser;
the View Zoom update below assigns Ctrl/Command + wheel to canvas zoom.
Default scrolling is prevented only when the viewport actually moves; page
scrolling remains available at boundaries or without canvas overflow.
- Removed scroll-chain containment. The Electron baseline reproduced boundary
trapping, but not a universal failure of native horizontal input. The new
Electron regression passed 30 scenarios; focused unit tests passed 13 tests.
Run `node tests/magic-canvas-wheel.electron.mjs` to repeat the native-input
fixture. It does not validate a physical mouse or the complete App shell.
- Typecheck and lint passed. The full test run during this fix reported 4,682
passed, 67 skipped and one unrelated Agent package inventory timeout.
## 2026-09-19: Canvas View Zoom
- Implemented editor/read-only zoom using the shared core. Controls and view-only
invariants are documented in the
[canvas reference](../../../src/renderer/src/magic-canvas/README.md#view-zoom)
and root UI design. The persisted model and export dimensions are unchanged.
- Real PDF plus flow validation reproduced an existing cleared-background defect:
automatic pagination reassigned an unchanged canvas width/height, erasing its
bitmap. Layout now retains the bitmap when dimensions are unchanged.
- Focused validation passed 9 files / 140 tests: `canvas-wheel`, `canvas-flow`,
`canvas-note`, canvas `model`, `MagicCanvasEditor`, `MagicCanvasThumbnail`,
`MagicNoteContent`, `MagicNotesWorkspace`, and `tests/canvas-core.electron.test.ts`.
- `node tests/magic-canvas-wheel.electron.mjs`: 41 checks passed in Electron 43.2.0.
Native input covered pen coordinates and object move/scale at 50% and 200%,
second-page Quill click/typing, Ctrl wheel, ordinary/Shift/diagonal wheel and
boundary propagation. Real PDF background pixels, unchanged PNG captures and
794-by-1123 output dimensions, stable flow pagination, zero view-only changes,
readonly controls, displayed page heights and fit-width resize all passed.
Dark narrow layout included a real 560px window resize across the responsive
padding breakpoint while the host width remained 360px.
- `npm run typecheck` and focused ESLint on all six changed JavaScript/test files
passed. Final `npm run lint` encountered two `react-hooks/immutability` errors
at lines 18/27 of concurrently added `src/renderer/src/FloatingPortal.tsx`;
those parallel changes were left intact. `git diff --check` passed.
- No full test suite, production build, physical-device test or macOS session was
run in this change. Meta-wheel is covered by focused tests. The Electron fixture
uses production canvas modules/styles with real Fabric, Quill and PDF.js; it
does not claim complete App-shell or release-package validation. No model calls
or commits were made. Deployed Agent paths are unaffected.
## 2026-09-19: Single Navigation Button and Unframed Note Detail
- Replaced the two header tabs with one destination-labelled button and icon,
immediately before New note. Switching retains the same button and restores
focus to it. The list section keeps its localized accessible name; tab roles
and references are removed. Navigation still uses the existing draft guard.
- Note detail removes the surrounding layout border/radius and stream padding.
Composer and entry borders, page margins and the AI divider remain. Overview
cards and the independent todo layout retain their frame and spacing.
- Focused workspace, editor, shared-primitives and i18n suites passed 122 tests.
`npm run typecheck`, `npm run lint` and `git diff --check` passed. The full suite
was not rerun for this change, as requested.
- Updated the existing `magic-notes-render-electron.cjs` probe in the session's
`opencode` temporary directory and ran `magic-notes-render-run.cjs` against the
current workspace component and real Quill in Electron. At 1280x820, 600x760,
600x520 dark and 420x640, the switch measures 104x36px and precedes New note.
Pointer and Space activation restore switch focus; no tab semantics remain.
- Computed detail border/radius and stream padding are all zero. Composer and
entry borders and the AI divider remain 1px. At 1280px, closing AI expands the
stream from 927px to the full 1216px layout width. Narrow layouts keep only the
native scrollbar width between layout and content, with no horizontal overflow.
AI moves below the editor and closing it increases editor height.
- The same probe passed card/scroll return, actual Quill draft cancellation and
discard, todo split/narrow layouts, source-note return, creation and completion
filters. Screenshots were reviewed; results are in
`magic-notes-render-results.json`. Console errors: 0; model calls: 0. Data and
AI output use fixtures. The esbuild probe emits an existing IIFE/import.meta
warning for the canvas PDF module; this run does not exercise PDF import.
## 2026-09-18: Integrated Canvas and Validation
Historical snapshot before file storage: the embedded payload and schema 37
statements below describe this stage only. Current persistence and its validation
are recorded in the schema 38 entry above.
- Implementation is complete across the workspace, PeopleLib Fabric + Quill
editor/viewer, shared v1/v2 contract, preload/IPC, SQLite and AI analysis.
Available operations include paged flow text, pen/highlighter, selection and
transforms, floating text, images, PDF import/native text extraction and raster
PDF export. Saving is manual; undo/redo is local to the page or Quill mode.
The toolbar has no PNG download action; PNG capture is an API used for analysis.
- Assets are embedded in SQLite `content_json`, not a separate resource table.
Schema 37 is a compatibility marker without conversion of existing records.
Flow checklist identity survives object moves and completion writes back to
flow. MCP exposes readable canvas metadata/text and rejects plain-text
replacement. See the [technical contract](./technical-design.md).
- Entry, draft and canvas-source todo analysis use the default model's
`supportsImageInput`: complete page captures plus extracted text, or labelled
text-only fallback. Purely visual entries/drafts fail clearly with a text-only
model. Canvas `immediate` mode explicitly offers manual analysis;
`after-save-auto` catches capture/settings errors without blocking storage.
Text-only entry comments survive equal `plainText`; layout changes invalidate
visual comments and trigger renewed analysis in after-save-auto mode.
- Confirmed interactive validation used the real workspace, preload, registered
IPC and SQLite to save an image and a two-page PDF, reopen them, and complete
three consecutive saves successfully. This covers persistence beyond the
earlier renderer-only fixtures. The temporary probe uses
`canvas-sqlite.tsx`, `canvas-sqlite-main.ts` and `run-canvas-sqlite.mjs` under
the session's `opencode` temporary directory.
- The isolated canvas core probe passed with a Vite production build loaded via
`file://` and the unchanged application CSP, including PDF import, PNG capture
and raster PDF download. The export review also verified the 20,000-character
flow boundary and list markers in captured pages and re-rendered exported PDF.
These are core rendering/export results, distinct from workspace persistence.
- Regression coverage includes storage reopening, checklist writeback, comment
retention/invalidation, revision conflicts, MCP protection, renderer integration
and analysis input selection. The preload/IPC integration suite uses real
SQLite but mocked Electron transport and model output; it is not evidence of
a live model request.
- Final `npm run typecheck`, `npm run lint` and `npm run build` passed. The full
test rerun completed with 4,665 passed, 7 failed and 67 skipped. Failures were
in Agent packaging (missing compound-package fixture and an inventory timeout)
and activity record/DOM reuse assertions. Those files were under concurrent
modification; this is not a fixed-snapshot all-green result. No Magic Notes or
App regression failed in that run. The earlier two App failures from eager
PDF.js loading were fixed with on-demand PDF imports and passed their targeted
rerun; core/editor/workspace focused coverage passed 87 tests.
- Live provider validation was blocked before any request: an isolated Electron
process could not decrypt the configured default model credential
(`runtime-model-credential-unreadable`). Settings were only read and remained
unchanged. Actual model calls: 0. Runtime request tests verify image input and
text fallback, but do not establish successful live provider generation.
- This canvas change does not alter the deployed Agent runtime or the
desktop-to-Agent production path, so it requires no separate remote Linux
validation. Concurrent runtime work has its own validation scope.
## 2026-09-18: Stable Todo Split View and Header Navigation
- Supersedes the inline task expansion described in the earlier entry below.
Wide containers now keep the grouped task list on the left and instructions,
source and AI in one right detail pane. Narrow containers retain the mounted
list and offer on-demand detail with an explicit return. Source-note return
preserves task context. Header tabs precede New note; filters follow search.
- Focused workspace, editor and shared-primitives suites passed all 93 tests.
`npm run typecheck` and targeted ESLint passed. No full `npm test` was run.
- Reused the temporary Electron renderer probe at 1280x820, 600x760,
600x520 dark and 420x640. The 134x36 header switch precedes New note at all
four sizes. Search/filter share a row at 1280 and 600; filters wrap at 420.
The wide list's width, top, scroll and adjacent row coordinates remain equal
before and after task selection/switching. Narrow returns preserve nonzero
list scroll and restore focus. No horizontal overflow or console errors.
- Also verified source-note round trips at all sizes, actual Quill draft
confirmation, note-card return, AI visibility, task completion/status filters,
AI fixture results, creation and Chromium keyboard activation.
- Visible-window capture initially failed with Electron `UnknownVizError`.
Final probe uses Electron offscreen rendering at scale 1, pointer input and
Chromium DevTools keyboard input. It runs production React/Quill/CSS with
in-memory API fixtures, not the full App/Main/SQLite/model path. Model calls: 0.
Evidence remains under the temporary `opencode` directory in
`magic-notes-render-results.json` and `magic-todos-*-expanded.png`.
## 2026-09-18: Independent Todo Task View
- Replaced todo full-page navigation with a grouped task list and one inline
expansion for instructions, source content and AI comments. Completion works
directly in each row. Search/status controls stay above the scrolling list;
source-note navigation retains expansion, filters and initiating focus.
- Kept the note-card overview and note editor/AI layout. Notes/To-dos PageTabs
now size to their contents at desktop and narrow widths. Source failures offer
an inline retry alongside the existing notification.
- Focused command: `npx vitest run src/renderer/src/MagicNotesWorkspace.test.tsx src/renderer/src/MagicNoteEditor.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx`:
93 tests passed. `npm run typecheck` and ESLint on the workspace, its tests and
both changed locale modules passed. Full `npm test` was not rerun, as requested.
- Reused the isolated Electron renderer probe at requested content sizes
1280x820, 600x760, 600x520 (dark) and 420x640. All four todo views retained
18 tasks, inline expansion, independently scrolling lists and no horizontal
overflow or detail/AI sidebar. Tabs measured approximately 133x35 CSS pixels.
Note-card navigation, actual Quill draft confirmation, AI visibility, source
navigation, direct completion, status filters and inline AI fixture output passed;
the renderer reported no console errors.
- The probe uses production React, Quill and CSS with in-memory API fixtures and
isolated Electron sessions; it does not validate Main/SQLite or a live model.
Model calls: 0. Scripts, screenshots and JSON results remain in the existing
temporary `opencode/magic-notes-render-*` probe location.
## 2026-09-18: Card Overview and Detail Navigation
- Replaced the persistent inner list with a responsive card overview and explicit
detail navigation. Search, todo filters, overview scroll and initiating focus
survive a round trip. Creation opens the new note; todo source navigation returns
to the todo overview. Back navigation protects entry and title drafts.
- Kept AI visibility and resizing preferences, removed obsolete list resizing,
and capped stacked AI height so short windows retain usable editor space.
- Focused workspace, editor and shared UI suites passed 90 tests. Typecheck and
full lint passed. The first full test invocation exceeded its 200-second limit.
- Isolated Electron rendering exercised the actual workspace, Quill and CSS at
1280x820, 600x760, 600x520 (dark), and 420x640. Navigation, creation, todo-source
jumps, draft confirmation, AI visibility, scroll and focus restoration passed.
These checks used in-memory API fixtures, not Main/SQLite integration; no real
model calls were made. Canvas functionality is outside this layout change.
## 2026-09-17: Note List Action Menu
- Moved pin/unpin and whole-note deletion from the editor header to each note's
action menu, following the [UI design system](../../../UI-DESIGN.md#137-魔法笔记).
The menu uses existing update/delete IPC and shared destructive confirmation.
- Added regression coverage for menu keyboard behavior, portal placement,
dismissal, pin ordering, revision failures, deletion and retained drafts.
- The focused Magic Notes suite passed 42 tests. The combined Magic Notes,
conversation and shared-primitives suites passed 332 tests. Typecheck and
targeted ESLint passed.
- Full `npm test`: 370 files and 4,440 tests passed, 2 files and 3 tests failed,
9 files and 67 tests skipped. Two heartbeat migration fixtures relabel a current
database as an old version and fail on the existing `pinned` column. One
OpenCode runtime-reuse request timed out; its two tests passed on isolated rerun.
- Full lint reported 22 `document` no-undef errors in the unrelated, untracked
`docs/features/story-graph/demo.js`. No unrelated files were changed to resolve
these validation failures. Interactive Electron and touch verification remain
unperformed.
## 2026-09-15: Automatic External Refresh
- Implemented the shared-database notification and Main/preload subscription
described in [technical design](./technical-design.md).
- The open workbench reloads external changes while retaining current selection
and unsaved drafts. Deleted notes and edited entries retain drafts with an
explicit explanation. The cancelled manual-refresh proposal is not implemented.
- An initial completed `npm test` run passed 357 files and 4,201 tests, with
9 files and 67 tests skipped (680.45 seconds). The first invocation had exceeded
the command tool's 120-second limit.
- After three additional renderer race/retry tests, the final `npm test` run
reported 355 files and 4,202 tests passed, 9 files and 67 tests skipped, and two
unrelated timeout failures: the large offline dependency inventory test in
`tests/agent-package.test.ts` (60 seconds), and the two-project OpenCode case in
`src/main/agent/local-runtime-reuse.test.ts` (40-second request timeout).
- Rerunning those two files together passed 24 tests with one skipped in
103.71 seconds, without source or timeout changes. The final renderer/preload
focused run also passed all 38 tests. No Magic Notes regression failed.
- `npm run typecheck` and `npm run lint`: passed. The new required subscription
method is also present in the application test API fixture.
- Validation uses production SQLite and Agent/MCP gateway code, the production
preload with mocked Electron transport, and renderer interaction tests. An
interactive Electron window was not exercised. No deployed Agent code or
desktop-to-Agent protocol changed, so no separate Linux Host scenario applies.
@@ -0,0 +1,477 @@
# Magic Notes Technical Design
## Continuous Record Workspace
The detail mounts every entry in a continuous newest-first stream. The left index
and AI source buttons scroll to an entry ID without changing the active editor,
clearing a composer, flushing content or invoking the draft-switch guard. Opening
a note shows a blank composer below the title and above the stream, without a New
record button. Editing another entry or
leaving the note still checks unsaved content after flushing canvas changes.
`MagicNotesWorkspace.onBeforeLeave` registers this same check with the App navigation
gate, including sidebar, application launcher and conversation jumps. The gate waits
for canvas flush, then offers Continue editing or Discard draft and switch for dirty
body or title content. Workspace cache eviction does not serve as draft protection.
Non-title detail responses retain a dirty title. A title-save response acknowledges
only the submitted input; text entered while that request runs remains a draft.
The shared store returns `MagicNoteEntryCreateResult`, the existing detail shape
plus a required `createdEntryId`. Production IPC and preload preserve that ID.
Creating an entry selects this exact ID and resets the composer through its key;
it does not enter editing, including after automatic analysis. Text/canvas selection
is retained. Canvas capture for automatic analysis completes before the reset.
the renderer never infers it from a list difference or timestamp. Agent/MCP
creation delegates to the same store; its existing public note projection is
unchanged.
Explicit Edit opens a saved record. Update and automatic-analysis responses advance its revision, so subsequent
saves use `updateEntry` and the returned revision. Failed writes retain the draft.
The dirty baseline belongs to the mounted editor. `MagicNoteEditor.onReady` reports
Quill's normalized contents after loading; `MagicCanvasEditor.onReady` reports the
first completed core flush, after Fabric restoration and flow pagination. Canvas
input stays disabled until this baseline exists, and imperative flush waits for
the same initialization promise. Raw stored JSON is not a valid comparison baseline
for an initialized editor: Quill can add its terminal newline, and Fabric/flow
serialization can add defaults or change field order. Later changes and navigation
flushes compare with the initialized baseline without resetting it. A successful
save remounts at the returned revision and establishes a new baseline; an unchanged
background revision retains the existing editor and baseline. Aborted canvas
initialization and file reads for an unmounted text editor cannot write back.
The real saved formatted-text probe reproduced identical values with different
Delta key order: storage returned `{insert, attributes}` and Quill emitted
`{attributes, insert}`. The old `JSON.stringify` comparison therefore reported
dirty after a successful save. The saved PDF probe's initialized content and
subsequent flush were byte-for-byte equal; readiness is still awaited for canvas
restoration rather than assumed from the save response.
Saving or leaving an editing context clears the immediate-analysis timer, queued
content and streamed draft comments through the shared draft-analysis cleanup.
The context counter rejects late results and prevents dispatch after abandoned
settings preparation. This cleanup does not clear the new-entry composer's content.
The left index defaults to 168px and resizes from its right edge between 140px
and 320px. It shares the AI pane's pointer-capture handlers, separator styling
and keyboard semantics (16px arrow steps, Home/End limits). Both widths are
stored in `goodbuddy.magic-notes-layout.v1`; missing index width defaults to
168px. Displayed widths clamp together to reserve at least 300px for the stream,
without overwriting saved widths when the window shrinks. The page header places
the index toggle immediately after the back button, followed by the AI toggle.
All three use icons and visible localized text; pane toggle labels reflect their
current show/hide action. Collapsing hides the entire index and removes its grid column,
border, resize separator and occupied width. AI has a separate
right pane with its existing resize separator and header toggle. Both panes scroll
independently and persist independent desktop visibility. At container widths of
800px or less, the index defaults to hidden and the same header toggle opens or
closes a fixed 168px drawer below the header, without reserving a rail or showing
an index resize separator; choosing
an item or pressing Escape closes it. AI moves below the stream, limited to 40%
of layout height and 280px. Overview search, scroll, task selection and return
focus stay mounted.
`MagicCanvasThumbnail` lazily queues visible records through a temporary core
renderer and caches results by content identity in a WeakMap. Core `capturePages`
accepts `firstPageOnly` and `thumbnailWidth`; thumbnails capture page zero at 240px
width, including paper/PDF background, Quill flow, images and Fabric objects.
They do not capture all pages or leave an editor mounted for every record.
An abort listener calls core `destroy()` immediately: PDF render cancellation and
the Fabric abort happen synchronously, before waiting for capture to settle.
The `finally` path awaits the same destruction promise and removes the temporary
surface, without destroying twice or waiting for capture before cancellation.
AI capture uses the configured page prefix described under Canvas Analysis and
Comments. Main canvas page height, wheel handling and file storage are unchanged.
Background refresh preserves dirty editing content and its original optimistic
revision, including externally deleted entries. Clean editors adopt refreshed
entries; deleting a clean selection updates `selectedEntryId` to a remaining
entry or the empty state. Later appends preserve that actual selection, rather
than recalculating a render-only fallback. Dirty deleted entries remain open
until explicitly discarded. No background refresh saves a draft.
## Canvas Storage Contract
`MagicNoteContent` is the union of unchanged v1 Quill rich content and v2
`paged-canvas`. The shared schema is authoritative. Canvas pages carry dimensions,
template/PDF backgrounds, opaque Fabric object records and optional `flowAuto`.
The optional Quill flow accepts native PeopleLib `version: 1` and page-break
embeds. Template names are `blank`, `ruled`, `grid`, `dot`; PDF backgrounds use
one-based `pageNumber` and optional extracted `text`.
API assets are document-local `{id,name,mimeType,dataUrl}` records. Backgrounds and
image objects reference stable asset IDs; image objects can also embed raster
data directly. Main validates data encoding, image/PDF signatures and references.
Canvas has no v1 resource-size budget. `image_bytes` counts decoded embedded
resource bytes for new entries. Bodies and binary assets use the file storage
contract below; there is no separate resource table, resource staging or extra
resource IPC. Renderer, preload and IPC continue to exchange hydrated v1/v2
content with data URLs.
Schema 38 replaces SQLite body payloads with file markers. Schema 37 was the
earlier canvas compatibility marker; its embedded-payload layout is historical.
Plain text combines flow text, PDF extracted text and object text for search.
Checklist indexes depend only on flow checklist lines; moving objects does not
change todo identity. Completion updates flow without replacing pages or assets.
Entry edits retain optimistic revisions. Canvas comment retention depends on the
actual analysis input, as described below.
`note_get` reports content kind/version and `plainTextEditable`. The text update
tool rejects canvas entries before mutation. These tools execute in the desktop
gateway; remote gbagent's injected image MCP server is unaffected. Browser page
renders can be sent as `analysisOptions.canvasImages`; analysis comments and draft
results can retain the actual `inputMode` used.
## File Storage and Writes
`MagicNoteStorage` owns files under `dirname(assistantDatabasePath)/notes`:
```text
notes/<noteId>/
note.json # Derived {id, entries} membership manifest
entries/<entryId>.json # Authoritative body envelope
assets/<sha256>.<ext> # Binary assets deduplicated within this note
```
Each body envelope contains `id`, `noteId`, `revision`, `updatedAt` and `content`.
SQLite owns note metadata, entry membership, search/plain-text and byte indexes,
optimistic revisions, todos and comments. Entry `content_json` contains only
`{storage: "file", version: <content version>, revision: <body revision>}`.
Comment-only revision increments do not change the indexed body revision.
Known media fields become `{$asset: "assets/<sha256>.<ext>", mimeType: "..."}`
on disk and hydrate back to data URLs on API reads. These fields are v1
`insert.image`, `localVideo.dataUrl` and `attachment.dataUrl`; canvas
`assets[].dataUrl`, `preview`, flow embeds and image-object `src`, including
nested objects. Ordinary text, insert strings, names and arbitrary `$asset`
objects remain unchanged. Canvas asset IDs and PDF references are preserved.
Write, read and garbage collection use the same scoped field traversal.
Revision checks run under `BEGIN IMMEDIATE` before file writes. Assets are
written before the temporary body file is atomically renamed over the entry
file; the database then commits its marker and indexes. This is not a single
atomic transaction spanning SQLite and the filesystem. If a replacement body
was renamed but indexing failed, the next database access or reopen reads it
back, repairs plain text, byte counts and todos, advances revisions and
invalidates old analysis. Missing bodies raise an error instead of becoming
empty content. Failed creates leave no accepted database membership; their
unaccepted files are removed on the next access or reopen.
After commit, reconciliation derives `note.json` from database membership and
scans all retained entry files before removing unreferenced assets. Manifest or
GC failures log `console.warn` with the note ID and error and remain in an
in-memory retry set. Later database access retries; reopening derives cleanup
again from membership. These cleanup failures do not turn a committed save,
delete or reset into a failed operation or suppress its notifications. Body
persistence and index-repair failures still propagate. Entry deletion, note
deletion and assistant-data reset share this cleanup path; deferred whole-tree
cleanup checks current membership so it preserves notes created after reset.
There is no persistent retry journal, and renderer-only drafts create no files.
## Schema 38 Migration
The existing startup storage worker runs before the business connection opens.
It upgrades the schema and converts legacy payloads one entry per SQLite
transaction: write assets and body, read back hydrated content, compare JSON
equality, then replace the old `content_json` payload with its file marker.
Already converted entries are reconciled on retry. Matching body revisions do
not require rehydrating binary payloads. Direct `AssistantDatabase.initialize`
callers and tests also run storage repair.
Space reclamation is separate from conversion. Every worker attempt checks
`PRAGMA freelist_count` and runs checkpoint/VACUUM when free pages remain.
Cancellation after the last converted entry or a VACUUM failure therefore
allows reclamation to retry on the next startup, even with no legacy rows left.
The schema marker alone does not establish that conversion and reclamation
finished. General migration requirements are in the
[database migration guide](../../development/database-migrations.md).
## Backup and Restore
A restorable backup must pair a consistent SQLite backup with the adjacent
`notes/` tree from the same period with note writes paused, or with the app
closed. Handle active WAL through SQLite's consistent backup mechanism; copying
only the database main file while it is active is insufficient. Restore both
parts together. SQLite-only backups of file-backed entries cannot recover
bodies or assets, and `note.json` alone cannot recover database metadata,
revisions, todos or comments.
The product has no one-click assistant backup action. This is an operational
backup boundary, not a new UI capability. The tested backup/restore and migration
scope, including the lack of a real user historical database run, is recorded
in [progress](./progress.md).
## Canvas Editor and Export
`MagicNotesWorkspace` selects rich-text or canvas content for new entries and
opens saved canvases in `MagicCanvasEditor`; `MagicNoteContent` delegates saved
canvas rendering to the paginated `MagicCanvasContent` viewer. The React adapter
wraps the retained PeopleLib Fabric + Quill core. Fabric owns page annotations;
Quill owns the continuous body flow. Saving awaits `flush()` so queued imports,
text edits and pagination reach the existing preload/IPC create/update handlers.
There is no autosave or separate canvas persistence path.
Editor and viewer heights follow the current paper element's displayed height
through natural CSS layout, including wrapped toolbars and viewport padding.
No fixed preview height or viewport-height cap clips the page. The outer note
stream scrolls vertically; the paper viewport retains horizontal scrolling.
View zoom is local to each mounted core instance. A displayed-size outer box and
one transformed logical-size surface keep PDF, flow and Fabric aligned; Quill
screen measurements are converted back to logical coordinates. Neither zoom nor
fit-width resize calls `onChange`, changes the persisted model, invalidates AI
comments or alters PNG/PDF export dimensions. See the
[view zoom implementation](../../../src/renderer/src/magic-canvas/README.md#view-zoom)
for controls, bounds, page-switch policy and observer cleanup.
Paper templates, pen, highlighter, whole-object eraser, selection/transforms,
floating text, images and PDF page backgrounds are integrated. Native PDF text
is stored on its background for search and analysis; scanned pages remain visual
without automatic OCR. The toolbar exports a raster PDF through jsPDF, composing
background, flow text and annotations per page. It does not retain a searchable
PDF text layer. `capturePages()` provides PNG data URLs for AI input, not a PNG
download button. Undo/redo is per annotation page or Quill mode; page operations
and PDF imports are not global undo steps.
Flow page-break removal and rejected-input history handling follow the
[canvas editing and limit rules](../../../src/renderer/src/magic-canvas/README.md#editing-and-assets).
`canvas-core.electron.test.ts` feeds the real Electron flow's before/after content
into production `AssistantDatabase` create/update and reopen, checking that the
derived checklist todo and its identity survive page-break removal. History
coverage exercises accepted typing, rejected overflow, selection and redo.
The [canvas integration reference](../../../src/renderer/src/magic-canvas/README.md)
owns core limits, import behavior and lifecycle details. Vite bundles Fabric,
Quill, jsPDF, PDF.js workers and binary assets; PDF workers use explicit module
Workers and local assets under the production `file://` CSP. The renderer does
not need Node integration or remote asset services.
## Canvas Analysis and Comments
Draft, saved-entry and todo analysis use the existing typed preload/IPC methods
and `createDefaultModelRuntime`. Each canvas request reads the current default
model profile's `supportsImageInput` (or the resolved default setting when no
profile is selected). Main resolves settings again for the actual request. This
does not change an Agent runtime or desktop-to-Agent protocol.
`magicNoteCanvasPageCount` is an application preference: an integer from 1 to 8,
defaulting to 1 when absent in historical settings. The application center and
`goodbuddy_config` application.update/get share the settings schema, store and
change event. It counts pages within the canvas being analyzed, never entries.
The first N pages follow the current `content.pages` array order, independent of
the viewed page, page IDs or PDF source page numbers. Fewer than N pages selects
the whole canvas. Saving and PDF export retain the 50-page canvas limit.
Renderer and Main share `selectMagicNotePages`. The core selects the prefix before
compositing, so it never captures 50 pages just to discard 49. Analysis capture
returns the selected pages' Quill text measured from the actual CSS columns;
binary searches locate the character boundaries for explicit and automatic page
breaks. Text-only requests skip image compositing but still read page text.
The transient `canvasPageText` IPC field carries these page IDs and text, without
changing saved canvas content. Main reads the persisted page count for all three
handlers, selects page text, PDF text, objects and images in the same order, and
ignores captures outside the prefix. A partial canvas with flow requires matching
page text; missing data fails instead of including the full cross-page flow.
Prompts state the selected and total page counts. A changed setting applies to
the next analysis; existing comments are not automatically regenerated.
| Default model capability | Analysis input and result |
| --- | --- |
| Image input supported | Renderer captures the selected prefix; Main requires each selected page's PNG/JPEG capture and sends it alongside that page's flow, PDF and object text. Comments carry `inputMode: canvas-images`. |
| No image input | Only text from the selected prefix is sent, with an explicit instruction that handwriting, images and layout were not seen. Comments carry `inputMode: text-fallback`. |
| No image input and no extracted text in the selected entry/draft pages | Analysis fails with guidance to switch to an image-capable default model; no visual understanding is claimed. |
Todo analysis includes its title and instructions plus the source canvas context,
and uses the same capability and capture rules. Non-canvas content keeps the text
analysis path. All analysis remains read-only with no tool calls.
Saved-entry requests accept `expectedRevision`; todo requests accept
`sourceEntryRevision`. Nonempty `canvasImages` or `canvasPageText` requires the corresponding revision,
even when the resolved model uses text fallback. Main loads the source after
resolving settings and checks the supplied revision before model invocation.
Existing rich-text callers may omit these fields. Entry writeback retains its
revision check; canvas todo writeback also requires the source entry revision,
including when no comments existed at request time. No additional stored snapshot
or schema migration is needed.
Canvas `immediate` mode provides an Analyze canvas draft action without a persistent
instruction banner; pen strokes do not trigger automatic requests. `after-save-auto`
prepares analysis input and then saves manually submitted content. Settings or
capture errors are caught so storage still proceeds; after a successful save,
the UI reports that automatic analysis failed. A later model error likewise
does not roll back the save. Failure to flush the document itself still prevents
saving incomplete content.
For v2-to-v2 entry updates, identical content retains saved comments. If all
comments have `text` or `text-fallback` input mode, equal analysis text from shared
`magicNoteCanvasAnalysisText(content)` (including page numbers)
also retains comments across layout-only changes. Other changed canvas content,
including layout changes with `canvas-images` comments, clears saved entry
comments and `analyzed_at`. Source canvas changes apply the same rule to saved
todo comments and increment the affected todo revision without replacing its ID.
In `after-save-auto`, visual comments trigger renewed
analysis when content changes, while existing text-only comments trigger it when
analysis text changes. A changed draft clears its prior canvas draft analysis.
The UI labels the actual input mode so a text fallback is visible to the user.
Workspace captures the saved entry and its revision before awaiting image capture;
an external refresh cannot pair an old capture with a newer revision. Automatic
post-save analysis takes the revision from the save response using `createdEntryId`
or the edited entry ID. Todo capture uses the selected source entry's actual page
IDs and revision. Renderer text-only change detection uses the same shared
analysis-text helper as persistence, so inserting a blank page before text triggers
renewed analysis when its cited page number changes. Draft analysis remains unversioned.
## Change Notifications
`AssistantDatabase` accepts `onMagicNotesChanged: () => void`. Every successful
note/entry create, update or delete emits once after the write commits, including
empty-note creation, initial content, title/pin updates and cascading deletion.
Saved note analysis, todo completion, saved todo analysis and assistant-data
clearing also emit. Revision conflicts, missing rows, rolled-back writes and
unchanged todo completion do not emit. Startup schema migrations run before the
workbench loads and do not publish notifications.
Desktop IPC and `KnowledgeMcpGateway` use this same database instance. The latter
also serves Agent tools; no tool-specific notification or daemon protocol change
is needed. Raw SQL writes from an unrelated process are outside this callback
contract.
Main queues notification delivery to the live window on `magic-notes:changed`.
The typed preload method `magicNotes.onChanged(listener)` exposes no Electron
objects and returns a function removing that listener. The existing
`onTodoStatusChanged` event remains the separate sidebar-count signal.
## Refresh Lifecycle
The overview's `libraryView` and the optional note-only `detailView` are independent:
opening a todo's source note does not lose the originating task view.
The overview remains mounted but hidden while detail is open, retaining scroll
position and filters. Returning restores the initiating control's focus and
invalidates outstanding detail requests. Initial loading fetches summaries and
todos without selecting the first note or task. Navigation away from unsaved content or
an unsaved title uses the existing draft confirmation; active writes finish
before navigation. No new persistence or IPC contract is required.
`MagicNotesWorkspace` subscribes on mount and coalesces notifications with a
100 ms debounce. Pending notifications wait for an active local save or analysis
to finish. Cleanup removes the listener, clears its timer and invalidates pending
refresh results. This does not introduce periodic database polling.
The existing list/detail loader reloads notes and todos, then the preferred note.
Request counters reject superseded results and preserve newer user selections.
Todo selection survives list reordering while the item exists; replacing the todo
snapshot also reloads the selected todo's source entry. Search, filters and layout
state are not reset.
Background application of a detail reads the current draft state after the
asynchronous fetch. A dirty title is retained, and the composer is not remounted.
A dirty entry editor uses its original editing content and revision for both its
React key and save request; external revisions cannot remount that draft or bypass
optimistic concurrency checks. Clean editors adopt newer revisions. Deleted entries
remain displayed only while their editor has a draft. If an externally deleted note has a draft, its editor remains available
with a deletion explanation instead of moving the draft to another note.
Read failures use the existing retryable refresh error and retain successful
data and drafts. Retry uses the same draft-preserving loader. Interaction rules
are owned by [the UI design system](../../../UI-DESIGN.md#137-魔法笔记).
## Todo Task View
The To-dos panel has its own search/status toolbar and independently scrolling
task list, grouped by source note. `selectedTodoId` controls one sibling detail pane;
the selected item is derived from the filtered list. Completion updates the
existing item and source cache without selecting or opening another task. Items
that no longer match the status filter leave the list. A removed focused control
returns focus to an available list/filter control.
Detail content includes instructions, the source entry, its existing note
navigation action, and AI controls/comments. AI requests still use the existing
analysis options and streaming subscription; failed analysis retains saved
comments. Source reads are guarded against superseded selections, report failures
through notifications and offer inline retry. The grid reserves both columns even
without a selection, so selecting or switching tasks cannot move list rows. The
detail is keyed by task ID to start each newly selected task at its top.
At a page container width of 700px or less, CSS hides the mounted list while a
task is selected and shows an explicit return button in the detail area. Returning
clears selection and restores row focus without scrolling; source-note return
retains selection and focuses this visible return button on narrow layouts.
There is no todo detail route or separate AI sidebar. A shared accessible resize
separator displays a 1px line with a transparent 9px hit area and 8px adjacent
content padding. It adjusts the list width, initially 320px, with a 240px minimum and at
least 300px reserved for detail. Pointer dragging and 16px arrow steps/Home/End
persist `todoPaneWidth` in `goodbuddy.magic-notes-layout.v1`. Narrow layouts hide
the separator; window constraints do not overwrite the saved desktop width.
Each source-note heading is a disclosure button with its matching task count.
Groups start expanded and can independently hide their mounted rows without
clearing the selected detail. Collapse state lasts while the group is mounted.
Note detail retains its existing AI pane preferences.
A single secondary button lives in `PageHeader.actions` before New note. Its
label and icon describe the destination, and `requestDraftSwitch` retains the
existing draft guard. Focus returns to the same `magic-library-switch` button
after switching. The overview section keeps its localized list `aria-label`;
there are no tab, tablist or tabpanel roles or tab-label references. The button
follows content width at all sizes. The shared status `SegmentedControl` follows
search in one wrapping toolbar.
Only note detail applies `magic-notes-layout--detail` to remove the layout frame
and corner radius. Its stream pane has no extra padding; editor borders and the
AI divider remain. Overview cards and the independent todo layout keep their
existing frame and spacing. These changes do not affect Main, preload or storage.
## Note List Actions
Each overview card has an open-detail button and a sibling action-menu button. The menu
uses the conversation action styles and shared `DestructiveConfirmActions`, and
is portalled to `document.body`. Its position follows the trigger on scrolling,
window resizing and confirmation-size changes. Entering detail, filtering out the
target or leaving the notes panel closes the menu.
Pinning uses the target summary's ID and revision through the existing update
IPC. It updates the sorted summary and matching selected detail without resetting
title, composer or entry-edit drafts. Deleting uses the explicitly confirmed
target ID and refreshes the overview after success, without opening another note.
These actions are available only in the overview. Database and Agent contracts
are unchanged.
## Agent Search Contract
`note_search` accepts an integer `limit` from 1 to 100, defaulting to 8.
The shared schema supplies both model tool definitions and MCP validation;
the existing 128 KiB output budget can reduce the returned result count.
The built-in MCP gateway returns invalid scoped-tool arguments as an
`isError: true` tool result with the field and validation message, allowing
the model to correct its arguments without an MCP internal-error response.
Validation failures do not execute the tool.
These note tools run in the desktop gateway. Remote gbagent ACP sessions
currently inject only the image MCP server, so this change requires no
daemon implementation update.
## Validation
- `magic-note-storage.test.ts` uses real files and SQLite for migration equality,
reopen/repair, binary deduplication, coordinated backup/restore and physical
database shrink. Failure injection covers body rename, manifest/GC cleanup,
delete/reset retries, post-conversion cancellation and VACUUM retry.
- Canvas integration coverage includes `MagicCanvasEditor.test.tsx`,
`MagicNoteContent.test.tsx`, `rich-content.test.ts`,
`magic-note-analyzer.test.ts` and `tests/magic-note-canvas-text.test.ts`.
`canvas-ipc.integration.test.ts` runs production preload and registered IPC
handlers against real SQLite with mocked Electron transport and model output.
Interactive workspace/SQLite and production core export evidence is recorded
separately in [progress](./progress.md).
- `assistant-database.test.ts` checks committed writes with a second SQLite
connection, successful mutation coverage, failures, no-op completion and reset.
- `knowledge-mcp-gateway.test.ts` exercises real database CRUD through Agent/MCP
access, including revision failure without an extra notification. A real HTTP
MCP client also checks searches above ten results, the published limit schema,
invalid arguments and successful retry against SQLite.
- `magic-notes-events.test.ts` executes the production preload, checking the
channel, payload-free callback and listener removal.
- `MagicNotesWorkspace.test.tsx` covers external updates and deletion, selection,
source reload, editor identity and draft saves, coalescing, deferred writes,
asynchronous selection races, error retry and unmount cleanup. List-action
coverage includes portal placement, keyboard navigation, dismissal, delete
confirmation, target selection, revision failures and draft preservation.
Schema 38 and the file layout above govern current persistence; refresh
notifications need no separate storage or network service. Dated validation
results and remaining repository checks are owned by [progress](./progress.md).
@@ -42,6 +42,7 @@ User Stories 和逻辑设计;本页定义产品边界,技术设计定义唯
认证信息。
- 自定义值作为普通连接设置保存。API Key 和其他密钥必须使用专用凭据字段,不应写入
自定义请求头或请求体。
- LLM 连接可以复制,已保存的 API Key 由 Main 进程安全复制到新连接。
## 相关功能
@@ -1,5 +1,15 @@
# 模型连接请求定制进度
## 2026-09-20:模型设置可用性修复
- 模型接口 URL 保存失败时按连接名称显示错误,URL 字段同步提供 `aria-invalid` 和就地
错误;共享契约同时拒绝空值、非 HTTP/HTTPS 地址和超长地址。
- 模型连接分类标题与 LLM、向量、重排、语音分段页签在模型内容滚动时共同固定;自定义
Header/Body 默认收纳在折叠的“高级设置”中,无效时自动展开。
- LLM 连接新增复制入口。复制保留当前草稿、请求定制和认证设置;已保存 API Key 由 Main
进程从加密凭据复制,不进入 Renderer 或错误信息。
- 已新增 SettingsPanel 与 RuntimeSettingsStore 回归覆盖;完整验证结果见后续条目。
## 2026-09-11:发布前固定模型说明修复
- 项目已保存固定 Runtime 模型时,恢复显示实际固定模型的帮助文字,保留简化后的菜单;
@@ -76,6 +76,15 @@ API Key 继续由现有 Main 加密设置和 Prompt-scoped Agent 凭据路径管
Body 是普通可见连接设置,不提供第二套通用秘密存储;UI 必须提示不要把密钥放入这两个
JSON 对象,标准认证 Header 也由 schema 拒绝。
模型连接输入使用 `modelConnectionUrlSchema`,先在 Renderer 以连接名称标识无效 URL,
再由共享契约拒绝空值、超过 2048 个字符或非 HTTP/HTTPS 地址。页头错误保留完整错误上下文,
字段错误通过 `aria-describedby` 关联到 URL 控件。
复制连接不在 Renderer 读取或暴露已保存 API Key。复制草稿提交
`{ action: 'copy', sourceProfileId }`,Main 在同一设置更新事务中从当前加密凭据或默认连接
的环境凭据创建目标凭据。源连接不存在时整个更新失败;目标连接的其他字段仍由普通输入
校验。正在编辑但尚未保存的 Key 仍以一次性 `replace` 值提交,因而不与旧凭据混合。
## 7. 验证
- 共享 schema:JSON 类型、边界、保留字段和优先级。
@@ -84,7 +93,8 @@ JSON 对象,标准认证 Header 也由 schema 拒绝。
- Runtime:真实 bundled OpenCode/Continue 对 loopback Provider 的请求探针;DeepSeek
Utility 启动配置和 Pi-AI Provider 配置。
- 远程:Main/Agent 网关合并、认证优先级、摘要绑定、大小限制和不重放。
- UI:默认值、保存、无效 JSON 保留和 Runtime 支持说明。
- UI:默认值、保存、无效 JSON 保留、连接级 URL 错误、粘附标题/页签、折叠高级设置和
包含凭据的连接复制。
## 8. 通道直连模型选择修复
+13 -3
View File
@@ -6,7 +6,8 @@
## 信息架构
“自定义请求头”和“自定义请求体”位于当前 LLM 连接详情的最底部、连接兼容性摘要之前。
它们不新增页签、弹窗或高级模式。每个编辑器使用可纵向调整大小的等宽文本域,默认值为
它们收纳在默认折叠的“高级设置”中,不新增页签或弹窗。只要其中一项无效,保存时自动
展开该区域并保留对应字段错误。每个编辑器使用可纵向调整大小的等宽文本域,默认值为
`{}`。
## 编辑与保存
@@ -17,6 +18,13 @@
`aria-invalid` 和就地错误;点击保存时阻止写入,并在现有设置错误区域显示对应连接。
- 保存成功后,编辑器使用两个空格缩进重新格式化已保存对象。
- 新连接从两个空对象开始。
- 保存前校验失败时,页头错误和对应字段错误都显示连接名称;模型接口 URL 同时标记
`aria-invalid` 并关联就地错误,避免只显示无法定位的“Invalid URL”。
- LLM 模型连接的分类标题和模型类型分段页签在内容滚动时共同粘附在内容区顶部;左侧连接
列表仍可独立滚动。
- 每个已保存或正在编辑的 LLM 连接提供“复制连接”。复制会生成新的连接名称和 ID,保留
地址、模型、协议、请求定制、认证方式及当前草稿 API Key。已保存的 API Key 通过 Main
进程的加密凭据复制路径保存,不回传到 Renderer;原连接和默认连接不改变。
## 兼容性说明
@@ -24,6 +32,8 @@
该值。请求体说明明确列出直连、Continue 和受管远程 OpenCode 支持,并说明本地
OpenCode 与 DeepSeek Harness 不使用该值。
两项说明使用标签同行的 `InlineHelp`,交互遵循[同行帮助规范](../../../UI-DESIGN.md#614-inlinehelp-同行帮助)。下列警告及字段错误保持常显。
同一区域还必须说明:
- Runtime、协议和认证生成的字段优先。
@@ -32,7 +42,7 @@ OpenCode 与 DeepSeek Harness 不使用该值。
## 无障碍与键盘
- 每个文本域有稳定的可访问名称,并通过 `aria-describedby` 关联兼容性说明。
- 每个文本域有稳定的可访问名称,并通过 `aria-describedby` 关联兼容性说明。帮助关闭时保留隐藏说明,展开时替换为同 ID 的浮层,避免关联丢失或 ID 重复。
- 无效状态不能只依赖颜色,必须同时提供错误文字和 `aria-invalid`。
- 文本域遵循标准键盘编辑、选择、复制和粘贴行为;保存继续使用设置页唯一的主保存操作。
- 焦点边框、字体、间距、颜色和深浅主题全部复用全局语义令牌。
@@ -49,7 +59,7 @@ Runtime 入口恢复跟随。该展示规则不改变模型解析、远程 Agent
DeepSeek Harness 的 `platform` 选项命名为“管理员环境优先,回退 GoodBuddy 兼容连接”,
不能沿用 OpenCode/Continue 的“Runtime 自有配置”标签。来源规则以
[Harness 模型配置](../deepseek-harness/technical-design.md#121-配置来源)为准。
帮助文字与选择器通过 `aria-describedby` 关联,并明确此选项不加载用户 profile 或
帮助文字收进标签同行的 `InlineHelp`,与选择器通过 `aria-describedby` 持续关联,并明确此选项不加载用户 profile 或
配置文件;无兼容 GoodBuddy 连接时仍可选择管理员环境来源。
概览使用 Main 提供的[平台来源投影](./technical-design.md#9-harness-平台来源公开投影),
+8
View File
@@ -9,5 +9,13 @@
| --- | --- |
| [远程主机与 Agent 技术设计](./technical-design.md) | 当前产品语义、Agent、Runtime、Workspace、协议、生命周期和验证事实 |
| [远程环境准备技术设计](./environment-provisioning-technical-design.md) | Host 下载、GoodBuddy 传输、安装事务、更新和恢复边界 |
| [执行清单验证记录](./runtime-checklist-validation.md) | 远程 OC/CN 原生清单、CN 开发包安装、取消及断线重放的实测结果与剩余验收 |
现有文档包含产品、功能逻辑和进度事实,后续修改远程功能时再按标准职责拆分。
跨会话共享 OpenCode 进程、ACP 连接与模型桥的实现设计,见
[Runtime 进程复用技术设计](../assistant-workbar/runtime-process-reuse-technical-design.md#6-ssh-opencode-与-agent)。
当前实现尚未发布;验收状态见[工作栏进度](../assistant-workbar/progress.md#2026-09-14-runtime-进程复用实施中)。
更新退役、任务完成后 Runtime 回收和下一轮历史恢复的当前源码验证,见
[2026-09-23 生命周期验证](./technical-design.md#生命周期验证2026-09-23)。
@@ -16,6 +16,10 @@ bootstrap 能力元数据。公开直连能力仍须完成本文末尾 GitHub/
[SSH 远程主机与 GoodBuddy Agent 实现说明](./technical-design.md)
和源码为准。
2026-09-18 补注:现有单 Runtime 复合包格式也支持 CN,安装器按包内 Runtime 身份
选择并登记 payload。CN 开发包的 Linux 验证及联合分发的剩余工作见
[执行清单验证记录](./runtime-checklist-validation.md#cn-部署与生命周期)。
## 设计结论
将安装和更新从项目切换移到 Host 管理是合理的,原因如下:
@@ -157,8 +161,15 @@ daemon 参与。两种 acquisition 的差别只在于如何把同一个签名 co
4. commit 后在同一条已固定身份的 SSH lease 上执行 Agent bootstrap/readiness 和 Runtime
activate。新 Agent 最多完整验证一次,新 Runtime 最多完整验证一次;注册成功后的
health、capabilities 和 prompt 启动只读取 registry、签名 manifest 与入口 metadata。
5. Main 确认 adoption 后显式执行 cleanup。cleanup 失败不回滚已经健康的 Agent/Runtime,
也不阻塞下一次更新。
5. Agent adoption 和所有 Runtime 激活成功后,Main 通过新安装执行 `cleanup-obsolete`,
尽力回收已证明停止且无待处理工作的旧 Agent payload。命令返回逐安装的延期原因,
不支持该命令的旧包或清理失败不会回滚健康环境。随后显式清理本次 operation staging;
staging cleanup 失败也不阻塞下一次更新。
新安装成为 current 后,包含退役逻辑的旧 Agent 排空已有任务再退出;当前 Agent 可以常驻。
仍存活且缺少退役逻辑的 legacy daemon 不会被升级清理命令终止,不能用无连接推断其空闲。
共享 Runtime digest 目录、历史和 Workspace 保留。清理条件、延期限制及验证记录见
[远程主机技术设计](./technical-design.md#升级残留清理)。
这不是两个并行安装管理器,也不存在把 Agent 与 Runtime payload 逐文件走 SFTP 安装的
第二条路径。两种 acquisition 共享相同的 prepare、commit、adoption、finalize 与 cleanup。
@@ -0,0 +1,252 @@
# 远程执行清单验证记录
日期:2026-09-18。对应 CL-P1、CL-P3、CL-P4;需求与统一契约见
[执行清单设计](../assistant-workbar/runtime-checklist-technical-design.md)。
本记录包含远程 Agent 到 Main Runtime 的事件验证,以及后续 Windows 完整桌面到
Linux CN 的安装、清单展示、保存、重启和取消验证;各阶段覆盖范围分别列出。
## OC 实现与原生证据
共享 Linux x64 经 `192.168.0.23` 连接,使用已保存的 SSH 凭据和固定 Host Key。
未使用 VPN 路由,不计作第二台 Host。Agent 由当前源码构建到专用测试目录,复用该机
受管 Node 24.19.0 和签名 OC 包;manifest 与二进制 `--version` 均为 1.18.29。
真实模型按提示两次调用 `todowrite`:先写入一个 `in_progress/high` 条目,再写入
`todos: []`。未适配时两次工具均成功,ACP 只有 `tool_call/tool_call_update`,
没有 `plan`。上游同版本
[acp/event.ts](https://github.com/anomalyco/opencode/blob/v1.18.29/packages/opencode/src/acp/event.ts)
也未处理 `todo.updated`,因此不能把 ACP 类型定义当作部署证据。
现有 `opencode-subagent-plugin.ts` 将精确原生 `todo.updated` 放入既有 ACP metadata,
由 Agent 原 Session 分派、operation 归属和 semantic transcript 传递。
`acp-remote-runtime.ts` 在工具截断之前消费该结构并校验 `RuntimeChecklist`。
原 Session 与当前 Prompt 不符、无效条目均不替换清单。工具活动保留;正文及泛化
工具名不触发更新。`cancelled` 原样保留,避免 ACP plan 只允许三种状态造成信息损失。
标准 OC `plan.entries` 的结构化转换也有测试,但本版本的真实成功路径使用插件原生事件。
## 共享 Host 结果
最终源码复验目录为 `/root/tmp/gb-acp-recovery-4bLjbp`,首次通过目录为
`/root/tmp/gb-acp-recovery-SJbdZQ`。首次清单抵达桌面运行时后,在其
semantic checkpoint 被消费前关闭 attach。随后恢复同一 controller、binding 和
operation,设置 `remoteRecoveryOnly: true`,没有再次提交 Prompt。
| 验证项 | 结果 |
| --- | --- |
| 原生清单新增 | sequence 1,`todo.updated`,一个 `in_progress/high` 条目 |
| 未 ACK 清单重放 | 重连后仍为 sequence 1,内容相同 |
| 原生清空 | sequence 5,`todos: []`,映射为 `{source:'opencode',items:[]}` |
| 重连后完成 | 两份清单、工具活动和终态继续到达 |
| 额外模型调用 | 重连未重发 Prompt;该次总计 3 个真实 provider 请求 |
| 清理 | Agent 停止后,测试目录所属残留进程 0;临时上传及运行目录已删除 |
更长条目、取消状态、非法输入、子 Session、标准空 plan、工具活动不重复生成清单、
相同 provenance 重放,由单元测试覆盖。远程真实模型的长清单、取消及连续多轮隔离
本次未实测,不能将单元测试记为真实 Host 验收。
一次较早的重放探测在 `done` 后才断开,之前已 ACK 的记录被正常裁剪,故不能从
sequence 0 重新读取整段历史。该次断言失败后,改为验证未 ACK 边界;产品未增加
历史快照或另一套重连机制。已 ACK 内容的恢复仍依赖 Main 已提交的消息。
另复现 OC 冷启动超过默认 15 秒控制超时:连续五次请求未进入 provider。
将 `runtime/startPrompt` 控制等待提高到至少 30 秒后,原生初始化、建会话和模型选择
完成。对应测试模拟 20 秒启动,确认仅提交一次 start;其他控制请求时限保持原设置。
## 模型调用口径
统计为测试 Agent 中实际向非 loopback provider 发出的 HTTP 请求,不将 ACP Prompt
次数、工具调用、内部 `/session` 路由请求或重连计作模型调用。读取已有 OC 模型配置,
测试未打印凭据或端点。
| 探测 | 实际模型请求 |
| --- | ---: |
| 五次冷启动定位 | 0 |
| 提高启动等待后的原始 ACP 观察 | 3 |
| 输出工具状态、确认两次 todowrite 成功 | 3 |
| 插件接入后在 done 边界重放,断言失败 | 3 |
| 最终未 ACK 边界断线重放通过 | 3 |
| 最终源码再次复验未 ACK 边界 | 3 |
| CN Linux 包与命令入口探测 | 0 |
| 合计 | 15 |
五次进入 provider 的 OC 探测均返回三个 HTTP 200;无受控假 provider 调用。
## CN 部署与生命周期
已从 npm 获取固定 `@continuedev/cli@1.5.47` 工件,压缩包为 17,945,195 bytes,
并在 Host 校验 SHA-512:
```text
gtpewV3RoIOD9dyTtKIBi1SY0VOHRu3Ehe7C/mmnswm+j34MPyrcQhQaWj/m+jdfGO4fNIKdrgGIlLso1ULDFw==
```
在 `/root/tmp/gb-checklist-upload-44f47e99-94e2-4e35-8efb-ef80b7a830ca` 解包,
以受管 Node 24.19.0 执行 `dist/cn.js --version` 得到 1.5.47;`serve --help`
确认 HTTP `/state`、`/message` 入口。`acp --help` 返回普通顶层帮助,并非 ACP
子命令;工件 `src/index.ts` 有 serve 注册、没有 acp 注册。探测未启动模型会话,
没有把包入口可运行记为远程 CN 功能完成。测试工件已从 Host 清理。
日志留存复验目录为 `/root/tmp/gb-checklist-upload-424f412c-cf2a-4857-810f-48da03285821`,
同样通过并清理,模型请求仍为 0。
以下为同日后续实施结果,取代最初调查时的未实现清单。
| 路径 | 当前实现 |
| --- | --- |
| 受管工件 | lock 固定 `@continuedev/cli@1.5.47`、SHA-512 和 `lib/continue/dist/cn.js`;组包保存 payload、manifest、签名和许可文本 |
| 工件安装 | Runtime 与复合 Agent 包校验接受 CN;桌面按 descriptor 的 Runtime ID 选择 payload,Host 复用 `prepare/commit` 和 Runtime registry |
| Agent 启动 | `continue-acp-helper.ts` 在 Host 上复用 `ContinueHostAdapter`,启动 Node 驱动的 CN HTTP 宿主,对既有 Agent 提供 ACP Session 接口 |
| 模型桥 | Main 选择 CN 模型 profile,Agent gateway 保留逐 operation 的模型配置;CN 请求携带 Session/operation 路由头,配置中使用桥接占位凭据 |
| 清单 | 宿主在原始参数层提取成功的 `Checklist.checklist`,经 `_meta.goodbuddyChecklist` 和原 semantic transcript 输出统一 `checklist` 事件 |
| 项目及聊天 | 创建、保存、安装激活、Main Runtime 工厂和聊天菜单均接受 CN;项目默认值与历史 CN 选择保持;安装身份必须匹配选择 |
| 生命周期 | ACP cancel 取消本轮宿主,已有 attach、transcript/ACK 负责断线恢复;后续 Prompt 可在同一对话继续 |
最终复验目录为 `/root/tmp/gb-cn-runtime-EALG99`。开发复合包为 188,609,399 bytes,
使用临时签名身份和隔离 trust registry,没有更改应用或 Host 的正式信任配置。
桌面验包函数、Host `prepare/commit`、安装后 Agent `bootstrap/health` 与 CN
`runtime activate` 均通过。随后当前源码 Agent 使用该安装得到的 CN payload 执行下列场景:
| 场景 | 实测结果 |
| --- | --- |
| Execute 原生清单 | sequence 3 为一个 pending 条目,sequence 6 为显式空列表 |
| 未 ACK 断线重连 | 同一 binding/operation 重放原 sequence 3,再收到空清单;不重发 Prompt |
| 取消 | 新一轮产生清单后取消,Main 返回 `RemotePromptCancelledError`;清单不改写为完成 |
| 同对话续发 | 切换 Ask 后返回 `RECOVERED`,没有旧清单事件 |
| 回收 | Agent 停止后测试所属进程 0;隔离目录和上传目录已删除 |
本轮实际向外部 provider 发出的 HTTP 请求数为 **17**:首次取消探针 5 次,修正探针对
预期取消异常的处理后 6 次,最终安装包复验 6 次。三轮分别取得 4、5、5 个 HTTP 200,
各有 1 次请求在取得响应前取消。安装探针的四次排错运行均为 0 次;重连没有额外提交。
此前已有 `cn-runtime-live.log` 记录过 3 次 CN 请求,属于接手前证据,不计入本轮 17 次,
也不并入上方原 OC 阶段的 15 次。本轮全部 CN 进程均运行于 Linux Host。
安装排错包括复用旧缓存时缺少 Koffi CJS 文件,以及探针把安装器 CLI 一起内联后污染
relay stdout。当前组包实现已包含 Koffi CJS 文件;探针改为单独导入安装器模块。
这些运行未进入模型。最终日志为临时目录中的 `cn-runtime-live.log`。
本阶段验证从 Main 受管 Runtime 工厂进入。后续完整桌面与联合开发包结果见下节。
CN 的 Linux arm64/macOS arm64 尚未实测;本轮没有提交或发布 Agent 包。
## 2026-09-19 联合包与完整桌面验收
默认 Agent 组包与 CI 构建已同时包含 OC 1.18.29、CN 1.5.47。描述符保留主 Runtime,
通过 `additionalRuntimes` 描述随包安装的 CN;目录、桌面验包、Host 安装和资源加载
均校验该列表。单 Runtime 包仍可读取,已有发布包无需迁移。联合包专项测试为
31 passed、1 skipped,包含生产安装器对单包及联合包的安装回归。
完整桌面使用当前生产 Main、Preload 和 Renderer,隔离 profile 与开发签名 registry,
保留 sandbox/context isolation。连接由既有凭据和固定 Host Key 到 `192.168.0.23`,
再转发至同机专属 SSH 服务;该服务将 HOME 限定在 `/root/tmp/gb-joint-ui-2oQ87z/home`。
本地 loopback 地址仅为 SSH 转发入口,CN 进程实际运行于 Linux。原生文件选择对话框
由测试驱动返回开发包路径;其后的导入、验签、传输和安装均走生产 IPC。
桌面完成联合包导入和“由 GoodBuddy 传输”安装后,通过实际项目表单创建 CN 远程项目,
选择 Execute,在真实输入框发送清单请求。两次 Checklist 更新后,顶部显示一项完成、
一项待办,工具活动仍可见;保存和退出重启后,原项目保留这两项状态。
重启续发首次实测失败:CN 内存原生会话已退出,但旧 ready binding 仍引用其 Session;
随后恢复同一个 start 请求又重复递增序号。修复后冷打开 CN ready binding 会从现有
对话历史新建原生会话,未确认 start 的恢复保持原 operation 和序号。相邻 4 文件
167 passed、1 skipped;新构建的真实桌面已验证重启续发成功、清单出现后点击停止、
取消条目仍为待办,以及随后返回 `REMOTE_CN_RESUMED` 且不显示旧轮清单。
测试启动器曾遗漏托盘资源,以及误清除托管 Node 所需的 `ELECTRON_RUN_AS_NODE`,导致
前一次构建未刷新产物;修复启动器后核对产物包含源码修复,再进行上述成功复验。
这两项是临时驱动问题。最初测试计数钩子未覆盖远端模型出口,改从隔离 Agent 的
`model-calls.sqlite` 读取 operation、dispatch 状态及取消结果,仅导出计数,不读取凭据。
首轮全量测试为 4707 passed、67 skipped、1 failed;失败为本地 OpenCode 压缩返回
`compacted: false`。随后单独复验该文件及远程 ACP、项目选择、顶部清单,共 136 passed。
不将专项复验写成一次全量全绿。类型检查和 lint 已通过。
完整桌面截图位于批准临时目录的 `joint-cn-real-before-restart.png` 和
`joint-cn-cancelled.png`,均已打开审阅。窗口中状态与内容对齐,取消后保留待办,
输入框仍可使用。UI 驱动为 `joint-desktop.cjs`、`joint-scenario.ps1`;共享 Host
及隔离 profile 由 `joint-ui-host.ts` 管理。
补充真实长清单测试收到 55 项、5,601 字符的条目正文,数据库读取数量和字符数一致。
随后成功调用空字符串清单,顶部隐藏;再次退出重启并打开原项目,保存的空数组仍有效,
没有复活旧清单。自动断言摘要保存在批准临时目录的 `joint-final-evidence.json`,覆盖
SSH 项目身份、CN 选择、长清单落库、取消保留待办、取消后续发和清空后的重启状态。
本轮新增模型请求以隔离 Agent gateway 调用账本为准,共 **16 次**,15 次 completed,
1 次 outcome-unknown/cancelled。三轮创建并更新两项清单分别 3 次;取消场景 2 次;
取消后续发 1 次;55 项长清单 2 次;清空 2 次。旧会话失败和启动排错未新增 gateway
dispatch。本轮 16 次与上文 CN 17 次、早期 OC 15 次分别记录,不重复累计。
清理后已用原固定身份重新连接 Host,确认测试目录不存在、所属进程为 0;本地隔离
profile 及其加密测试凭据已删除。正式 Host 安装、应用配置和业务文件未改动。最终
typecheck、lint、差异检查通过。尚未执行 Linux arm64/macOS arm64 真机与多远程
会话同时生成清单的完整桌面矩阵;隔离与去重仍有定向测试覆盖,不据此扩大真机结论。
## 测试与复查路径
源码相邻测试:7 个文件,185 passed、6 skipped。覆盖 ACP Runtime、协议通道、
managed Runtime、原生插件、Agent-owned Prompt、ACP connection 和 model bridge。
```text
npx vitest run src/main/agent/acp-remote-runtime.test.ts src/agent-daemon/opencode-subagent-plugin.test.ts src/main/remote-agent/protocol-remote-runtime-channel.test.ts src/main/remote-agent/managed-remote-acp-runtime.test.ts src/agent-daemon/agent-owned-acp-prompt.test.ts src/agent-daemon/agent-acp-connection.test.ts src/agent-daemon/model-bridge.test.ts
npm run typecheck
npm run lint
```
Typecheck、lint 通过。`npm test` 在 240 秒时超时,未得到全量完成结果;此前
`src/renderer/src/App.test.tsx` 两项 Magic Notes 测试因 `pdfjs` 的 `DOMMatrix is not defined`
失败。本次未修改该 Renderer 路径。
本机临时探测目录为 `C:\Users\jiang\AppData\Local\Temp\opencode`,保留这些脚本供复查:
- `checklist-host-driver.ts`:读取既有凭据、SSH 固定身份、部署源码 Agent、清单及断线断言。
- `checklist-host-build.cjs`、`checklist-host-run.cjs`:构建和 Electron 执行入口;`--verify` 跑 OC,`--cn-only` 跑 CN 无模型探测。
- `checklist-host-daemon.ts`:只记录原生 ACP 初始化阶段名称的诊断包装。
- `checklist-cn-probe.cjs`:CN 工件完整性与 Linux CLI 入口验证。
- 复用 `acp-recovery-host-build.cjs`、`acp-recovery-host-daemon.ts` 和 `image-host-agent.ts`,没有复制凭据文件。
最终 stdout 证据分别为该目录的 `checklist-oc-final.log` 和 `checklist-cn-final.log`。
文档审校扫描无阻断项;复核项为测试范围列举及必要的未验证边界,按事实保留。
构建顺序为先运行 `acp-recovery-host-build.cjs`,再运行 `checklist-host-build.cjs`,
最后运行 `checklist-host-run.cjs --verify`。真实 OC 探测会调用模型;早期 `--cn-only`
探测只检查 CN 入口,不调用模型。
后续 CN 全路径复验使用 `cn-runtime-build.cjs` 构建当前源码与临时签名复合包,再运行
`checklist-host-run.cjs --continue --verify --lifecycle`。该命令会发出真实模型请求,
与前述只运行 version/help 的 `--cn-only` 不同。`cn-runtime-daemon.ts` 在隔离 Home
调用桌面验包及 Host 安装器,验证 CLI 激活,然后使用当前源码 Agent 持有模型轮次;
provider HTTP 请求计数在 Agent gateway 的外部 fetch 层完成。
后续专项验证:远程相关 27 个测试文件、374 项通过;新增 CN 复合包测试经过桌面完整
验包与生产安装器的 prepare/commit。全量运行完成,4665 passed、67 skipped、6 failed;
其中一项是运行期间新增 CN 测试尚未修正的归档名,随后专项复验通过。其余五项为
`ActivityPanel.test.tsx` 与 `activity-store.test.ts` 的引用复用断言,本轮未修改对应实现。
这两个文件在并行工作区继续更新后重新运行,39 项全部通过;全量运行中的失败项均已
经后续专项复验,但没有重新运行全部测试来取得同一最终快照的全绿报告。
最终另复验项目选择、顶部清单和安装管理 3 文件、41 项通过;App 远程 Runtime 选择与
继承 3 项通过。`npm run typecheck`、`npm run lint` 均通过。中文审校扫描阻断项为 0,
复核项为测试列举、请求数分项与必要验收边界,按事实保留。
## 2026-09-19 Continue 会话 MCP 交付回归
模型桥 helper 原先把 Session MCP 写入原生配置,但独立模型 profile 生成分支只保留
显式能力,导致 Execute 最终配置丢失 MCP。修复后通过 run options 传递会话 HTTP
服务器;本机原生 MCP 隔离和 Ask 只读规则不变,具体机制见[Runtime](./technical-design.md#runtime)。
`continue-acp-helper.test.ts` 联合运行实际 helper 与 adapter 配置生成器,覆盖初次交付、
续接替换、Ask 排除、加载空列表以及本机 profile 不继承原生 MCP。
共享 Linux x64 Host 经 LAN 固定身份连接,使用保存的 GoodBuddy 凭据,当前源码 Agent、
helper、adapter 与 CN 1.5.47 在专用目录运行。Main 受管 Runtime → Agent-owned ACP →
Continue → Agent HTTP MCP → Main 工具桥往返通过;仅最末端图片服务替换为无计费
测试接收端,校验输入并返回 `SAFE_MCP_ROUNDTRIP`,未连接图片 Provider。
同一对话依次 Execute、Ask、Execute:两次 Execute 各实际调用工具一次,Ask 没有工具调用
并回复 `ASK_NO_IMAGE_TOOL`;三次均正常终结。最终源码再次构建后完整重复该顺序。
首轮成功目录 `/root/tmp/gb-cn-runtime-2kQ7wc`,最终复验目录
`/root/tmp/gb-cn-runtime-NNM89D`。每轮 5 次真实文本 Provider HTTP 请求,合计 **10 次**,
全部 HTTP 200;每轮上限 6 次,单次最大输出 1,024 tokens,单 Prompt 验证超时 120 秒。
图片 Provider 请求 **0 次**。此前一次测试目录权限错误在 Runtime 验证阶段退出,模型请求
0 次;修正测试目录权限后通过,未放宽产品校验。每轮结束确认所属剩余进程 0,专用运行
及上传目录已清理,原 Host 安装未修改。此验证不包含正式包安装、UI 点击或真实图片生成。
复查脚本位于上述本机临时目录:`mcp-host-build.cjs`、`mcp-host-run.cjs`,
最终日志 `mcp-host-live.log`。专项 4 文件 38 项通过;改动文件 ESLint 和 Agent 类型检查
通过。主任务随后完成合并后的 `npm test`:405 个文件通过、9 个跳过,4,826 项通过、
67 项跳过,耗时 982.48 秒,无失败;最终完整类型检查与 lint 通过。
+323 -29
View File
@@ -2,7 +2,7 @@
## 状态
本文记录截至 2026-09-10 的当前代码实现,不定义额外的信任框架。“新增 Host 只探测、Host 卡片手动准备
下述状态段保留 2026-09-10 的实现与验证截面,不定义额外的信任框架。“新增 Host 只探测、Host 卡片手动准备
Agent/Runtime、Host 直接从 GitHub/北京镜像下载、项目始终使用 Host current 环境”已经完成源码接线,
详细事务与验收边界见
[SSH Host 远程环境准备与直连下载设计](./environment-provisioning-technical-design.md);
@@ -11,9 +11,9 @@ Agent/Runtime、Host 直接从 GitHub/北京镜像下载、项目始终使用 Ho
Linux x64/arm64、取消和离线 GoodBuddy 传输的真实 Host 验收。
Windows 到 Linux x64 的安装、Agent-owned Prompt、Agent 本地模型 gateway、断线恢复、
同一 OpenCode Session 续接、取消和终态清理已经使用真实模型与工具验证。Agent
`0.11.14` 已通过独立 workflow 发布 Linux x64/arm64 复合包和签名累计目录;当前源码
`0.11.14` 已通过独立 workflow 发布 Linux x64/arm64 复合包和签名累计目录;当时源码
候选为 Agent `0.11.24`、Desktop `0.13.0`,修复远端原生问答转交和取消待答后的同会话
续发。Agent `0.11.24` 要求先升级至 Desktop `0.13.0`;候选尚未发布。远程文件/Git 管理、
续发。Agent `0.11.24` 要求先升级至 Desktop `0.13.0`;当时候选尚未发布。远程文件/Git 管理、
可选模型限额、无限请求时长下的独立连接超时和移除固定十分钟 Prompt 时限由
Agent `0.11.23` 提供,显式请求期限与取消仍有效。当前问答源码的真实 SSH/确定性模型证据
及完整生产链路验证边界见[工作栏进度](../assistant-workbar/progress.md)。
@@ -21,17 +21,43 @@ Agent `0.11.23` 提供,显式请求期限与取消仍有效。当前问答源
现有源码显示本地与远端 OpenCode 原生 Task,并取消 GoodBuddy 对生产 Prompt 的
固定墙钟总时限。失败的 `agent-v0.11.3` 保持不可变且未发布。
2026-09-14 发布状态补注:Desktop `0.13.2` 与 Agent `0.11.25` 已正式发布,包含此前
问答修复;Agent `0.11.25` 的维护发布要求 Desktop `0.13.2`。下节进程复用为当前未发布源码,
不是这两个发布的已有能力;历史验证段的日期与边界保留。
## 跨会话进程复用(未发布)
已发布版本的 `RuntimeAcpBackend` 按 binding 拥有原生进程,`AgentOwnedAcpPrompt` 的一条 ACP
连接对应一个原生 Session;同一会话后续 Prompt 可复用,但不同会话不共用进程。
上文“Host 共享 Runtime 环境”指安装与 Agent 环境,不代表原生 OpenCode 进程已共享。
锁定 OpenCode 1.18.29 / ACP SDK 0.25.1 的 Windows 隔离探针已确认:同一个 `acp`
进程与一条连接可运行两个工作区 Session,原生 `chat.headers` 能向本机合成模型请求
附带各自 sessionID。它未经过 Agent 所有权、生产 helper、真实工具或 Linux Host,
不是远端交付证明。
进程 owner 与 binding 拆分、单 ACP 连接分派、会话级权限以及模型桥逐 operation
路由的唯一方案见
[Runtime 进程复用技术设计 §6](../assistant-workbar/runtime-process-reuse-technical-design.md#6-ssh-opencode-与-agent)。
实施必须保留本文件的 released binding、Agent-owned Prompt、transcript/ACK、模型调用
结果不确定性与断线规则,并在开发期间执行下文真实 Host 验证,不为复用新增持久化账本。
当前源码已接入共享 process owner、`AgentAcpConnection` 和逐 operation 模型路由;
验收进展见[工作栏进度](../assistant-workbar/progress.md#2026-09-14-runtime-进程复用实施中)。
取消/失败的 Session 可以留下运行中的共享进程,结果中的 `processTree` 如实返回
`running`,不能再据 Prompt 终态推断整进程为空。新行为需要配套 Desktop/Agent,
已发布 Desktop `0.13.2` / Agent `0.11.25` 不包含这些改动。
## 产品语义
远程 Host 与项目能力是默认关闭的技术预览。用户在“设置 > 平台功能”的独立
“远程项目(技术预览)”页签启用开关后,Renderer 才显示 SSH Host 管理、托管 SSH 项目创建和已保存
远程 Host 与项目能力默认关闭。用户在“设置 > 平台功能”的独立
“远程项目”页签启用开关后,Renderer 才显示 SSH Host 管理、托管 SSH 项目创建和已保存
远程项目;Main 在 IPC 边界执行同一开关校验,避免隐藏界面被绕过。关闭开关不删除 Host、
项目或凭据;当前远程项目切回第一个普通本地项目。应用启动仍固定
选择普通本地项目,不会因为保存了远程项目而自动连接 Host。
远程项目只有两种工作模式:
- **Ask**:Runtime 在操作系统边界以只读方式访问项目 Workspace。
- **Ask**:Runtime 在原生工具权限边界只读访问项目 Workspace,不声称操作系统级只读隔离。
- **Execute**:用户已授权使用所选 SSH 账号的完整权限。Runtime 可以使用该账号可访问的文件、进程、网络和工具,不再要求额外 trust tier、consent checklist、逐工具审批或“受控执行”授权。
Execute 不获得 root 或 SSH 账号本身没有的权限。托管 SSH Prompt 会把当前选中的文本模型
@@ -44,7 +70,8 @@ profile 和凭据作为有界、逐 Prompt 的控制消息交给 Agent;该凭
桌面保存托管 SSH 子任务事件时,共用
[执行记录差量存储](../assistant-workbar/execution-history-storage.md)。
Agent transcript 与公共 Runtime 事件不改变;桌面的来源去重、消息投影事务和恢复序号
继续保留。该存储修复不要求单独升级 Agent。
继续保留。工具字段比较与 schema 35 对 schema 34 重复块的整理也在该共享桌面层完成;
不把工具过程改为仅存内存,不要求单独升级 Agent。
```text
Renderer
@@ -158,7 +185,36 @@ Detached GoodBuddy Agent
解析新的 current registry 并建立当前连接,无需刷新项目记录;当前环境无效时要求用户
显式修复,但不在项目切换中下载或安装。失败或取消保留 Host 配置、凭据、项目、
Workspace 和旧组件。
- 显式 stop、升级、身份冲突或进程退出时清理 GoodBuddy 自己的 socket、状态和子进程;不得删除或覆盖无关 Host 文件。
- 当前 Agent 可以常驻。新安装提升为 current 后,已包含退役逻辑的旧 Agent 每 250 ms 检查 registry 并进入
draining,不再接受新 Prompt;无活动任务时退出,有任务时等待完成。已连接 Desktop 的
终态 transcript ACK 也计入排空条件;断线任务完成后不等待离线 Desktop。待答问题仍是
活动任务。旧安装退出前可以重新 Attach 原任务,但不得重新 bootstrap 已退役安装。
- Prompt 完成后释放空闲 Runtime;共享进程仍有其他活动任务时保留,最后一个任务完成后
关闭并核对进程树为空。保留语义 transcript 与会话标识;下一轮重建 Runtime,OpenCode
加载原生 session,Continue 通过已有冷启动路径从持久对话历史初始化。
- 显式 stop/retire 可校验并停止已被 registry 替换的旧安装。退出只清理 GoodBuddy 自己的
socket、状态和子进程,不删除会话历史,不删除或覆盖无关 Host 文件。
### 升级残留清理
Agent adoption 和所有 Runtime 激活成功后,Main 调用新安装的
`cleanup-obsolete --installation-id <current-id>`。清理由当前 Agent 代码执行,不调用
旧安装的二进制。命令扫描现存 Agent installation 目录,跳过 current 和 candidate,
返回 `complete`、`removed` 与逐安装的 `deferred` 原因。清理不可用或存在延期项时记录
警告,已成功的安装不回滚;下次更新或再次调用该命令时重试,没有后台清理服务。
删除旧 payload 前须完整验证安装包,在既有 bootstrap lock 内确认原 daemon 的生命周期
记录已失效,并只读检查已有 `semantic-prompts.sqlite` 与 journal。活动、结果未知、
未确认的终态、尚存的 ACP channel,以及缺失或无法读取的记录都会阻止清理。随后根据
该安装的 Runtime owner 记录回收可确认归属的孤儿进程;身份冲突、无法检查或仍有子进程
的 owner 记录保留,等待重试。删除前还检查进程是否使用该 payload,并再次核对 registry。
只删除已确认不再使用的 Agent 可执行文件目录,保留 state、历史和 Workspace。
**仍存活的 legacy daemon 不会被此命令回收。** 旧实现没有原子排空并拒绝新工作的操作,
持久记录也不足以证明存活 daemon 的内存任务为空。因此即使没有连接或没有持久操作,
仍返回 `live-or-unproven-daemon-inactivity`,不能把升级后的旧版本自动退出规则追溯应用于
缺少退役逻辑的进程。此轮不裁剪共享 Runtime digest 目录,也不扫描 payload 已丢失而
仅剩 state 的安装。用户报告的六个 daemon、五个旧 helper 和约 2.67 GiB 占用未据此宣告解决。
### Host 级环境生命周期
@@ -198,12 +254,18 @@ Main 传入已规范化的绝对 POSIX root。Agent 返回 Workspace identity、
## Runtime
首个远程 Runtime 固定为签名 OpenCode ACP bundle。
托管 SSH 项目的 Composer Runtime 菜单只显示当前配置的 OpenCode 和管理入口,不显示
直连模型、Continue 或 DeepSeek Harness。激活旧远程会话时,若其保存了其他 Runtime
selection,Renderer 会恢复为当前 OpenCode 配置;Main 的远程请求校验仍是最终边界。
托管 SSH 项目支持签名 OpenCode ACP bundle 和 Continue 1.5.47 HTTP 宿主。
Composer Runtime 菜单显示 OpenCode、Continue 和管理入口,不显示直连模型或
DeepSeek Harness。项目与历史会话保留 OC/CN 选择;不支持的旧 selection 恢复为项目
默认值。Main 校验已安装 Runtime 身份与选择一致,按相应 CN/OC 模型 profile 建立模型桥。
本机配置的 SKILL 包和 stdio MCP Server 不上传或分配给远程 Runtime;远程 ACP Session
继续使用空 MCP Server 列表。Node.js、Python 等工具执行环境同样不修改或同步到 Host,
仅接收当前请求由 Agent 管理的 HTTP MCP 能力(例如 Execute 图片工具),不继承本机原生
MCP 配置。Continue helper 将 ACP Session 的服务器作为显式 `sessionMcpServers` 交给
共享 `ContinueHostAdapter`,与模型桥配置一起生成最终配置;不先写入会被独立模型分支
丢弃的原生配置。新建、加载和续接 Session 使用当前列表,空列表清除旧能力;Ask 在 helper
和 adapter 两层均不注入这些 Execute 能力。本机独立模型 profile 仍不继承原生 MCP,
既有知识库和自定义 MCP 的显式分配规则不变。
Node.js、Python 等工具执行环境同样不修改或同步到 Host,
完整边界见[工具执行环境](../local-tool-environment/README.md)。
### Ask
@@ -211,15 +273,16 @@ selection,Renderer 会恢复为当前 OpenCode 配置;Main 的远程请求
Ask 与 Execute 一样直接启动已签名 Runtime,不要求 Host 安装额外的进程隔离命令:
- `cwd` 为项目 Workspace,并继承 SSH 账号的正常环境;
- ACP 权限请求只有在工具种类为原生 `read` 且 Runtime 提供 `allow_once` 选项时才允许;
search、edit、execute、未知工具以及只提供持久授权的请求全部拒绝。
- Ask 启动时同时注入 OpenCode 顶层和 build Agent 的 `permission: "ask"`;Agent
工具权限分发边界只批准上述原生读取请求。该双重边界不另加文件系统 confinement,
也不改变 Runtime 的直接启动方式。
- Execute 的直接启动与模型桥启动均显式设置顶层和 build Agent 的 `permission: "allow"`,
避免继承 OpenCode 默认的 `external_directory` 询问;项目目录只是默认工作目录。
原生子代理同样使用当前 SSH 账号可访问的路径。仍出现的 ACP 权限请求由 Agent 按
当前 Prompt 模式自动答复,不增加 Desktop 人工审批,也不依赖日志解析。
- Agent-owned ACP 的备用权限分派允许原生 `read`/`search`,优先选择 `allow_once`;
写入、执行和未知工具仍拒绝。独占 raw ACP 保留其原有的读取权限分派。
- 未发布的共享路径以中性的 `permission: "ask"` 启动一个进程,再于每个 Session 的
`chat.message` 按已登记 workMode 设置原生工具规则。Ask 默认拒绝,放行已支持的
读取、搜索和业务问答;子 Session 同步根会话模式并保留原生显式限制,避免固定版本
ACP 不转发子 Session 权限请求而永远等待。
- Execute Session 放行工具,不再被另一个 Ask Session 的进程级默认值限制。项目目录
是默认 cwd,原生子代理仍使用所选 SSH 账号可访问的路径;不增加 Desktop 人工审批。
规则及模型路由的唯一实现说明见
[共享 Agent Session 设计](../assistant-workbar/runtime-process-reuse-technical-design.md#6-ssh-opencode-与-agent)。
### Execute
@@ -231,7 +294,7 @@ Execute 直接启动已签名 Runtime:
launcher 路径;
- `cwd` 为项目 Workspace;
- 继承 SSH 账号的正常环境、文件系统、进程和网络能力;
- 不注入 OpenCode Ask 权限配置;
- 独占启动使用 Execute 配置;共享启动由 Session 规则应用 Execute,不修改其他会话模式;
- 不进行 T2/T3、confinement attestation、approval bridge 或逐工具批准。
两种模式都保留输入字节上限、用户取消和进程组清理;输出只用有界内存队列与 journal
@@ -243,6 +306,37 @@ Execute 直接启动已签名 Runtime:
`0.11.10` 及更早版本仍会按其 manifest 上限夹紧,因此桌面启用这一语义前必须配套更新
到 Agent `0.11.13` 或更高版本。
## 执行清单接入范围
OC `todowrite` / CN `Checklist` 顶部展示的权威需求及接入设计见
[Runtime 执行清单](../assistant-workbar/runtime-checklist-technical-design.md),
远程实现与验证范围见[执行清单验证记录](./runtime-checklist-validation.md)。
目标范围包括远程 GoodBuddy Agent 运行 OC 和 CN;ACP 仅承担传输与远端协议。
CN 1.5.47 的 `serve` 提供 HTTP `/state`、`/message`,没有原生 ACP 命令。
Agent 的 `continue-acp-helper.ts` 复用 `ContinueHostAdapter` 启动该宿主,转换 ACP
Session、文字、工具、问答、清单与取消。Main 冻结的模型配置仍通过既有 Agent gateway
执行,CN 配置仅持有 loopback 路由及占位凭据。成功的原生清单经
`_meta.goodbuddyChecklist` 进入同一 operation 的 semantic transcript,Main 校验 CN
来源和统一 schema;普通工具参数和正文不生成清单。
2026-09-18 Linux x64 已通过 CN 开发复合包验包、安装、Agent 启动及 CN 激活,随后
通过真实模型清单新增/清空、未 ACK 断线重放、取消和同对话 Ask 续发。默认组包现同时
携带 OC/CN,`additionalRuntimes` 随主 Runtime 一起验签、安装和加载,仍可读取已有单
Runtime 包。2026-09-19 完整桌面验证覆盖联合包安装、CN 项目创建、清单更新、重启和
取消后续发;实测范围及尚未覆盖的平台见上述验证记录。开发包尚未发布。
2026-09-18 实测 OC 1.18.29 的 ACP 仅转发 `todowrite` 工具活动,没有原生 `plan`
通知。已有 OC 插件现将精确 `todo.updated` 事件经 `_meta.goodbuddyTodoEvent` 转发,
保留原 Session ID、完整条目及空数组。Main 校验原生事件类型、Session 和清单 schema
后输出 `checklist`,不从工具标题、截断字符串或正文推测清单。标准 `plan.entries`
也可映射 OC 清单;尚未确认的 `plan_update`、`plan_removed` 只形成活动状态。
清单恢复复用下节流程及既有 semantic transcript/ACK;已通过首份清单未 ACK 时断开、
同操作重连、随后收到空清单的真实 Host 验证。顶部显示与数据库历史由对应模块单独验收。
同次验证复现原生 ACP 冷启动超过 15 秒控制超时。`runtime/startPrompt` 的控制等待
现至少为 30 秒,其余控制请求沿用原时限;Runtime 外层仍保留启动超时和取消处理。
## ACP 与断线
- 管控面只负责有界 JSON-RPC:连接、查询、启动、取消、关闭和重连。数据面只负责
@@ -291,11 +385,24 @@ Execute 直接启动已签名 Runtime:
自动重放。插件随 OpenCode 实例释放临时端口,helper 退出时删除插件文件。
以上只适用于当前 Agent 管理的 SSH OpenCode 生产路径,不声明任意 ACP 服务的问答兼容性。
真实二进制、SSH 传输和取消验证见[工作栏进度](../assistant-workbar/progress.md#2026-09-13-托管-ssh-原生问答)。
未发布进程复用源码补齐 Desktop 重启后的多任务并行附加、实时待答快照及 Main 答案
持久化,详见[恢复与终态投影](../assistant-workbar/runtime-process-reuse-technical-design.md#63-恢复与终态投影);
不把该待答投影当作 durable transcript 或旧版本已经支持的行为。
Desktop Main 接收实时问题时保存其消息位置,回答后不移动到后续输出之后;实时待答
快照不伪造语义来源。位置去重、旧会话边界见
[Runtime 交互边界](../assistant-workbar/runtime-interactions.md#运行与失败),
当前源码的真实 Host 证据见[问答位置持久化](../assistant-workbar/progress.md#2026-09-19-问答位置持久化)。
- ACP 工具结果同时支持文本 `content` 和 `rawOutput.output`,移除 OpenCode 的
`<task><task_result>` 外层包装后渲染正文。恢复已有子代理时使用 ACP 所带的所属
`toolCallId` 继续路由,不等待新的父 Task metadata,也不重放模型请求。
远端实时过程需要包含该插件的 Agent;旧 Agent 的最终结果仍可查看,历史未采集过程
不会被补造。
- 远端 ACP 普通工具失败后,只有最后一次工具失败之后收到非空白助手正文、Prompt 正常
完成且没有 `pending` / `in_progress` 工具时,才在 `done` 前将仍失败的工具更新为
`recoverable`,保留调用 ID、名称、输入、输出和错误。失败前的正文、推理和纯空白
不作为恢复依据;后续再次失败会清除该依据。原生子代理失败不参与普通工具恢复,
Prompt 失败、取消或 `outcome-unknown` 也不触发转换。Agent-owned 与直接 ACP 路径
共用此规则;转换表示后续响应已处理失败,不表示工具重新执行成功。
- 本机 OpenCode SDK 路径允许不同会话并行,同一会话仍按请求顺序执行。每个请求独立拥有
SSE 事件订阅,并在正常完成、错误、取消或消费方结束迭代时主动关闭自己的响应流,
不关闭其他会话的订阅或共享 Server。聊天与原生 Compact 共用 `consumeEventSubscription`,
@@ -305,6 +412,10 @@ Execute 直接启动已签名 Runtime:
的断线继续执行语义。
- 同一 detached Agent 存活时,短暂 SSH 断线依次执行 `controller/resume`、`runtime/resumeAcpChannel` 和 `runtime/replayAcpChannel`,从 Main 已确认的 cursor 后只重放 Agent 到 Main 的已记录输出。重复 frame 会被确认但不会再次交给上层。若上一代连接只留下没有活动请求的 detached binding,完成精确 controller takeover 后会先有界停止并核对遗留 Runtime process,再用新 channel epoch 重新打开同一 binding 并恢复已有 ACP session;其他 controller、未证明 takeover 或仍有活动请求的 binding 仍被拒绝。
- Main 到 Runtime 的 ACP 输入、模型请求、工具请求和 blob 不自动重放。
- Continue helper 的会话与上下文仅保存在进程内。Desktop 冷打开已完成的 CN 对话时,
关闭旧空闲 binding,以新 binding 和已有对话历史创建会话;同一存活会话内续发仍递增
Prompt 序列。活动请求恢复沿用原 operation 和序列;start 未确认时通过既有 attach
核对,不递增序列或重新发送 Prompt。
- 无法确认外部 Provider 是否已处理的模型调用保持结果未知,避免重复计费或重复副作用。
- 只有远端返回 identity 匹配且 `closed: true` 时,Main 才删除持久化 binding;传输失败或未确认 close 会保留 recovery identity。
- 终态 close 由 Agent 在一个事务中为两个方向记录 sequence high-water tombstone,删除剩余 frame 和 active channel,并归零对应 journal quota。tombstone 用于拒绝迟到的旧 epoch frame,因此当前不会按时间自动裁剪。
@@ -332,6 +443,14 @@ Execute 直接启动已签名 Runtime:
时提交 Task 终态;事务成功后生成器才继续并向 Agent ACK。断电发生在同一 sequence
中间时会重读该 sequence,已写事件按 `(binding, operation, sequence, eventIndex)` 去重,
未写事件继续归并,不会重复 Provider 或工具执行。
- 工具失败恢复所需的正文依据从 Desktop 已提交的 `task_events` 推导:按
`remote_semantic_sequence` 数值及 `remote_event_index` 顺序判断最后一次失败后是否有
非空白 `text`,重连时与已有工具状态一起交给 Runtime,保留断线前的有效依据。
不从会原地更新或被截断的展示块推断顺序,不新增表、字段或持久快照。终态转换按
`callId` 排序,并重发已为 `recoverable` 的工具,使部分终态落库后的重读仍使用稳定的
`eventIndex`,沿用既有 provenance 去重和 checkpoint 提交。
重连时,工具及原生子代理的状态也以已提交的远端活动事件为准,覆盖消息终态处理派生的
失败状态,避免将未完成活动误判为可恢复错误,保证终态重放内容一致。
- 远程 context metrics 与对话级压缩后的估算值按本次请求实际使用的 Runtime 选择写入
`context_state_json`。实时接收与恢复入口都传入请求的选择;数据库在未传入时按对话、
项目、auto 顺序解析。继承项目设置的对话保持 `runtime_selection_json = NULL`,
@@ -352,7 +471,8 @@ Execute 直接启动已签名 Runtime:
- 完成提示是项目选择器内恢复进度的收尾反馈,首次收到该项目该 `requestId` 的完成状态后
显示 3 秒,然后从收起按钮和展开菜单同时隐藏;不另发全局成功通知。计时由
`ProjectSwitcher` 持有,切换项目、开合菜单或重复状态快照不延长期限,也不重放已隐藏的
提示;新 `requestId` 的恢复可以再次展示。进行中状态和失败/就地重试持续保留。
提示;新 `requestId` 的恢复可以再次展示。进行中状态和失败提示持续保留;重试操作仅放在
展开的项目列表对应项目旁,收起时只显示状态,不在新建项目按钮旁增加恢复按钮。
隐藏仅作用于展示层,`App` 保留完成业务状态,继续用于发送、队列恢复和事件顺序判断。
- 正常应用退出对已接受托管 SSH 请求执行 detach,不等待远端完成;本地请求和尚未接受的
请求仍按原行为取消。用户显式“停止”始终发送稳定 operation cancellation,并等待 Agent
@@ -387,11 +507,12 @@ Execute 直接启动已签名 Runtime:
usage/task 数据。
- 新组包的 Runtime manifest 将 `maximumPromptRuntimeMilliseconds` 设为 `0`,表示由
Prompt 自身的 deadline 决定时长,不再额外截为十分钟。旧 manifest 中的正数限制仍按其
声明执行。进程 owner 使用真实剩余时长安排 Prompt deadline;启动与停止等待的有界
超时不再缩短 Prompt。
- helper 可以接收同一 Prompt 内并发到达的模型桥请求;它在单一稳定模型桥上按到达
顺序等待并交付,不返回本地 `bridge-busy`,每个响应只有在 HTTP 完整 flush 后才
发送 delivery ACK。
声明执行。独占进程 owner 使用真实剩余时长安排 Prompt deadline;共享路径由 binding
执行逐 Prompt 输入与期限检查,不把历次 Prompt 累计当作整个进程的配额。
启动与停止等待的有界超时不再缩短 Prompt。
- helper 可以接收并发模型桥请求,同一 Session/operation 的稳定模型桥按到达顺序
等待并交付,不同 operation 可并行。请求在排队后再次核对原路由,过期请求不转交给
新 operation;每个响应只有在 HTTP 完整 flush 后才发送 delivery ACK。
- GoodBuddy 自己管理会话标题,因此传给 OpenCode 的配置禁用 title Agent;一次用户
Prompt 不会额外触发标题模型请求。
- Agent gateway 不再把 Provider 请求往返 Desktop;legacy blob bridge 只保留给旧的
@@ -570,6 +691,16 @@ bundle 构建通过;这组历史结果不替代完整 Host 支持改动后的
### Linux 历史验收
- 2026-09-16:当前源码通过本机 Linux x64/arm64 两份 `0.11.18` 旧包副本的生产签名、
manifest、包内 lock 和 payload 校验(OpenCode `1.18.9`),原缓存元数据未变。
共享 Linux x64 Host 的 LAN/VPN 两条路由均通过固定主机密钥验证;在 `/root/tmp`
独立目录运行当前源码校验与 `preparePackage` / `commitPackage`,随后旧包 Agent
bootstrap、health、stop 全部通过,未操作原有 Agent,模型请求为 0 次。
相关扩大回归为 326 通过、1 跳过;全量 `npm test` 为 4146 通过、67 跳过、4 失败,
另有 App 测试文件因缺少 `LocalInferencePage` 导入失败。大离线包超时用例复跑通过;
其余失败为 heartbeat fixture 重复 `pinned` 列及导航设置默认字段导致的断言差异。
Node/Agent 类型检查和本次改动文件 lint 通过;全量 typecheck 受上述缺失组件阻塞,
全量 lint 受 story-graph demo 的 22 项 `document` 未定义错误阻塞。
- 2026-09-06 使用当前源码隔离 Agent 验证原生子代理过程插件。最终场景收到 37 条 ACP
插件事件,桌面侧从已保留活动重建跟踪器后仍接收后续文本与工具;子代理和父请求正常
完成,最终模型调用账本为 4 次 completed。此前成功场景另 4 次,两轮真实 Host 共
@@ -733,6 +864,13 @@ goodbuddy-agent diagnostics --installation-id <installationId>
校验,但所有 production 层统一使用一组 GoodBuddy 通用发布身份并通过签名域区分用途,
不要求内部 Runtime 单独配置密钥。`build/agent-catalog.cjs` 为双架构包生成签名累计目录,
拒绝同一版本/架构改变字节。
- `remote-runtime-launch-contracts.ts` 中的 OpenCode Runtime lock 版本使用现有
`componentVersionSchema` 校验,不再硬编码为 `1.18.29`,避免 Desktop 升级后仅因版本
不同拒绝已有完整签名旧包。包内 manifest、lock 与 descriptor 的版本一致性,以及签名、
哈希和协议兼容性检查仍须通过;构建与发行的 OpenCode pin 保持 `1.18.29`。
- Agent lock 的 Node/koffi 版本同样使用 `componentVersionSchema`,允许完整旧包携带
不同于 Desktop 构建 pin 的依赖版本。验证时仍以包自身 lock 精确核对 manifest 的
Node.js、koffi 和 koffi native 版本;Node `24.19.0`、koffi `3.1.4` 发行 pin 不变。
- `.github/workflows/agents.yml` 只使用进程内临时测试 key 做分支/PR 原生验证,不发布。
`.github/workflows/agent-release.yml` 才可在 annotated `agent-v<version>` 标签和受保护
`agent-signing` Environment 中构建 production 包。Agent GitHub Release 必须
@@ -746,6 +884,93 @@ goodbuddy-agent diagnostics --installation-id <installationId>
信任条件。上传到 Host 前重新序列化为确定性 LF JSON。签名目录、描述符、manifest 和
payload 仍按原始签名字节及 SHA-256 验证。
## 2026-09-20 冗余代码清理验证
- 删除没有生产调用的结构化命令 runner、两个进程身份辅助函数、占位 peer provider、
平台查询辅助函数和五个旧 Runtime 请求构造/摘要辅助函数。保留实际 CLI、进程检查器、
请求 schema、原生 peer 校验及包签名验证,不改变 Ask/Execute 或协议行为。
- 清理后的当前源码 Agent、Main attach transport 和 protocol client 在共享 Linux x64
Host 上通过固定 Host Key 连接,完成启动、状态/能力读取、只读工作区打开及真实文件读取,
断开后再次连接并重复读取。验证没有使用模拟 Agent 或旧安装包的 Agent 实现。
- 本轮测试进程剩余 0;专属 Host 目录、上传文件及本地隔离 profile 已删除。
文本和图片模型请求均为 **0 次**。这是未使用代码删除后的启动/连接验证,不是新增的
Runtime 模型调用、安装更新或其他架构验收。
- 临时证据为 `redundancy-cleanup-host-{build,run}.cjs` 与
`redundancy-cleanup-host-live.log`;定向回归 230 项通过,typecheck、lint 通过。
- 同期全量 `npm test` 为 4,844 通过、67 跳过、11 失败;失败来自并行修改中的五个
Renderer 帮助提示/焦点测试套件。更新后的五个套件连同更新设置大小展示回归共 64 项
定向复跑全部通过;未把这次复跑记为又一轮全量通过。
## 生命周期验证(2026-09-23)
通过已有 `lifecycle-host` harness 重建当前源码的 Agent、daemon 和桌面驱动,使用已保存
凭据与固定 Host Key 经 LAN 连接共享 Linux x64 Host。运行文件均位于独立
`/root/tmp/gb-lifecycle-*` 目录,Runtime 使用 Host 已安装的 OpenCode。两次 Ask/Execute
运行合计 8 次真实 Provider 请求,另一次空闲更新验证没有模型请求。
- Ask 完成后测试所属子进程为 0;下一轮重建 Runtime、加载同一原生 session,并准确回忆
上轮标记。第二轮完成后子进程再次为 0。
- Execute 启动等待 8 秒后写文件的工具任务,Desktop 断开,再把新安装提升为 current。
旧 daemon 进入 draining,活动进程仍在;重连并恢复原 operation 后正常收尾,旧 daemon
退出,测试所属子进程为 0。
- 空闲更新中,旧 daemon 在提升后 3 秒断言窗口内退出,新 current daemon 仍为 ready。
各次运行清理时所属子进程均为 0,未修改共享安装 registry。
8 个定向套件共 213 项通过,覆盖 CLI 退役安装 stop/retire、daemon 常驻与排空、共享及
独占 Runtime 回收、待答问题仍属活动任务、断线恢复、原生历史加载和 Desktop 冷启动。
远程 UI/IPC 定向复跑 28 项通过,SettingsPanel 完整套件通过;IPC/App 扩展运行共
517 项通过、4 项失败,失败位于并行修改中的
心跳、设置读取次数和监督者导航断言。全量 `npm test` 运行到 600 秒工具时限仍无最终汇总,
期间报告数据库迁移、监督者和导航测试失败,不能记为全量通过。`npm run lint` 通过。
类型检查早期通过,最终复跑被并行新增的 `tests/incremental-review-live.electron.test.ts:23`
中环境变量对象类型错误阻塞。
本次实机覆盖当前源码的 daemon、registry 提升、托管 ACP、真实 Runtime 与模型请求。
未重复完整签名包安装和 UI 更新点击,也未在实机重跑 Continue 或问题答复;这些场景
不能以本次 OpenCode 结果替代。历史发布说明中的技术预览记录保留,当前远程项目设置与
说明文档已去除该标签;Linux 桌面控制等其他预览功能不受影响。
## 升级残留清理验证(2026-09-24)
本轮在产品源码改动完成后,重新从工作区构建 cleanup CLI 和隔离验证脚本,并在共享
Linux x64 Host 重跑。使用既有 GoodBuddy 加密凭据存储和固定 Host Key;没有输出凭据。
本机临时 harness 位于 `C:/Users/jiang/AppData/Local/Temp/opencode/`,执行命令为:
```powershell
node "C:\Users\jiang\AppData\Local\Temp\opencode\upgrade-cleanup-build.cjs"
node "C:\Users\jiang\AppData\Local\Temp\opencode\upgrade-cleanup-run.cjs"
```
`upgrade-cleanup-build.cjs` 打包 `upgrade-cleanup-cli.ts`、`upgrade-cleanup-linux.ts`
和 `upgrade-cleanup-host.ts`;run 脚本通过 Electron 启动 host 驱动。Host 驱动读取共享安装
registry 以定位 Node 和 koffi 依赖,仅在 `/root/tmp/gb-upgrade-upload-*` 上传测试脚本。
远端执行形式如下,其中 `<current-id>` 来自共享 registry,`<upload-root>` 是本轮唯一目录:
```sh
NODE_PATH='/root/.goodbuddy/agent/installations/<current-id>/lib/node_modules' TMPDIR='<upload-root>' '<installed-node>' '<upload-root>/linux.cjs' '<upload-root>/cli.cjs'
```
场景在独立 `/root/tmp/gb-upgrade-cleanup-*` HOME 中生成临时签名测试安装和既有格式的
生命周期、语义、journal、owner 记录,通过该 HOME 的 current 安装执行真实 CLI:
`goodbuddy-agent cleanup-obsolete --installation-id current`。测试用实际 Node 进程表示
不含退役逻辑的旧 daemon/helper,不是部署历史发布包或运行真实模型任务。
本次构建与远端运行退出码均为 0。远端断言确认:孤儿 Runtime 被回收,所属旧安装 payload
删除;存活 legacy daemon 延期;另一个带 `starting` 操作记录的进程存活;历史数据库字节
不变;current/candidate payload 保留;第二次调用没有重复删除。运行输出为:
```json
{"passed":true,"productionCli":true,"realOrphanReclaimed":true,"liveLegacyDeferred":true,"activeWorkPreserved":true,"historyPreserved":true,"idempotent":true,"providerCalls":0}
```
测试结束清除自身进程和隔离目录,没有清理共享用户安装或修改其 registry。早先隔离实测
发生在最终补充两个本地回归用例之前;本节记录的是之后重新构建源码的复跑。此前最终本地
9 个定向套件 99 项通过,typecheck 和改动文件 ESLint 通过。
本次三分钟时限内未找到可直接运行 backend/bridge Vitest 套件的 Linux harness,未安装
额外测试依赖,故这两类 Linux 套件未运行。上述 cleanup 场景不替代这些套件,不证明完整
UI 更新或签名复合包安装流程通过;真实模型调用为 0 次。
## 发布前完整回归(不能替代开发验证)
以下清单只用于复核开发期间已经在真实 Host 上通过的链路,不得作为 Agent 改动的首次
@@ -764,3 +989,72 @@ goodbuddy-agent diagnostics --installation-id <installationId>
9. 运行一次有界的真实模型调用,确认凭据不进入 Renderer、SSH 参数、远端环境或磁盘,
只在当前 accepted operation 生命周期内进入 Agent 内存。
10. 中断并恢复 SSH,确认活动 Runtime 不被网络抖动终止。
## Prompt 完成与空闲回收验证(2026-09-24)
Runtime 可以先收到完整模型正文并返回 ACP `end_turn`,随后 helper 才完成 delivery ACK。
`RuntimeAcpBackend` 的正常完成路径现在要求 broker 等待已接收的 exchange 完成,最多等待
2 秒,再关闭 socket;取消等强制关闭路径不等待这一步。ACK 超时或交付失败仍记录
`outcome-unknown`,语义 transcript 与 reconciliation 保持一致。
正常完成时,Agent 在串行 control 操作内先持久化语义终态,再将 binding 标为空闲并回收
Runtime。回收失败记录 `daemon.stop.failed`,reason 为 `idle-runtime-cleanup`,不把已提交的
`completed` 改成未知结果。确定性回归覆盖共享与独占 Runtime 的 stop/reconcile 失败,以及
等待 ACK 期间不得提交终态或停止 Runtime。
Desktop 的下一次显式发送会淘汰缓存中的不可用 binding,再通过 cold-open 创建新请求并
传入已有对话历史。OpenCode 和 Continue 的未知终态均有回归;不会重放原来的未知请求。
`remoteRecoveryOnly` 只允许附加同一活动 operation,已完成或结果未知的缓存不能借恢复
入口发起新请求。缓存仍有活动 operation 时,不同 request ID 的发送被拒绝;普通失败后
已回到 `ready` 的会话仍可继续使用。
### 本机模拟范围
`agent-owned-acp-prompt.local.test.ts` 使用本机已安装的 OpenCode 1.18.29、真实 ACP 和
loopback fake model。测试暂缓 delivery ACK,确认正文 `ACK_OK` 和 ACP 完成先到达,释放
ACK 后提交 `completed`,随后注入清理异常并确认终态不变。该用例注入了
`completePrompt` 回调,没有经过完整 `RuntimeAcpBackend` 完成路径;backend 的提交顺序、
清理诊断和 reconciliation 由 `runtime-acp-backend.test.ts` 的独立回归检查。
本轮本机定向复跑 7 个套件,199 项通过、10 项 Unix-only 用例在 Windows 跳过,耗时
5.60 秒。套件为 `runtime-acp-backend`、`agent-owned-acp-prompt`、
`agent-owned-acp-prompt.local`、`model-bridge`、`model-bridge-client`、
`runtime-composition` 和 `acp-remote-runtime`。本轮没有运行完整测试套件。
### 最终源码 Linux 场景
源码修正完成后,重新构建临时 `lifecycle-host-agent`、`lifecycle-host-daemon` 和
`lifecycle-host-driver`,通过既有 GoodBuddy 加密凭据存储与固定 Host Key 连接共享
Linux x64 Host。执行命令为:
```powershell
node "C:\Users\jiang\AppData\Local\Temp\opencode\lifecycle-host-build.cjs" agent daemon driver
node "C:\Users\jiang\AppData\Local\Temp\opencode\lifecycle-host-run.cjs" driver --fake-model --completion-only
```
测试在独立 `/root/tmp/gb-lifecycle-*` HOME 启动当前源码的 Agent daemon,通过 Desktop
`createManagedRemoteAcpRuntime`、SSH attach、Agent protocol、
`createProductionRuntimeProtocol` 和 `RuntimeAcpBackend` 运行 Host 上已安装的 OpenCode。
该路径使用真实 Runtime process owner、model bridge、ACP 和语义存储,没有注入 backend
的 completion 回调。测试只替换 daemon 的外部模型 HTTP 响应,返回确定的 SSE 文本。
两轮 Ask 均收到完成事件,每轮结束后测试目录所属的 Runtime/helper 进程数为 0。第二轮
在回收后重新启动 Runtime,保留同一原生 session ID,模型请求中带有第一轮保存的标记。
运行输出的关键记录为:
```json
{"inspected":true,"oldState":"ready","draining":false,"remainingOwnedProcesses":0,"realProviderCalls":0,"fakeModelCalls":1}
{"inspected":true,"oldState":"ready","draining":false,"remainingOwnedProcesses":0,"realProviderCalls":0,"fakeModelCalls":2}
{"currentSourceAsk":true,"runtimeRecreated":true,"nativeHistoryPreserved":true}
{"stopped":true,"realProviderCalls":0,"fakeModelCalls":2,"remainingOwnedProcesses":0}
{"exit":0,"stderrBytes":0}
```
构建和运行退出码均为 0,fake model exchange 共 2 次,真实 Provider 请求为 0 次。清理
只停止测试 daemon、其 Runtime 进程并删除隔离目录;没有更改共享用户安装或 registry,
没有停止无关 Host 进程。
此次 Linux 场景验证正常完成、空闲回收和下一轮恢复原生历史;没有在 Host 上注入 ACK
延迟或清理失败,也没有覆盖未知终态后的新 binding 场景、Continue、Execute、断线恢复、
完整 UI 操作或签名包安装。故障时序与未知终态的下一次发送以本机定向回归为证据,不能用
这两轮成功请求替代。
+9 -1
View File
@@ -1,9 +1,17 @@
# Smart Heartbeat
Smart Heartbeat 负责按计划或条件主动检查范围内信息,并生成可监督的建议或任务。
Smart Heartbeat 已提供按计划或手动执行的有界回顾,生成报告及后续建议。应用默认关闭;启用及计划条件见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。条件或事件唤醒仍需后续设计。
现有入口已升级为“监督者”,智能心跳作为其定期唤醒机制,驱动检查与整理;应用提供工作回顾、故事线图谱、自动监督、活动记录和设置五个页签。计划在自动监督管理,创建与编辑使用 Modal;设置只保留算法参数。定义见[监督者应用产品设计](../conversation-supervision/supervisor-prd.md),实施方案见[监督者技术设计](../conversation-supervision/technical-design.md)。现有计划和范围保留,监督调用继续禁止工具执行。
自动报告及监督回顾分别按来源版本和已读位置处理增量;没有待处理输入时记录无变化,跳过模型及新报告。手动历史重看和删除报告的边界见[自动增量规则](../conversation-supervision/logic-design.md#自动增量与手动重看)。
## 文档导航
当前监督者设置已接入报告与监督整理两项模型阶段超时(30 至 600 秒,默认 240)及共享监督并发数(1 至 4,默认 1),模型超时不包含排队、采集、保存或整次执行期限。报告先获槽再领取,实际请求开始前校验并刷新租约,报告提交或无变化后释放槽位再进入监督。普通聊天不入监督池,尚无跨聊天与监督的提供商全局并发或 RPM/TPM 限制。租约、固定传输时限及重试与用户超时分开,见[控制清单](../conversation-supervision/review-scheduling-design.md#当前超时与调度控制清单已实现)。完整分页与持久化续跑仍为设计,不能把现有增量检查称为完整调度器。
监督者“活动记录”页签同时显示手动回顾和心跳执行,将新心跳报告与下游监督按运行 ID 合并,保留各阶段真实状态。心跳审计、运行概览、指标、趋势、自动监督报告阅读和建议处理与计划列表同在自动监督页;工作回顾只读取统一监督结果,设置只保留配置。当前布局见[监督者 UI 设计](../conversation-supervision/ui-design.md#应用入口)。心跳审计继续读取报告自身状态,完整执行状态以[活动规则](../conversation-supervision/logic-design.md#活动记录)为准。
| 文档 | 权威职责 |
| --- | --- |
| [智能心跳 PRD](./prd.md) | 用户目标、范围、计划、运行和产品验收 |

Some files were not shown because too many files have changed in this diff Show More