Compare commits

..
67 Commits
Author SHA1 Message Date
mesalogo 670b598530 fix: remove divider from runtime controls dialog
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
Deploy website to GitHub Pages / Deploy static website (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
2026-10-08 16:23:08 +08:00
mesalogo 1a59c2d880 feat: apply selected experts as role prompts on the conversation runtime 2026-10-08 16:23:08 +08:00
mesalogo 13b58627c0 feat: surface expert and runtime controls in composer toolbar 2026-10-08 15:55:27 +08:00
mesalogo 4266ff5bea docs: expand VitePress user guide 2026-10-08 15:18:25 +08:00
mesalogo 377b14fca6 test: use immediate cancellation signal
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-10-08 14:38:59 +08:00
mesalogo a406f5962e test: restore cancellation and drain coverage
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-10-08 13:00:41 +08:00
mesalogo 7f0a73e6d0 fix: type storage failure assertion
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-10-08 12:29:16 +08:00
mesalogo 2d68d1043d test: drain storage after write completion
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-10-08 12:07:54 +08:00
mesalogo 4a2ea49069 docs: simplify README screenshots 2026-10-08 11:47:34 +08:00
mesalogo 277da413c6 test: remove stale cancellation fixture reference
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-10-08 11:38:23 +08:00
mesalogo e136026496 test: remove invalid pre-aborted storage request
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-10-08 11:19:08 +08:00
mesalogo 59a28209d3 test: match storage cancellation behavior
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-10-08 10:48:44 +08:00
mesalogo 9788b4cd8e test: make cancellation fixture deterministic
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-10-08 10:21:55 +08:00
mesalogo c90ad2af6f test: accept client readiness loss error
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-10-08 09:57:54 +08:00
mesalogo 95ece25c37 test: reject calls after confirmed storage loss
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-10-08 09:40:38 +08:00
mesalogo c4ab83a7e3 test: wait for storage loss state before retry
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-10-08 02:53:56 +08:00
mesalogo 7eb113028f test: keep crash write messages unique
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-10-08 02:37:31 +08:00
mesalogo 5ba127e332 test: accept storage readiness loss during crash
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
2026-10-08 02:16:49 +08:00
mesalogo 3d063937c3 feat(magic-notes): add rich table editing
Add native Quill table insertion and contextual row, column, and table actions. Preserve table-only drafts through composer validation, IPC, and storage, and document the saved behavior.\n\nRelease note: 魔法笔记支持插入和编辑表格,空单元格表格也可保存并在重新打开后恢复。
2026-10-08 02:02:24 +08:00
mesalogo 2a92f8f875 test: await storage replacement before retry
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-10-08 01:42:29 +08:00
mesalogo 95f441958d test: make storage loss rejection deterministic
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-10-08 01:06:13 +08:00
mesalogo 384683e861 test: stabilize storage loss validation races
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-10-08 00:39:03 +08:00
mesalogo 7eb51bd5ff test: isolate utility process database permissions
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-10-07 23:26:37 +08:00
mesalogo 9212fd4359 test: keep storage parity assertion deterministic
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-10-07 23:00:38 +08:00
mesalogo 42ad8a98b4 test: stabilize storage owner CI fixtures
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
Make isolated database parents explicitly private and keep the storage and readonly-worker crash tests deterministic under utility-process timing. Preserve committed-data recovery assertions and dedicated failure coverage.
2026-10-07 22:59:37 +08:00
mesalogo fbfafa15e1 chore: prepare desktop 0.15.13 and agent 0.15.6
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
Document the desktop storage utility-process owner, coordinated Agent runtime changes, bounded output continuation, and focused validation boundaries. Record schema 62 compatibility and the remaining concurrent acceptance matrix.
2026-10-07 22:27:02 +08:00
mesalogo c1e4bbf66a test(magic-notes): preserve empty global tags 2026-10-07 22:03:12 +08:00
mesalogo 3e3a021682 fix(magic-notes): preserve duplicate-tag error cause 2026-10-07 21:38:13 +08:00
mesalogo c465d39d58 docs(architecture): clarify storage pass-through mode 2026-10-07 21:26:40 +08:00
mesalogo 13c29badfb feat(magic-notes): improve tag management UI 2026-10-07 21:05:14 +08:00
mesalogo e0607a153f fix(supervisor): expand bounded source aliases 2026-10-07 17:15:00 +08:00
mesalogo 425accf49d fix(settings): preload route on direct navigation 2026-10-07 16:58:09 +08:00
mesalogo 904bb691b8 perf(parsing): remove unsupported worker queue limit 2026-10-07 16:15:28 +08:00
mesalogo 4c25e85b08 test(storage): update fault acceptance capacity 2026-10-07 16:01:18 +08:00
mesalogo 8473189fe2 perf(concurrency): remove unsupported product admission limits 2026-10-07 15:52:07 +08:00
mesalogo 1f85d0c0e9 docs(storage): set the C06 lifecycle acceptance window to five minutes 2026-10-06 15:43:46 +08:00
mesalogo d80d42b032 feat(storage): move desktop SQLite ownership into a storage utility process
Main no longer opens desktop business SQLite. A single utility-process
storage owner holds the Assistant, Knowledge, attachment, document-result,
remote-binding and native model-ledger databases and runs whole domain
transactions; Main and Runtime callers use explicit async domain operations.

- Remove the Main readWithFallback path and the supervision worker's
  separate business write connection; manifests are scanned read-only and
  committed by the sole writer.
- Remote events are ACKed only after commit; process loss reports
  unconfirmed writes instead of replaying them.
- Use native utilityProcess messaging with one in-flight bound (32) and
  ordered waiting instead of capacity rejection and retries.
- Start the storage process before the window so startup overlaps.
- Keep short tool output in memory and store full output once in owned
  storage; paging, branching, restart and deletion reuse that copy.
- Put required runtime launch files under goodbuddy-prefixed owned temp
  directories and reclaim confirmed stale ones; stop writing duplicate
  parsed.md.
- Add lint guards for SQLite ownership and architecture, concurrency
  acceptance fixtures, and maintenance/architecture documentation.

Validation: typecheck and lint pass. Alternating baseline (edf019aa) vs
candidate runs show no regression on single-session, C01, C02, C04 and a
5-minute C06; Main p99 drops from ~21 ms to ~16.7 ms under concurrency.
Fresh portable copy (1.75M events) upgrade content unchanged; real-model
output paging passes with 4 external requests. Full npm test runs had
1-9 different timing-sensitive failures per run that pass when rerun.
Not covered: C03 review publication, C07 source body, app-level C05.
2026-10-06 15:40:18 +08:00
mesalogo bc677a3718 chore(repo): keep agent temporary files under the ignored temp directory 2026-10-06 15:39:47 +08:00
mesalogo edf019aaa7 fix: soften the transition above the chat composer
Add a theme-aware 16px fade above the composer to soften the clipped message edge without changing layout or pointer interactions. Document the treatment in the UI design system.

Release note: 聊天输入区顶部增加淡渐变,让消息与输入框之间的衔接更自然,并适配浅色和深色主题。
2026-10-05 21:00:44 +08:00
mesalogo 4bb7573b10 fix: simplify workspace file list metadata
Show only modification time beside file names and share its space with hover actions. Remove the creation-time column and fixed minimum tree width to keep narrow workspace panels compact.

Release note: Workspace file lists now show only modification time and keep file names stable when more actions appear.
2026-10-05 16:20:25 +08:00
mesalogo 56b8b10d46 fix: let direct models continue after tool execution failures
Return ordinary tool exceptions as paired error results so the model can decide how to recover. Preserve cancellation, existing recovery guidance, and failed tool activities without adding automatic retries or retry limits.

Validate all three text protocols with real process execution and cover bounded errors and cancellation. Focused tests: 161 passed, 1 skipped; typecheck and lint passed. Full suite: 6230 passed, 87 skipped, 1 Obsidian temporary-directory assertion failed.

Release note: Direct-model conversations can continue after a tool fails, allowing the model to use the reported error to choose its next step.
2026-10-05 15:20:17 +08:00
mesalogo 0eaaa85d4c ci: provide a real keyring for encrypted Electron settings
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
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-10-05 15:05:28 +08:00
mesalogo 36e671a0a2 chore: prepare desktop 0.15.12 and agent 0.15.5
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
Document Telegram validation boundaries, custom review ranges, review recovery and responsiveness, channel delivery and remote cancellation fixes. Record schema 62 backup requirements and separate development evidence from release CI.
2026-10-05 14:41:15 +08:00
mesalogo 2be57492e6 fix: resume reviews after conversation deletion and update release docs 2026-10-05 14:22:16 +08:00
mesalogo b90ff50f2a fix: reduce supervisor stalls and preserve task delivery
Load Supervisor graphs only when opened, preserve source details on demand, reuse indexed story digests and window selection lists without losing keyboard or focus behavior. Route heavy UI reads through the existing worker, batch scoped queries and construct each publication snapshot once.

Keep remote cancellation responsive during model-route cleanup, remove late routes and retain reported usage after response-cap cancellation. Preserve Telegram pending replies through polling conflicts, show delivery errors and honor the final rate-limit cooldown.

Schema 62 adds lookup indexes without rewriting user content; older clients cannot open upgraded data. Large review publication still blocks Main and remains documented follow-up work. Record the future storage-service direction without performing the architecture migration.

Validation: exact staged code passed isolated full tests (6212 passed, 87 skipped), typecheck and lint. Three-round source performance comparisons, SQLite migration equivalence, native Electron interaction checks and current-source Linux x64 Host validation completed; eight external text-model requests.

Release note: Supervisor opens reviews without loading the entire graph, keeps long selection lists responsive and preserves details and keyboard navigation. Remote cancellation and Telegram reply recovery are more reliable; remote fixes require an Agent build containing this change.
2026-10-05 14:18:33 +08:00
mesalogo e50ca6d149 fix: retry transient supervisor model failures once 2026-10-05 10:47:09 +08:00
mesalogo b4df493be7 fix: clarify Telegram sender ID setup order
Explain in Chinese and English that sender IDs can be left blank while enabling and saving the channel. Guide users to retrieve their own numeric ID with /whoami and save again, and distinguish connection testing from message receiving.

Validation: 60 related tests, typecheck, lint and diff checks passed before submission.

Release note: Telegram setup now explains how to connect before obtaining your sender ID, avoiding confusion about which configuration step comes first.
2026-10-05 10:35:19 +08:00
mesalogo bbf24c282a fix: resolve runtime, review, channel and chat state failures
Keep remote startup cancellable without blocking other sessions and surface local OpenCode submission failures promptly. Preserve supervisor project isolation, pause behavior and valid story destinations.

Restore generated-image channel delivery, Telegram reconnection on unchanged Save and DingTalk business-error retries. Preserve virtualized message disclosures using sparse state and release stale supervisor UI requests.

Add production-path regressions, Electron checks, isolated SQLite validation and audit evidence. Fix the SDK cleanup test fixture lifetime under garbage collection without weakening its assertions.

Validation: 6136 tests passed, 87 skipped; typecheck, lint and diff checks passed. Seven real text-model calls, current-source Linux x64 Host scenarios and large-history Electron comparisons completed.

Release note: Fix stuck runtime requests, inconsistent review results and channel delivery failures, and retain expanded details while browsing long conversations. Remote runtime fixes require an Agent build containing these changes.
2026-10-05 10:25:43 +08:00
mesalogo 126dbc57e2 feat: support custom supervisor review date ranges 2026-10-05 02:49:02 +08:00
mesalogo d0877d9212 fix: distinguish missing historical timing from zero duration 2026-10-05 02:45:40 +08:00
mesalogo cf598121b1 feat: add Telegram private messaging channel
Connect a user-created Telegram bot to channel projects with encrypted credentials, numeric sender allowlists, unified execution and text replies. Use Electron networking for system proxy support, with polling recovery, cancellable delivery retries and live status updates.

Add schema 61 channel support, lifecycle regression tests, Electron integration probes and feature documentation. Require the performance principles in AGENTS.md and record three alternating baseline/current Main benchmark rounds.

Validation: typecheck, lint and diff checks passed. Full suite: 6087 passed, 87 skipped, one intermittent inline-help Electron failure that passed unchanged in isolation. Real Bot plus model acceptance remains pending; the local TLS proxy probe is opt-in.

Release note: 新增 Telegram 私聊通道,可配置 Bot Token 和用户白名单,通过通道项目处理文字请求并回复结果。支持系统代理、断线重连和停用取消;真实 Bot 与模型的完整联调仍待完成。
2026-10-05 02:42:28 +08:00
mesalogo 78afec7775 test: allow bounded activity paging equivalence checks on CI
Cross-platform packages / Validate source (push) Canceled after 0s
GoodBuddy Agent Release / Authorize Agent release (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
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
2026-10-05 00:01:17 +08:00
mesalogo 4e72aa0481 test: wait for workspace import controls to become ready
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-10-04 23:38:32 +08:00
mesalogo 29af409045 test: align review entry and await sidebar resize transitions
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-10-04 23:21:07 +08:00
mesalogo 245e296ad5 chore: prepare desktop 0.15.11 and agent 0.15.4
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
Document unified execution, coordinated Agent upgrade, workspace imports, review history and story colors. Record schema 60 and settings 22 backup boundaries and separate development evidence from release validation.
2026-10-04 22:56:38 +08:00
mesalogo ea3d607fdd fix: unify supervisor review layouts and story spiral colors
Review history and context were split across recap and graph views, while the story spiral rendered saved event intervals in a single neutral color. Share the history picker and context bar, collapse new-review inputs, improve recap and responsive detail layouts, and align graph toolbars and accessible legends.

Color actual event intervals and markers by primary story, preserve neutral gaps and unassigned events, and refresh colors across themes. Include related regression coverage, layout fixtures, overlay timing and workspace-import test adjustments, and updated design and validation documentation.

Release note: Work review and story graph now share the selected review and a compact history picker. Recaps and graph details fit smaller windows, and the time spiral uses story colors for recorded event intervals without filling gaps.
2026-10-04 22:50:31 +08:00
mesalogo b9186a616c fix: retain historical events in the story spiral
Render every selected review event, including events without current story membership, and label current grouping without inventing historical assignments. Keep history selection inside the central canvas heading, use shared camera controls, and fit the default scene within the viewport.

Validation: 46 focused tests, synthetic and read-only portable-data Electron checks, Node/Agent/Web typechecks, targeted ESLint, and diff checks passed. All 21 reported events remain represented at wide and narrow widths; no live data writes or model calls.

Release note: Historical story spirals now retain unassigned events. Review history sits inside the canvas column, and camera controls have consistent spacing and framing.
2026-10-04 21:09:32 +08:00
mesalogo 49fd0d3054 fix(workspace): make file import a compact icon action
Place the import icon after New File and retain destination tooltips, accessible labels, and disabled state while importing.

Validation: WorkspaceFilesPanel tests passed (41 tests).

Release note: Replace the prominent file import button with a compact icon beside New File.
2026-10-04 20:57:40 +08:00
mesalogo e205e90993 fix: align website with unified execution
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
Deploy website to GitHub Pages / Deploy static website (push) Canceled after 0s
Remove obsolete work-mode labels from bilingual product previews and describe tools through enabled capabilities and Runtime assignments. Fix preview overlap and narrow-screen clipping, guard the updated copy, and record execution migration and validation results.

Release note: 官网同步移除 Ask/Execute 模式展示,更新工具配置说明,并修复桌面与手机首屏示意的遮挡和裁切。
2026-10-04 20:40:39 +08:00
mesalogo 577210a8ae fix: synchronize graph review selection and attention radii
Share review history selection between overview and graph while preserving explicit navigation and stale-response guards. Return attention for the selected review scope and interval, and align spiral events and time with that review.

Normalize radii from message rates, including partial turns, without lifting quiet turns toward peaks. Preserve one continuous helix, uniform time height, round turn sections, and smooth transitions; equal densities retain equal radii. Document current-message and hourly-bucket limitations.

Validation: 51 focused unit tests, 2 production IPC tests, Electron DOM and geometry checks, Node/Agent/Web typecheck, targeted ESLint, and diff checks passed.

Release note: Story graphs now offer synchronized review history selection, and spiral radii reflect recorded message density within the selected review.
2026-10-04 20:24:49 +08:00
mesalogo 56ebd40bd8 feat: unify execution and add workspace file imports
Remove Ask/Execute modes and general product tool approvals so requests use the selected runtime, enabled capabilities, and execution account permissions. Migrate retired application metadata while preserving user content.

Retire the Results page while retaining conversation images and stored artifacts. Add local and remote workspace file imports, timestamps and sorting, improve image discovery and saving, and refine workspace navigation and supervision responsiveness. Include the accompanying web-search changes, tests, probes, and product documentation.

Remote imports require an updated Agent. Existing artifacts are not automatically copied into project directories. Validation evidence and outstanding failures remain documented in feature progress files; no full test suite or real-model calls were rerun for this commit.

Release note: Run requests without switching work modes, import files into a selected local or remote workspace folder, sort workspace files by date, and keep using conversation images after removal of the Results page.
2026-10-04 20:02:59 +08:00
mesalogo 0cd5da33f4 fix: correct OpenCode cache hit rates
Include cache reads and writes in the complete input denominator for OpenCode usage. Use the same calculation for stored records, summary totals, and renderer fallbacks so existing history is corrected without migration.

Add regressions for full cache hits, cache writes, mixed Runtime aggregation, and persisted conversation/system usage. Focused validation passed 41 tests; repository-wide checks were blocked by concurrent work-mode changes.

Release note: 修复 OpenCode 缓存命中率偏高或完全命中时显示为空的问题,已有用量记录在重新查询时按完整输入量计算。
2026-10-04 18:03:33 +08:00
mesalogo 4d18794e74 perf: track execution duration by state and bound summary I/O 2026-10-04 16:21:55 +08:00
mesalogo 2c2b6c31eb fix(composer): add icon to story graph toggle 2026-10-04 16:21:33 +08:00
mesalogo ee186f9f70 fix(projects): require creation directories and show remote directory links
Require a root folder in the local project creation dialog and IPC while preserving updates to historical projects with blank paths. Show remote symlinks whose targets are directories and resolve their canonical paths when browsing.

Validation: focused UI, contract, IPC and SSH tests passed; typecheck and lint passed. Verified directory links on the shared Linux Host. Full test run timed out after a Magic Notes storage timing failure.

Release note: 新建本地项目必须选择目录;远程项目选择目录时可查看并进入指向目录的符号链接。
2026-10-04 15:00:13 +08:00
mesalogo 629e82545b fix(ocr): report the actual provider and PPTX failure location
HTTP OCR failures in PPTX parsing were incorrectly labeled as local OCR failures. Report the selected provider and slide/image location, and retain bounded structured server diagnostics with configured-key redaction and log IDs. Preserve existing fallback behavior.

Validation: 38 focused tests, typecheck and lint passed. Full test run timed out after 200 seconds. Synthetic service probes reproduced HTTP 500; the server-side failure remains unresolved.

Release note: PPTX parsing warnings now identify the actual OCR provider and failed image location, with server diagnostics for troubleshooting.
2026-10-04 14:54:39 +08:00
mesalogo 658e7f100f fix(workspace): open project menu with all projects 2026-10-04 14:25:27 +08:00
662 changed files with 36902 additions and 12801 deletions
+7 -2
View File
@@ -43,7 +43,7 @@ jobs:
- name: Prepare sandboxed Electron tests
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends --yes xvfb xauth
sudo apt-get install --no-install-recommends --yes xvfb xauth dbus-x11 gnome-keyring
sandbox="node_modules/electron/dist/chrome-sandbox"
if [[ ! -f "$sandbox" ]]; then
rm -rf node_modules/electron/dist
@@ -55,7 +55,12 @@ jobs:
- name: Run validators
run: |
xvfb-run --auto-servernum npm test
dbus-run-session -- bash -euo pipefail -c '
export XDG_CURRENT_DESKTOP=GNOME
# An isolated CI login keyring exercises real safeStorage encryption.
head -c 32 /dev/urandom | base64 | gnome-keyring-daemon --unlock --components=secrets
xvfb-run --auto-servernum npm test
'
npm run typecheck
npm run lint
+1
View File
@@ -4,6 +4,7 @@ dist/
# Local build output left by the extracted ShareServer project.
shareserver/build-server/
coverage/
/temp/
.vite/
.runtime-resources/
.remote-runtime-resources/
+44 -8
View File
@@ -93,11 +93,14 @@ additional hardening beyond the actual internal-network threat model:
## Runtime Behavior
- Ask mode must remain read-only at the runtime boundary.
- Execute mode is the user's full authorization for the selected SSH account.
It may use all tools, processes, network access, and writable paths available
to that account. Do not add T2/T3 trust tiers, separate consent checklists,
per-tool approvals, or a second "controlled execution" concept.
- Normal requests use the selected Runtime, enabled capabilities, and execution
account permissions without Ask/Execute or the product `toolApproval` policy.
Do not restore a product work mode or general tool approval gate.
Implementation and validation status belongs
in `docs/features/unified-execution/progress.md`.
- Respect explicit user instructions such as explanation-only requests. Keep
dedicated internal text tasks tool-free. Do not add T2/T3 trust tiers,
separate consent checklists, per-tool approvals, or a replacement work mode.
- Preserve cancellation, timeout, bounded-output, and shutdown behavior.
- When fixing Runtime behavior or limits, also inspect the remote Runtime path
through GoodBuddy Agent (`gbagent`). Verify whether local and remote execution
@@ -107,7 +110,7 @@ additional hardening beyond the actual internal-network threat model:
development validation rules below. If no separate remote fix is needed,
record which shared implementation covers it or why the remote path is
unaffected.
- Keep model/provider credentials in Main, but do not otherwise reduce Execute
- Keep model/provider credentials in Main, but do not otherwise reduce execution
permissions with extra product policy gates. Keep child-process cleanup
straightforward and reliable.
- On test Hosts, GoodBuddy may install and start its own test resources and
@@ -137,6 +140,16 @@ additional hardening beyond the actual internal-network threat model:
## Implementation Conventions
- Read and follow
[`docs/architecture/performance-principles.md`](./docs/architecture/performance-principles.md)
before designing, implementing, or reviewing a feature. Treat its performance
budgets, process boundaries, state ownership, startup rules, and review
checklist as implementation constraints. Include this reference in parallel
agent assignments so all agents follow the same rules. For performance or
hot-path changes, provide the applicable measurements and validation required
by that document; do not claim improvements without evidence.
- For new feature work, also follow the repository maintenance rules in
[`docs/development/agent-feature-maintenance.md`](./docs/development/agent-feature-maintenance.md).
- Follow surrounding TypeScript and React patterns.
- Reuse installed libraries and shared contracts before adding dependencies.
- Keep changes focused. Do not add unrelated refactors or documentation.
@@ -294,6 +307,29 @@ Repository-wide release invariants:
## Validation
### Local Temporary Files
- Put all agent-created local temporary files under the repository-root `temp/`
directory (`D:\my_git\goodbuddy\temp` in this checkout), in a task-specific
subdirectory. This includes database copies, scratch scripts, test fixtures,
logs, screenshots, and temporary build or validation artifacts. Do not put
them in Windows Temp or `AppData/Local/Temp/opencode`.
- Resolve this directory from the checkout root rather than hard-coding a
machine path in scripts. Point test-tool temporary directory options and
child-process `TEMP`, `TMP`, or `TMPDIR` here when needed. Leave production
application data paths unchanged.
- `temp/` is Git-ignored and disposable. Never store original user data,
required backups, or the only copy of a deliverable there. Keep reusable
validation scripts in `scripts/` or `tests/` and concise results in the
owning feature documentation.
- Clean task temporary files after validation, including database copies and
their WAL/SHM files, on success and failure. Close owned processes and
database connections first. Reuse a baseline copy where possible instead
of retaining a full database for each test case. If cleanup is blocked,
report the remaining path; do not silently accumulate copies.
- Apply these rules to parallel agents as well. Remote Host temporary files
continue to follow the dedicated remote-testing directory rules below.
Run validators after source changes when function/fix done, not on every source change:
```text
@@ -330,8 +366,8 @@ validation are not substitutes for this development-time check.
previously published Agent. Run the affected production path as soon as it is
runnable and repeat the relevant scenario after the final change.
- Select the real-Host scenario from the changed surface: attach/bootstrap or
update for installation changes; Ask/Execute and a bounded real model request
for Runtime or model-bridge changes; and the applicable disconnect, restart,
update for installation changes; unified tool execution and a bounded real
model request for Runtime or model-bridge changes; and the applicable disconnect, restart,
cancellation, or reconnection sequence for lifecycle and recovery changes.
- Use the existing pinned Host identity and GoodBuddy credential storage. Never
add credentials to source, documentation, commands, logs, or test output, and
+9 -11
View File
@@ -89,7 +89,7 @@ broker 调用已分配自定义 MCP 的真实 OpenCode 和 Continue 用例。
`src/main/agent/opencode-runtime-permissions.test.ts`、
`src/main/agent/continue-runtime-permissions.test.ts` 和
`src/main/agent/deepseek-harness-acp-e2e.test.ts`。它们用本地确定性模型驱动真实 Runtime
或固定 Host,覆盖工作区外访问、原生问答及 Ask 边界。OpenCode 用例默认使用开发缓存
或固定 Host,覆盖工作区外访问、原生问答及能力范围。OpenCode 用例默认使用开发缓存
二进制;可通过 `GOODBUDDY_TEST_OPENCODE_BINARY` 指定已经安装的候选二进制,避免把
仍在运行的旧缓存版本当作候选版本验证。这不替代当前源码 Agent 的 Linux Host 验收。
@@ -403,11 +403,10 @@ integrity,以 `--ignore-scripts` 从官方 npm registry 获取归档;bundle
再次验证归档 SHA-512、包名、版本和 ELF 架构。
源码已经实现 Runtime bundle build/import/verify、Daemon 侧
manifest/Ed25519/payload/ELF/lock 校验、digest registry、ACP v3、直接进程 ownership 和
manifest/Ed25519/payload/ELF/lock 校验、digest registry、runtime/acp 6、直接进程 ownership 和
`runtime/model-bridge` v1。Agent 通过私有 Unix socket 按需 detached 启动,不依赖
systemd、D-Bus、Linger 或 bubblewrap。Ask 与 Execute 都直接启动签名 Runtime;两者的
差异由 OpenCode Ask 权限配置和 Agent 工具权限分发边界共同执行,Execute 不注入该
权限覆盖。
systemd、D-Bus、Linger 或 bubblewrap。请求直接启动签名 Runtime,使用所选 SSH 账号
权限与已接入能力;不携带产品工作模式。旧 capability 5 不兼容,发布前须重建配套工件。
OpenCode 通过已签名 Agent helper 和每次 Prompt 的私有 Unix socket 使用 Main-only 模型
网关;Provider URL、API Key 和真实 Provider 认证头不进入远端。helper 的 loopback
@@ -415,8 +414,7 @@ HTTP 入口使用每个进程随机生成的路径 capability,Anthropic/OpenAI
本地兼容标记由 helper 核对后丢弃。GoodBuddy 自己维护会话标题,所以生成的 OpenCode
配置必须禁用 title Agent;工具循环只使用 build Agent 的模型轮次。
Ask 模式的 ACP 权限中介只可为原生 `read` 选择 `allow_once`;其他工具种类和
`allow_always` 请求必须拒绝。Unix 模型桥
ACP 权限中介按活动请求与工具能力回复合法允许选项,保留取消及未知请求拒绝。Unix 模型桥
broker 必须按 socket 当前已缓冲字节增量读取长度帧,不能等待 `read(remaining)` 一次返回
完整大响应。Agent 传输或权限实现变化后,除了普通测试,还要在原生 Linux 上运行包含
至少 256 KiB 响应的模型桥回归。
@@ -589,11 +587,11 @@ Windows 代码签名仍未配置。对外分发前还应配置 Windows 签名凭
2. 中文输入法、窗口缩放和高分屏显示。
3. 系统密钥环和模型连接。
4. 本地知识库导入、检索和知识图谱。
5. Ask、Execute 的权限边界与旧版 Plan 数据兼容。
5. 无模式执行、schema 60 与 Runtime 设置 22 的历史字段清理及用户数据保留。
6. OpenCode 与 Continue 的权限边界、取消和超时。
7. DeepSeek Harness Ask 拒绝写入和第三方插件工具,可调用 Main 管理的 Web Search/Fetch;Execute 可调用已启用插件工具。文本模型在网络调用前拒绝图片,声明图片能力的模型可以实际接收 JPEG/PNG。
7. DeepSeek Harness 控制协议 2 可调用已注册内置及已启用插件工具,Main 代理沿用能力分配和请求归属。文本模型在网络调用前拒绝图片,声明图片能力的模型可以实际接收 JPEG/PNG。
8. OpenCode Agent/Command、原生上下文 Compact,以及 Continue Rules/Prompt 预设、结构化提问和 GoodBuddy 手动摘要压缩。
9. Runtime 原生清单把 Tools 与 Commands/LSP/Formatters 分开,显示来源及 Ask/Execute 可用性,不混入 GoodBuddy 分配的 Skills/MCP;外部 OpenCode 只报告连接状态,Continue 明确标记原生 Tools 静态发现不支持;内置 MCP 的启停与 Runtime 分配会持久化并限制后续请求,DeepSeek Harness 保持不可分配;MCP 测试只读取有界 Prompt/Resource 元数据,不读取 Resource 内容。
9. Runtime 原生清单把 Tools 与 Commands/LSP/Formatters 分开,显示来源及描述,不提供统一逐工具开关,不混入 GoodBuddy 分配的 Skills/MCP;外部 OpenCode 只报告连接状态,Continue 明确标记原生 Tools 静态发现不支持;内置 MCP 的启停与 Runtime 分配会持久化并限制后续请求,DeepSeek Harness 保持不可分配;MCP 测试只读取有界 Prompt/Resource 元数据,不读取 Resource 内容。
10. DSH 市场可安装、停用、重新启用和移除插件;启动失败插件不会阻止 Host,并显示为自动停用。
11. 智能心跳的创建、暂停、恢复和历史记录。
12. 应用退出后无残留 Runtime 子进程。
@@ -629,7 +627,7 @@ GOODBUDDY_DSH_MARKETPLACE_E2E=1 npm test -- src/main/agent/dsh-extension-marketp
该测试使用临时用户目录,经捆绑 npm 路径安装已审查的最小测试插件,并验证 Host
加载和真实工具调用;测试结束后删除临时目录。
要让真实模型同时验证插件的 Ask 拒绝与 Execute 调用,可显式提供兼容的
要让真实模型验证已启用插件的调用,可显式提供兼容的
OpenAI Chat Completions 配置:
```bash
+110 -65
View File
@@ -140,7 +140,7 @@ records are listed separately and do not introduce another feature status.
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
models, capability assignments, budgets, and timeouts in a Project, with reusable
templates.
- [x] **Application center and navigation**:
The footer shows equal-height Apps and Settings buttons with visible labels and a subtle vertical divider.
@@ -191,7 +191,7 @@ records are listed separately and do not introduce another feature status.
- [ ] **Additional assistant workbar and execution-space capabilities**
(planned): Builds on the current workbar and multiple terminals with
broader execution supervision, unified Runtime monitoring, managed processes,
target-pinnable workspace/browser/artifact instances, bottom
target-pinnable workspace/browser 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
@@ -232,7 +232,7 @@ records are listed separately and do not introduce another feature status.
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
generate images and edit uploads or earlier artifacts, 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.
@@ -247,14 +247,14 @@ records are listed separately and do not introduce another feature status.
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,
platform Shell and delegate one level of programming
Subagent work while inheriting the parent request's model, workspace,
and capability scope. OpenCode, Continue, DeepSeek Harness, and managed SSH
do not receive duplicate copies of these tools. The Windows local command
path and a real-model edit, test, fix, and review loop have passed; native
macOS and Linux command validation remains. Execute commands can select
macOS and Linux command validation remains. Commands can select
absolute, relative, or symbolic-link directories outside the workspace;
the workspace stays the default and relative-path base. Ask remains read-only.
the workspace stays the default and relative-path base.
- [x] **OpenCode and Continue**: Use isolated child processes, an environment
variable allowlist, unified configuration, cancellation, startup and control-request
deadlines, bounded streaming output, and activity records. Shared process
@@ -295,13 +295,12 @@ records are listed separately and do not introduce another feature status.
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
directories, and create Ask or Execute projects. Both modes start the
signed Runtime directly without bubblewrap. Ask applies OpenCode's Ask
permission configuration and permits only native read approvals at the
Agent tool-dispatch boundary, while Execute explicitly allows native permissions
and retains all permissions of the selected SSH account. The Agent owns accepted Prompts,
directories, and create remote projects. Requests start the signed Runtime
directly without bubblewrap and use the selected SSH account's permissions.
Enabled tools do not require a product work mode or general approval policy.
The Agent owns accepted Prompts,
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
semantic transcript over a private Unix socket and runtime/acp capability 6. 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,
@@ -330,7 +329,7 @@ records are listed separately and do not introduce another feature status.
revocation; catalog, package, manifest, payload signatures, and streaming
SHA-256 remain unchanged. A missing architecture package disables managed
SSH only for that architecture. Projects store only Host, remote path,
Runtime selection, and mode. Current Host identity is read when a
Runtime selection. 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 supported OpenCode and Continue choices;
Continue requires the updated compound package and matching Desktop.
@@ -345,7 +344,7 @@ records are listed separately and do not introduce another feature status.
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.15.3`, paired with the current Desktop release candidate `0.15.10`; formal
`0.15.6`, paired with the current Desktop release candidate `0.15.13`; 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
@@ -382,7 +381,7 @@ records are listed separately and do not introduce another feature status.
capabilities, and prompt startup do not scan the full payload. See the
[design](./docs/features/remote-host/environment-provisioning-technical-design.md).
- **SSH Host environment provisioning validation records**: The
current source passed isolated Linux x64 package installation, Ask/Execute,
earlier 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,
Linux x64/arm64, cancellation, and offline GoodBuddy transfer remains.
@@ -392,11 +391,8 @@ records are listed separately and do not introduce another feature status.
OpenAI-compatible model connection. It prefers an administrator-provided
connection, otherwise follows the compatible default model or first
compatible connection without requiring a duplicate selection. Settings
displays the actual administrator or fallback model source. Ask permits
only real Host-registered `read` and `skill` tools plus Main-managed Web
Search/Fetch and enabled Story Graph proxies, and rejects plugin impersonation
of those names.
Execute allows all enabled built-in and plugin tools with the current user's
displays the actual administrator or fallback model source. Registered
built-in and enabled plugin tools run with the current user's
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
@@ -410,18 +406,23 @@ records are listed separately and do not introduce another feature status.
after startup failure, and offline management of installed plugins.
Disabling the marketplace hides only the catalog and management interface;
it does not change the enabled state of installed plugins, and third-party
code is not subject to Ask initialization isolation.
- [x] **Ask and Execute work modes**: Ask remains read-only. Execute is the
user's authorization for all tools, processes, network access, and writable
paths available to the current local or SSH account, including paths outside
the workspace and native subagent work.
code runs with the current user's permissions during initialization.
- [x] **Unified execution source path**: Ask/Execute, the Safety category, and
`toolApproval` are removed. Requests use the selected Runtime, enabled
capabilities, and local or SSH account permissions. Clear local data is under
General / Platform Features and retains confirmation. Database schema 60 and
Runtime settings 22 remove legacy fields; remote execution requires
runtime/acp 6 and local Harness uses control protocol 2. Native inventories
do not provide uniform per-tool switches. Targeted Electron and real Linux
Host checks are recorded in [progress](./docs/features/unified-execution/progress.md);
full-suite and coordinated release-package validation remain pending.
- **Local native Runtime clients (preview)**: The composer opens Continue or
OpenCode in a workbar terminal, or the official DS Web client in the system
browser, using the current local project and model. These are independent
sessions, without imported GoodBuddy conversation history. Closing a DS page
does not stop its service; use GoodBuddy's stop action. Remote shortcuts are
not connected. Continue/OpenCode Ask now map Main-bound read tools through
native permissions. Managed standard Node delivery and full cross-platform
not connected. Continue/OpenCode use assigned Main-bound tools without a
product work mode. Managed standard Node delivery and full cross-platform
package acceptance remain incomplete;
existing Windows real-model evidence does not establish full capability parity.
See the [implementation and remaining work](./docs/features/assistant-workbar/progress.md#2026-09-27-local-native-client-validation).
@@ -433,8 +434,8 @@ records are listed separately and do not introduce another feature status.
[0.15.0 preparation record](./docs/development/release-preparation-0.15.0.md).
- [x] **Efficient direct-model workspace tools**: Direct models use bundled
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.
UTF-8 files by line, and apply multi-file patches. Searches use current-account
permissions; file read and patch APIs retain their workspace-relative contracts.
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.
@@ -454,8 +455,8 @@ records are listed separately and do not introduce another feature status.
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).
Native tool permission confirmations are handled automatically, without
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.
Details expand in an anchored, scrollable floating panel without shifting chat
@@ -466,8 +467,8 @@ records are listed separately and do not introduce another feature status.
[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
read-only. They are not remote OpenCode child sessions. Chat shows each
execution space and capability scope and can use enabled local direct-model
tools. They are not remote OpenCode child sessions. Chat shows each
expandable full expert response first and the main Agent's synthesis below,
and persists both with the conversation.
- [x] **OpenCode child progress and final results**: Child cards show ordered
@@ -560,7 +561,7 @@ records are listed separately and do not introduce another feature status.
- [x] **OpenCode Runtime customization**: GoodBuddy-managed OpenCode can
discover native Agents, Tools, Commands, LSPs, Formatters, MCP, Skills,
Prompts, and Resources. Tools show read, file-edit, command, network, Agent
orchestration, and other types, source, and Ask/Execute availability while
orchestration, and other types, source, and descriptions while
hiding OpenCode's internal `invalid` tool and temporary GoodBuddy MCP tools.
Users can save a default Agent, override it per request, run Commands through
the native SDK, view context usage, and invoke native Compact with a total
@@ -587,7 +588,7 @@ records are listed separately and do not introduce another feature status.
Runtime connectivity and distinguishes complete, partial, unavailable,
connection-only, and unsupported. DeepSeek Harness enumerates bounded
built-in/plugin Tools and Skills through the Host Registry, reports real
Ask/Execute boundaries, and excludes per-request GoodBuddy Skills and
actual registered capabilities, and excludes per-request GoodBuddy Skills and
Web/MCP proxies.
- [ ] **Runtime supervision section** (planned): A fixed section in the
application-level assistant workbar for Task-level delegation, background
@@ -634,7 +635,7 @@ records are listed separately and do not introduce another feature status.
models, GoodBuddy-managed OpenCode, and Continue. DeepSeek Harness remains
unsupported for these servers; Story Graph supports it through the Main proxy.
Built-in MCP uses short-lived local authority
for the current request, and user configuration cannot loosen Ask/Execute
for the current request, and user configuration cannot bypass resource
read/write boundaries.
- **Obsidian integration (implemented in source, acceptance in progress)**:
Disabled by default, with all locally registered vaults as the default scope
@@ -648,8 +649,8 @@ records are listed separately and do not introduce another feature status.
[feature docs and validation record](./docs/features/obsidian/README.md);
this is not a release claim.
- [x] **MCP Tools**: Explicitly enabled custom MCP can be assigned to direct
models, GoodBuddy-managed OpenCode, Continue Agent Execute, and DeepSeek
Harness, and loads only in Execute. Agent child processes receive only
models, GoodBuddy-managed OpenCode, Continue Agent, and DeepSeek
Harness. Agent child processes receive only
per-request local-loopback authority; MCP addresses, commands, and
credentials remain in Main. Dynamic tools pass through discovery and the
existing activity and permission boundaries; healthy direct-model
@@ -772,23 +773,26 @@ records are listed separately and do not introduce another feature status.
conversation-source metadata. 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.
checks, reconnection, schema cache, isolation, capability scope, and audit.
- [ ] **Knowledge/web excerpts and AI editing** (planned): Extend capture beyond
the implemented conversation-text path to knowledge and web excerpts, with
confirmation-based summarization, rewriting, and organization.
### Work management, long-term collaboration, and workflows
- [x] **Tasks, activity, and artifacts**: Centrally manages task state, audit
activity, and independent artifact files. Ordinary chat responses remain
only in the conversation and are no longer copied into Artifacts; existing
duplicate chat Markdown is hidden from the artifact list but not physically
deleted. Run history is stored in Main SQLite without the previous 500-item,
- [x] **Tasks, activity, and workspace files**: Centrally manages task state and audit
activity. The Results page is removed; Workspace lists actual project files with
modification and available creation times, sortable columns, and file import into
the selected local or remote directory. Remote upload requires the updated Agent.
Conversation images and attachments remain in messages; existing artifact data is
preserved without automatic project copies. Run history is stored in Main SQLite without the previous 500-item,
4,000-character, or 2 MB Renderer limits; the page renders it in batches,
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. Project and conversation
cache-reporting semantics when showing cache hit rate. OpenCode usage adds cache
reads and writes back to its uncached input count before calculating the rate,
including when querying existing records. Project and conversation
usage groups show collapsed totals by default and expand to Runtime/model child
rows; model grouping remains flat. A separate System tasks tab groups
background usage by source, with manual refresh and updates when usage is
@@ -808,8 +812,8 @@ records are listed separately and do not introduce another feature status.
left conversation list exposes Task children through a leading expand
button; children share status dots, the parent does not repeat task badges,
and the UI stops at Task rather than exposing Job/Run levels. A new custom
Task can use the current or a new Conversation, defaults to Execute, and
preserves Runtime, tool, and approval boundaries. Repeated triggers reuse the
Task can use the current or a new Conversation and
preserves Runtime, enabled-tool, and resource boundaries. Repeated triggers reuse the
same Task; text returns to the Conversation, while independent files and
images remain artifacts. Ordinary messages and due Scheduled Tasks share a
persistent Conversation queue that runs one item at a time. Users can keep
@@ -819,13 +823,13 @@ records are listed separately and do not introduce another feature status.
Center. Current schedules support one-time, daily, and weekly triggers;
advanced time zones, Cron, event triggers, and retry governance remain
incremental PRD work. Scheduled messages use the conversation's current
history, Runtime, work mode, and saved knowledge retrieval settings; task
details show the actual mode used, and an active occurrence blocks duplicate
history, Runtime, and saved knowledge retrieval settings; task
details show execution status, and an active occurrence blocks duplicate
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.
structured questions remain actionable in their conversations.
- [x] **Memory and Smart Heartbeat**: Plans trigger shared incremental reviews
instead of separate heartbeat reports. Choose memory/graph updates only or
evidence-backed suggestions for open items, disagreements, revisions, and
@@ -899,10 +903,10 @@ records are listed separately and do not introduce another feature status.
periods, current/expired/conflicting fact detection, fact checking, and
evidence tracing to memory and the knowledge graph.
- [ ] **Visual controlled workflows** (planned): Versioned DAGs, conditional
branches, approvals, cancellation, and recovery, with execution still
branches, business confirmations, cancellation, and recovery, with execution still
passing through Main Runtime boundaries.
- [ ] **Unified Run Graph and replay** (planned): Connects Tasks, Subagents,
models, knowledge, tool approvals, usage, and artifacts for failure
models, knowledge, tool activity, usage, and artifacts for failure
diagnosis, retry, and redacted export.
### Browser, communication, voice, and application maintenance
@@ -910,7 +914,7 @@ records are listed separately and do not introduce another feature status.
- [x] **Shared built-in browser for runtimes**: Uses GoodBuddy's isolated
Chromium and never controls a browser installed by the user. A separate
master switch decides whether assigned runtimes receive the capability in
Execute, with no per-use prompt after enablement. Turning Agent access off
requests, with no per-use prompt after enablement. Turning Agent access off
does not disable manual browser workbar actions. The Browser workbar and
Agent share the same Conversation-owned session and serialized operation
path, with Back, Refresh/Stop Loading, address entry, Go, Interaction, and
@@ -927,12 +931,13 @@ records are listed separately and do not introduce another feature status.
action errors.
- [x] **Client-computer control tools**: Managed separately from the built-in
browser with scope, cancellation, timeout, output, and activity boundaries.
- [x] **Remote messaging-channel projects**: WeChat ClawBot, WeCom, and
DingTalk each have a system-managed project, independent remote
conversations, working directory, processing backend, default Ask/Execute
mode, and task/activity ownership. Each channel controls complete-response
- [x] **Remote messaging-channel projects**: WeChat ClawBot, WeCom,
DingTalk, and Telegram each have a system-managed project, independent remote
conversations, working directory, processing backend, and
task/activity ownership. Each channel controls complete-response
length and segmentation according to platform capabilities rather than
relying on a shared service truncation.
relying on a shared service truncation. Telegram's live Bot + model chain
remains [pending acceptance](./docs/features/telegram-channel/progress.md).
- [x] **WeChat ClawBot QR login and media**: A separate Sidecar handles local
QR scanning, verification codes, encrypted credentials, and text messaging.
Personal WeChat chats support images and files, with at most four
@@ -978,9 +983,49 @@ records are listed separately and do not introduce another feature status.
### Open source, builds, and releases
- The current Desktop candidate is `0.15.10`; published Agent `0.15.3` requires Desktop `0.15.9`, with
- The current Desktop candidate is `0.15.13`; Agent candidate `0.15.6` requires Desktop `0.15.13`, with
OpenCode pinned to `1.18.29` and Continue to `1.5.47`. Publication status follows the independent
Desktop and Agent release channels.
- Desktop `0.15.13` / Agent `0.15.6` move desktop business SQLite ownership into
one typed storage utility process and keep Remote Runtime changes coordinated.
Assistant, Knowledge, attachments, runtime bindings and ledgers retain their
paths and transaction semantics. Direct models continue after tool failures,
while bounded paged output, Magic Notes tags, Supervisor aliases, settings
preload and workspace metadata are corrected. Schema remains 62.
Focused storage and Supervisor orchestration passed, but the required C01–C07
concurrent acceptance matrix, cross-platform storage-process installation and
low-end comparisons remain explicit follow-up gates; no universal performance
claim is made. See the
[preparation record](./docs/development/release-preparation-0.15.13.md).
- Desktop `0.15.12` / Agent `0.15.5` add Telegram private-channel configuration
(complete real-bot-plus-model acceptance remains pending) and custom local-date
review ranges. Supervisor uses lazy graph/source reads, indexed Worker queries,
cached digests and windowed selectors; final publication can still block Main.
Unpublished reviews omit deleted conversations and recompute affected batches,
retaining published history. Transient model failures allow one extra attempt
with possible additional cost. Remote startup/cancellation, channel delivery,
long-message disclosure state and missing historical timing are corrected.
Schema 62 adds channel support and lookup indexes; back up the complete data
directory and restore it to downgrade. Development audits recorded 15 external
text requests across two separate stages; this release preparation adds none.
Exact-source CI and native packaging remain required. See the
[preparation record](./docs/development/release-preparation-0.15.12.md).
- Desktop `0.15.11` / Agent `0.15.4` remove Ask/Execute and general tool
approvals, including the former read-only boundary of old Ask conversations.
Schema 60 and Runtime settings 22 remove retired metadata; back up the complete
data directory before upgrading, and restore that backup to downgrade.
Remote execution requires coordinated runtime/acp 6 packages. Workspace imports,
file timestamps/sorting, conversation-image discovery and remote expert image
saves accompany removal of the Results page without deleting stored artifacts.
State-based timing does not reconstruct old durations; cache rates use complete
OpenCode input totals. Review history, responsive layouts and story-colored
actual event intervals retain unassigned events and neutral gaps. Summary reads
and review initialization reduce bounded I/O without universal performance claims.
Development evidence includes 25 real text requests for unified execution and
expert image saves, not new release-preparation requests or image generation.
Exact-candidate CI, signed packages, public verification and cross-version
active-operation recovery are separate from those checks. See the
[preparation record](./docs/development/release-preparation-0.15.11.md).
- Desktop `0.15.10` unifies project/conversation navigation with ten recent
conversations, adds independent Supervisor model selection and a default-on
per-conversation Story Graph switch. Indexed activity paging and complete
@@ -1145,14 +1190,14 @@ records are listed separately and do not introduce another feature status.
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 validation records**: Host CRUD,
- **Historical 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
dual-architecture resource verification are wired. Current Linux x64 source
has passed a real-model and tool matrix covering detach, process exit, relay
dual-architecture resource verification were wired. The then-current Linux x64 source
passed a real-model and tool matrix covering detach, process exit, relay
loss, concurrency, cancellation, provider failures, Agent restart, and
Desktop SQLite recovery. A current-source, test-signed Agent `0.11.17`
Desktop SQLite recovery. A test-signed Agent `0.11.17`
package on Linux x64 additionally passed two concurrent Ask reads, an
Execute write/read, and six real managed-gateway model requests with the
configured custom headers and body fields. No provider request was replayed,
@@ -1177,5 +1222,5 @@ records are listed separately and do not introduce another feature status.
## Roadmap Principles
Planned workflows, Subagents, MCP, remote APIs, and sandbox capabilities must
not bypass existing Main Runtime, Ask/Execute, permission, cancellation,
not bypass existing Main Runtime, capability scope, permission, cancellation,
timeout, or audit boundaries.
+36 -32
View File
@@ -25,7 +25,7 @@
置顶与非置顶分组按最近一条消息的创建时间排序,流式文本和工具进度更新不再反复改变位置。
- [x] **可选桌面任务通知**:“平台功能 / 通用设置”提供默认开启、保存后立即生效的系统任务提醒开关,应用内提示独立保留;通用设置按快捷键、会话与通知、工作目录分组。
- [x] **按需上下文帮助**:页面和设置中的补充说明使用标题或字段旁的共享帮助入口,支持鼠标与键盘;关键操作后果和错误提示保持可见。
- [x] **紧凑会话控件**:输入选项集中在紧凑设置面板,Runtime、工作模式、发送和队列操作保持可用;会话搜索提供内嵌清除按钮,清除后恢复当前范围列表并把焦点返回输入框。
- [x] **紧凑会话控件**:输入选项集中在紧凑设置面板,Runtime、发送和队列操作保持可用;会话搜索提供内嵌清除按钮,清除后恢复当前范围列表并把焦点返回输入框。
- [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)。
@@ -35,11 +35,11 @@
- [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。主侧栏和魔法笔记编辑器的 AI 评论栏支持鼠标与键盘调宽;AI 评论栏记住显示状态及宽度,窄窗口下移至编辑器底部。
- [ ] **项目 Agent Space**(规划中):在 Project 中统一角色、知识、Skills/MCP、模型、审批策略、预算和超时,并支持模板复用。
- [ ] **项目 Agent Space**(规划中):在 Project 中统一角色、知识、Skills/MCP、模型、能力分配、预算和超时,并支持模板复用。
- [x] **应用中心与导航**:底部“应用”和“设置”为显示文字的等高按钮,中间使用弱竖分隔线。“应用”带向上箭头,点击后向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻筛选。点击应用行关闭菜单,本机推理监控打开保留底层工作区的独立 Modal,其余应用打开主内容区;底部“管理应用”使用 `LayoutGrid` 图标,打开标题仍为“应用中心”的可搜索列表及设置详情,布局与交互见 [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)。
- [x] **本地设备共享元数据**(源码已实现):测试阶段默认关闭,先在应用中心启用,再从应用菜单或应用中心打开;已明确保存的启用选择保留,关闭会退出页面,但不清除目录或撤销已发布记录。可保存服务地址和设备名称、注册本机、浏览服务返回的目录、发布能力或知识元数据并撤销本机 ID 的条目。默认服务为 `http://127.0.0.1:8787`。服务未认证,ID 只用于识别;服务器来源不上传或同步内容,知识搜索/读取/下载标志是独立声明,不提供正文访问或远程执行。详见[范围与验证](./docs/features/device-sharing/README.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)。
- [ ] **通用助手工作栏与执行空间后续能力**(规划中):在现有工作栏和多终端基础上继续加入更完整的执行监督、统一 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 与模型连接
@@ -47,30 +47,30 @@
- [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] **当前会话图片工具**:为图片模型开启会话调用后,支持工具调用的聊天模型可生成图片、修改上传图片或历史成果,无需另开会话或手动切换图片模型。直连文本模型、本地 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 限于工作区只读搜索和读取,Execute 按当前账号权限搜索;用户无需另行安装 ripgrep。搜索保留原生输出与退出码,大结果通过 `output_read` 完整续读,可处理的参数错误允许模型调整后继续。
- [x] **直连模型编程 Agent**:本机直连文本模型可运行平台 Shell,并可按父请求模型、工作区和能力范围委派一层编程 Subagent;OpenCode、Continue、DeepSeek Harness 和托管 SSH 不重复注入这两个工具。Windows 本机命令与真实模型“修改、测试、修复、复核”闭环已通过,macOS 与 Linux 真机命令仍待对应平台验收。
- [x] **高效直连模型工作区工具**:直连模型使用随包 ripgrep 的原生参数按当前账号权限发现文件和搜索内容,按行分页读取大型 UTF-8 文件,并应用多文件补丁;读取和补丁 API 保留工作区相对路径合同。用户无需另行安装 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 与 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.15.3`,配套 Desktop 候选为 `0.15.10`;详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以独立发布渠道为准。
- [x] **托管 SSH OpenCode 与 Continue**:该能力由“设置 > 平台功能”中的独立“远程项目”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。请求使用所选 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.15.5`,配套 Desktop 候选为 `0.15.12`;无模式源码要求 runtime/acp 6,发布前须重建配套工件。详细运行边界和 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 环境准备验证记录**:当前源码已在隔离 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 和已启用的 Story Graph 代理,拒绝插件同名冒充,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 账号可用工具、进程、网络和可写路径的完整授权,包括工作区外路径及原生子代理工作。
- **本地 Runtime 原生客户端(预览)**:输入区可按当前本地项目与模型,在工作栏终端打开 Continue/OpenCode,或在系统浏览器打开官方 DS Web。客户端使用独立会话,不导入 GoodBuddy 对话历史;关闭 DS 网页不停止服务,可在 GoodBuddy 中停止。Continue/OpenCode Ask 已通过原生权限映射 Main 绑定的只读工具。远程快捷入口未接通,托管标准 Node 交付和完整跨平台安装包验收仍有缺口;Windows 真实模型证据不代表全部会话能力等价,详见[实现与剩余工作](./docs/features/assistant-workbar/progress.md#2026-09-27-local-native-client-validation)。
- [x] **DeepSeek Harness(预览)**:使用 GoodBuddy 固定 Host 和 OpenAI 兼容模型连接;优先使用管理员提供的连接,否则跟随兼容的默认模型或首个兼容连接,无需单独重复选择,设置页显示实际管理员或回退模型来源。已注册内置工具及已启用插件工具以当前用户权限运行,Main 代理工具沿用能力分配和请求归属校验。图像输入跟随所选模型连接的能力声明,文本模型在 Host 或模型调用前拒绝图片,图片模型通过有界内联内容和临时 Attachment Store 接收 JPEG/PNG。Windows Host 启动和 ACP 会话共用规范工作区路径,同目录不同写法可复用正常 Runtime,不再因路径不一致而创建会话失败。
- [x] **DSH npm 插件市场**:市场默认关闭,由用户显式开启后搜索公共 npm 的 `dsh-plugin` 包,使用捆绑 npm 执行精确版本安装和普通 lifecycle scripts,并支持启停、JSON 配置、移除、失败启动自动停用和离线管理已安装插件;关闭市场只隐藏目录与管理界面,不改变已有插件的启停状态,第三方初始化代码使用当前用户权限。
- [x] **统一执行源码链路**:已移除 Ask/Execute、“安全”分类和 `toolApproval` 策略;请求按所选 Runtime、已启用能力及本机或 SSH 账号权限执行。“清除本地数据”位于“通用 > 平台功能”,保留二次确认。schema 60 和 Runtime 设置 22 清理旧字段;远程要求 runtime/acp 6,本地 Harness 控制协议为 2。原生工具清单不提供统一的逐工具开关。真实 Electron 与 Linux Host 定向证据见[进度](./docs/features/unified-execution/progress.md),全量测试及配套发布包验证仍待完成。
- **本地 Runtime 原生客户端(预览)**:输入区可按当前本地项目与模型,在工作栏终端打开 Continue/OpenCode,或在系统浏览器打开官方 DS Web。客户端使用独立会话,不导入 GoodBuddy 对话历史;关闭 DS 网页不停止服务,可在 GoodBuddy 中停止。Continue/OpenCode 使用 Main 按当前请求分配的工具,无产品工作模式。远程快捷入口未接通,托管标准 Node 交付和完整跨平台安装包验收仍有缺口;Windows 真实模型证据不代表全部会话能力等价,详见[实现与剩余工作](./docs/features/assistant-workbar/progress.md#2026-09-27-local-native-client-validation)。
- **Runtime 遥测关闭配置**:本地 DS Web、远程 Runtime 和模型桥启动共用遥测关闭环境,不限制模型、MCP 或用户主动联网。Continue 在自动更新关闭时跳过检查;远程改动随配套 Agent 新启动的 Runtime 生效,当前源码 Linux x64 Agent/model 验证见 [0.15.0 发布准备](./docs/development/release-preparation-0.15.0.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] **Runtime 原生交互转交**:OpenCode 与 Continue 的选择、yes/no、自由文本回答和跳过使用现有问答卡片,本机与托管 SSH OpenCode 同时转交属于当前请求的子会话提问。托管 SSH OpenCode 问答需要配套 Agent 和新启动的托管 Runtime,不扩展到任意 ACP 服务。回答或跳过成功后在原位置保留问题与答案,支持多轮记录和本地会话重载;旧版本已丢弃的答案不能恢复。并行问题按顺序等待回答,重复事件保留草稿,提交失败可重试;取消远程待答后可在同一会话继续发送。原生工具权限确认自动处理,不等待第二次审批;具体支持范围见[交互边界](./docs/features/assistant-workbar/runtime-interactions.md)。
- [x] **原生执行清单**:OpenCode 和 Continue 在对话顶部更新只读执行清单,进度随会话保存。详情在可滚动的锚定浮层中展开,不挤动聊天内容;点击外部关闭,Escape 关闭后焦点返回入口。显式清空和远程重放保留请求归属,取消不把未完成条目标成完成;远程交付需要配套 Agent,详见[清单契约](./docs/features/assistant-workbar/runtime-checklist-technical-design.md)。
- **取消后的历史**:模型可见历史保留已中断的部分回复或占位标记,避免旧问题合入下一轮;本地 OpenCode 复用会话时附带一次中断说明。
- [x] **专家与 Subagent**:支持显式专家、团队分析和最多三个专家并行分析。专家继承父请求 Ask/Execute 模式,可使用已启用的本机直连模型工具,Ask 仍只读;这些工具不是远程 OpenCode 子会话。聊天先展示可逐项展开的专家完整输出,再在其下展示总 Agent 的综合结果,并随会话保存。
- [x] **专家与 Subagent**:支持显式专家、团队分析和最多三个专家并行分析。专家继承父请求执行空间和能力范围,可使用已启用的本机直连模型工具;这些工具不是远程 OpenCode 子会话。聊天先展示可逐项展开的专家完整输出,再在其下展示总 Agent 的综合结果,并随会话保存。
- [x] **OpenCode 子代理过程与最终结果**:子代理卡片按顺序显示文字、推理和工具过程,最终结果独立展示。远程实时过程需要 Agent `0.11.20`,旧包仍显示最终结果但不补造缺失过程。
- [x] **OpenCode 事件连接回收**:聊天与原生上下文压缩在结束事件迭代前关闭各自订阅,覆盖完成、失败、取消和消费方提前结束,不取消其他并行会话。
- [x] **准确的消息底部状态**:区分请求准备、等待重试、开始重试、工具活动、待回答和终态;本机 OpenCode 使用原生重试次数和计划时间,直连模型显示自身退避阶段。消息底部状态点保持静态,未接入的 Runtime 重试不编造提示,会话列表闪点及发送、停止行为不变。
- [x] **直连模型命令工作目录**:Execute 命令可通过绝对路径、相对路径或符号链接使用工作区外目录;工作区仍为默认目录和相对路径基准,Ask 保持只读。
- [x] **直连模型命令工作目录**:命令可通过绝对路径、相对路径或符号链接使用当前账号可访问的工作区外目录;工作区仍为默认目录和相对路径基准。
- [x] **Runtime 与模型分层选择**:按会话、项目、全局逐字段解析 Runtime 和模型。会话双栏选择器显示来源,可覆盖或恢复项目默认;项目与通道设置共用规则,Main 在每次请求及排队派发时重新解析。已删除或不兼容的连接会回退,缺少凭据仍明确报错。schema 49 迁移已保存选择,用途模型仍为规划,详见[选择设计](./docs/features/model-connections/runtime-selection-design.md)。
- [x] **角色绑定模型连接**:每个角色可继承默认模型或选择独立文本模型连接,失效连接安全回退默认模型,综合角色始终继承默认模型。
- [x] **多协议模型配置**:支持 Anthropic Messages、OpenAI Responses、OpenAI Chat Completions、OpenAI Images 和无认证本机模型;新用户默认连接为本机 Ollama 兼容地址,不预置第三方云模型服务。升级时只替换从未配置凭据且仍完全等于旧内置值的历史默认连接,用户显式保存或加密凭据的连接保持不变;已有部署仅使用兼容环境变量提供凭据时继续沿用对应的历史连接参数,通用模型环境变量仍优先。“保存并测试模型”会发送有界的真实文本或图片生成请求并校验生成结果,而不是只检查 HTTP 连通性,因此可能产生少量服务商用量费用。
@@ -79,20 +79,20 @@
- [x] **上下文用量与自动压缩**:直连模型按每次成功调用更新供应商用量,图片与工具轮次使用同一口径,供应商缺失 usage 时才回退估算;界面明确区分“本次模型调用”和“压缩后对话估算”,压缩线始终根据当前设置与所选模型窗口即时计算,不在每个对话中保存旧配置;压缩标识的前后值使用同一估算口径,运行记录仍保留各次模型调用的供应商 usage。对话与多轮工具 Agent 可在已完成调用越过阈值后自动重复压缩,规划时先为固定提示、工具定义和摘要预留预算;同一回复会分别保留 Agent 工具上下文与对话历史的压缩标识,并在应用重启或较早消息滚出本地历史窗口后继续复用摘要。
- [x] **持久凭据保护**:API Key 由 Main 使用系统安全存储加密且不暴露给 Renderer。托管 SSH accepted Prompt 只把当前 profile 与密钥放入 Agent 内存,不写入 SSH 参数、远端环境或磁盘;其他路径仍在 Main 内使用。密钥随对应模型连接保存,修改服务地址或临时切换为无需认证不会要求重新输入;只有用户显式清除凭据或删除连接时才移除。
- [x] **有界故障诊断**:Desktop 在用户数据目录中轮转保存启动、Runtime 和远程连接的固定阶段失败,最多 4 个 256 KiB 文件;GoodBuddy Agent 在各 installation 的私有 state 目录中轮转保存 daemon、连接、恢复和 Runtime 生命周期,最多 3 个 64 KiB 文件,并可通过固定 `diagnostics --installation-id` 命令读取。两端都只记录白名单阶段、稳定错误码/类型和固定短消息,不保存 Prompt、凭据、文件内容、路径、环境、SSH 参数或 Provider 原始响应;诊断写入失败不改变正常运行。
- [x] **OpenCode Runtime 定制**:GoodBuddy 管理的内置 OpenCode 可发现原生 Agents、Tools、Commands、LSP、Formatters、MCP、Skills、Prompts 与 Resources;Tools 单独显示读取、文件修改、命令、网络、Agent 编排等类型、来源及 Ask/Execute 可用性,并隐藏 OpenCode 内部 `invalid` 与 GoodBuddy 临时 MCP 工具。支持保存默认 Agent、每次请求覆盖 Agent、通过原生 SDK 执行 Command、显示上下文用量并调用有总时限的原生 Compact;并发外部 Server 对话的提问使用请求级公开 ID 映射,回答不会串到其他会话。外部 OpenCode Server 只报告连接状态,不宣称原生清单可读。任意插件安装、Session Share、自动 Worktree 和 OpenCode 原生会话持久化仍不开放。
- [x] **OpenCode Runtime 定制**:GoodBuddy 管理的内置 OpenCode 可发现原生 Agents、Tools、Commands、LSP、Formatters、MCP、Skills、Prompts 与 Resources;Tools 单独显示读取、文件修改、命令、网络、Agent 编排等类型、来源及描述,并隐藏 OpenCode 内部 `invalid` 与 GoodBuddy 临时 MCP 工具。支持保存默认 Agent、每次请求覆盖 Agent、通过原生 SDK 执行 Command、显示上下文用量并调用有总时限的原生 Compact;并发外部 Server 对话的提问使用请求级公开 ID 映射,回答不会串到其他会话。外部 OpenCode Server 只报告连接状态,不宣称原生清单可读。任意插件安装、Session Share、自动 Worktree 和 OpenCode 原生会话持久化仍不开放。
- [x] **Continue Runtime 定制**:提供静态配置中的原生 Rules、Prompt 模板与 MCP 清单,以及可编辑的 GoodBuddy Rules/Prompt 配置预设;聊天可按请求选择预设和填入可继续编辑的 Prompt。当前 Continue Host 没有可信的静态原生 Tool 发现接口,且使用隔离的 `CONTINUE_GLOBAL_DIR`,因此界面明确标记 Tools 不支持静态发现,也不把 Host 实际不会加载的工作区或用户 Skills 冒充原生能力;GoodBuddy 分配的 Skills 仍按请求暂存执行。Continue 临时 Host 不复用原生会话压缩,手动压缩由 GoodBuddy 摘要模型完成并验证持久化摘要覆盖范围;Agent 交互提问转换为统一问答卡片。Resources、Hooks、后台 Job 和 Continue 原生会话管理继续暂缓。
- [x] **Runtime 原生清单语义**:原生能力以 Agents、Tools、Commands、Skills、MCP、Rules、Prompts、Resources、LSP、Formatters 和上下文 11 个页签展示;清单状态独立于 Runtime 连通性,区分完整、部分、不可用、仅连接和不支持。DeepSeek Harness 通过 Host Registry 枚举有界的内置/插件 Tools 与 Skills,显示真实 Ask/Execute 边界,并排除 GoodBuddy 按请求分配的 Skills、Web/MCP 代理。
- [x] **Runtime 原生清单语义**:原生能力以 Agents、Tools、Commands、Skills、MCP、Rules、Prompts、Resources、LSP、Formatters 和上下文 11 个页签展示;清单状态独立于 Runtime 连通性,区分完整、部分、不可用、仅连接和不支持。DeepSeek Harness 通过 Host Registry 枚举有界的内置/插件 Tools 与 Skills,显示来源和描述,并排除 GoodBuddy 按请求分配的 Skills、Web/MCP 代理。
- [ ] **Runtime 监督栏目**(规划中):在应用级助手工作栏的固定 Runtime 栏目统一承载 OpenCode、Continue 和 DeepSeek Harness 的 Task 级委派、后台执行、Workflow/Hook、长任务与原生会话监督;用户只选择 Conversation 或 Task,Job/Run 保持内部,不形成树或独立操作对象。
- [ ] **Subagent 高级监督**(规划中):在工作栏固定 Runtime 栏目按 Task 聚合,提供可配置的嵌套、并行、预算和生命周期控制。基础专家模式继承与单层直连模型编程委派已经可用。
- [ ] **Subagent 高级监督**(规划中):在工作栏固定 Runtime 栏目按 Task 聚合,提供可配置的嵌套、并行、预算和生命周期控制。基础专家能力继承与单层直连模型编程委派已经可用。
### Skills、MCP 与知识库
- [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、自定义解释器和运行中进程协调。
- [x] **内置 MCP 按需接入**:知识库、魔法笔记、GoodBuddy 配置与内置浏览器 MCP 可分别启停,并可分配给直连模型、GoodBuddy 管理的 OpenCode 和 Continue;这些 Server 在 DeepSeek Harness 中仍不支持。Story Graph 另通过 Main 代理支持 Harness。内置 MCP 仅通过当前请求的短期本机权限提供,Ask / Execute 读写边界不受用户配置放宽。
- [x] **内置 MCP 按需接入**:知识库、魔法笔记、GoodBuddy 配置与内置浏览器 MCP 可分别启停,并可分配给直连模型、GoodBuddy 管理的 OpenCode 和 Continue;这些 Server 在 DeepSeek Harness 中仍不支持。Story Graph 另通过 Main 代理支持 Harness。内置 MCP 仅通过当前请求的短期本机权限提供,继续校验启用、分配和资源归属。
- **Obsidian 接入(源码已实现,验收中)**:默认关闭,默认发现全部本机注册仓库,也可指定文件夹并在保存或启用前测试。随包提供 MCPVault 0.16.0,19 个共享工具保留全部上游能力;生产组件自动化、Electron 托管 Node、Windows 设置与聊天 UI,以及受限网络下的 Windows x64 解包应用已验证。物理断网、安装器及其他系统验收待完成。DeepSeek Harness 与远程执行保持不可用,详见[功能文档与验证记录](./docs/features/obsidian/README.md);此项不表示已发布。
- [x] **MCP Tools**:显式启用的自定义 MCP 可按 Runtime 分配给直连模型、GoodBuddy 管理的 OpenCode、Continue Agent Execute 和 DeepSeek Harness,并仅在 Execute 加载;Agent 子进程只获得按请求签发的本机回环权限,MCP 地址、命令和凭据保留在 Main,动态工具仍经过发现、执行记录与权限边界。直连模型与 Harness 的健康调用复用当前请求已发现的工具清单,不在每次调用前重复发现。
- [x] **MCP Tools**:显式启用的自定义 MCP 可按 Runtime 分配给直连模型、GoodBuddy 管理的 OpenCode、Continue Agent 和 DeepSeek Harness;Agent 子进程只获得按请求签发的本机回环权限,MCP 地址、命令和凭据保留在 Main,动态工具仍经过发现、执行记录与资源范围校验。直连模型与 Harness 的健康调用复用当前请求已发现的工具清单,不在每次调用前重复发现。
- [x] **MCP Prompts 与 Resources 元数据**:MCP 测试仅在 Server 声明对应能力时发现有界的 Prompt、参数与 Resource 元数据,不读取 Resource 内容;Runtime 支持的 Prompt 可填入聊天草稿后继续编辑。OpenCode 可报告实验性 Resource 清单,Continue 当前版本明确不支持 Resources。
- [x] **本地知识库**:支持文件、目录和网页导入、SQLite FTS5 检索及来源追溯。知识范围按对话保存,新对话默认不选择知识库;创建与检索高级参数默认折叠。文档区分可用、处理中和失败,支持打开来源、失败重试及移除来源前确认。
内容来源与文档索引共用可搜索列表,单文档来源不重复占行,目录保留分组文档,失败来源保留恢复操作;任务历史和次级操作从统一入口访问。
@@ -113,17 +113,18 @@
- [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 48 新增会话来源元数据;旧客户端不能再打开,回退需使用升级前备份。
- [ ] **MCP Server Control Plane**(规划中):统一 MCP 生命周期、健康检查、重连、Schema 缓存、隔离、审批和审计。
- [ ] **MCP Server Control Plane**(规划中):统一 MCP 生命周期、健康检查、重连、Schema 缓存、隔离、能力范围和审计。
- [ ] **知识/网页摘录与 AI 编辑**(规划中):在已实现的会话正文采集之外,扩展知识和网页摘录,并提供需确认的总结、改写和整理操作。
### 工作管理、长期协作与工作流
- [x] **任务、活动与成果**:集中管理任务状态、审计活动和独立成果文件;普通聊天回复只保留在会话中,不再自动复制到成果栏,已有重复聊天 Markdown 从成果列表隐藏但不物理删除。运行记录改由 Main SQLite 保存,不再受旧 Renderer 的 500 条、单详情 4,000 字符或约 2 MB 上限影响,页面按批继续显示;不再常驻旧缓存截断警告,但不会恢复此前已丢失的历史。Token 用量按 Runtime 与模型归类,并针对 OpenAI 兼容与 Anthropic Messages 的不同上报口径归一化展示缓存命中率;用量按项目或会话分组时默认收起并显示汇总,展开后显示 Runtime / 模型子行,按模型分组时保持平铺。独立“系统任务”页签按来源汇总后台用量,支持手动刷新和记录更新后刷新;监督回顾、图片重新生成、图谱提取和远程嵌入补充用量记录,不补算缺失的历史或供应商数据。活动按会话分组并默认收起,避免长历史占满页面。
- [x] **任务、活动与工作区文件**:集中管理任务状态和审计活动。成果页已移除,工作区按实际文件显示修改时间和可用的创建时间,支持排序及向所选本机或远程目录导入文件;远程上传需要更新后的 Agent。会话图片和附件保留在消息中,已有 artifact 数据不删除,也不自动复制到项目。运行记录改由 Main SQLite 保存,不再受旧 Renderer 的 500 条、单详情 4,000 字符或约 2 MB 上限影响,页面按批继续显示;不再常驻旧缓存截断警告,但不会恢复此前已丢失的历史。Token 用量按 Runtime 与模型归类,并针对 OpenAI 兼容与 Anthropic Messages 的不同上报口径归一化展示缓存命中率;用量按项目或会话分组时默认收起并显示汇总,展开后显示 Runtime / 模型子行,按模型分组时保持平铺。独立“系统任务”页签按来源汇总后台用量,支持手动刷新和记录更新后刷新;监督回顾、图片重新生成、图谱提取和远程嵌入补充用量记录,不补算缺失的历史或供应商数据。活动按会话分组并默认收起,避免长历史占满页面。
OpenCode 的输入量已扣除缓存,计算命中率时会加回缓存读取和写入量;查询已有记录时也按此口径重算。
- [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] **Task 与定制任务体验**:每个产品级 Task 只关联一条 Conversation,一条 Conversation 可承载多个 Task;左侧会话列表通过行首展开按钮显示带共享状态点的 Task 子项,父会话行不重复任务标签,UI 只展示到 Task,不暴露 Job/Run 层级。新建定制任务可关联当前或新 Conversation,沿用 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] **定时消息沿用会话**:定时消息使用会话当前历史、Runtime 和已保存的知识检索设置;任务详情显示运行状态,当前 occurrence 未结束时禁止重复“立即运行”。
任务中心通过状态数量和筛选定位运行中及待处理任务,结构化问题在对应会话中回答。
- [x] **智能心跳设置与范围**:“监督者 > 智能心跳”统一管理 Global 或指定 Project 的每日、每周计划及建议,Task Center 和设置中心不复制表单。待处理建议不重复,忽略后有新依据才再次提出;项目与全局共用按来源版本维护的时间线进度,会话级实时主动介入仍为规划。详见[实现规则](./docs/features/conversation-supervision/logic-design.md)。
- [x] **故事、经验与时间螺旋**:事件可组织为项目功能、子线索及可选跨项目故事,支持重命名、合并、调整归属、移除和撤销。经验保留形成及应用依据与人工调整标记,心跳可提示暂无进展故事或可能适用的经验。平铺和懒加载 WebGL 时间螺旋共用选择与来源,保留平铺回退及图形资源释放;整理与建议可能增加模型费用,自动结果须核对来源。详见[实施证据](./docs/features/conversation-supervision/progress.md)。
- [x] **监督者回顾、故事线图谱与活动**:按范围和时间回顾工作,查看保存结果的图谱及来源,确认或修订实体,并带入相关回顾上下文继续讨论。侧栏可跟随或固定 Conversation、Task,并打开匹配结果。活动合并心跳触发与下游监督的阶段状态,保留失败和旧结果入口。自动与默认手动回顾只处理新增、修改及未处理来源,无变化时跳过模型;显式“重新整理”重读区间,但不推进或重置共享进度,自动回顾不续跑用户暂停的运行。回顾只读且禁止工具,模型调用可能产生费用,详见[实施与验证记录](./docs/features/conversation-supervision/progress.md)。
@@ -134,16 +135,16 @@
- [ ] **批量运行与对比实验室**(规划中):对模型、Prompt、角色和工作流配置执行批量对比,汇总质量、耗时、Token、费用、失败率和成果差异。
- [ ] **时态记忆与事实冲突检测**(规划中):为记忆和知识图谱增加有效期、当前事实、过期与矛盾检测、事实核验及证据回溯。
- **Story Graph 只读工具(Desktop 0.15.9 / Agent 0.15.3)**:Agent 可搜索已保存工作、故事和经验,读取上下文并分页核对来源。监督者开关及 Runtime 分配同时控制发现和调用,旧会话也受限制;本地和远程路径共用桌面读取入口。共享来源版本事实按允许范围复用,关键词匹配和工具说明引导模型按需查询,不自动注入全图;经验是参考而非指令,查询本身不调用模型,`as_of` 仍未实现。详见[设计与验证](./docs/features/conversation-supervision/story-graph-mcp-design.md)。
- [ ] **可视化受控工作流**(规划中):提供版本化 DAG、条件分支、审批、取消和恢复,执行节点继续经过 Main Runtime 边界。
- [ ] **统一 Run Graph 与回放**(规划中):关联任务、Subagent、模型、知识、工具审批、用量和成果,支持失败定位、重试和脱敏导出。
- [ ] **可视化受控工作流**(规划中):提供版本化 DAG、条件分支、业务确认、取消和恢复,执行节点继续经过 Main Runtime 边界。
- [ ] **统一 Run Graph 与回放**(规划中):关联任务、Subagent、模型、知识、工具活动、用量和成果,支持失败定位、重试和脱敏导出。
### 浏览器、通信、语音与应用维护
- [x] **Runtime 共享内置浏览器**:使用 GoodBuddy 内置的隔离 Chromium,不控制客户端已安装的浏览器;用户通过内置 MCP 总开关和 Runtime 分配决定是否提供给 Execute,开启后不逐次询问。关闭 Agent 浏览器能力不影响用户继续在浏览器工作栏中手动前往、返回、刷新、停止加载、交互或关闭浏览器。直连模型、GoodBuddy 管理的 OpenCode 和 Continue 通过请求级权限共享按 Conversation 归属的会话和串行操作路径,用户导航会改变 Agent 下一步看到的页面。
- [x] **Runtime 共享内置浏览器**:使用 GoodBuddy 内置的隔离 Chromium,不控制客户端已安装的浏览器;用户通过内置 MCP 总开关和 Runtime 分配决定工具接入,开启后不逐次询问。关闭 Agent 浏览器能力不影响用户继续在浏览器工作栏中手动前往、返回、刷新、停止加载、交互或关闭浏览器。直连模型、GoodBuddy 管理的 OpenCode 和 Continue 通过请求级权限共享按 Conversation 归属的会话和串行操作路径,用户导航会改变 Agent 下一步看到的页面。
- [x] **浏览器多页签与按需资源**:同一对话可打开多个共享登录状态、页面与导航独立的浏览器页签,模型请求固定使用开始时绑定的目标。未使用的请求预留不创建浏览器资源,已释放页面可重新打开;保留显式截图,普通操作不再触发无用的自动截图。
- [x] **浏览器关闭与刷新**:AI 使用中的页签也可关闭,不取消整个请求;后续显式导航会创建独立替代页。空白页不能刷新,切换或关闭面板后清除旧操作错误。
- [x] **客户端电脑控制工具**:与内置浏览器分开管理,并保留范围、取消、超时、输出边界和执行记录。
- [x] **远程消息通道项目**:微信 ClawBot、企业微信和钉钉分别拥有系统管理的项目、独立远程会话、工作目录、处理后端、默认 Ask/Execute 模式及任务活动归属;完整回复交由各通道按平台能力控制长度与分段,不再由公共服务统一截断。
- [x] **远程消息通道项目**:微信 ClawBot、企业微信、钉钉和 Telegram 分别拥有系统管理的项目、独立远程会话、工作目录、处理后端及任务活动归属;完整回复交由各通道按平台能力控制长度与分段,不再由公共服务统一截断。Telegram 的真实 Bot 与模型链路仍[待验收](./docs/features/telegram-channel/progress.md)。
- [x] **微信 ClawBot 扫码与媒体**:通过独立 Sidecar 完成本机扫码、验证码、加密凭据和文字收发;支持个人微信私聊图片与文件,单条消息最多 4 个附件、解密后合计不超过 12MB。
- [x] **微信安全回传**:支持返回当前任务生成的图片,或在用户明确要求时将本次最终文本生成为 Markdown 附件;不自动读取或发送已有工作区文件。
- [x] **企业微信与钉钉连接**:支持 Main-only 加密设置、环境变量只读覆盖、连接测试、动态启停、发送者范围和状态诊断。
@@ -158,7 +159,10 @@
### 开源、构建与发布
- Desktop `0.15.3` 修复本地直连手动压缩和请求有效期间的工具授权,区分取消与失败并去除重复提示,优化侧栏搜索、注册成功后的快捷键提示及笔记操作布局,减少启动和存储中的重复处理,并精简重复依赖及开发文件。不宣称整体渲染性能已显著提升;Office 实时编辑仍为设计。证据见[0.15.3 发布准备](./docs/development/release-preparation-0.15.3.md)。
- 当前 Desktop 候选为 `0.15.10`,已发布 Agent 为 `0.15.3`、最低要求 Desktop `0.15.9`,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。
- 当前 Desktop 候选为 `0.15.13`,Agent 候选为 `0.15.6`、最低要求 Desktop `0.15.13`,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。
- Desktop `0.15.13` / Agent `0.15.6` 将桌面业务 SQLite 的唯一 owner 移到一个类型化 storage utility process,Assistant、Knowledge、附件、运行绑定和账本保留原路径及事务语义。直连模型在工具失败后可继续,分页输出、Magic Notes 标签、监督来源别名、设置直达预加载和文件元数据同步修正;schema 仍为 62。storage/监督 focused 编排已通过,但 C01–C07 并发验收矩阵、跨平台 storage 进程安装和低端机对照仍是明确后续门槛,不作通用性能承诺。见[发布准备](./docs/development/release-preparation-0.15.13.md)。
- Desktop `0.15.12` / Agent `0.15.5` 增加 Telegram 私聊配置(真实 Bot 与模型完整验收仍待完成)和本地日期自定义回顾。监督图谱/来源按需读取、索引 Worker 查询、摘要缓存和选择列表窗口化减少重复工作,最终发布仍可阻塞 Main;未发布回顾省略已删除会话并重算受影响批次,历史结果保留。临时模型失败最多多试一次,可能增加费用;远程启动取消、通道交付、长消息展开状态及历史缺失时长得到修正。schema 62 增加通道支持和查询索引,升级前备份完整数据目录,降级需恢复备份。两个独立开发审计阶段共记录 15 次外部文本请求,本次发布准备没有新增;精确源码 CI 与原生打包仍为门槛。见[发布准备](./docs/development/release-preparation-0.15.12.md)。
- Desktop `0.15.11` / Agent `0.15.4` 移除 Ask/Execute 与通用工具审批,旧 Ask 会话也不再有产品级只读边界。schema 60 和 Runtime 设置 22 清理退休元数据,升级前备份完整数据目录,降级需恢复备份;远程需配套 runtime/acp 6 工件。工作区导入、文件时间排序、会话图片发现和远程专家保存随成果页移除一起提供,不删除既有成果。执行时长按运行状态计时、不补算旧历史,OpenCode 缓存率采用完整输入量。回顾历史、响应布局及螺旋实际区间着色保留未归属事件与中性空档;汇总读取和回顾初始化减少限定路径 I/O,不承诺普遍提速。开发证据含无模式执行与专家图片保存共 25 次真实文本请求,不是本次发布准备新调用或图片生成;精确候选 CI、签名包、公开验证和跨版本活动操作恢复与这些检查分开记录。见[发布准备](./docs/development/release-preparation-0.15.11.md)。
- Desktop `0.15.10` 统一项目/会话菜单并显示十个最近会话,增加独立监督模型选择及默认开启的会话 Story Graph 开关。活动索引分页与完整汇总、文档 Worker、延迟启动、窄渲染订阅、列表窗口化、终端批次和旧 Runtime 缓存清理减少重复工作,不承诺通用提速比例。schema 58 要求升级前备份数据库及笔记;高频本地写入改用 NORMAL,断电/系统崩溃可能丢失最近部分写入,远程确认、通道投递和任务生命周期仍用 FULL。Agent 保持 0.15.3;四次真实 Host 模型请求及 1000 行 Unicode SSH 终端检查通过,精确候选 CI 与六平台打包仍为发布门槛,不宣称已修复尚未定位的远程恢复错误。见[发布准备](./docs/development/release-preparation-0.15.10.md)。
- Desktop `0.15.9` 增加共享回顾进度、故事经验与时间螺旋,schema 56 要求升级前备份,相关模型整理可能增加费用。会话与消息窗口化、Markdown 缓存、只读 Worker、活动差量保存及远程 checkpoint 批次落库减少重复工作,不承诺所有机器相同比例提速;页内查找不覆盖未挂载行,快速滚动可能显示占位,Worker 不可用时同步回退。Execute 配置修改取消重复原生确认,Electron 为 44.5.1。当前源码四次真实 Host 模型请求通过,仍需精确候选 CI 与打包验证,见[发布准备](./docs/development/release-preparation-0.15.9.md)。
- Desktop `0.15.8` 与 Agent `0.15.2` 支持 Execute 模式保存会话图片到本机或 Host 绝对路径,覆盖需明确指定,不支持转换为 WebP。MCP 设置列出图像工具,不代表所有模式均可用。笔记增加新记录取消、编辑焦点返回及可用内容全宽;缺少浏览器和磨砂设置时默认开启,已保存选择不变。schema 仍为 52。真实 Linux x64 Host 工具层证据与待完成的精确候选 CI、打包验证分别记录在[发布准备](./docs/development/release-preparation-0.15.8.md)。
@@ -189,9 +193,9 @@
- [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 传输矩阵,发布结果另行记录。
- [ ] **主机管理与云环境管理**(规划中):主机管理迁入应用中心,提供列表与详情工作页面;云环境管理作为同级应用,复用主机连接和执行能力。首版适配计划覆盖 AWS、Azure、阿里云、腾讯云、VMware vCenter / vSphere、ZStack,分批完成已有实例发现、接入及真实执行验收;实例创建、启停、销毁和网络编排后续交付。平台 API 尚未实现,现有云主机可按 SSH 条件手工接入。详见[产品范围](./docs/features/remote-host/prd.md)。
## 规划原则
规划中的工作流、Subagent、MCP、远程 API 和沙盒能力不得绕过现有 Main Runtime、Ask/Execute、权限、取消、超时和审计边界。
规划中的工作流、Subagent、MCP、远程 API 和沙盒能力不得绕过现有 Main Runtime、能力范围、权限、取消、超时和审计边界。
+6 -14
View File
@@ -19,7 +19,7 @@
[![Linux](https://img.shields.io/badge/Linux-x64%20%7C%20arm64-FCC624)](https://github.com/mesalogo/goodbuddy/releases)
</div>
![GoodBuddy workspace](./docs/screenshots/workspace-overview.png)
![GoodBuddy workspace](./docs/screenshots/GoodBuddy_en.png)
GoodBuddy is a desktop application for model chat, coding agents, knowledge
retrieval, notes, and scheduled tasks. No GoodBuddy account is required.
@@ -32,8 +32,8 @@ same device, on a private network, or at a provider selected by the user.
stays local by default, and users choose where model services run.
- **Unified Agent Runtimes**: Use direct models, OpenCode, Continue, and the
preview DeepSeek Harness from one desktop interface.
- **Explicit execution boundaries**: `Ask` remains read-only, while `Execute`
uses the current account's permissions. Tool activity, cancellation,
- **Unified execution**: Requests use the selected Runtime, enabled capabilities,
and current account permissions without choosing a work mode. Tool activity, cancellation,
timeouts, and token usage are recorded.
- **Broad platform coverage**: Official Linux releases cover `x64` and `arm64`,
including compatible distributions used in China's domestic computing
@@ -53,19 +53,15 @@ current limitations.
| Area | Available in GoodBuddy |
| --- | --- |
| Agent Runtimes | Direct model connections, OpenCode, Continue, and the preview DeepSeek Harness |
| Data and execution | Local SQLite and note-file storage, read-only `Ask`, full-account `Execute`, and recorded tool activity |
| Data and execution | Local SQLite and note-file storage, current-account tool execution, 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 |
| Integrations | Local or hosted model endpoints, custom MCP servers, WeChat ClawBot, WeCom, DingTalk, and Telegram ([live Bot + model acceptance pending](./docs/features/telegram-channel/progress.md)) |
## Product tour
### Knowledge bases and graphs
![GoodBuddy knowledge workspace](./docs/screenshots/knowledge-workspace.png)
![GoodBuddy knowledge graph](./docs/screenshots/knowledge-graph.png)
### Magic Notes and Smart Heartbeat
Magic Notes displays a continuous record stream, with a narrow left index and
@@ -77,10 +73,6 @@ 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)
## Agent Runtimes
| Runtime | Functions | Configuration |
@@ -142,7 +134,7 @@ See [BUILD.md](./BUILD.md) for build and packaging instructions.
default. API keys are encrypted by secure system storage.
- The Renderer has no access to raw Electron APIs or model credentials.
- The DeepSeek Harness plugin marketplace is off by default. Third-party
install scripts, initialization code, and Execute tools run with the current
install scripts, initialization code, and tools run with the current
user's permissions.
- Remote delegation is enabled only after the user configures an endpoint and
token.
+5 -13
View File
@@ -19,7 +19,7 @@
[![Linux](https://img.shields.io/badge/Linux-x64%20%7C%20arm64-FCC624)](https://github.com/mesalogo/goodbuddy/releases)
</div>
![GoodBuddy 工作空间](./docs/screenshots/workspace-overview.png)
![GoodBuddy 工作空间](./docs/screenshots/GoodBuddy_zh.png)
GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时任务的桌面应用。使用 GoodBuddy 无需注册账号,工作数据默认保存在本机。模型服务可以运行在本机、组织内网或用户选择的云端服务。
@@ -27,7 +27,7 @@ GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时
- **本地优先**:无需注册 GoodBuddy 账号,工作数据默认保存在本机,模型服务由用户选择。
- **统一 Agent Runtime**:在一个桌面界面中使用直连模型、OpenCode、Continue 和预览版 DeepSeek Harness。
- **执行边界明确**:`Ask` 保持只读,`Execute` 使用当前账号权限,工具调用、取消、超时和 Token 用量均有记录。
- **统一执行**:请求按所选 Runtime、已启用能力和当前账号权限运行,无需选择工作模式;工具调用、取消、超时和 Token 用量均有记录。
- **支持信创软硬件**:正式 Linux 版本覆盖国产 `x64` 与 `arm64` 架构,可用于统信 UOS、银河麒麟及海光、兆芯、鲲鹏、飞腾等兼容环境;龙芯 LoongArch 提供独立 `loong64` 实验预览版。
- **知识与长期工作一体化**:知识库、知识图谱、魔法笔记、任务和智能心跳共享同一个桌面工作空间。
@@ -38,19 +38,15 @@ GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时
| 范围 | GoodBuddy 提供的能力 |
| --- | --- |
| Agent Runtime | 直连模型、OpenCode、Continue 和预览版 DeepSeek Harness |
| 数据与执行 | 本地 SQLite 与笔记文件存储、只读 `Ask`、使用当前账号完整权限的 `Execute`、工具调用记录 |
| 数据与执行 | 本地 SQLite 与笔记文件存储、当前账号权限下的工具执行、工具调用记录 |
| 知识库 | 导入文件、目录和网页,通过全文、中文词组、向量和知识图谱检索 |
| 桌面版本 | Windows、macOS、Linux 的 `x64` 与 `arm64` 正式版本,以及龙芯 LoongArch 实验预览版 |
| 外部连接 | 本机或内网模型服务、自定义 MCP、微信 ClawBot、企业微信和钉钉 |
| 外部连接 | 本机或内网模型服务、自定义 MCP、微信 ClawBot、企业微信、钉钉和 Telegram([真实 Bot 与模型链路待验收](./docs/features/telegram-channel/progress.md)) |
## 产品界面
### 知识库与知识图谱
![GoodBuddy 知识工作区](./docs/screenshots/knowledge-workspace.png)
![GoodBuddy 知识图谱](./docs/screenshots/knowledge-graph.png)
### 魔法笔记与智能心跳
魔法笔记连续展示全部记录,左侧窄栏索引与右侧 AI 评论可独立收起,点击缩略图滚动定位并保留草稿。
@@ -59,10 +55,6 @@ GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时
默认模型的图像输入能力分析,不支持图像时明确提示仅文字降级。具体行为与最终验证状态见
[魔法笔记文档](./docs/features/magic-notes/README.md)。
![GoodBuddy 魔法笔记](./docs/screenshots/GoodBuddy_MFSGeK0NoT.gif)
![GoodBuddy 智能心跳](./docs/screenshots/smart-heartbeat.png)
## Agent Runtime
| Runtime | 适用场景 | 配置来源 |
@@ -116,7 +108,7 @@ npm run dev
- 模型请求只发送到用户选择的服务。
- 本地数据默认保存在系统应用数据目录,API Key 由系统安全存储加密。
- Renderer 不接触原始 Electron API 或模型凭据。
- DeepSeek Harness 插件市场默认关闭。第三方插件安装脚本、初始化代码和 Execute 工具以当前用户权限运行。
- DeepSeek Harness 插件市场默认关闭。第三方插件安装脚本、初始化代码和工具以当前用户权限运行。
- 远程委派只在用户配置端点和令牌后启用。
- 内网兼容模式允许应用内 HTTP 和非标准 HTTPS 证书;微信凭据和媒体端点仍执行严格校验。
+30 -24
View File
@@ -163,6 +163,7 @@
- 页面顶部和底部内边距默认为 `32px`。
- `master-detail` 的左栏建议为 `280px` 至 `360px`,右栏占剩余空间。分隔线和滚动容器属于壳层,不由内容卡片模拟。
- 聊天输入区可以粘附在 `reading` 壳层底部,但消息内容和输入区必须共享同一阅读宽度。
- 输入区顶部向消息区延伸 `--space-4`(16px)的透明到 `--surface-raised` 渐变,柔化消息裁切边缘;渐变不占布局空间、不拦截鼠标事件,并随主题切换。
- 宽表格可在 `dashboard` 内容区内部横向滚动,不得撑宽整个应用窗口。
## 5. 信息层级
@@ -314,7 +315,7 @@
并记住用户选择;分隔条使用 `separator` 语义,支持方向键、Home 和 End。小于
`900px` 时继续使用固定宽度抽屉,不显示拖拽分隔条。
- 关联 Task 的 Conversation 在行最左侧显示独立展开按钮,父会话行不重复任务标签或数量;
展开后的每个 Task 子项左侧显示名称,模式、计划和共享状态标记收在右侧,与父会话的
展开后的每个 Task 子项左侧显示名称,计划和共享状态标记收在右侧,与父会话的
更新时间位置一致;展开层级只到 Task,不展示 Job、Subjob 或 Run。
- 会话标题保持单行且不挤压更新时间。更新时间默认靠近行右侧;低频的更多操作按钮不常驻
占位,只在悬停会话行时替换右侧时间显示。键盘焦点进入会话行或操作菜单展开时,按钮仍
@@ -350,9 +351,12 @@
动态 Tab、“+”和终端细节见
[助手工作栏多终端页签 PRD](./docs/features/assistant-workbar/terminal-tabs-prd.md)。
- 升级后默认打开任务中心、工作区、一个浏览器实例和成果。任务中心与工作区是不可关闭的
单实例基础应用;浏览器是可关闭的多实例应用;成果保持可关闭的单实例应用。不可关闭不
- 升级后默认打开任务中心、工作区和一个浏览器实例。任务中心与工作区是不可关闭的
单实例基础应用;浏览器是可关闭的多实例应用。成果页及其导入入口已移除。不可关闭不
等于锁定顺序。
- 工作区以实际目录文件为准,提供向所选目录导入文件、本机复制及远程上传。文件列表按名称、
修改时间或可用的创建时间排序,目录优先,缺失时间显示短横线且排序置后。窄栏在文件视口内
横向滚动。创建时间只取文件系统 birthtime,不使用 Linux ctime。会话附件和图片保留在消息中。
- 资源监控作为本机单实例应用保留在能力目录,但不加入升级后的默认打开实例。面板只在可见
活动时采样,首期不持久化历史或提供后台负载告警。
- Tab 行只显示已打开实例,行末始终提供“+”普通按钮;点击后在内容区打开应用选择页。
@@ -453,14 +457,14 @@
### 6.11 单选选择器
模型、专家角色、工作模式、Runtime Agent、Runtime 预设和 Runtime 快捷操作属于同一输入上下文,其选择器必须共享结构、尺寸和菜单视觉,不能出现一个精细菜单与多个风格不一致的原生下拉框。
模型、专家角色、Runtime Agent、Runtime 预设和 Runtime 快捷操作属于同一输入上下文,其选择器必须共享结构、尺寸和菜单视觉,不能出现一个精细菜单与多个风格不一致的原生下拉框。
- 只有选项能由单行短标签充分区分、不需要补充来源、位置、状态或说明时,才使用原生 `select`。
- 当用户必须在选择前比较来源、范围、位置、连接状态或其他辅助信息时,使用与输入区选择器同类的自定义富信息单选菜单,不用 CSS 修饰原生 `select` 冒充富信息菜单。
- 触发按钮复用统一的模型选择按钮样式,保持相同高度、圆角、边框、展开指示和焦点状态。
- 菜单使用 `menu` 与 `menuitemradio` 语义,当前项同时显示选中标记和 `aria-checked`。选项可以包含一行简短说明,但标签和说明不得被截断到无法区分。
- 支持上、下方向键、Home、End、Enter 或 Space、Escape;打开后焦点进入当前项,关闭后返回触发按钮。
- 点击或聚焦菜单外部时关闭;同一输入区内的模型、专家和模式菜单互斥展开。
- 点击或聚焦菜单外部时关闭;同一输入区内的模型、专家和 Runtime 菜单互斥展开。
- 不可用选项保持可读并说明原因,键盘导航不得停留在不可选择项上。
- 当某个选项需要用户在其展开区域内完成路径选择、授权或其他前置操作时,不得因为前置条件
尚未完成而禁用该入口。选择入口只展开流程,验证成功前不提交实际值;不得自动采用第一个
@@ -500,6 +504,7 @@ Switch 用于在两个持久状态之间立即切换,例如启用能力、开
### 6.14 InlineHelp 同行帮助
页面、区块和字段的补充用途、格式及操作说明统一收进标题或标签旁的 `InlineHelp` 问号按钮。
图谱图例使用同一组件的 `info` 图标变体,只显示 i 图标,保留可访问名称与简短 title;浮层和键盘行为相同。
`PageHeader` 与 `SettingsSectionHeader` 通过 `help` 接收这类内容;`description` 保留需要常显的上下文,两处不重复说明。描述缺省或为空时不渲染段落,不保留空行。
当前范围、状态、字段错误、操作进度、风险、费用和影响用户决策的限制仍就地可见。混合文案先拆分,再将补充说明移入帮助。
@@ -682,17 +687,17 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- 本地会话的更多操作提供“在新会话中继续”。创建前必须先持久化当前稳定消息;有活动运行
或待发送内容时不可创建。新会话复制聊天内容与当前 Runtime/知识检索选择,重置压缩状态,
使用新的会话和消息 ID,且不复制 Task、队列、运行、记忆或成果归属。
- 模式、模型或工具权限属于上下文控制,不与页面导航页签混用。
- 模型、专家角色、工作模式、OpenCode Agent、Continue 预设和 Runtime 快捷操作使用统一的上下文单选菜单,并保持菜单互斥、键盘可达和选中状态明确。
- 模型和能力范围属于上下文控制,不与页面导航页签混用。
- 模型、专家角色、OpenCode Agent、Continue 预设和 Runtime 快捷操作使用统一的上下文单选菜单,并保持菜单互斥、键盘可达和选中状态明确。
- Runtime 选择菜单必须反映当前执行空间的真实能力。托管 SSH 项目当前只支持 OpenCode,因此输入区只渲染已配置的 OpenCode 选项和管理入口,不显示直连模型、Continue 或 DeepSeek Harness 的不可用入口;旧会话保存的其他 Runtime 选择在项目激活时恢复为当前 OpenCode 配置。
- 输入区使用一个完整容器,主工具栏保留附件、语音、Runtime、工作模式、会话选项及停止/发送。知识范围、专家角色、OpenCode Agent、Continue 预设和 Runtime 快捷操作收纳在会话选项浮层中;Runtime 专属选择在浮层内按提供方分组,无可选功能时不保留空分组。输入框上方以简短摘要显示生效的专家、知识范围、检索方式和 Runtime 专属选择。
- 输入区使用一个完整容器,主工具栏保留附件、语音、Runtime、会话选项及停止/发送。知识范围、专家角色、OpenCode Agent、Continue 预设和 Runtime 快捷操作收纳在会话选项浮层中;Runtime 专属选择在浮层内按提供方分组,无可选功能时不保留空分组。输入框上方以简短摘要显示生效的专家、知识范围、检索方式和 Runtime 专属选择。
- 会话选项浮层与主行菜单互斥,内部复用上下文选择菜单;子菜单就地展开,浮层统一限制宽高并滚动。Escape 先关闭子菜单并恢复其触发按钮焦点,再关闭会话选项并恢复入口焦点;点击外部、焦点离开、切换会话或开始/结束回复时关闭浮层。
- 会话选项入口紧随录音键,使用无常驻边框的调节图标,保留可访问名称、悬停说明和展开态反馈;浮层在工具栏左侧展开,最大宽度为 `300px`,窄窗口不超过输入区。浮层内选择器使用整行轻量列表样式,避免重复的按钮边框;知识库行右侧显示展开箭头,展开时朝上。
- OpenCode、Continue 和 DeepSeek Harness 后续的 Task 级委派与取消、后台执行进度/结果、Workflow/Hook 运行、长任务暂停/恢复/终止及原生会话监督统一进入应用级助手工作栏固定的“Runtime”栏目,不加入 Composer。栏目入口始终存在;只选择 Conversation 或 Task,并按 Task 聚合执行状态,不显示 Job/Run 树或独立操作菜单。内部可选区域按用户所选目标和 Runtime 的真实能力显示,不为未支持能力渲染空卡片或成排禁用按钮。切换跟随目标时必须清理上一归属的监督状态,固定目标则保持不变。
- 设置中心只管理持久 Runtime 配置、默认值和能力清单;右侧 Runtime 栏目管理用户当前跟随或固定目标的生命周期。两处不得复制同一实时操作,栏目中的高风险操作仍须就地确认并保留取消、权限、用量和活动审计。
- Runtime Prompt 快捷操作只把模板填入输入草稿,用户可以继续编辑;OpenCode Command 由 Runtime 原生 API 执行,输入框只承载可选参数,不以普通斜杠文本冒充执行。
- OpenCode 原生 Task 调用在本地项目和托管 SSH 项目中统一解析为子 Agent 状态卡,不显示为普通工具卡。状态卡持续显示 Agent 类型、任务说明、运行状态、完成输出或失败原因;身份徽标、Agent 名称和状态位于首层,任务说明作为主信息,Runtime 来源作为次级元数据,输出与错误在同一卡片内按需展开。父 Agent 的推理不冒充子 Agent 过程,必须与状态卡保持明确间距。状态不能只靠颜色表达。推理、子 Agent、工具和正文统一按事件首次出现顺序进入消息块;增量更新只更新原位置,Task 临时工具块重分类后必须原位替换,不能把子 Agent 卡统一提升到消息顶部。同一调用的增量事件按稳定调用 ID 更新,不能因 ACP 首帧尚无参数而永久保留重复工具卡或活动记录。一次请求内已观察到的子 Agent 均保留在消息与活动记录中;父请求取消、失败或意外中断时仍处于活动状态的卡片必须结算为对应终态,父请求成功结束但子 Agent 未报告终态时必须显示为失败而不是推断为完成。
- Agent 回复进行中锁定模型、专家角色、工作模式和 Runtime 定制选择器,并关闭已打开的上下文菜单;回复结束或停止后再恢复选择,避免界面状态与本次运行实际使用的上下文不一致。
- Agent 回复进行中锁定模型、专家角色和 Runtime 定制选择器,并关闭已打开的上下文菜单;回复结束或停止后再恢复选择,避免界面状态与本次运行实际使用的上下文不一致。
- 普通工具在主会话和子 Agent 过程中使用同一轻量折叠记录:不显示外层“工具执行”标题、计数、背景和边框,保留无障碍区域名称。记录纵向左对齐,头部按内容收宽且不超过消息宽度;展开箭头在左,工具名作为主标题,有额外信息的单行摘要弱化显示。右侧以绿色对勾显示成功完成,以图标显示运行和待执行状态,提供悬停文字和无障碍名称;失败和可恢复状态保留文字。仅重复工具名和执行状态的已知 Runtime 模板摘要在头部与详情中均隐藏;刷新重试、审批等额外提示保留。默认折叠,状态和输出更新不改变用户的展开选择;折叠时露出一行错误,展开详情使用消息可用宽度、浅背景和左侧细线,按错误、结果、独立折叠的输入参数阅读。详情保留原始文本,完整 JSON 输入可缩进显示,复制仍使用收到的原文,并通过应用通知反馈。详情工具栏只保留复制按钮,文本默认自动换行,长内容在固定最大高度内滚动,不提供换行切换和展开全部按钮。
- OpenCode 主会话和子 Agent 的工具记录均保留 Runtime 提供的操作标题作为摘要,显示文件路径、命令或操作描述;本地 SDK 标题去除首尾空白后最多保留 240 字符,没有标题时沿用工具名回退规则。主会话工具失败或中断时保留已收到的标题;远端 ACP 继续在记录名称中显示其提供的标题。
- 工具取消后保留“已取消”文字,避免与失败图标混淆。
@@ -701,10 +706,10 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- 待发送项默认在当前回复结束后顺序执行。每项提供明确的“立即中断并插入”(空闲时为“立即运行”)和删除操作;前者先取消当前 Conversation 的活动执行,再将所选项提升为下一项,不允许同一 Conversation 并发写入时间线。
- 队列区域向上增长并设置有界滚动高度,不提供展开、收起或冗余计数标题。宽容器中的操作采用弱化的行内样式,不渲染成强调卡片或胶囊;窄容器中保留来源、摘要和图标操作并隐藏冗长按钮文字。区域、来源和每个操作必须有可读无障碍名称。队列异步错误进入应用通知,不在 Composer 内复制通知样式。
- 支持上下文状态的 Runtime 在输入区下方复用同一紧凑用量条;文案必须区分“本次模型调用”和“压缩后对话估算”。手动压缩仅在当前 Runtime 明确支持且没有活动回复时显示,作为元信息区左下角的浮动次操作,不参与输入区高度计算;元信息区始终预留稳定高度,切换 Runtime 不得让输入框上下位移。元信息区与窗口底部只保留紧凑安全留白,不形成额外空白区。进行中禁用重复操作,结果通过应用通知反馈。
- 已选择的工作模式在触发按钮中只显示 `Ask` 或 `Execute`;完整中文含义和说明保留在菜单选项、可访问名称及输入区下方的模式说明中。
- 宽度大于 `700px` 时,主工具栏的添加内容、Runtime、模式、会话选项与发送保持同一行。窄输入区按容器宽度换行,保留发送及停止入口;选项浮层不得超出输入区宽度,不再常驻独立的 Runtime 专属功能行。
- 不显示 Ask/Execute 选择器、模式徽标或替代权限档位;能力由所选 Runtime 和已有启用、分配设置决定。
- 宽度大于 `700px` 时,主工具栏的添加内容、Runtime、会话选项与发送保持同一行。窄输入区按容器宽度换行,保留发送及停止入口;选项浮层不得超出输入区宽度,不再常驻独立的 Runtime 专属功能行。
- 输入框原生支持 `Ctrl+V`:文本直接进入草稿,系统截图和从文件管理器复制的一个或多个支持文件加入本次消息附件。文件粘贴与上传按钮共用读取、解析进度和数量限制;读取期间阻止重复导入及发送,错误就地关联输入框和附件按钮。具体类型、大小及批次失败规则见[聊天附件要求](./docs/features/document-processing/prd.md#321-聊天附件入口)。默认工具栏不提供独立“读取剪贴板”“截取当前屏幕”和“选择应用窗口”入口。
- 空输入框使用三行原生占位文案:第一行为消息提示(生图模式保留图片描述提示),第二行为“Enter 发送 · Shift+Enter 换行 · Ctrl+V 粘贴”,第三行为“Ctrl+N 新建对话 · 实际已注册快捷键 快捷唤起”。新建与粘贴快捷键按平台显示,macOS 使用 Command;全局快捷键不可用时省略该项。输入文字后占位提示自然隐藏,输入框下方不再重复全局快捷键,保留模式、安全边界和其他元信息。
- 空输入框使用三行原生占位文案:第一行为消息提示(生图模式保留图片描述提示),第二行为“Enter 发送 · Shift+Enter 换行 · Ctrl+V 粘贴”,第三行为“Ctrl+N 新建对话 · 实际已注册快捷键 快捷唤起”。新建与粘贴快捷键按平台显示,macOS 使用 Command;全局快捷键不可用时省略该项。输入文字后占位提示自然隐藏,输入框下方不再重复全局快捷键,保留能力范围和其他元信息。
- 输入操作提示不能替代表单的可访问名称,输入框始终保留持久的程序化标签。
- 草稿与消息中的附件操作使用紧凑图标按钮,并提供悬停和键盘焦点提示;文件名、解析状态与操作行的布局遵循[附件操作规则](./docs/features/document-processing/ui-design.md#12-附件操作菜单)。
- 空对话展示可执行的起始建议,发送失败保留输入并提供重试。
@@ -720,7 +725,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- 侧栏最近会话的更新时间在当天显示本地时间,非当天显示本地日期;非当年记录必须同时显示年份。悬停时间信息时提供完整日期和时间。
- 侧栏中有关联 Task 的 Conversation 显示行首展开按钮,父行不显示任务标签或数量;展开后
最多直接显示 3 个 Task,“查看全部 N 个任务”打开该 Conversation 的完整任务区。
- Task 子项左侧显示名称,右侧显示本地化的 Ask/Execute、计划摘要和共享状态标记,与父会话
- Task 子项左侧显示名称,右侧显示计划摘要和共享状态标记,与父会话
的更新时间位置一致;已完成使用带勾成功点,不重复显示完成文字。点击子项打开同一
Conversation 并定位 Task,不继续展开 Job/Run。
- 删除入口使用 `danger-ghost`,并按数据可恢复性执行确认或撤销策略。
@@ -736,9 +741,9 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
### 13.4 智能心跳
- 监督者入口使用 `supervisor` 壳层;智能心跳设置位于同一壳层内。
- 顶层页签为“工作回顾 / 故事线图谱 / 智能心跳 / 活动记录 / 设置”,使用一套共享 PageTabs,窄窗口单行横向滚动。页签下不重复同名标题,也不保留空标题容器;所选回顾使用“监督结果”标题,历史结果不得标为最新。
- 顶层页签为“工作回顾 / 故事线图谱 / 智能心跳 / 活动记录 / 设置”,使用一套共享 PageTabs,窄窗口单行横向滚动。页签下不重复同名标题,也不保留空标题容器;历史结果不得标为最新。
- 工作回顾只展示统一监督结果、历史选择和紧凑的新回顾控件;结果保留自身范围、覆盖区间和生成时间,图谱读取同一结果 ID。智能心跳集中展示心跳计划、监督建议和只读的历史心跳报告;活动记录只展示统一执行活动、进度和详情。每页保留所属数据的一处刷新入口,范围与时间选择仅作用于新回顾请求。详细布局与阅读规则见[监督者 UI 设计](docs/features/conversation-supervision/ui-design.md#应用入口)。
- 监督者保留统一页面壳层;工作回顾与活动子面板铺满壳层可用内容宽度,与页签左右对齐。工具栏、历史导航、状态和结果共享宽度,不再增加子面板宽度上限。工作回顾按纵向排列,空态独占工具栏下方整行并水平居中。活动筛选按内容紧邻排列,阶段使用编号圆点和连接线,次要统计收进详情;具体布局见[监督者 UI 设计](docs/features/conversation-supervision/ui-design.md)。
- 监督者保留统一页面壳层;工作回顾与活动子面板铺满壳层可用内容宽度,与页签左右对齐。工作回顾与图谱共用稳定的回顾上下文栏,新请求字段按需展开。工作回顾正文及辅助内容按实际容器宽度排列,空态独占上下文栏下方整行并水平居中。活动筛选按内容紧邻排列,阶段使用编号圆点和连接线,次要统计收进详情;具体布局见[监督者 UI 设计](docs/features/conversation-supervision/ui-design.md)。
- 范围明确区分 Global 与指定的一个或多个 Project;Global 与指定项目互斥,多项目选择使用
Checkbox,不能依靠进入页面时的当前项目推断。
- 状态卡片使用统一状态令牌,不只依赖颜色。
@@ -756,19 +761,18 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- 范围固定为当前项目,不提供全局或所有项目入口;切换会话不筛掉本项目其他会话的任务。
- 顶部紧凑显示当前会话消息总数和 Agent 累计回复时间,按每轮请求区间累加,包含轮内工具和
审批等待,不计轮间空闲。历史缺失显示不完整提示,远程缺少可靠证据时不虚构用时。
- 每项显示名称、关联 Conversation、所属项目、Ask/Execute、状态、最近进展、
- 每项显示名称、关联 Conversation、所属项目、状态、最近进展、
最近真实活动时间、累计执行时间及需要关注信息。任务用时汇总历次执行请求,不按父任务生命周期计算。
- 点击列表项打开关联 Conversation 并定位该 Task,不显示第二份内容载体。
- 项目任务标题右侧依次为“+”新建和筛选图标;图标按钮提供可访问名称,筛选按钮提供展开状态。
状态筛选默认收起且为全部;展开后用共享 `SegmentedControl` 提供全部、进行中、暂停、已结束,
窄栏不足时单行滚动。收起保留条件,非全部时图标高亮并显示条件。状态归类遵循[任务中心 PRD](docs/features/task-and-job/task-center-prd.md#5-交互)。
- 完整消息留在 Conversation;长错误、工具、审批和成果按 Task 关联到 Runtime、活动记录和
成果查看器,不撑高列表,也不显示 Job/Run 树。
会话消息或工作区文件,不撑高列表,也不显示 Job/Run 树。
- “新建定制任务”使用共享 Modal,默认立即执行和当前 Conversation,可选择本项目其他可用
Conversation 或新建会话,选择必须持续可见。内容必填,名称可选并从内容生成;定时提供单次、每日和每周。
- 定时消息创建 Modal 使用关联 Conversation 的 Runtime 与 Ask/Execute 模式,不再提供独立
模式开关。未运行计划的详情显示会话当前模式,已运行任务显示该次执行记录的模式;
不得用旧计划字段表示下一次执行权限。不支持工具时不得静默降级。
- 定时消息创建 Modal 使用关联 Conversation 的 Runtime,不提供模式开关。
详情显示状态、日程与 Runtime,不显示模式或从旧计划字段推断权限。不支持工具时说明真实限制。
### 13.6 运行记录
@@ -854,13 +858,15 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- “关于与更新”的更新源位于“启动时检查新版本”开关下方,常规宽度下将标签、同行帮助和原生单选下拉框放在同一行,用途说明收进 `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 Runtime 页面在低层程序与配置覆盖之外提供“能力与默认配置”区域。能力清单使用共享 `PageTabs`,按 Agents、Tools、Commands、Skills、MCP、Rules、Prompts、Resources、LSP、Formatters 和上下文 11 类单行滚动展示,一次只呈现当前分类的 `tabpanel`;清单只显示 Runtime 自有能力,不混入 GoodBuddy 分配的 Skills、临时 MCP 或 Continue 预设。Tools 必须独立于 Commands、LSP 和 Formatters,显示工具类型、来源及描述,不提供统一的原生逐工具开关;清单状态必须区分完整、部分、不可用、仅连接和不支持,不能用进程连通性冒充清单可读。
- “能力与默认配置”只显示一个模块标题,刷新入口位于该标题右侧,能力状态压缩为一行并排在默认 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 设置按“内置 MCP / 直连模型 / 自定义 MCP / 电脑控制”四个同级 `PageTabs` 组织。联网搜索保留在直连模型;内置浏览器归入内置 MCP,提供持久启停和 Runtime 分配,并明确说明其操作 GoodBuddy 隔离浏览器、不控制客户端已安装的浏览器,开启后可由已分配 Runtime 直接使用,不逐次询问。该开关只控制 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、离线、无工具能力或调用错误不改变勾选。图片模型默认关闭会话调用;默认生图模型独立于普通默认连接,关闭或删除后保留失效选择并提示改选,不自动回退。
- 内置 MCP 中的“图片生成与编辑”是自动管理例外:该行始终可见,标记“内置 · 自动管理”,不提供独立启停、分配保存、移除或凭据编辑。直连 Model、OpenCode、Continue、DeepSeek Harness 的分配只由已保存图片模型的“允许 AI 在会话中调用”设置推导;至少一个开启则全部勾选,没有开启模型则全部取消。使用禁用的原生 Checkbox、Runtime 标签及关联说明,保留可聚焦的“前往图片模型设置”导航;远程适用范围通过 GoodBuddy Agent 说明持续可见。离线、无工具能力或调用错误不改变勾选。图片模型默认关闭会话调用;默认生图模型独立于普通默认连接,关闭或删除后保留失效选择并提示改选,不自动回退。
“安全”设置分类及 `toolApproval` 策略已移除。“清除本地数据”位于“通用 > 平台功能”,使用既有危险操作按钮和二次确认;首次点击与取消均不得清除数据,确认后才调用 Main 清除入口。
### 13.9 文档解析设置
@@ -925,7 +931,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
- [ ] 实现 `ScopeBadge` 并覆盖全局、项目、失效和可切换状态。
- [ ] 实现 `EmptyState` 的首次为空、无结果、失败和只读变体。
- [ ] 实现 `danger-ghost`、`danger-solid` 和 `danger-zone`。
- [ ] 统一模型、专家角色和工作模式的单选菜单结构、视觉与键盘行为。
- [ ] 统一模型、专家角色和 Runtime 选项的单选菜单结构、视觉与键盘行为。
### 15.4 页面迁移
+1 -1
View File
@@ -1,6 +1,6 @@
{
"formatVersion": 1,
"agentVersion": "0.15.3",
"agentVersion": "0.15.6",
"protocol": {
"major": 2,
"minor": 0
+102 -8
View File
@@ -6,16 +6,17 @@
const { spawn } = require('node:child_process')
const { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } = require('node:fs')
const { createServer } = require('node:http')
const { tmpdir } = require('node:os')
const { join, resolve } = require('node:path')
const root = resolve(__dirname, '..')
const runnerRoot = resolve(__dirname, '..')
const root = resolve(process.env.GB_PERF_ROOT || runnerRoot)
const concurrentMode = process.env.GB_PERF_MODE?.startsWith('C')
// 11434 is the fresh-profile default endpoint. Another GoodBuddy (or Ollama)
// may already use it; GB_PERF_MODEL_PORT moves the fake model and points the
// isolated profile at it through the GOODBUDDY_MODEL_* environment variables.
const defaultModelPort = 11434
const modelPort = Number(process.env.GB_PERF_MODEL_PORT || defaultModelPort)
const totalTimeoutMs = Number(process.env.GB_PERF_TIMEOUT_MS || 600_000)
const totalTimeoutMs = Number(process.env.GB_PERF_TIMEOUT_MS || (process.env.GB_PERF_MODE === 'C06' ? 900_000 : 600_000))
const keepProfile = process.env.GB_PERF_KEEP_PROFILE === '1'
const streamBytes = Number(process.env.GB_PERF_STREAM_BYTES || 40_000)
const chunkChars = Number(process.env.GB_PERF_CHUNK_CHARS || 12)
@@ -55,6 +56,11 @@ function startModelServer(ledger) {
response.end(JSON.stringify({ object: 'list', data: [{ id: 'qwen3', object: 'model' }] }))
return
}
if (request.method === 'GET' && request.url === '/c03-knowledge.txt') {
response.writeHead(200, { 'content-type': 'text/plain; charset=utf-8' })
response.end('C03 deterministic knowledge source\n'.repeat(80))
return
}
if (request.method !== 'POST' || !request.url?.endsWith('/chat/completions')) {
response.writeHead(404).end()
return
@@ -67,6 +73,75 @@ function startModelServer(ledger) {
const messages = Array.isArray(parsed.messages) ? parsed.messages : []
const last = messages.at(-1)
const lastText = typeof last?.content === 'string' ? last.content : JSON.stringify(last?.content ?? '')
if (lastText.includes('OUTPUT CONTRACT:') && lastText.includes('BOUNDED EVIDENCE:')) {
const evidenceText = lastText.split('BOUNDED EVIDENCE:\n\n')[1]?.split('\n\nReturn only JSON.')[0]
let sourceId = 'missing-source'
try { sourceId = JSON.parse(evidenceText)?.[0]?.id || sourceId } catch { /* invalid fixture prompt is rejected by the production path */ }
const navigation = lastText.includes('navigation summaries')
const output = JSON.stringify({
summary: 'Deterministic concurrent acceptance review', changeDigest: 'fixture', openItems: [],
events: navigation ? [] : [{ title: 'Acceptance event', description: 'Deterministic review event', occurredAt: '2026-10-07T00:00:00.000Z', eventType: 'milestone', entityIds: ['acceptance'], sourceReferenceIds: [sourceId] }],
entities: navigation ? [] : [{ id: 'acceptance', label: 'Acceptance', description: 'Deterministic fixture', sourceReferenceIds: [sourceId] }],
entityChanges: [], relations: []
})
entry.kind = 'supervision-deterministic'
response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache', connection: 'keep-alive' })
const chunk = payload => response.write(`data: ${JSON.stringify(payload)}\n\n`)
chunk({ id: 'supervision', object: 'chat.completion.chunk', model: 'qwen3', choices: [{ index: 0, delta: { role: 'assistant', content: output }, finish_reason: null }] })
chunk({ id: 'supervision', object: 'chat.completion.chunk', model: 'qwen3', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] })
response.end('data: [DONE]\n\n')
entry.finishedAt = Date.now()
return
}
if (concurrentMode && lastText.includes('gb-fault:hang')) {
entry.kind = 'fault-hung'
entry.identity = 'fault-hung'
entry.stream = true
entry.aborted = false
// Keep the production request in flight until the UI cancels it.
response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' })
const timer = setTimeout(() => {
if (!response.destroyed) response.end('data: [DONE]\n\n')
}, 120_000)
response.on('close', () => { clearTimeout(timer); entry.aborted = !response.writableFinished; entry.finishedAt = Date.now() })
return
}
if (concurrentMode && messages.some(message => typeof message.content === 'string' && message.content.includes('gb-concurrent:'))) {
const marker = messages.map(message => typeof message.content === 'string' ? message.content : '').join('\n').match(/gb-concurrent:(parent|child):([a-zA-Z0-9-]+)/)
const child = marker?.[1] === 'child'
const toolsDone = messages.some(message => message.role === 'tool')
entry.kind = child ? 'concurrent-child' : 'concurrent-parent'
entry.identity = marker?.[2]
entry.stream = parsed.stream === true
const durationMs = child ? 12_000 : toolsDone ? 1_000 : 12_000
const count = Math.ceil(durationMs / 16)
const calls = !child && !toolsDone ? [0, 1].map(index => ({ index, id: `child-${marker?.[2]}-${index}`, type: 'function', function: { name: 'subagent_delegate', arguments: JSON.stringify({ task: `gb-concurrent:child:${marker?.[2]}-${index} deterministic output` }) } })) : []
entry.offeredFragments = count
entry.offeredToolCalls = calls.length
entry.chunks = 0
entry.bytes = 0
entry.latenessMs = []
response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' })
const send = (delta, finish_reason = null) => response.write(`data: ${JSON.stringify({ id: entry.identity, object: 'chat.completion.chunk', model: 'qwen3', choices: [{ index: 0, delta, finish_reason }] })}\n\n`)
const started = performance.now()
const tick = () => {
if (response.destroyed) { entry.aborted = true; entry.settledAt = Date.now(); return }
while (entry.chunks < count && performance.now() >= started + entry.chunks * 16) {
entry.latenessMs.push(performance.now() - started - entry.chunks * 16)
const text = `[${entry.identity}:${String(entry.chunks).padStart(5, '0')}] sample\n`
send({ content: text })
entry.bytes += Buffer.byteLength(text)
entry.chunks++
}
if (entry.chunks < count) { setTimeout(tick, 4); return }
if (calls.length) send({ tool_calls: calls })
send({}, calls.length ? 'tool_calls' : 'stop')
response.end('data: [DONE]\n\n')
entry.finishedAt = Date.now()
}
tick()
return
}
const benchmark = lastText.includes('perf-stream')
entry.kind = benchmark ? 'benchmark-stream' : 'auxiliary'
entry.stream = parsed.stream === true
@@ -153,10 +228,27 @@ function printSummary(report) {
}
async function main() {
const taskDirectory = join(runnerRoot, 'temp', 'goodbuddy-concurrent-upgrade')
mkdirSync(taskDirectory, { recursive: true })
if (process.argv.includes('--launch')) {
const { openSync, closeSync } = require('node:fs')
const output = resolve(process.env.GB_PERF_OUTPUT || join(taskDirectory, `run-${Date.now()}`))
mkdirSync(output, { recursive: true })
const log = openSync(join(output, 'execution.log'), 'w')
const child = spawn(process.execPath, [__filename], {
detached: true, stdio: ['ignore', log, log],
env: { ...process.env, GB_PERF_OUTPUT: output }
})
closeSync(log)
writeFileSync(join(output, 'supervisor.json'), JSON.stringify({ pid: child.pid, startedAt: new Date().toISOString(), root, mode: process.env.GB_PERF_MODE, timeoutMs: totalTimeoutMs }))
child.unref()
console.log(`Supervisor ${child.pid}; logs ${output}`)
return
}
const required = ['out/main/index.js', 'out/preload/index.cjs', 'out/renderer/index.html']
const missing = required.filter(file => !existsSync(join(root, file)))
if (missing.length) throw new Error(`Missing production build (${missing.join(', ')}); run npm run build:bundle first`)
const runDirectory = mkdtempSync(join(tmpdir(), 'goodbuddy-app-perf-'))
const runDirectory = mkdtempSync(join(taskDirectory, 'profile-'))
const outputDirectory = resolve(process.env.GB_PERF_OUTPUT || join(runDirectory, 'artifacts'))
mkdirSync(outputDirectory, { recursive: true })
const ledger = []
@@ -166,7 +258,8 @@ async function main() {
} catch (error) {
throw new Error(`Cannot listen on 127.0.0.1:${modelPort} for the fake model (${error.code ?? error.message}); stop the local service using that port, or set GB_PERF_MODEL_PORT to a free port`, { cause: error })
}
const environment = { ...process.env, GB_PERF_DIRECTORY: runDirectory, GB_PERF_ARTIFACTS: outputDirectory, GB_PERF_ROOT: root }
const environment = { ...process.env, GB_PERF_DIRECTORY: runDirectory, GB_PERF_ARTIFACTS: outputDirectory, GB_PERF_ROOT: root, TEMP: runDirectory, TMP: runDirectory, TMPDIR: runDirectory }
if (concurrentMode) environment.GB_PERF_DRIVER_TIMEOUT_MS = String(totalTimeoutMs - 30_000)
delete environment.ELECTRON_RUN_AS_NODE
delete environment.ELECTRON_RENDERER_URL
for (const key of ['GOODBUDDY_MODEL_API_KEY', 'GOODBUDDY_MODEL_BASE_URL', 'GOODBUDDY_MODEL_NAME', 'GOODBUDDY_BIGTOKEN_API_KEY', 'GOODBUDDY_BIGTOKEN_BASE_URL', 'GOODBUDDY_BIGTOKEN_MODEL']) delete environment[key]
@@ -179,7 +272,8 @@ async function main() {
environment.GOODBUDDY_WORKSPACE = join(runDirectory, 'workspace')
mkdirSync(environment.GOODBUDDY_WORKSPACE, { recursive: true })
const started = Date.now()
const child = spawn(require('electron'), [join(root, 'tests', 'support', 'app-perf-driver.mjs')], { cwd: root, env: environment, stdio: 'inherit', windowsHide: false })
const child = spawn(require('electron'), [join(runnerRoot, 'tests', 'support', 'app-perf-driver.mjs')], { cwd: root, env: environment, stdio: 'inherit', windowsHide: false })
writeFileSync(join(outputDirectory, 'owned-processes.json'), JSON.stringify({ supervisorPid: process.pid, electronPid: child.pid, root, runDirectory, startedAt: new Date().toISOString() }))
let timedOut = false
const timer = setTimeout(() => {
timedOut = true
@@ -200,13 +294,13 @@ async function main() {
total: ledger.length,
benchmarkStreams: ledger.filter(entry => entry.kind === 'benchmark-stream').length,
auxiliary: ledger.filter(entry => entry.kind === 'auxiliary').length,
ledger: ledger.map(({ method, path, kind, stream, chunks, bytes, aborted, startedAt, finishedAt }) => ({ method, path, kind, stream, chunks, bytes, aborted, durationMs: finishedAt ? finishedAt - startedAt : null }))
ledger: ledger.map(({ method, path, kind, stream, chunks, bytes, aborted, startedAt, finishedAt, identity, offeredFragments, offeredToolCalls, latenessMs }) => ({ method, path, kind, stream, chunks, bytes, aborted, startedAt, finishedAt, identity, offeredFragments, offeredToolCalls, latenessMs, durationMs: finishedAt ? finishedAt - startedAt : null }))
}
if (timedOut || exit.code !== 0) report.status = 'failed'
writeFileSync(resultPath, JSON.stringify(report, null, 2))
printSummary(report)
console.log(`\nreport: ${resultPath}`)
if (!keepProfile) rmSync(join(runDirectory, 'profile'), { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
if (!keepProfile) rmSync(runDirectory, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
process.exitCode = String(report.status).startsWith('passed') ? 0 : 1
// PERF-17: GB_PERF_CHECK=1 gates the run on build/perf-thresholds.json
// (exit 1 on a regression, 2 when the report cannot be judged, e.g. throttled).
+25 -13
View File
@@ -1,12 +1,12 @@
// Knowledge retrieval benchmark (PERF-05). Bundles the production
// Owner-local knowledge/read-worker microbenchmark (PERF-05), not a Main IPC probe.
// Bundles the production
// KnowledgeDatabase with esbuild, seeds synthetic libraries of increasing size
// in a temporary SQLite file and times the synchronous search calls that the
// Electron Main process runs inline during retrieval. Because these calls are
// synchronous, their duration is the time Main is blocked per query.
// storage owner runs during retrieval. Synchronous duration measures this harness's
// event loop, not the Electron Main process of the application.
const { buildSync } = require('esbuild')
const { createHash } = require('node:crypto')
const { mkdtempSync, rmSync, writeFileSync } = require('node:fs')
const { tmpdir } = require('node:os')
const { mkdtempSync, rmSync, writeFileSync, mkdirSync } = require('node:fs')
const { join, resolve } = require('node:path')
const { monitorEventLoopDelay, performance } = require('node:perf_hooks')
@@ -31,12 +31,16 @@ const dimensions = Number(process.env.GB_KPERF_DIMENSIONS || 384)
const chunksPerDocument = 100
const queries = Number(process.env.GB_KPERF_QUERIES || 15)
const directory = mkdtempSync(join(tmpdir(), 'goodbuddy-knowledge-perf-'))
const temporaryRoot = join(root, 'temp', 'goodbuddy-storage-foundation')
mkdirSync(temporaryRoot, { recursive: true })
const directory = mkdtempSync(join(temporaryRoot, 'knowledge-perf-'))
const bundle = join(directory, 'knowledge-database.cjs')
const workerBundle = join(directory, 'readonly-query-worker.cjs')
const readerBundle = join(directory, 'readonly-query-reader.cjs')
for (const [entry, outfile] of [
['src/main/knowledge/knowledge-database.ts', bundle],
['src/main/readonly-query-worker.ts', workerBundle]
['src/main/readonly-query-worker.ts', workerBundle],
['src/main/readonly-query-reader.ts', readerBundle]
]) {
buildSync({
entryPoints: [join(root, entry)],
@@ -51,6 +55,8 @@ for (const [entry, outfile] of [
})
}
const { KnowledgeDatabase } = require(bundle)
const { ReadonlyQueryReader } = require(readerBundle)
const ownedDatabases = new Set(), readers = new Set()
// Deterministic pseudo-random generator so runs are comparable.
let seed = 42
@@ -76,7 +82,7 @@ function time(body) {
}
const round = value => Math.round(value * 10) / 10
// Main event-loop delay while `queries` searches run back to back. A sync
// Harness event-loop delay while `queries` searches run back to back. A sync
// search blocks the loop for its whole duration; a worker search should not.
async function loopDelay(body) {
const histogram = monitorEventLoopDelay({ resolution: 5 })
@@ -103,6 +109,7 @@ async function main() {
for (const size of sizes) {
const path = join(directory, `knowledge-${size}.sqlite`)
const database = new KnowledgeDatabase(path)
ownedDatabases.add(database)
database.initialize()
const library = database.createKnowledgeBase({ name: `Perf ${size}`, storageMode: 'reference' })
const sqlite = database.database
@@ -119,6 +126,7 @@ async function main() {
// Reopen so timings start from a fresh connection, like an app restart.
const reopened = new KnowledgeDatabase(path)
ownedDatabases.add(reopened)
reopened.initialize()
const query = vector()
const entry = {
@@ -129,20 +137,24 @@ async function main() {
vectorMs: time(() => reopened.vectorSearch({ knowledgeBaseId: library.id, provider: 'perf', model: 'perf-model', vector: query, limit: 40 })),
hybridMs: time(() => reopened.hybridSearchWithDiagnostics(hybridOptions(library.id, query)))
}
// Main event-loop delay: synchronous search on Main vs the readonly worker.
// Harness loop delay: owner-local synchronous search vs its explicit read worker.
entry.mainLoopDelaySyncMs = await loopDelay(async () => reopened.hybridSearchWithDiagnostics(hybridOptions(library.id, query)))
reopened.enableReadonlyWorker(workerBundle)
entry.mainLoopDelayWorkerMs = await loopDelay(() => reopened.hybridSearchWithDiagnosticsAsync(hybridOptions(library.id, query)))
const reader = new ReadonlyQueryReader('knowledge', path, workerBundle)
readers.add(reader)
entry.mainLoopDelayWorkerMs = await loopDelay(() => reader.call('hybridSearchWithDiagnostics', [hybridOptions(library.id, query)]))
await reader.close()
reopened.close()
results.push(entry)
console.log(`[kperf] ${size} chunks: fts p95 ${entry.ftsMs.p95} ms, vector p95 ${entry.vectorMs.p95} ms, hybrid p95 ${entry.hybridMs.p95} ms (seed ${entry.seedSeconds} s)`)
console.log(`[kperf] ${size} chunks: Main loop delay max sync ${entry.mainLoopDelaySyncMs.max} ms vs worker ${entry.mainLoopDelayWorkerMs.max} ms (p99 ${entry.mainLoopDelaySyncMs.p99} / ${entry.mainLoopDelayWorkerMs.p99} ms; per query ${entry.mainLoopDelaySyncMs.perQueryMs} / ${entry.mainLoopDelayWorkerMs.perQueryMs} ms)`)
}
const output = process.env.GB_KPERF_OUTPUT
if (output) writeFileSync(output, JSON.stringify({ kind: 'goodbuddy-knowledge-retrieval-benchmark', node: process.version, results }, null, 2))
if (output) writeFileSync(output, JSON.stringify({ kind: 'goodbuddy-knowledge-retrieval-benchmark', executionBoundary: 'owner-local', node: process.version, results }, null, 2))
console.table(results.map(r => ({ chunks: r.chunks, 'fts p50/p95': `${r.ftsMs.p50} / ${r.ftsMs.p95}`, 'vector p50/p95': `${r.vectorMs.p50} / ${r.vectorMs.p95}`, 'hybrid p50/p95': `${r.hybridMs.p50} / ${r.hybridMs.p95}`, 'loop max sync/worker': `${r.mainLoopDelaySyncMs.max} / ${r.mainLoopDelayWorkerMs.max}` })))
}
const hybridOptions = (knowledgeBaseId, vector) => ({ knowledgeBaseId, query: 'lighthouse budget', limit: 40, provider: 'perf', model: 'perf-model', vector, graphEnabled: false, candidateMultiplier: 1 })
main().catch(error => { process.exitCode = 1; console.error(error) }).finally(() => {
main().catch(error => { process.exitCode = 1; console.error(error) }).finally(async () => {
await Promise.all([...readers].map(reader => reader.close()))
for (const database of ownedDatabases) database.close()
rmSync(directory, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
})
+25 -14
View File
@@ -1,10 +1,11 @@
// Main-process database benchmark (PERF-15). Bundles the production
// Owner-local database/read-worker microbenchmark (PERF-15). Not a Main IPC latency probe.
// Bundles the production
// AssistantDatabase and readonly worker with esbuild, seeds a temporary
// assistant database (default 1,000 conversations / 20,000 messages and 24,000
// activity records) and measures, for the IPC paths the renderer hits often:
// - per-call time
// - Main event-loop delay while the calls run back to back
// for the previous synchronous path ("sync") and the current path ("after").
// - harness event-loop delay while the calls run back to back
// for owner-local SQL ("sync") and explicit read-worker dispatch ("after").
// conversations:list-summaries sync listConversationSummaries vs worker
// conversations:get sync getConversation vs worker
// activity-history:replace legacy replace (zod parse + BEGIN IMMEDIATE +
@@ -17,7 +18,6 @@
// GB_MPERF_CALLS, GB_MPERF_OUTPUT (JSON), GB_MPERF_NODE=1 (skip Electron).
const { buildSync } = require('esbuild')
const { mkdtempSync, rmSync, writeFileSync, mkdirSync } = require('node:fs')
const { tmpdir } = require('node:os')
const { join, resolve } = require('node:path')
const { randomUUID } = require('node:crypto')
const { monitorEventLoopDelay, performance } = require('node:perf_hooks')
@@ -41,13 +41,17 @@ const messageCount = Number(process.env.GB_MPERF_MESSAGES || 20000)
const activityCount = Number(process.env.GB_MPERF_ACTIVITY || 24000)
const calls = Number(process.env.GB_MPERF_CALLS || 15)
const directory = mkdtempSync(join(tmpdir(), 'goodbuddy-main-perf-'))
const temporaryRoot = join(root, 'temp', 'goodbuddy-storage-foundation')
mkdirSync(temporaryRoot, { recursive: true })
const directory = mkdtempSync(join(temporaryRoot, 'main-perf-'))
const bundle = join(directory, 'assistant-database.cjs')
const workerBundle = join(directory, 'readonly-query-worker.cjs')
const schemaBundle = join(directory, 'assistant-contracts.cjs')
const readerBundle = join(directory, 'readonly-query-reader.cjs')
for (const [entry, outfile] of [
['src/main/assistant/assistant-database.ts', bundle],
['src/main/readonly-query-worker.ts', workerBundle],
['src/main/readonly-query-reader.ts', readerBundle],
['src/shared/assistant-contracts.ts', schemaBundle]
]) {
buildSync({
@@ -56,6 +60,8 @@ for (const [entry, outfile] of [
})
}
const { AssistantDatabase } = require(bundle)
const { ReadonlyQueryReader } = require(readerBundle)
let ownedDatabase, reader
const { activityHistorySnapshotSchema } = require(schemaBundle)
let seed = 42
@@ -70,7 +76,7 @@ const percentile = (values, p) => {
/**
* Runs `body` `calls` times (after one warm-up) and reports the per-call wall
* time and the Main event-loop delay histogram (p99/max) over the run.
* time and the harness event-loop delay histogram (p99/max) over the run.
*/
async function measure(body) {
await body(-1)
@@ -154,6 +160,7 @@ async function main() {
mkdirSync(workspace)
const path = join(directory, 'assistant.sqlite')
const seeding = new AssistantDatabase(path)
ownedDatabase = seeding
seeding.initialize(workspace)
const seedStarted = performance.now()
const projectId = seeding.listProjects()[0].id
@@ -186,6 +193,7 @@ async function main() {
seeding.close()
const database = new AssistantDatabase(path)
ownedDatabase = database
database.initialize(workspace)
const raw = database.database
const totalMessages = counts.reduce((sum, value) => sum + value, 0)
@@ -199,10 +207,10 @@ async function main() {
results.listSummariesSync = await measure(async () => database.listConversationSummaries(detailIds))
results.getLargestSync = await measure(async () => database.getConversation(largest))
results.getTypicalSync = await measure(async () => database.getConversation(typical))
database.enableReadonlyWorker(workerBundle)
results.listSummariesWorker = await measure(() => database.listConversationSummariesAsync(detailIds))
results.getLargestWorker = await measure(() => database.getConversationAsync(largest))
results.getTypicalWorker = await measure(() => database.getConversationAsync(typical))
reader = new ReadonlyQueryReader('assistant', path, workerBundle)
results.listSummariesWorker = await measure(() => reader.call('listConversationSummaries', [detailIds]))
results.getLargestWorker = await measure(() => reader.call('getConversation', [largest]))
results.getTypicalWorker = await measure(() => reader.call('getConversation', [typical]))
// The renderer sends a fresh structured clone of the whole list every save.
const unchanged = () => structuredClone({ records, legacyHistoryMayBeIncomplete: false })
@@ -236,11 +244,12 @@ async function main() {
results.activityFirstPageSync = await measure(async () => database.getActivityHistoryPage(firstPageRequest))
results.activitySummarySync = await measure(async () => database.getActivityHistorySummary(summaryRequest()))
results.activityStartupWorker = await measure(async () => {
const page = await database.getActivityHistoryPageAsync(firstPageRequest)
await database.getActivityHistorySummaryAsync({ conversationIds: [...new Set(page.records.map(record => record.conversationId))] })
const page = await reader.call('activityHistoryPage', [firstPageRequest])
await reader.call('activityHistorySummary', [{ conversationIds: [...new Set(page.records.map(record => record.conversationId))] }])
})
results.activityFullLoadBytes = JSON.stringify(database.getActivityHistory()).length
results.activityFirstPageBytes = JSON.stringify(database.getActivityHistoryPage(firstPageRequest)).length
await reader.close()
database.close()
console.log(`[mperf] seeded ${results.conversations} conversations / ${results.messages} messages (largest ${results.largestConversationMessages}), ${results.activityRecords} activity records in ${seedSeconds} s`)
@@ -264,9 +273,11 @@ async function main() {
})))
console.log(`[mperf] activity payload: full list ${results.activityFullLoadBytes} bytes, first page ${results.activityFirstPageBytes} bytes`)
const output = process.env.GB_MPERF_OUTPUT
if (output) writeFileSync(output, JSON.stringify({ kind: 'goodbuddy-main-database-benchmark', node: process.version, results }, null, 2))
if (output) writeFileSync(output, JSON.stringify({ kind: 'goodbuddy-main-database-benchmark', executionBoundary: 'owner-local', node: process.version, results }, null, 2))
}
main().catch(error => { process.exitCode = 1; console.error(error) }).finally(() => {
main().catch(error => { process.exitCode = 1; console.error(error) }).finally(async () => {
await reader?.close()
ownedDatabase?.close()
rmSync(directory, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
})
+1 -1
View File
@@ -66,7 +66,7 @@ if (!process.versions.electron || process.env.ELECTRON_RUN_AS_NODE === '1') {
await wait(500)
}
if (chat) {
await js(`(async()=>{const p=(await window.goodbuddy.projects.list())[0];await window.goodbuddy.projects.update(p.id,{name:p.name,description:p.description,rootPath:${JSON.stringify(join(root,'chat-workspace'))},defaultWorkMode:'execute',runtimeSelection:{provider:'opencode'}})})()`)
await js(`(async()=>{const p=(await window.goodbuddy.projects.list())[0];await window.goodbuddy.projects.update(p.id,{name:p.name,description:p.description,rootPath:${JSON.stringify(join(root,'chat-workspace'))},runtimeSelection:{provider:'opencode'}})})()`)
window.webContents.reload()
await wait(1000)
}
+78
View File
@@ -0,0 +1,78 @@
// Windows logical-I/O probe against an isolated copy of a quiescent data folder.
// GB_WPERF_DATA must contain assistant.sqlite. No source database is opened.
const { buildSync } = require('esbuild')
const { mkdtempSync, copyFileSync, cpSync, existsSync, rmSync } = require('node:fs')
const { tmpdir } = require('node:os')
const { join, resolve } = require('node:path')
const { randomUUID } = require('node:crypto')
const { performance } = require('node:perf_hooks')
const { execFileSync, spawnSync } = require('node:child_process')
const { deepStrictEqual } = require('node:assert')
if (process.platform !== 'win32') throw new Error('This probe uses Windows process I/O counters')
if (!process.versions.electron) {
const result = spawnSync(require('electron'), [__filename], {
stdio: 'inherit', env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' }
})
process.exit(result.status ?? 1)
}
const io = () => JSON.parse(execFileSync('powershell.exe', ['-NoProfile', '-Command',
`Get-CimInstance Win32_Process -Filter 'ProcessId=${process.pid}' | Select-Object ReadTransferCount,WriteTransferCount | ConvertTo-Json -Compress`
], { encoding: 'utf8' }))
const source = process.env.GB_WPERF_DATA
if (!source || !existsSync(join(source, 'assistant.sqlite'))) throw new Error('Set GB_WPERF_DATA')
const directory = mkdtempSync(join(tmpdir(), 'goodbuddy-write-io-'))
let database
try {
const path = join(directory, 'assistant.sqlite')
for (const suffix of ['', '-wal', '-shm']) {
if (existsSync(join(source, `assistant.sqlite${suffix}`))) {
copyFileSync(join(source, `assistant.sqlite${suffix}`), `${path}${suffix}`)
}
}
if (existsSync(join(source, 'notes'))) cpSync(join(source, 'notes'), join(directory, 'notes'), { recursive: true })
const bundle = join(directory, 'database.cjs')
buildSync({ entryPoints: [resolve(__dirname, '../src/main/assistant/assistant-database.ts')],
outfile: bundle, bundle: true, platform: 'node', format: 'cjs', external: ['electron'], logLevel: 'error' })
const { AssistantDatabase } = require(bundle)
database = new AssistantDatabase(path)
database.initialize(directory)
const raw = database.database
const largest = raw.prepare(`SELECT id, conversation_id, length(metadata_json) AS bytes
FROM messages ORDER BY length(metadata_json) DESC LIMIT 1`).get()
if (!largest) throw new Error('No messages')
const snapshot = database.getConversation(largest.conversation_id)
const message = snapshot.messages.find(item => item.id === largest.id)
// Summary reads must remain small even while a large history is streaming.
database.saveLocalConversations([{ header: { ...snapshot, messages: undefined },
messages: [{ ...message, state: 'streaming' }] }])
const taskId = randomUUID()
database.createTask({ id: taskId, projectId: snapshot.projectId,
conversationId: snapshot.id, title: 'I/O probe', instructions: '', visible: false })
database.startExecutionTiming(taskId)
raw.exec('PRAGMA wal_checkpoint(TRUNCATE)')
console.log(JSON.stringify({ schema: raw.prepare('PRAGMA user_version').get(), metadataBytes: largest.bytes }))
const results = []
for (const [variant, query, count] of [
['read-conversation', () => database.getConversation(snapshot.id), 1],
['read-summary', () => database.listConversationSummaries([]), 1],
['conversation-timing', () => database.getExecutionStats({ conversationId: snapshot.id }), 20],
...(snapshot.projectId ? [['project-timing', () => database.getExecutionStats({ projectId: snapshot.projectId }), 20]] : [])
]) {
const before = io()
const start = performance.now()
for (let index = 0; index < count; index++) query()
const elapsedMs = Math.round(performance.now() - start)
const after = io()
results.push({ variant, calls: count, elapsedMs,
readMiB: Number(((after.ReadTransferCount - before.ReadTransferCount) / 1024 / 1024).toFixed(3)),
writeMiB: Number(((after.WriteTransferCount - before.WriteTransferCount) / 1024 / 1024).toFixed(3)) })
}
database.endExecutionTiming(taskId)
deepStrictEqual(database.getConversation(snapshot.id).messages,
snapshot.messages.map(item => item.id === message.id ? { ...item, state: 'streaming' } : item))
console.table(results)
} finally {
database?.close()
rmSync(directory, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
}
+21 -11
View File
@@ -1,8 +1,8 @@
// Main-process database WRITE benchmark on a real data directory (PERF-15).
// Owner-local database WRITE microbenchmark on a closed data copy (PERF-15).
// Copies assistant.sqlite (+ -wal/-shm) from GB_WPERF_DATA (for example the
// portable build's data folder) into a temporary directory - the source is
// never opened - then measures, with the production AssistantDatabase, the
// writes Main performs synchronously while a run streams:
// synchronous repository writes. This is not a production Main IPC latency probe:
// task-events:subagent replay of the largest real subagent progress stream
// (restored events, re-compacted on write, as live)
// task-events:tool replay of real tool events
@@ -10,14 +10,14 @@
// conversations:save the renderer's 500 ms local save (header + the
// streaming assistant message) for a real conversation
// activity:update one incremental activity change
// For each: per-call time and the longest Main event-loop block.
// For each: per-call time and the longest owner-local harness event-loop block.
// Event replays are paced at GB_WPERF_RATE events/s (default 270, the busiest
// second in real data; 0 = back to back).
// Env: GB_WPERF_DATA (required), GB_WPERF_EVENTS (default 2000),
// GB_WPERF_OUTPUT (JSON), GB_WPERF_KEEP=1 (keep the copy).
const { buildSync } = require('esbuild')
const { mkdtempSync, rmSync, writeFileSync, mkdirSync, copyFileSync, cpSync, existsSync } = require('node:fs')
const { tmpdir } = require('node:os')
const { Worker } = require('node:worker_threads')
const { join, resolve } = require('node:path')
const { randomUUID } = require('node:crypto')
const { performance } = require('node:perf_hooks')
@@ -43,7 +43,10 @@ if (!source || !existsSync(join(source, 'assistant.sqlite'))) {
}
const eventLimit = Number(process.env.GB_WPERF_EVENTS || 2000)
const directory = mkdtempSync(join(tmpdir(), 'goodbuddy-write-perf-'))
const temporaryRoot = join(root, 'temp', 'goodbuddy-storage-foundation')
mkdirSync(temporaryRoot, { recursive: true })
const directory = mkdtempSync(join(temporaryRoot, 'write-perf-'))
let ownedDatabase, checkpoint, checkpointExit
const bundle = join(directory, 'assistant-database.cjs')
const progressBundle = join(directory, 'subagent-progress.cjs')
for (const [entry, outfile] of [
@@ -115,6 +118,7 @@ async function main() {
const workspace = join(directory, 'workspace')
mkdirSync(workspace)
const database = new AssistantDatabase(join(directory, 'assistant.sqlite'))
ownedDatabase = database
const openStarted = performance.now()
database.initialize(workspace)
const raw = database.database
@@ -124,9 +128,12 @@ async function main() {
results.synchronous = raw.prepare('PRAGMA synchronous').get().synchronous
if (process.env.GB_WPERF_AUTOCHECKPOINT) raw.exec(`PRAGMA wal_autocheckpoint = ${process.env.GB_WPERF_AUTOCHECKPOINT}`)
// As in production: checkpoints run on a worker thread (GB_WPERF_CHECKPOINT_WORKER=0 disables).
if (process.env.GB_WPERF_CHECKPOINT_WORKER !== '0' && typeof database.enableWalCheckpointWorker === 'function') {
database.enableWalCheckpointWorker(join(directory, 'readonly-query-worker.cjs'))
await new Promise(resolve => setTimeout(resolve, 500))
if (process.env.GB_WPERF_CHECKPOINT_WORKER !== '0') {
checkpoint = new Worker(join(directory, 'readonly-query-worker.cjs'), {
workerData: { kind: 'checkpoint', databasePath: join(directory, 'assistant.sqlite'), intervalMs: 250 }
})
checkpointExit = new Promise(resolve => checkpoint.once('exit', resolve))
await new Promise((resolve, reject) => { checkpoint.once('message', resolve); checkpoint.once('error', reject) })
}
results.walAutocheckpoint = raw.prepare('PRAGMA wal_autocheckpoint').get().wal_autocheckpoint
const count = (sql, ...args) => Number(raw.prepare(sql).get(...args).n)
@@ -150,7 +157,7 @@ async function main() {
`SELECT conversation_id AS id FROM messages GROUP BY conversation_id ORDER BY COUNT(*) DESC LIMIT 1`
).get()?.id
const taskId = randomUUID()
database.createTask({ id: taskId, title: 'write perf', instructions: 'replay', workMode: 'ask', conversationId: conversation })
database.createTask({ id: taskId, title: 'write perf', instructions: 'replay', conversationId: conversation })
const replay = (events, kind) => index => {
const event = { ...events[index % events.length], requestId: taskId }
@@ -208,6 +215,7 @@ async function main() {
}
results.walBytes = existsSync(join(directory, 'assistant.sqlite-wal'))
? require('node:fs').statSync(join(directory, 'assistant.sqlite-wal')).size : 0
if (checkpoint) { checkpoint.postMessage({ type: 'close' }); await checkpointExit; checkpoint = undefined }
database.close()
console.log(`[wperf] ${results.taskEvents} task events, ${results.messages} messages, ${results.activityRecords} activity records; open ${results.openMs} ms`)
@@ -226,10 +234,12 @@ async function main() {
for (const [name, r] of rows) if (r.slowCalls.length) console.log(`[wperf] slow calls (index, ms) ${name}: ${JSON.stringify(r.slowCalls)}`)
console.log(`[wperf] synchronous=${results.synchronous} wal_autocheckpoint=${results.walAutocheckpoint} wal=${results.walBytes} B`)
const output = process.env.GB_WPERF_OUTPUT
if (output) writeFileSync(output, JSON.stringify({ kind: 'goodbuddy-main-write-benchmark', node: process.version, results }, null, 2))
if (output) writeFileSync(output, JSON.stringify({ kind: 'goodbuddy-main-write-benchmark', executionBoundary: 'owner-local', node: process.version, results }, null, 2))
}
main().catch(error => { process.exitCode = 1; console.error(error) }).finally(() => {
main().catch(error => { process.exitCode = 1; console.error(error) }).finally(async () => {
if (checkpoint) { checkpoint.postMessage({ type: 'close' }); await checkpointExit }
ownedDatabase?.close()
if (process.env.GB_WPERF_KEEP === '1') console.log(`[wperf] kept ${directory}`)
else rmSync(directory, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 })
})
@@ -0,0 +1,64 @@
# Assistant Storage Consumers
The S02 domain consumers use the typed port in
`src/main/assistant-storage-port.ts`. This implements the consumer side of
[Desktop Storage Foundation](./desktop-storage-foundation.md). Production
composition in `src/main/index.ts` supplies the client after storage readiness;
the consumer services do not construct an Assistant database.
## Main Integration
```ts
const storage = new DesktopStorageClient({
...paths,
onChanged: createAssistantStorageOnChanged({
onMagicNotesChanged,
onMagicTodosChanged,
onModelUsageChanged,
onExecutionStatsChanged
})
})
await storage.ready
const assistant = createAssistantStoragePort(storage)
```
`createAssistantStoragePort` binds only the explicit Assistant operation allowlist
and `supervisionContext`. Its return type is `AsyncAssistantStoragePort`: every
operation returns a Promise. There is no SQL, arbitrary property proxy, remote
callback or live repository API. `AssistantStoragePort` uses `Awaitable` results
so existing synchronous database fixtures can be injected into unit tests.
Domain change callbacks run in Main through `DesktopStorageClient.onChanged`.
The subagent service, native client coordinator, terminal manager, computer-control
audit sink, channel dedup/outbox, image service and Magic Notes IPC helpers accept
this port. Notes helpers retain sender validation, schema validation, source-message
checks and revision checks. Note analysis awaits the model-usage callback before
publishing its result. Channel and audit adapters return persistence completion to
their existing asynchronous callers.
Image service `initialize`, `persistUploads`, `getOperation`, `cancel`,
`cancelConversation`, `regenerate` and `dispose` return Promises. Await upload
persistence before submitting generation. Await cancellation and shutdown writes.
`onUsage` may return a Promise and must return the actual persistence operation.
Notification-only `onOperation` errors are reported without changing provider
outcomes; storage and usage-write failures reject instead of reporting durable
success. Shutdown reports retained persistence failures.
Subagent event and model-usage callbacks may return Promises. The service awaits
task creation, events and terminal status writes; shutdown also drains task
creation and queued cancellation writes that have not entered a scheduler slot.
Native client composition additionally forwards the storage-backed
`openModelCallLedger` factory to both browser and terminal clients.
Stop producers and await service disposal before closing `DesktopStorageClient`.
This consumer migration does not establish full desktop performance acceptance or
replace the separate Main/IPC composition and concurrent acceptance work.
## Validation
The 11 focused suites pass with 94 tests, including asynchronous image storage,
commit-before-notification, usage-write rejection, subagent shutdown during task
creation, channel delivery, audit persistence and IPC sender/schema checks.
Repository-wide type checking remains dependent on the concurrent Main/IPC,
Knowledge and runtime-storage integrations. No latency or full concurrent-desktop
acceptance claim is made by these unit and loopback tests.
@@ -84,10 +84,10 @@ SQLite、FTS 和可选本地向量已经足够支撑第一阶段。只有出现
初期自动学习只允许产生可审查候选,不允许自动修改:
- 工具权限和审批策略。
- 工具启用、分配与资源范围。
- Electron 安全边界。
- 项目根目录和数据访问范围。
- Runtime 当前用户执行权限与 Ask/Execute 边界。
- Runtime 当前用户执行权限与实际能力范围。
- 系统级提示词。
- 远程消息发送或其他外部副作用策略。
@@ -105,11 +105,11 @@ SQLite、FTS 和可选本地向量已经足够支撑第一阶段。只有出现
### 3.2 产品目标
- 复用现有 Project、Conversation、Task、Run、Artifact、Approval 和 Notification 能力。
- 复用现有 Project、Conversation、Task、Run、Artifact 和 Notification 能力。
- 保持每个 Task 只关联一条 Conversation,同时允许一条 Conversation 承载多个 Task,不为
同一项工作建立第二份内容载体。
- 为所有后台工作提供统一的幂等、租约、恢复、取消、预算和审计语义。
- 保持 Ask 只读,Execute 继续经过现有能力和审批控制。
- 沿用统一执行与能力配置,不引入工作模式或一般工具审批。
- 保持本地优先,应用退出后不虚假承诺后台持续执行。
- 保证项目、会话、自动化和实验 Run 之间的记忆隔离。
- 先建立可观测和可评估能力,再允许任何形式的自动行为改变。
@@ -120,7 +120,7 @@ SQLite、FTS 和可选本地向量已经足够支撑第一阶段。只有出现
- 将 GoodBuddy 变为需要常驻服务器、Redis 或云端控制面的多租户平台。
- 在应用退出后依靠未安装的系统服务继续运行任务。
- 默认允许无人值守高风险 Execute。
- 自动扩大无人值守任务的能力或资源范围。
- 让模型自行扩大工具、目录、知识库、记忆或网络访问范围。
- 允许多个实验 Run 并发修改同一个用户工作区。
- 记录键盘、持续录屏或静默监控其他应用。
@@ -197,13 +197,12 @@ Conversation
- Plan 版本。
- 项目和工作目录。
- Runtime 和模型配置引用。
- 工作模式。
- 提示和变量。
- 工具、Skills、MCP 和知识库范围。
- 可读、可写记忆分区。
- 监督策略和评估器版本。
- 预算和并发限制。
- 审批策略。
- 能力分配。
运行开始后的设置变化只影响下一次 Run。用户可以在 Task 执行记录中查看当次快照与最新
Plan 的差异,但 Run 不作为独立导航对象。
@@ -290,7 +289,7 @@ Runtime 边界完成,用户可见结果通过所属 Task 汇入关联 Conversa
当前实现以 Main 和 SQLite 中的 `conversation_queue_items` 作为每条 Conversation 的权威
输入队列,而不是在 Renderer 分别维护聊天草稿队列和 Scheduled Task 队列:
- 普通消息在发送时冻结 Runtime、工作模式、专家/团队、知识范围和附件上下文,再以
- 普通消息在发送时冻结 Runtime、专家/团队、知识范围和附件上下文,再以
`source=user` 入队;附件内容使用有界序列化保存,应用重启后仍可恢复。
- 到期或手动启动的 Scheduled Task 先建立 `schedule_run`,再以 `source=schedule` 进入同一
队列,只派发任务要求和关联会话身份。执行时读取会话最新历史和配置,不保存任务上下文快照。
@@ -432,7 +431,7 @@ Renderer 只通过显式 IPC 列出、加入、删除、提升、释放和接收
任务自身
├─ 消息时间线
├─ Run、步骤与审批活动
├─ Run、步骤与工具活动
├─ 监督、指标与证据
└─ 独立成果
@@ -451,9 +450,9 @@ Task 或实验对象;详细范围与交互契约见
## 13. 安全与隐私
1. Ask 在 Runtime 边界保持只读,而不只是提示词要求只读。
2. Execute 继续通过现有审批、主机执行策略、工具和目录控制。
3. 无人值守只允许用户显式批准的能力集合;遇到未预授权动作时进入等待审批。
1. 正常请求按已注册工具、启用与分配配置执行,内部文本调用保持无工具。
2. 保留主机账号权限、参数与资源范围校验,不恢复一般工具审批策略。
3. 无人值守不获得未启用能力;需要结构化回答时按后台入口的既有支持范围处理。
4. Supervisor、Evaluator 和 Heartbeat 都把消息、工具输出、记忆和成果视为不可信数据。
5. 监督器不能读取隐藏推理,只能读取产品允许持久化和展示的事件。
6. 所有跨分区读取由 Main 根据绑定关系决定,Renderer 不能提交任意分区 ID。
@@ -469,7 +468,7 @@ Task 或实验对象;详细范围与交互契约见
- 触发来源和计划版本。
- 计划目标和当前 `goalStatus`。
- Runtime、工作模式和工作目录。
- Runtime 和工作目录。
- 实际读取的知识库与记忆分区。
- 实际调用的模型、Token、工具、耗时和成果大小。
- 当前预算和剩余预算。
@@ -541,7 +540,7 @@ Conversation 关联,旧 child-task 字段可兼容映射到 Job/Subjob;心
- 增加目标、成功标准、约束、预算和停止条件。
- 支持有界的观察、计划、行动、评估循环。
- 默认 Ask 或需要逐步审批的 Execute。
- 普通请求采用统一执行;专用内部分析采用无工具调用。
### 阶段 4:并行实验
@@ -578,8 +577,8 @@ Conversation 关联,旧 child-task 字段可兼容映射到 Job/Subjob;心
- [ ] Scheduled Task 的重复触发和并行 Job 不创建新的顶层 Task。
- [ ] 每次自动执行的内部 Run 都记录触发原因、目标、范围、预算、状态和结果,并在所属
Task 下提供有界可观测信息。
- [ ] Ask 自动化无法调用写工具或产生外部副作用。
- [ ] Execute 自动化不能绕过现有审批、主机执行策略和能力控制。
- [ ] 专用内部文本调用不获得工具。
- [ ] 自动化不能绕过主机账号权限和能力范围。
- [ ] 会话监督默认只评论,不能替用户发言或批准工具。
- [ ] 并行 Run 的变量、会话、运行记忆、任务和成果相互隔离。
- [ ] 失败 Run 不参与最佳结果选择,全部失败不报告成功。
@@ -0,0 +1,388 @@
# Desktop Storage And Concurrent Execution
## Decision and delivery
Updated 2026-10-05 against `2be57492`. The user selected one complete architecture
delivery for PERF-15/PERF-16 and the necessary performance guards. This document
supersedes the earlier proposal to leave full migration for a later discussion.
The integrated migration and acceptance are not complete. The implemented storage
foundation, production composition and remaining acceptance work are recorded in
[Storage Foundation Integration](./desktop-storage-foundation.md).
Local fixes in `b90ff50f` remain the measured starting point.
Deliver the integrated design in one candidate, with normal product workflows and
concurrent acceptance passing together. Internal implementation dependencies do
not create separate partial product deliveries. A worker-only prototype, an async
facade over Main SQL, or a dual-write transition does not meet completion criteria.
This document owns architecture and scope. [Performance principles](./performance-principles.md)
own normative budgets and concurrency rules; [concurrent acceptance](../quality/concurrent-desktop-acceptance.md)
owns reproducible workloads and evidence. The [roadmap](../roadmap/product-performance-experience-improvement-plan.md)
tracks status, and the [local repair record](../review/kiss-local-fixes-2026-10-05.md)
preserves earlier measurements. Migration completion requires new acceptance evidence.
## Requirements
| ID | Required outcome |
| --- | --- |
| SA-01 | Desktop Main does not open or query business SQLite, directly or through a helper. |
| SA-02 | Complete domain transactions execute under one write owner per database. |
| SA-03 | Multiple projects, conversations and subtasks progress without unrelated global waits. |
| SA-04 | Requests, event buffers, result payloads and caches have bounded lifetimes and capacity. |
| SA-05 | Persistence, ordering, cancellation, recovery and user interaction semantics are preserved. |
| SA-06 | Startup, process failure and shutdown have one tested ownership path. |
| SA-07 | Static guards and concurrent production-path tests enforce the architecture. |
| SA-08 | GoodBuddy eliminates unnecessary intermediate disk writes and duplicate full outputs; necessary temporary resources have explicit ownership and cleanup. |
## Responsibilities
```mermaid
flowchart TB
UI[Renderer: domain stores and visible views] <--> MAIN[Main: trusted IPC and lifecycle]
MAIN <--> STORAGE[Storage utilityProcess: domain operations and write owners]
STORAGE <--> READ[Existing bounded read workers]
MAIN <--> RUNTIME[Runtime hosts and compute workers]
MAIN <--> AGENT[Remote Agent]
```
- Renderer retains presentation, local interaction state and disposable visible-data
caches. Domain selectors subscribe narrowly. Hidden views retain required state,
not continuous full rendering or whole-history refreshes.
- Main retains windows, trusted sender/schema checks, OS APIs, credential decryption,
network/runtime orchestration and process lifecycle. SQL, large result assembly,
hashing and bulk attachment file work do not execute on its event loop.
- One desktop storage utility process owns the existing business database files and
exposes typed asynchronous domain operations. Keep synchronous SQLite inside this
dedicated environment where it simplifies transaction correctness.
- Reuse bounded read-only workers for expensive reads. They open only after upgrade,
read committed WAL snapshots, and do not run initialization or recovery. Storage
owns their lifecycle; a project or conversation does not create a new worker.
- Parsing, OCR, embedding and Runtime execution retain their existing hosts. Network
requests and model execution must not run inside storage transactions or hold the
storage write queue. This work does not relocate browser-dependent OCR engines.
- Remote Agent databases remain remote. Changes to desktop storage must preserve
remote replay, controller ownership and commit-before-ACK.
One write owner means exclusive business-write authority, not necessarily one OS
thread for all reads. Startup upgrades and existing maintenance/checkpoint workers
must be explicitly owned and excluded from competing business writes. Keep their
current bounded mechanisms where valid; do not create independent writers per domain.
## Ownership inventory
Inventory records current owners; destinations below are targets, not implementation facts.
| Surface | Current entry | Required destination |
| --- | --- | --- |
| `assistant.sqlite` | `src/main/assistant/assistant-database.ts`, constructed in Main | Storage service: conversations, tasks/events, activity, notes metadata, channels, supervision and timing persistence |
| `knowledge.sqlite` | `src/main/knowledge/knowledge-service.ts` and `knowledge-database.ts` | Storage service: schema, writes and external metadata; existing workers for costly reads |
| `conversation-attachments.sqlite` and referenced files | `src/main/conversation-attachment-storage.ts` | Storage service: references, adoption, reconciliation and bounded file IO |
| Document-result persistent lookup and note bodies | `document-result-storage.ts`, `magic-notes` storage | Async domain access to associated files; preserve original directories and reference semantics |
| Local remote-runtime binding DB | `src/main/remote-agent/managed-remote-execution-services.ts` | Storage service: transactional binding transitions and uniqueness |
| Native terminal / DS Web model-call ledgers | `native-terminal-client.ts`, `native-dsh-web-client.ts` instantiate `AgentModelCallLedger` | Storage service manages desktop ledger files under the existing client lifecycle; Main retains model/credential routing |
| Read, checkpoint and upgrade connections | `readonly-query-worker.ts`, `assistant-storage-worker.ts` | Service-owned steady-state reads/checkpoints; exclusive upgrade execution before readiness |
| Supervision manifest writer | `readonly-query-worker.ts` supervision mode and `SupervisionReviewStore` | Reuse scanning and manifest logic, but route its business mutations through the same write owner as publication; remove independent writer authority |
| Remote Agent journal, owners, prompts and ledger | `src/agent-daemon/daemon.ts`, `runtime-composition.ts` | Existing remote ownership, unchanged by desktop connection migration |
| Encrypted JSON settings and transient runtime caches | Existing settings/runtime services | Keep existing appropriate owners; async settings do not move merely to match a diagram |
An import scan must also find `openPrivateSqliteDatabase` and transitive constructors:
an implementation under `agent-daemon` can still open SQLite when called in desktop
Main. Do not weaken shared Agent code or relocate remote databases as a side effect.
Preserve existing database names, table formats, IDs, paths, scopes and durability
settings. Process relocation alone does not require a schema bump. Necessary schema
changes follow the [migration guide](../development/database-migrations.md).
## Existing work and consolidation
This inventory was checked against production callers on 2026-10-05. Reuse means
preserving working algorithms and contracts, not claiming their current transport,
cancellation or close semantics already meet the target. Implement the replacement
at the existing seam and remove superseded callers in the same delivery.
| Existing mechanism | Reuse or extend | Retire or avoid |
| --- | --- | --- |
| [ReadonlyQueryReader](../../src/main/readonly-query-reader.ts), [worker handlers](../../src/main/readonly-query-worker.ts) | Reuse request correlation, named operations, errors, lazy lifecycle and reader handlers; adapt the outer utility transport and strengthen typed operations, readiness, byte bounds and settlement-aware close | No second equivalent storage RPC client or admission queue; remove production infrastructure fallback through `readWithFallback`, not legitimate owner-local transaction reads |
| [Startup storage](../../src/main/assistant-storage-startup.ts), [upgrade worker](../../src/main/assistant-storage-worker.ts) | Preserve migration algorithms, progress, retry and cancellation; move SQL preflight into the storage-owned exclusive startup path | Remove Main SQL preflight and competing upgrade launch ownership; do not add a new migration framework |
| [Checkpoint and supervision connections](../../src/main/assistant/assistant-database.ts), [review store](../../src/main/assistant/supervision-review-store.ts) | Reuse PASSIVE checkpoint and manifest scan/revision algorithms; make checkpoint exit awaited and manifest writes owner-controlled | No second maintenance service or independent business writer hidden in a read-worker entry |
| [EmbeddingIndexRepository](../../src/main/knowledge/embedding-index-coordinator.ts), [binding store interface](../../src/main/agent/runtime-session-binding-store.ts), [activity repository](../../src/main/assistant/activity-history-repository.ts) | Reuse domain interfaces and transaction algorithms; adapt the desktop backing implementation and async callers | Do not add remote object proxies or duplicate repositories; Promise-returning methods are not proof of off-Main execution |
| [RemoteEventBatcher](../../src/main/remote-event-batcher.ts) | Extend existing batching with awaited persistence/flush and pending-plus-in-flight bounds; update recovery, background and interactive IPC consumers | No second durable event coalescer, receipt ledger or retry queue; no ACK or forwarding before commit |
| [AgentModelCallLedger](../../src/agent-daemon/agent-model-gateway.ts), [terminal client](../../src/main/agent/native-terminal-client.ts), [DS Web client](../../src/main/agent/native-dsh-web-client.ts) | Preserve schema, claim/deduplication and uncertain outcomes; desktop access becomes awaited storage operations | Do not relocate remote Agent ledgers; remove desktop concrete synchronous opens and early directory deletion |
| [Utility transport](../../src/main/knowledge/embedding-utility-transport.ts), [Harness readiness](../../src/main/agent/deepseek-harness-utility-launcher.ts), [shutdown helpers](../../src/main/shutdown.ts) | Reuse fork/entry/exit adapters and phased application shutdown; add storage readiness and drain at those seams | No new generic process supervisor, duplicate shutdown path or copied Harness streaming/version protocol |
| [Runtime controller](../../src/main/agent/runtime-controller.ts), [selected runtimes](../../src/main/agent/selected-runtime-manager.ts), [remote backend](../../src/agent-daemon/runtime-acp-backend.ts) | Keep request ownership, session reuse and retirement; inspect remaining global open/prepare/close waits with healthy peers | No second Runtime pool or global execution manager; the prior route-cleanup fix does not prove every global wait is gone |
| [Subagent scheduler](../../src/main/assistant/subagent-scheduler.ts), [supervision model pool](../../src/main/assistant/supervision-model-pool.ts), [review admission](../../src/main/assistant/supervisor-service.ts) | Retain task slots, review single-flight and per-model-call pool as distinct existing scopes; extend capacity/settlement tests where needed | Do not merge ordinary chat into the supervision pool or create another task state machine/provider scheduler |
The current ordinary reader removes a cancelled request from `pending` before worker
settlement, and `close()` provides no exit barrier. Supervision waits longer and can
substitute cancellation for a successful result. Extend these semantics to
represent actual execution and committed outcomes; do not copy them unchanged into
the storage client and call its capacity or shutdown complete.
Supervision's temporary scan table belongs to one SQLite connection. Retain the
bounded scan, transfer only bounded manifest data to its write owner, and preserve
initialization/resume invariants. Existing 200-row commits run in synchronous loops;
they do not themselves yield an execution turn. Yield at safe existing batch boundaries
without dividing final atomic publication or introducing a general scheduler.
Renderer consolidation stays within existing domains:
- [Conversation store](../../src/renderer/src/conversation-store.ts) and
[persistence](../../src/renderer/src/conversation-persistence.ts) already own ordered
deltas, save serialization, history requests and quit flushing. Extend those paths,
not a second `conversation-sync` queue. Its current React-aware integration is not
a reason to introduce another store library.
- [Activity sync](../../src/renderer/src/activity-sync.ts) owns paging, pending writes
and retry; [task sync](../../src/renderer/src/task-sync.ts) owns task/artifact refresh.
Tighten projections and retention there. A resolved flush that caught an error and
requeued data is not a durable success barrier; extend its explicit completion
semantics where dependent reads or shutdown require persistence.
- [Live messages](../../src/renderer/src/live-message-store.ts),
[public event buffering](../../src/main/agent-event-buffer.ts), and
[terminal batching](../../src/renderer/src/terminal-output-batcher.ts) serve distinct
display stages. Keep them separate from durable remote batching and avoid an
additional universal event bus or a second coalescing delay.
- Retain domain cache owners: [keep-alive](../../src/renderer/src/keep-alive-cache.ts),
[Markdown](../../src/renderer/src/markdown-render-cache.ts),
[channel status](../../src/renderer/src/channel-status-store.ts), and
[Supervisor scope reads](../../src/renderer/src/SupervisionStories.tsx).
Preserve pinned-pane exceptions and scope/revision guards; strengthen bounds at
their existing owners.
Runtime session behavior remains authoritative in the
[reuse design](../features/assistant-workbar/runtime-process-reuse-technical-design.md).
[Task timing](../features/task-and-job/technical-design.md) and
[supervision model scheduling](../features/conversation-supervision/technical-design.md)
retain their existing semantics. Ending an elapsed-time segment on abort does not
release an execution slot before cleanup; do not unify these clocks or restore
historical timing scans/statistics workers during this migration.
Completion includes a removal inventory tied to the table above: old Main opens,
external live-store factories, production sync fallbacks, obsolete transport callers
and independent business writers have no production references. Remove only the
superseded implementations, not valid in-owner synchronous algorithms, remote stores,
released-data compatibility or historical test evidence. No permanent dual path or
unused compatibility wrapper is part of the delivery.
## Operation boundary
Reuse repository algorithms behind domain operations: save conversation changes,
commit remote events, publish a review, adopt attachment references, or transition
a runtime binding. Public renderer IPC can keep its current shape while Main awaits
the storage client. No generic SQL endpoint or function serialization is introduced.
Existing transaction callbacks and returned live repository objects stay inside the
owner. Replace their external use with typed inputs/results. In particular,
`readSnapshot`, Knowledge publication callbacks and Supervisor store factories must
not be transported or accept an asynchronous callback that outlives a sync transaction.
The channel extends the request/handler and utility-adapter seams identified above,
correlates requests/results and carries named operations,
serializable arguments, cancellation and readiness/failure. Reuse installed Electron
transport patterns; do not add a general RPC dependency, persisted transport sequence,
protocol negotiation or a service discovery layer for this bundled child process.
Validate actual transport behavior for buffers, Maps and errors; use DTOs. Live classes,
subscriptions and handles remain with their owners. Errors retain usable codes and causes
without credentials or user content in diagnostic logs.
Queries return the requested projection. Lists use summaries/pages; source bodies,
tool outputs and attachments load through detail paths. Large results must not be
JSON-assembled in Main or copied through several caches. Existing source records are
not truncated to satisfy a latency check. A read transaction ends before posting its
reply so a slow consumer cannot keep a WAL snapshot open.
## Concurrency and ordering
Keep ordering at the smallest required scope. A conversation's persisted sequence
and a database's commits remain ordered; independent model waits, sessions and
read requests do not share a global long-running lock. Runtime process reuse retains
per-binding/session/operation identity; cancelling one request must not stop its peers.
Use existing finite concurrency and batch mechanisms before adding scheduling code.
Apply capacity to queued bytes as well as operation count where payload size varies.
When a queue fills, wait or report the existing actionable busy/error state; never
silently discard user messages, tool results or acknowledged events. Obsolete read
refreshes and display progress can coalesce; persisted evidence cannot. Preserve one
bounded batch of work before yielding so other admitted producers can progress;
change scheduling only where concurrent measurements demonstrate starvation.
Heavy reads use the read workers and leave necessary writes able to progress. UI/control
requests must not wait behind model networking, import parsing or route cleanup.
Control processing must not depend on acquiring another execution slot. This is a
resource-boundary rule, not a mandate for a new global priority scheduler.
For long publications, compute immutable data before the write transaction when
possible, then validate current database preconditions inside the transaction and
commit the complete result/checkpoint atomically. Do not await models, filesystem
callbacks or IPC while holding a write transaction. An atomic publication cannot be
split into externally visible partial commits to improve timing. Worker relocation
does not eliminate the single-writer bottleneck: acceptance measures queue wait and
commit time, as well as Main responsiveness.
## Persistence and cancellation
| Operation | Required completion meaning |
| --- | --- |
| Cached UI preference/read | Synchronous memory access is allowed; cache ownership and invalidation are explicit |
| Existing coalesced local saves | Preserve current flush semantics and expose the existing flush barrier to dependent reads/exit |
| Remote event batch | Persist event/checkpoint under existing durability, then notify/ACK; enqueueing is not commit |
| Channel outbox / runtime ledger | Preserve duplicate-dispatch prevention and uncertain-outcome handling; no blind retry after lost replies |
| Review publication | Facts, result status and checkpoints commit together or roll back together |
Abort removes queued work before dispatch. In-flight work reaches a defined safe
point and reports its actual outcome; capacity is released only after settlement or
confirmed host exit. Caller cancellation, operation termination and resource release
are distinct observations, not three new persistent product states.
`worker_threads` SharedArrayBuffer cancellation must not be assumed to cross
`utilityProcess` IPC. Reuse it only within verified thread boundaries. A message
cannot interrupt synchronous SQL while the receiver is blocked. Keep SQL/transaction
units bounded, check cancellation between safe units, and report a completed commit
as committed even if cancellation arrived too late. Do not terminate a shared storage
process to cancel one request or promise instantaneous rollback of a committed write.
Filesystem adoption and SQL references are not automatically one atomic transaction.
Preserve existing staging, adoption and reconciliation order with async file APIs.
Do not delete originals or release protected in-flight files early; do not introduce
a cross-database transaction engine or recovery journal.
## Lifecycle and failure
Main starts and supervises one storage process using existing utility-process
patterns. Process spawn is not readiness: required migrations, connection opening
and startup recovery must finish before storage-dependent operations are accepted.
Show the existing startup/upgrade progress and retry UI. Render the application shell
when safe without marking unavailable data as an empty successful result.
After process loss, reject affected pending requests with bounded diagnostics, stop
admitting writes, confirm exit and close transport resources before replacement.
The existing user retry/reopen lifecycle can re-establish storage readiness. Read-only
work can be repeated when requested. For writes without a response, consult existing
task/event/result identities and deduplication before claiming failure or retrying;
model dispatch marked uncertain must not be sent again automatically. No Main SQL
fallback, silent empty database, second durable receipt ledger or unbounded restart loop.
Shutdown stops producers and new admission, cancels unnecessary reads, drains required
writes, closes readers/checkpoints/connections, and observes storage process exit.
Extend the existing phased shutdown and deadline, including explicit storage failure
reporting; existing settled-error helpers alone do not prove a successful drain. Native client ledger
directories are removed only after their connection is closed. Upgrade/maintenance
requires exclusive ownership before changing files, following the existing maintenance
path; it cannot open an extra Main business connection.
## Output and temporary resource ownership
This work is included in the same architecture delivery. The [source investigation](./runtime-storage.md#output-and-temporary-file-investigation)
records current producers and persistence gaps. Scope is GoodBuddy-created material;
third-party MCP/Runtime internals and user workspaces are not cleanup targets.
Decide content ownership before choosing a temporary directory. Ordinary requests,
responses, concatenation and short outputs stay in bounded memory. Where an existing
record or artifact already contains complete content, pass its reference and read pages
from that owner. Do not write a temporary copy merely to cross IPC or call MCP.
Current tool events often contain only previews. Preserve full paging access by adapting
the existing `PagedOutputStore` interface to the storage/attachment owner: output that
must outlive bounded in-memory capture has one backing content object, with events
carrying previews and its reference. Write directly to that backing object; do not spool
to Temp and later copy the same bytes to another full-output store. Reuse existing file
adoption/reference/release operations and paged reads, without a second output database,
generic blob service or durable transport journal. Final answers, previews and full
stdout/stderr remain distinct data.
The storage object's retention follows its consumer. Call-only output is released when
its existing owner no longer needs paging; content referenced by persisted history is
released through that history's existing reference cleanup. Being stored by the storage
service does not make every intermediate output permanent. Keep small call-only output
in memory and preserve existing model limits. Avoid adding whole-output copies to events
or retaining all concurrent streams in an unbounded buffer. Size alone does not justify
an additional temporary copy.
The replacement must specify the complete-content reference and byte cursor through
process, search, subagent and `output_read` paths before removing the old file backend.
Inspect remote mapping separately: it does not use the local process spool and cannot
recover omitted bytes from already-pruned Agent events. Preserve supported remote
content/ACK semantics; this work does not promise new full-history capture from third-party
runtimes. Remove the old spool creation and synchronous Main paging only once the shared
replacement preserves the existing full-output contract.
### Necessary temporary material
External CLI configuration, plugin modules and atomic replacement/install staging may
still require files. Reuse environment/in-memory configuration when already supported;
otherwise keep the actual external contract. Atomic sibling files and installer staging
stay on their required filesystem under their existing owner, not a forced central root.
For disposable runtime launch material, pass a root from application initialization
through existing constructors; create it lazily:
```text
<userData>/temp/goodbuddy-runtime/<appVersion>/<runId>/
goodbuddy-runtime-launch/
```
Add purpose directories only for evidenced necessary files. Do not precreate process,
search or subagent output trees. User-visible disposable roots retain `goodbuddy-`
prefixes. Tests use `<repository>/temp/goodbuddy-<task>-<runId>/`; remote resources use
GoodBuddy-owned prefixed roots on the Host. Do not modify global `TEMP` or relocate
third-party histories/caches through an indiscriminate environment override.
The creator removes partial creations on failure. Existing service shutdown closes
children/handles and then releases its resources. Startup, including the next upgrade,
reclaims obsolete owned run directories after the existing single-instance check and
verification that no owning child remains active. Version/run IDs identify candidates;
they do not establish inactivity. Reuse process ownership checks, with only the minimal
owner information needed to identify surviving children; do not create a lease service.
Cleanup runs in bounded background work and cannot delay the first screen with a full
filesystem scan. Failed deletion leaves the known directory available to retry.
Legacy OS-Temp prefixes require an explicit list of layouts, creator and inactivity
evidence. Reclaim confirmed obsolete instances, preserve ambiguous/live directories,
and report the remaining scope. Never delete arbitrary `goodbuddy-*`, `.tmp` or `.partial`
paths by age. Existing installer/reference reconciliation retains its own roots and
rules; no global garbage collector, cleanup ledger or user-data migration is introduced.
## KISS constraints
- Keep Electron, TypeScript, SQLite and existing provider/runtime integrations.
- Add the one storage utility process, not processes per project/session/subtask.
- Reuse reader, checkpoint, parser, batching and lifecycle mechanisms where appropriate.
- Keep credentials/OS integration in Main; storage receives only required business data.
- Do not add general schedulers, distributed transactions, durable RPC journals,
compatibility readers for unshipped intermediates, new approval gates or user modes.
- Synchronous cache reads and pure calculations need not become RPC calls.
- The runtime host, database driver and UI framework are not replaced as incidental work.
## VS Code reference
VS Code demonstrates service separation, asynchronous execution, lazy activation,
cached state and coordinated close. Its application/profile/workspace storage is
owned by Electron Main, uses async `@vscode/sqlite3`, and batches cached key/value
writes. GoodBuddy retains `DatabaseSync` in a dedicated execution environment because
its relational history, graph queries and durable event acknowledgements differ.
Do not copy full-state memory loading or empty-store recovery into business history.
Sources reviewed on 2026-10-05, upstream main; no same-machine benchmark is claimed:
- [Connection ownership](https://github.com/microsoft/vscode/blob/main/src/vs/platform/storage/electron-main/storageMain.ts)
- [SQLite backend](https://github.com/microsoft/vscode/blob/main/src/vs/base/parts/storage/node/storage.ts)
- [Cache and flush lifecycle](https://github.com/microsoft/vscode/blob/main/src/vs/base/parts/storage/common/storage.ts)
- [Extension hosts](https://code.visualstudio.com/api/advanced-topics/extension-host)
## One-delivery completion
SA-01 through SA-08 must pass together under the [acceptance matrix](../quality/concurrent-desktop-acceptance.md).
All listed desktop owners, normal/background/error/cancel/exit callers, and native
client ledger paths must use the final boundary. Remove the superseded Main openings,
direct storage access and synchronous fallbacks, including helper-based bypasses.
No feature-disable workaround or loss of user interaction counts as completion.
Run production UI-to-storage tests, existing full validators, old-data integrity and
concurrent before/after measurements; validate affected desktop-to-Agent behavior on
the shared Linux x64 Host. Native platform CI verifies process startup, SQLite and
packaged entry/resource paths. Follow the release runbook for production builds and
packaged-app checks, including measurement runs.
The acceptance record must report latency, throughput, backlog and resource cleanup,
including failures. A responsive UI with stalled background commits is not success.
No target implementation, new performance baseline or migration completion is claimed
by this documentation update.
+129
View File
@@ -0,0 +1,129 @@
# Desktop File Storage Integration (S04)
Implemented domain operations, async consumers and production composition on
2026-10-05. The storage owner is registered by the foundation and the current Main
composition passes its file facades to ContextManager, document parsing and model
output. This does not claim that the full application has passed concurrent or
packaged acceptance.
The governing constraints are [performance principles](./performance-principles.md)
and [storage ownership](./desktop-storage-direction.md).
## Foundation API
`src/main/desktop-storage-files.ts` exports the complete integration surface:
- `DesktopFileStorageDomains`: intersect into `DesktopStorageDomains`. The domains
are `attachments` and `documentResults`.
- `attachmentStorageMethods` and `documentResultStorageMethods`: explicit allowed
operations. `attachments.reconcile` is an additional owner composite, with
arguments `[startup?, protectedIds?]`; its callback never crosses IPC.
- `openDesktopStorageFiles(userDataRoot, hasOwner, persistentResultLookup?)`:
construct once inside the existing utility process, after assistant and knowledge
databases open. Pass the existing userData root, not the assistant DB directory.
Add that root to foundation options and forward it in the client's open message.
- `DesktopStorageFilesOwner.dispatch(domain, method, args, signal)`: dispatch the
two domains here from the existing owner's dispatcher. It normalizes transported
original bytes, checks operation allowlists, and supplies cancellation to result
saves. Keep the existing transport admission and result limits.
- Before readiness, run `files.attachments.reconcile(hasOwner, true)`. The callback
uses the already-open assistant owner. It checks released references, marks
interrupted parsing, removes stale call-only output refs, and reclaims owned
orphan UUID directories. Do not run it concurrently with file producers.
- After draining admitted requests, `await files.close()` before closing assistant
and knowledge. Close drops call-only output refs, preserves history refs, removes
temporary document results, and closes the existing attachment database.
No new database, RPC transport, SQL endpoint, or process is introduced. Existing
attachment reference transactions and schema 1 remain in place. New relative paths
use slash separators; reads and reconciliation also accept released Windows paths.
## Main And Knowledge
After `storage.ready`, production composition uses `createDesktopStorageFiles(storage)` to obtain
`{ attachments, results, backingStore }`. Pass attachments to `ContextManager` and
results to `DocumentParsingService` and Knowledge's result-access dependency.
Do not also construct `ConversationAttachmentStorage` or `DocumentResultStorage`
in Main. Facades only call the existing storage client; importing them does not open
SQLite. Their result `close()` is intentionally inert: shared result shutdown belongs
to `DesktopStorageFilesOwner.close()`, after all producers drain.
Context operations that now require await: `storePastedImage`, `enrichRequest`,
`remove`, `serializeForQueue`, `restoreFromQueue`, `getDraft`, `hasImageInputs`,
`validateForSend`, `saveDraft`, `sendOriginal`, and `cancelUnavailableImport`.
Previously async import/reparse/image operations now await every storage operation.
Conversation/target validators may return Promises. Memory-only `clear` and
`activeContextIds` remain synchronous. `ContextManager.dispose()` cancels and drains
the active import before collecting unreferenced assets. Stop other context producers
and await their operations before disposing. Parsing-service disposal drains all
diagnostics, including those without an operation ID.
IPC must await attachment/result calls, including `results.original(id)` before
`shell.openPath`, and array predicates/maps after `get`, `draft`, `pendingParsing`,
and `getDraft`. Replace callback-based Main reconciliation with
`await attachments.reconcile(false, contextManager.activeContextIds())`.
Knowledge must accept `DocumentResultStorageAccess`, await `detach`, `original`,
and `isTemporaryResult`, and remove Main `setPersistentLookup` registration. Supply
that lookup to `openDesktopStorageFiles` instead: use owner-local
`knowledge.getDocumentByParsedResultId(id)` and the existing
`knowledge-assets/<libraryId>/<documentId>/<resultId>` directory and source location.
Keep Knowledge's publish/move/detach/release order. Result release never deletes an
external source original. No user-data directory migration is needed.
Project deletion supplies the project's conversation IDs captured before assistant
deletion to `attachments.deleteProject(conversationIds, protectedIds?)`. It removes
their refs in one attachment transaction, then collects only unreferenced assets.
Shared assets remain available to other conversations. Call this after the assistant
deletion commits; startup reconciliation covers an interrupted cleanup. Use
`deleteConversation` for single conversations and reconciliation for full clearing.
## Full Output
Pass the returned `backingStore` to each existing `PagedOutputStore`. It implements
`create/append/finish/read/release` directly in the existing attachment owner.
An output handle's UUID identifies its attachment row throughout capture, promotion,
history retention, restart, and deletion. Capture writes `original.txt` directly in
the owned attachment directory. Promotion renames that directory; no temporary full
copy or second backing is created. `request.json` contains only the descriptor.
Appends are bounded to 64 KiB; backing reads allow 32 KiB plus UTF-8 lookahead.
Small outputs remain in the existing bounded in-memory capture.
For history retention, await
`attachments.outputAdopt(callOwnerId, handle, conversationId, kind, referenceId)`
before the runtime releases its call reference. It returns the resource ID and adds
the reference without replacing the owner's existing attachments. Persist the
complete-output handle/reference in the owning event/message path; a preview alone
is not full output. Retain the call until required history adoption/persistence has
settled. Failed history publication uses existing release/reconciliation, not a
second durable operation log. Incomplete captures cannot be adopted.
`release` drops only the matching `output-call` ref. Persisted history can page after
call disposal or restart with
`attachments.outputRead(conversationId, handle, cursor, length)`, reading the same
bytes; it must use the owning conversation ID. History deletion uses the existing
message/queue/conversation reference cleanup. Runtime cancellation must await writer
abort/release. Startup removes stale call refs while retaining valid history refs.
The remote Agent path does not use this desktop backing and is unchanged.
## Validation Boundary
The focused suites exercise real SQLite/files through owner operations and async
facades, using structured-cloned arguments/results to cover Buffer-to-Uint8Array
conversion. They cover complete UTF-8 paging, one backing file, adoption, release,
abort, restart, project deletion with shared history, existing path separators,
temporary-result cleanup, failed adoption retry, async restore, and shutdown drain.
New result writes and copies omit `parsed.md`; old manifests and directories remain
readable. Result image writes settle sequentially before failure cleanup.
Local Windows validation: six focused suites passed (83 tests), covering this owner,
attachment references, ContextManager, parsing service, paged output, and HTTP OCR
integration. Scoped ESLint and diff whitespace checks passed. Node type checking
reported no diagnostics in S04 source/tests; the complete checkout still has pending
integration errors in Main/IPC and concurrent domain migrations.
These tests do not replace the foundation's utility-process tests or UI acceptance.
The real utility-process coverage for these domains is present in the focused
storage fixtures. Concurrent desktop acceptance and packaged validation remain
outstanding. No latency or throughput improvement is claimed from these focused
tests. Test resources live under `temp/goodbuddy-files-upgrade` and individual
fixtures remove their files.
@@ -0,0 +1,308 @@
# Desktop Storage Foundation Integration
Implemented 2026-10-05. This is the storage foundation for
[the desktop storage direction](./desktop-storage-direction.md). The production Main
composition now opens one `DesktopStorageClient` and supplies its typed facades to
the migrated domains. Full application acceptance is still incomplete. Do not open
the existing Main databases alongside the owner.
## Client API
Import `DesktopStorageClient` from `src/main/desktop-storage-client.ts` in Main.
Construct it after Electron readiness and the application's single-instance check.
The existing database directories must exist. Preserve the current database paths.
```ts
const storage = new DesktopStorageClient({
assistantPath,
knowledgePath,
defaultRootPath,
userDataPath,
onProgress: progress => publishExistingUpgradeProgress(progress),
onChanged: domain => publishExistingDomainNotification(domain),
onFailure: error => reportStorageFailure(error)
})
await storage.ready
await storage.call('assistant', 'saveLocalConversations', [changes])
const conversation = await storage.call(
'assistant', 'getConversation', [conversationId], { signal }
)
const results = await storage.call('knowledge', 'search', [searchOptions], { signal })
// After stopping producers and awaiting their persistence flushes:
await storage.close()
```
`call(domain, method, argumentTuple, { signal? })` infers argument and result types
from the explicit allowlists in `desktop-storage-contracts.ts`. All results are
Promises. Pass cancellation as the transport option, not inside a DTO. Functions,
signals and live class instances are rejected before posting. Maps and Uint8Arrays
are supported; Node Buffers arrive as Uint8Arrays across Electron IPC.
`userDataPath` is the existing application user-data root, not a database directory
inferred from a filename. `ready` means upgrade worker exit, database initialization,
attachment startup reconciliation and runtime-binding owner initialization completed.
Calls before readiness fail without queueing. `retry()` retains the same public
client and facades. A live failed startup retries in that process; a failed/exited
utility is replaced only after exit confirmation and old-request settlement. The
replacement gets fresh request correlation, never the old writes. The confirmed
upgrade decision is acknowledged into Main memory before upgrade execution, retained
across startup loss, and cleared when that upgrade completes. Wire `retry()` to the
existing retry UI. There is no automatic restart or replay of unconfirmed writes.
The defaults locate `desktop-storage-entry.js`, `readonly-query-worker.js` and
`assistant-storage-worker.js` as packaged siblings. `entryPath`, `readerWorkerPath`
and `upgradeWorkerPath` overrides support isolated tests and explicit composition.
The new entry is included in `electron.vite.config.ts`.
## Owner-Side Operations
| Existing call shape | Storage operation |
| --- | --- |
| Assistant/Knowledge ordinary database methods | `call('assistant' / 'knowledge', method, args)` using the explicit allowlists |
| SSH project create/update | `assistant.createSshProject` / `assistant.updateSshProject` accept `Omit<SshProjectWrite, 'assertCurrent'>`; Main checks the live host/runtime before dispatch, and the owner retains cancellation and project-revision checks inside the existing transaction |
| `activityHistory()` live repository | Existing `getActivityHistoryPage`, `getActivityHistorySummary`, `updateActivityHistory`, `replaceActivityHistory`, `clearActivityHistory`, `reconcileActivityHistory` operations |
| `supervisionReviewStore()` | `call('review', method, args)`; initialization/resume execute on the write owner, not the old independent writer |
| `supervisionStories()` | `call('stories', method, args)` |
| `supervisionExperiences()` | `call('experiences', method, args)` |
| `supervisionSuggestions()` | `call('suggestions', method, args)`; resolution uses `assistant.resolveSupervisionSuggestion` |
| Knowledge `externalStore` | `call('external', method, args)` |
| `readSnapshot(callback)` for review context | `call('assistant', 'supervisionContext', [request])`; summary/background share one reader snapshot |
| Knowledge `publishDocument(..., { afterChunksInserted })` | `call('knowledge', 'publishDocument', [input, chunks, { graph, embeddingReplacement, embeddingError }])` |
| Knowledge graph-only replacement | `call('knowledge', 'replaceDocumentGraph', [library, document, graph])`; reuses the existing evidence replacement transaction |
| Attachment references and full-output backing | `call('attachments', method, args)`; includes owner-local `reconcile(startup?, protectedIds?)` |
| Temporary and persisted document results | `call('documentResults', method, args)` |
| Runtime bindings and native model-call ledgers | `call('runtime', method, args)` |
Repository instances never leave the owner. The publication graph DTO is the
existing `GraphExtractionResult`. The owner creates the graph callback and performs
entity identity resolution, evidence insertion, chunk/embedding publication and
pruning within `KnowledgeDatabase.publishDocument`'s existing transaction. Replace
the consumer's publication callback with this DTO; remove its duplicate graph
publication path when migrating that consumer. Extraction/model work remains outside
storage. Publication and replacement share `storeExtractedGraph` from
`desktop-storage-knowledge-operations.ts`. Callback-bearing `replaceEvidenceForDocument`
is excluded from the transport allowlist. File adoption/release and task-status
orchestration remain outside this SQLite transaction.
After readiness, `createDesktopStorageFiles(storage)` supplies attachments, results
and output backing; `createDesktopRuntimeStorageAdapters((domain, method, args) =>
storage.call(domain, method, args))` supplies runtime bindings and ledger access.
The facade caller types now share the core DTO types, so these calls need no casts.
`StorageData` preserves the existing recursive `JsonValue` as-is rather than
recursively rebuilding it; runtime validation still rejects callbacks and handles.
The owner supplies both file callbacks. Attachment reconciliation checks
`assistant.hasAttachmentOwner`. Persistent document lookup reads
`knowledge.getDocumentByParsedResultId` and resolves the existing
`<userDataPath>/knowledge-assets/<libraryId>/<documentId>/<resultId>` directory and
source location. Main must not register another persistent lookup on the results
facade. The existing `onProgress`, `onChanged` and `onFailure` APIs are unchanged,
including `createAssistantStorageOnChanged`'s four notification mappings.
Keep these composites intact during integration:
- Remote task/conversation batches use the existing `appendRemote*Batch` or
`appendRemote*Once` methods. Await commit before forwarding or ACK. Never translate
a batch into independently acknowledged per-event writes.
- Review publication uses `assistant.saveSupervisionResult`, which owns result facts
and checkpoint commits. `commitCheckpoints` is deliberately not an external review
operation. No callback-based transaction API is exposed.
- Story/experience apply and review batch save retain their existing repository
transactions. A model request must finish before calling these operations.
- Additional multi-statement snapshots need a named owner/reader handler; do not
recreate `readSnapshot` as several awaited Main calls.
## Bounds And Lifecycle
The utility adapter retains `ReadonlyQueryReader` request correlation, errors and
admission. Its existing request limit is 32, with 32 MiB of argument admission
credit. This is no longer a content-size restriction: one oversized complete input
uses the entire credit budget exclusively. Smaller concurrent inputs still share
the budget; saturation returns `STORAGE_CAPACITY` without dropping work.
`desktop-storage-wire.ts` transfers complete values on the same channel in frames
of at most 256 KiB accounted data. Each direction sends one frame and waits for its
acknowledgement before constructing the next. Strings are split without assembling
a whole JSON/serialized copy; binary frames copy only their slice and fill the
receiver's final buffer. The receiver exposes a value only after its last frame.
These frame acknowledgements provide transport backpressure, not durable write ACKs.
Cancellation bypasses the frame queue and is retained for an incompletely received
request until dispatch can reject it safely.
Known complete-note/history/result reads share one materialization/transfer slot,
so queued requests do not build many huge reply objects while another reply drains.
Writes and bounded queries retain ordinary admission. A complete user value itself
still needs memory proportional to its size; the frame budget is not a claim that
total process memory or every supported history fits in 32 MiB. There is no result
size rejection or truncation, and no `STORAGE_RESULT_CAPACITY` outcome after a commit.
Renderer conversation persistence now targets 8 MiB batches as well as the existing
100-conversation bound. A single oversized conversation stays complete and uses the
framed transport. Quit flushing drains all remaining batches and propagates a failed
write. Oversized single histories no longer fail solely because of the transport cap.
`STORAGE_UNCONFIRMED` means the process exited without confirming a pending operation;
reconcile using existing domain identities, not blind retries.
Queued cancellation is checked before dispatch. In-flight reader cancellation uses
the existing thread SharedArrayBuffer inside the owner; that buffer is never sent
over utility-process IPC. Synchronous commits cannot be interrupted by an incoming
message, so a completed commit returns success even when cancellation was requested
too late. Request capacity remains occupied until settlement.
`close()` stops client admission, drains admitted requests, awaits file-owner cleanup,
closes runtime ledgers/bindings and read workers, awaits the existing PASSIVE
checkpoint worker's exit, closes both database owners
and observes utility-process exit. An exit without the close acknowledgement rejects
close. Apply the application's existing shutdown deadline to the whole operation;
the transport separately reports an unconfirmed exit after its 10-second exit wait.
Startup failure follows the same release barriers. A failed close attempts the
remaining independent closes, reports `STORAGE_CLOSE_FAILED`, and exits unsuccessfully;
it does not acknowledge a clean drain or permit a second owner to open in that host.
## Remaining Integration And Acceptance
### Supervision And Heartbeat Callers
`createSupervisionDomainPorts(storage)` in
`src/main/assistant/supervision-domain-ports.ts` supplies Main-side RPC facades:
```ts
const ports = createSupervisionDomainPorts(storage)
const supervisor = createProductionSupervisorService(ports.supervision, getSettings, resolveRuntime, pool, persistUsage)
const phrase = createProductionSuggestionPhraser(ports.supervision, getSettings, resolveRuntime, pool, persistUsage)
const heartbeat = new HeartbeatService(ports.heartbeat, actions)
await deriveSuggestions(ports.suggestions, phrase, input)
```
The production factory's other arguments are unchanged. If provided, `persistUsage`
must return the persistence Promise. All heartbeat methods, including configuration
CRUD and history, now return Promises. Review repository factories may return a
Promise of the typed facade. The facades contain closures only; no database or live
repository is transported. `initializeSupervisionReview` and `resumeSupervisionReview`
route to `review.initialize` / `review.resume`, with signals in transport options.
The caller does not invoke the old independent supervision writer.
`story-assignment-service.ts` and `experience-extraction-service.ts` are the sole
story/experience model orchestrators, including their prompts, output schemas and
model-facing types. Production, tests and live scripts import those services.
The repository files retain their SQL methods and transactions, with type-only
imports for validated model output. `supervision-suggester.ts` awaits candidate
selection and suggestion publication. The heartbeat live script also awaits plan
creation before using its ID.
Callers await leaf/navigation writes, final publication, task usage/status,
heartbeat projections, story/experience results and error bookkeeping. Cancellation
aborts model work immediately, but review admission remains occupied until the
cancel write and active execution settle. A late rejected cancel does not overturn
a confirmed publication. Failure to persist a suggestion failure is propagated;
a model phrasing failure whose status was saved preserves the completed review.
Validation includes unchanged behavioral assertions in the existing review/story/
experience suites, delayed-commit tests, and `supervision-storage.test.ts`: a real
Electron storage utility process runs heartbeat, two review leaves, navigation,
stories, experiences, suggestion publication, usage, a no-change heartbeat and
cancellation using a local deterministic Runtime. This does not validate final
Main/IPC composition or concurrent latency budgets.
On 2026-10-05, after removing the duplicate orchestrators, the targeted supervision/
heartbeat run passed 215 tests across 21 files, including the real utility-process
test. Scoped ESLint and Node type checking passed.
The bounded `scripts/supervision-storage-live.cjs <runtime-settings.json>` probe
used the current supervisor profile (`gpt-6-astra`, OpenAI Responses) and production
utility storage with three synthetic calibration messages. Exactly four actual
HTTP requests completed, all HTTP 200: review, story assignment, experience
extraction and suggestion phrasing. It committed one leaf covering all three
sources (589 characters), assigned three events to one story, created one experience
and published two suggestions. The unchanged review made zero additional requests.
Persisted usage was four calls, 3,495 input and 1,251 output tokens. Two initial
preflight attempts sent zero requests; the probe was corrected to install the
isolated Electron encryption state before readiness. Current settings remained
byte-identical; credentials were not logged, and isolated databases, encryption
state and bundles were removed. This is functional evidence, not concurrent
performance acceptance.
### Integrated Owner
The foundation registers assistant, knowledge, external metadata, supervision,
attachments, document results and runtime operations without Main SQL fallback. The
production composition in `src/main/index.ts` constructs the Assistant, Knowledge,
file and Runtime facades after storage readiness and passes them to the relevant
Main services. Remote Agent database ownership is unchanged.
The remaining work is product-level acceptance: complete C01-C07/A01-A10 coverage,
packaged cross-platform checks, and removal or verification of any caller that is
outside the migrated desktop domains. Focused domain tests do not establish those
results.
The S05 runtime-domain owner, Main adapters, required composition wiring and focused
validation are documented in [Runtime Storage Integration](./desktop-storage-runtime-integration.md).
Supervision now uses `ReadonlyQueryReader.reviewManifest(runId)`. The existing
read-only assistant worker scans once under a read snapshot into connection-local
temporary metadata, ends the snapshot, and returns at most 200 metadata rows per
page. It holds at most one live scan manifest, rejects a competing scan explicitly,
and releases that temporary data in the operation's settlement path. It never
opens a business-write connection.
The sole write owner clears/rebuilds disposable manifest rows in bounded batches,
commits each insertion batch atomically, and yields between commits. Cancellation
and persisted pause/cancel status are checked between batches. `initializing`
remains set after interruption; clearing it and deleted-source cleanup share the
final transaction. Resume rebuilds partial manifests, or validates existing frozen
revisions in read-worker pages of at most 200 rows, yielding between pages. A failed
scan never falls back to synchronous owner scanning. Final result/checkpoint
publication remains the existing atomic operation.
The old writable `supervision` worker mode, connection opener and separate reader
field were removed. `AssistantDatabase`'s direct initialization/resume methods use
the same bounded implementation; in-memory owner-local use retains its synchronous
algorithm because a separate read connection cannot share that database. Other
non-worker operations still execute synchronously inside storage; no latency
improvement or full concurrent performance acceptance is claimed.
The current focused validation passes 70 tests across eleven suites: core storage,
file/runtime domains, assistant facade, read workers, bounded review-store algorithms,
existing supervision worker regressions, startup, wire backpressure, renderer batching
and real supervision/storage orchestration.
Real Electron coverage includes graph replacement rollback, SSH revision conflicts,
owner-side persistent document lookup after reopen, full-output retention and crash
reconciliation, ledger recovery, runtime startup failure/retry and pending file-save
drain before cleanup. Review tests cover 200-row bounds, concurrent owner writes,
frozen scan revisions, cancellation, failed-batch rollback, reader loss, marker
recovery, deleted sources and final marker/cleanup atomicity.
Real Electron large-value tests save an explicitly typed 16 MiB MP4 payload, read a
complete two-entry note, and round-trip all 25,600,230 characters of an 80-message
history. Hashes verify complete video and message bodies. The measured accounted
frame high-water marks were 248,332 bytes in Main and 252,578 bytes in storage, below
262,144 bytes; argument credit peaked at 33,554,432 bytes. Main RSS peaked at
426,049,536 bytes including fixture inputs, complete results and application memory.
This is queue-bound evidence, not a whole-process memory or latency budget claim.
The real process tests also kill an in-progress write transfer and confirm no replay
after retry, and verify startup decision retention/clearing across process loss.
Full Node, Agent and renderer typechecking passes, as does scoped ESLint. Temporary checks and databases use
`temp/goodbuddy-storage-foundation`; tests remove their generated bundles/data.
The earlier foundation production-build check predates this domain integration.
Packaged cross-platform and full UI-to-storage concurrent acceptance remain outstanding.
### Reader Ownership Cleanup
The obsolete `readWithFallback` export, database-owned readonly-reader fields and
checkpoint lifecycle methods, and the database async wrappers were removed. Heavy
reads are dispatched by `DesktopStorageOwner`; owner-local synchronous reads remain
available inside transactions and focused database tests. Read-worker and checkpoint
tests now create explicit `ReadonlyQueryReader`/`Worker` owners and await their close.
The three perf harnesses create explicit owner-local reader/checkpoint resources under
the repository temporary root, report `executionBoundary: owner-local`, and close
those resources before deleting their copies. No production or test source reference
remains to the removed compatibility APIs.
The cleanup validation adds termination barriers and failed-frame poisoning: a reader
cannot reopen capacity while its prior worker termination is pending, a transport
posting failure cannot feed later values into a partial decoder, and close waits for
wire drain and owner settlement. The focused cleanup run passes 122 tests; Node and
renderer type checks and scoped lint pass.
@@ -0,0 +1,66 @@
# Knowledge Main Storage Migration (S03)
Knowledge Main now receives `KnowledgeServiceOptions.database`, a
`KnowledgeStoragePort`. Production composition constructs it with
`createKnowledgeStoragePort(storageClient)` after `storageClient.ready`.
`KnowledgeService` no longer opens, initializes, closes, or starts readers for
the business database. The application storage owner retains those duties.
The adapter sends explicit `knowledge` and `external` domain calls. Search
signals travel as transport options, not serialized arguments. The port's
`Awaitable` return type permits synchronous owner-local test implementations;
the production adapter always returns the storage client's promises.
Main awaits source, document, task, graph, embedding and external metadata
operations. IPC mutations await their writes before returning or refreshing a
snapshot. Parsing, models, credentials and source watchers remain in their
existing Main services. Embedding batches retain their existing sizes and
staging/publication sequence.
Publication passes the extracted graph through foundation
`publishDocument(..., { graph })`. Graph-only replacement calls
`replaceDocumentGraph(library, document, result)`. Its implementation in
`src/main/desktop-storage-knowledge-operations.ts` runs the existing synchronous
graph algorithm inside `replaceEvidenceForDocument`'s transaction. No callback
or SQL crosses the storage boundary.
Shutdown aborts producers immediately, drains tracked work and observes the
task-interruption write and embedding status persistence. The service does not
close the shared storage client. Embedding allocation returns its replacement
ID even if cancellation arrives with the reply, allowing cleanup; a committed
finish is not reported as rolled back. Rebuild admission remains occupied
until its final status write settles, and status-write failures reach the
completion barrier.
## Current Production Boundary
The following boundary is implemented in the current production composition:
- `replaceDocumentGraph(library: KnowledgeBase, document: Document,
result: GraphExtractionResult): void` to the knowledge domain contract and
dispatch it to the exported owner-local operation, passing the owned database
as its first argument. It is not part of the concrete database method
allowlist because it is a composite owner operation.
- Foundation publication reuses the exported `storeExtractedGraph` helper.
- `StorageData` preserves the existing recursive JSON type without rebuilding it
in the adapter.
- Persistent document lookup is resolved by the storage owner through the
Knowledge domain; Main does not register a second lookup.
- Application composition injects the port, awaits asynchronous gateway and
tool-provider calls, and closes shared storage after Knowledge disposal.
## Validation
The focused Knowledge service, external service, embedding coordinator and
embedding repository suites use a Promise-backed adapter over a test-owned
SQLite connection. Coverage includes publication rollback, embedding batch
ordering, late cancellation, replacement cleanup, external request cancellation,
credential persistence and restart. The production gateway now awaits its library
and search operations. Remaining evidence is limited to the focused suites and
does not establish full concurrent desktop acceptance.
Owned production files pass ESLint. The current worktree typecheck and focused
storage validation cover the injected production port and owner dispatch. Global
architecture acceptance, packaged checks and concurrent performance acceptance
remain with the parent delivery; these tests do not establish a new performance
baseline.
@@ -0,0 +1,96 @@
# Desktop Runtime Storage Integration
S05 domain implementation and production composition, 2026-10-05. This supplies
the runtime domain for the [desktop storage foundation](./desktop-storage-foundation.md).
The current composition passes the storage-owned binding store and model-call
ledger factory to Main runtime services. Full desktop migration and performance
acceptance remain incomplete.
## Integration API
`src/main/desktop-storage-runtime-operations.ts` exports:
- `DesktopStorageRuntimeOperations`: the existing `RuntimeSessionBindingStore`
interface plus named ledger lifecycle and transition operations.
- `runtimeStorageMethods`: the explicit host dispatch allowlist.
- `DesktopStorageRuntimeOwner.open(userDataPath)`: creates the sole binding owner
at `remote-runtime-bindings.sqlite`. Await it before publishing storage readiness.
Native ledgers open lazily at their existing paths through `openLedger(id, path)`.
- `createDesktopRuntimeStorageAdapters(call)`: Main adapters returning
`bindingStore` and `openModelCallLedger`. `call` has the same domain/method/tuple
shape as `DesktopStorageClient.call`, restricted to the `runtime` domain.
- `DesktopStorageRuntimeOwner.close()`: closes remaining native ledgers and the
binding database. The outer storage owner must first drain admitted operations.
This method is not part of the external domain allowlist.
The storage-core integration adds `runtime: DesktopStorageRuntimeOperations` to
`DesktopStorageDomains`, initializes one runtime owner from the existing user-data
directory, and dispatches allowed runtime methods against that owner. Include its
close in the storage owner's release path, including startup failure. No new RPC
client, SQL endpoint, admission queue or process is needed.
After registering the domain, Main composition is:
```ts
await storage.ready
const runtimeStorage = createDesktopRuntimeStorageAdapters(
(domain, method, args) => storage.call(domain, method, args)
)
// ManagedRemoteExecutionServices options:
// bindingStore: runtimeStorage.bindingStore
// The former userDataPath option is removed; the host owns that path.
// NativeClientCoordinator, NativeTerminalClient and NativeDshWebClientService options:
// openModelCallLedger: runtimeStorage.openModelCallLedger
```
`ManagedRemoteExecutionServices` borrows the binding interface and does not close
it on disposal. Native clients stop their producers and proxy, await their ledger
close, then delete launch material. DS Web retains native history. A failed or
unconfirmed close retains files and propagates the error. A lost open reply also
retains files because the host may still own the connection. Shutdown must stop
native/remote producers before closing the shared storage client.
## Ordering And Ownership
The shared `ModelCallLedger` type allows synchronous or Promise-returning methods;
the concrete `AgentModelCallLedger` stays synchronous and remains the remote
Agent's owner. Schema, database names, call identities, retention and transaction
algorithms are unchanged.
`AgentModelGateway` awaits claim before provider dispatch, complete before returning
the response, delivered before resolving the delivery acknowledgement, and
outcome-unknown before reporting provider failure. Cancellation while claim is
pending does not dispatch after claim completes. Persistence rejection propagates;
the gateway does not replay a provider request after an unconfirmed write.
## Validation
The final focused run passed 113 tests across eight files, with 11 existing opt-in
cases skipped. It included the official DS Web process using standard Node,
binding uniqueness/rotation/cursor persistence, concrete ledger reopen and uncertain
outcomes, delayed/rejected persistence, and both native clients' close ordering.
`desktop-storage-runtime-operations.test.ts` also builds real Electron fixtures.
The production native terminal loopback gateway uses Main adapters and the real
`DesktopStorageClient`/transport to reach a utility-process runtime owner, then
closes its ledger before removing launch files. Production core registration is
covered by `src/main/index.ts`; the fixture remains focused on the runtime domain.
Its model response is local and deterministic, with no external provider call.
The shared gateway was separately validated on the pinned Linux x64 test Host via
the existing lifecycle harness rebuilt from current source. OpenCode ran a bounded
tool command in a dedicated `/root/tmp/gb-lifecycle-*` workspace. Desktop detached,
the test Agent was promoted, the controller resumed, and the prompt completed.
There were exactly **2 real text-provider calls**, both HTTP 200, **0 image calls**,
33 persisted event entries, 17 checkpoints, and successful replay deduplication.
The old test daemon retired; final owned process count was zero. The harness removed
its remote directories. Local artifacts were isolated in
`temp/goodbuddy-ledger-upgrade` and cleaned after validation.
Scoped ESLint and `git diff --check` passed. The current composition supplies
`bindingStore` to `ManagedRemoteExecutionServices` and `openModelCallLedger` to
the native coordinator. Whole-application acceptance, packaged cross-platform
checks and concurrent performance measurements are not established by these
focused tests.
+56 -11
View File
@@ -5,11 +5,12 @@
- **适用范围:** Desktop(Electron Main、Preload、Renderer)及其后台进程和线程。
- **用途:** 新功能设计和代码评审的约束。多个 agent 并行修改代码时,以本文为统一标准。
- **关联文档:** `docs/roadmap/product-performance-experience-improvement-plan.md`(测量记录与实施进度)。
- **架构与验收:** [桌面存储与并发执行](./desktop-storage-direction.md)定义一次完整交付的职责和范围;[桌面并发验收](../quality/concurrent-desktop-acceptance.md)定义多项目、多会话、多子任务负载及证据。本文件是性能规范的唯一数值来源。
- **参照:** VS Code 的进程分工和渲染做法。我们没有在同一台机器上实测过 VS Code,下面的预算按常见的流畅标准设定。
## 1. 性能预算
“流畅”用下面的数字判断,不靠感觉。“标准机”指不降速运行 `npm run perf:app`,“低端机”指加上 `GB_PERF_CPU_THROTTLE=4`。
“流畅”用下面的数字判断,不靠感觉。“标准机”指记录了硬件环境、不降速运行的参考机;Renderer 降速场景使用 `GB_PERF_CPU_THROTTLE=4`。该降速仅作用于 Renderer,不等于真实低配整机,也不模拟慢 Main 或磁盘。预算适用于单场景及规定的并发负载。
| 指标 | 标准机 | 低端机 |
| --- | --- | --- |
@@ -21,7 +22,9 @@
| Main 事件循环延迟,p99 | ≤ 30 ms | — |
| DOM 节点数 | 不随会话数、消息数增长 | 同左 |
标准机的阈值由 `npm run perf:check` 检查。低端机的数值目前只做记录,等结果稳定后再加进阈值检查。
当前 `npm run perf:check` 使用历史回归上限,部分数值宽于本表,也未覆盖全部并发入口。检查通过不能宣称本表达标。目标交付须在验收报告中分别给出规范预算和回归上限的结果;低端机降速目前只做记录,真实低配和新增自动检查的完成状态另行记录。
点击反馈、所需内容可用和后台工作完成分别计时。显示加载状态只能证明及时反馈;不能用它替代结果就绪。每个进程分别报告长任务/事件循环和排队耗时,禁止把多个独立探针相加为端到端延迟。
## 2. 原则
@@ -32,6 +35,7 @@ Main 负责窗口、生命周期、权限和消息转发。数据库查询、文
- 新代码不能在 Main 里使用 `DatabaseSync` 和同步文件 API。lint 已有白名单,名单只能减少。
- 暂时必须留在 Main 的同步操作,单次耗时要能证明在 16 ms 以内,并在代码里注明理由。
- 耗时可能超过 16 ms 的操作,必须支持取消和进度上报,不能一直占着 Main。
- 目标交付后 Main 不再直接或通过 helper 打开桌面业务 SQLite;只读、失败回退、维护及退出均在检查范围内。当前迁移例外不是新增同步工作的许可。存储宿主可继续使用同步 SQLite,事务和计算仍需有界。
### P2 界面状态局部订阅
@@ -53,7 +57,8 @@ Main 负责窗口、生命周期、权限和消息转发。数据库查询、文
- `App.tsx` 只负责布局外壳、路由和把 store 实例通过 context 提供给子树。新增领域状态一律走上表四层。
- 每个 store 的单元测试和 store 文件放在一起(`<domain>-store.test.ts`),覆盖顺序、失败重试和边界情况。
- Main 侧对应:`<domain>` 的数据库访问集中在一个 repository 类里,IPC handler 只做校验和转发。
- 领域数据库访问集中在 repository,目标架构中由存储宿主执行;Main 的 IPC handler 只做校验并等待领域操作。受管只读 Worker 与远端 Agent 按各自明确的连接归属执行,不能按源码目录把它们误判为 Main 访问。
- 多项目/多会话流式更新只通知相关领域与实体;后台子任务不得让全部项目视图重渲染。隐藏视图保留草稿、选择和必要执行订阅,停止无关绘制与全量重读,切回时按已有摘要及增量恢复。
### P3 高频数据走专门通道,按帧合并
@@ -62,12 +67,16 @@ Main 负责窗口、生命周期、权限和消息转发。数据库查询、文
- 多条更新合并后,每帧最多提交一次;同步到全局状态的频率要更低,目前是 250 ms。
- 高频路径上不能读取布局(`getBoundingClientRect`、`scrollHeight` 等),布局读取要集中到每帧一次。
- 合并更新不能丢失、重复或打乱顺序。每条数据带序号,并有测试覆盖。
- 显示合并与持久化分开。可以合并进度或替换过时刷新,不能静默丢弃消息、工具结果或需确认的事件;多生产者缓冲只有在用户设置、外部协议、数据安全或实测故障证据要求时才设置容量,否则将负载交给所属 Host。
### P4 数据只有一份来源,只传变化部分
- 每类数据只有一个权威来源,由数据所在进程负责持久化。界面不把整份数据写回。
- 跨进程的通知直接带上变化的内容,接收方不再“收到通知后重新查全量”。
- 写入合并成批次和事务,不按每个事件单独开一个事务。
- 一个数据库有一个业务写入所有者,完整事务不跨 IPC 拆成单条 SQL。Main 不保存第二份可写业务状态;依赖写入的读取等待对应提交,远程 ACK 等待所需事件与 checkpoint 提交成功。
- 缓存必须说明归属、失效和释放条件,不按浏览过的项目/会话无限积累。查询返回摘要、分页或所需详情,保持完整信息可访问,不用截断历史满足预算。
- GoodBuddy 自身的中转数据优先使用有界内存;已有完整记录或文件时复用引用,避免重复落盘。预览、最终回答与工具全文分别核实,不能以“已保存消息”推断全文已保存。需要超出内存保留的内容直接使用所属存储的唯一正文,分页读取同一内容,避免临时全文再复制到正式存储。必要文件及清理范围遵守[输出与临时资源设计](./desktop-storage-direction.md#output-and-temporary-resource-ownership)。
### P5 工作量和可见内容成正比
@@ -80,22 +89,26 @@ Main 负责窗口、生命周期、权限和消息转发。数据库查询、文
- 首屏之前只做显示应用壳和恢复上次界面所需的工作。
- 其他初始化(Provider、Runtime、知识库、通知渠道等)按需执行,或等首屏显示后再空闲执行。
- 后台模块在第一次使用时才加载。
- 冷启动、首次进入功能和暖返回分别验收;预加载只使用有界空闲工作,不能为每个项目预建进程、连接或整份历史缓存。进程 spawn 不代表服务就绪,首次可用反馈与数据实际可用分开记录。
### P7 先测量,再修改
- 先跑测量,拿到数据再改代码;每项改动都要有前后对比,基线用临时 worktree 构建。
- 每个版本至少跑 3 轮,交替运行;结果差异小于同一版本多次运行之间的波动时,不能说成“有提升”。
- 测量用隔离的用户目录和本地假模型,不发出外部模型请求,结果里不含用户内容。
- 必测多项目、多会话、多子任务及导入/检索/回顾发布重叠场景,负载和采样按[并发验收](../quality/concurrent-desktop-acceptance.md)。记录 offered load、实际吞吐、排队和排空、资源高水位;不得降低输入速率、串行化夹具或丢事件来取得流畅结果。
- 基准使用固定版本、数据和环境。性能测量与真实模型功能验收分开;受发布构建限制时遵守发布手册,不以性能原则授权额外生产打包。
### P8 正确性优先于速度
- 事件顺序、持久化、取消、崩溃恢复和显示结果都不能因为性能而改变。
- 异步改造要先确认“先写入、后读取”的顺序,没有测试覆盖就不合并。
- 风险高、收益小,或者会改变现有功能的优化先不做,在计划文档里记录理由。
- 取消请求、执行结算和容量释放不是同一时刻;调用方停止等待不能提前释放仍运行的工作。取消一个会话或子任务不得杀死仍服务其他任务的共享宿主。已提交事务如实报告成功,不伪装成回滚;未确认写入不盲目重放。
### P10 体验不倒退
性能优化只能让体验变好,不能用体验换速度。“行为不变”不仅指数据和结果,也包括用户能感知到的交互:
性能优化必须保留数据、结果和用户交互语义,不能用体验换速度:
- **动画和过渡保持原样。** 平滑滚动(如“到底部”按钮)、展开收起、悬停、淡入淡出都不能因为优化而消失。任何瞬间写入 `scrollTop`/`scrollTo` 的代码都会打断正在进行的平滑滚动,必须在平滑滚动期间暂停。
- **只渲染可见部分(P5)时,用户状态不能丢。** 行被卸载后,它的局部状态也会消失。以下行必须保持挂载,或者把状态存到行外:有焦点的行、用户展开过 `<details>` 或输入过内容的行、选中文本的两端、待回答的问题和待审批项、打开菜单或正在重命名的行。
@@ -109,6 +122,25 @@ Main 负责窗口、生命周期、权限和消息转发。数据库查询、文
- lint 规则和性能阈值是合并前的硬性条件,不靠人工记得。
- 规则只能收紧,白名单和上限只能往下调。
- 每个 agent 只提交自己改的文件和片段,不把别人未提交的改动带进自己的提交。
- 检查直接 SQL 导入及已知间接打开函数/构造点,按实际宿主区分测试、存储与远端代码。白名单和上限需要与已提交基线比较,不能只靠注释约定;全局同步回退同样要被发现。
- 普通 CI 与性能执行环境分别提供功能和预算证据;新增并发与首次进入场景纳入候选必需检查,缺少运行环境或结果时保留验收缺口,不由单测通过推断达标。
### P11 并发隔离与有界工作
同一会话的事件顺序和同一数据库的提交顺序必须保持;不同项目、会话、子任务的模型等待、重查询与清理不因此共用一把长时间全局锁。
- Main 全局控制队列不得等待模型、网络、原生启动或耗时清理。必要登记和状态提交可短暂串行,等待在所属操作范围内完成。
- 重查询复用有界 Worker;必要写入不排在整个导入解析或模型请求之后。长事务先减少事务内无关计算,再测写入排队,不把阻塞简单搬进唯一存储队列。
- 复用现有池、批处理和队列,记录在途数、排队数及实际资源使用。只有有明确依据时才设置容量;没有依据时将负载交给所属 Host,不因内部数字拒绝用户工作。有实测饥饿才引入最小调度调整,不预建通用优先级框架。
- 控制和取消不等待新的执行槽位;如果外部或用户设置要求排队,按该语义执行。禁止静默丢失、无限重启或自动扩增进程;不能把未经证明的队列上限包装成稳定性。
- `ReadonlyQueryReader` 的生产 storage 模式不传 `maxPending`,因此请求直接透传给 storage host;其等待队列只服务显式有界的调用方和测试,不构成第二个 storage owner。
- 每个异步响应携带已有项目/会话/任务/操作归属;迟到响应不得覆盖新选择或新操作。归属检查先于改变共享请求代次、缓存或队列状态。
### P12 架构完整性与 KISS
一次性交付按最终职责边界验收,不以全面 Promise 化或局部热点迁出代替。目标范围内正常、后台、失败、重试、取消和退出路径均使用同一权威实现,清理原直接入口和回退。
保持现有技术栈、数据库文件及业务合同,复用已有资源机制。不得顺带新增分布式事务、持久 RPC receipt、通用恢复框架、每项目独立服务或产品审批模式。同步内存缓存读取可以保留,不把纯函数和每个 getter 远程化。设计新增机制必须指出当前失败、现有机制的不足与对应验证,不以“将来可能并发”代替证据。
## 3. 评审清单
@@ -125,17 +157,30 @@ Main 负责窗口、生命周期、权限和消息转发。数据库查询、文
9. 动画、平滑滚动、过渡还在吗?有没有新增会打断平滑滚动的瞬间滚动写入?
10. 只渲染可见部分的列表,滚走再滚回来,展开状态、输入内容、选区、焦点还在吗?
11. 合并或延迟处理后,第一次响应(打字回显、点击反馈)有没有变慢?复制、导出读到的是不是屏幕上的最新内容?
12. 多项目/多会话/多子任务同时运行时,取消或清理一个请求是否拖住其他任务?队列和缓存是否持续增长?
13. 存储提交、远程 ACK、调用方返回和资源释放的顺序是否有真实路径及故障注入验证?
14. 是否删除目标范围内的旧直接访问和同步回退?新增进程或调度机制是否复用现有设施并有实际必要性?
15. 请求或输出是否产生非必要临时文件、重复全文?是否核实完整内容与预览的区别,并验证分页、引用释放、必要文件的创建失败清理及升级后的残留回收?
## 4. 当前差距
截至 2026-10-04(第一期结项,见 `docs/review/performance-phase-1-report-2026-10-04.md`),按原则对照的主要差距:
截至 2026-10-05,以下状态结合[存储升级实施记录](../quality/storage-upgrade-implementation-2026-10-05.md)和[并发验收规范](../quality/concurrent-desktop-acceptance.md)核对。表中“已实现”只表示代码或定向测试已有证据,不表示 PERF-15、PERF-16 或 C01-C07 已完成。
| 原则 | 差距 |
| --- | --- |
| P1 | 同步数据库调用仍在 Main,没有独立数据进程;但热点读已在 worker,高频写单次 0.1–0.3 ms,WAL checkpoint 在后台线程,启动恢复按索引读取。文档解析、分块和大文件哈希已移到 worker。消息附加数据冗余(子任务工具输出、`tools` 与 `blocks` 重复),大会话读取约 170 ms |
| P1 | Assistant、Knowledge、文件和 Runtime 生产域已由 `DesktopStorageClient` 接入 utility process;剩余缺口是全量间接访问扫描、异常/维护/退出路径核验,以及 Main 阻塞和存储排队的并发测量。文档解析、分块和大文件哈希仍在各自 worker。 |
| P2 | `App.tsx` 约 7,900 行。输入区、隐藏路由、任务和结果事件、切换会话已不再整体重渲染 App;当前会话的流式吸收(每 250 ms)、队列和活动记录仍经过 App |
| P3 | 已基本满足:流式输出和终端输出都已按帧合并 |
| P4 | 活动记录已改为增量保存、按页读取(界面只保留已加载的页,计数和分组来自 Main 的汇总);部分 `*Changed` 通知之后还会重新查全量 |
| P5 | 已基本满足:消息、会话、笔记、文件树、知识库文档表格都只渲染可见部分 |
| P6 | 空配置启动到输入框可用约 0.96 秒;真实数据(2.4 GB)约 1.36 秒。本地工具环境已后台初始化,低频模块按需加载 |
| P9 | 性能阈值还没有接入 CI,也没有真实低端机的基线 |
| P3 | 流式输出和终端输出已有按帧合并;C01/C02/C06 的长期负载仍需验证是否出现排队增长、长帧或内容丢失。 |
| P4 | 存储域已统一到各自 owner,输出 backing 和附件引用已接入;部分 `*Changed` 通知仍会重新读取全量,重复缓存和跨域通知的并发影响待测。 |
| P5 | 消息、会话、笔记、文件树、知识库文档表格已有窗口化;C07 仍需记录冷/暖内容可用时刻和切换后的状态保留。 |
| P6 | 空配置和真实数据启动有历史测量;当前 C07 尚未完成监督概览、图谱、历史选择和来源正文的完整冷/暖验收。 |
| P9 | 已有 lint 和历史回归上限;间接 SQLite 打开、白名单单调收紧、并发指标缺失和 CI/性能环境接入仍待补齐。 |
| P10 | 已有局部输入、刷新和项目切换修复;需要在 C01/C02/C06 中继续验证动画、焦点、滚动、草稿和取消语义没有因降载或合并而退化。 |
| P11 | 存储请求已移除产品侧 admission 上限并透传给 storage host;运行时仍有若干并发与消息容量数字,需逐项证明属于用户设置、外部协议、数据安全或实测故障约束,否则应删除。C02、C04、C05、C06 尚未证明健康 peer 隔离、故障恢复和队列排空。 |
| P12 | 目标边界和 KISS 约束已写入架构文档;S01-S07 已有生产组合,但删除旧直接入口、异常路径和全候选统一验收仍未完成。 |
2026-10-04 补充:监督回顾的来源初始化和继续前版本校验已移入现有 Worker 入口的独立连接,扫描不持有写锁,清单按 200 条提交,生产失败不回退 Main 同步扫描。最终发布事务仍在 Main;实现与实测边界见[监督者进度](../features/conversation-supervision/progress.md#2026-10-04-回顾初始化阻塞修复)。
2026-10-05 局部修复记录:监督者 UI 的概览、图谱、故事读取也已迁到现有只读 Worker;概览按需加载、列表窗口化及摘要缓存见[KISS 局部修复](../review/kiss-local-fixes-2026-10-05.md)。这与工具侧 Story Graph 读取是不同入口。Main 最终发布及部分大响应仍有超预算样本,不能把局部完成视为全项达标。
随后用户确定按[完整存储与并发架构](./desktop-storage-direction.md)一次性交付。当前已完成存储 owner、主要生产组合和部分输出迁移,但没有完成全面架构验收。P11/P12 与[并发验收矩阵](../quality/concurrent-desktop-acceptance.md)约束当前候选的剩余验证,不能作为已有实现或实测结果;上述第一期历史状态也不替代当前验收。
+219 -16
View File
@@ -1,8 +1,18 @@
# Runtime Storage And Background Reads
## Current Implementation
This file records current runtime timing, reader behavior and temporary-resource
ownership. The production runtime receives the storage-owned output backing store and
output adoption function during Main startup. Main also computes one per-version,
per-run temporary root and passes it to the local runtime cache paths and native
client coordinator.
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.
Persisted evidence is not necessarily full tool output; the inventory below records
the current distinction. The target ownership rules are in the architecture document.
Startup storage upgrades run in `assistant-storage-worker` before the business
connection opens. Runtime readers must not invoke initialization, migrations or
@@ -10,20 +20,22 @@ recovery. `SubagentProgressStorage` caches event encoding state, not task statis
## 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.
Execution duration is persisted on running-state transitions in Main. Snapshots
read small indexed timing rows; they require no historical evidence query, worker
or result cache. Main owns current segments independently of renderer visibility.
The [duration design](../features/task-and-job/technical-design.md) defines the
state boundaries, restart handling and incomplete history.
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.
## Planned Architecture
The [complete desktop architecture](./desktop-storage-direction.md) defines the
remaining target, including desktop binding databases and native-client model ledgers.
Its migration and [concurrent acceptance](../quality/concurrent-desktop-acceptance.md)
are one delivery; remote Agent database ownership remains unchanged. Main will still
observe runtime transitions while the storage service persists them in order; the
indexed timing representation and semantics remain.
## Runtime Read Lifecycle
Read transactions end before replies are posted. Long-lived read transactions
would prevent WAL checkpoints from reclaiming old pages. Each reader has its own
@@ -36,9 +48,200 @@ rules, without introducing a general query protocol before a second use case exi
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.
Duration snapshots refresh on Main change notifications while the task center is
open. The renderer ticks locally for open segments and fetches fresh snapshots on
reopening. Results remain scoped; superseded responses are 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
snapshot contract and timing rules. Tests and timings demonstrate only their fixture
conditions; they do not establish a latency guarantee for arbitrary retained history.
## Output And Temporary File Investigation
Source review on 2026-10-05. The inventory records the current production flow. No
user Temp directory was scanned or deleted.
| Producer | Current content flow | Retention and consequence |
| --- | --- | --- |
| Local process and ripgrep | [Process capture](../../src/main/agent/direct-model-process-service.ts) writes stdout/stderr through [PagedOutputStore](../../src/main/agent/paged-output-store.ts); model receives a JSON-bounded preview, up to 96 KiB per stream, and a reference when truncated | Every stream opens a file, including empty stderr. Small files are deleted on finish; retained files and their in-memory handles belong to the conversation/service |
| Direct-model subagent | [Subagent service](../../src/main/assistant/direct-model-subagent-service.ts) captures child text through the same store, returning up to 192 KiB and an optional reference | [Provider event bridge](../../src/main/agent/model-tool-provider.ts) forwards preview/status but drops the output reference from the subagent event; child text is not an independent full parent-history record |
| Tool events and conversation history | [Model runtime](../../src/main/agent/model-runtime.ts) emits at most 16,000 characters of serialized tool result as the event preview; [IPC](../../src/main/ipc.ts) and [database](../../src/main/assistant/assistant-database.ts) persist the supplied event | Tool events still carry bounded previews, while output history adopts finished handles through attachment storage. Even `output_read` responses are previewed in tool events. Persisted event history does not by itself guarantee a complete stdout/stderr copy |
| Remote ACP | [Agent transcript](../../src/agent-daemon/semantic-prompt-store.ts) retains received notifications pending ACK; [desktop adapter](../../src/main/agent/acp-remote-runtime.ts) bounds mapped events before persistence | ACK prunes Agent transcript rows. Notifications themselves may already be bounded by the Runtime. Do not treat this journal as a permanent full-output source or remove commit-before-ACK |
| MCP calls | [MCP transport](../../src/main/capabilities/mcp-client-transport.ts), provider and [gateway](../../src/main/agent/knowledge-mcp-gateway.ts) pass objects through stdio/HTTP/SSE | No per-call request/response scratch-file creation was found in these paths. Calling a tool over MCP does not itself require a payload file; third-party server internals are outside this inventory |
`PagedOutputStore` keeps short and empty output in memory and delegates larger
or JSON-preview-truncated content to an injected `PagedOutputBackingStore`. Capture
has a shared 8 MiB budget, at most 1,024 pending chunks per writer and 4,096 active or
retained handles per store; backing writes are at most 64 KiB. Producers must await
append, or receive an explicit capacity error. Local process pipes pause until each
append settles. The store does not create a temporary file backend or another DB.
UTF-8 byte cursors, JSON-escaped preview bounds, owner checks and failed/cancelled
output remain covered by focused tests using real async files and local children.
The production owner exposes output create, append, finish, read, adopt and release
through the existing storage transport. Main passes its `backingStore` to the runtime
and passes `outputAdopt` to the tool provider. When output history is enabled, finished
stdout/stderr references are adopted into the conversation attachment reference for
the target message; the bounded preview and output reference are returned together.
Missing backing configuration still explicitly fails capture that requires full
storage. A model's final answer is not an equivalent copy of its tool output.
### Planned Architecture And Remaining Acceptance
The planned architecture still requires full end-to-end history and restart coverage,
resource reconciliation and the remaining storage-owner migration. The file-backed
test fixture alone does not establish that architecture or its acceptance.
Tool previews can also appear in both conversation `tools` and `blocks`. This is a
separate persisted-preview duplication issue, not evidence that full paged output is
already stored. Existing [subagent progress compaction](../../src/main/assistant/subagent-progress-storage.ts)
already handles repeated progress snapshots; retain that implementation.
### Other Owned Files
| Existing owner | Source finding | Disposition for the upgrade |
| --- | --- | --- |
| [DocumentResultStorage](../../src/main/document-result-storage.ts) and [attachment storage](../../src/main/conversation-attachment-storage.ts) | `parsed.md` duplicates `manifest.json.content`; normal reads use the manifest and `copyResult` copies the extra file | Remove the redundant new write/copy. Preserve original files, extracted images, manifests and ownership transfer; old directories follow existing reference cleanup |
| [OpenCode runtime](../../src/main/agent/opencode-runtime.ts) | Configuration already uses `OPENCODE_CONFIG_CONTENT`; scratch supplies isolated XDG state and skill registration | Keep required isolation, reuse immutable skill caches. Do not invent a config-file optimization for a path already using memory/environment |
| [Continue adapter](../../src/main/agent/continue-host-adapter.ts) | CLI uses `--config` plus an isolated global directory; creation can fail before the outer owner receives the path | Retain the external-file interface and clean partial creation at the creating function, including skill-staging failure; preserve pre-existing files on `EEXIST` |
| [Continue helper](../../src/agent-daemon/continue-acp-helper.ts) and [model bridge helper](../../src/agent-daemon/model-bridge-helper.ts) | Temporary config/plugin resources use helper finalization; setup before the protected scope and termination need lifecycle coverage | Extend existing ownership and shutdown, verify child exit before deletion. Plugin file URLs and Unix sockets remain necessary interfaces under current contracts |
| [Managed Python](../../src/main/local-tool-environment/managed-python-install.ts), [model packages](../../src/main/model-package-utils.ts), [Agent packages](../../src/main/remote-agent/agent-package-manager.ts) | Install staging, partial downloads and backups support validation/publication; existing cleanup has owner-specific patterns | Extend those cleanup functions for demonstrated leftovers; preserve active staging and verified reusable installations. No global installer sweep |
| [Magic note storage](../../src/main/magic-notes/magic-note-storage.ts) | Sibling atomic-write `.tmp` files protect replacement; memory-database fixtures have a separate temporary root | Keep atomic replacement and normal failure cleanup. Test scratch follows repository rules; persistent note directories are user data |
Document results may move from temporary ownership into attachment references, including
shared branch references. A directory under `temp/document-parsing` can still be owned
by persisted data. Its location alone does not authorize deletion. Native client history,
model caches, database WAL/SHM and exported user files are likewise not disposable scratch.
Some tests/build helpers share production-looking prefixes, including `goodbuddy-process-*`
and `goodbuddy-opencode-*`. Prefix and age alone cannot establish who owns a legacy directory.
The [architecture rules](./desktop-storage-direction.md#output-and-temporary-resource-ownership)
define the replacement and cleanup scope; the [acceptance matrix](../quality/concurrent-desktop-acceptance.md#输出与临时资源验收)
defines evidence required before claiming improvement.
### Creator Cleanup Validation (2026-10-05)
Continue now removes its partially written config and failed skill-staging directory
at the creator, before returning ownership to the run. Exclusive creation failures
leave existing files/directories intact. Run finalization preserves prepared caches
and external configuration. Both Agent helpers protect setup with finalization and
handle SIGTERM/SIGINT; Continue stops prompt admission, aborts and drains active
runs, while the OpenCode helper waits for child exit before deleting its plugin.
Managed Python's existing pre-install cleanup also removes regular download files
with the exact UUID layout, preserving directories, unknown names, caches and history.
Validation in the concurrent working tree:
- Five focused suites (`continue-host-adapter`, `continue-acp-helper`, `model-bridge`,
`managed-python-install`, `local-tool-environment-service`): 81 passed, 10 Unix-only
cases skipped on Windows. Failure injection covers partial creation, EEXIST,
failed skill staging, setup-time signals and shutdown cleanup.
- Final `npm run typecheck` passed; ESLint passed for all eight owned source/test files.
Repository lint passed with `npm run lint -- --ignore-pattern temp`; unfiltered
lint encountered generated validation bundles in ignored task/release directories.
Task-owned `temp/goodbuddy-scratch-upgrade` was subsequently removed and its absence
verified; unrelated release scratch was preserved.
- Full Vitest run: 517 suites passed, 7 failed, 15 skipped; 6,244 tests passed,
6 failed, 94 skipped. Failures outside this cleanup scope included OpenCode config
preparation (`spawnSync npm.cmd EINVAL`), the MCPVault Electron fixture, and
message-expansion and workspace-import Electron assertions. These results do not
establish full architecture acceptance.
- Reused the existing `a01-runtime-host-build.cjs`/`a01-runtime-host-run.cjs` harness
logic, rebuilding Agent, daemon and Desktop driver from current source under
`temp/goodbuddy-scratch-upgrade`. Connected to Linux x64 over the LAN using
GoodBuddy encrypted credentials and the pinned Host identity. Both routes were
reachable; only the LAN route was used for execution.
- Both OpenCode and Continue passed real tool execution, startup cancellation,
disconnect/reconnect during execution, checkpoint persistence/replay deduplication,
and retirement. Final-source cancellation took 217 ms and 233 ms respectively;
each run reported zero owned processes after retirement and shutdown. There were
eight real model requests across the initial and final runs, four per runtime.
Remote artifacts stayed in dedicated `/root/tmp/gb-lifecycle-*` directories and
followed the harness cleanup. These timings are scenario observations, not a
comparative performance claim.
### Version And Run Scratch Follow-up (2026-10-05)
`runtimeTemporaryRoot(userDataPath, appVersion)` computes the lazy
`temp/goodbuddy-runtime/<version>/<runId>/goodbuddy-runtime-launch` path.
Main passes that same value through existing `continueHostCacheRoot` and
`opencodeSharedCacheRoot` options. The native client coordinator receives it as
`temporaryRoot` and passes it to both native clients. The existing constructors need
no new parameter.
OpenCode creates its isolated launch directory beneath the supplied shared root;
Continue puts generated configuration and isolated globals in one per-call directory.
Prepared bundles and immutable Skill/config caches remain reusable within the run.
The helpers on a remote Host use `goodbuddy-runtime-launch` beside their existing
model-bridge socket, under that process owner's scratch directory. Global `TEMP`
is unchanged.
Each local launch has a sibling owner file containing creator and child PIDs. Keeping
metadata outside the deleted tree preserves retry evidence after partial deletion.
The launch root also records its creator for cache-only reclamation. After the
single-instance check and first screen, call `cleanupRuntimeTemporaryDirectories`
in the existing delayed background startup work. It examines at most 256 directory
entries per invocation, sequentially, and accepts the startup shutdown signal.
Service disposal still stops children before deleting its own launch material;
an unconfirmed child exit retains the material. Startup preserves live/reused PIDs,
permission-denied process checks, malformed ownership and the ambiguous spawn-to-PID
recording window. Empty version/run parents are removed after owned material.
This is not a new process owner or an installer collector.
Legacy candidates are limited to the known OS-Temp layouts `goodbuddy-opencode-*`
(desktop isolated state), `goodbuddy-continue-*` (Continue helper), and
`goodbuddy-opencode-plugin-*` (model-bridge plugin). Deletion requires valid owner
metadata and confirmed inactive creator/child PIDs. Releases that wrote no such
metadata cannot be identified as obsolete from their names, contents or age alone;
those directories are retained and reported. A bounded pass can leave candidates
unvisited. Native-client histories, existing version-only caches and unrelated
OS-Temp files are outside this reclamation contract. The old version/layout-only
cache sweep is not evidence that a surviving child has exited.
Remote environment preparation now retains its existing pending-operation record
when connection acquisition or staging cleanup fails. Because that store has one
record per Host, a replacement prepare retries cleanup before overwriting it;
confirmed cleanup or `operation-unavailable` clears it. Already adopted environments
remain usable. Agent package delayed removals retain their pending entry until
deletion succeeds, retry on inventory access, and rediscover verified obsolete
versions through the existing package scan after restart. Active archive leases
still prevent deletion.
Continue bundle staging now waits for all parallel writes to settle before failure
cleanup. Existing settings atomic replacement and selected-runtime diagnostic
teardown already use `finally`; no global `.tmp` sweep or new diagnostic owner was
added. Native clients now accept an optional `temporaryRoot` for the same application
version/run launch root. `NativeTerminalClient` keeps histories and reusable caches
under its existing `rootDirectory`; only per-launch material moves to scratch.
DS Web keeps `DSH_HOME`, sessions and user preferences in place and supplies its
generated configuration through `web --patch <scratch-file>`. Its ledger also lives
in scratch. Both reuse the existing sibling PID metadata and await ledger close
before releasing material; unconfirmed child exit retains it. ConPTY registration
waits for the actual child PID, which can be unavailable immediately after spawn.
The collector recognizes `goodbuddy-native-dsh-*` alongside the existing prefixes.
The production wiring propagates `temporaryRoot` through the native coordinator to
both constructors without relocating the retained-history root.
Focused verification passed 60 tests, including a real Windows PTY, official DS Web
session persistence across restart, and delayed ledger-close cleanup. Typechecking
and scoped lint passed; this native follow-up made zero real model calls.
Final-source verification for this follow-up is separate from the earlier eight
real requests above: two real provider requests total, one per runtime. Current-source
Continue/OpenCode helpers ran on Linux x64 over pinned SSH, each executed its native
command tool, returned the real model answer, and removed launch files after exit.
A separate active-request shutdown probe passed for both helpers with zero real
provider requests. Four successful scenarios used deterministic command responses;
probe setup retries before these results did not reach a provider. A previous-process
version/run directory was reclaimed using actual dead PIDs. After the final
enumeration-error guard, a separate Host probe confirmed that the current collector
retains a real live child and reclaims that same run after its exit, with zero model
requests. The timed-out probe's ledger was audited and contained zero calls; all
task probe artifacts were removed locally and remotely. These probes cover the
changed helper and scratch lifecycle, not full Desktop UI or installation acceptance.
The seven primary suites passed 267 tests with 10 platform skips; related runtime,
native-terminal and diagnostic-owner suites also passed. Agent and web typechecks
passed. Full desktop typecheck and repository lint remain blocked by concurrent
`portable-storage-upgrade-fixture.ts` typing and `portable-storage-upgrade.mjs` lint
errors; unfiltered lint also visits generated files under `temp`. Focused lint for
the edited source/tests passed. The C01-C07 concurrent acceptance matrix remains
incomplete; these results do not establish full storage-architecture acceptance.
@@ -401,7 +401,7 @@ ShareServer 可以部署到公网,因此公网连接、外部组织和服务
4. ShareServer 按组织隔离查询,搜索和计数接口不能泄露无权查看的设备或能力。
5. 设备只建立主动配置的网关出站连接,不接受 ShareServer 发起任意系统调用。
6. 局域网发现不等于信任,组织成员身份也不等于所有能力授权。
7. 远程能力不能扩大本机 Ask/Execute、Workspace、Runtime、Skill 或 MCP 现有权限。
7. 远程能力不能扩大本机 Workspace、Runtime、Skill 或 MCP 既有能力范围,沿用统一执行规则。
8. 密钥只进入对应安全存储,诊断和审计使用固定字段与脱敏错误码。
9. 限制握手、清单、帧、文件、并发、速率、任务时长和审计查询大小。
10. 撤销设备、用户、Grant、Publication、Package 或 FederationTrust 后停止签发新令牌。
@@ -0,0 +1,188 @@
# Agent 功能维护约束
本文面向在 GoodBuddy 中新增功能的 Agent。它只记录做决定时必须遵守的维护规则,
不替代架构设计、性能规范或功能文档。若本文与代码或权威文档不一致,以已验证的生产
代码和下列权威文档为准,并修正文档链接或内容:
- [桌面存储与并发执行](../architecture/desktop-storage-direction.md):职责边界、存储所有权和操作边界。
- [桌面存储基础集成](../architecture/desktop-storage-foundation.md):`DesktopStorageClient`、domain operation 和生命周期。
- [桌面 Runtime 存储集成](../architecture/desktop-storage-runtime-integration.md):binding、model-call ledger 和关闭顺序。
- [性能原则](../architecture/performance-principles.md):唯一的性能数值、并发规则和评审清单。
- [桌面并发架构验收](../quality/concurrent-desktop-acceptance.md):真实生产路径、负载和证据要求。
- [运行时存储与后台读取](../architecture/runtime-storage.md):输出正文、预览、临时资源和当前迁移状态。
- [数据库迁移与大数据升级指南](./database-migrations.md):已发布用户数据的迁移边界。
- [AGENTS.md](../../AGENTS.md):临时文件、真实 Host 验证、测试和文档维护规则。
## 决策检查
开始编码前,逐项回答下面的问题。任一项没有答案时,先补充设计或查找现有机制,
不要直接增加新的 owner、队列、缓存或恢复记录。
- 数据的唯一权威来源和写入 owner 是什么?它是否已经存在?
- 该功能是否需要数据库事务、跨表快照、文件引用或运行时 ledger?若需要,操作是否落在对应 owner 内?
- Main 是否只负责 IPC 校验、凭据、生命周期和调度?是否把 SQL、大文件读写、哈希或模型工作留在了 Main?
- 新操作能否使用现有 domain port 和显式 allowlist?是否误把 SQL、回调、实例、函数或 handle 放进跨进程 DTO?
- 依赖链是否按“先提交,再通知、ACK、读取或返回成功”的顺序实现?提交未确认时是否禁止盲目重放?
- 排队取消、在途取消、执行结算和资源释放分别是什么时刻?容量是否一直占用到实际结算或确认的 Host 退出?
- 输出是预览还是完整正文?完整正文是否只有一个所属存储,引用是否随历史、分支和重启继续有效?
- 是否创建了临时文件?创建者、使用者、删除条件、子进程退出确认和删除失败后的重试位置是否明确?
- 新的操作数、排队数、字节数、句柄数和缓存是否有界?是否复用了已有预算并避免无限缓冲?
- 测试是否覆盖真实 UI 到生产实现的路径、失败、取消、重试、关闭和迟到响应?是否需要真实数据、模型或 Host?
- 受影响的架构、功能、质量、构建或运维文档由谁维护?是否已有同一事实的权威定义,新增内容能否只链接它?
## 存储所有权
桌面业务 SQLite 由一个 storage utility process 持有。Main 通过
`DesktopStorageClient.call(domain, method, args, { signal })` 使用显式操作;
`src/main/desktop-storage-contracts.ts` 的 allowlist 是可调用面的边界。Main 不得再
打开同一业务数据库,也不得通过 helper、同步回退或隐藏的 repository 构造绕过 owner。
远程 Agent 的数据库仍由远程 Agent 持有,不能因桌面存储改造而迁移。
新增持久化时,优先扩展已有 domain repository 和 owner operation。完整事务、跨表快照、
去重、checkpoint 与发布规则留在 owner 内;模型、网络、解析和文件准备在事务外完成,
进入事务后只校验当前前提并提交完整结果。需要多个语句的读快照,应增加一个有名字的
owner/reader operation,不要在 Main 中拆成多次 `await`。
Main 侧服务只持有异步 port 和普通 DTO。live repository、数据库连接、事务 callback、
订阅、函数、AbortSignal 和运行时 handle 不跨 utility transport。`StorageData` 允许的
值以当前契约为准;新增类型先确认序列化、大小计算和错误传播,再加入 allowlist。
文件和输出也有 owner。附件引用、document result、paged output backing、Runtime
binding 和 native model-call ledger 应通过既有 storage facade 访问。文件所在目录不等于
可删除的临时目录;持久化引用、WAL/SHM、native history、用户导出文件和仍被分支引用的
正文都必须按 owner 释放。
## 增加领域操作
按以下顺序定位扩展点:
1. 找到已有 repository、port 或 facade,以及它实际打开的数据库和文件。
2. 在 owner 内保留原有事务和 ID、顺序、去重、耐久性语义,必要时抽出可复用的内部函数。
3. 为外部调用增加一个有明确名称的 domain operation,更新类型 allowlist 和 transport handler。
4. 在 Main 组合处注入已有 typed port;调用者等待 Promise,不保存第二份可写事实。
5. 为提交前、提交后响应前、取消、失败、重试、关闭和重启增加针对性断言。
操作应表达业务动作,例如 `appendRemoteTaskEventsBatch`、`publishDocument` 或
`transition`。不要暴露 `executeSql`、任意方法代理、通用对象代理或把 repository
整个返回给调用者。一个已有操作能表达需求时,扩展其参数或返回值并更新权威文档,
不要平行创建同义 operation。
通知不是提交确认。持久化成功后才能发送依赖该事实的 change notification、远程 ACK、
模型下一轮输入或成功响应;通知失败要按现有语义处理,不能把已提交事实伪装成未提交。
## Main 与 utility 的边界
Main 保留窗口、可信 sender 和 schema 校验、凭据解密、网络与 Runtime 编排、OS API、
请求归属和进程生命周期。utility storage 保留业务 SQLite、owner-local 事务、升级后
的读 worker 生命周期和必要的同步 SQL。Parser、OCR、embedding、模型请求和远程 Agent
继续使用各自宿主,不因“改成异步”而塞入 storage 事务。
Main 里的同步数据库调用、同步文件 IO 和可能超过 16 ms 的计算都要按性能原则处理。
Promise 返回本身不能证明工作已离开 Main。启动顺序是先完成 storage readiness,再发布
依赖它的服务;关闭顺序是先停止生产者并等待必要的持久化,再关闭 ledgers、bindings、
读 worker、数据库 owner 和 utility process。失败或未确认退出时保留可能仍被 child
持有的文件,并将错误传播给调用方。
## 异步顺序与取消
为每个操作写清四个时点:入队、开始执行、提交或执行结算、资源释放。最小必要范围内
保持顺序:同一会话事件按序提交,同一数据库事务按序落盘;不同项目、会话和子任务
不得共用长时间的全局等待。
取消规则如下:
- 排队中的请求在 dispatch 前检查 abort,取消后不得执行。
- 在途请求先向实际 owner 或 Host 发送取消,调用方可以先得到取消结果,但容量只能在执行结算或确认 Host 退出后释放。
- 同步 SQL 无法被 IPC 消息打断;安全批次之间检查取消,已经提交的事务必须报告为已提交。
- 取消一个请求不得杀掉仍被其他请求使用的共享 Runtime、utility process 或 Worker。
- 迟到结果必须携带已有项目、会话、任务或 operation 归属,先通过归属检查,再改变代次、缓存或队列。
- 存储进程替换前等待旧进程退出并结算旧请求;未确认写入按 domain identity reconciliation 处理,不能自动重放写入。
`AbortSignal` 是 transport option,不是业务 DTO 字段。不要用 UI 隐藏、组件卸载或清空
按钮代替服务层取消。控制和取消不得等待新的执行槽位。
## 输出引用与临时文件
短输出可以保留有界预览;超过预览上限的输出必须由所属 backing store 保存完整正文,
并在后续模型 `output_read`、历史和分支路径中复用同一 handle。分别核对模型收到的内容、
工具事件预览和历史正文,不能用最终回答代替工具全文,也不能把正文再次复制成临时全文。
输出 writer 的 append 必须被生产者等待;容量错误要显式传播,不能丢块或无限排队。
创建临时配置、插件、socket 或 launch 目录的函数承担创建失败清理。删除前关闭拥有
文件或目录的 child、ledger 和句柄,并确认 child 已退出;无法确认时保留材料和 owner
metadata,等待后续有界回收。Agent 和测试产生的验证文件使用仓库根目录 `temp/<task>/`;
产品运行时临时资源继续使用各自 owner 管理的运行时路径,不能因为位于仓库 `temp/` 就获得删除权。
远程 Host 使用专用 `/root/tmp` 子目录。不要扫描或删除用户 Temp、未知归属目录或仅凭名称和年龄
判断为过期的资源。
持久化目录可以位于 `temp/` 下,位置本身不能授权删除。删除前先检查 attachment、
document result、history、branch 或其他 durable reference;释放一个调用引用不能删除
仍被历史引用的正文。旧数据清理沿用所属 owner,不增加全局 Temp sweep。
## 并发预算
数值只引用[性能原则](../architecture/performance-principles.md),负载和证据只引用
[并发验收](../quality/concurrent-desktop-acceptance.md)。当前 storage client 的请求、
读写 pending 不设置产品侧 admission 上限,负载透传给 storage host;paged output 的 8 MiB capture、
每 writer 1,024 个 pending chunks、4,096 个 active/retained handles 和 64 KiB backing
write 也必须服从当前实现与测试。修改前先确认代码中的常量和适用范围,不在本文件复制
一套新数字。
只有用户明确配置、外部协议要求、数据结构安全,或已有实测证明不限制会导致崩溃、
数据损坏或不可恢复失败时,才设置上限。读写请求默认透传给所属 Host;不能用内部
并发数、队列长度或容量数字替用户决定吞吐。仍禁止静默丢事件、无限重启、按项目预建
服务、为每个调用新增 scheduler,或用串行夹具掩盖真实负载。
Worker 发生故障时可以进入已有退避和恢复流程;这不应被伪装成负载上限。正常排队的
解析、存储和 Host 工作不能因为一个未经测量的队列数字直接返回 busy。
`ReadonlyQueryReader` 同时服务只读 Worker 和桌面 storage client。只有调用方显式传入
`maxPending` 时,`waiting` 队列才承担有界等待语义;生产 storage client 不传该选项,
请求直接交给 storage host。这个共享类不是第二个 storage owner,不能因为看到等待队列
就给生产路径恢复默认 admission 上限。若未来实测证明某个边界需要容量策略,应先说明
资源、故障和测量依据,再拆出最小的显式 wrapper 或增加调用方配置。
## 测试与真实验证
单元测试用于确定字节边界、事务回滚、顺序、归属、取消竞态、失败清理和重试。行为改动
还必须覆盖生产组合:正常 UI action 经过 Preload、Main、storage/Runtime 宿主并产生
实际结果。mock bridge、独立 SQL、只测试握手或只测试 schema 都不能替代这条路径。
需要持久化时使用真实 SQLite owner 和隔离数据库;需要文件时使用真实异步文件;需要
Runtime 时使用本地确定性模型验证工具、事件、取消和关闭。不要把真实提供商网络速度
混入性能基准。需要模型能力证明时,配置检查不够,必须发起实际生成或工具请求,并
记录实际请求数和范围。
涉及桌面到 Agent 的 Runtime、安装、workspace、模型 bridge 或生命周期时,按
`AGENTS.md` 在共享 Linux x64 Host 使用当前源码验证。覆盖面应对应改动,包括断开、重连、
取消或重启;远程临时目录必须专用,验证后清理。未能连接 Host 时记录为验证阻塞,不能
用单测或旧发布包替代当前源码证据。
性能改动先取得基线,至少按权威验收矩阵记录 offered load、排队、吞吐、排空、资源高水位、
Main/Renderer 指标和样本条件。功能测试通过不等于并发预算通过;未测平台和缺失指标保留
为缺口。
## 文档与重复设计
代码行为改变后,检查受影响的架构、功能、质量、构建和运维文档,更新真正的 owner、
顺序、限制和验证证据。每项事实只保留一个权威定义:本文件写维护决策,架构文档写
职责与边界,性能原则写数值,验收文档写场景和证据,功能 README 或 progress 写功能
行为和状态。其他地方使用链接,不复制完整表格、状态机、预算或验收结果。
新增机制前先搜索同类名称、队列、owner、transport、shutdown 和测试。若现有机制只缺
一个操作、一个容量检查或一个清理分支,扩展它并补测试;只有现有 owner 无法表达真实
边界时才提出新设计,并在对应架构文档中记录问题、取舍和验证条件。未提交的实验代码、
测试夹具或接口存在,不等于生产路径已经接入。
## 允许与禁止的模式
| 允许 | 禁止 |
| --- | --- |
| 在已有 storage domain 增加命名 operation,并由 owner 完成事务 | 在 Main 新建同一 SQLite 的第二个连接、同步回退或隐藏 writer |
| 先在 Main 完成模型/解析,再把不可变 DTO 交给 owner 原子发布 | 在 SQLite 事务内 `await` 模型、网络、IPC 或文件回调 |
| `storage.call('assistant', 'appendRemoteTaskEventsBatch', [batch], { signal })`,提交后再 ACK | 暴露 `executeSql`、任意 repository proxy,或把 live repository/callback 放进 DTO |
| 使用同一 output handle 进行预览、分页、历史引用和 release | 每个工具结果写一个临时全文,再复制到 history 或用最终回答代替全文 |
| child 退出确认后删除创建者拥有的 launch material | 按目录前缀、年龄或路径位置删除未知归属的用户文件 |
| 取消在途请求并等待 settlement,再归还容量 | 调用方停止等待后立即释放槽位,或取消一个请求时杀共享宿主 |
| 用真实 owner、隔离数据、确定性模型和生产 UI 路径验证 | 只验证 schema、mock bridge、旧包或 utility ready 就宣称功能完成 |
| 更新一个权威文档,其他文档链接它 | 在多个文档复制同一预算、状态机、所有权表并分别维护 |
@@ -0,0 +1,93 @@
# Desktop 0.15.11 / Agent 0.15.4 发布准备
日期:2026-10-04。基线为已发布的 `v0.15.10`,冻结源码
`ea3d607fdd16fe24bc4be6a172884ef4d14ef244`。中断后按用户最新要求纳入
全部已提交改动,开始准备时工作区干净;不纳入随后出现的未提交工作。
标准六平台 Desktop、三平台 Agent,不含 LoongArch。
## 范围与升级边界
- 无模式执行:删除 Ask/Execute、安全设置分类及通用 toolApproval,
普通请求使用启用能力和执行账号权限,内部文本任务保持无工具。
旧 Ask 会话不再有产品只读隔离,必须在发布说明中明确。
- schema 59 增加状态计时和会话汇总索引,不扫描旧历史补算时长;
schema 60 按已知 JSON 路径分批清理工作模式,并删除三列。
保留正文、图片、稳定引用、远程去重身份;损坏元数据可见失败并保留已提交批次。
Runtime 设置 22 清理顶层 toolApproval,保持加密凭据字节,不递归删除同名用户字段。
- 升级前关闭应用,备份完整数据目录(数据库、相邻 notes 与设置),
大库可能耗时,回退须恢复升级前备份。0.15.10 的 NORMAL/FULL 边界不变。
- 远程 runtime/acp 能力从 5 升到 6,新增工作区 importFile 动作及时间字段;
Continue helper 采用原生 agent。Agent 必须升级 0.15.4,最低 Desktop 0.15.11;
wire major/minor 保持 2.0,Node/OpenCode/Continue 锁不变。
更新前处理活动任务,不声称覆盖旧操作跨版本恢复。
- 工作区分块导入二进制与空文件,同名不覆盖,失败项独立反馈;按名称和时间排序。
每目录最多读取 500 条,排序只覆盖已读条目,无传输中取消和百分比。
移除成果页不删除数据库成果,不自动把会话附件或图片复制到项目目录。
- 图片仅消息引用也可发现,生成关闭时仍可保存;远程专家保持父请求执行空间。
- 项目菜单默认全部项目、归档恢复和明确进入,新本地项目必选目录、
远程目录符号链接、OCR 提供方与 PPTX 图片位置诊断、完整输入缓存率。
- 回顾来源初始化和续跑移到 Worker;不宣称最后发布大事务已移出 Main。
历史选择与布局统一,真实区间按主故事着色,空档中性、无归属事件保留;
消息密度来自当前消息/小时桶,非历史快照,不补造故事归属。
## 证据与限制
审阅开发记录和临时原始验证说明:
- `docs/features/unified-execution/progress.md` 与临时
`opencode/unified-execution-host/validation.md`:13 次真实文本请求。
OpenCode/Continue 无模式、原生工具、detach、controller 恢复、重放、
取消和续发检查;Continue 首次原生工具尝试暴露 readonly helper,
改为 agent 后工具及取消场景通过,每轮所属残留进程为零。
- 临时 `opencode/expert-image-validation.md`:12 次真实文本请求,
包含两次预算不足的准备尝试各 2 次及最终两个 Runtime 各 4 次。
Host 保存、冲突、显式覆盖及替换 PNG 字节一致,桌面保存次数为零。
这些是已有开发证据,累计 25 次文本请求、0 次真实图片生成;
本次发布准备未新增提供方请求。
- 工作区 Host 探针:当前源码 Desktop 访问层、协议方法和真实文件系统,
700001 字节二进制及空文件、同名冲突、时间与归属通过,模型调用零。
不等于已安装签名 Agent 完整 attach/传输矩阵。
- 图片保存 Host 探针验证 SQLite 重开和仅 artifactIds 引用图片的分块保存;
专家验证使用测试 PNG,不是提供方图片生成。
- 监督图谱定向测试、真实只读备份和 Electron 几何验证见监督进度;
真实样本是点事件,不冒充真实持续区间着色证据。新增样式不影响 Agent。
- schema 60 合成迁移及真实 Worker 夹具通过,但用户生产旧库完整迁移取样、
外部委派服务兼容性和跨版本旧远程操作恢复尚未覆盖。
各性能数值仅限原报告路径、数据和机器,不作为普遍速度承诺。
## 已有 CI 与准备状态
`e205e909` 的 Desktop CI `37202987733`:6032 通过、56 跳过、3 失败;
Agent CI `37202987727` 通过。失败为监督旧 inline-error 断言、
窄栏 Escape 后状态未就绪、文件导入默认五秒超时及其清理竞态。
这些定向修正已经包含在 `ea3d607f`:改查通知回调的语气和消息,
等侧栏 inert 后再改变尺寸,导入多块真实文件测试专用 15 秒。
不跳过场景,不减少数据,不删除保存、冲突、键盘或布局断言。
最新开发验证不是本次精确候选全量 CI,不记为全绿。
按用户要求,不跑本地测试套件、类型检查、lint 或生产构建;
只校验发布元数据后推送,由 main CI 验证和构建。
通过后创建新不可变双版本标签,跟进原生打包和独立发布;
不下载、哈希或启动 Desktop 安装包,不发布宣传文章。
双语 FEATURES 与发布说明同步权限、迁移、配套升级和验收边界。
### 精确候选首轮 CI
`245e296a`:Desktop `37211139053` 为 6038 通过、56 跳过、2 失败,
Agent `37211139043` 通过;生产构建未执行。
App 旧测试仍假定回顾表单默认展开,改为点击“新回顾”后保留原按钮断言。
覆盖层堆栈通过仅编译测试 driver(不运行应用或生产构建)定位到窄转宽后的
侧栏按钮,先前 Escape 等待已通过;补齐宽高与 CSS 过渡结束后再原生点击。
保留 hit-test、侧栏 inert 和后续归档/弹层操作断言,等待下一轮 CI 验证。
`29af4090` 的 CI `37212673749`:上轮两项通过,6039 通过、56 跳过,
仅工作区导入 Electron 探针失败(Renderer 脚本执行异常)。
探针取消选择后按旧名称查询按钮并直接读 disabled,导入中的临时名称会使
查询为空;改为等待匹配按钮存在且可用,点击也使用同一就绪条件,并在脚本
异常中保留执行表达式和原始 cause。字节核对、同名保护、取消和旧图片保留
断言均保留;此为探针时序修正,不宣称修复产品导入故障。
`4e72aa04` 的 CI `37213768876`:导入及此前修正项通过,6039 通过、
56 跳过,唯一失败为活动分页等价测试默认五秒超时。该测试四个种子、
三种筛选分别渲染新旧组件共 24 次;仅将此项设为 15 秒,完整快照、
分组、数量、标题、状态和清空计数对比不变。
@@ -0,0 +1,74 @@
# Desktop 0.15.12 / Agent 0.15.5 发布准备
日期:2026-10-05。基线 `v0.15.11`,冻结源码
`2be57492e6de2d639597d0fde740a18b917d7dbb`,8 个提交、144 个文件。
检查时工作区干净,只纳入已提交实现和发布准备;保留后来出现的并行工作。
标准六平台 Desktop、三平台 Agent,不含 LoongArch。
## 范围
- Telegram 私聊配置、数字白名单、whoami 设置顺序、系统代理路径、
轮询恢复、待发回复、业务错误与跨层 retry_after;通道图片结果合同及钉钉错误修复。
- 自定义回顾天数和本地日期,包含结束日、今天截止提交时刻,
保留增量及显式重新整理规则。
- 回顾项目隔离、暂停后发布校验、失效故事目标拒绝;已删除会话从未发布
清单省略、混合批次和依赖导航重算,历史结果不变,已发模型输入不可撤回。
- 监督临时错误最多重试一次,认证/配置/取消/超时/容量不重试,
保留失败流用量,额外尝试可能增加费用。超容量中止保留已收到用量,
正文清空,最多再消费一秒用量事件,仍返回容量错误。
- 概览不预载图谱、来源详情按需加载、现有 Worker 重读取、索引与批量查询、
摘要缓存、选择列表窗口化及键盘焦点;发布快照只构造一次,仍在原 Main 事务。
- 长消息展开状态仅存用户覆盖项,删除来源清理;历史计时缺失不显示为零。
- 本地 OpenCode 提交失败及时终止;Agent 原生启动和路由清理不占全局队列,
迟到 Session/路由清理,保持取消、peer、回放去重。
独立守护进程生产实现改变,必须发 Agent 0.15.5,最低 Desktop 0.15.12;
wire 2.0/runtime-acp 6 和 Runtime/Node/Electron 锁不变。
## 数据与性能边界
schema 61 定向扩展 projects/conversations 通道 CHECK,事务中校验并恢复
writable_schema/defensive 设置,保留行、rowid 和索引;schema 62 仅新增监督索引。
升级前关闭应用备份完整数据目录(数据库、相邻 notes、设置),旧客户端回退需恢复备份。
没有进行独立存储服务迁移,不把架构方向文档写成已实现。
已审阅[性能原则](../architecture/performance-principles.md)与
[阶段审计](../review/stage-audit-fixes-2026-10-05.md)、
[KISS 实测](../review/kiss-local-fixes-2026-10-05.md)。
三轮交替测量仅限指定合成库、设备和源码路径。schema 61→62 的 55 表、
32947 行含 rowid 哈希一致及完整性检查通过,不代表全部用户库。
大批发布仍约 271~303ms、部分大响应和冷模块存在超预算样本,不宣称全部达标。
最终删除会话改动另有四文件 95 项回归,未做完整 UI 或大库删除延迟测量。
## 开发证据与发布验证分开
- 第一阶段审计:7 次外部文本请求(Host OpenCode 2、Continue 2、
本机 OpenCode 1、监督服务 2),0 次图片;6136 测试通过、87 跳过,
typecheck/lint 通过。Host 工具、断开恢复、Agent 提升、回放和启动挂起取消
经生产路径验证,结束后所属进程清零。
- K07/K08:8 次外部文本 HTTP 请求、均 200、0 次图片,含首次预算
不足尝试的 2 次;当前源码两 Runtime 的路由清理、peer、取消、工具、
恢复及去重通过,自有进程和目录清零。隔离暂存源码全量 6212 通过、
87 跳过,typecheck/lint 通过,不含之后的删除会话改动。
- 上述两阶段共 15 次外部文本调用属于既有开发记录,本次发布准备新增调用零。
- Telegram 内部链路有真实 Electron/IPC/SQLite 与 loopback API/模型证据,
真实 Bot 加模型完整收发仍待验证,旧独立 Node API 成功不替代该验收。
本次不使用凭据向外部收件人发送消息,不把内部测试当作真实平台通过。
- 最终精确候选仍需 CI 全量验证及生产构建;按用户要求不执行本地
测试套件、lint、typecheck、生产构建或安装包探针,只校验发布元数据。
## 发布检查
同步审阅并更新双语 FEATURES、Desktop 和 Agent 发布说明。
推送双远端、等待精确源码验证后创建不可变双版本标签;
公开核验六平台 Desktop 20 项资产/12 安装包、macOS 签名公证、双源索引、
网站 12 种标准选择,以及 Agent 三平台目录/签名和生产读取器。
不下载 Desktop 安装包,不发布已提交宣传稿。
## 精确候选 CI
`36e671a0` 的 Desktop CI `37273689682`:6254 通过、57 跳过、1 失败,
生产构建未执行;Agent CI `37273689788` 通过。
唯一失败为 Telegram Electron 探针启动时真实 safeStorage 不可用。
Ubuntu 验证环境增加 gnome-keyring/D-Bus,在独立会话中以随机临时密码解锁
CI keyring 后运行 sandboxed Electron 测试;不使用明文回退、不替换加密接口,
保留生产配置加密保存断言。密码仅经管道交给临时 keyring,不写源码或日志。
@@ -0,0 +1,51 @@
# Desktop 0.15.13 / Agent 0.15.6 发布准备
日期:2026-10-07。基线 `v0.15.12`,冻结源码
`c1e4bbf66a418dbd4e0f99a36ff0522dbf610553`。只纳入已提交实现和发布准备,
保留开始准备后出现的并行工作。标准六平台 Desktop、三平台 Agent,不含 LoongArch。
## 范围与兼容性
- 一个 Desktop storage utility process 统一拥有 Assistant、Knowledge、附件、
Runtime binding/ledger 等业务 SQLite;Main 通过类型化异步操作访问。
保留数据库路径、表、ID、事务、提交顺序和远程 Agent 数据库。
- storage utility 使用现有读取 Worker 和受界定 IPC,覆盖 readiness、取消、
容量、进程退出、关闭排空和错误传播;移除生产 Main 业务 SQLite owner。
- schema 仍为 62,无数据迁移;升级前仍备份完整数据目录、notes 和设置。
- 直连模型工具失败后可继续下一轮,分页输出保持有界;Magic Notes 标签、
监督来源别名、设置直达预加载、文件元数据和 Composer 过渡同步修复。
- Agent 生产 Runtime/模型桥有变化,需 0.15.6;runtime/acp 6、wire 2.0、
Node/Electron/OpenCode/Continue 锁不变。
## 验证边界
storage foundation focused 结果:utility process 真实 Electron 编排、监督
heartbeat/回顾叶子/导航、异步 Worker、存储帧高水位约 248,332 bytes Main、
252,578 bytes storage;focused 70 项跨 11 suites 通过,Node/Agent/Renderer
typecheck 和 scoped lint 通过。工具失败继续、分页输出、Magic Notes 和监督
别名另有定向回归。
并发验收文档明确的 C01–C07 和 A01–A10 完整矩阵、跨平台 storage process
安装、低端机对照及完整 UI→Preload→Main→storage→SQLite 验收仍不是这份文档
中的已完成事实。不得把 focused 结果或架构文档目标写成全部 PERF-15/PERF-16
已验收。Agent 远程路径需在当前源码 CI/Host 规则下重新验证。
## 发布门槛
按用户约定不运行本地测试套件、typecheck、lint、生产构建或安装包探针;
先校验发布元数据,推送双远端,要求精确候选 main CI 和生产构建通过后再创建
Desktop/Agent 不可变标签。通过后核验六平台 20 项 Desktop 资产、macOS 签名公证、
Agent 三平台目录、OSS/GitHub 元数据和网站 12 种标准下载选择。
## 精确候选首轮 CI
`fbfafa15` 的 Agent CI 已通过。Desktop CI `37636800633` 为 6430 通过、
58 跳过、3 失败,生产构建未执行:
- C05 storage 进程故障 fixture 没有稳定观察到拒绝;
- utility-process fixture 的临时数据库父目录权限在 Linux runner 上未满足;
- readonly reader 测试把快速完成的 worker 请求固定断言为拒绝。
修正只涉及测试稳定性:显式把隔离目录设为 `0700`,允许快速 worker 在终止
竞争中完成(专用 crash 测试仍覆盖拒绝),并保留 storage 故障恢复和已提交
数据断言。下一候选 CI 仍必须重新验证这些场景;不跳过 storage owner 测试。
+1 -1
View File
@@ -100,7 +100,7 @@ to a public channel; follow the user's explicit publication instruction.
production signing secrets, publish installable release artifacts, or modify
the checked-in public key registry. The Darwin arm64 target uses `macos-15`
and the same production locks. Native CI checks do not replace real Host
installation, Attach, Ask/Execute, and lifecycle validation during development.
installation, Attach, mode-free tool execution, and lifecycle validation during development.
- `.github/workflows/agent-release.yml` is the only production compound Agent
publication path. It requires an annotated immutable Agent tag, the protected
`agent-signing` Environment, native x64/arm64 builds, one production
@@ -189,7 +189,7 @@ warming/loading/live/partial/error -> inactive
| 工具打开决策表 | 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 |
| 活动聚合、完成通知、查看清除与归档恢复 | FR-14 | US-E1、US-E2、US-E3、US-E4 |
| 后续私有市场与 YAML | FR-15 | US-F1、US-F2 |
| 服务状态、资源可用性、服务生命周期 | FR-16 | US-F3、US-F5;US-F4 入口已移除 |
@@ -267,8 +267,14 @@ ASR 按请求加载与释放,支持独立取消;TTS 尚无执行服务。具
默认筛选依次列待处理、运行中、最近会话。前两组不限条数且不重复列入最近;组内排序时间取最新消息创建时间与本次应用会话内访问/状态变化时间的最大值,无消息时用会话更新时间参与比较,同时间按 ID 排序,不按置顶优先排序。最近组最多 10 条,已完成未查看会话归入最近组并标注状态;“已完成”筛选列出全部完成未查看会话,不受 10 条限制。
行内显示时间只取会话 `updatedAt`,不使用上述排序时间、访问时间或任务状态变化时间。只有活动而没有会话摘要时不显示时间,也不构造日期或读取详情补齐。流式输出可以更新行内时间,但不因此改变原有排序规则。
首次活动状态快照只建立基线,不更新最近时间;后续状态变化更新时间,唯独从已完成变为无活动的提醒清除不更新时间。实际打开会话仍记录访问时间;悬停预览不记录。访问与状态时间仅保留在当前 Store 内存中,不跨重载或重启恢复。
点击项目经离开检查进入聊天,优先恢复本次应用会话内为该项目记住的有效 Conversation ID,否则按最新消息时间从候选会话中选最近一条,无消息时取会话更新时间。普通项目无会话时创建;通道项目只选通道会话,无会话时显示空态。记忆不跨重载或重启保存。点击会话仍按确切 ID 导航;设置按钮独立,不触发项目切换。“新建会话”使用预览项目,全部项目范围使用当前项目;目标缺失或为消息通道时禁用。
全局无活动时第二行显示“暂无活动”,按钮仍可打开菜单并预览当前项目的最近会话或空态,也可在菜单内切换全部项目范围。活动归零不改变控件高度,也不关闭已打开的菜单。点击外部、焦点离开、Escape 或隐藏侧栏关闭菜单,后台活动不自动打开。查看预览不清除完成通知,实际进入可见聊天才按上述规则清除。
归档视图是左栏列表的临时模式,不新增顶部分类,不改变右栏预览范围。它只列已归档用户项目,保留本地/SSH Host 分组、搜索和远程能力过滤;归档行只有恢复操作,不作为可进入或可预览项目。恢复先同步会话摘要,再让项目回到活动项目集合,保持当前项目、会话和页面。成功后从归档列表移除;失败保留操作入口,可重复提交同一恢复动作。恢复不修改 Task 状态,不重放任务或启动远程连接。
右下角“进入项目”与点击普通项目行共用导航规则,目标为预览项目,全部项目范围回退到当前项目;目标不可用时禁用。归档视图中按钮仍对应右栏原预览,不能直接进入未恢复项目。
@@ -150,9 +150,15 @@ Renderer 内存中最多保留最近 5 分钟。任一条件不满足时停止
左栏依次提供“全部项目、本地、远程、消息通道”分类,远程能力关闭时隐藏远程分类及其项目;支持跨分类搜索、远程主机折叠及独立项目设置。项目行按名称、活动数量、目录分行显示,无活动时省略数量行,项目顺序不随活动数量变化。
悬停或键盘聚焦项目只预览右栏,点击项目直接进入,优先恢复本次应用会话内上次打开的有效会话,否则选择最近会话;普通项目无会话时创建,消息通道无会话时保持空态。点击右栏会话进入确切会话。
右栏默认依次列待处理、运行中、最近会话,最近会话最多 10 条;已完成未查看在行内标注。“全部、待处理、运行中、已完成”筛选与“新建会话”同排,新建使用预览项目,全部项目范围使用当前项目,消息通道禁用手动新建。左栏底部“新建项目”进入真实项目创建流程。
新建本地项目必须填写名称并选择根目录,空目录或纯空白目录不能创建。此要求仅适用于创建;历史空目录项目仍可编辑和保存,内置默认项目与消息通道项目的初始化规则保持不变。
活动计数、待处理、运行中及已完成筛选不受最近 10 条限制;菜单不继承侧栏会话搜索条件。计数与排除规则见
[活动逻辑](./logic-design.md#9-项目活动汇总),交互见[活动界面](./ui-design.md#10-项目活动汇总)。
右栏会话显示与侧栏一致的实际更新时间;缺少会话摘要的任务活动不显示时间。菜单从完整入口下方展开,短窗口内缩小高度并保持内部可滚动,入口不被菜单覆盖。
左栏底部“新建项目”旁提供低强调的“已归档”入口,支持搜索和逐项恢复。恢复保留项目数据与原本地/远程分类,不自动进入项目;通道项目及关闭远程能力时的 SSH 项目不提供恢复操作。右下角固定“进入项目”按钮,进入预览项目;全部项目范围使用当前项目,与“新建会话”的目标规则一致。进入仍经过未保存修改检查。
验收覆盖跨项目后台运行、同会话多任务去重、提问与审批优先、较早的仍在运行或等待处理的
会话可见,以及从带搜索条件的页面或有未保存修改的设置页跳转。成功完成后保留“已完成”
通知,直到用户实际查看该会话;从活动菜单或普通侧栏进入均可清除。设置覆盖下的聊天及
@@ -162,7 +168,7 @@ Renderer 内存中最多保留最近 5 分钟。任一条件不满足时停止
## 6. 权限与安全
- 快捷入口不授予新的文件、命令、网络或浏览器权限。
- 系统工具继续使用 Main、Preload、执行空间 Grant、Ask/Execute 和现有审批边界。
- 系统工具继续使用 Main、Preload、执行空间 Grant 和既有能力范围,不恢复产品工作模式或一般工具审批。
- Renderer 不因新增入口获得任意文件系统、PTY、进程或 Electron API。
- 资源指标由 Main 采集并以有界结构返回;Renderer 不接收进程命令行、环境变量或任意 PID。
- 资源样本只保存在当前 Renderer 内存中,不写入运行记录、SQLite 或遥测服务。
@@ -1,5 +1,84 @@
# 应用导航实施进度
## 2026-10-05:展开状态缓存复查
独立复查发现,首次 A10 修复在每个折叠项挂载时保存默认值;无引用的消息也会因引用 Hook 留下一条记录。下节首次验收只检查了 DOM 上限,没有覆盖这部分缓存。新增回归在该实现上失败:分批挂载 10,000 个未操作的消息作用域后,预期缓存为 0,实际为 10,000;恢复默认状态后也没有释放条目。
现改为只保存偏离自动默认值的用户选择,恢复默认时立即删除覆盖项。消息更新后,清理逻辑只检查已保存的覆盖项,借用历史面板已有的消息索引定位来源,清除已删除消息、工具/输入/推理块/子代理/引用及已过期的流式默认值对应的记录。清理不在滚动或渲染期执行。正常的虚拟行卸载、子代理收起继续保留有效选择,行卸载期间完成的推理仍按原规则自动收起。
本次执行结果:
- `npm test -- --project unit src/renderer/src/message-expansion.test.tsx src/renderer/src/ChatHistoryPane.test.tsx src/renderer/src/ChatTimeline.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:4 个文件、112 项通过。其中新增 4 项覆盖 10,000 个未操作作用域零缓存、用户选择跨卸载保留、恢复默认释放、消息及折叠项删除、未挂载推理完成。
- `npm test -- --project unit src/renderer/src/App.test.tsx -t 'keeps each conversation history window|isolates history and task strips'`:2 项通过,346 项未选中。
- `npm test -- --project integration tests/message-expansion.electron.test.ts`:三轮交替比较后,最终测量探针调整也单独复跑通过。`npm test -- --project integration tests/frosted-glass.electron.test.ts`:1 项通过,继续覆盖浅深主题、原生点击、跳底、锚点和缓存切换。
- `npm run typecheck`、`npm run lint` 通过。该组定向验收后,完整集成测试为 6,136 项通过、0 失败、87 跳过,见[阶段修复验收](../../review/stage-audit-fixes-2026-10-05.md#最终检查);本组模型调用 0 次。
Electron 环境与合成消息结构同下节。此次比较对象为首次 A10 的默认值缓存实现和本次稀疏覆盖实现,不是 `126dbc57`。测试通过 `GB_EXPANSION_DENSE_SOURCE` 加载修改前保留的模块副本,其余生产 Renderer 源码相同。fixture 临时观察首次展开状态写入,捕获该 Map 后恢复原 `Map.set`;产品未增加诊断接口。每个数据量先进行 200 次未操作导航,实际见到 1,397 个不同消息行(一轮旧实现为 1,401),再原生展开 13 条消息的推理、工具结果和输入,返回原行确认恢复,用 Space 收起一项,最后删除消息。
| 历史消息数 | 旧实现:未操作导航后条目 | 新实现:未操作导航后条目 | 用户操作后:旧/新条目 | 删除消息后:旧/新条目 |
| --- | --- | --- | --- | --- |
| 2,000 | 5,600 | 0 | 5,708 / 38 | 5,708 / 0 |
| 10,000 | 5,604 | 0 | 5,904 / 38 | 5,904 / 0 |
以上计数在三轮比较中一致。交互阶段两版均最多采样到 15 个消息行、926 个 DOM 元素,原消息的三个展开项均恢复;A09 迟到来源响应检查也继续通过。计数针对缓存条目,不是浏览器堆内存字节数。
| 消息数 | 旧实现每轮导航中位数(ms) | 新实现每轮导航中位数(ms) | 旧实现每轮 p95(ms) | 新实现每轮 p95(ms) |
| --- | --- | --- | --- | --- |
| 2,000 | 38.8 / 38.8 / 38.4 | 38.8 / 39.1 / 38.5 | 40.4 / 39.6 / 46.9 | 39.2 / 46.7 / 40.1 |
| 10,000 | 38.7 / 39.1 / 38.9 | 38.8 / 38.9 / 38.6 | 39.7 / 39.5 / 46.9 | 39.4 / 39.3 / 46.6 |
导航计时仍为目标聚焦后再等待两个动画帧,每轮 14 个样本;此次先扫过历史,不能与下节首次进入的计时直接比较。三轮比较中,200 次未操作导航的总耗时,旧实现为 4,753.5–5,080.5 ms,新实现为 4,820.3–5,228.8 ms。数据证明默认条目和删除后的条目不再积累,未证明提速;未测堆内存峰值、低端机节流或完整 App/生产 IPC。
## 2026-10-05:虚拟消息行的展开状态
聊天历史面板通过 `message-expansion.tsx` 按消息、子代理及折叠项的稳定标识保留展开状态。虚拟行卸载后只保留状态值,不保留 DOM;仍沿用最多 8 个近期交互行及焦点、选区、问题的挂载保护。阅读行为见[聊天阅读状态](./ui-design.md#聊天阅读状态)。本次没有修改 Runtime 执行或消息持久化。
旧代码上的新增组件回归在返回原消息时失败:预期展开,实际收起。最终三文件 108 项通过,覆盖嵌套工具、父子同名调用隔离、手动收起、行卸载期间流式完成、既有焦点/选区及平滑滚动。命令:`npm test -- --project unit src/renderer/src/ChatHistoryPane.test.tsx src/renderer/src/ChatTimeline.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`。另有会话存储、同步、流式缓冲、窗口模型和侧栏回归 48 项,以及 App 历史加载、重试、热切换、流式隔离和超过 500 条消息的定向回归 5 项通过。
真实 Electron 使用当前 `ChatHistoryPane`、样式及 Markdown 渲染,数据为 2,000/10,000 条合成助手消息,每条含正文、推理、工具结果与输入。Windows x64、Electron 44.5.1、1280×800、缩放 1;原生鼠标展开 13 条相距较远的消息,确认原行卸载后再返回,并用原生 Space 操作。基线为临时 worktree 中的 `126dbc57`。三轮交替运行 `npm test -- --project integration tests/message-expansion.electron.test.ts`;基线运行另设 `GB_HISTORY_SOURCE` 指向该 worktree。每轮、每种数据量采样 14 次导航;计时从请求消息定位到目标 article 获得焦点后的两个动画帧,含这两个帧的等待,不等同于纯渲染耗时。
| 消息数 | 基线每轮中位数(ms) | 修复后每轮中位数(ms) | 基线每轮 p95(ms) | 修复后每轮 p95(ms) |
| --- | --- | --- | --- | --- |
| 2,000 | 47.0 / 39.2 / 39.3 | 39.4 / 39.0 / 39.1 | 177.6 / 136.4 / 137.4 | 129.7 / 128.8 / 138.7 |
| 10,000 | 47.2 / 39.4 / 39.2 | 38.8 / 39.2 / 39.5 | 56.0 / 40.2 / 43.7 | 41.4 / 39.7 / 40.0 |
两版在两种数据量下的采样最大值均为 15 个消息行、926 个 DOM 元素;基线三轮均丢失原消息的三个展开状态,修复后三轮均恢复。数字只证明本场景的 DOM 数量有界,计时存在波动,不据此宣称提速或整体性能预算达标。该 Electron fixture 替换了数据来源,不是完整 App、生产 IPC 或真实模型验收;模型调用 0 次。
既有 `npm test -- --project integration tests/frosted-glass.electron.test.ts` 单独复跑通过,覆盖浅深主题、原生点击、跳底、阅读锚点、会话缓存、加载更早消息及 960×540 短窗口。首次与监督者布局测试合并运行在工具的 60 秒期限内未返回结果,不计为通过;`tests/supervisor-layout.electron.test.ts` 随后单独后台运行通过,总耗时 65.26 秒,stderr 为空。最终完整 typecheck、全仓 lint 和修改范围差异检查通过。首次 lint 曾受其他并行修改中的三个 Runtime 错误阻断,后续复跑已通过。未运行完整测试集或低端机节流测量。
## 2026-10-04:新建本地项目必选目录
对应 FR-14 的项目创建入口,规则见 [PRD](./prd.md#fr-14-项目活动汇总)及[界面设计](./ui-design.md#10-项目活动汇总)。创建表单已增加中英文必填提示、无障碍关联及空目录提交检查;共享创建 schema 拒绝空白路径,更新 IPC 使用独立 schema,保留历史空目录项目的保存能力。
- `npx vitest run --project unit src/renderer/src/ProjectSwitcher.test.tsx src/shared/assistant-contracts.test.ts`:57 项通过,覆盖空白目录、目录选择与取消、正常创建、中英文提示、历史空目录设置保存及既有 SSH 界面回归。
- `npx vitest run src/main/ipc.test.ts -t "requires a directory through project creation IPC"`:1 项通过,172 项未选中。调用生产 IPC handler 和真实内存 SQLite,验证空目录创建不写库、有效目录创建落库,以及历史空目录更新成功。
- `npm run typecheck` 和本次修改的 8 个源码/测试文件的定向 ESLint 检查通过。
本次未运行完整测试集或原生 Electron 目录选择器验收,未修改远程主机文档、Agent 或远程连接流程;模型调用 0 次。
## 2026-10-04:归档恢复与右下角进入项目
对应 FR-14、US-E4。生产菜单左栏底部已接入按需归档列表、搜索和逐项恢复,右下角已接入“进入项目”。恢复复用既有归档 IPC,并在项目重新可选前合并目标项目会话摘要;不自动切换当前项目,不改变 Task 状态或启动远程连接。进入按钮复用带离开检查的选择回调。规则见[活动逻辑](./logic-design.md#9-项目活动汇总)和[界面设计](./ui-design.md#10-项目活动汇总)。
本次验证:
- `npx vitest run --project unit src/renderer/src/WorkspaceMenu.test.tsx src/renderer/src/ProjectSwitcher.test.tsx src/renderer/src/workspace-menu-selectors.test.ts src/renderer/src/WorkspacePrimitives.test.tsx`:90 项通过。包含按需 `list(true)`、分类及远程开关、搜索、加载/恢复失败重试、重复提交锁定、预览不导航及全部项目进入目标。
- App 项目/通道相关筛选首次运行 83 项通过,1 条旧断言未包含此前新增的更新时间。修正该断言后运行 `npx vitest run --project unit src/renderer/src/App.test.tsx -t "keeps cross-project counts independent|restores archived|single guarded Magic Notes discard"`,7 项通过,覆盖本地/远程恢复、真实 `setArchived(id, false)` 接线、摘要读取失败重试、原会话恢复和未保存草稿离开检查。
- `npx vitest run tests/overlay-layering.electron.test.ts -t "keeps nested previews"`:真实 Electron App 用例通过。生产 Preload、IPC、SQLite 验证归档恢复、保留历史正文、恢复不切换及进入不新建替代会话;浅深主题分别覆盖 `1280×800`、`1280×480`、`560×640`、`375×480` 的右下角位置、边界和内部滚动,并保留原生浏览器相交遮挡及同页面恢复检查。模型调用 0 次。
- `npm run typecheck`、`npm run lint`、`git diff --check` 通过。未运行全量测试或实机 SSH 连接;本次没有修改 Agent、SSH 协议或 Runtime 路径。Demo 未修改,生产菜单为本次交付对象。
## 2026-10-04:会话更新时间与菜单下方定位
对应 FR-14、US-E1、US-E2。右栏时间按会话 ID 局部订阅,使用实际 `updatedAt` 及侧栏共用格式;仅有活动而无摘要时省略时间。显示文字与提示未变的流式更新不触发 React 提交,跨显示精度更新只提交时间子组件。菜单按完整按钮底边定位,剩余视口高度限制菜单高度,保留内部滚动。规则见[活动界面](./ui-design.md#10-项目活动汇总)及[技术设计](./technical-design.md#12-项目活动汇总)。
本次实际验证:
- `npx vitest run --project unit src/renderer/src/WorkspaceMenu.test.tsx src/renderer/src/workspace-menu-selectors.test.ts src/renderer/src/ProjectSwitcher.test.tsx src/renderer/src/time-format.test.ts`:4 个文件、52 项通过。覆盖中英文当天/当年/跨年格式、提示和 ISO 时间、访问时间不替代更新时间、缺少摘要不读取详情、局部提交与未变化时零提交。
- `npm run typecheck`:Node、Agent、Web 全部通过。`npx eslint src/renderer/src/WorkspaceMenu.tsx src/renderer/src/workspace-menu-selectors.ts src/renderer/src/WorkspaceMenu.test.tsx tests/support/overlay-paths-main.ts` 通过。
- `npx vitest run --project integration tests/overlay-layering.electron.test.ts -t 'keeps nested previews'`:选中的真实 Electron App 用例通过,其余 3 项未选中。浅深主题分别覆盖 `1280×800`、`1280×480`、`560×640`、`375×480`,验证 `8px` 间隔、按钮两行命中、视口边界、时间与状态位置、原生滚轮内部滚动、End 到末行和 Escape 焦点恢复;连续 12 帧几何一致,并检查菜单打开时缩短及恢复窗口。
- 原生浏览器与菜单相交时隐藏、不相交时保持显示;菜单关闭后恢复同一页面及标记。模型调用 0 次。截图和 JSON 保存在临时 `workspace-menu-validation` 目录,已查看桌面、短窗口深色和窄窗口浅色截图。
- 临时换回旧的按菜单高度上移公式后,Electron 用例在首个桌面场景的间隔断言失败(实际 `6px`,要求 `8px`);恢复最终代码后复跑通过。未运行全量测试,未修改监督者代码。
## 2026-10-04:双行入口合并为单个按钮
对应 FR-14、US-E1 至 US-E3。项目名和活动摘要现在属于同一个 `60px` 按钮,共用键盘焦点、展开状态和菜单关联。点击任一行默认预览当前项目,全部项目范围在菜单内切换;活动描述和按钮外的恢复反馈通过 `aria-describedby` 关联。移除了独立摘要触发引用与入口范围状态,保留最近 10 条及现有活动计数,没有新增轮询。
@@ -479,13 +479,17 @@ OCR 模型可用性由 Main 校验,Worker 运行状态由实际持有它的 Re
- 聚合状态新增 `completed`,全局和项目计数包含 `attention`、`running`、`completed`,
同一 Conversation ID 只按最高优先级计数;活动行为空时以“暂无活动”替换计数,保留全局入口。
- `App` 向 `ProjectSwitcher` 传入 `conversationStore`、活动行、侧栏可见状态和确切会话跳转、新建回调。`WorkspaceMenu` 持有分类、预览范围、搜索及状态筛选;分类复用 `PageTabs`,状态筛选复用 `SegmentedControl`,项目行与真实设置/创建表单继续由 `ProjectSwitcher` 提供。
- 本地创建使用共享 `projectCreateSchema`,`rootPath` 经 trim 后要求 1 至 4096 个字符,`projectsCreate` IPC 在写库前校验。`projectUpdateSchema` 保留原有空目录兼容性,`projectsUpdate` 请求显式使用该 schema;持久化执行空间、默认项目和通道初始化不增加目录限制。`ProjectSwitcher` 在创建按钮和保存入口检查空目录,设置保存沿用原规则。
- `workspace-menu-selectors.ts` 订阅会话 Store,投影 ID、项目、标题、排序时间及通道标记,以字段相等比较保留未变化结果;排序时间为 `Math.max(conversationActivityTime(conversation), activityTimes.get(conversation.id) ?? 0)`。与活动行按 ID 合并后派生分组和最近 10 条;仅有活动、尚无摘要的行使用 Store 时间,缺失时取 0。悬停只改变 Renderer 预览范围,不发 IPC、不加载详情或请求模型。打开菜单所需的既有主机快照读取与悬停预览分开。
- Store 的 `activityTimes` 保存本次应用会话内的访问/状态变化时间,更新时间为 `Math.max(Date.now(), lastActivityTime + 1)`。`ProjectSwitcher` 调用 `recordActivityStatuses`:首次快照只建立基线,后续状态变化触发更新,但 `completed` 变为无状态的完成提醒清除不触发更新。`WorkspaceMenu` 经 `useSyncExternalStore` 订阅该时间映射;时间不持久化,悬停不更新,普通侧栏原有消息排序规则不变。
- 可见会话行的 `WorkspaceConversationTime` 通过 `useWorkspaceConversationTime` 按 ID 订阅 Store,使用 `formatConversationListTime(updatedAt, locale)` 和 `formatMediumDateTime`,`time.dateTime` 保留对应快照的实际 ISO 时间。选择器按显示文字及提示相等复用结果,忽略显示精度以下的流式变化;时间变化只重渲染该子组件,不向整菜单投影加入原始 `updatedAt`。缺少摘要时返回空值,不读取详情。
- 项目、会话列表均使用共享 `useListWindow`,分别估算 `64px`、`56px` 行高并测量实际高度,保留键盘目标行;范围改变时重置滚动。窗口化仅限制挂载行数,不截断活动集合,不代表已有性能基准结果。
- 菜单通过 `FloatingPortal` 显示同一双栏表面,定位和焦点恢复共用项目按钮的引用;尺寸、窗口和滚动变化时重算位置。非模态 `dialog` 不调用焦点隔离,Escape 关闭整个菜单,Tab 可访问内部控件,焦点离开后关闭。窄窗口仍为双栏,侧栏隐藏时卸载菜单。
- 菜单通过 `FloatingPortal` 显示同一双栏表面,定位和焦点恢复共用项目按钮的引用;按钮尺寸、窗口和滚动变化时重算位置。顶部由按钮底边加 `8px` 决定,最大高度为视口底边减顶部及 `16px` 边距,不读取菜单高度参与定位。`ResizeObserver` 只观察按钮,避免菜单收缩后反复改变自身位置。非模态 `dialog` 不调用焦点隔离,Escape 关闭整个菜单,Tab 可访问内部控件,焦点离开后关闭。窄窗口仍为双栏,侧栏隐藏时卸载菜单。
- 浮层沿用共享 `browser-viewport-occlusion.ts` 相交检测,不增加浏览器实例的特殊隐藏条件,也不关闭或重建浏览器会话。
- `conversationStore.rememberOpened` 在 `App.setActiveId` 时为存在的会话记录访问时间,并记住其项目最后打开的 ID。`projectConversation` 优先返回仍在候选集合中的记忆 ID,否则按 `conversationActivityTime` 选择最近会话,不使用菜单的访问/状态时间;通道项目只选带 `remote` 的会话。记忆仅存在当前 Store 的 Map 中,不持久化、不跨重载恢复。普通项目无会话时创建,通道无会话时清空活动 ID。
- 项目进入、新建会话及创建项目后的导航均经过 `requestWorkspaceLeave`。获准后的 `commitProjectSelection` 与新建会话 `ready` 回调直接调用 `commitView('chat')`,避免经 `setView` 再次触发离开检查;新建会话就绪后才提交项目、会话及输入框焦点。
- `WorkspaceMenu.onEnterProject` 接到 `ProjectSwitcher.onSelect`,与项目行共用 App 的离开检查及会话恢复。归档入口首次点击才调用 `projects.list(true)` 并筛选 `status === 'archived'`,当前菜单内缓存结果,重新打开菜单后按需重读;不新增启动读取、轮询或逐项目请求。
- `restore-project.ts` 调用既有 `projects.setArchived(id, false)`,成功后并行读取活动项目和会话摘要,只合并目标项目摘要,以 `mergePersistedConversations` 保留本地未保存正文及需要保留的详情,再发布 App 项目列表。恢复不调用选择、新建或任务执行路径;摘要读取失败时保留重试入口,重复设置活动状态可安全重试。归档预览不读会话详情,正常进入才按原路径加载历史。后端通道限制、远程功能检查及删除保护不变。
- 持久化快照刷新时,同一条仍在流式输出的消息保留本地待审批、待回答信息,持久化终态
则清除这些信息。响应成功后对应 Task 立即退出等待状态;响应期间到达的新问题、审批或
终态不能被旧响应覆盖。
@@ -270,6 +270,14 @@ CPU 4% · 内存 620 MB · 桌面进程 7
导出也适用于消息通道会话。持久化与刷新规则见
[会话置顶存储](../assistant-workbar/execution-history-storage.md#会话置顶)。
### 聊天阅读状态
在同一历史面板内,滚动到别处再返回时,消息的推理、工具结果、工具输入、引用和子代理详情保留用户的展开或收起选择。子代理收起后重新展开,其内部工具详情也保留选择;不同消息及父子工具分别记忆。状态保留在历史面板中,不依赖把所有已展开的消息留在 DOM 中;面板被缓存淘汰或应用重启后不恢复这些临时阅读选择。
未操作的折叠项不留下默认状态记录;用户恢复默认选择、消息或对应折叠项被删除后,释放相应记录。正常滚动卸载不视为删除。
推理仍在流式输出时默认展开、完成时自动收起,流式期间手动收起不因滚动重挂载而重新展开。有焦点、文本选区端点、待回答问题和待审批项的消息继续按既有规则保留挂载;平滑跳到底部及滚动锚定行为不变。
## 9. 未来插件入口
FR-15、US-F1/US-F2 的市场 URL、YAML 导入导出及 ShareServer 分发在后续设计中定义。
@@ -311,16 +319,24 @@ OCR 忙碌或模型不可用时禁用加载与释放,并在执行模块再次
左栏固定搜索、分类及底部“新建项目”,项目列表独立滚动。分类使用共享 `PageTabs` 的 `segmented` 变体,依次为“全部项目、本地、远程、消息通道”,保留 `tablist`/`tab`/`tabpanel`、游标焦点及方向键语义;远程能力关闭时隐藏远程分类。搜索跨分类匹配,SSH Host 分组可折叠,连接状态独立于会话数量。
新建本地项目的根目录字段常显“根目录为必填项,请选择目录后创建项目。”,英文为“Required: select a root folder to create the project.”。只读路径框声明 `aria-required`,通过 `aria-describedby` 关联提示;沿用目录选择按钮。名称或目录去除首尾空白后为空时禁用“创建”。取消目录选择保留原选择和其他草稿;项目设置不显示创建专用提示,也不要求历史空目录项目补选目录。
底部“新建项目”旁放低强调的“已归档”按钮。点击后左栏保留搜索框,以“已归档”和“返回”替代顶部分类,列表显示名称、目录与独立“恢复”按钮,继续按本地和 SSH Host 分组。加载、空列表、搜索无结果和加载失败重试分别呈现;异步错误与恢复成功使用共享应用通知。恢复期间禁用重复操作,完成后移除该行,键盘焦点仍在该行时移到“已归档”按钮;“返回”恢复选中分类的焦点。
左栏使用 `--surface-subtle`,与右栏之间以 `1px` 的 `--border-control` 竖线分隔。搜索框使用 `border-box`,宽度包含边框和内边距,并扣除左右外边距,避免越过分隔线。全部项目及搜索结果按实际存在的本地、远程、消息通道显示分组标题,组间用横线和留白区分;远程组内保留 Host 标题。
项目行显示名称、活动数量、目录三行,无活动时省略数量行,高度随内容增长;恢复状态继续显示在目录下方。当前项目以勾选和 `aria-checked` 标识,预览使用单独背景。设置按钮独立,桌面悬停或聚焦显示,窄窗口及粗指针设备可直接访问;打开既有真实项目设置,保留目录选择、Runtime、模式、删除及远程恢复等适用操作,不触发项目切换。悬停或聚焦项目只预览右栏,点击项目直接进入;通道无会话时保留空态。
右栏顶部用共享 `SegmentedControl` 显示“全部、待处理、运行中、已完成”,采用辅助字号和紧凑内边距,与靠右的“新建会话”同排。新建使用预览项目,全部项目范围使用当前项目;消息通道禁用并显示自动创建说明。默认会话列表按待处理、运行中、最近会话分组,最近最多 10 条,完成未查看在行内标注;“已完成”筛选不受此上限限制。会话标题单行截断,全部项目范围补充项目名,不展开 Task、Job 或 Run。无活动仍显示最近会话,无匹配会话时显示空态。
会话行右侧显示实际更新时间,格式与普通会话侧栏一致:当天显示时分,当年显示月日,跨年带年份,随界面中英文切换;悬停提供完整日期与时分。时间使用弱文本色,状态放在其下方并右对齐。窄窗口优先截断标题,保留时间;仅有任务活动、尚无会话摘要的行省略时间,预览不为补时间读取详情。显示时间与最近排序时间的区别见[活动逻辑](./logic-design.md#9-项目活动汇总)。
右栏底部固定右对齐的主按钮“进入项目”,英文为“Enter project”,工具提示和可访问名称包含目标项目名。目标与顶部“新建会话”一致,但消息通道允许进入;目标缺失时禁用。打开归档视图保持右栏原预览及该目标。按钮不随会话列表滚动,短窗口仍保留底部操作区。
点击会话关闭菜单并请求确切 ID 导航;通过既有未保存离开检查后,选择所属项目、清除侧栏搜索、未读标记、任务选择和会话菜单,窄窗口关闭侧栏。取消离开保留原页面和完成通知。完成通知仅在目标聊天实际可见后清除,预览、设置覆盖或隐藏文档不触发清除。
菜单使用共享 `FloatingPortal`,外层为非模态 `dialog`,入口提供 `aria-haspopup="dialog"`、展开状态及控制关系,不增加遮罩或背景隔离。项目列表使用 `menu`/`menuitemradio`,独立操作使用 `menuitem`;会话列表使用 `list`/`listitem` 和按钮。原生浏览器相交遮挡沿用共享机制。
打开后聚焦选中分类。列表内上下方向键循环移动,Home、End 到首尾;右方向键进入会话列表,左方向键返回预览项目,项目不可见时回到选中分类。Enter、Space 激活按钮;Tab 在控件间正常移动,焦点离开后关闭。Escape 关闭整个菜单并恢复仍可用的触发入口焦点,触发入口已卸载时回到项目入口,不继续关闭窄侧栏。点击外部、再次点击入口或隐藏侧栏也关闭。
菜单最大宽度 `920px`,高度 `640px` 且受视口限制,距窗口边缘至少 `16px`,锚点下方间隔 `8px`。桌面左栏为 `240px` 至 `360px`,窄窗口仍保留双栏并各自滚动,不使用旧级联返回页。状态筛选可横向滚动,新建按钮保持可见。项目和会话列表均采用共享窗口化渲染,键盘可到达完整列表首尾。
菜单最大宽度 `920px`,目标高度 `640px`,顶部始终位于完整双行按钮底边下方 `8px`。最大高度按该位置到视口底部的剩余空间计算,并留出 `16px` 底边距;短窗口缩小菜单、让列表内部滚动,不向上移动覆盖入口或翻转到上方。左右距窗口边缘至少 `16px`。桌面左栏为 `240px` 至 `360px`,窄窗口仍保留双栏并各自滚动,不使用旧级联返回页。状态筛选可横向滚动,新建按钮保持可见。项目和会话列表均采用共享窗口化渲染,键盘可到达完整列表首尾。
@@ -380,6 +380,7 @@
- Then 默认预览当前项目;点击“全部项目”恢复全局范围,“已完成”筛选列出全部完成未查看会话,不受最近 10 条限制
- When 实际打开会话、产生新消息或活动状态变化
- Then 菜单按最新消息与本次应用会话内访问/状态变化时间中的较晚者排序;首次状态载入和仅清除完成提醒不更新时间,悬停不改变排序
- And 行右侧显示会话实际更新时间,中英文格式与会话侧栏一致;访问或任务状态变化不冒充会话更新时间,没有会话摘要时省略时间且不请求详情
- When 点击与状态筛选同排的“新建会话”
- Then 在预览项目创建,全部项目范围使用当前项目;消息通道禁用手动新建并说明自动创建规则
- When 点击项目行独立设置按钮或左栏底部“新建项目”
@@ -390,6 +391,7 @@
- Given 菜单已打开
- Then 初始焦点进入选中分类,上下方向键与 Home、End 移动列表焦点,右方向键进入会话列表,左方向键返回预览项目或选中分类;Enter、Space 激活按钮
- And 菜单顶部距完整入口底边 `8px`,短窗口内保留底部 `16px` 边距,列表内部滚动;入口两行仍可点击,时间与标题不重叠
- When 按 Escape
- Then 关闭整个工作空间菜单并恢复可用触发入口焦点,窄侧栏保持打开
- Given 窄窗口或长列表
@@ -421,6 +423,23 @@
- Given 同一会话仍有待处理或运行中工作
- Then 按实时状态优先展示,只计入一个类别
### US-E4 恢复归档项目并显式进入
对应 FR-14。
- Given 用户打开项目菜单
- When 点击左下角“已归档”
- Then 在左栏查看和搜索已归档用户项目,右栏保持原预览;首次加载显示进度,失败提供重试及应用通知
- When 点击某个项目的“恢复”
- Then 项目回到原本地/远程分类,当前项目和聊天保持不变;失败保留该行并允许重试,提交期间禁止重复恢复
- Given 恢复后的项目已有会话
- When 返回项目列表,预览该项目并点击右下角“进入项目”
- Then 通过离开检查后按既有规则恢复会话,不因摘要尚未刷新创建替代会话
- Given 范围为全部项目
- Then “进入项目”目标为当前项目,按钮提示及可访问名称包含项目名;悬停和键盘预览不导航
- Given 远程项目功能关闭,或项目属于系统消息通道
- Then 归档列表不提供对应恢复操作,正常消息通道分类继续可用
## 7. 后续需求场景
US-F1、US-F2 为后续市场计划;US-F3 至 US-F5 的本机推理监控已接入源码,当前支持边界见技术设计,验证范围见实施进度。
+4 -1
View File
@@ -1,6 +1,7 @@
# 助手工作栏
助手工作栏统一承载本机与远程执行空间、Task、成果、文件、浏览器、终端和笔记等持续工作界面。
助手工作栏统一承载本机与远程执行空间、Task、文件、浏览器、终端和笔记等持续工作界面。
成果页已移除;项目文件统一在工作区查看和导入,会话图片继续由消息引用读取。
“笔记”为应用级、可关闭的单实例 Tab,新布局默认不打开。它提供搜索、浏览和快速追加文字,
与主页面魔法笔记共用存储;会话采集、来源跳转与草稿规则由
@@ -16,10 +17,12 @@
| [浏览器多实例技术设计](./browser-tabs-technical-design.md) | 浏览器 Tab 身份、Conversation 绑定、IPC、BrowserService 和 MCP 路由 |
| [实现与验证进度](./progress.md) | 工作区与 Runtime 相关改动的实现事实、验证证据与剩余阻塞 |
| [执行记录存储与升级回收](./execution-history-storage.md) | 子任务差量存储、远程去重、旧库转换、会话置顶、摘要与完整历史按需读取、内存引用释放 |
| [工作区文件导入与时间](./workspace-files.md) | 真实文件来源、导入操作、排序、图片存储边界及本机和远程实现 |
| [Runtime 交互边界](./runtime-interactions.md) | 原生权限、结构化问答、后台拒绝、终端交互、并行与输出、消息请求状态及资源生命周期规则 |
| [Runtime 进程复用技术设计](./runtime-process-reuse-technical-design.md) | 跨 Runtime 资源调研、锁定协议证据、进程与会话拆分、远端模型桥、实施顺序和性能验收 |
| [Runtime 原生客户端启动方案](./runtime-native-client-proposal.md) | Local terminal/DS Web shortcut requirements, startup rules, current integration, validation boundaries and remaining remote/resource work |
| [Runtime 执行清单技术设计](./runtime-checklist-technical-design.md) | OC/CN 原生清单提取、远程 Agent 传递复用、消息恢复与真实部署验收;尚未实现 |
| [移除 Ask / Execute 工作模式](../unified-execution/README.md) | 跨入口统一执行、历史字段迁移及清除数据入口;主要源码已落地,定向 UI/Host 通过,完整验收待完成 |
当前尚未拆出独立 User Stories、功能逻辑和 UI 设计文档;产品场景与验收保留在现有 PRD,
Runtime 资源状态规则由交互边界文档维护。浏览器多实例和 Runtime 进程复用涉及不同的
@@ -7,7 +7,7 @@
子任务仍实时显示完整的文字、思考、工具过程和最终结果。运行时间较长、工具输出较多时,
数据库不再为每个流式更新重复保存此前的整份过程;本机和托管 SSH OpenCode 使用相同
的桌面存储机制。此修复不关闭子任务、不截短已有过程,也不增加 Execute 权限门槛。
的桌面存储机制。此修复不关闭子任务、不截短已有过程,也不增加工具审批。
已有数据库在首次升级时自动处理。启动页铺满窗口,显示处理数量、全宽进度条和近期
处理速度;处理时间取决于历史数据量与磁盘速度,不承诺固定几分钟。用户可以退出,
+36 -58
View File
@@ -13,7 +13,7 @@
## 1. 背景
GoodBuddy 已经在聊天右侧提供任务中心、工作区、浏览器和成果面板,也已经具备
GoodBuddy 已经在聊天右侧提供任务中心、工作区和浏览器面板,也已经具备
Runtime 事件、Git 变更、文件预览、成果存储、受控浏览器和专家执行等基础能力。后续还
计划增加:
@@ -37,14 +37,14 @@ Runtime 统一抽象为“执行空间”。
助手工作栏是 GoodBuddy 中始终可访问的应用级工具容器。它提供稳定能力目录,用户从中
查看任务中心,并按需打开一个或多个监督、Runtime、终端、进程、工作区、浏览器、资源监控、
成果和文档面板实例。稳定的是能力的可发现性,不是十个同时占据界面的固定面板。
文档面板实例。成果页已移除,项目文件由工作区统一展示;会话图片保留在消息中。
工作栏不是:
- 只在编程项目中出现的 IDE 面板。
- 当前聊天消息的附属详情框。
- 根据能力探测结果自动增删入口的动态菜单。
- 绕过 Main、Preload、Ask/Execute 或审批边界的控制台。
- 绕过 Main、Preload、能力范围或用户数据边界的控制台。
- 全系统进程管理器、任意文件浏览器或无边界远程管理工具。
### 2.2 执行空间
@@ -84,7 +84,6 @@ Runtime
工作区
浏览器
资源监控
成果
文档
笔记
```
@@ -115,7 +114,7 @@ Runtime
- 终端创建时固定到当前项目执行空间,由项目配置自动决定本机或托管 SSH;没有当前项目时
使用本机 Home。
- 工作区默认显示当前项目目录,用户可以打开其他本机目录或远程目录。
- 成果默认使用当前范围,用户可以切换到项目、全局或其他允许范围。
- 项目文件统一使用工作区范围,会话图片按原消息引用读取。
- 文档创建时固定到用户明确打开的文件和 ShareServer 编辑会话,不提供跟随模式。
- 下一次请求的附件和知识库由当前 Composer 展示与管理,不作为工作栏能力。历史执行上下文
随关联 Task 查看;记忆功能实现后也归入任务中心,而不是新增独立上下文面板。
@@ -177,7 +176,7 @@ Runtime
- 建立统一范围、执行空间、生命周期、成果和控制契约。
- 复用现有 Project、Conversation、Task、Artifact、Activity 和 Approval 数据。
- 保持 Renderer 无任意文件、进程、PTY、SSH 或 Electron API 能力。
- 保持 Ask 只读、Execute 账号完整授权、取消、超时、输出边界和活动审计。
- 使用当前账号权限,保留能力范围、取消、超时、输出边界和活动审计。
- 为本机与远程能力提供一致 UI,同时准确表达能力差异。
## 5. 非目标
@@ -232,8 +231,8 @@ Runtime
- 每个实例显示稳定实例 ID、能力名称、跟随或固定状态与当前目标;同能力多实例不能只靠位置区分。
从当前聊天右栏迁移时,Task Center 和工作区作为默认打开、单实例且不可关闭的基础应用保留;
浏览器保留一个默认打开实例,但注册为可关闭的多实例应用;成果保持默认打开、单实例且可
关闭;终端不默认打开,由用户通过“+”创建,并从首期开始支持多实例。Tab 必须单行横向滚动,
浏览器保留一个默认打开实例,但注册为可关闭的多实例应用;成果入口移除;
终端不默认打开,由用户通过“+”创建,并从首期开始支持多实例。Tab 必须单行横向滚动,
不能自动隐藏或缩写到不可辨认。“+”固定在滚动区外的行末,溢出时页签区两端提供固定滚动箭头,箭头不覆盖“+”。Task Center 继续作为 Task 的现有
入口;审批定位到所属 Task 或 Runtime。智能心跳不作为工作栏页签,其报告、建议、历史和
完整配置统一归属“智能心跳”菜单入口。当前阶段不新增独立自动化中心。
@@ -265,10 +264,10 @@ Task Center 保留为工作栏中的稳定入口,并在现有基础上适度
- 只索引 Task;每个 Task 只关联一条 Conversation,一条 Conversation 可以关联多个 Task。
- 顶部显示当前会话消息总数和 Agent 累计回复时间;列表显示本项目任务的名称、关联 Conversation、
所属项目、Ask/Execute、状态、最近进展、真实活动时间、累计执行时间和需要关注信息。
所属项目、状态、最近进展、真实活动时间、累计执行时间和需要关注信息。
- 点击 Task 打开其关联 Conversation 并定位 Task。
- 完整消息留在 Conversation;工具、Subagent、审批、错误和成果按 Task 关联到 Runtime、
活动记录和成果查看器中,不在窄栏复制,也不呈现 Job/Run 树。
活动记录、会话消息和工作区中,不在窄栏复制,也不呈现 Job/Run 树。
- 智能心跳的报告、建议、历史和配置不进入任务中心。
任务中心是应用级单例,默认存在且不可关闭。当前项目是唯一范围,标题明确显示项目;切换
@@ -282,8 +281,8 @@ Task Center 保留为工作栏中的稳定入口,并在现有基础上适度
主侧栏最近会话为关联 Task 提供轻量入口:父会话行只显示行首展开按钮,Task 子项使用
任务图标和本地化摘要,展开层级只到 Task;点击 Task 子项打开同一 Conversation 并定位。
新建定制任务使用共享 Modal,默认立即执行和当前 Conversation,可选本项目其他可用会话或
新会话,以及单次定时、每日或每周。内容必填,名称可留空从内容生成;工作模式使用关联
会话的执行时配置,未设置时使用项目默认值,不在创建表单另设模式。
新会话,以及单次定时、每日或每周。内容必填,名称可留空从内容生成;使用关联
会话的执行时配置,不在项目、会话或创建表单保存工作模式。
详细产品边界以 [Task Center PRD](../task-and-job/task-center-prd.md) 和
[Task 与 Job 统一领域模型](../task-and-job/task-and-job-model.md) 为准。
@@ -308,7 +307,7 @@ Task Center 保留为工作栏中的稳定入口,并在现有基础上适度
- 未解决、已查看、已解决、忽略和误报状态。
- “带入输入框”“查看证据”“追问”“停止当前回复”等用户介入操作。
“采纳”只生成可编辑草稿或显式会话操作,不自动发送、执行、切换 Execute 或批准工具。
“采纳”只生成可编辑草稿或显式会话操作,不自动发送、执行或批准工具。
### 7.3 Runtime
@@ -543,37 +542,16 @@ type BrowserToolbarState = {
指标缺失时保留面板入口并显示不可用原因。资源监控入口、采样频率、数据契约与验收由
[应用导航与系统工具入口](../application-tool-navigation/README.md)定义。
### 7.9 成果
### 7.9 工作区文件与会话图片
成果面板统一显示全局、项目、Conversation、Task 执行和监督显式产生的独立成果:
- Markdown、纯文本和 JSON。
- 图片和图表。
- HTML 安全预览。
- 后续的 PDF、Office、表格和其他受支持格式。
普通聊天回复只保留在会话消息流中,不自动复制为成果。只有 Runtime 或受管工具显式声明的
Artifact、自动化和监督生成的独立输出,以及用户手动导入或明确保存的内容进入成果面板。
升级前已经自动保存的普通对话 Markdown 可以从成果列表中隐藏,但不应通过升级迁移物理
删除用户数据库内容。
用户可以切换范围、搜索、预览、查看来源、导出或打开关联对象。成果必须保留项目、
Conversation、Task、内部 Run、创建者、MIME、大小、校验值和时间等可用归属;界面按
Task 呈现来源,不把 Run 作为导航对象。
#### HTML 即时预览
- Runtime 或受管工具通过显式 Artifact 事件声明成果,不能让 Renderer 猜测任意路径。
- Main 验证成果属于当前授权执行空间,限制大小、类型和读取范围后再持久化。
- HTML 使用 `iframe sandbox=""` 和严格 CSP 进行脚本关闭、网络关闭的静态预览。
- 清理脚本、事件属性、嵌套 frame、object、embed、base、link、meta refresh、表单和活动 URL。
- 提供“预览 / 源码”切换,并持续标注“静态安全预览,脚本和网络已禁用”。
- 不使用 `dangerouslySetInnerHTML`,不启用 Electron `webviewTag`。
- 外部打开是明确的用户操作,并说明外部浏览器可能执行脚本或联网。
成果页面已移除。FR-WF1~FR-WF3、导入与排序场景以及图片保留边界统一见
[工作区文件导入与时间](./workspace-files.md)。实际文件使用项目目录作为唯一来源;会话图片
继续使用已有消息引用和 artifact 存储。HTML 会话预览由
[会话 HTML 渲染](../conversation-html-rendering/README.md)维护。
### 7.10 文档
文档是可创建多个实例的工作栏应用。用户从工作区、成果或会话文件引用打开受支持的 Office
文档是可创建多个实例的工作栏应用。用户从工作区或会话文件引用打开受支持的 Office
文件时新增文档 Tab;每个实例固定到一个 ShareServer 编辑会话,不随当前 Conversation 或
Project 切换。Tab 使用文档文件名,同名实例增加序号,完整来源通过 Tooltip 和页签内信息
显示。编辑器、保存、冲突、AI 选区修改和关闭行为以
@@ -686,7 +664,7 @@ SSH/远程项目操作;已有 Host、项目和凭据保持不变。若当前
连接信息、认证前 Host Key 核对、密码/系统 SSH Agent 认证和平台/架构/Shell/Home 有界
探针,全部成功后才原子保存主机、加密凭据和完整 Host Key。认证失败可在弹窗内修改凭据
重试;取消或失败不保留新主机。Key 变化显示旧/新指纹并要求显式高风险确认。项目创建和
设置现可选择托管 SSH、已保存 Host、远端工作目录、OpenCode 和 Ask/Execute,不要求
设置现可选择托管 SSH、已保存 Host、远端工作目录和 Runtime,不要求
额外 trust tier 或 consent checklist。候选完成签名 Agent/Runtime、Workspace、ACP、模型桥
和文本模型 profile 验证后原子提交。私钥导入仍未开放。旧流程已保存但
没有成功探针记录的主机继续读取,但在完成新向导前显示为“需要重新验证”。
@@ -727,7 +705,7 @@ digest。当前 registry、连接或 capability 无效时项目保持未切换
匹配 manifest 元数据,不重新验签或哈希完整 Agent payload。项目切换器立即提交本地选择,
不展示远程激活进度;完整进度和取消只属于新建或显式保存项目。
项目只持久化 Host ID、规范远端路径、Runtime 选择和默认工作模式。新建和保存验证当前
项目只持久化 Host ID、规范远端路径和 Runtime 选择。新建和保存验证当前
Workspace/Runtime,但不写入组件 identity;普通切换不做远程验证。未就绪或版本过旧时,
实际 Workspace/Runtime 操作显示错误。桌面更新造成组件版本变化时,用户先在 Host 管理中
准备环境;项目切换本身不连接、不下载、不扫描、不安装,也不改写项目。
@@ -802,8 +780,8 @@ Runtime 会话失效。产品不维护 T1/T2/T3 等附加信任层级。
- Main 保持可信控制面,远程 GoodBuddy Agent 只接受有范围、有期限的请求。
- GoodBuddy 控制协议负责 Agent、Workspace、Git、Process、Blob 和 Runtime supervision;
Runtime Session、stream、tool、permission、usage 和取消优先复用 ACP。
- Ask 的只读限制在远程 GoodBuddy Agent 和 Runtime 适配层共同强制。
- Execute 是用户对所选 SSH 账号完整权限的授权,直接以该账号可用的文件、进程、网络和
- GoodBuddy Agent 与 Runtime 适配层均不保留产品模式字段或只读名单。
- 请求直接以所选 SSH 账号可用的文件、进程、网络和
工具能力运行,不要求逐工具审批或第二个“受控执行”模式。取消、超时、输出上限和活动
记录继续保留。
- GoodBuddy 模型凭据和 Provider URL 必须留在 Main。远程 Runtime 只获得不含凭据的模型
@@ -901,7 +879,7 @@ remote_operations
- 应用重启后先通过远程 Daemon 的 journal、lease、boot identity 和 event cursor 核对活动
终端、进程与 Runtime;无法确认时标记 `interrupted` 或 `outcome_unknown`。
- 日志使用有界环形缓冲或分页持久化,不能无限写入 SQLite。
- Artifact 继续作为成果的权威实体,不把完整成果复制进工作栏状态。
- 工作区目录是文件的权威来源;会话图片继续使用 Artifact 引用,不复制进工作栏状态。
- 运行记录由 Main SQLite 持久化,Renderer 只保留当前内存视图。升级时迁移旧
`localStorage` 记录;若旧缓存达到原数量、详情或总大小边界,页面持续说明更早记录或
部分详情可能已经丢失。新记录不再按固定活动数或详情字符数丢弃,长列表只做可继续展开的
@@ -916,7 +894,7 @@ Renderer 只通过显式方法访问:
- 终端创建、输入、调整大小、关闭和有界输出订阅。
- 受管进程列表、日志和允许的控制动作。
- Runtime Inspector 快照、事件和允许的控制动作。
- 工作区、成果、浏览器和监督的既有或扩展服务,以及有界的本机资源快照。
- 工作区、会话图片、浏览器和监督的既有或扩展服务,以及有界的本机资源快照。
每个 Main Handler 都必须验证可信发送者、Zod 输入、对象归属和当前状态。不得暴露 raw
Electron、ChildProcess、PTY、SSH Client、Socket 或文件句柄。
@@ -924,8 +902,8 @@ Electron、ChildProcess、PTY、SSH Client、Socket 或文件句柄。
## 12. 安全边界
1. 工作栏能力目录项和面板实例不授予任何能力;权限只由 Main 中的范围和控制契约产生。
2. Ask 在本机和远程 Runtime 边界保持只读。
3. Execute 使用当前执行账号的完整权限,不另加目录或逐工具审批;工作栏不能绕过 Main、Preload 与用户数据边界。
2. 本机和远程 Runtime 按实际注册目录、启用与分配设置提供能力。
3. 使用当前执行账号权限,不另加目录或逐工具审批;工作栏不能绕过 Main、Preload 与用户数据边界。
4. 用户终端和 Agent 工具执行使用不同身份和事件来源。
5. 进程面板只控制 GoodBuddy 受管对象,不接受任意 PID。
6. 本机和远程路径分别在对应文件系统上 canonicalize 并验证符号链接边界。
@@ -1030,7 +1008,7 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
- **FR-RR2**:仅查看被动状态不启动重型 Runtime。显式连接测试、原生清单和执行请求
复用兼容的已启动实例;尚未握手时不能显示为已经验证连接。
- **FR-RR3**:共享进程仍保持各会话的目录、历史、模型用量、工具、权限和业务问答归属。
同一会话顺序执行,不同会话并行;Ask 保持只读,Execute 不增加第二层审批。
同一会话顺序执行,不同会话并行;统一执行不增加一般工具审批。
- **FR-RR4**:请求结束、会话释放与进程退出分别处理。取消一个普通请求、关闭工作栏
或释放一个会话不得误停其他健康会话;无会话的空闲重型实例及时释放。正常续聊保留
原生上下文,应用退出、配置替换和真实进程故障仍有明确的终态与有界回收。
@@ -1067,7 +1045,7 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
## 16. 分阶段实施
本节只表示内部依赖顺序,不表示可以发布临时架构。SSH 远程执行只使用用户级 Daemon,
不发布连接期 stdio Agent。OpenCode Ask、OpenCode Execute 和后续 Runtime 分别通过自身的
不发布连接期 stdio Agent。OpenCode、Continue 和后续 Runtime 分别通过自身的
安装、权限、取消和重连门槛后开放,未完成的能力继续显示不可用。
### 阶段 0:应用级工作栏壳层
@@ -1075,7 +1053,7 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
- 将当前聊天专属右栏提升为应用级壳层。
- 建立稳定能力目录和用户打开、关闭、排序、停靠的面板实例模型。
- 建立实例级跟随、固定和失效目标语义。
- 保留现有任务中心、工作区、浏览器和成果行为;附件与知识库继续由 Composer 管理。
- 保留现有任务中心、工作区和浏览器行为;成果页移除,附件与知识库继续由 Composer 管理。
- 将 Task Center 明确为 Task 的单例索引,并补齐范围、状态、最近进展、需要关注和直接打开 Conversation。
- 审批在所属任务或 Runtime 中持续可见;任务中心只为未关联任务的审批保留按需显示的处理
区域,具体规则见 [Task Center 交互](../task-and-job/task-center-prd.md#5-交互)。
@@ -1085,7 +1063,7 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
- 本机 CPU、内存、磁盘 IO 和 GoodBuddy 桌面进程摘要的按需资源监控。
- 通用 Artifact Event。
- HTML 工作区和成果的静态即时预览。
- HTML 工作区和会话内容的静态即时预览。
- Runtime Inspector Snapshot 与事件。
- OpenCode 会话、子会话、Todo、工具、用量和取消;本地与托管 SSH 的原生 Task 调用在
Conversation 中统一显示为可追踪的 Subagent 状态,而不是普通工具参数卡。
@@ -1115,8 +1093,8 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
- 签名、side-by-side GoodBuddy Agent 安装和按需 detached Daemon。
- SSH connection pool、固定 attach relay、Agent 控制协议、Agent 本地 ACP 日志和断线重连同步。
- 远程工作区、Git、终端和受管进程。
- 当前签名 OpenCode bundle 的 Ask/Execute 均直接启动,Ask 在工具边界保持只读,
Execute 使用账号完整权限;官方 ACP channel、Agent 本地模型网关、取消、超时和活动记录
- 当前源码直接启动 OpenCode bundle,使用账号权限和已接入能力;配套签名包须按 runtime/acp 6 重建。
官方 ACP channel、Agent 本地模型网关、取消、超时和活动记录
以[远程主机技术设计](../remote-host/technical-design.md)为准。
- OpenCode 闭环稳定后,Continue 与 DeepSeek Harness 复用相同面板和通道契约逐个接入。
- 新增或重新验证 Host 时只保存并探测 Agent/Runtime,不自动安装。用户在 Host 卡片用
@@ -1132,7 +1110,7 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
Workspace/Runtime 时直接使用新的 current 环境,无需刷新项目绑定。应用启动和项目切换
本身都不连接 Host,也不下载或安装组件。
- SSH、Main、Daemon、Runtime 和远端重启后的核对、恢复与 `outcome_unknown`。
- 支持平台的安装、Ask、Execute、取消和重连冒烟测试通过后开放对应 OpenCode 能力。
- 支持平台的安装、无模式工具执行、取消和重连冒烟测试通过后开放对应 OpenCode 能力。
### 阶段 5:开放后的扩展
@@ -1156,7 +1134,7 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
### 17.2 通用使用
- [ ] 没有项目时仍可创建终端、选择工作区、打开浏览器和查看成果。
- [ ] 没有项目时仍可创建终端、选择工作区和打开浏览器。
- [ ] 监督可以作用于普通 Conversation、Task 和后续实验对象,不假设编程语境。
- [ ] 工作区不是 Git 仓库时仍可浏览文件。
- [ ] Runtime 不支持某项原生能力时仍可从目录打开面板并获得准确说明。
@@ -1168,9 +1146,9 @@ Escape 收起并返回按钮;点击外部或焦点移出也收起。实时区
- [ ] Renderer 没有任意文件、Shell、进程、PTY、SSH 或 Electron API。
- [ ] Agent 不能未经授权向用户终端注入命令。
- [ ] 进程面板不能枚举或终止任意系统进程。
- [ ] Ask 在本机和远程执行空间均无法调用写入或外部副作用工具。
- [ ] 本机和远程按已启用能力执行,不保留模式限制或一般工具审批。
- [ ] HTML 预览无法执行脚本、联网、打开窗口、提交表单或访问 Electron API。
- [ ] Supervisor 不能自动发送消息、批准工具、切换工作模式或扩大范围。
- [ ] Supervisor 不能自动发送消息、批准工具或扩大范围。
- [ ] SSH 首次连接和 Host Key 变化均经过明确验证流程。
- [ ] 凭据不进入 Renderer、日志、SQLite 明文或命令参数。
- [ ] Host 地址、用户或 Host Key 变化会关闭旧连接,并使旧 Workspace 和 Runtime 会话失效。
@@ -1,5 +1,68 @@
# 工作栏实现与验证进度
## 2026-10-05:文件列表只保留修改时间
按 [FR-WF3、US-WF2](./workspace-files.md) 移除创建时间列及排序入口,修改时间采用紧凑格式,
完整日期保留在悬停提示中。更多操作与时间共用行尾空间,悬停、聚焦及菜单展开时文件名宽度不变。
本机与远程共用 Renderer,无文件访问或 Agent 协议变化。
验证:`WorkspaceFilesPanel.test.tsx` 41 项通过,Renderer TypeScript 检查、相关文件 ESLint
及 `git diff --check` 通过。尚未进行真实 Electron 窗口视觉验收。
## 2026-10-05:Runtime 提交失败与启动取消
本机 OpenCode 提交失败后会结束所属 SSE 等待;远程原生 Session 启动等待期间,
取消与其他 binding 的控制请求可继续处理。保留无默认 Prompt 总时限、共享 peer
隔离、取消后的会话清理及原有事件顺序。实现规则见
[进程复用设计](./runtime-process-reuse-technical-design.md),旧/新回归、当前源码
Windows/Linux x64 实测、真实数据检查及本轮 5 次文本模型调用计数统一记录在
[统一执行进度](../unified-execution/progress.md#2026-10-05-runtime-生命周期审查修复)。
## 2026-10-04:移除成果页,工作区导入与文件时间
对应 [FR-WF1~FR-WF3、US-WF1~US-WF3](./workspace-files.md)。成果 Tab、目录注册及独立导入
界面已移除,旧布局过滤成果实例。工作区显示实际文件的修改时间(2026-10-05 收敛为唯一时间列),支持同级排序,
向所选目录导入原件。本机及远程共用分块管理动作,同名文件不覆盖,失败项独立反馈并清理部分文件。
来源检查确认:`AssistantDatabase.createImageArtifact` 把图片保存为 `artifacts.inline_content`
数据 URL,消息通过 `artifactIds` 引用;上传源图还可通过 `imageSourceArtifactIds` 引用。
这些数据不是工作区文件。本次未改数据库 schema、删除或迁移 artifact,也未自动复制附件。
生成图片继续保留在消息,需要项目文件时由既有 `save_image` 显式保存。
验证证据:
- 最终定向验证:16 个文件,252 项通过、2 项跳过。覆盖 WorkspaceAccess、导入服务、文件管理、
时间传递、布局恢复、工作区排序和虚拟列表、Sidebar、WorkbarShell、ChatTimeline、图片服务及
ProtocolRemoteWorkspaceTransport。命令使用 `npm test --` 加对应文件;新测试入口为
`src/main/workspace/workspace-import.test.ts` 和 `tests/workspace-import.electron.test.ts`。
- `src/main/ipc.test.ts -t "resolves the project root and validates file requests"`:1 项通过,验证
原生选择取消、真实文件导入、路径校验以及已有目录读取。
- App 的成果入口相关测试修正后复跑:2 项通过。真实 Electron 从完整 App 的工作区按钮进入
生产 preload、IPC 和本机文件系统,导入 700,001 字节二进制文件,逐字节核对,同名项不覆盖,
取消无写入;原生文件选择器以固定路径替代人工操作。历史会话图片在 Renderer 重载后仍能解码,
SQLite artifact 数量保持 1,项目根目录只有显式创建的 `selected` 目录。
- `tests/workspace-files-layout.electron.test.ts`:16 个场景通过,包含 Git/非 Git、浅深主题、
四种窗口尺寸、原生滚轮与点击、隐藏/恢复位置,窄栏横向滚动不撑宽 Sidebar。
`tests/workbar-settings-layout.electron.test.ts`:80 项工作栏观察及 12 项设置观察通过,
中英文、浅深主题及原生键盘操作继续可用。
- Linux x64 共享 Host:`scripts/workspace-import-host-probe.ts` 将当前源码打包后,通过现有
pinned SSH 连接运行。Desktop `RemoteWorkspaceAccess` 经 SSH stdio 测试桥调用真实
`createWorkspaceProtocolMethods`、`WorkspaceRegistry` 和 `manageWorkspace`;上传二进制
700,001 字节及空文件成功,Host 核对每个字节、目录归属、mtime/birthtime,同名重传失败且
原文件不变。临时工作区在 `/root/tmp` 下并在结束时清理。模型调用 0 次。
此探针未经过安装版 daemon 的完整二进制帧/attach 路径;该传输契约的本地回归通过,未替换
已安装 Agent。生产远程导入需要部署包含新增 `importFile` 动作的 Agent,旧 Agent 不支持。
- `npm run typecheck`、`npm run lint` 通过。新工作区文档经 `deai-writing` 扫描无阻断项;
两个复核项属于布局偏好字段列举及明确的旧 Agent 支持边界,保留事实说明。
全仓 `npm test` 运行 852.36 秒:505 文件通过、6 文件失败、14 文件跳过;5,974 项通过、
8 项失败、86 项跳过。此次改动对应的 4 项旧断言(App 2 项、虚拟列表缩进 1 项、工作栏
默认 Tab 数量 1 项)已修正,定向复跑通过。其余失败为 `assistant-storage-worker.test.ts`
2 项升级失败、`supervisor-layout.electron.test.ts` 等待 inline error 超时、
`opencode-runtime.test.ts` 的 cleanup-timeout 断言失败。存储升级 2 项单独复跑仍失败;
OpenCode 用例单独复跑通过;监督者布局未重复运行。未改动这些非本次范围的实现,未将
全仓测试记为通过。未提交、发布或打发行包。
## 2026-10-01:自定义 MCP 失败中断回复
已修复共用网关将单个上游发现失败升级为整轮失败的问题,行为见
@@ -1,13 +1,13 @@
# Runtime 交互边界
本文定义当前原生交互的适配范围,不增加工作模式或第二层 Execute 审批。
本文定义当前原生交互的适配范围,不增加产品工作模式或一般工具审批。
| Runtime | 权限确认 | 业务问答 |
| --- | --- | --- |
| 本机 GoodBuddy 管理的 OpenCode | 内部默认配置允许权限;每个 Ask 请求仍使用 deny-all 与显式只读能力。Execute 自动回复属于当前请求的父、子孙会话权限事件,不处理其他并行请求 | 通过 `question.asked` 转交父、子孙会话问题,支持原生单选、多选、yes/no、自由文本及跳过;使用公开 ID 回传 `question.reply/reject` |
| 托管 SSH OpenCode | Agent 按已接受 Prompt 的 Ask/Execute 模式处理 ACP 权限,规则见远程主机技术设计 | 当前 Agent 插件转交原生父、子孙会话 `question.asked`;前台使用同一问答队列,回答和跳过经认证 Agent 控制通道回传,已知 Task 关联显示子任务归属 |
| Continue | Execute 启动传入 `--auto`,剩余原生权限请求按当前模式自动答复 | 对接 `1.5.47` QuizService 的扁平 `pendingQuestion`,其中 `question` 是字符串。选项回答与自定义文本分别回传正确的 `isCustomAnswer`;跳过发送拒绝回答的文本 |
| DeepSeek Harness | 固定 Host 的 Execute 文件与命令工具不询问目录权限;若收到 ACP 权限请求,活跃 Execute 直接选一次性允许,缺少该选项时使用 Runtime 提供的允许选项,Ask 拒绝,不等待额外 authorizer | 固定 Host 当前未提供通用原生业务问答服务,不把普通模型正文误识别成待答协议。模型以普通回复提问时,用户通过下一条消息回答 |
| 本机 GoodBuddy 管理的 OpenCode | 自动回复属于当前请求的父、子孙会话权限事件,不处理其他并行请求;无产品模式名单 | 通过 `question.asked` 转交父、子孙会话问题,支持原生单选、多选、yes/no、自由文本及跳过;使用公开 ID 回传 `question.reply/reject` |
| 托管 SSH OpenCode | Agent 按已接受 Prompt 的归属和实际能力处理 ACP 权限,规则见远程主机技术设计 | 当前 Agent 插件转交原生父、子孙会话 `question.asked`;前台使用同一问答队列,回答和跳过经认证 Agent 控制通道回传,已知 Task 关联显示子任务归属 |
| Continue | 启动使用原生 agent 与 `--auto`,剩余原生权限请求自动答复 | 对接 `1.5.47` QuizService 的扁平 `pendingQuestion`,其中 `question` 是字符串。选项回答与自定义文本分别回传正确的 `isCustomAnswer`;跳过发送拒绝回答的文本 |
| DeepSeek Harness | 固定 Host 文件与命令工具不询问目录权限;活跃请求优先选择一次性允许,缺少时使用 Runtime 提供的允许选项,保留取消和未知请求拒绝 | 固定 Host 当前未提供通用原生业务问答服务,不把普通模型正文误识别成待答协议。模型以普通回复提问时,用户通过下一条消息回答 |
托管 SSH OpenCode 的权限、原生问题事件与回复链路由
[远程主机技术设计](../remote-host/technical-design.md)定义。该远端业务问答基线需要
@@ -17,6 +17,11 @@ Host 环境。2026-09-14 的已发布 Desktop `0.13.2` / Agent `0.11.25` 已包
## 运行与失败
OpenCode 提交请求失败时直接显示该失败,不等待没有被接受的请求产生结束事件。
远程 Runtime 在原生 Session 启动期间也可取消;启动等待不阻塞其他会话的状态读取。
取消及清理范围仍限于所属请求,完整规则见
[Runtime 进程复用技术设计](./runtime-process-reuse-technical-design.md)。
- 结构化 yes/no 是普通选项,不从日志、自然语言或 Shell 输出猜测用户意图。
- 前台同一请求的待答问题按公开 `questionId` 去重,按到达顺序保存在消息的
`pendingQuestions` 中。每次显示队首表单;有多个待答请求时显示数量。后续提问和重复
@@ -177,10 +182,10 @@ Markdown 或名称含 `list` 的工具。子会话清单不覆盖父请求顶部
不变量:
1. 一条请求始终使用接受时的 execution space、工作模式、配置和归属;不能读取后来
1. 一条请求始终使用接受时的 execution space、配置和归属;不能读取后来
切换的 UI 选择,也不以进程 cwd 表示所有会话的 cwd。
2. 同一会话串行,跨会话不增加全局 Prompt 队列。权限、子代理、问题、用量和最终清理
必须沿同一 Session/请求归属,不因共享进程放宽 Ask 或限制 Execute。
必须沿同一 Session/请求归属,不因共享进程改变能力范围。
3. owner 的退出条件依据所有使用者,不依据某个 adapter 的空闲状态。
未启动对象的 dispose 不产生冷启动;一个等待者取消不取消其他等待者的初始化。
4. 正常会话仍持有原生上下文时,不用任意 TTL 清掉它来声称“空闲内存已回收”。
@@ -94,7 +94,7 @@ Skills/MCP 承接、取消和产品生命周期仍待实现与验证。此次可
| FR-2 | 按项目执行空间启动 | 本地使用本地目录,SSH 使用绑定 Host 与远端目录;目录失效时说明原因 |
| FR-3 | 注入点击时选定模型 | 客户端首轮直接使用该模型,无需重复输入供应商密钥 |
| FR-4 | 创建独立客户端会话 | 不修改原聊天历史,不把打开操作伪装成当前聊天的续接 |
| FR-5 | 保持所选工作模式的执行边界 | Ask 的 AI 工具路径只读;Execute 使用相应账号已有权限 |
| FR-5 | 沿用统一执行与能力范围 | AI 工具路径使用已接入能力和相应账号已有权限,无产品模式 |
| FR-6 | 管理启动、重开和关闭 | 不因收起工作栏或切换项目中断;关闭对应客户端资源时释放其桥接 |
| FR-7 | 保留可处理的失败信息 | 启动、模型和远程错误可区分,允许修正后重试,不清空聊天草稿 |
| FR-8 | 明确原生能力与 GoodBuddy 能力差异 | 已分配的 Skills、MCP 等逐项接入并验证;未接入项有明确说明 |
@@ -105,7 +105,7 @@ Task 关联恢复或客户端历史导入。原生客户端产生的历史由对
活动记录中显示为已经同步的聊天内容。
FR-8 的实施清单须区分原生工具、GoodBuddy 分配的 Skills、自定义 MCP、内置 MCP、知识工具
以及遵循当前工作模式授权的配置工具。配置应用不再额外要求 GoodBuddy 原生确认,Ask 仍只读。
以及按当前请求资源范围提供的配置工具。配置应用不再额外要求 GoodBuddy 原生确认。
支持情况以真实调用为准;现有 DS 宿主插件能力不能
直接算作 DS Web 能力。发现现有分配无法承接时,在实施评审中明确差异和处理方式,不能默默忽略。
@@ -121,18 +121,18 @@ FR-8 的实施清单须区分原生工具、GoodBuddy 分配的 Skills、自定
| US-4 | 启动期间连续点击 | 同一启动只创建一次;按钮显示启动中 | FR-6 |
| US-5 | 已打开客户端后切换项目或模式 | 旧客户端保持原项目和启动模式;新操作使用新选择 | FR-5、6 |
| US-6 | 启动失败或模型请求失败 | 保留原因与可操作的重试方式;聊天内容和草稿不变 | FR-7 |
| US-7 | Ask 下从原生界面请求写文件、执行写命令或使用写入 MCP | AI 执行边界拒绝写入,不能通过客户端模式开关绕过 | FR-5、8 |
| US-7 | 从原生界面请求写测试文件、执行命令或使用已分配 MCP | 工具正常执行,无产品模式拒绝;未启用能力不可调用 | FR-5、8 |
| US-8 | 关闭终端、停止 DS 服务或退出 GoodBuddy | 对应进程和桥接释放,其他会话不受影响;原生历史保留 | FR-4、6 |
### 4.2 行为决策
启动参数按点击时的项目、执行空间、Runtime、模型和工作模式确定。界面后续切换不修改已运行
启动参数按点击时的项目、执行空间、Runtime、模型和能力配置确定。界面后续切换不修改已运行
客户端。这里只需保留进程运行所需的内存记录,不增加持久启动快照、恢复日志或数据库迁移。
Continue 和 OpenCode 启动成功后,每次新的点击创建独立终端。启动中的重复点击合并到同一
进行中操作。终端会话数量沿用现有管理器规则,不新增一套数量控制。
DS 按项目、模型配置及工作模式匹配运行服务。配置相同且服务可用时重新打开原服务;配置变更后
DS 按项目、模型及能力配置匹配运行服务。配置相同且服务可用时重新打开原服务;配置变更后
新建匹配实例,保留原实例直到用户停止或应用退出,避免在已有页面中静默改变模型和权限。
同一匹配项并发启动只执行一次。服务已退出则清除内存记录,下一次点击重新启动。
@@ -148,16 +148,13 @@ DS 按项目、模型配置及工作模式匹配运行服务。配置相同且
| 系统浏览器打开失败 | 保留可重开的就绪服务,通知用户重试;不复制含令牌地址到普通日志 | US-6 |
| 服务启动失败 | 清理本次创建的临时资源,再允许重试;不删除用户原生历史 | US-6 |
Ask 沿用现有 Runtime 按工具命令名判断的只读规则,不改变用户手动终端的已有权限。
DS 后台的 `read` 和原生 `skill` 可在 Ask 执行;`write`、`edit`、`pwsh`/`bash`
在 Ask 拒绝,在 Execute 放行,代理工具沿用现有分类。工具出现在模型请求目录中不等于
获准执行,判断仍在 `tools/execute` 路径。Web 接入复用这些规则,不增加 Shell 文本分析、
目录围栏、逐工具二次确认或新的权限等级。Execute 使用当前账号已有的文件、进程和网络权限。
原生客户端使用实际注册工具和已分配能力,不注入产品模式插件、只读名单或逐工具确认。
Main 代理保留请求身份、启用与分配和参数校验。工具使用当前账号已有的文件、进程和网络权限,
用户手动终端的 OS 权限不变。
官方 `dsh-plan-mode@0.1.7-rc.2` 的 Plan 只改变规划提示和状态,工具目录不随之变化;
权限预设由另外的 sandbox/approval 组件处理。因此不能把 Plan 名称当成 Ask 实现,
也不能把官方默认“工作区内修改”当成 GoodBuddy Execute。接入时将启动模式接到现有
工具名规则,原生页面的模式选择保持同一语义。
权限预设由另外的 sandbox/approval 组件处理,不能与已移除的 GoodBuddy 工作模式混同。
保留原生 full-access/approval 适配,不恢复 GoodBuddy 模式钩子。
这些规则覆盖普通启动、使用、关闭和已知失败。尚需技术验证的项目见第 7 节;它们不构成新增
授权层级或通用恢复框架的理由。
@@ -184,7 +181,7 @@ DS 服务运行后,打开入口仍显示“打开DSH Web”,旁边显示“
### 6.1 进程职责
Renderer 仅提交当前会话或项目标识及选择标识。Main 重新解析项目、模型配置、工作模式与
Renderer 仅提交当前会话或项目标识及选择标识。Main 重新解析项目、模型配置与
程序路径,通过共享 Zod 契约和显式 preload 方法完成调用。保留已有可信发送方校验,供应商
密钥留在 Main 加密设置和模型请求路径中。
@@ -282,7 +279,7 @@ Runtime。远端配置与模型桥需要客户端生命周期,不能直接复
| 阶段 | 工作与应提交证据 | 通过条件 |
| --- | --- | --- |
| P1 原生配置验证 | 固定三个客户端版本;保存脱敏命令、环境与程序路径;隔离已有全局配置 | Continue/OpenCode 使用指定模型;DS 自动选中目标项目 |
| P2 模型与模式 | 每个客户端发送最小真实请求;验证流式、取消、Ask 写入拒绝及 Execute 专用目录写入 | 请求确实经过 GoodBuddy 模型桥;客户端模式切换不能破坏 Ask 边界 |
| P2 模型与工具 | 每个客户端发送最小真实请求;验证流式、取消及专用目录写入 | 请求确实经过 GoodBuddy 模型桥,工具沿用已接入能力 |
| P3 生命周期接入 | 配置、PTY、DS 进程和模型桥接入现有管理路径 | 重开、失败重试、关闭及退出能释放对应资源,原生历史保留 |
| P4 远程验证 | 在共享 Host 部署当前代码,验证 Continue/OpenCode 的 SSH PTY 与模型桥 | 启动、请求、取消、断开和重试均有真实 Host 证据 |
| P5 UI 与交付资源 | 接入按钮、通知、停止 DS 服务;完善平台资源与文档 | 从真实按钮到客户端完成对话,无手动补依赖步骤 |
@@ -290,7 +287,7 @@ Runtime。远端配置与模型桥需要客户端生命周期,不能直接复
P1、P2 是实施前段的实验任务,完成后继续交付完整入口,不将实验程序或不可用按钮作为最终功能。
DS 单项目工作区选中和当前模型桥协议已通过本轮验证。后续优先接入三个客户端的既有
Ask/Execute 工具规则、Skills/MCP 配置及客户端生命周期,再完成快捷入口和交付资源。
统一执行规则、Skills/MCP 配置及客户端生命周期,再完成快捷入口和交付资源。
官方 DS 首次预览声明保留原生“继续”流程,模型配置提前准备。
## 8. 验收计划
@@ -299,11 +296,11 @@ Ask/Execute 工具规则、Skills/MCP 配置及客户端生命周期,再完成
| --- | --- | --- |
| 实际使用 | 每个本地客户端从 GoodBuddy 按钮进入当前项目,使用当前模型完成一轮真实对话 | FR-1~4、US-1、2 |
| 配置隔离 | 预置另一套全局模型后,仍使用当前选择;两个项目不串目录或配置 | FR-2、3、US-5 |
| 工作模式 | Ask 尝试文件写入、Shell 写入和 MCP 写入均不执行;Execute 在专用测试目录成功写入 | FR-5、8、US-7 |
| 工具能力 | 在专用测试目录验证文件、Shell 和已分配 MCP 写入;关闭能力后不可调用,无模式限制 | FR-5、8、US-7 |
| 能力复用 | 为声明支持的 Skills、MCP 各验证实际加载及最小调用;列出未承接能力 | FR-8 |
| 生命周期 | 连点启动、切项目、隐藏工作栏、关闭页签、重开 DS、停止服务及退出 | FR-6、US-4、5、8 |
| 已知失败 | Node/原生依赖缺失、失效目录、模型错误、浏览器打开失败、SSH 中断 | FR-7、9、US-6 |
| 远程 | 当前源码在共享 Linux x64 Host 上完成 Continue/OpenCode 的 Ask/Execute、模型和断开场景 | FR-2、3、5、US-3 |
| 远程 | 快捷入口接通后,须在共享 Linux x64 Host 验证 Continue/OpenCode 工具、模型和断开场景;普通远程 Runtime 验证不替代原生客户端入口 | FR-2、3、5、US-3 |
| UI | 浅深色、键盘操作、窄输入区、无压缩按钮、空对话、200% 文字缩放 | FR-1、US-1、2 |
| 平台资源 | 对实际交付的 Windows、macOS、Linux 架构逐项验证 Node、PTY、CLI 和 DS 原生依赖 | FR-9 |
@@ -325,6 +322,9 @@ The DS Web backend implementation and its focused validation are recorded below.
### 9.1 DS Web Backend, 2026-09-26
本节保留 2026-09-26 的接口与测试记录,其中 `workMode` 和 Ask/Execute 限制已由
[统一执行](../unified-execution/README.md)替代;不作为当前接口或工具权限规范。
`src/main/agent/native-dsh-web-client.ts` exports `NativeDshWebClientService`.
Construct it with `rootDirectory`, an async `resolveLaunchEnvironment` returning
`{ nodeExecutablePath, environment }`, and `resourcesPath` in packaged builds.
@@ -380,20 +380,20 @@ launch paths are unchanged because this service supports local DS Web only.
The production coordinator resolves the saved conversation/project and current
settings for both `get` and `open`. Matching includes the selected model profile
and credentials, work mode, capability assignments, knowledge scope, Obsidian
and credentials, capability assignments, knowledge scope, Obsidian
settings and local Node selection. Ready services keep their MCP gateway after an
OS browser-open error and can be reopened or explicitly stopped by their owner.
The coordinator passes its configuration key into the service's existing match
hash, so the inner DS reuse decision also observes updated skill digests and
launch settings even when directory paths are unchanged.
For DS Ask, the built-in gateway's existing read grant supplies the allowed raw
tool names; the Web adapter maps these through the official MCP naming contract.
Custom MCP is exposed in Execute, matching the existing Runtime behavior; custom
`readOnlyHint` annotations do not introduce a separate Ask permission rule.
The gateway supplies enabled and assigned tools; the Web adapter maps these
through the official MCP naming contract. Custom MCP follows the same assignment
rules, without product-mode filtering or approval policy. `readOnlyHint` describes
tool behavior and does not filter the catalog.
Assigned skill directories containing `SKILL.md` load through the official skill
plugin. Continue/OpenCode native Ask now retain Main-bound endpoints and map the
granted read-tool names through both native permissions and execution hooks.
plugin. Continue/OpenCode retain Main-bound endpoints and map the registered
tool names through native configuration without GoodBuddy mode hooks.
OpenCode prefixes names with the bound MCP server name; Continue uses raw names.
Story Graph additionally rechecks Supervisor enablement on every call.
@@ -145,9 +145,9 @@ SelectedRuntimeManager
混用会引入另一项真实风险。不同 profile ID 不通过比较或序列化明文 API Key 来合并;
密钥、Header 和插件配置更新通过现有设置失效路径使旧 owner 退役。
调研基线的 SSH launch profile 把 workMode 写入原生启动配置。当前实现把工作模式
放入 Session/当前 Prompt 路由,并设置原生 Session 工具规则,详见 §6.1;
不能仅从兼容键中删除 workMode,也不向用户增加逐工具审批。
调研基线的 SSH launch profile 曾把 workMode 写入原生启动配置。当前源码已从启动、
Session 和 Prompt 路由删除产品模式,按实际能力和请求归属接入工具,详见 §6.1;
不向用户增加逐工具审批。
## 4. 本机 OpenCode
@@ -168,6 +168,11 @@ SelectedRuntimeManager
使用独立的 ACP 取消路径。
现有 `snapshot: false` 必须保留。
提交 Prompt 的 Promise 与 SSE 消费同时运行。提交返回错误、抛错或控制请求超时后,
立即中止本请求的 SSE 订阅并保留提交错误;不能等待服务端为未接受的 Prompt 发送
`session.idle`。回收仍先 abort 订阅再结束迭代器,随后完成该 Session 的有界取消,
不关闭其他会话的订阅或共享 Server。生产运行不增加整个 Prompt 的默认总时限。
`mcpMutationTail` 目前属于 Runtime 对象;共享后同一原生目录的变更必须由同一 owner
串行协调。注册名称、工具可见性、knowledge token 和清理仍按请求区分,不能用一个全局
“当前 MCP 列表”覆盖同目录或跨目录的其他请求。探针未覆盖动态 MCP,这是实施验收项。
@@ -220,7 +225,7 @@ OpenCode 1.18.x 的 `mcp.add` 在服务端独立执行:客户端超时或取
不依赖 Windows 大小写字符串猜测,也不取消路径存在性校验。
- 保留一个本地 Filesystem、Subprocess 和平台 Shell Provider。Session 使用原生 cwd
路由;非 Session 的清单查询显式传入查询工作区,不能回退到第一个项目。
- 保留 `setup(agentCtx)` 注册 Skill 和工具视图、每 Session 的 Ask definition 身份校验、
- 保留 `setup(agentCtx)` 注册 Skill 和工具视图、每 Session 的请求归属校验、
`sessionId + requestId` 准备及事件路由。Main 的 Web/MCP callback 按对应请求找
ToolProvider,不挂到某个最先创建的 adapter 上。
- 第一轮保持同 profile、图片能力和插件组合共享,`queueUsage` 的模型配置因此仍一致。
@@ -229,7 +234,7 @@ OpenCode 1.18.x 的 `mcp.add` 在服务端独立执行:客户端超时或取
第三方插件的初始化可能依赖配置中的路径,不能因为内置工具支持 cwd 就声称所有插件
天然多工作区安全。已启用插件组合属于进程配置;对实际产品支持的插件验证它使用
Agent scope/Session cwd。若某插件确实有项目级初始化状态,将该状态显式作为不兼容
启动配置,而不是默认给所有工作区各起一套 Provider 或禁止 Execute 工具。
启动配置,不默认给所有工作区各起一套 Provider 或禁用工具。
### 5.2 状态与取消
@@ -282,8 +287,24 @@ Agent 当前 Runtime installation + 兼容配置
- 同一 PID 的多个 binding 不代表同一权限或同一 operation。Host/installation identity
变化仍使用已有失效和退役规则,不把不同 Host、账号或 Runtime generation 合并。
原生 `chat.message` 在模型请求前设置该 Session 的工具规则:Execute 允许工具;
Ask 只开放原生只读、搜索和读取网络内容的工具,不开放 Shell/编辑/写入。
`runtime/startPrompt` 校验及 owner 登记经过 backend 控制队列,原生握手、Session
创建/恢复和模型路由准备在队列外等待。协议接收端也不等待该 RPC 的最终响应才分派
后续帧;未完成的响应继续占用已有 request channel,沿用连接通道容量限制及 FIFO
写入。其他方法的分派顺序保持不变,Prompt 终态仍经 backend 控制队列提交。
同一 binding 的相同启动请求共用 Promise,内容不同的并发启动返回冲突。
启动等待属于 Session owner,可以被取消、启动 watchdog 或关闭中断。取消后不得
发送 Prompt;原生调用迟到返回的 Session 只关闭自身,不能回收仍服务其他 binding
的共享进程。原生握手本身仍由进程 owner 共享,一个等待者退出不取消其他等待者。
模型路由撤销不等待原生 HTTP 响应后才释放 backend 控制队列;broker 关闭、凭据释放
及 terminal 提交仍按原顺序执行。启动准备保存原 transport 和取消 signal,安装完成时
若已取消,按原 Session/operation 再撤销一次,避免早先清理被迟到安装覆盖。撤销沿用
helper 的 operation 匹配检查,不删除同 Session 的其他 operation。HTTP 沿用 10 秒
超时,进程退出或 transport dispose 中止未完成请求;无需新的持久状态或协议阶段。
原生 `chat.message` 在模型请求前设置该 Session 的工具与模型路由,不再按产品模式
过滤 Shell、编辑或写入;请求级端点与子会话归属仍独立。
OpenCode 1.18.29 的 ACP 不转发未注册子 Session 的权限请求,且原生子代理只继承父
Session 的拒绝规则、不继承允许规则,因此子 Session 也按根 Prompt 设置规则,再
保留原生子代理的显式限制。不能只把进程启动配置统一设成 `ask`,否则子代理工具会等待。
@@ -382,7 +403,7 @@ token、global dir、活动 Session 或权限/问题队列。设置替换及窗
| --- | --- | --- |
| 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、图片能力、用量和业务问答不串归属 |
| V3 | FR-RR3 | 第二轮复用同一批会话;不同能力分配不串用,无产品模式或一般审批;Skill、MCP、图片能力、用量和业务问答不串归属 |
| V4 | FR-RR3、FR-RR4 | 一会话取消真实长工具/子代理时另外三会话完成并通过独立项目测试;分别检查请求 abort、SSE、问题映射和工具句柄释放 |
| V5 | FR-RR4 | 关闭面板不取消工作;释放一个会话不关闭共享进程;无会话 owner 空闲后退出;App 退出后本机受管进程无残留,远端按已有 detach 规则保留 Agent-owned 工作 |
| V6 | FR-RR1、FR-RR4 | 等价默认/显式选择复用;不同实际连接/插件配置不串用;配置更新期间旧请求完成,旧 generation 不永久积累;超过八个项目不因轻量 adapter 报重型容量满 |
@@ -24,11 +24,11 @@
[通用助手工作栏与执行空间 PRD](./prd.md)
继续负责工作栏的应用级位置、稳定能力目录、执行空间、范围绑定和总体产品边界。本文不重新
定义 Host Key、凭据、Agent 安装、Ask/Execute Runtime 或项目持久化契约。
定义 Host Key、凭据、Agent 安装、Runtime 执行或项目持久化契约。
## 2. 背景与当前问题
当前工作栏在聊天页面右侧固定显示“任务中心、工作区、浏览器、成果”四个 Tab。用户不能:
最初的聊天右栏固定显示“任务中心、工作区、浏览器、成果”四个 Tab。当时用户不能:
- 通过统一入口按需打开新的工作栏应用。
- 按应用策略关闭并重新打开可关闭的工作栏应用。
@@ -43,7 +43,7 @@ SSH 连接池,但尚不存在工作栏应用实例模型、终端 IPC、终端
### 3.1 工作栏应用
工作栏应用是可以在助手工作栏中打开的一个能力,例如任务中心、工作区、浏览器、成果或
工作栏应用是可以在助手工作栏中打开的一个能力,例如任务中心、工作区、浏览器或
终端、笔记。每项应用通过统一注册表声明:
- 稳定应用 ID。
@@ -77,14 +77,14 @@ SSH 连接池,但尚不存在工作栏应用实例模型、终端 IPC、终端
`defaultOpen`、`required` 和 `closable` 不互相推导。例如浏览器默认打开但不是必需实例;任务
中心和工作区既默认打开又必须存在。`required` 不表示锁定顺序,顺序只由 `reorderable` 控制。
当前六个应用的 `visibleAcrossContextSwitches` 均为 `true`。该属性记录应用目录的显示策略,
当前五个应用的 `visibleAcrossContextSwitches` 均为 `true`。该属性记录应用目录的显示策略,
不改变内容归属,也不授予其他会话的 MCP 访问权限;当前没有使用 `false` 的应用,尚未实现
按上下文隐藏 Tab 的策略。
浏览器在创建时绑定当前 Conversation;切换会话后仍保留原 Tab、选中状态和页面,导航操作
继续使用原 `targetRef.conversationId`。此时新建浏览器才绑定新会话。默认或旧布局中尚未绑定
的浏览器在首次使用时绑定活动会话,此后不再随会话切换改绑。终端同样保留创建时执行目标;
任务中心、工作区和成果的内容继续跟随当前项目。笔记使用应用级范围,选择与草稿不随项目或
任务中心和工作区的内容继续跟随当前项目。笔记使用应用级范围,选择与草稿不随项目或
会话切换,不接受 `targetRef`;重启仅恢复工作栏 Tab 描述,不恢复笔记选择和草稿。
### 3.2 工作栏 Tab 实例
@@ -119,8 +119,8 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
6. 多实例应用每次选择都创建新实例。
7. 终端从首期开始就是多实例应用。
8. 任务中心和工作区保持单实例、默认打开且不可关闭,分别固定在第一、第二位,`reorderable` 为 `false`。恢复旧布局时也按此规则修正顺序,其余 Tab 保持相对顺序。
9. 浏览器注册为可关闭的多实例应用,升级后保留一个默认实例;成果保持单实例、默认打开且
可关闭。
9. 浏览器注册为可关闭的多实例应用,升级后保留一个默认实例。成果应用已移除,旧布局过滤
其实例,其他 Tab 及偏好保留。
10. 用户关闭可关闭实例、切换或重排应用 Tab 后,“+”仍保持可见。
11. 选择“终端”后不再显示第二层目标选择器。存在当前项目时直接绑定该项目,由 Main
根据项目执行空间自动打开本机或托管 SSH Shell;没有当前项目时打开本机 Home Shell。
@@ -128,7 +128,7 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
Shell,不伪装为恢复旧进程。
13. 工作栏收起、切换主页面或切换 Tab 不终止终端;显式关闭终端 Tab 才终止对应 Shell。
14. 应用重启不自动启动 Shell。恢复的终端 Tab 显示“会话已结束”,由用户显式重新连接。
15. 终端是用户交互表面。Ask/Execute 继续约束 Agent Runtime,不把用户键盘操作误判为
15. 终端是用户交互表面。Agent Runtime 沿用能力与资源范围,不把用户键盘操作误判为
Agent 工具调用;Agent、自动化和模型不得未经明确用户操作向终端注入输入。
16. 工作栏继续按上位 PRD 提升为应用级容器;切换聊天、知识、魔法笔记或活动记录等主页面
不销毁 Tab 实例或终端 Session。
@@ -185,7 +185,7 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
- 标签全部可见时移除左右滚动按钮及其占位;溢出时左右箭头在滚动视口两侧各占独立的 28px 宽度,不覆盖标签、关闭按钮或“+”。鼠标移入标签栏时显示可滚动方向的箭头,移出后隐藏;键盘聚焦箭头时显示,无悬停能力的设备保持可滚动方向的箭头可见。悬停箭头时显示轻微底色。点击滚动约一屏,边界方向禁用并隐藏;溢出期间隐藏和禁用均保留占位,避免布局跳动。切换或新增激活标签时将整个标签(含关闭按钮)滚入可见区域。未激活标签使用低对比度灰蓝底色和细边框,激活标签保留亮底与强调色顶线。
- 终端 Tab 默认使用递增标题,例如“终端 1”“终端 2”;状态栏持续显示项目、Host、
工作目录和 Shell。用户可以重命名终端。
- 任务中心、工作区、成果及新建终端的默认标题随界面语言切换,恢复布局时也使用当前语言。
- 任务中心、工作区及新建终端的默认标题随界面语言切换,恢复布局时也使用当前语言。
终端自定义名称保持原文;旧布局中的终端名称保持原值,即使看起来像“终端 1”。浏览器
标题沿用现有行为,不在本次标题国际化范围内。
- 当前 Tab 同时使用连续内容表面、选中边框、顶部强调线、图标和较高字重,不能只靠文字颜色。
@@ -218,7 +218,6 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
| 任务中心 | `single` | 当前项目 | 是 | 是 | 否 | 否 | 聚焦已有 Tab |
| 工作区 | `single` | 当前项目 | 是 | 是 | 否 | 否 | 聚焦已有 Tab |
| 浏览器 | `multiple` | 当前 Conversation | 是,一个实例 | 否 | 是 | 是 | 每次创建新 Tab |
| 成果 | `single` | 当前项目 | 是 | 否 | 是 | 是 | 聚焦已有 Tab |
| 终端 | `multiple` | 当前项目 | 否 | 否 | 是 | 是 | 每次创建新 Tab |
| 笔记 | `single` | 应用级 | 否 | 否 | 是 | 是 | 聚焦已有 Tab |
@@ -334,7 +333,7 @@ type TerminalSessionState =
```ts
type WorkbarAppDefinition = {
id: 'tasks' | 'workspace' | 'browser' | 'results' | 'terminal'
id: 'tasks' | 'workspace' | 'browser' | 'notes' | 'terminal'
label: string
icon: WorkbarAppDefinition['id']
description: string
@@ -463,8 +462,8 @@ Sidebar 在渲染时按当前语言派生应用和默认终端标题,再交给
## 13. 权限与边界
- 用户终端以当前本机账号或所选 SSH 账号的正常权限运行,不增加额外虚构的 trust tier。
- Ask/Execute 是 Agent Runtime 模式,不自动改变用户直接操作终端的 OS 权限。
- UI 必须持续将其标注为“用户终端”,避免被理解为 Ask Runtime 的只读输出。
- Agent Runtime 配置不自动改变用户直接操作终端的 OS 权限。
- UI 必须持续将其标注为“用户终端”,避免被理解为 Runtime 的只读输出。
- Agent、模型、监督器和自动化不能调用终端 write,也不能把指令注入已有 Session。
- 终端输出不会自动进入对话上下文、模型 Prompt、成果或活动记录。
- Renderer 只能引用项目和 Session 的公开 ID;Main 从权威 store 解析目录、Host、凭据和
@@ -569,7 +568,7 @@ Schema、假数据终端或不可用入口的中间产品。
- [x] 关闭可关闭的活动 Tab 后激活正确的相邻 Tab 并恢复焦点。
- [x] 当前 Tab 的背景、边框、顶部指示和字重清晰可辨;关闭按钮仅在悬停、焦点或无悬停
设备上出现,且不引发布局位移。
- [x] 升级后任务中心、工作区、一个浏览器实例和成果按默认布局打开;布局恢复会补回缺失的
- [x] 升级后任务中心、工作区和一个浏览器实例按默认布局打开;布局恢复会补回缺失的
任务中心与工作区。
- [ ] 在所有主要页面都能打开工作栏;切换主要页面不会销毁 Tab 实例或终端 Session。
@@ -0,0 +1,57 @@
# 工作区文件导入与时间
## 产品与交互
FR-WF1:工作区展示当前项目执行目录内的实际文件。成果页、工作栏注册项和独立成果导入入口
已移除。旧布局只过滤 `results` 实例,保留其他实例、目标、展开状态和宽度;若活动实例被移除,
按现有布局规则选择第一个保留实例。
FR-WF2:文件页在“新建文件”后提供“导入文件”小图标按钮,悬停提示包含动作和目标目录,导入期间禁用并更新可访问名称。初始目标为项目根目录;点击树中的目录后以该目录为目标,
点击文件后以其父目录为目标,“浏览此目录”同时更新目标。目标路径常显,打开选择器后固定
本次项目与目标,不因切换会话或项目改变写入位置。本机从选定文件复制字节,远程上传到所选
Host 的项目目录。支持二进制和空文件,不调用文档解析器,也不创建 artifact 数据库记录。
同名文件不覆盖。批次逐项处理,失败后继续其余文件;成功数量、目标和失败原因通过应用通知
显示。写入失败时尝试移除本次新建的部分文件,清理失败则报告残留路径。取消文件选择不写入,
导入期间禁用重复提交。完成后刷新目录。当前不提供传输中取消或进度百分比。
FR-WF3:文件列表仅显示名称和修改时间,不显示创建时间列。名称默认升序,两列均可点击切换排序;首次
选择时间列按最新优先。每一级目录独立排序且目录优先,缺失时间放在同类条目末尾,显示 `-`。
每目录沿用最多 500 条的读取上限,排序只覆盖已读取条目,截断提示保留。修改时间常显月日和时分,
悬停显示完整日期时间;行悬停、键盘聚焦或菜单展开时,在时间位置显示更多操作,文件名宽度保持不变。
窄栏中文件名省略,不因时间列撑宽列表。
US-WF1:选中子目录后导入二进制及空文件,目录刷新后可见,原文件不变。同名项失败而已有
内容保留;成功项仍落在所选目录。
US-WF2:切换排序后文件按真实修改时间显示;即使文件系统提供创建时间,列表也只显示修改时间。
缺失修改时间显示 `-`。文件夹使用目录本身的修改时间,不递归计算子文件的最新时间。
US-WF3:升级前会话图片在聊天和重新打开应用后仍可读取;打开工作区不会把会话图片或附件复制
到项目目录。生成的普通文件由 Runtime 文件工具写入工作区并出现在同一目录列表。
## 存储与实现
`WorkspaceFilesPanel` → preload `workspace.importFiles(projectId, path)` → Main
`workspace:import-files` 验证发送方与共享路径契约,固定项目执行空间并打开原生文件选择器。
`importWorkspaceFiles` 使用已有 `WorkspaceAccess.manage`,先独占创建目标,再以 256 KiB
块读取和写入;每次写入检查目标大小与偏移。内存不随文件总大小增长。文件大小受磁盘和权限
约束,不沿用旧成果导入的预览类型与大小限制。
远程复用 `RemoteWorkspaceAccess`、`ProtocolRemoteWorkspaceTransport` 和 Agent
`workspace/manage`。新增 `importFile` 动作的 Base64 块受共享 schema 限制,完整请求低于
1 MiB 控制帧上限。该动作需要含本次源码的新 Agent;旧 Agent 会拒绝新动作并报告失败,
不能声称旧版已支持上传。文件读取和既有管理操作仍走原通路。
本机目录按条目读取 `lstat`,远程复用 Agent 已有目录元数据;两端传递 `mtime` 和有效正值
`birthtime`。缺失 birthtime 时省略 `createdAt`,从不回退为 `ctime`。Renderer 不逐文件发 IPC。
现有 `artifacts` 表包含 `createImageArtifact` 保存的内联图片、会话上传源图引用以及旧导入
内容,并非项目文件索引。移除成果页不迁移、不删除这些记录;`artifacts.get/list`、消息
`artifactIds`、图片恢复与编辑读取保留。图片生成结果继续作为会话图片保存;需要实际图片文件
时由 `save_image` 使用会话图片 ID 显式保存到 Runtime 主机的指定路径,远程项目写到远程 Host,
不需要成果页。引用发现与保存契约见[图片保存工具](../conversation-media-generation/technical-design.md#图片保存工具)。
不会自动给旧图片批量创建文件,也不会把聊天附件
复制到项目。图片工具仍接受其既有路径契约,本次未修改生成或保存工具的运行权限。
验收命令与实际验证边界记录在[进度](./progress.md)。
+1 -1
View File
@@ -43,7 +43,7 @@ Observe
2. 新候选默认 `candidate`,通过离线评估后先进入 `shadow`。
3. 第一阶段只允许人工晋升。
4. 每次晋升必须记录 Baseline、候选、评估结果、作用域和回滚版本。
5. 学习不能修改安全边界、工具审批、目录权限、沙箱或 Electron 配置。
5. 学习不能修改能力分配、目录权限、沙箱或 Electron 配置。
6. 失败案例和用户负反馈只作为评估数据,不直接成为新规则。
7. 回放案例必须脱敏、版本化,并得到用户明确选择或来自仓库公开样例。
8. 模型评估不是唯一真值,优先使用确定性验收和人工反馈。
@@ -4,7 +4,7 @@
## 能力与调用时点
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 管理。
L-1(FR-1、FR-3、FR-7、FR-9;US-01、US-08、US-11、US-15 至 US-19):自动生成与编辑要求图片连接允许会话调用、协议支持所需操作、聊天模型支持工具调用。直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness 及 GoodBuddy Agent 远程适用 Runtime 均须接入同一图片能力,内部工具或 MCP 注入由 GoodBuddy 管理。
允许调用的模型目录取自已保存且“允许 AI 在会话中调用”开启的图片模型配置。目录非空时,图片生成与编辑能力的全部支持 Runtime 分配自动勾选;目录为空时全部取消勾选。模型配置是唯一事实来源,不保存独立可变的能力启用或分配设置,用户不能操作分配复选框。
@@ -13,11 +13,11 @@ L-1(FR-1、FR-3、FR-7、FR-9;US-01、US-08、US-11、US-15 至 US-19):
| 首次开启或重新开启一个图片模型 | 自动勾选全部支持 Runtime,目录包含该模型 |
| 关闭或删除一个模型,仍有其他开启模型 | 分配保持勾选,从目录移除该模型 |
| 最后一个开启模型被关闭或删除,或从未开启任何模型 | 全部分配取消勾选,目录为空;能力行、说明和模型设置入口仍可见 |
| Runtime 离线、聊天模型无工具能力、Ask、配置或执行错误 | 保持按模型开关推导的勾选和目录,另行显示调用限制或错误 |
| Runtime 离线、聊天模型无工具能力、配置或执行错误 | 保持按模型开关推导的勾选和目录,另行显示调用限制或错误 |
配置保存成功后同步刷新分配展示和允许模型目录;保存失败时保留最后已保存配置推导的状态。分配不表示运行就绪,也不保证每个图片模型支持编辑;协议及实际提交校验仍适用。
请求开始和聊天模型、Runtime、配置切换后刷新可用能力;进程复用也须刷新。运行中的聊天回合保留原工具协议,Main 在实际提交前检查最新配置及工作模式,已关闭的连接不能接受新操作。无工具模型说明限制并提示已有直连图片工作流;Ask 不提交生成或编辑,不自动切换模式。Runtime 接入尚未完成属于实施缺口,不能作为减少支持范围的规则。
请求开始和聊天模型、Runtime、配置切换后刷新可用能力;进程复用也须刷新。运行中的聊天回合保留原工具协议,Main 在实际提交前检查最新配置,已关闭的连接不能接受新操作。无工具模型说明限制并提示已有直连图片工作流。Runtime 接入尚未完成属于实施缺口,不能作为减少支持范围的规则。
## 模型和素材选择
@@ -47,7 +47,7 @@ L-3(FR-4;US-03 至 US-07、US-11、US-15):Main 接受提交时固定原
| 离开或重开会话 | 使用当前请求及全局配置 | 只更新原会话,历史成果仍可引用 |
| 远程断连 | 未被 Main 接收的请求不能声称已提交 | 桌面仍运行时由 Main 继续管理已接收操作 |
“停止回复”只结束文字回合;“取消生成”单独处理图片操作。切到 Ask 不撤销先前 Execute 已提交的操作,也不允许新增生成。运行时切换不自动唤起新的 LLM 请求。
“停止回复”只结束文字回合;“取消生成”单独处理图片操作。关闭会话调用后不接受新操作,已提交操作沿用原有取消流程。运行时切换不自动唤起新的 LLM 请求。
## 操作状态
@@ -75,4 +75,4 @@ L-5(FR-5、FR-8;US-07、US-12、US-14、US-15):历史上下文提供成
## 完整性与实施验证
规则覆盖配置、新建、上传图与历史图编辑、模型与 Runtime 切换、Ask、无工具或无视觉模型、取消、失败、重启和会话归属。没有待定产品范围;各 Runtime 工具注入、远程往返及取消传播的具体接法需在实施时核验,按[技术设计](./technical-design.md)完成真实路径验收。视频、跨设备同步和应用退出后的本地执行不在范围内。
规则覆盖配置、新建、上传图与历史图编辑、模型与 Runtime 切换、无工具或无视觉模型、取消、失败、重启和会话归属。没有待定产品范围;各 Runtime 工具注入、远程往返及取消传播按[技术设计](./technical-design.md)完成真实路径验收。视频、跨设备同步和应用退出后的本地执行不在范围内。
@@ -24,11 +24,11 @@ GoodBuddy 自动管理内部工具或 MCP 注入,复用模型连接与凭据
| FR-4 | 支持聊天模型、Runtime 及生成模型切换 | 切换后可发起新的生成和编辑;已提交操作固定原模型和原会话,不重复提交 |
| FR-5 | 支持上传素材、历史成果引用与连续编辑 | 重开会话及切换 Runtime 后可引用已保存原图;缺失素材或能力不足时保留输入并提示处理方式 |
| FR-6 | 生成操作独立显示状态,支持取消及人工重新生成 | 离开会话不取消;保存成功才完成;不虚构进度,不自动重试收费请求;重启后未完成操作显示结果未确认 |
| FR-7 | 遵循工作模式及模型能力边界 | Ask 不提交生成或编辑;无工具模型说明限制,可使用已有直连图片工作流,无需新增表单 |
| FR-7 | 遵循模型与能力配置 | 已允许会话调用的连接可生成或编辑;无工具模型说明限制,可使用已有直连图片工作流,无需新增表单 |
| FR-8 | 图片作为会话成果保存 | 可预览、导出和重新打开,来源模型可追溯;无视觉聊天模型只接收文字事实及引用,不编造观察 |
| FR-9 | 本地及 GoodBuddy Agent 远程适用 Runtime 均支持图片工具;GoodBuddy 管理的图片生成与编辑能力及 Runtime 分配持续可见 | 分别完成真实生成、编辑、切换验收;分配复选框只读,不可手动切换;无开启模型时仍显示该行、未勾选状态、自动管理说明及模型设置入口;无独立能力启停、分配、移除或凭据编辑设置,用户无需维护 Skill 或 MCP server |
分配表示模型配置允许的调用范围。聊天模型不支持工具、Ask、Runtime 离线、配置错误或执行失败均单独说明,不取消配置分配的勾选;不保存可独立修改的能力启用或 Runtime 分配状态。具体推导与刷新规则由 L-1 定义。
分配表示模型配置允许的调用范围。聊天模型不支持工具、Runtime 离线、配置错误或执行失败均单独说明,不取消配置分配的勾选;不保存可独立修改的能力启用或 Runtime 分配状态。具体推导与刷新规则由 L-1 定义。
## 产品验收
@@ -81,3 +81,34 @@
- `deai-writing` 扫描:阻断项为 0;人工复核保留设计状态、能力边界和禁止行为等必要限定,以及真实并列的验收项。
- 独立一致性审阅发现“重新生成”与“重试保存”的操作身份表述不一致,已在 L-4 和 US-09 中统一:前者创建新操作,后者更新原操作。
- `git diff --check` 通过。上述检查只验证文档,不构成功能验收。
## 2026-10-04:会话图片保存工具发现修复
`save_image` 现在也列出仅由消息 `artifactIds` 引用的图片,过滤缺失及非图片记录,
按最近引用去重后取最近 32 张。已发布字段、图片记录及普通消息 metadata 保留。
共享参数说明明确 ID 来自会话图片、上传图片或 `generate_image`,无需成果页,
保存会在 Runtime 主机写入实际文件。契约见[图片保存工具](./technical-design.md#图片保存工具)。
验证:四个专项文件 `image-generation-service.test.ts`、`image-tool-mcp.test.ts`、
`protocol-remote-runtime-channel.test.ts`、`image-runtime-integration.test.ts` 共 70 项通过;
覆盖关闭图片生成后的保存发现、SQLite 重新打开后的字节保存、混合引用过滤与去重、
数据库错误传播、远程 save-only 描述及 MCP 参数说明。`npm run typecheck` 和
`npm run lint` 通过。
[源码 Host 探针](../../../scripts/image-save-host-probe.ts)沿用现有 pinned SSH/stdin 工具消息传输,
桌面运行当前 `ImageGenerationService` 与 `MainImageToolSession`,共享 Linux x64 Host
运行当前 `AgentImageToolMcp`。实测从重新打开的桌面 SQLite 发现单独 `artifactIds` 图片,
远程 MCP 仅列出 `save_image`,分三块读取后写入 307268 字节 PNG,SHA-256 与桌面一致:
`5beca5523d8adbb5eb9719b692f9c9c0ccc06efd05d770a5e8a541fece4c1aa3`。
图片包含用于分块验证的尾随字节。模型调用为 0,测试目录内的数据库与图片在结束时清理。
复跑时用 esbuild 将探针打包为 Node CJS,`electron` 和 `*.node` 保持 external,
`zod`、`zod/v4`、`zod/v3`、`zod/v4-mini` 分别 alias 到各自包内的 `index.cjs`,
避免当前 Zod ESM bundle 初始化错误。将 bundle 传至 Host 专用 `/root/tmp` 目录,
桌面设置 `NODE_PATH` 指向仓库 `node_modules` 后运行:
```text
node <local-probe.cjs> <ssh-host> <remote-probe.cjs>
```
本次真实验证覆盖 MCP 发现及同格式文件保存;生产 `preparePrompt` 描述传递由协议测试覆盖。
没有执行完整桌面 UI、Agent 安装/升级、生产二进制帧传输或真实模型选工具,也未验证远程格式转换。
@@ -25,7 +25,7 @@ Main 持有图片请求、取消句柄及结果保存职责,生命周期独立
稳定图片工具 `generate_image` 同时负责生成和编辑。当前输入为 `prompt`、可选 `modelProfileId`、`intent`(`create` 或 `edit`)、`sourceArtifactIds` 和可选 `quality`;类型及编辑素材约束由 `src/shared/image-generation-contracts.ts` 校验。每次请求沿用已有协议的单图生成和尺寸行为,没有新增数量或尺寸参数。原会话、触发消息和工具调用关联由运行上下文绑定,LLM 不能自由指定结果写入位置或任意文件路径。
每个新回合提供可调用图片模型的脱敏 ID、名称及生成、编辑能力,模型切换、配置变化或进程复用时刷新。Main 提交前重新校验工作模式、连接开关、所需能力及素材。优先由工具描述承载模型选择、图片引用和结果使用说明,不把 Skill 管理作为依赖。
每个新回合提供可调用图片模型的脱敏 ID、名称及生成、编辑能力,模型切换、配置变化或进程复用时刷新。Main 提交前重新校验连接开关、所需能力及素材。优先由工具描述承载模型选择、图片引用和结果使用说明,不把 Skill 管理作为依赖。
直连文本 Model Runtime 使用内置工具契约及分发;外部 Runtime 复用内部 MCP 注入,由 GoodBuddy 自动注册、配置和管理生命周期,远程路径经现有 Agent 工具桥接调用同一服务。内置 MCP 列表中的图片行只展示这项受管能力,不代表直连 Model 也通过 MCP 调用。无需用户创建 Skill、另建 MCP server 或复制凭据,不按模型连接复制工具或 Skill。
@@ -35,6 +35,19 @@ Main 从已保存图片 profile 的会话调用开关推导允许模型目录,
工具等待遵循现有超时约束,返回操作 ID、实际模型、真实状态及完成后的成果 ID,不把 Base64 塞入工具历史。原回合仍有效时通过合法工具响应返回结果;回合结束或 Runtime 切换后,Main 继续写原会话,下一回合读取操作摘要及成果引用,不补发旧响应、不自动唤起 LLM 或重发图片请求。
## 图片保存工具
`save_image` 的 `artifactId` 沿用已发布的会话图片引用,来自上传图片、消息 `artifactIds`
或 `generate_image` 返回的图片 ID,不是已移除成果页的入口要求。图片字节仍保存在
会话图片存储中;保存工具按 `path` 将实际文件写入 Runtime 主机,远程项目写到远程 Host。
`ImageGenerationService.describeSave` 从 `imageSourceArtifactIds`、消息 `artifactIds`
及 `imageOperations[].artifactIds` 收集引用,过滤不存在及非图片记录,按最近出现位置去重后
保留最近 32 张,并按从旧到新排列。读取中的数据库错误继续上抛。即使没有启用图片生成模型,
已有会话图片仍会启用保存工具;没有图片且不能生成时不提供该工具。
远程 `preparePrompt` 转发同一保存说明,Agent MCP 使用共享输入契约并从 Desktop 分块读取
图片后写文件。无需改名、数据库迁移或重新生成历史图片。
## 配置与持久化
| 对象 | 最小数据与职责 |
@@ -69,9 +82,9 @@ Main 仅在图片和会话引用持久化成功后发布完成事件。素材必
| 本地 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 注入成功不能代替真实生成与编辑。
本地 OpenCode、Continue 和 DeepSeek Harness 经内部 MCP gateway 注入;直连 Model 使用内置工具分发。远程通过 `main-image-tool-session.ts`、Agent 协议和 `image-tool-mcp.ts` 回到同一 Main 服务。当前受管远程 Runtime 包括 OpenCode 和 Continue;Continue 模型桥的会话 MCP 交付见[远程 Runtime](../remote-host/technical-design.md#runtime)。保留会话在目录清空时也刷新 MCP,Continue 准备失败时释放本次分配的令牌。工具发现、无计费测试接收端往返或 Skill 注入成功不能代替真实生成与编辑。
Main 已接收的请求在桌面仍运行时独立于远程连接继续;未确认是否接收的请求不自动重放。聊天回合停止等待与用户取消图片是不同动作,须验证现有取消传播不会误杀 Main 图片请求。
@@ -79,7 +92,7 @@ Main 已接收的请求在桌面仍运行时独立于远程连接继续;未确
实施工作包括配置与选择、Main 图片服务提取、各本地及远程 Runtime 工具适配、成果与状态 UI。它们共同完成本图片功能,不按 Runtime 划分交付范围。
针对 L-1 至 L-5 验证选择优先级、失效配置、上传与历史素材、无视觉模型、Ask、会话归属、重复通知、取消及晚到结果。检查切换聊天模型、图片默认模型、本地与远程 Runtime 后的新生成和编辑,以及旧操作不重复提交。回归已有直连图片工作流。
针对 L-1 至 L-5 验证选择优先级、失效配置、上传与历史素材、无视觉模型、会话归属、重复通知、取消及晚到结果。检查切换聊天模型、图片默认模型、本地与远程 Runtime 后的新生成和编辑,以及旧操作不重复提交。回归已有直连图片工作流。
按 US-16 至 US-19 验证从零到一个开启模型、多个中关闭或删除一个、最后一个关闭或删除、重新开启,以及配置错误和 Runtime 离线时的派生分配、目录刷新、行可见性与只读交互。重开设置和应用后均从模型配置推导,不恢复独立分配值;辅助技术可读取标签、勾选状态和说明,键盘可到达模型设置导航。
@@ -26,7 +26,7 @@
直连文本、本地各 Runtime 和 GoodBuddy Agent 远程适用 Runtime 使用一致的图片操作与成果反馈。切换后可继续生成及编辑历史图;工具调用异常时显示具体原因,不宣称已提交。接入未完成的开发状态记录在进度文档,不能作为产品支持范围。
不支持工具调用时提示“当前聊天模型无法自动调用图片工具,可使用已有直连图片工作流”。Ask 下提示“生成或编辑图片需要 Execute 模式”,由用户切换,不自动切换。无视觉能力时说明无法直接查看图片,不妨碍按用户文字调用编辑工具。
不支持工具调用时提示“当前聊天模型无法自动调用图片工具,可使用已有直连图片工作流”。不显示工作模式切换提示。无视觉能力时说明无法直接查看图片,不妨碍按用户文字调用编辑工具。
## 图片卡片
@@ -4,7 +4,7 @@
## 场景验收
适用的自动调用场景须在直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness 和 GoodBuddy Agent 远程适用 Runtime 分别验证,前提为 Execute 且聊天模型支持工具调用。切换验收同时检查原操作归属和目标 Runtime 的新生成、编辑能力。
适用的自动调用场景须在直连文本 Model Runtime、本地 OpenCode、Continue、DeepSeek Harness 和 GoodBuddy Agent 远程适用 Runtime 分别验证,前提为图片连接允许会话调用且聊天模型支持工具调用。切换验收同时检查原操作归属和目标 Runtime 的新生成、编辑能力。
| 编号 | 需求 | Given | When | Then |
| --- | --- | --- | --- | --- |
@@ -15,7 +15,7 @@
| 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-08 | FR-7 | 当前聊天模型不支持工具调用 | 请求生成或编辑 | 说明可使用已有直连图片工作流,不显示模式切换提示 |
| US-09 | FR-6 | 操作正在生成或保存,或已经失败 | 取消,或明确要求重新生成 | 如实显示取消结果;人工重新生成创建新操作,不自动重复收费请求,不提供独立保存重试入口 |
| US-11 | FR-4、FR-9 | 使用 GoodBuddy Agent 远程适用 Runtime | 生成及编辑图片,切换本地后继续编辑,再切回远程生成 | 工具往返可用,成果可继续引用;活动归原会话,凭据留在 Main;远程断连不取消 Main 已接收的操作 |
| US-12 | FR-5、FR-7、FR-8 | 聊天模型无视觉能力但支持工具 | 请求分析图片或根据文字修改图片 | 说明无法直接查看图片;可传原图给编辑工具,不编造视觉观察 |
@@ -25,10 +25,10 @@
| 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-19 | FR-1、FR-7、FR-9 | 至少一个图片模型开启会话调用 | 尝试用鼠标或键盘切换分配,或切换到无工具聊天模型、离线 Runtime,或遇到配置或执行错误 | 分配不可手动修改,运行限制与错误不取消勾选;无独立启用、分配保存、移除或凭据编辑操作;自动管理说明持续可读,辅助技术可识别勾选和禁用状态,键盘可通过“前往图片模型设置”导航修改源配置 |
## 编号变更
US-10(图生视频)移出本图片设计范围,编号保留,不安排实施。US-02、US-05、US-06 的会话选择预期由全局默认和本次指定替代;US-08 使用已有直连图片工作流;US-09 取消独立保存重试要求;US-13 的视频查询恢复由图片中断状态说明替代。其余编号保持,US-15 补充上传图片编辑的显式验收。
US-16 至 US-19 补充自动管理分配验收,设置展示不依赖 Execute 或当前聊天模型支持工具;自动生成与编辑仍遵循本节的调用前提。
US-16 至 US-19 补充自动管理分配验收,设置展示不依赖当前聊天模型支持工具;自动生成与编辑仍遵循本节的调用前提。
@@ -12,6 +12,8 @@
监督回顾按来源版本及正文位置增量处理,手动回顾与心跳共享进度,无变化时跳过模型;“重新整理”才重读所选区间。旧的进度、失败重试和删除边界见[自动增量与手动重看](./logic-design.md#自动增量与手动重看)。
未发布回顾在会话删除后省略相关来源并继续,具体覆盖和历史保留规则见[会话删除](./logic-design.md#未发布回顾遇到会话删除)。
| 文档 | 权威职责 |
| --- | --- |
| [会话监督 PRD](./prd.md) | 监督目标、证据、介入方式和产品验收 |
@@ -24,6 +26,7 @@
| [回顾分块调度设计](./review-scheduling-design.md) | 第 0 节定义生产分页、批次持久化、续跑、当前设置及剩余边界;其余章节保留改造前基线、目标设计和原型证据 |
| [故事线模型改造设计](./storyline-model-design.md) | 全局 › 项目 › 功能 › 子线索的故事层级、故事归属与经验抽取阶段、用户调整及实施顺序(目标设计) |
| [实施进度](./progress.md) | 已验证实现、验证证据和当前边界 |
| [Renderer 修复与测量](./frontend-progress.md) | K01、K03、K04 的界面回归、源码 Electron 对照及测量限制 |
| [故事线图谱设计](../story-graph/prd.md) | 图谱对象、环绕时间轴与实体交互 |
生产入口已接通完整来源分页、叶子持久化、持续处理至完成、活动页主动暂停/继续及批次详情;设置可调页大小、批次正文/消息上限、单次响应容量、两个模型超时和共享并发。实际合同及限制见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。真实 DeepSeek 验证已覆盖一个完整的 27 条消息会话;较早的 129 条消息实验仍未完成。两次实验共 5 次付费请求,详情见进度。原始 AbortError 原因尚未由日志确定。
@@ -0,0 +1,99 @@
# Renderer 修复与测量
最终集成补注:本轮暂存内容在排除并行功能的隔离工作树通过完整验收,6,212 项通过、0 失败、87 跳过;类型检查与 lint 通过。下文保留分组与早期失败记录,最终提交验收以[统一记录](../../review/kiss-local-fixes-2026-10-05.md#最终检查)为准。
## 2026-10-05:K01、K03、K04
对应[局部修复清单](../../review/kiss-local-fixes-2026-10-05.md)及[性能原则](../../architecture/performance-principles.md)的 P5、P6、P7、P8、P10。此处只记录 Renderer;Main 查询、Worker、发布事务和数据库验证由[实施进度](./progress.md)记录。
`SupervisorWorkspace` 首次工作回顾不再请求图谱。进入图谱页时按所选结果读取,内存中只保留当前结果的一份图谱;普通页签切换复用,刷新、历史导航和修改后的刷新清除缓存。图谱错误不阻止阅读回顾,失效请求不能重新填入旧缓存。来源正文继续在点击后读取,未改变 IPC DTO。
工作回顾与图谱使用同一份故事读取状态,按范围和刷新版本复用。摘要在 workspace 中按故事快照和时间区间计算一次,子组件直接渲染该结果;父子关系索引替代逐故事全量扫描。保留直接子线索聚合、孤立子线索、跨项目关联、时间边界及排序语义。App 只移除了监督者刷新中的成果列表读取,未拆分全局状态或新增领域 store。
事件、实体、关系列表使用 `useListWindow`,保留选中和焦点行,按实际行高计算占位。新增组件处理跨窗口的方向键、Home/End、Tab/Shift+Tab 和选择,完整记录仍可滚动访问。原生 Electron 回归发现,窗口切换把已渲染行换成估计高度的占位后,末条焦点行会移出视口;共享 hook 已补可见焦点保护,并验证主动滚离焦点行时不会被拉回。该行为也有独立共享 hook 回归。
## 回归证据
修改前运行新用例,三项均按预期失败:概览调用了 `graph`;同一故事快照计算摘要两次;600 个事件全部挂载。修改后,监督者、心跳中心、摘要及共享列表相关回归通过。历史结果、迟到图谱和来源响应、图谱失败与重试、实体修改后重新读取、来源正文按需读取均有覆盖。App 集成回归验证首次和缓存路由返回不再重读成果列表。
最后复查补充了历史结果已删除的回归:显式结果 ID 查询为空时,工作回顾显示可重试错误,重试仍使用原 ID,不退回最新结果,也不请求图谱。该失败分支在修复前已运行并失败,修复后单独验证;不改变下述性能 fixture 的成功结果路径。
`tests/supervisor-layout.electron.test.ts` 在 Vite 中运行生产组件,新增 `?windowed=1&recap=1` 固定数据,包含各 600 条事件、实体和关系。原生键盘遍历跨越未挂载行,检查首尾可达、焦点可见、选中状态、滚动后视口有内容;随后执行来源读取、回顾与图谱切换、历史切换、实体编辑和 390px 窗口检查。常规 1440、1024、390px、浅深主题及密集图谱布局场景继续通过。
测试使用真实 Chromium 布局、鼠标及键盘输入和生产 React 组件。bridge 返回固定合成数据,编辑写入测试内存;这不是生产 Main、SQLite、模型调用的端到端证明。完整 App 路由由 jsdom 集成用例覆盖。未运行模型请求、生产构建、打包或已打包应用探针。
## 源码性能对照
Windows x64,Intel Core Ultra X7 358H,Electron 44.5.1,不降速。基线取独立临时 worktree 的 `e50ca6d1`,复用现有 Supervisor Electron/Vite fixture;Vite 从 worktree 读取基线 Renderer/shared 源码。两版本使用相同合成数据和原生鼠标输入,最终顺序为基线 1、修复 1、基线 2、修复 2、基线 3、修复 3。每次启动独立 Vite 缓存和 Electron 用户目录。
下表每格按三轮列值,单位为 ms。点击反馈为点击事件至两次 `requestAnimationFrame` 的代理指标;就绪帧指帧回调中观察到 `aria-busy=false`,不等于完整内容提交或合成器呈现时刻。长任务来自 Renderer `PerformanceObserver`,未与 Main 查询耗时相加。
| 场景与指标 | 基线三轮 | 修复三轮 |
| --- | --- | --- |
| 已预载路由,2,000 事件,图谱点击反馈 | 346.3 / 325.0 / 188.3 | 29.9 / 38.5 / 24.3 |
| 同场景,图谱长任务时长 | 338+203 / 317+169 / 182+131 | 0 / 0 / 0 |
| 同场景,挂载行数 / 文档元素数 | 2,000 / 8,207 | 24 / 329 |
| 2,000 故事各十事件,首次回顾摘要 CPU | 80.0 / 73.2 / 48.2 | 15.4 / 16.2 / 9.3 |
| 同场景,首次回顾长任务时长 | 83 / 75 / 50 | 0 / 0 / 0 |
| 同场景,图谱返回回顾的摘要 CPU | 140.8 / 117.5 / 88.7 | 0 / 0 / 0 |
| 同场景,缓存路由返回的摘要 CPU | 67.4 / 74.1 / 42.6 | 0 / 0 / 0 |
| 同场景,缓存路由返回点击反馈 | 73.4 / 81.1 / 47.5 | 5.2 / 8.7 / 6.7 |
| 注入 250ms 图谱延迟,首次回顾就绪帧 | 282.7 / 265.1 / 263.8 | 16.2 / 10.9 / 10.6 |
| 未预载模块,八事件,首次回顾出现帧 | 340.9 / 341.9 / 308.1 | 312.5 / 343.8 / 331.1 |
修复版本首次回顾的图谱请求数为零;图谱首次打开才承担模拟延迟。普通图谱/回顾切换不再请求故事,缓存路由返回不再计算摘要。摘要首次的计数包含一次空数据计算和一次已加载快照计算,后续同一快照复用。
未预载模块的首次回顾仍约需 0.31~0.34 秒,包括 Vite 模块加载及 React Suspense 行为,未证明这一项有改善。在该场景随后首次打开八事件图谱时,修复版本第二轮仍出现一次 55ms 长任务;其余修复场景未观察到长任务。三轮样本不足以声明 p95 达标,源码模式也不能代替发布包测量。三个场景均未加载 Three.js,此处没有测量螺旋的 WebGL 初始化。
原始记录及临时测量脚本保留在本机批准的临时目录 `opencode`:`supervisor-comparison.jsonl`、`supervisor-audit-runner.mjs`、`supervisor-audit-driver.mjs`。最终数据标签为 `baseline-final` 和 `fixed-final`;文件还保留较早的对照轮次,不能混算。首次审查和各轮间波动明显,因此记录逐轮数值,不把模块加载、查询等待和渲染 CPU 合并归因为存储。
## 验证状态
- 最终定向 Renderer 与共享列表测试:8 个文件、139 个用例通过。此前包含相关 Main 文件的定向重跑为 368 通过、1 跳过。
- App 首次入口、缓存路由返回及侧栏图谱导航:3 通过。
- 完整 Supervisor 源码 Electron 布局与交互:通过;`npm run lint` 通过。
- 完整 `npm test` 执行结束:515 个测试文件通过、6 个失败、15 个跳过;6,208 个用例通过、7 个失败、87 个跳过,另有一个未处理拒绝。随后修正并重跑了本任务的焦点问题;相关 Main 失败在其负责方更新后重跑通过,Magic Notes 原生布局独立重跑通过。
- 当时 `tests/message-expansion.electron.test.ts` 独立重跑仍触发 90 秒超时;后续定位、修复及定向绿色验证见下节。全量测试尚未整体重跑,不能将定向验证记为全量通过。
- 最终 `npm run typecheck`、`npm run lint`、`git diff --check` 通过。文案扫描无阻断项;复核候选为加载、错误和测量范围的必要限制说明,已保留。
## 2026-10-05:消息展开测试复查
该 Electron 测试在 2,000 和 10,000 条聊天记录场景之后,还执行监督者来源请求的语言切换失效场景。聊天历史使用独立的消息窗口机制,不调用本轮修改的 `useListWindow`;监督者 fixture 只有一个事件,也未进入长列表窗口路径。相同断言下,`e50ca6d1` 和当前源码的普通复跑分别在 16.4 秒和 15.8 秒通过,未复现确定性的 Renderer 性能回退。
加入有界的 100ms 替换图谱响应后,两版本均重现测试同步错误:语言已切换,旧来源按钮仍短暂存在,测试便继续执行;固定两帧后读取按钮的 `disabled` 时,刷新已卸载该节点,抛出空引用错误。测试现在通过 fixture 的来源标题区分第几次图谱响应,等待第二次响应实际渲染后再完成旧来源请求并检查失效结果。初始语言固定为中文,保证切换到英文确实触发刷新。
轮询辅助函数另有一个已验证的超时问题:第二次及后续动画帧中的谓词异常没有进入 Promise 的拒绝路径,Promise 会永久等待,直到外层 90 秒看门狗杀死 Electron。批准临时目录中的 `expansion-wait-probe.mjs` 使用两版本的原始辅助函数和同一“节点消失”输入验证:旧版为 `pending` 加未捕获错误,修复版为 `rejected` 且无未捕获错误。已将谓词异常接入拒绝路径,并保留原有 10 秒等待上限;外层 90 秒上限、DOM 数量、展开状态、键盘和旧来源断言均未放宽。
原先的 90 秒日志没有阶段信息,无法证明它一定由上述某一个等待点触发。此次修复针对已重现的同步错误与未结束的等待 Promise,不将先前超时归因于共享列表或存储。新增阶段耗时及 Renderer 错误输出便于后续定位。没有修改产品 Renderer 或后端代码来绕过测试。
严格 fixture 下,基线完整测试在 15.6 秒通过。当前源码最终合并运行消息展开、监督者布局和共享列表等 10 个测试文件,141 个用例通过,总耗时 69.7 秒;其中消息展开各阶段合计 14.7 秒。两种聊天规模均访问 1,397 条不同记录,最多挂载 15 行、926 个文档元素;保留的展开项从 38 条在删除后归零,原生 Space 收起、输入草稿、监督者迟到来源失效均通过。Supervisor 继续覆盖原生首尾跳转、跨窗口 Tab、焦点可见、主动滚离焦点、历史、来源、编辑及窄窗口。最终类型检查和 lint 通过。
## 2026-10-05:独立复核修正
独立复核发现本轮改动引入了两个问题,均在修复前运行回归确认。
故事加载的旧 `act()` 会保留发起时的 `load()` 回调。A 范围的重命名尚未结束时切到 B,若 A 修改完成后再调用旧回调,它会先递增共享 generation,导致 B 的在途读取也被丢弃。相同范围换刷新版本时,旧回调还会覆盖新结果。新测试分别重现了 B 保持空数据和刷新后显示旧快照;另一个测试确认旧刷新回调会多发一次读取。
`SupervisionStories` 现在在提交时记录当前范围与刷新版本,在递增 generation 和发出读取之前核对回调归属;响应也须属于同一归属对象和请求序号。卸载后清除归属。四个 hook 回归覆盖跨范围修改完成、同范围刷新版本变化、保留回调在刷新后及卸载后的调用,以及当前修改正常重读并在页签返回时复用快照。修复前四项中三项失败,修复后全部通过;没有改变修改请求本身或增加全局状态管理。
列表原先只保留视口附近、选中和焦点行。滚到中间后,从面板按 Tab 会进入第一条已挂载记录;从后续控件按 Shift+Tab 会进入最后一条已挂载记录。原生 Electron 回归实际得到 `event-285` 和 `event-314`,且随后不能按完整列表的边界顺序退出。
`SupervisorSelectionList` 通过已有 `keepIds` 额外保留逻辑首、末行,继续使用原生 Tab 进入和退出,不增加焦点哨兵或拦截外部控件的键盘事件。新增 Electron 场景先选择中间记录、将列表滚到中部,再从真实前后控件用原生 Tab/Shift+Tab 进入。事件、实体、关系三类均检查首末记录、焦点可见、反向退出至前一控件和正向退出至后一控件;随后继续原有方向键、Home/End、跨窗口 Tab、滚动保留、历史、来源和编辑检查。测试准备阶段在同一次浏览器求值中检查并选择中间行,等待选中提交,避免把尺寸重测期间的临时 DOM 当作稳定状态。
最终定向运行包括消息展开、完整 Supervisor 布局、故事加载、workspace、心跳中心、摘要和共享列表:11 个文件、145 个用例通过,总耗时 69.3 秒。App 中相关监督者、心跳和图谱导航另有 11 个用例通过;类型检查和 lint 通过。消息展开仍覆盖 2,000/10,000 条记录,最多挂载 15 行、926 个元素,三个展开状态恢复、原生 Space、草稿保留、删除清理及迟到来源失效均通过。
本次没有运行完整仓库测试、生产构建或模型调用,也没有提交。上文性能表保留其测量时的代码状态;首末行保留会额外挂载至多两行,本次未重跑性能对照,不将旧节点数称为此次修正后的测量结果。原生列表回归中的有界 DOM 断言继续通过。
## 修改归属
本次 Renderer 任务的产品文件为 `App.tsx`(仅删除成果刷新)、`SupervisorWorkspace.tsx`、`SupervisorSelectionList.tsx`、`SupervisionStories.tsx`、`SupervisionStoryDigest.tsx`、`supervision-story-digest.ts`、`supervisor-workspace.css`、`use-list-window.ts`,以及对应的 `App.test.tsx`、`HeartbeatCenter.test.tsx`、`SupervisorWorkspace.test.tsx`、`SupervisionStories.test.tsx`、`supervision-story-digest.test.ts`、`use-list-window.test.tsx`。当前 `SupervisorWorkspace.tsx` 相对 `e50ca6d1` 的修改均来自本任务,包括最后的历史结果缺失检查。
本任务的 Electron 文件为 `tests/supervisor-layout.electron.test.ts`、`tests/message-expansion.electron.test.ts`,以及 `tests/support/` 下的 `supervisor-layout-driver.mjs`、`supervisor-layout-regression.tsx`、`supervisor-selection-driver.mjs`、`message-expansion-driver.mjs`、`message-expansion-regression.tsx`。
| 文档 | 本任务修改 | 并行修改 |
| --- | --- | --- |
| `frontend-progress.md` | 全文 | 无已识别的其他修改 |
| `README.md` | 文档表中的“Renderer 修复与测量”一行 | 未发布回顾遇到会话删除的说明段落 |
| `ui-design.md` | 按需图谱读取、历史和刷新等待规则、列表窗口与键盘规则 | 删除会话后省略来源的活动说明段落 |
其余 Main、Agent、shared DTO、`SupervisorActivity`、`SupervisionReviewStages`、中英文 heartbeat 文案、监督者技术/主进度/需求/逻辑文档、公共评审与路线图、Telegram 和宣传稿修改均不属于本 Renderer 任务。此归属用于区分本任务的改动,未替其他负责方审定或提交其代码。
@@ -10,11 +10,21 @@
回顾获得执行位置后,读取一次连接配置;同次执行中的批次、输出重试、汇总、故事与经验阶段保持该模型。修改监督者选择、应用默认模型或连接配置,仅影响后续执行。继续暂停或失败的回顾、单独重试故事、生成或重试建议时重新读取当前选择;跨重启不保存模型或凭据快照。显式选择的连接被删除、改为图片协议或缺少所需 API Key 时,执行报错并提示修改设置,不自动换成默认模型。
对应 FR-S6。监督模型遇到可识别的临时失败时,统一等待 500ms 后重试一次:HTTP 429、500/502/503/504,以及短暂网络故障或响应流中断。取消、模型超时、认证、配置、额度或容量错误直接停止;无法确认可恢复的错误也不重试。失败尝试的部分正文丢弃,已报告用量和已保存批次保留。叶子输出校验后的缩块重试、导航输出重试保持原规则;请求生命周期见[模型阶段超时与调度](./technical-design.md#模型阶段超时与调度)。
## 整体逻辑架构
报告、监督叶子与导航整理共用监督专用 FIFO 并发池,普通聊天不入池;监督服务外层 run 仍串行。报告在排队前冻结超时,获槽后领取,实际模型请求前校验并刷新租约;报告提交或无变化后先释放槽位,再执行监督。监督超时在 run 创建时保存,节点获槽并解析 Runtime 后计时。阶段超时、租约、传输时限、后台检查与活动轮询各自独立,取值及生效规则见[当前设置合同](./review-scheduling-design.md#当前设置合同)。
完整回顾的生产接线已增加分页来源清单、逐批输出/offset 事务和导航节点。用户主动暂停后显示 `paused`,模型或保存失败显示 `failed`;两者均保留此前成功批次,可继续同一 run。回顾不按累计运行时长自动暂停。存在未读来源、覆盖空洞或未成功导航时不能标 `completed`。手动位置与自动 checkpoint 分开,自动位置只在最终发布事务推进。来源变更目前要求重新回顾,模型配置跨重启固定等差异见[生产接线与剩余边界](./review-scheduling-design.md#0-生产接线与剩余边界)。真实模型验证覆盖 27 条消息的完整会话,较大样本失败单独记录。
完整回顾的生产接线已增加分页来源清单、逐批输出/offset 事务和导航节点。用户主动暂停后显示 `paused`,模型或保存失败显示 `failed`;两者均保留此前成功批次,可继续同一 run。回顾不按累计运行时长自动暂停。存在未读来源、覆盖空洞或未成功导航时不能标 `completed`。手动位置与自动 checkpoint 分开,自动位置只在最终发布事务推进。现存来源修改仍要求重新回顾;会话删除按下节规则省略。其他差异见[生产接线与剩余边界](./review-scheduling-design.md#0-生产接线与剩余边界)。真实模型验证覆盖 27 条消息的完整会话,较大样本失败单独记录。
### 未发布回顾遇到会话删除
对应 FR-S9、US-S35。会话物理删除后,尚未发布的回顾省略其冻结来源,继续处理其他会话;省略包含消息、消息携带的知识引用及已记录会话归属的任务。归档不等同于删除,现存来源修改、单独消息移除或其他不可用原因仍明确失败。初始化前已删除的会话不进入清单。
含删除来源的叶子整批丢弃,其父级导航失效。混合批次中的存活来源从本次初始 offset 重做,包含这些来源的其他批次也失效,避免覆盖空洞或重复事实;与之无关的批次及导航保留。不从保存片段恢复或重读已删除会话。
活动及运行详情显示省略来源数;完成仅表示保留来源已处理并发布,不代表原冻结集合全部覆盖。所有来源均被省略时记录 `no_change`,不生成空报告、不推进这些来源的 checkpoint。删除前已发布的结果、事实和历史片段保留规则不变。已发出的模型请求可能仍返回,但其受影响输出不得发布。
```mermaid
flowchart TB
@@ -224,7 +224,7 @@ type SupervisorDecision = {
以下检查由代码执行:
- Ask 出现写工具请求。
- 明确要求只分析的任务偏离用户意图,尝试写入。
- 工具或路径超出计划快照。
- 未经批准的跨项目或跨分区读取。
- Token、时间、工具、Job/Subjob 和成果预算。
@@ -379,7 +379,7 @@ type SupervisorRecord = {
5. 普通会话监督失败不影响主回答;自动化配置的强制监督失败进入明确关注状态。
6. 评论和通知不包含完整私人消息,证据点击后才在原对象中查看。
7. Renderer 传入的 Evidence ID 必须由 Main 重新验证归属。
8. 删除会话时按产品数据保留策略删除或匿名化监督记录。
8. 删除会话时省略未发布回顾中的该会话来源,继续其他来源,并显示省略范围;已发布历史结果保留。具体规则见[未发布回顾遇到会话删除](./logic-design.md#未发布回顾遇到会话删除)。
9. 用户关闭监督后停止新分析,但保留历史,除非用户明确删除。
## 19. 性能与预算
@@ -4,6 +4,183 @@
当前记录以已验证生产行为为准。监督者尚未覆盖全部 user stories。
## 2026-10-05 监督页面读取与发布局部优化
本节记录 K01 的后端来源投影及 K02、K05、K06,基线为 `e50ca6d1`。生产 IPC 的概览、图谱、故事/经验读取已接入现有只读 Worker,文件数据库失败时不回退 Main 查询。schema 62 只增加概览查询索引;图谱只传来源标题,完整正文仍可按来源 ID 读取。发布只构造一次最终图谱快照,原 Main 事务和原子提交规则保留。接口及顺序规则见[技术设计](./technical-design.md#页面读取与发布2026-10-05)。本节不承担 Renderer 懒加载、摘要和虚拟列表的验证。
先加回归后改实现:最初 6 个用例在旧代码全部失败,分别暴露来源正文随图谱返回、无关项目仍读取全部经验、缺少 UI Worker 路径及重复重写快照等问题。新增文件最终 8 项通过,包含另一连接提交期间的读快照一致性、跨叶子复用身份时首叶子快照与来源引用保留。原子性用例在 checkpoint 写入处注入失败,确认结果、来源、运行状态和 checkpoint 一起回滚,叶子批次可重试。IPC 回归使用真实文件数据库和在临时目录编译的 Worker 源码,断言三个生产 handler 不调用 Main 同步读取;Worker 终止后请求拒绝,来源详情仍返回完整正文。
源码测量复用 `build/run-main-perf.cjs` 的 esbuild/Electron-as-Node 方法。基线从独立临时 worktree 读取,修复版从工作树读取,各运行三轮,第二轮反转版本及数据量顺序。Windows x64,Core Ultra X7 358H,Electron 44.5.1/Node 24.21.0,无 CPU 降速;同机还有并行开发活动,保留全部波动。每个版本每轮使用独立合成 SQLite,100 个会话、20 份结果,最新结果持有全部图谱事实;小/中/大样本分别为 2,000/20,000/40,000 条消息和 200/2,000/8,000 个事件及来源,消息与来源正文各 2,048 个 ASCII 字节。最大库约 212 MB。这是压力分布,不代表用户库。
每个读取入口测首次调用和三个立即重复调用,期间用 `setImmediate` 记录调用方事件循环最大间隔。首次概览包含懒启动 Worker;它不是冷 OS 缓存测量。下表为大样本每轮四次调用中的最大 Main 间隔,单位 ms:
| 入口 | 基线三轮 | 修复后三轮 |
| --- | --- | --- |
| 概览 | 389.03/471.16/872.84 | 2.45/2.10/3.17 |
| 结果图谱 | 583.03/571.86/646.16 | 12.04/12.29/15.79 |
| 故事及经验 | 82.92/77.06/72.28 | 8.56/6.94/19.74 |
修复后大样本概览的首次完成耗时为 94.01/89.65/117.37 ms,暖调用中位数为 0.74/0.74/1.05 ms。图谱首次完成仍为 453.89/476.75/518.11 ms,暖调用中位数为 573.05/551.27/686.15 ms,小时密度仍需读取消息正文。故事及经验首次完成为 90.99/84.18/95.01 ms;SQL 数量从 1,204 降至 6,但全局返回的 17,201 条 SQL 行和完整成员响应保留,不能据此声称整个故事请求变快。
大样本图谱 JSON 从 25,742,067 降至 7,143,667 字节;概览保持 7,901 字节,故事响应保持 3,317,180 字节。数值是 UTF-8 JSON 大小,不是 Electron IPC 实际传输字节。以上计时覆盖生产仓储方法、真实 Worker 及线程传输;不含 Renderer 解析/渲染、窗口 IPC 序列化、点击到下一帧,也不把几个探针耗时相加为端到端结果。
发布另用独立合成库,每批 20 个真实消息来源、20 个事件和 20 个实体,实际执行清单初始化、叶子保存和 `saveSupervisionResult`。本次复测保留并行加入的删除会话校验,测试来源确实存在。测量期间关闭自动 WAL checkpoint,以分离事务本身,未改变产品配置或落盘同步级别。三轮交替结果如下,单位 ms:
| 批数/来源数 | 基线同步耗时 | 修复后同步耗时 |
| --- | --- | --- |
| 10/200 | 27.86/28.77/30.96 | 28.60/18.33/24.88 |
| 50/1,000 | 194.20/228.55/223.33 | 146.28/151.44/127.86 |
| 100/2,000 | 686.15/729.16/729.95 | 271.02/286.14/303.41 |
100 批的快照 UPDATE 从 101 次降至 1 次,快照 SQL 从 444.31/473.67/463.00 降至 14.85/15.49/14.78 ms;修复后 COMMIT 为 27.40/30.89/33.01 ms,整个事务仍为 271.00/286.11/303.39 ms,事件循环最大间隔为 271.06/313.32/303.73 ms。小批次波动大于收益,不声明稳定提速。所有发布样本的 checkpoint 数都与来源数相等。合并后的叶子保存有一次 21.93 ms 峰值,也超过 16 ms,未修改其校验或放宽断言。
迁移验证直接复制基线 worktree 生成并已关闭的 schema-61 合成库,再运行生产 `upgradeAssistantStorage`。55 张用户表、32,947 行及 rowid 的逐行哈希在升级前后相同;20 份概览、2,000 条来源标题及完整正文、100 个故事、200 条经验的结果与排序等价,另比对项目及无关项目读取。升级耗时 61.73 ms,文件由 95,526,912 增至 95,711,232 字节;只报告 `upgrading`,没有内容转换或压缩,重复升级不再执行。`integrity_check` 为 `ok`、外键检查为空,基线文件 SHA-256 未变。单元测试另覆盖旧库有空闲页时也不回收。
最终定向命令包含 `supervision-ui-reads`、`supervision-history`、`supervision-timeline`、`supervision-stories`、`supervision-experiences`、`supervision-review`、`supervision-worker`、`readonly-query-reader`、`assistant-storage-upgrade`、`assistant-database` 和 `ipc` 的测试文件:11 文件、422 项通过、1 项原有跳过。`tests/supervisor-layout.electron.test.ts` 与 `tests/magic-notes-layout.electron.test.ts` 独立复跑,2 项通过。`npm test` 全量曾有 6 项失败、6,209 项通过、87 项跳过:两个新发布 fixture 未保留真实会话、旧 IPC fixture 只模拟一次 429 却要求整个请求失败,以及并行删除和两项 Electron 布局回归。发布 fixture 已改用真实消息;IPC fixture 覆盖重试耗尽后再次执行恢复;上述失败文件的最终复跑均通过。未将这次复跑记成全量重跑通过。
最终 `npm run typecheck`、`npm run lint` 和 `git diff --check` 通过。类型检查发现新 fixture 的阶段字面量不在合同中,已改为既有 `saving`,该文件 8 项再次通过。两份后端文档的中文审校扫描阻断项为 0;人工复核保留数据范围、性能预算和未测项说明。
原始脚本、18 组读取样本及迁移报告保留在系统临时 `opencode/supervisor-kiss-comparison-DzVb85`,读取汇总为 `entry-summary.json`,迁移报告为 `migration-integrity.json`。并行删除校验合并后的 18 组发布样本位于其 `publication-current/publication-measurements.json`;此前同目录外层的发布报告只代表合并前源码。
剩余预算问题:故事读取有一次 19.74 ms 的 Main 间隔,超过 16 ms;大图谱仍要等待数百毫秒的 Worker 查询;首次 Worker 启动也会增加小库概览等待。Main 最终发布仍有约 271~303 ms 的同步阻塞,并未达到性能原则。业务写入迁移、独立存储服务、IPC 实际序列化与并发写锁等待不在本次已验证范围,后续方向见[桌面存储职责](../../architecture/desktop-storage-direction.md)。本轮不迁移全部写入,也不放宽预算。外部模型调用为 0;没有读取用户正文、修改用户库、读取环境文件、生产构建、打包或启动已打包应用,没有提交。
## 2026-10-05 删除会话后继续未发布回顾
对应 FR-S9、US-S35。删除会话现在使受影响的未发布批次及依赖导航失效,其他来源继续;混合批次重算、省略覆盖和历史保留规则见[逻辑设计](./logic-design.md#未发布回顾遇到会话删除)。活动和运行详情已增加中英文省略数量提示。原先因已删除会话而标记需要重建的运行,可重新继续;现存来源的修改或归档仍失败。
验证使用隔离文件 SQLite、真实初始化/恢复 Worker 和可控模型返回,未接触用户数据库:
- `npx vitest run src/main/assistant/supervision-review.test.ts src/main/assistant/supervision-worker.test.ts src/main/assistant/supervision-production.test.ts src/renderer/src/SupervisorActivity.test.tsx`:4 文件、95 项通过。新增场景包括恢复前删除整会话及知识引用/任务、Worker 复制清单时删除、两路处理及导航期间删除、发布前另一连接删除、混合批次存活来源重做、父导航递归失效、历史结果及来源逐字段不变、全部来源删除无空报告和中英文省略提示。
- Node/Web TypeScript 检查、11 个改动 TS/TSX 文件的定向 ESLint、`git diff --check` 通过。差异检查仅有仓库既有 LF/CRLF 提示。
- `npx vitest run src/main/assistant/assistant-database.test.ts -t explicitly.deletes.only.local`:既有本地会话删除与消息级联回归通过,其余 124 项按筛选跳过。
外部模型调用为 0;未运行全量测试、Electron 界面实测或大库删除延迟测量,未提交或推送。回顾仍在桌面直连模型路径执行,不改变远程 Agent。已发出的请求可在删除后返回,结果会被丢弃;不承诺撤回已发送给模型的输入。旧清单若未保存任务会话归属且任务早已被外部删除,不能推断其归属,仍按来源不可用失败。
## 2026-10-05 监督模型临时失败重试
对应 FR-S6。`runSupervisionModel` 已接入一次可取消的临时失败重试,覆盖监督者各模型阶段;错误分类、清理及每次尝试的超时规则见[技术设计](./technical-design.md#模型阶段超时与调度)。直连文本请求保留 HTTP status,并交出失败流中已报告的用量。OpenCode/Continue、直连传输重试和叶子/导航输出重试未改。监督者仍在桌面 Main 直连模型,不经过远程 Agent 或模型桥。
验证结果:
- `npx vitest run src/main/assistant/supervision-model-retry.test.ts src/main/assistant/supervision-production.test.ts src/main/assistant/supervision-review.test.ts src/main/assistant/supervision-model-pool.test.ts src/main/agent/model-runtime.test.ts`:5 个文件,177 项通过,1 项原有跳过。覆盖 429、可恢复 5xx、部分正文后 terminated、连续失败最多两次包装层调用、认证/配置/容量/超时不重试、等待中取消、流和会话清理、用量保留,以及已保存叶子在失败和继续后不重算。
- `npx tsc --noEmit -p tsconfig.node.json`、`npx tsc --noEmit -p tsconfig.agent.json`、4 个改动 TypeScript 文件的定向 ESLint 和 `git diff --check` 通过。
请求验证使用生产 `ModelAgentRuntime` 和注入的假 fetch/响应流,数据保存使用内存 SQLite;外部模型调用为 0。未运行全量测试、真实模型或远程 Host 验证,未提交或推送。
## 2026-10-05 后端阶段审查 A03/A04/A05
对应 SL-5、SL-8、FR-S6、FR-S10。生产候选读取现在将批次项目和跨项目设置传到只读 Worker;最终候选读取返回后检查暂停/取消;故事归属和拆分在原写事务内拒绝已移除或合并的目标。存储、停止与重试规则见[技术设计](./technical-design.md#单次执行与取消)。没有 schema 变更,原始消息、历史结果及人工确认字段保留。
先加回归、后改实现的结果:8 个新用例中旧代码失败 7 个,取消用例原本通过。失败覆盖跨项目开/关两种生产候选、最终候选读取期间暂停、归属期间移除/合并目标、拆分前移除功能/子线索。旧暂停用例实际返回 `completed` 并发布结果;旧归属用例写入隐藏目标且把事件标为已处理。修复后暂停不发布结果或 checkpoint,继续仍复用原批次;失效故事批次回滚且事件可重试。生产身份测试补充真实项目事件关联,保留 UUID 别名、确认字段及失败后继续的断言。
验证命令与结果:
- `npm test -- src/main/assistant/supervision-production.test.ts src/main/assistant/supervision-worker.test.ts src/main/assistant/supervision-stories.test.ts src/main/assistant/supervision-review.test.ts src/main/assistant/supervision-timeline.test.ts src/main/assistant/supervision-experiences.test.ts src/main/assistant/supervisor-service.test.ts src/main/readonly-query-reader.test.ts`:8 个文件、96 项通过。Worker 回归每轮使用两个项目、422 条来源,并检查候选隔离、实体数量、外键及暂停后的完整发布。
- 本次修改的 Main 源码、测试和验证脚本定向 ESLint、`git diff --check` 通过。`npm run typecheck` 首轮发现本轮 fixture 缺默认设置字段,修正后曾被并行 `tests/message-expansion.electron.test.ts:39` 类型错误阻塞;该文件由其负责方修正后,Main/Agent/Web 三组最终全部通过。本轮未修改该文件。
- 定向验收时 `npm run lint` 曾被并行 OpenCode 修改中的三项错误阻塞;负责方修正后,最终全仓 lint 与 typecheck 均通过。两份后端文档扫描阻断项为 0;人工复核保留取消与事务顺序的边界说明。完整集成结果见[阶段修复验收](../../review/stage-audit-fixes-2026-10-05.md#最终检查)。
- `node --import jiti/register scripts/supervision-audit-validation.ts scale <临时父目录>`:真实文件 SQLite,10 个项目、1,000 个实体、10,000 个事件、10,000 条事件实体关联、10 条来源,库文件 10,964,992 字节。三轮交替查询中,每轮十次旧相关子查询合计 3,517/5,302/4,190 ms;新 Worker 查询合计 39.18/34.01/36.04 ms,包含传输。逐项目返回的 100 个候选 ID 及顺序与旧 SQL 完全一致;跨项目读取每轮 0.95~1.79 ms。120 个事件的单批归属事务为 6.63 ms。计数不变,`foreign_key_check` 为空,`integrity_check` 为 `ok`。
- 查询测量是同一合成库内的 SQL 对照,非完整 App 基线;同时有其他本地验证任务运行。原相关查询在修复传参后首次实测暴露每十次 3.60~4.65 秒耗时,随后改为实体集合查询。没有新增索引或迁移,也不据此宣称整窗性能改善。
真实模型检查沿用 `scripts/supervision-production-live.ts` 的环境文件读取与 `ModelAgentRuntime` 无工具模式,由新增合成验证脚本执行。`node --import jiti/register scripts/supervision-audit-validation.ts live <临时父目录> .env.deepseek.local` 只发送一条 147 字符合成消息,真实生产工厂和文件 Worker 完成提取、保存及故事归属。外部请求准确为 **2 次**:提取 1 次、故事归属 1 次;无变化重跑新增请求 0 次。原文片段精确重建,外键与完整性检查通过,总耗时 3.84 秒。脚本上限为 3 次 HTTP 请求、每次模型阶段 45 秒、1,800 输出 token;凭据仅在进程内读取,终端与指标文件只输出计数和验证信息。本机继承的 TLS 环境设置未由本次修改。
报告位于系统临时 `opencode/supervision-audit-Ggn5uW/metrics.json`(规模检查)和 `opencode/supervision-audit-WNu08s/metrics.json`(模型检查)。只写隔离合成库,未读取或修改用户库,未运行完整 App/Electron 回归或整包构建;没有提交。修改位于桌面监督服务及其 SQLite Worker,GoodBuddy Agent 与远程 Runtime 控制路径未改变。
## 2026-10-04 螺旋工具栏与事件颜色修正
接续未提交的回顾布局调整。平铺与螺旋共用标题工具栏样式,项目层级菜单放在面包屑旁,画布内的视角入口和左下角圈数说明已移除。默认斜俯视,拖动及键盘操作保留;两种图谱均以共享 InlineHelp 的 i 图标打开图例。共用历史回顾栏、下钻返回、详情选择与中窄窗口详情面板保留。当前规则见 [UI 设计](./ui-design.md#故事线图谱)。
颜色缺失的直接原因是 `StoryGraph3D` 将整条螺旋创建为一个 muted 材质的管状网格;`buildStoryTree` 只保留事件起点,丢弃结束时间,也未保留事件的主故事 ID。现在模型保留实际区间与精确成员归属,渲染在同一网格上按区间切换材质,事件点和短横按主故事配色。Three.js 默认的弧长参数化会移动时间边界,因此该曲线明确按时间采样位置与切线;区间规则及重叠处理见 [3D 概念](../story-graph/3d-concept.md#时间事件与故事)。主题变化重新读取令牌,不重置相机。
验证结果:
- `npx vitest run src/renderer/src/story-graph-3d-model.test.ts src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/InlineHelp.test.tsx src/renderer/src/AnchoredMenu.test.tsx src/renderer/src/HeartbeatCenter.test.tsx`:98 项通过。覆盖历史 ID、空成员、真实起止、点事件、空档、重叠与裁剪、半径、选择和浮层键盘行为。
- `npx vitest run src/renderer/src/App.test.tsx -t "routes Supervisor review feedback|shows retryable page-local Supervisor errors|graph navigation opens the pinned"`:3 项通过,其余 345 项按名称未运行。
- `tests/supervisor-layout.electron.test.ts` 的默认平铺分支和 `GOODBUDDY_SUPERVISOR_SPIRAL=1` 分支通过。螺旋使用两个故事的确定性区间 fixture,测量实际 TubeGeometry 顶点环和材质组,断言彩色面没有覆盖空档、主故事与木片颜色一致、主题切换更新 WebGL 材质;另检查两层布局、平铺与螺旋字体一致、原生拖动和 Space/Escape、层级菜单键盘、根返回、历史切换及详情选择。
- 只读复用已有临时 `spiral-portable.sqlite`,通过生产 Store 与图谱查询读取历史结果。1440px 浅深主题、1024px 浅色、390px 浅深主题均保留 21 个事件,其中 17 个当前有主故事、4 个未归属。该结果全部为时间点,没有可着色的非零事件区间,因此真实数据验证的是彩色点及中性未归属点,不宣称真实历史有彩色持续时段。测试 API 返回空成员后仍显示 21 个事件。单螺旋实际为 2,801 个顶点环,半径约 78~152,根场景全部顶点在视口内,无页面横向溢出或 Renderer 错误。数值报告位于系统临时 `opencode/supervisor-refinements/portable-measurements.json`。
- `npm run typecheck`、`npm run lint`、`git diff --check` 通过。中文文档扫描及人工复核只处理本轮段落,保留数据口径和未实现边界说明。
一次初测的控件边界断言失败,未改变产品代码的复跑通过;后续尺寸切换先等待实际视口达到目标宽高,再测布局。最终断言保留。所有 Electron 运行关闭截图,模型调用为 0;没有修改原用户库或备份、打包、全量测试、提交或推送。此次变更仅影响桌面展示和测试,Agent 与远程 Runtime 路径不受影响。
## 2026-10-04 回顾与图谱布局统一
本节保留此前验证记录;视角入口、图例及事件着色以本页上一节和当前 UI 设计为准。
工作回顾与图谱共用页签下方的回顾栏,新回顾表单按需展开。回顾正文使用主次两栏,辅助内容为空时恢复全宽;图谱保留独立层级面包屑、共享模式切换、画布角落的视角菜单及图例。中窄工作区的详情改为图谱内按需打开的非模态面板。尺寸、键盘和范围显示规则见 [UI 设计](./ui-design.md#应用入口)与[图谱布局](./ui-design.md#故事线图谱)。本轮接续此前未提交的历史选择移动,将“历史选择替代全部面包屑”改为上述共用栏,并保留原有下钻与历史切换回归。
验证结果:
- `npx vitest run src/renderer/src/AnchoredMenu.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/story-graph-3d-model.test.ts src/renderer/src/supervision-story-digest.test.ts`:96 项通过,覆盖默认最新、显式导航优先、迟到响应、新回顾草稿、摘要展开、故事与经验选择及视角菜单键盘操作。
- `npx vitest run src/renderer/src/App.test.tsx -t "routes Supervisor review feedback|shows retryable page-local Supervisor errors|graph navigation opens the pinned"`:3 项通过,其他 345 项未运行。通知、后台执行和固定结果导航保留。
- `tests/supervisor-layout.electron.test.ts` 的默认布局、`GOODBUDDY_SUPERVISOR_RECAP=1`、`GOODBUDDY_SUPERVISOR_SPIRAL=1`、`GOODBUDDY_SUPERVISOR_CONTENT_LAYOUT=1` 分别通过。内容分支检查 40 个场景/宽度/主题组合,增加实际 2:1 列宽与窄屏顺序断言;螺旋分支检查共用栏跨页签位置、单一历史选择、层级返回、视角键盘、中宽详情开关及短窗口高度。默认分支还约束实际工作区宽度至 800~1191px,验证容器响应。测试窗口加入仓库既有的 `CalculateNativeWinOcclusion` 禁用设置,修正窗口被遮挡时的动画帧等待超时。
- 只读复用临时 `spiral-portable.sqlite`,没有重新读取或修改在线用户库。真实历史结果仍有 21 个事件,1440px 浅深主题、1024px 浅色、390px 浅深主题全部通过;所有根场景顶点在视口内,页面无横向溢出。额外将测试 API 成员响应置空后,21 个事件仍保留。几何测量保存于系统临时 `opencode/supervisor-redesign-final/portable-measurements.json`,连续螺旋采样点仍为 1,401 个,半径范围仍为 78~152。
- `npm run typecheck`、修改文件的定向 ESLint、`git diff --check` 通过。中文 UI 文档扫描无阻断项;复核提示中涉及范围与未实现边界的原有说明保留。
本轮只修改桌面 UI、组件测试、Electron 布局检查及设计文档。保留工作区其他并行修改,包括布局 fixture 的通知消息记录。所有 Electron 运行使用 `GOODBUDDY_SUPERVISOR_NO_SCREENSHOT=1`,不输出截图。没有真实模型调用、数据库或设置写入、全量测试、打包、提交或推送。平铺图仍保留 660px 最小高度,短窗口可纵向滚动;螺旋单独使用 320px 最小高度。390px 平铺标签随画布缩小,完整名称和选择入口保留在列表中。
## 2026-10-04 历史螺旋缺失事件与工具栏
以下保留本轮布局调整前的验证记录;历史入口和视角的当前规则以上节及其 UI 设计链接为准。
读取仓库便携版 `data/assistant.sqlite`,用只读连接及 SQLite online backup 保存到系统临时 `opencode` 目录,后续查询只读备份。报告对应结果 `b1ec5e99-5a72-444b-8c2b-1c4eb4c50960`:本地时间 9 月 28 日 09:48:58 生成,区间 9 月 21 日 09:43:13 至 9 月 28 日 09:43:13,21 个事件、16 个实体。21 个事件均未被 supersede;36 条来源都有 source key、revision 和项目 locator。按同 source key、revision 及片段范围相交检查,没有其他当前事件替代这批来源,排除本例的 ID 过期猜测。
备份时已有 3 个功能故事,均于 10 月 4 日 20:46:32 创建,其中包含该结果的 17 个事件,另 4 个没有故事成员。结果早于故事生成;数据库未保存截图时刻的成员快照,不能声称重现了截图当时的空成员响应。代码中的缺失原因可确定:3D 仅接收当前故事成员,没有所选结果的完整事件输入;无成员时过滤后只剩灰色螺旋,部分有成员时其余事件仍丢失。现在完整事件输入独立于故事成员,显示规则见[故事线模型](./storyline-model-design.md#5-展示与使用)。历史选择移入中间画布标题区,视角改用共享分段控件,并修正默认相机底部裁切。
真实数据验证使用生产 `listSupervisionResults`、`getSupervisionGraph`、故事/经验 Store 查询备份,经本地测试 API 渲染生产 HeartbeatCenter、SupervisorWorkspace 和 StoryGraph3D;没有启动便携版后台服务。`GOODBUDDY_SUPERVISOR_BACKUP` 指定备份路径,`GOODBUDDY_SUPERVISOR_RESULT` 指定结果 ID,运行 `tests/supervisor-layout.electron.test.ts`。1440×1000 浅色和 390×1000 深色均保留 21 个真实事件 ID;根场景分别为 6 个、4 个事件簇,全部几何顶点位于视口内。画布分别为 844×524、309×320,半径 78~152,连续螺旋仍有 1,401 个采样点。历史选择位于中列标题内,工具栏顶部间隔 16px,视角按钮间隔 2px、分段控件内边距 3px、图例间隔 8px,无横向页面溢出、Renderer 错误。事件菜单能逐条定位;额外将测试 API 成员响应置空后,同一批 21 个真实事件仍可在未归属分组查看,数据库未变。
模型与工作区定向回归 46 项、模拟数据螺旋 Electron 回归、上述真实数据 Electron 检查通过;Node/Agent/Web 类型检查、定向 ESLint、diff 检查通过。只截取并读取 1 张初测截图;最终相机修正通过逐顶点投影数值复验,测量保存在临时 `opencode/portable-measurements.json`。未打包替换便携版,未运行全量测试,真实回顾/模型调用为 0,未提交或推送。变更只涉及桌面展示与测试,Agent 和远程 Runtime 路径不受影响。
## 2026-10-04 图谱历史选择与螺旋密度
图谱左上角增加与工作回顾共用的历史选择,默认最新结果,保留显式结果、故事、经验导航及请求代际检查。空图谱与失败状态也保留选择入口。交互规则见 [UI 设计](./ui-design.md#故事线图谱)。
半径不变的直接原因是生产 UI 按 `resultId` 请求图谱,而该数据库分支没有返回 `attention`,只有聚合图谱分支返回。结果级分支现按保存的范围和时间区间统计当前消息;螺旋不再用范围内全部故事的跨度代替回顾区间。模型按每小时消息数归一化,修正边界小时桶、末尾半圈和短区间比例,移除把安静圈半径向峰值抬高的传播步骤。连续曲线、圆形圈段和余弦过渡保留;相同密度不制造半径差异。口径与限制见 [3D 密度规则](../story-graph/3d-concept.md)。
验证:模型、工作区、真实 SQLite 时间线与历史回归共 51 项通过;`ipc.test.ts -t 'collects supervision within the exact UI timeRange'` 的 2 项通过,覆盖 global/projects 结果图谱的精确区间。设置 `GOODBUDDY_SUPERVISOR_SPIRAL=1` 运行 `tests/supervisor-layout.electron.test.ts` 通过:检查实际 Three.js 曲线的 1,401 个采样点、均匀高度、非均匀消息密度的半径差、结果事件过滤,以及 1440px 浅色、390px 深色布局和历史选择同步,未生成截图。Electron 使用模拟 API 数据,SQLite 与 IPC 另用隔离数据库验证,没有读取生产用户库或调用付费模型。Node/Agent/Web 类型检查、定向 ESLint 和 `git diff --check` 通过;未运行全量测试,未提交或推送。本次修改不涉及 Agent 或远程 Runtime 执行路径。
## 2026-10-04 回顾初始化阻塞修复
对应 FR-S4、FR-S6、FR-S10、US-S12、US-S26、US-S27。原全局七天回顾在 Main 的一个 `BEGIN IMMEDIATE` 内扫描 25 页来源,首次 Electron 复现初始化 16.208 秒,IPC 等待 16.221 秒,阻塞发生在模型调用前。初始化现由现有 Worker 入口的独立连接执行,单次扫描后按 200 条写入;取消、未完成清单重建及发布事务的合同见[调度与存储](./review-scheduling-design.md#已接入的调度与存储)。继续前的全清单版本检查同样移出 Main;单批片段读取约 2 ms,保留原路径。候选、摘要和背景复用既有只读查询 Worker。
数据沿用此前 SQLite online backup 得到的 2,429,247,488 字节安全快照,输入 schema 58。所有写入只发生在临时副本;当前工作树含并行 schema 59 执行统计改动,副本初始化会应用该升级。本次未修改迁移、原库、设置或自动计划。周范围为 UTC+08:00 的 2026-09-27 13:58:52 至 2026-10-04 13:58:52,包含 1,167 条消息、68 个会话;五分钟控制范围包含 9 条消息、4 个会话。
Electron 44.5.1,复用原生产服务探针、最小隔离 Renderer、10 ms Main 定时器、20 ms Renderer 定时器及 IPC ping。本地确定性 Runtime 首次响应后请求暂停,来源收集、SQL、提示组装、状态和取消处理使用生产代码。原复现 bundle 与修复版交替测量各三轮:
| 场景/指标 | 原版三轮范围 | 修复版三轮范围 | 最终版本复核 |
| --- | --- | --- | --- |
| 周范围初始化 | 16.641–17.911 s | 1.099–1.146 s | 1.122 s |
| 周范围 Main 定时器最大延迟 | 16.663–17.939 s | 21.96–36.37 ms | 17.17 ms |
| 周范围最慢 IPC | 16.642–17.928 s | 8.1–14.7 ms | 4.7 ms |
| 小范围初始化 | 1.404–1.468 s | 0.588–0.872 s | 0.832 s |
| 小范围 Main 定时器最大延迟 | 1.430–1.476 s | 18.37–29.69 ms | 14.51 ms |
| 小范围最慢 IPC | 1.417–1.485 s | 0.6–25.3 ms | 1.3 ms |
三轮测量后在来源哈希回调增加取消检查,再复核上表最后一列及初始化中暂停/取消。周范围到本地 Runtime 首次调用 1.260 秒,到暂停返回 1.314 秒;小范围分别为 0.956、1.004 秒。全部完成初始化的运行与原清单逐行比较来源键、版本、项目、会话、顺序、码点长度及起始/已处理位置,哈希一致;周范围 1,167 条、小范围 9 条均未截断。Renderer 最大定时器间隔约 21–23 ms。计时包含 Main 调度和系统定时器误差,不等同于每次同步调用耗时;有一轮 Main 最大延迟 36.37 ms,未宣称达到全部性能预算。
初始化启动约 210 ms 时发出暂停,调用受理耗时 0.37 ms,约 310 ms 时完全返回;取消受理耗时 3.41 ms,约 312 ms 时完全返回。两个场景都保留停止中的执行位置,到 Worker 结束才释放;模型调用、清单来源和新 checkpoint 均为零。同时每 25 ms 执行一次数据库点查和生产任务状态写入:点查最大 0.55 ms、写入最大 7.26 ms,IPC 最大 10.8 ms。此前完整初始化并发写入轮的 43 次写入最大 21.65 ms。该探针验证数据库竞争和 Main 响应,未模拟完整聊天流或打开正式监督者页面。
验证:
- `npx vitest run src/main/assistant/supervision-worker.test.ts src/main/assistant/supervision-review.test.ts src/main/assistant/supervision-production.test.ts src/main/assistant/supervisor-service.test.ts src/main/readonly-query-reader.test.ts src/main/assistant/supervision-timeline.test.ts`:最终 6 个文件、74 项通过。真实 Worker 用例覆盖项目/全局/重分析、消息/任务/知识引用、Unicode 长度、旧 checkpoint、初始化暂停和取消、200 条后写入失败及重启重建、旧完整清单版本变化、缺失 Worker 不回退、Worker 异常/关闭后的请求结束、完整发布和下一次无变化。
- 最终 `npm run typecheck` 通过 Main、Agent、Web;`npm run lint`、`git diff --check` 通过。源码没有新增 Worker 打包入口,真实 Worker 测试和 Electron 探针使用 esbuild 编译当前入口;未运行整包构建。中文文档扫描只复核本次新增和修改段落,既有历史段落不作无关改写。
- 原报告和本次 JSON 保存在系统临时 `opencode/supervisor-freeze-compare-*` 目录。`supervisor-compare.cjs` 交替运行保留的原 bundle 与 `supervisor-fix-probe.cjs`,后者使用 `supervisor-fix-worker.cjs`。第三轮小范围在备份时曾因临时盘满失败;清理本次已完成运行的重复数据库、保留报告与原安全快照后,以 `4retry` 完成补测。失败尝试未进入回顾计时。
外部模型调用为 0 次。未运行全量测试、便携版打包或全局完整模型回顾;未提交或推送。监督取数及模型调度属于桌面服务,Agent 和远程模型桥未改变。最终发布大事务的 Main 延迟仍需另测,本次没有迁移该事务。
## 2026-10-04 工作回顾 Toast 反馈
对应 FR-S4、FR-S6、FR-S10、US-S24。`SupervisorWorkspace` 经 `HeartbeatCenter` 使用 App 的既有通知回调,移除工作区操作状态条和错误横幅。开始、完成、暂停、取消、无变化及失败的展示规则见 [UI 设计](./ui-design.md#应用入口)。发起前占用检查、本地请求锁和 Main 的最终准入保留;后台执行、活动持久化和活动页轮询未修改。刷新期间的结束反馈与卸载后的迟到响应分别处理,见 [技术设计](./technical-design.md#单次执行与取消)。未修改搜索范围。
验证记录:
- `npx vitest run src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorActivity.test.tsx`:102 项通过。随后补充来源读取失败重试,单独复跑 `SupervisorWorkspace.test.tsx`,35 项通过;三个组件文件合计覆盖 103 项。
- `npx vitest run src/renderer/src/App.test.tsx -t "routes Supervisor review feedback|shows retryable page-local Supervisor errors|graph navigation opens the pinned"`:3 项通过,其余 349 项未运行。覆盖真实 App 通知接线、关闭、同键替换、跨页签失败、页面内加载重试及历史图谱跳转。
- `GOODBUDDY_SUPERVISOR_RECAP=1` 下执行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过。使用生产 React 组件和模拟数据验证工作回顾布局、历史阅读、重新整理确认及活动页签访问;未调用真实模型。布局 fixture 只记录通知回调的语义,Toast DOM 由上述 App 测试验证。
- `npx vitest run src/main/assistant/supervision-review.test.ts src/main/assistant/supervision-activity.test.ts`:30 项通过、1 项失败。失败为 schema 44 重开测试遇到 `table execution_timing already exists`,来自工作区并行新增的执行统计迁移,本次未修改该迁移。
- 修改的 TypeScript/TSX 和布局 driver 定向 ESLint 通过,`git diff --check` 通过。`npm run typecheck` 被并行执行统计改动阻塞:Node 检查报告 `execution-timing.ts` SQL 参数可能为 undefined、IPC 的 `getExecutionStatsAsync` 不存在;末次 Web 检查报告 App 的 `statsMessageCount` 未定义及 `use-execution-stats.test.tsx` 的字段、参数不匹配。未更改这些并行代码。
- 中文文档扫描后人工复核新增段落。新增内容无阻断项;全文件扫描的 4 个阻断项均在既有用户故事段落,未作无关改写。技术段落的否定句提示用于说明历史通知和卸载边界,按实际行为保留。
没有运行全量测试、其余 Electron 布局分支或生产构建,真实模型请求为 0 次。此次仅调整桌面 Renderer 反馈,未修改 Agent、远程 Runtime 或传输路径;未提交、推送或覆盖已有无关改动。
## 2026-10-03 会话故事图谱开关
FR-S11、US-S30 的“使用故事图谱”已接通 Composer、生产 Preload/IPC、SQLite 和工具网关。会话默认开启,仅在监督者应用启用时显示,位置在知识库之前。存储和执行合同见 [MCP 接入](./story-graph-mcp-design.md#6-内置-mcp-与-runtime-接入),没有数据库版本升级或迁移。另保留本轮开始前已有的监督者独立模型选择及并行性能改动。
@@ -42,7 +219,7 @@ FR-S13 已接通监督者“设置 → 模型”、应用设置持久化和 Main
- 叶子批次输出不可用(无效 JSON、结构不符、引用不存在的来源或实体)时,在同一段待处理正文上缩小到 1/2、再到 1/4(不少于 500 字符)重试,最多 2 次;缩小只是先处理前一部分,其余正文留在待处理里,不跳过、不截断。
- 导航合并输出不可用时,用相同输入再问 1 次。
- 提供方错误、超时、取消和响应超出容量不重试,仍按原来的方式失败并保留已保存批次。
- 当时提供方错误、超时、取消和响应超出容量不重试,失败保留已保存批次。2026-10-05 起临时提供方错误采用上文的一次重试;超时、取消和响应超出容量仍不重试。
改后结果(DeepSeek `deepseek-flash`):
@@ -625,7 +802,7 @@ npx eslint scripts/review-algorithm.mjs scripts/review-algorithm.test.mjs script
### 2026-09-24 模型阶段超时、并发与设计边界
- 本次静态核对当前新代码:报告 `heartbeatReportTimeoutSeconds`、监督 `supervisorOrganizeTimeoutSeconds` 已接入共享校验、设置存储、App/设置表单及 Main 摘要器;整数 30..600 秒、默认 240。报告在等待池前冻结值,监督在摘要器入池前读取值,均在获槽及 Runtime 解析后才计时。完整控制及未覆盖环节见[控制清单](./review-scheduling-design.md#当前超时与调度控制清单已实现)。
- 本次静态核对当前新代码:报告 `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 通过。本次文档修订未复跑这些命令,不将数量相加当作独立用例总数,也不推断全量测试已通过。
@@ -10,11 +10,21 @@
## 0. 生产接线与剩余边界
Main 的手动 IPC 和心跳完成回调均使用 `createProductionSupervisorService`,该工厂连接 `SupervisorService`、`SupervisionReviewStore`、共享模型池及实际 ask Runtime。生产路径不再调用旧的 20 会话 × 20 消息 collector,也不使用旧服务分支的 100 条 / 48,000 字符裁剪。旧的有界服务注入方式仍供原有测试和实验脚本使用;心跳报告本身继续使用原报告预算。
Main 的手动 IPC 和心跳完成回调均使用 `createProductionSupervisorService`,该工厂连接 `SupervisorService`、`SupervisionReviewStore`、共享模型池及无工具 Runtime。生产路径不再调用旧的 20 会话 × 20 消息 collector,也不使用旧服务分支的 100 条 / 48,000 字符裁剪。旧的有界服务注入方式仍供原有测试和实验脚本使用;心跳报告本身继续使用原报告预算。
2026-10-05 起,监督模型包装层对可识别的临时失败统一重试一次,包含 429、可恢复 5xx 和网络/响应流中断;取消、模型超时、配置及容量错误不重试。每次尝试独立计时,等待可取消,成功批次保留。具体错误与生命周期合同见[技术设计](./technical-design.md#模型阶段超时与调度);下文旧草案中“不增加节点网络重试”的描述已由此规则替代。
### 已接入的调度与存储
schema 47 增加运行配置、来源清单、成功批次、导航节点四张表和一个实时来源视图。迁移只创建新结构,保留此前迁移、结果及用户数据。来源清单通过 `(project_id, conversation_id, sequence, source)` 稳定键分页,SQL 事务冻结范围内的来源身份、版本、长度及自动起点;清单不保存正文。项目和会话由来源页共同枚举,不另设全量 JavaScript 目录数组。正文随后按主键和有界 `substr` 读取。
schema 47 增加运行配置、来源清单、成功批次、导航节点四张表和一个实时来源视图。迁移只创建新结构,保留此前迁移、结果及用户数据。清单只保存来源身份、版本、长度及增量起点;正文随后按主键和有界 `substr` 读取,派发顺序仍由 `(project_id, conversation_id, sequence, source)` 决定。
2026-10-04 起,生产初始化和继续前的逐来源版本检查通过已有 `readonly-query-reader.ts` 通信,使用同一打包入口 `readonly-query-worker.ts` 的独立 `supervision` Worker。它自行打开可写连接,不执行数据库迁移或启动恢复。Main 先保存配置、`collecting` 阶段和 `initializing: true`,然后等待 Worker。Worker 在一个读事务内单次扫描来源视图,将元数据保存到连接内的临时表,结束读事务后每次最多写入 200 条清单记录;扫描期间不持有主数据库写锁,不把来源正文或全量清单传回 Main。
清单全部写入后移除 `initializing`。暂停、失败或退出留下的未完成清单在继续时按原时间范围、配置重新收集,再分批替换;初始化完成前不进入模型提取,发布检查也拒绝未完成清单。旧运行没有该标记,继续时仍校验原清单的来源版本,保留已成功批次。仅在既有运行 JSON 内增加可选字段,无 schema 升级。
取消复用共享原子标志,版本哈希回调和写入批次之间检查 signal。Main 可在收集期间响应暂停、取消和活动查询;执行位置保持到 Worker 确认停止,或异常 Worker 确认退出。SQLite 在没有进入哈希回调的扫描片段、文件 I/O 或锁等待内仍不能立即中断。活动显示 `collecting`,写入期间可见的来源数是已保存数量,尚非最终总数。数据库关闭时终止该 Worker;临时表随连接消失,未完成标记供下次继续使用。
候选实体、旧摘要及记忆背景复用已有只读查询 Worker,仍按批次读取当前背景。这几项及初始化的 Worker 故障均明确失败,不在生产磁盘数据库上同步回退;内存测试库保留同步路径。来源片段按主键读取,实测约 2 ms,仍在 Main;最终结果、图谱、运行完成状态和 checkpoint 保持原有同一发布事务。初始化和版本检查使用独立 Worker,避免长扫描占住普通会话的只读查询队列。入口已由现有 Vite Worker entry 和 `out/main/**/*` 打包规则覆盖。
派发按项目及项目内会话轮转,每个会话同一轮至多一个批次,会话内按 sequence/source 排序。批次跨数据库页累计;长消息未装入部分保留待处理。每片最多 8,000 UTF-16 单元、每批最多 50 片,均为封块条件,不是范围上限。位置采用零基、左闭右开的 Unicode 码点;消息 locator 保存 messageId、conversationId、projectId、role、sequence、source、revision、start、end、length。知识引用另保留原 locator 及所属消息,任务使用当前状态和区间内创建/完成时间。
@@ -30,7 +40,7 @@ schema 47 增加运行配置、来源清单、成功批次、导航节点四张
2026-09-26 已接入整次回顾 single-flight、`supervision:cancel` 及实时 `supervision:execution`。手动重复请求不排队,自动请求等待;已取消运行从自动未完成查询中排除,也不能手动继续。持久取消与实际停止分开呈现,准入保留至 Runtime 清理结束。完整生命周期见[单次执行与取消](./technical-design.md#单次执行与取消),第 1 节关于 `pending` 队列和缺少取消入口的描述仅为改造前基线。
当前阶段以 `phase` 写入既有 `supervision_review_runs.state_json`,取值为 `collecting / extracting / summarizing / saving`;无新表或迁移。它记录服务最后进入的阶段,失败和重启后保留;完成仍由 `supervision_runs.status` 的发布事务决定。活动的 `navigationNodes` 从已保存导航表计数,`inFlight` 仍来自进程内计数。旧运行没有 phase 时不回填推测值。清单和最终发布为同步事务,短阶段不保证被轮询看到,清单事务失败也不会留下部分阶段记录。批次接口只有成功记录,没有逐个失败/在途任务或项目、会话聚合状态;界面仅按分页返回的真实归属组织详情。
当前阶段以 `phase` 写入既有 `supervision_review_runs.state_json`,取值为 `collecting / extracting / summarizing / saving`;无新表或迁移。它记录服务最后进入的阶段,失败和重启后保留;完成仍由 `supervision_runs.status` 的发布事务决定。活动的 `navigationNodes` 从已保存导航表计数,`inFlight` 仍来自进程内计数。旧运行没有 phase 时不回填推测值。初始化异步执行并保留未完成标记,短阶段仍不保证被轮询看到;最终发布是 Main 内同步事务。批次接口只有成功记录,没有逐个失败/在途任务或项目、会话聚合状态;界面仅按分页返回的真实归属组织详情。
### 当前设置合同
@@ -42,7 +52,7 @@ schema 47 增加运行配置、来源清单、成功批次、导航节点四张
| `supervisionReview.executionSeconds` | 300 | 30..3600 | 旧设置兼容字段,执行时忽略,设置页不再展示;恢复旧 run 同样不按此值暂停 |
| `supervisionReview.crossProject` | 关闭 | 开关 | 创建回顾时冻结;关闭时每批只以本项目已有实体为候选,也不生成跨项目故事;开启后候选来自全部项目,故事归属可建立跨项目故事 |
| `supervisionReview.storyThreadEvents` | 20 | 4..500 | 创建回顾时冻结;功能的事件数低于此值时,模型提出的子线索并入功能 |
| `supervisorOrganizeTimeoutSeconds` | 240 | 30..600 | 创建回顾时冻结,节点获槽并解析 Runtime 后计时 |
| `supervisorOrganizeTimeoutSeconds` | 240 | 30..600 | 创建回顾时冻结,节点获槽并解析 Runtime 后,每次模型尝试独立计时 |
| `heartbeatReportTimeoutSeconds` | 240 | 30..600 | 维持报告发起时冻结及既有租约规则 |
| `supervisorModelConcurrency` | 1 | 1..4 | 回顾保存接纳并发;共享池上限仍随应用设置实时变化,实际执行取较小值 |
@@ -51,9 +61,9 @@ schema 47 增加运行配置、来源清单、成功批次、导航节点四张
### 未完成与实测限制
- 真实验证覆盖一个 27 条消息的完整会话;较早的 129 条消息实验在第二次输出 JSON 无效后停止。两实验合计 5 次付费请求,没有重复付费或回退。范围及缓存复核说明见[进度](./progress.md#2026-09-24-完整会话生产验证与收尾)。
- 来源被修改或删除时保留旧批次并明确失败,标记需要新建回顾,活动页停止提供无效的继续按钮;下一次自动检查可创建新 run,不会永久重复该失败。尚未实现仅使受影响叶子和父节点失效后重算。清单记录冻结版本,不保存全库正文快照。
- 现存来源修改仍明确失败并要求新建回顾;会话物理删除时省略该会话来源,丢弃受影响的未发布叶子及依赖导航,保留其他进度。混合批次重算及覆盖规则见[会话删除](./logic-design.md#未发布回顾遇到会话删除)。清单记录冻结版本,不保存全库正文快照。
- 分块配置、超时、接纳并发及算法版本已保存。监督者模型连接在每次执行开始时解析,同次回顾复用;继续或重试读取当前选择,规则见[监督者模型选择](./logic-design.md#监督者模型选择)。候选背景仍在执行时读取;未实现跨重启模型/提示词版本固定,也未实现共享池排空后切换冻结并发的目标规则。
- 元数据清单和最终发布仍是同步 SQLite 事务,缺少大库延迟验证及可中断来源读取。批次详情已分页,既有历史图谱查询仍返回整份结果;大图谱的响应大小尚未改造。
- 元数据初始化已移到 Worker,并通过 2.43 GB 隔离副本验证;[测量记录](./progress.md#2026-10-04-回顾初始化阻塞修复)包括取消与来源一致性。最终发布仍在 Main 同步提交,未完成同等规模的全局发布延迟验收。批次详情已分页,既有历史图谱查询仍返回整份结果;大图谱的响应大小尚未改造。
- 没有 token/费用预算、提供商 RPM/TPM、Retry-After 冷却、执行硬期限、独立清理期限或逐层可配置超时。默认正文预算不保证任意模型上下文容量;超长输出、非法 JSON 和引用错误会失败,保留已成功批次。
- 片段以码点边界切分,尚未增加句界优先和重叠背景。事实保留指保留模型已产出的结构化记录,不证明语义无遗漏;生产契约校验引用 token,不要求原型的逐字引文和 reviewedTokens 回执。提供过的 locator.source 仅在本节点唯一对应一个片段时可作为引用别名;歧义及未知来源拒绝。
@@ -88,9 +98,11 @@ schema 47 增加运行配置、来源清单、成功批次、导航节点四张
目标是不再静默裁剪,把单次上限改成分页、分块和暂缓派发的依据,直至冻结范围内全部项目、会话、消息及其他入选来源处理完毕。模型提取仍可能遗漏原文含义,但已产出的叶子事实、事件、冲突及来源必须完整保留、可分页读取。摘要只用于导航,不能替代叶子结果;“原文已处理”也不等于模型识别了全部事实。
### 当前超时与调度控制清单(已实现)
### 初稿时超时与调度控制清单(历史截面)
以下是当前代码事实,不是后文拟议的多层调度预算。设置暴露两个阶段超时和一个监督并发数。
以下表格及其后说明保留初稿时的实现截面,不定义当前生产行为或本次存储迁移需要新增的机制。
其中“无用户取消入口”“收齐后检查 100KB”和原心跳报告交接已被后续实现替代。
现状以本页第 0 节及[监督者技术设计](./technical-design.md)为准,复用既有回顾准入、模型池和暂停/继续机制,不据此再建一套调度器。
| 控制 | 实际值、读取时点与范围 | 代码依据与边界 |
| --- | --- | --- |
@@ -276,7 +288,7 @@ run(request):
| 执行软预算到达 | 停止读页、接纳和派发,在飞节点在节点及硬期限内结束并保存成功批次 | 未完成时显示预算暂停,可继续;若已满足全部完成条件则可正常发布 |
| 批次或发布事务失败 | 该事务回滚;此前成功批次不回滚 | 从最后持久化位置恢复;发布失败不重算已有合法叶子 |
| 达到采集预算,已接纳任务全部成功 | 保存有限覆盖批次及位置,仍有未读内容 | 显示“未完成,可继续”,不以 `completed` 加提示代替真实状态 |
| 有来源读取失败或运行中被删除 | 初版按本轮失败处理,不将读取失败当作没有新证据 | 显示来源不可用的数量和范围,后续重新查询 |
| 有来源读取失败或运行中被删除 | 会话物理删除按[省略规则](./logic-design.md#未发布回顾遇到会话删除)继续;其他读取失败仍报错 | 省略数独立于保留来源的覆盖统计 |
| 自动无新来源 | 保存 `no_change`,无新结果或图谱事件 | 保留旧结果,模型调用数为零 |
成功批次持久化是用户已授权的续跑要求。最小记录包含 run 冻结范围、目录/页位置、来源 revision 与连续 offset、已校验叶子输出及引用、必要的汇总依赖和成功输出。输出与对应位置同事务提交,以 run 和来源区间等稳定身份防止重复保存;不持久化每次进度动画或完整提示词。崩溃前未提交的在飞批次允许重算,已提交成功批次必须复用。表结构及保留/清理实现仍待设计落地,不能声称现有 schema 46 已支持此能力。
@@ -118,15 +118,15 @@ D1 来源页默认 4,000、上限 8,000 码点。`current` 通过既有 locator
| 监督整理参数 | 超时、并发、来源页与批次设置已存在 | 控制后台整理;不让一次 MCP 查询启动整理,也不以正文批次预算截断检索结果 |
| 计划 `enabled` | 决定自动回顾是否到期执行 | 不决定已有图谱能否读取;应用开启且能力可用时,无计划也能查已有结果 |
Main 在工具发现和调用分发时共同检查应用启用、会话开关、能力启用、Runtime 分配和 scope;只隐藏菜单不足以阻止旧会话调用。关闭后拒绝新的读取请求,已在途响应交付前复核状态,避免旧绑定继续返回数据。启用工具本身不产生回顾或图谱写入。Ask 与 Execute 均可读取,同样没有写工具,也不借此扩大 Execute 权限。
Main 在工具发现和调用分发时共同检查应用启用、会话开关、能力启用、Runtime 分配和 scope;只隐藏菜单不足以阻止旧会话调用。关闭后拒绝新的读取请求,已在途响应交付前复核状态,避免旧绑定继续返回数据。启用工具本身不产生回顾或图谱写入。工具本身只读,不依赖产品工作模式,也不增加写工具。
会话开关只决定 Agent 能否按需使用这三个 MCP 工具,不自动查询或注入图谱上下文,也不触发回顾。监督者应用关闭时,输入区完全隐藏开关并保留会话选择;再次启用应用后恢复该选择。后台回顾和计划仍由各自设置控制。
开关复用已发布的 `conversations.context_state_json`,字段为可选布尔值,不增加数据库版本或迁移。Renderer 经串行会话保存队列与 `conversations:set-story-graph` IPC 保存,成功后更新显示,失败保留原值并通知。Main 按会话主键只读设置,不加载消息历史;显式设置写入后,旧的自动保存快照不能覆盖它。新会话及新建分支使用默认开启。
本地 OpenCode、Continue 和原生客户端复用 [KnowledgeMcpGateway](../../../src/main/agent/knowledge-mcp-gateway.ts) 的 MCP 注册和分发;直连 Model 与 DeepSeek Harness Main 代理调用同一服务。远程 OpenCode/Continue 复用现有图像工具的受管 HTTP MCP 和二进制通道,`storyGraph` 描述独立于图像生成描述,Ask 可只携带图谱读取。请求仍由桌面 Main 解析范围和读取 SQLite,不把数据库、正文库或凭据复制到 Host。
本地 OpenCode、Continue 和原生客户端复用 [KnowledgeMcpGateway](../../../src/main/agent/knowledge-mcp-gateway.ts) 的 MCP 注册和分发;直连 Model 与 DeepSeek Harness Main 代理调用同一服务。远程 OpenCode/Continue 复用现有图像工具的受管 HTTP MCP 和二进制通道,`storyGraph` 描述独立于图像生成描述,可只携带图谱读取。请求仍由桌面 Main 解析范围和读取 SQLite,不把数据库、正文库或凭据复制到 Host。
远程发现每次向 Main 复核启用状态;调用复用同一个范围、分页及版本读取入口。OpenCode 根会话和子会话精确放行当前受管端点的图谱工具;Continue Ask 保留 Main 绑定的会话端点并只允许共享只读工具名。DeepSeek Harness 的 Main 目录、参数转换和 Host 执行钩子均接入这三个工具;其他普通内置 MCP 的 Harness 限制保留。当前远程 Runtime 支持 OpenCode/Continue,不新增远程 Harness。桥接不可用时明确失败。
远程发现每次向 Main 复核启用状态;调用复用同一个范围、分页及版本读取入口。OpenCode 根会话和子会话精确接入当前受管端点的图谱工具;Continue 保留 Main 绑定的会话端点,不按产品模式过滤工具。DeepSeek Harness 的 Main 目录、参数转换和 Host 执行路径均接入这三个工具;其他普通内置 MCP 的 Harness 限制保留。当前远程 Runtime 支持 OpenCode/Continue,不新增远程 Harness。桥接不可用时明确失败。
远程使用须配套部署含此接线的 Desktop 与 Agent;旧 Agent 不识别新增的工具描述字段。本次没有发布或更新受管包,源码验证不代表已发布客户端具备该能力。
@@ -67,6 +67,7 @@
## 5. 展示与使用
- 故事线图谱新增 3D 视图,与现有平铺视图并列,几何、时间尺度、聚合规则沿用 [3D 概念](../story-graph/3d-concept.md);Three.js 仅在打开 3D 视图时加载,WebGL 不可用时使用平铺视图。
- 结果级图谱与聚合图谱都返回 `attention`。结果级查询按该结果保存的范围及时间区间统计当前消息,螺旋使用同一时间窗口。当前故事成员与结果事件按精确 ID 求交,点的名称与时间取所选结果记录。没有当前主故事归属的事件也进入螺旋,按来源确定的 `project_id` 归入显式未归属分组;分组仅用于显示,不持久化为功能故事。重提取后的不同 ID 不通过名称、时间或当前成员反推历史归属,所选事件集合不扩大。界面明确标注当前分组口径,历史故事成员快照仍不可用。
- MCP 增加按故事、按经验查询;经验返回时附“自动归纳 / 用户编辑”标记和依据。
- 心跳建议增加“故事长期无进展”和“已有经验可能适用于当前故事”。两者都由规则选出:前者按设置的天数判断,只提示不改状态;后者要求共享不过于通用的实体。每次心跳各最多 3 条,排在未决事项之前。
- 工作回顾按故事组织:推进了哪些故事、新出现哪些故事、形成或应用了哪些经验。
@@ -21,7 +21,7 @@ FR-S1:将现有智能心跳入口整合、升级为监督者,不增加另一
FR-S2:应用包含“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”五个主视图。工作回顾提供统一监督结果阅读、历史选择和手动新回顾,结果显示自身范围与日期;自动监督集中展示计划状态、范围、周期及下次执行时间,以及运行概览、指标、趋势、自动监督报告、待处理记忆与行动建议和心跳审计,创建和编辑使用同一份 Modal 表单;活动记录展示统一实际执行、进度、详情及结果入口,支持按计划 ID 筛选;设置管理模型选择、超时、并发、分页、分批及单次响应容量,分类页签见 UI 设计。计划保存失败保留草稿,关闭未保存修改须确认;编辑已暂停计划保持暂停,创建须显式保存并启用。原有运行记录及应用、存储标识保留。
当前设置还支持分别调整报告与监督整理的模型超时,以及两者共用的监督并发数。并发设置不限制普通聊天,也不保证避免上游限流;排队不消耗模型阶段时限,降低并发不打断在途请求。取值及生效规则由[当前控制清单](./review-scheduling-design.md#当前超时与调度控制清单已实现)定义。
当前设置支持监督整理模型超时及监督模型并发数。并发设置不限制普通聊天,也不保证避免上游限流;排队不消耗模型阶段时限,降低并发不打断在途请求。取值、生效规则及历史报告设置的保留边界由[模型阶段调度](./technical-design.md#模型阶段超时与调度)定义。
FR-S13:监督者的“设置 → 模型”可选择跟随应用默认文本模型或已有文本模型连接,复用连接及其凭据。回顾执行中修改设置不切换模型;生效时点、继续和重试规则见[监督者模型选择](./logic-design.md#监督者模型选择)。
@@ -55,6 +55,8 @@ FR-S8:本地知识库提供双向定位:资料详情可进入相关故事线
FR-S9:来源事实、自动归纳、待核对关联和用户确认内容分别标注。移除关联不删除原文,纠正操作可撤销。来源失效时显示不可用状态;回放历史时使用当时已有的归纳与依据,无法恢复旧版原文时明确提示,不能以当前版本冒充历史内容。
未发布回顾遇到用户删除会话时,跳过该会话来源并继续其他会话,明确显示省略数;无剩余来源时结束且不生成空报告。已发布历史的保留规则不变。具体失效与覆盖语义见[会话删除规则](./logic-design.md#未发布回顾遇到会话删除),验收见 US-S35。
## 落地范围
FR-S11(D1 已实现,D2/D3 待实现):监督者以时态 Story Graph 为主要产出,统一用户侧工作记忆概念并保留已确认偏好与已有数据。Agent 在继续工作、查询此前决定及其修订时,按需使用只读的 `story_graph_search`、`story_graph_get_context`、`story_graph_read_source`,读取范围、时间、演变、覆盖及可引用来源;工具不调用模型或触发回顾,监督者关闭时不暴露且拒绝调用。本地和远程 Runtime 共用读取语义。后续目标为同一来源版本提取一次并跨 scope 复用叶子事实;当前精确 scope 独立处理及 memory 读取继续保留。`as_of` 仅在历史重建能力具备后另行交付。合同与过渡阶段由[专项设计](./story-graph-mcp-design.md)定义,验收见 US-S28 至 US-S31;本轮没有数据迁移。
@@ -9,15 +9,17 @@
| 产品设计 | [监督者应用产品设计](./supervisor-prd.md) |
| 行为规则 | [监督者逻辑设计](./logic-design.md) |
| 界面设计 | [监督者 UI 设计](./ui-design.md) |
| 实施进度 | SQLite schema 50(心跳介入与监督建议见[心跳触发与建议](#心跳触发与建议2026-09-30));当前算法、设置和验收边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界) |
| 实施进度 | SQLite schema 62;页面读取与索引见[页面读取与发布](#页面读取与发布2026-10-05),当前算法、设置和验收边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界) |
本文回答如何在现有 GoodBuddy 桌面端中实现监督者。它不改变产品范围,也不把模拟 Demo 当作生产数据模型。
FR-S11 的读取架构见[时态 Story Graph 内置 MCP 设计](./story-graph-mcp-design.md)。三个只读工具通过 `readStoryGraph` 投影现有 SQLite,Main 在发现、调用和返回前检查监督者启用、会话开关及 Runtime 分配。绑定携带会话 ID,按主键读取当前设置;原生客户端的复用键包含会话 ID 和开关值。本地 MCP、Model、Harness Main 代理和远程受管工具通道共用此入口;会话开关复用 `context_state_json`,无 schema 迁移。该文独立定义对象/版本合同、时间语义、配置映射及后续跨 scope 复用和记忆过渡。当前 memory 背景读取、精确 scope checkpoint 与候选身份保留;结果快照不支持 `as_of` 历史重建。
FR-S11 的读取架构见[时态 Story Graph 内置 MCP 设计](./story-graph-mcp-design.md)。三个只读工具通过 `readStoryGraph` 投影现有 SQLite,Main 在发现、调用和返回前检查监督者启用、会话开关及 Runtime 分配。绑定携带会话 ID,按主键读取当前设置;原生客户端的复用键包含会话 ID 和开关值。本地 MCP、Model、Harness Main 代理和远程受管工具通道共用此入口;会话开关复用 `context_state_json`,无 schema 迁移。当前监督阶段使用共享时间线 checkpoint;候选按下文的批次项目与跨项目设置筛选。memory 继续作为背景读取,结果快照不支持 `as_of` 历史重建。
生产入口由 `supervision-production.ts` 组装服务、SQLite、共享池和 ask Runtime。`supervision-review-store.ts` 保存冻结来源清单,按有界页和片段供 `SupervisorService` 派发;叶子及连续位置先提交,导航和完整结果之后发布。消息已有知识引用保留本地或外部 locator,已确认记忆通过独立背景输入提供。不会检索外部全库。详细存储、调度及当前限制统一见[分块调度第 0 节](./review-scheduling-design.md#0-生产接线与剩余边界)。
生产入口由 `supervision-production.ts` 组装服务、SQLite、共享池和无工具 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。
模型实体 `id` 只在单次输出内有效。生产服务将批次的 `projectId` 和 `crossProject` 经 `supervisionCandidates` 传到只读 Worker,再由 `listSupervisionCandidates` 查询共享时间线。跨项目关闭时,只提供该项目当前事件关联的未撤销实体;开启时允许其他项目的实体。候选仍最多 100 个,按更新时间和 ID 排序。查询先取得项目事件关联的实体集合,再筛选候选,避免对每个实体重复扫描事件。省略批次参数的旧读取入口保留精确 scope 查询。
生产提示只暴露 `candidateRef`(如 `known_1`)、名称和说明。模型显式选择候选后,Main 转换为严格 UUID 类型的内部 `persistedId`。服务校验候选集成员和重复映射,保存事务再次按实体主键校验存在性及撤销状态。没有有效候选引用时分配新 UUID,不按名称或裸模型 ID 合并。来源仍按每次结果分配 UUID,保留原始 `source_id` 和 locator。
新实体省略 `candidateRef`,不要求模型生成数据库 ID。未知非空 `candidateRef`、冲突身份和候选集之外的 UUID 均拒绝。旧输出中的 `persistedId` 仅在 UUID 合法且属于候选集时复用;空值、占位符和格式错误值不能证明已有身份,保留为独立新实体。局部 ID 或同名不会触发合并。
@@ -27,9 +29,11 @@ schema 42 为实体、实体变化和关系增加 `source_reference_ids_json`,
schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、说明、确认状态、关系理由及本次来源引用。后续 run 只更新 `automatic` 的当前实体与关系,已确认、修订或移除状态受保护。历史结果读取自身内容;人工操作带 `resultId` 时先校验成员归属,在事务内更新当前对象及所选结果,其他结果不变。移除关系记录 `revoked`,防止后续相同端点和类型的自动关系恢复它。
结果级图谱另由 `supervisionAttention` 按所属 run 保存的范围和时间区间查询当前消息,返回小时桶 `attention`;该字段不写入历史快照。既有 `supervisionGraph` IPC 和 Preload 原样传递,Renderer 按选定区间生成螺旋。归一化、小时内估算和历史数据限制见 [3D 密度规则](../story-graph/3d-concept.md),故事与结果事件的对应规则见[故事线模型设计](./storyline-model-design.md#5-展示与使用)。
升级从现有事件实体关联、实体变化和来源引用恢复结果成员,不猜测名称匹配;旧数据缺失的归属或已经覆盖的内容无法还原,原对象仍保留。新结果即使实体没有事件或来源,也会保存完整成员。结果内容与运行、事件、来源在同一事务提交。
`overview({ target? })` 返回稳定的 `id`、`storyLineId` 及来源 ID。指定 Conversation/Task 时,Main 读取目标实际项目,查询同时要求目标来源匹配、结果 scope 为 global 或包含该项目;LIMIT 在过滤之后。卡片展示结果真实范围,global 结果不会伪装成单会话摘要。`graph({ resultId, storyLineId? })` 校验二者归属,只返回该结果的事件、对象和来源;显式 ID 无效时不回退到最新结果。继续讨论按结果主键读取,并验证来源属于该结果。
`overview({ target? })` 返回稳定的 `id`、`storyLineId` 及来源 ID。指定 Conversation/Task 时,只读 Worker 读取目标实际项目,查询同时要求目标来源匹配、结果 scope 为 global 或包含该项目;LIMIT 在过滤之后。卡片展示结果真实范围,global 结果不会伪装成单会话摘要。`graph({ resultId, storyLineId? })` 校验二者归属,只返回该结果的事件、对象和来源标题;显式 ID 无效时不回退到最新结果。继续讨论按结果主键读取,并验证来源属于该结果。
工作回顾保存所选结果,切换或刷新使用请求序号忽略迟到图谱。侧栏按目标重建卡片,清空来源和预览,并忽略旧请求;工作栏 tasks 实例的 `targetRef` 可保存 conversation/task 固定目标,取消固定后恢复跟随当前选择。该绑定只控制监督卡片,不改变任务列表的项目范围。继续讨论预览显示实际目标名称和 ID,发送回调显式携带该会话 ID,由 Main 使用该会话的 Runtime 与项目。
@@ -37,10 +41,24 @@ schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、
继续讨论预览将 `supervision:continue-context` 返回的 `prompt` 直接作为可编辑草稿。侧栏与图谱入口发送当前草稿,不重新拼接原始上下文。`supervision:continue` 沿用现有正文校验(去除首尾空白,最多 12,000 字符),使用空 `attachments` 入队,无附件请求省略 `serializedContexts`;请求未指定 Runtime 时使用会话保存的选择。会话已有的记忆和知识库配置仍按普通会话发送规则处理。知识库预览与提交均检查目标库存在且为本地可写库,更新实体还检查其实际所属库。侧栏确认框展示 Main 返回的实体字段、目标库和来源正文,取消或写入失败后可以重新预览。
监督来源清单将 UI 时间区间归一化为 UTC,先筛选再按稳定键分页。项目必须存在且 active;消息按闭区间筛选,任务按区间内创建或完成时间筛选。手动和自动监督共用该路径,自动清单另读取 supervisor checkpoint;手动心跳报告保留原有 collector。
监督来源清单将 UI 时间区间归一化为 UTC,在 Worker 读快照内按范围筛选,元数据按小批次写入;后续片段按稳定键分页。项目必须存在且 active;消息按闭区间筛选,任务按区间内创建或完成时间筛选。手动和自动监督共用该路径,增量清单另读取 supervisor checkpoint;手动心跳报告保留原有 collector。Worker 连接、未完成初始化重建、取消确认和打包规则见[调度与存储](./review-scheduling-design.md#已接入的调度与存储)。
已确认记忆作为当前背景,不参与新增正文覆盖。最多四条、每条 500 字符,时间来自真实更新时间;模型不能把背景引用为本批新增证据。旧结果继续保留原来源类型和 locator。监督正文上限现用于分批,余段继续处理;心跳报告的有界输入语义没有改变。
## 页面读取与发布(2026-10-05)
对应 K01 的来源投影及 K02、K05、K06。`supervision:overview`、`supervision:graph`、`supervision:stories` 的生产 IPC 校验后分别调用 `listSupervisionResultsAsync`、`getSupervisionGraphAsync`、`getSupervisionStoriesAsync`。它们复用 `readonly-query-worker` 的 AssistantDatabase 独立只读连接,每次领域读取包在一个 `readSnapshot` 内;故事响应中的故事、经验、未归属计数及撤销状态来自同一 SQLite 快照。调用前已提交的写入对下次读取可见。
文件数据库缺少 Worker 配置、启动失败、崩溃或处于原有退避期时,请求直接失败,页面沿用既有错误与重试显示;这三个入口不使用 `readWithFallback`,不在 Main 重跑重查询。内存 SQLite 测试保留同步分支。原工具 `readStoryGraphAsync`、来源详情及其他存储入口保持各自原有实现,没有增加存储进程或通用协议。
结果图谱和 scope 图谱的 `sources` 都只查询共享合同已有的 `id`、`title`、`occurred_at`,并按原表 rowid 顺序返回。正文、来源类型、locator 等仍完整保存在 `supervision_sources`,通过 `supervision:source`/`source-context` 按 ID 读取;历史结果、所有事件、对象和关联均保留。概览是否请求图谱由 Renderer 的页面加载规则决定,后端不截断图谱内容。
schema 62 在既有迁移事务中新增 `supervision_sources(result_id, occurred_at, source_type, id)` 和 `supervision_results(created_at)` 索引。前者供每份概览的首来源定位,后者支持结果时间及 rowid 倒序读取。升级沿用启动存储 Worker,保留表内容和 rowid,不重写正文,不触发历史转换或 `VACUUM`。新版本仍遵守既有的禁止旧客户端打开更高 schema 规则。
故事查询先按 scope 选择故事,再一次读取这些故事的事件成员;经验查询在 SQL 中筛选仍有形成事件、且与所选项目有关的经验,再一次读取其全部有效事件关联。经验的跨项目关联不会随项目筛选被裁掉。两类列表各至多两条 SQL,原列表和成员排序、人工调整状态及完整内容访问保留。
发布继续使用 Main 原有 `BEGIN IMMEDIATE` 事务。每个叶子写入后收集当时的实体、关系及来源引用,末尾只写一次 `graph_snapshot_json`;重复持久身份仍采用原 SQL 分组规则,保留首叶子快照值和全部叶子详情。结果、运行状态、来源、时间线与 checkpoint 仍一起提交或回滚。没有将写入迁到 Worker,也没有把一个完整结果分多次提交。大批量发布仍超过 Main 16 ms 预算,实测与剩余限制见[实施进度](./progress.md#2026-10-05-监督页面读取与发布局部优化)。独立存储服务的职责迁移继续留在[目标方向](../../architecture/desktop-storage-direction.md)。
## 监督者模型连接
`ApplicationSettings.supervisorModelProfileId` 只保存已有连接 UUID,`null` 或旧设置缺少字段时跟随 `RuntimeSettings.defaultModelProfileId`。Main 的 `resolveSupervisorRuntime` 从 `getResolvedSettings()` 读取连接和已有凭据,校验文本协议及认证后,用 `createModelProfileRuntime` 创建无工具直连 Runtime。Renderer 仅接收公开连接信息,不新增凭据存储或 IPC 方法。
@@ -51,9 +69,13 @@ schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、
监督整理超时由 run 创建时冻结;共享模型池仍采用实时设置上限。`supervision:pause` 取消该 run 的排队和在途工作,`supervision:resume` 继续已保存批次;监督整理输出在流中按 `supervisionReview.responseKiB` 检查响应容量,默认 1024 KiB,可在设置页调整为 100 至 16384 KiB。该容量每次请求读取当前设置,继续旧运行也采用新值;超限保留成功批次并提示调高后继续。生成摘要及事实内容不设独立短字符限制,监督旧摘要读取保留全文,具体边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。以下报告领取和租约规则继续适用。
手动和自动监督均在桌面 Main 调用生产工厂,Runtime 解析请求只有 `workMode: ask`,不携带项目、Runtime 选择或执行空间。`resolveRequestRuntime` 因而返回桌面默认 Runtime,不进入 SSH 项目或 `selectedRuntimes.getRuntime` 分支;回顾 scope 只筛选证据。摘要校验、合并和持久化在 Main 完成,single-flight、取消及 UI 控制不改变 `gbagent`、远端 Runtime 启动或桌面到 Agent 协议。
手动和自动监督均在桌面 Main 调用生产工厂,由 `resolveSupervisorRuntime` 解析上文的直连模型,不进入 SSH 项目或 `selectedRuntimes.getRuntime` 分支;内部分析使用无工具构造,回顾 scope 只筛选证据。摘要校验、合并和持久化在 Main 完成,single-flight、取消及 UI 控制不改变 `gbagent`、远端 Runtime 启动或桌面到 Agent 协议。
`ApplicationSettingsStore` 持久化报告和整理模型超时,均为 30..600 整数秒,默认 240。报告排队前冻结,监督创建 run 时冻结;计时从获槽及 Runtime 解析后开始,覆盖模型执行及其内部重试。流结束校验 signal 和完成事件,finally 释放临时会话,心跳报告保持原有 100KB 上限,监督整理使用上文的可调响应容量。采集、排队、保存和清理不计入模型超时。
`ApplicationSettingsStore` 持久化报告和整理模型超时,均为 30..600 整数秒,默认 240。报告排队前冻结,监督创建 run 时冻结;计时从获槽及 Runtime 解析后开始,覆盖单次 Runtime 执行及其内部重试。监督包装层的第二次尝试重新按同一秒数计时,500ms 等待和临时会话清理不计入该时限。流结束校验 signal 和完成事件,finally 释放临时会话,心跳报告保持原有 100KB 上限,监督整理使用上文的可调响应容量。采集、排队、保存和清理不计入模型超时。
`runSupervisionModel` 为提取、导航、故事、经验和建议共用一次临时错误重试,错误范围见[逻辑规则](./logic-design.md#监督者模型选择)。每次尝试使用新的 request/conversation ID 和隐藏任务,正文缓冲单独创建;用量按各次任务保存。失败后先清理计时器并释放临时会话,再等待;等待继续持有监督池槽位,受父 signal 和池取消控制,结束或失败后释放槽位。`error` 事件的 `status: cancelled` 优先于错误文字;抛出的结构化错误检查 status、statusCode 和 cause,字符串只识别明确的 HTTP 状态或已知网络/流中断格式。
直连文本请求保留 HTTP 错误的 status,并在响应流失败前交出已收到的用量;没有提供方用量时不估算补记。监督响应超容量后立即 abort 并清空正文,最多继续消费 1 秒,仅保存用量事件,不累积后续文本;随后请求关闭迭代器、释放临时会话和池槽位,迟到事件不再写入。容量错误保持原文,不被随后到达的超时或流错误替换,也不重试;已缓冲完成响应和中止响应均沿用原 call ID 记账。重试决策只在监督包装层,直连传输原有重试、OpenCode 和 Continue 策略不变。429 使用同一 500ms 等待,不新增 Retry-After、UI 或配置项。
`supervisorModelConcurrency` 为 1 至 4 的整数,默认 1。报告和监督整理共用 Main 内 `SupervisionModelPool` FIFO 池,普通聊天不入池;降低上限不终止在途请求。报告槽位从领取前持有至报告完成或无变化提交后,在下游监督开始前释放,避免并发 1 时相互等待。监督服务外层运行仍串行;该池不是完整分块调度器,也不提供跨聊天与监督的提供商全局并发、RPM、TPM 或 Retry-After 控制。
@@ -61,6 +83,10 @@ schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、
schema 47 的四张批次相关表使用外键随监督运行清理,未完成运行不新增自动过期删除规则。启动时原有 running 记录会标 failed,但已保存批次和配置保留,活动页仍可继续。发布逐页读取叶子并复用现有结果/来源/图谱写入,在同一事务更新完成状态和自动 checkpoint。
会话删除复用 `SupervisionReviewStore.omitDeletedConversations`,只处理 `running/paused/failed` 的暂存记录。删除入口在事务内补齐旧任务清单的会话归属;新清单由 Worker 在初始化时记录任务所属的现存会话。初始化完成、恢复、分块读取、模型调用前及返回落库、导航合并和发布前均检查删除。初始化中的清单仍可丢弃重建,省略计数在完整清单提交后计算。
失效计算只取批次的来源键,不把保存正文作为删除会话的替代输入;依赖导航沿 `children_json` 递归删除。省略数写入既有 `state_json.omittedSources`,透出到活动进度及结果 `coverage_json`,不增加 schema。发布事务先核对省略数与待发布结果一致,再保存事实和 checkpoint;若删除使摘要过时,服务返回现有提取/合并循环。已发布历史不参与失效,行为定义见[逻辑规则](./logic-design.md#未发布回顾遇到会话删除)。
当前设置及冻结范围见[当前设置合同](./review-scheduling-design.md#当前设置合同)。未实现的高级参数继续保留在目标清单,不显示为可保存控件;没有兼容原型数据库的读取器。
### 单次执行与取消
@@ -69,7 +95,13 @@ schema 47 的四张批次相关表使用外键随监督运行清理,未完成
`supervision:execution` 返回进程内 `{ active, runId?, stopping? }`,配置读取期间可有 `active: true` 而尚无 runId。`supervision:cancel({ runId })` 先在既有 `supervision_runs` 写入 `cancelled`、清除错误并保存取消时间,再向父 controller 传播 abort;重复取消保留原时间。该接口等待执行 Promise 结束,包含并行批次的 `allSettled` 和 Runtime `releaseConversation`,之后才释放准入。暂停只请求 abort,执行停止后保存 `paused`;取消优先于暂停。迟到模型输出不得保存叶子、导航或完整结果,成功批次保留,自动 checkpoint 不推进。发布是同步 SQLite 事务,已完成或无变化的运行拒绝取消,不撤销已发布结果;本次无 schema 迁移。
Preload 暴露类型化 `execution`、`cancel`,取消输入使用 UUID schema 并校验可信 sender。活动 UI 用实时执行状态覆盖持久记录的停止显示;工作回顾挂载期间每两秒读取执行状态,发起前再次查询,Main 仍作最终准入判断。`resume` IPC 直到运行结束才返回,Renderer 发出请求后释放提交锁并独立处理结果,确保继续中的回顾仍可暂停或取消。状态条 X 仅修改本地显示状态。
发布前重读已保存批次所需候选后,再检查父 signal。此时收到暂停或取消,走原有停止分支;完整叶子和导航保留,结果及 checkpoint 尚未提交。该检查与同步保存之间没有新的异步等待。
故事归属和子线索拆分在既有写事务内,按主键检查模型选中的目标、父故事和跨项目功能仍为 `current`。模型等待期间若用户移除或合并目标,整批写入回滚,包含新建故事和已处理标记;重新整理使用当前候选。拆分同样检查原功能及目标子线索。已发布回顾、用户调整和此前成功批次保留,不增加版本表或恢复日志。
Preload 暴露类型化 `execution`、`cancel`,取消输入使用 UUID schema 并校验可信 sender。活动 UI 用实时执行状态覆盖持久记录的停止显示;工作回顾只在发起前查询执行状态,Main 仍作最终准入判断。`resume` IPC 直到运行结束才返回,Renderer 发出请求后释放提交锁并独立处理结果,确保继续中的回顾仍可暂停或取消。
`App` 将既有通知回调经 `HeartbeatCenter` 传给 `SupervisorWorkspace`。回顾操作用 `supervisor-review` 去重键发送本地化 Toast,结果读取失败仍写入 `loadError` 并就地重试;实体、关系操作和来源读取失败也使用通知。工作回顾不再为状态条轮询执行状态,历史读取不发送操作通知。请求锁覆盖发起前检查和整次运行;页面刷新只使旧结果选择失效,不丢弃运行结束反馈。卸载后忽略迟到响应,检查阶段尚未提交的回顾不再发起,已提交的后台运行不取消。活动查询、原始错误持久化和 Main 生命周期保持原路径。
### 心跳触发与建议(2026-09-30)
@@ -94,7 +126,7 @@ IPC `supervision:suggestions`、`supervision:suggestion-action`、`supervision:r
只有实际送入模型、通过结果校验且成功落库的完整片段推进位置。心跳报告及其进度、监督结果及其进度分别在各自 SQLite 事务提交。监督服务串行执行,后续自动请求在前次保存后重新收集。报告成功而监督失败时,下一次自动检查跳过已成功的报告输入,但重试监督输入;无变化不创建报告、来源或图谱事件。
已有同 scope 故事线的 scope 表示和实体 ID 保留;比较项目集合时忽略排序。模型接收最近摘要和同 scope 最多 100 个未撤销实体的候选引用、名称、说明,显式选择候选才复用,身份转换按上文执行。不同范围不合并,已有重复实体不自动修复。知识引用继续以独立来源保存 locator;当前记忆和旧摘要不作为新事件。
已有故事线的 scope 表示和实体 ID 保留;比较项目集合时忽略排序。生产候选按上文的项目归属与跨项目设置读取,显式选择候选才复用;已有重复实体不自动修复。监督 checkpoint 的共享时间线规则见[故事线模型](./storyline-model-design.md)。知识引用继续以独立来源保存 locator;当前记忆和旧摘要不作为新事件。
迁移在事务中扩展心跳计划及运行表的状态 CHECK,保留列、索引、ID、计划、报告与引用;消息版本列使用默认值,无需遍历或重写旧正文。进度初始为空,首次自动运行从配置窗口开始。报告保留期限覆盖 `no_change` 运行,但不删除来源进度;删除报告不触发重新消费。
@@ -255,7 +287,7 @@ IPC 返回图谱查询结果时使用分页和有界字段;详情中的来源
## 5. 安全与一致性
- 监督者模型调用固定为 `ask` 语义,输入来自 `EvidenceCollector` 的有界快照,工具授权始终拒绝。
- 监督者模型调用使用无工具构造,不依赖产品模式或提示词限制;生产输入来自冻结来源的分页读取,见分块调度设计。
- `Global`、Project、多项目和固定 Conversation/Task/Experiment 目标在 Main 冻结;模型不能生成或改变范围。
- 每个来源引用必须属于本次冻结范围,跨范围引用、伪造项目 ID 和不存在的来源 ID 直接使结果失败。
- 心跳运行沿用现有 `heartbeat_runs` 的领取、租约和重试机制;监督运行按心跳运行 ID 关联,活动据此合并阶段。监督取消不撤销已保存的心跳报告,合并活动优先显示监督已取消状态。
@@ -322,7 +354,7 @@ IPC 返回图谱查询结果时使用分页和有界字段;详情中的来源
## 当前剩余差距
- 实体与关系 revoke 保存状态;界面没有 undo 或恢复入口。
- 跨 run identity 依赖同 scope 候选 UUID 的显式引用,不自动合并历史重复实体;模型漏选候选时会创建新身份。
- 跨 run identity 依赖候选 UUID 的显式引用,候选范围受批次项目及跨项目设置控制;不自动合并历史重复实体,模型漏选候选时会创建新身份。
- 结果图谱使用真实事件实体连线,侧栏已按固定目标过滤并支持结果直达图谱;逐事件状态回放和 Experiment 尚未实现。
- 用户暂停已接到排队/在途 signal,并保留已保存批次;源版本变更后的局部重算、完整模型配置冻结及其他限制见分块调度第 0 节。
- 监督知识实体与知识库正文条目仍是不同对象,知识正文只能通过现有条目定位和预览接口处理。
@@ -8,11 +8,19 @@
应用中心中的智能心跳入口升级为“监督者”,沿用既有启用、常驻与排序能力。未来应用市场使用同一应用身份。应用已启用时,停用单条自动心跳计划仍可手动回顾;关闭应用后,已打开页面显示关闭状态并禁用工作区操作。默认值及运行边界见[应用启停与执行](./logic-design.md#应用启停与执行)。
当前应用页的顶层导航只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 智能心跳 / 活动记录 / 设置”(英文活动标签为 Activity records)。智能心跳独立管理心跳计划;设置不含计划、报告或审计,也不显示整页全局徽标或独立刷新行。设置内用一排分段页签按用途分为“模型 / 回顾整理 / 故事与经验 / 建议”,每次只显示一个表单:模型含整理超时与并发;回顾整理含读取条数、每批文本量与消息数、单次响应容量;故事与经验含子线索与经验阈值及跨项目关联开关;建议含暂无进展天数。各分类单独保存,只提交本分类字段并沿用其他字段的已存值;切换分类保留未保存的草稿。故事线图谱没有数据时,空状态占满可见高度,标题、说明与“工作回顾 / 刷新”按钮水平、垂直居中,不再在上方单独放刷新行。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。故事线图谱面板内另有分类页签,见[故事线图谱](#故事线图谱)。
当前应用页的顶层导航只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 智能心跳 / 活动记录 / 设置”(英文活动标签为 Activity records)。智能心跳独立管理心跳计划;设置不含计划、报告或审计,也不显示整页全局徽标或独立刷新行。设置内用一排分段页签按用途分为“模型 / 回顾整理 / 故事与经验 / 建议”,每次只显示一个表单:模型含整理超时与并发;回顾整理含读取条数、每批文本量与消息数、单次响应容量;故事与经验含子线索与经验阈值及跨项目关联开关;建议含暂无进展天数。各分类单独保存,只提交本分类字段并沿用其他字段的已存值;切换分类保留未保存的草稿。故事线图谱没有数据时,空状态占满剩余可见高度,标题、说明与“工作回顾”按钮居中,刷新保留在共用回顾栏。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。故事线图谱面板内另有分类页签,见[故事线图谱](#故事线图谱)。
工作回顾只读取统一监督结果。顶部紧凑排列范围、时间、发起与刷新控件,不重复显示“新回顾”标题和来源说明,不新增 Modal。历史结果选择与图谱入口位于正文之前,即使只有一条结果也显示选择器。新回顾工具栏、状态、历史选择和结果面板铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设阅读宽度上限;结果内部按面板内边距排版,不再单独居中窄正文。正文使用“监督结果”标题,常显生成日期、真实范围和覆盖区间,历史结果不标为最新。工作总结按原有空行分段并保留换行;本次变化和未解决事项直接使用已有字段,不补写结论或截断正文。390px 下新请求字段与操作各占两列,结果元数据可换行。
工作回顾与故事线图谱在主页签下方共用回顾上下文栏。历史选择器最多占 280px,收起时只显示生成日期时间,随后是“刷新”和“新回顾”,不在右侧重复范围和覆盖区间。展开复用共享锚定单选菜单,每项显示生成时间及该结果自身的真实范围、覆盖起止时间,长说明换行;当前项带选中标记,触发按钮的无障碍描述保留所选结果的完整上下文。即使只有一条结果也显示选择器。切换页签和图谱模式时,这一栏的位置、内容和选择保持一致,空态及读取失败时仍可访问。
新回顾运行中保留已显示结果及其日期、范围,范围、时间、发起和刷新控件保持可用;重复点击发起只重新显示当前运行提示,不排入新回顾。状态条提供活动记录入口,清除计划筛选并聚焦活动页签;右侧 X 只关闭提示,不暂停或取消执行,同一次运行的轮询不重新打开已关闭提示。范围、时间和活动计划筛选保留可访问名称,不再重复显示字段标题。工作回顾不读取历史心跳报告,也不显示自动报告空态、记忆与行动建议或第二行全局范围与刷新。无监督结果时,空态独占工具栏下方整行,标题与手动回顾入口水平居中;自动报告是否为空不影响此判断。运行中和暂停后使用独立状态条,提示当前状态及活动记录入口。
首次打开工作回顾只读取回顾及其范围的故事摘要,不等待图谱。进入故事线图谱时才读取所选结果的图谱;读取期间在图谱页显示加载状态,返回工作回顾仍可阅读及选择历史。工作回顾与图谱共用已读的故事数据,普通页签切换不重复读取。当前结果的图谱在切换页签时保留;切换历史结果、显式刷新、实体或关系修改后的刷新会使旧图谱失效。来源列表只需标题和时间,点击来源才读取完整正文。
点击“新回顾”才展开范围、时间、发起按钮和重新整理菜单。表单位于上下文栏下方,以次级表面区分新请求与已选结果;再次点击收起并保留输入。工作回顾正文铺满页面壳层,不另设阅读宽度上限。容器宽于 860px 且有辅助内容时,左侧约占三分之二,显示工作总结和本次变化;右侧显示未解决事项、故事进展和经验,以弱分隔线分区。没有辅助内容时只保留全宽正文。窄屏按正文、辅助内容顺序排列为单列。摘要保留原有段落和换行,故事摘要保留“显示更多”及进入图谱的选择。
新回顾运行中保留已显示结果及其日期、范围,范围、时间、发起和刷新控件保持可用。手动发起、完成、暂停、取消、无变化和失败统一使用应用 Toast;重复点击发起提示已有运行,不新建或排队。同一回顾操作的通知互相替换,关闭通知不暂停或取消执行。成功和信息沿用共享自动消失时间,错误由用户关闭。页面不保留操作状态条或错误横幅,加载历史结果、切换页签和重新进入页面不触发回顾通知。
通知使用中英文可理解的状态及下一步说明,失败时引导到活动记录查看原因、继续已保存进度或重新发起。原始技术错误仍保留在活动详情。共享通知当前只提供关闭按钮,查看进度使用既有“活动记录”页签。内容加载失败继续在页面内显示本地化说明和重试,操作反馈不会清空范围、时间或已有结果;运行中刷新不丢弃随后到达的结束通知。
范围、时间和活动计划筛选保留可访问名称,不再重复显示字段标题。工作回顾不读取历史心跳报告,也不显示自动报告空态、记忆与行动建议或第二行全局范围与刷新。无监督结果时,空态独占工具栏下方整行,标题与手动回顾入口水平居中;自动报告是否为空不影响此判断。
## 会话故事图谱开关
@@ -38,6 +46,8 @@
“已保留事实与来源”展开后才查询既有批次接口,每页 10 批,按本页持久记录组成项目、会话、批次层级。项目使用可用名称,会话优先使用保存来源的标题;只有已保存批次标记“已保存”,不创建未落库的待执行、在途或失败叶子,也不把本页数量当作项目总量。批次摘要收起时最多两行,展开后保留完整摘要、事件、实体、变化、关系及原文片段。来源显示消息 ID、码点范围及版本;长原文局部滚动。原生 details/summary 保留键盘展开和可见焦点,轮询不主动展开用户收起的会话。加载、空批次和读取失败分开显示,失败可重试;换页失败不显示上一页批次,切换 run 后旧响应失效。暂停状态为“未完成,可继续”,手动回顾主动暂停后在工作回顾页提示去活动继续,不显示完成通知。
删除会话导致省略来源时,阶段进度下方和运行详情均显示省略数量,并说明覆盖统计只含保留来源。中文为“会话已删除,已跳过 N 个来源”;英文为“N sources skipped because their conversations were deleted”。全部省略时活动显示无新变化,不提供空结果跳转;已发布历史仍可查看。规则见[会话删除](./logic-design.md#未发布回顾遇到会话删除)。
活动页运行中同时提供“暂停回顾”和“取消回顾”,继续后的运行也可操作。停止期间禁用两项操作,并在运行标题和当前阶段显示“正在暂停”或“正在取消”;即使持久记录已经为已取消,只要执行尚未停止就继续显示“正在取消”。重新进入活动页通过实时执行状态恢复该提示。停止后显示“已取消”,保留事实详情,不显示继续按钮或失败恢复提示;任一回顾执行或停止清理期间,其他记录的继续按钮禁用。
## 智能心跳设置
@@ -80,9 +90,17 @@ Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复
2026-09-30 已实现(规则见[心跳职责与介入](./logic-design.md#心跳职责与介入目标设计)):主按钮“回顾”执行增量整理,文案不再称“重新分析”;旁边“更多”菜单提供“重新整理…”,确认对话说明将重新分析所选区间并可能产生模型费用。计划 Modal 增加“介入方式”单选:“生成建议”“仅更新记忆”;主动介入上线前不显示。最近建议替代,每条建议显示依据、打开来源及转任务/确认/忽略。“最近建议”替代,每条建议显示依据、打开来源及转任务/确认/忽略。
当前页面提供全局或单项目范围,以及 1、7、30 天时间区间选择。没有自动计划也能手动回顾;成功结果显示自身冻结的范围、时间和未解决事项,可选择历史结果或进入图谱。来源筛选由本次回顾范围和时间限制;图谱详情只列出所选事件的关联来源,实体或关系列出相关事件的来源。独立的来源类型选择器尚未实现。
当前页面提供全局或单项目范围。时间选择保留 1、7、30 天快捷项,默认 7 天,并提供“自定义天数”和“指定日期”。快捷项与自定义天数均从提交时刻向前计算,每天按 24 小时计;自定义值必须为正整数,且起始时间不得早于公元 1 年。
选择历史结果后,图谱读取该结果的成员和内容,标题显示其回顾区间和生成时间。切换时清空旧图谱和详情并显示加载状态,迟到请求不得替换新选择。确认、修订和移除反馈更新当前所选结果,后续自动回顾不改写旧结果。
指定日期使用带可见标签的原生日期输入,按本地时区解释。起始时间为开始日期的零点,结束时间包含结束日期全天:取下一个本地日期的零点减 1 毫秒,跨夏令时不按固定 24 小时推算;结束日期为今天时截到提交时刻。允许同一天,拒绝缺失、无效、未来日期及开始晚于结束。天数和日期输入复用共享字段样式,窄窗口换行;错误就地显示,通过 `aria-invalid` 和 `aria-describedby` 关联输入,无效时禁用回顾和重新整理,提交入口再次校验。
重新整理确认显示当前范围及最近天数或明确起止日期,日期文案说明本地日期和今天截至当前的规则。修改范围或时间输入后关闭旧确认,重新整理仍需再次确认。新选择只改变请求时间范围,普通回顾继续跳过已处理且未变化的来源,重新整理仍使用既有显式标记,不改变增量处理规则。
没有自动计划也能手动回顾;成功结果显示自身冻结的范围、时间和未解决事项,可选择历史结果或进入图谱。来源筛选由本次回顾范围和时间限制;图谱详情只列出所选事件的关联来源,实体或关系列出相关事件的来源。独立的来源类型选择器尚未实现。
选择历史结果后,历史菜单显示该结果的回顾区间和生成时间,旧图谱和来源详情清空。在图谱页选择历史时立即读取对应成员和内容;在工作回顾页选择时,图谱读取留到下次进入图谱页。迟到请求不得替换新选择。确认、修订和移除反馈更新当前所选结果,后续自动回顾不改写旧结果。
切换语言或手动回顾完成引起刷新时,同时解除被替代的来源读取、实体和关系操作的等待状态。工作回顾在回顾读取完成后恢复历史选择和刷新;图谱页等待对应图谱读取完成后恢复操作。图谱失败只在图谱页提示并提供重试,不阻止阅读已读回顾;旧请求迟到时既不显示旧内容,也不能解除后来发起的请求的等待状态。
手动“回顾当前进展”新建回顾,活动页“继续”恢复原 run;运行中再次发起按上文显示提示,停止清理结束后才能开始新回顾。失败保留上次结果、本次范围和成功批次;已取消回顾不能继续。手动操作不推进自动位置,删除报告也不重置自动位置。
@@ -107,19 +125,29 @@ Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复
## 故事线图谱
历史选择统一由上述回顾上下文栏承载,不占用画布面包屑。首次进入默认最新结果;两页之间切换保留所选结果,从侧栏或建议显式打开的结果、故事、经验优先于默认值。切换结果时清除旧来源详情,迟到请求不得覆盖新选择。平铺与时间螺旋均为上方标题工具栏、下方画布两层。标题工具栏使用相同的正文大小、内边距和淡分隔线;螺旋的“全部 / 项目 / 功能”面包屑旁放层级下拉菜单、图例 i 图标,右侧保留共享 SegmentedControl 的“平铺 / 时间螺旋”切换。窄窗口允许标题区控件换行,画布内不另设工具栏。
时间螺旋使用所选回顾的时间区间与完整事件集合,半径和区间着色规则见 [3D 密度规则](../story-graph/3d-concept.md)。当前故事成员按精确事件 ID 与结果求交;其余事件按来源确定的项目进入“当前无故事归属”分组,项目未知时显示“项目范围不可用”。图例说明“本次回顾事件按当前故事归属分组;未保存当时的故事归属”。故事与经验列表仍提供该范围的当前完整记录,供查看和调整。
根视图直接显示事件点,同一主故事的邻近事件按画布密度聚合并显示数量,未归属点使用中性色。事件和经验可从左侧列表或画布标记选择,画布不重复提供这两种选择器。保留一个层级菜单:左侧列表选择记录并打开详情,标题区层级菜单改变画布中的项目、功能或子线索层级。菜单与面包屑均可用键盘操作。无选择时默认斜俯视,不显示视角按钮、菜单或页签;自由拖动、平移、滚轮、方向键、数字键和 Escape 返回上层操作保留。
时间螺旋选中故事、事件或经验后,相机沿较短方向旋转并平移到对应位置,保留当前缩放;缩放超出 0.8~1.4 时收回到该区间。列表选择的目标不在当前层级时,切换到能显示该目标的层级;经验须在本次回顾内有跨木片的形成与应用连接才能定位,没有对应几何对象的记录仍可查看详情。选中的经验不受默认六条连接的显示上限限制。鼠标拖动、滚轮和方向键立即接管相机,后续相同选择和同内容刷新不重新定位。启用减少动态效果时直接定位,离开视图时取消未完成动画。
关联来源详情属于会话时,显示“打开会话”按钮,使用来源保存的会话 ID 跳转并切换到所属项目;不发送消息或启动回顾。另提供“继续讨论”,使用上述可编辑正文预览,点击发送后提交到来源会话并打开它。详情继续展示整理时保存的原文快照。会话已删除或读取失败时,沿用应用会话导航的提示;其他来源类型不显示此按钮。
左侧使用共享 PageTabs 切换“事件 / 实体 / 关系”,显示各类数量,一次只展示一类列表,无记录时显示本次回顾的分类空态。页签固定在顶部,列表占满剩余高度并独立滚动。切换页签不清除当前图谱选择;从画布、事件浏览或详情选择对象时同步切到对应页签。支持方向键、Home/End 和面板键盘聚焦。
事件、实体和关系长列表复用共享列表窗口,只挂载视口附近的行,并保留选中行和焦点行。记录总数、完整名称及末条记录均可访问,不以截断记录提速。列表内上、下方向键移动焦点,Home/End 到首、末条,Enter 或 Space 选择;Tab/Shift+Tab 按完整记录顺序移动,在首尾离开列表。键盘移动跨过未挂载的行时,先挂载目标行再聚焦。行高测量或窗口变化不得把刚聚焦的可见行移出视口;用户主动滚走时保留焦点行,但不把滚动位置拉回。
使用 `supervisor` 页面壳层,最大内容宽度为 `1440px`。当前生产图谱左侧为紧凑的事件、实体和关系列表,中间为 SVG 画布,右侧为详情与来源。桌面列宽为 `216px minmax(0, 1fr) 280px`,画布获得主要空间。外圈事件按发生时间排序后等距逆时针排列,不表示实际时间间隔;起止之间保留缺口和方向箭头。内部显示真实实体名称,实线仅连接实际事件与实体,虚线仅连接实际实体关系。
高度:容器宽度不小于 861px 时,图谱这一行的高度等于窗口可见高度减去图谱顶部位置,并在底部保留约 24px 边距(不小于页面底部内边距),最低 660px(再矮平铺图的文字会小于 11px,这时改为页面滚动)。三栏时整页不再出现纵向滚动;左侧列表和右侧详情在这一行内独立滚动,滚动只影响自身,不带动页面。中间画布列不出现滚动条:标题栏和底部单行“事件浏览”栏之外的高度全部给图,平铺与时间螺旋一致。画布标题为“事件与知识”,下方一行小字依次显示图谱范围、时间区间、生成时间和事件/实体数,刷新为标题栏右侧图标按钮;图谱上方不再单独占一行显示范围与时间。没有图谱数据时,刷新按钮保留在页签顶部。图例默认收起,从标题栏“图例”按钮打开,浮在画布右上角,可用 X 关闭;可见事件与实体数量常驻画布左下角。两栏(861~1190px)时第一行按同样方式填满,详情换到下一行,保持自然高度并随页面滚动。单栏保持原有页面滚动和列表 320px 上限。窗口大小或图谱上方内容(提示、工具栏)变化时重新计算。
高度按窗口剩余可见空间计算,底部保留约 24px 边距(不小于页面底部内边距)。平铺图最低 660px,时间螺旋最低 320px;低于最小高度时页面允许纵向滚动。中间画布不内部滚动,工具栏与事件浏览栏之外的空间给图;左侧列表和右侧详情各自滚动。两种视图的图例均使用共享 InlineHelp 的纯 i 图标,保留可访问名称和 title;点击或 Enter/Space 固定展开,再次点击、Escape、点击或聚焦外部关闭。平铺可见数量仍在画布角落;螺旋左下角不显示圈长及圈数,这些信息收进图例。窗口尺寸及上方表单展开、收起后重新计算可用高度。
实体及其影响连线复用现有 `--graph-node-*-border` 令牌;颜色用于辅助辨认实体,不代表故事分类或确认状态。选中项加粗并突出连线,其余节点降低图形强调度但保留可读文字。详情分为名称与说明、图谱关联、确认操作和来源,来源正文按需读取。图例常显,读图说明可展开。
实体及其影响连线复用现有 `--graph-node-*-border` 令牌;颜色用于辅助辨认实体,不代表故事分类或确认状态。选中项加粗并突出连线,其余节点降低图形强调度但保留可读文字。详情分为名称与说明、图谱关联、确认操作和来源,来源正文按需读取。图例及读图说明按需展开。
画布每批最多显示 8 个事件、6 个实体;同时间事件分开排布。列表保留全部返回记录和完整名称,选择实体会定位到真实关联事件所在批次,选择关系会优先显示两端实体。滑块明确命名“事件浏览”,只切换当前事件,不还原实体历史状态,也不宣称完整历史回放。当前接口返回单条故事线,不复制 Demo 中的三条模拟故事。后续视图规划见[图谱设计](../story-graph/prd.md)。
按实际内容容器宽度响应:不超过 `1190px` 时变为 `184px` 列表加画布,详情移到下方;不超过 `860px` 时变为单列,画布优先,列表限制高度并独立滚动。SVG 最小宽度为 `660px`,窄屏只在画布内部横向滚动,页面本身不横向溢出。范围、时间和操作可换行。节点、关系与事件均可从列表选择,画布节点支持 Enter、Space 和可见焦点。加载、无数据、读取失败和服务不可用各自呈现,错误提供可行的恢复说明或重试入口。
按实际工作区容器宽度响应:宽于 `1190px` 保留三栏和左侧分类页签;`861~1190px` 为 `184px` 列表加画布,分类改用紧凑选择框。详情通过工具栏按钮或选择记录打开,作为图谱右侧的非模态面板,不在画布下方增加长区块;关闭按钮和 Escape 返回详情触发按钮。`860px` 以下画布优先,列表在下方、最高 320px,详情面板不超过画布高度。SVG 随画布缩放,390px 下页面和画布均不横向滚动;缩小后的完整名称可从列表读取。范围、时间和操作按需换行。节点、关系与事件均可从列表选择,画布节点支持 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`。
@@ -163,16 +163,19 @@
| Given | When | Then |
| --- | --- | --- |
| 范围有效且有来源 | 点击“回顾当前进展” | 页面显示整理中状态和活动入口,活动页提供暂停与取消 |
| 范围有效且有来源 | 点击“回顾” | 应用 Toast 提示已开始,可从活动记录查看进度、暂停与取消 |
| 整理完成并有新变化 | 返回结果 | 工作回顾、监督反馈和图谱引用同一次结果 |
| 没有有效新变化 | 整理完成 | 显示检查时间和“暂无新进展”,不重复推送旧内容 |
| 用户点击取消 | 整理仍在运行 | 清理期间显示正在取消,停止后显示已取消,保留上一次成功结果和成功批次 |
| 回顾正在执行或停止清理 | 再次点击手动发起 | 显示当前运行提示,不新建或排队;关闭提示不取消执行 |
| 回顾运行中刷新或切换页签 | 本次请求随后失败 | 显示本地化错误 Toast,技术详情留在活动记录,工作区不增加错误横幅 |
| 已有历史结果或运行记录 | 加载页面或选择历史 | 只读取内容,不补发开始或结束通知;读取失败就地重试 |
**界面行为**
- 运行按钮在整理期间保持可用,重复点击按上表提示;取消停止后可以新建,已取消运行不能继续。
- “无新进展”与“失败”使用不同状态和文案。
- 手动操作的开始、结束和失败反馈遵守 [Toast 规则](./ui-design.md#应用入口),关闭通知后可继续阅读已有结果。
- 结果必须显示本次冻结的范围,不能因用户后来切换项目而改变解释。
### US-S03 自动检查不过度打扰
@@ -518,7 +521,7 @@
- 页面显示本次运行的冻结范围。
- 权限不足、来源不可用和没有新内容使用不同状态。
- 监督者的 Agent 身份不自动获得工具执行、批准或切换 Execute 的能力。
- 监督者的内部评审保持无工具,不因 Agent 身份获得工具执行或批准能力。
## 活动记录
@@ -647,6 +650,19 @@
| 已选连接被删除或变成图片模型 | 打开设置并尝试执行 | 显示连接不可用,执行报错,用户可改选;不自动回退 |
| 模型选择保存失败 | 留在设置页 | 保留选择草稿及重试入口 |
### US-S35 删除会话后继续未发布回顾
对应 FR-S9,规则见[会话删除](./logic-design.md#未发布回顾遇到会话删除)。
| Given | When | Then |
| --- | --- | --- |
| 回顾已保存批次但尚未发布 | 删除其中一个会话后继续 | 跳过该会话的消息、知识引用和已记录归属的任务,保留无关批次 |
| 来源正在初始化、提取、合并或等待发布 | 用户删除会话 | 丢弃受影响输出及依赖摘要,继续其余来源,不从片段恢复会话 |
| 一个旧批次包含已删除及仍存在的会话来源 | 继续回顾 | 重做受影响的存活来源,无关来源不重做 |
| 所有待回顾来源所属会话均已删除 | 继续或结束当前请求 | 显示省略数及无新变化,不生成空报告 |
| 删除前已有发布结果 | 删除会话 | 既有历史结果和历史片段不变;未发布结果按省略后的覆盖发布 |
| 会话仍存在,来源正文被修改或会话被归档 | 校验来源 | 明确失败,不把修改或归档记为删除省略 |
## 验收优先级与依赖
首个可用版本应先打通以下路径:
+1 -1
View File
@@ -18,7 +18,7 @@
未发布实现的验收状态见[工作栏进度](../assistant-workbar/progress.md#2026-09-14-runtime-进程复用实施中)。
The separate official Web client is pinned to `@deepseek-ai/dsh@0.1.7-rc.2`.
Its Main service API, Ask/Execute policy, capability adapters, packaged resource
Its Main service API, unified execution rules, capability adapters, packaged resource
layout, and Windows validation are documented in
[Native Client Proposal, section 9.1](../assistant-workbar/runtime-native-client-proposal.md#91-ds-web-backend-2026-09-26).
This Web resource tree is prepared separately from the background Harness Host.
@@ -13,19 +13,19 @@
| GoodBuddy 目标平台 | Windows、macOS、Linux,x64 与 arm64 |
| 本文性质 | 设计与发布验收约定 |
本文定义 DeepSeek Harness 在 GoodBuddy 中的架构边界、协议、执行策略、插件市场、界面、打包和验收要求。实现必须继续遵守 GoodBuddy 已有的 Main 进程安全边界、Ask/Execute 语义、取消、超时、有界输出和资源回收约定。
本文定义 DeepSeek Harness 在 GoodBuddy 中的架构边界、协议、执行策略、插件市场、界面、打包和验收要求。实现遵守 Main 进程边界、能力范围、取消、超时、有界输出和资源回收约定;当前执行规则见[统一执行](../unified-execution/logic-design.md)。
## 2. 摘要
DeepSeek Harness 的底层库使用 Cordis 组合服务。GoodBuddy 不采用官方产品 profile,也不允许用户配置覆盖内部 Host 或控制服务;GoodBuddy 自行维护 Host、控制协议和生命周期,同时提供一个由 Main 管理、默认关闭的 npm 插件市场。用户显式开启后,市场只搜索带精确 `dsh-plugin` 关键字的公共 npm 包,不代表 GoodBuddy 审核、推荐或承诺兼容这些包。
用户明确安装并启用的插件以当前用户权限运行。Ask/Execute 只控制模型经过 `tools/execute` 发起的工具调用:Ask 只允许 Host 中真实注册的 `read`、`skill` 和 Main 管理的 Web Search/Fetch、Story Graph 只读代理,Execute 放行 Host 中全部已注册工具。插件不能用同名工具冒充 Ask 允许项。插件安装脚本和初始化代码不属于模型工具调用,不能由 Ask 限制,因此界面在安装前必须明确确认这一边界。
用户明确安装并启用的插件以当前用户权限运行。模型可调用 Host 注册的内置及已启用插件工具,不维护产品只读名单。Main 代理工具仍校验请求、分配及资源归属。插件安装脚本和初始化代码也以当前用户权限运行,安装前保留既有确认。
整体分成两个互相约束的部分:
1. **GoodBuddy Main Control Plane**
- 运行在 Electron Main 进程。
- 持有加密设置、模型连接选择、Ask 只读策略、Runtime 生命周期、插件市场状态和审计归属。
- 持有加密设置、模型连接选择、能力配置、Runtime 生命周期、插件市场状态和审计归属。
- 通过 Electron `utilityProcess` 启动受控 Harness 子进程。
- 对环境、输入、输出、超时、取消和进程树执行强制限制,并只把已启用插件的受管入口传给 Host。
@@ -67,7 +67,7 @@ Filesystem Provider,以 GoodBuddy 客户端进程的当前用户权限运行
### 3.3 自维护边界
GoodBuddy 自己的 Runtime 和控制面不包装成标准 DSH 插件,也不加载用户 profile 或自定义 Host。只有 GoodBuddy Main 可以启动内部 Host、选择受管插件入口并处理启动失败。公共 npm 市场是第三方扩展来源,不改变 GoodBuddy 对内部 Host、Ask/Execute 语义和协议版本的控制。
GoodBuddy 自己的 Runtime 和控制面不包装成标准 DSH 插件,也不加载用户 profile 或自定义 Host。只有 GoodBuddy Main 可以启动内部 Host、选择受管插件入口并处理启动失败。公共 npm 市场是第三方扩展来源,不改变 GoodBuddy 对内部 Host、能力接入和协议版本的控制。
## 4. 目标与非目标
@@ -76,13 +76,13 @@ GoodBuddy 自己的 Runtime 和控制面不包装成标准 DSH 插件,也不
- 增加 `deepseek-harness` Runtime,并在设置、聊天和消息通道中可选择。
- 使用 GoodBuddy 管理的模型连接,不在 Renderer 或持久化 Harness 配置中写入 API Key。
- 当所选模型连接明确声明支持图像输入时,允许向 DeepSeek Harness 发送有界的 JPEG/PNG;文本模型在启动 Host 或调用模型前拒绝图片。
- Ask 模式在 Runtime 工具分发边界强制只读,阻止 Shell、写入和编辑工具。
- 在 Web Search 能力启用时,通过 Main 代理向 Ask 与 Execute 提供有界的 `web_search` 和 `web_fetch`,Harness Utility 不持有服务凭据。
- Execute 模式使用 DSH 本地 Provider,以当前用户权限执行文件与命令工具;工作区是默认工作目录,不是 OS 权限边界。
- 正常请求使用实际注册工具,不保留工作模式或一般审批策略。
- 在 Web Search 能力启用时,通过 Main 代理提供有界的 `web_search` 和 `web_fetch`,Harness Utility 不持有服务凭据。
- 使用 DSH 本地 Provider,以当前用户权限执行文件与命令工具;工作区是默认工作目录,不是 OS 权限边界。
- 提供默认关闭的公共 npm DSH 插件市场总开关;用户显式开启后可搜索、查看详情、精确版本安装、启用、停用、配置和移除,首次安装前明确确认当前用户权限。
- 只加载 Main 明确传入的已启用插件;单个插件启动失败不得阻止 Host,并自动停用失败插件。
- 允许 Skills 和自定义 MCP 显式分配给 DeepSeek Harness;自定义 MCP 只在 Execute 中通过 Main 代理。
- 设置页可读取有界的 Host/插件原生 Tool 与 Skill 清单;Tool 元数据显示类型、来源及 Ask/Execute 可用性,并明确排除 GoodBuddy 分配的 Skills、Web/MCP 请求代理。
- 允许 Skills 和自定义 MCP 显式分配给 DeepSeek Harness;自定义 MCP 通过 Main 代理。
- 设置页可读取有界的 Host/插件原生 Tool 与 Skill 清单;Tool 元数据显示类型、来源及描述,不提供统一逐工具开关,并明确排除 GoodBuddy 分配的 Skills、Web/MCP 请求代理。
- 支持多会话、同会话串行、跨会话并行。
- 支持按请求取消、超时、会话释放和应用退出时完整回收。
- 输出文本、推理、工具参数、工具结果、stderr 和协议队列全部有界。
@@ -130,7 +130,7 @@ Main 在创建 Session 前解析该请求工作区的 `realpath`,把绝对路
Harness 子进程内控制面不能取代 Main 控制面,Main 控制面也不能代替进程内的 Session/Tool 适配层:
- Harness Control Plane 最接近 Session、Agent、Tool 和 Usage seam,适合做内部协议转换与 Ask 工具拦截。
- Harness Control Plane 在 Session、Agent、Tool 和 Usage 接口处处理内部协议转换与请求归属。
- Main 控制面是可信安全边界,适合持有模式授权策略、加密设置、进程控制和 IPC。
任何一侧缺失能力握手时,Runtime 必须报告不可用,不能降级为不受控执行。
@@ -160,7 +160,7 @@ Electron Main
├─ RuntimeSettingsStore
├─ RuntimeExtensionStore / npm Marketplace
├─ AgentRuntimeController
├─ RuntimeAuthorizer(Ask 拒绝 / Main 代理工具授权)
├─ Main 代理工具能力与请求归属校验
└─ DeepSeekHarnessRuntime / Main Control Plane
│ ACP + goodbuddy/* 扩展,stdin/stdout
▼
@@ -199,7 +199,7 @@ Harness 子进程崩溃、输出异常、拒绝协议或加载错误时,Main
- 启动 ACP 兼容的 JSON-RPC stdio 服务。
- 创建、查找和释放 Harness Agent。
- 在 Prompt 前应用 GoodBuddy 指定的 Ask/Execute 权限。
- 在 Prompt 前绑定请求标识与能力上下文。
- 将 DSH Session 事件转换为有界的 GoodBuddy 事件。
- 将 LLM 用量转换为稳定的模型用量事件。
- 根据 Main 传入的模型能力声明 ACP 图片能力,验证内联图片并转换为 DSH 的不可变 Attachment 引用。
@@ -226,7 +226,7 @@ GoodBuddy 控制面自身不导出 `apply(ctx, config)`,也不提供默认 std
- 插件按清单依次加载;导入、导出形态或激活失败只记录该插件,不阻止其他插件和 Host 启动。失败 Fiber 的清理同样有界。
- 有限但超过预算的同步导入或同步 `apply` 在返回后按超时失败并继续加载后续插件;JavaScript 不能在同一事件循环内抢占永不返回的同步第三方代码,此时由 Main 的独立启动截止时间终止整个 Utility。
- 失败 ID 在 ready 握手中返回 Main;Main 原子写入停用状态和启动错误。
- 插件成功激活后可注册工具或后台生命周期逻辑。Ask 只能拦截模型工具调用,不能撤销初始化阶段已经发生的副作用。
- 插件成功激活后可注册工具或后台生命周期逻辑;初始化阶段使用当前用户权限。
GoodBuddy 不扫描任意目录、不读取用户 profile 插件清单,也不接受 Renderer 直接提供文件路径。
插件安装、升级和移除在目录重命名前写入受管变更日志。Main 下次初始化时以持久
@@ -246,7 +246,7 @@ Store 是否已经提交为准,确定性完成新目录或恢复旧目录,
### 8.2 标准 ACP 方法
首版保留 ACP 的初始化、`session/new`、`session/prompt` 和 `session/cancel` 语义。`promptCapabilities.image` 必须与所选模型连接的 `supportsImageInput` 完全一致,不能仅根据 Provider 或模型名称猜测。标准 ACP 客户端可以使用只读默认行为,但只有完成 GoodBuddy 能力握手的客户端才能启用 Execute。
保留 ACP 的初始化、`session/new`、`session/prompt` 和 `session/cancel` 语义。`promptCapabilities.image` 必须与所选模型连接的 `supportsImageInput` 完全一致,不能仅根据 Provider 或模型名称猜测。GoodBuddy 请求须完成能力握手和逐轮 prepare,不宣告产品工作模式。
### 8.3 GoodBuddy 扩展
@@ -255,12 +255,12 @@ Store 是否已经提交为准,确定性完成新目录或恢复旧目录,
| 方法或事件 | 方向 | 用途 |
| --- | --- | --- |
| `goodbuddy/handshake` | Main → Control Plane | 交换控制协议、Harness、ACP 版本和能力 |
| `goodbuddy/session/prepare` | Main → Control Plane | 在下一次 Prompt 前设置工作模式和请求标识 |
| `goodbuddy/session/prepare` | Main → Control Plane | 在下一次 Prompt 前设置请求标识 |
| `goodbuddy/session/release` | Main → Control Plane | 取消并释放指定 Session |
| `goodbuddy/session/event` | Control Plane → Main | 文本、推理、工具、状态和用量事件 |
| `goodbuddy/credential/resolve` | Control Plane → Main | 按已登记引用请求当前 Runtime 的临时凭据 |
| `goodbuddy/tools/list` | Control Plane → Main | 取得 Main 管理的有界 Web 工具与当前 Execute 请求的 MCP 工具 schema |
| `goodbuddy/tools/call` | Control Plane → Main | 校验活动请求、工作模式、参数和精确注册代理身份后调用 Main Web/MCP 工具 |
| `goodbuddy/tools/list` | Control Plane → Main | 取得 Main 管理的有界 Web 工具与当前请求的 MCP 工具 schema |
| `goodbuddy/tools/call` | Control Plane → Main | 校验活动请求、参数和精确注册代理身份后调用 Main Web/MCP 工具 |
| `goodbuddy/native/snapshot` | Main → Control Plane | 从无 Agent scope 的 Host Registry 读取有界的原生 Tool/Skill 元数据,排除 GoodBuddy 分配项与请求级代理 |
| `goodbuddy/shutdown` | Main → Control Plane | 停止接收新请求并有序清理 |
@@ -270,7 +270,7 @@ Header 与固定 8 MiB 帧上限;旧版本或缺少必需字段的启动消息
```ts
type GoodBuddyHarnessCapabilities = {
controlProtocolVersion: 1
controlProtocolVersion: 2
harnessVersion: string
acpProtocolVersion: number
supports: {
@@ -291,18 +291,18 @@ type GoodBuddyHarnessCapabilities = {
### 8.4 每轮权限准备
GoodBuddy 的工作模式属于每个请求,不属于 Runtime 进程全局状态。同一对话可以在 Ask 和 Execute 之间切换。因此:
每个请求准备状态绑定到独立 Session,不含工作模式:
1. `session/new` 后默认是 Ask。
1. `session/new` 建立会话,不设置默认产品模式。
2. 每个 Prompt 前,Main 发送一次 `goodbuddy/session/prepare`。
3. Harness Control Plane 将准备状态绑定到 `sessionId + requestId`。
4. `session/prompt` 只能消费匹配且尚未使用的准备状态。
5. 缺少准备状态、重复使用、请求标识不匹配时,Control Plane 直接拒绝请求。
6. 同一 Session 只允许一个 Prompt 在途。
若 Runtime 回传 ACP 权限请求,Main 只按活跃 Session 的当前模式答复:Execute 优先选择
`allow_once`,缺少时使用提供的 `allow_always`,不调用第二次人工 authorizer;
Ask、已关闭或未知 Session 拒绝。这里不写入永久授权规则,也不把业务问答当作权限确认。
若 Runtime 回传 ACP 权限请求,Main 对活跃 Session 优先选择 `allow_once`,缺少时使用
提供的 `allow_always`,不调用第二次人工 authorizer;已取消、已关闭或未知 Session 拒绝。
这里不写入永久授权规则,也不把业务问答当作权限确认。
### 8.5 事件模型
@@ -379,12 +379,10 @@ Main 的 Web/MCP ToolProvider 按工作区保存,最后一个对应 Session
## 10. 权限与主机执行
### 10.1 模式映射
### 10.1 工具接入
| GoodBuddy 模式 | Host 内置与插件工具 | Main Web Search/Fetch | GoodBuddy 自定义 MCP | 行为 |
| --- | --- | --- | --- | --- |
| Ask | 只允许 Host Registry 中真实注册的 `read` 与 `skill`;其他 Host/插件工具一律拒绝 | 能力启用时注册 Main 的精确代理对象,无逐次审批 | 不注册 | 模型工具调用保持只读 |
| Execute | 放行 Host 中全部已注册的内置与插件工具 | 能力启用时注册 Main 代理 | 按分配注册并经过既有 RuntimeAuthorizer | 不增加插件权限层,以当前用户权限运行 |
Host 提供实际注册的内置及已启用插件工具;Main Web Search/Fetch 在能力启用时注册,
自定义 MCP 按启用与 Runtime 分配注册。不增加产品审批或插件权限层。
2026-09-28:内置 Story Graph 的三个读取工具在 Ask/Execute 均通过 Main 代理提供,
Host 只接受 Main 注册的具体工具对象。Main 逐次检查监督者启用、Harness 分配及请求项目范围,
@@ -392,31 +390,31 @@ Host 只接受 Main 注册的具体工具对象。Main 逐次检查监督者启
[Story Graph 设计](../conversation-supervision/story-graph-mcp-design.md)和
[验证进度](../conversation-supervision/progress.md#2026-09-28-story-graph-只读工具)。
### 10.2 Ask 模式
### 10.2 Main 代理
- Harness Control Plane 在 `tools/execute` 分发边界识别当前 Session 和在途请求。
- 采用所有权感知的只读允许列表,只接受 Registry 中真实的 `read`、`skill` 和 Main 注册的 Web/Story Graph 代理对象;`write`、`edit`、Shell、自定义 MCP 及任意新插件工具默认拒绝。只比较工具名不足以授权,插件注册同名工具仍会被拒绝。
- Ask 不注册 Main 代理的自定义 MCP;Story Graph 使用共享只读合同,scope 的互斥分支转换为 Harness 支持的 `oneOf`,完整参数仍由 Main 的 Zod schema 校验。
- Main 代理按实际注册身份与请求上下文调用,不维护原生工具只读允许列表。
- Story Graph 使用共享只读合同,scope 的互斥分支转换为 Harness 支持的 `oneOf`,完整参数仍由 Main 的 Zod schema 校验。
- Web Search/Fetch 的凭据、传输与结果限制保留在 Main;Utility 只看到有界 schema 和结果。
- 只读不等于无限输出,读取仍受字节和工具结果上限控制。
- 插件安装脚本和 Cordis 初始化生命周期不经过 `tools/execute`。Ask 不能把已启用第三方代码变成沙箱,也不能保证第三方代码没有启动副作用。
- 插件安装脚本和 Cordis 初始化生命周期不经过模型工具调用,不属于 OS 沙箱。
### 10.3 Execute 模式
### 10.3 主机工具
- 工作区来自 Session 创建时的规范化绝对路径,并作为文件与命令工具的默认工作目录。
- DSH 本地 Filesystem、Bash 或 PowerShell Provider 直接使用 GoodBuddy 客户端当前用户的 OS 权限。
- 工作区不是 containment 边界;绝对路径和命令可访问当前用户本来有权访问的主机资源。
- 已启用插件注册的工具与内置工具使用同一分发路径;GoodBuddy 不增加插件权限矩阵或逐工具确认。
- Main 代理的 MCP 工具继续执行分配、schema、活动请求、模式和 RuntimeAuthorizer 校验。
- Main 代理的 MCP 工具继续执行分配、schema、活动请求和资源范围校验。
`goodbuddy/tools/list` 按 Session 保存本次发现的工具定义;`goodbuddy/tools/call` 复用该定义
校验参数与授权,不因调用健康工具而重新等待故障 MCP 的发现。发现重试留在清单刷新,
Session 释放及 Runtime dispose 清除内存定义,不持久化第二份能力状态。
- 所有工具调用仍作为活动事件记录;自定义 MCP 的 Ask 和 delegation 路径继续固定拒绝。
- 所有工具调用仍作为活动事件记录;委派沿用已接入能力与请求资源范围。
### 10.4 Runtime OS 沙箱
- GoodBuddy 不加载 DSH 平台 Sandbox Provider,也不执行启动沙箱探测。
- “安全与数据”不提供 Runtime OS 沙箱开关。
- 不提供 Runtime OS 沙箱开关;“安全”设置分类已移除。
- 握手明确报告 `execution.mode = 'host'`,状态文案明确说明工具使用当前用户权限。
- Electron Renderer、Preload、Browser Session 等应用安全沙箱不在本设计变更范围内。
@@ -567,7 +565,7 @@ Host 始终由当前 GoodBuddy 版本提供,不存在自定义 Host 入口。
- 高级设置默认收起。
- DSH 插件市场提供共享 Switch 样式的总开关并默认关闭。关闭时不请求公共 npm 目录且隐藏市场管理界面,但不修改已有插件的逐项启停状态;因此已启用插件继续随 Host 加载,重新开启后恢复原有管理状态。
- 同一 Runtime 页面提供紧凑的 DSH 插件市场:客户端筛选名称、包名、描述和许可证,已安装插件优先显示。
- 安装前使用一个明确 Checkbox 确认 npm 安装脚本、插件初始化和 Execute 工具均使用当前用户权限;不展示权限矩阵或逐工具审批。
- 安装前使用一个明确 Checkbox 确认 npm 安装脚本、插件初始化和工具均使用当前用户权限;不展示权限矩阵或逐工具审批。
- 已安装插件使用共享 Switch 启停,并提供 JSON 配置、明确移除确认和启动失败信息。
- npm 目录离线时仍显示并允许管理已安装插件;目录错误就地显示并可重试。
- 安装、启停、配置和移除的短期结果通过应用通知显示,不重复保留页内成功提示。
@@ -581,7 +579,7 @@ OpenCode、Continue 和 DeepSeek Harness 的后续能力按操作生命周期放
| 表面 | 负责内容 | 不负责内容 |
| --- | --- | --- |
| Composer 通用行 | 附件、语音、知识范围、专家、Ask/Execute、Runtime 和发送 | Session 监督、后台进度、历史任务管理 |
| Composer 通用行 | 附件、语音、知识范围、专家、Runtime 和发送 | Session 监督、后台进度、历史任务管理 |
| Composer Runtime 专属行 | 仅对当前消息生效且需要高频选择的 Agent、预设、Prompt/Command 快捷操作 | Task 级委派、后台执行、Workflow/Hook 生命周期 |
| 助手工作栏固定“Runtime”栏目 | 用户所选 Conversation 或 Task 的 Runtime 状态、Task 级委派与取消、后台执行进度/结果、Workflow/Hook 运行、长任务暂停/恢复/终止和会话监督;不显示 Job/Run 树 | 持久模型、程序路径、默认 Agent/预设配置 |
| 设置 > Agent Runtime | 持久 Runtime 配置、默认值、插件管理、能力清单和连接诊断 | 某次活动会话的实时控制 |
@@ -695,11 +693,11 @@ Renderer 只接收公开 npm 元数据和受管插件状态。任何凭据、完
- 二进制检测、版本解析和路径规范化。
- ACP 握手、事件转换和请求关联。
- 每个会话单请求、跨会话并行。
- Ask 在工具分发边界只允许真实注册的 `read`、`skill` 与 Main Web/Story Graph 代理,并拒绝同名冒充和任意新插件工具;Execute 放行插件工具。
- 原生及已启用插件工具可调用;Main 代理保留注册身份与请求范围校验。
- 握手只接受明确的 `execution.mode = 'host'`。
- 未分配 Skill/MCP 不可见;分配后的 Skill catalog 可调用 `skill` 加载。
- 原生能力快照只包含 Host/插件原生 Skills,不包含 GoodBuddy 分配的 Skills 或 MCP。
- Ask 不注册自定义 MCP 工具;Web 和 Story Graph 代理可用于 Ask 与 Execute;Execute 每轮刷新有界自定义 MCP schema,并在调用前再次校验活动请求、模式、参数和 RuntimeAuthorizer 结果。
- 按设置注册 Web、Story Graph 与自定义 MCP;每轮刷新有界 MCP schema,调用前再次校验活动请求、参数和资源范围。
- MCP URL、启动命令和凭据不进入 Utility 启动配置或协议结果。
- 未知授权结果失败关闭。
- 超时、取消、迟到帧和进程意外退出。
@@ -732,18 +730,18 @@ Renderer 只接收公开 npm 元数据和受管插件状态。任何凭据、完
真实测试已经获得用户授权,但必须由显式环境门禁启用。Web3D Skill 和 MCP 仅作为 `tests/fixtures` 下的测试资产使用,不属于内置发布能力。至少验证:
1. 文本问答成功,并记录正确 Runtime 和模型用量。
2. Ask 可以读取工作区,但写入被拒绝,且不会弹出权限对话框。
3. 启用 Web Search 后,Ask 可以调用 Main 管理的 `web_search` 与 `web_fetch`,插件同名工具仍被拒绝。
4. Execute 可以在工作区创建测试文件。
5. Execute 工具确实以当前用户权限运行,且状态和握手不宣称 OS 隔离。
6. Ask、delegation 和无活动请求不能绕过工具分发检查。
2. 正常请求可以读取工作区,无产品模式或一般审批等待。
3. 启用 Web Search 后,可调用 Main 管理的 `web_search` 与 `web_fetch`,保留代理归属检查。
4. 可以在工作区创建测试文件。
5. 工具确实以当前用户权限运行,且状态和握手不宣称 OS 隔离。
6. 无活动请求或未分配能力的调用不能绕过工具分发检查。
7. 取消长请求后不再产生文本,并可继续使用其他 Session。
8. 两个 Session 可并行,事件不会串线。
9. 释放会话和关闭应用后没有残留 Harness 或工具进程。
10. 从全新用户设置流程启用一个 3D 游戏 Skill 和实际本地或开放 MCP,工具事件能够证明二者确实被调用。
11. Harness 生成的 3D 游戏项目可以安装、启动和实际游玩,包含 3D 渲染、玩家控制、目标和反馈,浏览器无关键错误。
12. 使用公共 npm 搜索,通过 GoodBuddy 捆绑的 npm 安装经审查的最小第三方插件,Host 成功加载并执行其真实工具。
13. 实际 ACP 路径中 Ask 拒绝该插件工具,Execute 允许该工具,不出现 GoodBuddy 逐工具确认。
13. 实际 ACP 路径可调用已启用插件工具,不出现 GoodBuddy 逐工具确认。
测试不得打印、快照或提交 API Key。测试创建的文件只能位于专用临时工作区,并在确认可再现后清理。
@@ -768,11 +766,11 @@ npm run build
- Runtime 详情卡内显示状态、路径、版本和当前用户执行权限。
- 该阶段的 Skills 与自定义 MCP 设置可把能力分配给 DeepSeek Harness;当时请求级 GoodBuddy 内置 MCP 不支持分配,并在卡片中置灰。布局、键盘语义、文案和保存回显通过真机检查。2026-09-28 的 Story Graph 源码另接入 Harness 分配与 Main 只读代理,验证证据见上文专项记录。
- DSH 市场初始关闭且不加载 npm 目录;显式开启后可以搜索、安装、启停、配置和移除插件,安装前只出现一次准确的当前用户权限确认。关闭市场后已有启用插件继续运行,重新开启后管理状态不变。
- Ask 写入测试在 Runtime 边界失败。
- Ask 可使用已启用的 Main Web Search/Fetch,且插件无法通过同名工具绕过所有权校验。
- Ask 拒绝任意插件工具,Execute 可调用全部已启用插件工具。
- 无模式请求可以执行测试目录写入,不保留只读名单。
- 可使用已启用的 Main Web Search/Fetch,代理调用保留所有权校验。
- 可调用全部已启用插件工具。
- Runtime 原生清单只显示 Host/插件原生 Skills,不显示 GoodBuddy 分配项。
- Execute 工作区内写入成功。
- 工作区内写入成功。
- Runtime OS 沙箱设置、平台 Runner、启动探测和原生沙箱打包产物均不存在。
- 取消、超时、切换 Runtime 和退出应用均能回收进程。
- 多会话不串流、不串权限请求、不串用量。
@@ -790,11 +788,11 @@ npm run build
- DeepSeek Harness 底层库当前是 RC,但 GoodBuddy 不自动跟随升级;每次升级都可能要求同步修改内部控制面。
- Harness 文件和命令工具没有 Runtime OS 隔离,会继承 GoodBuddy 客户端当前用户能够访问的主机资源。
- 首版不恢复 Harness 原生 Session,Runtime 重启后由 GoodBuddy 历史重建。
- 图片输入仅在所选模型连接明确声明支持时可用;首版仍不支持知识库、浏览器控制和 Harness Subagent。Web Search/Fetch 仅使用 Main 代理,MCP 仅支持用户分配、Main 代理和 Execute 自动单次授权路径。
- 图片输入仅在所选模型连接明确声明支持时可用;首版仍不支持知识库、浏览器控制和 Harness Subagent。Web Search/Fetch 仅使用 Main 代理,MCP 仅支持用户分配和 Main 代理路径。
- Harness Subagent、后台 Job、Workflow、Hook 和原生会话监督尚未实现;未来按 Task 聚合到右侧 Runtime 监督栏,不扩张 Composer 工具栏或暴露 Job/Run 层级。
- 推理、工具和用量扩展属于 GoodBuddy 协议,不是标准 ACP 保证。
- 市场来自公共 npm 关键字搜索,不是精选目录;包的质量、兼容性和维护状态由发布者负责。
- 插件安装、初始化、后台生命周期和 Execute 工具使用当前用户权限,不受 Runtime OS 沙箱保护;Ask 只控制模型工具调用。
- 插件安装、初始化、后台生命周期和工具使用当前用户权限,不受 Runtime OS 沙箱保护。
- 不支持用户 profile、自定义 Host、任意本地插件路径或 profile patch。
## 20. 自维护与升级策略
+2 -2
View File
@@ -6,7 +6,7 @@ Task 和 Runtime 能力的一个轻量触发面和状态镜面。
## 核心边界
- 桌面宠物**不**新增 Ask/Execute 执行通道,也不在自身窗口内直接展示模型回复。快速发起
- 桌面宠物**不**新增工具执行通道,也不在自身窗口内直接展示模型回复。快速发起
和拖放暂存的内容始终交给主窗口现有输入区确认后发送,从不自动发送或自动执行。
- 桌面宠物**不**持续读取屏幕内容、剪贴板或全局选中文本。
- 桌面宠物显示的任务状态、通知和历史记录均来自现有 Task/Conversation 数据,不建立第二套
@@ -41,7 +41,7 @@ Task 和 Runtime 能力的一个轻量触发面和状态镜面。
- [助手工作栏](../assistant-workbar/README.md):主窗口执行空间和输入区的权威定义,宠物的
快速发起和拖放暂存最终落地到该输入区,不复制其行为。
- [直连模型 Agent 能力](../direct-model-agent/README.md)、[远程主机与远程执行](../remote-host/README.md):
Ask/Execute 授权边界的权威定义,宠物不扩大或绕过该边界。
统一执行与能力范围的权威定义,宠物不扩大或绕过该边界。
- [文档处理](../document-processing/README.md):拖放文件解析和格式支持的权威定义,宠物拖放
暂存复用同一处理管线,不重新定义支持类型。
- 根 [`UI-DESIGN.md`](../../../UI-DESIGN.md):全局视觉、通知视口、Switch/Checkbox 语义和
+3 -3
View File
@@ -11,15 +11,15 @@
| User Stories | [桌面宠物 User Stories](./user-stories.md) |
| 任务模型 | [Task 与 Job 统一领域模型](../task-and-job/task-and-job-model.md) |
本文负责桌面宠物自身的状态、不变量和决策规则,不重新定义 Task、Conversation、Ask/
Execute 或附件的既有语义;这些语义以各自权威文档为准。
本文负责桌面宠物自身的状态、不变量和决策规则,不重新定义 Task、Conversation、工具执行
或附件的既有语义;这些语义以各自权威文档为准。
## 1. 核心不变量
### INV-1 宠物不是第二条执行通道
快速起草和拖放暂存只产生主窗口会话输入区的草稿数据(文本、附件引用),不产生消息、
不产生 Task、不触发模型调用。发送、模式选择和 Runtime 选择继续完全由主窗口既有流程
不产生 Task、不触发模型调用。发送和 Runtime 选择继续完全由主窗口既有流程
拥有。
### INV-2 状态镜面是既有状态的投影
+4 -4
View File
@@ -27,7 +27,7 @@ Conversation、Task 和 Runtime 能力上的一层轻量触发面和状态镜面
3. 桌面宠物默认不产生新的干扰:不主动弹窗抢焦点,不重复现有原生通知。
4. 桌面宠物的开关、位置、外观和提示强度完全由用户控制,且可随时完全关闭并恢复到没有该
功能之前的界面状态。
5. 桌面宠物不新增或绕过 Ask/Execute 授权边界,也不建立第二条消息发送或任务执行路径。
5. 桌面宠物不新增或绕过既有能力范围,也不建立第二条消息发送或任务执行路径。
## 3. 功能范围
@@ -45,7 +45,7 @@ Conversation、Task 和 Runtime 能力上的一层轻量触发面和状态镜面
### 3.2 不包含
- 在宠物窗口内直接展示模型回复、执行工具调用或建立第二条 Ask/Execute 通道。
- 在宠物窗口内直接展示模型回复、执行工具调用或建立第二条执行通道。
- 未经用户确认自动发送消息或自动执行任务。
- 持续读取屏幕内容、剪贴板或系统级选中文本。
- 用户上传任意自定义头像图片、养成机制、货币或每日签到。
@@ -74,7 +74,7 @@ Conversation、Task 和 Runtime 能力上的一层轻量触发面和状态镜面
- 点击宠物打开一个紧邻宠物的小型弹出框,包含文本输入和“附加文件”按钮。
- 文本输入支持多行、粘贴图片;附加文件复用现有附件类型、数量和大小限制。
- 弹出框显示将要进入的目标:当前活跃会话,或“新建对话”。用户可在弹出框内切换目标,
但不能在弹出框内选择模型、Runtime 或 Ask/Execute 模式。
但不能在弹出框内选择模型或 Runtime。
- 提交后主窗口被显示或聚焦,目标会话被激活,文字和附件作为草稿填入该会话的输入区,
输入焦点落在该输入区。提交动作本身不发送消息、不创建任务。
- 取消或关闭弹出框放弃当前草稿,不影响主窗口任何已有草稿。
@@ -131,7 +131,7 @@ Conversation、Task 和 Runtime 能力上的一层轻量触发面和状态镜面
### FR-11 与现有能力的边界
- 快速起草和拖放暂存最终使用主窗口现有的会话、附件、模型和 Ask/Execute 选择逻辑,不
- 快速起草和拖放暂存最终使用主窗口现有的会话、附件、模型和能力配置逻辑,不
新增第二套发送或授权路径。
- 状态镜面和气泡的任务状态数据来自现有 Task 与 Conversation 记录,不建立独立存储、不
与 Task Center 的状态定义产生分歧。
@@ -270,7 +270,7 @@ function shouldSuppressBecauseMainWindowVisible(mainWindow: BrowserWindow): bool
## 11. 与既有模块集成
- 快速起草和拖放最终只调用主窗口渲染进程已有的“设置输入框文本”“追加附件”“激活
会话”“创建新会话”状态更新函数,不新建与 Ask/Execute、模型选择、Runtime 选择相关
会话”“创建新会话”状态更新函数,不新建与模型选择、Runtime 选择相关
的任何路径。
- 点击状态镜面/气泡打开 Task Center 复用现有 Task Center 导航(既有 IPC/路由,与托盘
“新建对话”发送 `conversation:new` 事件属于同一类模式:Main 发送一个事件,主窗口
+1 -1
View File
@@ -170,7 +170,7 @@ GoodBuddy 的能力当前主要在本机执行,SSH 远程项目则要求用户
### FR-14 与现有能力的边界
- 远端调用不能扩大 Ask/Execute、Workspace、MCP、Skill 和知识库现有权限。
- 远端调用不能扩大 Workspace、MCP、Skill 和知识库既有能力范围,沿用统一执行规则。
- 本机提供任务时使用能力声明对应的 Runtime 边界。
- SSH 远程项目、本机工具环境和模型配置默认不向 Peer 或网关传播。
+3 -4
View File
@@ -41,11 +41,10 @@ Task、Job、Subjob、Run 和 Subagent 的对象关系以
- 直连文本模型的普通问答、工具轮次和上下文摘要会在尚无可见输出时,对瞬时网络错误或
单次请求超时最多自动重试 3 次。
- OpenCode、Continue 和 DeepSeek Harness 继续使用各自原生执行与委派能力。
- Ask 可搜索和分页读取工作区,但不允许补丁写入或进程执行;Subagent 若在 Ask 中使用,
只能继承 Ask 的只读能力。
- 正常请求可使用已注册的搜索、读取、补丁与进程工具;Subagent 继承父请求能力范围。
- 搜索、进程和 Subagent 的长输出返回预览与续读位置,完整内容保留到所属会话或 Runtime 释放。
Ask 和 Execute 均可续读当前会话已取得的输出,详见 [输出契约](./technical-design.md#43-输出边界)。
- Execute 表示用户授权当前执行空间账号的完整能力,不增加第二套工具审批或权限档位。
可续读当前会话已取得的输出,详见 [输出契约](./technical-design.md#43-输出边界)。
- 使用当前执行空间账号权限,不增加第二套工具审批或权限档位。
- 首版跨平台指桌面本机 Windows、macOS 和 Linux。托管 SSH 项目当前继续使用远端
OpenCode;未来若向远端直连模型开放本工具,命令必须由 Host Agent 执行,绝不能回退到
桌面本机。
@@ -13,8 +13,8 @@
## 1. 权威不变量
1. `process_execute` 和 `subagent_delegate` 只属于直连文本模型。
2. Ask 永远不能启动命令、写入工作区或创建 Execute Subagent。
3. 子级的项目、执行空间、工作模式、模型连接和能力范围只能继承或缩小。
2. 正常请求按已注册工具和当前账号权限执行,不含产品工作模式或一般工具审批策略。
3. 子级的项目、执行空间、模型连接和能力范围只能继承或缩小。
4. 子级工具清单不包含 `subagent_delegate`,最大深度固定为 1。
5. 命令必须在父请求绑定的执行空间运行,禁止跨设备静默回退。
6. 一个 `process_execute` 调用只拥有自己创建的前台进程树,不能管理无关进程。
@@ -24,9 +24,8 @@
10. OpenCode、Continue 和 DeepSeek Harness 的原生能力不被本功能替换或包装。
11. 直连模型只在尚未显示文本或推理内容时自动重试瞬时网络错误或请求超时,最多重试
3 次;用户取消和已显示的部分输出禁止重放。
12. Ask 可调用 `workspace_rg` 和 `workspace_read_text`,但不能调用
`workspace_apply_patch`;Execute 可调用三者。
13. 搜索直接使用随包 rg 的原生参数和输出;Ask 的参数检查保持工作区内只读,Execute 使用账号权限。
12. 本机正常工具请求可调用 `workspace_rg`、`workspace_read_text` 和 `workspace_apply_patch`。
13. 搜索直接使用随包 rg 的原生参数和输出,使用当前账号权限;读取和补丁保留工作区 API 语义。
14. FR-7 / US-A6:预览之外的搜索、进程和 Subagent 输出保留在所属会话下,分页续读不启动新命令。
会话释放先停止活动工作,再删除输出;子会话结束不删除父会话拥有的 Subagent 结果。
@@ -35,7 +34,6 @@
| 维度 | 值 |
| --- | --- |
| Runtime | `model`、`opencode`、`continue`、`deepseek-harness` |
| 工作模式 | `ask`、`execute` |
| 执行空间 | `local`、`ssh` |
| 本机 Shell | `available`、`unavailable` |
| Subagent 深度 | `0`、`1` |
@@ -44,21 +42,20 @@
## 3. 工具可见性决策表
| Runtime | 模式 | 工作区读/搜索 | 补丁 | `process_execute` | `subagent_delegate` |
| Runtime | 执行条件 | 工作区读/搜索 | 补丁 | `process_execute` | `subagent_delegate` |
| --- | --- | --- | --- | --- | --- |
| `model` | Ask | 提供 | 隐藏 | 隐藏 | 提供,只读继承 |
| `model` | Execute,本机 Shell 可用 | 提供 | 提供 | 提供 | 提供,Execute 继承 |
| `model` | Execute,本机 Shell 不可用 | 提供 | 提供 | 隐藏并报告诊断 | 提供 |
| `model` | 本机 Shell 可用 | 提供 | 提供 | 提供 | 按既有委派设置提供 |
| `model` | 本机 Shell 不可用 | 提供 | 提供 | 隐藏并报告诊断 | 按既有委派设置提供 |
| `model` | SSH,远端后端未实现 | 当前入口不开放 | 当前入口不开放 | 隐藏 | 隐藏 |
| 其他 Runtime | 任意 | 不注入本功能工具 | 不注入 | 不注入 | 不注入 |
工具清单隐藏不是最终授权边界。`callTool` 仍须重新验证 Runtime、工作模式、执行空间和父请求
工具清单隐藏不是最终授权边界。`callTool` 仍须重新验证 Runtime、执行空间和父请求
是否活动,以拒绝旧快照或伪造调用。
FR-4 / FR-12 / US-B0:Ask 工作区搜索和读取不依赖知识库、联网搜索或委派开关,不需要
切到 Execute 或额外批准。MCP 服务器失败也不改变本机只读工具的可用性。
FR-4 / FR-12 / US-B0:工作区搜索和读取不依赖知识库、联网搜索或委派开关,不需要
额外批准。MCP 服务器失败也不改变本机工具的可用性。
`output_read` 在直连模型拥有搜索、进程或 Subagent 服务时提供,Ask/Execute 均可用,不取决于
`output_read` 在直连模型拥有搜索、进程或 Subagent 服务时提供,不取决于
本轮 Shell 可用性、是否允许再次委派或委派深度。读取只接受当前会话的句柄;其他 Runtime
不注册该工具,调用边界同样拒绝。
@@ -96,7 +93,7 @@ validating
| 不存在或不是目录 | 拒绝 |
| 当前工作区身份在验证期间变化 | 拒绝本次调用 |
工作区提供默认目录和相对路径基准,不是 Execute 命令的文件系统边界。单次命令选择其他
工作区提供默认目录和相对路径基准,不是命令的文件系统边界。单次命令选择其他
工作目录不会修改父请求绑定的项目或工作区,也不会影响后续未指定 `cwd` 的命令。
## 6. Shell 选择
@@ -143,23 +140,18 @@ type SubagentDelegateResult = {
## 8. 权限与能力继承
| 父请求 | 子级模式 | 子级能力 |
| --- | --- | --- |
| Ask | Ask | 父级只读工具的子集,不含进程、写入和再次委派 |
| Execute | Execute | 父级已启用工具的子集,包含本机进程能力,不含再次委派 |
- 子级不能请求更高模式。
子级继承父级已启用工具的子集,包含可用的本机进程能力,不含再次委派;不保存模式字段。
- 子级使用父请求开始时的工具与能力快照;运行中设置变化不重写快照。
- 子级调用 MCP、浏览器或知识能力时继续经过这些能力已有的作用域和生命周期边界。
- FR-5 / FR-7 / US-C1:Execute 子级使用父请求的浏览器标签页,不另开标签页或改变所有权;
- FR-5 / FR-7 / US-C1:子级使用父请求的浏览器标签页,不另开标签页或改变所有权;
子级结束后父模型仍可继续操作该页,父请求继续负责其使用租约。
- Execute 子级不弹出额外的“Subagent 执行授权”,也不获得父级没有的 MCP 分配。
- 子级不弹出额外的“Subagent 执行授权”,也不获得父级没有的 MCP 分配。
## 9. 并发和顺序
- 全局编程 Subagent 最大并发为 3,额外请求进入有界队列。
- 单个父模型的工具调用仍按直连模型现有顺序结算,不为本功能改写全部工具并发语义。
- 不同父请求的 Subagent 可以并发;共享工作区的并发写入属于用户已授权的 Execute 行为,
- 不同父请求的 Subagent 可以并发;共享工作区的并发写入使用当前账号权限,
活动记录必须保留实际顺序和结果。
- 首版不自动创建工作树、锁文件或回滚层。出现真实冲突时由父模型读取当前工作区事实并处理。
@@ -198,7 +190,7 @@ attempt
| 领域 | 结论 |
| --- | --- |
| Runtime 隔离 | 完整,工具只注入 `model` |
| Ask/Execute | 完整,清单过滤和调用边界双重检查 |
| 统一执行 | 清单与调用均校验实际能力、资源归属和生命周期,无产品模式 |
| 本机三平台 | 完整,统一契约和平台 Shell 决策已定义 |
| Subagent 递归 | 完整,深度固定为 1 |
| 取消与失败 | 完整,父子和进程传播及优先级已定义 |
+24 -26
View File
@@ -18,7 +18,7 @@ GoodBuddy 直连模型已经可以读取、列出和写入工作区文本,使
Git 或项目脚本,因此无法独立验证结果。
本功能设计时,GoodBuddy 已有面向用户专家协作的只读 Subagent,但直连模型尚不能在工具
循环中主动委派编程子任务。当前专家协作已继承父请求 Ask/Execute 模式并使用本机直连模型
循环中主动委派编程子任务。当前专家协作继承父请求能力范围并使用本机直连模型
工具,与本功能的 `subagent_delegate` 仍为不同入口。OpenCode、Continue 和 DeepSeek Harness 一般已经提供
Shell、Agent 或 Task 能力,再向它们注入 GoodBuddy 同名工具只会造成重复入口和语义冲突。
@@ -30,10 +30,10 @@ Shell、Agent 或 Task 能力,再向它们注入 GoodBuddy 同名工具只会
## 2. 产品目标
1. 让直连模型在 Execute 模式完成“读取代码 → 修改 → 运行 → 根据结果修正”的闭环。
1. 让直连模型完成“读取代码 → 修改 → 运行 → 根据结果修正”的流程。
2. 在 Windows、macOS 和 Linux 使用同一个模型工具契约,不要求模型调用平台专属工具名。
3. 让直连模型可以把独立工作委派给临时 Subagent,并取得有界、可核验的结果。
4. 保持 Ask 只读、Execute 完整授权、执行空间、取消、有界输出和进程回收语义不变。
4. 使用当前账号权限,保留执行空间、取消、有界输出和进程回收语义。
5. 不向已有原生执行能力的 Agent Runtime 重复注入 GoodBuddy 进程或 Subagent 工具。
6. 使用安装包内置 ripgrep 提供跨平台一致的快速代码搜索,不依赖用户预装命令。
@@ -54,7 +54,7 @@ macOS/Linux 改用 `bash_execute`。
| Runtime | GoodBuddy `process_execute` | GoodBuddy `subagent_delegate` |
| --- | --- | --- |
| 直连模型 `model` | Execute 提供 | Ask/Execute 提供 |
| 直连模型 `model` | 本机 Shell 可用时提供 | 按既有委派设置提供 |
| OpenCode | 不提供 | 不提供 |
| Continue | 不提供 | 不提供 |
| DeepSeek Harness | 不提供 | 不提供 |
@@ -64,25 +64,23 @@ Agent 或插件能力。
### 3.4 Subagent 继承,不扩权
Subagent 使用父请求的项目、执行空间、工作模式、直连模型连接和已启用能力快照:
Subagent 使用父请求的项目、执行空间、直连模型连接和已启用能力快照:
- Ask 父请求只能创建 Ask Subagent。
- Execute 父请求创建 Execute Subagent。
- 子级不能切换父请求绑定的项目、工作区、Runtime、模型连接或工作模式;Execute 命令的
- 子级不能切换父请求绑定的项目、工作区、Runtime 或模型连接;命令的
单次工作目录选择遵循 FR-3。
- 子级不再看到 `subagent_delegate`,首版最大委派深度固定为 1。
- 子任务不是新的顶层 Task 或 Conversation,结果先返回父模型。
### 3.5 不新增权限档位
Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完整授权。进程和 Subagent 不增加
工具使用当前执行空间账号权限。进程和 Subagent 不增加
“受控执行”“仅测试”“可信命令”或逐命令风险等级,也不重复弹出第二套确认。
### 3.6 精简工作区工具
直连模型使用 `workspace_rg` 发现文件和搜索内容,使用 `workspace_read_text` 按行分页读取,
使用 `workspace_apply_patch` 新增、修改或删除文本文件。新请求不再注入低效的单层目录列表和
整文件覆盖工具。Ask 提供前两个只读工具;Execute 额外提供补丁工具。
整文件覆盖工具。正常本机工具请求提供这三个工具。
## 4. 功能范围
@@ -107,7 +105,7 @@ Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完
- 向 OpenCode、Continue 或 DeepSeek Harness 注入本功能工具。
- 递归 Subagent、长期运行的自治 Agent、Workflow、Hook 或恢复未完成子任务。
- 新的权限矩阵、逐命令批准、沙箱档位或命令白名单。
- 自动安装项目依赖;模型可以在 Execute 中显式运行项目已有的安装命令。
- 自动安装项目依赖;模型可以按用户请求显式运行项目已有的安装命令。
## 5. 功能需求
@@ -134,18 +132,18 @@ Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完
- 本机执行只能发生在当前桌面执行空间。
- 当前请求绑定 SSH 执行空间且没有远端进程后端时不注册工具,绝不在桌面目录回退执行。
### FR-4 Ask/Execute
### FR-4 统一执行
- Ask 工具清单不包含 `process_execute`。
- Runtime 边界必须再次拒绝 Ask 发起的伪造命令调用。
- Execute 命令使用 GoodBuddy 客户端当前用户权限,不声称 Runtime OS 沙箱。
- 消息通道的直连模型 Execute 继续服从该通道既有工具策略。
- `process_execute` 按本机 Shell 与执行空间可用性提供,不依赖产品模式。
- Runtime 边界拒绝未注册工具或失效请求的伪造调用。
- 命令使用 GoodBuddy 客户端当前用户权限,不声称 Runtime OS 沙箱。
- 消息通道沿用已启用能力和资源范围,不保留 `toolApproval` 策略。
### FR-5 Subagent 委派
- `subagent_delegate` 接受一个清晰的任务说明,不要求用户预先创建专家。
- 子级默认使用父请求选择的直连模型连接。
- 子级继承父请求的工作模式和能力上限,但不获得再次委派能力。
- 子级继承父请求的能力上限,但不获得再次委派能力。
- 子级最终输出、部分输出、错误、用量和状态返回父请求。
- 父模型负责综合结果并向用户作最终回答。
@@ -162,7 +160,7 @@ Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完
- 命令、Subagent 提示、单次返回的输出预览、队列和并发有界;完整输出不因超过预览容量而丢弃。
- 预览省略内容时明确标记并提供续读位置。模型可用 `output_read` 逐页取得完整日志及
Subagent 结果,包括失败或取消前已取得的部分输出。
- Ask 和 Execute 均可续读当前会话的输出。会话或 Runtime 释放后,临时输出删除,旧句柄失效。
- 可续读当前会话的输出。会话或 Runtime 释放后,临时输出删除,旧句柄失效。
### FR-8 环境和凭据
@@ -199,19 +197,19 @@ Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完
完全不可读;超长单行可使用返回的字节位置继续读取。
- `workspace_apply_patch` 支持一个补丁新增、修改和删除多个 UTF-8 文件;执行前验证全部
路径和 hunk,执行失败时准确报告已完成文件。
- 搜索和读取在 Ask/Execute 均可用;补丁只在 Execute 可用,Main 调用边界再次检查模式。
- `workspace_rg` 直接调用随包 ripgrep;Ask 限于工作区内只读搜索,Execute 使用当前账号权限。
- 搜索、读取与补丁按已注册目录提供,Main 调用边界检查资源与请求归属。
- `workspace_rg` 直接调用随包 ripgrep,使用当前账号权限。
具体参数与模式边界见[工具契约](./technical-design.md#工具输入与执行)。
- 工作区工具不限制文件总大小、补丁总大小、搜索表达式长度、glob 数量或模型请求的结果
数量。单次返回按上下文容量截断或分页;读取结果提供续读位置,搜索完整输出通过 `output_read` 续读。
## 6. 产品验收
- [ ] Windows 直连模型 Execute 使用 PowerShell 创建文件、运行测试并读取退出码。
- [ ] macOS/Linux 直连模型 Execute 使用 Bash/Sh 完成同一工作流。
- [ ] Ask 中模型看不到进程工具,伪造调用也在 Main 边界失败。
- [ ] Windows 直连模型使用 PowerShell 创建文件、运行测试并读取退出码。
- [ ] macOS/Linux 直连模型使用 Bash/Sh 完成同一工作流。
- [ ] 未注册工具的伪造调用在 Main 边界失败。
- [ ] 直连模型可以委派编程子任务,子级运行命令并把结果返回父模型。
- [ ] Ask Subagent 保持只读;Execute Subagent 不增加第二套批准。
- [ ] Subagent 继承能力范围,不增加第二套批准。
- [ ] Subagent 不能递归委派,不能切换父请求工作区、模式或模型。
- [ ] 切换到 OpenCode、Continue 或 DeepSeek Harness 后不出现本功能工具。
- [ ] 非零退出、超时、取消和输出截断均准确展示。
@@ -221,7 +219,7 @@ Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完
- [ ] 子任务不进入 Task Center,不创建独立可导航 Conversation。
- [ ] TLS 建连重置和请求超时在未输出内容时最多自动重试 3 次,成功后继续原请求。
- [ ] 重试等待可取消,已显示部分内容的失败不自动重放。
- [ ] Ask 可使用 ripgrep 搜索和分页读取,但不能应用补丁。
- [ ] Execute 可通过一个补丁新增、修改和删除多个工作区文件,并运行测试验证。
- [ ] 可使用 ripgrep 搜索和分页读取,无模式限制。
- [ ] 可通过一个补丁新增、修改和删除多个工作区文件,并运行测试验证。
- [ ] Windows、macOS、Linux 的 x64/arm64 安装包均携带对应 ripgrep,且无需系统预装。
- [ ] 全量测试、类型检查、Lint 和生产构建通过。
+18 -1
View File
@@ -10,7 +10,24 @@
| 日期 | 2026-09-09 |
| 关联入口 | [直连模型 Agent 能力](./README.md) |
## 当前结论
## 2026-10-05 工具执行异常回传
- 普通工具执行异常也返回模型,保留失败活动与错误详情,由模型决定后续行动;规则见
[失败恢复](./technical-design.md#读取与搜索失败恢复)。请求取消与 `AbortError` 仍停止循环。
- 新增三协议回归使用真实 Provider、进程服务和文件系统:无效 cwd 产生 `ENOENT`,
下一次模拟模型请求收到对应失败结果,再调用同一工具执行真实命令成功。另覆盖错误详情
有界处理和取消;未使用真实外部模型,不作为桌面 UI 或真实模型自主决策验收。
- 修复验证中发现 `DOMException` 不一定满足 `instanceof Error`,改为按错误名称识别取消;
取消测试在意外后续模型请求时立即失败,避免模拟响应无限重复耗尽内存。
- `model-runtime.test.ts`、`model-tool-provider.test.ts`、`direct-model-process-service.test.ts`:
161 项通过、1 项跳过。`npm run typecheck`、`npm run lint` 通过。
- 全量 `npm test`:6,230 项通过、87 项跳过、1 项失败;失败为
`obsidian-electron.test.ts:29` 要求临时目录在仓库外,与本次按仓库规范设置
`TEMP`/`TMP` 到仓库 `temp/` 冲突。未修改该测试,全量不能记为通过。
- 托管 SSH 使用远端 OpenCode/Continue 的工具循环,不运行本次修改的 `ModelAgentRuntime`;
不涉及 gbagent 生产路径。临时目录创建、保留和清理逻辑未修改。
## 既有本机验收结论
本机生产路径已经完成源码接线:直连模型 Execute 可运行平台 Shell,Ask/Execute 均可按
父模式委派单层编程 Subagent;长输出可通过 `output_read` 续读。OpenCode、Continue、
@@ -21,8 +21,8 @@
- `WorkspaceAccess` 的本机和远端工作区抽象。
- 跨平台子进程树终止辅助函数。
- 面向用户专家协作的 `SubagentScheduler` 和状态事件。专家使用具备已启用本机能力的
直连模型 Runtime,继承父请求 Ask/Execute 及 authorizer,工具事件记入子任务;
Ask 保持只读,不把这些本机工具表述为远程 OpenCode 子会话。
直连模型 Runtime,继承父请求执行空间与能力范围,工具事件记入子任务;
不把这些本机工具表述为远程 OpenCode 子会话。
- OpenCode、Continue 和 DeepSeek Harness 各自的 Shell/Agent 能力。
- 直连模型 `process_execute`、`subagent_delegate` 和 `output_read`。
- 不创建顶层 Task/Conversation 的编程 Subagent actor 与活动归并。
@@ -66,11 +66,8 @@ type BuiltinModelToolSummary = {
}
```
`process_execute` 标记为 `write`,只在 Execute 清单中出现。
`subagent_delegate` 的实际访问级别由父模式决定:
- Ask 清单中提供只读委派定义。
- Execute 清单中提供继承 Execute 的定义。
`process_execute` 标记为 `write`,按本机进程能力提供。
`subagent_delegate` 继承父请求实际能力,不按读写属性过滤:
- `callTool` 不依赖静态 `access` 单独授权,必须检查父请求上下文。
工具总数和 schema 总字节继续计入直连模型现有 100 个工具与 512 KiB 上限。
@@ -88,7 +85,7 @@ type BuiltinModelToolSummary = {
覆盖跨多个页面的超长单行。
- `workspace_apply_patch` 接受一个 `*** Begin Patch` 文本,支持 `Add File`、`Update File`
和 `Delete File`。全部操作先解析并验证;更新文件保留原换行和权限位,单文件原子替换。
- Ask 只注册前两个只读工具;Execute 注册全部三个工具。旧的目录列表和整文件覆盖不再进入
- 本机正常工具请求注册全部三个工具。旧的目录列表和整文件覆盖不再进入
新工具清单。
- 输入和目标文件不设固定总量上限。单次工具结果仍遵守直连模型上下文边界;读取返回续读
位置,搜索预览省略的内容通过 `output_read` 续读。
@@ -101,13 +98,14 @@ type BuiltinModelToolSummary = {
`EISDIR`、`EACCES`、`EPERM`、`ELOOP`、`ENAMETOOLONG` 等已列明的文件系统错误。
Runtime 向模型返回 `{ ok: false, recoverable: true, error, nextAction }` 的 JSON 文本工具结果,
工具活动记为 `recoverable`,继续工具循环,由模型修正路径、分页参数、glob 或搜索表达式;
提示不得原样重复失败参数。取消、远端断线、失效工作区、缺失 rg 可执行文件及未分类的内部
错误继续向外传播,不转为可恢复结果。
提示不得原样重复失败参数。其他工具执行异常(包括进程启动或输出文件的 `ENOENT`、
远端断线、失效工作区、缺失 rg 可执行文件)由 Runtime 返回 `{ ok: false, error }`,
工具活动记为 `failed`,错误详情使用已有有界处理并保留 cause 信息。失败结果继续进入
下一次模型请求,由模型决定重试、调整方法或说明失败;不自动重放工具,也不增加重试次数限制。
Anthropic 的失败结果同时设置 `is_error: true`。请求取消及 `AbortError` 仍向外传播并停止循环。
`workspace_read_text` 仍只接受工作区内相对路径。搜索在 Ask 中检查 cwd、搜索路径、模式文件
和忽略文件位于工作区内,并阻止 `--pre`、`--hostname-bin`、`--follow/-L`、`--search-zip/-z`,
避免只读调用启动外部命令或跟随链接越界。Execute 原样接受 rg 参数及账号可访问的路径,
不增加逐工具授权。两种模式均禁用外部 rg 配置,不提升系统权限。
`workspace_read_text` 仍只接受工作区内相对路径。搜索原样接受 rg 参数及账号可访问的路径,
不增加逐工具授权;保留 `--no-config` 默认参数、原生退出码、取消和分页,不提升系统权限。
ripgrep 结果按退出状态处理:
@@ -157,7 +155,7 @@ const processExecuteInputSchema = z.object({
`..` 和符号链接,不限制目标位于工作区内。工具 schema 向模型明确这一规则。
- 首版不接受 `env`、`stdin`、`background`、`pty`、`shell` 或任意 executable 参数。
- Shell 选择由执行后端决定,防止模型绕过平台契约启动另一套受管接口。
- 命令字符串仍可以调用当前账号本来有权运行的程序;Execute 不增加命令白名单。
- 命令字符串仍可以调用当前账号本来有权运行的程序,不增加命令白名单。
### 4.2 结果
@@ -203,7 +201,7 @@ type ProcessExecuteResult = {
位于 UTF-8 延续字节的输入 cursor、负数、越界和未知字段均拒绝。
Provider 根据 `rg:`、`process:` 或 `subagent:` 前缀选择服务,以当前 `conversationId` 校验所有者,
不接受模型另传 owner。Ask/Execute 均可用,工具总数为此预留一个槽位;不要求本轮能够启动
不接受模型另传 owner。工具总数为此预留一个槽位;不要求本轮能够启动
命令或再次委派。分页结果直接 JSON 序列化,不进行二次文本裁剪,以免 cursor 与内容不一致。
32 KiB 的页面即使全部需要 JSON 转义,也能保留在现有工具结果容量内。
@@ -306,7 +304,6 @@ type DirectModelSubagentContext = {
childRunId: string
projectId?: string
conversationId: string
workMode: 'ask' | 'execute'
}
```
@@ -317,8 +314,8 @@ type DirectModelSubagentContext = {
- 子级复用父请求已授权能力快照和知识范围。
- 子级复用父 Provider;Main 写入的 `delegationDepth=1` 同时在清单和调用边界过滤
`subagent_delegate`。
- Ask 子级使用 Ask 清单;Execute 子级使用 Execute 清单。
- Execute 子级经 `ModelSubagentRequestContext` 继承父 `browserTabId` 和浏览器所属
- 子级使用父请求已启用的工具清单,不注册再次委派。
- 子级经 `ModelSubagentRequestContext` 继承父 `browserTabId` 和浏览器所属
`browserConversationId`。后者只在 Main 内部请求与工具上下文中传递:浏览器操作使用父
Conversation,历史与分页输出仍使用子 Conversation。子级不新建标签页或申请使用租约,
结束时只释放自己的临时会话,父标签页和租约仍由父请求生命周期管理。
@@ -354,7 +351,6 @@ type ModelToolCallContext = {
conversationId: string
requestId: string
runtimeTarget: 'model'
workMode: 'ask' | 'execute'
executionSpaceIdentity: string
delegationDepth: 0 | 1
knowledgeCapabilityToken?: string
@@ -363,19 +359,17 @@ type ModelToolCallContext = {
要求:
- `listTools` 根据模式、执行空间能力和深度生成快照。
- `getApproval` 不为两个工具创建第二套逐次确认;沿用父 Execute 授权和现有通道工具策略。
- `callTool` 再次检查 `runtimeTarget`、模式、执行空间 identity、深度和请求是否活动。
- `listTools` 根据执行空间能力和深度生成快照。
- 工具调用不进入一般审批等待,通道也不保留 `toolApproval` 策略。
- `callTool` 再次检查 `runtimeTarget`、执行空间 identity、深度和请求是否活动。
- `releaseConversation` 取消并释放该会话拥有的浏览器、Subagent 和进程。
- `dispose` 等待有界清理,不因模型或子进程不响应而阻塞应用退出。
### 8.1 Ask 工具循环
### 8.1 工具循环
普通 Ask 请求进入工具循环,不再依赖知识、联网搜索或 Subagent 是否启用。Runtime 对
`workspace_rg`、`workspace_read_text`、`output_read` 三个明确只读工具免去 Ask authorizer
调用,避免生产 authorizer 的默认拒绝使已列出的只读工具无法执行。Execute 仍沿用既有
authorizer;补丁、进程和其他写入不因此获准。Provider 按清单和调用边界继续校验模式,
浏览器工具不加入 Ask 免审批集合。内部上下文摘要使用 `noModelTools`,保留无工具问答路径。
正常工具请求不依赖知识、联网搜索或 Subagent 开关来进入工具循环。Provider 按实际注册
目录和调用边界检查能力与资源,不保留模式判断或只读免审批名单。内部上下文摘要使用
`noModelTools`,保留无工具问答路径;模型不支持工具时仍可普通文本回答。
### 8.2 MCP 发现与调用
@@ -476,7 +470,7 @@ Runtime;GoodBuddy 不再额外保留最后 20 条或限制为 128,000 字符
### 12.1 单元测试
- 工具可见性矩阵和 Runtime target 过滤。
- Ask 伪造进程调用拒绝。
- 未注册或无对应执行空间的伪造进程调用拒绝。
- Windows PowerShell、POSIX Bash/Sh 选择和参数。
- cwd 默认、相对子目录、绝对路径、工作区外目录、指向外部的符号链接和不存在的目录。
- exit 0、非零退出、spawn 失败、超时、取消和输出截断。
@@ -505,7 +499,7 @@ Runtime;GoodBuddy 不再额外保留最后 20 条或限制为 128,000 字符
使用一个最小真实直连文本模型请求:
1. Execute 创建一段有缺陷的小程序。
1. 在专用测试目录创建一段有缺陷的小程序。
2. 调用进程工具运行聚焦测试并观察失败。
3. 修改程序并再次运行到通过。
4. 委派一个 Subagent 检查实现或补充测试。
@@ -537,7 +531,7 @@ GoodBuddy Agent 或桌面到 Agent 生产路径,因此不要求远端 Host 验
1. Shared 工具摘要、schema 和 Runtime target 过滤。
2. 本机 Shell 探测、进程执行、输出边界和进程树取消。
3. `ModelToolProvider` Execute 接入及 UI 活动。
3. `ModelToolProvider` 工具接入及 UI 活动。
4. 编程 Subagent 服务、过滤 Provider、事件和用量。
5. 设置能力目录与状态诊断。
6. 三平台真实命令、真实模型和完整项目验证。
@@ -40,7 +40,7 @@ Job、Subjob 和 Run 继续是内部对象,不创建树形 UI、独立路由
进程执行
使用当前用户权限运行 PowerShell、Bash 或 Sh 命令,默认在工作区执行,也可指定其他工作目录。
编程 Subagent
将聚焦任务委派给同一模型连接;继承当前工作模式和工作区。
将聚焦任务委派给同一模型连接;继承当前能力范围和工作区。
```
要求:
@@ -107,7 +107,7 @@ GoodBuddy 服务崩溃。
```text
编程 Subagent
检查认证模块的测试失败并提出修复
正在运行 · 直连模型 · Execute
正在运行 · 直连模型
```
终态:
@@ -119,12 +119,11 @@ GoodBuddy 服务崩溃。
活动块不提供单独的“批准”“提高权限”或“继续递归”按钮。用户取消父请求即可取消子级。
## 5. Ask/Execute 表达
## 5. 执行能力表达
- Ask 中不显示不可用的进程按钮,因为模型工具没有手动调用入口。
- Ask Subagent 活动明确显示“只读”。
- Execute 活动显示“当前用户权限”,不声称 OS 沙箱或工作区隔离。
- 如果用户从 Ask 切到 Execute,只有下一条请求使用新模式;运行中的 Subagent 不切换。
- 模型工具没有手动调用入口,不显示模式按钮或子代理模式徽标。
- 工具使用当前用户权限,不声称 OS 沙箱或工作区隔离。
- 能力配置变更按既有生命周期作用于后续请求,运行中的 Subagent 保留请求归属。
## 6. 错误与通知
@@ -31,7 +31,7 @@
关联:FR-2、FR-3、FR-4。
Given 当前项目位于 Windows 本机且选择直连模型 Execute,When 模型调用
Given 当前项目位于 Windows 本机且选择直连模型,When 模型调用
`process_execute`,Then GoodBuddy 使用可用 PowerShell 在项目目录运行命令,并返回
Shell、目录、退出码、输出和耗时。
@@ -46,7 +46,7 @@ Bash,Bash 不可用时使用 Sh,工具契约保持不变且实际 Shell 明
关联:FR-3。
Given 当前请求为本机 Execute,When 模型传入绝对路径或相对工作区路径,Then 命令在
Given 当前请求在本机运行,When 模型传入绝对路径或相对工作区路径,Then 命令在
解析后的目录执行,包括工作区外目录和符号链接目标,And 后续省略 `cwd` 时仍使用原工作区。
When 目标不是目录或不存在,Then 命令不启动并返回准确错误。
@@ -86,15 +86,15 @@ Given 直连模型在 TLS 建连、网络传输或单次请求超时时失败且
关联:FR-4、FR-12。
Given 用户在 Ask 或 Execute 中处理本机项目,When 模型需要定位代码或读取大文件,Then
Given 用户处理本机项目,When 模型需要定位代码或读取大文件,Then
它可以使用随包 ripgrep 搜索内容或列出文件,并按行分页读取命中位置,不要求用户预装 `rg`。
Given Ask 未启用知识库、联网搜索或编程委派,Then 工作区搜索和读取仍可使用,不要求额外批准。
Given 未启用知识库、联网搜索或编程委派,Then 工作区搜索和读取仍可使用,不要求额外批准。
### US-B1 修改后运行验证
关联:FR-2、FR-4。
Given 用户选择 Execute 并要求修复代码,When 直连模型修改文件,Then 它可以运行聚焦测试
Given 用户要求修复代码,When 直连模型修改文件,Then 它可以运行聚焦测试
或检查命令,并根据真实结果继续修改,直到给出完成或阻塞结论。
### US-B2 使用本机工具环境
@@ -109,15 +109,15 @@ Given 用户选择了 GoodBuddy 托管或自定义 Node/Python,When 新命令
关联:FR-2、FR-9。
Given 项目缺少所需命令或依赖,When Shell 返回错误,Then 工具保留退出码和错误文本,
And 模型说明缺失项或在 Execute 中采取用户要求的安装步骤,不把环境缺失报告为模型故障。
And 模型说明缺失项或采取用户要求的安装步骤,不把环境缺失报告为模型故障。
### US-B4 使用补丁修改文件
关联:FR-4、FR-12。
Given 用户选择 Execute,When 模型提交包含新增、修改或删除操作的工作区补丁,Then
Given 用户要求修改文件,When 模型提交包含新增、修改或删除操作的工作区补丁,Then
GoodBuddy 在写入前验证全部路径和上下文,逐文件原子写入,并返回实际修改的文件列表。
Given 用户选择 Ask,Then 补丁工具不出现在清单中且伪造调用会被拒绝。
Given 请求的执行空间不支持补丁,Then 工具不出现在清单中且伪造调用会被拒绝。
## 4. Epic C:Subagent 委派
@@ -126,16 +126,16 @@ Given 用户选择 Ask,Then 补丁工具不出现在清单中且伪造调用
关联:FR-5、FR-6。
Given 父直连模型正在处理复杂请求,When 它调用 `subagent_delegate` 并给出独立任务,
Then 子级使用同一项目、模型连接和 Execute 能力完成工作,And 最终结果返回父模型综合。
Then 子级使用同一项目、模型连接和已启用能力完成工作,And 最终结果返回父模型综合。
Given 父请求已有浏览器标签页,When 委派任务需要浏览器,Then 子级使用同一页;子级完成后,
父模型仍可继续操作该页,不因子会话清理而关闭父页。
### US-C2 Ask 中只读委派
### US-C2 委派能力继承
关联:FR-4、FR-5。
Given 父请求为 Ask,When 模型委派分析任务,Then 子级保持 Ask,不能写文件或运行命令,
And 不因 Subagent 存在而扩大权限。
Given 父请求已有能力范围,When 模型委派分析任务,Then 子级不获得未启用能力,
And 遵循用户明确的仅分析要求,不引入产品只读模式。
### US-C3 禁止递归委派
@@ -179,7 +179,7 @@ GoodBuddy 不注入 `process_execute` 或 `subagent_delegate`,And Runtime 原
关联:FR-1。
Given 会话从直连模型切换为 Agent Runtime,When 下一次请求开始,Then 新请求只使用目标
Runtime 的能力;切回直连模型时重新获得符合工作模式的直连工具。
Runtime 的能力;切回直连模型时重新获得已注册的直连工具。
### US-D3 不在错误设备执行
@@ -192,5 +192,5 @@ Given 当前执行空间是 SSH 且远端进程后端尚不可用,When 解析
关联:FR-1、FR-4。
Given 消息通道项目选择直连模型,When 合法消息触发 Ask 或 Execute,Then 使用与桌面会话
相同的工具矩阵和通道既有工具策略,不维护第二套 Shell 能力配置。
Given 消息通道项目选择直连模型,When 合法消息触发请求,Then 使用与桌面会话
相同的工具矩阵和能力分配,不维护第二套 Shell 能力配置或通道审批策略。
@@ -52,7 +52,9 @@
每次任务冻结配置,沿用原文件大小、页数、文本输出与取消边界;HTTP 增加有界响应读取和
独立请求超时。多页请求超时不能冒称单页耗时。HTTP 状态、服务 `errorCode`、任务取消及
图片解码错误分别保留,错误正文脱敏后限制长度。没有自动重试或 Provider 轮换。
图片解码错误分别保留。错误展示读取结构化 `errorMsg` 和 `logId`,读取与显示限制见
[生产实现](./implementation.md#解析与设置),不展示原始响应正文。PPTX 错误按实际来源命名并附图片位置。
没有自动重试或 Provider 轮换。
## 高级参数
@@ -114,13 +116,11 @@ confidence 改为可选,保持本地返回原值;多块内容组成逐页 se
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>/
@@ -153,7 +153,7 @@ confidence 改为可选,保持本地返回原值;多块内容组成逐页 se
也需验证同一规则。采用暂存、现有事务和按数据库引用核对清理,不新增恢复引擎。
测试解析使用任务临时目录,结束预览且无有效引用后清理。知识库正文及 chunks 继续存数据库,
`knowledge-assets` 的 `parsed.md` 为解析正文文件,随同一次结果提交保持一致,不独立修改
`knowledge-assets` 的 `manifest.json` 保存解析正文,随同一次结果提交保持一致,不独立修改
或替代数据库中的检索内容。图片归知识库文档所有,不依赖聊天存活;managed 与 reference
派生文件均存该目录,reference 外部原件保持原路径,现有 managed 原件目录不迁移。
重解析或同步先在本次暂存目录完成新结果与必要资源校验,成功提交替换后清理无引用旧资源;
@@ -5,7 +5,9 @@
## 解析与设置
`DocumentParsingService` 统一服务聊天文档、知识库、成果导入和设置测试。`HttpDocumentOcr`
`DocumentParsingService` 统一服务聊天文档、知识库、旧成果导入 API 和设置测试。成果导入界面
已移除;工作区“导入文件”只复制或上传原件,不解析、不自动加入消息。会话附件仍由下述会话
资产存储管理,不自动复制到项目目录。`HttpDocumentOcr`
在 Main 调用 PaddleOCR-VL,`render-ocr-pdf.ts` 使用 PDF.js 和本机 Canvas 渲染选中页,
不需要本地 OCR 权重。来源未配置时继续原有本地行为;产品没有测试地址默认值。
@@ -24,7 +26,9 @@ HTTP 请求固定返回 Markdown 图片、关闭调试可视化。高级布尔
部署模型能力需以用户样例确认,连接检查只执行 health 和 OpenAPI。
响应流上限 32 MiB,单张解码图上限 12 MiB、4000 万像素,文档文字上限 500 万字符。
请求超时独立于本地单页超时;HTTP 状态和服务错误码保留,凭据和响应正文不出现在错误中。
请求超时独立于本地单页超时;错误保留 HTTP 状态、服务错误码及结构化 `errorMsg` / `logId`。
非成功响应最多读取 8 KiB,消息移除已配置密钥、整理换行后限制为 240 字符,不展示原始响应正文。
PPTX 回退提示按实际 OCR 来源显示,并在图片处理失败时包含幻灯片号与图片序号。
不下载服务返回的图片 URL,不自动重试或切换 Provider。
图片按来源页和页内键解码,以 UUID 替换 Markdown 与 HTML img 引用。同名键跨页不会覆盖。
@@ -40,17 +44,21 @@ HTTP 请求固定返回 Markdown 图片、关闭调试可视化。高级布尔
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>/
```
S04 (2026-10-05): new results store parsed content in `manifest.json` only;
`parsed.md` is no longer written or copied. Released manifests remain readable,
including directories containing the old duplicate. Async storage-host integration
and its remaining composition work are specified in
[Desktop File Storage Integration](../../architecture/desktop-storage-files.md).
会话资产的 `request.json` 保存已转换的请求内容,用于恢复内存 context;原件字节单独保存,
缩放和 JPEG 请求副本不覆盖原图。提取图片文件使用 UUID 无扩展名,MIME 在清单记录。
图片显式 OCR 后生成解析文件。切回原图保留已有结果,并重新进入图片能力校验。
@@ -64,6 +72,9 @@ Main 先保存资产,再保存草稿或接受队列;消息、分支建立独
会话删除、项目删除及清空数据均核对引用,启动回收已无归属的记录和孤立 UUID 目录。
清理失败保留无引用数据库记录供下次重试,不回滚已接受的草稿。
Renderer 仅在活动会话 ID 非空时读取附件草稿;切换到尚无会话的消息通道项目时不发出
`context:get-draft` 请求。切回本地会话后照常恢复附件,Main 继续校验会话 ID 的 UUID 格式。
引用保存按输入顺序去重并查询资产。已有引用的顺序、会话归属均相同,且资产无需从临时目录
转存时,直接返回,不重复开启写事务或删除、插入引用。空引用同样适用;顺序、归属或附件
发生变化时仍按原事务更新,临时资产仍须转存。
+2 -2
View File
@@ -103,7 +103,7 @@ PDF、DOCX、PPTX 和 XLSX 的解析结果保留章节定位及全文,不在
### 3.3 本地优先
- 文本层和本地 OCR 均在设备上处理;
- 本地处理不因 Ask 或 Execute 模式改变;
- 本地处理遵循解析配置,不依赖产品工作模式;
- OCR 来源必须在“本地模型 / 远程服务”之间明确选择;
- 配置并保存远程服务即表示用户选择该处理路径,不再增加逐场景授权;
- API 密钥只能保存在主进程加密设置中;
@@ -451,7 +451,7 @@ FR-H8 至 FR-H12 的资源与预览要求针对已解析文档及其产物,也
不接入 MinerU、旧 Office 转换、
DOCX/XLSX 图片 OCR、图片向量检索、服务安装管理或 DOCX 导出。服务接受 PNG 的探测结果
也不改变聊天截图目前作为图片附件的行为。HTTP 上传路径不因 Ask/Execute 模式改变;
也不改变聊天截图目前作为图片附件的行为。HTTP 上传路径沿用独立上传许可;
沿用第 3.3 节的来源选择与 Main 凭据边界,不增加逐文档授权。
不增加完整配置快照、结果版本历史或通用恢复系统;仅记录本次结果需要的来源、选项和资源关系。
@@ -2,6 +2,29 @@
## 状态
2026-10-04:HTTP OCR 故障复核见下节。历史成功记录不能代表服务当前可用。
## 2026-10-04 HTTP OCR 故障复核
PPTX 自动解析的异常分支原来将所有来源显示为“本地 OCR 不可用”,已改为实际来源,
并补充幻灯片与图片位置。HTTP 客户端原来丢弃非成功响应正文,现在保留有界结构化错误
消息及日志 ID,规则见 [生产实现](./implementation.md#解析与设置)。未切换 Provider,
自动聊天回退和严格索引失败规则保持不变。本次改动位于桌面 Main 文档解析链路,不改变
Agent 或远端 Runtime 执行协议。
使用合成内容对已配置测试服务执行 14 次请求(3 GET、11 POST):health 前后均为 200,
普通文字图 PNG、JPEG、透明文字 logo 和横幅均为 500;空白图和无文字小图返回 200,
但没有文字或图片结果。关闭版面检测、关闭返回图片、保留全部标签均未消除文字图的 500。
单独开启方向校正或去畸变返回 `the input params for model settings are invalid!`。
普通文字 PNG 的服务日志 ID 为 `050320f8-d515-4636-99ca-c422af0a61b4`,
JPEG 为 `0dccbb31-2026-42db-95a4-4e333fe73526`,需服务端日志进一步确认内部异常。
未读取或发送用户 PPTX,不能据此断言该文件的具体失败图片或服务器内部根因。
专项测试 38 项、类型检查、lint 及差异空白检查通过。全量 `npm test` 在 200 秒后超时,
未取得完整结果,不计为全量通过。新增说明按 deai-writing 清单人工复核。
## 历史状态
当前状态以 [2026-09-19 实现与验证](#2026-09-19-实现与验证)、[附件操作布局复验](#2026-09-19-附件操作布局复验)、[全局浮层复验](#2026-09-19-全局浮层复验)及[空正文与草稿恢复修正](#2026-09-19-空正文与草稿恢复修正)为准。
以下至“菜单核对检查”的内容是实施前历史记录,保留原日期与原调用数。
@@ -13,7 +13,7 @@
| 相关基线 | [长期助手路线图](../../roadmap/long-term-assistant-roadmap.md)、[统一界面设计系统](../../../UI-DESIGN.md) |
本文定义 GoodBuddy 中类似自然通话的全双工实时语音能力,包括本地与云端语音引擎、
音频平面、会话状态、打断语义、工具审批、数据留存、失败恢复、跨平台交付和验收指标。
音频平面、会话状态、打断语义、工具与结构化提问、数据留存、失败恢复、跨平台交付和验收指标。
本文所称“支持本地与云端”是指用户可以显式配置并选择不同语音引擎,不代表系统可以在
它们之间自动切换。**GoodBuddy 不设计静默降级。**
@@ -38,7 +38,7 @@
控制、Provider Adapter、工具权限、持久化和资源回收。
7. 音频帧不进入普通 `AgentEvent` 和聊天消息持久化通道。默认只保存最终文本、会话状态和
有界诊断,不保存原始录音。
8. 语音不能成为新的授权通道。Ask 继续只读,Execute 的工具调用继续经过现有审批控件。
8. 语音沿用文本请求的统一执行规则与能力范围,不引入产品工作模式或一般工具审批。
---
@@ -51,7 +51,7 @@ GoodBuddy 当前已经具备:
- 将完整录音重采样为 16 kHz PCM 的能力。
- 基于 `sherpa-onnx` 的本地离线识别、模型下载、ZIP 迁移、选择和删除。
- Main 中受信任发送者校验、Zod IPC 输入校验、超时、取消和应用关闭回收。
- `AgentRuntime.run()` 的流式文本、工具事件、审批、取消和会话持久化。
- `AgentRuntime.run()` 的流式文本、工具事件、结构化提问、取消和会话持久化。
- Renderer 中的流式聊天时间线、全局通知和可访问的输入控件。
当前链路仍是:
@@ -89,7 +89,7 @@ GoodBuddy 当前已经具备:
- 用户开口时可以自然打断,助手在很短时间内停止出声并开始听取新内容。
- 用户始终知道当前使用本地还是云端、具体引擎是什么、音频或转写文本会去哪里。
- 本地或云端引擎失败时,用户能看到准确状态并决定下一步,不被系统暗中换模型。
- 语音对话继续拥有文本聊天中的项目、知识库、角色、Ask/Execute、工具审批和历史能力。
- 语音对话继续拥有文本聊天中的项目、知识库、角色、工具、结构化提问和历史能力。
### 3.2 产品目标
@@ -113,7 +113,7 @@ GoodBuddy 当前已经具备:
- 在实时语音失败后自动改成一次性听写、纯文本发送或系统 TTS。
- 默认保存、上传或训练用户原始音频。
- 声音克隆、未成年人声音模仿、电话呼入或多人会议。
- 使用口头“同意”替代工具审批按钮或键盘确认。
- 使用口头“同意”替代必须明确操作的业务确认按钮或键盘确认。
- 绕过当前 Agent Runtime 和权限边界的 Provider 直连工具、MCP 或 Connector。
- 保证所有本地原生语音模型都能在 CPU 或全部六个发布目标上运行。
@@ -151,7 +151,7 @@ GoodBuddy 当前已经具备:
- 原生全双工与模块化全双工之间切换。
- ASR、LLM 或 TTS 组件切换。
- 从音频输入改成仅文本输入,或从语音输出改成仅文本输出。
- 禁用原本声明可用的工具、知识库、角色或 Execute 能力后继续运行。
- 禁用原本声明可用的工具、知识库或角色后继续运行。
- 把原始音频改为上传,或改变云端数据地域和保留策略。
- 采用明显更慢、更低质量或成本不同的路径。
@@ -183,7 +183,7 @@ GoodBuddy 当前已经具备:
### 6.4 产品级适用范围
本契约不仅适用于语音。GoodBuddy 中 Provider、模型、Runtime、数据处理位置、工作模式、
本契约也适用于 GoodBuddy 中 Provider、模型、Runtime、数据处理位置、
权限范围和质量档位等影响隐私、成本或能力的用户选择,都不得被静默替换。
用户明确选择名为“自动”的策略时,系统可以在该策略事先声明的范围内选择,但实际结果和
@@ -204,13 +204,13 @@ GoodBuddy 当前已经具备:
- 当前平台满足该引擎能力要求。
- 麦克风权限可申请。
- 当前 Conversation 没有冲突的活动请求。
- 当前工作模式和引擎能力兼容。
- 当前请求所需能力与引擎能力兼容。
### 7.2 会话界面
活动会话显示一个持续可见的语音控制区:
- 当前状态:准备中、正在听、用户说话、正在思考、助手说话、正在打断、等待审批、
- 当前状态:准备中、正在听、用户说话、正在思考、助手说话、正在打断、等待回答、
正在重连、失败。
- 本地/云端徽标、引擎名称和数据去向。
- 实时用户转写和与播放同步的助手文本。
@@ -234,12 +234,12 @@ GoodBuddy 当前已经具备:
键盘点击“停止说话”与语音 Barge-in 使用相同取消和提交语义。
### 7.4 工具和审批
### 7.4 工具和提问
- Ask 模式继续在 Runtime 边界保持只读。
- Execute 模式的工具调用进入现有 Approval Broker。
- 等待审批时暂停新的助手音频,可播放一次确定性的短提示,例如“需要你确认一个操作”。
- 工具参数、风险、范围和确认操作使用现有可访问审批控件。
- 工具调用由实际注册目录、启用与分配和资源范围决定,无产品模式。
- 不调用已移除的一般工具 Approval Broker。
- 等待结构化回答时暂停新的助手音频,可播放一次确定性的短提示,例如“有一个问题需要你回答”。
- 结构化提问与独立业务确认复用现有可访问控件。
- 麦克风中的“同意”“确认”或相似内容只作为普通用户文本,不构成授权。
- 用户拒绝或取消后,结果作为结构化工具事件返回当前引擎,不私自换模型继续。
@@ -263,7 +263,7 @@ GoodBuddy 当前已经具备:
- Provider、模型和版本。
- 支持的语言。
- 系统级或原生模型全双工。
- 是否支持工具、图像和当前 Ask/Execute 模式。
- 是否支持工具、图像和当前请求所需能力。
- 所需硬件或云端地域。
- 音频和文本的数据去向。
- 安装、已验证、不可用或需要凭据状态。
@@ -322,7 +322,7 @@ Renderer 不持有长期 API Key、不创建本地模型目录、不决定工具
- `voice.stopSession(sessionId)`
- `voice.setMuted(input)`
- `voice.interrupt(input)`
- `voice.respondApproval(...)` 继续复用现有审批接口
- 结构化问题回答复用现有问题接口,不新增 `voice.respondApproval(...)`
- `voice.onEvent(listener)`
音频帧不使用逐帧 `ipcRenderer.invoke`、JSON 或 Base64。Main 通过
@@ -338,7 +338,7 @@ Renderer 不持有长期 API Key、不创建本地模型目录、不决定工具
- 建立所选 Adapter,不执行自动 Adapter 选择。
- 维护输入、输出和生命周期状态。
- 将 Barge-in 传播到 Provider、Agent Runtime、TTS 和播放队列。
- 桥接工具调用、审批、问题和取消。
- 桥接工具调用、问题和取消。
- 只提交已经确认或实际播放的文本。
- 处理超时、重连预算、应用退出、系统休眠和窗口销毁。
- 对错误和诊断执行脱敏与边界限制。
@@ -426,8 +426,6 @@ type VoiceEngineProfile = {
capabilities: {
nativeDuplex: boolean
supportsTools: boolean
supportsAsk: boolean
supportsExecute: boolean
inputLanguages: string[]
outputLanguages: string[]
}
@@ -447,7 +445,6 @@ type VoiceSessionSnapshot = {
profileRevision: string
engineConfigurationDigest: string
runtime?: VoiceRuntimeSnapshot
workMode: 'ask' | 'execute'
inputFormat: VoiceAudioFormat
outputFormat: VoiceAudioFormat
turnDetection: VoiceTurnDetectionConfig
@@ -633,7 +630,7 @@ AudioWorklet
- 权重下载、ZIP 迁移、校验和、磁盘占用和卸载。
本地原生模型不作为六平台默认能力。只有能力检测和一次真实会话测试通过后才允许选择。
缺少结构化工具能力的模型可以声明为 Ask-only;Execute 入口必须阻塞并说明原因,不能暗中
缺少结构化工具能力的模型须明确标为不支持工具,普通问答仍可用;工具需求须说明限制,不能暗中
调用另一个文本模型补齐工具。
### 12.3 云端原生全双工
@@ -696,7 +693,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
- 最终用户文本、已展示的助手消息、`visual-only` Block、实际播放边界和中断状态。
- 中断、失败、取消和完成状态。
- 有界延迟、音频中断和用量指标。
- 工具与审批事件继续进入现有任务和活动记录。
- 工具与问题事件继续进入现有任务和活动记录。
### 14.2 默认不保存
@@ -730,7 +727,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
| Provider 限流或余额不足 | 结束生成并显示原因,不切本地模型 |
| Agent Runtime 失败 | 终止当前轮次,允许重试同一 Runtime,不换连接 |
| TTS 失败 | 当前轮次失败,不静默改成系统 TTS 或仅文本成功 |
| 工具等待审批 | 暂停响应,保留会话;拒绝后把结果返回当前引擎 |
| 等待结构化回答 | 暂停响应,保留会话;回答或跳过后把结果返回当前引擎 |
| 应用退出/窗口销毁 | 取消请求、停止 Track、关闭 Port/PeerConnection、终止 Sidecar |
| 系统休眠/锁屏 | 停止采集和上传;恢复后要求用户显式继续 |
@@ -747,7 +744,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
4. 云端会话在开始前显示 Provider、地域、发送内容和可能费用。
5. API Key 只在 Main 的加密设置或受控环境变量中使用。
6. 临时 Provider 凭据具有最短可行期限、最小能力和单会话作用域。
7. Provider 工具调用必须回到 Main 的白名单、Schema、Ask/Execute 和审批边界。
7. Provider 工具调用必须回到 Main 的注册目录、Schema、能力与资源范围校验。
8. 本地 Sidecar 只监听 loopback,使用短期认证,不开放外部端口。
9. 模型权重按受信任目录、固定来源、大小和 SHA-256 校验,导入 ZIP 防止路径穿越和压缩炸弹。
10. 日志只记录状态、耗时、错误分类和匿名引擎 ID,不记录语音正文和音频。
@@ -797,7 +794,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
- 临时文本、已提交文本和播放位置对齐测试。
- 同一引擎重连预算与超时测试。
- “禁止静默降级”矩阵测试:任何 Adapter、Provider、模型、地域或 Runtime 变化都必须失败。
- Ask 只读和 Execute 审批测试。
- 无模式工具调用、已关闭能力拒绝和内部无工具调用测试。
- 窗口销毁、应用退出、休眠和设备丢失的资源释放测试。
- 不持久化音频、临时 Token 和 Provider 原始正文的数据库测试。
@@ -809,7 +806,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
- 网络抖动、丢包、重复和断开。
- 超前文本、迟到音频和错误播放位置。
- 用户抢话、回声、短噪音和长停顿。
- 工具调用、审批、拒绝和取消。
- 工具调用、结构化问题、跳过和取消。
真实声学测试使用预录双声道夹具,一路作为助手扬声器参考,一路作为用户麦克风输入。
不能只通过静态单段 WAV 验证全双工。
@@ -819,7 +816,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
- 六个发布目标分别验证麦克风权限、采集、播放、设备拔插和应用退出。
- 本地模型在声明的最低硬件上完成 30 分钟稳定性和实时系数测试。
- 云端 Provider 测试会产生外部调用和费用,只在明确授权的 gated 测试中运行。
- 每个云端 Adapter 至少验证一次真实音频输入、真实音频输出、打断和工具审批。
- 每个云端 Adapter 至少验证一次真实音频输入、真实音频输出、打断和工具调用。
- 真实测试失败时不使用配置握手成功替代生成验证。
---
@@ -870,7 +867,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
- 在线 ASR 和 VAD 模型管理。
- Agent Runtime 流式文本桥。
- 本地 TTS 模型管理、短语规划、音频回调和取消。
- 最终文本持久化与工具审批。
- 最终文本持久化与工具调用。
- 六个平台/架构验证。
### 阶段 3:首个云端原生 Adapter
@@ -878,7 +875,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
- Main-only 凭据和引擎 Profile。
- WebRTC 或 WebSocket 会话。
- 转写、音频、截断、用量和 Provider 错误。
- sideband 工具与审批。
- sideband 工具与结构化提问。
- 真实有费用的 gated 验证。
### 阶段 4:本地原生全双工 Adapter
@@ -906,7 +903,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
4. 用户开口触发 150 ms 内静音和响应取消。
5. 持久化已提交用户文本、已展示助手内容、`visual-only` Block 和实际播放边界;下一轮
模型上下文只使用已播放文本与完整显示的 `visual-only` Block。
6. 模拟工具审批时暂停语音,拒绝口头授权。
6. 模拟结构化问题时暂停语音,验证回答、跳过和取消。
7. 注入 Adapter 失败后明确结束,不切换任何引擎。
8. 关闭窗口后所有 Track、Port、计时器和模拟任务归零。
@@ -921,7 +918,7 @@ Endpoint 不能因为 URL 相似就被标记为实时语音。
- 任何引擎、Provider、模型、地域、Runtime 或模式变化都不能在测试中静默发生。
- 助手播放期间继续采集麦克风,用户可在 P95 150 ms 内打断。
- 中断后未播放音频与文本不进入下一轮上下文。
- Ask 和 Execute 在语音中与文本中使用同一权限和审批边界。
- 语音与文本使用同一能力与资源范围,不保留产品模式或一般工具审批。
- 语音口令不能批准工具。
- 云端长期凭据不进入 Renderer,本地 Sidecar 不监听外部地址。
- 默认数据库、日志和 Artifact 中没有原始音频。
@@ -17,6 +17,14 @@ Main 仅在 `capability === 'image-generation'` 时调用 `withImageConversation
公开成果事件和消息 metadata 保存,不新增表、迁移、服务端 ID 或 Runtime 会话缓存。
它是该回复的展示事实,不进入下一轮提示词。
成果页移除后,这条会话图片存储和读取链路继续保留;没有批量迁移或删除 artifact。
需要项目图片文件时使用 `save_image` 显式保存。其 `artifactId` 是会话图片、上传图片或
`generate_image` 返回的图片 ID,不依赖成果页;旧消息中的 `artifactIds` 继续有效。
工具将实际图片文件写入 Runtime 所在主机的绝对路径,远程项目写到远程 Host。
工作区导入不会创建会话图片记录。工具发现规则见
[会话图片工具](../conversation-media-generation/technical-design.md#图片保存工具)。
文件与会话数据的边界见[工作区文件](../assistant-workbar/workspace-files.md#存储与实现)。
## 上游请求
`ModelAgentRuntime` 将已有文字历史作为会话数据加入提示词,并把本轮要求放在末尾。
+2 -2
View File
@@ -65,7 +65,7 @@ OpenAI 兼容向量模型、RRF 混合检索、知识图谱、任务状态和来
UI 必须在修改前持续说明该行为。
13. 第一阶段和第二阶段均不新增付费或外部模型调用。现有 Embeddings 调用仍由
用户配置决定。
14. Ask 的运行时边界保持只读。知识库内容始终被标记为不可信证据,
14. 知识检索工具保持只读。知识库内容始终被标记为不可信证据,
不能成为系统指令。
15. 向量连接是 GoodBuddy Main 管理的知识服务依赖,不是 Agent Runtime 模型。它不进入
直连模型、OpenCode、Continue、DeepSeek Harness、远程 Runtime 或项目 Runtime 的
@@ -231,7 +231,7 @@ Tokenizer、原始向量或索引访问能力传入 Runtime。切换 Runtime 不
- 预检索零结果不阻止回答,但必须显示“已检索,未找到相关内容”。
- 预检索失败不得自动扩大范围或访问未选知识库。
- 图片生成能力不执行知识预检索。
- Ask 和 Execute 使用相同的只读检索范围。
- 普通请求使用相同的只读检索范围,不依赖产品工作模式。
### 7.2 中文全文检索
+1 -1
View File
@@ -503,7 +503,7 @@
- Main 重新检查知识库、文档、分块和来源归属
- Renderer 不访问 SQLite、文件系统、Electron shell 或凭据
- 知识内容标记为不可信证据
- Ask 不获得写工具
- 只读检索工具不因检索内容获得写入能力
- 错误和日志不包含密钥、授权头和未限制正文
### US-I3 取消和关闭
@@ -339,13 +339,13 @@ MCP 的连接、凭据、启用或 Runtime 分配状态。
“依赖未验证”不是错误,也不能显示为“已就绪”。
## 10. Ask、Execute 与 MCP
## 10. Runtime 与 MCP
| 场景 | 解释器是否可见 | 是否允许执行 |
| --- | --- | --- |
| 本机 Execute Runtime | 是 | 按现有 Execute 权限 |
| 本机 Ask Runtime | 可以存在于进程环境 | 仍由 Runtime Ask 边界决定,不能因此获得命令权限 |
| Main 启动的已启用 stdio MCP | 是 | 按当前 MCP 分配和模式规则 |
| 本机 Runtime | 是 | 按实际注册能力和当前账号权限 |
| 内部无工具文本调用 | 可以存在于进程环境 | 不注入工具,解释器不改变调用用途 |
| Main 启动的已启用 stdio MCP | 是 | 按当前 MCP 分配和资源范围 |
| HTTP/SSE MCP | 不适用 | 按当前网络 MCP 规则 |
| 远程 Runtime | 否 | 使用远程 Host 自身环境 |
@@ -392,7 +392,7 @@ MCP 的连接、凭据、启用或 Runtime 分配状态。
| 新托管版本 vs 健康旧版本 | 候选完整通过后才发布 |
| 系统发现新版本 vs 已保存自定义路径 | 不自动切换 |
| 解释器成功 vs 缺少第三方包 | 工具 ready,能力 missing/unknown |
| Ask 环境包含解释器 vs Ask 只读 | Ask 权限边界优先 |
| 内部文本调用环境包含解释器 | 保持无工具构造 |
| 本机设置 vs 远程项目 | 远程隔离优先 |
| 镜像不可达 vs 其他来源可用 | 不在同一次操作中静默切换来源 |
| 工具下载源 vs 模型/更新下载源 | 各自独立,工具执行环境设置不覆盖其他功能 |
@@ -415,7 +415,7 @@ MCP 的连接、凭据、启用或 Runtime 分配状态。
| 删除生命周期 | 通过 | 使用中进程先停止,失败不删除 |
| 设置生效 | 通过 | 进程启动快照不可变,新进程使用新设置 |
| 升级兼容 | 通过 | 旧系统解释器一次性固化为自定义路径 |
| Ask/Execute | 通过 | 工具环境不扩大工具集合或权限 |
| 执行范围 | 通过 | 工具环境不扩大工具集合或权限 |
| 远程边界 | 通过 | 本机配置不进入 Host |
| 受限网络 | 通过 | 来源失败可见,不在同次操作静默切换 |
| 完全离线 | 有明确限制 | Node 可用;Python 首期要求已有自定义环境 |
@@ -454,7 +454,7 @@ MCP 的连接、凭据、启用或 Runtime 分配状态。
| SKILL 命令解析 | FR-4 | US-A1、US-A3 |
| MCP 命令解析 | FR-5 | US-B1、US-B2、US-B3 |
| 生效时机 | FR-7 | US-C3、US-C4 |
| Ask/Execute | FR-10 | US-D6 |
| 执行范围 | FR-10 | US-D6 |
| 远程隔离 | FR-7、FR-9 | US-D1 |
| 受限网络与离线 | FR-2、FR-11 | US-E1、US-E2、US-E3 |
| 统一设置入口 | FR-12 | US-F1、US-F2 |
+3 -3
View File
@@ -27,7 +27,7 @@ Python、虚拟环境或 Conda 环境时,也不能被 GoodBuddy 静默替换
3. 专业用户可以选择检测到的解释器或指定其他可执行文件。
4. 设置始终展示实际来源、版本、路径、工具状态和能力依赖状态。
5. 工具执行环境不修改系统环境,也不传播到远程主机。
6. 保持 Ask/Execute、Runtime、MCP、取消和进程清理的现有边界。
6. 保持 Runtime、MCP 能力范围、取消和进程清理的现有边界。
7. 用户可以在“工具执行环境”Tab 中明确选择受管工具工件使用原生地址还是 OSS 镜像。
8. Skills、MCP 和工具执行环境通过一个“能力与工具”设置入口形成清晰的能力配置链路。
@@ -159,8 +159,8 @@ Python、虚拟环境或 Conda 环境时,也不能被 GoodBuddy 静默替换
### FR-10 权限模式
- Ask 不因解释器可用而获得额外命令或写入权限。
- Execute 继续使用当前本机账号的完整授权。
- 解释器可用不改变工具的注册、分配与资源范围。
- 工具使用当前本机账号权限,无产品工作模式。
- 不为工具环境增加第二套批准流程。
### FR-11 工具下载源
@@ -316,7 +316,7 @@ Renderer 不能提交 PATH、环境变量、下载 URL、摘要、安装目录
## 13. 权限与远程边界
- Ask/Execute 语义不变。
- 统一执行规则不因工具环境变化而改变。
- 不新增解释器批准流程。
- 不上传 shim、Python、pip 包或自定义路径。
- 不修改 Host PATH。
@@ -350,7 +350,7 @@ Renderer 不能提交 PATH、环境变量、下载 URL、摘要、安装目录
- Node SKILL 与 MCP。
- Python SKILL 与 MCP。
- 自定义解释器。
- Ask/Execute、重建、取消和退出。
- 无模式工具执行、内部无工具调用、重建、取消和退出。
- 六平台打包。
这些阶段是开发顺序,不是可长期发布的残缺功能。合并能力前完成普通用户和专业用户的完整
@@ -50,7 +50,7 @@
验收:
- Given 系统 PATH 没有 Node.js
- When 本机 Execute 请求运行普通 JavaScript SKILL
- When 本机请求运行普通 JavaScript SKILL
- Then `node` 使用 GoodBuddy 托管来源
- And `npm`、`npx` 与该 Node 来源一致
- And 设置显示实际 Node 版本
@@ -234,16 +234,16 @@
- 候选完整诊断成功后才发布
- 更新失败继续使用健康旧版本
### US-D6 安装解释器不扩大 Ask 权限
### US-D6 安装解释器不改变能力范围
作为 Ask 用户,我希望安装 Node/Python 后仍保持原有只读边界,以免环境准备改变授权范围。
作为用户,我希望安装 Node/Python 后仍按已启用能力运行,内部文本调用保持无工具。
验收:
- 工具环境不新增 Runtime 工具
- Ask 是否允许命令仍由当前 Runtime 边界决定
- Ask 中禁止的写入和执行不会因解释器存在而放行
- Execute 继续使用用户已授权的本机账号权限
- 命令可用性由当前 Runtime 的实际注册能力决定
- 内部文本调用不因解释器存在而获得工具
- 工具继续使用本机账号权限
## 6. Epic E:受限网络与离线行为明确
+19
View File
@@ -1,5 +1,24 @@
# Magic Notes Progress
## 2026-10-08: Rich Text Table Editor Fix
Replaced the Electron-incompatible native prompt flow for table insertion with the shared form
Modal. The dialog validates 1-20 rows and 1-12 columns, supports cancellation and Escape, restores
focus to the toolbar trigger, and preserves the editor selection while it is open. Table row,
column, and delete controls now use compact localized icon buttons, remain disabled outside a table
cell, guard against nested insertion, preserve the active cell, and create separate Quill undo
steps. Non-empty table Delta content remains readable in the read-only record renderer.
Focused validation: `npx vitest run src/renderer/src/MagicNoteEditor.test.tsx
src/renderer/src/MagicNoteContent.test.tsx` passed 18 tests. `npm run typecheck` and affected-file
ESLint passed. Full suite and runtime Electron validation were not run.
The table content check now also applies to save validation. An empty-cell table remains an active
draft after focus leaves the composer, saves through the renderer, IPC and storage layers, and is
restored after reopening. A rich Delta containing only a terminal newline remains rejected, and a
failed save leaves the draft available for retry. Focused renderer and storage regression tests
cover these cases.
## 2026-10-03: Compact Notes List And Narrow Drawer
The notes list now defaults to 200px with a 160px minimum. At workspace widths
@@ -71,6 +71,16 @@ save remounts at the returned revision and establishes a new baseline; an unchan
background revision retains the existing editor and baseline. Aborted canvas
initialization and file reads for an unmounted text editor cannot write back.
Rich text tables remain native Quill table Delta operations. Insertion stores the active editor
range while the shared table-size dialog is open, rejects invalid dimensions before changing the
Delta, and refuses insertion when that range is already a table cell. Row, column, and whole-table
commands require a cell selection, preserve the selected range, and cut Quill history around each
command so each toolbar action is independently undoable. The read-only rich renderer loads the
same Delta with Quill's `readOnly` mode, so table content follows the same persisted schema without
an editable toolbar. A table row operation is content even when its cell text is empty: the
workspace keeps the composer open after focus leaves it and submits the Delta through the normal
IPC and storage path. A genuinely empty rich Delta is still rejected before IPC.
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
+8
View File
@@ -49,4 +49,12 @@
Tab 使用工作栏现有的共享页签、焦点和关闭模式。搜索有持久标签,列表用可键盘访问的按钮;打开采集后聚焦预览,取消返回触发按钮,触发按钮已不可见时尝试返回会话标题菜单按钮。新建标题出现后聚焦输入框。面板中的 Escape 取消当前草稿,或从详情返回列表;需要放弃输入时先显示草稿确认。确认框初始聚焦“继续编辑”,限制 Tab 焦点范围,Escape 保留草稿并关闭确认。
## 富文本表格
富文本编辑器使用共享表单 Modal 插入表格,行数限制为 1-20,列数限制为
1-12。取消、关闭、Escape 和遮罩点击不会修改内容,关闭后焦点返回“插入表格”
按钮。表格操作使用紧凑图标按钮,保留本地化的可访问名称和提示;未选中表格单元格
时,插入行、插入列和删除操作保持禁用,避免误操作或嵌套表格。每个表格操作形成
独立的撤销步骤。已保存表格在只读记录视图中继续由 Quill 以不可编辑单元格显示。
面板服从工作栏现有宽度和停靠规则,以单列切换列表、详情和采集编辑。按钮组可换行,文字摘要保留换行并折行长文本;Markdown 和代码以原始文字显示。头部与快速追加区固定,中间内容独立滚动;完整预览输入区可纵向调整大小。配色、按钮、焦点环、通知和确认使用共享组件与令牌。真实 Electron 已在实测 214px 面板验证无横向溢出,以及原生键盘返回列表、打开笔记和追加保存。
+2 -2
View File
@@ -462,8 +462,8 @@ Project 记忆与 Global 偏好冲突时:
5. Embedding 只能发送用户已配置允许的记忆,Restricted 默认不发送外部服务。
6. 记忆内容和来源不出现在普通日志与通知。
7. 跨分区晋升需要明确操作和审计。
8. Ask 和 Execute 使用同一只读记忆检索边界。
9. 记忆不能绕过系统指令、工具审批和工作区权限。
8. 普通请求使用同一只读记忆检索边界,不依赖产品工作模式。
9. 记忆不能绕过系统指令、能力范围和工作区权限。
## 18. 实施顺序
+3 -3
View File
@@ -13,7 +13,7 @@
| LR-1 | 仅启用操作改变启用状态;仅保存操作改变持久化范围;测试不改变二者。 | FR-1、FR-7;US-1~US-3 |
| LR-2 | 非空指定路径优先,完全绕过注册信息;空路径读取全部注册条目,忽略条目的打开状态。 | FR-2、FR-6;US-1、US-2、US-6 |
| LR-3 | 先解析当前范围,再校验 `vaultId`,成功后才启动目标仓库的工具进程。 | FR-3;US-5 |
| LR-4 | 上游能力完整保留;启用、分配和应用现有工作模式由既有会话入口执行,不另加 Obsidian 权限层。 | FR-4、FR-9;US-4、US-9~US-11 |
| LR-4 | 上游能力完整保留;启用、分配和资源范围由既有会话入口执行,不另加 Obsidian 权限层。 | FR-4、FR-9;US-4、US-9~US-11 |
| LR-5 | 范围解析失败使本次请求失败,不跳过失效注册条目,不选择替代仓库。 | FR-8;US-7 |
| LR-6 | 测试仅发现仓库并列举工具,逐仓库连接;工具数按上游名称去重,不按仓库数相乘。 | FR-7;US-1、US-2 |
| LR-7 | 操作结束或取消后关闭本次 MCP 连接和子进程;取消不承诺撤销已落盘写入。 | FR-8;US-8 |
@@ -51,10 +51,10 @@
Main 对范围保存、开关和分配请求先验证来源及输入,再撤销现存的含 Obsidian 授权,随后保存设置。提交相同值也触发撤销。输入无效时在撤销前返回错误;持久化失败时原设置保留,但旧授权已经失效。草稿编辑、文件夹选择和连接测试均不触发此撤销。
撤销按整个请求授权执行。若同一个授权同时含 Obsidian 和知识库等能力,该授权整体失效;独立的不含 Obsidian 授权保留。后续请求按当前保存的范围、开关、分配和工作模式重新取得授权;关闭后不再签发 Obsidian 能力。
撤销按整个请求授权执行。若同一个授权同时含 Obsidian 和知识库等能力,该授权整体失效;独立的不含 Obsidian 授权保留。后续请求按当前保存的范围、开关和分配重新取得授权;关闭后不再签发 Obsidian 能力。
撤销会立即移除旧授权并发出取消信号,设置响应不等待子进程完全退出。工具调用负责在结束路径关闭 transport 和 client;网关销毁则等待活动调用完成清理,包含先前已撤销但仍在关闭的调用。取消过程中已落盘的内容保留。
## 完整性检查
规则已覆盖首次使用、草稿测试、保存、范围恢复、单仓库、多仓库、失效路径、旧配置、工作模式,以及设置变更对旧授权和在途调用的影响。当前没有待定的产品决策。实现及各层自动化、Electron 与安装包验证状态见[进度](./progress.md)。
规则已覆盖首次使用、草稿测试、保存、范围恢复、单仓库、多仓库、失效路径、旧配置,以及设置变更对旧授权和在途调用的影响。当前没有待定的产品决策。实现及各层自动化、Electron 与安装包验证状态见[进度](./progress.md)。
+3 -3
View File
@@ -13,12 +13,12 @@
| FR-1 | 首次使用及旧设置缺少 Obsidian 字段时默认关闭。保存范围、测试连接和启用是独立操作,不互相隐式触发。 |
| FR-2 | 默认 `vaultPath` 为空,访问本机已注册的全部仓库,包含未打开的仓库。用户可改选一个本机文件夹,也可恢复默认范围。 |
| FR-3 | 工具调用使用仓库 ID 定位。可用仓库超过一个时必须明确指定 `vaultId`;缺失或无效时返回错误,不按顺序、名称或最近使用情况选择写入目标。 |
| FR-4 | 提供 MCPVault 0.16.0 的全部 18 个上游工具及其参数能力。不得增加 Obsidian 专用只读设置、工具过滤或额外审批;应用现有 Ask/Execute 工作模式照常生效。 |
| FR-4 | 提供 MCPVault 0.16.0 的全部 18 个上游工具及其参数能力。不得增加 Obsidian 专用只读设置、工具过滤或额外审批;按启用、分配和资源范围执行。 |
| FR-5 | MCPVault 及完整运行依赖随安装包交付,使用现有托管 Node,首次调用无需联网安装 npm 包,也无需用户安装系统 Node。 |
| FR-6 | 不依赖正在运行的 Obsidian、社区插件、REST 服务或 API 密钥。指定文件夹可绕过注册信息,不要求该文件夹已在 Obsidian 注册。 |
| FR-7 | 功能关闭时也可测试未保存的草稿。测试返回仓库名称、路径和上游工具数,不修改笔记、不保存范围、不启用功能。 |
| FR-8 | 范围无效、仓库不可访问、内置依赖缺失或工具执行失败时说明原因并保留草稿。取消文件夹选择不清空输入;会话调用取消后释放对应子进程。 |
| FR-9 | 沿用内置 MCP 的启用与 Runtime 分配方式。设置范围属于桌面端全局配置,具体会话仍受既有能力分配和工作模式约束。 |
| FR-9 | 沿用内置 MCP 的启用与 Runtime 分配方式。设置范围属于桌面端全局配置,具体会话仍受既有能力分配和资源范围约束。 |
| FR-10 | 提交保存范围、切换 Obsidian 开关或修改分配时,撤销此前含 Obsidian 的请求授权并取消其在途调用,避免继续使用旧范围或旧分配。应用关闭须等待活动工具清理;不回滚已经落盘的修改。 |
“离线”指本地 MCP 服务的启动和文件操作不需要下载依赖;模型服务是否联网由模型配置决定。工具返回的笔记内容会进入当前会话处理流程,不能将本地接入描述为模型推理也完全离线。
@@ -31,6 +31,6 @@
## 产品验收
发布前须完成[技术设计中的验证矩阵](./technical-design.md#验证与实施顺序),并在[进度](./progress.md)记录真实结果。验收至少覆盖:关闭状态测试草稿、保存后重新进入设置、恢复全部仓库、多仓库明确选择、实际读取和写回、工作模式约束,以及安装包断网启动。
发布前须完成[技术设计中的验证矩阵](./technical-design.md#验证与实施顺序),并在[进度](./progress.md)记录真实结果。验收至少覆盖:关闭状态测试草稿、保存后重新进入设置、恢复全部仓库、多仓库明确选择、实际读取和写回、能力分配与范围,以及安装包断网启动。
18 个上游工具按名称和参数契约逐项比对;加上仓库发现,共有 19 个共享工具。自动化验证分别覆盖注册 IPC、HTTP 网关和直连工具提供器,安装包及完整 Electron 界面仍单独验收。配置提交时还须验证旧授权立即失效、在途调用收到取消,以及网关销毁等待子进程清理完成。
+3 -3
View File
@@ -14,7 +14,7 @@
| 共享目录与直连 | [obsidian-tools.ts](../../../src/shared/obsidian-tools.ts) 定义 19 个工具;[model-tool-provider.ts](../../../src/main/agent/model-tool-provider.ts) 发现并调用同一目录,保留上游错误详情 |
| 构建 | [package-mcpvault.ts](../../../src/main/obsidian/package-mcpvault.ts) 复制依赖树,[electron.vite.config.ts](../../../electron.vite.config.ts) 调用打包函数,[package.json](../../../package.json) 配置版本及解包资源 |
正式链路为设置页面经 Preload、受信任的 Main IPC 处理器到配置服务或 ObsidianService;会话经既有能力入口到 ObsidianService,再通过 stdio 到 MCPVault。[index.ts](../../../src/main/index.ts) 创建服务并注入工具启动环境、网关及 IPC;[capability-ipc.ts](../../../src/main/capabilities/capability-ipc.ts) 注册设置、测试和目录选择处理器,接收 [ipc.ts](../../../src/main/ipc.ts) 的注册及刷新回调。`ipc.ts` 保留处理器跟踪、Runtime 重载和生命周期管理,并在会话入口按保存设置及工作模式签发请求授权。
正式链路为设置页面经 Preload、受信任的 Main IPC 处理器到配置服务或 ObsidianService;会话经既有能力入口到 ObsidianService,再通过 stdio 到 MCPVault。[index.ts](../../../src/main/index.ts) 创建服务并注入工具启动环境、网关及 IPC;[capability-ipc.ts](../../../src/main/capabilities/capability-ipc.ts) 注册设置、测试和目录选择处理器,接收 [ipc.ts](../../../src/main/ipc.ts) 的注册及刷新回调。`ipc.ts` 保留处理器跟踪、Runtime 重载和生命周期管理,并在会话入口按保存设置与资源范围签发请求授权。
## 配置与 IPC
@@ -69,7 +69,7 @@
| `get_note_outline` | 获取笔记大纲 |
| `read_note_lines` | 按行读取笔记 |
服务分页读取上游工具,不设置功能白名单。读写属性供应用既有工作模式使用,不能据此增加 Obsidian 专用过滤。保留上游的错误结果与参数规则,例如删除参数 `confirmPath`;不把参数要求改造成额外确认弹窗。
服务分页读取上游工具,不设置功能白名单。读写属性用于说明,不据此过滤工具。保留上游的错误结果与参数规则,例如删除参数 `confirmPath`;不把参数要求改造成额外确认弹窗。
## 离线打包与生命周期
@@ -97,7 +97,7 @@
| --- | --- | --- |
| TS-1 配置与服务 | 缺省字段、持久化独立性、各平台注册路径、ID 路由、真实上游读写、取消清理 | LR-1~LR-3、LR-5~LR-7 |
| TS-2 设置 UI | 草稿测试不保存不启用、切换范围、取消选择、失败保留输入、键盘与窄窗口 | US-1~US-3、US-7、US-8 |
| TS-3 网关与 IPC | 正式 Main 处理器、19 个共享工具、分配、Ask/Execute、配置提交撤销旧授权、在途取消与销毁等待;核对 DeepSeek Harness 和远程执行不可用边界 | LR-3、LR-4、LR-7、LR-8;US-4、US-5、US-9~US-12 |
| TS-3 网关与 IPC | 正式 Main 处理器、19 个共享工具、分配、无模式读写、配置提交撤销旧授权、在途取消与销毁等待;核对 DeepSeek Harness 和远程执行不可用边界 | LR-3、LR-4、LR-7、LR-8;US-4、US-5、US-9~US-12 |
| TS-4 安装包与端到端 | 在实际支持系统上断网且无系统 Node 测试;Obsidian 关闭;许可证及原生资源完整;从设置到真实会话读写 | FR-5、FR-6;US-6 |
当前分配目标为 `model`、`opencode`、`continue`;[DeepSeek Harness](../../../src/main/agent/deepseek-harness-runtime.ts)和[远程 ACP Runtime](../../../src/main/agent/acp-remote-runtime.ts)的 `supportsScopedDataTools` 为 `false`,沿用既有不可用边界。已通过的生产组件自动化包含设置组件到注册 IPC、HTTP 网关,以及直连工具提供器全部 19 个工具的实际执行。真实聊天会话、Electron 托管 Node、Windows 解包应用及网络限制的不同验证范围见[进度](./progress.md)。
+3 -3
View File
@@ -7,14 +7,14 @@
| US-1 | 首次检查,FR-1、FR-2、FR-7 | 本机有已注册仓库,功能尚未启用 | 用户进入 MCP 设置并测试默认范围 | 开关保持关闭;返回注册仓库列表和工具数;未保存配置,也未改动笔记 |
| US-2 | 指定草稿范围,FR-2、FR-6、FR-7 | 用户选择一个未注册文件夹,尚未保存 | 用户测试该草稿 | 仅探测此文件夹;已保存范围及启用状态不变 |
| US-3 | 保存与恢复默认,FR-1、FR-2 | 用户已测试指定文件夹 | 保存后重新进入设置,再切换为全部仓库并保存 | 保存结果可重新加载;恢复默认时保存空 `vaultPath`;两次保存均不自动启用 |
| US-4 | 单仓库日常操作,FR-4、FR-9 | 功能已启用并分配给当前会话,范围只有一个仓库 | 助手在允许写入的既有工作模式下读取、修改并再次读取笔记 | 写回当前唯一仓库,结果可从文件核验;无需额外 Obsidian 审批 |
| US-4 | 单仓库日常操作,FR-4、FR-9 | 功能已启用并分配给当前会话,范围只有一个仓库 | 助手读取、修改并再次读取笔记 | 写回当前唯一仓库,结果可从文件核验;无需额外 Obsidian 审批 |
| US-5 | 多仓库同名文件,FR-3 | 有两个仓库,都包含 `daily.md` | 助手先取得仓库 ID,再指定其中一个修改;另行尝试省略 ID 或传入无效 ID | 明确 ID 的请求仅作用于对应仓库;另两次调用返回错误,两个仓库均不因失败请求改变 |
| US-6 | 离线与关闭 Obsidian,FR-5、FR-6 | 安装包已有依赖,Obsidian 未运行,系统未安装 Node,网络断开 | 使用内置服务测试、读取和写入测试仓库 | 本地操作通过现有托管 Node 完成,不请求 npm,也不要求插件;模型联网另行验证 |
| US-7 | 范围失败恢复,FR-8 | 注册文件缺失或含失效路径,或者草稿路径不可访问 | 用户测试后改为有效文件夹并重试 | 显示具体原因,保留输入;重试使用新草稿,不偷偷跳过坏仓库或切换目标 |
| US-8 | 取消与失败保留,FR-8 | 用户正在编辑草稿或执行会话工具 | 取消文件夹选择、遇到保存失败,或取消会话工具 | 选择取消和保存失败均保留草稿;工具取消关闭对应进程,不报告成功,也不宣称回滚已发生的写入 |
| US-9 | 既有权限规则,FR-4、FR-9 | 功能启用且已分配,用户分别使用 Ask 和 Execute | 助手请求写入 | Ask 按应用现有规则保持只读;Execute 沿用正常执行规则,不增加 Obsidian 专用开关、过滤或审批 |
| US-9 | 既有能力规则,FR-4、FR-9 | 功能启用且已分配 | 助手请求写入,再关闭能力后请求写入 | 启用时正常执行,关闭后不能绕过设置;不增加 Obsidian 专用开关、过滤或审批 |
| US-10 | 旧配置与分配,FR-1、FR-9 | 旧配置没有 Obsidian 字段,已有其他 MCP 配置 | 加载设置并修改 Obsidian 分配 | Obsidian 默认关闭,其他配置保留;修改分配不改写仓库范围;未分配的会话不可调用 |
| US-11 | 上游能力完整,FR-4 | 在允许完整调用的工作模式下启用接入 | 获取工具集合并对照 MCPVault 0.16.0 | 18 个上游工具均有可调用映射,参数能力保留;仓库发现辅助工具不计入这 18 个 |
| US-11 | 上游能力完整,FR-4 | 启用接入并分配给受支持 Runtime | 获取工具集合并对照 MCPVault 0.16.0 | 18 个上游工具均有可调用映射,参数能力保留;仓库发现辅助工具不计入这 18 个 |
| US-12 | 设置变更与旧调用,FR-10 | 有含 Obsidian 的旧请求授权和正在执行的工具 | 提交保存范围、切换开关或修改分配,随后关闭网关 | 旧授权失效,在途调用收到取消;网关等待工具关闭。保存失败也不恢复旧授权;后续请求按实际保存状态重新取得授权 |
跨平台注册路径和安装包测试分别取证。通过模拟平台字符串测试注册路径,不等于在对应操作系统上完成安装包验收。
@@ -83,7 +83,7 @@ type DocumentEditState = {
6. ONLYOFFICE 插件在一个撤销上下文中执行全部操作。
7. 任一操作失败则回滚整个事务;成功后高亮修改并使检查点变为 `dirty`。
AI 编辑继承发起请求的 Ask/Execute 模式。Ask 只生成建议,不调用编辑器写入;Execute 才能
AI 编辑沿用发起请求的能力范围。用户明确只要建议时不调用编辑器写入;要求修改时可
应用结构化修改。用户在编辑器中发起操作只授权当前文档和冻结选区,不授予 Agent 其他文件或
系统能力。

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