Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47ba54f47d | ||
|
|
76e781a0a8 | ||
|
|
0cdf04261a | ||
|
|
66f77b83c2 | ||
|
|
83a3ea261e | ||
|
|
013e72f69e | ||
|
|
61590395ef | ||
|
|
4e5239256d | ||
|
|
65a21390de | ||
|
|
706726bad6 | ||
|
|
5cdffee9ee | ||
|
|
d9e1e42288 | ||
|
|
d47f967781 | ||
|
|
f35198df01 | ||
|
|
8e607366ca | ||
|
|
ee1bf8a187 | ||
|
|
b8b6007b51 | ||
|
|
12594961fe | ||
|
|
5a6ba6c14f | ||
|
|
0f8bf405db | ||
|
|
5ecc19dfe0 | ||
|
|
4a2986ca5d | ||
|
|
2713a81bd9 | ||
|
|
11facf0392 | ||
|
|
0f0b6ac21d | ||
|
|
eb53c827c6 | ||
|
|
8b15399577 | ||
|
|
67dedc2ed2 | ||
|
|
ceb275bdb8 | ||
|
|
44cd944601 | ||
|
|
40c108da1a | ||
|
|
5177b47aeb | ||
|
|
4edf06b685 | ||
|
|
f81f342cde | ||
|
|
1094433a7d | ||
|
|
55efca3ee1 | ||
|
|
5116613615 |
@@ -103,6 +103,13 @@ npm run build
|
||||
|
||||
中间构建输出位于 `out`。该目录为生成内容,应修改源文件后重新构建,不要直接编辑。
|
||||
|
||||
Obsidian 接入的构建钩子将锁定的 npm `@bitbonsai/mcpvault@0.16.0` 及递归运行依赖、
|
||||
许可证和原生辅助资源复制到 `out/main/obsidian-mcpvault`,桌面打包时解包到
|
||||
`app.asar.unpacked`。运行时复用现有工具执行环境的 Node,不执行在线 npm 安装。
|
||||
生产组件自动化、Electron 托管 Node、Windows 设置与聊天 UI,以及受限网络下的
|
||||
Windows x64 解包应用已验证;物理断网、安装器及其他系统验收待完成。
|
||||
验证记录见 [Obsidian 进度](./docs/features/obsidian/progress.md)。
|
||||
|
||||
## 图标生成
|
||||
|
||||
应用图标源文件位于 `icons`。修改源图或图标处理逻辑后运行:
|
||||
|
||||
+97
-14
@@ -52,6 +52,12 @@ records are listed separately and do not introduce another feature status.
|
||||
longer stop at 100 conversations, and consecutive sends no longer truncate
|
||||
saved history beyond 500 messages. Model context limits still apply;
|
||||
previously lost messages cannot be restored.
|
||||
Pinned and unpinned groups order by the latest message creation time,
|
||||
keeping positions stable during streaming text and tool updates.
|
||||
- [x] **Optional desktop task notifications**: A default-on switch under
|
||||
Platform Features / General immediately controls system task reminders.
|
||||
In-app notifications remain available independently; General settings group
|
||||
shortcuts, conversations and notifications, and workspace and downloads.
|
||||
- [x] **On-demand contextual help**: Supplementary page and settings
|
||||
explanations use shared title- or field-adjacent help controls with mouse
|
||||
and keyboard access. Important operation consequences and errors remain visible.
|
||||
@@ -324,7 +330,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.13.4`, while the current Desktop release candidate is `0.13.16`; formal
|
||||
`0.15.0`, paired with the current Desktop release candidate `0.15.2`; 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
|
||||
@@ -373,7 +379,8 @@ records are listed separately and do not introduce another feature status.
|
||||
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 proxies, and rejects plugin impersonation of those names.
|
||||
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
|
||||
permissions. Image input follows the selected model connection's declared
|
||||
capability: text models reject images before Host or model invocation, while
|
||||
@@ -393,6 +400,22 @@ records are listed separately and do not introduce another feature status.
|
||||
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.
|
||||
- **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
|
||||
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).
|
||||
- **Runtime telemetry opt-out**: Local DS Web and remote Runtime/model-bridge
|
||||
launches share the telemetry opt-out environment without blocking model, MCP,
|
||||
or user-requested networking. Continue skips disabled automatic-update checks.
|
||||
Remote changes require a newly started Runtime from the matching Agent;
|
||||
current-source Linux x64 Agent/model validation is recorded in the
|
||||
[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
|
||||
@@ -577,10 +600,22 @@ records are listed separately and do not introduce another feature status.
|
||||
the applicable checks pass.
|
||||
- [x] **On-demand built-in MCP**: Knowledge, Magic Notes, GoodBuddy
|
||||
configuration, and built-in browser MCP can be enabled independently and assigned to direct
|
||||
models, GoodBuddy-managed OpenCode, and Continue. Settings explicitly marks
|
||||
DeepSeek Harness unsupported. Built-in MCP uses short-lived local authority
|
||||
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
|
||||
read/write boundaries.
|
||||
- **Obsidian integration (implemented in source, acceptance in progress)**:
|
||||
Disabled by default, with all locally registered vaults as the default scope
|
||||
and an optional folder that can be tested before saving or enabling.
|
||||
Bundled MCPVault 0.16.0 provides all upstream abilities through 19 shared tools.
|
||||
Automated production-component checks, Electron managed-Node tests, Windows
|
||||
settings and chat UI checks, and restricted-network Windows x64 unpacked-app
|
||||
checks passed. Physical-offline, installer, and other-platform acceptance
|
||||
remain pending. DeepSeek
|
||||
Harness and remote execution remain unavailable. See the
|
||||
[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
|
||||
@@ -601,6 +636,10 @@ records are listed separately and do not introduce another feature status.
|
||||
retrieval parameters are collapsed by default. Documents distinguish ready,
|
||||
processing, and failed states, with source opening, retry, and confirmation
|
||||
before source removal.
|
||||
Sources and indexed documents share one searchable list: single-document
|
||||
sources do not duplicate rows, while directories retain grouped documents and
|
||||
failed sources retain recovery actions. Task history and secondary operations
|
||||
remain reachable from the consolidated controls.
|
||||
- [x] **External knowledge-base connections**: Manage Dify, FastGPT,
|
||||
and RAGFlow instances from the Knowledge page, then discover remote
|
||||
knowledge bases or enter their IDs and verify bindings with provider-specific
|
||||
@@ -663,6 +702,17 @@ records are listed separately and do not introduce another feature status.
|
||||
navigation can show the incomplete-todo count, and create, save, and comment
|
||||
results use application-wide notifications. Agent/MCP note writes automatically
|
||||
refresh the open workbench while preserving selection and unsaved drafts.
|
||||
- **Conversation capture and quick notes** (implemented; real Electron acceptance passed):
|
||||
Preview and edit an assistant response or complete conversation text before
|
||||
appending to an existing note or creating one with its first entry. The compact
|
||||
Notes workbar tab supports title/body search, quick text appends and full-workspace
|
||||
navigation. Saved sources link back to conversations and messages; drafts survive
|
||||
conversation switches. Real App/preload/IPC/SQLite acceptance passed, including
|
||||
complete 502-message history and native keyboard use in a 214px panel, with zero
|
||||
model calls. The [feature validation record](./docs/features/magic-notes/progress.md)
|
||||
separates focused passes, fixture repairs and earlier execution-time failures;
|
||||
the [0.15.0 candidate validation](./docs/development/release-preparation-0.15.0.md)
|
||||
passed the full suite, typecheck and lint.
|
||||
- [x] **Paged canvas notes**: The integrated PeopleLib Fabric + Quill editor
|
||||
combines flowing body text with pen/highlighter, object selection/transforms,
|
||||
floating text, images and paper templates. Editors and read-only viewers support
|
||||
@@ -685,13 +735,13 @@ records are listed separately and do not introduce another feature status.
|
||||
invalidated by layout changes and reanalyzed in after-save-auto mode. MCP
|
||||
rejects plain-text replacement of canvas entries. Implementation is complete;
|
||||
final validation status is tracked in the [feature progress record](./docs/features/magic-notes/progress.md).
|
||||
The first upgrade migrates stored bodies into files and raises the database
|
||||
schema to 39 together with attachment changes. Older clients cannot reopen
|
||||
Historical body migration moved stored bodies into files; schema 48 now adds
|
||||
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.
|
||||
- [ ] **Traceable note excerpts and AI editing** (planned): Collects sourced
|
||||
excerpts from conversations, knowledge, and the web and provides
|
||||
- [ ] **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
|
||||
@@ -751,7 +801,7 @@ records are listed separately and do not introduce another feature status.
|
||||
[Smart Heartbeat PRD](./docs/features/smart-heartbeat/prd.md).
|
||||
- [x] **Supervisor reviews, story graphs, and activity**: Review a selected scope
|
||||
and period, read dated supervision results and history in Work review, inspect saved result graphs and sources, confirm or revise entities,
|
||||
and preview local knowledge entity writes. Sidebar feedback follows or pins a
|
||||
and continue a discussion with source-linked review context. Sidebar feedback follows or pins a
|
||||
Conversation or Task and opens its matching result. It shows target names with
|
||||
compact pin/refresh buttons and preserves long-title readability and keyboard
|
||||
focus in narrow sidebars. Activity combines heartbeat
|
||||
@@ -766,6 +816,14 @@ records are listed separately and do not introduce another feature status.
|
||||
flow with a separate run status and correctly placed empty state.
|
||||
Calls remain read-only without tools and may incur model charges. See the
|
||||
[implementation and evidence](./docs/features/conversation-supervision/progress.md).
|
||||
Manual starts, automatic triggers, and resume share one active review;
|
||||
cancellation from Activity preserves saved batches and holds admission until
|
||||
cleanup finishes. Cancelled reviews cannot resume. Story-graph events, entities,
|
||||
and relations have separate counted tabs synchronized with canvas selection.
|
||||
Continue discussion opens directly from feedback or graph sources, with an
|
||||
editable question and explicit target-conversation navigation. The UI no
|
||||
longer exposes knowledge-entity writeback controls or redundant context
|
||||
explanations, and Automatic supervision hides empty metrics until activity exists.
|
||||
- [ ] **Further supervision capabilities** (planned): Full event-by-event replay,
|
||||
Experiment targets, chronological manual-edit audits, event
|
||||
triggers, and broader execution observation remain incomplete. Current automatic
|
||||
@@ -775,6 +833,13 @@ records are listed separately and do not introduce another feature status.
|
||||
- [ ] **Batch runs and comparison lab** (planned): Compares model, Prompt,
|
||||
role, and workflow configurations in batches and summarizes quality,
|
||||
duration, tokens, cost, failure rate, and artifact differences.
|
||||
- **Story Graph read tools (unreleased source)**: Agents can search saved work,
|
||||
read current claims or timelines, and page through source evidence. Supervisor
|
||||
enablement and Runtime assignment control discovery and calls, including old
|
||||
sessions. Model, local Harness, and local/remote OpenCode and Continue share
|
||||
the desktop reader. Queries make no model calls and do not infer replacement
|
||||
from newer reviews. Cross-scope fact reuse and `as_of` remain deferred; see the
|
||||
[design and validation](./docs/features/conversation-supervision/story-graph-mcp-design.md).
|
||||
- [ ] **Temporal memory and fact-conflict detection** (planned): Adds validity
|
||||
periods, current/expired/conflicting fact detection, fact checking, and
|
||||
evidence tracing to memory and the knowledge graph.
|
||||
@@ -855,9 +920,21 @@ records are listed separately and do not introduce another feature status.
|
||||
|
||||
### Open source, builds, and releases
|
||||
|
||||
- The current Desktop candidate is `0.13.16`; Agent `0.13.4` is published, with
|
||||
- The current Desktop candidate is `0.15.2` and the Agent candidate is `0.15.0`, 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.1` carries forward the unpublished `0.15.0` Obsidian integration, conversation capture into Magic
|
||||
Notes, and preview local native clients, plus knowledge, Supervisor, and UI
|
||||
fixes. Schema 48 adds conversation-source metadata; older clients require a
|
||||
complete pre-upgrade backup for rollback. Agent `0.15.0` requires Desktop
|
||||
`0.15.2`, retains Node `24.19.0`, and updates remote telemetry opt-out and
|
||||
disabled Continue update-check behavior without adding remote client shortcuts.
|
||||
- Desktop `0.15.1` completed all six native release targets and GitHub/OSS publication.
|
||||
Desktop `0.15.2` adds read-only Story Graph tools and a fully editable
|
||||
Supervisor follow-up message, simplifies note capture, and improves exit diagnostics.
|
||||
Remote Story Graph requires the paired Agent `0.15.0`; planned Host and cloud
|
||||
management applications remain unimplemented. Current candidate evidence is in the
|
||||
[0.15.2 preparation record](./docs/development/release-preparation-0.15.2.md).
|
||||
- Desktop `0.13.15` carries forward the unpublished `0.13.14` changes to paged Supervisor reviews and resume behavior,
|
||||
native ripgrep searches, to-do layouts, and explicit remote follow-up after an
|
||||
uncertain result. Database schema 47 requires a complete pre-upgrade backup
|
||||
@@ -879,7 +956,7 @@ records are listed separately and do not introduce another feature status.
|
||||
packaging, and publication verification remain separate acceptance steps.
|
||||
No local production build, packaging, or LoongArch preview is requested.
|
||||
Current validation status is recorded in the
|
||||
[release preparation record](./docs/development/release-preparation-0.13.16.md).
|
||||
[release preparation record](./docs/development/release-preparation-0.15.0.md).
|
||||
- Desktop `0.13.12` and Agent `0.13.3` have completed their native release jobs;
|
||||
this does not replace development-time real-Host scenario coverage.
|
||||
- Validation records remain separate from implementation status. Current-source
|
||||
@@ -960,9 +1037,15 @@ records are listed separately and do not introduce another feature status.
|
||||
offline GoodBuddy transfer release acceptance. Until these gates pass, this
|
||||
path must not be described as published or as having passed formal release
|
||||
acceptance.
|
||||
- [ ] **Multi-cloud remote sandbox Agents** (planned): Manages dedicated Linux
|
||||
sandboxes through cloud-provider APIs and SSH Agents. Credentials remain in
|
||||
Main, and high-risk control-plane operations receive separate confirmation.
|
||||
- [ ] **Host and cloud environment management** (planned): Move host management
|
||||
into App Center with a list/detail workspace. A sibling cloud application
|
||||
reuses host connections and remote execution. The first-version adapter plan
|
||||
covers AWS, Azure, Alibaba Cloud, Tencent Cloud, VMware vCenter / vSphere,
|
||||
and ZStack, delivered in batches with real instance discovery, import, and
|
||||
execution acceptance. Instance creation, start/stop, deletion, and network
|
||||
orchestration follow later. Provider APIs are not implemented; existing cloud
|
||||
servers can already be added manually when SSH requirements are met. See the
|
||||
[product scope](./docs/features/remote-host/prd.md).
|
||||
|
||||
## Roadmap Principles
|
||||
|
||||
|
||||
+21
-9
@@ -22,6 +22,8 @@
|
||||
- [x] **跨项目会话活动**:统一入口汇总各项目正在运行和需要处理的对话,保留仍有活动的较早会话。锚定的“项目 → 会话”菜单支持键盘导航和精确打开对话,不再使用整块活动弹窗;空闲时隐藏摘要,长会话子菜单独立滚动,项目选择器继续显示活动数量。
|
||||
后台会话成功完成后,在本次客户端运行期间保留完成提示,直到对应聊天可见且加载完成;提示不跨重启保留。
|
||||
- [x] **会话置顶与完整历史**:常用会话可置顶,重启后保留。列表和搜索不再限于最近 100 个会话,连续发送不再截断超过 500 条的已保存历史;模型上下文限制仍有效,已丢失的历史无法恢复。
|
||||
置顶与非置顶分组按最近一条消息的创建时间排序,流式文本和工具进度更新不再反复改变位置。
|
||||
- [x] **可选桌面任务通知**:“平台功能 / 通用设置”提供默认开启、保存后立即生效的系统任务提醒开关,应用内提示独立保留;通用设置按快捷键、会话与通知、工作目录与下载分组。
|
||||
- [x] **按需上下文帮助**:页面和设置中的补充说明使用标题或字段旁的共享帮助入口,支持鼠标与键盘;关键操作后果和错误提示保持可见。
|
||||
- [x] **紧凑会话控件**:输入选项集中在紧凑设置面板,Runtime、工作模式、发送和队列操作保持可用;会话搜索提供内嵌清除按钮,清除后恢复当前范围列表并把焦点返回输入框。
|
||||
- [x] **会话历史按需加载**:列表只加载轻量摘要,不再一次读入全部会话的过程元数据;已打开和活动会话保留完整详情,搜索、复制、导出、续聊和远程待答问题保持可用。图片合并不会重新发送未变化消息,已确认保存的空闲详情随现有视图缓存释放。详见[历史读取与保留规则](./docs/features/assistant-workbar/execution-history-storage.md#会话列表读取与前端保留)。
|
||||
@@ -50,14 +52,16 @@
|
||||
- [x] **高效直连模型工作区工具**:直连模型使用随包 ripgrep 的原生参数发现文件和搜索内容,按行分页读取大型 UTF-8 文件,并在 Execute 中应用多文件补丁。Ask 限于工作区只读搜索和读取,Execute 按当前账号权限搜索;用户无需另行安装 ripgrep。搜索保留原生输出与退出码,大结果通过 `output_read` 完整续读,可处理的参数错误允许模型调整后继续。
|
||||
- [x] **长上下文与工具输出分页**:历史消息、长回复和文档解析文本不再采用旧的固定截断限制。直连模型可分页续读已保存的搜索、命令和 Subagent 输出;所选模型的上下文窗口及传输边界仍然有效。
|
||||
- [x] **OpenCode 与 Continue**:使用隔离子进程、环境变量白名单、统一配置、取消、启动与控制请求时限、有界流式输出和活动记录;共享进程回收逻辑保留 Windows 完整进程树终止,并对采用独立进程组的 POSIX 子进程执行组回收。聊天状态检查使用一次性探测 Runtime 并在返回后立即回收,不进入按模型与项目隔离的执行 Runtime 缓存;本机 GoodBuddy 管理的 OpenCode 在该被动检查中只确认所选路径和模型凭据,不启动用后即毁的 Server,设置中的显式连接测试和原生能力清单仍执行完整启动及健康检查,原生能力清单随后与首次实际请求复用同一个执行 Runtime,不再重复冷启动;执行 Runtime 仍按项目复用,但 OpenCode 配置依赖和按内容摘要生成的 Skill 快照在所有本机项目间全局复用,会话数据、工具输出及请求级 MCP 状态继续隔离。本机 GoodBuddy 管理的 OpenCode 只对同一会话内的请求保持顺序,不同会话即使属于同一项目也可并行,不同项目同样可并行;请求级动态 MCP 通过默认通配禁用和当前请求精确放行保持隔离。托管的本机和远端 OpenCode 跳过界面未使用的自动 Git 快照,避免同步差异计算拖住会话,文件工具、子代理与工作区 Git 差异保留;Continue 持续排空不用展示的宿主 stdout,防止控制台输出填满管道。这些调整随新启动的 Runtime 生效,不改写外部 OpenCode Server 配置。OpenCode 与 Continue 不再按固定工具调用数或活动数中止单次运行,已观察到的工具与 Subagent 活动全部保留在本地会话,不再在原生任务继续执行时丢弃后续详情。交互提问只由前台对话回答,定时任务、远程通道和委派等后台执行遇到提问时会立即失败并提示改为前台运行,避免无限等待。本机 OpenCode 启动时直接使用已选择的绝对路径,不预先检查文件或运行 `--version`,路径失效时由实际启动返回错误;托管 Linux ARM Host 激活 Runtime 时复用准备阶段已验证的 registry 和 manifest,不重新哈希或检查完整 OpenCode 二进制。
|
||||
- [x] **托管 SSH OpenCode 与 Continue**:该能力由“设置 > 平台功能”中的独立“远程项目”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。Ask 在 Runtime 工具边界保持只读,Execute 使用所选 SSH 账号的完整权限。Agent 持有 Prompt、模型和工具轮次、Runtime 进程及语义 transcript,Desktop 断开后重新 Attach 原操作而不重发 Prompt;模型凭据仅在当前操作内存中使用,不写入 Renderer、SSH 参数、环境或磁盘。当前 Agent 可以常驻,更新替换的旧 Agent 排空活动任务后退出。任务完成后回收空闲 Runtime,共享进程仍有任务或待答问题时保留;下一轮重建 Runtime 并恢复对话历史。设置页按平台和架构显示包状态,用户显式下载、更新或导入/导出包含 Agent、固定 Node、OpenCode 和 Continue 的签名 `.gbagent`;三个目标共用一个签名 catalog,继续校验签名和 SHA-256。Continue 需要更新后的联合包与配套 Desktop。首次发布混合 catalog 前需先升级 Desktop,旧版 Linux-only 目录读取器无法解析包含 Darwin 的目录。历史 macOS OpenCode 原生安装、生命周期、Attach、真实 Ask/Execute 和独立进程组工具取消已在真实 Host 验证,不代表 Continue 的同等实机覆盖。Agent `0.13.4` 已发布,当前 Desktop 候选为 `0.13.16`;详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以独立发布渠道为准。
|
||||
- [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.0`,配套 Desktop 候选为 `0.15.2`;详细运行边界和 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/原生打包与系统休眠唤醒也尚未验证;开发记录不代表版本已经发布。
|
||||
- [x] **远程工作区与长任务更新**:Agent `0.11.23` 新增远程文件/Git 管理和可选模型限额,随包 Runtime 不再施加固定十分钟 Prompt 时限;无限请求时长仍保留独立连接超时。请先升级 Desktop 至 `0.12.11`,再下载并更新 Host 上的 Agent。
|
||||
- [x] **远程原生问答与取消修复**:Agent `0.11.24` 修复远程原生问答转交和取消待答后同一会话续发,需要先升级 Desktop 至 `0.13.0`。
|
||||
- [x] **DeepSeek Harness(预览)**:使用 GoodBuddy 固定 Host 和 OpenAI 兼容模型连接;优先使用管理员提供的连接,否则跟随兼容的默认模型或首个兼容连接,无需单独重复选择,设置页显示实际管理员或回退模型来源。Ask 只允许调用 Host 中真实注册的 `read`、`skill` 以及 Main 管理的 Web Search/Fetch 代理,拒绝插件同名冒充,Execute 放行全部已启用内置及插件工具,并以当前用户权限运行。图像输入跟随所选模型连接的能力声明,文本模型在 Host 或模型调用前拒绝图片,图片模型通过有界内联内容和临时 Attachment Store 接收 JPEG/PNG。Windows Host 启动和 ACP 会话共用规范工作区路径,同目录不同写法可复用正常 Runtime,不再因路径不一致而创建会话失败。
|
||||
- [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)。
|
||||
- **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] **原生执行清单**:OpenCode 和 Continue 在对话顶部更新只读执行清单,进度随会话保存。显式清空和远程重放保留请求归属,取消不把未完成条目标成完成;远程交付需要配套 Agent,详见[清单契约](./docs/features/assistant-workbar/runtime-checklist-technical-design.md)。
|
||||
- [x] **专家与 Subagent**:支持显式专家、团队分析和最多三个专家并行分析。专家继承父请求 Ask/Execute 模式,可使用已启用的本机直连模型工具,Ask 仍只读;这些工具不是远程 OpenCode 子会话。聊天先展示可逐项展开的专家完整输出,再在其下展示总 Agent 的综合结果,并随会话保存。
|
||||
@@ -83,10 +87,12 @@
|
||||
- [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;DeepSeek Harness 在设置中明确显示为暂不支持。内置 MCP 仅通过当前请求的短期本机权限提供,Ask / Execute 读写边界不受用户配置放宽。
|
||||
- [x] **内置 MCP 按需接入**:知识库、魔法笔记、GoodBuddy 配置与内置浏览器 MCP 可分别启停,并可分配给直连模型、GoodBuddy 管理的 OpenCode 和 Continue;这些 Server 在 DeepSeek Harness 中仍不支持。Story Graph 另通过 Main 代理支持 Harness。内置 MCP 仅通过当前请求的短期本机权限提供,Ask / Execute 读写边界不受用户配置放宽。
|
||||
- **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 Prompts 与 Resources 元数据**:MCP 测试仅在 Server 声明对应能力时发现有界的 Prompt、参数与 Resource 元数据,不读取 Resource 内容;Runtime 支持的 Prompt 可填入聊天草稿后继续编辑。OpenCode 可报告实验性 Resource 清单,Continue 当前版本明确不支持 Resources。
|
||||
- [x] **本地知识库**:支持文件、目录和网页导入、SQLite FTS5 检索及来源追溯。知识范围按对话保存,新对话默认不选择知识库;创建与检索高级参数默认折叠。文档区分可用、处理中和失败,支持打开来源、失败重试及移除来源前确认。
|
||||
内容来源与文档索引共用可搜索列表,单文档来源不重复占行,目录保留分组文档,失败来源保留恢复操作;任务历史和次级操作从统一入口访问。
|
||||
- [x] **外部知识库接入**:在知识库页面管理 Dify、FastGPT 和 RAGFlow 实例,通过目录或手工 ID 验证并绑定远端知识库,保留 Provider 专属检索配置和引用;对话支持按需检索及每次回答前检索。外部系统只负责知识检索,不接入其 App、Chat、Workflow 或 Agent,也不批量同步、本地索引或修改远端内容。被回答引用的有界检索片段随会话保存在本机。详见[外部知识库接入 PRD](./docs/features/knowledge-base/external-knowledge-prd.md)。
|
||||
- **外部知识库验证记录**:三家 Provider 已通过真实服务、本机 HTTP MCP 和生产 IPC 短答案检查,Dify 另通过完整 App 输入区到回答及引用的链路。现有记录未覆盖 FastGPT/RAGFlow 桌面专属参数、复杂多库问答和远程回答前检索,详见[验证记录](./docs/features/knowledge-base/progress.md)。
|
||||
- [x] **知识图谱**:支持规则、模型和混合抽取,以及实体、关系、别名和证据维护。
|
||||
@@ -98,13 +104,14 @@
|
||||
- [x] **受控知识本体**:每个知识库可定义实体、关系、别名和端点约束,保留证据偏移、置信度和抽取来源,并显式提示图谱重建。
|
||||
- [x] **强制检索与引用上下文**:对话可按需或每次先检索,显示零结果、降级、失败与取消状态,并可查看引用上下文或安全打开来源。
|
||||
- [x] **魔法笔记 / Magic Notes**:提供本地优先的笔记与待办工作台、范围管理、编辑、筛选和受控 AI 评论。笔记从自适应卡片总览打开;独立待办页提供搜索、状态筛选、分组列表和直接勾选,宽屏列表稳定在左侧,任务说明、来源记录及 AI 评论在右侧同一区域显示,窄屏按需显示详情并提供返回列表。紧凑页签放在页头新建按钮左侧,筛选放在搜索右侧。打开原笔记再返回可恢复任务上下文;切换任务不会混入上一项的来源内容,读取失败使用应用通知并提供就地重试,来源缺失有明确提示。左侧入口可按设置显示未完成待办数量,创建、保存和评论结果使用统一应用通知。Agent / MCP 写入后,已打开的工作台自动更新,并保留当前选择与未保存草稿。
|
||||
- **会话采集与快速笔记**(已实现,真实 Electron 验收通过):单条助手回答或完整会话正文可先预览、编辑,再追加到已有笔记或连同首条记录创建新笔记。右侧笔记 Tab 支持标题与正文搜索、快速追加及打开完整工作区,记录来源可返回会话和消息;切换会话保留草稿。真实 App/preload/IPC/SQLite 验收覆盖完整 502 条历史及 214px 面板原生键盘操作,模型调用为 0。[功能验证进度](./docs/features/magic-notes/progress.md)保留聚焦通过、fixture 修复与当时的测试和类型检查失败;[0.15.0 候选验证](./docs/development/release-preparation-0.15.0.md)已通过全量测试、类型检查和 lint。
|
||||
- 待办宽屏列表与详情之间可拖动分隔条并记住宽度;来源笔记分组可独立折叠,不清除当前选中任务的详情。
|
||||
- [x] **连续记录工作区**:笔记详情连续展示全部记录,左侧索引默认 168px,可拖动右边界或用键盘在 140–320px 内调宽,并记住偏好;索引与右侧 AI 评论独立收起。每条画布记录显示真实首页缩略图、页数和时间,文字记录显示摘要;点击索引只滚动定位,保留当前编辑器和草稿。打开笔记就在标题下方显示空白输入区,保留文字/画布选择和保存操作;新增保存后清空,供下一条记录使用,旧记录通过编辑入口修改。空白输入区和保存后未再修改的记录返回时不提示;保存失败和真实修改仍受保护。窄屏索引使用固定宽度折叠抽屉,不显示调宽分隔条,AI 评论移至底部。
|
||||
- [x] **分页画布笔记**:已集成 PeopleLib Fabric + Quill 编辑器,支持跨页正文、笔迹、高亮、对象选择及变换、浮动文字、图片和纸张模板。PDF 可作为分页底版导入并提取原生文字;正文和批注可导出为栅格 PDF,导出文件不保留可搜索文字层。用户手动保存后,记录正文与二进制资源存入本地笔记文件,SQLite 保留元数据、索引、修订、待办和评论。备份须协调保存 SQLite 与笔记目录,产品无一键备份入口。撤销/重做限当前批注页或 Quill 模式,不提供全局文档撤销、无限画布或 PNG 下载按钮。草稿、已保存记录和来源为画布的待办按默认模型图像输入能力使用页面图像加文字,或明确标注仅文字降级;“发送画布页数”可选 1~8,默认 1,按当前顺序发送前 N 页,文字和图片范围一致,保存与导出仍支持 50 页。纯视觉记录/草稿需要支持图像输入的模型。即时评论模式提供手动画布分析,保存后自动分析失败不阻断或回滚保存。提取文字不变时保留文字评论;布局变化使视觉评论失效,并在保存后自动评论模式下重新分析。MCP 禁止以纯文字覆写画布记录。实现已完成,最终验证状态见[功能进展](./docs/features/magic-notes/progress.md)。
|
||||
编辑与只读画布均支持 25%–300% 视图缩放、100% 还原及随视口调整的“适应宽度”,不改变保存内容或导出尺寸。
|
||||
首次升级将历史正文迁移到文件,并随附件变更把数据库升级至 schema 39;旧客户端不能再打开,回退需使用升级前备份。
|
||||
历史正文迁移已改用文件存储,当前 schema 48 新增会话来源元数据;旧客户端不能再打开,回退需使用升级前备份。
|
||||
- [ ] **MCP Server Control Plane**(规划中):统一 MCP 生命周期、健康检查、重连、Schema 缓存、隔离、审批和审计。
|
||||
- [ ] **可追溯笔记摘录与 AI 编辑**(规划中):从对话、知识和网页收集带来源的摘录,并提供需确认的总结、改写和整理操作。
|
||||
- [ ] **知识/网页摘录与 AI 编辑**(规划中):在已实现的会话正文采集之外,扩展知识和网页摘录,并提供需确认的总结、改写和整理操作。
|
||||
|
||||
### 工作管理、长期协作与工作流
|
||||
|
||||
@@ -115,11 +122,14 @@
|
||||
- [x] **定时消息沿用会话**:定时消息使用会话当前历史、Runtime、工作模式和已保存的知识检索设置;任务详情显示实际运行模式,当前 occurrence 未结束时禁止重复“立即运行”。
|
||||
任务中心通过状态数量和筛选定位运行中及待处理任务,审批操作保留在对应任务卡片中。
|
||||
- [x] **自动监督设置与范围**:“监督者 > 自动监督”统一管理计划、自动监督报告和建议,支持创建和编辑 Global 或指定一个、多个 Project 的每日、每周计划;工作回顾阅读带日期的统一监督结果及历史。旧单项目配置无损迁移,项目级记忆与行动输出必须显式指定范围内的 Project。Task Center 和设置中心不再复制心跳表单;按分区回顾、候选生成和唤起条件仍待设计。详见 [智能心跳 PRD](./docs/features/smart-heartbeat/prd.md)。
|
||||
- [x] **监督者回顾、故事线图谱与活动**:按范围和时间回顾工作,查看保存结果的图谱及来源,确认或修订实体,预览写入本地知识库实体。侧栏可跟随或固定 Conversation、Task,并打开匹配结果。活动合并心跳报告与下游监督的阶段状态,保留失败和旧结果入口。自动阶段只处理新增、修改及未处理的来源片段,无变化时跳过模型;手动回顾重新整理有界历史。回顾只读且禁止工具,模型调用可能产生费用,详见[实施与验证记录](./docs/features/conversation-supervision/progress.md)。
|
||||
- [x] **监督者回顾、故事线图谱与活动**:按范围和时间回顾工作,查看保存结果的图谱及来源,确认或修订实体,并带入相关回顾上下文继续讨论。侧栏可跟随或固定 Conversation、Task,并打开匹配结果。活动合并心跳报告与下游监督的阶段状态,保留失败和旧结果入口。自动阶段只处理新增、修改及未处理的来源片段,无变化时跳过模型;手动回顾重新整理有界历史。回顾只读且禁止工具,模型调用可能产生费用,详见[实施与验证记录](./docs/features/conversation-supervision/progress.md)。
|
||||
手动开始、自动触发和继续共用一个活动回顾;活动记录可取消并保留已保存批次,清理结束前不接纳新回顾,已取消回顾不可继续。故事线事件、实体和关系按独立页签展示数量,并与画布选择同步。
|
||||
可从监督反馈或图谱来源直接继续讨论,编辑问题并明确打开目标会话;界面不再提供知识实体写回控件或重复上下文说明,自动监督没有活动时隐藏空指标。
|
||||
- 手动回顾分页处理所选历史,并按批保存事实与来源;不再按固定时长自动暂停,仍支持主动暂停和从保存进度继续。设置提供并发、单次请求超时、批次大小和响应容量;活动记录突出相连阶段、已保存批次与剩余来源,次要统计可展开查看。工作回顾采用全宽纵向布局,运行状态单独显示,空态不再挤在控件旁。
|
||||
- [ ] **监督后续能力**(规划中):逐事件完整回放、Experiment 目标、人工修改时间序列审计、事件触发及更完整的执行观察仍未完成。当前自动回顾受输入预算和滚动窗口限制,删除报告不会重置处理进度或重建图谱。详见 [会话监督 PRD](./docs/features/conversation-supervision/prd.md)。
|
||||
- [ ] **批量运行与对比实验室**(规划中):对模型、Prompt、角色和工作流配置执行批量对比,汇总质量、耗时、Token、费用、失败率和成果差异。
|
||||
- [ ] **时态记忆与事实冲突检测**(规划中):为记忆和知识图谱增加有效期、当前事实、过期与矛盾检测、事实核验及证据回溯。
|
||||
- **Story Graph 只读工具(未发布源码)**:Agent 可搜索已保存的工作,读取当前主张或时间线,并分页核对来源。监督者应用开关及 Runtime 分配同时控制发现和调用,旧会话也受限制。Model、本地 Harness、本地与远程 OpenCode/Continue 共用桌面读取入口;查询不调用模型,不以较新回顾推断替代关系。跨 scope 事实复用及 `as_of` 仍待实现,详见[设计与验证](./docs/features/conversation-supervision/story-graph-mcp-design.md)。
|
||||
- [ ] **可视化受控工作流**(规划中):提供版本化 DAG、条件分支、审批、取消和恢复,执行节点继续经过 Main Runtime 边界。
|
||||
- [ ] **统一 Run Graph 与回放**(规划中):关联任务、Subagent、模型、知识、工具审批、用量和成果,支持失败定位、重试和脱敏导出。
|
||||
|
||||
@@ -143,12 +153,14 @@
|
||||
|
||||
### 开源、构建与发布
|
||||
|
||||
- 当前 Desktop 候选为 `0.13.16`,Agent `0.13.4` 已发布,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。
|
||||
- 当前 Desktop 候选为 `0.15.2`,Agent 候选为 `0.15.0`,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。
|
||||
- Desktop `0.15.1` 已完成六平台原生打包及 GitHub/OSS 发布。Desktop `0.15.2` 新增 Story Graph 只读工具和完整可编辑的监督讨论消息,简化笔记采集并补充退出诊断;远程图谱读取需配套 Agent `0.15.0`。主机与云环境管理应用仍为待实现规划,当前候选证据见[0.15.2 发布准备](./docs/development/release-preparation-0.15.2.md)。
|
||||
- Desktop `0.15.1` 承接未发布的 `0.15.0` Obsidian 接入、会话采集到魔法笔记和预览中的本地原生客户端,并修复知识库、监督者与界面操作。schema 48 新增会话来源元数据,回退旧客户端需使用升级前完整备份。Agent `0.15.0` 要求 Desktop `0.15.2`,保留 Node `24.19.0`,补齐远程遥测关闭配置及 Continue 跳过已关闭的更新检查,不新增远程客户端快捷入口。
|
||||
- Desktop `0.13.15` 承接未发布的 `0.13.14`,改善监督者分页回顾与续跑、ripgrep 原生搜索、待办布局和远程结果未知后的显式续聊。数据库升级至 schema 47,回退旧客户端需使用升级前完整备份。
|
||||
- Desktop `0.13.16` 分离结构升级与旧历史回收,移除回顾固定时长自动暂停,并改善监督者布局。相较 `0.13.15` 不新增 schema 变更,也不要求更新 Agent。
|
||||
- Agent `0.13.3` 回收空闲 Runtime,并使被替换的 Agent 等已有工作结束后退出;复合包要求 Desktop `0.13.12`,请先升级 Desktop,再更新 Host 环境。Node 保持 `24.19.0`。
|
||||
- Agent `0.13.4` 在完成前等待模型交付确认,空闲 Runtime 回收失败不改变已完成结果,并在环境更新后清理已确认停用的旧程序及所属孤儿进程;保留历史、活动及状态不明的安装。独立包要求 Desktop `0.13.14` 或更新版本;因 `0.13.14` 未发布,请先升级至 Desktop `0.13.15`,再更新 Host。
|
||||
- 发布准备要求全量测试、发布说明校验、类型检查及 lint;候选主分支 CI、生产构建、原生打包及公开发布核验为独立验收步骤。本轮不做本地生产构建、打包或龙芯预览,当前验证状态见[发布准备记录](./docs/development/release-preparation-0.13.16.md)。
|
||||
- 发布准备要求全量测试、发布说明校验、类型检查及 lint;候选主分支 CI、生产构建、原生打包及公开发布核验为独立验收步骤。本轮不做本地生产构建、打包或龙芯预览,当前验证状态见[发布准备记录](./docs/development/release-preparation-0.15.0.md)。
|
||||
- Desktop `0.13.12` 和 Agent `0.13.3` 已完成原生发布流程,但不替代开发期真实 Host 场景覆盖。
|
||||
- 验证记录与功能状态分开。存储已有本地及 Linux x64 Host 真实工具任务、多项目多会话、取消和无损迁移验证;进程复用有 Windows 完整 App 与 Linux x64 Host 证据,PPTX OCR 与空闲内存释放有 Windows 真实文件证据。图片工具已有真实服务商生成/编辑及 Host 传输证据,这些记录未覆盖完整界面自然语言调用和本地/远程切换。候选仍需主分支 CI 及原生发布打包,详见[Runtime 验证记录](./docs/features/assistant-workbar/progress.md)与[图片工具验证记录](./docs/features/conversation-media-generation/progress.md)。
|
||||
- 发布准备前,`9836a4c` 已通过本地 4,858 项测试(67 项跳过)、完整类型检查和 lint;真实服务商画布分析仍未验证。该提交的三个原生 Agent CI 构建通过;桌面 CI `35485936807` 为 4,875 项通过、8 项 Electron 测试失败,均因 Linux 沙箱辅助程序未配置,在页面断言前退出。候选配置该程序并在 Xvfb 下运行测试,不关闭沙箱;候选本地验证、主分支 CI 与原生标签打包分别记录,不在发布准备中运行本地生产构建或打包。
|
||||
@@ -166,7 +178,7 @@
|
||||
- [ ] **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 传输矩阵,发布结果另行记录。
|
||||
- [ ] **多云远程沙盒 Agent**(规划中):通过云厂商 API 和 SSH Agent 管理专用 Linux 沙盒;凭据留在 Main 进程,高风险控制面操作单独确认。
|
||||
- [ ] **主机管理与云环境管理**(规划中):主机管理迁入应用中心,提供列表与详情工作页面;云环境管理作为同级应用,复用主机连接和执行能力。首版适配计划覆盖 AWS、Azure、阿里云、腾讯云、VMware vCenter / vSphere、ZStack,分批完成已有实例发现、接入及真实执行验收;实例创建、启停、销毁和网络编排后续交付。平台 API 尚未实现,现有云主机可按 SSH 条件手工接入。详见[产品范围](./docs/features/remote-host/prd.md)。
|
||||
|
||||
## 规划原则
|
||||
|
||||
|
||||
+22
-5
@@ -68,7 +68,7 @@
|
||||
| `--success`、`--success-subtle` | 成功状态 |
|
||||
| `--warning` | 警告状态 |
|
||||
| `--danger`、`--danger-strong` | 危险文本和状态 |
|
||||
| `--danger-solid` | 最终确认按钮等需要反白文字的危险背景 |
|
||||
| `--danger-solid`、`--danger-solid-hover` | 最终确认按钮等需要反白文字的危险背景及悬停背景 |
|
||||
| `--danger-border`、`--danger-subtle` | 危险入口和失败状态背景 |
|
||||
|
||||
浅色与深色具体值只在 `styles.css` 的主题根节点维护。状态组件必须同时显示文字或图标,不能仅靠颜色区分。
|
||||
@@ -116,6 +116,7 @@
|
||||
|
||||
- 业务组件通过 `--font-family-ui` 与字体尺寸令牌继承字体,不创建页面专属字体栈。
|
||||
- 表单按钮、输入框、选择框和文本域必须继承界面字体,避免回退为原生控件字体。
|
||||
- 文本表单复用 `.field` 下的共享输入规则;不适合增加字段包装层的独立输入或文本域使用 `.field-control`。文本输入规则排除 Checkbox 和 Radio,保留全局 `:focus-visible` 轮廓,不清除 Switch 焦点环。
|
||||
- 连续阅读内容使用 `14px`,持久辅助信息不得小于 `11px`。
|
||||
- 本地兜底字体资源必须随生产包交付,并同时包含 Inter 与 Noto Sans SC 的 OFL 许可证。
|
||||
- 页面内不得通过同时放大字号、加粗和使用强调色制造多个同级主标题。
|
||||
@@ -269,6 +270,11 @@
|
||||
- `danger-solid`:确认对话框中的最终危险操作。
|
||||
- `danger-zone`:设置页中集中展示的高影响操作区域。
|
||||
|
||||
`danger-solid` 必须是全局共享类,不依赖页面祖先;通过 Portal 挂到 `body` 后仍须生效。
|
||||
尺寸、圆角、字体、间距和禁用状态与 `primary-button`、`secondary-button` 共用规则,
|
||||
键盘焦点沿用全局 `button:focus-visible`。背景及可用态悬停使用危险实心色令牌,文字使用
|
||||
`--text-on-accent`;禁用时不触发悬停变色。
|
||||
|
||||
普通页面不得用实心红色按钮与主操作并列。危险入口必须使用具体动词和对象,例如“删除知识条目”,避免只写“确定”。
|
||||
|
||||
### 6.7 数据表格与活动列表
|
||||
@@ -289,6 +295,8 @@
|
||||
- 当前导航项和当前会话使用一致的淡蓝选中背景、强调色文字和中等字重,不额外描边或添加侧边标记;导航图标随选中状态使用强调色,悬停时保留选中状态。浅深主题遵循相同规则,强制颜色模式保留系统选中边框。
|
||||
- 固定导航行高至少 `36px`、行间距 `4px`,图标统一为 `16px`;导航与最近会话分隔线上下各留 `16px`。普通待办和待处理建议数量使用中性色徽标,不使用危险红色;徽标高度不得改变导航行高。搜索框获得内部焦点时显示完整焦点环。
|
||||
- 未选中项保持平整,不为每一行添加卡片边框或阴影。悬停反馈不得强于选中状态。
|
||||
- 最近会话的顺序不随流式输出增量反复变化;置顶与新消息排序遵守
|
||||
[会话列表排序规则](./docs/features/application-tool-navigation/ui-design.md#会话置顶与操作菜单)。
|
||||
- 项目选择器顶行保留项目切换与新建按钮;项目设置统一从下拉列表对应行的管理按钮进入,关闭后焦点返回项目选择器。
|
||||
- 项目下拉列表通过 `FloatingPortal` 浮在侧栏与会话区上方,默认宽度 `380px`,不受侧栏裁剪;距窗口边缘至少 `16px`,窄窗口自动收窄,长列表内部滚动。浮层锚定项目选择按钮,窗口、滚动和尺寸变化后重新定位。
|
||||
- 项目选择器下方仅在有运行中、待处理或已完成未查看会话时显示跨项目活动汇总,无活动时隐藏;项目计数与项目、会话级联菜单遵守
|
||||
@@ -348,13 +356,14 @@
|
||||
“当前项目”和“所有项目”;无项目任务在“所有项目”中标注“未绑定项目”。范围变化不得创建第二个 Task Center。每个 Task 只
|
||||
关联一条 Conversation,一条 Conversation 可以关联多个 Task;列表不得复制会话内容,也
|
||||
不得把 Job/Run 提升为可导航 UI 对象。
|
||||
- 各栏目读取当前会话或项目的对应内容;当前内容不可用时仍保留栏目入口并说明原因。
|
||||
- 能力、连接和内容可以动态变化,栏目入口不能随之自动隐藏。不可用状态必须说明原因、影响和可执行入口。
|
||||
- 各栏目按声明的范围读取内容;笔记使用应用级数据,选择和草稿不跟随项目或会话变化。当前内容不可用时仍保留栏目入口并说明原因。
|
||||
- 能力、连接和内容可以动态变化,栏目入口不能随之自动隐藏。不可用状态必须说明原因、影响和可执行入口。笔记遵循应用中心的业务启用设置,禁用前处理草稿,禁用后隐藏目录入口并移除笔记 Tab。
|
||||
- 用户可以主动排序栏目,并可关闭可关闭实例或恢复默认布局;任务中心和工作区不能隐藏或
|
||||
关闭。应用不能用用户偏好机制实现自动能力裁剪。
|
||||
- 已打开实例保留 `tablist`、`tab`、`tabpanel`、方向键、Home、End 和焦点恢复语义。
|
||||
实例过多时 Tab 区单行横向滚动,“+”保持可见,不换行或改成下拉框。不可关闭实例不渲染
|
||||
关闭按钮,也不响应关闭快捷命令。
|
||||
“+”位于滚动容器外并占用固定宽度;滚动箭头限定在剩余的页签区域内,不能覆盖添加入口。
|
||||
- 当前工作栏 Tab 必须同时使用连续内容表面、可见边框、顶部指示、图标或较高字重中的至少
|
||||
两种信号;悬停反馈不得强于当前选中态。Tab 关闭按钮保留固定布局空间,默认仅在对应 Tab 悬停或
|
||||
`focus-within` 时显示;无悬停或粗指针设备始终显示,键盘与辅助技术始终可访问。
|
||||
@@ -411,13 +420,13 @@
|
||||
绘制,避免被 Modal 或滚动容器遮挡。菜单和帮助挂在触发按钮所属 Modal 内,通知跟随当前活动
|
||||
Modal,无 Modal 时挂在 `body`;不能只提高 `z-index` 或解除背景的 `inert`。
|
||||
- 原生浏览器 `WebContentsView` 不受网页 `z-index` 控制。应用级 Modal 打开时统一释放
|
||||
浏览器 viewport;非模态对话框、菜单、局部确认或应用通知与浏览器区域相交时也释放。
|
||||
浏览器 viewport;非模态对话框、菜单、同行帮助 tooltip、局部确认或应用通知与浏览器区域相交时也释放。
|
||||
浮层关闭后只恢复当前活动实例的 viewport,不关闭、重建或刷新网页。接线与测试见
|
||||
[浏览器多实例技术设计](./docs/features/assistant-workbar/browser-tabs-technical-design.md#4-renderer-与-viewport)。
|
||||
- 遮罩必须 `inset: 0` 铺满整窗,并由 `styles.css` 中统一的遮罩选择器声明
|
||||
`-webkit-app-region: no-drag`,否则 `.brand` 与 `.topbar` 的原生拖动区域会截获指针,
|
||||
使遮罩上的控件无法悬停和点击。新增整窗遮罩时扩展该共享选择器,不要在各自规则里复制
|
||||
`no-drag`,也不要用 `inset: 38px 0 0` 之类的偏移绕开标题栏。
|
||||
`no-drag`,也不要用 `inset: 38px 0 0` 之类的偏移绕开标题栏。遮罩回归按 fixed/inset 声明发现,不能只按 `backdrop` 或 `modal` 类名后缀筛选;知识引用的 `-dialog` 同样适用。
|
||||
- 当前会话存在 Task 时,Task 上下文栏位于标题栏下方并承载任务摘要与详情;没有 Task 时
|
||||
整行隐藏。折叠状态保持为 `46px` 单行表面,与右侧工作栏标签栏等高,左右分隔线必须水平
|
||||
对齐。该栏不重复放置新建入口。
|
||||
@@ -500,6 +509,7 @@ Switch 用于在两个持久状态之间立即切换,例如启用能力、开
|
||||
- `footer` 使用共享操作区,按钮整体右对齐,次要取消在前、主要保存在后。放弃修改确认沿用同一操作区,继续编辑在前、危险操作在后。
|
||||
- header、滚动 body、footer 是 surface 的三个直接子区域。沿用共享 Grid 的 `auto minmax(0, 1fr) auto`,只有 body 滚动,短窗口仍可见标题、关闭和提交操作;窄窗口沿用共享外边距、内边距和主题令牌。
|
||||
- X、取消、Escape 和遮罩关闭共用业务关闭检查,不绕过未保存确认或提交锁定。确认中的 Escape 或 X 返回编辑;继续复用 `activateModalFocus`、`trapTabFocus`,保留背景隔离、初始焦点和关闭后的焦点恢复。保存失败保留草稿及可恢复错误。
|
||||
- 从已有 Modal 打开的子 Modal 同样复用 `--z-dialog` 和 `activateModalFocus`,以较后挂载的 body Portal 绘制在父层上方,并隔离父层;不能只手动聚焦或使用旧的页面层级常量。子层关闭后恢复父层的隔离状态与触发按钮焦点,Escape 不继续关闭父层。
|
||||
|
||||
## 7. 交互状态
|
||||
|
||||
@@ -516,6 +526,8 @@ Switch 用于在两个持久状态之间立即切换,例如启用能力、开
|
||||
|
||||
异步提交成功后更新内容并通过统一应用通知提供明确反馈。失败时保留用户输入和筛选上下文。
|
||||
|
||||
共享控件的状态规则须在容器组合中生效:菜单通用按钮样式不能覆盖危险按钮,普通图标悬停不能覆盖危险图标或作用于禁用按钮,页签悬停保留当前选中态。深色主题通过令牌提供控件颜色,避免用页面祖先下的通用输入规则替换控件边框。验证时读取最终 computed style;共享令牌对比度与源码规则存在性检查不能代替容器内、body Portal 和伪状态回归。修复与验证记录见[共享控件审查](docs/quality/shared-controls-review-2026-09-27.md)。
|
||||
|
||||
## 8. 范围与数据语义
|
||||
|
||||
### 8.1 页面范围
|
||||
@@ -654,6 +666,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
全屏预览遵循统一 Modal 的背景隔离、初始焦点、Tab 循环、Escape 关闭和焦点恢复规则,
|
||||
并沿用同一套清理与 Sandbox/CSP 边界。关闭平台功能后必须显示 HTML 源码,不能隐藏内容
|
||||
或保留旧预览。
|
||||
Tab 和 Shift+Tab 必须能进入及离开 iframe;焦点在预览内部时 Escape 仍关闭当前全屏层并返回入口。空 sandbox 与禁脚本 CSP 保持不变,跨文档键盘接线见[HTML 渲染技术设计](docs/features/conversation-html-rendering/technical-design.md#ui-与无障碍)。
|
||||
- Mermaid 大图支持缩放和拖动;图形超过画布时,四个边缘都必须能通过滚动到达,不能因居中而裁掉顶部或左侧。
|
||||
弹窗工具栏提供“导出 PNG”,保存完整图形,不受当前缩放和滚动位置影响;背景使用当前主题的画布底色。
|
||||
导出按原图四周留出 `16px`,以 2 倍分辨率绘制,任一边最多 `4096px`。导出期间禁用重复点击,失败在按钮旁提示重试。
|
||||
@@ -759,6 +772,8 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
|
||||
### 13.7 魔法笔记
|
||||
|
||||
会话联动已接入:右侧提供可关闭的单实例笔记 Tab,通过工作栏“+ → 笔记”打开;左下角和聊天页头不再提供重复的“快速笔记”入口。助手消息和会话标题菜单可打开文字采集预览;保存记录在面板和完整工作区显示独立来源条。草稿由 App 保留,关闭、替换及禁用前确认。具体交互由[魔法笔记会话联动界面设计](./docs/features/magic-notes/ui-design.md)维护。真实 Electron 验收已通过,全仓验证未全绿,见[验证进度](./docs/features/magic-notes/progress.md)。下列规则描述独立工作区。
|
||||
|
||||
- 笔记总览与待办使用一个普通按钮切换,放在页头“新建笔记”按钮左边。按钮显示目标“切换到待办”或“切换到笔记”及对应图标,按内容宽度显示,不拉伸;窄窗口操作组可换行到标题下方,保留控件高度。
|
||||
- 切换按钮不使用页签语义,切换后焦点回到同一个按钮;内容 section 持续提供当前列表的 `aria-label`。切换沿用草稿保护入口,待办状态仍使用独立的 `SegmentedControl`。
|
||||
- 文字记录编辑器保留 Quill 原生列表快捷输入:输入 `-`、`*`、`1.`、`[ ]` 或 `[x]` 后按空格,分别转换为项目符号、编号或待办清单。快捷输入只生成现有 Delta 富文本,不引入 Markdown 源码模式、第二套文字记录持久化格式或第三方 Markdown 插件;新建文字记录的操作提示收进类型选择旁的 `InlineHelp`。
|
||||
@@ -808,11 +823,13 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
“打开智能心跳”导航,不复制创建、暂停、恢复或删除表单。
|
||||
- 保存或测试成功统一进入应用通知视口,并按全局规则自动消失,不在分类页头或内容卡片中保留持久成功文案。加载、保存和测试错误显示在分类页头下方,并保留可处理的上下文。
|
||||
- “模型连接”的认证字段按“认证方式 → API Key → 凭据状态”排列;只有选择 API Key 时显示输入框。已保存的 API Key 属于该连接,修改模型服务地址或临时选择“无需认证”不得自动清除,也不得要求重新输入;仅由明确的“清除凭据”或“删除连接”操作移除。
|
||||
- 设置中的连接列表与详情共用 `.model-connection-manager`,按容器可用宽度响应:不超过 `640px` 时上下排列,列表内部滚动高度最多 `180px`;更宽时恢复左右排列。LLM、向量连接和角色编辑复用此规则,不以整窗宽度代替表单宽度。接口协议的中英文当前值须完整可读。回归范围见[窄容器布局修复记录](docs/quality/responsive-controls-review-2026-09-27.md)。
|
||||
- “保存并测试模型”必须在保存后发送一次有界的真实模型请求。文本连接校验本次随机测试文本,图像连接校验实际返回的内联图片;仅收到成功 HTTP 状态、模型列表或健康检查响应不算测试成功。界面持续说明该操作可能产生少量服务商用量费用,成功反馈明确写为“真实生成测试通过”。
|
||||
- 所有显式保存的设置草稿都参与离开保护:关闭、切换分类、主侧栏或工作区导航及托盘导航不得静默丢弃,统一通过设置中心的就地确认提供继续编辑与明确放弃入口,保存失败后保留输入。“平台功能 / 通用设置”承载全局快捷唤起的共享 Switch、可访问 accelerator 录制输入、恢复默认、保存及注册、停用或冲突状态,不新增分类或页签;注册或持久化失败时保留上一组可用快捷键和当前草稿,保存或停用成功后同步更新输入区的快捷键提示。
|
||||
- “平台功能 / 通用设置”同时提供默认开启的“在会话中渲染 HTML”持久 Switch。说明必须
|
||||
明确该能力只生成禁用脚本、联网、表单提交和窗口操作的静态预览;保存成功后当前会话立即
|
||||
更新,保存失败时保留最后确认状态。
|
||||
- 通用设置按快捷唤起、会话与通知、工作目录与下载分组,复用现有卡片。“桌面通知”为默认开启的持久 Switch,保存后立即控制窗口未聚焦时的任务完成、失败和取消系统通知,关闭不影响应用内通知。旧配置缺少该字段时默认开启,明确关闭后重启仍保持关闭;保存失败保留最后确认状态并显示应用内错误通知。
|
||||
- “平台功能”使用共享 `PageTabs` 区分“通用设置”和“远程项目”,默认进入通用设置。“远程项目”页将持久 Switch 与用户数据目录中的 Linux x64、Linux arm64、macOS arm64 Agent 包清单分成独立卡片;每张卡片与下载进度按平台和架构区分,不能把两个 arm64 目标合并。清单延迟到首次打开该页签时由 Main 完整校验,并读取所选更新源的小型签名目录,显示本地版本、在线最新兼容版本以及“有更新”“已是最新”“未下载”“校验失败”。打开页签或刷新不得自动下载 `.gbagent`;只有用户点击带目标版本的下载/更新按钮后才传输包。签名目录不可用时继续显示并允许导出已验证本地包,提供就地重试错误;清单不混入 Host 已安装状态、缓存路径或敏感签名细节。下载和导入期间保留上下文进度,成功进入全局通知视口。全局模型下载源使用 `fieldset`、持久 `legend` 与整行可点击的原生 Radio 卡片;选中状态同时依靠 Radio、边框和背景表达,读取失败时不得用默认值伪装为已保存选择。
|
||||
- “关于与更新”的更新源位于“启动时检查新版本”开关下方,常规宽度下将标签、同行帮助和原生单选下拉框放在同一行,用途说明收进 `InlineHelp`,并复用设置表单的统一控件样式;关闭启动检查后仍可选择更新源,手动检查继续使用当前选择。选项显示“GitHub(默认)”和中性的“镜像节点”。该选择同时控制手动检查、启动时检查、下载页和 Agent 包在线下载,不显示底层服务商名称。
|
||||
- “关于与更新”在版本信息之后提供应用内反馈卡片。反馈对话框使用原生字段、明确隐私说明和一张可选截图;另提供默认关闭的原生 Checkbox,让用户主动附加最近桌面诊断摘要。Checkbox 附近必须持续说明摘要只含时间、组件、阶段、稳定错误码、错误类型和固定短消息,不含对话、Prompt、凭据、文件内容、路径、Provider 原始响应或远端 Agent 日志。勾选时为诊断摘要预留描述字符预算,超长草稿必须保留并显示字段错误,不得静默截断。失败时保留草稿、勾选状态和请求编号并提供就地重试,成功时在对话框内保留可复制编号而不重复发送全局通知;对话框遵循统一 Modal 的背景隔离、初始焦点、Tab 循环、Escape、焦点恢复和窄窗口布局。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"agentVersion": "0.13.4",
|
||||
"agentVersion": "0.15.0",
|
||||
"protocol": {
|
||||
"major": 2,
|
||||
"minor": 0
|
||||
|
||||
@@ -159,7 +159,10 @@ function portableRequiredPaths(directory) {
|
||||
}
|
||||
|
||||
function assertPortableOutput(directory) {
|
||||
const requiredPaths = portableRequiredPaths(directory)
|
||||
const requiredPaths = [
|
||||
...portableRequiredPaths(directory),
|
||||
join(directory, 'resources', 'runtimes', 'dsh', 'node_modules', '@deepseek-ai', 'dsh', 'lib', 'bin.js')
|
||||
]
|
||||
const missing = requiredPaths.filter(
|
||||
(filePath) => !statSync(filePath, { throwIfNoEntry: false })?.isFile()
|
||||
)
|
||||
|
||||
@@ -184,7 +184,7 @@ async function run() {
|
||||
model: 'qwen-plus',
|
||||
supportsImageInput: false,
|
||||
requestHeaders: {},
|
||||
harnessVersion: '0.1.2-rc.1',
|
||||
harnessVersion: '0.1.7-rc.2',
|
||||
credentialRefs: ['GOODBUDDY_HARNESS_MODEL_API_KEY'],
|
||||
skillPackages: [],
|
||||
extensionPackages: [],
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
const { cp, mkdir, readFile, rm } = require('node:fs/promises');
|
||||
const { existsSync } = require('node:fs');
|
||||
const { dirname, join, relative } = require('node:path');
|
||||
const tar = require('tar');
|
||||
|
||||
// Preserve npm's installed layout (including nested versions) and every package
|
||||
// asset. The official loader discovers plugins and browser assets at runtime.
|
||||
async function prepareNativeDshResources(projectDir, platform, architecture, downloadPackage, lockedIntegrity) {
|
||||
const target = join(projectDir, '.runtime-resources', `dsh-${architecture}`);
|
||||
await rm(target, { recursive: true, force: true });
|
||||
await mkdir(target, { recursive: true });
|
||||
const visited = new Set();
|
||||
function locate(name, from) {
|
||||
for (let directory = from; ; directory = dirname(directory)) {
|
||||
const candidate = join(directory, 'node_modules', name);
|
||||
if (existsSync(join(candidate, 'package.json'))) return candidate;
|
||||
if (dirname(directory) === directory) return undefined;
|
||||
}
|
||||
}
|
||||
async function copyPackage(directory) {
|
||||
if (visited.has(directory)) return;
|
||||
visited.add(directory);
|
||||
const manifest = JSON.parse(await readFile(join(directory, 'package.json'), 'utf8'));
|
||||
await cp(directory, join(target, relative(projectDir, directory)), { recursive: true, verbatimSymlinks: true });
|
||||
const optional = manifest.optionalDependencies ?? {};
|
||||
for (const name of Object.keys({ ...manifest.dependencies, ...manifest.peerDependencies, ...optional })) {
|
||||
const dependency = locate(name, directory);
|
||||
if (dependency) await copyPackage(dependency);
|
||||
else if (!(name in optional) && !manifest.peerDependenciesMeta?.[name]?.optional) {
|
||||
throw new Error(`DS Web bundle is missing ${name} required by ${manifest.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
await copyPackage(join(projectDir, 'node_modules', '@deepseek-ai', 'dsh'));
|
||||
// Cross-architecture packaging cannot rely on the build host's npm optional
|
||||
// selection. Fetch the exact target Sharp packages from the existing lock.
|
||||
const sharp = JSON.parse(await readFile(join(projectDir, 'node_modules', 'sharp', 'package.json'), 'utf8'));
|
||||
const lock = JSON.parse(await readFile(join(projectDir, 'package-lock.json'), 'utf8'));
|
||||
const nativePackages = [`@img/sharp-${platform}-${architecture}`];
|
||||
if (platform !== 'win32') nativePackages.push(`@img/sharp-libvips-${platform}-${architecture}`);
|
||||
for (const name of nativePackages) {
|
||||
const version = sharp.optionalDependencies[name];
|
||||
if (!version) throw new Error(`DS Web Sharp does not support ${platform}/${architecture}`);
|
||||
const destination = join(target, 'node_modules', 'sharp', 'node_modules', name);
|
||||
if (existsSync(join(destination, 'package.json'))) continue;
|
||||
const locked = lock.packages[`node_modules/sharp/node_modules/${name}`] ?? lock.packages[`node_modules/${name}`];
|
||||
if (locked?.version !== version || !locked.integrity) throw new Error(`Missing locked ${name}@${version}`);
|
||||
const integrity = locked.integrity;
|
||||
const archive = await downloadPackage(projectDir, name, version, integrity);
|
||||
await mkdir(destination, { recursive: true });
|
||||
await tar.x({ file: archive, cwd: destination, strip: 1 });
|
||||
}
|
||||
const koffi = JSON.parse(await readFile(join(projectDir, 'node_modules', 'koffi', 'package.json'), 'utf8'));
|
||||
const koffiName = `@koromix/koffi-${platform}-${architecture}`;
|
||||
const koffiDestination = join(target, 'node_modules', koffiName);
|
||||
if (!existsSync(join(koffiDestination, 'package.json'))) {
|
||||
const integrity = await lockedIntegrity(projectDir, koffiName, koffi.version);
|
||||
const archive = await downloadPackage(projectDir, koffiName, koffi.version, integrity);
|
||||
await mkdir(koffiDestination, { recursive: true });
|
||||
await tar.x({ file: archive, cwd: koffiDestination, strip: 1 });
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
module.exports = { prepareNativeDshResources };
|
||||
@@ -88,7 +88,7 @@ async function main() {
|
||||
`${JSON.stringify(
|
||||
{
|
||||
name: '@deepseek-ai/dsh-llm',
|
||||
version: '0.1.2-rc.1',
|
||||
version: '0.1.7-rc.2',
|
||||
private: true,
|
||||
type: 'module'
|
||||
},
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
const { dirname, join, resolve } = require('node:path')
|
||||
const { copyFileSync, mkdtempSync, mkdirSync, rmSync } = require('node:fs')
|
||||
const { tmpdir } = require('node:os')
|
||||
const { pathToFileURL } = require('node:url')
|
||||
const assert = require('node:assert/strict')
|
||||
|
||||
if (!process.versions.electron || process.env.ELECTRON_RUN_AS_NODE === '1') {
|
||||
const { spawnSync } = require('node:child_process')
|
||||
const root = mkdtempSync(join(tmpdir(), 'goodbuddy-obsidian-ui-'))
|
||||
const env = { ...process.env, GOODBUDDY_OBSIDIAN_ACCEPTANCE_ROOT: root }
|
||||
if (process.argv.includes('--live') || process.argv.includes('--chat')) {
|
||||
env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_LIVE = process.argv.includes('--live') ? '1' : '0'
|
||||
env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_CHAT = process.argv.includes('--chat') ? '1' : '0'
|
||||
env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_CONTINUE_ONLY = process.argv.includes('--continue-only') ? '1' : '0'
|
||||
require('esbuild').buildSync({ entryPoints: ['tests/obsidian-runtime-acceptance.ts'],
|
||||
outfile: join(root, 'runtime.cjs'), bundle: true, external: ['electron', 'ssh2', 'node-pty', 'koffi'], platform: 'node', format: 'cjs',
|
||||
banner: { js: `require = require('node:module').createRequire(${JSON.stringify(resolve('package.json'))});` } })
|
||||
}
|
||||
delete env.ELECTRON_RUN_AS_NODE
|
||||
try {
|
||||
const result = spawnSync(require('electron'), [__filename], {
|
||||
env, stdio: 'inherit', timeout: 300_000
|
||||
})
|
||||
if (result.error) throw result.error
|
||||
process.exitCode = result.status ?? 1
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 })
|
||||
}
|
||||
} else {
|
||||
const { app, BrowserWindow } = require('electron')
|
||||
const root = process.env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_ROOT
|
||||
assert(root)
|
||||
const profile = join(root, 'profile')
|
||||
mkdirSync(profile)
|
||||
if (process.env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_LIVE === '1' || process.env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_CHAT === '1') {
|
||||
const settings = process.env.GOODBUDDY_ACCEPTANCE_SETTINGS ?? join(app.getPath('appData'), 'goodbuddy', 'runtime-settings.json')
|
||||
copyFileSync(join(dirname(settings), 'Local State'), join(profile, 'Local State'))
|
||||
}
|
||||
mkdirSync(join(root, 'Temporary Vault'))
|
||||
app.setPath('userData', profile)
|
||||
app.setAppPath(resolve('.'))
|
||||
const timer = setTimeout(() => { console.error('Acceptance timed out'); app.exit(1) }, 280_000)
|
||||
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms))
|
||||
;(async () => {
|
||||
const chat = process.env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_CHAT === '1'
|
||||
? await require(join(root, 'runtime.cjs')).prepareChat(root, profile) : undefined
|
||||
await import(pathToFileURL(resolve('out/main/index.js')).href)
|
||||
let window
|
||||
for (let attempt = 0; attempt < 120; attempt++) {
|
||||
window = BrowserWindow.getAllWindows().find(item => item.webContents.getURL().includes('index.html'))
|
||||
if (window && await window.webContents.executeJavaScript('Boolean(document.querySelector("button"))')) break
|
||||
await wait(250)
|
||||
}
|
||||
assert(window, 'Production main must open its BrowserWindow')
|
||||
assert.equal(app.getPath('userData'), profile)
|
||||
const preferences = window.webContents.getLastWebPreferences()
|
||||
assert.equal(preferences.contextIsolation, true)
|
||||
assert.equal(preferences.nodeIntegration, false)
|
||||
const js = code => window.webContents.executeJavaScript(code).catch(error => { throw new Error(`${code}: ${error.message}`) })
|
||||
const click = async expression => {
|
||||
window.focus()
|
||||
for (let i=0;i<100;i++) { if (await js(`Boolean(${expression})`)) break; await wait(100) }
|
||||
const point = await js(`(() => { const element = ${expression}; if (!element) throw new Error('Missing control'); element.scrollIntoView({block:'center'}); const r=element.getBoundingClientRect(); return {x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2)} })()`)
|
||||
window.webContents.sendInputEvent({ type: 'mouseDown', button: 'left', clickCount: 1, ...point })
|
||||
window.webContents.sendInputEvent({ type: 'mouseUp', button: 'left', clickCount: 1, ...point })
|
||||
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'}})})()`)
|
||||
window.webContents.reload()
|
||||
await wait(1000)
|
||||
}
|
||||
await click(`document.querySelector('button[aria-label="\u8bbe\u7f6e"]')`)
|
||||
await click(`Array.from(document.querySelectorAll('button')).find(b=>b.innerText.startsWith('\u80fd\u529b\u4e0e\u5de5\u5177'))`)
|
||||
await click(`Array.from(document.querySelectorAll('[role="tab"]')).find(b=>b.innerText==='MCP' && b.getClientRects().length)`)
|
||||
const card = `Array.from(document.querySelectorAll('.capability-card')).find(c=>c.querySelector('strong')?.textContent==='Obsidian')`
|
||||
const poll = async (expression, attempts=100) => {
|
||||
for (let i=0;i<attempts;i++) { if (await js(expression)) return; await wait(200) }
|
||||
console.error('VISIBLE_TABS',await js(`JSON.stringify(Array.from(document.querySelectorAll('[role="tab"]')).map(e=>({text:e.innerText,selected:e.getAttribute('aria-selected')})))`))
|
||||
throw new Error(`Timed out: ${expression}`)
|
||||
}
|
||||
await poll(`Boolean(${card})`)
|
||||
assert.equal(await js(`(${card}).querySelector('[role="switch"]').checked`), false)
|
||||
await click(`(${card}).querySelector('select')`)
|
||||
window.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'End' })
|
||||
window.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Return' })
|
||||
window.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'Return' })
|
||||
await poll(`Boolean((${card}).querySelector('input:not([type="checkbox"])'))`)
|
||||
const input = `(${card}).querySelector('input:not([type="checkbox"])')`
|
||||
const vault = join(root, 'Temporary Vault')
|
||||
await click(input)
|
||||
await window.webContents.insertText(vault)
|
||||
await click(`(${card}).querySelectorAll('.capability-card__actions button')[1]`)
|
||||
await poll(`Boolean((${card}).querySelector('[role="status"]'))`)
|
||||
assert.match(await js(`(${card}).querySelector('[role="status"]').textContent`), /18/)
|
||||
assert.equal((await js('window.goodbuddy.capabilities.getSnapshot()')).obsidian.vaultPath, '')
|
||||
assert.equal(await js(`(${card}).querySelector('[role="switch"]').checked`), false)
|
||||
await click(`(${card}).querySelectorAll('.capability-card__actions button')[2]`)
|
||||
await poll(`!(${card}).querySelector('.primary-button').disabled`)
|
||||
assert.equal((await js('window.goodbuddy.capabilities.getSnapshot()')).obsidian.vaultPath, vault)
|
||||
await click(input)
|
||||
window.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A', modifiers: ['control'] })
|
||||
window.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'A', modifiers: ['control'] })
|
||||
await window.webContents.insertText(join(root, 'missing-vault'))
|
||||
await click(`(${card}).querySelectorAll('.capability-card__actions button')[1]`)
|
||||
await poll(`Boolean((${card}).querySelector('[role="alert"]'))`)
|
||||
assert.equal(await js(`(${input}).value`), join(root, 'missing-vault'))
|
||||
assert.equal((await js('window.goodbuddy.capabilities.getSnapshot()')).obsidian.vaultPath, vault)
|
||||
console.log('PASS: native UI draft test (18 tools), no implicit save/enable, save through IPC, missing-folder error preserves draft')
|
||||
if (process.platform === 'win32') {
|
||||
await click(`(${card}).querySelectorAll('.capability-card__actions button')[0]`)
|
||||
const user32 = require('koffi').load('user32.dll')
|
||||
const getWindow = user32.func('uintptr_t __stdcall GetWindow(uintptr_t hwnd, uint32_t command)')
|
||||
const getPid = user32.func('uint32_t __stdcall GetWindowThreadProcessId(uintptr_t hwnd, _Out_ uint32_t *pid)')
|
||||
const postMessage = user32.func('bool __stdcall PostMessageW(uintptr_t hwnd, uint32_t message, uintptr_t wparam, intptr_t lparam)')
|
||||
const handle = Number(window.getNativeWindowHandle().readBigUInt64LE())
|
||||
const popup = getWindow(handle, 6)
|
||||
assert(popup && Number(popup) !== handle, 'Native folder dialog must open')
|
||||
const pid = [0]
|
||||
getPid(popup, pid)
|
||||
assert.equal(pid[0], process.pid, 'Only close this acceptance process dialog')
|
||||
assert(postMessage(popup, 0x10, 0, 0))
|
||||
await poll(`!(${card}).querySelector('.primary-button').disabled`)
|
||||
assert.equal(await js(`(${input}).value`), join(root, 'missing-vault'))
|
||||
console.log('PASS: real Windows folder dialog opened and cancelled without changing draft')
|
||||
}
|
||||
window.setMinimumSize(640, 480)
|
||||
window.setSize(760, 720)
|
||||
await wait(500)
|
||||
const geometry = await js(`(() => {const c=${card};const r=c.getBoundingClientRect();return {width:innerWidth,left:r.left,right:r.right,scroll:c.scrollWidth,client:c.clientWidth}})()`)
|
||||
assert(geometry.scroll <= geometry.client + 1 && geometry.right <= geometry.width && geometry.left >= 0, JSON.stringify(geometry))
|
||||
console.log('PASS: narrow card has no horizontal overflow', JSON.stringify(geometry))
|
||||
await click(input)
|
||||
window.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A', modifiers: ['control'] })
|
||||
window.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'A', modifiers: ['control'] })
|
||||
await window.webContents.insertText(vault)
|
||||
await click(`(${card}).querySelectorAll('.capability-card__actions button')[1]`)
|
||||
await poll(`Boolean((${card}).querySelector('[role="status"]'))`)
|
||||
await js(`(${card}).querySelector('[role="switch"]').focus()`)
|
||||
window.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Space' })
|
||||
window.webContents.sendInputEvent({ type: 'keyUp', keyCode: 'Space' })
|
||||
await poll(`(${card}).querySelector('[role="switch"]').checked && !(${card}).querySelector('[role="switch"]').disabled`)
|
||||
await click(`(${card}).querySelector('button[aria-expanded]')`)
|
||||
await poll(`(${card}).querySelectorAll('.mcp-server-tools li').length === 19`)
|
||||
console.log('PASS: keyboard switch enables Obsidian; expanded production UI lists 19 tools')
|
||||
for (const theme of ['light', 'dark']) {
|
||||
await js(`document.documentElement.dataset.theme=${JSON.stringify(theme)}`)
|
||||
const bounds = await js(`(()=>{const c=${card};const r=c.getBoundingClientRect();return {right:r.right,width:innerWidth,scroll:c.scrollWidth,client:c.clientWidth,background:getComputedStyle(c).backgroundColor}})()`)
|
||||
assert(bounds.right <= bounds.width && bounds.scroll <= bounds.client + 1)
|
||||
console.log(`PASS: ${theme} narrow Obsidian bounds`, JSON.stringify(bounds))
|
||||
}
|
||||
if (process.env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_LIVE === '1') {
|
||||
await require(join(root, 'runtime.cjs')).run(root, await js('window.goodbuddy.capabilities.getSnapshot()'))
|
||||
}
|
||||
if (chat) {
|
||||
await click(`document.querySelector('.settings-panel__header button')`)
|
||||
window.setSize(1280, 850)
|
||||
try {
|
||||
const providers = process.env.GOODBUDDY_OBSIDIAN_ACCEPTANCE_CONTINUE_ONLY === '1' ? ['Continue'] : ['OpenCode', 'Continue']
|
||||
for (const provider of providers) {
|
||||
if (provider === 'Continue') {
|
||||
await poll(`!document.querySelector('button[title^="Runtime \u548c\u6a21\u578b"]').disabled`)
|
||||
await js(`document.querySelector('button[title^="Runtime \u548c\u6a21\u578b"]').click()`)
|
||||
await click(`Array.from(document.querySelectorAll('.runtime-picker__menu strong')).find(b=>b.innerText==='Continue Runtime')?.nextElementSibling`)
|
||||
}
|
||||
await poll(`!document.querySelector('button[title^="Runtime \u548c\u6a21\u578b"]').disabled`)
|
||||
const mode = `document.querySelector('button[aria-label^="\u5de5\u4f5c\u6a21\u5f0f"]')`
|
||||
if (!(await js(`(${mode}).innerText.includes('Execute')`))) {
|
||||
await click(mode)
|
||||
await click(`Array.from(document.querySelectorAll('[role="menuitemradio"]')).find(b=>b.innerText.startsWith('Execute'))`)
|
||||
}
|
||||
const marker = `CHAT_${provider}_${require('node:crypto').randomUUID()}`
|
||||
const before = chat.observations.length
|
||||
await click(`document.querySelector('textarea')`)
|
||||
await window.webContents.insertText(`Use only Obsidian tools. Write ${provider}-chat.md containing ${marker} using obsidian_write_note, then read it using obsidian_read_note. Reply with the content read. Do not use other tools.`)
|
||||
await click(`document.querySelector('button[aria-label="\u53d1\u9001"]')`)
|
||||
await poll(`Array.from(document.querySelectorAll('.message--assistant')).some(e=>e.innerText.includes(${JSON.stringify(marker)}))`,450)
|
||||
await poll(`!document.querySelector('button[title^="Runtime \u548c\u6a21\u578b"]').disabled`,450)
|
||||
assert((require('node:fs').readFileSync(join(vault,`${provider}-chat.md`),'utf8')).includes(marker))
|
||||
console.log(JSON.stringify({ provider, fullChatUi: 'passed', realModelHttpRequests: chat.observations.length-before }))
|
||||
}
|
||||
} finally {
|
||||
console.log(JSON.stringify({ chatModelHttpRequests: chat.observations.length }))
|
||||
await chat.close()
|
||||
}
|
||||
}
|
||||
await wait(1000)
|
||||
clearTimeout(timer)
|
||||
app.exit(0)
|
||||
})().catch(error => { console.error(error); clearTimeout(timer); app.exit(1) })
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const { spawn } = require('node:child_process')
|
||||
const { mkdtemp, mkdir, readFile, readdir, writeFile, rm, stat } = require('node:fs/promises')
|
||||
const { tmpdir } = require('node:os')
|
||||
const { join, resolve } = require('node:path')
|
||||
|
||||
async function main() {
|
||||
const executable = resolve(process.argv[2])
|
||||
await stat(executable)
|
||||
const root = await mkdtemp(join(tmpdir(), 'goodbuddy-obsidian-packaged-'))
|
||||
const profile = join(root, 'profile')
|
||||
const vault = join(root, 'Packaged Vault')
|
||||
await mkdir(profile)
|
||||
await mkdir(vault)
|
||||
const audit = join(root, 'node-audit.jsonl')
|
||||
const guard = join(root, 'offline.cjs')
|
||||
await writeFile(guard, `
|
||||
const fs = require('node:fs');
|
||||
const net = require('node:net');
|
||||
const original = net.Socket.prototype.connect;
|
||||
net.Socket.prototype.connect = function(...args) {
|
||||
const options = net._normalizeArgs(args)[0];
|
||||
const host = options.host || 'localhost';
|
||||
if (!options.path && !['localhost','127.0.0.1','::1'].includes(host)) throw new Error('Offline acceptance blocked network');
|
||||
return original.apply(this,args);
|
||||
};
|
||||
globalThis.fetch = async () => { throw new Error('Offline acceptance blocked fetch'); };
|
||||
require('node:module').syncBuiltinESMExports();
|
||||
fs.appendFileSync(${JSON.stringify(audit)}, JSON.stringify({entry:process.argv[1],exe:process.execPath,electron:process.versions.electron})+'\\n');
|
||||
`)
|
||||
const env = { ...process.env, NODE_OPTIONS: `--require ${JSON.stringify(guard)}`,
|
||||
PATH: join(process.env.SystemRoot, 'System32') }
|
||||
delete env.Path
|
||||
delete env.ELECTRON_RUN_AS_NODE
|
||||
const child = spawn(executable, [`--user-data-dir=${profile}`, '--remote-debugging-port=0', '--inspect=0',
|
||||
'--proxy-server=http://127.0.0.1:9', '--proxy-bypass-list=<-loopback>'], { env, stdio: ['ignore', 'ignore', 'pipe'] })
|
||||
let diagnostic = ''
|
||||
child.stderr.on('data', chunk => { diagnostic += chunk.toString() })
|
||||
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||
let socket
|
||||
let mainSocket
|
||||
try {
|
||||
let port
|
||||
for (let i=0;i<100;i++) {
|
||||
try { port = (await readFile(join(profile, 'DevToolsActivePort'), 'utf8')).split('\n')[0]; break } catch { await wait(200) }
|
||||
}
|
||||
assert(port, 'Packaged app must expose debugging only in the isolated profile')
|
||||
const mainEndpoint = diagnostic.match(/ws:\/\/127\.0\.0\.1:\d+\/[-a-f0-9]+/u)?.[0]
|
||||
assert(mainEndpoint, 'The isolated Main inspector must be available')
|
||||
mainSocket = new WebSocket(mainEndpoint)
|
||||
await new Promise((resolve, reject) => { mainSocket.onopen=resolve;mainSocket.onerror=reject })
|
||||
// Packaged Electron strips NODE_OPTIONS at startup. Restore it only for this
|
||||
// disposable Main so its real MCPVault child receives the offline guard.
|
||||
await new Promise((resolve,reject) => {
|
||||
const timer=setTimeout(()=>reject(new Error('Main inspector timed out')),10_000)
|
||||
mainSocket.onmessage=message=>{const value=JSON.parse(message.data);if(value.id===1){clearTimeout(timer);if(value.error || value.result?.exceptionDetails)reject(new Error('Unable to set child network guard'));else resolve()}}
|
||||
mainSocket.send(JSON.stringify({id:1,method:'Runtime.evaluate',params:{expression:`process.env.NODE_OPTIONS=${JSON.stringify(env.NODE_OPTIONS)}`}}))
|
||||
})
|
||||
mainSocket.close()
|
||||
let target
|
||||
for (let i=0;i<100;i++) {
|
||||
target = (await (await fetch(`http://127.0.0.1:${port}/json/list`)).json()).find(item => item.type === 'page' && item.url.includes('index.html'))
|
||||
if (target) break
|
||||
await wait(200)
|
||||
}
|
||||
assert(target, 'Packaged production renderer must load')
|
||||
socket = new WebSocket(target.webSocketDebuggerUrl)
|
||||
await new Promise((resolve, reject) => { socket.onopen = resolve; socket.onerror = reject })
|
||||
let id = 0
|
||||
const pending = new Map()
|
||||
socket.onmessage = message => { const value = JSON.parse(message.data); if (pending.has(value.id)) { const {resolve,reject}=pending.get(value.id); pending.delete(value.id); if (value.error) reject(new Error(value.error.message)); else resolve(value.result) } }
|
||||
const send = (method, params={}) => new Promise((resolve,reject) => {
|
||||
const key=++id
|
||||
const timer=setTimeout(()=>{pending.delete(key);reject(new Error(`CDP timed out: ${method}`))},10_000)
|
||||
pending.set(key,{
|
||||
resolve: value=>{clearTimeout(timer);resolve(value)},
|
||||
reject: error=>{clearTimeout(timer);reject(error)}
|
||||
})
|
||||
socket.send(JSON.stringify({id:key,method,params}))
|
||||
})
|
||||
const js = async expression => { const result=await send('Runtime.evaluate',{expression,awaitPromise:true,returnByValue:true});assert(!result.exceptionDetails,JSON.stringify(result.exceptionDetails));return result.result.value }
|
||||
const poll = async expression => { for(let i=0;i<100;i++){ if(await js(expression))return;await wait(200) } throw new Error(`Timeout: ${expression}`) }
|
||||
const click = async expression => {
|
||||
await js(`(()=>{const e=${expression};if(!e)throw new Error('Missing control');e.scrollIntoView({block:'center'});e.focus();e.click()})()`)
|
||||
await wait(500)
|
||||
}
|
||||
await poll('Boolean(window.goodbuddy && document.querySelector("button"))')
|
||||
await stat(join(profile,'assistant.sqlite'))
|
||||
assert.equal(await js('typeof require'), 'undefined')
|
||||
await click(`document.querySelector('button[aria-label="\u8bbe\u7f6e"]')`)
|
||||
await poll(`Array.from(document.querySelectorAll('button')).some(b=>b.innerText.startsWith('\u80fd\u529b\u4e0e\u5de5\u5177'))`)
|
||||
await click(`Array.from(document.querySelectorAll('button')).find(b=>b.innerText.startsWith('\u80fd\u529b\u4e0e\u5de5\u5177'))`)
|
||||
await click(`Array.from(document.querySelectorAll('[role="tab"]')).find(b=>b.innerText==='MCP' && b.getClientRects().length)`)
|
||||
const card=`Array.from(document.querySelectorAll('.capability-card')).find(c=>c.querySelector('strong')?.textContent==='Obsidian')`
|
||||
await poll(`Boolean(${card})`)
|
||||
await js(`{const e=(${card}).querySelector('select');e.value='folder';e.dispatchEvent(new Event('change',{bubbles:true}));}`)
|
||||
await poll(`Boolean((${card}).querySelector('input:not([type="checkbox"])'))`)
|
||||
await click(`(${card}).querySelector('input:not([type="checkbox"])')`)
|
||||
await send('Input.insertText',{text:vault})
|
||||
await click(`(${card}).querySelectorAll('.capability-card__actions button')[1]`)
|
||||
await poll(`Boolean((${card}).querySelector('[role="status"]'))`)
|
||||
assert.match(await js(`(${card}).querySelector('[role="status"]').textContent`),/18/)
|
||||
await click(`(${card}).querySelectorAll('.capability-card__actions button')[2]`)
|
||||
await poll(`!(${card}).querySelector('.primary-button').disabled`)
|
||||
assert.equal((await js('window.goodbuddy.capabilities.getSnapshot()')).obsidian.vaultPath,vault)
|
||||
const bin=join(profile,'tool-environment','bin')
|
||||
const shims=(await readdir(bin,{recursive:true})).filter(name=>name.endsWith('node.cmd'))
|
||||
assert(shims.length>0)
|
||||
assert((await readFile(join(bin,shims[0]),'utf8')).includes(executable))
|
||||
console.log('PASS: packaged app.asar UI -> real IPC -> packaged MCPVault (18 tools), save, managed packaged Electron shim, PATH=System32, Chromium dead proxy')
|
||||
const records=(await readFile(audit,'utf8')).trim().split('\n').map(line=>JSON.parse(line))
|
||||
assert(records.some(item=>item.entry?.includes('app.asar.unpacked') && item.entry.endsWith('server.js') && item.exe===executable && item.electron))
|
||||
console.log('PASS: packaged MCPVault child loaded Node network guards; audited packaged executable and unpacked server entry')
|
||||
console.log('Boundary: MCPVault Node API guards + Chromium proxy; no OS firewall, physical disconnection, or installer execution')
|
||||
} finally {
|
||||
socket?.close()
|
||||
mainSocket?.close()
|
||||
if (child.exitCode === null) {
|
||||
const killer=spawn('taskkill',['/PID',String(child.pid),'/T','/F'],{stdio:'ignore'})
|
||||
await new Promise(resolve=>killer.once('exit',resolve))
|
||||
}
|
||||
await rm(root,{recursive:true,force:true,maxRetries:10,retryDelay:200})
|
||||
}
|
||||
}
|
||||
main().catch(error=>{console.error(error);process.exitCode=1})
|
||||
@@ -18,6 +18,7 @@ const {
|
||||
prepareBundledOpenCodeConfig,
|
||||
} = require("./opencode-config.cjs");
|
||||
const { npmInvocation } = require("./npm-invocation.cjs");
|
||||
const { prepareNativeDshResources } = require("./native-dsh-resources.cjs");
|
||||
|
||||
const architectureNames = {
|
||||
1: "x64",
|
||||
@@ -184,6 +185,7 @@ module.exports = async function prepareBundledRuntimes(context) {
|
||||
architecture === "x64" ? `${architecture}-baseline` : architecture;
|
||||
const packageName = `opencode-${packagePlatform}-${suffix}`;
|
||||
const projectDir = context.packager.projectDir;
|
||||
await prepareNativeDshResources(projectDir, platform, architecture, downloadPackage, lockedIntegrity);
|
||||
await prepareBundledOpenCodeConfig(projectDir);
|
||||
const projectPackage = JSON.parse(
|
||||
await readFile(join(projectDir, "package.json"), "utf8"),
|
||||
|
||||
@@ -225,6 +225,10 @@
|
||||
|
||||
### 7.1 布局
|
||||
|
||||
通用设置按快捷唤起、会话与通知、工作目录与下载分成三张卡片。模型下载源位于
|
||||
“工作目录与下载”卡片内的工作目录输入之后;以下仅展示下载源控件,完整分组及通知
|
||||
开关行为见 [UI 设计规范](../../UI-DESIGN.md#138-设置中心)。
|
||||
|
||||
```text
|
||||
平台功能
|
||||
管理通用平台行为与可选工作区能力
|
||||
@@ -244,7 +248,7 @@
|
||||
└────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
页面只保留一个视觉上最突出的任务。模型下载源保存属于即时设置,不增加与来源选项竞争的
|
||||
模型下载源保存属于即时设置,不增加与来源选项竞争的
|
||||
大型“保存全部”按钮。
|
||||
|
||||
### 7.2 控件
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
# Desktop 0.15.1 与 Agent 0.15.0 发布准备及恢复
|
||||
|
||||
日期:2026-09-27 至 2026-09-28。原 Desktop 0.15.0 标签构建失败,
|
||||
用户已选择 Desktop 0.15.1 与 Agent 0.15.0 恢复发布;验证通过不代表已发布。
|
||||
|
||||
## 范围与批准
|
||||
|
||||
- 用户要求 Desktop 与 GoodBuddy Agent 同步升级至 `0.15.0`,明确不构建 LoongArch。
|
||||
- 源码基线:`0f8bf405db7ca21a85cc70009ad083eb837e1e03`。
|
||||
- Desktop 差异基线为已发布的 `v0.13.16`,Agent 为 `agent-v0.13.4`。
|
||||
- `v0.15.0` 已创建并推送,保持不可变;恢复使用 `v0.15.1`,
|
||||
`agent-v0.15.0` 尚未创建或推送。
|
||||
- 用户已批准两份完整双语说明,正式正文仅维护在
|
||||
[Desktop 发布资源](../../resources/release-notes.json) 的 `0.15.1` 条目与
|
||||
[Agent 发布资源](../../resources/agent-release-notes/0.15.0.md)。
|
||||
已逐条核对写入文本与批准草案一致,本记录不再复制正文。
|
||||
- `FEATURES.md` 与 `FEATURES.zh-CN.md` 已核对本轮能力、限制和候选版本。
|
||||
- 用户要求两张未跟踪宣传截图保留原位、不提交,打标签前另行处理;
|
||||
不移动、删除、暂存或更改忽略规则。
|
||||
- 2026-09-28 用户更新授权:当前工作区的宣传文档与素材可以一起提交、推送,
|
||||
后续候选由代理自行审核确认,不再逐次等待用户批准,继续完成双产品正式发布。
|
||||
三张新增候选图已读取检查,保留原始草稿属性,不冒充正式发布截图或对外媒体发文。
|
||||
发布说明正文保持不变,`releasedAt` 更新为实际准备发布日期 2026-09-28。
|
||||
|
||||
## 本地验证
|
||||
|
||||
- `npm run typecheck`:通过。
|
||||
- `npm run lint`:通过。
|
||||
- `npm run release:notes:verify`:通过。
|
||||
- 发布说明相关三个文件定向测试:16 项通过。
|
||||
- 首轮 `npm test`:5,313 通过、79 跳过、4 失败,耗时 697.61 秒。
|
||||
三项失败来自当时尚未批准写入的 0.15.0 Desktop/Agent 发布说明;
|
||||
另一项为 `magic-notes-layout.electron.test.ts` 的布尔断言。
|
||||
该布局用例单独复验 1 项通过,未修改源码或放宽断言。
|
||||
单独通过不替代全量结果。
|
||||
- 正式说明写入后的最终 `npm test`:**5,317 通过、79 跳过、0 失败**,
|
||||
453 个测试文件通过、11 个跳过,耗时 645.49 秒。随后再次执行完整
|
||||
`npm run typecheck` 与 `npm run lint`,均通过。
|
||||
- 最终验证期间出现并行宣传文档编辑及新增 `storygraph.png`,均不属于本次
|
||||
发布准备改动,不暂存或覆盖。打标签前仍须处理工作区洁净条件。
|
||||
- 修改文档的相对文件链接、版本一致性和 `git diff --check` 通过。
|
||||
- 不运行本地生产构建、打包或安装包启动。准确候选提交须经用户批准,
|
||||
然后双远端推送、主分支 CI、生产构建、原生发布与公开核验分别进行。
|
||||
|
||||
## Agent 当前源码实机验证
|
||||
|
||||
共享 Linux x64 Host 的 LAN/VPN SSH 端口均可达,实际通过 LAN 与既有固定
|
||||
Host Key、加密凭据连接。重新捆绑当前源码的 Agent CLI、daemon、model bridge
|
||||
与 Desktop 适配器,仅上传独立 `/root/tmp/gb-lifecycle-*` 测试目录,复用已安装且
|
||||
版本锁未变的 Node/OpenCode/Continue,不替换原安装,不构建正式包。
|
||||
|
||||
最终 OpenCode、Continue 各通过一次 Ask 和一次 Execute,无工具请求,
|
||||
验证回答、后续历史、空闲 Runtime 回收及测试停止后所属进程为 0。
|
||||
模型请求时读取所属进程的固定遥测字段,确认 `DO_NOT_TRACK=1`、
|
||||
`OTEL_SDK_DISABLED=true` 及三个 exporter 均为 `none`,不输出整个环境。
|
||||
这些结果补齐 `eb53c82` 的 Agent/model bridge 真实链路验证,
|
||||
但不证明后台没有其他网络流量,也不替代三目标正式包验收。
|
||||
|
||||
首轮 Continue 探针沿用了 OpenCode 原生历史持久化假设,未像 Desktop 一样传入
|
||||
`request.history`,第二轮历史断言失败;当前 Continue 冷启动源码明确要求传入
|
||||
保存历史。修正测试输入及会话 ID 断言后通过,未修改产品实现。
|
||||
|
||||
有完整计数的实机运行共 **8 次**真实模型 HTTP 请求:首次 OpenCode 2 次、
|
||||
首次 Continue 2 次、最终 Continue 2 次、最终 OpenCode 2 次。
|
||||
此前一次直接启动 Electron 的探针未捕获子进程输出,不能确认是否实际发出请求,
|
||||
该次上限 2 次,不纳入通过证据;因此不能把 8 次写成本轮精确总数,总量为 8–10 次。
|
||||
后续均使用等待退出并记录脱敏输出的启动器;本轮探针 Electron 进程已退出。
|
||||
本地诊断证据在临时 `opencode/release-015-host/`,不提交脚本、日志或凭据。
|
||||
|
||||
## 首次候选 CI 与测试修正
|
||||
|
||||
用户批准的首次候选 `5a6ba6c14f5c58f3da5e9afe281b18e0cbdb1ae8`
|
||||
已推送至 `origin/main` 和 `github/main`,未创建两个发布标签。
|
||||
|
||||
- [Agent CI 36329271991](https://github.com/mesalogo/goodbuddy/actions/runs/36329271991)
|
||||
源码验证及 Linux x64、Linux arm64、Darwin arm64 原生构建全部通过。
|
||||
- [Desktop CI 36329271967](https://github.com/mesalogo/goodbuddy/actions/runs/36329271967)
|
||||
为 5,342 通过、51 跳过、3 失败。两个 App 长历史场景超过默认 5 秒单用例时限;
|
||||
会话采集 Electron 场景等待 Escape 返回笔记列表超时。生产构建未运行。
|
||||
- 测试修正仅让原生键盘辅助函数显示窗口、获取焦点并等待 `document.hasFocus()`,
|
||||
然后发送真实输入;两个长历史场景采用仓库既有的 15 秒单用例时限。
|
||||
不修改产品实现、不跳过 Linux、不关闭沙箱、不删除或放宽数据和键盘断言。
|
||||
- Windows 聚焦复验三个失败场景通过,其中真实 Electron 覆盖完整 502 条历史、
|
||||
214px 窄栏键盘操作、浅深主题确认按钮、真实 IPC/SQLite 和重开保留。
|
||||
修正后首次全量复验被中断,不计为通过;随后完整重跑为 **5,317 通过、
|
||||
79 跳过、0 失败**,453 个文件通过、11 个跳过,耗时 646.48 秒。
|
||||
类型检查、lint 和发布说明校验均通过,新候选 Linux CI 仍需独立通过。
|
||||
- 本次只改测试和验证文档,不改变 Agent、远程 Runtime 或模型桥;
|
||||
沿用上方当前源码 Host 验证,不新增真实模型请求。
|
||||
- 新提交必须重新取得准确 SHA 批准,原发布说明正文保持不变。
|
||||
|
||||
## Linux 实机复现与最终同步修正
|
||||
|
||||
2026-09-28:第二次批准候选 `12594961fe1e814e89f55de1fbfe245310505115`
|
||||
的 [Desktop CI 36331853137](https://github.com/mesalogo/goodbuddy/actions/runs/36331853137)
|
||||
为 5,343 通过、51 跳过、2 失败。两个 App 长历史场景已通过;
|
||||
Escape 返回列表仍失败,活动历史迁移这次超过默认 5 秒时限,生产构建仍未执行。
|
||||
|
||||
按用户要求直接在共享 Linux x64 Host 测试准确候选源码。使用独立磁盘测试目录、
|
||||
锁定 Node 24.19.0 和 `npm ci`,补齐 Xvfb 与匹配的 X server 系统依赖;
|
||||
没有创建新账号,使用既有非 root 账号运行测试,保留 Electron 沙箱及其标准 helper 权限。
|
||||
不替换既有 Agent,不修改其安装或工作区,不执行生产构建或打包。
|
||||
|
||||
原测试在 Host 上稳定复现:缩窄窗口会按产品既有行为收起工作栏,隐藏面板仍有非零宽度。
|
||||
诊断显示 Escape 实际发给 `aria-expanded=false` 的工作栏开关,而不是笔记输入框。
|
||||
因此仅获取窗口焦点不够;测试改为等待窄视口及响应式渲染完成,按真实按钮打开工作栏,
|
||||
确认展开状态和目标输入框焦点后再发送键盘输入。临时诊断代码已移除。
|
||||
|
||||
迁移测试在 Host 上以原默认时限通过,保留 10,000 条记录、完整迁移、WAL 字节比、
|
||||
扫描次数与无变化写入断言;仅为这一个大数据场景采用已有的 15 秒单用例时限,
|
||||
不改数据库实现或全局时限。
|
||||
|
||||
- 最终 Linux Host 完整 `npm test`:**5,345 通过、51 跳过、0 失败**,
|
||||
449 个文件通过、15 个跳过,耗时 751.09 秒。
|
||||
- Windows 聚焦回归:3 项通过,包含真实 Electron 和迁移。
|
||||
- Windows 完整类型检查、lint、发布说明校验:全部通过。
|
||||
- Linux Electron 实测覆盖 502 条历史、218px 窄栏、Escape/Tab/Enter、
|
||||
浅深主题确认按钮、真实 IPC/SQLite 与重开保留;Windows 对应窄栏为约 214px。
|
||||
- 本阶段不新增真实模型请求,不改变产品代码或双语说明。
|
||||
新候选仍须重新批准 SHA 并通过自己的主分支 CI/生产构建,两个标签仍未创建。
|
||||
|
||||
## 后续 CI 焦点前提与确定性取消验证
|
||||
|
||||
`b8b6007` 的 [CI 36335076841](https://github.com/mesalogo/goodbuddy/actions/runs/36335076841)
|
||||
两次执行均为 5,344 通过、51 跳过、1 失败,失败点改为 `new title focus`,
|
||||
生产构建没有运行。该次失败未在 Host 的 10 次带诊断复验中重现,其中 5 次限制单 CPU;
|
||||
移除临时诊断后再测也通过。这些独立结果不替代 CI。
|
||||
|
||||
测试原先直到键盘阶段才显示窗口,前面的自动聚焦检查仍在隐藏窗口执行。
|
||||
现让测试窗口从开始即显示并确认焦点,保留标题自动聚焦原断言;
|
||||
超时增加实际焦点、标题禁用/可见/隔离状态和保存状态,避免只报告阶段名称。
|
||||
可见窗口版本在 Host 连续五次聚焦复验通过。
|
||||
|
||||
随后 Host 全量出现一个独立的 Continue 取消测试竞争:
|
||||
`setTimeout(0)` 没有保证取消发生在子进程启动前,空 mock 已被调用。
|
||||
用例改在既有启动环境回调处精确取消,仍验证回调执行、子进程未启动和临时配置删除,
|
||||
不更改 Continue 产品实现或取消机制。
|
||||
|
||||
- 最终 Host 完整测试:**5,345 通过、51 跳过、0 失败**,449 个文件通过、
|
||||
15 个跳过,耗时 743.34 秒。
|
||||
- Windows 可见窗口 Electron 回归及确定性取消回归通过。
|
||||
- 完整类型检查、lint 通过;双语说明正文不变,日期调整后另行验证发布资源。
|
||||
- 上述均为测试或验证文档调整,不新增真实模型调用,不改变 Agent 发布输入。
|
||||
正式发布仍要求当前候选自己的 CI/生产构建通过。
|
||||
|
||||
## 完整分支快照重新核对
|
||||
|
||||
`d9e1e42` 的 [Desktop CI 36340682594](https://github.com/mesalogo/goodbuddy/actions/runs/36340682594)
|
||||
已通过原生会话采集焦点测试,但为 5,367 通过、51 跳过、4 失败。
|
||||
核对祖先后确认准备期间另有四个并行提交进入分支:
|
||||
`ee1bf8a` 输入区控件样式、`8e60736` 流式会话稳定排序、
|
||||
`f35198d` 桌面通知开关、`d47f967` 监督结果继续讨论。
|
||||
它们已随用户授权的当前分支推送;前述 Host 的 5,345 项通过使用较早源码快照,
|
||||
不能作为包含这四项的新候选验证结果。
|
||||
|
||||
本轮已重新核对四个提交的实现和文档,补充两份功能矩阵与双语发布说明:
|
||||
通知开关默认开启且不影响应用内提示;流式更新不改变排序;
|
||||
监督结果可直接编辑问题并继续讨论,界面不再提供知识实体写回控件。
|
||||
Agent 产品源码及 Runtime 锁未因这四项改变,Agent
|
||||
[CI 36340682607](https://github.com/mesalogo/goodbuddy/actions/runs/36340682607)
|
||||
源码校验和三个原生构建全部通过。
|
||||
|
||||
补齐通知默认值的契约 fixture;Obsidian 多次真实子进程调用使用单用例 15 秒时限,
|
||||
避免前项超时后的在途调用污染下一项取消断言;笔记来源用例按面板查询控件,
|
||||
并等前次来源操作释放禁用状态后再发起下一次。
|
||||
这些修正不改变产品实现。Windows 聚焦回归 19 项通过,类型、lint 和说明校验通过。
|
||||
从 Git tree `1758ff58a6ff33fcf7ddaa3d35dd2fcc57cd1265` 导出完整快照,
|
||||
覆盖 Host 测试源码,而非只同步早期快照中的少数文件。
|
||||
该完整快照 `npm test` 为 **5,371 通过、51 跳过、0 失败**,
|
||||
451 个文件通过、15 个跳过,耗时 751.42 秒,包含全部四个并行提交。
|
||||
最终提交前再次核对源码、测试、依赖和发布资源与该已验证 tree 一致,
|
||||
仅追加本段验证结果;完整候选仍需自己的主分支 CI 与生产构建通过。
|
||||
|
||||
### 完整候选后的焦点同步修正
|
||||
|
||||
`5cdffee` 的 Agent [CI 36343184917](https://github.com/mesalogo/goodbuddy/actions/runs/36343184917)
|
||||
三个原生构建和源码校验通过;桌面
|
||||
[CI 36343184916](https://github.com/mesalogo/goodbuddy/actions/runs/36343184916)
|
||||
为 5,369 通过、51 跳过、2 失败,生产构建未执行。
|
||||
会话采集的焦点等待可被 `body` 后代文字误满足,菜单随后被真正的消息聚焦关闭;
|
||||
另一个原生 Runtime 控件用例在启动完成前打开失焦关闭的菜单。
|
||||
本轮收紧消息焦点的元素断言,并等待项目能力请求及启动渲染后再打开 Runtime 选项。
|
||||
不重试菜单、不跳过场景、不改产品实现。
|
||||
|
||||
完整冻结 tree `2ddc0da69b6bcb42d8272c44dd11e3e6904d0438` 在 Host 全量
|
||||
**5,371 通过、51 跳过、0 失败**,耗时 748.21 秒。
|
||||
Windows 原生 Runtime 聚焦用例、类型检查和 lint 通过。
|
||||
提交仅追加本段记录;仍须等待修正候选的主分支 CI 和生产构建。
|
||||
|
||||
`706726b` 的桌面 [CI 36345672365](https://github.com/mesalogo/goodbuddy/actions/runs/36345672365)
|
||||
已通过上述采集与 Runtime 用例,但另一个外部知识库测试失败:
|
||||
5,370 通过、51 跳过、1 失败,生产构建未执行。
|
||||
该 fixture 只在查询新库 ID 时返回包含已创建库的快照,普通轮询会返回创建前数据;
|
||||
同时错误地要求最后一次请求必须属于新库,忽略后台轮询。
|
||||
改为模拟已持久化的库列表,等待新库请求发生,保留原库与新增库选中、被排除库未选中的
|
||||
全部界面断言,不修改产品代码。Windows 完整 App 300 项通过,类型、lint 和说明校验通过。
|
||||
冻结 tree `42a92450ad7d9ebc50167c7946bff53cff2889e6` 的 Linux 全量为
|
||||
**5,371 通过、51 跳过、0 失败**,耗时 748.67 秒。
|
||||
|
||||
## Desktop 0.15.1 恢复
|
||||
|
||||
`65a21390debe0dd04871417a363a9d87a29339e6` 的主分支
|
||||
[CI 36347785076](https://github.com/mesalogo/goodbuddy/actions/runs/36347785076)
|
||||
验证与生产构建全部通过后,两个远端创建了不可变 `v0.15.0`。
|
||||
其正式 [CI 36348938269](https://github.com/mesalogo/goodbuddy/actions/runs/36348938269)
|
||||
两个 macOS 目标在 `codesign --verify --deep --strict` 失败,
|
||||
报 `invalid destination for symbolic link in bundle`;GitHub Release 未发布。
|
||||
|
||||
Host 隔离 fixture 经实际 DS 资源准备和 electron-builder 文件复制,
|
||||
复现相对 npm CLI 链接变成原构建目录的绝对链接。不是签名凭据或测试超时问题。
|
||||
用户明确选择 **Desktop 0.15.1 + Agent 0.15.0** 恢复,不移动或复用旧标签。
|
||||
修正仅让复制保留相对链接;新增回归移除原安装与暂存目录后验证最终链接仍可读。
|
||||
该路径不用于 Agent,远程 Runtime 与 Agent 源码未改,不增加真实模型调用。
|
||||
未发布 Desktop 0.15.0 的双语条目迁移到 0.15.1,保留全部功能说明;
|
||||
Agent 版本不变,其包元数据从新版 Desktop manifest 派生最低版本 0.15.1,
|
||||
两份说明和功能矩阵同步这个升级顺序。
|
||||
|
||||
原标签最终 Windows/Linux 四个目标成功,两个 macOS 失败,发布任务跳过,
|
||||
公开 Latest 仍为 `v0.13.16`。
|
||||
Windows 打包相关测试 45 通过、1 个 POSIX 链接用例跳过,类型、lint、
|
||||
发布说明校验通过。冻结 tree `99d3271174a68922fbf0a1a4cfbadbb85a6067c8`
|
||||
在 Linux Host 全量 **5,372 通过、51 跳过、0 失败**,耗时 745.88 秒;
|
||||
新增符号链接回归实际执行通过。提交仅追加验证记录,恢复候选 CI 与 macOS 签名待验证。
|
||||
|
||||
## 数据、兼容与验收边界
|
||||
|
||||
Desktop schema 48 新增魔法笔记会话来源元数据。升级前应备份完整用户数据,
|
||||
包括数据库及相邻 `notes/` 目录;旧客户端不能打开升级后的数据库。
|
||||
本次发布准备不修改数据库迁移实现,也不直接改写用户数据库。
|
||||
|
||||
Agent 发布工作流以候选 `package.json` 设置最低 Desktop 版本,因此
|
||||
Agent 0.15.0 包要求 Desktop 0.15.1;Node、OpenCode、Continue 锁不变。
|
||||
Agent 与 Desktop 独立发布,Agent 不替代 Desktop Latest。
|
||||
|
||||
原生客户端的远程 coordinator、托管标准 Node 交付整合、Continue/OpenCode
|
||||
Ask 内置 MCP 映射及完整跨平台安装包验收仍有缺口,见
|
||||
[原生客户端进度](../features/assistant-workbar/progress.md#2026-09-27-local-native-client-validation)。
|
||||
本次说明明确标为本地预览,不将这些未完成部分宣称为已提供能力。
|
||||
@@ -0,0 +1,48 @@
|
||||
# Desktop 0.15.2 与 Agent 0.15.0 发布准备
|
||||
|
||||
日期:2026-09-28。用户要求包含当前本地工作、推送双远端,等待候选 CI 和生产构建通过后打标签;
|
||||
确认同时发布 Agent 0.15.0,不构建 LoongArch。沿用此前自行审核候选和双语说明的授权。
|
||||
|
||||
## 发布范围
|
||||
|
||||
- Desktop 基线为已发布的 `v0.15.1`(`4e5239256d42c856bb1420d87c4a338f17867b80`)。
|
||||
[发布 CI 36368878429](https://github.com/mesalogo/goodbuddy/actions/runs/36368878429)
|
||||
六个原生目标和 GitHub/OSS 发布全部成功,0.15.1 的历史说明保留。
|
||||
- 新源码截止 `66f77b83c2b6ebe0018a6c5c2bc72e1b4c1e43b1`:
|
||||
renderer 退出诊断、笔记采集界面、双语网站及 Story Graph 只读工具与完整讨论草稿。
|
||||
- Agent 0.15.0 尚未发布,基线仍为 `agent-v0.13.4`。
|
||||
既有遥测和 Continue 更新检查修复保留,补充远程 Story Graph 工具;
|
||||
最低 Desktop 版本由当前 manifest 派生为 0.15.2。三个原生目标和 Runtime 锁不变。
|
||||
- 当前未提交的功能矩阵、主机/云环境设计和宣传文稿一并审核纳入。
|
||||
云平台适配与主机入口迁移明确为待实现规划,不进入发布功能说明。
|
||||
宣传稿保持草稿和原核查截止点,不代表已对外发布。
|
||||
- 正式双语正文仅维护在 `resources/release-notes.json` 的 0.15.2 条目和
|
||||
`resources/agent-release-notes/0.15.0.md`。两份 FEATURES 已对照实现与规划核对。
|
||||
|
||||
## 验证边界
|
||||
|
||||
Story Graph 已有开发期证据见
|
||||
[监督者验证进度](../features/conversation-supervision/progress.md#2026-09-28-story-graph-只读工具):
|
||||
真实 Linux Host 上的 OpenCode/Continue Ask、Execute 调用与禁用检查通过;
|
||||
推理为本机确定性响应,10 次请求含 2 次标题生成,外部付费请求 0 次。
|
||||
不将这些证据称为真实模型建议质量验证,也不宣称历史重建、跨范围事实合并或大库性能已验收。
|
||||
|
||||
本轮类型、lint、双语说明及网站静态校验通过。按当前源码重建隔离 Agent 探针,
|
||||
在共享 Linux x64 Host 经固定身份完成 OpenCode/Continue Ask、Execute、空闲进程回收、
|
||||
冷启动历史恢复和遥测关闭环境检查;每个 Runtime 2 次真实模型请求,共 **4 次**,
|
||||
没有重试或假模型调用,结束时归属进程均为 0。该补充检查不等于真实模型图谱建议质量验收。
|
||||
|
||||
准备期间另有 `0cdf042` 宣传稿提交,已核对只有三份文档变化,产品源码仍截止 `66f77b8`,
|
||||
一并纳入完整候选。用户随后中断本地全量步骤,明确要求直接推送等待 CI;
|
||||
本地全量不记为通过,由精确候选的主分支 CI 执行完整测试和生产构建,通过后才打标签。
|
||||
不运行本地生产构建、安装包打包或安装包启动;使用既有隔离 Host 目录,不更改生产 Agent。
|
||||
schema 保持 48;从旧版升级应备份完整数据库和相邻 `notes/`,不能用旧客户端打开升级后的库。
|
||||
|
||||
## 首次候选 CI
|
||||
|
||||
`76e781a` 已同步双远端。Agent [CI 36379713606](https://github.com/mesalogo/goodbuddy/actions/runs/36379713606)
|
||||
通过;Desktop [CI 36379713607](https://github.com/mesalogo/goodbuddy/actions/runs/36379713607)
|
||||
5,389 通过、51 跳过、1 失败,生产构建未执行,未打标签。
|
||||
失败的设置测试用固定五个服务器名称过滤按钮,却用新增 Story Graph 后的六项目录断言数量。
|
||||
同步把旧模拟列表与按钮身份断言改为复用正式目录,保留数量和原交互断言,并逐项验证目录按钮存在。
|
||||
最终完整 SettingsPanel 文件、类型、lint 和发布说明检查通过;全量继续交由新提交 CI。
|
||||
@@ -21,8 +21,10 @@ diagnosis of their cause.
|
||||
- Native crashes and main-frame load failures use the existing bounded desktop
|
||||
diagnostics store (`desktop.renderer.gone`, `desktop.renderer.load-failed`,
|
||||
stage `renderer`). Persistent records retain fixed summaries rather than raw
|
||||
errors. Crash reason/exit code and React errors/component stacks go to their
|
||||
respective process consoles. There is no new diagnostic IPC.
|
||||
errors. Renderer exit records also retain Electron's enumerated `reason` and
|
||||
numeric `exitCode` when written, read back and exported; older records without
|
||||
these fields remain readable. The exit details still go to the Main console.
|
||||
React errors/component stacks remain console-only. There is no new diagnostic IPC.
|
||||
|
||||
The boundary does not catch module evaluation/startup failures before React
|
||||
mounts, event-handler exceptions, asynchronous rejections, a hung renderer, or
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
|
||||
## 文档导航
|
||||
|
||||
待实现的应用扩展见 [FR-17](./prd.md#fr-17-主机管理与云环境管理待实现):
|
||||
主机管理从设置中心迁入独立工作页面,云环境管理作为同级应用;业务定义由
|
||||
[远程主机功能文档](../remote-host/README.md)维护。上文四应用描述仍为当前实现。
|
||||
|
||||
现有 `heartbeat` 入口承载监督者工作回顾与故事线图谱,可管理启用、常驻和排序。应用默认关闭;完整执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。未来市场复用同一应用身份,不增加平级重复入口,目标定义见[监督者产品设计](../conversation-supervision/supervisor-prd.md)。
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
|
||||
@@ -264,3 +264,14 @@ Modal 只有服务列表,显示名称、简短状态、引擎、模型、资
|
||||
资源监控仍是只读系统工具,本机推理监控聚焦本地服务状态。该项独立于商店接入生产源码,支持 OCR 空闲加载与释放、内置向量进程启动/停止/重启;TTS 显示不可用。后端保留请求登记及原有取消能力,供执行路径和服务影响校验使用。各引擎边界见
|
||||
[技术清单](./technical-design.md#111-本机推理任务与执行服务),验证范围见[实施进度](./progress.md)。
|
||||
原首期资源监控的 GoodBuddy 桌面进程统计范围不因此扩大。
|
||||
|
||||
### FR-17 主机管理与云环境管理(待实现)
|
||||
|
||||
2026-09-28 确认新增两个同级应用:“主机管理”和“云环境管理”。主机管理从设置中心迁入,
|
||||
两个应用均从应用菜单、管理应用和常驻入口打开主内容区独立页面,不嵌入管理 Modal。
|
||||
应用中心负责通用应用配置,业务操作在各自页面完成。
|
||||
|
||||
导航扩展须保留已有应用排序、常驻偏好和远程能力启用值;取消主机常驻不影响云环境使用
|
||||
主机能力。两个应用的能力依赖、迁移范围及产品验收由
|
||||
[主机与云环境 PRD](../remote-host/prd.md)和[功能逻辑](../remote-host/logic-design.md)定义,
|
||||
业务页面布局见[界面设计](../remote-host/ui-design.md)。该项尚未实现,不改变上文四应用的当前状态描述。
|
||||
|
||||
@@ -1,5 +1,28 @@
|
||||
# 应用导航实施进度
|
||||
|
||||
## 2026-09-28:并行会话输出时保持列表顺序
|
||||
|
||||
修复多个会话交替输出时,左侧列表随每个增量反复换位的问题。展示顺序统一使用消息创建
|
||||
时间,摘要查询补充只读 `latestMessageAt`。交互规则见[会话置顶与操作菜单](./ui-design.md#会话置顶与操作菜单),
|
||||
时间字段与合并边界见[会话列表读取与前端保留](../assistant-workbar/execution-history-storage.md#会话列表读取与前端保留)。
|
||||
本地及远程事件的更新时间写入、快照合并判定和增量保存保持原实现。
|
||||
|
||||
聚焦验证命令:
|
||||
|
||||
```text
|
||||
npx vitest run src/renderer/src/chat-conversation.test.ts src/renderer/src/App.test.tsx src/main/assistant/assistant-database.test.ts -t "conversation display order|parallel streaming conversations|summary and detail order|sorts pinned conversations|merges persisted pin metadata|lists lightweight history|persists only the changed conversation|preserves live .* across snapshot|loads only the opened history"
|
||||
```
|
||||
|
||||
结果:3 个文件、13 项通过。覆盖并行文字增量与完成后顺序、保存时间继续推进、本地和消息
|
||||
通道的摘要/详情刷新、新消息前移、置顶合并、同时间固定顺序、空会话回退、摘要附带实时
|
||||
消息及真实 SQLite 摘要时间。历史加载回归继续验证刷新不重复保存未变化的消息。
|
||||
修改过的 7 个 TypeScript 文件定向 ESLint 通过。中文文档按 deai-writing 自查清单复核。
|
||||
|
||||
本轮验证为 App 组件事件回归与真实 SQLite 测试,未进行 Electron 窗口或真实模型调用。
|
||||
改动仅涉及桌面展示及桌面 Main 的只读摘要,未改变 Agent 协议或桌面到 Agent 的执行路径,
|
||||
无需部署 Host。全仓 typecheck、ESLint 和 `git diff --check` 通过;全量测试运行十分钟后
|
||||
超时,期间报告配置摘要和 Agent 离线安装测试失败,未取得全仓测试通过结果。
|
||||
|
||||
2026-09-23 文档校正:当前 `heartbeat` 已作为可选应用管理,默认值与执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行),设置与导航契约见[技术设计](./technical-design.md#4-应用导航偏好)。下方日期记录保留当时实现与验证结果;其中“心跳始终显示”“无应用级开关”和旧字段删除说明不再代表当前行为。本次仅核对源码并更新文档,未重新执行功能测试。
|
||||
|
||||
## 2026-09-18:窄幅单列应用列表
|
||||
|
||||
@@ -231,7 +231,10 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
|
||||
会话行的“更多会话操作”提供“置顶会话/取消置顶”,本地与消息通道会话均可使用。
|
||||
置顶会话显示带“已置顶”可访问名称的图标;当前项目和搜索过滤仍生效,过滤后的列表先显示
|
||||
置顶会话,两组内各按更新时间倒序。取消置顶恢复时间排序,不改变会话更新时间。
|
||||
置顶会话,两组内各按最新消息的创建时间倒序;时间相同按会话 ID 固定顺序,无消息时使用
|
||||
会话更新时间。新增消息可以前移会话,同一消息的文字、推理、工具进度和完成状态更新不
|
||||
改变排序。有消息的会话重命名或修改设置也不前移。摘要刷新、打开详情和重新启动沿用同一
|
||||
规则,行右侧仍显示实际更新时间。取消置顶恢复消息时间排序,不改变会话更新时间。
|
||||
置顶状态重启后保留;保存成功后更新图标和顺序,失败保留原状态并显示应用错误通知。
|
||||
存储尚未就绪或置顶请求进行中时禁用置顶操作。
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# 助手工作栏
|
||||
|
||||
助手工作栏统一承载本机与远程执行空间、Task、成果、文件、浏览器和终端等持续工作界面。
|
||||
助手工作栏统一承载本机与远程执行空间、Task、成果、文件、浏览器、终端和笔记等持续工作界面。
|
||||
|
||||
“笔记”为应用级、可关闭的单实例 Tab,新布局默认不打开。它提供搜索、浏览和快速追加文字,
|
||||
与主页面魔法笔记共用存储;会话采集、来源跳转与草稿规则由
|
||||
[魔法笔记文档](../magic-notes/README.md)维护。该应用已接入并通过真实 Electron 验收;
|
||||
全仓验证未全绿,聚焦复跑与执行时失败见[笔记验证进度](../magic-notes/progress.md)。
|
||||
|
||||
## 文档导航
|
||||
|
||||
@@ -13,6 +18,7 @@
|
||||
| [执行记录存储与升级回收](./execution-history-storage.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 传递复用、消息恢复与真实部署验收;尚未实现 |
|
||||
|
||||
当前尚未拆出独立 User Stories、功能逻辑和 UI 设计文档;产品场景与验收保留在现有 PRD,
|
||||
|
||||
@@ -81,12 +81,14 @@ Record<ConversationId, Record<BrowserTabId, BrowserLiveState>>
|
||||
|
||||
`BrowserViewport` 统一观察 body Portal 的插入、移除和可见属性变化,以及浮层尺寸、
|
||||
窗口尺寸、滚动和过渡结束。`browser-viewport-occlusion.ts` 按共享语义识别
|
||||
`aria-modal="true"`、dialog、alertdialog、menu 和应用通知:可见应用级 Modal 始终释放
|
||||
`aria-modal="true"`、dialog、alertdialog、menu、tooltip 和应用通知:可见应用级 Modal 始终释放
|
||||
viewport,其余浮层只在与浏览器区域相交时释放。隐藏节点和隐藏祖先不阻塞浏览器。
|
||||
最后一个遮挡消失后,只有当前活动实例恢复同一 viewport 租约;网页、历史和页面状态不变。
|
||||
终端关闭确认复用此机制,不再维护单独的隐藏条件。页面无需逐个登记弹窗,也不能靠提高
|
||||
CSS `z-index` 覆盖原生子视图。此逻辑仅涉及 Desktop Renderer 的显示租约,不改变
|
||||
Runtime、MCP 或远端 Agent 的请求、导航与生命周期。
|
||||
同行帮助仍使用 `role="tooltip"`;相交判定使用其内容矩形,不使用铺满窗口的
|
||||
`FloatingPortal` 宿主。真实 App 的相交与非相交验证见[浮层审查修复记录](../../quality/overlay-review-2026-09-27.md)。
|
||||
|
||||
浏览器应用声明 `visibleAcrossContextSwitches: true`,打开的 Tab 跨项目、会话切换继续显示。
|
||||
切换活动 Conversation 不改变当前选中的浏览器或其 `targetRef.conversationId`;用户在会话 B
|
||||
|
||||
@@ -52,11 +52,11 @@ WAL 增长与 SQL 全历史扫描次数的定向验证见 [BUILD.md](../../../BU
|
||||
本次读取优化不修改 schema、迁移或用户数据:
|
||||
|
||||
- Renderer 启动及焦点、会话、队列刷新使用 `conversations.listSummaries(detailIds)`。
|
||||
普通未打开会话只返回标题、项目、置顶状态、上下文设置和消息数量/首条角色,不解析或跨 IPC
|
||||
普通未打开会话只返回标题、项目、置顶状态、上下文设置和消息数量/首条角色/最新消息创建时间,不解析或跨 IPC
|
||||
传输正文、工具、子任务及图片操作元数据。
|
||||
- 当前会话、现有 KeepAlive 视图,以及 Main 仍持有请求、数据库中仍有活动 Task 或
|
||||
streaming 消息的会话返回完整消息。列表包含全部 active 会话,不限制数量,
|
||||
按置顶优先、组内更新时间倒序返回;仅置顶不会加载完整历史。
|
||||
Main 按置顶优先、组内更新时间倒序返回;Renderer 使用下述展示排序。仅置顶不会加载完整历史。
|
||||
- 打开历史通过 `conversations.get(id)` 读取全部消息,不使用会截断历史的分页上限。
|
||||
加载时明确显示进度;失败保留会话与输入,提供原地重试。发送、队列派发和压缩上下文
|
||||
必须先确认完整历史,发送时不按消息条数截断;复制与导出临时读取完整内容,不把未打开会话留入视图缓存。
|
||||
@@ -71,6 +71,11 @@ WAL 增长与 SQL 全历史扫描次数的定向验证见 [BUILD.md](../../../BU
|
||||
接口保留供其他调用方使用。
|
||||
- 图片状态通过同一合并函数处理。本地更新与 Main 图片操作按原有时间优先级合并,
|
||||
未变化消息返回原对象,避免每次列表刷新后把无变化的整个历史当作脏消息再次保存。
|
||||
- 展示顺序由 Renderer 的 `sortConversationsForDisplay` 统一派生,规则见
|
||||
[会话列表排序](../application-tool-navigation/ui-design.md#会话置顶与操作菜单)。完整消息取
|
||||
`createdAt` 最大值;摘要的 `latestMessageAt` 在已有聚合查询中取 `MAX(created_at)`,
|
||||
与摘要上附带的实时消息取最大值。该字段仅用于桌面只读投影,不增加数据库列或持久化副本。
|
||||
`updatedAt`/`updated_at` 继续随流式事件更新,原有快照合并、终态优先与逐消息保存规则不变。
|
||||
- 本机和托管 SSH 的 Renderer/Main 读取、持久化与恢复投影共用以上实现。Agent 的
|
||||
transcript、原生 Session、权限及模型桥协议未改变;完整列表、摘要和详情都附带
|
||||
Main 当前恢复请求的问题投影。
|
||||
|
||||
@@ -86,6 +86,7 @@ Runtime
|
||||
资源监控
|
||||
成果
|
||||
文档
|
||||
笔记
|
||||
```
|
||||
|
||||
- 应用不得根据当前项目、会话、Runtime、主机或探测结果无提示地增删能力目录项。
|
||||
@@ -96,6 +97,7 @@ Runtime
|
||||
Conversation、Job、Run 或心跳事项。
|
||||
- 当前能力、连接、数据和空状态可以动态变化。
|
||||
- 能力不可用时,目录项或已打开面板显示原因、影响和可执行的配置或切换入口,不能只通过隐藏表示。
|
||||
- 笔记遵循魔法笔记的业务启用设置,启用时提供应用级单实例 Tab,禁用前处理草稿,禁用后移除入口和实例;取消主侧栏常驻不影响工作栏可用性。采集与来源规则见[魔法笔记逻辑设计](../magic-notes/logic-design.md)。
|
||||
- 用户可以在设置中调整目录顺序;恢复默认布局恢复标准目录与默认打开面板,不强制打开全部能力。
|
||||
- 浏览器、终端及其他声明为多实例的能力可以创建多个实例,每个实例拥有独立身份和范围绑定。
|
||||
|
||||
@@ -232,7 +234,7 @@ Runtime
|
||||
从当前聊天右栏迁移时,Task Center 和工作区作为默认打开、单实例且不可关闭的基础应用保留;
|
||||
浏览器保留一个默认打开实例,但注册为可关闭的多实例应用;成果保持默认打开、单实例且可
|
||||
关闭;终端不默认打开,由用户通过“+”创建,并从首期开始支持多实例。Tab 必须单行横向滚动,
|
||||
不能自动隐藏或缩写到不可辨认,“+”紧随最后一个 Tab 并随标签滚动,溢出时两端提供固定滚动箭头。Task Center 继续作为 Task 的现有
|
||||
不能自动隐藏或缩写到不可辨认。“+”固定在滚动区外的行末,溢出时页签区两端提供固定滚动箭头,箭头不覆盖“+”。Task Center 继续作为 Task 的现有
|
||||
入口;审批定位到所属 Task 或 Runtime。智能心跳不作为工作栏页签,其报告、建议、历史和
|
||||
完整配置统一归属“智能心跳”菜单入口。当前阶段不新增独立自动化中心。
|
||||
|
||||
@@ -662,6 +664,10 @@ SSH/远程项目操作;已有 Host、项目和凭据保持不变。若当前
|
||||
|
||||
启用后,设置中心显示“主机与远程执行”分类,管理:
|
||||
|
||||
> 上述入口为当前实现。2026-09-28 已确认后续迁入应用中心的“主机管理”,并新增同级
|
||||
> “云环境管理”;范围、能力依赖及验收见[主机与云环境 PRD](../remote-host/prd.md)。
|
||||
> 本节保留现有行为,入口迁移尚未实现。
|
||||
|
||||
- 主机名称、地址、端口和用户名。
|
||||
- 认证方式和凭据配置状态。
|
||||
- Host Key 算法与 SHA-256 指纹。
|
||||
|
||||
@@ -1,5 +1,188 @@
|
||||
# 工作栏实现与验证进度
|
||||
|
||||
## 2026-09-28:DS Web 资源符号链接与 macOS 发布恢复
|
||||
|
||||
Desktop `v0.15.0` 原生构建中,两个 macOS 目标均因包外符号链接未通过签名校验。
|
||||
Linux Host 隔离 fixture 调用实际 DS 资源准备及 electron-builder 文件复制后,
|
||||
确认相对 CLI 链接变成构建目录的绝对路径。复制现使用 `verbatimSymlinks`,
|
||||
保留 npm 相对链接;回归移除原始安装和暂存目录后,仍须能读取最终包内链接目标。
|
||||
Windows 打包聚焦回归 45 项通过,1 项 POSIX 符号链接测试不适用于 Windows;
|
||||
类型、lint 和 0.15.1 双语说明校验通过。完整冻结快照在 Linux Host 全量
|
||||
5,372 通过、51 跳过,新增符号链接用例实际执行通过;macOS 原生签名仍待发布 CI。
|
||||
这只改变 Desktop 的本地 DS Web 资源准备,不改变 Agent 的独立 Runtime 包装、
|
||||
模型桥或启动控制,模型调用 0 次。恢复版本及最终证据见
|
||||
[发布恢复记录](../../development/release-preparation-0.15.0.md)。
|
||||
|
||||
## 2026-09-27:窄栏添加入口
|
||||
|
||||
“+”已移出页签滚动容器;左右箭头和溢出测量使用扣除添加按钮宽度后的页签区域。
|
||||
Windows Electron 回归覆盖 `300px` 工作栏、默认四页签和 16 个含长标题的页签,验证
|
||||
原生鼠标打开应用目录、创建浏览器实例、Escape 返回、箭头滚动及 Home/End 激活。
|
||||
窄栏扩到 `900px` 后,默认页签的溢出箭头消失,缩回后恢复。
|
||||
中英文、浅深主题均通过,模型调用 0 次。范围与命令见
|
||||
[窄容器布局修复记录](../../quality/responsive-controls-review-2026-09-27.md)。
|
||||
|
||||
## 2026-09-27 Continue Update Status
|
||||
|
||||
The shared Continue adapter now skips the update service before it emits the
|
||||
checking state; behavior is specified in the [native client design](./runtime-native-client-proposal.md#62-continue-与-opencode).
|
||||
The regression executes the patched method and verifies idle-only state when
|
||||
disabled, while retaining the original path when the flag is absent.
|
||||
|
||||
- Windows production coordinator/PTY and real DS browser validation completed:
|
||||
Continue made 2 supplier requests, OpenCode 1, and DS Web 2; all five returned
|
||||
HTTP 200 and the clients displayed the expected answers. Captured terminal
|
||||
output contained no `Checking for updates`. Original settings stayed byte-identical.
|
||||
- Current adapter source was bundled and uploaded through pinned-identity SSH to
|
||||
the shared Linux x64 Host, under a dedicated `/root/tmp/gb-update-check-*`
|
||||
directory. Continue 1.5.47 reached its initialized Ask TUI and displayed
|
||||
`Continue CLI`; 15 seconds of PTY capture contained no update-check text.
|
||||
This startup check made zero model calls and did not run the Agent protocol or
|
||||
remote model bridge. Agent helper imports this same adapter, but complete
|
||||
remote shortcut and real-model acceptance remain unfinished.
|
||||
- Adapter tests: 30 passed. Repository typecheck and lint passed. Full `npm test`
|
||||
reached the tool's 600-second limit without a final summary; it is not recorded
|
||||
as a passing full run.
|
||||
|
||||
Evidence is in the local temporary `opencode/dsh-017-verify` directory:
|
||||
`update-check-evidence.json`, `update-check-main-result.json`, and
|
||||
`remote-pty-run.cjs`. The local action/IPC harness uses jsdom and simulated
|
||||
Electron transport with real child clients. It does not reproduce or rule out
|
||||
full desktop-window freezing. The inherited TLS verification override and lack
|
||||
of a complete background-network capture also limit the evidence.
|
||||
|
||||
### Full production App follow-up
|
||||
|
||||
A fresh `npm run build` passed. The resulting production Main, Preload and
|
||||
Renderer were launched in an isolated Windows Electron profile. Native mouse
|
||||
input selected Continue and opened its terminal; native text input and Enter
|
||||
submitted a minimal request. The actual Continue output contained the expected
|
||||
joined answer and no `Checking for updates`. Main HTTP dispatch instrumentation
|
||||
recorded two additional provider requests, both HTTP 200: this fix's validation
|
||||
now totals seven external model requests. A subsequent no-submit probe copied
|
||||
initialized TUI content and typed input from the active xterm buffer.
|
||||
|
||||
Early driver attempts targeted a restored inactive terminal, giving an empty
|
||||
clipboard; selecting the newly opened terminal resolved this without product
|
||||
changes. Another attempt sent Enter before focus/input settled and dispatched
|
||||
zero requests. These harness failures are not product freeze fixes. The full App
|
||||
remained callable and accepted terminal input in the successful runs; the user's
|
||||
original freeze has not been reproduced or uniquely diagnosed. Captured PNGs
|
||||
have not been visually reviewed. The production TLS policy still enabled the
|
||||
verification override, despite the launcher's strict environment setting.
|
||||
|
||||
Local evidence: `full-app-model-events.json`, `full-app-calls.json`,
|
||||
`full-app-buffer.txt` and the `full-app*.cjs` drivers in the same temporary folder.
|
||||
The isolated profile was removed after checking that no probe-owned Electron or
|
||||
Node processes remained; the separate diagnostic evidence was retained.
|
||||
The full-suite rerun completed in 734 seconds: 5,298 passed,
|
||||
86 skipped and 3 failed tests, plus one failed suite setup. Failures were the
|
||||
OpenCode isolation fixture (`spawnSync npm.cmd EINVAL`), Magic Notes layout's
|
||||
90-second timeout, and two HeartbeatCenter tablist assertions. These files were
|
||||
not changed here; this is not an all-green suite. Original runtime settings were
|
||||
byte-identical after the full-App probes, and no probe-owned processes remained.
|
||||
Remote shortcut integration and full Agent/model acceptance remain outstanding.
|
||||
|
||||
### Failure triage follow-up
|
||||
|
||||
The OpenCode isolation and native Magic Notes layout files passed together through
|
||||
`npm test` (8 tests). The earlier full-suite supervisor invoked Vitest directly,
|
||||
without npm's launcher environment; its OpenCode setup error did not recur through
|
||||
the npm entry point. The layout timeout also did not recur. These focused results
|
||||
do not replace the failed full-suite result.
|
||||
|
||||
Both HeartbeatCenter language cases failed independently at that point. Source inspection
|
||||
shows the graph contains a separate, named PageTabs for events/entities/relations
|
||||
inside the page-level tabs; the older test assumes there can only be one tablist
|
||||
anywhere on the page. Neither that feature nor its test was changed for this
|
||||
Continue fix. No unrelated UI change or assertion relaxation was made.
|
||||
|
||||
A separate 2026-09-27 follow-up corrected the navigation scope assertions after
|
||||
checking the graph design. HeartbeatCenter, SupervisorWorkspace and shared tab
|
||||
tests passed together (96 tests); the full suite was not rerun. See the
|
||||
[validation record](../../quality/responsive-controls-review-2026-09-27.md#heartbeatcenter-断言修复).
|
||||
|
||||
The retained full-App log contains no matching unresponsive, crash, timeout or
|
||||
maximum-update-depth errors. This does not establish the cause of the reported
|
||||
freeze. This follow-up made zero external model requests (the fix-validation total
|
||||
remains seven); the OpenCode regression uses loopback model fixtures.
|
||||
|
||||
## 2026-09-27 Local Native Client Validation
|
||||
|
||||
Scope: [native client proposal](./runtime-native-client-proposal.md), FR-2/3/5/6/7/8,
|
||||
US-2/4/5/6/8, local portions of P1/P2/P3/P5. Source baseline:
|
||||
`55efca3ee1d8a491c4d9709888e302188cd988bd` plus the concurrent working tree.
|
||||
|
||||
- Fixed the coordinator's browser-open failure path: a ready DS service and its
|
||||
gateway remain available for owner-scoped reopen/stop. Startup failures still
|
||||
release their gateway. Focused regressions cover current credentials, work mode,
|
||||
Node selection, concurrent open, owner isolation and retry.
|
||||
The DS service also includes the coordinator configuration key in its own match
|
||||
hash; updated skill digests or launch settings cannot reuse an older instance.
|
||||
- DS Ask now maps built-in gateway read grants to the official MCP tool names.
|
||||
Custom MCP remains Execute-only, following existing Runtime behavior. The real
|
||||
DS process test loads an assigned directory containing `SKILL.md`, rather than
|
||||
only a synthetic loose Markdown skill file.
|
||||
- Custom standard Node uses the current local tool environment selection without
|
||||
a separate npm install. The managed Node fallback still has a bespoke pinned npm
|
||||
install and remains a delivery gap; it was not exercised by this real-model run.
|
||||
- The opt-in IPC integration test uses the production action component, preload
|
||||
methods, registered handlers, saved project/model stores, coordinator, official
|
||||
DS Web process, and real Continue/OpenCode PTYs. Electron transport is simulated
|
||||
by the existing IPC test harness and the action component runs in jsdom; this is
|
||||
not a full desktop-window or packaged-app acceptance result. DS Web itself was
|
||||
driven in real headless Edge. The hook retains credentials only in process
|
||||
memory; the temporary settings file stores opaque test-cipher identifiers.
|
||||
|
||||
| Client | Configured Model | Supplier HTTP Dispatches | Result |
|
||||
| --- | --- | ---: | --- |
|
||||
| DS Web 0.1.7-rc.2 | `deepseek-flash` | 2 | Workspace/model selected automatically; Web displayed `NATIVE_WEB_OK` |
|
||||
| Continue 1.5.47 | `gpt-6-astra` | 4 | Two prompts and two title requests; second transcript confirms the assistant response, with underscores consumed by Markdown rendering |
|
||||
| OpenCode 1.18.29 | `gpt-6-astra` | 1 | Native TUI displayed `NATIVE_OPENCODE_OK`; supplier response also contains it |
|
||||
| Total | | 7 | All HTTP 200; no further dispatches needed |
|
||||
|
||||
The first terminal assertion matched echoed prompt text and was rejected as
|
||||
evidence. The second prompt did not contain the expected joined answer. The
|
||||
OpenCode probe waits for the rendered TUI and uses bracketed paste before Enter.
|
||||
The initial six-dispatch bound was increased to eight to cover the remaining
|
||||
OpenCode path; seven were used in total. The host environment supplied
|
||||
`NODE_TLS_REJECT_UNAUTHORIZED=0`, so this run does not verify strict TLS.
|
||||
|
||||
Evidence under the approved temporary `opencode/dsh-017-verify` directory:
|
||||
`native-live-evidence.json`, `native-main-result.json`, `native-web.png`, and
|
||||
`native-{continue,opencode}-terminal.txt`. `native-live-run.cjs` composes the existing
|
||||
encrypted settings snapshot/safeStorage bootstrap with the production IPC test.
|
||||
Original `runtime-settings.json` remained byte-identical. The final test verifies
|
||||
DS stop/reopen/owner isolation and failed fetch after stop, closes both terminal
|
||||
sessions, and removes its temporary workspace/configuration. Owned live client
|
||||
processes were absent after completion.
|
||||
|
||||
Checks: official DS/coordinator focused tests passed (12) under standard Node 24.19.0;
|
||||
the opt-in UI/preload/IPC test passed both without provider calls and with the live
|
||||
hooks. Four installed Continue/OpenCode Ask/Execute tests passed with real clients
|
||||
and deterministic supplier responses; these made zero external supplier calls.
|
||||
Repository lint and Agent/Renderer TypeScript checks passed. Main typecheck is
|
||||
currently blocked by concurrent SSH
|
||||
test doubles missing `forwardLoopback`; no local native-client type errors remain.
|
||||
The full `npm test` run finished with 5,285 passed, 79 skipped and one failure:
|
||||
the desktop runtime-hook fixture lacked the new DS Web package tree. The fixture
|
||||
was corrected and all 44 build-release tests then passed in a focused rerun. The
|
||||
full run began before the final configuration-key regression and fixture updates;
|
||||
the final DS/coordinator and UI/preload/IPC paths were rerun separately and passed.
|
||||
This is not a claim of a subsequent all-green full-suite run. Full output is in
|
||||
`native-full-tests.log` beside the live evidence.
|
||||
|
||||
Remaining at this checkpoint: remote coordinator wiring and real Host acceptance belong to the
|
||||
concurrent remote implementation; Continue/OpenCode Ask built-in MCP mappings,
|
||||
managed standard Node distribution, full Electron/packaged UI acceptance and other
|
||||
platforms are not verified here. `ipc.ts` was not edited in this work.
|
||||
|
||||
2026-09-28 update: native Continue/OpenCode Ask now pass Main-bound read-tool
|
||||
names to native permission flags and execution hooks. The Story Graph work also
|
||||
adds Supervisor enablement to native-client reuse keys and gateway checks.
|
||||
The OpenCode generated plugin is executed by a focused test; packaged native UI
|
||||
acceptance remains separate. See [Story Graph validation](../conversation-supervision/progress.md#2026-09-28-story-graph-只读工具).
|
||||
|
||||
## 2026-09-26 结构升级与历史回收分离(未发布)
|
||||
|
||||
- schema 45/46 的回归先复现普通结构升级执行 `VACUUM`,修复后不转换历史、
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
# Runtime 原生客户端启动方案
|
||||
|
||||
日期:2026-09-26
|
||||
Status: Local shortcut/coordinator paths and configured-model responses verified on Windows; remote integration and packaged Node delivery remain open.
|
||||
范围:Continue、OpenCode 的交互终端,以及 DeepSeek Harness(DS)的官方 Web 客户端。
|
||||
|
||||
本文集中维护该新增入口的需求、场景、行为规则、接入设计和验收计划。既有终端行为由
|
||||
[多终端页签 PRD](./terminal-tabs-prd.md)维护,既有 Runtime 权限和资源边界由
|
||||
[Runtime 交互边界](./runtime-interactions.md)维护;本文描述新增客户端路径,不宣称它已经具备后台 Runtime 的全部能力。
|
||||
|
||||
## 1. 方案选择
|
||||
|
||||
在聊天输入区“压缩上下文”左侧增加原生客户端入口。Continue 和 OpenCode 打开 GoodBuddy
|
||||
工作栏中的交互终端;DS 启动官方 Web 服务并在系统默认浏览器中打开。使用当前项目目录和
|
||||
当前模型,默认建立独立客户端会话,不自动复制 GoodBuddy 聊天历史。
|
||||
|
||||
| 当前 Runtime | 按钮文字 | 打开目标 | 本轮计划范围 |
|
||||
| --- | --- | --- | --- |
|
||||
| Continue | 在终端中打开 | 工作栏新终端页签,启动原生 CLI | 本地和托管 SSH 项目 |
|
||||
| OpenCode | 在终端中打开 | 工作栏新终端页签,启动原生 TUI | 本地和托管 SSH 项目 |
|
||||
| DeepSeek Harness | 在浏览器中打开 | 系统默认浏览器,访问官方 Web 应用 | 本地项目 |
|
||||
| 内置模型 | 不显示此快捷入口 | 无 | 保持现状 |
|
||||
|
||||
DS 采用官方 `@deepseek-ai/dsh` Web 应用,不开发新的 DS 终端前端,也不在首轮嵌入
|
||||
GoodBuddy 浏览器工作栏。远程 DS 不在本轮范围:当前共享远程启动契约只接受
|
||||
`opencode` 和 `continue`,不能通过快捷按钮绕过该范围。
|
||||
|
||||
## 2. 调研依据与证据范围
|
||||
|
||||
下表保留早期测试报告,作为技术选型线索。2026-09-26 新增的 DS 0.1.7-rc.2
|
||||
实测单列在表后;Continue、OpenCode 和远程交互终端本轮未复测。
|
||||
|
||||
| 历史报告项目 | 报告结果 | 尚需验证 |
|
||||
| --- | --- | --- |
|
||||
| Continue 1.5.47 | 标准 Node 与真实 PTY 可显示交互输入、配置和模型名称;直接用 Electron 启动失败 | 启动路径错误、当前模型注入、真实请求和权限 |
|
||||
| OpenCode 1.18.29 | 随包二进制可显示 TUI,响应终端尺寸变化 | 排除已有用户配置干扰,验证指定模型实际生效 |
|
||||
| DS Web 0.1.5-rc.3 | Node 24.19.0 启动;Edge 可访问页面及模型设置 | 项目自动选中、模型桥、真实对话、工作模式 |
|
||||
| DS Windows 依赖 | 首次缺少 Sharp 原生包,补装后可启动 | 正式资源中预先带齐各目标平台依赖 |
|
||||
| DS 工作目录 | 指定 cwd 后仍要求选择工作区 | 工作区创建及页面选中的完整路径 |
|
||||
| 终端相关回归 | 报告为 5 个文件、29 项通过 | 新入口尚无回归或端到端验收 |
|
||||
| 远程 Host | 两条网络路线 SSH 端口可达 | 登录后的当前源码、Runtime、模型桥真实验证 |
|
||||
|
||||
早期文档整理和依赖升级报告的真实模型调用为 0 次。本轮核查另发现临时目录
|
||||
`dsh-017-live` 的 7 次历史 dispatch 记录;本轮在独立 `dsh-017-verify` 目录新增
|
||||
9 次,两个计数分别保留。测试过程和结果以 [DS 实现进度](../deepseek-harness/progress.md#2026-09-26-真实模型与官方-web-验证)为准。
|
||||
|
||||
本轮直接检查了以下仓库材料:
|
||||
|
||||
- `src/main/terminal/terminal-session-manager.ts`:已有本地与 SSH 会话创建、窗口归属、事件转发和关闭管理,当前创建路径启动普通终端。
|
||||
- `src/shared/remote-runtime-launch-contracts.ts`:远程 Runtime 枚举包含 Continue 和 OpenCode。
|
||||
- `UI-DESIGN.md`:规定 Composer 元信息区、工作栏、通知、键盘及响应式规则。其聊天章节仍写远程只支持 OpenCode,与当前共享契约不一致;实施时需沿 UI 到执行路径核实并同步修正,不能单凭枚举认定远程交互已可用。
|
||||
|
||||
上游参考:[官方仓库](https://github.com/deepseek-ai/deepseek-harness)、
|
||||
[CLI 文档](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/README.md)、
|
||||
[使用指南](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/guide/index.md)。
|
||||
这些链接随上游变化;实施时须为选定包版本补充对应源码 revision、启动参数和接口依据。
|
||||
历史测试版本是候选版本,不作为未经复测的最终发布版本。
|
||||
|
||||
2026-09-26 仓库内后台 DS Runtime 已升级至 `0.1.7-rc.2`,Cordis 升级至
|
||||
`4.0.4`,验证记录见 [DS 实现进度](../deepseek-harness/progress.md)。此版本来自
|
||||
npm `next` 标签;查询时 `latest` 仍为较旧的 `0.1.0-rc.6`。本轮另用官方
|
||||
`@deepseek-ai/dsh@0.1.7-rc.2` 验证 Web。Prepared Web resources and backend validation are recorded in section 9.1.
|
||||
|
||||
### 2.1 DS 0.1.7-rc.2 实测结论
|
||||
|
||||
Windows x64、标准 Node 24.19.0、Edge 154.0.4258.37 下,官方 Web 可承接当前
|
||||
工作区和已配置模型,完成页面真实对话。Main 使用 safeStorage 解密隔离副本中的模型
|
||||
配置,经现有 managed model bridge、AgentModelGateway 和 loopback proxy 转发。
|
||||
浏览器与 DS 只取得本地桥接信息,原始 `runtime-settings.json` 测前测后逐字节一致。
|
||||
|
||||
| 实测路径 | 结果 | 供应商 HTTP dispatch |
|
||||
| --- | --- | ---: |
|
||||
| 后台 Ask → `read` | 返回文件中未写入 prompt 的随机校验码,收到工具完成及用量事件 | 2 |
|
||||
| 同一后台会话切 Execute → `write` | 创建测试文件,内容校验通过 | 2 |
|
||||
| Execute → `pwsh` | PowerShell 读回该文件,结果正确 | 2 |
|
||||
| 官方 Web → 当前模型 → 文件读取 → 回答 | 页面返回另一随机校验码,刷新后保留结果 | 3 |
|
||||
| 合计 | 请求中的模型均为 `deepseek-flash`,HTTP 均为 200 | 9 |
|
||||
|
||||
`0.1.7-rc.2` 是 Harness/Web 包版本;`deepseek-flash` 是实际配置和请求中的模型 ID。
|
||||
Web 的 3 次请求包含 1 次未携带工具的请求,计数没有只按用户提交次数计算。
|
||||
后台使用当前源码的真实 ACP、Host、控制面和供应商流式响应,但 Host 在测试进程内组合;
|
||||
该结果不等于重新验收 GoodBuddy 完整 UI 或 Utility Process。Web 使用真实 CLI 子进程和
|
||||
浏览器自动化,未通过尚未实现的快捷按钮。测试继承了环境中的
|
||||
`NODE_TLS_REJECT_UNAUTHORIZED=0`,因此没有验证严格 TLS 证书校验。
|
||||
|
||||
工作区和模型自动准备已验证;官方 Web 到 GoodBuddy Ask/Execute 的工具执行适配、
|
||||
Skills/MCP 承接、取消和产品生命周期仍待实现与验证。此次可行性验证不要求完成按钮。
|
||||
|
||||
## 3. 产品需求
|
||||
|
||||
| ID | 要求 | 用户可见的验收结果 |
|
||||
| --- | --- | --- |
|
||||
| FR-1 | 在压缩按钮左侧提供 Runtime 对应入口 | 无历史、无压缩能力时仍能打开客户端 |
|
||||
| FR-2 | 按项目执行空间启动 | 本地使用本地目录,SSH 使用绑定 Host 与远端目录;目录失效时说明原因 |
|
||||
| FR-3 | 注入点击时选定模型 | 客户端首轮直接使用该模型,无需重复输入供应商密钥 |
|
||||
| FR-4 | 创建独立客户端会话 | 不修改原聊天历史,不把打开操作伪装成当前聊天的续接 |
|
||||
| FR-5 | 保持所选工作模式的执行边界 | Ask 的 AI 工具路径只读;Execute 使用相应账号已有权限 |
|
||||
| FR-6 | 管理启动、重开和关闭 | 不因收起工作栏或切换项目中断;关闭对应客户端资源时释放其桥接 |
|
||||
| FR-7 | 保留可处理的失败信息 | 启动、模型和远程错误可区分,允许修正后重试,不清空聊天草稿 |
|
||||
| FR-8 | 明确原生能力与 GoodBuddy 能力差异 | 已分配的 Skills、MCP 等逐项接入并验证;未接入项有明确说明 |
|
||||
| FR-9 | 交付可直接运行的客户端资源 | 用户无需自行运行 npx、补装 Sharp 或查找 Node |
|
||||
|
||||
聊天附件、知识范围、专家提示及历史不自动移入独立客户端。首轮不提供聊天双向同步、
|
||||
Task 关联恢复或客户端历史导入。原生客户端产生的历史由对应客户端保存,不能在 GoodBuddy
|
||||
活动记录中显示为已经同步的聊天内容。
|
||||
|
||||
FR-8 的实施清单须区分原生工具、GoodBuddy 分配的 Skills、自定义 MCP、内置 MCP、知识工具
|
||||
以及需要 GoodBuddy 原生确认的配置工具。支持情况以真实调用为准;现有 DS 宿主插件能力不能
|
||||
直接算作 DS Web 能力。发现现有分配无法承接时,在实施评审中明确差异和处理方式,不能默默忽略。
|
||||
|
||||
## 4. 场景与状态规则
|
||||
|
||||
### 4.1 用户场景
|
||||
|
||||
| ID | 前提与动作 | 预期结果 | 需求 |
|
||||
| --- | --- | --- | --- |
|
||||
| US-1 | 本地项目选中 Continue 或 OpenCode,点击入口 | 展开工作栏,新建带 Runtime 与项目名称的终端,进入可输入的原生界面 | FR-1~4 |
|
||||
| US-2 | 本地项目选中 DS,首次点击入口 | 服务就绪后打开浏览器,当前项目已选中、模型可直接使用 | FR-2~4、9 |
|
||||
| US-3 | 在支持的 SSH 项目点击终端入口 | 使用已保存 Host 连接,在远端项目目录启动对应 Runtime | FR-2、3 |
|
||||
| US-4 | 启动期间连续点击 | 同一启动只创建一次;按钮显示启动中 | FR-6 |
|
||||
| US-5 | 已打开客户端后切换项目或模式 | 旧客户端保持原项目和启动模式;新操作使用新选择 | FR-5、6 |
|
||||
| US-6 | 启动失败或模型请求失败 | 保留原因与可操作的重试方式;聊天内容和草稿不变 | FR-7 |
|
||||
| US-7 | Ask 下从原生界面请求写文件、执行写命令或使用写入 MCP | AI 执行边界拒绝写入,不能通过客户端模式开关绕过 | FR-5、8 |
|
||||
| US-8 | 关闭终端、停止 DS 服务或退出 GoodBuddy | 对应进程和桥接释放,其他会话不受影响;原生历史保留 | FR-4、6 |
|
||||
|
||||
### 4.2 行为决策
|
||||
|
||||
启动参数按点击时的项目、执行空间、Runtime、模型和工作模式确定。界面后续切换不修改已运行
|
||||
客户端。这里只需保留进程运行所需的内存记录,不增加持久启动快照、恢复日志或数据库迁移。
|
||||
|
||||
Continue 和 OpenCode 启动成功后,每次新的点击创建独立终端。启动中的重复点击合并到同一
|
||||
进行中操作。终端会话数量沿用现有管理器规则,不新增一套数量控制。
|
||||
|
||||
DS 按项目、模型配置及工作模式匹配运行服务。配置相同且服务可用时重新打开原服务;配置变更后
|
||||
新建匹配实例,保留原实例直到用户停止或应用退出,避免在已有页面中静默改变模型和权限。
|
||||
同一匹配项并发启动只执行一次。服务已退出则清除内存记录,下一次点击重新启动。
|
||||
|
||||
| 事件 | 处理 | 关联场景 |
|
||||
| --- | --- | --- |
|
||||
| 启动中切换项目 | 操作仍属于原项目;完成时不抢占新项目的工作栏焦点 | US-4、5 |
|
||||
| 收起工作栏、切换页面 | 保留终端与其模型桥 | US-5 |
|
||||
| 关闭终端页签 | 停止该 PTY/远端进程,释放相应配置和桥接 | US-8 |
|
||||
| 关闭 DS 浏览器标签 | 不推断服务已停止;再次点击可重开 | US-2、8 |
|
||||
| 用户停止 DS 服务 | 停止该服务和桥接;浏览器页面失去连接;保留原生历史 | US-8 |
|
||||
| GoodBuddy 退出 | 清理自己创建的进程与桥接,不停止用户其他进程 | US-8 |
|
||||
| SSH 断开 | 显示断开;收束对应请求,重试时重建连接,不宣称原会话已恢复 | US-3、6 |
|
||||
| 系统浏览器打开失败 | 保留可重开的就绪服务,通知用户重试;不复制含令牌地址到普通日志 | US-6 |
|
||||
| 服务启动失败 | 清理本次创建的临时资源,再允许重试;不删除用户原生历史 | US-6 |
|
||||
|
||||
Ask 沿用现有 Runtime 按工具命令名判断的只读规则,不改变用户手动终端的已有权限。
|
||||
DS 后台的 `read` 和原生 `skill` 可在 Ask 执行;`write`、`edit`、`pwsh`/`bash`
|
||||
在 Ask 拒绝,在 Execute 放行,代理工具沿用现有分类。工具出现在模型请求目录中不等于
|
||||
获准执行,判断仍在 `tools/execute` 路径。Web 接入复用这些规则,不增加 Shell 文本分析、
|
||||
目录围栏、逐工具二次确认或新的权限等级。Execute 使用当前账号已有的文件、进程和网络权限。
|
||||
|
||||
官方 `dsh-plan-mode@0.1.7-rc.2` 的 Plan 只改变规划提示和状态,工具目录不随之变化;
|
||||
权限预设由另外的 sandbox/approval 组件处理。因此不能把 Plan 名称当成 Ask 实现,
|
||||
也不能把官方默认“工作区内修改”当成 GoodBuddy Execute。接入时将启动模式接到现有
|
||||
工具名规则,原生页面的模式选择保持同一语义。
|
||||
|
||||
这些规则覆盖普通启动、使用、关闭和已知失败。尚需技术验证的项目见第 7 节;它们不构成新增
|
||||
授权层级或通用恢复框架的理由。
|
||||
|
||||
## 5. 界面设计
|
||||
|
||||
输入区下方从左到右排列“在终端中打开/在浏览器中打开”“压缩上下文”和现有用量信息。
|
||||
两个按钮独立判断可用性。采用同一轻量操作组,避免两组绝对定位相互覆盖;窄输入区允许
|
||||
操作组换行,保留模式说明、用量信息和对称边距。稳定预留元信息高度的实现应与
|
||||
[统一 UI 规范](../../../UI-DESIGN.md)一起更新并验证。
|
||||
|
||||
按钮使用原生 `button`、共享主题令牌和焦点环,支持 Tab、Enter 与 Space。打开终端后焦点
|
||||
进入新终端;外部浏览器由系统处理焦点。启动期间保留按钮宽度并显示“正在打开…”,完成后
|
||||
恢复原文案。缺少项目目录时在相关位置提供选择目录的操作;其他异步失败进入全局通知视口。
|
||||
|
||||
DS 服务运行后,在入口旁提供轻量菜单“重新打开”“停止此服务”;操作指向当前匹配服务。
|
||||
若正在生成,停止前说明会中断该服务中的请求。输入区不常驻显示独立会话和服务生命周期说明。不在每次启动时增加确认弹窗。
|
||||
|
||||
按钮快捷入口根据所选 Runtime 显示,不改变工作栏“+”中的普通终端和浏览器入口。已有服务
|
||||
的停止入口应在切换回来后仍可找到。
|
||||
|
||||
## 6. 技术接入
|
||||
|
||||
### 6.1 进程职责
|
||||
|
||||
Renderer 仅提交当前会话或项目标识及选择标识。Main 重新解析项目、模型配置、工作模式与
|
||||
程序路径,通过共享 Zod 契约和显式 preload 方法完成调用。保留已有可信发送方校验,供应商
|
||||
密钥留在 Main 加密设置和模型请求路径中。
|
||||
|
||||
建议新增小型原生客户端启动服务,负责配置准备、启动去重和资源释放。终端会话仍由
|
||||
`TerminalSessionManager` 管理;DS 服务只需内存中的子进程、地址和桥接引用。不要新建平行
|
||||
终端管理器或通用进程恢复引擎。
|
||||
|
||||
共享接口应返回可供界面使用的启动状态、终端标识或 DS 服务标识。外部 URL 由 Main 打开,
|
||||
不允许 Renderer 提交任意程序、命令行、凭据或任意服务地址。错误保留组件、阶段和脱敏原因。
|
||||
|
||||
DS Web 随包依赖通过 `build/native-dsh-resources.cjs` 保留 npm 的嵌套布局。
|
||||
复制时原样保留相对符号链接,不能将 CLI 链接改写成构建机的绝对路径;
|
||||
经过 electron-builder 资源复制后,即使移除原始安装和暂存目录,链接仍须能解析到包内文件。
|
||||
这一交付约束覆盖 FR-9;不改变 Agent 的独立 Runtime 打包路径。
|
||||
|
||||
### 6.2 Continue 与 OpenCode
|
||||
|
||||
Continue 使用已解析的标准 Node 启动固定版本 CLI,不能直接假定 Electron 的
|
||||
`process.execPath` 等于 Node。OpenCode 使用已有平台对应可执行资源,启动交互 TUI。
|
||||
两者均复用现有配置生成机制,但单独验证原生交互模式可接受的参数、模型 ID、环境变量及
|
||||
Skills/MCP 配置,避免继承测试机器的全局默认模型。
|
||||
|
||||
本地 PTY 创建需接受 Main 生成的可执行路径、参数、环境和 cwd,不能先打开普通 Shell 再
|
||||
模拟键入一段含凭据的启动命令。退出 CLI 后是否保留终端输出沿用既有终端体验;其桥接应
|
||||
随 CLI 生命周期收束,不等到整个聊天结束。
|
||||
|
||||
Continue 的共享宿主适配器在 `GOODBUDDY_DISABLE_CONTINUE_UPDATES=1` 时,直接让更新服务保持 idle,并显示 `Continue CLI`,不进入 checking 状态。仅跳过底层版本查询仍会短暂显示 `Checking for updates`,因此服务入口和版本查询两处都需跳过。本地原生终端与 Agent Continue helper 使用同一适配器;已有缓存按补丁内容哈希重新生成。实测范围见[更新提示验证记录](./progress.md#2026-09-27-continue-update-status)。
|
||||
|
||||
模型桥作为客户端会话资源保持有效。复用前检查当前桥接是否依赖单轮请求、单一消息 ID
|
||||
或短时授权;必要时只提取所需的会话级资源接口。真实供应商密钥不写入 CLI 配置,客户端
|
||||
仅获得桥接所需的局部连接信息。
|
||||
|
||||
### 6.3 DS Web
|
||||
|
||||
使用固定版本的官方 Web 包,通过标准 Node 在回环地址启动。以下命令已在
|
||||
`@deepseek-ai/dsh@0.1.7-rc.2`、Windows x64、Node 24.19.0 下启动成功:
|
||||
|
||||
```text
|
||||
<managed-node> <resolved-dsh-cli> web --no-open --host 127.0.0.1 --port 0
|
||||
```
|
||||
|
||||
生产环境直接使用已准备的资源,不在用户点击时执行不固定版本的 `npx`。Web 资源与当前
|
||||
DeepSeek Harness 后台 Host 依赖分开管理,避免为了 Web UI 升级而隐式替换后台 Agent 库。
|
||||
沿用既有受管工具环境和资源分发机制,不新增下载体系。若需在线分发,按该功能文档规范
|
||||
明确下载源设置范围、固定版本、大小和 SHA-256;镜像与原地址使用相同字节内容。
|
||||
|
||||
使用 GoodBuddy 管理的独立 `DSH_HOME`,与用户已有 DS 安装隔离。原生历史和用户设置属于
|
||||
持久数据,服务退出只删除可重建启动配置。多个运行实例不得共享不支持并发访问的数据库;
|
||||
在验证官方存储行为后确定按项目及配置分目录的最小方案。
|
||||
|
||||
启动后经官方支持的配置/API 创建或查找项目工作区,初始化模型连接,再打开已选中目标
|
||||
工作区的页面。仅设置 cwd 或成功创建后台工作区都不足以完成该步骤。不得直接修改未经
|
||||
确认的 DS 私有数据库结构。
|
||||
|
||||
本轮验证使用 `$DSH_HOME/cordis.patch.yml` 配置 `llm-pi-ai` 的 `goodbuddy` provider,
|
||||
设置 `api: openai-completions`、本地桥 `baseURL`、`apiKeyEnv` 和模型列表;
|
||||
`agent-default-model` 指向同一 provider/model。环境变量中只放本地桥接标识。
|
||||
官方 CLI 的 `profile-boot` 代码将该 patch 作为用户配置层加载。
|
||||
|
||||
取得官方启动地址并建立认证 cookie 后,调用 `POST /api/workspace/create`,请求形态为:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "client-request",
|
||||
"rpcId": "<uuid>",
|
||||
"method": "workspace/create",
|
||||
"payload": { "args": { "request": { "path": "<project-directory>" } } }
|
||||
}
|
||||
```
|
||||
|
||||
该接口在首次运行返回 `created: true`,重开同一测试目录时返回 `created: false`。
|
||||
随后访问根页面,两次均自动选中 `workspace` 和 `deepseek-flash`,没有手动选择模型或
|
||||
再次输入供应商密钥。此证据覆盖单项目、独立 `DSH_HOME`;多项目共享服务的选中规则未测。
|
||||
首次页面保留官方“内测声明”的“继续”操作;测试通过原生按钮继续,没有伪造接受记录。
|
||||
官方默认权限显示“工作区内修改”,本轮只验证读取;权限适配按第 4.2 节另行接入。
|
||||
|
||||
Main 检查服务就绪后,使用 Electron `shell.openExternal` 打开官方规定的访问地址。
|
||||
如地址带认证令牌,沿用官方认证方式,不写入普通日志。浏览器只访问本机服务;模型请求
|
||||
通过 Main 中的桥接到达供应商。HTTP、流式响应、取消和错误映射都需实测。
|
||||
|
||||
### 6.4 远程 Continue / OpenCode
|
||||
|
||||
沿用 SSH Host 身份、凭据存储和 SSH PTY,并通过既有 GoodBuddy Agent 部署资源定位远端
|
||||
Runtime。远端配置与模型桥需要客户端生命周期,不能直接复用一次聊天请求结束即释放的
|
||||
桥接实例。远端使用已有模型桥协议及传输路径,是否需要补充生命周期接口由源码验证决定。
|
||||
|
||||
开发时必须在共享 Linux x64 Host 上运行当前改动,按
|
||||
[远程主机技术设计](../remote-host/technical-design.md)执行真实启动、模型请求、取消、
|
||||
断开和清理。端口可达不等于验证通过;若 Host 不可达,应记录阻塞,不能宣布远程功能完成。
|
||||
|
||||
远程 DS 将来若扩展,可评估远端 Web 加 SSH 本地端口转发;本轮不修改 DS 远程能力声明。
|
||||
|
||||
## 7. 实施顺序与技术验证
|
||||
|
||||
| 阶段 | 工作与应提交证据 | 通过条件 |
|
||||
| --- | --- | --- |
|
||||
| P1 原生配置验证 | 固定三个客户端版本;保存脱敏命令、环境与程序路径;隔离已有全局配置 | Continue/OpenCode 使用指定模型;DS 自动选中目标项目 |
|
||||
| P2 模型与模式 | 每个客户端发送最小真实请求;验证流式、取消、Ask 写入拒绝及 Execute 专用目录写入 | 请求确实经过 GoodBuddy 模型桥;客户端模式切换不能破坏 Ask 边界 |
|
||||
| P3 生命周期接入 | 配置、PTY、DS 进程和模型桥接入现有管理路径 | 重开、失败重试、关闭及退出能释放对应资源,原生历史保留 |
|
||||
| P4 远程验证 | 在共享 Host 部署当前代码,验证 Continue/OpenCode 的 SSH PTY 与模型桥 | 启动、请求、取消、断开和重试均有真实 Host 证据 |
|
||||
| P5 UI 与交付资源 | 接入按钮、通知、停止 DS 服务;完善平台资源与文档 | 从真实按钮到客户端完成对话,无手动补依赖步骤 |
|
||||
|
||||
P1、P2 是实施前段的实验任务,完成后继续交付完整入口,不将实验程序或不可用按钮作为最终功能。
|
||||
|
||||
DS 单项目工作区选中和当前模型桥协议已通过本轮验证。后续优先接入三个客户端的既有
|
||||
Ask/Execute 工具规则、Skills/MCP 配置及客户端生命周期,再完成快捷入口和交付资源。
|
||||
官方 DS 首次预览声明保留原生“继续”流程,模型配置提前准备。
|
||||
|
||||
## 8. 验收计划
|
||||
|
||||
| 验收面 | 检查内容 | 需求与场景 |
|
||||
| --- | --- | --- |
|
||||
| 实际使用 | 每个本地客户端从 GoodBuddy 按钮进入当前项目,使用当前模型完成一轮真实对话 | FR-1~4、US-1、2 |
|
||||
| 配置隔离 | 预置另一套全局模型后,仍使用当前选择;两个项目不串目录或配置 | FR-2、3、US-5 |
|
||||
| 工作模式 | Ask 尝试文件写入、Shell 写入和 MCP 写入均不执行;Execute 在专用测试目录成功写入 | 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 |
|
||||
| UI | 浅深色、键盘操作、窄输入区、无压缩按钮、空对话、200% 文字缩放 | FR-1、US-1、2 |
|
||||
| 平台资源 | 对实际交付的 Windows、macOS、Linux 架构逐项验证 Node、PTY、CLI 和 DS 原生依赖 | FR-9 |
|
||||
|
||||
完成源码后执行 `npm test`、`npm run typecheck`、`npm run lint`。修改生产构建资源时再按
|
||||
仓库开发规则执行构建验证。单元测试覆盖行为和回归,不能代替真实按钮、模型和远程 Host 路径。
|
||||
每份实测报告记录日期、源码 commit、包版本、平台、结果、脱敏证据位置及实际模型调用次数。
|
||||
|
||||
## 9. 文档交付状态
|
||||
|
||||
本轮完成 DS 后台真实模型与官方 Web 主流程验证,更新方案和
|
||||
[DS 进度证据](../deepseek-harness/progress.md#2026-09-26-真实模型与官方-web-验证)。
|
||||
P1 中 DS 单项目启动、模型预配置和工作区选中,以及 P2 中 DS 模型桥请求已实测。
|
||||
The DS Web backend implementation and its focused validation are recorded below. Current local shortcut evidence is recorded in [workbar progress](./progress.md#2026-09-27-local-native-client-validation); full Electron UI acceptance remains separate.
|
||||
后续入口交付事实写入工作栏 [progress.md](./progress.md),按 FR、US 和阶段编号关联本方案。
|
||||
|
||||
实施时同步更新工作栏产品范围、终端规范、Runtime 交互边界及根 UI 规范;DS Web 的版本、
|
||||
依赖与兼容性写入 [DeepSeek Harness 文档](../deepseek-harness/README.md),远程部署事实写入
|
||||
[远程主机文档](../remote-host/README.md)。保持各自职责,不复制多个相互冲突的支持矩阵。
|
||||
|
||||
### 9.1 DS Web Backend, 2026-09-26
|
||||
|
||||
`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.
|
||||
`start({ projectId, workspace, profile, workMode, configurationKey?, skillDirectories?, mcpServers? })`
|
||||
returns `{ id, url }`. Main opens this URL with `shell.openExternal`; it must not
|
||||
log the token-bearing URL or pass it to Renderer. `stop(id)` and `dispose()` close
|
||||
the owned process tree and bridge. A browser-open failure leaves the service ready
|
||||
for another call. Matching concurrent starts reuse one instance; changed model
|
||||
credentials, project, mode, or capability assignments create a separate instance.
|
||||
|
||||
The service uses the existing managed model bridge, AgentModelGateway, and loopback
|
||||
proxy. Provider credentials stay in Main. Each matching configuration has its own
|
||||
persistent DSH_HOME. Shutdown removes launch configuration and the bridge ledger,
|
||||
while retaining native history and settings. Startup authenticates through the
|
||||
official launch URL and calls `workspace/create` before returning.
|
||||
|
||||
The host-level `tools/execute` hook permits native `read` and `skill` in Ask.
|
||||
Assigned MCP servers supply their already-classified `readOnlyTools` as raw MCP
|
||||
tool names; all other tool names are denied in Ask. Execute uses native full-access
|
||||
permissions with no additional approval. The native permission picker is hidden;
|
||||
changing Plan or creating another native session does not change the launch policy.
|
||||
Skill directories and stdio/streamable-HTTP MCP configurations use official plugins.
|
||||
Knowledge, built-in MCP, and GoodBuddy confirmation/configuration tools require a
|
||||
caller-owned MCP adapter and are not automatically exposed by this service.
|
||||
|
||||
`@deepseek-ai/dsh@0.1.7-rc.2` is a pinned build dependency. The existing packaging
|
||||
hook copies its installed dependency tree, including frontend assets and nested
|
||||
dependency versions, to `resources/runtimes/dsh` outside ASAR. Target Sharp and
|
||||
Koffi packages come from exact lockfile entries through the existing artifact
|
||||
preparation code. The Web package itself needs no click-time npm install. The
|
||||
coordinator now honors the current custom Node selection from the local tool
|
||||
environment. Its managed fallback still installs `node-<platform>-<arch>@22.22.0`
|
||||
through npm; this does not yet satisfy the prepared-resource delivery requirement
|
||||
in section 6.3. No new download setting was added.
|
||||
|
||||
Focused tests use standard Node 24.19.0 on Windows x64 and the prepared resource
|
||||
tree. They exercise Web startup, authentication, project creation, reuse,
|
||||
mode isolation, stop/restart, retained history, and real Web API tool execution.
|
||||
Deterministic model responses travel through the production model bridge: Ask
|
||||
rejects writes, Execute creates the expected file, assigned skills load, read-only
|
||||
MCP executes, and writing MCP is rejected before dispatch in Ask. These backend
|
||||
tests make zero external model-provider calls. This evidence does not claim a
|
||||
signed installer, other platforms, or the complete shortcut UI has been verified.
|
||||
|
||||
Validation: seven focused tests passed against the prepared resource tree;
|
||||
the copied Sharp 0.35.4 native module encoded a PNG successfully; Main TypeScript
|
||||
checking and `npm run build:bundle` passed. Full `npm run build`/typecheck and
|
||||
`npm run lint` encountered concurrent Renderer errors in App.test.tsx and
|
||||
RightAssistantSidebar.tsx; the backend files passed focused lint. Remote Agent
|
||||
launch paths are unchanged because this service supports local DS Web only.
|
||||
|
||||
### 9.2 Local Coordinator Integration
|
||||
|
||||
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
|
||||
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.
|
||||
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.
|
||||
OpenCode prefixes names with the bound MCP server name; Continue uses raw names.
|
||||
Story Graph additionally rechecks Supervisor enablement on every call.
|
||||
|
||||
The coordinator remains local-only at this checkpoint. The remote implementation
|
||||
is being developed separately and must be connected and verified on the shared
|
||||
Host before remote shortcut support can be claimed. Current checks and exact
|
||||
supplier counts are maintained in the workbar progress record linked above.
|
||||
@@ -70,6 +70,9 @@ schema 35 的存储修复与本方案独立,不能回退或删历史来改善
|
||||
| Cordis | 4.0.2 | Host 根组件与 Agent scope 的生命周期 |
|
||||
| Continue CLI | 1.5.47 | 实际受管补丁入口的单 `M.session`、QuizService 和权限状态 |
|
||||
|
||||
本表保留进程复用探针执行时的版本。2026-09-26 DS 已升级为 `0.1.7-rc.2`,
|
||||
Cordis 为 `4.0.4`;当前依赖及升级回归见 [DS 实现进度](../deepseek-harness/progress.md)。
|
||||
|
||||
已通过的三个隔离探针:
|
||||
|
||||
1. **OpenCode HTTP**:调用当前生产 Server launcher,只有一次 spawn;同一个 SDK client
|
||||
|
||||
@@ -44,7 +44,7 @@ SSH 连接池,但尚不存在工作栏应用实例模型、终端 IPC、终端
|
||||
### 3.1 工作栏应用
|
||||
|
||||
工作栏应用是可以在助手工作栏中打开的一个能力,例如任务中心、工作区、浏览器、成果或
|
||||
终端。每项应用通过统一注册表声明:
|
||||
终端、笔记。每项应用通过统一注册表声明:
|
||||
|
||||
- 稳定应用 ID。
|
||||
- 用户可见名称、图标和说明。
|
||||
@@ -54,7 +54,8 @@ SSH 连接池,但尚不存在工作栏应用实例模型、终端 IPC、终端
|
||||
- 当前不可用时的原因和修复入口。
|
||||
|
||||
目录中的应用可发现性与实例是否已经打开相互独立。应用不可用时保留入口并说明原因,不以
|
||||
隐藏代替状态。
|
||||
隐藏代替状态。笔记遵循其业务启用设置:关闭魔法笔记时隐藏目录入口并移除对应 Tab,
|
||||
关闭前处理草稿,规则见[会话联动逻辑](../magic-notes/logic-design.md)。
|
||||
|
||||
注册表字段如下:
|
||||
|
||||
@@ -76,14 +77,15 @@ SSH 连接池,但尚不存在工作栏应用实例模型、终端 IPC、终端
|
||||
`defaultOpen`、`required` 和 `closable` 不互相推导。例如浏览器默认打开但不是必需实例;任务
|
||||
中心和工作区既默认打开又必须存在。`required` 不表示锁定顺序,顺序只由 `reorderable` 控制。
|
||||
|
||||
当前五个应用的 `visibleAcrossContextSwitches` 均为 `true`。该属性记录应用目录的显示策略,
|
||||
当前六个应用的 `visibleAcrossContextSwitches` 均为 `true`。该属性记录应用目录的显示策略,
|
||||
不改变内容归属,也不授予其他会话的 MCP 访问权限;当前没有使用 `false` 的应用,尚未实现
|
||||
按上下文隐藏 Tab 的策略。
|
||||
|
||||
浏览器在创建时绑定当前 Conversation;切换会话后仍保留原 Tab、选中状态和页面,导航操作
|
||||
继续使用原 `targetRef.conversationId`。此时新建浏览器才绑定新会话。默认或旧布局中尚未绑定
|
||||
的浏览器在首次使用时绑定活动会话,此后不再随会话切换改绑。终端同样保留创建时执行目标;
|
||||
任务中心、工作区和成果的内容继续跟随当前项目。
|
||||
任务中心、工作区和成果的内容继续跟随当前项目。笔记使用应用级范围,选择与草稿不随项目或
|
||||
会话切换,不接受 `targetRef`;重启仅恢复工作栏 Tab 描述,不恢复笔记选择和草稿。
|
||||
|
||||
### 3.2 工作栏 Tab 实例
|
||||
|
||||
@@ -178,9 +180,9 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
|
||||
|
||||
- 已打开实例位于一个语义化 `tablist` 中。
|
||||
- 每个实例使用独立 `tab` 和 `tabpanel`。
|
||||
- “+”固定在 Tab 行末尾,但位于 `tablist` 之外。
|
||||
- Tab 过多时实例区域单行横向滚动,不换行、不自动隐藏实例。“+”紧随最后一个标签并随标签一起滚动,滚到末尾时可见。
|
||||
- 标签全部可见时隐藏左右滚动按钮;溢出时左右箭头透明浮动于工作区两端,不占标签布局宽度。鼠标移入标签栏时显示箭头,移出后隐藏;键盘聚焦箭头时显示,无悬停能力的设备保持可见。悬停箭头时显示轻微底色。点击滚动约一屏,边界方向禁用且不拦截下方标签的指针操作。切换或新增激活标签时将其滚入可见区域。未激活标签使用低对比度灰蓝底色和细边框,激活标签保留亮底与强调色顶线。
|
||||
- “+”固定在 Tab 行末尾,位于 `tablist` 和页签滚动容器之外,占用独立宽度。
|
||||
- Tab 过多时实例区域单行横向滚动,不换行、不自动隐藏实例。“+”在 `300px` 工作栏内也保持可见和可点击。
|
||||
- 标签全部可见时隐藏左右滚动按钮;溢出时左右箭头透明浮动于页签区域两端,不占标签布局宽度,也不进入“+”的点击区域。鼠标移入标签栏时显示可滚动方向的箭头,移出后隐藏;键盘聚焦箭头时显示,无悬停能力的设备保持可滚动方向的箭头可见。悬停箭头时显示轻微底色。点击滚动约一屏,边界方向禁用并隐藏,且不拦截下方标签的指针操作。切换或新增激活标签时将其滚入可见区域。未激活标签使用低对比度灰蓝底色和细边框,激活标签保留亮底与强调色顶线。
|
||||
- 终端 Tab 默认使用递增标题,例如“终端 1”“终端 2”;状态栏持续显示项目、Host、
|
||||
工作目录和 Shell。用户可以重命名终端。
|
||||
- 任务中心、工作区、成果及新建终端的默认标题随界面语言切换,恢复布局时也使用当前语言。
|
||||
@@ -218,6 +220,7 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
|
||||
| 浏览器 | `multiple` | 当前 Conversation | 是,一个实例 | 否 | 是 | 是 | 每次创建新 Tab |
|
||||
| 成果 | `single` | 当前项目 | 是 | 否 | 是 | 是 | 聚焦已有 Tab |
|
||||
| 终端 | `multiple` | 当前项目 | 否 | 否 | 是 | 是 | 每次创建新 Tab |
|
||||
| 笔记 | `single` | 应用级 | 否 | 否 | 是 | 是 | 聚焦已有 Tab |
|
||||
|
||||
实例策略、默认状态和可关闭策略都是应用注册数据,不能分散硬编码在多个点击处理器中。
|
||||
|
||||
@@ -562,7 +565,7 @@ Schema、假数据终端或不可用入口的中间产品。
|
||||
- [x] 连续选择浏览器会创建页面、导航状态和元素引用相互独立的浏览器 Tab。
|
||||
- [x] 连续选择终端会创建多个互相独立的终端 Tab。
|
||||
- [ ] 关闭可关闭实例、切换和重排实例后,“+”仍保持可用。
|
||||
- [ ] Tab 溢出时单行横向滚动,两端箭头固定,“+”跟随最后一个 Tab,滚到末尾可见,不把 Tab 换成下拉框。
|
||||
- [ ] Tab 溢出时单行横向滚动,页签区两端箭头固定,“+”在滚动区外持续可见,不把 Tab 换成下拉框。
|
||||
- [x] 关闭可关闭的活动 Tab 后激活正确的相邻 Tab 并恢复焦点。
|
||||
- [x] 当前 Tab 的背景、边框、顶部指示和字重清晰可辨;关闭按钮仅在悬停、焦点或无悬停
|
||||
设备上出现,且不引发布局位移。
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# 会话 HTML 渲染实现进度
|
||||
|
||||
2026-09-27 浮层审查修复:全屏 iframe 已纳入共享焦点循环,跨文档 Escape 通过受限
|
||||
Main/Preload 通知返回外层。真实 App 中的带链接及纯正文预览均已验证键盘进入和 Escape,
|
||||
带链接预览另验证正反向 Tab 返回;没有开放 sandbox 权限或执行预览脚本。
|
||||
实现与测试范围见[浮层审查修复记录](../../quality/overlay-review-2026-09-27.md)。
|
||||
|
||||
## 已实现
|
||||
|
||||
- [x] 应用级持久开关,默认开启,并提供旧设置迁移。
|
||||
|
||||
@@ -56,6 +56,17 @@ iframe 无法按内容自适应高度,因为 `sandbox=""` 不含 `allow-same-o
|
||||
嵌入预览完全相同的清理后文档,不额外放宽 Sandbox 或 CSP。切换回复时,源码面板与全屏
|
||||
预览按当前 source 派生开合状态,不使用会引发级联渲染的重置 effect。
|
||||
|
||||
全屏 iframe 提供 `tabIndex=0`,共享焦点候选包含 iframe。关闭按钮的正向 Tab 显式进入
|
||||
预览,支持没有链接的长文;iframe 后的焦点守卫将原生 Tab 返回到后续 Modal 控件或关闭
|
||||
按钮。守卫本身不作为焦点循环的首尾,通知关闭按钮仍参与循环。
|
||||
|
||||
iframe 内的 Escape 不会冒泡到父文档。`registerWindowIpcHandlers` 只在主窗口直接子
|
||||
`about:srcdoc` frame 获得焦点且收到 Escape keyDown 时,向主 Renderer 发送
|
||||
`window:preview-escape`。Preload 暴露无参数订阅 `app.onPreviewEscape`;全屏组件仅在
|
||||
自己的 iframe 当前聚焦且所属 Modal 未被 inert 隔离时关闭。组件卸载移除订阅,窗口 IPC
|
||||
释放时移除输入监听。此路径不执行预览脚本、不授予同源权限,也不暴露原始键盘事件或
|
||||
Electron 对象;iframe 的 Sandbox/CSP 保持原值。
|
||||
|
||||
## 验证范围
|
||||
|
||||
- 应用设置默认值、关闭持久化和版本 10 迁移。
|
||||
@@ -65,4 +76,5 @@ iframe 无法按内容自适应高度,因为 `sandbox=""` 不含 `allow-same-o
|
||||
- 源码按钮的 `aria-expanded` 切换,以及全屏预览的初始焦点、背景 inert、Escape 关闭和
|
||||
焦点恢复。
|
||||
- 主 Agent、子 Agent、用户、推理和流式状态边界。
|
||||
- 真实 Electron 中带链接与纯正文 iframe 的进入、正反向 Tab 返回和内部 Escape;通过生产 Main/Preload 事件订阅完成关闭。
|
||||
- 从设置 Switch 到当前会话重新渲染的应用级集成路径。
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| [UI 设计](./ui-design.md) | 应用导航、工作回顾、侧栏反馈及页面状态 |
|
||||
| [User Stories](./user-stories.md) | 监督者与故事线图谱的用户操作和验收场景 |
|
||||
| [技术设计](./technical-design.md) | Main/Renderer 分层、数据模型、IPC、安全边界和实施顺序 |
|
||||
| [时态 Story Graph MCP 设计](./story-graph-mcp-design.md) | 只读工具、时间与版本语义、Runtime 接入,以及后续跨范围事实复用和记忆过渡合同 |
|
||||
| [回顾分块调度设计](./review-scheduling-design.md) | 第 0 节定义生产分页、批次持久化、续跑、当前设置及剩余边界;其余章节保留改造前基线、目标设计和原型证据 |
|
||||
| [实施进度](./progress.md) | 已验证实现、验证证据和当前边界 |
|
||||
| [故事线图谱设计](../story-graph/prd.md) | 图谱对象、环绕时间轴与实体交互 |
|
||||
@@ -23,3 +24,5 @@
|
||||
生产入口已接通完整来源分页、叶子持久化、持续处理至完成、活动页主动暂停/继续及批次详情;设置可调页大小、批次正文/消息上限、单次响应容量、两个模型超时和共享并发。实际合同及限制见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。真实 DeepSeek 验证已覆盖一个完整的 27 条消息会话;较早的 129 条消息实验仍未完成。两次实验共 5 次付费请求,详情见进度。原始 AbortError 原因尚未由日志确定。
|
||||
|
||||
实施状态以代码和[实施进度](./progress.md)为准。逻辑待定项见[逻辑设计](./logic-design.md),架构边界见[技术设计](./technical-design.md)。现有心跳行为见[智能心跳](../smart-heartbeat/README.md),原型范围见[Story Graph 演示](../story-graph/README.md)。
|
||||
|
||||
2026-09-28:Agent 可按需通过三个只读 MCP 工具读取决定、演变和来源;监督者关闭后不暴露工具并拒绝旧会话调用。本地与远程接线及验证边界见[实施进度](./progress.md#2026-09-28-story-graph-只读工具)。当前 memory 读取、精确 scope 增量进度和候选身份保留;跨 scope 叶子复用、读取入口切换及 `as_of` 仍属后续能力。
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
报告、监督叶子与导航整理共用监督专用 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 条消息的完整会话,较大样本失败单独记录。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
@@ -127,7 +127,7 @@ flowchart LR
|
||||
|
||||
## 状态与处理规则
|
||||
|
||||
下表包含目标规则;当前活动执行状态以本节后的“活动记录”为准,取消及部分完成尚未接入。
|
||||
下表包含目标规则;当前活动执行状态以本节后的“活动记录”为准,取消已接入,部分完成状态尚未接入。
|
||||
|
||||
| 规则 | 条件或状态 | 处理与用户可见结果 | 需求与场景 |
|
||||
| --- | --- | --- | --- |
|
||||
@@ -152,7 +152,9 @@ flowchart LR
|
||||
|
||||
旧监督成功记录继续显示;旧心跳与监督之间未保存关联时分别呈现,不用时间接近猜测关系。旧心跳未保存的执行范围显示不可用,不从当前计划反推。此前未保存的手动运行与失败无法恢复。心跳报告沿用计划的历史保留和删除规则;监督结果独立保留,原心跳被清理后仍可读取监督记录。活动不是永久审计库。
|
||||
|
||||
当前活动没有 `cancelled` 或 `partial` 状态,也不提供取消按钮。实体和关系修改只有当前状态及结果内容,没有按时间追加的人工修改记录。
|
||||
同一时刻只允许一个监督回顾执行,包括停止清理阶段;不同 scope 也共用此限制。手动新建或继续遇到占用时立即提示,不排队;自动回顾等待占用释放,再检查未完成运行和增量来源。暂停或失败后可以新建回顾,也可继续有效的原 run。
|
||||
|
||||
取消立即持久化 `cancelled`,但执行与清理尚未停止时 UI 显示“正在取消”,此时仍不能开始另一个回顾。停止后显示“已取消”,保留上次成功结果和本次已保存批次,不发布部分结果、不推进自动 checkpoint。已取消运行不能手动继续,自动检查也不恢复它;后续自动检查可新建 run 处理尚未提交的来源。关联心跳活动显示监督取消状态,已保存的报告保持原状态。界面入口及提示关闭规则见[UI 设计](./ui-design.md#活动)。当前没有 `partial` 状态;实体和关系修改仍没有按时间追加的人工修改记录。
|
||||
|
||||
收集、叶子整理、导航合并和发布分别展示。阶段推进只依据服务记录,最终完成只依据结果事务;“六批已保存、剩余零来源”仍可处于合并失败或发布失败。继续保留成功叶子和导航节点,缺失阶段不得靠错误文本或来源数猜测。导航摘要允许省略事实数组,叶子缺少事实数组仍失败;导航不得生成替代叶子事实的非空数组。阶段及输出合同见[生产调度](./review-scheduling-design.md#已接入的调度与存储)。
|
||||
|
||||
@@ -170,6 +172,8 @@ flowchart LR
|
||||
|
||||
## 完整性与待定项
|
||||
|
||||
FR-S11、US-S28 至 US-S31 的规则由[时态 Story Graph MCP 设计](./story-graph-mcp-design.md)统一定义:时间/确认与有效性见第 3 节,读取范围与分页见第 4 节,应用关闭与 Runtime 接入见第 6 节,跨 scope 复用与记忆过渡见第 7、8 节。D1 读取已接入,后两项仍待实现;Global/项目独立 checkpoint 和同 scope 候选身份保留。MCP 不接受 `as_of`,较晚生成的结果也不自动使旧决定失效。
|
||||
|
||||
本设计覆盖触发、证据输入、静默检查、正常整理、取消与失败、跨视图共享结果、用户纠正、知识库写入及历史回放。它补充产品逻辑,不代表接口、持久化或迁移方案已经完成。
|
||||
|
||||
仍待产品决策:事件触发与阶段识别条件、反馈频率与静默判定、实体合并与拆分的交互、时间轴比例和集中度含义。应用启停按[应用启停与执行](#应用启停与执行)处理;既有入口偏好、计划与运行记录的保留要求见 FR-S1、S2 和 US-S14。
|
||||
|
||||
@@ -1,11 +1,68 @@
|
||||
# 监督者实施进度
|
||||
|
||||
日期:2026-09-24。
|
||||
日期:2026-09-28。
|
||||
|
||||
当前记录以已验证生产行为为准。监督者尚未覆盖全部 user stories。
|
||||
|
||||
## 2026-09-28 Story Graph 只读工具
|
||||
|
||||
对应 FR-S11、US-S28 至 US-S30 的 D1 读取实现。输入、分页与版本语义由[专项设计](./story-graph-mcp-design.md)负责。US-S31 的跨范围事实复用和 memory 读取切换仍待实现。
|
||||
|
||||
实现路径:
|
||||
|
||||
- [共享工具合同](../../../src/shared/story-graph-tools.ts)定义 `story_graph_search`、`story_graph_get_context`、`story_graph_read_source`。内置目录与能力设置支持 Model、OpenCode、Continue、DeepSeek Harness 分配;应用关闭时 Main 不签发图谱读取权限。
|
||||
- [只读 SQLite 投影](../../../src/main/assistant/story-graph-reader.ts)读取精确 scope 下的历史结果、当前对象、事件、变化、关系、来源和成功叶子描述。保留旧决定与修订依据,确认状态和有效性分别返回,未知有效性不按时间推断。没有新增 schema、业务写入或模型调用。
|
||||
- [Main gateway](../../../src/main/agent/knowledge-mcp-gateway.ts)在发现、读取前和返回前复核监督者开关及 Runtime 分配;IPC 将项目和 Runtime 固定到请求。Model 和 Harness 代理、本地 MCP、原生客户端共用此入口。Harness 的目录、scope schema 转换及 Host Ask 执行钩子均已接通。
|
||||
- 远程沿用 `ProtocolRemoteRuntimeChannel`、`MainImageToolSession`、`AgentImageToolMcp` 和 `RuntimeAcpBackend` 的现有工具通道。Ask 可以只有图谱描述,没有图像生成描述。OpenCode 根/子会话仅放行当前端点;Continue Ask 保留 Main 会话 MCP,并由原生只读工具名和 Main 双重校验。
|
||||
- 对象页为 10/50 项,正文页为 4,000/8,000 码点(默认/上限)。Context 用带位置的 JSON 片段续读长对象;游标绑定过滤、scope 和读取修订,变更后返回 `stale_cursor`。来源默认读保存片段;显式 current 通过 locator 读当前记录,version 仅接受可验证的现存版本。知识引用片段不声称是整份知识原文,来源迁出项目、删除、未知历史和响应过大均有明确结果。
|
||||
|
||||
定向验证:
|
||||
|
||||
```text
|
||||
npx vitest run src/main/assistant/story-graph-reader.test.ts src/agent-daemon/story-graph-integration.test.ts src/main/assistant/supervision-production.test.ts src/main/capabilities/capability-service.test.ts src/main/ipc.test.ts src/main/remote-agent/protocol-remote-runtime-channel.test.ts src/agent-daemon/runtime-acp-backend.test.ts src/agent-daemon/continue-acp-helper.test.ts src/agent-daemon/opencode-subagent-plugin.test.ts src/main/agent/native-terminal-client.test.ts
|
||||
```
|
||||
|
||||
10 文件、337 项通过,1 项既有条件测试跳过。覆盖真实 SQLite、HTTP MCP、Main/Agent 消息往返、协议准备和释放、四个 Runtime 的 IPC 项目绑定、Supervisor 关闭后不再读取、取消、来源删除/迁移、旧游标、Unicode 正文续页、超长响应及 A → B 修订后 C 仍为选项。冷启动审查发现 current 来源读取依赖运行回顾后才注册的 SQLite 函数,改为按现存 context 直接计算同一修订;新连接回归通过。最终读取相关 3 文件、28 项再次通过。
|
||||
|
||||
其他受影响路径也已定向验证:
|
||||
|
||||
- `continue-runtime.test.ts`、`opencode-runtime.test.ts`、`deepseek-harness-runtime.test.ts`、`native-client-coordinator.test.ts`、`protocol-remote-runtime-channel.test.ts`、`knowledge-mcp-gateway.test.ts`:6 文件、243 项通过。
|
||||
- `goodbuddy-harness-control-plane.test.ts`、`continue-host-adapter.test.ts` 及相关桥接/读取测试:6 文件、98 项通过。`model-tool-provider.test.ts`、能力设置及 Continue helper 组合:3 文件、92 项通过。
|
||||
- `npx vitest run src/main/agent/deepseek-harness-acp-e2e.test.ts -t "reads Story Graph"`:1 项通过、13 项按名称跳过。真实受控 Harness Host 经 ACP 和 Main 代理执行 Ask/Execute 读取,随后关闭监督者并确认工具移除;推理使用测试实现,没有外部模型请求。
|
||||
- `npx tsc --noEmit -p tsconfig.node.json`、`tsconfig.agent.json`、`tsconfig.web.json` 均通过;最终 Main 类型检查再次通过。本次变更的 TypeScript 源码和测试定向 ESLint 通过,`git diff --check` 通过。
|
||||
|
||||
### Linux Host 验证
|
||||
|
||||
[SSH 探针](../../../scripts/story-graph-host-probe.ts)从当前源码临时打包,在共享 Linux x64 Host 运行 `AgentImageToolMcp`、`AgentOwnedAcpPrompt` 和 `ContinueHostAdapter`,使用 Host 安装的 OpenCode 和 Continue(1.5.47)。桌面启动隔离内存 SQLite、Main gateway 和 `MainImageToolSession`,通过 SSH 搬运工具通道消息;用户数据库、模型凭据和正文库没有复制到 Host。Host 临时运行目录在结束时清理。
|
||||
|
||||
探针通过:HTTP 发现三个只读工具、搜索/上下文/来源读取及分页、`as_of` 拒绝、OpenCode Ask/Execute、Continue Ask/Execute 的实际工具调用,以及关闭后的发现和旧会话调用拦截。成功探针有 10 次本机确定性推理请求,其中 2 次为 Runtime 标题生成,桌面读取 10 次,付费调用 0 次。RPC blob framing 与 prompt 生命周期另由上述 `runtime-acp-backend` 和 `protocol-remote-runtime-channel` 测试覆盖;该 SSH 探针使用标准输入输出承载工具消息,没有执行安装包升级或完整桌面 UI 操作。
|
||||
|
||||
复跑时在仓库使用 esbuild 将脚本输出到临时目录,再通过已有 SSH 身份传至 Host 的 GoodBuddy 测试目录。桌面命令参数为:
|
||||
|
||||
```text
|
||||
node <local-probe.cjs> <ssh-host> <remote-probe.cjs> <host-opencode-path> <host-continue-cn.js-path>
|
||||
```
|
||||
|
||||
### 保留边界
|
||||
|
||||
没有执行全量测试、付费模型请求、提交、推送或发布。真实模型如何根据冲突证据形成最终建议、跨平台安装包和完整桌面 UI 仍未在本次验收;没有据此声明 US-S28 的建议质量已验证。查询目前同步扫描所选 scope 的已发布事实,大库延迟和运行中断粒度没有性能验收。`replaces`/`contradicts`、历史有效期、`as_of`、跨 scope 事实复用和 memory 消费切换均没有伪造实现;已有记忆与 checkpoint 保留。中文设计扫描无阻断项,复核项是时间、范围和证据限制,按技术含义保留。
|
||||
|
||||
## 已验证
|
||||
|
||||
### 2026-09-26 单次执行与取消集成复核
|
||||
|
||||
对应 FR-S4、FR-S6、FR-S10、US-S24、US-S26。复核当前 Main、SQLite、IPC、Preload 与 Renderer diff,确认手动新建/继续遇到占用立即拒绝,自动回顾等待;取消立即落库,执行位置保留至并行批次及 Runtime 清理结束。UI 在此期间显示正在取消,X 仅关闭提示;已取消运行不能继续,也不会被自动恢复。实现合同见[单次执行与取消](./technical-design.md#单次执行与取消),界面见[UI 设计](./ui-design.md)。
|
||||
|
||||
发现并修复一个可达问题:活动页等待整个 `resume` 请求结束才释放操作锁,继续运行后的暂停和取消按钮因此一直禁用。现在发出继续请求后释放提交锁,独立处理完成/失败,Main 仍保证唯一执行。新增回归在修复前因暂停按钮禁用而失败,修复后验证继续中的取消、停止提示,以及原继续请求返回时不清除后续取消操作的状态。
|
||||
|
||||
本轮验证:
|
||||
|
||||
- `npx vitest run src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:38 项通过,包含上述新增回归、取消清理期间的状态、重新进入页面、手动重复提示及 X 不取消。
|
||||
- `npx vitest run src/main/assistant/supervision-review.test.ts src/preload/supervision-activity.test.ts src/main/ipc.test.ts -t "reserves before setup|persists cancellation|allows a new review after|cancels navigation|projects cancellation|wakes waiting automatic|production supervision IPC cancels|supervision"`:27 项通过、144 项按名称跳过;其中 Preload 文件未命中过滤,另用 `npx vitest run src/preload/supervision-activity.test.ts` 单独验证,1 项通过。
|
||||
- `npx eslint src/renderer/src/SupervisorActivity.tsx src/renderer/src/SupervisorActivity.test.tsx` 通过;本轮文件的 `git diff --check` 通过。中文新增内容按 `deai-writing` 清单人工审校,文档链接目标已核对。
|
||||
|
||||
上述测试使用隔离 SQLite、生产 handler/工厂及可控 Runtime、Renderer API 替身;本轮未运行 Electron 端到端、全量测试或类型检查,真实模型调用 0 次。用户提供的后端与前端实现代理测试通过记录作为先前证据,不并入本轮计数。远程不受影响的依据是 `ipc.ts` 生产工厂调用 `resolveRequestRuntime({ workMode: 'ask' })`,无项目、Runtime 选择或执行空间,解析直接返回桌面默认 Runtime;不进入 SSH/Agent 路径,未修改远程协议或取消实现。未运行远程实测,未改动其他工作的 Runtime/package 文件,未提交。
|
||||
|
||||
### 2026-09-26 移除回顾自动时间预算暂停
|
||||
|
||||
对应 FR-S4、FR-S6、FR-S10。生产服务移除提取循环和导航合并中的整次执行期限判断,持续处理至完成;已保存运行中的 `executionSeconds` 继续兼容读取,但不再控制暂停。设置页移除该控件,中英文帮助和活动配置改为持续处理说明。单次模型超时、共享并发、逐批保存、主动暂停及失败续跑保持原路径,具体合同见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。主动暂停仍中断在途请求,仅保留此前已保存批次。
|
||||
@@ -409,10 +466,19 @@ npx eslint scripts/review-algorithm.mjs scripts/review-algorithm.test.mjs script
|
||||
- 结束时间收尾:手动心跳使用真实报告完成时间,下游失败单独记录结束时间。`npx vitest run src/main/assistant/supervision-activity.test.ts src/main/assistant/heartbeat-service.test.ts src/main/assistant/heartbeat-database.test.ts src/main/assistant/supervision-history.test.ts`:4 个文件、21 项通过,新增受控时钟用例区分 10:00 开始、10:02 报告完成、10:03 下游失败。随后 Node typecheck 和这 3 个改动源码/测试文件的 ESLint 通过。
|
||||
- 已存在的手动失败无法补回;旧心跳没有冻结范围和关联 ID,保留缺失提示。心跳仍按原保留期限清理,监督结果独立保存。取消和人工修改时间序列审计未实现;`no_change` 的后续接入见下节。
|
||||
|
||||
### 2026-09-28 继续讨论正文与布局
|
||||
|
||||
侧栏与图谱来源详情直接显示可编辑的发送正文,移除隐藏上下文拼接及附带提示。目标会话独立分隔,底部复用共享右对齐操作区。失败保留草稿,取消不发送,目标切换继续使旧预览失效。Main 的来源筛选与发送契约未修改。
|
||||
|
||||
- `npx vitest run src/renderer/src/SupervisionDiscussion.test.tsx src/renderer/src/SupervisionCard.test.tsx`:10 项通过,覆盖正文可见、编辑后精确提交、删除引用后不补回、失败重试、取消、重复提交和目标切换。
|
||||
- `npx vitest run src/main/ipc.test.ts -t 'scopes supervision continuation|dispatches supervision continuation'`:2 项通过,使用真实 SQLite 与队列解析器核验来源范围及空附件发送。
|
||||
- `npx vitest run tests/supervisor-layout.electron.test.ts` 分别设置 `GOODBUDDY_SUPERVISOR_DISCUSSION=1`、`GOODBUDDY_SUPERVISOR_SIDEBAR=1` 运行通过。图谱覆盖 1440/390px,侧栏覆盖 480/300/200px,均含浅深主题;检查 16px 区块间距、8px 字段间距、目标分隔、右对齐及无横向溢出,侧栏另检查滚动后按钮可命中。截图与测量保存在临时目录 `opencode/discussion-feedback-graph`、`opencode/discussion-feedback-sidebar`。
|
||||
- Renderer 类型检查 `npx tsc --noEmit -p tsconfig.web.json`、8 个改动 TS/TSX/MJS 文件的定向 ESLint 与改动文件 `git diff --check` 通过。Electron 使用生产组件和模拟数据,未调用模型;未跑全套测试,未提交。
|
||||
|
||||
### 其他边界
|
||||
|
||||
- 新批次来源读取按 messageId 返回已保存片段及版本;旧历史缺少消息 ID 时保留会话/时间定位。本地知识引用通过现有 `KnowledgeService` 解析文档/分块,失效时显示不可用;外部引用只使用已保存 locator,不调用远端全库。
|
||||
- 继续讨论和知识库写入使用现有本地会话队列与知识库实体接口,并要求先预览、再由用户确认提交。侧栏已支持结果直达图谱,尚无可编辑的上下文预览。
|
||||
- 继续讨论和知识库写入使用现有本地会话队列与知识库实体接口,并要求先预览、再由用户确认提交。侧栏支持结果直达图谱;继续讨论的侧栏与图谱入口均已提供可编辑的实际上下文正文,发送规则见 [UI 设计](./ui-design.md)。
|
||||
- 关系移除保留撤销状态及原始来源;界面没有提供恢复入口。
|
||||
- 候选集最多 100 个实体,复用依赖模型显式选择;不会自动合并历史重复实体或跨 scope 合并。结果级历史内容已保存,事件滑块仍不重建逐事件的实体演变。Experiment 和完整图谱回放尚未实现;批次暂停/继续已接通。
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ schema 47 增加运行配置、来源清单、成功批次、导航节点四张
|
||||
|
||||
`supervision:pause` 将父 signal 传入排队及在途模型;取消后的输出不提交。`supervision:resume` 恢复已保存 run;`supervision:batches` 默认每页 10 批、最多 20 批,返回完整叶子及来源。活动返回已保存批次、码点数、剩余来源、当前阶段、排队或执行中的模型任务数及已保存配置。提取和导航汇总持续运行至完成,不按累计运行时长暂停;用户暂停会中断在途请求,仅保留已保存批次。完成判定不依赖摘要措辞。
|
||||
|
||||
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 时不回填推测值。清单和最终发布为同步事务,短阶段不保证被轮询看到,清单事务失败也不会留下部分阶段记录。批次接口只有成功记录,没有逐个失败/在途任务或项目、会话聚合状态;界面仅按分页返回的真实归属组织详情。
|
||||
|
||||
### 当前设置合同
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
# 时态 Story Graph 内置 MCP 设计
|
||||
|
||||
日期:2026-09-28。状态:D1 只读工具及本地、远程接线已实现,验证记录见[实施进度](./progress.md#2026-09-28-story-graph-只读工具)。D2 跨范围复用、D3 记忆读取切换及历史重建仍未实现。
|
||||
|
||||
本文负责 FR-S11 的 MCP 工具契约、时间语义、范围复用和记忆过渡设计。产品要求见[监督者产品设计](./supervisor-prd.md),用户验收见 [US-S28 至 US-S31](./user-stories.md#us-s28-依据决定演变继续工作)。既有图谱对象与交互仍由[图谱 PRD](../story-graph/prd.md)定义。可执行输入以[共享工具合同](../../../src/shared/story-graph-tools.ts)为准,数据投影见 [Story Graph Reader](../../../src/main/assistant/story-graph-reader.ts);本文件同时保留后续阶段目标。
|
||||
|
||||
## 1. 目标与现状
|
||||
|
||||
监督者的主要产出是带时间、演变关系和来源的 Story Graph。工作回顾、侧栏和 Agent 上下文从同一组事实中组织内容。扁平记忆条目无法独立表达“当时决定了什么、后来为何修订、现在应采用什么”,因此不再作为目标架构的主要产物。
|
||||
|
||||
用户侧统一为监督者维护的工作记忆:包含长期偏好、决定、事件、未解决事项及其依据。“替代记忆”指统一概念与后续读取入口,保留已确认偏好、用户修订和已有数据。它不授权删除 `memory_items`、抹掉历史或给旧内容补造事件。
|
||||
|
||||
| 能力 | 当前代码事实 | 本设计目标 |
|
||||
| --- | --- | --- |
|
||||
| 图谱与来源 | 已有结果、事件、实体、关系、结果图谱快照及成功叶子;历史归属存在缺失边界 | 按对象、时间和相关性读取事实与演变,并返回来源版本和覆盖信息 |
|
||||
| MCP | [内置目录](../../../src/shared/builtin-mcp-servers.ts)和[能力合同](../../../src/shared/capability-contracts.ts)已有 `story-graph`,包含三个只读工具 | 按需读取,沿用应用启停及 Runtime 分配 |
|
||||
| 时间与关系 | [监督合同](../../../src/shared/supervision-contracts.ts)已有 `occurredAt`、确认状态和 `revised`;关系枚举没有明确替代/矛盾类型 | 分离发生、生成、记录时间;用有证据的关系表达替代和矛盾 |
|
||||
| 范围与身份 | checkpoint 按阶段、精确 scope、来源区分;候选实体只取同 scope | 同一来源版本的有效叶子事实可跨 scope 复用,范围仍控制可见性与解释 |
|
||||
| 记忆 | `reviewBackground` 仍读已确认记忆;最多四条、每条 500 字符,作为当前背景 | 逐步由统一查询承接,保留确认状态与适用范围 |
|
||||
| 历史重建 | 有结果快照,不具备完整来源版本及人工修改时间序列 | 首期只提供 `current`、`timeline`;`as_of` 留待历史重建能力具备后设计交付 |
|
||||
|
||||
现状依据还包括 [AssistantDatabase](../../../src/main/assistant/assistant-database.ts) 的 `saveReviewCheckpoints`、`listSupervisionCandidates`、`reviewBackground`,以及[生产分块调度](./review-scheduling-design.md#0-生产接线与剩余边界)。现有结果快照只证明某次回顾保存过哪些内容,不能据此声称能还原任意时刻的认识。
|
||||
|
||||
## 2. 对象与稳定身份
|
||||
|
||||
复用现有 SQLite、领域对象和来源定位。设计不要求图数据库、独立向量库、完整正文副本、新持久化日志或通用历史快照层。所需读取视图先从已有结果、叶子、关系和 locator 推导;无法推导的内容明确返回未知。
|
||||
|
||||
| 对象 | 概念合同 | 身份与关联规则 |
|
||||
| --- | --- | --- |
|
||||
| 故事线 `story_line` | `id`、主题、范围、事件与实体引用 | 沿用故事线 ID;不把项目名当 ID |
|
||||
| 事件 `event` | `id`、类型、描述、发生时间、实体及来源引用 | 决定、讨论、变化、里程碑是事件类型;一次回顾运行不是工作事件 |
|
||||
| 实体 `entity` | `id`、描述、确认状态、有效性、演变引用 | 对象 ID 与内容修订分离;同名或模型局部 ID 不证明同一实体 |
|
||||
| 变化 `entity_change` | 实体、事件、变化类型、依据、适用时间 | 指向具体对象版本;无独立主键时以已有所属对象和稳定关联键定位,不以页内下标造身份 |
|
||||
| 关系 `relation` | `id`、两端引用、类型、理由、来源、确认状态 | 替代/矛盾关系必须能定位两端主张及支持该关系的证据 |
|
||||
| 阶段总结 `summary` | 结果 ID、范围、区间、正文、事实引用 | 用于导航;检索与上下文仍能访问完整叶子事实 |
|
||||
| 来源引用 `source_reference` | 引用 ID、来源类型与原 ID、版本、locator、可用性 | 引用 ID 与原消息/任务 ID 分开;跨结果的引用 UUID 不等于新的原始来源 |
|
||||
|
||||
偏好、方案、决定涉及的概念沿用实体及事件表达,不为每个业务名词另建基础设施。业务上的“已决定”(`decided`)与“已完成”(`completed`)是不同状态,这里不声明新增任务状态枚举;运行 `completed` 仅表示整理发布成功。决定采用方案、计划执行、模型写出“已决定”均不能推出任务完成,完成须有任务记录或明确执行证据。
|
||||
|
||||
返回对象携带 `object_ref`(类型和稳定 ID)、`object_revision`、`result_id` 及适用 scope。版本可使用已有结果身份与可用修订信息表达;没有可靠对象版本时返回 `unknown`,不把 `updated_at` 伪装成完整版本链。旧文本主键及 Main 精确别名映射继续保留,`candidateRef` 只是一次模型输入内的候选引用,不作为 MCP 持久 ID。跨 scope 身份合并须有明确映射依据,不能因复用叶子事实而自动合并同名实体。
|
||||
|
||||
## 3. 时间、确认与有效性
|
||||
|
||||
| 维度 | 含义 | 禁止的推断 |
|
||||
| --- | --- | --- |
|
||||
| `event_time` | 原记录描述的事件发生时刻或区间;带精度、来源依据,未知可空 | 不以回顾时间填补未知发生时间;消息发送时间只有在确实表示该事件时才可采用 |
|
||||
| `review_generated_at` | 产生该归纳的回顾时间 | 较晚生成不表示事件较晚发生或旧决定已被替代 |
|
||||
| `recorded_at` | 对象或版本实际写入系统的时间 | 迟录、补录不改变事件先后,也不证明当时已知 |
|
||||
| `validity` | 该主张适用的区间及当前有效、被替代、存在争议或未知状态 | 不用确认时间、记录时间直接推导有效起止 |
|
||||
| `confirmation_state` | 自动归纳、已确认、用户修订、已撤销等内容审核状态 | 已确认不等于永久有效;未确认不等于已失效 |
|
||||
|
||||
`replaces` 表示有明确证据的新主张替代旧主张,记录方向、两端版本、适用范围、理由和来源。`contradicts` 表示两项主张存在证据支持的冲突,不自动决定谁正确。它们是目标关系语义,不能将当前 `contrasts` 或单纯 `revised` 无条件映射为这两类关系。新增选项、日期较新、措辞更肯定都不足以建立替代关系;没有证据时保留并列主张或待核对差异。
|
||||
|
||||
时间区间输入使用带时区的 ISO 8601 时间,查询归一化为 UTC。概念 `time_range` 使用闭区间 `[from, to]`,与现有监督范围保持一致;区间事件按相交判定。检索支持明确的 `time_basis`:默认 `event_time`,也可指定 `review_generated_at` 或 `recorded_at`。响应回显所用维度。未知时间不冒充命中区间,返回可计算的排除数量或 `unknown`,允许通过不限定时间的查询另行查找。
|
||||
|
||||
`timeline` 按发生时间和稳定 ID 排序,未知时间单列,展示目前保存的演变及缺口。它可以包含后来补录的旧事件,因而不等于“当时已知”。`current` 给出截至读取时、在已覆盖证据内可支持的当前理解;有冲突或缺口时保留并说明,不能仅挑最新摘要作为答案。
|
||||
|
||||
`as_of` 是未来能力,首期输入 schema 不接受该模式或参数,调用必须明确返回不支持,不能悄悄降级为 `current`。只有具备所需对象版本、人工修订记录以及可用来源版本/历史重建,并能验证不会泄漏当时未知的信息后,才另行定义其语义和验收。本文不批准为它新建持久化日志或快照体系。
|
||||
|
||||
## 4. 公共读取合同
|
||||
|
||||
三个工具都只读已有数据,不调用模型、不自动发起回顾、不更新 checkpoint、不确认或修订对象、不创建任务,也不写入记忆或知识库。检索采用已有数据的确定性过滤与相关性排序,不能在调用中借模型补齐缺失事实。生成面向用户的建议由调用方 Agent 完成,必须引用返回证据并遵守其范围与限制。
|
||||
|
||||
默认 scope 为调用会话的当前项目,由 Main 解析并冻结;没有当前项目时要求显式选择 scope,不默认为 Global。调用方可显式请求 Global 或指定项目集合,沿用已有范围与能力授权校验。分页继续使用首请求范围,切换 UI 当前项目不改变旧游标。对象与来源读取也要重新校验归属,稳定 ID 不能绕过 scope。
|
||||
|
||||
首期沿用现有精确 scope 结果归属,不把 Global 摘要冒充单项目结论,也不隐式把 Global 结果投影为项目上下文。项目查询缺少该 scope 数据时明确返回空结果及覆盖状态。未来跨 scope 复用只接纳能按来源项目和事实依据正确筛选的叶子,跨项目归纳仍保留真实范围。
|
||||
|
||||
| 响应部分 | 必须表达的信息 |
|
||||
| --- | --- |
|
||||
| 范围 | `requested_scope`、`resolved_scope`、每项原始 scope、所选时间区间和时间维度 |
|
||||
| 修订 | 对象/结果修订引用、来源版本、读取时间;区分已知版本与 `unknown` |
|
||||
| 覆盖 `coverage` | 已知处理范围、来源版本和片段区间、缺失或未处理部分、未纳入来源类型;缺少数据时为 `unknown`,不能用零代替未知 |
|
||||
| 分页 `page` | `returned_count`、`has_more`、`next_cursor`、本页是否完整及限制原因;集合数量不能计算时为 `unknown` |
|
||||
| 证据 | 来源引用与 locator、关系依据、确认与有效性、已知冲突和历史缺口 |
|
||||
|
||||
覆盖与分页独立:最后一页只证明匹配结果读完,不能证明所有原始消息已被整理;已处理全部入选正文也不证明模型识别了全部事实。旧 `coverage_json` 只有条数时,不能推导完整的时间、版本或语义覆盖。
|
||||
|
||||
列表及长字段都需有界分页,超过响应容量时提供可续读的位置,无法分页的响应明确失败。不得静默裁剪后标完整。游标绑定过滤条件、排序和可用结果修订;分页期间内容变化、无法维持同一读取版本时返回 `stale_cursor` 并要求重新查询,不混入新旧页。该规则可基于现有修订检查完成,不要求持久化查询快照。取消、读取失败与真实空页分别返回。
|
||||
|
||||
## 5. 工具定义
|
||||
|
||||
### `story_graph_search`
|
||||
|
||||
输入:`query`、可选 `scope`、`object_types`、`time_range`、`time_basis`、`page_size` 和 `cursor`。`object_types` 对应第 2 节的对象类型;省略时检索全部支持类型。默认 scope 及时间规则按第 3、4 节执行。对象页默认 10 项,上限 50 项。匹配为不区分大小写的连续文本匹配,标题、实体标签和摘要字段权重高于其他字段;同分按类型、稳定 ID 和结果 ID 排序。
|
||||
|
||||
输出相关对象页,包含匹配原因或命中字段、明确标为摘录的预览、稳定引用、修订、相关演变和来源入口,以及公共响应信息。过滤须先于分页,按相关性排序并以稳定 ID 消除同分歧义。不能只在最近若干摘要中筛选后声称查完历史,也不能将摘要中的命中替代叶子事实。
|
||||
|
||||
### `story_graph_get_context`
|
||||
|
||||
输入:`object_ref`、可选 `scope`、`mode: current | timeline`(默认 `current`)、可选时间范围与维度、页大小及游标。`current` 表达当前理解;时间过滤只限定证据集合,响应需标明“在所选证据范围内”,不能称作该区间终点时的历史状态。
|
||||
|
||||
输出对象当前描述、明确的决定/工作状态、偏好与适用范围、未解决事项、相关实体及关系、演变和证据引用。`timeline` 分页列出提出、补充、验证、修订、替代和冲突证据,保留旧决定及新修订;多组集合或长正文须各自提供续读位置,不能只对顶层对象分页。返回的是存储事实及其组织视图,不在工具内生成新建议。
|
||||
|
||||
D1 返回各结果快照、当前投影、事件、变化、关系、来源及成功叶子的独立描述。既有存储没有可靠的有效期和替代关系枚举,因此 `validity` 返回 `unknown`,保留 `revised`、`contrasts` 及原始理由供调用方核对。当前实体投影不会遮掉旧结果或叶子描述。`content` 是可拼接的 JSON 文本片段,每项附零基码点位置;按对象引用和结果 ID 拼接后解析。`content_page_size` 默认 4,000、上限 8,000 码点,游标可停在对象内部。完整响应超过 100,000 字节时明确失败,要求缩小页大小。
|
||||
|
||||
对象不存在、scope 不匹配、版本缺失或只有部分历史时分别说明。已存在的某个结果快照可作为具体版本读取依据,但不能作为任意 `as_of` 查询的替代品。
|
||||
|
||||
### `story_graph_read_source`
|
||||
|
||||
输入:`source_reference_id`、可选 `scope`、明确的读取种类 `snapshot | version | current`、所需 `source_version`(读取指定版本时必填)及片段页位置。默认 `snapshot`,读取该引用实际保存的证据片段;不默认换成当前原文。
|
||||
|
||||
输出正文页、`content_kind`、请求版本与实际返回版本、`source_location`、原来源 ID、已保存片段与整份来源的范围和长度、可用时间元数据,以及公共覆盖和分页信息。位置用零基、左闭右开的 Unicode 码点 `[start, end)`;消息 locator 尽量保留项目、会话、消息、顺序和角色,本地资料保留文档/分块,外部片段保留已有 locator。缺少位置时明确标记,不伪造链接。
|
||||
|
||||
`snapshot` 只声称返回当次实际保存的片段;`version` 仅在该来源支持并保留指定版本时成功;`current` 显式读取当前内容并报告与引用版本是否不同。历史版本不可用时返回 `historical_version_unavailable` 及可用读取选项,由调用方决定是否另读当前版本。来源删除或不可访问时返回对应可用性;已保存片段仍可读时准确标注其范围。读取完一个片段不能称整份原文已读完,超长内容必须可续页,无静默截断。
|
||||
|
||||
D1 来源页默认 4,000、上限 8,000 码点。`current` 通过既有 locator 精确读取消息、任务状态正文或保存的知识引用片段;不拉取外部知识库原文。`version` 仅在当前可读来源的修订恰好等于请求版本时返回该版正文。旧版正文没有保留时明确不可用;无 locator 的旧引用仍可读 snapshot。当前来源迁出所选项目时拒绝读取。已保存引用的范围与当前位置分别校验。
|
||||
|
||||
## 6. 内置 MCP 与 Runtime 接入
|
||||
|
||||
内置 MCP 列表提供 `Story Graph`,能力 ID 为 `story-graph`,默认分配给 Model、OpenCode、Continue 和 DeepSeek Harness;监督者应用默认关闭,开启应用后才可发现工具。工具描述说明适用于“继续上次工作”“此前如何决定”“方案为何改变”等请求。普通聊天不强制查询,也不把整份图谱自动注入每轮上下文;Agent 先搜索、再取相关对象上下文,在需要核对原话、处理冲突或详细引用时读取来源。
|
||||
|
||||
| 配置或目录 | 当前状态 | 目标映射 |
|
||||
| --- | --- | --- |
|
||||
| `heartbeatEnabled` | 监督者应用开关,新设置默认关闭 | 为 Story Graph 暴露和每次调用的必要条件;关闭后不列出三个工具,旧会话直接调用也拒绝 |
|
||||
| 内置能力 `enabled` 与 `assignments` | [CapabilityService](../../../src/main/capabilities/capability-service.ts)管理现有 MCP 开关与 Runtime 分配 | `story-graph` 沿用同一管理方式;应用关闭时不得由内置能力开关绕过,保留用户原分配 |
|
||||
| 内置工具目录 | 已有 Story Graph 条目 | 映射 `story_graph_search`、`story_graph_get_context`、`story_graph_read_source`,全部 `access: read`、`readOnlyHint: true` |
|
||||
| 监督整理参数 | 超时、并发、来源页与批次设置已存在 | 控制后台整理;不让一次 MCP 查询启动整理,也不以正文批次预算截断检索结果 |
|
||||
| 计划 `enabled` | 决定自动回顾是否到期执行 | 不决定已有图谱能否读取;应用开启且能力可用时,无计划也能查已有结果 |
|
||||
|
||||
Main 在工具发现和调用分发时共同检查应用启用、能力启用、Runtime 分配和 scope;只隐藏菜单不足以阻止旧会话调用。关闭后拒绝新的读取请求,已在途响应交付前复核状态,避免旧绑定继续返回数据。启用工具本身不产生回顾或数据写入。Ask 与 Execute 均可读取,同样没有写工具,也不借此扩大 Execute 权限。
|
||||
|
||||
本地 OpenCode、Continue 和原生客户端复用 [KnowledgeMcpGateway](../../../src/main/agent/knowledge-mcp-gateway.ts) 的 MCP 注册和分发;直连 Model 与 DeepSeek Harness Main 代理调用同一服务。远程 OpenCode/Continue 复用现有图像工具的受管 HTTP MCP 和二进制通道,`storyGraph` 描述独立于图像生成描述,Ask 可只携带图谱读取。请求仍由桌面 Main 解析范围和读取 SQLite,不把数据库、正文库或凭据复制到 Host。
|
||||
|
||||
远程发现每次向 Main 复核启用状态;调用复用同一个范围、分页及版本读取入口。OpenCode 根会话和子会话精确放行当前受管端点的图谱工具;Continue Ask 保留 Main 绑定的会话端点并只允许共享只读工具名。DeepSeek Harness 的 Main 目录、参数转换和 Host 执行钩子均接入这三个工具;其他普通内置 MCP 的 Harness 限制保留。当前远程 Runtime 支持 OpenCode/Continue,不新增远程 Harness。桥接不可用时明确失败。
|
||||
|
||||
远程使用须配套部署含此接线的 Desktop 与 Agent;旧 Agent 不识别新增的工具描述字段。本次没有发布或更新受管包,源码验证不代表已发布客户端具备该能力。
|
||||
|
||||
## 7. Global 与项目复用
|
||||
|
||||
当前 Global、单项目和不同项目集合分别维护 checkpoint 和故事线候选身份,项目集合排序归一化后才比较。项目 A 已处理某消息,不会使 Global 的相同消息自动成为已处理;Global 当前会按自身范围重新收集原始来源,不能描述为“只聚合已有项目图谱”。报告与监督也是独立阶段,不能互相推进位置。当前手动重看还会重新整理所选历史。
|
||||
|
||||
目标是同一来源版本的事实提取成功一次后,在 Global 和项目查询/整理中复用可用叶子事实,包括事件、实体变化、关系证据及来源位置,不只复用项目摘要。Global 可在已提取事实之上组织跨项目关系和摘要;范围变化本身不应造成相同正文再次提取。范围控制查询归属和综合判断,不再充当重复提取的唯一分界。
|
||||
|
||||
复用须核对原来源身份、版本、已覆盖片段及提取合同是否仍适用。仅部分成功时继续缺失部分;来源改版或用户明确要求重新分析时可产生新的提取结果,不能覆盖已确认内容。任务的当前状态、偏好背景和既有摘要仍须标明时间角色,不能作为跨 scope 复用时新发生的事件。共享事实的可见范围不能被 Global 摘要扩大。
|
||||
|
||||
这一目标尚未实现。本轮不批准具体数据迁移、跨 scope 主键重写、历史实体合并、checkpoint 转换或缓存表方案。实施前应从已有成功叶子和来源版本定位验证最小复用方案,列出旧数据的可复用程度与缺口;保留现有精确 scope 行为直到替代方案完成验证,不提前修改现有 checkpoint 含义。
|
||||
|
||||
## 8. 记忆过渡与分阶段交付
|
||||
|
||||
已确认的长期偏好必须继续可用,并保留 Global/项目适用范围。旧记忆只有更新时间时,只能显示为当前偏好背景和实际更新时间,不能补造“首次决定”“当时生效”或替代关系。新的图谱归纳与已确认偏好冲突时返回待核对差异,不以较新生成时间覆盖用户选择。
|
||||
|
||||
| 阶段 | 交付内容与退出条件 | 当前状态 |
|
||||
| --- | --- | --- |
|
||||
| D0 设计 | 记录工具、时间、范围、过渡与验收合同 | 已记录 |
|
||||
| D1 只读查询 | 基于现有数据实现三个工具及本地/远程接线、配置目录、关闭拦截和分页;缺失版本与覆盖返回未知,对应 US-S28 至 US-S30 | 已实现;验证范围见实施进度,当前 memory 读取继续保留 |
|
||||
| D2 事实复用 | 验证同一来源版本跨 scope 复用完整叶子;明确身份、版本变化与已有数据处理方案,达到 US-S31 的范围验收 | 待设计具体存储调整并实施;无已批准迁移方案 |
|
||||
| D3 概念与读取统一 | 让统一入口承接已确认偏好及图谱上下文;核对引用、范围、冲突和旧数据可读性后,才切换现有 memory 消费路径 | 待实现;不以删除旧记录作为退出条件 |
|
||||
| 未来历史重建 | 单独定义并验证 `as_of` 所需版本与当时可知性 | 不属于首期,不承诺交付时间 |
|
||||
|
||||
D1 可将现有已确认偏好作为明确标记的背景投影,原读取路径在过渡期仍工作。重复背景按原记忆身份识别,图谱未知历史不能由记忆更新时间补齐。具体迁移与旧入口退出时点须根据实现证据确定,本文没有要求立即删除旧 memory API、设置或 UI。
|
||||
|
||||
## 9. 验收映射
|
||||
|
||||
核心场景使用可核对的旧决定 A、后来明确修订为 B 的证据,以及“现在继续做应采用什么”的请求。工具返回 A、修订事件、替代依据与 B;Agent 基于它们推荐 B,并引用旧决定和修订来源。新增选项 C 但没有替代依据时,不得把 B 判为失效;决定 B 也不能被回答为 B 已执行完成。
|
||||
|
||||
| 验收 | 对应故事 | 通过条件 |
|
||||
| --- | --- | --- |
|
||||
| 决定演变 | US-S28 | 完成上述旧决定 → 有证据修订 → 当前建议及引用链;冲突没有证据解决时保持待核对 |
|
||||
| 时间 | US-S28、US-S29 | 迟录旧事件不变成新事件;确认与有效性分开;timeline 不声称当时已知;`as_of` 明确不支持 |
|
||||
| 分页与来源 | US-S29 | 匹配项、演变和长原文均可读到末页;覆盖未知与零命中可区分;游标失效不混页;旧版不可用不返回伪历史 |
|
||||
| 启停与只读 | US-S30 | 应用关闭后发现与直接调用均不可用;开启但无计划可读已有数据;工具自身模型调用与业务写入均为零 |
|
||||
| Runtime 一致性 | US-S30 | 本地及 GoodBuddy Agent 远程实际链路具有相同 scope、版本、分页、引用与关闭行为,不能以共享 schema 代替接线验收 |
|
||||
| 记忆及范围复用 | US-S31 | 确认偏好与已有引用保留;同版本成功叶子跨 scope 复用且能检索事实,不只返回摘要;历史缺口不补造 |
|
||||
|
||||
验证命令和证据集中记录在[实施进度](./progress.md#2026-09-28-story-graph-只读工具)。本次只运行定向测试、类型检查、限定文件 lint 和确定性 Host 探针,没有全量测试、付费模型请求或发布操作。真实模型如何根据冲突证据形成最终建议,仍需与工具读取正确性分开验收。
|
||||
@@ -51,6 +51,8 @@ FR-S9:来源事实、自动归纳、待核对关联和用户确认内容分别
|
||||
|
||||
## 落地范围
|
||||
|
||||
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;本轮没有数据迁移。
|
||||
|
||||
触发、整理与结果共享规则,以及整体架构和图谱对象关系见[逻辑设计](./logic-design.md)。
|
||||
|
||||
首个完整流程为:启用监督者、选择范围、生成回顾、进入图谱、核对来源、携带选定上下文继续讨论。知识库双向定位与内容补充随后接入同一流程。图谱关系辅助检索需单独验证效果,不作为首版默认检索策略。
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
本文回答如何在现有 GoodBuddy 桌面端中实现监督者。它不改变产品范围,也不把模拟 Demo 当作生产数据模型。
|
||||
|
||||
FR-S11 的读取架构见[时态 Story Graph 内置 MCP 设计](./story-graph-mcp-design.md)。三个只读工具通过 `readStoryGraph` 投影现有 SQLite,Main 在发现、调用和返回前检查监督者启用及 Runtime 分配。本地 MCP、Model、Harness Main 代理和远程受管工具通道共用此入口;无 schema 迁移。该文独立定义对象/版本合同、时间语义、配置映射及后续跨 scope 复用和记忆过渡。当前 memory 背景读取、精确 scope checkpoint 与候选身份保留;结果快照不支持 `as_of` 历史重建。
|
||||
|
||||
生产入口由 `supervision-production.ts` 组装服务、SQLite、共享池和 ask Runtime。`supervision-review-store.ts` 保存冻结来源清单,按有界页和片段供 `SupervisorService` 派发;叶子及连续位置先提交,导航和完整结果之后发布。消息已有知识引用保留本地或外部 locator,已确认记忆通过独立背景输入提供。不会检索外部全库。详细存储、调度及当前限制统一见[分块调度第 0 节](./review-scheduling-design.md#0-生产接线与剩余边界)。
|
||||
|
||||
模型实体 `id` 只在单次输出内有效。Main 从同一 scope 的故事线提供最多 100 个未撤销实体候选,生产提示只暴露 `candidateRef`(如 `known_1`)、名称和说明。模型显式选择候选后,Main 转换为严格 UUID 类型的内部 `persistedId`。服务校验候选集成员和重复映射,保存事务再次按实体主键校验故事线归属及撤销状态。没有有效候选引用时分配新 UUID,不按名称或裸模型 ID 合并。来源仍按每次结果分配 UUID,保留原始 `source_id` 和 locator。
|
||||
@@ -33,7 +35,7 @@ schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、
|
||||
|
||||
侧栏通过 `onOpenSupervisionGraph(resultId)` 交给 App 导航;App 遵守现有离页检查,向 HeartbeatCenter / SupervisorWorkspace 传递类型化 `graphNavigation`,复用 `heartbeat` 路由及 keepalive 页面。每次点击创建新的导航请求,打开 graph 页签并更新历史选择共用的 resultId。overview 未包含该 ID 时仍直接请求其 graph;旧 overview、graph、来源和操作响应通过同一请求序号失效,不覆盖新结果。没有新增 window 事件、页面或持久化字段。
|
||||
|
||||
继续讨论入队时,无附件请求省略 `serializedContexts`;请求未指定 Runtime 时使用会话保存的选择。知识库预览与提交均检查目标库存在且为本地可写库,更新实体还检查其实际所属库。侧栏确认框展示 Main 返回的实体字段、目标库和来源正文,取消或写入失败后可以重新预览。
|
||||
继续讨论预览将 `supervision:continue-context` 返回的 `prompt` 直接作为可编辑草稿。侧栏与图谱入口发送当前草稿,不重新拼接原始上下文。`supervision:continue` 沿用现有正文校验(去除首尾空白,最多 12,000 字符),使用空 `attachments` 入队,无附件请求省略 `serializedContexts`;请求未指定 Runtime 时使用会话保存的选择。会话已有的记忆和知识库配置仍按普通会话发送规则处理。知识库预览与提交均检查目标库存在且为本地可写库,更新实体还检查其实际所属库。侧栏确认框展示 Main 返回的实体字段、目标库和来源正文,取消或写入失败后可以重新预览。
|
||||
|
||||
监督来源清单将 UI 时间区间归一化为 UTC,先筛选再按稳定键分页。项目必须存在且 active;消息按闭区间筛选,任务按区间内创建或完成时间筛选。手动和自动监督共用该路径,自动清单另读取 supervisor checkpoint;手动心跳报告保留原有 collector。
|
||||
|
||||
@@ -43,7 +45,7 @@ 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`,不携带 SSH 项目或执行空间。摘要校验、合并和持久化在 Main 完成;本次取消内容长度限制不改变 `gbagent`、远端 Runtime 启动或桌面到 Agent 协议。
|
||||
手动和自动监督均在桌面 Main 调用生产工厂,Runtime 解析请求只有 `workMode: ask`,不携带项目、Runtime 选择或执行空间。`resolveRequestRuntime` 因而返回桌面默认 Runtime,不进入 SSH 项目或 `selectedRuntimes.getRuntime` 分支;回顾 scope 只筛选证据。摘要校验、合并和持久化在 Main 完成,single-flight、取消及 UI 控制不改变 `gbagent`、远端 Runtime 启动或桌面到 Agent 协议。
|
||||
|
||||
`ApplicationSettingsStore` 持久化报告和整理模型超时,均为 30..600 整数秒,默认 240。报告排队前冻结,监督创建 run 时冻结;计时从获槽及 Runtime 解析后开始,覆盖模型执行及其内部重试。流结束校验 signal 和完成事件,finally 释放临时会话,心跳报告保持原有 100KB 上限,监督整理使用上文的可调响应容量。采集、排队、保存和清理不计入模型超时。
|
||||
|
||||
@@ -55,6 +57,14 @@ schema 47 的四张批次相关表使用外键随监督运行清理,未完成
|
||||
|
||||
当前设置及冻结范围见[当前设置合同](./review-scheduling-design.md#当前设置合同)。未实现的高级参数继续保留在目标清单,不显示为可保存控件;没有兼容原型数据库的读取器。
|
||||
|
||||
### 单次执行与取消
|
||||
|
||||
对应 FR-S4、FR-S6、FR-S10;状态规则见[活动记录](./logic-design.md#活动记录)。同一个 `SupervisorService` 在异步配置读取前占用唯一执行位置,`run` 和 `resume` 共用准入。已有执行或停止清理时,手动新建及继续立即返回 `SUPERVISION_REVIEW_BUSY`;`trigger: heartbeat` 等待当前 Promise 结束,醒来后重新检查占用。等待中的自动请求尚未创建监督 run,不占模型池;心跳报告在进入下游监督前已释放自己的槽位。
|
||||
|
||||
`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 仅修改本地显示状态。
|
||||
|
||||
## 自动增量收集
|
||||
|
||||
本节的 100 来源/2,000 码点批次查询现用于原心跳报告及旧服务测试;生产监督使用 schema 47 清单及批次位置,自动 checkpoint 只在完整发布时提交。已有 schema 46 指纹算法和 checkpoint 仍复用,不重置用户已处理位置。
|
||||
@@ -154,7 +164,7 @@ sequenceDiagram
|
||||
|
||||
运行状态与内容确认状态分开保存:
|
||||
|
||||
- 当前监督运行状态:`running`、`paused`、`completed`、`failed`、`no_change`。心跳及合并活动的映射见[活动记录](./logic-design.md#活动记录)。
|
||||
- 当前监督运行状态:`running`、`paused`、`cancelled`、`completed`、`failed`、`no_change`。心跳及合并活动的映射见[活动记录](./logic-design.md#活动记录)。
|
||||
- 内容状态:来源事实、自动归纳、待核对、用户确认、用户修订、已移除关系。
|
||||
- 运行成功只表示模型结果已按契约保存,不表示所有实体和关系已经被用户确认。
|
||||
- 失败保留上次完整结果和本次已保存批次;暂停不生成完整结果,继续入口恢复同一 run。
|
||||
@@ -213,6 +223,8 @@ erDiagram
|
||||
| `supervision:overview` | 工作回顾、运行状态、未解决事项和最近结果 |
|
||||
| `supervision:run` | 手动开始一次整理,完成后返回包含 runId 的结果 |
|
||||
| `supervision:activity` | 有界读取真实执行和阶段状态 |
|
||||
| `supervision:execution` | 读取唯一执行位置及停止清理状态 |
|
||||
| `supervision:pause` / `supervision:cancel` / `supervision:resume` | 暂停、终止或继续指定回顾;生命周期见上文 |
|
||||
| `supervision:graph` | 按故事线、时间区间和回放时刻分页读取图谱 |
|
||||
| `supervision:object` | 读取事件、实体、关系及来源详情 |
|
||||
| `supervision:confirm` | 确认、修订或移除自动关系和实体变化 |
|
||||
@@ -229,7 +241,7 @@ IPC 返回图谱查询结果时使用分页和有界字段;详情中的来源
|
||||
- 监督者模型调用固定为 `ask` 语义,输入来自 `EvidenceCollector` 的有界快照,工具授权始终拒绝。
|
||||
- `Global`、Project、多项目和固定 Conversation/Task/Experiment 目标在 Main 冻结;模型不能生成或改变范围。
|
||||
- 每个来源引用必须属于本次冻结范围,跨范围引用、伪造项目 ID 和不存在的来源 ID 直接使结果失败。
|
||||
- 心跳运行沿用现有 `heartbeat_runs` 的领取、租约和重试机制;新增监督运行按心跳运行 ID 关联,活动据此合并阶段。没有新增取消协议。
|
||||
- 心跳运行沿用现有 `heartbeat_runs` 的领取、租约和重试机制;监督运行按心跳运行 ID 关联,活动据此合并阶段。监督取消不撤销已保存的心跳报告,合并活动优先显示监督已取消状态。
|
||||
- 同一配置同时触发手动和定时运行时,沿用现有幂等键和运行领取规则;已完成结果不能重复写入。
|
||||
- 用户确认或修订后的实体、关系不被后续整理静默覆盖;新证据以补充或冲突待核对状态写入。
|
||||
- 外部知识库只读。图谱可以引用保存到本地的外部片段和定位信息,不调用远端写入、不承诺打开远端原文。
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
应用中心中的智能心跳入口升级为“监督者”,沿用既有启用、常驻与排序能力。未来应用市场使用同一应用身份。应用已启用时,停用单条自动心跳计划仍可手动回顾;关闭应用后,已打开页面显示关闭状态并禁用工作区操作。默认值及运行边界见[应用启停与执行](./logic-design.md#应用启停与执行)。
|
||||
|
||||
当前应用页只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”(英文活动标签为 Activity records)。自动监督独立管理计划;设置只显示模型超时与并发、回顾算法,不含计划、报告、审计或内部页签,也不显示整页全局徽标或独立刷新行。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。
|
||||
当前应用页的顶层导航只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”(英文活动标签为 Activity records)。自动监督独立管理计划;设置只显示模型超时与并发、回顾算法,不含计划、报告、审计或内部页签,也不显示整页全局徽标或独立刷新行。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。故事线图谱面板内另有分类页签,见[故事线图谱](#故事线图谱)。
|
||||
|
||||
工作回顾只读取统一监督结果。顶部紧凑排列范围、时间、发起与刷新控件,不重复显示“新回顾”标题和来源说明,不新增 Modal。历史结果选择与图谱入口位于正文之前,即使只有一条结果也显示选择器。新回顾工具栏、状态、历史选择和结果面板铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设阅读宽度上限;结果内部按面板内边距排版,不再单独居中窄正文。正文使用“监督结果”标题,常显生成日期、真实范围和覆盖区间,历史结果不标为最新。工作总结按原有空行分段并保留换行;本次变化和未解决事项直接使用已有字段,不补写结论或截断正文。390px 下新请求字段与操作各占两列,结果元数据可换行。
|
||||
|
||||
新回顾运行中保留已显示结果及其日期、范围,禁用重复提交,并显示简短状态和活动记录入口;该入口清除计划筛选并聚焦活动页签。工作回顾不读取自动监督报告,也不显示自动报告空态、记忆与行动建议或第二行全局范围与刷新。无监督结果时,空态独占工具栏下方整行,标题与手动回顾入口水平居中;自动报告是否为空不影响此判断。运行中和暂停后使用独立状态条,提示当前状态及活动记录入口。
|
||||
新回顾运行中保留已显示结果及其日期、范围,范围、时间、发起和刷新控件保持可用;重复点击发起只重新显示当前运行提示,不排入新回顾。状态条提供活动记录入口,清除计划筛选并聚焦活动页签;右侧 X 只关闭提示,不暂停或取消执行,同一次运行的轮询不重新打开已关闭提示。范围、时间和活动计划筛选保留可访问名称,不再重复显示字段标题。工作回顾不读取自动监督报告,也不显示自动报告空态、记忆与行动建议或第二行全局范围与刷新。无监督结果时,空态独占工具栏下方整行,标题与手动回顾入口水平居中;自动报告是否为空不影响此判断。运行中和暂停后使用独立状态条,提示当前状态及活动记录入口。
|
||||
|
||||
## 活动
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
自动监督页依次展示范围与“刷新自动监督”、当前状态及计划列表、运行指标、报告趋势、待确认记忆与行动建议、“自动监督报告”历史、心跳报告阶段审计。刷新统一更新此页数据。报告与运行历史每次追加 20 条,报告可展开全部摘要和洞察,切换页签保留展开与分页位置;不再单独复制最近报告。建议保留确认、忽略、带入对话、标记完成及长内容展开。活动的计划筛选不改变自动监督的统计范围。当前状态下复用计划列表的唯一创建按钮,无计划时只显示列表的未配置说明,不叠加第二个计划空态或创建入口;点击直接打开现有 Modal。报告为空时明确写“暂无自动监督报告”,与工作回顾的结果状态区分。
|
||||
|
||||
加载、空记录和读取失败分开显示;读取失败保留已显示记录并提供就地重试。活动页面激活时,存在运行中记录则在上次读取结束 2 秒后刷新,否则 10 秒后刷新;每次最多一个请求。切换顶层页签、离开监督者或关闭应用后停止定时器,迟到响应不更新界面。错误后等待用户重试,不持续重发失败查询。
|
||||
加载、空记录和读取失败分开显示;读取失败保留已显示记录并提供就地重试。活动页面激活时,同轮读取活动和实时执行状态;有活动执行(包括停止清理)或运行中记录则在上次读取结束 2 秒后刷新,否则 10 秒后刷新,不重叠轮询。切换顶层页签、离开监督者或关闭应用后停止活动定时器,迟到响应不更新界面。错误后等待用户重试,不持续重发失败查询。
|
||||
|
||||
活动列表与工具栏铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设仪表盘宽度上限。范围、计划筛选、清除筛选和刷新紧邻排列,空间不足时换行。列表使用共享表面、边框、状态颜色和按钮。每条运行首先显示类型、状态、开始时间和范围;恢复与事实操作紧随其后,再展示“收集来源 / 整理批次 / 合并摘要 / 发布回顾”。阶段使用编号圆点和连接线,宽窗口四项等宽,窄于 720px 时两列;当前及已完成阶段突出显示,不为每个阶段铺设独立色块卡片。阶段读取已保存位置和真实运行状态,剩余来源为零不表示发布成功。旧运行没有阶段字段时显示“未记录”,不猜测失败位置;已提交来源清单可确认收集结束。无变化运行不显示新结果跳转,后续无需执行的阶段单独标注。
|
||||
|
||||
@@ -30,8 +30,12 @@
|
||||
|
||||
“已保留事实与来源”展开后才查询既有批次接口,每页 10 批,按本页持久记录组成项目、会话、批次层级。项目使用可用名称,会话优先使用保存来源的标题;只有已保存批次标记“已保存”,不创建未落库的待执行、在途或失败叶子,也不把本页数量当作项目总量。批次摘要收起时最多两行,展开后保留完整摘要、事件、实体、变化、关系及原文片段。来源显示消息 ID、码点范围及版本;长原文局部滚动。原生 details/summary 保留键盘展开和可见焦点,轮询不主动展开用户收起的会话。加载、空批次和读取失败分开显示,失败可重试;换页失败不显示上一页批次,切换 run 后旧响应失效。暂停状态为“未完成,可继续”,手动回顾主动暂停后在工作回顾页提示去活动继续,不显示完成通知。
|
||||
|
||||
活动页运行中同时提供“暂停回顾”和“取消回顾”,继续后的运行也可操作。停止期间禁用两项操作,并在运行标题和当前阶段显示“正在暂停”或“正在取消”;即使持久记录已经为已取消,只要执行尚未停止就继续显示“正在取消”。重新进入活动页通过实时执行状态恢复该提示。停止后显示“已取消”,保留事实详情,不显示继续按钮或失败恢复提示;任一回顾执行或停止清理期间,其他记录的继续按钮禁用。
|
||||
|
||||
## 自动监督设置
|
||||
|
||||
没有自动运行、报告或关联建议时,只展示计划配置和已有计划,不显示零值统计、空趋势、建议、报告或审计区。手动回顾不计入自动监督统计;产生自动运行或报告后展示对应结果区。
|
||||
|
||||
2026-09-23 当前已实现“模型超时与并发”表单:心跳报告与监督者整理分别设置,均为 30 至 600 的整数秒,默认 240;监督模型并发数为 1 至 4 的整数,默认 1。保存真实字段 `heartbeatReportTimeoutSeconds`、`supervisorOrganizeTimeoutSeconds`、`supervisorModelConcurrency`;无效输入不能提交,设置加载/保存中或锁定时禁用,失败保留草稿并提供重试。超时以阶段读取设置的时点为准,不改变已冻结的值;并发数影响后续派发,降低上限不打断在途请求。
|
||||
|
||||
并发说明明确报告和监督整理共用上限,排队不计入模型超时;普通聊天不受此设置限制,但仍与监督竞争上游额度。表单不宣称提供商全局并发、RPM、TPM 或 Retry-After 限流已实现。
|
||||
@@ -68,7 +72,7 @@ Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复
|
||||
|
||||
选择历史结果后,图谱读取该结果的成员和内容,标题显示其回顾区间和生成时间。切换时清空旧图谱和详情并显示加载状态,迟到请求不得替换新选择。确认、修订和移除反馈更新当前所选结果,后续自动回顾不改写旧结果。
|
||||
|
||||
手动“回顾当前进展”生成中禁用重复提交。该操作新建回顾,活动页“继续”恢复原 run;两者不得混淆。失败保留上次结果、本次范围和成功批次。手动操作不推进自动位置,删除报告也不重置自动位置。
|
||||
手动“回顾当前进展”新建回顾,活动页“继续”恢复原 run;运行中再次发起按上文显示提示,停止清理结束后才能开始新回顾。失败保留上次结果、本次范围和成功批次;已取消回顾不能继续。手动操作不推进自动位置,删除报告也不重置自动位置。
|
||||
|
||||
## 会话侧栏
|
||||
|
||||
@@ -85,10 +89,16 @@ Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复
|
||||
|
||||
有匹配结果时提供“在图谱中查看”,打开现有监督者页面的图谱页签并读取该结果;无匹配结果不显示入口。按钮保留原生键盘操作,进入后焦点落在图谱页签,继续使用共享 PageTabs 的方向键导航。固定目标不因跳转改变;再次点击相同或不同结果均重新定位,包括页面缓存后再次进入。结果不可用时显示错误并重试原 ID,不展示默认最新结果。
|
||||
|
||||
当前“继续讨论”预览实际发送会话名称、ID 和上下文,确认后发送到该会话;固定任务使用其关联会话,无法确定发送会话时禁用。切换目标使旧来源请求和预览失效。编辑预览中的引用仍属后续工作。
|
||||
“继续讨论”直接准备上下文,不要求先查看来源。预览顶部单独显示目标会话,下方“发送内容”文本域直接填入实际回顾与来源正文,用户可编辑或删除其中任意内容。发送仅提交当前文本域内容,不再暗中拼接上下文,也不提供额外的展开区。只有点击“发送”才提交,取消不发送,失败保留整份草稿。固定任务使用其关联会话,无法确定发送会话时禁用。上下文仅包含所选来源关联的事件、实体、关系及原文快照,不带入整份全局回顾摘要;没有关联事件时只使用来源。切换目标使旧来源请求和预览失效。查看来源不自动跳转,用户点击“打开会话”才导航。侧栏不再显示实体写入或知识库选择器,现有知识库数据不受影响。
|
||||
|
||||
侧栏与图谱来源详情共用编辑区样式:目标区底部使用弱分隔线,区块间距为 `--space-4`,标签与文本域间距为 `--space-2`。文本域默认十行,可纵向调整大小;长内容在文本域内滚动。底部复用 `custom-task-dialog__actions`,取消在前、发送在后,整体右对齐。
|
||||
|
||||
## 故事线图谱
|
||||
|
||||
关联来源详情属于会话时,显示“打开会话”按钮,使用来源保存的会话 ID 跳转并切换到所属项目;不发送消息或启动回顾。另提供“继续讨论”,使用上述可编辑正文预览,点击发送后提交到来源会话并打开它。详情继续展示整理时保存的原文快照。会话已删除或读取失败时,沿用应用会话导航的提示;其他来源类型不显示此按钮。
|
||||
|
||||
左侧使用共享 PageTabs 切换“事件 / 实体 / 关系”,显示各类数量,一次只展示一类列表,无记录时显示本次回顾的分类空态。页签固定在顶部,列表占满剩余高度并独立滚动。切换页签不清除当前图谱选择;从画布、事件浏览或详情选择对象时同步切到对应页签。支持方向键、Home/End 和面板键盘聚焦。
|
||||
|
||||
使用 `supervisor` 页面壳层,最大内容宽度为 `1440px`。当前生产图谱左侧为紧凑的事件、实体和关系列表,中间为 SVG 画布,右侧为详情与来源。桌面列宽为 `216px minmax(0, 1fr) 280px`,画布获得主要空间。外圈事件按发生时间排序后等距逆时针排列,不表示实际时间间隔;起止之间保留缺口和方向箭头。内部显示真实实体名称,实线仅连接实际事件与实体,虚线仅连接实际实体关系。
|
||||
|
||||
实体及其影响连线复用现有 `--graph-node-*-border` 令牌;颜色用于辅助辨认实体,不代表故事分类或确认状态。选中项加粗并突出连线,其余节点降低图形强调度但保留可读文字。详情分为名称与说明、图谱关联、确认操作和来源,来源正文按需读取。图例常显,读图说明可展开。
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
|
||||
### US-S24 手动回顾当前进展
|
||||
|
||||
**状态:待实现;当前智能心跳已有“立即运行一次心跳”。**
|
||||
**状态:已接入工作回顾;执行状态与取消规则见[活动记录](./logic-design.md#活动记录)。**
|
||||
|
||||
作为工作使用者,我希望主动检查当前范围,而不必等待下一次计划运行。
|
||||
|
||||
@@ -163,14 +163,15 @@
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 范围有效且有来源 | 点击“回顾当前进展” | 页面显示整理中状态、进度说明和取消操作 |
|
||||
| 范围有效且有来源 | 点击“回顾当前进展” | 页面显示整理中状态和活动入口,活动页提供暂停与取消 |
|
||||
| 整理完成并有新变化 | 返回结果 | 工作回顾、监督反馈和图谱引用同一次结果 |
|
||||
| 没有有效新变化 | 整理完成 | 显示检查时间和“暂无新进展”,不重复推送旧内容 |
|
||||
| 用户点击取消 | 整理仍在运行 | 显示已取消,保留上一次成功结果 |
|
||||
| 用户点击取消 | 整理仍在运行 | 清理期间显示正在取消,停止后显示已取消,保留上一次成功结果和成功批次 |
|
||||
| 回顾正在执行或停止清理 | 再次点击手动发起 | 显示当前运行提示,不新建或排队;关闭提示不取消执行 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 运行按钮在整理期间禁用,取消后可以重试。
|
||||
- 运行按钮在整理期间保持可用,重复点击按上表提示;取消停止后可以新建,已取消运行不能继续。
|
||||
- “无新进展”与“失败”使用不同状态和文案。
|
||||
- 结果必须显示本次冻结的范围,不能因用户后来切换项目而改变解释。
|
||||
|
||||
@@ -537,7 +538,7 @@
|
||||
| 已保存批次跨多个项目和会话 | 展开事实并翻页 | 按本页真实归属分层,标明本页数量;原文可读,失败叶子不伪造为已保存 |
|
||||
| 原始错误是多行校验 JSON | 展开错误详情 | 默认折叠,展开后有界滚动,完整文本可选择复制;恢复操作和阶段仍在其前面 |
|
||||
|
||||
取消和人工修改时间序列审计不属于当前已实现范围;实际状态及保留规则见[活动记录](./logic-design.md#活动记录)。
|
||||
继续后的运行仍可暂停或取消;任一运行执行或停止清理期间,其他运行不能继续。重新进入页面仍显示实际停止状态。人工修改时间序列审计不属于当前已实现范围;实际状态及保留规则见[活动记录](./logic-design.md#活动记录)。
|
||||
|
||||
### US-S27 自动增量与历史重看
|
||||
|
||||
@@ -552,6 +553,56 @@
|
||||
| 来源已经自动处理 | 用户手动回顾所选区间 | 重新整理有界历史,保留自动进度;删除报告不等同重看或重建 |
|
||||
| 只有已确认记忆或背景摘要变化 | 自动检查 | 背景不单独触发模型调用 |
|
||||
|
||||
## 时态图谱与 Agent 读取
|
||||
|
||||
### US-S28 依据决定演变继续工作
|
||||
|
||||
**状态:D1 工具读取已实现;真实模型建议质量尚未验收。** 对应 FR-S11,读取合同见[时态 Story Graph MCP 设计](./story-graph-mcp-design.md),验证范围见[实施进度](./progress.md#2026-09-28-story-graph-只读工具)。
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 已保存旧决定 A,后来有明确证据将其修订为 B | 用户询问“继续上次工作,现在采用什么” | Agent 按需读取图谱,说明 A 到 B 的变化、推荐 B 的依据,并引用旧决定与修订来源 |
|
||||
| 新记录只是增加选项 C | Agent 查询当前理解 | B 不因 C 更新而自动失效;替代或矛盾关系必须有明确证据 |
|
||||
| 记录只表明决定采用 B,没有完成证据 | 用户询问执行进度 | 回答已决定且完成状态未知,不推断已完成 |
|
||||
| 已确认旧认识与新证据冲突 | Agent 组织当前建议 | 分别展示确认状态、有效性与待核对差异,不静默覆盖 |
|
||||
|
||||
### US-S29 分页核对时间与来源
|
||||
|
||||
**状态:D1 已实现,缺失历史版本明确返回不可用。** 对应 FR-S11、US-S13。
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 旧事件在较晚回顾中才生成并记录 | 按发生时间查询并查看 timeline | 区分事件、回顾生成和记录时间;不把补录当成新发生,也不声称当时已知 |
|
||||
| 搜索、演变或原文超过一页 | Agent 继续读取 | 每页说明范围、修订、覆盖与剩余页,能读取全部匹配内容,无静默截断 |
|
||||
| 旧记录只有证据条数,没有完整覆盖信息 | Agent 读完最后一页 | 返回覆盖未知,不称已覆盖全部原始工作记录 |
|
||||
| 引用版本原文已不可用或当前内容已修改 | 读取历史来源 | 返回实际保存片段或明确版本不可用;读取当前版本须显式选择并标注差异 |
|
||||
| 分页期间对象版本变化 | 继续使用旧游标 | 明确提示游标失效并重新查询,不混合新旧版本 |
|
||||
| 用户要求截至过去时刻的完整认识 | 调用 `as_of` | 明确不支持,不用 current 或 timeline 冒充历史重建 |
|
||||
|
||||
### US-S30 按需读取并遵守应用开关
|
||||
|
||||
**状态:D1 已实现;跨平台安装包验收未执行。** 对应 FR-S11、US-S14、US-S20。
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 会话属于当前项目 | Agent 未显式指定 scope 搜索 | 只查该项目;无项目时要求明确范围,不自动扩大为 Global |
|
||||
| 用户进行普通聊天 | 没有继续工作或历史依据需求 | 不强制每轮查询或注入完整图谱;需要核对原话时才进一步读来源 |
|
||||
| 监督者开启且内置能力已分配,没有自动计划 | 查询已有结果 | 可以读取;查询不触发模型、回顾或业务写入 |
|
||||
| 监督者关闭,旧会话仍缓存工具 | 列出工具或直接调用 | 不暴露工具,调用被拒绝;已有数据保留 |
|
||||
| 相同范围与证据分别通过本地和远程 Runtime 查询 | 翻页、读取版本或关闭监督者 | 返回相同业务语义与引用,远程实际桥接须通过验收 |
|
||||
|
||||
### US-S31 保留偏好并复用跨范围事实
|
||||
|
||||
**状态:待实现;当前仍独立处理精确 scope,读取现有已确认 memory。** 对应 FR-S11、US-S16、US-S27。
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户已有已确认的 Global 或项目偏好 | 逐步切换统一工作记忆入口 | 原内容、确认状态、适用范围和引用保留,不删除旧数据 |
|
||||
| 旧偏好只有更新时间 | 查询演变 | 标记为当前背景和历史未知,不补造决定时间或替代关系 |
|
||||
| 项目 A 的来源版本已有成功叶子事实 | 目标复用能力交付后进行 Global 整理 | 复用该版本的事件、变化、关系和来源,不因 scope 改变重复提取相同正文,也不只复用摘要 |
|
||||
| 仅部分片段成功或原来源已改版 | 再次整理 | 识别未覆盖或变更部分,不把旧版本覆盖算作新版本完成 |
|
||||
| Global 结果含多个项目的归纳 | 从单项目查询 | 不把跨项目摘要伪装成项目结论,复用事实仍保留来源归属 |
|
||||
|
||||
## 验收优先级与依赖
|
||||
|
||||
首个可用版本应先打通以下路径:
|
||||
|
||||
@@ -16,3 +16,12 @@
|
||||
[Runtime 进程复用技术设计](../assistant-workbar/runtime-process-reuse-technical-design.md)
|
||||
统一维护;本 Runtime 的生命周期见[技术设计 §9](./technical-design.md#9-runtime-生命周期),
|
||||
未发布实现的验收状态见[工作栏进度](../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
|
||||
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.
|
||||
Current shortcut/coordinator behavior and configured-model evidence are tracked in
|
||||
[local native client progress](../assistant-workbar/progress.md#2026-09-27-local-native-client-validation),
|
||||
including the remaining managed Node delivery gap.
|
||||
|
||||
@@ -1,5 +1,91 @@
|
||||
# DeepSeek Harness 实现进度
|
||||
|
||||
## 2026-09-26 真实模型与官方 Web 验证
|
||||
|
||||
本轮验证 `0.1.7-rc.2` 后台对话/工具路径与同版本官方 Web 的本地接入可行性,关联
|
||||
[原生客户端方案](../assistant-workbar/runtime-native-client-proposal.md)的 FR-2、3、5,
|
||||
US-2 和 P1/P2。快捷按钮与完整客户端集成本轮未实现。
|
||||
|
||||
环境:Windows x64,标准 Node `24.19.0`,Edge `154.0.4258.37`,官方
|
||||
`@deepseek-ai/dsh@0.1.7-rc.2`,仓库 HEAD `51166136157a646530e1ad9f6a0242c1c6390e95`
|
||||
加当前未提交修改。源码哈希记录在本机临时报告 `dsh-017-verify/source.json`。
|
||||
供应商实际模型为当前 DS 配置中的 `deepseek-flash`,协议为 `openai-chat-completions`。
|
||||
|
||||
| 场景 | 实测结果 | HTTP dispatch |
|
||||
| --- | --- | ---: |
|
||||
| 后台 Ask | `read` 返回未出现在 prompt 中的随机校验码 | 2 |
|
||||
| 同会话切 Execute | `write` 创建文件,实际内容为 `DSH_EXECUTE_OK` | 2 |
|
||||
| Execute Shell | `pwsh` 执行 `Get-Content execute-marker.txt`,返回正确内容 | 2 |
|
||||
| 官方 Web 页面提交 | 当前模型读工作区文件并返回另一随机校验码;刷新仍保留回答 | 3 |
|
||||
| 本轮合计 | 9 次均请求 `deepseek-flash`,9 次均返回 HTTP 200 | 9 |
|
||||
|
||||
计数在 `AgentModelGateway` 向供应商执行 `fetch` 时记录,包含 Web 的 1 次无工具请求;
|
||||
4 次用户提交实际产生 9 次供应商请求。后台 3 轮均收到 `done`、工具 `completed`
|
||||
和每轮 2 个用量事件。调用经过现有 `createManagedModelBridge`、`AgentModelGateway`
|
||||
和 `ModelBridgeLoopbackProxy`,由隔离 Electron profile 中的 safeStorage 解密配置副本。
|
||||
供应商凭据未写入 Web 配置或报告;正式 `runtime-settings.json` 测前测后逐字节一致。
|
||||
环境继承 `NODE_TLS_REJECT_UNAUTHORIZED=0`,本轮不证明严格 TLS 证书校验可用。
|
||||
|
||||
官方 Web 以标准 Node、独立 `DSH_HOME` 和回环随机端口启动。通过官方 patch 配置 provider
|
||||
和默认模型,再调用 `workspace/create`;首次创建和已有目录复用均通过。根页面自动选中
|
||||
测试工作区和当前模型,无需手动填供应商密钥。保留并通过官方“内测声明”的“继续”按钮,
|
||||
真实页面输入、发送、回答、刷新及截图检查通过。配置/API 细节统一维护在方案第 6.3 节。
|
||||
|
||||
本轮驱动曾因缺少 `skillPackages` 默认值和临时目录动态模块解析失败退出,均为 0 次请求;
|
||||
修正驱动后后台成功。首次 Web `fill` 后发送按钮未启用,没有发出 Web 模型请求;改为键盘
|
||||
输入并仅重跑 Web 后成功,未重复后台调用。本轮 9 次 dispatch 逐次记录于同一 `calls.json`。
|
||||
|
||||
补查发现此前 `dsh-017-live/calls.json` 已有 7 次 HTTP 200 记录,其中后台 4 次、Web 3 次;
|
||||
对应历史报告记录模型 `deepseek-flash`、工作区与模型选中、文件结果及配置未改。本轮保留
|
||||
这些文件,未覆盖或重算为新增调用。该历史批次与本轮合计可核查 16 次,下面依赖升级阶段
|
||||
“0 次”仅指升级回归本身。
|
||||
|
||||
定向命令使用标准 Node 执行 `node_modules/vitest/vitest.mjs run`,目标为
|
||||
`src/main/agent/deepseek-harness-acp-e2e.test.ts`、
|
||||
`src/main/agent/goodbuddy-harness-control-plane.test.ts` 和
|
||||
`src/main/agent/deepseek-harness-runtime.test.ts`:3 个文件、52 项通过、3 项跳过,11.12 秒。
|
||||
3 个环境开关 live case 未在 Vitest 中启用;上表为独立真实模型驱动结果,不把跳过项算通过。
|
||||
确定性回归包含 Ask 写入拒绝和 Execute 工作区外写入,无需为验证重复调用真实模型。
|
||||
|
||||
本机证据位于临时 `dsh-017-verify/`:`calls.json`、`backend-result.json`、`result.json`、
|
||||
`main-result.json`、`source.json` 和 `web-conversation.png`。报告保留首次 Web 驱动失败,
|
||||
最终 Web 结果另见 `result.json`。桥接、worker、Web 和浏览器已关闭,进程检查未发现本轮
|
||||
目录所属的残留进程;临时 profile、测试工作区和 DS home 清理后仅保留脚本及脱敏证据。
|
||||
这些本机临时文件不作为仓库长期资源。
|
||||
|
||||
证据范围:后台运行真实 ACP/Host/控制面和供应商模型,Host 在测试进程内组合;Web 为
|
||||
官方 CLI 子进程与真实浏览器页面。未重新验收 GoodBuddy Composer/Utility Process,
|
||||
也未实现 Web 的 GoodBuddy Ask/Execute 适配。官方 Plan 只控制规划状态,默认
|
||||
“工作区内修改”也不等同于 Execute;后续复用现有工具命令名判断,不增加权限层级、
|
||||
目录限制或逐工具二次授权。Skills/MCP 承接、取消、产品生命周期及交付资源仍待验证。
|
||||
未改远程源码;DS 仍不支持 SSH execution space,本轮没有远程模型调用或 Linux Host 验证。
|
||||
|
||||
## 2026-09-26 依赖升级至 0.1.7-rc.2
|
||||
|
||||
23 个直接引用的 `@deepseek-ai/dsh-*` 包统一锁定为 `0.1.7-rc.2`,Cordis
|
||||
升级为上游要求的 `4.0.4`,同步更新依赖锁文件、Runtime 版本及打包测试夹具。
|
||||
版本取自 npm 发布列表与 `next` 标签;查询时 `latest` 仍指向 `0.1.0-rc.6`。
|
||||
|
||||
控制面改为监听 `agent/assistant-stream` 的 chunk 帧,以转发文本、推理和用量;
|
||||
工具结果从 message 层读取 `toolCallId` 和 `isError`。测试同步适配新版本的
|
||||
Agent 配置、Shell 执行和系统消息接口。
|
||||
|
||||
- DS 定向回归:152 项通过、5 项跳过。
|
||||
- 最终复跑 DS、Runtime discovery 和发布打包夹具:12 个文件通过、2 个跳过,
|
||||
170 项通过、5 项跳过,耗时 19 秒。
|
||||
- 扩大至打包夹具和设置面板:196 项通过、5 项跳过;设置面板套件因并行
|
||||
Obsidian 修改缺少 `obsidian-tools` 模块而未能加载。
|
||||
- `npm run smoke:deepseek-harness`:开发构建通过,真实 Electron Utility Host 返回 `ready`。
|
||||
- 升级后的首次类型检查与 lint 通过;后续类型检查受到并行 Obsidian 修改中的
|
||||
模块缺失、项目文件列表和类型错误阻塞,不能将当前整个工作区标为检查通过。
|
||||
- 全量测试两次运行分别在 200 秒、600 秒超时,期间出现 App、SupervisorActivity
|
||||
和 Electron UI 测试失败;未取得全量通过结果,未修改这些并行工作的文件。
|
||||
|
||||
本次依赖升级回归的真实供应商模型调用为 0 次;后续真实模型及 Web 实测见上节。上述升级验证不包含官方 Web 应用,
|
||||
“在浏览器中打开”入口仍按[原生客户端方案](../assistant-workbar/runtime-native-client-proposal.md)实施。
|
||||
远程启动契约和资源打包仅支持 Continue、OpenCode,`create-runtime.ts` 拒绝 SSH
|
||||
执行空间使用 DS,因此本次升级未改变远程 Runtime 路径,也未进行 Linux Host 验证。
|
||||
|
||||
## 2026-09-14 Windows 工作区路径修复
|
||||
|
||||
本机 Runtime 的 Host 启动与 `newSession.cwd` 已共用初始化时解析的 `realpath`,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
| 文档状态 | 实现与发布验收基线 |
|
||||
| 设计目标 | 将 DeepSeek Harness 作为 GoodBuddy 的第三个 Agent Runtime |
|
||||
| Runtime 标识 | `deepseek-harness` |
|
||||
| 当前依赖基线 | 实际使用的 `@deepseek-ai/dsh-*` 底层库,精确锁定 `0.1.2-rc.1` |
|
||||
| 当前依赖基线 | 实际使用的 `@deepseek-ai/dsh-*` 底层库,精确锁定 `0.1.7-rc.2`,Cordis 为 `4.0.4` |
|
||||
| 上游状态 | Developer Preview,允许出现破坏性变更 |
|
||||
| 上游许可证 | MIT |
|
||||
| GoodBuddy 目标平台 | Windows、macOS、Linux,x64 与 arm64 |
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
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 代理,Execute 放行 Host 中全部已注册工具。插件不能用同名工具冒充 Ask 允许项。插件安装脚本和初始化代码不属于模型工具调用,不能由 Ask 限制,因此界面在安装前必须明确确认这一边界。
|
||||
用户明确安装并启用的插件以当前用户权限运行。Ask/Execute 只控制模型经过 `tools/execute` 发起的工具调用:Ask 只允许 Host 中真实注册的 `read`、`skill` 和 Main 管理的 Web Search/Fetch、Story Graph 只读代理,Execute 放行 Host 中全部已注册工具。插件不能用同名工具冒充 Ask 允许项。插件安装脚本和初始化代码不属于模型工具调用,不能由 Ask 限制,因此界面在安装前必须明确确认这一边界。
|
||||
|
||||
整体分成两个互相约束的部分:
|
||||
|
||||
@@ -145,7 +145,7 @@ Harness 子进程内控制面不能取代 Main 控制面,Main 控制面也不
|
||||
|
||||
Harness Session 只在当前 Runtime 进程生命周期内存在。释放 GoodBuddy 会话时必须同步释放对应 Harness Agent。
|
||||
固定 Host 显式加载 `dsh-session-projection` 的内存 SessionProjectionRegistry,这是
|
||||
`0.1.2-rc.1` AgentLoop 的必需依赖;不加载 Session persistence,也不新增磁盘会话副本。
|
||||
`0.1.7-rc.2` AgentLoop 的必需依赖;不加载 Session persistence,也不新增磁盘会话副本。
|
||||
该依赖与其余 DSH 依赖一起由 Vite 打入 Host,不能从 `app.asar.unpacked` 外部导入
|
||||
仅存在于 `app.asar` 的 npm 包。最终打包检查及 Windows CI 的真实 UtilityProcess
|
||||
启动探针见 [Runtime 资源与打包](../../../BUILD.md#runtime-资源)。
|
||||
@@ -368,7 +368,7 @@ GoodBuddy conversationId -> Harness sessionId + workspace + process generation
|
||||
|
||||
### 9.5 跨项目复用
|
||||
|
||||
锁定 0.1.2-rc.1 的文件和 Bash/PowerShell 工具已按原生 Session header.cwd 路由,
|
||||
锁定 0.1.7-rc.2 的文件和 Bash/PowerShell 工具已按原生 Session header.cwd 路由,
|
||||
Provider 的根 cwd 只是默认值。不为每工作区复制本地 Provider;
|
||||
Main 的 Web/MCP ToolProvider 按工作区保存,最后一个对应 Session 释放时 dispose。
|
||||
|
||||
@@ -386,11 +386,17 @@ Main 的 Web/MCP ToolProvider 按工作区保存,最后一个对应 Session
|
||||
| Ask | 只允许 Host Registry 中真实注册的 `read` 与 `skill`;其他 Host/插件工具一律拒绝 | 能力启用时注册 Main 的精确代理对象,无逐次审批 | 不注册 | 模型工具调用保持只读 |
|
||||
| Execute | 放行 Host 中全部已注册的内置与插件工具 | 能力启用时注册 Main 代理 | 按分配注册并经过既有 RuntimeAuthorizer | 不增加插件权限层,以当前用户权限运行 |
|
||||
|
||||
2026-09-28:内置 Story Graph 的三个读取工具在 Ask/Execute 均通过 Main 代理提供,
|
||||
Host 只接受 Main 注册的具体工具对象。Main 逐次检查监督者启用、Harness 分配及请求项目范围,
|
||||
不逐次审批。其他普通内置 MCP 仍不支持 Harness 分配;合同与证据见
|
||||
[Story Graph 设计](../conversation-supervision/story-graph-mcp-design.md)和
|
||||
[验证进度](../conversation-supervision/progress.md#2026-09-28-story-graph-只读工具)。
|
||||
|
||||
### 10.2 Ask 模式
|
||||
|
||||
- Harness Control Plane 在 `tools/execute` 分发边界识别当前 Session 和在途请求。
|
||||
- 采用所有权感知的只读允许列表,只接受 Registry 中真实的 `read`、`skill` 和 Main 注册的 Web 代理对象;`write`、`edit`、Shell、MCP 及任意新插件工具默认拒绝。只比较工具名不足以授权,插件注册同名工具仍会被拒绝。
|
||||
- Ask 不注册 Main 代理的 MCP 工具。
|
||||
- 采用所有权感知的只读允许列表,只接受 Registry 中真实的 `read`、`skill` 和 Main 注册的 Web/Story Graph 代理对象;`write`、`edit`、Shell、自定义 MCP 及任意新插件工具默认拒绝。只比较工具名不足以授权,插件注册同名工具仍会被拒绝。
|
||||
- Ask 不注册 Main 代理的自定义 MCP;Story Graph 使用共享只读合同,scope 的互斥分支转换为 Harness 支持的 `oneOf`,完整参数仍由 Main 的 Zod schema 校验。
|
||||
- Web Search/Fetch 的凭据、传输与结果限制保留在 Main;Utility 只看到有界 schema 和结果。
|
||||
- 只读不等于无限输出,读取仍受字节和工具结果上限控制。
|
||||
- 插件安装脚本和 Cordis 初始化生命周期不经过 `tools/execute`。Ask 不能把已启用第三方代码变成沙箱,也不能保证第三方代码没有启动副作用。
|
||||
@@ -405,7 +411,7 @@ Main 的 Web/MCP ToolProvider 按工作区保存,最后一个对应 Session
|
||||
`goodbuddy/tools/list` 按 Session 保存本次发现的工具定义;`goodbuddy/tools/call` 复用该定义
|
||||
校验参数与授权,不因调用健康工具而重新等待故障 MCP 的发现。发现重试留在清单刷新,
|
||||
Session 释放及 Runtime dispose 清除内存定义,不持久化第二份能力状态。
|
||||
- 所有工具调用仍作为活动事件记录;Ask 和 delegation 路径继续固定拒绝。
|
||||
- 所有工具调用仍作为活动事件记录;自定义 MCP 的 Ask 和 delegation 路径继续固定拒绝。
|
||||
|
||||
### 10.4 Runtime OS 沙箱
|
||||
|
||||
@@ -418,7 +424,7 @@ Main 的 Web/MCP ToolProvider 按工作区保存,最后一个对应 Session
|
||||
|
||||
- 使用环境变量白名单构造 utilityProcess 环境。
|
||||
- 不继承 `NODE_OPTIONS`、调试端口、任意 npm 配置、用户 `DSH_*` 覆盖或白名单之外的凭据。
|
||||
- `DSH_TELEMETRY_DISABLED=1` 必须固定设置。
|
||||
- `DSH_TELEMETRY_DISABLED=1` 必须固定设置。后台 Host 与独立 Web 启动器均叠加 `src/shared/runtime-privacy-environment.ts` 的通用遥测关闭配置,设置 `DO_NOT_TRACK=1`、`OTEL_SDK_DISABLED=true`,并关闭日志、指标和追踪 exporter。模型、MCP 和用户主动联网请求不受此配置限制。
|
||||
- Harness Home 指向 GoodBuddy 管理的隔离目录。
|
||||
- 不调用官方 `loadEnv` 或 `loadLayeredEnv`。
|
||||
- API Key 由 Main 从加密设置中解析。
|
||||
@@ -546,7 +552,7 @@ Runtime: GoodBuddy 内置 DeepSeek Harness
|
||||
模型配置: 跟随 GoodBuddy · 企业网关(qwen-plus)
|
||||
状态: 已就绪
|
||||
路径: <受控 Host 路径>
|
||||
版本: 0.1.2-rc.1
|
||||
版本: 0.1.7-rc.2
|
||||
执行权限: 当前用户权限
|
||||
|
||||
Host 始终由当前 GoodBuddy 版本提供,不存在自定义 Host 入口。
|
||||
@@ -689,11 +695,11 @@ Renderer 只接收公开 npm 元数据和受管插件状态。任何凭据、完
|
||||
- 二进制检测、版本解析和路径规范化。
|
||||
- ACP 握手、事件转换和请求关联。
|
||||
- 每个会话单请求、跨会话并行。
|
||||
- Ask 在工具分发边界只允许真实注册的 `read`、`skill` 与 Main Web 代理,并拒绝同名冒充和任意新插件工具;Execute 放行插件工具。
|
||||
- Ask 在工具分发边界只允许真实注册的 `read`、`skill` 与 Main Web/Story Graph 代理,并拒绝同名冒充和任意新插件工具;Execute 放行插件工具。
|
||||
- 握手只接受明确的 `execution.mode = 'host'`。
|
||||
- 未分配 Skill/MCP 不可见;分配后的 Skill catalog 可调用 `skill` 加载。
|
||||
- 原生能力快照只包含 Host/插件原生 Skills,不包含 GoodBuddy 分配的 Skills 或 MCP。
|
||||
- Ask 不注册 MCP 工具;Web 代理可用于 Ask 与 Execute;Execute 每轮刷新有界 MCP schema,并在调用前再次校验活动请求、模式、参数和 RuntimeAuthorizer 结果。
|
||||
- Ask 不注册自定义 MCP 工具;Web 和 Story Graph 代理可用于 Ask 与 Execute;Execute 每轮刷新有界自定义 MCP schema,并在调用前再次校验活动请求、模式、参数和 RuntimeAuthorizer 结果。
|
||||
- MCP URL、启动命令和凭据不进入 Utility 启动配置或协议结果。
|
||||
- 未知授权结果失败关闭。
|
||||
- 超时、取消、迟到帧和进程意外退出。
|
||||
@@ -760,7 +766,7 @@ npm run build
|
||||
|
||||
- `deepseek-harness` 可被保存、选择、检测和显示。
|
||||
- Runtime 详情卡内显示状态、路径、版本和当前用户执行权限。
|
||||
- Skills 与自定义 MCP 设置可把能力分配给 DeepSeek Harness;请求级 GoodBuddy 内置 MCP 当前不支持分配,并在内置 MCP 卡片中以置灰、未选择状态明确显示。布局、键盘语义、文案和保存回显通过真机检查。
|
||||
- 该阶段的 Skills 与自定义 MCP 设置可把能力分配给 DeepSeek Harness;当时请求级 GoodBuddy 内置 MCP 不支持分配,并在卡片中置灰。布局、键盘语义、文案和保存回显通过真机检查。2026-09-28 的 Story Graph 源码另接入 Harness 分配与 Main 只读代理,验证证据见上文专项记录。
|
||||
- DSH 市场初始关闭且不加载 npm 目录;显式开启后可以搜索、安装、启停、配置和移除插件,安装前只出现一次准确的当前用户权限确认。关闭市场后已有启用插件继续运行,重新开启后管理状态不变。
|
||||
- Ask 写入测试在 Runtime 边界失败。
|
||||
- Ask 可使用已启用的 Main Web Search/Fetch,且插件无法通过同名工具绕过所有权校验。
|
||||
|
||||
@@ -50,7 +50,7 @@ reading 与知识库 master-detail 壳层,KnowledgeWorkspace 不再嵌套壳
|
||||
| 应用中心 → 本机推理监控 | [应用定义](../../../src/renderer/src/ApplicationCenter.tsx)仍为四项;[监控](../../../src/renderer/src/LocalInferencePage.tsx)显示本地服务并加载/释放 OCR | 不承担远程 OCR 配置或管理;关闭监控入口不等于关闭解析能力 |
|
||||
| 监控 → 模型与连接设置 | [IPC](../../../src/main/ipc.ts)的 `localInferenceOpenSettings` 只发出无分类的 `settingsOpen`;[App](../../../src/renderer/src/App.tsx)仅打开设置,[SettingsPanel](../../../src/renderer/src/SettingsPanel.tsx)默认分类为 `runtime` | 文案与默认落点不一致,未直达模型或文档解析;本轮仅记录,产品跳转未修复 |
|
||||
| 聊天附件 | App 的附件按钮直接选文件;当前卡片显示名称、容量、缩略图或图标、文本 tooltip 和移除 | 尚无逐附件更多菜单、显式 OCR、解析结果预览、重解析或文档选图 |
|
||||
| 知识库文档 | [DocumentsView](../../../src/renderer/src/KnowledgeWorkspace.tsx)提供任务、打开来源、失败文档重试和分块管理;来源行另有同步,库页头另有“用于当前对话” | 解析文档详情、成功文档重解析及选图须新增,不能把分块管理 Modal 当作已有完整解析预览 |
|
||||
| 知识库文档 | 当时位于 `KnowledgeWorkspace.tsx` 的 DocumentsView 提供任务、打开来源、失败文档重试和分块管理;来源行另有同步,库页头另有“用于当前对话” | 解析文档详情、成功文档重解析及选图须新增,不能把分块管理 Modal 当作已有完整解析预览 |
|
||||
| 新建选图目标会话 | App 的 `startNewConversation` 返回布尔值,拒绝消息通道项目,并在复用空会话或新建后切到聊天 | 预览保留、目标 ID 取得及返回确认仍需接线;直接调用不满足 US-I9 |
|
||||
|
||||
本轮更正文档中“既有文档详情”的描述,补齐菜单矩阵及 US-H16 / A-15;队列修改统一先
|
||||
@@ -58,6 +58,12 @@ reading 与知识库 master-detail 壳层,KnowledgeWorkspace 不再嵌套壳
|
||||
同步标明本轮范围;本地 OCR 仍为现有能力,HTTP 和新附件流程仍为规划。
|
||||
未修改产品代码、探索脚本或既有应用排序,OCR、文本及视觉模型调用均为 0。
|
||||
|
||||
2026-09-27 模块归属复核:上述表格保留实施前状态。当前
|
||||
[DocumentsView](../../../src/renderer/src/knowledge-workspace/DocumentsView.tsx)
|
||||
已从工作区入口拆出,提供解析预览、返回列表、重新解析及预览内选图;工作区入口保留库选择、
|
||||
检索与分块管理。文档与来源统一列表的行为及验证见
|
||||
[知识库进度](../knowledge-base/progress.md#2026-09-27-文档与来源统一列表)。
|
||||
|
||||
## 2026-09-19 实现与验证
|
||||
|
||||
HTTP PaddleOCR-VL 已接入 `DocumentParsingService` 的生产 PDF/PPTX 路径,设置支持来源切换、
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
|
||||
## 1. 容器与共享组件
|
||||
|
||||
遵循根目录 [UI 设计系统](../../../UI-DESIGN.md),保留当前应用容器结构。下表依据
|
||||
2026-09-18 源码核对,标为新增的内容需要实现,不能当作现有 API。
|
||||
遵循根目录 [UI 设计系统](../../../UI-DESIGN.md),保留当前应用容器结构。下表的知识库
|
||||
模块归属已于 2026-09-27 复核;各入口的实现与验证状态见[进度](./progress.md)。
|
||||
|
||||
| 位置 | 现有结构及设计用法 |
|
||||
| --- | --- |
|
||||
| 设置中心 | `SettingsPanel.tsx` 整窗 Modal 内的 `DocumentParsingSettingsSection.tsx`;继续使用 `SettingsCategoryHeader`、`settings-section`、`field`,不套第二个 PageShell 或一级标题 |
|
||||
| 聊天 | `App.tsx` 的 `PageShell variant="reading"`;附件卡位于原 Composer 内,待发送队列仍在 Composer 外,不在助手工作栏增加附件栏目 |
|
||||
| 知识库 | `App.tsx` 已提供 `PageShell variant="master-detail"`,`KnowledgeWorkspace.tsx` 内含库导航与 DocumentsView;需在文档区域新增解析详情及返回列表,不再嵌套页面壳层或新建知识库级页签 |
|
||||
| 解析预览 | 新增共享内容视图,分别嵌入设置诊断 Modal、聊天附件 Modal 和知识库文档详情;共享数据和交互,外层容器负责标题、范围、关闭与滚动 |
|
||||
| 知识库 | `App.tsx` 提供 `PageShell variant="master-detail"`,`KnowledgeWorkspace.tsx` 保留库导航并挂载 `knowledge-workspace/DocumentsView.tsx`;文档区域内切换解析详情与列表,不嵌套页面壳层或新建知识库级页签 |
|
||||
| 解析预览 | `DocumentResultPreview.tsx` 分别嵌入设置诊断 Modal、聊天附件 Modal 和知识库文档详情;共享数据和交互,外层容器负责标题、范围、关闭与滚动 |
|
||||
| 页签与选择 | `WorkspacePrimitives.tsx` 的 `PageTabs variant="segmented"` 承载正文、图片、详情;来源及过滤模式使用 `SegmentedControl`,不能用它替换页签语义 |
|
||||
| 浮层 | Modal 沿用 body Portal、`activateModalFocus` 与 `trapTabFocus`;测试及附件菜单复用 `AnchoredMenu`,由共享 `FloatingPortal` 挂入所属 Modal 并在 top layer 绘制。通知遵循根 UI 规范 |
|
||||
| 图文内容 | 复用 `MarkdownRenderer.tsx` 的排版、表格滚动和静态 HTML 处理边界;补齐受管图片与 OCR HTML 表格适配,现有 Markdown 渲染器并未直接支持这些资源 |
|
||||
@@ -36,8 +36,8 @@
|
||||
| 管理本地 OCR 文件 | 同一“文档解析”分类的本地模型区 | 下载、校验、ZIP、删除和目录操作;全局模型下载源仍在“平台功能 / 通用设置”,与 OCR 来源选择分开 |
|
||||
| 查看或释放本地 OCR 内存 | “应用中心” → “本机推理监控”(`local-inference` Modal),或其常驻入口 | 只管理本机执行服务;应用启用及常驻控制入口,不控制解析功能;HTTP 服务不出现在本机服务列表,也不提供远程启停 |
|
||||
| 添加文件或图片 | “当前对话” → “添加附件”,或输入框粘贴 | 先加入当前草稿,再按输入类型处理;上传按钮继续直接选文件,不改成先选 OCR 的菜单 |
|
||||
| 查看聊天解析结果 | 草稿或历史消息的附件卡 | 规划中的共享预览;来源消息与目标草稿分别显示,历史输入只读 |
|
||||
| 查看知识文档结果 | “知识库” → 选库 → “文档与来源”中的文档操作 | 规划新增“查看解析结果”和文档详情;返回恢复库、列表筛选和位置;不改变活动会话 |
|
||||
| 查看聊天解析结果 | 草稿或历史消息的附件卡 | 打开共享预览;来源消息与目标草稿分别显示,历史输入只读 |
|
||||
| 查看知识文档结果 | “知识库” → 选库 → “文档与来源”中的文档操作 | “查看解析结果”进入文档详情;返回恢复库、列表筛选和位置;不改变活动会话 |
|
||||
| 把文档插图用于聊天 | 聊天或知识文档预览 → “图片” → “添加所选图片” | 明确目标会话草稿后复制;设置测试预览无此操作;已有“用于当前对话”仍负责知识库检索范围,不代替选图 |
|
||||
|
||||
从解析失败或未配置提示提供“前往文档解析设置”时,须直达 `document-parsing` 分类并保留
|
||||
@@ -65,7 +65,7 @@
|
||||
| 草稿图片,发送提取文字 | 查看解析结果、打开原图 | 改为发送原图、使用当前设置重新提取文字、移除 |
|
||||
| 历史消息附件 | 查看已保存结果、打开原件 | 需要重解析或改变发送方式时先添加到当前可编辑草稿;不修改原消息 |
|
||||
| 已入队附件 | 仅有附件时显示“查看附件”,查看入队时冻结的结果与发送方式;无附件时隐藏入口 | 修改整条输入使用“恢复到草稿”,按 L-H7 合并;不直接修改队列附件 |
|
||||
| 知识文档 | 查看解析结果、打开来源、管理分块 | 使用当前设置重新解析;失败时使用重试,不再并列一个同义重解析按钮;来源同步仍属于来源级操作 |
|
||||
| 知识文档 | 查看解析结果、打开来源、管理分块 | 重新解析;失败时使用重试,不再并列一个同义重解析按钮;来源同步仍属于来源级操作 |
|
||||
| 设置测试结果 | 查看正文、图片、详情及原件 | 重新测试、关闭;没有加入草稿、知识库或发送操作 |
|
||||
|
||||
操作依据对象、资源和任务状态显示。解析中仅保留可用旧结果的查看、进度和取消,不允许重复
|
||||
@@ -138,6 +138,10 @@
|
||||
聊天标明会话,知识库显示库名与 `ScopeBadge`。下一行固定正文、图片、详情三个 PageTabs,
|
||||
页签及直接包装层 `flex: 0 0 auto`,仅当前面板滚动。查看已存结果不发 OCR 请求。
|
||||
|
||||
知识库将原件与维护操作收进详情头部的共享菜单,并将添加图片工具限制在图片页;聊天与
|
||||
设置预览保持原有入口。具体顺序、来源与解析时原件的区别及返回规则见
|
||||
[知识库来源操作](../knowledge-base/ui-design.md#6-来源操作)。
|
||||
|
||||
正文面板按来源页或章节呈现 Markdown、HTML 表格及受管图片。页面导航显示真实页号;
|
||||
PPTX 显示“幻灯片 N”,没有分页的原生文档显示工作表或标题定位,不生成假页码。点击图片
|
||||
旁的“第 N 页”或“查看正文位置”切到正文并聚焦对应标题;正文图片的“在图片中查看”反向
|
||||
@@ -172,7 +176,7 @@ reference 使用原路径。reference 原件失效时显示“原文件不可用
|
||||
和“重试”;有部分产物时可以查看,但不能标为成功。关闭测试预览后释放无引用测试资源。
|
||||
测试预览不提供“添加到会话”,防止诊断文件进入用户草稿或知识库。
|
||||
|
||||
“使用当前设置重新解析”是聊天草稿和知识库文档的次操作,点击前显示将使用的已保存来源。
|
||||
“使用当前设置重新解析”是聊天草稿的次操作;知识库列表和预览中的同一操作简写为“重新解析”,仍使用当前已保存的设置。
|
||||
历史消息和队列冻结已接受的输入;其预览只读。历史消息使用“添加到当前草稿重新解析”,
|
||||
产生草稿附件而不改写已发送正文;队列修改使用“恢复到草稿”,避免原队列和修改稿重复发送。
|
||||
重解析期间旧结果仍可查看,顶部显示“正在重新解析,
|
||||
|
||||
@@ -33,10 +33,28 @@
|
||||
| `src/shared/external-knowledge-contracts.ts` | Provider 配置、实例与绑定输入、目录、测试结果和外部引用定位契约 |
|
||||
| `src/shared/knowledge-contracts.ts`、`contracts.ts`、`assistant-contracts.ts` | 现有知识结果、列表和会话引用增加外部字段 |
|
||||
| `src/shared/knowledge-reference.ts` | 预检索与 MCP 共用的引用转换和去重键 |
|
||||
| `src/main/ipc.ts`、`src/preload/index.ts`、`src/shared/ipc-channels.ts` | 可信发送方检查、Zod 输入解析、类型化跨进程调用 |
|
||||
| `src/main/ipc.ts`、`src/main/knowledge/knowledge-ipc.ts`、`src/preload/index.ts`、`src/shared/ipc-channels.ts` | 可信发送方检查、Zod 输入解析、类型化跨进程调用 |
|
||||
| `src/renderer/src/ExternalKnowledge.tsx` | 实例 Modal、绑定表单、Provider 配置、外部详情与检索结果 |
|
||||
| `KnowledgeWorkspace.tsx`、`App.tsx`、`KnowledgeCitationDialog.tsx` | 统一列表、聊天选择、刷新及历史引用显示 |
|
||||
|
||||
Renderer 的 `KnowledgeWorkspace.tsx` 保留库选择、页签路由、检索与分块管理状态及回调。
|
||||
原有子组件位于 `src/renderer/src/knowledge-workspace/`:
|
||||
|
||||
| 文件 | 职责 |
|
||||
| --- | --- |
|
||||
| `DocumentsView.tsx` | 来源导入、文档列表与解析结果预览 |
|
||||
| `KnowledgeSettingsView.tsx` | 索引、分块及图谱配置,向量索引状态轮询 |
|
||||
| `KnowledgeTasksView.tsx` | 任务筛选、层级展示、取消和重试 |
|
||||
| `GraphView.tsx`、`GraphEditors.tsx` | 图谱浏览、实体与关系编辑 |
|
||||
| `KnowledgeGraphChartLoader.tsx` | 图谱画布延迟加载及失败重试 |
|
||||
| `LibraryDialogs.tsx`、`RemoveSourceDialog.tsx`、`GraphDestructiveDialog.tsx` | 建库表单、库编辑与删除、来源移除及图谱破坏性操作确认 |
|
||||
| `types.ts` | 工作区 Props 与组件间使用的类型 |
|
||||
| `formatting.ts`、`helpers.ts` | 多个组件共用的本地化格式化、标签、别名解析、进度和错误处理 |
|
||||
|
||||
子组件直接导入所需模块,不反向依赖工作区入口。单个视图专用的格式化与标签留在该视图,
|
||||
检索结果转换留在入口。模块拆分保留组件 Props、挂载位置、key 与副作用顺序;文档与来源
|
||||
合并后的列表结构及操作归属见 [UI 设计](./ui-design.md#6-来源操作)。
|
||||
|
||||
`ExternalKnowledge.tsx` 的字段改用全局 `.field`,修正 body Portal 脱离知识库祖先后
|
||||
丢失 scoped 控件样式的问题。CSS 提供 560px 紧凑弹窗和粘附底部操作区;实例选择复用
|
||||
既有 picker 样式并实现菜单键盘行为。没有新增 Main 模块,具体控件规则由
|
||||
|
||||
@@ -16,6 +16,69 @@
|
||||
|
||||
## 验证证据
|
||||
|
||||
### 2026-09-27 独立评审 P2 修复
|
||||
|
||||
- `DocumentsView` 将任务来源 ID 与维护用来源分开传递。目录和一对多 URL 的文档预览
|
||||
只筛选当前文档任务,父批次作为上下文保留,同组 B 的任务不再因来源 ID 被纳入。
|
||||
真正单文档合并行在列表和预览中均保留来源、文档双 ID;判定使用完整文档集合。
|
||||
- 在飞状态由全局单 ID 改为按操作 ID 计数,完成或失败时只清理本次操作。来源暂停、
|
||||
同步及文档操作使用各自 ID 判断禁用,A 同步或重解析期间仍可暂停 B。
|
||||
- 新增 9 项行为回归:目录及一对多 URL 各覆盖列表、预览入口,并在搜索只剩 A 时确认
|
||||
不纳入 B 或其他来源级任务;单文档合并预览覆盖双 ID。四组 deferred 用例验证
|
||||
A 同步/重解析与 B 暂停的调用 ID、重复提交禁用、A 先完成及 B 先失败后的独立状态。
|
||||
预览测试同时确认所属来源的同步和移除入口仍可用。
|
||||
- `npx vitest run src/renderer/src/KnowledgeWorkspace.test.tsx src/renderer/src/DocumentResultPreview.test.tsx`:
|
||||
2 个文件、66 项通过;`npm run typecheck`、`npm run lint`、`git diff --check` 通过。
|
||||
- 本轮仅修改上述组件、工作区测试及本地知识库 UI/进度文档。未重跑全量或 Electron
|
||||
渲染验收,未提交;此前全量结果及渲染验证边界见下方记录。
|
||||
|
||||
### 2026-09-27 文档入口与任务历史整理
|
||||
|
||||
对应 US-D1、US-D2、US-D4,完成文档行、解析预览与知识任务中心的界面修改。当前交互
|
||||
以 [来源操作](./ui-design.md#6-来源操作) 和 [任务历史](./ui-design.md#8-任务批次与历史)
|
||||
为准,替代下方同日早期记录中的原生折叠操作区。
|
||||
|
||||
- 合并 URL 行恢复地址,保留查询参数与片段;同标题、不同查询参数的两条 URL 已验证
|
||||
可以区分、分别搜索并打开正确来源。
|
||||
- 文档与来源行使用一个主要按钮和共享菜单,分块、任务、重解析、同步、暂停、重试及
|
||||
移除继续可达。预览按返回、标题、维护操作分层,保留搜索、焦点与滚动恢复;解析时
|
||||
原件与当前来源使用各自原有 API。共享菜单补充向下翻转及缩窗后的视口高度限制。
|
||||
- 任务中心按真实父子记录计批次,对象筛选同步影响计数。历史包含已结束失败批次,
|
||||
运行批次中的成功子步骤不会单独混入历史。取消、重试继续服从服务契约。
|
||||
- 最终定向命令 `npx vitest run src/renderer/src/ApplicationMenu.test.tsx src/renderer/src/KnowledgeWorkspace.test.tsx src/renderer/src/DocumentResultPreview.test.tsx src/renderer/src/KnowledgeChunkManager.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx tests/knowledge-layout.electron.test.ts tests/attachment-layout.electron.test.ts tests/overlay-layering.electron.test.ts`:8 个文件、106 项通过。
|
||||
- `tests/knowledge-layout.electron.test.ts` 使用真实 DocumentsView、KnowledgeTasksView、
|
||||
DocumentResultPreview、菜单与 CSS,覆盖浅深主题及 1100×720、720×720、420×480。
|
||||
验证页面无横向溢出、表格内部滚动、菜单展开不改变行高、顶部菜单翻转、原生 End/Escape、
|
||||
预览焦点恢复及历史默认折叠。截图已检查宽屏列表、窄屏预览与菜单、任务历史。
|
||||
截图复核发现文档 Grid 原先会被表格最小宽度撑开;列宽改用 `minmax(0, 1fr)`,
|
||||
并增加内部页面容器无横向溢出的断言,确保导入工具栏不随表格横向滚动。
|
||||
隐藏窗口无截图时曾停在帧等待;改用可见窗口并确认真实焦点后,无截图运行通过。
|
||||
- 附件布局和 Modal 浮层的真实 Electron 回归通过,验证共享菜单修改没有破坏这两个入口。
|
||||
- 完整 `npm test` 最后一次完成:448 个文件通过、1 个失败、11 个跳过;5291 项通过、
|
||||
2 项失败、83 项跳过。失败仅为下方已记录的 HeartbeatCenter 中英文 tablist 数量断言。
|
||||
此后补回解析时原件菜单入口、补测锚点滚出视口,并修正窄屏 Grid,均通过上述最终定向回归。
|
||||
- `npm run typecheck`、`npm run lint`、`git diff --check` 通过。布局 fixture 使用合成数据与桥接替身,
|
||||
本轮未从完整 App 重做 UI → Main 的文件导入及真实解析服务验收;没有新增外部模型调用。
|
||||
|
||||
### 2026-09-27 文档与来源统一列表
|
||||
|
||||
在现有 `knowledge-workspace` 拆分基础上修改 `DocumentsView.tsx`,单文件、单 URL 与
|
||||
唯一关联文档合成一行;目录及一对多来源保留组标题,零文档来源和孤立文档继续可见。
|
||||
统一搜索、完整移除数量、次要操作折叠及预览返回规则见 [UI 设计](./ui-design.md#6-来源操作)。
|
||||
`KnowledgeTasksView.tsx` 支持合并行同时传入来源和文档上下文,展示两者相关任务。
|
||||
|
||||
- `npx vitest run src/renderer/src/KnowledgeWorkspace.test.tsx src/renderer/src/DocumentResultPreview.test.tsx src/renderer/src/KnowledgeChunkManager.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx`:4 个文件、94 项通过。
|
||||
- 新增用例覆盖单文件与 URL 去重、来源和文档搜索、一对多过滤、零文档来源重试与暂停、
|
||||
孤立文档、完整移除数量、合并任务入口、拖放,以及预览返回后的搜索、焦点和滚动恢复。
|
||||
工作区测试使用解析预览替身;真实预览组件由同次运行的 `DocumentResultPreview.test.tsx` 验证。
|
||||
- `npm run typecheck`、`npm run lint` 和 `git diff --check` 通过。
|
||||
- 完整 `npm test` 运行 200 秒后超时,超时前输出 `HeartbeatCenter.test.tsx` 两项失败,
|
||||
全量测试未完成。独立运行 `npm test -- src/renderer/src/HeartbeatCenter.test.tsx`
|
||||
复现 40 项通过、2 项失败:zh-CN/en-US 的 `keeps tab titles unique...` 用例在第 213 行
|
||||
期望 1 个 `tablist`,实际为 2 个。该失败与本次知识库修改无关,本轮未修改心跳中心。
|
||||
- 未做真实 Electron 浅深主题、窄窗口、原生键盘及 UI → Main 导入验收。
|
||||
未调用外部模型或知识服务,未提交改动。
|
||||
|
||||
### 2026-09-20 清理孤立的内存图谱搜索
|
||||
|
||||
- 删除仅剩测试调用的 `searchGraph`、专属评分/遍历辅助函数、搜索上限及测试 fixture。
|
||||
|
||||
@@ -51,8 +51,8 @@ Conversation 的检索范围,返回对话并聚焦输入框。
|
||||
- 向量状态。
|
||||
- 启用图谱时的图谱状态。
|
||||
|
||||
失败文档保留错误信息和“重新处理”。只要仍有可用分块,就允许打开分块管理,避免隐藏
|
||||
上一版可用索引。
|
||||
失败文档保留错误信息和“重新处理”,入口位置见第 6 节。只要仍有可用分块,就允许
|
||||
打开分块管理,避免隐藏上一版可用索引。
|
||||
|
||||
## 5. 问答效果与高级参数
|
||||
|
||||
@@ -64,6 +64,42 @@ Conversation 的检索范围,返回对话并聚焦输入框。
|
||||
|
||||
## 6. 来源操作
|
||||
|
||||
“文档与来源”使用一张按来源组织的表格。单文件、单 URL 来源只有一篇关联文档时,
|
||||
来源与文档合成一行,以文档名为主标题,同步状态、同步时间和索引状态就近显示。
|
||||
不再单独展示同名来源行,也不重复任务入口。目录及其他一对多来源保留来源组标题,
|
||||
文档紧随其后;零文档和失败来源仍保留同步、暂停、重试及移除入口。
|
||||
没有 `sourceId` 或找不到对应来源的文档独立显示。
|
||||
|
||||
搜索同时匹配来源名称、来源位置、文档名称和路径。来源命中时显示该组全部文档;
|
||||
仅文档命中时保留组标题和命中文档。一对多关系按完整数据判断,搜索不能将其临时合成
|
||||
单文档行。移除确认使用来源的完整文档数量,不使用搜索结果数。
|
||||
|
||||
导入文件、目录、URL 及拖放入口保持可用,拖放区使用紧凑的图标与文字提示。
|
||||
合并行继续显示 URL,包含路径、查询参数和片段,使同标题的不同页面可以区分;
|
||||
长地址允许换行。本地文件沿用文件名摘要。来源组中的 URL 同样可见。
|
||||
|
||||
每行只直接显示一个主要动作及一个“更多操作”按钮。有解析结果时主要动作是
|
||||
“查看解析结果”;没有结果时,失败文档显示“重新处理”,其余文档有任务时显示
|
||||
“查看任务”,否则显示“打开来源”。来源组优先提供失败重试或查看任务,没有任务时
|
||||
提供同步或暂停。状态与错误始终在行内可见。
|
||||
|
||||
更多操作复用 `AnchoredMenu`,依次放置打开来源、打开解析时原件、文档分块、查看任务,
|
||||
再用分隔线区分重新处理、来源同步和移除来源。仅在有对应资源时显示原件或任务入口;
|
||||
没有分块时禁用分块入口。来源组的菜单提供任务及来源维护。菜单支持方向键、Home、End、
|
||||
Escape 和焦点返回,展开不改变行高;空间不足时翻转方向或在菜单内滚动。
|
||||
大小与分块数保持单行,窄窗口在表格内部横向滚动。
|
||||
操作文案使用“打开来源 / 重新解析 / 同步 / 暂停 / 重试 / 移除来源”,不附带对象名称;更多操作按钮的可访问名称和移除确认框仍保留对象名称。
|
||||
列表和预览的操作菜单宽度为 `220px`,小视口下收窄并保留左右各 `16px` 边距。
|
||||
|
||||
解析预览头部按返回、文档标题与库名及全局范围、维护菜单排列,地址仍可辨认。
|
||||
正文、图片和详情使用共享结果组件;知识库的会话选择及添加图片工具仅在图片页显示,
|
||||
切换页签保留选择。返回列表恢复搜索、原触发按钮焦点及页面滚动位置。重新解析继续
|
||||
调用文档重建,使用当前设置并保留上次结果,失败时显示可恢复错误。维护菜单同时保留
|
||||
当前来源与解析时原件的打开操作,两者分别读取知识来源和已保存的解析资产。
|
||||
|
||||
在飞操作只禁用对应的操作入口。同步或重解析 A 期间,其他来源 B 的暂停仍可用;
|
||||
并发操作分别恢复可用状态,A 结束不能提前解除仍在执行的 B。
|
||||
|
||||
文档行提供“打开来源”,Main 根据知识库和文档归属重新读取保存的文件路径或 URL。
|
||||
移除来源使用确认对话框,说明文档数、索引、图谱证据和托管副本影响。初始焦点位于
|
||||
取消,提交期间禁止重复操作,失败时保留对话框和错误。
|
||||
@@ -77,3 +113,30 @@ Conversation 的检索范围,返回对话并聚焦输入框。
|
||||
均切换一次选择;选中背景和整行焦点环保持可见。Tab 聚焦、Space 切换,Escape 关闭并
|
||||
返回触发按钮。点击行内非焦点元素时产生的空目标失焦不关闭弹层;点击外部或焦点明确
|
||||
移到外部时才关闭,避免原生标签完成勾选前被卸载。
|
||||
|
||||
## 8. 任务批次与历史
|
||||
|
||||
任务中心按服务返回的 `parentTaskId` 组织一次操作及其文档、阶段子任务。顶层操作按
|
||||
创建时间倒序排列,一个操作计为一个批次;缺失父记录的旧任务仍独立显示。重试产生的
|
||||
新操作保留真实历史,使用已有 `attempt` 显示尝试次数,不按标题猜测合并关系。
|
||||
|
||||
文档或来源进入任务中心后,列表与计数使用同一对象范围,并保留命中任务的父级上下文。
|
||||
合并文档行同时匹配来源 ID 和文档 ID,包括没有来源 ID 的文档任务。“清除对象筛选”
|
||||
恢复当前库的任务集合。
|
||||
|
||||
列表与解析预览使用相同的文档任务范围。目录及一对多来源下的文档只按文档 ID 筛选,
|
||||
即使搜索后只剩一篇也不扩大到整个来源;只有真正的单文档合并行同时包含来源 ID。
|
||||
预览菜单中的来源同步、暂停和移除仍作用于所属来源,来源维护范围独立于任务筛选。
|
||||
|
||||
- 进行中:批次内有排队或运行记录。
|
||||
- 失败:批次内有失败或中断记录;运行批次也可能同时存在失败子任务。
|
||||
- 历史:批次内没有排队或运行记录,包含完成、失败、中断、取消和跳过。
|
||||
|
||||
计数均按批次计算,失败与历史可以重叠。标题旁的共享帮助解释口径。运行批次中已成功的
|
||||
阶段不会单独进入历史。切换到进行中或失败时展开相关批次;全部和历史默认收起子任务,
|
||||
用户仍可逐项展开。批次状态保留服务原值,子任务失败以额外文字提示,不改写父状态。
|
||||
|
||||
只有进行中的记录显示进度条;失败和中断保留停止阶段、错误及处理建议。已结束记录
|
||||
不再显示“当前阶段”、百分比或默认“等待处理”。摘要保留时间、服务消息、子任务数及
|
||||
重试次数。取消和重试严格使用服务返回的 `canCancel`、`canRetry`,提交期间禁用重复
|
||||
操作,失败后保留筛选、任务内容及就地错误。
|
||||
|
||||
@@ -8,8 +8,17 @@ stdio MCP 提供明确、可诊断的 Node.js 与 Python 环境,同时允许
|
||||
用户从设置中心的“能力与工具”进入,并在 `Skills`、`MCP`、`工具执行环境` 三个水平
|
||||
Tab 之间切换。
|
||||
|
||||
[Obsidian 接入](../obsidian/README.md)复用本功能的 `LaunchEnvironmentProvider` 启动
|
||||
随包交付的 MCPVault,不新增 Node 安装或下载源设置。生产组件自动化已覆盖真实 MCPVault
|
||||
调用,Electron 托管 Node 离线冒烟仍待完成,具体见[验证记录](../obsidian/progress.md)。
|
||||
|
||||
## 文档导航
|
||||
|
||||
Local native Continue and DS Web clients now honor the current custom Node
|
||||
selection. They require standard Node rather than Electron's Node mode; the
|
||||
remaining managed fallback and validation are tracked in
|
||||
[native client progress](../assistant-workbar/progress.md#2026-09-27-local-native-client-validation).
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [PRD](./prd.md) | 用户问题、产品目标、范围、需求和产品验收 |
|
||||
|
||||
@@ -81,6 +81,49 @@
|
||||
|
||||
## 已验证证据
|
||||
|
||||
### 2026-09-27 MCP 编辑器遮挡修复
|
||||
|
||||
真实 Electron App 经“设置 → 能力与工具 → MCP → 自定义 MCP → 添加 Server”复现。
|
||||
修复前编辑器层级为 `70`、设置为 `130`,输入框中心命中设置页签容器;名称输入框虽已
|
||||
获得焦点,父设置仍为 `inert=false`。添加和编辑现接入共享 Modal 层级与背景隔离,
|
||||
遮罩关闭保留触发按钮焦点。没有修改 Runtime、Agent 或 MCP 传输实现。
|
||||
|
||||
`overlay-layering.electron.test.ts` 新增真实 App 用例,复用生产 preload、IPC、
|
||||
`CapabilityService`、设置存储与 `BrowserService`。已通过的场景:
|
||||
|
||||
| 场景 | 验证结果 |
|
||||
| --- | --- |
|
||||
| 无浏览器,浅色 `1280 × 900`、深色 `960 × 420` | 添加/编辑鼠标命中、文字输入、父设置隔离、Tab/Shift+Tab 循环通过 |
|
||||
| 右侧原生浏览器打开,浅色 `1280 × 900`、深色 `1280 × 420` | 设置及 MCP 期间原生视图隐藏;设置关闭后恢复相同 webContents ID、URL 和页面内标记,无重建或刷新 |
|
||||
| 关闭与焦点 | X、取消、Escape、遮罩、保存成功返回原触发按钮;关闭子层保留设置,关闭设置返回其入口 |
|
||||
| 持久化 | 四次 UI 保存经生产 IPC 写入临时配置,新建 `CapabilityService` 重读四条记录;编辑重开回显名称 |
|
||||
|
||||
原有 overlay Electron 用例也通过,继续覆盖解析菜单、二级结果、通知、应用中心、本机
|
||||
推理监控和原生 `dialog`。回归禁用 Windows 原生窗口遮挡节流,避免其他桌面窗口暂停
|
||||
测试的帧等待;未改变产品窗口选项。浏览器页面使用本地 HTTP 服务;MCP 测试配置保存为
|
||||
停用状态,不启动外部 MCP 命令。真实模型调用 **0 次**。
|
||||
|
||||
证据范围为 Windows x64 开发态 Electron。无关启动服务使用 harness 替身,未配置的
|
||||
向量模型与 Runtime 原生能力服务会返回不可用;未验收这些服务、外部 MCP 连接、远程
|
||||
Agent 或其他操作系统。短窗口从已打开设置调整尺寸;宽度跨越工作栏自动收起阈值的路径
|
||||
不作为浏览器保持打开的验收场景。
|
||||
|
||||
最终 focused 集合为 6 文件、215 项通过;`npm run typecheck`、`npm run lint` 和
|
||||
`git diff --check` 通过。本轮未运行全仓 `npm test` 或打包。验证命令:
|
||||
|
||||
```text
|
||||
npx vitest run tests/overlay-layering.electron.test.ts src/renderer/src/McpSettingsSection.test.tsx src/renderer/src/SettingsPanel.test.tsx src/renderer/src/FloatingPortal.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx src/renderer/src/RightAssistantSidebar.resize.test.tsx
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
git diff --check
|
||||
```
|
||||
|
||||
调试期间的失败记录:旧代码在真实鼠标命中断言失败;接入共享焦点后补修遮罩关闭的默认
|
||||
焦点转移。harness 的帧等待曾受 Windows 窗口遮挡节流影响,浏览器导航曾在创建或输入
|
||||
状态提交前触发;分别关闭测试进程的遮挡节流,并等待可用控件与实际页面加载后验证。
|
||||
这些失败未计为产品验收通过。中文文档扫描无阻断项;本节的并列项是实测关闭入口和覆盖
|
||||
组件清单,保留以便核对。
|
||||
|
||||
### 2026-09-03 `v0.12.0` 失败标签恢复
|
||||
|
||||
- 不可变标签工作流 `33704483505` 的 Windows x64/ARM64 与 macOS x64/ARM64 托管 Python
|
||||
|
||||
@@ -279,6 +279,14 @@ stdio MCP:
|
||||
Tab、不渲染占位面板。
|
||||
- 页签直接包装层使用 `flex: 0 0 auto`,并由 CSS 回归测试保证在滚动设置布局中不可收缩。
|
||||
|
||||
`McpSettingsSection` 的添加与编辑共用 body Portal,遮罩使用 `--z-dialog`。
|
||||
打开前记录并聚焦触发按钮,再由 `activateModalFocus` 隔离父设置、聚焦名称并在关闭时
|
||||
恢复原隔离状态与焦点;键盘事件在子层停止传播。遮罩的 `mousedown` 关闭分支阻止默认
|
||||
焦点转移,避免恢复到触发按钮后又失焦。原生浏览器继续复用
|
||||
`browser-viewport-occlusion.ts`,无需 MCP 专用隐藏条件。
|
||||
真实 App 回归位于 `tests/overlay-layering.electron.test.ts`,使用生产 preload、IPC、
|
||||
配置存储及 `BrowserService`,验证添加、持久化、编辑、父层隔离与原生浏览器恢复。
|
||||
|
||||
### 11.2 工具环境状态与 IPC
|
||||
|
||||
Renderer 接收:
|
||||
|
||||
@@ -113,6 +113,12 @@ MCP 面板
|
||||
- MCP 原有四个内层页签继续保留,外层和内层使用不同的 `aria-label` 与 `idPrefix`。
|
||||
- 窄屏时外层页签保持单行并允许水平滚动,不折成多行,不替换为下拉框。
|
||||
|
||||
“MCP / 自定义 MCP”的添加和编辑共用设置上方的子 Modal。打开后聚焦名称输入框,
|
||||
父设置与工作区保持不可交互;Tab 和 Shift+Tab 在编辑器内循环。X、取消、Escape、
|
||||
遮罩及保存成功均返回原添加或编辑按钮,Escape 只关闭编辑器。保存期间禁止关闭,
|
||||
失败保留输入。右侧浏览器沿用应用级 Modal 的视图释放规则,编辑器关闭后仍由父设置
|
||||
保持隔离,设置关闭后恢复网页。
|
||||
|
||||
## 4. 工具下载源
|
||||
|
||||
使用原生 Radio Group:
|
||||
|
||||
@@ -26,9 +26,26 @@ context and protects unsaved editor changes. A successful save followed by no
|
||||
further edits does not prompt again on return. Narrow note layouts use an index
|
||||
drawer and bounded bottom AI pane.
|
||||
|
||||
Conversation capture and the compact Notes workbar panel are implemented. Users
|
||||
can preview and edit one assistant response or the complete conversation text,
|
||||
then append it to an existing note or create a note with its first entry. Quick
|
||||
notes opens the same panel for search, browsing and text appends. Saved entries
|
||||
carry structured conversation sources with return navigation. The panel and full
|
||||
workspace share storage and change notifications; drafts remain in App memory.
|
||||
Real Electron acceptance passed through the App UI, production preload, IPC and
|
||||
SQLite. Repository validation is not all green; the [progress record](./progress.md)
|
||||
separates focused passes, repaired schema fixtures and execution-time failures.
|
||||
|
||||
## Documents
|
||||
|
||||
- [Technical design](./technical-design.md): file storage, schema 38 migration,
|
||||
- [Conversation integration PRD](./prd.md): scope and acceptance for
|
||||
conversation capture and the compact workbar panel.
|
||||
- [Conversation integration logic](./logic-design.md): capture boundaries, source
|
||||
lifetime, panel state, draft protection and application availability.
|
||||
- [Conversation integration UI](./ui-design.md): implemented entry points, compact
|
||||
panel, capture preview and source navigation, following the root UI system.
|
||||
- [Technical design](./technical-design.md): file storage, schema 38 body migration,
|
||||
schema 48 source metadata, desktop search and atomic initial-entry creation,
|
||||
backup boundaries, database notifications, process boundaries, canvas and AI
|
||||
contracts, refresh lifecycle and regression coverage.
|
||||
- [Progress](./progress.md): implementation and validation evidence.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# 会话联动逻辑设计
|
||||
|
||||
状态:已按当前实现核对,验证进度见 [progress.md](./progress.md)。需求编号来自 [PRD](./prd.md)。
|
||||
|
||||
## 内容与来源
|
||||
|
||||
FR-2、FR-3:单条采集读取所选助手消息的非空正文;整段采集先加载该会话完整历史,再按消息顺序收录用户和助手的非空正文,保留角色分隔。预览建立后,切换会话或收到新消息不会替换正文及来源标识。生成中的消息不可采集,停止或失败后已有正文可保存,单条预览注明回答未完成。整个会话正在生成时禁用整段采集,提示等待完成或停止生成。
|
||||
|
||||
首版按文字保存,保留原始 Markdown 文本、换行和代码文本,不承诺还原聊天渲染效果。推理、工具调用日志、系统提示、附件二进制和图片不进入正文;预览持续说明保存范围。用户可编辑预览,来源仍指向本次采集的会话。读取完整会话失败时不以屏幕上已有消息代替。
|
||||
|
||||
FR-5:来源独立于可编辑正文。单条来源包含会话和消息标识;整段来源包含会话标识、此次收录的消息标识及采集时间。预览使用采集时的会话标题和项目名称;保存时主进程校验消息归属,并以当时数据库中的会话标题和项目名称写入来源,无项目时显示全局。若预览期间源会话或消息已删除,保存失败并保留预览。保存完成后,修改或删除源消息不回写笔记正文和来源。
|
||||
|
||||
点击来源时读取完整会话。消息不存在但会话仍在时打开会话并提示“原消息已不存在”;确认会话不存在后保留来源文字,并在当前来源条禁用跳转。其他读取失败显示可重试错误。打开未渲染的来源消息时扩大聊天历史显示范围,再滚动定位并聚焦。编辑笔记正文不移除来源;删除笔记不影响源会话。
|
||||
|
||||
## 面板与草稿
|
||||
|
||||
FR-1、FR-4:笔记应用为可关闭、可排序的单实例工作栏 Tab,新布局默认不打开。再次触发入口激活现有 Tab;笔记选择属于应用级状态,切换项目或会话不改写它。列表按置顶优先、最近更新时间排列,搜索匹配标题与记录文字,同一笔记只显示一次。当前应用会话内保留最近选择,重启不恢复笔记选择或草稿。该笔记已删除时回到列表,清除失效保存目标并保留待保存文字。
|
||||
|
||||
FR-2、FR-3:面板有浏览、采集编辑、保存中三个状态。触发采集进入编辑态;选择目标只改变写入位置;明确点击“加入笔记”才写入。每次提交新增一条记录,不覆盖旧记录。新建目标时先填写标题,会话标题作为可修改默认值;提交一次创建笔记和首条记录,取消不遗留空笔记。普通“新建笔记”仍可显式创建空笔记。
|
||||
|
||||
保存中禁用重复提交和面板离开操作,结果返回后可再次操作。成功后清除本次草稿,显示响应返回的新增记录;失败保留预览、标题和目标。目标已删除时提示重新选择。容量校验使用现有文字记录限制,超限由用户缩减正文后重试;一次提交始终对应一条完整记录。
|
||||
|
||||
关闭 Tab、返回面板列表、从面板打开完整工作区、禁用应用或以另一条采集替换草稿前,显示“继续编辑 / 放弃草稿并切换”确认。普通主页面、项目和会话切换以及隐藏工作栏保留 App 持有的草稿及来源。取消采集只在正文或标题相对预览初值有修改时确认;未修改预览可直接取消。完整工作区自身的编辑草稿仍由其离开确认保护。
|
||||
|
||||
FR-6:成功写入触发现有通知刷新视图;面板重新挂载或工作栏重新展开时读取最新数据。面板挂载期间保留搜索词;笔记选择和草稿由 App 保留,不受搜索响应或后台刷新覆盖。既有记录编辑继续使用原修订号处理冲突。此处同步仅指本机共享存储的视图更新。
|
||||
|
||||
## 启用与评论
|
||||
|
||||
FR-1~FR-4:遵循魔法笔记应用的启用状态。禁用时隐藏会话采集及工作栏目录入口,并移除笔记 Tab;禁用前处理草稿。外部设置变更要求禁用时,选择继续编辑会尝试恢复启用设置,失败显示设置错误。应用未常驻不影响会话采集或工作栏可用性。设置继续由应用中心管理。
|
||||
|
||||
会话采集和快速追加本身不调用模型。保存后沿用现有 AI 评论模式;只有已有设置要求保存后自动评论时才触发,失败不撤销保存,也不自动重新追加。
|
||||
|
||||
## 完整性检查
|
||||
|
||||
当前实现覆盖首次创建、追加、取消、重复点击、写入失败、目标删除、来源删除、跨会话切换及刷新与草稿的关系。完整历史读取、来源定位和存储接口已接入;产品范围无待决项。真实 Electron 已验证采集、保存、定位、窄面板键盘操作和持久化,具体范围见进展记录。全仓测试和类型检查仍有执行时记录的失败,聚焦复跑通过不等于全仓通过。
|
||||
@@ -0,0 +1,32 @@
|
||||
# 魔法笔记与会话联动
|
||||
|
||||
状态:会话联动前后端已实现,真实 Electron 验收通过;全仓验证未全绿。聚焦结果、fixture 修复后复跑及执行时的全仓失败见 [progress.md](./progress.md)。
|
||||
|
||||
## 目标与范围
|
||||
|
||||
用户在阅读回答时,可将内容保存到已有笔记或新笔记,并从笔记返回原会话。会话界面提供文字预览和保存位置选择,与已有 Agent 笔记工具共用存储。
|
||||
|
||||
独立工作区继续承载完整编辑、画布、待办和 AI 评论;右侧笔记面板提供浏览和快速追加。笔记仍属于全局,不因入口所在项目改变归属。
|
||||
|
||||
## 功能要求
|
||||
|
||||
| 编号 | 要求 |
|
||||
| --- | --- |
|
||||
| FR-1 | 右侧助手工作栏增加“笔记”应用,支持搜索、打开已有笔记、新建笔记、查看记录摘要、快速追加文字,以及打开对应完整工作区。 |
|
||||
| FR-2 | 助手消息操作栏提供“加入笔记”,打开可编辑预览,用户选择已有笔记或新建后明确提交。 |
|
||||
| FR-3 | 会话标题的更多菜单提供“保存当前对话到笔记”,按触发时的会话内容生成预览,保存为目标笔记中的一条记录。 |
|
||||
| FR-4 | 通过右侧工作栏“+ → 笔记”打开面板并恢复当前应用会话中最近选择的笔记,首次使用显示列表,不隐式保存当前对话。左侧底部和聊天页头不再提供重复的“快速笔记”入口。 |
|
||||
| FR-5 | 从会话保存的记录保留结构化来源,显示会话名称、所属项目(如有)和采集时间,支持返回原会话及单条来源消息。源会话删除后保留笔记内容。 |
|
||||
| FR-6 | 面板、完整工作区和 Agent 写入使用同一份笔记数据,成功写入后更新已打开视图,同时保护未保存草稿。 |
|
||||
|
||||
首版不包含自动总结、自动分类、自动选择保存目标、选中文本采集、多条消息勾选、附件文件复制或跨设备同步。“总结后加入”和选区采集作为后续扩展,不预留不可用按钮。
|
||||
|
||||
## 产品验收
|
||||
|
||||
- 首次使用且没有笔记时,可从一条助手回答创建笔记并保存首条记录;已有笔记时可搜索并追加,原记录保持不变。
|
||||
- 保存当前对话包含未在屏幕上渲染的历史正文;预览范围明确,超出容量时提示缩减内容,不静默截断。
|
||||
- 保存成功后显示目标笔记并定位新增记录;取消不写入,失败保留内容和目标供重试。
|
||||
- 从来源条可返回对应会话;会话已删除时显示“原会话已不存在”,已保存正文仍可阅读和编辑。
|
||||
- 面板追加后完整工作区可见,Agent 修改后面板可见;后台刷新和切换会话不覆盖未保存文字。
|
||||
|
||||
状态与内容范围见 [逻辑设计](./logic-design.md),入口和操作细节见 [界面设计](./ui-design.md),接口与存储见 [技术设计](./technical-design.md#conversation-integration)。
|
||||
@@ -1,5 +1,131 @@
|
||||
# Magic Notes Progress
|
||||
|
||||
## 2026-09-27: Shared Destructive Button Styles
|
||||
|
||||
The draft discard Portal now receives the global `danger-solid` styles. The
|
||||
shared rule also covers note deletion, HeartbeatSettings, SupervisorWorkspace,
|
||||
WorkspaceActionDialog and ToolEnvironmentSettingsSection without caller changes.
|
||||
Sizing, typography and disabled feedback share the primary/secondary button
|
||||
rules; theme tokens provide default and hover backgrounds. See
|
||||
[shared destructive controls](../../../UI-DESIGN.md#66-危险操作).
|
||||
|
||||
- `npm test -- tests/magic-notes-capture.electron.test.ts src/renderer/src/WorkspacePrimitives.test.tsx`
|
||||
passed 2 files / 37 tests. The real App/preload/IPC/SQLite capture test opens the
|
||||
draft guard outside the notes page ancestor. Both themes render a 36px-high,
|
||||
10px-radius button with the same font and padding as Continue editing. Native
|
||||
Tab reaches the visible shared focus ring; continuing editing retains the draft.
|
||||
- Hover uses Chromium `CSS.forcePseudoState`; disabled is set on the rendered
|
||||
button to check opacity, cursor and suppressed hover. These are CSS state
|
||||
checks, not verification of native pointer targeting or an asynchronous submit.
|
||||
Theme contrast checks include both default and hover backgrounds.
|
||||
- Windows Electron screenshots were captured and visually inspected for both
|
||||
themes with keyboard focus. Set `GB_CAPTURE_SCREENSHOT_DIRECTORY` to an existing
|
||||
directory to retain `draft-danger-light.png` and `draft-danger-dark.png`.
|
||||
Model calls: 0. Other callers and other OSes were not individually rendered.
|
||||
- `npm run typecheck` and `npm run lint` passed. Repository-wide
|
||||
`npm test -- --bail=1` stopped at the unrelated
|
||||
`HeartbeatCenter.test.tsx:213` assertion (expected one tablist, received two);
|
||||
this is not an all-green full-suite result.
|
||||
|
||||
## 2026-09-27: Analysis IPC Extraction
|
||||
|
||||
The three analysis handlers now live in `magic-notes-analysis-ipc.ts`, with
|
||||
the existing tracked registration injected at the original positions around
|
||||
note and todo CRUD. Model selection and lifecycle are documented in
|
||||
[Main IPC Ownership](./technical-design.md#main-ipc-ownership) and
|
||||
[Canvas Analysis and Comments](./technical-design.md#canvas-analysis-and-comments).
|
||||
No runtime factory, model-selection rule or Agent protocol changed.
|
||||
|
||||
- Before and after extraction, with `GB_NOTES_LIVE=0`:
|
||||
`npm test -- src/main/ipc.test.ts src/main/magic-notes src/preload/magic-notes-events.test.ts tests/magic-note-canvas-text.test.ts tests/magic-notes-analysis.electron.test.ts`
|
||||
passed 8 files, 232 tests, with 1 skipped (96.49s baseline, 97.90s after).
|
||||
Electron exercised vision and text-only analysis through production UI/preload/IPC
|
||||
with a local model stub; real provider calls for these checks: 0.
|
||||
- `npm run lint` passed. `npm run typecheck` stopped in the Node project with
|
||||
11 unrelated missing-`forwardLoopback` errors in `ssh-connection-pool.test.ts`,
|
||||
`ssh-host-directory-browser.test.ts` and `ssh-terminal-session.test.ts`.
|
||||
Separate `npx tsc --noEmit -p tsconfig.agent.json` and
|
||||
`npx tsc --noEmit -p tsconfig.web.json` passed.
|
||||
- A 120-second-bounded `npm test -- --bail=1` stopped after 94.45s:
|
||||
20 files passed, 1 failed; 1004 tests passed, 1 failed, 25 skipped.
|
||||
`RightAssistantSidebar.resize.test.tsx:387` expected two persisted terminal
|
||||
titles but received one. This was not a complete full-suite run, and the
|
||||
unrelated failure was not changed.
|
||||
- `git diff --check` passed with existing LF/CRLF warnings.
|
||||
|
||||
## 2026-09-26: Conversation Capture and Compact Notes Panel
|
||||
|
||||
- FR-1/FR-4: `notes` is an application-scoped optional singleton in the workbar.
|
||||
The compact panel supports title/body search, browsing, text appends and full
|
||||
workspace navigation. Quick notes uses the same tab; app enablement controls
|
||||
availability independently of sidebar pinning.
|
||||
- FR-2/FR-3: Assistant-message and conversation-header actions open editable text
|
||||
previews. Whole-conversation capture loads complete history. App owns drafts
|
||||
and selection; close, replacement and disable actions use the draft guard.
|
||||
Success focuses the returned entry ID; failed saves retain input.
|
||||
- FR-5: Schema 48 adds nullable source metadata. Main validates message ownership
|
||||
and resolves display labels at save time. Panel and workspace share source
|
||||
navigation, including missing-message and missing-conversation handling.
|
||||
- FR-6: Desktop search, preload/IPC and note-level SQL results are connected.
|
||||
Creating a note with initial content commits note/entry membership in one
|
||||
transaction. Both views use existing change notifications; file reconciliation
|
||||
and workspace optimistic revisions retain their existing boundaries. See
|
||||
[the implemented contract](./technical-design.md#conversation-integration).
|
||||
|
||||
Validation reported by implementation subagents in this session:
|
||||
|
||||
| Scope | Reported result | Evidence boundary |
|
||||
| --- | --- | --- |
|
||||
| Backend focused checks | 362 tests passed | Subagent handoff; not a repository-wide result |
|
||||
| Frontend focused checks | 8 files / 592 tests passed | Subagent handoff; not real Electron acceptance |
|
||||
| Web TypeScript and lint | Passed | Subagent handoff; does not establish full-repository typecheck/lint |
|
||||
|
||||
The coordinating agent subsequently reported real Electron acceptance passing.
|
||||
[magic-notes-capture.electron.test.ts](../../../tests/magic-notes-capture.electron.test.ts)
|
||||
and its [Main driver](../../../tests/support/magic-notes-capture-main.ts) /
|
||||
[renderer](../../../tests/support/magic-notes-capture-renderer.tsx) mount the real
|
||||
App UI and use production preload, registered IPC and SQLite/file storage.
|
||||
Unrelated services use test dependencies; the renderer bridge is not replaced.
|
||||
|
||||
- Saving an assistant response to an existing note retains its earlier entry;
|
||||
whole-conversation capture creates a new note with its first entry.
|
||||
- All 502 historical messages are compared verbatim in the preview and saved
|
||||
content, including history initially outside the rendered timeline.
|
||||
- Full-workspace navigation locates the exact saved entry; source navigation
|
||||
restores the conversation and focuses the source message. The coordinating
|
||||
agent also reports source-history expansion coverage.
|
||||
- Native Electron keyboard input covers returning to the list, opening a note
|
||||
and appending text in a measured 214px panel, without horizontal overflow.
|
||||
- SQLite close/reopen preserves both notes. Deleting the source conversation
|
||||
retains their saved content and source metadata. Model calls: **0**.
|
||||
|
||||
The combined Electron, `canvas-ipc.integration`, `MagicNotesPanel` and
|
||||
`ChatTimeline` run passed **4 files / 70 tests**. Lint and web TypeScript passed.
|
||||
This validates the App capture path in a test Electron window with isolated data;
|
||||
it does not establish release-package or cross-platform acceptance.
|
||||
|
||||
Repository validation and subsequent fixture repairs:
|
||||
|
||||
| Check | Result at execution time | Follow-up |
|
||||
| --- | --- | --- |
|
||||
| `npm test` | 457 files / 5,359 cases: 5,272 passed, 4 failed, 83 skipped | No all-green full-suite rerun reported |
|
||||
| Three schema-fixture failures | Activity-history and two worker fixture variants omitted `DROP COLUMN source_json` when reconstructing old schemas | Fixed; the five-file regression run below passed |
|
||||
| Remaining test failure | `tests/build-release.test.ts`: missing DSH resources | Unrelated to notes; not fixed in this validation pass |
|
||||
| Full-repository typecheck | Failed on parallel SSH mocks missing `forwardLoopback`, an IPC native-terminal fixture referencing undefined `terminalManager`, and mismatched arguments in `obsidian-runtime-acceptance` | Execution-time findings; later concurrent changes may alter them |
|
||||
|
||||
After fixture repairs, **5 files / 58 tests passed**:
|
||||
`src/main/assistant/activity-history-io.test.ts`,
|
||||
`tests/assistant-storage-worker.test.ts`,
|
||||
`src/main/assistant/assistant-storage-upgrade.test.ts`,
|
||||
`src/main/magic-notes/magic-note-storage.test.ts` and
|
||||
`src/main/magic-notes/canvas-ipc.integration.test.ts`.
|
||||
This focused rerun does not replace the failed full-suite/typecheck results.
|
||||
|
||||
Status: **implemented; real Electron acceptance passed; repository validation is
|
||||
not all green**. Results above come from the session handoffs, not a rerun by this
|
||||
documentation task. Later fixes require their own verification evidence; older
|
||||
Electron/full-suite results below do not validate this integration.
|
||||
|
||||
## 2026-09-24: Resizable Todo List and Collapsible Note Groups
|
||||
|
||||
- Todo list/detail uses a 1px divider with a transparent 9px pointer target and
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# Magic Notes Technical Design
|
||||
|
||||
## Main IPC Ownership
|
||||
|
||||
[magic-notes-ipc.ts](../../../src/main/magic-notes/magic-notes-ipc.ts) owns
|
||||
note search/list/get/create/update/delete, entry create/update/delete and todo
|
||||
list/status/update handlers, including conversation-source validation and entry
|
||||
content mapping. Its two registration functions receive the existing tracked
|
||||
`registerHandler`, window and database from [ipc.ts](../../../src/main/ipc.ts),
|
||||
preserving registration order: note CRUD, entry/draft analysis, todo CRUD, then
|
||||
todo analysis. [magic-notes-analysis-ipc.ts](../../../src/main/magic-notes/magic-notes-analysis-ipc.ts)
|
||||
owns the three analysis handlers through two registration functions. It receives
|
||||
the window, database, settings readers, usage persistence callback and existing
|
||||
error formatter; it imports `createDefaultModelRuntime` directly. `ipc.ts` retains
|
||||
tracked registration/removal and shared usage persistence.
|
||||
Sender checks and shared input schemas stay at each handler boundary.
|
||||
[rich-content.ts](../../../src/main/magic-notes/rich-content.ts) remains shared
|
||||
by CRUD and draft analysis. Analysis retains revision checks, hidden task status,
|
||||
stream events, error causes and nested release/dispose cleanup.
|
||||
|
||||
## Continuous Record Workspace
|
||||
|
||||
The detail mounts every entry in a continuous newest-first stream. The left index
|
||||
@@ -252,7 +270,13 @@ Draft, saved-entry and todo analysis use the existing typed preload/IPC methods
|
||||
and `createDefaultModelRuntime`. Each canvas request reads the current default
|
||||
model profile's `supportsImageInput` (or the resolved default setting when no
|
||||
profile is selected). Main resolves settings again for the actual request. This
|
||||
does not change an Agent runtime or desktop-to-Agent protocol.
|
||||
does not change an Agent runtime or desktop-to-Agent protocol. All three handlers
|
||||
read global resolved settings per request, without applying chat/project runtime
|
||||
selection. The factory creates a tool-free direct `ModelAgentRuntime`, using the
|
||||
resolved default model connection and that profile's headers, body options and
|
||||
output limit. Environment-managed default credentials retain the settings store's
|
||||
environment overrides. The existing context-compression configuration can select
|
||||
a separate summary model; it does not select the main analysis model.
|
||||
|
||||
`magicNoteCanvasPageCount` is an application preference: an integer from 1 to 8,
|
||||
defaulting to 1 when absent in historical settings. The application center and
|
||||
@@ -449,6 +473,124 @@ These note tools run in the desktop gateway. Remote gbagent ACP sessions
|
||||
currently inject only the image MCP server, so this change requires no
|
||||
daemon implementation update.
|
||||
|
||||
## Conversation Integration
|
||||
|
||||
The desktop API and renderer integration are implemented. Scope and behavior
|
||||
are owned by the [PRD](./prd.md), [logic design](./logic-design.md) and
|
||||
[UI design](./ui-design.md). Real Electron acceptance passed; focused results,
|
||||
fixture repairs and non-green repository checks are recorded in [progress](./progress.md).
|
||||
|
||||
### Workbar and Capture
|
||||
|
||||
`WORKBAR_APP_DEFINITIONS` registers `notes` as an optional, closable, reorderable
|
||||
singleton with application scope, no `targetRef` and `defaultOpen: false`.
|
||||
`RightAssistantSidebar` opens or activates it through the existing
|
||||
[workbar tab model](../assistant-workbar/terminal-tabs-prd.md), and removes it when
|
||||
Magic Notes is disabled. Sidebar pinning does not control this availability.
|
||||
`MagicNotesPanel` renders the compact view; `WorkbarShell` supplies the shared tab
|
||||
controls. The footer shortcut and collapsed-sidebar chat-header shortcut both
|
||||
activate the same instance.
|
||||
|
||||
`App.captureToNote` receives the message identity from `ChatTimeline`, or calls
|
||||
`ensureConversationHistory` for whole-conversation capture. It freezes text and
|
||||
source IDs before opening the preview and waits for queued conversation persistence
|
||||
so Main can validate newly completed messages. Whole-conversation text includes
|
||||
nonempty user/assistant bodies with role headings. It excludes reasoning, tool
|
||||
logs and attachment binaries, and rejects streaming conversations.
|
||||
|
||||
`useMagicNoteDraft` lives in App and owns the draft, selected note, saving state
|
||||
and one shared confirmation promise. Selection and drafts survive panel remounts
|
||||
and conversation/project switches, but are not persisted across app restarts.
|
||||
The guard rejects leaving while saving and confirms discarding pending input;
|
||||
explicit cancellation compares text/title with their initial preview values.
|
||||
Application disablement and tab closing call the same guard. Workspace navigation
|
||||
also passes through the full workspace's existing leave check.
|
||||
|
||||
`capturedNoteContent` converts text into v1 Quill string inserts, preserving
|
||||
newlines and adding the terminal newline when needed. It splits inserts at the
|
||||
200,000-code-unit operation limit without splitting surrogate pairs; the complete
|
||||
record still passes the shared rich-content schema. Markdown stays literal text.
|
||||
No summarization or attachment copying runs during capture. New-note titles are
|
||||
limited to 100 characters. A synchronous submission ref prevents duplicate writes.
|
||||
|
||||
`magicNotes.create({ title, content?, source? })` returns `MagicNoteCreateResult`,
|
||||
with `createdEntryId` when initial content exists. `createMagicNote` inserts the
|
||||
note, first entry, source and derived todos inside one SQLite transaction; failure
|
||||
leaves no committed empty note and sends no success notification. This atomicity
|
||||
applies to database membership; filesystem writes retain the reconciliation
|
||||
boundary in [File Storage and Writes](#file-storage-and-writes).
|
||||
`magicNotes.createEntry({ noteId, content, source? })` always returns the exact
|
||||
`createdEntryId`. The panel uses that ID to scroll, focus and briefly highlight the
|
||||
new record. Opening the full workspace passes `{noteId, entryId?, requestId}` into
|
||||
its existing guarded note-navigation path.
|
||||
|
||||
`magicNotes.search({ query, limit? })` is exposed by `DesktopApi`, preload and
|
||||
`magic-notes:search`. Trusted-sender and shared-schema validation precede
|
||||
`AssistantDatabase.searchMagicNoteSummaries`. The trimmed query has a 1,000-character
|
||||
limit; result limits are 1-200, defaulting to 200. The panel requests 100 results
|
||||
and debounces nonempty queries by 180 ms. SQL searches note titles or indexed entry
|
||||
text using `EXISTS`, so each note occurs once before `LIMIT`. Empty queries list
|
||||
notes; ordering is pinned first, then updated time and row ID descending.
|
||||
`%`, `_` and backslashes are escaped for literal matching. Agent `note_search`
|
||||
retains its separate entry-level output and 1-100 limit.
|
||||
|
||||
### Source Metadata
|
||||
|
||||
`magicNoteSourceSchema` defines `kind` (`message` or `conversation`),
|
||||
`conversationId`, nonempty unique `messageIds`, ISO `capturedAt`,
|
||||
`conversationTitle`, and optional `projectId` / `projectName`. A message source
|
||||
requires exactly one message ID. Create/create-entry inputs accept `source`;
|
||||
note creation requires initial content when source is supplied. Entry reads
|
||||
return it independently of editable content, while update inputs cannot replace it.
|
||||
|
||||
The IPC helper `resolveMagicNoteSource` loads the stored conversation and verifies
|
||||
that every message ID belongs to it; a single-message source must identify an
|
||||
assistant message. It replaces renderer-supplied title/project labels with current
|
||||
database values while retaining the supplied capture time and IDs. Preview labels
|
||||
therefore describe capture time, but stored labels describe save time. Missing
|
||||
source rows reject creation; later source deletion does not block note reads or edits.
|
||||
|
||||
Schema 48 adds nullable `magic_note_entries.source_json` in a transaction and
|
||||
advances `PRAGMA user_version`. Existing entries read without a source. It adds no
|
||||
conversation foreign key or deletion cascade, and does not rewrite file-backed
|
||||
bodies. Schema 38 remains the historical body migration. Structure-only upgrade
|
||||
does not independently trigger body conversion or VACUUM; see the
|
||||
[database migration guide](../../development/database-migrations.md). Coordinated
|
||||
SQLite/files backups include the source metadata.
|
||||
|
||||
`MagicNoteSource` is shared by panel summaries and workspace records. Its callback
|
||||
`App.openNoteSource` reads `conversations.get`, then navigates through the workspace
|
||||
leave guard. Message navigation increases the mounted timeline range before
|
||||
scrolling and focusing. A missing message opens its conversation with a notice;
|
||||
a confirmed missing conversation disables that source bar's jump buttons. Other
|
||||
read errors remain retryable. No deleted-source flag is stored.
|
||||
|
||||
### Refresh and Validation
|
||||
|
||||
Both views use the same Main store and `magic-notes:changed` notification. The
|
||||
panel reloads search and selected detail when active, and ignores superseded
|
||||
responses. Search/loading state belongs to the mounted panel; input and selected
|
||||
note belong to App. A deleted selected note clears the selection and matching
|
||||
target ID while preserving draft text. Read and save failures have local recovery
|
||||
feedback. The full workspace retains its existing revision and dirty-editor rules.
|
||||
|
||||
After a successful save, only `after-save-auto` starts entry analysis using the
|
||||
returned `createdEntryId` and configured comment format. Analysis errors notify
|
||||
without undoing the save or recreating the entry. Other comment modes perform no
|
||||
panel analysis.
|
||||
|
||||
Regression cases are in `MagicNotesPanel.test.tsx`, `App.test.tsx`,
|
||||
`ChatTimeline.test.tsx`, `MagicNotesWorkspace.test.tsx`, workbar contract tests,
|
||||
database/storage tests and production preload/IPC integration tests.
|
||||
`tests/magic-notes-capture.electron.test.ts` mounts the real App with production
|
||||
preload and capture IPC, backed by SQLite/files. It validates existing/new-note
|
||||
saves, verbatim 502-message history, workspace/source navigation, native keyboard
|
||||
input in a narrow panel, database reopen and retention after source deletion,
|
||||
with zero model calls. Failed writes and external refresh retain their focused
|
||||
regression coverage; this Electron result does not claim every failure path.
|
||||
Repository test/typecheck failures and repaired migration fixtures are documented
|
||||
separately in [progress](./progress.md).
|
||||
|
||||
## Validation
|
||||
|
||||
- `magic-note-storage.test.ts` uses real files and SQLite for migration equality,
|
||||
@@ -476,6 +618,6 @@ daemon implementation update.
|
||||
coverage includes portal placement, keyboard navigation, dismissal, delete
|
||||
confirmation, target selection, revision failures and draft preservation.
|
||||
|
||||
Schema 38 and the file layout above govern current persistence; refresh
|
||||
Schema 48 adds source metadata to the schema 38 file-backed body layout; refresh
|
||||
notifications need no separate storage or network service. Dated validation
|
||||
results and remaining repository checks are owned by [progress](./progress.md).
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 会话联动界面设计
|
||||
|
||||
状态:已接入前端,真实 Electron 验收通过;全仓验证未全绿,结果见 [progress.md](./progress.md)。遵循根目录 [UI-DESIGN.md](../../../UI-DESIGN.md),需求和行为分别由 [PRD](./prd.md) 与 [逻辑设计](./logic-design.md)定义。独立工作区布局继续由根文档 13.7 节维护。
|
||||
|
||||
## 入口
|
||||
|
||||
| 位置 | 文案与动作 |
|
||||
| --- | --- |
|
||||
| 助手工作栏“+”菜单 | 增加“笔记”,打开或激活单个笔记 Tab。 |
|
||||
| 助手消息底部,复制旁 | 图标按钮“加入笔记”,进入该消息的采集预览。 |
|
||||
| 会话标题更多菜单 | “保存当前对话到笔记”,加载完整历史后进入整段采集预览;生成、采集加载或保存期间禁用。 |
|
||||
| 面板笔记标题旁 | “在完整工作区打开”,进入当前笔记;有待保存文字时先处理草稿。 |
|
||||
|
||||
左侧主导航中的“魔法笔记”继续打开独立工作区。紧凑面板通过工作栏“+ → 笔记”打开,左侧底部和聊天页头不再提供重复的“快速笔记”入口。
|
||||
|
||||
## 紧凑面板
|
||||
|
||||
面板按单列显示列表或笔记详情,不嵌入完整工作区的三栏布局。搜索位于列表顶部,“新建笔记”在列表之后;笔记行显示标题、置顶状态、两行摘要和更新时间。无笔记时提供新建动作;搜索无结果时保留搜索词并允许创建。
|
||||
|
||||
详情顶部提供返回列表、笔记标题和打开完整工作区,不显示“全局”标识。中间以可滚动记录摘要供浏览,每条文字摘要最多显示前 600 个字符,画布显示首页缩略图及页数;记录按钮可在完整工作区定位该记录。底部提供快速追加文字输入区与“加入笔记”按钮,画布编辑、待办管理和 AI 评论阅读仍在完整工作区。
|
||||
|
||||
从会话触发时,面板切到采集编辑视图:
|
||||
|
||||
```text
|
||||
加入笔记
|
||||
来自:项目 / 会话标题 · 单条回答或当前对话
|
||||
保存范围:用户与助手正文,不含推理、工具日志和附件文件
|
||||
|
||||
[可编辑文字预览]
|
||||
|
||||
保存到
|
||||
[搜索或选择已有笔记]
|
||||
[新建笔记 → 标题输入]
|
||||
|
||||
[取消] [加入笔记]
|
||||
```
|
||||
|
||||
范围说明随单条或整段来源调整。预览初始包含完整待保存文字,可滚动编辑。新建目标标题默认取会话标题前 100 个字符,可修改;已有目标与新建目标使用共享 `SegmentedControl` 切换。采集内容为空或超限、缺少目标、新建标题为空或超限时就地提示,并禁用保存;普通新建允许创建空笔记。
|
||||
|
||||
预览和快速追加输入框通过无障碍名称“待加入内容”标识,不再显示重复标签。提交区仅显示校验或保存错误,不显示“全局”。提交与来源跳转按钮组均居右,可在窄面板换行;来源没有项目名时省略项目及分隔符,不补写“全局”。
|
||||
|
||||
读取失败显示就地重试按钮;保存失败保留输入并在提交区显示错误,用户可再次点击“加入笔记”。成功通过应用通知反馈,切回目标笔记详情,滚动定位、聚焦并高亮新增记录约 2.4 秒。仅“保存后自动评论”模式发起后续分析,分析失败另行通知,已保存内容保留。
|
||||
|
||||
## 来源与可访问性
|
||||
|
||||
记录摘要和完整工作区的已保存记录均显示“来自会话”来源条,内容含保存时标题、项目及采集时间。单条来源提供返回会话和定位原消息按钮,整段来源提供返回会话按钮。点击后确认原会话不存在才显示对应文案并禁用跳转;其他错误提供就地重试。来源条与正文分开,编辑正文时仍可识别来源。
|
||||
|
||||
Tab 使用工作栏现有的共享页签、焦点和关闭模式。搜索有持久标签,列表用可键盘访问的按钮;打开采集后聚焦预览,取消返回触发按钮,触发按钮已不可见时尝试返回会话标题菜单按钮。新建标题出现后聚焦输入框。面板中的 Escape 取消当前草稿,或从详情返回列表;需要放弃输入时先显示草稿确认。确认框初始聚焦“继续编辑”,限制 Tab 焦点范围,Escape 保留草稿并关闭确认。
|
||||
|
||||
面板服从工作栏现有宽度和停靠规则,以单列切换列表、详情和采集编辑。按钮组可换行,文字摘要保留换行并折行长文本;Markdown 和代码以原始文字显示。头部与快速追加区固定,中间内容独立滚动;完整预览输入区可纵向调整大小。配色、按钮、焦点环、通知和确认使用共享组件与令牌。真实 Electron 已在实测 214px 面板验证无横向溢出,以及原生键盘返回列表、打开笔记和追加保存。
|
||||
@@ -1,5 +1,15 @@
|
||||
# 模型连接请求定制进度
|
||||
|
||||
## 2026-09-27:窄容器协议显示
|
||||
|
||||
连接管理布局改用命名容器查询,不超过 `640px` 时列表和表单上下排列;移除原先仅在
|
||||
窗口不超过 `720px` 时切换的规则。LLM、向量连接及角色编辑共用同一布局样式。
|
||||
Windows Electron 验证覆盖 `680/720/721/740/960/1180px` 窗宽、短窗口、中英文及浅深
|
||||
主题;另在 `1180px` 窗口中单独缩窄设置面板,确认布局由实际容器宽度决定。
|
||||
协议文字完整、表单无横向溢出,原生 Tab 与方向键可选择并还原协议。没有保存配置或发送
|
||||
模型请求。验证命令、截图说明和未测范围见
|
||||
[窄容器布局修复记录](../../quality/responsive-controls-review-2026-09-27.md)。
|
||||
|
||||
## 2026-09-20:模型设置可用性修复
|
||||
|
||||
- 模型接口 URL 保存失败时按连接名称显示错误,URL 字段同步提供 `aria-invalid` 和就地
|
||||
|
||||
@@ -125,3 +125,25 @@ Main 的 `toPublicSettings` 复用 `resolveDeepSeekHarnessModelProfile`,只在
|
||||
`source: 'profile'` 描述来源分支,不代表凭据来源:仅环境密钥或旧环境变量可能仍影响该
|
||||
GoodBuddy 连接,但不会被误报为完整管理员预置。连接解析不代替真实模型请求或可用性测试。
|
||||
UI 的未保存兼容候选只作草稿预览,保存后继续以 Main 返回的投影为准。
|
||||
|
||||
## 10. Settings IPC Ownership
|
||||
|
||||
[model-settings-ipc.ts](../../../src/main/model-settings-ipc.ts) registers the
|
||||
11 model/runtime settings handlers: public settings, customization reads and
|
||||
updates, native inventory, settings updates, workspace/file selection, runtime
|
||||
detection, config actions, and model/runtime connection tests. It owns sender
|
||||
checks, shared schema parsing, config-file metadata, and activation rollback.
|
||||
|
||||
[ipc.ts](../../../src/main/ipc.ts) calls this registrar at the original position
|
||||
and retains tracked registration, the shared settings-update queue, runtime
|
||||
activation, conversation-selection repair, and project execution-space resolution.
|
||||
These operations are injected as narrow callbacks. Runtime activation still
|
||||
cancels affected expert-team requests; failed activation restores the previous
|
||||
settings and reactivates them before the next queued update can run. Cleanup waits
|
||||
for the tracked handler promises. Usage recording, runtime refresh subscriptions,
|
||||
and runtime/Agent lifecycle ownership remain in the composition root.
|
||||
|
||||
The integration tests in [ipc.test.ts](../../../src/main/ipc.test.ts) exercise the
|
||||
production registrar through `registerIpcHandlers`, including registration order,
|
||||
trusted senders, serialized updates, rollback failures, cleanup waiting for reload,
|
||||
native inventory resolution, and connection/config actions.
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
`aria-invalid` 并关联就地错误,避免只显示无法定位的“Invalid URL”。
|
||||
- LLM 模型连接的分类标题和模型类型分段页签在内容滚动时共同粘附在内容区顶部;左侧连接
|
||||
列表仍可独立滚动。
|
||||
- 连接列表和编辑表单按自身容器宽度切换左右或上下排列,断点及列表滚动上限遵循
|
||||
[设置中心布局规则](../../../UI-DESIGN.md#138-设置中心)。窄窗口中收起的接口协议选择框
|
||||
保留完整中英文当前值,不能依赖展开选项才能辨认协议。
|
||||
- 每个已保存或正在编辑的 LLM 连接提供“复制连接”。复制会生成新的连接名称和 ID,保留
|
||||
地址、模型、协议、请求定制、认证方式及当前草稿 API Key。已保存的 API Key 通过 Main
|
||||
进程的加密凭据复制路径保存,不回传到 Renderer;原连接和默认连接不改变。
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Obsidian 本机仓库接入
|
||||
|
||||
GoodBuddy 通过内置 MCPVault 访问本机 Obsidian 仓库,供会话读取、搜索和修改笔记。功能默认关闭;默认范围为本机已注册的全部仓库,也可指定一个文件夹。接入直接访问本地文件,不要求 Obsidian 正在运行或安装插件。
|
||||
|
||||
当前源码已实现主服务、设置 IPC、HTTP 网关和直连工具提供器。自动化覆盖 19 个共享工具;Windows 真实 Electron 设置操作、原生目录对话框取消、键盘开关、浅深主题窄窗口,以及 OpenCode / Continue 从聊天输入框发起的实际笔记读写均已通过。Windows x64 解包应用也通过设置到随包 MCPVault 的连接与保存验证,测试限制了子进程 Node 网络 API 和 Chromium 代理。物理断网、安装器及其他目标系统仍待验收;命令、模型调用次数和全量检查阻塞见[进度](./progress.md)。
|
||||
|
||||
## 文档职责
|
||||
|
||||
| 文档 | 唯一职责 |
|
||||
| --- | --- |
|
||||
| [产品需求](./prd.md) | 用户问题、范围、稳定需求 ID 和产品验收 |
|
||||
| [用户故事](./user-stories.md) | 使用场景及 Given/When/Then 验收 |
|
||||
| [功能逻辑](./logic-design.md) | 范围决策、状态转换、失败和取消规则 |
|
||||
| [技术设计](./technical-design.md) | 模块、契约、打包、验证,以及独立章节中的 UI 设计 |
|
||||
| [进度](./progress.md) | 已核对的实现事实、剩余工作和验证记录 |
|
||||
|
||||
界面仅在现有 MCP 设置中增加一个卡片,因此不另设 `ui-design.md`。技术设计的“UI 设计”章节承担信息结构、控件、反馈、无障碍和窄窗口适配职责;全局视觉规范仍由 [UI-DESIGN.md](../../../UI-DESIGN.md)定义。
|
||||
|
||||
## 术语与依赖
|
||||
|
||||
| 术语 | 含义 |
|
||||
| --- | --- |
|
||||
| 仓库(vault) | 一个本机笔记文件夹;显示名称不作为调用身份 |
|
||||
| 注册仓库 | 本机 Obsidian 的 `obsidian.json` 中登记的仓库,包含当前未打开的仓库 |
|
||||
| `vaultPath` | 保存的范围设置;空字符串代表注册仓库集合,非空代表指定文件夹 |
|
||||
| `vaultId` | 一次工具调用选择的仓库标识,与文件相对路径分开传递 |
|
||||
| MCPVault | npm 包 `@bitbonsai/mcpvault`,本功能固定使用 `0.16.0` |
|
||||
| 草稿 | 设置页面尚未保存的范围,可单独测试 |
|
||||
|
||||
共享目录包含 `obsidian_list_vaults` 和 18 个加 `obsidian_` 前缀的上游工具。连接测试显示的上游工具数仍为 18。保存范围、切换开关或修改分配会撤销此前含 Obsidian 的请求授权,规则见[功能逻辑](./logic-design.md#授权撤销与清理)。
|
||||
|
||||
Node 启动环境复用[工具执行环境](../local-tool-environment/README.md),不建立第二套解释器安装或下载配置。接入目标为直连模型、GoodBuddy 管理的本机 OpenCode 和 Continue;DeepSeek Harness 与远程执行路径沿用现有不可用边界。本功能的仓库位于桌面所在设备。
|
||||
@@ -0,0 +1,60 @@
|
||||
# 功能逻辑
|
||||
|
||||
## 状态维度
|
||||
|
||||
启用状态、Runtime 分配、已保存范围和页面草稿分别管理。启用状态不表示服务已连接,测试成功也不表示会话可调用。会话使用已保存范围;测试使用本次提交的草稿。
|
||||
|
||||
测试状态为未测试、测试中、成功或失败。修改草稿使先前测试结果失效;测试结果只描述该次请求,不持久化为服务健康状态。
|
||||
|
||||
## 规则与追溯
|
||||
|
||||
| ID | 规则 | 需求 / 故事 |
|
||||
| --- | --- | --- |
|
||||
| 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-5 | 范围解析失败使本次请求失败,不跳过失效注册条目,不选择替代仓库。 | FR-8;US-7 |
|
||||
| LR-6 | 测试仅发现仓库并列举工具,逐仓库连接;工具数按上游名称去重,不按仓库数相乘。 | FR-7;US-1、US-2 |
|
||||
| LR-7 | 操作结束或取消后关闭本次 MCP 连接和子进程;取消不承诺撤销已落盘写入。 | FR-8;US-8 |
|
||||
| LR-8 | 保存范围、切换开关和修改分配在输入校验后、持久化前撤销含 Obsidian 的旧请求授权并取消在途调用;网关销毁等待活动调用清理。 | FR-10;US-12 |
|
||||
|
||||
## 仓库决策
|
||||
|
||||
| 条件 | 结果 |
|
||||
| --- | --- |
|
||||
| 指定路径非空 | 验证为可访问的绝对目录,得到单仓库范围 |
|
||||
| 默认范围且注册信息有效 | 使用全部注册条目,保留各自注册 ID |
|
||||
| 注册文件不可读、无条目、条目无路径或任一目录不可访问 | 返回对应错误;用户可修复注册信息或选择文件夹 |
|
||||
| 范围只有一个仓库且请求省略 ID | 使用唯一仓库 |
|
||||
| 范围超过一个仓库且请求省略 ID | 拒绝调用,要求明确 ID |
|
||||
| 请求提供 ID 且属于当前范围 | 使用该 ID 对应仓库 |
|
||||
| 请求 ID 不属于当前范围 | 拒绝调用,不回退到唯一仓库或首个仓库 |
|
||||
|
||||
工具清单描述与实际文件操作分开。服务可用首个仓库读取固定版本的工具 schema,这不赋予首个仓库任何默认写入地位。跨仓库搜索或修改由调用方对明确的仓库 ID 分别发起请求,不把一次请求广播到全部仓库。
|
||||
|
||||
## 设置转换
|
||||
|
||||
| 动作 | 草稿与结果 | 持久化影响 |
|
||||
| --- | --- | --- |
|
||||
| 选择文件夹 | 更新草稿,切换为指定文件夹,清除旧测试结果 | 无 |
|
||||
| 取消文件夹选择 | 保留原草稿 | 无 |
|
||||
| 切换范围或编辑路径 | 清除旧结果和关联错误;指定文件夹模式空路径时报错 | 无 |
|
||||
| 测试 | 使用提交时的草稿;忙碌期间禁止重复提交,结束后显示结果或错误 | 无 |
|
||||
| 保存成功 | 使用返回快照显示已保存范围,结束当前草稿编辑状态 | 仅范围 |
|
||||
| 保存失败 | 保留草稿并显示错误,可修改后重试 | 已保存范围不变 |
|
||||
| 启用、关闭或调整分配 | 更新对应状态,草稿不自动保存 | 仅对应状态 |
|
||||
|
||||
上表仅列持久化影响;涉及授权的副作用按 LR-8 执行。连接测试不提供独立取消按钮,其临时连接不持有会话授权。
|
||||
|
||||
## 授权撤销与清理
|
||||
|
||||
Main 对范围保存、开关和分配请求先验证来源及输入,再撤销现存的含 Obsidian 授权,随后保存设置。提交相同值也触发撤销。输入无效时在撤销前返回错误;持久化失败时原设置保留,但旧授权已经失效。草稿编辑、文件夹选择和连接测试均不触发此撤销。
|
||||
|
||||
撤销按整个请求授权执行。若同一个授权同时含 Obsidian 和知识库等能力,该授权整体失效;独立的不含 Obsidian 授权保留。后续请求按当前保存的范围、开关、分配和工作模式重新取得授权;关闭后不再签发 Obsidian 能力。
|
||||
|
||||
撤销会立即移除旧授权并发出取消信号,设置响应不等待子进程完全退出。工具调用负责在结束路径关闭 transport 和 client;网关销毁则等待活动调用完成清理,包含先前已撤销但仍在关闭的调用。取消过程中已落盘的内容保留。
|
||||
|
||||
## 完整性检查
|
||||
|
||||
规则已覆盖首次使用、草稿测试、保存、范围恢复、单仓库、多仓库、失效路径、旧配置、工作模式,以及设置变更对旧授权和在途调用的影响。当前没有待定的产品决策。实现及各层自动化、Electron 与安装包验证状态见[进度](./progress.md)。
|
||||
@@ -0,0 +1,36 @@
|
||||
# 产品需求
|
||||
|
||||
## 用户问题与目标
|
||||
|
||||
用户需要在会话中查找本机已有的 Obsidian 笔记,并将整理结果写回指定仓库。手动配置 Node、MCP 命令或 Obsidian 插件会增加首次使用步骤。接入应随应用提供依赖,支持先测试范围,再决定保存和启用。
|
||||
|
||||
## 功能需求
|
||||
|
||||
以下 ID 在后续实施中保持稳定。场景验收见[用户故事](./user-stories.md),行为决策见[功能逻辑](./logic-design.md)。
|
||||
|
||||
| ID | 产品要求 |
|
||||
| --- | --- |
|
||||
| FR-1 | 首次使用及旧设置缺少 Obsidian 字段时默认关闭。保存范围、测试连接和启用是独立操作,不互相隐式触发。 |
|
||||
| FR-2 | 默认 `vaultPath` 为空,访问本机已注册的全部仓库,包含未打开的仓库。用户可改选一个本机文件夹,也可恢复默认范围。 |
|
||||
| FR-3 | 工具调用使用仓库 ID 定位。可用仓库超过一个时必须明确指定 `vaultId`;缺失或无效时返回错误,不按顺序、名称或最近使用情况选择写入目标。 |
|
||||
| FR-4 | 提供 MCPVault 0.16.0 的全部 18 个上游工具及其参数能力。不得增加 Obsidian 专用只读设置、工具过滤或额外审批;应用现有 Ask/Execute 工作模式照常生效。 |
|
||||
| FR-5 | MCPVault 及完整运行依赖随安装包交付,使用现有托管 Node,首次调用无需联网安装 npm 包,也无需用户安装系统 Node。 |
|
||||
| FR-6 | 不依赖正在运行的 Obsidian、社区插件、REST 服务或 API 密钥。指定文件夹可绕过注册信息,不要求该文件夹已在 Obsidian 注册。 |
|
||||
| FR-7 | 功能关闭时也可测试未保存的草稿。测试返回仓库名称、路径和上游工具数,不修改笔记、不保存范围、不启用功能。 |
|
||||
| FR-8 | 范围无效、仓库不可访问、内置依赖缺失或工具执行失败时说明原因并保留草稿。取消文件夹选择不清空输入;会话调用取消后释放对应子进程。 |
|
||||
| FR-9 | 沿用内置 MCP 的启用与 Runtime 分配方式。设置范围属于桌面端全局配置,具体会话仍受既有能力分配和工作模式约束。 |
|
||||
| FR-10 | 提交保存范围、切换 Obsidian 开关或修改分配时,撤销此前含 Obsidian 的请求授权并取消其在途调用,避免继续使用旧范围或旧分配。应用关闭须等待活动工具清理;不回滚已经落盘的修改。 |
|
||||
|
||||
“离线”指本地 MCP 服务的启动和文件操作不需要下载依赖;模型服务是否联网由模型配置决定。工具返回的笔记内容会进入当前会话处理流程,不能将本地接入描述为模型推理也完全离线。
|
||||
|
||||
## 范围与非目标
|
||||
|
||||
本期接入原有仓库文件,不复制为 GoodBuddy 知识库,不提供同步服务、仓库管理器或 Obsidian 编辑器。附件相关能力按上游工具提供的范围开放,不承诺未包含在上游中的附件编辑能力。文件删除等参数沿用上游语义;上游参数校验不构成新增产品审批。
|
||||
|
||||
注册仓库发现不扫描整块磁盘,不推断最近活动仓库。接入支持直连模型、GoodBuddy 管理的本机 OpenCode 和 Continue;DeepSeek Harness 和远程执行路径保持不可用。远程 Host 仓库发现、跨设备同步和仓库迁移不在本期范围。
|
||||
|
||||
## 产品验收
|
||||
|
||||
发布前须完成[技术设计中的验证矩阵](./technical-design.md#验证与实施顺序),并在[进度](./progress.md)记录真实结果。验收至少覆盖:关闭状态测试草稿、保存后重新进入设置、恢复全部仓库、多仓库明确选择、实际读取和写回、工作模式约束,以及安装包断网启动。
|
||||
|
||||
18 个上游工具按名称和参数契约逐项比对;加上仓库发现,共有 19 个共享工具。自动化验证分别覆盖注册 IPC、HTTP 网关和直连工具提供器,安装包及完整 Electron 界面仍单独验收。配置提交时还须验证旧授权立即失效、在途调用收到取消,以及网关销毁等待子进程清理完成。
|
||||
@@ -0,0 +1,109 @@
|
||||
# 实施进度
|
||||
|
||||
## 当前状态
|
||||
|
||||
2026-09-27:主服务、设置 UI、Main IPC、HTTP 网关和直连工具提供器已实现,共享目录共 19 个工具。Windows 真实 Electron 设置操作与 OpenCode / Continue 聊天输入框读写已通过;Windows x64 解包应用通过受限网络下的设置连接和保存验证。物理断网、安装器及其他系统验收仍待完成,全量检查存在下述无关失败;当前状态不表示已发布。
|
||||
|
||||
| 阶段 | 已核对事实 | 剩余工作 |
|
||||
| --- | --- | --- |
|
||||
| TS-1 配置与服务 | 默认关闭、范围独立保存、注册发现及 ID 路由已实现;复制后的真实 MCPVault 已用于文件操作和错误传播测试 | 跨平台安装包验证归入 TS-4 |
|
||||
| TS-2 设置 UI | 真实 Electron 窗口中验证草稿测试不保存、不启用,保存和错误保留输入,Windows 原生目录对话框取消、键盘开关、19 工具展开及浅深主题窄窗口;修复表单横向撑宽 | 其他系统原生窗口验收 |
|
||||
| TS-3 网关与 IPC | 19 工具、Ask/Execute、撤销、取消及清理自动化通过;OpenCode / Continue 真实会话及聊天输入框读写通过 | 安装后的聊天读写与其他系统验收 |
|
||||
| TS-4 打包与交付 | 当前源码 `build:bundle` 和 Windows x64 `--dir` 打包通过;实际 `app.asar` UI 连接随包 MCPVault 并保存,子进程加载网络 API 限制,PATH 仅含 System32 | 物理断网或 OS 级隔离、安装器、macOS/Linux/其他架构原生资源验收 |
|
||||
|
||||
DeepSeek Harness 和远程执行保持既有不可用边界。本轮产品代码仅修改 Obsidian 表单布局,没有改变 Agent 或远程 Runtime 路径。
|
||||
|
||||
## 提交快照验证
|
||||
|
||||
提交前从 HEAD 与 Obsidian 暂存内容生成独立目录,排除并行开发的原生终端、DSH 升级、IPC 拆分与 Magic Notes 改动。锁文件从 HEAD 重新生成,仅添加固定版本 MCPVault 及其依赖,没有升级或删除既有依赖项;独立目录安装该锁文件后验证。
|
||||
|
||||
`npm run typecheck`、`npm run lint` 和 `npm run build` 均通过。聚焦服务、Electron、网关、直连工具、配置、Continue 适配器及共享 UI 的 11 个文件共 237 项通过;完整 IPC、设置面板与构建测试的 3 个文件共 303 项通过。合计 14 个文件、540 项测试通过。本次没有重跑全量套件、安装包或真实模型调用,新增模型调用为 0。下文全仓失败记录来自此前混合工作区,不代表此提交快照的类型检查或构建结果。
|
||||
|
||||
## 2026-09-27 验收补充
|
||||
|
||||
以下结果来自独立临时应用配置、项目和 vault。模型凭据通过临时复制的 `runtime-settings.json` 与配套 `Local State` 在 Electron Main 中解密;源配置和用户 vault 均未修改。测试结束删除临时配置、凭据副本和笔记;Windows 解包构建与检查日志保留在系统临时目录的 `opencode` 下。没有提交、发布、安装器或 portable 构建。
|
||||
|
||||
| 验证项 | 实际结果与边界 |
|
||||
| --- | --- |
|
||||
| 聚焦测试 | 8 文件、133 项通过,含 `obsidian-electron.test.ts` 和 Continue 适配器;IPC 定向 5 项通过、152 项跳过。布局修正后 MCP 设置、共享 UI 与 Electron 服务共 43 项通过 |
|
||||
| `npm run build` / `npm run typecheck` | `build` 停在类型检查;末次类型检查仍为 SSH pool、SSH directory browser 和 SSH terminal 测试替身缺少 `forwardLoopback`。本轮脚本类型错误已修复;未修改并行开发的 SSH 测试 |
|
||||
| `npm run build:bundle` | 当前源码通过,布局修改后再次通过;不能据此将 `npm run build` 记为通过 |
|
||||
| `npm run lint` | 当前工作区通过 |
|
||||
| 分项类型检查 | `npx tsc --noEmit -p tsconfig.web.json` 与 `npx tsc --noEmit -p tsconfig.agent.json` 均通过 |
|
||||
| `npm test` | 首次 200 秒超时;随后后台运行完整结束,691.25 秒,444 文件通过、3 失败、11 跳过;5,274 测试通过、3 失败、83 跳过 |
|
||||
| 全量失败定位 | `activity-history-io.test.ts`:迁移重复添加 `source_json`;`magic-notes-capture.electron.test.ts`:宽度 0、溢出 82 的布局断言;`build-release.test.ts`:fixture 缺少 `@deepseek-ai/dsh/package.json`。均属于并行改动范围,本轮保留原状 |
|
||||
| 真实设置 UI | [Electron runner](../../../build/run-obsidian-electron-acceptance.cjs) 导入生产 Main、使用生产 Preload/Renderer 和原生鼠标键盘,覆盖测试 18 个上游工具、保存、错误、目录对话框取消、Space 启用和展开 19 个工具。未替换 IPC 或 ObsidianService |
|
||||
| 布局缺陷与修复 | 原表单复用了横向 `.capability-diagnostic`;759 像素内容视口中卡片右边界达到 1116 像素。改为 Obsidian 纵向表单、按钮及路径换行后,普通状态右边界 691,含测试结果和工具展开时 732;浅深主题均无卡片内部横向溢出 |
|
||||
| 真实 Runtime 会话 | [Runtime runner](../../../tests/obsidian-runtime-acceptance.ts) 使用真实 OpenCode / Continue、生产 HTTP 网关和 MCPVault,断言工具事件、模型返回和实际文件。两轮各 Runtime 各 3 次模型 HTTP 请求,共 12 次;第二轮使用 UI 保存并启用的能力快照 |
|
||||
| 完整聊天输入框 | `--chat` 中 OpenCode 实际发送、读写及回复验证通过,计 4 次模型 HTTP 请求;该次随后在 Continue 菜单定位处失败。修正选择器后 `--chat --continue-only` 通过,同样 4 次请求。合计本轮 20 次真实模型 HTTP 请求,其他启动、菜单和凭据准备失败均为 0 次。聊天证据来自开发生产 bundle;没有将独立 Runtime harness 冒充聊天 UI |
|
||||
| Windows x64 解包应用 | 正常 electron-builder 配置以 `--dir --publish never` 生成 `GoodBuddy.exe` 和 `app.asar`,含解包 MCPVault 及 `windows-trash.exe`。[打包 runner](../../../build/run-obsidian-packaged-acceptance.cjs) 通过 CDP 驱动真实页面控件,设置连接返回 18 工具,保存快照匹配临时 vault;托管 node shim 指向该打包 EXE |
|
||||
| 网络限制 | 打包 EXE 的 PATH 仅含 System32,Chromium 指向不可用代理;Electron 启动会移除 `NODE_OPTIONS`,首次尝试未加载 guard。随后仅在测试 Main 的本机 inspector 中恢复该变量,审计确认真实 MCPVault 子进程加载 guard,入口为 `app.asar.unpacked/.../server.js`。Node guard 拒绝非 loopback socket 和 fetch;不覆盖 OS 全部联网方式,不算物理断网或防火墙隔离 |
|
||||
|
||||
复现命令如下。打包命令须使用真正的 Node 可执行文件;本机 PATH 中的 Electron Node shim 会使 electron-builder CLI 误读 argv。`--live` 和 `--chat` 会调用真实文本模型;默认读取当前用户 GoodBuddy 模型配置,也可用 `GOODBUDDY_ACCEPTANCE_SETTINGS` 指向含配套 `Local State` 的测试配置目录中的设置文件。
|
||||
|
||||
```powershell
|
||||
npx vitest run src/shared/obsidian-tools.test.ts src/main/obsidian/obsidian-service.test.ts src/main/obsidian/obsidian-electron.test.ts src/main/agent/knowledge-mcp-gateway.obsidian.test.ts src/main/agent/model-tool-provider.obsidian.test.ts src/main/capabilities/capability-service.test.ts src/renderer/src/McpSettingsSection.test.tsx src/main/agent/continue-host-adapter.test.ts
|
||||
npx vitest run src/main/ipc.test.ts -t Obsidian
|
||||
npx vitest run src/renderer/src/McpSettingsSection.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx src/main/obsidian/obsidian-electron.test.ts
|
||||
npm run build:bundle
|
||||
node build/run-obsidian-electron-acceptance.cjs
|
||||
node build/run-obsidian-electron-acceptance.cjs --live
|
||||
node build/run-obsidian-electron-acceptance.cjs --chat
|
||||
node build/run-obsidian-electron-acceptance.cjs --chat --continue-only
|
||||
# 以下占位路径替换为本机 Node、临时构建目录及打包 EXE。
|
||||
& "<node.exe>" build/run-electron-builder.cjs --win --x64 --dir --publish never --config.directories.output="<temporary-output>"
|
||||
node build/run-obsidian-packaged-acceptance.cjs "<temporary-output>/win-unpacked/GoodBuddy.exe"
|
||||
```
|
||||
|
||||
本机打包输出为 `%TEMP%/opencode/obsidian-package-acceptance/win-unpacked`;全量日志为 `%TEMP%/opencode/obsidian-full-tests.log` 与 `.err`。以下保留 2026-09-26 的历史记录,不能用其中旧阻塞覆盖本节最新结果。
|
||||
|
||||
本轮文档 deai-writing 扫描 6 文件,0 个阻断项、12 个复核项;复核项均为测试条件、状态或能力范围限定,通读后保留。`git diff --check` 通过。
|
||||
|
||||
## 源码与测试证据
|
||||
|
||||
2026-09-26 核对以下源码与测试。本轮重新执行的结果见验证记录;此前构建结果保留原来源,不作为当前工作区构建通过的证明。
|
||||
|
||||
| 证据 | 可支持的结论 |
|
||||
| --- | --- |
|
||||
| [obsidian-service.ts](../../../src/main/obsidian/obsidian-service.ts) 与[服务测试](../../../src/main/obsidian/obsidian-service.test.ts) | 注册发现、真实复制包后的读写、许可证与资源、工具集合、多仓库 ID 以及取消后进程清理 |
|
||||
| [capability-service.test.ts](../../../src/main/capabilities/capability-service.test.ts) | 范围持久化、旧字段缺省和拒绝 `readOnly` 参数 |
|
||||
| [ipc.test.ts](../../../src/main/ipc.test.ts) | 设置组件调用正式注册的 IPC 处理器、真实配置服务及 MCPVault;Electron 传输和目录对话框由测试替代,不是安装后完整界面验证 |
|
||||
| [共享工具测试](../../../src/shared/obsidian-tools.test.ts) | 19 工具集合和 18 个上游工具的字段、默认值、必填项与约束对应 |
|
||||
| [HTTP 网关测试](../../../src/main/agent/knowledge-mcp-gateway.obsidian.test.ts) | 实际 HTTP MCP 发现 19 工具,真实多仓库读写、删除及错误返回;另覆盖撤销、取消和等待 transport 关闭 |
|
||||
| [直连提供器测试](../../../src/main/agent/model-tool-provider.obsidian.test.ts) | 通过实际服务执行全部 19 个工具并核验文件,包含笔记移动、二进制附件移动和删除;覆盖 Ask、失效令牌、原生错误及关闭清理 |
|
||||
| [index.ts](../../../src/main/index.ts)、[ipc.ts](../../../src/main/ipc.ts) 与[capability-ipc.ts](../../../src/main/capabilities/capability-ipc.ts) | 正式启动实例化、依赖注入和 IPC 注册;能力处理器位于 `capability-ipc.ts`,范围保存、开关及分配提交在持久化前撤销旧 Obsidian 请求授权 |
|
||||
| [package-mcpvault.ts](../../../src/main/obsidian/package-mcpvault.ts) 与[构建配置](../../../electron.vite.config.ts) | 构建期递归复制运行依赖,并固定上游版本 |
|
||||
|
||||
## 验证记录
|
||||
|
||||
| 验证项 | 2026-09-26 结果及范围 |
|
||||
| --- | --- |
|
||||
| 聚焦生产组件自动化 | 本轮 7 个文件、132 项测试通过,包含 Continue 适配器;另运行 IPC 的 Obsidian 用例,5 项通过、151 项跳过 |
|
||||
| `npm run build` | 此前主实施方反馈通过;本轮未重跑 |
|
||||
| `npm run typecheck` | 首次运行报原生终端依赖未列入项目的 TS6307;并行改动后复跑,该错误已消失,当前阻塞为 `native-terminal-client.test.ts:33` 的 TS2352 类型转换错误。未修改这部分并行开发代码 |
|
||||
| `npm run lint` | 本轮通过;最终测试文件改动另行定向检查 |
|
||||
| `npm test` | 本轮在 200 秒超时;结束前出现数据库迁移、Magic Notes 等失败。全量套件没有通过结论;Continue 适配器定向复跑通过 |
|
||||
| Electron 托管 Node 冒烟 | 将服务测试改为真实 Electron 可执行文件,以 `ELECTRON_RUN_AS_NODE=1` 启动复制后的包,10 项通过。Windows 子进程 PATH 仅保留测试 shim 与 System32;覆盖发现、读写、局部修改、属性、回收站和取消清理。没有隔离网络,物理断网安装包验收仍待完成 |
|
||||
| 安装后完整 Electron UI 与跨平台验收 | 待完成;组件自动化使用替代 Electron 传输,不能覆盖原生窗口及安装环境 |
|
||||
|
||||
2026-09-26 本次文档修订新增 FR-10、LR-8、US-12,保留原有 ID。交付检查结果:
|
||||
|
||||
| 文档检查 | 结果 |
|
||||
| --- | --- |
|
||||
| 相对链接及锚点 | 6 份功能文档的 53 个链接、4 份跨功能文档新增的 5 个引用通过 |
|
||||
| 稳定 ID 与工具清单 | FR-1~FR-10、US-1~US-12、LR-1~LR-8、TS-1~TS-4 唯一声明通过;18 个上游工具和 19 个共享工具映射一致 |
|
||||
| deai-writing 审校 | 功能文档 0 个阻断项、11 个复核项,逐项核对后保留状态和能力边界限定;跨功能新增段落单独扫描,0 个阻断项、0 个复核项 |
|
||||
| 差异与空白 | `git diff --check` 通过;临时脚本另检查未跟踪的功能文档,无行尾空白或旧网关接入状态 |
|
||||
|
||||
本轮修改服务测试和验证记录;测试使用独立临时仓库,未修改用户笔记或应用设置。
|
||||
|
||||
最终定向 ESLint 和 `git diff --check` 通过。README 与本进度文档经 deai-writing 扫描,0 个阻断项、3 个复核项;复核项均描述验收范围或草稿状态,保留事实限定。
|
||||
|
||||
聚焦测试复现入口如下;这些命令用于定位相关用例,不是全量通过证明:
|
||||
|
||||
```powershell
|
||||
npx vitest run src/shared/obsidian-tools.test.ts src/main/obsidian/obsidian-service.test.ts src/main/agent/knowledge-mcp-gateway.obsidian.test.ts src/main/agent/model-tool-provider.obsidian.test.ts src/main/capabilities/capability-service.test.ts src/renderer/src/McpSettingsSection.test.tsx
|
||||
npx vitest run src/main/ipc.test.ts -t Obsidian
|
||||
```
|
||||
|
||||
剩余验收以本文顶部阶段表为准,按[验证矩阵](./technical-design.md#验证与实施顺序)补充物理断网、安装后及其他目标系统证据。配置变更触发取消与销毁等待的具体语义以[功能逻辑](./logic-design.md#授权撤销与清理)为准。
|
||||
@@ -0,0 +1,103 @@
|
||||
# 技术设计
|
||||
|
||||
本文负责实现架构及 UI 设计,落实 [FR-1~FR-10](./prd.md) 和 [LR-1~LR-8](./logic-design.md)。实现与验证状态统一在[进度](./progress.md)记录。
|
||||
|
||||
## 模块边界
|
||||
|
||||
| 层 | 职责与源码 |
|
||||
| --- | --- |
|
||||
| Renderer | [McpSettingsSection.tsx](../../../src/renderer/src/McpSettingsSection.tsx) 编辑草稿、显示已保存范围和测试结果;不直接访问文件系统或启动 Node |
|
||||
| Shared / Preload | [capability-contracts.ts](../../../src/shared/capability-contracts.ts)、[contracts.ts](../../../src/shared/contracts.ts)、[ipc-channels.ts](../../../src/shared/ipc-channels.ts) 和 [preload/index.ts](../../../src/preload/index.ts) 定义窄接口 |
|
||||
| 配置服务 | [capability-service.ts](../../../src/main/capabilities/capability-service.ts) 保存范围,沿用内置 MCP 的启用和分配状态 |
|
||||
| Main 服务 | [obsidian-service.ts](../../../src/main/obsidian/obsidian-service.ts) 发现仓库、连接上游、列工具和调用工具 |
|
||||
| 会话网关 | [knowledge-mcp-gateway.ts](../../../src/main/agent/knowledge-mcp-gateway.ts) 提供 HTTP MCP、仓库发现、目标路由、请求授权撤销及活动调用清理 |
|
||||
| 共享目录与直连 | [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 重载和生命周期管理,并在会话入口按保存设置及工作模式签发请求授权。
|
||||
|
||||
## 配置与 IPC
|
||||
|
||||
`obsidianSettingsSchema` 是严格对象,仅有 `vaultPath`:字符串,去除首尾空白,最大 4096 字符,默认 `''`。不接收 `readOnly`。持久化范围为桌面全局,沿用 CapabilityService 的存储,不增加数据库或配置副本。
|
||||
|
||||
| 接口 | 输入与结果 | 副作用 |
|
||||
| --- | --- | --- |
|
||||
| `updateObsidianSettings` | `{ vaultPath }` → `CapabilitySnapshot` | 先撤销旧 Obsidian 请求授权,再保存范围;启用与分配值不变 |
|
||||
| `testObsidianConnection` | 草稿 `{ vaultPath }` → `{ vaults, toolCount }` | 临时连接,结束即清理,不保存设置 |
|
||||
| `selectObsidianVault` | 无输入 → 目录字符串或 `null` | Main 打开系统目录选择器,取消返回 `null` |
|
||||
|
||||
`vaults` 的元素为 `{ id, name, path }`。Main 处理器验证调用来源和共享 schema。范围保存只验证设置形状;目录存在性由发现或测试校验,保存成功不能显示为连接成功。已有配置缺少新字段时补默认值,其他能力设置保持原值。
|
||||
|
||||
设置更新、内置 MCP 开关及分配处理器在参数校验后调用 `knowledgeGateway.revokeObsidianCapabilities()`;后两者只在 `serverId === 'obsidian'` 时调用。撤销发生在持久化前,之后通过既有 `refreshCapabilities` 刷新配置;失败语义由 [LR-8](./logic-design.md#授权撤销与清理)定义。
|
||||
|
||||
## 仓库发现与路由
|
||||
|
||||
注册文件位置如下,优先使用相应环境变量:
|
||||
|
||||
| 系统 | 注册文件 |
|
||||
| --- | --- |
|
||||
| Windows | `%APPDATA%/obsidian/obsidian.json`,缺少变量时使用用户目录下 `AppData/Roaming` |
|
||||
| macOS | `~/Library/Application Support/obsidian/obsidian.json` |
|
||||
| Linux | `$XDG_CONFIG_HOME/obsidian/obsidian.json`,缺少变量时使用 `~/.config` |
|
||||
|
||||
注册条目的键直接用作 ID。指定文件夹经绝对路径规范化后生成 SHA-256 ID,Windows 对哈希输入转小写;这是路径身份,不是文件完整性校验或跨机器永久身份。目录移动或注册 ID 变化后,调用方须重新发现仓库。
|
||||
|
||||
`callTool` 接收 `{ vaultId?, name, arguments }`,按 LR-3 验证目标后转发。共享工具 `obsidian_list_vaults` 返回仓库列表,其余工具名称为 `obsidian_` 加上游名称,输入额外包含 `vaultId`。网关校验共享 schema,提取 `vaultId`,移除名称前缀后调用上游;其余参数原样保留。输入默认值保持可选,frontmatter 键不受外层严格对象限制。[共享契约测试](../../../src/shared/obsidian-tools.test.ts)逐项比对上游字段、必填项、默认值及约束。
|
||||
|
||||
## 上游工具契约
|
||||
|
||||
名称集合依据本地安装的 `@bitbonsai/mcpvault@0.16.0` 中 `dist/src/createServer.js` 核对。HTTP 网关和直连工具提供器共享 19 个工具:`obsidian_list_vaults` 加下表 18 个工具的带前缀映射。设置连接测试返回的 `toolCount` 为上游名称去重数,正常为 18,不包含仓库发现工具。
|
||||
|
||||
| 上游工具 | 能力 |
|
||||
| --- | --- |
|
||||
| `read_note` | 读取笔记 |
|
||||
| `write_note` | 写入笔记 |
|
||||
| `patch_note` | 局部替换笔记内容 |
|
||||
| `list_directory` | 列出目录 |
|
||||
| `delete_note` | 按上游删除参数处理笔记 |
|
||||
| `search_notes` | 搜索笔记 |
|
||||
| `move_note` | 移动或重命名笔记 |
|
||||
| `move_file` | 移动文件,包括上游支持的附件文件 |
|
||||
| `read_multiple_notes` | 批量读取笔记 |
|
||||
| `update_frontmatter` | 更新 frontmatter |
|
||||
| `get_notes_info` | 获取笔记信息 |
|
||||
| `get_frontmatter` | 获取 frontmatter |
|
||||
| `manage_tags` | 管理标签 |
|
||||
| `get_vault_stats` | 获取仓库统计 |
|
||||
| `list_all_tags` | 列出标签 |
|
||||
| `wiki_link` | 处理 wiki 链接 |
|
||||
| `get_note_outline` | 获取笔记大纲 |
|
||||
| `read_note_lines` | 按行读取笔记 |
|
||||
|
||||
服务分页读取上游工具,不设置功能白名单。读写属性供应用既有工作模式使用,不能据此增加 Obsidian 专用过滤。保留上游的错误结果与参数规则,例如删除参数 `confirmPath`;不把参数要求改造成额外确认弹窗。
|
||||
|
||||
## 离线打包与生命周期
|
||||
|
||||
构建固定 npm 版本为 `0.16.0`,复制包及递归运行依赖,保留许可证、模块相对资源和 `trash` 的原生辅助程序。资源位于 `out/main/obsidian-mcpvault/node_modules`,生产安装包将其放在 `app.asar.unpacked`。缺包或版本不符必须明确失败,不在运行时执行 `npx` 或回退到在线安装。
|
||||
|
||||
服务将 `app.asar` 路径映射到解包目录,检查 `@bitbonsai/mcpvault/dist/server.js` 存在,使用现有 `LaunchEnvironmentProvider` 注入命令环境后运行 `node <server.js> <vault.path>`,工作目录为目标仓库。托管 Node 的准备和用户自定义解释器规则由[工具执行环境技术设计](../local-tool-environment/technical-design.md)负责。Obsidian 不新增下载源;已有工具下载源不控制随包交付的 MCPVault。
|
||||
|
||||
每次列举或调用创建 stdio 连接,在 `finally` 中关闭 transport 和 client,初始化失败也清理。`callTool` 传递 `AbortSignal`,启动前已取消时不启动进程。连接测试逐仓库列工具,不调用写入工具;它没有独立取消 IPC。网关沿用现有 MCP 结果大小约束,保留上游原生内容和错误详情,超时沿用 SDK 与既有会话路径。
|
||||
|
||||
`callObsidianTool` 合并请求、授权撤销及单次调用的取消信号,并在结果返回前再次检查取消。`revokeObsidianCapabilities` 对含 Obsidian 的授权调用既有 `revoke`,移除令牌并中止其 controller;混合授权整体失效。网关以 `activeObsidianCalls` 跟踪调用 Promise,`dispose()` 先撤销授权,再等待这些 Promise 完成,随后关闭下游会话和 HTTP 服务。由于服务 Promise 包含 `finally` 中的 transport 关闭,等待范围包含真实子进程清理,也包含先前撤销后尚未关闭的调用。
|
||||
|
||||
## UI 设计
|
||||
|
||||
此章节独立承担功能 UI 设计职责,服从[统一界面设计系统](../../../UI-DESIGN.md)。入口为“能力与工具 → MCP → 内置 MCP”的 Obsidian 卡片,复用 `PageTabs`、卡片、表单、按钮、Switch 及 Runtime 分配 Checkbox。
|
||||
|
||||
卡片顺序为名称及状态、启用开关、能力说明、仓库范围、可选路径输入、已保存范围、选择/测试/保存操作、关联反馈、Runtime 分配和可展开工具列表。主按钮为“保存”,测试与选择文件夹为次按钮。范围选项为“所有本机仓库”和“指定文件夹”;指定模式才显示路径输入框。页面明确区分草稿与“已保存范围”。
|
||||
|
||||
关闭状态仍允许选择、测试和保存。快照尚未载入或操作忙碌时禁用相关提交,测试按钮显示进度。修改范围或路径清除旧结果;目录选择取消保留原值。连接结果在卡片内显示仓库名称、路径和工具数,作为可复查的诊断内容;字段错误及可立即重试的测试错误就近显示,保留输入。普通保存提示如需增加,使用应用通知,不重复显示同一事件。
|
||||
|
||||
启用控件使用 `role="switch"`;范围、路径和按钮具备可访问名称,路径错误使用 `aria-describedby`,错误用 `role="alert"`,测试结果用 `role="status"`。工具展开按钮提供 `aria-expanded` 和 `aria-controls`。沿用原生键盘行为和共享焦点样式。Obsidian 的多字段诊断表单采用纵向布局,操作按钮换行,结果中的长路径允许折行;浅色和深色主题均复用语义令牌。Windows 760 像素宽窗口的实际测量及原生输入结果见[进度](./progress.md)。
|
||||
|
||||
## 验证与实施顺序
|
||||
|
||||
| 阶段 | 验证内容 | 追溯 |
|
||||
| --- | --- | --- |
|
||||
| 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-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)。
|
||||
@@ -0,0 +1,20 @@
|
||||
# 用户故事
|
||||
|
||||
本期角色为使用本机笔记的桌面用户,以及替用户调用工具的会话助手。需求定义见[PRD](./prd.md)。下表描述验收条件,不表示场景已经通过。
|
||||
|
||||
| ID | 场景与需求 | Given(前提) | When(动作) | Then(预期结果) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 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-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-10 | 旧配置与分配,FR-1、FR-9 | 旧配置没有 Obsidian 字段,已有其他 MCP 配置 | 加载设置并修改 Obsidian 分配 | Obsidian 默认关闭,其他配置保留;修改分配不改写仓库范围;未分配的会话不可调用 |
|
||||
| US-11 | 上游能力完整,FR-4 | 在允许完整调用的工作模式下启用接入 | 获取工具集合并对照 MCPVault 0.16.0 | 18 个上游工具均有可调用映射,参数能力保留;仓库发现辅助工具不计入这 18 个 |
|
||||
| US-12 | 设置变更与旧调用,FR-10 | 有含 Obsidian 的旧请求授权和正在执行的工具 | 提交保存范围、切换开关或修改分配,随后关闭网关 | 旧授权失效,在途调用收到取消;网关等待工具关闭。保存失败也不恢复旧授权;后续请求按实际保存状态重新取得授权 |
|
||||
|
||||
跨平台注册路径和安装包测试分别取证。通过模拟平台字符串测试注册路径,不等于在对应操作系统上完成安装包验收。
|
||||
@@ -3,15 +3,24 @@
|
||||
本功能负责 SSH Host 管理、Host Key、远程环境安装、GoodBuddy Agent、签名 Runtime、
|
||||
远程 Workspace 和 Ask/Execute 生产路径。
|
||||
|
||||
2026-09-28 新增待实现方向:主机管理迁入应用中心,与“云环境管理”成为同级应用;
|
||||
云平台发现已有实例,接入执行时复用 SSH 主机。产品范围见 [PRD](./prd.md)。
|
||||
本文中的平台连接指云账号或私有化 API 连接,云实例指平台中的虚拟机,主机指 GoodBuddy
|
||||
保存的 SSH 连接目标;三者不等同。现有实现和历史验收仍以下列技术文档为准。
|
||||
|
||||
## 文档导航
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [产品需求](./prd.md) | 应用迁移、六平台适配范围、分期与产品验收;待实现 |
|
||||
| [功能逻辑](./logic-design.md) | 平台、实例、主机关系,独立状态、依赖和删除规则;待实现 |
|
||||
| [界面设计](./ui-design.md) | 独立工作页面、主机列表与详情、云实例接入和响应式交互;待实现 |
|
||||
| [远程主机与 Agent 技术设计](./technical-design.md) | 当前产品语义、Agent、Runtime、Workspace、协议、生命周期和验证事实 |
|
||||
| [远程环境准备技术设计](./environment-provisioning-technical-design.md) | Host 下载、GoodBuddy 传输、安装事务、更新和恢复边界 |
|
||||
| [执行清单验证记录](./runtime-checklist-validation.md) | 远程 OC/CN 原生清单、CN 开发包安装、取消及断线重放的实测结果与剩余验收 |
|
||||
|
||||
现有文档包含产品、功能逻辑和进度事实,后续修改远程功能时再按标准职责拆分。
|
||||
既有技术文档保留当前 SSH 行为及历史进度;新增 PRD、逻辑和 UI 文档只定义本次待实现范围,
|
||||
不替代已有验证事实。验收场景随 PRD 维护,本次不单独创建 User Stories 或进度占位文档。
|
||||
|
||||
跨会话共享 OpenCode 进程、ACP 连接与模型桥的实现设计,见
|
||||
[Runtime 进程复用技术设计](../assistant-workbar/runtime-process-reuse-technical-design.md#6-ssh-opencode-与-agent)。
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 主机与云环境功能逻辑
|
||||
|
||||
状态:2026-09-28 设计,待实现。对应[产品需求 FR-RH-1 至 FR-RH-6](./prd.md),验收场景使用其中的 AC-RH 编号。
|
||||
|
||||
## 对象与关联
|
||||
|
||||
| 对象 | 所有权与关系 |
|
||||
| --- | --- |
|
||||
| 平台连接 | 云环境管理负责平台类型、账号或 API 端点、凭据引用与查询范围 |
|
||||
| 云实例 | 以平台连接及该平台稳定资源标识定位,保留必要的账号、区域或私有化资源范围;不能仅靠名称或 IP 判定同一实例 |
|
||||
| 主机 | 主机管理负责 SSH 地址、账号、认证、Host Key 和执行环境;可选关联云实例 |
|
||||
| 远程项目 | 继续引用主机与远端目录,执行空间保持 local / ssh |
|
||||
|
||||
首版一个云实例最多关联一个受管主机,一个主机最多关联一个云实例;一个主机可被多个项目引用。重复导入显示已关联主机;关联已有主机须由用户选择并验证连接目标,不能根据相同 IP 自动合并。平台凭据和 SSH 凭据分别管理,不复制到项目中。(FR-RH-3、FR-RH-5;AC-RH-3)
|
||||
|
||||
## 导入与状态
|
||||
|
||||
发现实例后先展示平台返回的事实。用户选择“接入主机”,选定地址和认证配置,复用主机验证流程,成功后保存主机与关联;失败或取消不留下半成品主机,也不改变已有有效关联。保存成功不自动安装环境,由用户显式触发。(FR-RH-2、FR-RH-3;AC-RH-2 至 AC-RH-4)
|
||||
|
||||
| 状态维度 | 来源与显示规则 |
|
||||
| --- | --- |
|
||||
| 实例运行状态 | 平台 API 查询结果;查询失败显示错误及上次成功时间,不能推断为已删除 |
|
||||
| SSH 连通性 | 所选主机的连接检查;未检查、成功、失败与检查时间独立显示 |
|
||||
| 执行环境 | Agent / Runtime 实际检查和安装结果;版本匹配不等于环境健康 |
|
||||
|
||||
进入主机页面只读取本地记录,不逐台连接 SSH。云资源刷新只查询平台 API,不顺带验证 SSH 或安装环境。变更连接目标后按现有规则重新验证,不用旧结果宣称新目标就绪。平台报告实例消失时保留主机及项目,显示关联异常并允许用户处理,不自动清理数据。(FR-RH-4;AC-RH-1、AC-RH-4)
|
||||
|
||||
## 导航与能力依赖
|
||||
|
||||
常驻只控制导航捷径,取消常驻不停止任务、禁用能力或删除数据。应用中心的主机管理设置承载共享远程能力启用状态,迁移保留原 `remoteProjectsEnabled` 值;旧设置不再维护第二个独立开关。云环境应用的启用单独控制云管理入口和新平台操作。(FR-RH-1、FR-RH-4、FR-RH-6)
|
||||
|
||||
| 操作 | 目标行为 |
|
||||
| --- | --- |
|
||||
| 打开云环境但远程能力未启用 | 允许配置平台及查询实例;接入主机时提供就地启用,保存成功后继续 |
|
||||
| 取消主机常驻 | 云环境仍可引用主机,已有关联和运行不变 |
|
||||
| 关闭共享远程能力 | 沿用现有远程项目停用语义,保留主机、凭据和项目;云资源浏览仍可用,依赖操作显示原因与启用入口 |
|
||||
| 关闭云环境应用 | 保留平台配置和主机关联;已接入的 SSH 主机及远程项目继续可用 |
|
||||
| 切换业务页面 | 保留搜索、选择与正在进行的操作状态,遵守未保存编辑离开检查 |
|
||||
|
||||
能力启用与校验须同时覆盖 UI 和 Main,不能仅移动开关或隐藏按钮。(AC-RH-5)
|
||||
|
||||
## 删除语义
|
||||
|
||||
移除主机沿用现有本地引用项目及关联数据删除确认,移除成功后清除云实例到该主机的关联,实例重新显示为未接入。操作不连接远端删除目录,也不调用实例销毁 API。
|
||||
|
||||
移除平台连接前列出关联主机;确认后移除平台凭据及关联,保留 SSH 主机、凭据和项目,使其继续作为独立主机使用。远端实例生命周期操作属于后续功能,须在云环境页面独立定义影响及结果。(FR-RH-5;AC-RH-6)
|
||||
|
||||
## 完整性与待定事项
|
||||
|
||||
本文覆盖导航迁移、发现与导入、状态来源、依赖关闭、重复关联及删除。各平台适配顺序、支持版本和验收环境待确认;私钥与私网连接方案属于后续技术选型。实例创建、销毁、网络编排及批量操作不在首版范围,不能从本设计推导其行为。
|
||||
@@ -0,0 +1,62 @@
|
||||
# 主机管理与云环境管理产品需求
|
||||
|
||||
状态:2026-09-28 确认产品方向,待实现。本次仅更新文档;现有 SSH 实现与验证范围见[技术设计](./technical-design.md)。
|
||||
|
||||
## 目标与范围
|
||||
|
||||
主机管理从设置中心迁入应用中心,在主内容区提供独立工作页面。云环境管理作为同级应用,管理公有云及私有化平台中的虚拟机,接入远程执行时复用主机管理。
|
||||
|
||||
首版围绕已有虚拟机完成“连接平台 → 发现实例 → 接入主机 → 验证 SSH → 安装远程环境 → 创建项目 → 执行任务”。先交付主机管理入口迁移与页面调整,再分批接入平台适配器。
|
||||
|
||||
| 平台范围 | 接入目标 |
|
||||
| --- | --- |
|
||||
| AWS | 已有 EC2 实例 |
|
||||
| Azure | 已有 Virtual Machines |
|
||||
| 阿里云 | 已有 ECS 实例 |
|
||||
| 腾讯云 | 已有 CVM 实例 |
|
||||
| VMware | 先聚焦 vCenter / vSphere 管理的虚拟机 |
|
||||
| ZStack | 已有云主机 |
|
||||
|
||||
六个平台纳入首版适配计划,逐批实现和验收,不把手工 SSH 接入计为平台 API 已适配。候选顺序为阿里云、腾讯云加一个私有化平台,再补其余平台;私有化平台优先级按真实客户与测试环境确定,该顺序尚未锁定。各平台支持版本、认证方式和可验收环境须在对应适配器开发前确认。
|
||||
|
||||
首版不包含自动创建、销毁虚拟机、网络编排、镜像管理和计费管理;已有实例启停作为后续增量。现有云服务器仍可手工通过 SSH 接入,受网络、认证和远端操作系统支持范围限制。
|
||||
|
||||
## 功能要求
|
||||
|
||||
### FR-RH-1 应用入口
|
||||
|
||||
应用中心分别提供“主机管理”和“云环境管理”,打开独立主内容区页面,支持排序与常驻。应用中心只负责访问和通用应用设置,主机和云资源操作位于业务页面。迁移后移除设置中心内的重复主机管理表单;旧入口可提供跳转。
|
||||
|
||||
### FR-RH-2 主机工作页面
|
||||
|
||||
主机作为全局资源,提供可搜索、按来源及状态筛选的列表与所选主机详情。保留现有新增、编辑、验证、Host Key、凭据管理、安装、更新、重装及删除能力。详情包含概览、连接配置、执行环境和关联项目。来源为手工添加或关联平台,不要求手工主机填写云字段。
|
||||
|
||||
### FR-RH-3 云平台连接与已有实例接入
|
||||
|
||||
一个平台连接管理多台实例。用户可先浏览云资源,再选择实例创建主机或关联已有主机。实例接入须选定可达地址和认证方式,经 SSH 验证后才成为可用主机;仅查询到实例不代表接入成功。认证或连接失败保留平台及实例上下文,允许修正后重试。
|
||||
|
||||
### FR-RH-4 依赖与状态
|
||||
|
||||
云环境浏览不要求存在主机;安装环境、创建远程项目和远程执行复用主机能力。云实例运行状态、SSH 连通性、执行环境就绪状态独立呈现,并显示检查时间或未检查状态。启用、常驻及共享远程能力的规则由[功能逻辑](./logic-design.md)定义。
|
||||
|
||||
### FR-RH-5 资源关联与删除
|
||||
|
||||
已接入实例可跳转至对应主机,主机可回到平台实例。移除本机主机记录、移除平台连接与销毁云实例是独立动作,不能相互隐式触发。主机删除沿用现有引用项目影响确认及本地数据清理语义,明确不会删除远端内容。
|
||||
|
||||
### FR-RH-6 迁移与接入条件
|
||||
|
||||
升级保留已有 Host ID、凭据、Host Key、项目引用、功能启用状态及原有应用导航偏好。私钥认证和私网连接路径属于云接入前的补齐项;在对应能力实际交付前,页面必须说明可用认证与网络条件,不能将平台 API 可达等同于 SSH 可达。具体私钥保存方式和跳板连接方案另行技术设计,不因本文而宣称已经支持。
|
||||
|
||||
## 产品验收
|
||||
|
||||
| 场景 | 操作与预期结果 |
|
||||
| --- | --- |
|
||||
| AC-RH-1 已有用户升级 | 打开主机管理,原有主机及项目引用可用;导航排序和原启用偏好保留,不自动连接远端 |
|
||||
| AC-RH-2 主机维护 | 从应用中心新增主机,完成验证、显式安装环境并创建远程项目;取消或失败可重试,页面跳转不重复安装 |
|
||||
| AC-RH-3 云实例导入 | 已配置平台连接时发现并选择实例,验证后创建或关联主机,进入同一主机详情;重复导入定位已有记录 |
|
||||
| AC-RH-4 网络或认证失败 | 实例运行中但 SSH 不通或认证失败时,分别显示实例状态和连接错误;不显示执行就绪,不安装环境 |
|
||||
| AC-RH-5 导航与依赖 | 取消主机常驻不影响云环境或执行能力;云环境接入流程可就地启用所需远程能力,无须寻找设置开关 |
|
||||
| AC-RH-6 移除关联资源 | 删除主机前列出受影响的本地项目;完成后云实例仍存在,云列表显示未接入。移除平台连接不删除主机和远端资源 |
|
||||
| AC-RH-7 页面与权限 | 窄窗口和键盘可完成列表、详情和返回;平台账号仅有查询权限时仍可发现和导入实例,不要求实例生命周期权限 |
|
||||
|
||||
每个平台必须通过真实 API 发现已有实例,并完成 SSH、环境安装、项目创建和真实任务执行的生产路径验收,才可标记该适配器已交付。UI、模拟响应和单独 SSH 测试不能替代平台验收。
|
||||
@@ -27,6 +27,12 @@ Agent `0.11.23` 提供,显式请求期限与取消仍有效。当前问答源
|
||||
|
||||
## 跨会话进程复用(未发布)
|
||||
|
||||
2026-09-28 Story Graph 补充:远程 OpenCode/Continue 的 Ask 和 Execute
|
||||
通过现有受管 HTTP MCP、工具二进制通道读取桌面图谱。Main 固定项目范围并复核监督者
|
||||
开关及 Runtime 分配;图谱读取不复制数据库到 Host,也不调用模型。三个只读工具、
|
||||
分页和版本限制由[专项设计](../conversation-supervision/story-graph-mcp-design.md)定义,
|
||||
源码 Host 探针及协议测试见[验证记录](../conversation-supervision/progress.md#2026-09-28-story-graph-只读工具)。
|
||||
|
||||
已发布版本的 `RuntimeAcpBackend` 按 binding 拥有原生进程,`AgentOwnedAcpPrompt` 的一条 ACP
|
||||
连接对应一个原生 Session;同一会话后续 Prompt 可复用,但不同会话不共用进程。
|
||||
上文“Host 共享 Runtime 环境”指安装与 Agent 环境,不代表原生 OpenCode 进程已共享。
|
||||
@@ -49,6 +55,9 @@ Agent `0.11.23` 提供,显式请求期限与取消仍有效。当前问答源
|
||||
|
||||
## 产品语义
|
||||
|
||||
本节记录现有实现。2026-09-28 确认的应用中心迁移与云环境管理目标见
|
||||
[PRD](./prd.md)、[功能逻辑](./logic-design.md)及[UI 设计](./ui-design.md),尚未接入源码。
|
||||
|
||||
远程 Host 与项目能力默认关闭。用户在“设置 > 平台功能”的独立
|
||||
“远程项目”页签启用开关后,Renderer 才显示 SSH Host 管理、托管 SSH 项目创建和已保存
|
||||
远程项目;Main 在 IPC 边界执行同一开关校验,避免隐藏界面被绕过。关闭开关不删除 Host、
|
||||
@@ -67,6 +76,30 @@ profile 和凭据作为有界、逐 Prompt 的控制消息交给 Agent;该凭
|
||||
|
||||
## 组件
|
||||
|
||||
### 应用迁移与云平台接入计划(待实现)
|
||||
|
||||
实现顺序对应 FR-RH-1 至 FR-RH-6:
|
||||
|
||||
1. 从 `SshHostsSettingsSection` 提取独立主机工作页面,接入 `ApplicationCenter` 和 `App`
|
||||
页面导航;复用现有 SSH 服务、验证弹窗、安装操作及通知,完成列表与详情改版。
|
||||
2. 扩展应用标识、排序、常驻和配置读取。读取已发布四应用配置时保留原相对顺序及偏好,
|
||||
新入口追加且默认不常驻;云管理初始关闭,共享远程能力继承 `remoteProjectsEnabled`。
|
||||
配置工具公开契约、默认值、持久化、Preload 和 Main 校验同步修改,不能仅扩展 Renderer。
|
||||
3. 按功能逻辑整理能力依赖,将共享远程开关的设置入口迁入应用中心。保留旧值、Host ID、
|
||||
加密凭据、Host Key 与项目引用;不为页面迁移重建主机或修改 Agent 协议。
|
||||
4. 云平台适配器在 Main 中查询真实 API,凭据复用加密存储;Renderer 仅接收脱敏配置、
|
||||
实例快照与操作结果。采用满足首批平台发现和导入的最小接口,不预建资源编排框架。
|
||||
平台连接和实例关联采用独立记录,SSH 主机仍为执行目标,项目继续使用 `local / ssh`。
|
||||
5. 确认首批平台版本及测试环境,补齐需要的私钥和私网连接路径,逐个验收发现、导入、
|
||||
SSH 验证、环境安装和真实任务执行。具体存储字段、迁移及适配 API 在实施时细化;
|
||||
变更已发布存储前遵守[数据库迁移规范](../../development/database-migrations.md)。
|
||||
|
||||
验证覆盖已有用户配置读取、重复实例关联、状态来源、平台查询失败、SSH 不通、删除影响、
|
||||
能力依赖与页面离开。平台 API 测试与原有远程 Agent 测试分别记录;导航迁移通过不代表
|
||||
云平台适配完成。新增能力交付前,本文件以下 SSH 组件及历史记录继续描述现有实现。
|
||||
|
||||
### 现有远程执行组件
|
||||
|
||||
桌面保存托管 SSH 子任务事件时,共用
|
||||
[执行记录差量存储](../assistant-workbar/execution-history-storage.md)。
|
||||
Agent transcript 与公共 Runtime 事件不改变;桌面的来源去重、消息投影事务和恢复序号
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# 主机管理与云环境界面设计
|
||||
|
||||
状态:2026-09-28 设计,待实现。范围见[PRD](./prd.md),状态及依赖遵守[功能逻辑](./logic-design.md)。视觉、页面壳层和组件遵守[统一界面设计系统](../../../UI-DESIGN.md)。
|
||||
|
||||
## 应用入口
|
||||
|
||||
应用中心新增“主机管理”和“云环境管理”两个同级应用。应用菜单、管理应用中的“打开”和常驻侧栏入口进入同一主内容区页面,关闭来源菜单或管理 Modal,不在应用中心 Modal 内管理主机。业务页面使用全局范围标识;打开页面不切换当前项目、不连接主机或执行安装。
|
||||
|
||||
通用启用、常驻和排序遵守[应用导航设计](../application-tool-navigation/ui-design.md)。设置中心移除主机操作表单,保留必要的迁移跳转;共享远程能力开关归入主机管理的应用设置。Agent 包下载、导入等现有全局包管理暂留原位置,主机执行环境页提供相关入口,不复制包状态存储。
|
||||
|
||||
## 主机页面
|
||||
|
||||
页面使用共享 `dashboard` 壳层。标题区显示“主机管理”、全局范围和主操作“添加主机”。工具栏提供名称或地址搜索、来源筛选、连接状态筛选。列表显示名称、地址、来源、SSH 状态和执行环境摘要,避免每行展开安装表单。
|
||||
|
||||
宽窗口使用列表与详情两栏,点击主机只更新详情,不重排列或滚动列表。详情使用共享 `PageTabs`:
|
||||
|
||||
| 页签 | 内容与操作 |
|
||||
| --- | --- |
|
||||
| 概览 | 名称、来源、系统架构、独立状态及检查时间;关联云实例时提供“查看云实例” |
|
||||
| 连接配置 | 地址、端口、用户名、认证配置状态、Host Key;编辑与重新验证复用既有流程,不回显凭据 |
|
||||
| 执行环境 | Agent / Runtime 版本、检查结果、安装进度;保留安装、更新、重装及既有下载方式选择 |
|
||||
| 关联项目 | 所有引用该主机的项目,明确归档状态;提供打开项目和创建远程项目 |
|
||||
|
||||
云关联字段按需显示,手工主机不出现必填平台、区域和实例 ID。删除主机置于次要操作,确认内容列出本地项目影响及远端资源保留范围。
|
||||
|
||||
未选择主机时提示选择列表项;没有主机时提供添加入口;搜索无结果时提供清除筛选。连接失败、安装失败和数据读取失败分别显示原因与对应重试操作。用户主动检查所选主机时才连接远端,不能因列表渲染而检查全部主机。
|
||||
|
||||
## 云环境页面
|
||||
|
||||
标题区显示“云环境管理”、全局范围及“添加平台连接”。通过平台连接和适用的区域或资源范围筛选实例,列表展示名称、平台、位置、实例状态、地址和接入状态。刷新明确针对当前查询范围,并显示上次成功时间。
|
||||
|
||||
未接入实例提供“接入主机”,进入地址与认证选择,再完成现有验证向导。已接入实例提供“打开主机”;关联已有主机时可搜索选择,明确展示目标后验证。两个页面之间往返恢复来源筛选、所选记录和滚动位置。
|
||||
|
||||
实例运行、SSH 连接和环境就绪分别显示文字状态。只读平台账号可以查询实例并接入 SSH;权限不足时说明失败操作,不把整个平台笼统标为不可用。平台 API 可达但 SSH 不通时保留资源详情与修正连接的入口。
|
||||
|
||||
首版不展示尚未实现的创建、销毁或网络编排按钮。各适配器随真实能力交付,不能用不可操作的平台卡片代替接入功能。
|
||||
|
||||
## 响应式与交互
|
||||
|
||||
两栏无法满足共享最小宽度时改为单面板,选择主机进入详情并提供“返回主机列表”,返回恢复筛选、滚动和列表项焦点。页签可横向滚动,搜索与筛选换行,长地址和错误信息折行,页面不整体横向溢出。
|
||||
|
||||
列表项及操作使用语义按钮,提供可访问名称和可见焦点;页签沿用 `tablist`、`tab`、`tabpanel`、方向键及 Home/End。弹窗遵守共享焦点恢复及未保存编辑规则。通知使用全局应用通知,进行中的安装进度保留在对应主机,页面切换不重启操作。
|
||||
|
||||
验收对应 AC-RH-1 至 AC-RH-7,覆盖浅深主题、窄窗口、键盘操作、空列表、长名称、认证失败及安装取消;具体实施记录需附真实 UI 验证结果。
|
||||
@@ -0,0 +1,224 @@
|
||||
# GoodBuddy 更新:故事图谱、Obsidian接入、Runtime原生终端
|
||||
|
||||
> **编辑标注说明(对外发布前删除):** 本稿汇总桌面版 0.13.8 至 0.15.1,以及此后截至 `66f77b8` 的已提交变化,更新于 2026-09-28。所述功能与修复均已开发完成并提交;0.15.1 之后的提交归属版本待确认。“待补图”表示缺少可发布素材,“待替换”表示发布信息未核验,“待核对”表示发布前需要确认的行为。本文是宣传草稿,尚未对外发布。
|
||||
|
||||
GoodBuddy 这一轮更新围绕工作记录的整理与使用展开:监督者按项目回顾进展,并通过图谱保留结论依据;新加入的笔记连接支持保存聊天正文、返回原会话,以及在聊天中读写本机 Obsidian 笔记。本地项目还可以从输入区打开 Continue、OpenCode 原生终端或 DS Web 客户端,沿用当前项目与模型配置。
|
||||
|
||||
本文接续上一份画布笔记与文档处理更新稿,覆盖 0.13.8 起的实际增量。远程 Runtime 配置修复需要先升级至 Desktop 0.15.1,再将 Host 环境更新至 Agent 0.15.0。下文新增的远程故事图谱读取还需要包含对应改动的 Desktop 与 Agent,配套版本见发布前核验项。
|
||||
|
||||
> **【待替换 R01|版本与体验入口】** 最新已核查桌面 tag 为 `v0.15.1`,对应 `4e52392`,承接未公开发布的 0.15.0;本轮增量为 `65a2139..66f77b8`。故事图谱读取、完整讨论正文编辑及笔记面板简化位于该 tag 之后,发布归属待确认;远程图谱读取需要同步包含 `66f77b8` 改动的 Desktop 与 Agent,不能标为 Agent 0.15.0 已有。对外发布前核验最终版本、公开安装包、发布日期、下载地址和完整更新日志链接,并在此补入。桌面 0.13.14 的发布失败后由 0.13.15 承接。
|
||||
|
||||
**升级前,请先关闭应用并备份完整用户数据。** 数据库和相邻的 `notes/` 目录需要成套保留。本稿中的聊天采集功能将数据库格式升至 48,用于保存笔记来源;升级后旧客户端不能直接打开该数据库,回退需要升级前的备份。
|
||||
|
||||
## 新增功能
|
||||
|
||||
### 监督者:从工作记录中整理进展,查看每条结论的依据
|
||||
|
||||
项目讨论持续一段时间后,可以打开“监督者”的“工作回顾”,选择全局或一个项目,以及最近 1 天、7 天或 30 天的范围。回顾会整理工作总结、本次变化和未解决事项;历史结果保留各自的范围和生成时间,方便回看。
|
||||
|
||||
监督者默认关闭,在应用中心启用后即可手动回顾,无需先建立自动计划。当前分析来源包括范围内的用户与助手消息、任务标题与当前状态,以及消息中已有的知识引用片段。它不会自动遍历全部笔记、整个知识库或附件原件。
|
||||
|
||||
> **【待补图 01|必补|工作回顾结果】** 在隔离演示项目中通过正常聊天和任务操作形成公开、虚构的工作记录,运行一次真实回顾。截图同时展示范围、时间和实际生成的总结、未解决事项;不能用预置答案冒充模型结果。计划文件:`assets/01-work-review.png`。
|
||||
>
|
||||
> 建议图注:按项目回顾近期讨论与任务,查看进展和仍待处理的问题。
|
||||
|
||||
回顾中的事件、实体与关系可以在“故事线图谱”中查看。选中一个对象,就能检查它的说明、关联和保存的来源正文,再判断模型的归纳是否准确。实体支持确认、改名或撤销,关系可以确认或移除。
|
||||
|
||||
这适合检查“某个结论来自哪段讨论”“一个事项与哪些事件有关”。图谱按事件顺序提供浏览,但还不能逐时刻还原当时的全部实体状态;图上的间距也不表示实际时间间隔。
|
||||
|
||||
> **【待补图 02|必补|图谱与来源】** 使用图 01 同一份真实回顾结果,选中事件或实体,展示对应的来源正文。画面应能辨认对象与依据的关联;不使用设计原型代替生产结果。计划文件:`assets/02-story-sources.png`。
|
||||
>
|
||||
> 建议图注:从回顾图谱查看关联对象,并打开保存的讨论依据。
|
||||
|
||||
### 助手可以查阅故事图谱,接着已有决定继续工作
|
||||
|
||||
继续一个项目时,可以请助手“查一下此前选了哪个方案,依据是什么”,或“找出这项决定后来的修订”。助手可搜索监督者已经保存的事件、实体、关系和回顾结果,读取相关上下文,再查看来源片段,作为后续讨论的依据。
|
||||
|
||||
在应用中心启用监督者,并在 MCP 设置中保持 Story Graph 开启、分配给所用 Runtime 后即可使用。支持直连模型、DeepSeek Harness,以及本地和远程 OpenCode、Continue;Ask 与 Execute 均可只读查询。本地 Continue、OpenCode 原生终端也可读取。
|
||||
|
||||
查询默认使用当前项目,也可明确指定全局或项目集合;各范围读取各自保存的结果。工具只读取已有记录,不会为一次查询重新运行回顾或修改图谱;助手根据结果生成回答仍使用当前模型。保存的来源片段可以分页读取,但完整历史状态重建和跨范围事实自动复用不在本次功能范围内。
|
||||
|
||||
### 在任务中心跟踪当前会话或任务
|
||||
|
||||
右侧任务中心的项目概览下方新增监督反馈。选中任务时查看该任务的匹配结果,否则跟随当前会话;也可以固定目标,切换聊天后继续查看同一个事项。没有匹配回顾时会明确显示空状态。
|
||||
|
||||
监督反馈中的“继续讨论”可以直接使用,无需先打开来源。侧栏和图谱来源中的入口都会展示完整、可编辑的发送正文,包含相关回顾结论和来源内容;可以删改引用、补充问题,再确认单独显示的目标会话。发送以编辑后的正文为准,已删除的上下文不会被重新附加;发送失败保留草稿。在图谱来源中发送后会打开目标会话,需要先看原讨论时,也可单独点击“打开会话”。
|
||||
|
||||
带入的结论按所选来源关联的事件、实体与关系整理,方便围绕当前事项继续追问。监督界面已移除知识实体写回控件。
|
||||
|
||||
后续讨论的最终内容最多 12,000 字符。固定任务没有关联会话时,不能直接发送继续讨论;后续对话沿用目标会话的模型和工作模式。
|
||||
|
||||
### 聊天正文存入魔法笔记,并保留来源
|
||||
|
||||
遇到值得保留的回答,可以点击助手消息旁的“加入笔记”,预览并编辑文字,再追加到已有笔记,或新建笔记保存。需要保留整段讨论时,会话菜单中的“保存当前对话到笔记”会读取完整历史,收录用户与助手的正文,不局限于屏幕上已经显示的消息。
|
||||
|
||||
保存后的记录带有“来自会话”入口。单条回答可以返回对应消息;原会话或消息被删除后,已保存的笔记内容仍保留。每次采集新增一条记录,后续对话修改不会自动同步到笔记。
|
||||
|
||||
工作栏“+ → 笔记”可以打开紧凑面板,在聊天旁搜索笔记标题与正文、浏览摘要、追加文字。切换项目或会话时草稿保留,关闭有未保存内容的面板前会提示;草稿仅在本次应用运行期间保留。画布、待办和完整编辑继续在魔法笔记工作区中使用。
|
||||
|
||||
快速笔记入口集中在工作栏和聊天采集操作中,移除了侧栏与顶栏的重复快捷按钮。面板精简重复标签,保存和来源操作统一靠右排列,输入校验与错误提示继续保留。
|
||||
|
||||
采集保存文本与 Markdown,不复制推理过程、工具日志或附件图片。生成中的回答暂不可采集,单条记录上限为 500 KiB,超限会提示缩减。采集本身无需调用模型;保存后自动评论仍按已有设置执行。笔记继续属于全局,来源项目只用于帮助回看。
|
||||
|
||||
> **【待补图 06|必补|聊天采集与来源返回】** 用公开演示对话预览并保存一条回答,展示笔记记录中的来源入口,再验证能返回原消息。计划文件:`assets/06-chat-note-source.png`。
|
||||
>
|
||||
> 建议图注:把有用的回答存入笔记,需要上下文时返回原会话。
|
||||
|
||||
### 在聊天中查找和整理 Obsidian 笔记
|
||||
|
||||
已有 Obsidian 笔记可以直接接入 GoodBuddy。在“设置 → 能力与工具 → MCP”的 Obsidian 卡片中,选择本机已注册的仓库,或限定一个文件夹,测试连接并保存,然后启用并分配给所用 Runtime。无需启动 Obsidian,也无需额外安装 Obsidian 插件或配置 REST 服务。
|
||||
|
||||
接入后,可以请助手查找笔记、读取内容,或将讨论结果写回仓库;也支持追加、局部修改、目录浏览,以及标签和属性操作。笔记仍保存在原仓库,不会自动复制成 GoodBuddy 知识库。
|
||||
|
||||
目前支持直连模型及 GoodBuddy 管理的本机 OpenCode、Continue 聊天,默认关闭;DeepSeek Harness 和远程执行暂不支持。Ask 只读,Execute 可写入;多仓库调用需要明确目标仓库。接入本机文件不等于离线推理,读出的内容会随当前会话交给所选模型处理。
|
||||
|
||||
> **【待补图 07|建议|Obsidian 接入与读写】** 已有候选 `obsidian.png` 展示仓库范围和 Runtime 分配,尚需确认构建来源;正文主图优先补公开笔记的搜索、写入与回读结果。计划文件:`assets/07-obsidian-notes.png`。
|
||||
>
|
||||
> 建议图注:按仓库范围接入本机 Obsidian,在聊天中查找笔记并保存整理结果。
|
||||
|
||||
### 从输入区打开本地原生客户端
|
||||
|
||||
本地项目选择 Continue 或 OpenCode 后,输入框下方可以打开工作栏内的原生交互终端;选择 DS 后,可以在系统默认浏览器中打开官方 DeepSeek Harness Web 客户端。启动时沿用当前项目目录与所选模型,适合希望使用 Runtime 自身交互界面的用户。
|
||||
|
||||
原生客户端使用独立会话,不会自动接续或同步 GoodBuddy 中的聊天历史。DS 网页关闭后,本地服务继续运行,可以回到 GoodBuddy 重新打开或停止;退出应用会清理所属服务。
|
||||
|
||||
当前入口支持本地项目,远程原生客户端尚未接通。Continue 与 DS Web 需要可用的标准 Node 环境,首次准备环境可能需要下载。Continue、OpenCode 原生终端在 Ask 下可使用已分配的内置只读工具,包括故事图谱查询;自定义 MCP 仍仅在 Execute 下接入。
|
||||
|
||||
> **【待补图 08|建议|原生终端与 DS Web】** 已有 `continue.png`、`opencode.png`、`deepseekweb.png` 候选;发布前确认构建来源,并处理本机路径、浏览器书签及与演示无关的会话。Continue 图含模型能力警告,建议换用合适模型重拍;不能据启动画面宣称完整对话验收通过。计划文件:`assets/08-native-clients.png`。
|
||||
>
|
||||
> 建议图注:在工作栏打开原生终端,或在系统浏览器中使用 DS Web 的独立会话。
|
||||
|
||||
### 复制模型连接,保留已有配置
|
||||
|
||||
在“设置 → 模型连接”中打开一条 LLM 连接,点击“复制”,即可在副本中修改模型名称、地址或其他选项,再保存为新连接。协议、请求定制和认证配置随副本带入,已保存的 API Key 无需重新输入。
|
||||
|
||||
复制后仍需保存才会生效;当前最多保留 20 条连接。保存地址有误时,界面会指出对应连接并定位到地址字段,便于处理多条连接中的配置问题。
|
||||
|
||||
### Mermaid 图表可导出完整 PNG
|
||||
|
||||
聊天中的流程图或关系图可以打开大图窗口,再导出完整 PNG。导出内容不受当前缩放比例和拖动位置影响,适合插入报告、文档或演示材料。
|
||||
|
||||
图片沿用当前主题背景,最长边上限为 4096 像素。此功能用于 Mermaid 图表,故事线图谱的图片导出不在本轮范围内。
|
||||
|
||||
> **【待补图 03|建议|图表导出】** 在正常聊天中打开一张公开示例 Mermaid 图,执行 PNG 导出,并核对图片包含整张图。优先展示实际导出结果,图注注明它是导出图片。计划文件:`assets/03-mermaid-export.png`。
|
||||
>
|
||||
> 建议图注:从 Mermaid 大图窗口导出完整 PNG,保留图中的边缘节点。
|
||||
|
||||
### 桌面通知可以单独关闭
|
||||
|
||||
“设置 → 平台功能 → 通用设置”新增“桌面通知”开关,默认开启。关闭后,应用处于后台时的任务完成、失败或取消等系统提醒停止弹出,应用内通知仍然保留。设置保存后立即生效,重启后也会记住选择。
|
||||
|
||||
## 功能改进
|
||||
|
||||
### 知识库文档与来源集中浏览
|
||||
|
||||
知识库将内容来源与文档索引合并为统一列表。单文档来源合成一行,目录继续按组展示,导入失败、尚未生成文档的来源也保留恢复入口。搜索可以同时查找来源与文档,网址来源保留原网址,便于辨认同名资料。
|
||||
|
||||
列表突出查看入口,打开原文、分块管理、重新解析等操作收进更多菜单。解析结果页也整理了返回导航和图片操作,减少长名称、多个按钮挤在一行的情况。
|
||||
|
||||
任务历史改为按批次展示,可以展开处理步骤,查看进行中、失败与历史记录。筛选与计数跟随当前文档或来源,便于检查一次导入的处理情况。
|
||||
|
||||
### 图谱按事件、实体和关系切换
|
||||
|
||||
故事线图谱侧栏将原来上下堆叠的列表改为“事件、实体、关系”页签,各自显示数量并独立滚动。切换页签保留已有选中对象,在画布选中对象时,侧栏会切到对应类别,查看关联信息时少一些来回滚动。
|
||||
|
||||
### 自动监督与手动回顾分开管理
|
||||
|
||||
原有的每日、每周计划现在集中到“自动监督”。新建或编辑计划时,可以选择全局或多个项目,设置时间、时区、回顾窗口和历史保留期限;暂停的计划在编辑保存后仍保持暂停。
|
||||
|
||||
自动报告与监督回顾分别记录处理进度。对于对应阶段没有新增、修改或待处理内容的情况,会跳过模型调用;手动回顾则重新分析选择的时间范围。删除报告或计划不会重置已经记录的来源处理进度。
|
||||
|
||||
启用监督者不会自动创建计划。定时工作需要桌面应用保持运行,关闭监督者会停止发起新的回顾,并保留计划和历史;从导航取消常驻只改变入口显示。尚无活动时,自动监督页会隐藏空的统计、建议和历史区域,先展示计划入口。
|
||||
|
||||
监督分析会将入选内容交给配置的模型服务,可能产生费用。它以只读、无工具的方式分析记录;自动报告与回顾可能分别调用模型,长时间范围也可能分成多批。并发数和超时设置不等于总费用上限。
|
||||
|
||||
### 长回顾可以看进度、保留批次并继续处理
|
||||
|
||||
监督者的“活动记录”集中展示手动和自动执行情况。展开记录,可以查看当前阶段、已保存批次、剩余来源和失败原因,再按项目、会话、批次检查已保留事实与来源片段。
|
||||
|
||||
回顾会分页读取选定范围内的来源,将长内容拆成批次处理。成功批次保存后,后续阶段失败不会把这些内容一并丢弃;暂停或失败后可以继续。0.13.16 还取消了按固定累计时长自动暂停的行为,长回顾可以持续处理至完成,无需隔一段时间手动续跑。
|
||||
|
||||
需要调整处理方式时,可在“监督者 → 设置”中配置模型并发、单次模型超时、每次读取的来源条数、每批消息和文本量,以及单次响应容量。新建回顾会保存分批配置;来源已被修改或删除时,需要重新发起回顾。
|
||||
|
||||
> **【待补图 04|必补|回顾进度与来源批次】** 展开真实运行的活动记录,展示阶段、已保存批次和来源正文。若展示暂停后继续,须通过真实操作完成,不注入运行状态。图 01 的演示可复用同一份记录。计划文件:`assets/04-review-progress.png`。
|
||||
>
|
||||
> 建议图注:从活动记录查看处理阶段与已保存批次,展开核对来源。
|
||||
|
||||
> **【待核对 C01|暂停与取消配图】** `v0.15.0` 已包含 `5116613` 的暂停控件修复与取消入口,见末尾“回顾运行期间的控制”。截图须对应目标版本,展示实际暂停、继续和取消操作。
|
||||
|
||||
分页和批次记录便于核查处理范围,模型给出的总结、实体和关系仍需人工判断;来源处理完成不代表每个事实都被模型准确理解。
|
||||
|
||||
### 待办、任务与用量更便于浏览
|
||||
|
||||
魔法笔记的待办页可以拖动列表与详情之间的分隔条,调整后的宽度会保存。来源笔记标题支持独立折叠,收起一个分组不会清空正在查看的任务详情。分组折叠状态不跨重启保存,窄窗口下不提供横向拖宽。
|
||||
|
||||
笔记详情中的记录索引开关移到返回按钮旁,索引和 AI 评论开关都补上了文字标签。任务中心展开列表后,可以直接选择“全部、进行中、暂停、已结束”;默认显示进行中的任务,历史任务仍可通过切换状态查看。
|
||||
|
||||
> **【待补图 05|建议|待办布局】** 用虚构笔记创建不同来源的待办,拖动分隔条并折叠一个来源组,保持另一项详情可见。计划文件:`assets/05-note-todos.png`。
|
||||
>
|
||||
> 建议图注:调整待办列表宽度,按来源笔记收起暂时不看的任务。
|
||||
|
||||
运行记录中的 Token 用量先显示项目或会话总计,展开后再看 Runtime 和模型明细,按模型分组的视图继续保留。这里汇总的是应用已有的用量记录,并非服务商计费账单。
|
||||
|
||||
### 回复期间减少重复读写和界面等待
|
||||
|
||||
任务统计转到后台查询,隐藏的任务面板保留操作状态并减少不必要的更新。较长任务列表也减少了屏幕外卡片的绘制工作。
|
||||
|
||||
流式活动更新时,只保存变化的记录,并减少连续保存过程中反复读取整份历史的次数。改动针对工具活动较多、运行记录较长的对话,保留原有内容与顺序;实际响应改善取决于历史规模和设备,不以测试中的数据库指标代替整机提速幅度。
|
||||
|
||||
### 设置页减少来回查找
|
||||
|
||||
长设置表单的分类标题与相关页签固定在顶部,请求 Header、Body 等低频选项收进高级设置。默认工作目录移到“平台功能 → 通用设置”,Skills 和 MCP 页面移除重复标题,保留导入与添加操作。
|
||||
|
||||
通用设置进一步按用途整理为快捷键、会话与通知、工作区与下载等区域,桌面通知、默认工作目录和模型下载源更容易找到。
|
||||
|
||||
HTTP OCR 高级设置可以直接选择是否保留页眉、页脚、页码等内容,并恢复默认过滤方式。版面与生成参数的分组和间距也重新整理;具体选项效果仍取决于所连接的 OCR 服务。
|
||||
|
||||
成功和提示通知采用更紧凑的卡片,两秒后自动消失;错误提示继续保留,直至手动关闭。删除会话的确认框也去掉了重复警告,永久删除的含义不变。
|
||||
|
||||
## 问题修复
|
||||
|
||||
### 远程回答完成后,可以继续对话
|
||||
|
||||
远程请求现在等待模型交付确认后再结束。回答已经完成时,随后回收空闲 Runtime 失败不会再把结果改成“未知”。确实遇到结果未知的请求,用户显式发送新消息后会建立新的运行绑定并带入已有历史;自动恢复仍只重新连接原请求,不会擅自重复执行结果未知的任务。
|
||||
|
||||
最后一个活动任务结束后,远程空闲 Runtime 会被释放,下一轮对话再恢复历史。仍有任务或待答问题的共享进程会保留;更新后被替换的旧 Agent 会等待已有工作结束再退出,当前 Agent 保持常驻。
|
||||
|
||||
Host 更新还会清理已确认停用的旧 Agent 程序和归属明确的孤儿进程,保留会话历史、活动安装和状态不明的安装。这些完成状态与进程清理修复自 Agent 0.13.4 起提供;本轮建议配套 Agent 0.15.0,仅升级桌面不会直接替换 Host 上已经运行的旧进程。
|
||||
|
||||
### 启动、搜索与回顾结果
|
||||
|
||||
- 修复正常聊天或保存笔记后,重启反复显示历史迁移的问题。普通数据库结构升级也不再因正常空闲页额外整理整库;确需转换的旧数据仍会处理,启动页区分实际操作。
|
||||
- 修复本机直连模型文件搜索参数受限和大结果截断的问题。模型可使用 ripgrep 原生参数,保留原生结果与退出码,并分页续读长输出。Ask 保持工作区只读,Execute 使用当前账号权限;这项变更不属于远程 Agent 或其他 Runtime 的新增工具。
|
||||
- 修复早期监督回顾只取部分会话、消息和正文的问题,改为对入选来源分页、分批处理。事实与来源独立保存,只有必要阶段完成并成功保存结果后才标记完成。
|
||||
- 修复监督回顾中的旧实体引用、摘要合并校验及长摘要报错。非法格式、未知引用和超出响应容量的结果仍会报错,成功保存的批次可以保留供后续处理。
|
||||
- 修复历史回顾入口跳到其他结果、切换后旧响应覆盖当前选择的问题。侧栏、活动记录和图谱按对应结果定位,人工确认与修订的内容保留。
|
||||
|
||||
### 日常界面操作
|
||||
|
||||
- 多个会话同时回复时,侧栏列表不再随流式文本或工具进度反复换位。会话按最新消息的创建时间排序,新消息仍会把会话向前移动,置顶会话继续优先显示。
|
||||
- 输入框的附件、语音与选项按钮统一尺寸、间距和悬停反馈,录音中和选项展开时仍保留对应状态提示。
|
||||
- 切回已查看的会话时,立即显示缓存的累计回复时长,再后台刷新;缓存限本次应用运行期间。
|
||||
- Mermaid 大图放大后,顶部和左侧等边缘仍可通过滚动、拖动到达,窄窗口中的控件可以换行。
|
||||
- Git 分支选择器悬浮在工作区上方,不再把文件列表和提交历史向下挤动;项目选择菜单可以浮出侧栏,长名称与路径有更多显示空间。
|
||||
- 聊天与浏览器、终端工作栏之间的分隔条恢复完整拖动区域;笔记缩略图与内容边缘对齐,键盘焦点轮廓保持可见。
|
||||
- 纯文本排队消息不再显示无效的“查看附件”按钮;浏览器页签关闭失败后重试成功,会清除残留错误。
|
||||
- 监督反馈显示会话或任务名称,固定与刷新按钮在窄侧栏中排列更紧凑。工作回顾和活动页使用可用宽度,运行阶段与已保存批次更醒目。
|
||||
|
||||
### 运行配置与界面修复
|
||||
|
||||
- 修复 Continue 禁用自动更新后仍显示“Checking for updates”的问题,避免空闲终端显示误导性的更新状态。
|
||||
- 补齐 DS Web、远程 Runtime 和模型桥辅助进程启动时的遥测关闭配置;模型、MCP 和用户主动发起的联网请求保持可用。远程遥测配置与 Continue 更新检查修复由 Agent 0.15.0 配套提供,对更新 Host 后新启动的 Runtime 生效,已有进程不会热更新。
|
||||
- 修复知识库不同来源的操作相互阻塞,以及多文档来源中任务范围、筛选计数不一致的问题。
|
||||
- 修复深浅主题中危险操作按钮、禁用状态、控件边框和键盘焦点显示不一致的问题,包括放弃笔记草稿的确认框。
|
||||
- MCP 编辑器和图片预览可以在父弹窗上正常操作;项目弹窗的 Escape 只处理当前弹窗,帮助层覆盖浏览器时不再被原生页面遮挡。
|
||||
- HTML 全屏预览恢复键盘进入与 Escape 返回,继续保持原有沙箱限制。
|
||||
- 窄工作栏的添加按钮不再被滚动箭头遮挡,模型连接协议文字保持可读;DS 入口移除常驻说明,重新打开和停止服务操作继续保留。
|
||||
|
||||
### 回顾运行期间的控制
|
||||
|
||||
回顾运行期间,范围、时间选择和刷新入口继续可用;修改选择不会改变已经启动的回顾。重复点击发起按钮会提示已有运行,避免同时创建重叠回顾。
|
||||
|
||||
活动记录增加取消正在运行回顾的入口,并修复继续回顾后无法再次暂停的问题。暂停保留进度供继续处理;取消会保留已成功保存的批次,但该次回顾不能再继续。取消清理结束前显示“正在取消”,未完成结果不会发布,之后可以新建回顾。
|
||||
|
||||
手动发起、继续与自动回顾共用执行限制;自动回顾在忙碌时等待,手动操作会提示当前已有运行。同一次回顾内部仍可按设置并行处理批次。
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 329 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 149 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 182 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 260 KiB |
@@ -0,0 +1,261 @@
|
||||
# 0.13.8 起综合宣传稿核查记录
|
||||
|
||||
## 2026-09-28 故事图谱读取与讨论编辑增量核查
|
||||
|
||||
本节为最新依据,下方记录保留各轮历史状态。本轮固定范围 `65a2139..66f77b83c2b6ebe0018a6c5c2bc72e1b4c1e43b1`,共 5 个提交;全文范围为 `v0.13.7..66f77b8`。保留工作区已有宣传稿修改,仅追加本轮变化,未纳入其他未提交功能文档。
|
||||
|
||||
| 提交 | 内容与入文处理 | 依据与边界 |
|
||||
| --- | --- | --- |
|
||||
| `66f77b8` | 新增助手只读查询故事图谱;更新继续讨论的完整正文编辑说明 | `story-graph-reader.ts`、`story-graph-tools.ts`、`knowledge-mcp-gateway.ts`、IPC、本地与远程 Runtime 接线;读取已有事实、上下文和来源,按精确 scope 查询,不发起回顾、不写图谱。支持 Model、Harness、本地及远程 OpenCode/Continue;远程须部署对应 Desktop/Agent 改动,不能归入 Agent 0.15.0 |
|
||||
| `66f77b8` | 同步修正原生终端 Ask 的旧限制 | `native-client-coordinator.ts`、`native-terminal-client.ts` 已放行 Main 分配的内置只读工具,自定义 MCP 仍仅 Execute。`SupervisionDiscussion.tsx` 与 `RightAssistantSidebar.tsx` 精确发送可见草稿,不重新拼接已删除引用,失败保留草稿 |
|
||||
| `013e72f` | 笔记章节补充入口及控件精简 | `App.tsx`、`MagicNotesPanel.tsx`、`MagicNoteSource.tsx`;工作栏与会话采集入口保留,移除侧栏/顶栏重复快捷按钮,操作靠右,保留输入可访问名称及校验。正文已有入口修订予以保留 |
|
||||
| `83a3ea2` | 官网文案、下载入口及移动布局,不进入应用功能正文 | `sites/index.html`、`sites/en.html`、`sites/styles.css`;未把网站宣传调整计作桌面新增功能 |
|
||||
| `6159039` | 诊断日志维护,仅记内部依据 | `desktop-diagnostics.ts`、`window.ts` 保留 Renderer 退出原因和退出码,便于排障;不据此宣称解决窗口崩溃或内存不足 |
|
||||
| `4e52392` | 打包修复不进入正文,只修正版本与升级依据 | 最新桌面 tag `v0.15.1` 承接未公开发布的 0.15.0,Agent 保持 0.15.0 并要求 Desktop 0.15.1;本轮其余提交均在此 tag 之后,已完成但发布归属待核验 |
|
||||
|
||||
故事图谱依据及历史验证见[监督实施进度](../../features/conversation-supervision/progress.md)与[工具设计](../../features/conversation-supervision/story-graph-mcp-design.md)。已提交记录包含真实 SQLite、MCP、受控 Linux Host 工具调用与讨论布局验证;没有把确定性推理探针写成真实模型建议质量验收,也没有宣称跨平台安装包已验收。跨范围事实复用、任意时刻历史重建和记忆读取替换未纳入宣传。
|
||||
|
||||
本轮仅静态核查提交并编辑文档,未运行产品测试、构建、模型请求或拍摄截图;配图缺口沿用原清单。使用 deai-writing 审校正文;未提交或对外发布。
|
||||
|
||||
交付检查:文字扫描阻断项 0、复核项 13、读取错误 0。按原文行号复核,保留版本、升级、素材和功能边界,以及真实设置分组名称;无须为清零删除限定。`git diff --check -- docs/pr-articles` 通过,仅有既有 LF/CRLF 提示;结束核查时 HEAD 仍为 `66f77b8`。
|
||||
|
||||
## 2026-09-28 截至 65a2139 的增量核查
|
||||
|
||||
本节保留上一轮核查结果;当前截止点、版本归属与控件行为以文首最新记录为准。
|
||||
|
||||
- 本轮固定范围 `0f8bf40..65a21390debe0dd04871417a363a9d87a29339e6`,共 11 个提交;目标提交带 `v0.15.0` tag。文章总范围为 `v0.13.7..65a2139`,标题沿用用户指定措辞。
|
||||
- 已确认版本归属,删除正文 R02;R01 保留公开发布状态、安装包和下载链接核验,C01 改为目标版本的操作配图核验。目录暂保留原路径,文章仍未发布。
|
||||
- 依据已提交 Agent 0.15.0 说明,远程升级顺序更新为 Desktop 0.15.0 后再更新 Host。遥测与 Continue 更新检查修复对新启动 Runtime 生效,已有进程不热更新。不把桌面 tag 当作 Agent 独立发布渠道或所有安装包已可下载的证明。
|
||||
- 工作区已有构建脚本、依赖与桌面/Agent 发布说明修改,均排除在本轮提交核查之外,未改动。
|
||||
|
||||
| 内容 | 入文处理 | 实现依据与边界 |
|
||||
| --- | --- | --- |
|
||||
| 桌面通知开关 | 新增功能,独立短节 | `f35198d`;`application-settings-contracts.ts`、`application-settings-store.ts`、`ipc.ts`、`PlatformFeaturesSettingsSection.tsx`。默认开启、持久保存、保存后立即生效;控制后台任务结果系统通知,应用内提示不变;未宣传操作系统必定送达 |
|
||||
| 通用设置分组 | 功能改进,合入设置节 | `f35198d`;按快捷键、会话与通知、工作区与下载整理入口 |
|
||||
| 流式会话排序 | 问题修复,日常操作首项 | `8e60736`;`chat-conversation.ts`、`assistant-database.ts`。置顶优先,其余按最新消息创建时间;流式 delta 与工具进度不反复重排,新消息仍可前移 |
|
||||
| 输入区工具按钮 | 问题修复,简述 | `ee1bf8a`;`App.tsx`、`styles.css`。附件、语音、选项尺寸/图标/间距/悬停统一,保留录音与展开状态 |
|
||||
| 监督继续讨论 | 更新现有主介绍,不重复列为独立新增 | `d47f967`;`RightAssistantSidebar.tsx`、`SupervisionDiscussion.tsx`、`SupervisorWorkspace.tsx`、`ipc.ts`。侧栏无需先开来源,可编辑问题;上下文仅取所选来源关联的事件/实体/关系与来源正文;图谱会话来源可打开会话、发送后导航。知识实体写回控件已移除;正文已有 `5cdffee` 修订,本轮保留并补齐细节 |
|
||||
| 自动监督空状态 | 功能改进,合入自动监督节 | `d47f967`;`HeartbeatCenter.tsx`。无运行、条目、记忆或跟进活动时隐藏空统计及历史等区域,不改变计划执行能力 |
|
||||
| 版本与配套资料 | 更新范围、升级提示和内部证据 | `5a6ba6c`、`d9e1e42`、`5cdffee`;版本说明、功能矩阵、文章和候选图片归档不再计作新功能 |
|
||||
| 测试同步与夹具修复 | 不进入宣传正文 | `1259496`、`b8b6007`、`706726b`、`65a2139`;聚焦/导航等待、Linux 窄面板键盘及知识库轮询夹具修正;发布提交中的测试调整也不作为产品能力宣传 |
|
||||
|
||||
历史验证:`65a2139` 记录完整冻结 Linux 套件 5371 通过、51 跳过,Windows App 300 通过及 typecheck/lint/双语说明校验通过。通知功能提交注明原生系统通知送达未人工验证;这些均为已有记录,本轮没有重跑产品测试、模型调用或构建。旧记录中早期失败和待验收状态不等于最新固定提交的测试结果,也不能用本轮静态核查代替跨平台实机验收。
|
||||
|
||||
本轮继续使用 deai-writing 审校新增文字;配图缺口不变,没有新截图。仅修改正文、核查记录和索引,不提交或发布。
|
||||
|
||||
交付检查:文字扫描阻断项 0、人工复核项 12;逐条保留必要的版本、内容和功能范围限定,“通用设置”一项为真实界面分组及入口列举。`git diff --check -- docs/pr-articles` 通过,仅提示既有行尾策略;正文无开发状态标签、打包修复条目或过时 R02。完成时 HEAD 仍为 `65a2139`。
|
||||
|
||||
## 2026-09-28 发布候选归档
|
||||
|
||||
- 用户授权将当前工作区宣传文档与素材一并提交、推送,并由代理自行审核后继续
|
||||
Desktop/Agent 0.15.0 发布,不再逐次等待提交确认。
|
||||
- 下方 2026-09-27 记录及正文中的 `0f8bf40` 截止点保留为当时的内容核查截面,
|
||||
其中“发布准备未提交”“Agent 实机未完成”等是历史状态,不代表当前发布进度。
|
||||
当前候选、实机与 CI 结果以 [0.15.0 发布准备记录](../../development/release-preparation-0.15.0.md)
|
||||
和两个独立发布渠道为准,正式版本未完成公开核验前不宣称已经发布。
|
||||
- 新增候选 `deepseekweb.png`、`obsidian.png`、`storygraph.png` 已逐张检查,
|
||||
未发现可见 API Key、密码或令牌。原图按用户授权归档,不裁切或改写;
|
||||
DS 图仍有本机用户名、路径和浏览器书签,图谱包含既有会话摘要,
|
||||
不能直接当作隔离公开演示素材。正式宣传配图仍遵循下方素材核验及重拍要求。
|
||||
- 本次提交原始候选素材不等于确认其构建 SHA,也不授权发布文章到外部媒体。
|
||||
不因候选截图归档宣称跨平台安装包或完整对话验收通过。
|
||||
|
||||
## 2026-09-27 增量更新
|
||||
|
||||
本节保留 2026-09-27 的核查结论;当前截止点、版本归属和后续变化见文首最新核查。
|
||||
|
||||
- 用户要求检查“上一次到目前最新提交中间的功能更新”,并更新已有草稿;沿用已确认的综合稿结构,直接补充。
|
||||
- 上次截止 `51166136157a646530e1ad9f6a0242c1c6390e95`;本轮固定至 `0f8bf405db7ca21a85cc70009ad083eb837e1e03`,共 17 个提交。全文总范围为 `v0.13.7..0f8bf40`。
|
||||
- 目标提交没有 tag;已核查正式版仍截至 `v0.13.16`。用户确认所述功能均已开发完成,正文已去除开发状态标签;后续已提交功能只保留发布版本待确认的说明。工作区的 `package.json`、锁文件、Agent Runtime 锁、功能矩阵和未跟踪 `release-preparation-0.15.0.md` 不作为已发布依据,也未修改。
|
||||
- 选题:保留工作回顾主线,新增聊天采集、Obsidian 与原生客户端三个独立使用场景;知识库统一列表和图谱页签归入改进;运行配置和交互问题归入修复。按用户要求,打包类修复只保留内部核查依据,不进入宣传正文。标题随覆盖范围更新。
|
||||
- `1094433` 将数据库格式升至 48。正文已替换仅适用于 0.13.15→0.13.16 的升级说明,明确完整备份和旧客户端回退限制。
|
||||
|
||||
### 本轮完整盘点
|
||||
|
||||
下表“实现”只指进入固定提交。逐项依据提交 diff、生产实现和已提交的功能记录;本轮未执行产品测试、打包、真实模型调用或远程 Host 验收。
|
||||
|
||||
| 内容 | 分类与入口 | 功能与边界 | 提交及证据 |
|
||||
| --- | --- | --- | --- |
|
||||
| 本机 Obsidian 接入 | 新增/设置→能力与工具→MCP | 注册仓库发现、指定文件夹、独立测试与保存、开关和 Runtime 分配;搜索、读取、写入、追加、局部修改等。默认关闭;直连模型及受管理本机 OpenCode/Continue 支持,DSH 与远程不支持;Ask 只读、Execute 可写;不是知识库复制或同步 | `55efca3`;`src/main/obsidian/obsidian-service.ts`、`src/shared/obsidian-tools.ts`、[Obsidian 进度](../../features/obsidian/progress.md) |
|
||||
| 聊天采集到魔法笔记 | 新增/助手消息、会话菜单 | 单条助手正文或完整会话用户/助手正文,预览编辑、追加或原子创建笔记;保留来源回跳,源删除后内容保留;不复制工具日志/附件,单记录 500 KiB,生成中不可采集 | `1094433`;`App.tsx`、`ChatTimeline.tsx`、`magic-notes-contracts.ts`、[笔记进度](../../features/magic-notes/progress.md) |
|
||||
| 快速笔记与草稿 | 工作栏“+ → 笔记” | 标题/正文搜索、摘要、文字追加;切项目/会话保留草稿,关闭/替换/禁用有确认,重启不恢复;仍是全局笔记,完整画布/待办编辑在工作区;采集本身不调模型,自动评论沿用设置 | `1094433`;`MagicNotesPanel.tsx`、`MagicNoteSource.tsx`、`use-magic-note-draft.tsx` |
|
||||
| 来源存储升级 | 兼容性/启动 | schema 48 保存来源元数据,不改写文件化正文;旧客户端不能直接打开升级数据库 | `1094433`;`assistant-database.ts`、笔记技术设计 |
|
||||
| 本地原生客户端 | 新增/聊天输入区 | Continue/OpenCode 工作栏终端、DS 系统浏览器 Web;沿用项目与模型,独立历史;DS 可复用、重开、停止,关闭网页不停止服务。远程协调入口未接通;Continue/DS 需标准 Node,托管准备可能联网;Continue/OpenCode 原生 Ask 暂无 MCP | `f81f342`;`native-client-coordinator.ts`、`native-terminal-client.ts`、`native-dsh-web-client.ts`、[客户端方案](../../features/assistant-workbar/runtime-native-client-proposal.md) |
|
||||
| DSH 依赖升级 | 新功能配套/内部版本 | DSH 0.1.7-rc.2、Cordis 4.0.4;不把依赖版本当模型名或稳定版承诺,正文并入原生客户端 | `f81f342`;提交中的 package/锁文件 |
|
||||
| 知识库统一列表 | 改进/知识库文档页 | 单文档来源合行、目录分组、失败来源保留、来源与文档统一搜索、紧凑拖放区域;归属语义不改变 | `5177b47`;`knowledge-workspace/DocumentsView.tsx`、[知识库界面](../../features/knowledge-base/ui-design.md) |
|
||||
| 文档操作及任务批次 | 改进兼修复/列表、解析预览、任务页 | 保留 URL,突出查看与更多菜单,解析结果导航和图片操作归位;按批次展开历史,计数/筛选跟随对象;多文档来源范围修正,不同来源操作不互相阻塞 | `ceb275b`、`67dedc2`;`DocumentsView.tsx`、`KnowledgeTasksView.tsx`、`DocumentResultPreview.tsx` |
|
||||
| 图谱侧栏页签 | 改进/故事线图谱 | 事件、实体、关系计数与独立滚动,切页签保留选择,画布选中联动类别;没有新增图谱导出或回放 | `40c108d`;`SupervisorWorkspace.tsx` |
|
||||
| 遥测配置传播 | 修复/启动 | 补到 DS Web、远程 Runtime、模型桥 helper;不影响主动联网,不是新设置页或无联网保证;对应 Agent 发布归属待定,不能归给 Agent 0.13.4 | `eb53c82`;`runtime-privacy-environment.ts`、Agent profile/helper |
|
||||
| Continue 更新状态 | 修复/原生终端 | 禁用更新时更新服务不再发出检查状态;不称终端卡死已修复,不涉及关闭桌面更新 | `0f0b6ac`;`continue-host-adapter.ts` |
|
||||
| DS Web portable 依赖 | 内部维护/不进入宣传正文 | 修正 CLI、嵌套依赖和 Web 资源复制,新增 CLI 入口存在性检查;仅保留核查依据,未据此宣称最终包完整验证 | `5ecc19d`;`build/native-dsh-resources.cjs`、`build/build-portable.cjs`、package 复制规则与测试 |
|
||||
| 控件、弹层与窄布局 | 修复/多页面 | 危险按钮、深浅主题、禁用与焦点、Continue 字段;MCP/图片嵌套弹层可交互、项目 Escape 范围、帮助与原生浏览器遮挡;HTML 键盘与 Escape;工作栏添加按钮和模型协议文字 | `8b15399`、`11facf0`、`2713a81`;`styles.css`、`dialog-focus.ts`、`StaticHtmlPreview.tsx`、`WorkbarShell.tsx`、[弹层复核](../../quality/overlay-review-2026-09-27.md) |
|
||||
| DS 输入区精简 | 改进/输入区 | 删除常驻独立会话/服务说明,保留重开与停止;正文合入界面调整,实际生命周期照实说明 | `0f8bf40`;`RuntimeNativeClientActions.tsx` |
|
||||
| IPC 与聊天组件抽取 | 维护/不单独宣传 | 模型设置 IPC、聊天历史组件及文档归属说明;不当作新增用户功能 | `4edf06b`、`44cd944`;`model-settings-ipc.ts`、`ChatHistoryPane.tsx` |
|
||||
| 导航测试修正及文章素材 | 维护/不单独宣传 | 嵌套页签断言修正;上一稿提交及 Continue/OpenCode 图片入库 | `4a2986c`、`11facf0`、`5ecc19d` |
|
||||
|
||||
### 验证证据与限制
|
||||
|
||||
- Obsidian 已提交记录包含 Windows Electron 设置操作、本机 OpenCode/Continue 读写及解包应用连接验证;未扩大为所有平台安装包或物理断网通过。
|
||||
- 聊天采集记录包含真实 App/Preload/IPC/SQLite、502 条历史、来源回跳、214px 面板输入和源删除保留;502 是样例,非产品上限。schema 和正文容量另查契约。
|
||||
- 原生客户端有本地真实模型历史证据,但部分组件/IPC 使用测试模拟;Continue 后续记录有完整 Windows 生产 App 操作。DS 正常 UI、最终 portable 和跨平台整体验收不能据此视为全通过。部分历史模型验证有 TLS override,不作为严格 TLS 验证证据。
|
||||
- 遥测修复有 16 项定向测试记录,修改后的 Agent 实机与后台联网监测尚未完成;DS 打包有 3 项复制规则回归,非最终包真实对话。
|
||||
- `2713a81` 全量记录为 5309 通过、3 失败、83 跳过;项目焦点修正后定向 331 通过,其余两个 HeartbeatCenter 断言由 `4a2986c` 修正,后续 96 项通过及 typecheck/lint 通过。最终固定提交未重跑全量,不能合写为全量全绿。
|
||||
- 知识库有定向及 Electron 布局回归,真实导入/解析链路未在对应改动轮重跑。历史证据详见[知识库进度](../../features/knowledge-base/progress.md)、[工作栏进度](../../features/assistant-workbar/progress.md)、[控件与响应式复核](../../quality/responsive-controls-review-2026-09-27.md)。
|
||||
|
||||
### 配图更新
|
||||
|
||||
已逐张读取四张现有图片,未改动或裁切原文件;`continue.png`、`opencode.png` 已由 `5ecc19d` 跟踪,`obsidian.png`、`deepseekweb.png` 是用户工作区未跟踪素材。图片只作候选,尚未绑定可核验构建 SHA,因此未直接作为最终配图插入正文。本轮没有新拍截图,也没有将上一轮构建检查结论冒称为本轮重新验证。
|
||||
|
||||
| 编号 | 内容与路径 | 最新状态 |
|
||||
| --- | --- | --- |
|
||||
| 01–05 | 原工作回顾、来源、图表、批次、待办图 | 仍待补,原计划保留;最终截图需反映最新图谱页签 |
|
||||
| 06 | 聊天采集与来源返回;`assets/06-chat-note-source.png` | 必补;现有四张图均不能证明该流程 |
|
||||
| 07 | Obsidian;候选 `obsidian.png`,计划 `assets/07-obsidian-notes.png` | 建议;现图设置可读、浅色中文,能展示范围和分配,不能证明读写结果;需核验构建来源或补拍公开示例 |
|
||||
| 08 | 原生客户端;候选 `continue.png`、`opencode.png`、`deepseekweb.png`,计划 `assets/08-native-clients.png` | 建议;前两张展示工作栏终端并含本机路径及聊天;Continue 有模型能力警告;DS 图含书签、头像和本机用户路径。发布前重拍或生成明确标注的脱敏副本,并确认构建来源 |
|
||||
| R01/R02/C01 | 最终版本、后续提交的发布归属、回顾控件 | 仍待最终安装包核验;R02 覆盖 0.13.16 之后已完成的功能与修复 |
|
||||
|
||||
### 本轮交付检查
|
||||
|
||||
仅更新正文、核查记录和索引;产品测试、构建、新截图及真实模型调用为 0。
|
||||
|
||||
- Python 3.13.13 执行 deai-writing 扫描:阻断项 0、复核项 16、读取错误 0。按命中行逐条核对:15 项为发布状态、功能范围、升级或取消语义等必要限定,1 项是“模型准确理解”的能力边界表述,保留。
|
||||
- 独立只读复核未发现重大事实过度宣称、版本混淆或前后矛盾;新增 8 个相对 Markdown 链接目标均存在,图 06–08 的编号、场景、文件名与优先级对应,R01/R02/C01 和索引一致。
|
||||
- 四张候选素材均已打开审阅;没有插入不存在的计划图片链接,没有把启动界面当作完整流程验收。
|
||||
- `git diff --check -- docs/pr-articles` 通过,仅提示既有 LF→CRLF 行尾策略。完成时再次确认 HEAD 仍为 `0f8bf405db7ca21a85cc70009ad083eb837e1e03`。
|
||||
- 未提交 Git、打 tag 或对外发布;既有版本准备与图片文件保持原状。
|
||||
|
||||
## 状态与范围
|
||||
|
||||
- 状态:草稿,未对外发布,截图待补。首次建档与检查日期:2026-09-26。
|
||||
- 用户要求:“0.13.8到目前的所有功能,准备一个新的草稿”。据此直接起草,未另设选题批准步骤。
|
||||
- 起点包含 0.13.8 的新增变化,因此比较基线采用其前一版 `v0.13.7`,而非排除 0.13.8 的 `v0.13.8..HEAD`。
|
||||
- 基线解引用提交:`fe093bc15f94d90c478e1e96fef0f9feb7b819e9`。
|
||||
- 本轮正式 tag 核查截止 `v0.13.16`:`0759d5dcffb3273411fe7c486bbcde904dd23e48`。Agent 配套截止 `agent-v0.13.4`。
|
||||
- 核查期间 HEAD 新增 `51166136157a646530e1ad9f6a0242c1c6390e95`。本稿固定到该提交,范围为 `v0.13.7..5116613`;其相对 0.13.16 的变化在正文单列未发布补充。最终宣传目标 tag 未定,沿用 `unreleased` 目录,不推测下一版号。
|
||||
- `0.13.14` 桌面发布失败,产品内容由 `0.13.15` 恢复版承接;Agent 发布独立。未在线验证各平台包及最新 CI,不把本地 tag 当作全部工件可下载的证明。
|
||||
- `8dce1d7` 的发布说明沿用了原 0.13.7 多项内容。已按基线实际 diff 去重,画布、PDF 批注、HTTP OCR 接入、远程 Continue、应用管理、会话置顶不重复当作新增。
|
||||
- 开始及后续检查均有其他任务的未提交修改,涉及 DSH Runtime、MCP、能力设置、构建与依赖、工作栏方案;随后出现 Obsidian 新目录。它们只登记为工作区进行中内容,未核验为已交付功能,不纳入正文。没有修改或撤销这些文件。
|
||||
|
||||
## 选题与篇幅
|
||||
|
||||
综合稿固定按“新增功能 → 功能改进 → 问题修复”排列,修复放最后。用户已要求直接起草,以下是编辑取舍记录,不冒称用户逐项确认了提纲。
|
||||
|
||||
| 主次 | 入文内容 | 理由与处理 |
|
||||
| --- | --- | --- |
|
||||
| 主线 | 监督者工作回顾、故事线图谱与来源、任务反馈后续操作 | 新增完整工作入口,用户可从历史记录整理进展、检查依据并继续讨论;展开介绍,同时说明采集范围与人工复核边界 |
|
||||
| 主线的使用改进 | 自动监督、活动记录、分页批次与续跑 | 决定长时间范围能否持续处理、失败后如何继续,单独放在改进段,不把原有每日每周计划重新包装成新增 |
|
||||
| 独立新增 | 模型连接复制、Mermaid PNG 导出 | 各自可直接完成一个新动作,短节说明入口与限制 |
|
||||
| 日常改进 | 待办调宽与分组折叠、任务筛选、用量、设置、历史读写 | 按使用场景合并;待办操作是现有工作区扩展,采用“功能改进”分类,不照抄 feat 前缀 |
|
||||
| 主要修复 | 远程完成确认与恢复、进程回收、启动迁移、搜索、回顾完整性 | 影响工作能否继续和数据处理可信度,保留具体条件及 Agent 配套要求 |
|
||||
| 简述修复 | 菜单、工作栏拖动、时长、队列附件、通知及页面布局 | 分别归入改进或修复,避免每个控件占一节 |
|
||||
| 未发布补充 | `5116613` 回顾控制、取消、单次执行限制 | 用户要求截至目前,因而纳入,但与 0.13.16 严格区分;最终版本确定后合并对应段落 |
|
||||
| 不进入正文 | CI 上传、测试调度与夹具、发布元数据、社群二维码、开发规划 | 清单保留追溯,不算应用内新能力;OSS 上传加速不能写成客户端下载提速 |
|
||||
|
||||
## 完整盘点
|
||||
|
||||
以下“已实现”指进入固定目标提交,不代表本轮完成所有平台实机验证。文件证据以 Git 对应提交为准,工作区文件可能继续变化。相同功能的连续修复合并计一项。
|
||||
|
||||
| 功能 | 分类/状态 | 原来与现在、入口 | 条件与边界 | 来源与验证线索 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 监督者工作回顾 | 新增/目标代码具备 | 应用中心启用监督者;全局或单项目、近 1/7/30 天回顾,查看总结、变化和未解决事项 | 默认关闭;来源为消息、任务当前信息、已有知识引用等,不扫描全部笔记或附件原件 | `7d487f9`、`bf8ece0`;`SupervisorWorkspace.tsx`、`supervisor-service.ts`、`supervision-review-store.ts`;见监督进度记录 |
|
||||
| 故事线图谱与来源 | 新增/目标代码具备 | 选取事件、实体、关系,查看保存的依据;实体确认、改名、撤销,关系确认、移除 | 按顺序浏览事件,不是完整状态回放;间距不代表时长;无关系类型编辑与撤销恢复入口 | `7d487f9`;`SupervisorWorkspace.tsx`、`assistant-database.ts`;有组件、存储和隔离视觉证据 |
|
||||
| 任务/会话监督反馈 | 新增/目标代码具备 | 任务中心项目概览下跟随目标、固定、刷新、查看来源和图谱 | 仅匹配目标结果;Experiment 未接入 | `7d487f9`、`b16fc90`、`c71c0ad`;`RightAssistantSidebar.tsx`、`workbar-contracts.ts` |
|
||||
| 继续讨论和本地实体预览 | 新增/目标代码具备 | 确认上下文后加入目标会话队列;预览后创建本地知识实体 | 继续内容上限 12000 字符,实体描述 2000;外部库只读;无关联会话的任务不能发送 | `7d487f9`;`ipc.ts`、`supervision-contracts.ts`、`RightAssistantSidebar.tsx`;IPC 定向验证 |
|
||||
| 活动与批次查看 | 新增/目标代码具备 | 查看手动/自动运行、范围、状态、阶段、事实与来源树 | 活动每页 50 条,详情每页 10 批;已保存批次可跨重启保留 | `bf8ece0`、`db5e43d`;`SupervisorActivity.tsx`、`SupervisionBatchDetails.tsx`、`supervision-review-store.ts` |
|
||||
| 自动监督增量 | 改进/目标代码具备 | 在既有每日/每周计划上接入监督;报告与回顾分开记录来源进度,无待处理输入时跳过模型 | 手动回顾重新分析区间;删除报告/计划不清检查点;关闭应用不执行计划 | `bf8ece0`、`db5e43d`;`heartbeat-service.ts`、`assistant-database.ts`;历史增量实模证据 |
|
||||
| 自动计划与开关 | 改进/目标代码具备 | 五页签分工,弹窗创建编辑,选择多项目、时间及时区、窗口与保留期限 | 窗口 1–720 小时、保留 1–365 天;启用不自动建计划;关闭保留数据,取消常驻仅影响导航 | `bf8ece0`、`db5e43d`;`HeartbeatCenter.tsx`、`HeartbeatSettings.tsx` |
|
||||
| 分页、批次、失败续跑 | 修复兼改进/目标代码具备 | 替代有限条数截断;分页读取、长文本分片,成功批次独立保存,合并及发布失败可重试 | 来源变化需新回顾;在途输出不保证保存;处理覆盖不等于模型语义无遗漏 | `db5e43d`;`supervision-production.ts`、`supervisor-service.ts`、`supervision-review-store.ts`;506 来源隔离存储回归等 |
|
||||
| 监督参数 | 改进/目标代码具备 | 设置并发、超时、读取条数、批次文本及消息量、响应容量 | 默认并发 1,范围 1–4;报告/整理单次超时默认 240 秒,范围 30–600;容量默认 1024 KiB,范围 100–16384;没有总费用预算 | `db5e43d`、`e6ac073`;`SupervisionReviewSettings.tsx`、`supervision-review-contracts.ts` |
|
||||
| 连续处理与结果校验 | 修复/截至 0.13.16 | 取消累计固定时长暂停;修复旧实体引用、导航输出校验、任意摘要长度限制,精确定位历史结果 | 主动暂停、单次超时仍保留;0.13.16 续跑期间同卡片暂停控件存在缺口 | `db5e43d`、`e6ac073`;生产服务与契约;定向/实模证据见下文 |
|
||||
| 模型连接复制 | 新增/0.13.8 | LLM 连接详情复制地址、认证、协议、模型和请求定制,保存副本 | 最多 20 条;已保存密钥在主进程复制,不回传明文 | `8dce1d7`;`SettingsPanel.tsx`、`runtime-settings-store.ts`,组件和凭据复制测试 |
|
||||
| 模型地址错误定位 | 修复/0.13.8 | 保存时显示错误连接名称、切回连接并关联 URL 字段 | 校验格式不是验证服务可用;HTTP/HTTPS、最多 2048 字符 | `8dce1d7`;`SettingsPanel.tsx`、`contracts.ts`,组件/schema 回归 |
|
||||
| 设置导航与操作归位 | 改进/0.13.8 | 长表单标题页签吸顶;高级参数折叠;工作目录归入平台通用;Skills/MCP 标题精简与操作归位 | 既有能力的入口调整,不扩大 Runtime 或 MCP 支持范围 | `8dce1d7`;`SettingsPrimitives.tsx`、`PlatformFeaturesSettingsSection.tsx`、相关设置组件及测试 |
|
||||
| HTTP OCR 过滤设置 | 改进/0.13.10 | 直接选保留内容,恢复默认;重排参数间距 | HTTP OCR 接入本身基线已有;取决于远端服务 | `6c4c054`;`HttpOcrSettings.tsx`、文档解析设置测试 |
|
||||
| 任务筛选与长列表 | 改进/0.13.8 | 展开即可选全部/进行中/暂停/已结束,默认进行中;减少屏幕外绘制和重复查找 | 不删除历史,不新增分页;无整机性能倍数结论 | `6739e8e`;`RightAssistantSidebar.tsx`、`task-center.css` |
|
||||
| 后台执行统计与隐藏面板 | 改进/0.13.10 | 统计移至只读 worker,任务面板隐藏后保留状态并减少更新 | 非所有数据库操作后台化,浏览器和终端保留所需生命周期 | `a0b35d1`;`execution-stats-reader.ts`、`execution-stats-worker.ts`、`WorkbarShell.tsx`;worker 和组件测试 |
|
||||
| 用量汇总 | 改进/0.13.12 | 项目/会话先总计,展开 Runtime/模型;按模型视图保留 | 基于应用记录,不是账单;命中率按汇总分子分母算 | `cdd5737`;`ActivityPanel.tsx`、`token-usage.ts`;25 项定向记录 |
|
||||
| 回复时长缓存 | 修复/0.13.12 | 切回已查看会话先恢复时长再刷新 | Renderer 生命周期内缓存,不跨重启、不推算未知执行时长 | `f754120`;`use-execution-stats.ts`;15 项定向记录 |
|
||||
| 活动历史读写 | 改进/0.13.12–0.13.13 | 仅保存变化记录,复用已提交历史减少全量读取,保留顺序与重复 ID | 缓存随关闭、清空和外部提交失效;保留回滚与重试 | `984f16f`、`bf8ff7f`;`assistant-database.ts`、`activity-history-io.test.ts`;受控 WAL 和扫描测试 |
|
||||
| 待办布局 | 改进/0.13.15 承接 | 魔法笔记待办拖动/键盘调宽并保存宽度,来源分组独立折叠 | 窄布局不调宽;折叠状态仅组件内;保留选中详情 | `f68584b`;`MagicNotesWorkspace.tsx`、`MagicTodoDirectory.tsx`;115 项含原生输入验证 |
|
||||
| 笔记控件与索引 | 改进及修复/0.13.12–0.13.15 | 索引开关移至返回按钮旁,索引及 AI 评论显示文字;缩略图贴齐边缘、焦点可见 | 不改笔记正文、画布能力或缩略图生成 | `0265cfb`、`344008a`;笔记工作区、样式与 Electron 布局测试 |
|
||||
| Mermaid 导出及查看 | 新增及修复/0.13.12 | 完整 PNG 导出;放大后边缘可达,弹层覆盖窗口,窄屏控件换行 | 当前主题,最长边 4096;不等于故事图导出 | `785b1ee`;`MermaidDiagram.tsx`、`markdown-mermaid.css`;58 项含 9 Electron 场景、10 次 PNG 下载 |
|
||||
| Git/项目菜单 | 修复/0.13.12–0.13.15 | 分支菜单悬浮不挤内容,保留搜索与焦点;项目菜单通常 380px、窄屏收窄 | 受视口约束,不是新增 Git 或项目类型 | `fb4b9bd`、`0ed1be1`;`WorkspaceGitTools.tsx`、`ProjectSwitcher.tsx`;组件与 overlay 验证 |
|
||||
| 工作栏与监督布局 | 修复/0.13.13–0.13.16 | 恢复分隔条 9px 命中区域;反馈名称、按钮、排序修正;回顾和活动铺满宽度,阶段、批次突出 | 布局变化不等于新增监督目标或执行权限 | `cebd513`、`b16fc90`、`c71c0ad`、`d4bc10c`、`325b702`;组件、CSS、隔离 Electron 视觉证据 |
|
||||
| 队列附件与浏览器错误 | 修复/0.13.12–0.13.15 | 依据保存输入判断附件;纯文本隐藏按钮;关闭页签重试成功清除错误 | 首次关闭失败保留页签,不保证底层失败自动恢复 | `d5f6dc6`、`bf8ece0`;`ConversationInputQueue.tsx`、`RightAssistantSidebar.tsx`、数据库与组件测试 |
|
||||
| 通知和删除确认 | 改进/0.13.10–0.13.12 | 紧凑通知,成功/提示 2 秒消失,错误手动关闭;删除确认去重复文案 | 永久删除语义不变,非系统通知策略变化 | `a0b35d1`、`be84534`;`App.tsx`、`styles.css`;假时钟回归 |
|
||||
| 远程空闲进程及旧 Agent | 修复/Agent 0.13.3 起 | 最后任务完成后释放 Runtime,下轮恢复历史;退役 Agent 排空已有工作后退出 | 当前 Agent 常驻;保留活动任务及待答问题;旧进程不热更新 | `83a48a4`;`runtime-acp-backend.ts`、`daemon.ts`、`cli.ts`;213 项生命周期及 Linux 实机记录 |
|
||||
| 远程完成确认与续聊 | 修复/Agent 0.13.4 配套 | 交付确认后完成,清理失败不改已完成结果;未知后显式新消息新绑定带历史 | 自动恢复仅 attach,不重放未知任务;不抢占仍活动请求 | `7adae97`;`model-bridge-broker.ts`、`agent-owned-acp-prompt.ts`、`acp-remote-runtime.ts`;Linux 双轮 Ask 探针及回归 |
|
||||
| 旧安装清理与远程名称 | 修复及调整/0.13.12–0.13.15 | 清理确认停用 payload 与所属孤儿;移除“技术预览”标识 | 保留未知归属、活动安装、共享 Runtime 和历史;远程项目默认仍关闭 | `83a48a4`、`7adae97`;`installation-cleanup.ts`、`remote-environment-preparer.ts`;Linux 隔离清理验证 |
|
||||
| 直连原生搜索 | 修复/0.13.15 承接 | workspace_rg 原生 args/cwd、输出/退出码,大输出由 output_read 续读 | 本机直连路径;Ask 工作区只读,Execute 当前账号;句柄随会话释放 | `ff746bd`;`direct-model-ripgrep.ts`、`model-tool-provider.ts`;169 通过、1 跳过及真实 Ask/Execute |
|
||||
| 正常重启重复迁移 | 修复/0.13.8–0.13.9 | 不因聊天/文件化笔记写入形成的空闲页再触发迁移 | 保留真正旧数据转换与失败重试 | `8dce1d7`、`38d0923`;`assistant-storage-upgrade.ts`、`assistant-storage-startup.ts`;SQLite、启动回归 |
|
||||
| 结构升级与空间回收 | 修复/0.13.16 | 结构、内容转换、空间回收分开,普通结构更新不全库 VACUUM,启动文案准确 | 必要转换仍执行;仅同次启动保留重试意图;schema 仍为 47 | `4c7193e`;数据库/升级服务、`StorageUpgrade.tsx`;47 项定向 |
|
||||
| 回顾取消与避免重叠 | 修复及扩展/未发布 | 控件运行期间可用;继续后可暂停/取消;手动与自动共享单次执行限制 | 取消保留已存批次,不能继续,不发布未完成结果;清理期间仍占执行位置;自动等待、手动提示忙;范围选择不改当前运行 | `5116613`;`supervisor-service.ts`、`SupervisorActivity.tsx`、`SupervisorWorkspace.tsx`;38 UI、27 后端、1 Preload 定向记录 |
|
||||
|
||||
### 内部维护及不入正文条目
|
||||
|
||||
| 条目 | 来源 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| OSS 上传加速 | `e6a25b7`,随 0.13.11 | 发布上传改动,无独立客户端功能,不写下载提速 |
|
||||
| 测试 worker 调度、OpenCode 隔离路径、异步 UI 与大桥接载荷超时 | `984f16f`、`163e245`、`bb8c04a`、`2ca19eb`、`cd27163` | 测试维护,不把用例超时调整写成生产模型超时变化 |
|
||||
| 发布元数据、功能矩阵、保留分支历史与恢复版 | `598c68f`、`bf8ece0`、`7756231`、`d23af99`、`107dc97`、`e8d93b5`、`0759d5d` 等 | 用于范围和升级依据,不重复计功能 |
|
||||
| 社群二维码更新 | `4366bdc`、`24bde0c` 及合并提交 | 项目资料更新,不作为应用功能 |
|
||||
| 迁移夹具与架构规范 | 相关发布/迁移提交 | 只作验证与维护证据 |
|
||||
| 逐事件状态回放、事件触发监督、Experiment、自动纠偏接管、完整知识正文双向编辑、费用预算、私有应用市场 | 设计文档或后续规划 | 未作为当前功能宣传;图谱能力以生产组件为准 |
|
||||
|
||||
## 事实与验证边界
|
||||
|
||||
主要交叉核查资料:
|
||||
|
||||
- [监督实施记录](../../features/conversation-supervision/progress.md)、[监督设计](../../features/conversation-supervision/technical-design.md)、[故事图 PRD](../../features/story-graph/prd.md)。设计目标不等于已实现事实。
|
||||
- [0.13.12 准备](../../development/release-preparation-0.13.12.md)、[0.13.14 联合准备](../../development/release-preparation-0.13.14.md)、[0.13.15 恢复版](../../development/release-preparation-0.13.15.md)、[0.13.16 准备](../../development/release-preparation-0.13.16.md)。
|
||||
- [版本更新说明](../../../resources/release-notes.json)、[Agent 0.13.4 说明](../../../resources/agent-release-notes/0.13.4.md)、[笔记备份说明](../../features/magic-notes/technical-design.md#backup-and-restore)。
|
||||
|
||||
本次为只读代码/提交核查与文档编辑,未重跑产品测试、未构建或打包、未调用真实模型。下列均为历史验证记录,不能写成本次实测:
|
||||
|
||||
| 对象 | 已有证据 | 限制 |
|
||||
| --- | --- | --- |
|
||||
| 回顾覆盖与恢复 | 隔离 SQLite 22×23 共 506 个来源,长文本重建、跨重开、合并/发布失败与来源变化测试 | 不证明所有模型都正确提取全部事实 |
|
||||
| 回顾真实模型 | 27 条消息、9677 码点,2 次提取、1 次合并,保存 53 条结构化记录;自动增量首次 2 次、重复 0 次、修改后 1 次的记录 | 更大 129 条消息样本第二次 JSON 无效,首批保留,不能写大样本全程已跑通 |
|
||||
| 持续回顾 | e6ac073 的 13 项定向测试超过旧预算仍完成,主动暂停保留批次 | 非真实模型长时间压力验证 |
|
||||
| 布局 | 325b702 记录 33 项组件、40 个 Electron fixture 场景通过 | 不是正式安装包完整端到端验收 |
|
||||
| 远程 | Linux x64 真实 Host 生命周期、两轮 Ask 完成与历史恢复、隔离清理探针 | 不扩大到所有架构,未覆盖所有异常注入和正式更新 UI |
|
||||
| 原生搜索 | 成功一轮真实 Ask/Execute 分页验证精确 6 次请求;定向 169 通过、1 跳过 | 早期失败轮无完整计数;非所有平台完整 UI |
|
||||
| 历史 I/O | 1 万条历史受控回归,连续保存减少 WAL 增量和全量读取 | 不宣传整机“百倍提速”或真实磁盘吞吐收益 |
|
||||
| 0.13.16 候选整体 | 5128 通过、84 跳过、1 失败;失败 Mermaid Electron 随后独立复跑通过;类型、lint、说明校验通过 | 不能改写成全量零失败,准备文档的 CI/打包待核验状态保持原义 |
|
||||
| 5116613 | Renderer 38 通过;后端/IPC 筛选 27 通过、144 跳过;Preload 1 通过;定向 ESLint、diff 检查 | 无该轮 Electron 端到端、全量、类型检查或真实模型验证;记录中的“未提交”为提交前遗留措辞 |
|
||||
|
||||
## 截图准备与缺口
|
||||
|
||||
已读取 [Electron UI 自动化规范](../../quality/electron-ui-automation.md) 并检查已有产物。当前平台 Windows;拟用中文、浅色主题、1280×800 内容区,实际运行尺寸尚未取得。
|
||||
|
||||
- `dist/GoodBuddy-windows-x64/` 的便携标记及 `resources/app.asar` 版本均为 0.13.13,不能作 0.13.16/5116613 的产品截图。
|
||||
- `out/` 有 Main、Preload、Renderer,也含新取消逻辑,但未找到可绑定目标 SHA 的构建身份。含某段新逻辑不证明三部分产物一致。
|
||||
- 两个 portable staging 目录缺少完整 `resources/app.asar`,不能认定为目标包。
|
||||
- 因无可确认匹配的生产产物,本次未启动应用/fixture,未做本地生产构建,未拍图;不能将视觉测试假数据或旧版图伪装成新版本实机结果。没有创建空 assets 目录或不存在的图片链接。
|
||||
|
||||
| 编号 | 优先级 | 场景与计划文件 | 当前状态 |
|
||||
| --- | --- | --- | --- |
|
||||
| 01 | 必补 | 真实工作回顾结果;`assets/01-work-review.png` | blocked:目标生产构建身份未确认 |
|
||||
| 02 | 必补 | 同一结果图谱与来源;`assets/02-story-sources.png` | blocked:同上,需真实生成结果 |
|
||||
| 03 | 建议 | Mermaid 正常 UI 导出并打开核对;`assets/03-mermaid-export.png` | blocked:同上 |
|
||||
| 04 | 必补 | 同一回顾的阶段与保存批次;`assets/04-review-progress.png` | blocked:同上,区分 0.13.16 与后续取消控件 |
|
||||
| 05 | 建议 | 虚构笔记待办调宽与折叠;`assets/05-note-todos.png` | blocked:同上 |
|
||||
| R01 | 必填 | 最终版本、下载、日期、完整更新说明 | 尚未在线核验,不编造 |
|
||||
| R02 | 必处理 | 5116613 的最终归属 | 已提交但未归入目标 tag;发布前核验对应版本并处理小节中的编辑提示 |
|
||||
| C01 | 必核对 | 续跑期间暂停与取消控件版本 | 保留具体差异,待匹配最终安装包 |
|
||||
|
||||
取得可核验产物后的步骤:在本轮独立临时目录建立 Node supervisor 与 Electron bootstrap,ready 前隔离 userData/sessionData,通过生产 Main/Preload/Renderer 启动;正常 UI 创建虚构项目、消息、任务和笔记。回顾演示仅发送所需最小模型请求并从实际请求边界计数。检查保存与重新打开、对象来源、暂停续跑后,逐张 capturePage 并打开审阅,确认无私人资料、裁切、错误状态;只归档实际采用图片,停止本次进程并清理演示 profile。
|
||||
|
||||
## 文案审校与交付检查
|
||||
|
||||
采用 deai-writing 1.3.0,读者为 GoodBuddy 日常用户;标题和正文以操作与结果为主,保留必要的数据、费用、版本与功能边界。不引入虚构效率数字、用户评价或发布状态。
|
||||
|
||||
交付检查结果:
|
||||
|
||||
- Python 3.13.13 执行 `deai_scan.py article.md --json`:阻断项 0、复核项 13、读取错误 0。逐条审阅:12 项是编辑状态、升级兼容、发送限制、回放能力或取消语义等必要否定表述;另 1 项命中“模型准确理解”,用于说明处理覆盖不保证语义正确,保留原义。未机械删除功能边界。
|
||||
- 独立只读复核按固定提交检查正文与清单,未发现重大过度宣称、明确功能遗漏或版本混淆。11 个相对 Markdown 链接目标及备份锚点存在;图 01–05、R01、R02、C01 与核查表对应。
|
||||
- `git diff --check -- docs/pr-articles` 通过;仅提示既有 Git 行尾策略可能将 LF 转 CRLF。新增文件另作 no-index 空白检查。
|
||||
- 已通读结构与篇幅,主要能力展开,控件调整合并,未把同一能力在三类中重复当作新增;性能无虚构量化承诺。
|
||||
- 本次产品测试、构建、截图与真实模型请求均为 0。草稿所用验证数字来自已提交历史记录,不是本次执行结果。
|
||||
|
||||
本次只改文章、核查记录和索引,不提交、不打 tag、不对外发布。交付仍为带编辑提示的草稿;待补图片和发布信息见上表。
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 394 KiB |
@@ -386,3 +386,4 @@ Agent 专题使用对应的 `agent-v…` tag。
|
||||
| 日期 | 目标 tag | 标题 | 状态 | 正文 | 发布链接 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 2026-09-19 | 未确定(unreleased) | GoodBuddy 更新预览:画布笔记、文档处理与应用管理 | 草稿,截图待补 | [综合更新稿](./2026-09-19-unreleased/article.md) | 未发布 |
|
||||
| 2026-09-26(09-28 更新) | v0.15.1(4e52392)及后续至 66f77b8;汇总 v0.13.8 起的变化,后续提交发布归属待确认 | GoodBuddy 更新:故事图谱、Obsidian接入、Runtime原生终端 | 草稿,已补故事图谱读取与完整讨论正文编辑;配图与公开发布信息待核验 | [综合更新稿](./2026-09-26-unreleased/article.md) | 未发布 |
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# 浮层审查修复记录
|
||||
|
||||
日期:2026-09-27。本阶段处理全局样式审查中的五项浮层问题。既有 MCP 子 Modal 修复和
|
||||
[共享控件阶段](shared-controls-review-2026-09-27.md)保持原实现,菜单危险色覆盖未重复修改。
|
||||
|
||||
## 修复与验证
|
||||
|
||||
| 入口与问题 | 实现 | 实际验证 |
|
||||
| --- | --- | --- |
|
||||
| 附件解析结果打开大图,图片被父遮罩盖住 | 图片遮罩复用 `--z-dialog`;解析正文及图片回调保持稳定,关闭后原触发节点仍在 | 完整 App、浅深主题;读取窗口合成截图中大图中心红色像素,检查父层 inert,Escape 返回图片入口,再关闭解析层 |
|
||||
| 同行帮助被原生浏览器盖住 | 共享 occlusion selector 增加 tooltip,内容矩形沿用相交检测及 ResizeObserver | 运行记录页真实 InlineHelp;1600px 窗口与工作栏最小宽度下不相交,1000px 窗口重开工作栏并调至最大宽度后相交;检查真实 WebContentsView 隐藏/恢复,实例 ID 与页面内标记不变 |
|
||||
| HTML 全屏无法键盘进入 iframe | iframe 加入共享焦点候选,显式进入及返回守卫;内部 Escape 使用受限窗口通知 | 完整 App、生产 Main/Preload;带链接预览正反向进入/返回及内部 Escape,纯正文进入、PageDown 和内部 Escape;保留空 sandbox 与禁脚本 CSP |
|
||||
| 窄侧栏项目 Modal 的 Escape 同时关闭两层 | ProjectSwitcher 接入 `activateModalFocus`,忽略已处理或被隔离弹层的按键 | 760px 窗口侧栏中新建项目,一次 Escape 仅关闭项目且焦点回到项目入口,第二次才关闭侧栏 |
|
||||
| 知识引用遮罩仍是原生拖动区域 | 加入共享 no-drag;自动发现测试按 fixed/inset 扫描,覆盖 `-dialog` 类名 | 完整 App 点击知识引用;Windows 原生命中返回 HTCLIENT,临时清除 no-drag 的对照返回 HTCAPTION |
|
||||
|
||||
解析大图路径另暴露了焦点恢复问题:ReactMarkdown 的图片组件在 App Context 更新时被
|
||||
重建。稳定 `SectionContent` 与图片页签回调后,返回焦点断言通过,没有改为聚焦无关按钮。
|
||||
|
||||
HTML 键盘桥只转发主窗口直接 srcdoc 子 frame 的 Escape keyDown。窗口级单测另检查
|
||||
主 frame、普通网页、间接子 frame、Tab、keyUp 不转发,以及监听释放后不再发送。
|
||||
它不为预览授予 scripts、same-origin、forms 或 navigation 权限。技术接线由
|
||||
[HTML 渲染技术设计](../features/conversation-html-rendering/technical-design.md#ui-与无障碍)维护。
|
||||
|
||||
## 测试组织
|
||||
|
||||
真实路径位于 [overlay-paths-main.ts](../../tests/support/overlay-paths-main.ts),由
|
||||
[overlay-layering.electron.test.ts](../../tests/overlay-layering.electron.test.ts)启动。App、
|
||||
preload、窗口事件 IPC、BrowserService 及 viewport 调用使用生产实现;解析结果及图片是
|
||||
受控数据,其他无关启动服务使用 fixture。测试使用独立 profile/SQLite 和本地 HTTP 网页,
|
||||
不启动模型,模型请求尝试为 0。
|
||||
|
||||
设置 `GB_OVERLAY_PATHS_ARTIFACTS` 可保留浅深大图、两种帮助布局的 PNG 与结果 JSON。
|
||||
原生命中在 Windows 使用 WM_NCHITTEST;其他平台只执行其余行为和 computed no-drag 检查。
|
||||
测试启动器退出后删除隔离 profile 和构建目录。
|
||||
|
||||
此前漏测的原因分别是:只检查 Modal DOM 存在、原生遮挡用例未枚举 tooltip、HTML 用例
|
||||
未用真实 Tab 穿越 frame、项目和侧栏单独测试,以及遮罩自动发现依赖类名后缀。本次回归
|
||||
对应加入组合路径与原生断言。
|
||||
|
||||
## 验证边界
|
||||
|
||||
本机验证为 Windows、Electron 43.2.0,DPR 1。未把解析 fixture 当作真实 OCR 验收;未执行
|
||||
macOS/Linux 原生输入、全页面缩放矩阵;本阶段之后的全仓结果见
|
||||
[最终验证汇总](responsive-controls-review-2026-09-27.md#全部样式修复最终验证)。
|
||||
|
||||
已执行的定向回归:
|
||||
|
||||
- Renderer 共享控件、浮层、浏览器租约、Markdown、ProjectSwitcher、DocumentResultPreview 及窗口事件:9 文件、176 项通过。
|
||||
- Main IPC、窗口事件、Preload sandbox 及画布 IPC:4 文件、197 项通过,1 项原有跳过。旧 WebContents fixture 补充了事件订阅/移除方法,没有给生产代码添加 mock 专用兼容分支。
|
||||
- App Modal 与侧栏筛选:分别 4 项和 15 项通过,其余由名称筛选排除。
|
||||
- `tests/overlay-layering.electron.test.ts`:3 项通过,包含原有菜单/通知、MCP 完整 App 以及本阶段五条路径。MCP 保留四个浅深/短窗口/浏览器场景及生产保存重读断言。
|
||||
- `tests/shared-controls.electron.test.ts`、`tests/inline-help.electron.test.ts`、`tests/magic-notes-capture.electron.test.ts`:通过,保留前一阶段 338 组 computed style 与危险按钮回归。
|
||||
- 最终调整项目初始焦点与 iframe 事件归属断言后,`MarkdownRenderer.test.tsx`、`ProjectSwitcher.test.tsx`、`window-ipc.test.ts` 共 53 项通过;整个 overlay Electron 文件再次 3 项通过。
|
||||
- `npm run typecheck`、`npm run lint`、`git diff --check`:通过。新增质量记录中文审校无阻断项;键盘事件枚举保留为技术测试条件。
|
||||
|
||||
最终测试已通过 `GB_OVERLAY_PATHS_ARTIFACTS` 导出 PNG 与结果 JSON,并人工查看浅深图片
|
||||
Modal 和帮助相交截图。浏览器可见性以 Main 中实际 WebContentsView 状态为准,Renderer
|
||||
截图不作为原生视图已隐藏的替代证据。
|
||||
|
||||
扩大验证曾发现窗口先销毁再执行 IPC dispose 的清理错误,已改为使用注册时捕获的
|
||||
WebContents 引用,单测及 MCP Electron 复跑通过。测试脚本也修正了异步窗口尺寸/工作栏
|
||||
展开的等待条件和旧 fixture 接口,未放宽像素、焦点、命中或浏览器可见性断言。
|
||||
@@ -43,6 +43,20 @@ main CI `36164698647`:原生能力重试用例在启动状态未稳定时打
|
||||
是否消除 CI 失败以修复提交的 Linux CI 结果为准。
|
||||
同日 Windows 全量测试及独立复跑中,Magic Notes 原生拖拽分别停在索引列 168 和
|
||||
AI 列 280。测试同步原生输入与实际状态后独立复跑通过,保留原有宽度和持久化断言。
|
||||
2026-09-27 的 0.15.0 主分支 CI 中,会话采集测试向隐藏 Electron 窗口发送 Escape,
|
||||
未返回笔记列表。原生键盘辅助函数同样必须显示窗口、取得焦点并等待
|
||||
`document.hasFocus()`,再发送输入;不靠跳过 Linux 或关闭沙箱处理。
|
||||
随后 Linux CI 与共享 Host 的实测表明窗口焦点并不足够:缩窄窗口会收起工作栏,
|
||||
隐藏面板仍有非零宽度,Escape 实际发给工作栏开关。测试须等待响应式状态完成,
|
||||
按真实入口重新打开工作栏,并确认目标输入框取得焦点,再测试键盘返回和导航。
|
||||
验证输入框自动聚焦时,Electron 测试窗口也应从开始即显示并取得焦点,不要到后续
|
||||
键盘阶段才显示;失败信息应包含实际焦点、输入框禁用/可见状态及保存状态。
|
||||
取消边界测试不使用 `setTimeout(0)` 猜测异步 I/O 的完成次序。Continue 启动前取消
|
||||
用例改在既有启动环境回调处触发中止,仍断言未启动子进程及临时配置清理。
|
||||
2026-09-28 完整候选 CI 又暴露两处同类等待缺陷:采集用例以
|
||||
`activeElement.textContent` 包含消息文字判断导航完成,`body` 也会满足,随后真正的
|
||||
消息聚焦会关闭刚打开的菜单。应同时断言焦点元素是消息 `article`,不只匹配后代文本。
|
||||
原生 Runtime 控件用例须等当前项目的能力请求及启动渲染完成,再打开失焦关闭的选项菜单。
|
||||
|
||||
## 整窗遮罩与原生拖动区域
|
||||
|
||||
@@ -57,11 +71,40 @@ AI 列 280。测试同步原生输入与实际状态后独立复跑通过,保
|
||||
[UI 设计系统](../../UI-DESIGN.md)。
|
||||
|
||||
**防复发**:`WorkspacePrimitives.test.tsx` 的
|
||||
"releases the native drag region for every full-window overlay" 从 Renderer 顶层全部 CSS 文件自动发现
|
||||
全部 `position: fixed` 遮罩并逐个校验,新增遗漏会直接失败。
|
||||
"releases the native drag region for every full-window overlay" 递归扫描 Renderer CSS,按
|
||||
`position: fixed` 与 `inset` 声明发现遮罩,排除不接收指针的透明宿主,并校验共享 no-drag。
|
||||
|
||||
**历史**:`e9679fe`、`ca4a58c` 各修了一部分被人工点到的弹窗,同一批改动又新增了两个未覆盖
|
||||
的遮罩;后续复查改为共享规则加自动发现测试。
|
||||
2026-09-27 复查发现自动发现仍依赖 `backdrop/modal` 后缀,漏掉知识引用的
|
||||
`.knowledge-citation-dialog`。补入共享规则并移除后缀限制;真实 App 的 Windows
|
||||
`WM_NCHITTEST` 回归验证修复后为 `HTCLIENT`,临时清除该规则的对照为 `HTCAPTION`。
|
||||
|
||||
## 共享控件样式依赖页面祖先
|
||||
|
||||
**症状**:放弃笔记草稿弹窗中的危险按钮显示为原生按钮,其他页面的危险确认也缺少样式。
|
||||
|
||||
**原因**:唯一的 `.danger-solid` 定义限定在 `.magic-notes-page` 下;Portal 挂到 `body`
|
||||
后失去该祖先,心跳、监督者、工作区文件删除和工具环境设置也无法命中。
|
||||
|
||||
**规则**:共享按钮在全局定义并复用基础控件规则,不向每个调用处补页面类。
|
||||
具体状态和令牌见 [UI 设计系统](../../UI-DESIGN.md#66-危险操作)。
|
||||
|
||||
**防复发**:`magic-notes-capture.electron.test.ts` 在真实 App 中触发草稿确认,检查
|
||||
按钮不属于页面祖先时的浅深主题 computed style、尺寸、字体、禁用及原生 Tab 焦点;
|
||||
悬停通过 Chromium 调试协议强制伪状态检查,不作为原生鼠标命中验证。
|
||||
共享令牌测试检查默认和悬停危险背景上的文字对比度。
|
||||
|
||||
**历史**:2026-09-27 同次检查确认草稿确认及上述四处调用均受同一作用域错误影响。
|
||||
|
||||
同日后续全局审查发现,控件已有全局定义仍会被容器与状态规则覆盖:深色通用输入规则替换
|
||||
控件边框,普通图标 hover 覆盖危险图标,菜单按钮规则覆盖危险确认;`.field input` 还将
|
||||
Radio 与 Switch 当成文本字段。共享规则现已排除二元控件,恢复全局焦点轮廓,降低菜单
|
||||
默认样式特异性,并移除深色控件兜底覆盖。Continue 独立字段通过 `.field-control` 复用
|
||||
输入规则。`shared-controls.electron.test.ts` 检查浅深主题、设置容器、body Portal、菜单
|
||||
组合、默认/悬停/禁用/选中态及原生 Tab 焦点,并渲染实际 Continue 组件。令牌测试同时检查
|
||||
主按钮 hover 白字与控件背景的对比度。[审查修复记录](shared-controls-review-2026-09-27.md)
|
||||
保留本次范围、验证结果和未测项。
|
||||
|
||||
## 网页浮层遗漏原生浏览器视图
|
||||
|
||||
@@ -80,6 +123,9 @@ AI 列 280。测试同步原生输入与实际状态后独立复跑通过,保
|
||||
|
||||
**历史**:先前浏览器多实例修正只覆盖了终端关闭确认;2026-09-13 用户在知识库实例
|
||||
Modal 再次复现,同一检查确认其他应用级 Modal 均缺少处理。
|
||||
2026-09-27 同行帮助的 tooltip 仍未进入选择器。现已纳入同一相交判定和尺寸观察;
|
||||
Electron 回归从运行记录页打开真实 InlineHelp,并通过工作栏分隔条形成相交与非相交布局,
|
||||
检查实际 WebContentsView 可见性及恢复后的页面标记,确认网页没有重建或刷新。
|
||||
|
||||
## Modal 内的菜单与通知被遮挡或隔离
|
||||
|
||||
@@ -100,6 +146,18 @@ Modal,兼顾 top layer 绘制、辅助技术的模态归属及焦点循环;
|
||||
**历史**:2026-09-19 同次检查确认解析测试菜单和应用级通知两处挂载机制错误,后者影响
|
||||
多个全局 Modal;统一宿主后补完整 App 的真实保存通知与 TXT 解析复验。
|
||||
|
||||
2026-09-27 在真实 App 的“设置 → 能力与工具 → MCP → 自定义 MCP → 添加 Server”
|
||||
再次复现子层遮挡:MCP body Portal 沿用 `z-index: 70`,低于设置的 `130`,输入框中心
|
||||
实际命中父设置页签。编辑器仅手动聚焦,父设置未设为 `inert`。添加与编辑统一接入
|
||||
`--z-dialog` 和 `activateModalFocus`;遮罩关闭阻止默认鼠标焦点转移,Escape 停在子层。
|
||||
新增 Electron 回归检查真实鼠标命中、输入、Tab 循环、各关闭入口、生产 IPC 保存及重读,
|
||||
并检查浏览器在父子 Modal 期间隐藏、最后恢复同一未刷新的网页。子 Modal 应使用共享模态
|
||||
机制;上述 `FloatingPortal` 仍用于非模态菜单与通知。
|
||||
同次审查确认解析结果打开图片时也沿用低于父层的 `z-index: 80`,现复用 `--z-dialog`。
|
||||
解析正文使用稳定的组件与图片回调,避免 App 打开大图时重建触发节点而丢失返回焦点。
|
||||
窄侧栏打开项目 Modal 也已接入共享背景隔离,避免一次 Escape 同时关闭两层。
|
||||
本阶段实现、键盘边界和 Electron 证据见[浮层审查修复记录](overlay-review-2026-09-27.md)。
|
||||
|
||||
## Flex 滚动内容压缩页签和分段控件
|
||||
|
||||
**症状**:知识库来源选择被长参数表单压成窄缝,标签和按钮文字被裁掉。
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# 窄容器布局审查修复记录
|
||||
|
||||
日期:2026-09-27。范围为全局样式审查第三阶段的工作栏添加入口和模型设置布局。
|
||||
此前 MCP、共享控件、浮层和深色主按钮对比度修复均保留。
|
||||
|
||||
## 修复
|
||||
|
||||
工作栏原先将“+”放在页签滚动区内,`300px` 栏宽下默认四个页签就会将按钮卷出可见区。
|
||||
现在添加按钮独立占用行末宽度,页签及两端滚动箭头放在剩余区域内;溢出测量也读取该区域。
|
||||
没有改动应用目录、实例策略、持久化或终端生命周期。
|
||||
|
||||
模型设置原先按整窗 `720px` 断点切换单列。`721px` 窗口仍保留分类、连接列表和表单三列,
|
||||
协议选择框宽度只有约 `136px`。现在 `.model-connection-manager` 自身作为命名容器,
|
||||
可用宽度不超过 `640px` 时让列表及详情各占整行,列表保留 `180px` 的内部滚动上限。
|
||||
更宽时沿用原双栏。LLM、向量连接和角色编辑共用这条规则,没有增加页面专属断点。
|
||||
|
||||
产品规则见 [UI-DESIGN.md](../../UI-DESIGN.md)、
|
||||
[工作栏页签 PRD](../features/assistant-workbar/terminal-tabs-prd.md#71-tab-行)和
|
||||
[模型连接 UI 设计](../features/model-connections/ui-design.md)。工作栏两份 PRD 中“+ 随页签
|
||||
滚动”的旧说明已纠正。
|
||||
|
||||
## Electron 证据
|
||||
|
||||
[workbar-settings-layout.electron.test.ts](../../tests/workbar-settings-layout.electron.test.ts)
|
||||
在隔离 profile 中加载当前生产 `WorkbarShell`、`SettingsPanel`、共享样式及字体。
|
||||
测试使用受控 IPC 配置数据,不访问模型服务。Windows Electron 共记录 32 组结果,包含:
|
||||
|
||||
- 中文与英文、浅色与深色主题。
|
||||
- `300px` 工作栏的默认四个页签、16 个含长标题的页签,以及从目录新建第 17 个实例。
|
||||
检查“+”位于可见栏内且中心点可命中,原生鼠标打开目录、创建浏览器实例;Escape 返回
|
||||
添加按钮。滚动箭头不改变活动页签,Home/End 激活并显示首末页签。
|
||||
- 默认页签下将工作栏扩到 `900px` 再缩回 `300px`,检查溢出箭头退出及恢复。
|
||||
- 设置内容区窗口为 `680×560`、`720×560`、`721×560`、`740×560`、`960×560`、
|
||||
`1180×760`。另在 `1180×560` 窗口中将设置面板限定为 `740px`,检查同一窗口内的
|
||||
容器响应,而非仅检查媒体断点。
|
||||
- 长连接名称下,协议当前值的真实字体测量宽度小于控件扣除内边距及箭头后的可用宽度。
|
||||
设置内容区、连接管理区及详情无横向溢出,协议框可命中;原生 Tab 进入后,方向键改变
|
||||
协议,Home 还原。`721px` 窗宽下控件 clientWidth 为 `370px`,英文协议文字约 `233px`。
|
||||
|
||||
通过 `GB_LAYOUT_ARTIFACTS` 可输出截图和 `result.json`。已查看英文深色 `721px` 设置、
|
||||
英文宽窗口窄容器及中英文工作栏截图。测试退出后删除专用 profile 与 Vite 缓存。
|
||||
|
||||
## 验证命令
|
||||
|
||||
```text
|
||||
npm test -- src/renderer/src/WorkbarShell.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx src/renderer/src/SettingsPanel.test.tsx src/renderer/src/EmbeddingSettingsSection.test.tsx src/renderer/src/RolePromptSettingsSection.test.tsx src/renderer/src/RightAssistantSidebar.resize.test.tsx tests/workbar-settings-layout.electron.test.ts tests/shared-controls.electron.test.ts
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Focused 回归 8 文件、238 项通过;typecheck 和 lint 通过。前阶段共享控件 Electron
|
||||
回归的 338 组状态测量仍通过。模型调用 0 次,未提交代码。
|
||||
补强页签焦点与选中态同步断言后,新增 Electron 回归连续三次完整通过,每次 32 组结果。
|
||||
|
||||
新驱动调试期间修正了目录按钮选择器、环境变量类型声明和 Electron 方向键名称。
|
||||
原生 select 弹出菜单的关闭时序曾使驱动超时,最终使用原生 Tab 与方向键验证字段操作,
|
||||
并保留命中、文字宽度和溢出断言;没有放宽产品断言或增加生产代码补丁。
|
||||
后续联合运行曾停在调整窗口后的动画帧等待。驱动补充了超时脚本记录,并在测试进程中关闭
|
||||
Windows 原生遮挡节流,等待窗口焦点和尺寸后再同步绘制及发送鼠标输入。该设置不进入产品。
|
||||
最终连续三次回归通过;超时记录不足以单独证明此前每次停顿的唯一原因。
|
||||
|
||||
## 限制
|
||||
|
||||
这是当前生产组件的真实 Electron 布局与输入回归,IPC 数据使用 fixture;不证明完整 App
|
||||
的配置保存或模型调用链路。未覆盖原生 select 弹出菜单的鼠标选项选择、macOS/Linux、
|
||||
系统缩放、200% 字号、原生窗口遮挡节流和全部设置类别。向量连接和角色编辑运行了组件回归,但本次 Electron
|
||||
矩阵仅验证 LLM 连接表单。本阶段定向验证未运行全仓测试或生产构建;后续全仓结果见下节。
|
||||
|
||||
## 全部样式修复最终验证
|
||||
|
||||
2026-09-27 在 Windows 开发环境完成最终验证。范围为共享控件 8 项、浮层 5 项、
|
||||
窄容器 2 项,共 15 项样式问题,以及 MCP 子 Modal 修复。先读取根目录 `AGENTS.md`,
|
||||
检查未提交源码和文档 diff;原有 37 个已跟踪修改文件、12 个未跟踪文件均保留,暂存区为空。
|
||||
本次新增的生产修正仅为 `ProjectSwitcher.tsx` 的初始字段选择器一行,未改测试断言。
|
||||
|
||||
| 命令与执行范围 | 实际结果 |
|
||||
| --- | --- |
|
||||
| `npm test`,当前工作树,全仓且无 bail | 463 文件:450 通过、2 失败、11 跳过;5395 项:5309 通过、3 失败、83 跳过。耗时 696.22 秒 |
|
||||
| `npm test -- src/renderer/src/HeartbeatCenter.test.tsx`,隔离 HEAD `11facf0` | 1 文件失败;40 项通过、2 项失败,耗时 4.86 秒,与当前工作树同一行、同一中英文断言失败 |
|
||||
| `npm test -- src/renderer/src/App.test.tsx src/renderer/src/ProjectSwitcher.test.tsx tests/overlay-layering.electron.test.ts`,焦点修复后 | 3 文件、331 项通过,耗时 80.57 秒 |
|
||||
| `npm run typecheck` | 全仓测试后及焦点修复后均通过,覆盖 node、agent、web 三份 TypeScript 配置 |
|
||||
| `npm run lint` | 全仓测试后及焦点修复后均通过 |
|
||||
| `git diff --check` | 通过;Git 提示已有 LF 将转换为 CRLF,没有空白错误 |
|
||||
|
||||
完整 `npm test` 只启动一次,超时设为 1,200,000 毫秒,等待正常结束,没有 bail、
|
||||
中止或重复启动。修复后只复跑相关集合,未把定向通过改写成全仓通过。
|
||||
|
||||
### 失败归因与处理
|
||||
|
||||
`App.test.tsx:8921` 是本次回归。项目 Modal 新接入的初始焦点逻辑只查询 input,
|
||||
覆盖了通道项目说明 textarea 的原有自动聚焦,实际焦点落到后面的默认工作目录。
|
||||
确认组件结构和失败输出后,选择器改为按 DOM 顺序查询未禁用的 input、textarea、select,
|
||||
无字段时仍退回按钮。原有说明字段焦点断言保持不变,App 全文件及项目切换组件、真实
|
||||
Electron 浮层回归共 331 项通过,包含窄侧栏 Escape 隔离与 MCP 保存重读路径。
|
||||
|
||||
`HeartbeatCenter.test.tsx:213` 的中文、英文两项失败为既有问题。测试要求全页只有
|
||||
1 个 tablist,并要求内容面板内没有 tablist;但图谱有事件数据时,
|
||||
`SupervisorWorkspace.tsx:677` 已渲染事件、实体、关系的内层 `PageTabs`,实际总数为 2。
|
||||
未带本轮改动的 HEAD `11facf0` 使用相同依赖独立执行该文件,也得到 40 通过、2 失败。
|
||||
当时保留组件与断言;后续修复及定向验证见下节。
|
||||
|
||||
### HeartbeatCenter 断言修复
|
||||
|
||||
2026-09-27 后续核对[监督者 UI 设计](../features/conversation-supervision/ui-design.md#故事线图谱)、
|
||||
`HeartbeatCenter`、`SupervisorWorkspace` 和共享 `PageTabs`:顶层导航与图谱分类导航各有
|
||||
独立可访问名称,图谱内层页签符合设计。修改前按测试名称复跑,中英文均在原第 213 行失败。
|
||||
|
||||
测试现按 role 和可访问名称分别定位两层导航,核对顶层五页签、图谱事件/实体/关系及数量、
|
||||
唯一选中态和 tab/panel 双向关联;方向键切换内层页签时检查焦点、事件内容或分类空态,
|
||||
并确认顶层仍选中故事线图谱。非图谱页面继续断言没有内层 tablist,原有标题、范围和操作
|
||||
断言保留。生产代码未改;设计文档澄清“一套 PageTabs”指顶层导航。
|
||||
|
||||
`npm test -- src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx`
|
||||
通过 3 文件、96 项,包含 HeartbeatCenter 全部 42 项。`npm run typecheck` 通过;
|
||||
`npm run lint` 首次超过 60 秒执行时限,提高时限重跑后通过。本轮未重新运行全仓测试,
|
||||
上表历史全量结果及跳过项保持原记录。
|
||||
|
||||
### Electron 结果与未测范围
|
||||
|
||||
全仓运行中,共享控件输出 338 组 computed style 测量,窄容器输出 32 组布局和输入结果。
|
||||
overlay 文件 3 项通过,包含 MCP 四种主题/窗口/浏览器场景、生产 IPC 保存与重新读取,
|
||||
以及大图、iframe、项目 Modal、tooltip、知识引用五条路径;焦点修正后该文件再次通过。
|
||||
这些样式与 MCP 驱动的模型请求尝试为 0。全仓另有隔离的 403 fixture 输出,每种凭据来源
|
||||
各记录 1 次请求;这不作为真实模型生成证据,也不据此声称全仓外部调用总数为 0。
|
||||
|
||||
当时剩余的两项 HeartbeatCenter 断言已由上述定向验证确认修复。全仓跳过的 11 文件、83 项仍未形成通过证据。
|
||||
本次没有运行生产构建、打包、macOS/Linux 验证、远程 Agent 实机或外部 MCP 连接;
|
||||
未覆盖强制颜色、完整系统缩放/200% 字号矩阵、原生 select 弹出菜单的鼠标选项选择,
|
||||
也未将组件 fixture 当作全部 App 业务入口验收。部分 Electron 驱动关闭了 Windows
|
||||
原生遮挡节流,不能据此宣称产品在原生遮挡状态下也已覆盖。没有 commit 或 push。
|
||||
@@ -0,0 +1,55 @@
|
||||
# 共享控件审查修复记录
|
||||
|
||||
日期:2026-09-27。范围为全局样式审查第一阶段,包含原审查六项、会话菜单危险按钮覆盖及
|
||||
深色主按钮 hover 对比度。原有 MCP 子 Modal 修复保留,本阶段没有改动其组件或业务逻辑。
|
||||
|
||||
## 修复范围
|
||||
|
||||
| 问题与触发位置 | 共享修复 | 验证 |
|
||||
| --- | --- | --- |
|
||||
| 深色设置输入框、次按钮边框被兜底规则覆盖 | 删除通用输入覆盖及次按钮边框覆盖,使用主题 `--border-control` | 设置容器和 body Portal 读取最终边框;输入边框与实际背景至少 3:1 |
|
||||
| `.field` 与浏览器地址框丢失焦点环;Radio 被拉高 | 文本字段规则排除 Checkbox/Radio,删除局部 outline 清除和未定义 `--focus-ring` 引用 | 原生 Tab 后轮廓为 2px 强调色;Switch 为 38×22px,Checkbox/Radio 为 15×15px |
|
||||
| 图标按钮、分段控件禁用后仍像可操作 | 共享禁用透明度和光标;普通图标 hover 排除禁用项 | 禁用时光标为 not-allowed,透明度降低,hover 不恢复可用颜色 |
|
||||
| Continue 删除图标丢失危险颜色 | 普通图标 hover 排除危险修饰类,移除深色普通图标覆盖 | 组合类及实际 Continue 删除按钮在两种主题下保留危险前景和悬停背景 |
|
||||
| Continue Rule/Prompt 输入回退原生样式 | 五个独立文本字段使用 `.field-control`,复用原表单规则 | 实际组件字段使用共享边框、字体、内边距和至少 38px 高度,并保留键盘焦点 |
|
||||
| 默认 PageTabs hover 覆盖 active | hover 只作用于未选中项 | 当前项悬停前后背景及文字颜色不变 |
|
||||
| 会话菜单 button 覆盖 danger-button | 会话与工作区文件菜单的通用规则降低特异性,删除多余文件菜单危险补丁 | 实际 `DestructiveConfirmActions` 从入口进入确认后保留危险实心背景、反白文字;禁用危险图标不高亮 |
|
||||
| 深色主按钮 hover 白字对比度约 4.31:1 | `--accent-solid-hover` 调整为 `#1a77c5` | 白字对比度 4.68:1;令牌及真实按钮默认/hover 均检查至少 4.5:1 |
|
||||
|
||||
回归另发现浅色 `--border-control` 在 `--surface-subtle` 上只有约 2.90:1。
|
||||
共享边框令牌由 `#7f95aa` 调整为 `#748a9f`,对白底、subtle、muted 背景分别为
|
||||
3.57:1、3.35:1、3.02:1。没有按页面另设边框颜色。
|
||||
|
||||
实现主要位于 [styles.css](../../src/renderer/src/styles.css);Continue 接线位于
|
||||
[RuntimeCustomizationSection.tsx](../../src/renderer/src/RuntimeCustomizationSection.tsx)。
|
||||
共享规范由 [UI-DESIGN.md](../../UI-DESIGN.md) 维护。
|
||||
|
||||
## 回归证据
|
||||
|
||||
[shared-controls.electron.test.ts](../../tests/shared-controls.electron.test.ts) 使用当前源码组件、
|
||||
共享样式和主题函数。Continue 数据使用受控 IPC fixture,菜单使用实际
|
||||
`DestructiveConfirmActions`。测试在真实 Electron/Chromium 中读取 computed style,
|
||||
覆盖浅深主题、设置容器和真实 body Portal,共记录 338 组状态测量,包含 PageTabs 的
|
||||
default 与 segmented 变体。焦点通过原生 Tab
|
||||
进入;hover 通过 CDP 强制伪状态检查,不作为原生鼠标命中证据。模型调用为 0。
|
||||
|
||||
本机环境为 Windows、Electron 43.2.0、Chromium 150,内容区 1280×800,DPR 1。
|
||||
已打开审阅浅深 Continue 字段及深色 Portal 的原尺寸 PNG;共享输入边框、删除图标和
|
||||
焦点轮廓符合计算值。截图与完整 computed JSON 可通过 `GB_SHARED_CONTROLS_ARTIFACTS`
|
||||
指定目录输出,测试专用 profile 与 Vite 缓存在退出后删除。
|
||||
|
||||
已执行:
|
||||
|
||||
- `npx vitest run tests/shared-controls.electron.test.ts tests/overlay-layering.electron.test.ts src/renderer/src/WorkspacePrimitives.test.tsx src/renderer/src/SettingsPanel.test.tsx src/renderer/src/WorkspaceFilesPanel.test.tsx`:最终 5 文件、181 项通过。
|
||||
- MCP 浮层回归的 4 个场景通过:浅深主题、短窗口、原生鼠标命中与焦点循环、生产 IPC 保存及重读;有浏览器的场景保持 Modal 期间隐藏、关闭后恢复同一未刷新的网页。模型请求尝试为 0。
|
||||
- `npm run typecheck` 通过;`npm run lint` 通过。
|
||||
|
||||
首轮新回归在对话框输入的浅色边框对比度断言失败,修正共享令牌后通过。测试启动器曾有
|
||||
环境变量对象类型错误,增加 `NodeJS.ProcessEnv` 标注后类型检查通过。未放宽视觉断言。
|
||||
|
||||
## 剩余边界
|
||||
|
||||
本阶段上述八类问题已修复。共享控件测试属于组件视觉层,不证明完整 App 的全部业务入口;
|
||||
MCP 原有集成回归独立验证其真实保存与浏览器隔离路径。本阶段未执行全仓测试、macOS/Linux、
|
||||
强制颜色、200% 缩放及全页面四档窗口矩阵。后续全部 15 项样式修复与 MCP 的
|
||||
[最终验证汇总](responsive-controls-review-2026-09-27.md#全部样式修复最终验证)记录全仓结果及剩余失败。
|
||||
+11
-1
@@ -3,6 +3,7 @@ import { resolve } from 'node:path'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||
import type { Plugin } from 'vite'
|
||||
import { packageObsidianMcpVault } from './src/main/obsidian/package-mcpvault'
|
||||
|
||||
type ProjectPackage = {
|
||||
dependencies?: Record<string, string>
|
||||
@@ -106,7 +107,16 @@ export default defineConfig({
|
||||
'zod'
|
||||
]
|
||||
}),
|
||||
deepSeekHarnessBundleManifestPlugin()
|
||||
deepSeekHarnessBundleManifestPlugin(),
|
||||
{
|
||||
name: 'obsidian-mcpvault-package',
|
||||
async writeBundle(options) {
|
||||
await packageObsidianMcpVault(
|
||||
resolve('.'),
|
||||
resolve(options.dir ?? 'out/main', 'obsidian-mcpvault')
|
||||
)
|
||||
}
|
||||
}
|
||||
],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
|
||||
Generated
+7885
-583
File diff suppressed because it is too large
Load Diff
+33
-26
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "goodbuddy",
|
||||
"version": "0.13.16",
|
||||
"version": "0.15.2",
|
||||
"private": true,
|
||||
"description": "Secure desktop AI workspace with controlled Agent Runtimes",
|
||||
"desktopName": "GoodBuddy",
|
||||
@@ -58,6 +58,7 @@
|
||||
"beforePack": "build/runtime-hooks.cjs",
|
||||
"asar": true,
|
||||
"asarUnpack": [
|
||||
"out/main/obsidian-mcpvault/**/*",
|
||||
"out/main/package.json",
|
||||
"out/main/deepseek-harness-*",
|
||||
"out/main/embedding-inference-bootstrap.js",
|
||||
@@ -80,6 +81,10 @@
|
||||
"!node_modules/npm{,/**/*}"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": ".runtime-resources/dsh-${arch}/node_modules",
|
||||
"to": "runtimes/dsh/node_modules"
|
||||
},
|
||||
{
|
||||
"from": "resources/skills",
|
||||
"to": "skills",
|
||||
@@ -296,30 +301,30 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "0.25.1",
|
||||
"@deepseek-ai/cordis": "4.0.2",
|
||||
"@deepseek-ai/dsh-agent": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-agent-loop": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-bash-local": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-credentials": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-fs-local": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-llm": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-llm-pi-ai": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-pwsh-local": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-sandbox": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-shell-env": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-skill": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-subprocess-local": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-token-meter": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-tool-bash": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-tool-fs": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-tool-pwsh": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-tool-skill": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-tools": "0.1.2-rc.1",
|
||||
"@deepseek-ai/dsh-user-approval": "0.1.2-rc.1",
|
||||
"@deepseek-ai/cordis": "4.0.4",
|
||||
"@deepseek-ai/dsh-agent": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-agent-loop": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-bash-local": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-fs-local": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-llm": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-llm-pi-ai": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-pwsh-local": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-sandbox": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-session": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-session-projection": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-shell-env": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-skill": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-subprocess-local": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-system-prompt": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-token-meter": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-tool-bash": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-tool-fs": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-tool-pwsh": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-tool-skill": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-tools": "0.1.7-rc.2",
|
||||
"@deepseek-ai/dsh-user-approval": "0.1.7-rc.2",
|
||||
"@huggingface/tokenizers": "^0.1.3",
|
||||
"@modelcontextprotocol/sdk": "^1.30.0",
|
||||
"@napi-rs/canvas": "1.0.3",
|
||||
@@ -366,7 +371,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antv/g6": "^5.1.1",
|
||||
"@bitbonsai/mcpvault": "0.16.0",
|
||||
"@continuedev/cli": "1.5.47",
|
||||
"@deepseek-ai/dsh": "0.1.7-rc.2",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@fontsource-variable/inter": "^5.3.0",
|
||||
"@fontsource-variable/noto-sans-sc": "^5.3.0",
|
||||
@@ -406,7 +413,7 @@
|
||||
"@koromix/koffi-win32-x64": "3.1.4"
|
||||
},
|
||||
"allowScripts": {
|
||||
"@deepseek-ai/dsh-subprocess-local@0.1.2-rc.1": true,
|
||||
"@deepseek-ai/dsh-subprocess-local@0.1.7-rc.2": true,
|
||||
"@google/genai@1.52.0": true,
|
||||
"cpu-features@0.0.10": true,
|
||||
"electron-winstaller@5.4.0": true,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# GoodBuddy Agent 0.15.0 更新内容
|
||||
|
||||
## 功能更新
|
||||
|
||||
- **远程 Agent 可查阅故事图谱**:配套 Desktop 启用监督者并分配 Story Graph 后,远程 OpenCode、Continue 可在 Ask 或 Execute 中搜索已保存决定、读取变化过程和来源片段。工具仅只读,不生成新回顾、不修改依据,也不提供完整历史重建或跨范围事实合并。
|
||||
|
||||
## 问题修复
|
||||
|
||||
- **远程 Runtime 遵循遥测关闭设置**:通过更新后的 Host 环境启动 OpenCode 或 Continue 时,Agent 与模型桥统一传入遥测关闭配置。保留模型、MCP 和用户主动联网能力,不把关闭遥测变成禁止正常联网。
|
||||
- **Continue 不再重复检查更新**:远程 Continue 使用的共享适配器在自动更新关闭时跳过更新检查。正常模型请求和执行能力不受影响,该修改随新启动的 Runtime 生效。
|
||||
|
||||
## 使用说明
|
||||
|
||||
- 请先升级 **GoodBuddy Desktop 0.15.2**,再下载并更新 Host 环境。此版本不提供远程原生客户端快捷入口,已有进程不会热更新。
|
||||
- 提供 Linux x64、Linux arm64 和 macOS arm64 包,继续使用 Node **24.19.0**、OpenCode **1.18.29** 和 Continue **1.5.47**。
|
||||
- Ask 保持只读,Execute 使用所选 SSH 账号的权限。Agent 独立发布,不替代桌面端 Latest。
|
||||
|
||||
# What's New in GoodBuddy Agent 0.15.0
|
||||
|
||||
## Features
|
||||
|
||||
- **Remote agents can consult Story Graph**: With Supervisor enabled and Story Graph assigned in the matching Desktop, remote OpenCode and Continue can search saved decisions and read their evolution and source fragments in Ask or Execute. These read-only tools do not generate reviews, modify evidence, reconstruct complete history, or merge facts across scopes.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Remote Runtimes respect telemetry opt-out**: OpenCode and Continue launched through the updated Host environment receive consistent telemetry opt-out settings from the Agent and model bridge. Model, MCP, and user-requested networking remain available rather than being blocked alongside telemetry.
|
||||
- **Continue skips disabled update checks**: The shared adapter used by remote Continue skips update checks when automatic updates are disabled. Normal model requests and execution remain available, and the change takes effect for newly started Runtimes.
|
||||
|
||||
## Usage Notes
|
||||
|
||||
- Upgrade to **GoodBuddy Desktop 0.15.2** before downloading and updating the Host environment. This release does not provide remote native-client shortcuts, and existing processes are not hot-updated.
|
||||
- Packages cover Linux x64, Linux arm64, and macOS arm64, retaining Node **24.19.0**, OpenCode **1.18.29**, and Continue **1.5.47**.
|
||||
- Ask stays read-only and Execute uses the selected SSH account's permissions. Agent releases remain separate and do not replace Desktop Latest.
|
||||
@@ -1,6 +1,96 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"releases": [
|
||||
{
|
||||
"version": "0.15.2",
|
||||
"releasedAt": "2026-09-28",
|
||||
"notes": {
|
||||
"zh-CN": {
|
||||
"highlights": [],
|
||||
"features": [
|
||||
"**让 Agent 查阅故事图谱**:启用监督者并分配 Story Graph 工具后,可让模型查找已保存的决定、变化过程和来源片段,在继续工作前核对已有依据。支持直连模型、本地 DeepSeek Harness,以及本地和远程 OpenCode、Continue;读取不修改图谱,也不代表完整历史重建或跨范围事实合并。"
|
||||
],
|
||||
"fixes": [
|
||||
"**继续讨论前编辑完整消息**:从监督结果继续讨论时,可查看并修改即将发送的完整上下文和问题,并确认目标会话。发送以编辑后的可见内容为准,不会重新附加已删除的上下文。",
|
||||
"**简化笔记采集界面**:采集和快速追加面板移除重复标签、范围提示及重复入口,保留内容编辑、目标选择和必要的校验错误。输入框仍有可访问名称,保存行为和来源关联不变。",
|
||||
"**保留窗口异常退出线索**:诊断日志现在记录渲染进程退出原因和退出码,便于排查窗口异常。此项改善诊断信息,不代表已修复所有导致窗口退出的问题。",
|
||||
"**下载与入门信息更容易查找**:双语网站调整产品介绍、使用场景和设置问答,下载入口紧接首页介绍。移动端展示同步优化,DeepSeek Harness 与 LoongArch 仍保留预览标识。"
|
||||
],
|
||||
"notices": [
|
||||
"远程 Story Graph 需要配套更新 Desktop **0.15.2** 和 Agent **0.15.0**,先升级桌面端,再更新 Host。",
|
||||
"数据库保持 schema 48;从更早版本升级前仍应备份完整用户数据,包括数据库和相邻 `notes/` 目录。"
|
||||
]
|
||||
},
|
||||
"en-US": {
|
||||
"highlights": [],
|
||||
"features": [
|
||||
"**Let agents consult Story Graph**: Enable Supervisor and assign the Story Graph tools to let agents look up saved decisions, their evolution, and source fragments before continuing work. Supports direct models, local DeepSeek Harness, and local or remote OpenCode and Continue; reads do not modify the graph or provide complete historical reconstruction or cross-scope fact merging."
|
||||
],
|
||||
"fixes": [
|
||||
"**Edit the complete follow-up message**: Continuing from Supervisor results now shows the full context and question for editing, together with the destination conversation. Sending uses the visible draft without reattaching context you removed.",
|
||||
"**Simpler note capture controls**: Capture and quick-append panels remove repeated labels, scope hints, and duplicate entry points while retaining editing, destination selection, and actionable validation errors. Inputs keep accessible names, and saving and source links behave as before.",
|
||||
"**Retain window-exit diagnostics**: Diagnostic logs now retain renderer exit reasons and exit codes to help investigate unexpected window exits. This improves diagnostics rather than claiming to fix every underlying cause.",
|
||||
"**Clearer downloads and setup guidance**: The bilingual website refreshes product explanations, usage scenarios, and setup FAQs, with downloads immediately after the introduction. Mobile layouts improve while DeepSeek Harness and LoongArch retain their preview labels."
|
||||
],
|
||||
"notices": [
|
||||
"Remote Story Graph requires Desktop **0.15.2** and Agent **0.15.0**. Upgrade Desktop before updating Hosts.",
|
||||
"The database remains on schema 48. Before upgrading from older versions, back up complete user data, including the database and adjacent `notes/` directory."
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "0.15.1",
|
||||
"releasedAt": "2026-09-28",
|
||||
"notes": {
|
||||
"zh-CN": {
|
||||
"highlights": [],
|
||||
"features": [
|
||||
"**按需关闭桌面通知**:在“平台功能 / 通用设置”中可随时开关系统任务提醒,默认保持开启且保存后立即生效。关闭后仍保留应用内提示,通用设置也按快捷键、会话与通知、工作目录与下载分组,便于查找。",
|
||||
"**连接本地 Obsidian 仓库**:在 MCP 设置中测试并启用全部已注册仓库或指定文件夹,即可在受支持的本地对话中搜索、阅读笔记,并在 Execute 中写回结果,无需安装 Obsidian 插件。默认关闭,支持直连模型及 GoodBuddy 管理的本地 OpenCode、Continue,暂不支持 DeepSeek Harness 和远程执行。",
|
||||
"**把对话保存到魔法笔记**:单条 AI 回答或完整会话可先预览、编辑,再追加到已有笔记或创建新笔记,保存后可返回来源会话。右侧工作栏新增笔记入口,支持搜索与快速追加,切换会话时保留未提交草稿。",
|
||||
"**从输入区打开本地 Runtime 客户端(预览)**:本地项目选择 Continue 或 OpenCode 后可打开工作栏原生终端,选择 DS 后可在系统浏览器打开官方 Web 客户端,使用当前项目和模型。客户端是独立会话,不复制 GoodBuddy 聊天历史;DS 网页关闭后服务仍运行,可在 GoodBuddy 中停止,远程快捷入口尚不支持。"
|
||||
],
|
||||
"fixes": [
|
||||
"**流式回复时会话排序稳定**:对话按最近一条消息的创建时间排序,不再因流式文本或工具进度更新反复交换位置。新消息仍会推动会话前移,置顶会话继续排在前面。",
|
||||
"**从监督结果直接继续讨论**:监督反馈和图谱来源可直接发起后续讨论,编辑问题后将相关回顾结论与来源正文带入目标会话,无需先打开来源。界面移除重复上下文说明和知识实体写回控件,自动监督没有活动时不再显示空指标。",
|
||||
"**知识库列表与操作更清楚**:内容来源和文档索引合并展示,单文档来源不再重复占行,目录分组、失败来源和统一搜索仍可使用。整理重新处理、移除与任务历史入口,减少重复菜单和不明确的操作。",
|
||||
"**监督回顾避免重复运行**:手动开始、自动触发和继续回顾共用运行状态,已有回顾执行时会定位当前任务,不再叠加新回顾。可从活动记录取消,已保存批次保留;故事线事件、实体和关系改为独立页签浏览。",
|
||||
"**修复弹窗与窄窗口操作**:恢复删除确认按钮的可见样式,修正弹层遮挡、控件状态及窄布局滚动。助手工作栏的添加按钮不再被滚动箭头覆盖,设置和知识库中的操作保持可达。",
|
||||
"**本地 Runtime 启动更可靠**:修复便携包缺少 DS Web 入口及依赖导致无法打开浏览器的问题。Continue 关闭自动更新后不再停留在更新检查提示,DS 输入区保留停止服务操作而不再常驻重复说明。",
|
||||
"**补齐 Runtime 遥测关闭设置**:DS Web 与配套 Agent 的远程 Runtime 启动统一应用遥测关闭配置。模型、MCP 和用户主动联网请求保持可用,已有自动更新关闭设置不变。"
|
||||
],
|
||||
"notices": [
|
||||
"首次启动会将数据库升级至 schema 48。升级前请备份完整用户数据,包括数据库及相邻 `notes/` 目录;旧客户端不能打开升级后的数据库,回退需使用升级前备份。",
|
||||
"远程用户请先升级 Desktop 至 **0.15.1**,再下载并更新 Host 上的 Agent **0.15.0**。已有进程不会热更新,Agent 独立发布,不替代桌面端 Latest。",
|
||||
"原生客户端仍为预览,其 Skills/MCP 与 GoodBuddy 会话能力并非完全等价;Continue 和 DS Web 首次启动可能需要准备标准 Node 环境。真实模型请求可能产生费用,其他平台与安装包验收以本次发布流程结果为准。"
|
||||
]
|
||||
},
|
||||
"en-US": {
|
||||
"highlights": [],
|
||||
"features": [
|
||||
"**Control desktop notifications**: Turn system task notifications on or off under Platform Features / General; the default remains on and saved changes take effect immediately. In-app notifications remain available when system notifications are off, and General settings are grouped into shortcuts, conversations and notifications, and workspace and downloads.",
|
||||
"**Connect local Obsidian vaults**: Test and enable all registered vaults or a selected folder in MCP settings to search and read notes in supported local conversations, and write results back in Execute, without an Obsidian plugin. Disabled by default, this supports direct models and GoodBuddy-managed local OpenCode and Continue, but not DeepSeek Harness or remote execution.",
|
||||
"**Save conversations to Magic Notes**: Preview and edit an AI response or a complete conversation before appending it to an existing note or creating a new one, with links back to the source conversation. The new Notes workbar entry supports search and quick appends while preserving unsent drafts across conversation switches.",
|
||||
"**Open local Runtime clients from the composer (preview)**: In a local project, open a native workbar terminal for Continue or OpenCode, or the official DS Web client in your system browser, using the current project and model. These are independent sessions without copied GoodBuddy chat history; closing the DS page leaves its service running until stopped in GoodBuddy, and remote shortcuts are not supported yet."
|
||||
],
|
||||
"fixes": [
|
||||
"**Stable conversation order while replies stream**: Conversations are ordered by the creation time of their latest message rather than repeatedly swapping positions as text or tool progress arrives. New messages still move conversations forward, with pinned conversations first.",
|
||||
"**Continue directly from Supervisor results**: Start a follow-up from supervision feedback or graph sources, edit the question, and send relevant review conclusions and source text to the target conversation without opening the source first. Redundant context explanations and knowledge-entity writeback controls are removed, and Automatic supervision hides empty metrics until activity exists.",
|
||||
"**Clearer knowledge lists and actions**: Sources and indexed documents now share one list, without duplicate rows for single-document sources, while retaining directory groups, failed sources, and unified search. Reprocessing, removal, and task-history actions are organized to reduce duplicate menus and ambiguous operations.",
|
||||
"**Avoid overlapping Supervisor reviews**: Manual starts, automatic triggers, and resumed reviews share the active-run state, directing you to existing work instead of starting an overlapping review. Cancel from Activity while retaining saved batches, and browse story-graph events, entities, and relations in separate tabs.",
|
||||
"**Usable dialogs and narrow layouts**: Restores visible destructive-confirmation buttons and fixes overlay occlusion, control states, and scrolling in narrow layouts. Workbar scroll arrows no longer cover the add button, and settings and knowledge actions remain reachable.",
|
||||
"**More reliable local Runtime startup**: Fixes missing DS Web entry files and dependencies in portable packages that prevented browser launch. Continue no longer remains on an update-check message when automatic updates are disabled, and the DS composer keeps its stop action without persistent duplicate help text.",
|
||||
"**Consistent Runtime telemetry opt-out**: DS Web and remote Runtimes in the matching Agent apply the shared telemetry opt-out settings at startup. Model, MCP, and user-requested network access remain available, and existing automatic-update opt-outs are unchanged."
|
||||
],
|
||||
"notices": [
|
||||
"First launch upgrades the database to schema 48. Back up complete user data, including the database and adjacent `notes/` directory, before upgrading; older clients cannot open the upgraded database, so rollback requires a pre-upgrade backup.",
|
||||
"Remote users should upgrade Desktop to **0.15.1** before downloading and updating Hosts to Agent **0.15.0**. Existing processes are not hot-updated, and the independent Agent release does not replace Desktop Latest.",
|
||||
"Native clients remain a preview and do not offer full parity with GoodBuddy conversation Skills/MCP capabilities; Continue and DS Web may need to prepare a standard Node environment on first launch. Real model requests may incur charges, and other-platform and packaged-app acceptance depend on this release's validation results."
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"version": "0.13.16",
|
||||
"releasedAt": "2026-09-26",
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
// Deterministic SSH validation. Desktop owns SQLite; only tool messages cross SSH.
|
||||
import assert from 'node:assert/strict'
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { spawn } from 'node:child_process'
|
||||
import { createServer } from 'node:http'
|
||||
import { mkdtemp, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { createInterface } from 'node:readline'
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
||||
import { AgentImageToolMcp } from '../src/agent-daemon/image-tool-mcp'
|
||||
import { AgentOwnedAcpPrompt } from '../src/agent-daemon/agent-owned-acp-prompt'
|
||||
import { SemanticPromptStore } from '../src/agent-daemon/semantic-prompt-store'
|
||||
import { createOpenCodeModelBridgeProviderConfig } from '../src/agent-daemon/model-bridge-helper'
|
||||
import type { RuntimeAcpProcessOwner } from '../src/agent-daemon/runtime-acp-backend'
|
||||
import { ContinueHostAdapter } from '../src/main/agent/continue-host-adapter'
|
||||
import { AssistantDatabase } from '../src/main/assistant/assistant-database'
|
||||
import { KnowledgeMcpGateway } from '../src/main/agent/knowledge-mcp-gateway'
|
||||
import type { KnowledgeService } from '../src/main/knowledge/knowledge-service'
|
||||
import { MainImageToolSession } from '../src/main/remote-agent/main-image-tool-session'
|
||||
import type { RuntimeProtocolBinaryChannel, RuntimeProtocolBinaryFrame } from '../src/main/remote-agent/protocol-remote-runtime-channel'
|
||||
import { isStoryGraphTool, storyGraphToolNames } from '../src/shared/story-graph-tools'
|
||||
|
||||
const emit = (value: object) => process.stdout.write(`${JSON.stringify(value)}\n`)
|
||||
|
||||
async function hostProbe() {
|
||||
const [, , , opencode, continueEntry] = process.argv
|
||||
const root = await mkdtemp(join(tmpdir(), 'goodbuddy-graph-'))
|
||||
const adapter = new AgentImageToolMcp({ channelId: 'graph-probe', channelEpoch: '1', storyGraph: true }, async payload => { emit({ frame: Buffer.from(payload).toString('base64') }) })
|
||||
let disabled!: () => void
|
||||
const disabling = new Promise<void>(resolve => { disabled = resolve })
|
||||
const lines = createInterface({ input: process.stdin })
|
||||
lines.on('line', line => { const value = JSON.parse(line); if (value.frame) adapter.onReply(Buffer.from(value.frame, 'base64')); if (value.disabled) disabled() })
|
||||
const client = new Client({ name: 'story-graph-host-probe', version: '1' })
|
||||
let inferenceRequests = 0
|
||||
let backgroundRequests = 0
|
||||
const failures: unknown[] = []
|
||||
const inference = createServer((req, res) => { void (async () => {
|
||||
let text = ''; for await (const part of req) text += part
|
||||
const body = JSON.parse(text)
|
||||
if (++inferenceRequests > 20) throw new Error('Unexpected inference loop')
|
||||
const tools = body.tools as Array<{ function: { name: string } }> | undefined
|
||||
const tool = tools?.find(tool => tool.function.name.endsWith('story_graph_search'))
|
||||
if (tools?.length) assert.ok(tool, 'Runtime must expose Story Graph in Ask and Execute')
|
||||
else backgroundRequests++
|
||||
const result = body.messages.findLast((message: { role: string }) => message.role === 'tool')
|
||||
if (result) assert.match(JSON.stringify(result), /Decision B/)
|
||||
const delta = !tool ? { role: 'assistant', content: 'Graph probe title' } : result ? { role: 'assistant', content: 'GRAPH_RUNTIME_OK' } : { role: 'assistant', tool_calls: [{ index: 0, id: 'graph-call', type: 'function', function: {
|
||||
name: tool.function.name, arguments: JSON.stringify({ query: 'Decision B', page_size: 1 })
|
||||
} }] }
|
||||
res.writeHead(200, { 'content-type': 'text/event-stream' })
|
||||
res.end(`data: ${JSON.stringify({ id: 'graph-response', object: 'chat.completion.chunk', created: 1, model: 'graph-probe', choices: [{ index: 0, delta, finish_reason: null }] })}\n\ndata: ${JSON.stringify({ id: 'graph-response', object: 'chat.completion.chunk', created: 1, model: 'graph-probe', choices: [{ index: 0, delta: {}, finish_reason: result || !tool ? 'stop' : 'tool_calls' }] })}\n\ndata: [DONE]\n\n`)
|
||||
})().catch(error => { failures.push(error); res.writeHead(500).end(String(error)) }) })
|
||||
try {
|
||||
await adapter.start()
|
||||
await client.connect(new StreamableHTTPClientTransport(new URL(adapter.url!)))
|
||||
assert.deepEqual((await client.listTools()).tools.map(tool => tool.name), storyGraphToolNames)
|
||||
const call = async (name: string, args: Record<string, unknown>) => {
|
||||
const result = await client.callTool({ name, arguments: args }); assert.notEqual(result.isError, true)
|
||||
return JSON.parse((result.content as { text: string }[])[0]!.text)
|
||||
}
|
||||
const search = await call('story_graph_search', { query: 'Decision', page_size: 1 })
|
||||
assert.equal(search.page.has_more, true)
|
||||
await call('story_graph_search', { query: 'Decision', page_size: 1, cursor: search.page.next_cursor })
|
||||
const context = await call('story_graph_get_context', { object_ref: search.items[0].object_ref, mode: 'timeline' })
|
||||
assert.ok(context.items.length)
|
||||
const sourceId = context.items.flatMap((item: { source_reference_ids: string[] }) => item.source_reference_ids)[0]
|
||||
assert.ok(sourceId)
|
||||
const source = await call('story_graph_read_source', { source_reference_id: sourceId, page_size: 7 })
|
||||
assert.equal(source.page.has_more, true)
|
||||
await call('story_graph_read_source', { source_reference_id: sourceId, page_size: 7, cursor: source.page.next_cursor })
|
||||
const unsupported = await client.callTool({ name: 'story_graph_get_context', arguments: { object_ref: search.items[0].object_ref, mode: 'as_of' } })
|
||||
assert.equal(unsupported.isError, true)
|
||||
await new Promise<void>(resolve => inference.listen(0, '127.0.0.1', resolve))
|
||||
const origin = `http://127.0.0.1:${(inference.address() as { port: number }).port}/${randomBytes(32).toString('base64url')}`
|
||||
for (const workMode of ['ask', 'execute'] as const) {
|
||||
const config = createOpenCodeModelBridgeProviderConfig({ protocol: 'openai-chat-completions', model: 'graph-probe', loopbackOrigin: origin, workMode })
|
||||
const permission = workMode === 'execute' ? 'allow' : { '*': 'deny', 'graph_story_graph_*': 'allow' }
|
||||
const child = spawn(opencode!, ['acp'], { cwd: root, stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env,
|
||||
HOME: root, XDG_CONFIG_HOME: join(root, 'config'), XDG_DATA_HOME: join(root, 'data'), XDG_CACHE_HOME: join(root, 'cache'), XDG_STATE_HOME: join(root, 'state'),
|
||||
OPENCODE_DISABLE_AUTOUPDATE: '1', OPENCODE_DISABLE_MODELS_FETCH: '1', OPENCODE_DISABLE_PROJECT_CONFIG: '1', OPENCODE_DISABLE_EXTERNAL_SKILLS: '1', OPENCODE_DISABLE_CLAUDE_CODE_SKILLS: '1',
|
||||
OPENCODE_CONFIG_CONTENT: JSON.stringify({ ...config, permission, agent: { build: { permission } } })
|
||||
} })
|
||||
child.stderr.resume()
|
||||
const exited = new Promise<void>(resolve => child.once('close', () => resolve()))
|
||||
const processOwner = {
|
||||
writeStdin: (payload: Uint8Array) => new Promise<void>((resolve, reject) => child.stdin.write(payload, error => error ? reject(error) : resolve())),
|
||||
subscribeOutput: (listener: Parameters<RuntimeAcpProcessOwner['subscribeOutput']>[0]) => { const data = (data: Buffer) => { void listener({ stream: 'stdout', data }) }; child.stdout.on('data', data); return () => { child.stdout.off('data', data) } },
|
||||
subscribeExit: (listener: () => void) => { child.on('close', listener); return () => { child.off('close', listener) } }
|
||||
} as RuntimeAcpProcessOwner
|
||||
const transcript = new SemanticPromptStore(join(root, `${workMode}.sqlite`))
|
||||
let done!: () => void
|
||||
const completed = new Promise<void>(resolve => { done = resolve })
|
||||
const owner = new AgentOwnedAcpPrompt({ bindingId: 'graph', controllerId: 'probe', workspaceDirectory: root, process: processOwner, transcript,
|
||||
mcpServers: () => [{ type: 'http', name: 'graph', url: adapter.url!, headers: [] }],
|
||||
completePrompt: async (_operation, _status, _response, commit) => { commit(); done() } })
|
||||
try {
|
||||
transcript.prepare({ bindingId: 'graph', controllerId: 'probe', operationId: workMode, requestId: workMode, preparationDigest: `sha256:${'a'.repeat(64)}`, promptSequence: 0 })
|
||||
await owner.start({ bindingId: 'graph', operationId: workMode, requestId: workMode, prompt: [{ type: 'text', text: 'Search the graph.' }] }, workMode)
|
||||
await completed
|
||||
const page = transcript.page({ bindingId: 'graph', operationId: workMode, controllerId: 'probe', afterSequence: '0', limit: 128 })
|
||||
assert.equal(page.state, 'completed'); assert.match(JSON.stringify(page), /GRAPH_RUNTIME_OK/)
|
||||
} finally { owner.close(); child.kill(); await exited; transcript.close() }
|
||||
const continueAdapter = new ContinueHostAdapter({ binaryPath: continueEntry!, configPath: '', workspace: root, cacheRoot: join(root, 'continue'), mode: 'chat',
|
||||
modelProfile: { id: 'probe', name: 'Probe', modelName: 'graph-probe', baseUrl: `${origin}/v1`, protocol: 'openai-chat-completions', authentication: 'none' },
|
||||
launchHost: (entry, args, options) => {
|
||||
const process = spawn(globalThis.process.execPath, [join(dirname(entry), 'utility-bootstrap.mjs'), entry, ...args], { ...options, env: { ...options.env, ELECTRON_RUN_AS_NODE: '1' }, stdio: ['ignore', 'ignore', 'pipe'] })
|
||||
process.stderr.on('data', data => globalThis.process.stderr.write(data))
|
||||
return process
|
||||
} })
|
||||
try {
|
||||
const result = await continueAdapter.run('Search the graph.', AbortSignal.timeout(40_000), async approval => workMode === 'execute' || isStoryGraphTool(approval.toolName ?? '') ? 'once' : 'deny', {
|
||||
workMode, sessionMcpServers: [{ name: 'graph', type: 'streamable-http', url: adapter.url!, requestOptions: { headers: {} } }]
|
||||
})
|
||||
assert.match(result.text, /GRAPH_RUNTIME_OK/)
|
||||
} finally { await continueAdapter.dispose() }
|
||||
}
|
||||
emit({ disable: true }); await disabling
|
||||
assert.deepEqual((await client.listTools()).tools, [])
|
||||
assert.equal((await client.callTool({ name: 'story_graph_search', arguments: { query: 'Decision' } })).isError, true)
|
||||
assert.deepEqual(failures, [])
|
||||
emit({ passed: true, inferenceRequests, backgroundRequests, paidCalls: 0, runtimes: ['OpenCode Ask', 'OpenCode Execute', 'Continue Ask', 'Continue Execute'] })
|
||||
} finally { lines.close(); await client.close(); adapter.close(); inference.closeAllConnections(); inference.close(); await rm(root, { recursive: true, force: true }) }
|
||||
}
|
||||
|
||||
async function desktopProbe() {
|
||||
const [, , host, remoteScript, opencode, continueEntry] = process.argv
|
||||
assert.ok(host && remoteScript && opencode && continueEntry, 'Expected host, remote bundle, OpenCode and Continue paths')
|
||||
const db = new AssistantDatabase(':memory:'); db.initialize(process.cwd())
|
||||
const projectId = db.listProjects()[0]!.id
|
||||
db.saveSupervisionResult({ request: { trigger: 'manual', scope: { kind: 'projects', projectIds: [projectId] }, timeRange: { from: '2026-09-01T00:00:00Z', to: '2026-09-30T00:00:00Z' } },
|
||||
evidence: [{ id: 'quote', sourceType: 'conversation', sourceId: 'probe', title: 'Decision evidence', occurredAt: '2026-09-01T00:00:00Z', content: 'Decision A explicitly revised to Decision B; implementation remains pending.' }],
|
||||
output: { summary: 'Decision B', changeDigest: 'Revision A to B', openItems: ['Implementation pending'], events: [], entityChanges: [], relations: [],
|
||||
entities: [{ id: 'decision', label: 'Decision B', description: 'Explicit revision with evidence', sourceReferenceIds: ['quote'] }] } })
|
||||
let enabled = true, reads = 0, passed = false
|
||||
const gateway = new KnowledgeMcpGateway({} as KnowledgeService, { storyGraphService: { available: async () => enabled,
|
||||
read: (...args) => { assert.ok(enabled); reads++; return db.readStoryGraph(...args) } } })
|
||||
const signal = AbortSignal.timeout(150_000)
|
||||
const token = gateway.grant('probe', [], signal, 'none', undefined, undefined, undefined, undefined, undefined, { projectId, runtimeTarget: 'opencode' })!
|
||||
const quote = (value: string) => `'${value.replaceAll("'", "'\\''")}'`
|
||||
const child = spawn('ssh', ['-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=yes', '-o', 'ConnectTimeout=5', host!,
|
||||
`TMPDIR=/root/tmp ELECTRON_RUN_AS_NODE=1 /opt/GoodBuddy/goodbuddy ${quote(remoteScript!)} --host ${quote(opencode!)} ${quote(continueEntry!)}`], { stdio: ['pipe', 'pipe', 'inherit'], signal })
|
||||
const exited = new Promise<number | null>((resolve, reject) => { child.once('close', resolve); child.once('error', reject) })
|
||||
const queue: RuntimeProtocolBinaryFrame[] = []
|
||||
let receive: ((frame: RuntimeProtocolBinaryFrame) => void) | undefined
|
||||
let reject: ((error: Error) => void) | undefined
|
||||
const channel: RuntimeProtocolBinaryChannel = { channelId: 'graph-probe', channelEpoch: '1',
|
||||
send: async payload => { child.stdin.write(`${JSON.stringify({ frame: Buffer.from(payload).toString('base64') })}\n`) },
|
||||
receive: async () => queue.shift() ?? await new Promise((resolve, fail) => { receive = resolve; reject = fail }),
|
||||
close: () => reject?.(new Error('closed')), onClose: () => () => {} }
|
||||
const main = new MainImageToolSession(channel, undefined, signal, gateway.bindRemoteStoryGraph(token))
|
||||
const lines = createInterface({ input: child.stdout })
|
||||
lines.on('line', line => {
|
||||
const value = JSON.parse(line)
|
||||
if (value.frame) {
|
||||
const frame = { payload: Buffer.from(value.frame, 'base64'), sequence: '1', consume: async () => {} }
|
||||
if (receive) { const resolve = receive; receive = undefined; resolve(frame) } else queue.push(frame)
|
||||
} else if (value.disable) { enabled = false; child.stdin.write('{"disabled":true}\n') }
|
||||
else if (value.passed) { passed = true; emit({ ...value, desktopReads: reads, desktopOwnsDatabase: true }) }
|
||||
})
|
||||
try { assert.equal(await exited, 0); assert.ok(passed) }
|
||||
finally { lines.close(); main.close(); child.kill(); await gateway.dispose(); db.close() }
|
||||
}
|
||||
|
||||
void (process.argv[2] === '--host' ? hostProbe() : desktopProbe()).catch(error => { console.error(error); process.exitCode = 1 })
|
||||
+10
-5
@@ -4,11 +4,16 @@
|
||||
|
||||
正式站点地址:<https://mesalogo.github.io/goodbuddy/>
|
||||
|
||||
首页将 GoodBuddy 定位为免注册、支持信创软硬件的跨平台开源桌面 AI 助手,优先展示
|
||||
三大桌面系统、正式 x64 / arm64 下载入口、独立编译的龙芯 loong64 预览版、Agent Runtime,
|
||||
以及知识库、魔法笔记、智能心跳、桌面上下文和远程消息通道等桌面助手能力。
|
||||
下载区位于主要功能说明之前,并明确列出统信 UOS、银河麒麟、海光、兆芯、
|
||||
鲲鹏、飞腾和龙芯。页面不重复设置底部下载推广区。
|
||||
中文首页定位为面向国产化与信创环境的桌面 AI 工作空间,突出内置集成 OpenCode、
|
||||
Continue 与 DeepSeek Harness(预览)。中英文页面依次展示首屏编程示意、平台下载、
|
||||
知识引用与笔记计划场景、产品亮点、桌面助手和常见问题。下载区是 `main` 中的第二个
|
||||
`section`,紧接首屏、位于使用场景之前。导航和首屏保留直达下载的入口。
|
||||
下载区明确列出统信 UOS、银河麒麟、海光、兆芯、鲲鹏、飞腾和龙芯,区分正式
|
||||
x64 / arm64 安装包与独立龙芯 loong64 预览版,并说明兼容性与认证的区别。
|
||||
产品演示使用可维护的 HTML/CSS 与 SVG 图标,不使用真实截图。界面中直接呈现
|
||||
任务、回答和处理结果,不附加“界面示意”“示例回答”等标签。更新时同时维护
|
||||
中英文文本,演示内容须符合实际功能,不编造用户案例或性能指标。
|
||||
FAQ 使用原生 details/summary,说明模型费用、数据流向、组件准备和远端使用条件。
|
||||
英文页面位于 `en.html`,用平台与架构覆盖描述同一产品事实,不使用中文信创
|
||||
政策语汇;页面会标明独立的 LoongArch 实验预览,但三个平台的下载按钮仍始终
|
||||
前往 GitHub 最新正式 Release。
|
||||
|
||||
+117
-53
@@ -5,14 +5,14 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="GoodBuddy is an open-source desktop AI assistant for model chat, coding agents, knowledge, notes, and scheduled work. No GoodBuddy account is required."
|
||||
content="GoodBuddy is an open-source desktop AI workspace with built-in OpenCode, Continue, and DeepSeek Harness (preview) integrations for coding, knowledge, notes, and scheduled work."
|
||||
/>
|
||||
<link rel="canonical" href="https://mesalogo.github.io/goodbuddy/en.html" />
|
||||
<link rel="alternate" hreflang="zh-CN" href="https://mesalogo.github.io/goodbuddy/" />
|
||||
<link rel="alternate" hreflang="en" href="https://mesalogo.github.io/goodbuddy/en.html" />
|
||||
<link rel="alternate" hreflang="x-default" href="https://mesalogo.github.io/goodbuddy/en.html" />
|
||||
<meta name="theme-color" content="#f6f8fb" />
|
||||
<title>GoodBuddy | Open-source desktop AI assistant</title>
|
||||
<title>GoodBuddy | Open-source desktop AI workspace</title>
|
||||
<link
|
||||
rel="icon"
|
||||
href="./assets/goodbuddy-light.png"
|
||||
@@ -72,8 +72,10 @@
|
||||
|
||||
<nav class="site-navigation" id="site-navigation" aria-label="Main navigation" data-navigation>
|
||||
<a href="#download">Download</a>
|
||||
<a href="#features">Highlights</a>
|
||||
<a href="#assistant">Desktop assistant</a>
|
||||
<a href="#workflows">Workflows</a>
|
||||
<a href="#features">Features</a>
|
||||
<a href="#assistant">Assistant</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
</nav>
|
||||
|
||||
<div class="header-actions">
|
||||
@@ -119,17 +121,17 @@
|
||||
Windows · macOS · Linux
|
||||
</div>
|
||||
<h1 id="hero-title">
|
||||
No account required.<br />
|
||||
Model chat and coding agents<br />
|
||||
<span>on your desktop.</span>
|
||||
Your desktop<br />
|
||||
<span>AI workspace.</span>
|
||||
</h1>
|
||||
<p class="hero-lead">
|
||||
GoodBuddy manages conversations, knowledge, notes, and tasks on the desktop.
|
||||
Its Agent Runtime connects direct models, OpenCode, Continue, and DeepSeek Harness.
|
||||
Code, consult your sources, keep notes, and schedule follow-ups in GoodBuddy.
|
||||
Built-in integrations bring three coding agents to the desktop:
|
||||
OpenCode, Continue, and DeepSeek Harness (preview), alongside direct model chat.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button button--primary" href="#download">Download now</a>
|
||||
<a class="button button--secondary" href="#features">Explore Agent Runtime</a>
|
||||
<a class="button button--secondary" href="#workflows">Explore workflows</a>
|
||||
</div>
|
||||
<ul class="hero-facts" aria-label="Product highlights">
|
||||
<li>
|
||||
@@ -138,7 +140,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="m5 10 3 3 7-7" /></svg>
|
||||
Four Agent Runtime options
|
||||
Built-in coding agents, configurable models
|
||||
</li>
|
||||
<li>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="m5 10 3 3 7-7" /></svg>
|
||||
@@ -150,7 +152,7 @@
|
||||
<div
|
||||
class="product-stage"
|
||||
role="img"
|
||||
aria-label="GoodBuddy desktop app showing an AI coding task running through the unified Agent Runtime"
|
||||
aria-label="GoodBuddy coding workspace: Continue fixes a search issue with file changes and test results"
|
||||
data-tilt-stage
|
||||
>
|
||||
<div class="stage-glow stage-glow--one"></div>
|
||||
@@ -159,7 +161,7 @@
|
||||
<div class="window-bar">
|
||||
<div class="window-dots" aria-hidden="true"><span></span><span></span><span></span></div>
|
||||
<div class="window-title">GoodBuddy</div>
|
||||
<div class="window-status"><span></span> Runtime connected</div>
|
||||
<div class="window-status"><span></span> Agent connected</div>
|
||||
</div>
|
||||
<div class="app-layout">
|
||||
<aside class="app-sidebar" aria-hidden="true">
|
||||
@@ -180,13 +182,13 @@
|
||||
<div class="app-content">
|
||||
<div class="app-content-header">
|
||||
<div>
|
||||
<strong>Fix cross-platform build</strong>
|
||||
<span>Project: Desktop client</span>
|
||||
<strong>Fix empty search results</strong>
|
||||
<span>Project: Knowledge search</span>
|
||||
</div>
|
||||
<div class="mode-pill">Continue · Execute</div>
|
||||
</div>
|
||||
<div class="message-area">
|
||||
<div class="message message--user">Fix the build and validate all three desktop platforms.</div>
|
||||
<div class="message message--user">Search returns nothing with extra spaces. Fix it and add tests.</div>
|
||||
<div class="message message--assistant">
|
||||
<div class="assistant-label">
|
||||
<span class="assistant-avatar" aria-hidden="true">
|
||||
@@ -197,15 +199,15 @@
|
||||
</span>
|
||||
<strong>GoodBuddy</strong>
|
||||
</div>
|
||||
<p>Agent Runtime loaded the project, Skills, and controlled tools.</p>
|
||||
<p>Trimmed the query. Searches with no matches still show the empty state.</p>
|
||||
<div class="tool-card">
|
||||
<div class="tool-icon">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h10M4 18h7" /></svg>
|
||||
</div>
|
||||
<div><strong>Preparing environment</strong><span>Continue · Project scope · Controlled tools</span></div>
|
||||
<span class="tool-state">Ready</span>
|
||||
<div><strong>File changes</strong><span>search.ts · search.test.ts</span></div>
|
||||
<span class="tool-state">Updated</span>
|
||||
</div>
|
||||
<div class="plan-lines" aria-hidden="true"><span></span><span></span><span></span></div>
|
||||
<div class="demo-result"><code>query.trim()</code><span>Tests passed: whitespace and no-match cases</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="composer">
|
||||
@@ -217,9 +219,9 @@
|
||||
</div>
|
||||
<div class="floating-card floating-card--approval">
|
||||
<span class="floating-icon">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 5 6v5c0 4.5 2.8 8.6 7 10 4.2-1.4 7-5.5 7-10V6l-7-3Z" /><path d="m9 12 2 2 4-4" /></svg>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m8 7-5 5 5 5M16 7l5 5-5 5M14 4l-4 16" /></svg>
|
||||
</span>
|
||||
<span><strong>Unified Agent Runtime</strong><small>Direct models · OpenCode · Continue · DSH</small></span>
|
||||
<span><strong>Built-in coding agents</strong><small>OpenCode · Continue · DeepSeek Harness (preview)</small></span>
|
||||
</div>
|
||||
<div class="floating-card floating-card--scope">
|
||||
<span class="scope-dot"></span>
|
||||
@@ -229,15 +231,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="proof-strip" aria-label="Platform and Runtime support">
|
||||
<div class="section-inner proof-grid">
|
||||
<div><strong>4 Runtimes</strong><span>Multiple AI coding paths</span></div>
|
||||
<div><strong>3 platforms</strong><span>Windows / macOS / Linux</span></div>
|
||||
<div><strong>2 architectures</strong><span>x64 / arm64</span></div>
|
||||
<div><strong>1 workspace</strong><span>Select, configure, run, audit</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section download-section" id="download" aria-label="Cross-platform downloads">
|
||||
<div class="section-inner">
|
||||
<div class="download-grid">
|
||||
@@ -285,15 +278,53 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="workflows" class="section workflows-section" aria-labelledby="workflows-title">
|
||||
<div class="section-inner">
|
||||
<div class="section-heading">
|
||||
<div><p class="kicker">Everyday workflows</p><h2 id="workflows-title">From research to follow-up</h2></div>
|
||||
<p>Search project material, save decisions, and set reminders for what comes next.</p>
|
||||
</div>
|
||||
<div class="workflow-grid">
|
||||
<article class="workflow-card">
|
||||
<div class="workflow-copy">
|
||||
<p class="kicker">Knowledge and citations</p>
|
||||
<h3>Find an answer and check its source</h3>
|
||||
<p>Ask questions about local documents or your organization's knowledge base. Follow citations in the answer to the original material.</p>
|
||||
</div>
|
||||
<div class="workflow-demo">
|
||||
<div class="demo-bar">Knowledge Q&A</div>
|
||||
<p class="demo-question">What should we check before a staging release?</p>
|
||||
<p class="demo-answer">Check configuration, run regression tests, and confirm a rollback package is available. [1]</p>
|
||||
<div class="demo-source">[1] Release handbook · Pre-release checks</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="workflow-card">
|
||||
<div class="workflow-copy">
|
||||
<p class="kicker">Notes and schedules</p>
|
||||
<h3>Keep notes and schedule the next step</h3>
|
||||
<p>Save decisions in Magic Notes and schedule a reminder for work that needs a follow-up.</p>
|
||||
</div>
|
||||
<div class="workflow-demo">
|
||||
<div class="demo-bar">Project follow-up</div>
|
||||
<p class="demo-question">Save these notes and remind me to check release readiness next Monday.</p>
|
||||
<p class="demo-answer">Saved: review the search fix and check regression results before release.</p>
|
||||
<ul class="demo-checklist"><li>To-do: review the search fix</li><li>Schedule: next Monday, 09:00, release readiness check</li></ul>
|
||||
<div class="demo-source">Magic Notes · Release preparation</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section features-section" id="features" aria-labelledby="features-title">
|
||||
<div class="section-inner">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<p class="kicker">Product highlights</p>
|
||||
<h2 id="features-title">Agent Runtimes in one desktop interface</h2>
|
||||
<h2 id="features-title">Projects, knowledge, and agents on your desktop</h2>
|
||||
</div>
|
||||
<p>
|
||||
Configure Runtime selection, model connections, Skills, MCP, and work modes on the desktop.
|
||||
Choose an agent for the task, connect project files and knowledge sources, and review tool calls and results.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -306,10 +337,10 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">01</span>
|
||||
<h3>One entry point for multiple Agent Runtimes</h3>
|
||||
<p>Choose direct models, OpenCode, Continue, or DeepSeek Harness for each task without learning a new entry point.</p>
|
||||
<h3>Built-in OpenCode, Continue, and DeepSeek Harness</h3>
|
||||
<p>Select a coding agent, configure its model, and start a task in your project from the desktop interface. DeepSeek Harness is in preview. Direct model chat is also available.</p>
|
||||
<div class="provider-pills" aria-label="Supported Agent Runtimes">
|
||||
<span>Direct models</span><span>OpenCode</span><span>Continue</span><span>DeepSeek Harness</span>
|
||||
<span>Direct models</span><span>OpenCode</span><span>Continue</span><span>DeepSeek Harness (preview)</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -321,8 +352,8 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">02</span>
|
||||
<h3>Broad desktop architecture coverage</h3>
|
||||
<p>Official Windows, macOS, and Linux releases cover x64 and arm64, with a separate experimental LoongArch preview.</p>
|
||||
<h3>Local and SSH projects</h3>
|
||||
<p>Work with local projects, or use OpenCode and Continue with remote projects over SSH. Remote projects require the companion gbagent on the remote host.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card">
|
||||
@@ -333,8 +364,8 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">03</span>
|
||||
<h3>Runtime setup in the desktop app</h3>
|
||||
<p>Select the Runtime, model, work mode, and project in the desktop interface.</p>
|
||||
<h3>Connect existing knowledge services</h3>
|
||||
<p>Use a local knowledge base or configure Dify, FastGPT, and RAGFlow connections to retrieve material for your conversations.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card">
|
||||
@@ -345,11 +376,11 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">04</span>
|
||||
<h3>Runtime-specific capabilities and work modes</h3>
|
||||
<p>Skills, MCP, and tools follow each Runtime. Ask stays read-only, while Execute remains approval-controlled and auditable.</p>
|
||||
<h3>Separate questions from execution</h3>
|
||||
<p>Ask stays read-only. Execute runs with the current account's permissions and records tool calls. Available tools depend on the selected Runtime and configuration.</p>
|
||||
<div class="mode-row" aria-label="Two work modes">
|
||||
<span>Ask <small>Read-only</small></span>
|
||||
<span class="is-accent">Execute <small>Controlled</small></span>
|
||||
<span class="is-accent">Execute <small>Account permissions</small></span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -361,8 +392,8 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">05</span>
|
||||
<h3>Project context in one place</h3>
|
||||
<p>Organize conversations, knowledge, tasks, and run history by project without losing context when switching Runtime.</p>
|
||||
<h3>Skills and MCP tools</h3>
|
||||
<p>Import Skills and connect MCP services to give your chosen agent the tools you use at work.</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card feature-card--wide">
|
||||
@@ -392,8 +423,8 @@
|
||||
<p class="kicker">Desktop assistant</p>
|
||||
<h2 id="assistant-title">Conversations, knowledge, notes, and tasks on your desktop</h2>
|
||||
<p>
|
||||
GoodBuddy keeps reference material, to-dos, and long-running work in one desktop workspace,
|
||||
so everyday assistance and AI coding share the same project context.
|
||||
Keep reference material, to-dos, and ongoing work in one desktop workspace.
|
||||
Choose project files for each conversation. See the configuration guide for each agent's context and tool support.
|
||||
</p>
|
||||
<a class="text-link" href="#download">
|
||||
Choose your desktop release
|
||||
@@ -404,11 +435,11 @@
|
||||
<div class="assistant-list">
|
||||
<article>
|
||||
<span class="assistant-number">01</span>
|
||||
<div><h3>Turn sources into searchable knowledge</h3><p>Import files, folders, and web pages, then search with full text, vectors, and a knowledge graph.</p></div>
|
||||
<div><h3>Search files and web pages</h3><p>Import files, folders, and web pages, then configure full-text and vector search. Enable the knowledge graph to explore relationships between sources.</p></div>
|
||||
</article>
|
||||
<article>
|
||||
<span class="assistant-number">02</span>
|
||||
<div><h3>Notes, to-dos, and long-term follow-up</h3><p>Magic Notes captures ideas and tasks, while Heartbeat reviews progress, builds memory, and proposes next steps.</p></div>
|
||||
<div><h3>Notes, to-dos, and follow-up</h3><p>Magic Notes captures ideas and tasks. Heartbeat reviews work at configured intervals; both Heartbeat and scheduled work require the desktop app to be running.</p></div>
|
||||
</article>
|
||||
<article>
|
||||
<span class="assistant-number">03</span>
|
||||
@@ -416,11 +447,42 @@
|
||||
</article>
|
||||
<article>
|
||||
<span class="assistant-number">04</span>
|
||||
<div><h3>Keep working away from your computer</h3><p>Connect messaging channels to separate conversations and hand tasks to GoodBuddy on your desktop.</p></div>
|
||||
<div><h3>Reach your desktop assistant through messages</h3><p>Configure messaging channels to send tasks in separate conversations. Message processing requires your desktop to be online and the channel connection to be available.</p></div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="faq" class="section faq-section" aria-labelledby="faq-title">
|
||||
<div class="section-inner">
|
||||
<div class="section-heading">
|
||||
<div><p class="kicker">Before you start</p><h2 id="faq-title">Frequently asked questions</h2></div>
|
||||
<p>See the <a class="text-link" href="https://github.com/mesalogo/goodbuddy/blob/main/README.md">README getting-started guide</a> for installation and connection steps.</p>
|
||||
</div>
|
||||
<div class="faq-list">
|
||||
<details>
|
||||
<summary>Do I need an account or a paid model subscription?</summary>
|
||||
<p>No GoodBuddy account is required. External model services may need an account or API key and charge at their own rates. Local models need a suitable runtime on your machine.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Where does my data go?</summary>
|
||||
<p>Conversation history and notes are stored on your machine. Using models, knowledge services, MCP tools, or messaging channels sends relevant content to the services you configure. Model requests may include retrieved passages and attachments.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>How do I set up a coding agent?</summary>
|
||||
<p>Select an agent, configure its model connection, and open a project. Prepare or download the runtime components required by your agent and operating system. DeepSeek Harness is in preview. See the getting-started guide for installation steps.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Which platforms and Chinese domestic systems are supported?</summary>
|
||||
<p>Windows, macOS, and Linux releases cover x64 and ARM64, including corresponding Chinese domestic hardware. LoongArch has a separate experimental preview. For systems such as UOS and Kylin, choose the matching architecture and package; compatibility information does not imply vendor or whole-system certification.</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>What do SSH projects require on the remote host?</summary>
|
||||
<p>Remote projects with OpenCode and Continue require a working SSH connection, a companion gbagent matching your desktop version, and the required runtime environment on the host. Operations use the remote login account's permissions. See the README for setup.</p>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
@@ -432,11 +494,13 @@
|
||||
</span>
|
||||
<span>GoodBuddy</span>
|
||||
</a>
|
||||
<p>Open-source desktop AI assistant. No account required.</p>
|
||||
<p>Open-source desktop AI workspace. No GoodBuddy account required.</p>
|
||||
<div class="footer-links">
|
||||
<a href="#download">Download</a>
|
||||
<a href="#features">Agent Runtime</a>
|
||||
<a href="#assistant">Desktop assistant</a>
|
||||
<a href="#workflows">Workflows</a>
|
||||
<a href="#features">Features</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
<a href="https://github.com/mesalogo/goodbuddy/blob/main/README.md">Getting started</a>
|
||||
<a href="https://github.com/mesalogo/goodbuddy" target="_blank" rel="noreferrer">
|
||||
GitHub<span class="sr-only">(opens in a new window)</span>
|
||||
</a>
|
||||
|
||||
+120
-57
@@ -5,14 +5,14 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="GoodBuddy 是免注册的开源桌面 AI 助手,支持 Windows、macOS、Linux、国产 x64/ARM64 和龙芯 LoongArch 实验预览版。"
|
||||
content="GoodBuddy 是面向国产化与信创环境的开源桌面 AI 工作空间,内置集成 OpenCode、Continue 与 DeepSeek Harness(预览),支持 AI 编程、知识引用、笔记和定时计划。"
|
||||
/>
|
||||
<link rel="canonical" href="https://mesalogo.github.io/goodbuddy/" />
|
||||
<link rel="alternate" hreflang="zh-CN" href="https://mesalogo.github.io/goodbuddy/" />
|
||||
<link rel="alternate" hreflang="en" href="https://mesalogo.github.io/goodbuddy/en.html" />
|
||||
<link rel="alternate" hreflang="x-default" href="https://mesalogo.github.io/goodbuddy/en.html" />
|
||||
<meta name="theme-color" content="#f6f8fb" />
|
||||
<title>GoodBuddy|免注册的跨平台开源桌面 AI 助手</title>
|
||||
<title>GoodBuddy|面向国产化与信创环境的桌面 AI 工作空间</title>
|
||||
<link
|
||||
rel="icon"
|
||||
href="./assets/goodbuddy-light.png"
|
||||
@@ -72,8 +72,10 @@
|
||||
|
||||
<nav class="site-navigation" id="site-navigation" aria-label="主导航" data-navigation>
|
||||
<a href="#download">下载</a>
|
||||
<a href="#features">产品亮点</a>
|
||||
<a href="#assistant">桌面助手</a>
|
||||
<a href="#workflows">场景</a>
|
||||
<a href="#features">功能</a>
|
||||
<a href="#assistant">助手</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
</nav>
|
||||
|
||||
<div class="header-actions">
|
||||
@@ -119,18 +121,18 @@
|
||||
Windows · macOS · Linux · 龙芯预览版
|
||||
</div>
|
||||
<h1 id="hero-title">
|
||||
免注册,<br />
|
||||
支持信创软硬件的<br />
|
||||
<span>桌面 AI 助手。</span>
|
||||
面向国产化<br />
|
||||
与信创环境的<br />
|
||||
<span>桌面 AI 工作空间。</span>
|
||||
</h1>
|
||||
<p class="hero-lead">
|
||||
GoodBuddy 提供模型对话、AI 编程、知识库、笔记和任务管理。
|
||||
Agent Runtime 可连接直连模型、OpenCode、Continue 和 DeepSeek Harness;
|
||||
选择项目和 Runtime 后即可开始。
|
||||
在 GoodBuddy 中写代码、查资料、记笔记,安排日常任务。
|
||||
内置集成 OpenCode、Continue 与 DeepSeek Harness(预览)三种编程 Agent,
|
||||
也支持直连模型对话。
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button button--primary" href="#download">立即下载</a>
|
||||
<a class="button button--secondary" href="#features">了解 Agent Runtime</a>
|
||||
<a class="button button--secondary" href="#workflows">查看使用场景</a>
|
||||
</div>
|
||||
<ul class="hero-facts hero-facts--aligned" aria-label="产品特性概览">
|
||||
<li>
|
||||
@@ -143,7 +145,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="m5 10 3 3 7-7" /></svg>
|
||||
4 类 Agent Runtime 统一接入
|
||||
内置编程 Agent,按需配置模型
|
||||
</li>
|
||||
<li>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true"><path d="m5 10 3 3 7-7" /></svg>
|
||||
@@ -155,7 +157,7 @@
|
||||
<div
|
||||
class="product-stage"
|
||||
role="img"
|
||||
aria-label="GoodBuddy 桌面应用界面示意:在统一 Agent Runtime 中选择 AI 编程工具并受控执行"
|
||||
aria-label="GoodBuddy 编程工作区:使用 Continue 修复搜索问题,查看文件变更与测试结果"
|
||||
data-tilt-stage
|
||||
>
|
||||
<div class="stage-glow stage-glow--one"></div>
|
||||
@@ -164,7 +166,7 @@
|
||||
<div class="window-bar">
|
||||
<div class="window-dots" aria-hidden="true"><span></span><span></span><span></span></div>
|
||||
<div class="window-title">GoodBuddy</div>
|
||||
<div class="window-status"><span></span> Runtime 已连接</div>
|
||||
<div class="window-status"><span></span> Agent 已连接</div>
|
||||
</div>
|
||||
<div class="app-layout">
|
||||
<aside class="app-sidebar" aria-hidden="true">
|
||||
@@ -185,13 +187,13 @@
|
||||
<div class="app-content">
|
||||
<div class="app-content-header">
|
||||
<div>
|
||||
<strong>修复跨平台构建</strong>
|
||||
<span>项目:桌面客户端</span>
|
||||
<strong>修复搜索空结果</strong>
|
||||
<span>项目:知识检索</span>
|
||||
</div>
|
||||
<div class="mode-pill">Continue · 执行</div>
|
||||
</div>
|
||||
<div class="message-area">
|
||||
<div class="message message--user">修复构建问题,并验证三个桌面系统。</div>
|
||||
<div class="message message--user">搜索词带空格时没有结果,请修复并补充测试。</div>
|
||||
<div class="message message--assistant">
|
||||
<div class="assistant-label">
|
||||
<span class="assistant-avatar" aria-hidden="true">
|
||||
@@ -202,15 +204,15 @@
|
||||
</span>
|
||||
<strong>GoodBuddy</strong>
|
||||
</div>
|
||||
<p>已通过 Agent Runtime 载入项目、Skills 和受控工具。</p>
|
||||
<p>已去除查询词首尾的空格,无匹配时仍显示空状态提示。</p>
|
||||
<div class="tool-card">
|
||||
<div class="tool-icon">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h10M4 18h7" /></svg>
|
||||
</div>
|
||||
<div><strong>准备编程环境</strong><span>Continue · 项目范围 · 受控工具</span></div>
|
||||
<span class="tool-state">已就绪</span>
|
||||
<div><strong>文件变更</strong><span>search.ts · search.test.ts</span></div>
|
||||
<span class="tool-state">已修改</span>
|
||||
</div>
|
||||
<div class="plan-lines" aria-hidden="true"><span></span><span></span><span></span></div>
|
||||
<div class="demo-result"><code>query.trim()</code><span>测试通过:空格输入、无匹配结果</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="composer">
|
||||
@@ -222,9 +224,9 @@
|
||||
</div>
|
||||
<div class="floating-card floating-card--approval">
|
||||
<span class="floating-icon">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 5 6v5c0 4.5 2.8 8.6 7 10 4.2-1.4 7-5.5 7-10V6l-7-3Z" /><path d="m9 12 2 2 4-4" /></svg>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m8 7-5 5 5 5M16 7l5 5-5 5M14 4l-4 16" /></svg>
|
||||
</span>
|
||||
<span><strong>统一 Agent Runtime</strong><small>直连模型 · OpenCode · Continue · DSH</small></span>
|
||||
<span><strong>内置编程 Agent</strong><small>OpenCode · Continue · DeepSeek Harness(预览)</small></span>
|
||||
</div>
|
||||
<div class="floating-card floating-card--scope">
|
||||
<span class="scope-dot"></span>
|
||||
@@ -234,15 +236,6 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="proof-strip" aria-label="平台与 Runtime 支持概览">
|
||||
<div class="section-inner proof-grid">
|
||||
<div><strong>4 类 Runtime</strong><span>多种 AI 编程路径</span></div>
|
||||
<div><strong>3 大系统</strong><span>Windows / macOS / Linux</span></div>
|
||||
<div><strong>3 种架构</strong><span>x64 / arm64 / loong64 预览</span></div>
|
||||
<div><strong>1 个入口</strong><span>选择、配置、运行、审计</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section download-section" id="download" aria-label="跨平台下载">
|
||||
<div class="section-inner">
|
||||
<div class="download-grid">
|
||||
@@ -351,8 +344,7 @@
|
||||
<h3 id="domestic-support-title">桌面版本覆盖国产 x64、ARM64 和龙芯 LoongArch</h3>
|
||||
<p>
|
||||
正式下载覆盖国产 x64 与 ARM64;龙芯 LoongArch 使用独立编译的
|
||||
loong64 预览版。这里描述的是系统、指令集与安装包兼容性,
|
||||
不代表已完成所有厂商和整机认证。
|
||||
loong64 预览版。请按系统与处理器架构选择安装包,具体兼容和认证范围见下方常见问题。
|
||||
</p>
|
||||
</div>
|
||||
<div class="domestic-support__items">
|
||||
@@ -371,15 +363,53 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="workflows" class="section workflows-section" aria-labelledby="workflows-title">
|
||||
<div class="section-inner">
|
||||
<div class="section-heading">
|
||||
<div><p class="kicker">使用场景</p><h2 id="workflows-title">从查资料到跟进工作</h2></div>
|
||||
<p>查询项目资料,保存讨论结论,为待办安排提醒。</p>
|
||||
</div>
|
||||
<div class="workflow-grid">
|
||||
<article class="workflow-card">
|
||||
<div class="workflow-copy">
|
||||
<p class="kicker">知识检索</p>
|
||||
<h3>答案附带资料出处</h3>
|
||||
<p>向本地资料或组织知识库提问,在回答中查看引用,找到对应原文。</p>
|
||||
</div>
|
||||
<div class="workflow-demo">
|
||||
<div class="demo-bar">知识问答</div>
|
||||
<p class="demo-question">测试环境发布前需要检查什么?</p>
|
||||
<p class="demo-answer">需要核对配置、执行回归测试,并确认回滚包可用。[1]</p>
|
||||
<div class="demo-source">[1] 《发布手册》 · 发布前检查</div>
|
||||
</div>
|
||||
</article>
|
||||
<article class="workflow-card">
|
||||
<div class="workflow-copy">
|
||||
<p class="kicker">笔记与定时计划</p>
|
||||
<h3>记录待办,安排下一次跟进</h3>
|
||||
<p>用魔法笔记保存讨论结论,把需要跟进的事项设为定时任务。</p>
|
||||
</div>
|
||||
<div class="workflow-demo">
|
||||
<div class="demo-bar">项目跟进</div>
|
||||
<p class="demo-question">记录本次讨论,下周一提醒我检查发布准备。</p>
|
||||
<p class="demo-answer">已记录:搜索修复待复核,发布前检查回归结果。</p>
|
||||
<ul class="demo-checklist"><li>待办:复核搜索修复</li><li>定时计划:下周一 09:00 检查发布准备</li></ul>
|
||||
<div class="demo-source">魔法笔记 · 发布准备</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section features-section" id="features" aria-labelledby="features-title">
|
||||
<div class="section-inner">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<p class="kicker">产品亮点</p>
|
||||
<h2 id="features-title">本地工作空间,统一 Runtime,也支持信创环境</h2>
|
||||
<h2 id="features-title">项目、知识与编程 Agent</h2>
|
||||
</div>
|
||||
<p>
|
||||
从模型连接到执行记录,从桌面工作到国产软硬件适配,都由同一个应用承载。
|
||||
选择适合任务的 Agent,连接项目与知识来源,并查看工具调用和运行结果。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -392,10 +422,10 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">01</span>
|
||||
<h3>一个入口,连接多种 Agent Runtime</h3>
|
||||
<p>按任务选择直连模型、OpenCode、Continue 或 DeepSeek Harness,不必为每种工具重新适应一套入口。</p>
|
||||
<h3>内置 OpenCode、Continue 与 DeepSeek Harness</h3>
|
||||
<p>在图形界面中选择编程 Agent、配置模型,直接向项目发起任务。DeepSeek Harness 为预览功能,也支持直连模型对话。</p>
|
||||
<div class="provider-pills" aria-label="支持的 Agent Runtime">
|
||||
<span>直连模型</span><span>OpenCode</span><span>Continue</span><span>DeepSeek Harness</span>
|
||||
<span>直连模型</span><span>OpenCode</span><span>Continue</span><span>DeepSeek Harness(预览)</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -407,8 +437,8 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">02</span>
|
||||
<h3>覆盖主流桌面与信创环境</h3>
|
||||
<p>Windows、macOS、Linux 正式提供 x64 与 arm64 版本,龙芯 LoongArch 另有独立实验预览。</p>
|
||||
<h3>本地项目与 SSH 远端项目</h3>
|
||||
<p>处理电脑上的代码,也可通过 OpenCode、Continue 连接 SSH 远端项目。远端运行配套的 GoodBuddy Agent。</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card">
|
||||
@@ -419,8 +449,8 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">03</span>
|
||||
<h3>图形化 Runtime 配置</h3>
|
||||
<p>在图形界面中选择 Runtime、模型、工作模式和项目,不用先记住复杂命令。</p>
|
||||
<h3>接入已有知识服务</h3>
|
||||
<p>使用本地知识库,或配置 Dify、FastGPT、RAGFlow 连接,将已有资料用于检索与问答。</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card">
|
||||
@@ -431,11 +461,11 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">04</span>
|
||||
<h3>按 Runtime 分配能力与工作模式</h3>
|
||||
<p>Skills、MCP 和工具按 Runtime 分配;Ask 保持只读,Execute 继续经过审批和审计。</p>
|
||||
<h3>区分问答与执行</h3>
|
||||
<p>Ask 保持只读;Execute 按当前账号权限执行操作,并记录工具调用。可用工具随所选 Runtime 和配置变化。</p>
|
||||
<div class="mode-row" aria-label="两种工作模式">
|
||||
<span>Ask <small>只读</small></span>
|
||||
<span class="is-accent">Execute <small>受控执行</small></span>
|
||||
<span class="is-accent">Execute <small>按账号权限执行</small></span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -447,8 +477,8 @@
|
||||
</svg>
|
||||
</div>
|
||||
<span class="feature-number">05</span>
|
||||
<h3>项目上下文集中管理</h3>
|
||||
<p>对话、知识、任务和运行记录按项目组织,切换 Runtime 不必丢掉工作上下文。</p>
|
||||
<h3>Skills 与 MCP 工具扩展</h3>
|
||||
<p>导入 Skills,连接 MCP 服务,为所选 Agent 配置工作中需要的工具。</p>
|
||||
</article>
|
||||
|
||||
<article class="feature-card feature-card--wide">
|
||||
@@ -476,10 +506,10 @@
|
||||
</span>
|
||||
</div>
|
||||
<p class="kicker">桌面助手</p>
|
||||
<h2 id="assistant-title">对话、知识、笔记与任务,都在桌面上</h2>
|
||||
<h2 id="assistant-title">桌面上的资料与日常工作</h2>
|
||||
<p>
|
||||
GoodBuddy 把资料、待办和长期任务放进同一个桌面工作区,
|
||||
日常协作与 AI 编程共享项目上下文。
|
||||
资料、笔记和待办集中管理,对话时选择需要的项目与文件。
|
||||
不同 Agent 支持的上下文和工具见配置说明。
|
||||
</p>
|
||||
<a
|
||||
class="text-link"
|
||||
@@ -493,11 +523,11 @@
|
||||
<div class="assistant-list">
|
||||
<article>
|
||||
<span class="assistant-number">01</span>
|
||||
<div><h3>资料变成可问的知识</h3><p>导入文件、目录和网页,用全文、向量和知识图谱一起检索。</p></div>
|
||||
<div><h3>检索文件与网页</h3><p>导入文件、目录和网页,配置全文与向量检索。启用知识图谱后,还可查看资料之间的关系。</p></div>
|
||||
</article>
|
||||
<article>
|
||||
<span class="assistant-number">02</span>
|
||||
<div><h3>笔记、待办和长期跟进</h3><p>魔法笔记管理灵感与待办,智能心跳持续回顾、沉淀记忆并提出后续任务。</p></div>
|
||||
<div><h3>笔记与工作回顾</h3><p>魔法笔记记录灵感与待办,智能心跳按设定周期回顾工作。定时任务和心跳需要桌面应用保持运行。</p></div>
|
||||
</article>
|
||||
<article>
|
||||
<span class="assistant-number">03</span>
|
||||
@@ -505,11 +535,42 @@
|
||||
</article>
|
||||
<article>
|
||||
<span class="assistant-number">04</span>
|
||||
<div><h3>离开电脑也能继续</h3><p>通过微信、企业微信和钉钉连接独立会话,把任务交给桌面上的 GoodBuddy。</p></div>
|
||||
<div><h3>从微信或钉钉发送任务</h3><p>连接微信、企业微信或钉钉,用独立会话联系桌面助手。使用时保持电脑在线、消息渠道已连接。</p></div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="faq" class="section faq-section" aria-labelledby="faq-title">
|
||||
<div class="section-inner">
|
||||
<div class="section-heading">
|
||||
<div><p class="kicker">开始之前</p><h2 id="faq-title">常见问题</h2></div>
|
||||
<p>安装与连接步骤见 <a class="text-link" href="https://github.com/mesalogo/goodbuddy/blob/main/README.zh-CN.md">README 入门指南</a>。</p>
|
||||
</div>
|
||||
<div class="faq-list">
|
||||
<details>
|
||||
<summary>需要注册账号或支付模型费用吗?</summary>
|
||||
<p>无需注册 GoodBuddy 账号。外部模型服务可能需要账号或 API 密钥,费用按服务商定价收取。使用本地模型时,需准备相应的运行环境。</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>我的资料会发送到哪里?</summary>
|
||||
<p>对话记录与笔记保存在本机。使用模型、知识服务、MCP 工具或消息渠道时,相关内容会发送到你配置的服务。模型请求可能包含检索到的资料片段和附件。</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>第一次使用编程 Agent,需要配置什么?</summary>
|
||||
<p>选择 Agent,配置模型连接并打开项目;根据所选 Agent 和系统准备或下载运行组件。DeepSeek Harness 为预览功能,安装步骤见入门指南。</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>支持哪些平台和信创环境?</summary>
|
||||
<p>Windows、macOS、Linux 提供 x64 与 ARM64 版本,国产环境覆盖相应架构;龙芯 LoongArch 提供独立实验预览版。统信 UOS、银河麒麟等环境需按系统、架构和安装包选择,兼容性说明不等同于整机或厂商认证。</p>
|
||||
</details>
|
||||
<details>
|
||||
<summary>使用 SSH 远端项目需要准备什么?</summary>
|
||||
<p>OpenCode、Continue 的远端项目需要可用的 SSH 连接,并在远端准备与桌面版本配套的 gbagent 及所需运行环境。操作使用远端登录账号权限;具体配置见 README 入门指南。</p>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
@@ -521,11 +582,13 @@
|
||||
</span>
|
||||
<span>GoodBuddy</span>
|
||||
</a>
|
||||
<p>免注册的跨平台开源桌面 AI 助手</p>
|
||||
<p>面向国产化与信创环境的开源桌面 AI 工作空间</p>
|
||||
<div class="footer-links">
|
||||
<a href="#download">下载</a>
|
||||
<a href="#features">Agent Runtime</a>
|
||||
<a href="#assistant">桌面助手</a>
|
||||
<a href="#workflows">场景</a>
|
||||
<a href="#features">功能</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
<a href="https://github.com/mesalogo/goodbuddy/blob/main/README.zh-CN.md">入门指南</a>
|
||||
<a href="https://github.com/mesalogo/goodbuddy" target="_blank" rel="noreferrer">
|
||||
GitHub<span class="sr-only">(在新窗口打开)</span>
|
||||
</a>
|
||||
|
||||
+22
-11
@@ -248,10 +248,11 @@ for (const breakpoint of ["1199px", "959px", "719px"]) {
|
||||
}
|
||||
|
||||
const requiredCopy = [
|
||||
"免注册",
|
||||
"支持信创软硬件的",
|
||||
"无需注册 GoodBuddy 账号",
|
||||
"面向国产化",
|
||||
"与信创环境的",
|
||||
"产品亮点",
|
||||
"本地工作空间,统一 Runtime,也支持信创环境",
|
||||
"桌面 AI 工作空间",
|
||||
"桌面版本覆盖国产 x64、ARM64 和龙芯 LoongArch",
|
||||
"龙芯 LoongArch 独立编译预览版",
|
||||
"龙芯 LoongArch(实验预览)",
|
||||
@@ -262,13 +263,12 @@ const requiredCopy = [
|
||||
"银河麒麟",
|
||||
"海光 · 兆芯(x64)",
|
||||
"鲲鹏 · 飞腾(ARM64)",
|
||||
"Agent Runtime 可连接",
|
||||
"直连模型、OpenCode、Continue",
|
||||
"内置集成 OpenCode、Continue",
|
||||
"DeepSeek Harness",
|
||||
"魔法笔记",
|
||||
"智能心跳",
|
||||
"文件、截图、应用窗口、剪贴板和离线语音",
|
||||
"微信、企业微信和钉钉",
|
||||
"微信、企业微信或钉钉",
|
||||
];
|
||||
|
||||
for (const copy of requiredCopy) {
|
||||
@@ -276,13 +276,10 @@ for (const copy of requiredCopy) {
|
||||
}
|
||||
|
||||
const requiredEnglishCopy = [
|
||||
"No account required.",
|
||||
"No GoodBuddy account required",
|
||||
"Product highlights",
|
||||
"Model chat and coding agents",
|
||||
"on your desktop.",
|
||||
"Windows, macOS, and Linux",
|
||||
"separate experimental LoongArch preview",
|
||||
"Agent Runtimes in one desktop interface",
|
||||
"LoongArch has a separate experimental preview",
|
||||
"Direct models",
|
||||
"OpenCode",
|
||||
"Continue",
|
||||
@@ -294,6 +291,20 @@ for (const copy of requiredEnglishCopy) {
|
||||
report(englishHtml.includes(copy), `英文页面缺少准确文案:${copy}`);
|
||||
}
|
||||
|
||||
for (const [relativePath, content] of [["index.html", html], ["en.html", englishHtml]]) {
|
||||
for (const id of ["workflows", "faq"]) {
|
||||
report(content.includes(`id="${id}"`), `${relativePath} 缺少 ${id} 区域`);
|
||||
}
|
||||
const mainContent = content.match(/<main\b[^>]*>([\s\S]*?)<\/main>/)?.[1] ?? "";
|
||||
const mainSections = [...mainContent.matchAll(/<section\b[^>]*>/g)];
|
||||
report(/\sid="download"/.test(mainSections[1]?.[0] ?? ""),
|
||||
`${relativePath} 下载区必须是 main 中的第二个 section`);
|
||||
report((content.match(/<summary>/g) ?? []).length >= 4,
|
||||
`${relativePath} 常见问题必须使用原生可展开控件`);
|
||||
report(!/approval-controlled|受控执行|继续经过审批/.test(content),
|
||||
`${relativePath} 不得保留过时的执行审批文案`);
|
||||
}
|
||||
|
||||
for (const forbiddenCopy of ["信创", "国产", "统信 UOS", "银河麒麟", "海光", "兆芯", "鲲鹏", "飞腾"]) {
|
||||
report(!englishHtml.includes(forbiddenCopy), `英文页面不得包含中文信创文案:${forbiddenCopy}`);
|
||||
}
|
||||
|
||||
+167
-16
@@ -957,24 +957,145 @@ p {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.plan-lines {
|
||||
.demo-result {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
margin-top: var(--space-3);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.plan-lines span {
|
||||
height: 5px;
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--surface-muted);
|
||||
.demo-result code {
|
||||
padding: 8px 10px;
|
||||
border-left: 2px solid var(--success);
|
||||
background: var(--success-subtle);
|
||||
}
|
||||
|
||||
.plan-lines span:nth-child(2) {
|
||||
width: 88%;
|
||||
.workflow-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
.plan-lines span:nth-child(3) {
|
||||
width: 62%;
|
||||
.feature-grid > .feature-card:last-child {
|
||||
grid-column: 1 / -1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.workflow-card {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: var(--radius-large);
|
||||
background: var(--surface-raised);
|
||||
}
|
||||
|
||||
.workflow-copy {
|
||||
padding: var(--space-8);
|
||||
}
|
||||
|
||||
.workflow-copy h3 {
|
||||
font-size: clamp(1.25rem, 2vw, 1.65rem);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.workflow-copy > p:last-child {
|
||||
margin-bottom: 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.workflow-demo {
|
||||
flex: 1;
|
||||
padding: var(--space-6);
|
||||
margin: 0 var(--space-6) var(--space-6);
|
||||
border: 1px solid var(--border-default);
|
||||
border-radius: var(--radius-card);
|
||||
background: var(--surface-subtle);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.demo-bar {
|
||||
padding-bottom: var(--space-4);
|
||||
margin-bottom: var(--space-6);
|
||||
border-bottom: 1px solid var(--border-default);
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.demo-question {
|
||||
width: fit-content;
|
||||
padding: var(--space-3) var(--space-4);
|
||||
margin-left: auto;
|
||||
border-radius: 12px 12px 2px 12px;
|
||||
background: var(--accent-solid);
|
||||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
.demo-answer {
|
||||
margin-block: var(--space-6) var(--space-4);
|
||||
}
|
||||
|
||||
.demo-source {
|
||||
padding: var(--space-3) var(--space-4);
|
||||
border-left: 2px solid var(--accent);
|
||||
background: var(--surface-raised);
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.demo-checklist {
|
||||
padding-left: var(--space-6);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.demo-checklist li + li {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.faq-list {
|
||||
border-top: 1px solid var(--border-default);
|
||||
}
|
||||
|
||||
.faq-list details {
|
||||
padding: var(--space-6) 0;
|
||||
border-bottom: 1px solid var(--border-default);
|
||||
}
|
||||
|
||||
.faq-list summary {
|
||||
cursor: pointer;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.faq-list summary::marker {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.faq-list summary:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 6px;
|
||||
}
|
||||
|
||||
.faq-list details p {
|
||||
max-width: 900px;
|
||||
margin: var(--space-4) 0 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
@media (max-width: 719px) {
|
||||
.workflow-grid {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.workflow-copy {
|
||||
padding: var(--space-6);
|
||||
}
|
||||
|
||||
.workflow-demo {
|
||||
padding: var(--space-4);
|
||||
margin: 0 var(--space-4) var(--space-4);
|
||||
}
|
||||
}
|
||||
|
||||
.composer {
|
||||
@@ -1849,16 +1970,46 @@ p {
|
||||
}
|
||||
|
||||
.product-stage {
|
||||
min-height: 365px;
|
||||
min-height: 0;
|
||||
margin-top: var(--space-4);
|
||||
padding: 24px 0 72px;
|
||||
}
|
||||
|
||||
.app-window {
|
||||
--window-scale: 0.59;
|
||||
--window-translate-x: -50%;
|
||||
left: 50%;
|
||||
width: 680px;
|
||||
transform-origin: top center;
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.app-layout {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.app-sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message-area {
|
||||
padding: 20px 16px 88px;
|
||||
}
|
||||
|
||||
.message {
|
||||
max-width: 100%;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tool-card strong,
|
||||
.tool-card span,
|
||||
.app-content-header span,
|
||||
.mode-pill {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.composer {
|
||||
right: 12px;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.floating-card--scope {
|
||||
|
||||
@@ -46,14 +46,19 @@ const server = (name: string) => ({
|
||||
|
||||
// Keep the actual helper and adapter config builder together: mocking the
|
||||
// adapter constructor would miss the model-profile configuration-loss bug.
|
||||
it('delivers current ACP session MCP capabilities through the bridged model config, but not Ask', async () => {
|
||||
it('delivers current ACP session MCP capabilities in Execute and Ask with read-only Ask authorization', async () => {
|
||||
const configs: Array<Record<string, unknown>> = []
|
||||
vi.stubGlobal('fetch', vi.fn(async () => Response.json({
|
||||
operationId: 'operation', workMode: transport.mode
|
||||
})))
|
||||
vi.spyOn(ContinueHostAdapter.prototype, 'run').mockImplementation(async function (
|
||||
this: ContinueHostAdapter, _prompt, _signal, _authorize, options
|
||||
this: ContinueHostAdapter, _prompt, _signal, authorize, options
|
||||
) {
|
||||
if (options?.workMode === 'ask') {
|
||||
expect(await authorize!({ toolName: 'story_graph_search' } as never)).toBe('once')
|
||||
expect(await authorize!({ toolName: 'generate_image' } as never)).toBe('deny')
|
||||
expect(await authorize!({ toolName: 'write_file' } as never)).toBe('deny')
|
||||
}
|
||||
const path = await (this as unknown as {
|
||||
createRunConfig(options: ContinueHostRunOptions): Promise<string>
|
||||
}).createRunConfig(options!)
|
||||
@@ -83,7 +88,8 @@ it('delivers current ACP session MCP capabilities through the bridged model conf
|
||||
requestOptions: { headers: { Authorization: 'Bearer session-test-token' } } }],
|
||||
[{ name: 'replacement', type: 'streamable-http', url: server('replacement').url,
|
||||
requestOptions: { headers: { Authorization: 'Bearer session-test-token' } } }],
|
||||
[], []
|
||||
[{ name: 'replacement', type: 'streamable-http', url: server('replacement').url,
|
||||
requestOptions: { headers: { Authorization: 'Bearer session-test-token' } } }], []
|
||||
])
|
||||
expect(configs[0].models).toEqual([expect.objectContaining({ model: 'test-model' })])
|
||||
} finally {
|
||||
@@ -92,7 +98,7 @@ it('delivers current ACP session MCP capabilities through the bridged model conf
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps local profile MCP scoping and filters explicit session servers in Ask at the adapter boundary', async () => {
|
||||
it('keeps local profile MCP scoping and includes Main-bound session servers in Ask', async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'goodbuddy-cn-scope-'))
|
||||
try {
|
||||
const configPath = join(root, 'native.json')
|
||||
@@ -112,7 +118,9 @@ it('keeps local profile MCP scoping and filters explicit session servers in Ask
|
||||
const path = await create({ workMode: 'ask', sessionMcpServers: [{
|
||||
name: 'remote', type: 'streamable-http', url: 'http://127.0.0.1:12346', requestOptions: { headers: {} }
|
||||
}] })
|
||||
expect(JSON.parse(await readFile(path, 'utf8')).mcpServers ?? []).toEqual([])
|
||||
expect(JSON.parse(await readFile(path, 'utf8')).mcpServers).toEqual([{
|
||||
name: 'remote', type: 'streamable-http', url: 'http://127.0.0.1:12346', requestOptions: { headers: {} }
|
||||
}])
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { promptWithUntrustedConversationHistory } from '../main/agent/runtime-co
|
||||
import type { AgentExecutionRequest, AgentImage } from '../main/agent/runtime'
|
||||
import { createUnixModelBridgeExchange } from './model-bridge-broker'
|
||||
import { ModelBridgeLoopbackProxy, MODEL_BRIDGE_SDK_AUTH_SENTINEL, openCodeModelBridgeModelId, type ModelBridgeProtocol } from './model-bridge-helper'
|
||||
import { isStoryGraphTool } from '../shared/story-graph-tools'
|
||||
|
||||
export async function runContinueAcpHelper(options: {
|
||||
socketPath: string
|
||||
@@ -88,11 +89,11 @@ export async function runContinueAcpHelper(options: {
|
||||
return await response.json() as { operationId: string; workMode: 'ask' | 'execute' }
|
||||
}) : undefined
|
||||
const workMode = route?.workMode ?? options.workMode
|
||||
const sessionMcpServers = workMode === 'execute' ? session.mcpServers.map(server => {
|
||||
const sessionMcpServers = session.mcpServers.map(server => {
|
||||
if (!('type' in server) || server.type !== 'http') throw new Error('Continue remote MCP requires HTTP')
|
||||
return { name: server.name, type: 'streamable-http' as const, url: server.url,
|
||||
requestOptions: { headers: Object.fromEntries(server.headers.map(header => [header.name, header.value])) } }
|
||||
}) : []
|
||||
})
|
||||
const adapter = new ContinueHostAdapter({
|
||||
binaryPath: options.entrypoint, configPath: '', workspace: session.cwd, cacheRoot: root, mode: 'chat',
|
||||
modelProfile: { id: 'agent-bridge', name: 'GoodBuddy', modelName: options.model,
|
||||
@@ -111,7 +112,7 @@ export async function runContinueAcpHelper(options: {
|
||||
session.active = { abort, adapter, finished: new Promise(resolve => { finish = resolve }) }
|
||||
try {
|
||||
const result = await adapter.run(promptWithUntrustedConversationHistory({ prompt: text, history: session.history }, true), abort.signal,
|
||||
async () => workMode === 'execute' ? 'once' : 'deny', {
|
||||
async approval => workMode === 'execute' || isStoryGraphTool(approval.toolName ?? '') ? 'once' : 'deny', {
|
||||
workMode, images, sessionMcpServers,
|
||||
onEvent: async event => {
|
||||
if (event.type === 'text') await connection.sessionUpdate({ sessionId, update: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema, type CallToolResult } from '@modelcontextprotocol/sdk/types.js'
|
||||
import { z } from 'zod'
|
||||
import { imageToolInputSchema, imageToolName } from '../shared/image-generation-contracts'
|
||||
import { storyGraphTools, type StoryGraphToolName } from '../shared/story-graph-tools'
|
||||
import {
|
||||
decodeRemoteImageToolMessage, encodeRemoteImageToolMessage,
|
||||
remoteImageToolMaximumBytes, remoteImageToolReplySchema, type remoteImageToolSchema
|
||||
@@ -44,12 +45,23 @@ export class AgentImageToolMcp {
|
||||
}
|
||||
const body: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))
|
||||
const mcp = new McpServer({ name: 'goodbuddy-image', version: '1' }, { capabilities: { tools: {} } })
|
||||
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [{
|
||||
name: imageToolName, description: this.descriptor.description,
|
||||
inputSchema: schema as { type: 'object' }
|
||||
}] }))
|
||||
mcp.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
const available = this.descriptor.storyGraph ? await this.call({}, 'story_graph_list') : undefined
|
||||
const graphEnabled = available?.content.some(part => part.type === 'text' && JSON.parse(part.text).available === true)
|
||||
return { tools: [
|
||||
...(this.descriptor.description ? [{ name: imageToolName, description: this.descriptor.description, inputSchema: schema as { type: 'object' } }] : []),
|
||||
...(graphEnabled ? storyGraphTools.map(tool => ({ name: tool.name, description: tool.description,
|
||||
inputSchema: z.toJSONSchema(tool.inputSchema, { target: 'draft-7', io: 'input' }) as { type: 'object' },
|
||||
annotations: { readOnlyHint: true, destructiveHint: false } })) : [])
|
||||
] }
|
||||
})
|
||||
mcp.setRequestHandler(CallToolRequestSchema, async (call, extra) => {
|
||||
if (call.params.name !== imageToolName) throw new Error('Unknown image tool')
|
||||
const graphTool = storyGraphTools.find(tool => tool.name === call.params.name)
|
||||
if (graphTool && this.descriptor.storyGraph) {
|
||||
try { return await this.call(call.params.arguments ?? {}, graphTool.name) }
|
||||
catch (error) { return { isError: true, content: [{ type: 'text', text: error instanceof Error ? error.message : 'Story Graph read failed' }] } }
|
||||
}
|
||||
if (call.params.name !== imageToolName || !this.descriptor.description) throw new Error('Unknown image tool')
|
||||
const input = imageToolInputSchema.parse(call.params.arguments)
|
||||
// Repeated delivery of an MCP request returns the same outcome, including unknown delivery.
|
||||
const key = `${typeof extra.requestId}:${extra.requestId}`
|
||||
@@ -87,8 +99,8 @@ export class AgentImageToolMcp {
|
||||
const reply = remoteImageToolReplySchema.parse(decodeRemoteImageToolMessage(payload))
|
||||
const pending = this.pending.get(reply.callId)
|
||||
if (!pending) return
|
||||
if (reply.error || !reply.result) pending.reject(new Error(reply.error ?? 'Image tool response has no result'))
|
||||
else pending.resolve({ content: [{ type: 'text', text: JSON.stringify(reply.result) }] })
|
||||
if (reply.error || (!reply.result && !reply.storyGraphResult)) pending.reject(new Error(reply.error ?? 'Tool response has no result'))
|
||||
else pending.resolve({ content: [{ type: 'text', text: JSON.stringify(reply.storyGraphResult ?? reply.result) }] })
|
||||
}
|
||||
|
||||
close(): void {
|
||||
@@ -100,7 +112,7 @@ export class AgentImageToolMcp {
|
||||
this.server?.close()
|
||||
}
|
||||
|
||||
private async call(input: z.infer<typeof imageToolInputSchema>): Promise<CallToolResult> {
|
||||
private async call(input: Record<string, unknown>, name?: StoryGraphToolName | 'story_graph_list'): Promise<CallToolResult> {
|
||||
if (this.closed) throw new Error('Image tool prompt has ended')
|
||||
const callId = randomUUID()
|
||||
let timer: NodeJS.Timeout | undefined
|
||||
@@ -110,7 +122,7 @@ export class AgentImageToolMcp {
|
||||
timer.unref()
|
||||
})
|
||||
// Register the wait before sending; send failures must not cause a retry.
|
||||
void this.send(encodeRemoteImageToolMessage({ callId, input })).catch(() => {
|
||||
void this.send(encodeRemoteImageToolMessage({ callId, input, ...(name ? { name } : {}) })).catch(() => {
|
||||
this.pending.get(callId)?.reject(new Error('Image tool delivery may be unknown. Do not retry automatically.'))
|
||||
})
|
||||
try { return await result } finally {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { runtimePrivacyEnvironment } from '../shared/runtime-privacy-environment'
|
||||
import { randomBytes, randomUUID } from 'node:crypto'
|
||||
import { spawn as nodeSpawn } from 'node:child_process'
|
||||
import { mkdtemp, writeFile, rm } from 'node:fs/promises'
|
||||
@@ -931,7 +932,9 @@ function credentialFreeHelperEnvironment(
|
||||
'XDG_STATE_HOME'
|
||||
] as const
|
||||
const environment: NodeJS.ProcessEnv = {
|
||||
...runtimePrivacyEnvironment,
|
||||
OPENCODE_CONFIG_CONTENT: config,
|
||||
|
||||
OPENCODE_ENABLE_QUESTION_TOOL: 'true'
|
||||
}
|
||||
for (const name of allowed) {
|
||||
|
||||
@@ -60,6 +60,11 @@ describe('OpenCode direct launch profile', () => {
|
||||
workMode
|
||||
})
|
||||
const config = JSON.parse(profile.env.OPENCODE_CONFIG_CONTENT!)
|
||||
expect(profile.env).toMatchObject({
|
||||
DO_NOT_TRACK: '1', OTEL_SDK_DISABLED: 'true',
|
||||
OTEL_LOGS_EXPORTER: 'none', OTEL_METRICS_EXPORTER: 'none', OTEL_TRACES_EXPORTER: 'none',
|
||||
OPENCODE_DISABLE_AUTOUPDATE: '1', OPENCODE_DISABLE_MODELS_FETCH: '1'
|
||||
})
|
||||
expect(config.snapshot).toBe(false)
|
||||
expect(config.permission).toBe(workMode === 'ask' ? 'ask' : 'allow')
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { runtimePrivacyEnvironment } from "../shared/runtime-privacy-environment";
|
||||
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
import {
|
||||
OPENCODE_REMOTE_RUNTIME_ENVIRONMENT_NAMES,
|
||||
@@ -148,6 +149,7 @@ export function createOpenCodeLaunchProfile(input: {
|
||||
|
||||
const environmentBase = {
|
||||
...executeEnvironment(process.env, workspaceDirectory),
|
||||
...runtimePrivacyEnvironment,
|
||||
...(isContinue ? {} : { ...OPEN_CODE_RUNTIME_ENVIRONMENT,
|
||||
OPENCODE_CONFIG_DIR: join(bundleDirectory, "config", "opencode") }),
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user