From caaaf89b1e39055d22b419dd645119aadb8dd4da Mon Sep 17 00:00:00 2001 From: zhc Date: Thu, 17 Sep 2026 20:34:33 +0800 Subject: [PATCH] fix(client): separate developer tool policy and harden static previews --- ...6-09-17-developer-tools-settings.i18n.yaml | 4 +- .../2026-09-17-developer-tools-settings.md | 8 +-- .../2026-09-17-developer-tools-settings.zh.md | 8 +-- THIRD_PARTY_NOTICES.md | 1 + apps/web/tests/document-preview.e2e.ts | 9 +++- apps/web/tests/scaffold.ts | 2 +- docs/subsystems/conversation.i18n.yaml | 4 +- docs/subsystems/conversation.md | 2 +- docs/subsystems/conversation.zh.md | 2 +- .../src/client/AgentPresetSeat.tsx | 10 ++-- .../ui-agent-preset/src/client/index.ts | 4 +- .../tests/apply.client.spec.ts | 2 +- .../tests/components.client.spec.tsx | 2 +- .../ui-chat/src/client/chat/ChatView.tsx | 9 ++-- .../ui-chat/src/client/contract/slots.ts | 2 +- .../tests/apply-inject.client.spec.tsx | 5 +- .../ui-chat/tests/chat-apply.client.spec.tsx | 5 +- .../ui-chat/tests/chat-view.client.spec.tsx | 4 +- .../client/ui-conversation/README.i18n.yaml | 4 +- packages/client/ui-conversation/README.md | 2 +- packages/client/ui-conversation/README.zh.md | 2 +- .../ui-conversation/src/client/apply.ts | 39 +++++++++++---- .../src/client/contract/conversation.ts | 6 +++ .../src/client/contract/slots.ts | 9 ++-- .../skeleton/DefaultConversationViews.tsx | 5 +- .../tests/apply-inject.client.spec.tsx | 44 ++++++++++++++++- .../tests/apply-wiring.client.spec.tsx | 5 +- .../tests/assembly-surfaces.client.spec.tsx | 14 ++++-- .../tests/skeleton.client.spec.tsx | 1 + .../src/client/Deliverables.tsx | 8 +-- .../ui-deliverables/src/client/index.ts | 4 +- .../tests/deliverables.client.spec.tsx | 9 ++-- .../ui-deliverables/tsconfig.client.json | 3 ++ packages/client/ui-deliverables/tsconfig.json | 3 -- .../tests/browser-plugin.client.spec.ts | 5 +- .../ui-layout/tests/apply.client.spec.ts | 5 +- .../tests/browser-plugin.client.spec.ts | 5 +- .../ui-settings-general/src/client/index.ts | 6 +-- .../tests/apply.client.spec.ts | 6 +-- .../tests/shell.client.spec.ts | 2 +- .../tests/stores.client.spec.ts | 2 + packages/client/ui-settings/README.i18n.yaml | 4 +- packages/client/ui-settings/README.md | 4 +- packages/client/ui-settings/README.zh.md | 4 +- .../ui-settings/src/client/developer-tools.ts | 25 +++++++--- .../client/ui-settings/src/client/index.ts | 4 ++ .../src/client/settings-contract.ts | 15 +++--- .../ui-settings/src/client/settings-scope.ts | 32 ++++++------ .../tests/developer-tools.client.spec.ts | 49 +++++++++++++++++-- .../tests/settings-scope.client.spec.ts | 12 ++--- .../README.i18n.yaml | 4 +- .../ui-sidebar-documentpreview/README.md | 2 +- .../ui-sidebar-documentpreview/README.zh.md | 2 +- .../ui-sidebar-documentpreview/package.json | 7 +-- .../src/client/html/HtmlBody.tsx | 10 ++-- .../src/client/html/basic-document.ts | 21 +++----- .../src/client/html/index.ts | 2 +- .../src/client/index.ts | 2 +- .../tests/apply.client.spec.ts | 4 +- .../tests/basic-html.client.spec.ts | 13 +++++ .../tests/document-seat.client.spec.tsx | 4 +- .../tests/html-apply.client.spec.ts | 5 +- .../tests/html-body.client.spec.tsx | 4 +- .../tests/browser-plugin.client.spec.ts | 5 +- .../tests/assembly-surfaces.client.spec.tsx | 5 +- .../tests/chat-ptc-subcalls.client.spec.tsx | 7 +-- .../tests/toolview-slot.client.spec.tsx | 8 +-- .../src/client/trajectory-snapshot-builder.ts | 1 + .../tests/client-bundle.client.spec.ts | 5 +- .../conversation-definitions.client.spec.ts | 4 ++ .../ui-trajectory/tests/views.client.spec.tsx | 12 +++-- .../tests/workflow-run.client.spec.tsx | 5 +- .../src/client/slot-catalog.ts | 5 +- .../client-runtime/README.i18n.yaml | 4 +- .../test-support/client-runtime/README.md | 2 +- .../test-support/client-runtime/README.zh.md | 2 +- .../test-support/client-runtime/src/index.ts | 2 +- .../client-runtime/src/settings-scope.ts | 25 ++++++++-- .../tests/runtime.client.spec.tsx | 17 ++++++- pnpm-lock.yaml | 3 ++ scripts/gen-cordis-catalog.ts | 1 + 81 files changed, 404 insertions(+), 210 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.i18n.yaml b/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.i18n.yaml index 43b546d998..fe05798168 100644 --- a/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-09-17-developer-tools-settings.md -2026-09-17-developer-tools-settings.md: 694e2a09fc20ad3185ba9c09058e8684a2247e46 -2026-09-17-developer-tools-settings.zh.md: e03aa258675be8f49dc13a9b43f2e3e00c8b85fa +2026-09-17-developer-tools-settings.md: 62ec235d8d21ad621956363898da26935e1af487 +2026-09-17-developer-tools-settings.zh.md: b320c973be0e8b4d4a061db00d871d7e95f5460e diff --git a/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.md b/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.md index 694e2a09fc..62ec235d8d 100644 --- a/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.md +++ b/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.md @@ -10,11 +10,11 @@ Diagnostic views, preset selection and changed-file summaries add complexity to ## Decision -The settings owner exposes one accepted `ui-developer-tools.enabled` preference, defaulting off. General Settings offers the same switch on Web and desktop. The existing settings transport owns validation, ordered writes, persistence and recovery; loopback Web and desktop follow Host acceptance. Remote Web owns one browser-local observable shared by all consumers until reload, with no Host writes. +The dedicated `ctx.developerTools` service binds the generic settings scope and exposes one accepted `ui-developer-tools.enabled` preference, defaulting off. General Settings offers the same switch on Web and desktop. The existing settings transport owns validation, ordered writes, persistence and recovery; loopback Web and desktop follow Host acceptance. Remote Web owns one browser-local observable shared by all consumers until reload, with no Host writes. -Off exposes only Chat, hides tool Inspect actions whose trajectory target is unavailable, removes the conversation View tab bar, hides the new-session preset chip, and omits the changed-files card and its summary read. Turning it off from another View activates Chat. The preset composition, saved default, Session events and explicit delivery cards remain unchanged. This preference is presentation policy, not Host authorization. +Off exposes only Chat, hides tool Inspect actions when no visible View declares tool-call inspection, removes the conversation View tab bar, hides the new-session preset chip, and omits the changed-files card and its summary read. Turning it off from another View activates Chat. The preset composition, saved default, Session events and explicit delivery cards remain unchanged. This preference is presentation policy, not Host authorization. -The builtin HTML renderer enforces its own preview policy. Off parses a complete inert document, preserves root styling, recursively sanitizes template contents, removes active documents, resource hints, SVG animation and navigation, and mounts an iframe with no sandbox permissions and a CSP denying scripts and external resources. It reads no related files. On retains the opaque scripted Blob renderer and its bounded related-file reads. Switching modes replaces the browsing context and disposes pending reads. The [document-preview decision](../architecture/2026-09-08-document-preview-operations.md) and [filesystem-read authority](../architecture/2026-09-09-workspace-file-read-authority.md) remain authoritative for advanced rendering and Host read access. +Plugin assembly translates the shared preference into component-specific capabilities (`showCodeDiff`, `showPresetPicker`, and `interactivePreview`). Conversation supplies the inspection callback using the visible target's `toolCallFocus`; Chat does not name the target. The builtin HTML renderer uses DOMPurify for complete-document sanitization and parser-mutation defenses. Off preserves root styling, removes active documents, resource hints, SVG animation and all `href` attributes, and mounts an iframe with no sandbox permissions and a CSP denying scripts and external resources. It reads no related files. On retains the opaque scripted Blob renderer and its bounded related-file reads. Switching modes replaces the browsing context and disposes pending reads. The [document-preview decision](../architecture/2026-09-08-document-preview-operations.md) and [filesystem-read authority](../architecture/2026-09-09-workspace-file-read-authority.md) remain authoritative for advanced rendering and Host read access. ## Alternatives considered @@ -26,4 +26,4 @@ The builtin HTML renderer enforces its own preview policy. Off parses a complete Basic HTML sacrifices JavaScript, external and related resources, and link navigation; inline styles and data images remain available. Advanced mode retains normal browser networking and does not gain parent-origin, popup, form or top-navigation privileges. The setting does not constrain third-party preview implementations or revoke Host filesystem authority. -Component and settings tests cover defaulting, schema rejection, accepted-state updates, chip disclosure, changed-file visibility and iframe disposal. The recorded-session [document preview](../../../../apps/web/tests/document-preview.e2e.ts) and [changed-files](../../../../apps/web/tests/changed-files-turn.e2e.ts) scenarios exercise the real settings control and the shared renderer. The browser scaffold explicitly seeds developer mode for diagnostic scenarios; settings chrome, document preview and changed-file scenarios leave the shipped default untouched. Native Electron execution remains unverified. +Component and settings tests cover defaulting, schema rejection, accepted-state updates, chip disclosure, changed-file visibility and iframe disposal. The recorded-session [document preview](../../../../apps/web/tests/document-preview.e2e.ts) and [changed-files](../../../../apps/web/tests/changed-files-turn.e2e.ts) scenarios exercise the real settings control and the shared renderer. The browser scaffold enables developer tools by default. Only settings chrome, document preview, changed-files and developer-tools scenarios explicitly retain the shipped default; together they cover hidden diagnostic controls, static previews, and switching back to Chat. Refused Host writes recover accepted state and show the localized retry message. Native Electron execution remains unverified. diff --git a/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.zh.md b/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.zh.md index e03aa25867..b320c973be 100644 --- a/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.zh.md +++ b/.agents/notes/implemented/feature/2026-09-17-developer-tools-settings.zh.md @@ -10,11 +10,11 @@ Status: implemented ## Decision -设置所有者提供一个已接受的 `ui-developer-tools.enabled` 偏好,默认关闭。Web 和桌面端的通用设置提供同一个开关。现有设置传输负责验证、有序写入、持久化和恢复;回环 Web 和桌面端跟随 Host 接受的状态。远程 Web 持有一个所有消费者共享的浏览器本地可观察值,刷新后重置,不发送 Host 写入。 +独立的 `ctx.developerTools` 服务绑定通用设置 scope,提供一个已接受的 `ui-developer-tools.enabled` 偏好,默认关闭。Web 和桌面端的通用设置提供同一个开关。现有设置传输负责验证、有序写入、持久化和恢复;回环 Web 和桌面端跟随 Host 接受的状态。远程 Web 持有一个所有消费者共享的浏览器本地可观察值,刷新后重置,不发送 Host 写入。 -关闭时仅展示 Chat,隐藏轨迹目标不可用的工具 Inspect 操作,移除会话 View 标签栏,隐藏新会话预设入口,并省略改动文件卡片及其摘要读取。在其他 View 中关闭开关会激活 Chat。预设组合、保存的默认值、Session 事件和显式交付卡片保持不变。此偏好是展示策略,不是 Host 授权。 +关闭时仅展示 Chat,在没有可见 View 声明工具调用检查能力时隐藏 Inspect 操作,移除会话 View 标签栏,隐藏新会话预设入口,并省略改动文件卡片及其摘要读取。在其他 View 中关闭开关会激活 Chat。预设组合、保存的默认值、Session 事件和显式交付卡片保持不变。此偏好是展示策略,不是 Host 授权。 -内置 HTML 渲染器执行自己的预览策略。关闭时,内容被解析为完整的惰性文档,保留根节点样式,递归清理 template 内容,移除主动文档、资源提示、SVG 动画与导航,再放入不授予沙箱权限的 iframe,并通过 CSP 禁止脚本和外部资源。不读取关联文件。开启时保留不透明源的脚本 Blob 渲染器及其有界关联文件读取。切换模式会替换浏览上下文并清理待处理读取。[文档预览决策](../architecture/2026-09-08-document-preview-operations.zh.md)和[文件系统读取权限](../architecture/2026-09-09-workspace-file-read-authority.zh.md)继续定义高级渲染与 Host 读取访问。 +插件组装层将共享偏好转换为组件自身的能力(`showCodeDiff`、`showPresetPicker` 和 `interactivePreview`)。Conversation 根据可见目标的 `toolCallFocus` 提供检查回调;Chat 不再指定目标名称。内置 HTML 渲染器使用 DOMPurify 清理完整文档并防护解析变异。关闭时保留根节点样式,移除主动文档、资源提示、SVG 动画及所有 `href` 属性,再放入不授予沙箱权限的 iframe,并通过 CSP 禁止脚本和外部资源。不读取关联文件。开启时保留不透明源的脚本 Blob 渲染器及其有界关联文件读取。切换模式会替换浏览上下文并清理待处理读取。[文档预览决策](../architecture/2026-09-08-document-preview-operations.zh.md)和[文件系统读取权限](../architecture/2026-09-09-workspace-file-read-authority.zh.md)继续定义高级渲染与 Host 读取访问。 ## Alternatives considered @@ -26,4 +26,4 @@ Status: implemented 基础 HTML 放弃 JavaScript、外部与关联资源和链接导航;行内样式与 data 图片仍可用。高级模式保留正常浏览器网络能力,不增加父源、弹窗、表单或顶层导航权限。此设置不约束第三方预览实现,也不撤销 Host 文件系统读取权限。 -组件和设置测试覆盖默认值、schema 拒绝、已接受状态更新、入口折叠、改动文件展示和 iframe 清理。基于已记录 Session 的[文档预览](../../../../apps/web/tests/document-preview.e2e.ts)与[改动文件](../../../../apps/web/tests/changed-files-turn.e2e.ts)场景验证真实设置控件及共用渲染器。浏览器脚手架为诊断场景显式开启开发者模式;设置外壳、文档预览和改动文件场景保留产品默认值。原生 Electron 执行尚未验证。 +组件和设置测试覆盖默认值、schema 拒绝、已接受状态更新、入口折叠、改动文件展示和 iframe 清理。基于已记录 Session 的[文档预览](../../../../apps/web/tests/document-preview.e2e.ts)与[改动文件](../../../../apps/web/tests/changed-files-turn.e2e.ts)场景验证真实设置控件及共用渲染器。浏览器脚手架默认开启开发者工具,仅设置外壳、文档预览、改动文件和开发者工具场景显式保留产品默认值;这些场景共同覆盖诊断控件隐藏、静态预览和切回 Chat。Host 拒绝写入后会恢复已接受状态,并显示本地化重试提示。原生 Electron 执行尚未验证。 diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 7eb0c36e18..ec698dca33 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -76,6 +76,7 @@ External packages installed for runtime use or distributed inside the prebuilt b | [`commander`](https://github.com/tj/commander.js) | MIT | | [`compression`](https://github.com/expressjs/compression) | MIT | | [`diff`](https://github.com/kpdecker/jsdiff) | BSD-3-Clause | +| [`dompurify`](https://github.com/cure53/DOMPurify) | (MPL-2.0 OR Apache-2.0) | | [`electron-updater`](https://github.com/electron-userland/electron-builder) | MIT | | [`eventsource-parser`](https://github.com/rexxars/eventsource-parser) | MIT | | [`execa`](https://github.com/sindresorhus/execa) | MIT | diff --git a/apps/web/tests/document-preview.e2e.ts b/apps/web/tests/document-preview.e2e.ts index 57d513c72a..bee048c828 100644 --- a/apps/web/tests/document-preview.e2e.ts +++ b/apps/web/tests/document-preview.e2e.ts @@ -155,6 +155,8 @@ describe.skipIf(MODE === 'record')('web e2e: document preview through Files', ()
SVG link + x +