Merge origin/master into agent/onboarding-modal-flow

This commit is contained in:
Tianyi Cui
2026-08-13 16:57:01 +08:00
237 changed files with 266 additions and 247 deletions

View File

@@ -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 packages/client/connection/README.md
README.md: 3315552dd0400697d5a96639598c5f5bb2bb4f5b
README.zh.md: 0dc251f1e5410bd0d74ef450c6d8df3dc9cdab81
README.md: f0a707cc5f3a962c7852f7323c727d0a39a57b10
README.zh.md: b529eebf9af93e36b6b92c19964678b7f5a04ea1

View File

@@ -6,7 +6,7 @@ Wire consumer layer: the client plugin's apply mounts `ctx.connection` (shared a
## /api browser-trust fence
The node half guards every entry under `/api` before bridging or upgrading (`src/api-request-trust.ts`). Every request — browser-marked or not — must present a `Host` that is a loopback authority or matches a `trustedHosts` entry: exact on `host:port` entries, any port on port-less entries, both sides compared through WHATWG normalization (DNS-rebinding defense). There is deliberately no shortcut for unmarked HTTP requests: over plain HTTP a browser attaches neither `Origin` nor Fetch-Metadata to image and navigation reads, so an unmarked request may still be a rebound browser read with a readable response, and Host is the one header rebinding cannot forge; a browser WebSocket handshake carries `Origin` and passes the same comparison. Non-browser clients pass the same fence via loopback, the CLI-derived LAN IP literals, or a declared authority. When markers are present, an attached `Origin` must equal the Host authority, and an explicit `sec-fetch-site: cross-site` marker is refused. A `trustedHosts` entry that is not a bare, canonical `host[:port]` authority — one WHATWG parsing reads back exactly as written — fails the plugin load loudly: parsing would otherwise quietly authorize the hostname inside `harness.internal/path`, or broaden a dangling-colon or zero-padded port to an any-port grant. HTTP failures answer plain 403 before any RPC dispatch; upgrade failures reject the handshake before any event stream starts. A non-loopback (`--host 0.0.0.0`) deployment therefore needs its serving authorities trusted: the dsh CLI derives the machine's LAN IP literals itself and its `--trusted-host` flag declares named ones, so `trustedHosts` in cordis.yml is for compositions the CLI does not boot. The fence is a reachability policy, not authentication; the Web carrier provides no authentication layer. Decision record: [the api browser-trust boundary Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md).
The node half guards every entry under `/api` before bridging or upgrading (`src/api-request-trust.ts`). Every request — browser-marked or not — must present a `Host` that is a loopback authority or matches a `trustedHosts` entry: exact on `host:port` entries, any port on port-less entries, both sides compared through WHATWG normalization (DNS-rebinding defense). There is deliberately no shortcut for unmarked HTTP requests: over plain HTTP a browser attaches neither `Origin` nor Fetch-Metadata to image and navigation reads, so an unmarked request may still be a rebound browser read with a readable response, and Host is the one header rebinding cannot forge; a browser WebSocket handshake carries `Origin` and passes the same comparison. Non-browser clients pass the same fence via loopback, deployment-derived LAN IP literals, or a declared authority. When markers are present, an attached `Origin` must equal the Host authority, and an explicit `sec-fetch-site: cross-site` marker is refused. A `trustedHosts` entry that is not a bare, canonical `host[:port]` authority — one WHATWG parsing reads back exactly as written — fails the plugin load loudly: parsing would otherwise quietly authorize the hostname inside `harness.internal/path`, or broaden a dangling-colon or zero-padded port to an any-port grant. HTTP failures answer plain 403 before any RPC dispatch; upgrade failures reject the handshake before any event stream starts. Non-loopback compositions must trust their serving authorities explicitly: the Web runtime derives LAN IP literals from an all-interfaces server config, while `trustedHosts` in cordis.yml and the CLI's `--trusted-host` flag declare named authorities. `dsh web --host 0.0.0.0` is intentionally unsupported until remote access has an authentication layer. The fence is a reachability policy, not authentication; the Web carrier provides no authentication layer. Decision record: [the api browser-trust boundary Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md).
## `/api` WebSocket downlinks

View File

@@ -6,7 +6,7 @@
## /api 浏览器信任栅栏
node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-request-trust.ts`)。每个请求——无论是否带浏览器标记——`Host` 都必须是回环地址权威,或与某个 `trustedHosts` 条目匹配:带端口的 `host:port` 条目精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化后比较DNS rebinding 防御)。刻意不为无浏览器标记的 HTTP 请求开捷径:明文 HTTP 下浏览器的图片与导航读取既不带 `Origin` 也不带 Fetch-Metadata因此无标记请求仍可能是被重绑页面发起的、响应可被读走的读取而 Host 是重绑唯一伪造不了的请求头WebSocket 浏览器握手会带 `Origin` 并通过同一道比较。非浏览器客户端经由回环地址、CLI命令行界面推导的 LAN IP 字面量或已声明的权威通过同一道栅栏。当标记存在时,如附带 `Origin`,则它必须与 Host 权威完全一致;显式的 `sec-fetch-site: cross-site` 标记一律拒绝。不是纯的、规范形 `host[:port]` 权威的 `trustedHosts` 条目——即 WHATWG 解析读回后与原文不完全一致的——会让插件加载明确报错:否则解析会悄悄授权 `harness.internal/path` 这类笔误里的 hostname或把悬空冒号、补零端口放大成任意端口授权。HTTP 失败在任何 RPC 分发之前以纯 403 应答upgrade 失败在启动任何事件流前拒绝握手。因此非回环`--host 0.0.0.0`部署需要让自己的服务权威被信任dsh CLI 会自行推导本机的 LAN IP 字面量,其 `--trusted-host` flag 用于声明具名权威,所以 cordis.yml 中的 `trustedHosts` 面向 CLI 不参与引导的组合。这道栅栏是可达性策略而不是认证Web 载体不提供认证层。决策记录:[api 浏览器信任边界 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md)。
node 半侧在桥接或 upgrade 前守卫 `/api` 下的每个入口(`src/api-request-trust.ts`)。每个请求——无论是否带浏览器标记——`Host` 都必须是回环地址权威,或与某个 `trustedHosts` 条目匹配:带端口的 `host:port` 条目精确匹配,不带端口的条目匹配任意端口,两侧均经 WHATWG 归一化后比较DNS rebinding 防御)。刻意不为无浏览器标记的 HTTP 请求开捷径:明文 HTTP 下浏览器的图片与导航读取既不带 `Origin` 也不带 Fetch-Metadata因此无标记请求仍可能是被重绑页面发起的、响应可被读走的读取而 Host 是重绑唯一伪造不了的请求头WebSocket 浏览器握手会带 `Origin` 并通过同一道比较。非浏览器客户端经由回环地址、部署推导的 LAN IP 字面量或已声明的权威通过同一道栅栏。当标记存在时,如附带 `Origin`,则它必须与 Host 权威完全一致;显式的 `sec-fetch-site: cross-site` 标记一律拒绝。不是纯的、规范形 `host[:port]` 权威的 `trustedHosts` 条目——即 WHATWG 解析读回后与原文不完全一致的——会让插件加载明确报错:否则解析会悄悄授权 `harness.internal/path` 这类笔误里的 hostname或把悬空冒号、补零端口放大成任意端口授权。HTTP 失败在任何 RPC 分发之前以纯 403 应答upgrade 失败在启动任何事件流前拒绝握手。非回环组合必须显式信任其服务权威Web 运行时从全接口服务器配置推导 LAN IP 字面量cordis.yml 中的 `trustedHosts` 与 CLI命令行界面 `--trusted-host` flag 声明具名权威`dsh web --host 0.0.0.0` 在远程访问具备认证层之前有意不受支持。这道栅栏是可达性策略而不是认证Web 载体不提供认证层。决策记录:[api 浏览器信任边界 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-28-api-browser-trust-boundary.md)。
## `/api` WebSocket 下行

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-connection",
"description": "Wire consumer layer: HTTP-up/WebSocket-down client, ConnectionController dual streams with reconnect, and fixture api",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -7,8 +7,8 @@
* nor Fetch-Metadata to reads (images and navigations — those
* headers go only to trustworthy destinations), so an unmarked request may
* still be a rebound browser read and Host is the one header rebinding cannot
* forge. Non-browser and remote clients pass the same fence via loopback, the
* CLI-derived LAN IP literals, or a declared `trustedHosts` authority.
* forge. Non-browser and remote clients pass the same fence via loopback,
* deployment-derived LAN IP literals, or a declared `trustedHosts` authority.
* Network reachability and authentication stay out of scope: binding policy
* belongs to the webserver config, and this fence is not an auth layer.
*/

View File

@@ -199,8 +199,8 @@ describe('connection node half', () => {
const loopback = fakeResponse()
await routes[0]!.handler(fakeRequest({ host: '127.0.0.1:3080' }), loopback.response)
expect(loopback.state.status).toBe(404)
// LAN authority declared as a port-less IP literal — the shape the CLI
// derives for `--host 0.0.0.0` — passes markerless curl on any port.
// An all-interfaces composition derives port-less LAN IP literals, which
// pass markerless curl on any port.
const lan = fakeResponse()
await routes[0]!.handler(fakeRequest({ host: '192.168.1.5:3080' }), lan.response)
expect(lan.state.status).toBe(404)

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-hmr",
"description": "Dev-only hot-reload driver for script-loaded client entries: SSE rebuilt frames → invalidate/prefetch → fiber swap through the vendored Loader entry",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-locale",
"description": "Locale plugin: Host-backed zh/en preference, browser-derived fallback, locale snapshots, and typed namespace dictionaries",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-modules",
"description": "Client module system, dual-face: node half composes the __DSH_BOOT__ entry graph (incremental dsh.client scan, bundle route, index tap, webPlugins service); browser half is the lazy-CJS module table the vendored cordis Loader consumes as its internal seam",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-runtime",
"description": "Client core services: SlotRegistry, SessionRuntime (scope tree + object layer)",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-schema-form",
"description": "Schema/draft model layer for settings editors: rehydrates a serialized schemastery schema, validates drafts, and edits them immutably by path",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-agent-preset",
"description": "Agent-preset surfaces: the default for later sessions, this session's seat, and the composition editor",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-attachment",
"description": "Pure React attachment atoms for the dsh web UI: draft-image rail, message image gallery, and original-image lightbox (zero cordis)",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-commands",
"description": "Client command surface: global directory cache, '/' source, three command UI kinds, popupSelect registry",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-conversation",
"description": "Conversation domain: skeleton, ordered chat flow, composer with the Host-backed busy-Enter preference, and details host",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-deliverables",
"description": "Produced-files turn tail and clickable final-response file references for Web",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-directory-picker-browse",
"description": "In-app directory browsing surface: the workspace directory-flow owner rendering the host's listing and creation primitives",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-directory-picker-native",
"description": "Native directory-picker surface: the renderless workspace directory-flow occupant driving the host's OS chooser",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-goal",
"description": "Session goal surface: GoalBar docked above the composer, read from the goal session projection",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-input-trigger",
"description": "Input trigger pipeline: '/' and '@' detection, candidate menu, pick routing to registered sources",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-jobs",
"description": "Session-header background-job list: live registry state mirrored from session/jobs frames",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"type": "module",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-layout",
"description": "Shell plugin: three-column AppFrame with drag handles, ctx.layout viewing-state service (navigation + panels)",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-message-feedback",
"description": "Per-message feedback controls contributed to the assistant-message action strip, backed by the messageFeedback Host Remote",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-model-selection",
"description": "Model selection: the /model popupSelect over session.models / session.selectModel",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-permission-presets",
"description": "Permission surfaces: a new-session default in General settings and a current-session /permission popup over the permissions projection",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-plan",
"description": "Plan-mode composer control: the conversation.input.plan seat over the plan projection and the /plan command channel",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-primitives",
"description": "Pure React atoms for the dsh web UI: controls, icons, markdown, and JSON inspectors (zero cordis)",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-settings-general",
"description": "Settings ownerless-copy and product onboarding plugin: the General section, shell trigger/header chrome content, settings dictionaries, and the versioned welcome notice",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-settings-models",
"description": "Models settings and shared product-onboarding dialogs over existing settings and credential joins",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-settings-plugin-inventory",
"description": "Read-only Cordis Loader inventory tab in Web Plugins settings",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-settings-plugins",
"description": "Plugins settings section with feature-owned tabs and configurable host-plane plugin cards",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-settings",
"description": "Settings domain base plugin: the settings-namespace scope service and the canonical settings slot-type contract",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-sidebar",
"description": "Sidebar plugin: session multi-level tree, search, grouping, state dots",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-skill",
"description": "Web skill references and the dedicated skill tool row",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-slots",
"description": "Slot registry pure core: SlotMap declaration merging, single register composition API, four-share props types, store-seat types, renderer install seam",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-subagent",
"description": "Subagent conversation catalog, continuation routing UI, and '@' reference source",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-theme",
"description": "Theme plugin: Host bootstrap for the pre-plugin palette; DOM-free ThemeRuntime for light/dark/system state; --dsw-* token styles and Appearance settings row",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-tool",
"description": "Client Tool call-tree renderer and keyed per-tool presentation slot",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-trajectory",
"description": "Trajectory event ledger with an interactive timing overview: pure-consumer plugin registering into the conversation ViewMap (no service)",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-user-questions",
"description": "Web ask_user_question feature: host tool mount plus composer-takeover question UI",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-workflow-run",
"description": "Durable workflow-run Conversation Node and nested member disclosure for dsh web",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-ui-workspace",
"description": "Workspace picker plugin: one WorkspacePicker registered into the sidebar and empty-state workspace slots",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-web-react",
"description": "Shell-side React glue: createSlotRenderer, SessionProvider, bindSnapshotSelector (uSES bridge), useInvoke",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@deepseek-ai/dsh-client-web",
"description": "Web shell kernel: bootWebShell (module system holding + seed table + two-stage boot + AppRoot gate + app-shell assembly entry), consumed by the apps/web vite entry",
"version": "0.0.1-rc.5",
"version": "0.1.0-rc.1",
"publishConfig": {
"access": "restricted"
},