dsh-remote-vs 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -37,8 +37,9 @@ dsh plugin --profile web add dsh-remote-vs
37
37
  (From npm, recommended; installing straight from the repository also works:
38
38
  `dsh plugin --profile web add github:xiray-xia/dsh-remote-vs`.)
39
39
 
40
- Requires DSH `0.2.1-alpha.3` or newer (`engines.dsh` enforces the range; **the tested
41
- baseline is `0.2.1-rc.2`**). `--profile web` selects which profile to install into —
40
+ Requires DSH `0.2.2-alpha.2` or newer (`engines.dsh` enforces the range; **the tested
41
+ baseline is `0.2.2-alpha.2`**). Older Hosts (`0.2.1-rc.2` generation) need plugin `0.9.3`: the
42
+ Remote preset of this version uses the `0.2.2-alpha.2` tool schema. `--profile web` selects which profile to install into —
42
43
  this is a Web plugin, so it has to go into the profile your Web GUI runs from (`web` is
43
44
  the common name; use your own profile's name).
44
45
 
@@ -301,7 +302,7 @@ over helper RPC (fs) / ssh2 (processes), local stays untouched; both share one H
301
302
 
302
303
  - Alias dirs: `$DSH_HOME/remote-ssh/aliases/<id>` (empty shells, routing keys only), auto-registered into the native workspace list as `<server> > <base>`
303
304
  - Routing switch (dropdown at the top of Settings → Remote hosts; underneath it is profile patch `- id: remote-vs`, `config.routingMode`, volatile, no restart):
304
- `off` (default, local passthrough) / `shadow` (logs, still local) / `enforce` (real routing)
305
+ default `enforce` (real routing); switch to `off` (pure local passthrough) / `shadow` (logs, still local) as debugging escapes
305
306
  - New workspace: the native "Add workspace…" entry is adopted (local/remote dual tab; remote leg = host→push→bind wizard)
306
307
  - Presets: a `Remote` preset (bash toolset); under enforce, new remote sessions switch to it automatically (default preset only, never overrides an explicit choice)
307
308
  - Ground truths (measured, not assumed):
@@ -319,7 +320,7 @@ over helper RPC (fs) / ssh2 (processes), local stays untouched; both share one H
319
320
  Upstream never published helper binaries (all Release assets are empty), so this plugin builds its own:
320
321
 
321
322
  - Build machine: WSL2 Ubuntu-22.04 (glibc 2.35, matching the remote targets), Node v24.21.0 + pnpm 11.28.5
322
- - Sources: the `dsh-v0.2.1-alpha.2` tag (the **build baseline**: what must match is the wire protocol version, not the Host version — it is still 2, so a Host upgrade to alpha.4 or alpha.5 does not require a rebuild)
323
+ - Sources: the `dsh-v0.2.1-alpha.2` tag (the **build baseline**: what must match is the wire protocol version, not the Host version — our protocol is still **2**, so a Host upgrade to `0.2.2-alpha.2` needs no rebuild either: the plugin intentionally stays on `dsh-ssh@0.2.1-rc.2`, and the new generation's protocol 3 is deliberately not followed — see §2.26 of the maintenance notes)
323
324
  - Command: stock script `pnpm exec tsx scripts/build-exe-for-ssh-helper.ts --target=node24-linux-x64`
324
325
  - Only deviation: no docker for the manylinux 2.28 rebuild, so a `DSH_GLIBC_CEILING`
325
326
  env override was added (default stays 2.28); this build targets 2.35 (see `glibc-ceiling.patch` in the cache dir)
package/README.md CHANGED
@@ -34,7 +34,8 @@ dsh plugin --profile web add dsh-remote-vs
34
34
 
35
35
  (从 npm 装,推荐;也可以直接跟仓库装:`dsh plugin --profile web add github:xiray-xia/dsh-remote-vs`。)
36
36
 
37
- 要求 DSH `0.2.1-alpha.3` 及以上(`engines.dsh` 门到底;**当前实测基线 `0.2.1-rc.2`**)。
37
+ 要求 DSH `0.2.2-alpha.2` 及以上(`engines.dsh` 门到底;**当前实测基线 `0.2.2-alpha.2`**)。
38
+ 旧 Host(`0.2.1-rc.2` 一代)请用插件 `0.9.3`:新版的 Remote 预设用了 `0.2.2-alpha.2` 的工具 schema。
38
39
  `--profile web` 指明装进哪个 profile —— 本插件是 Web 插件,
39
40
  必须装进 Web GUI 所用的那个(`web` 是常见命名,以你自己的 profile 名为准)。
40
41
 
@@ -266,7 +267,7 @@ helper RPC(fs)/ ssh2(进程),本地一切不变;两者同 Host、共
266
267
 
267
268
  - 别名目录:`$DSH_HOME/remote-ssh/aliases/<id>`(空壳,只做路由键),自动注册进原生工作区列表,标题 `<server> > <base>`
268
269
  - 路由开关(设置 → 远程主机页顶部下拉;底层是 profile patch `- id: remote-vs` 下 `config.routingMode`,volatile,免重启):
269
- `off`(默认,直通本地)/ `shadow`(记日志仍走本地)/ `enforce`(真路由)
270
+ 默认 `enforce`(真路由);可切 `off`(纯直通本地)/ `shadow`(记日志仍走本地)作为排障开关
270
271
  - 新建工作区:原生"添加工作区…"入口已接管(本地/远端双 tab;远端腿=主机→推送→绑定向导)
271
272
  - 预设:`Remote` 预设(bash 工具集);enforce 下远端新会话自动切过去(默认预设才切,不践踏显式选择)
272
273
  - 顶层真相(实测结论,非假设):
@@ -284,7 +285,9 @@ helper RPC(fs)/ ssh2(进程),本地一切不变;两者同 Host、共
284
285
  官方从未发布 helper 二进制(Release assets 全空),本插件走自编译:
285
286
 
286
287
  - 构建机:WSL2 Ubuntu-22.04(glibc 2.35,与目标远端一致),Node v24.21.0 + pnpm 11.28.5
287
- - 源码:`dsh-v0.2.1-alpha.2` tag(**构建基线**:对齐的是 wire 协议号,不是 Host 版本号 —— 协议号至今仍是 2,故 Host 升到 alpha.4 / alpha.5 都无需重编)
288
+ - 源码:`dsh-v0.2.1-alpha.2` tag(**构建基线**:对齐的是 wire 协议号,不是 Host 版本号 —— 我们用到的
289
+ 协议号仍是 **2**,Host 升到 `0.2.2-alpha.2` 也不需要重编:插件自行停在 `dsh-ssh@0.2.1-rc.2`,
290
+ 新 Host 那一代 `dsh-ssh` 的协议 3 我们故意不跟,差异见维护文档 §2.26)
288
291
  - 命令:官方原脚本 `pnpm exec tsx scripts/build-exe-for-ssh-helper.ts --target=node24-linux-x64`
289
292
  - 唯一偏离:无 docker 跑不了 manylinux 2.28 重编,加了 `DSH_GLIBC_CEILING`
290
293
  环境覆盖(默认仍 2.28),本次以 2.35 构建(见缓存目录 `glibc-ceiling.patch`)
package/cordis.patch.yml CHANGED
@@ -54,7 +54,7 @@
54
54
  inject: [localFileReferences, remoteWorlds]
55
55
 
56
56
  # Remote agent preset, bound to this bundle's lifecycle.
57
- # Mirrored from preset-standard @ dsh 0.2.1-alpha.3 with these diffs:
57
+ # Mirrored from preset-standard @ dsh 0.2.2-alpha.2 with these diffs:
58
58
  # 1. persona prefix reframed for remote SSH work (transparent routing);
59
59
  # 2. tool-bash moved into an isolated shell group with our routed bash
60
60
  # executor (stock root shell stays pwsh on Windows);
@@ -67,6 +67,12 @@
67
67
  # preset) right after `tool-plugin-manager` — mirrored here for the same
68
68
  # reason as above: a row missing from the mirror is a preset that silently
69
69
  # loads a different toolset than stock.
70
+ # 0.2.2-alpha.2 note: stock dropped `tool-subagent-list-agents` (the
71
+ # `dsh-tool-subagent-control/list-agents` subpath is gone from the package),
72
+ # renamed `modelSelectionSettings` to `modelSelection` + `modelDiscovery`,
73
+ # and made the plugin-manager row conditional on `profileContext`. All
74
+ # three are mirrored here: the first one is a hard module-resolution error,
75
+ # the other two are schema drift.
70
76
  - id: preset-remote
71
77
  name: '@deepseek-ai/dsh-agent-preset'
72
78
  config:
@@ -207,14 +213,13 @@
207
213
  config:
208
214
  - id: tool-subagent-control
209
215
  name: '@deepseek-ai/dsh-tool-subagent-control'
210
- - id: tool-subagent-list-agents
211
- name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'
212
216
  - id: tool-subagent
213
217
  name: '@deepseek-ai/dsh-tool-subagent'
214
218
  config:
215
219
  provider: spawn
216
220
  toolName: subagent
217
- modelSelectionSettings: true
221
+ modelSelection: true
222
+ modelDiscovery: true
218
223
  - id: tool-subagent-fork
219
224
  name: '@deepseek-ai/dsh-tool-subagent'
220
225
  config:
@@ -241,7 +246,7 @@
241
246
  name: '@deepseek-ai/dsh-tool-present'
242
247
  - id: tool-plugin-manager
243
248
  name: '@deepseek-ai/dsh-plugin-manager/tools'
244
- disabled: true
249
+ disabled: !!js "!ctx.get('profileContext')"
245
250
  - id: tool-plugin-market
246
251
  name: '@deepseek-ai/dsh-plugin-market/tools'
247
252
  disabled: true
package/lib/client.js CHANGED
@@ -1355,7 +1355,7 @@ window.__ModuleLoader__.load({
1355
1355
  "route.off": "关闭(直通本地)",
1356
1356
  "route.shadow": "影子(记日志仍走本地)",
1357
1357
  "route.enforce": "强制(真路由)",
1358
- "route.hint": "切换即时生效并写入 profile,无需重启",
1358
+ "route.hint": "切换即时生效并写入 profile,无需重启;默认「强制(真路由)」。",
1359
1359
  "route.saving": "保存中…",
1360
1360
  "route.tombstones": "墓碑",
1361
1361
  "route.aliasPath": "本地别名目录(原生工作区入口)",
@@ -1706,7 +1706,7 @@ window.__ModuleLoader__.load({
1706
1706
  "route.off": "Off (local passthrough)",
1707
1707
  "route.shadow": "Shadow (log only, still local)",
1708
1708
  "route.enforce": "Enforce (real routing)",
1709
- "route.hint": "Applies immediately and persists to the profile; no restart needed.",
1709
+ "route.hint": "Applies immediately and persists to the profile; no restart needed. Default: enforce (real routing).",
1710
1710
  "route.saving": "Saving…",
1711
1711
  "route.tombstones": "tombstones",
1712
1712
  "route.aliasPath": "Local alias dir (native workspace entry)",
@@ -35259,7 +35259,7 @@ window.__ModuleLoader__.load({
35259
35259
  const { actx, input } = resolved;
35260
35260
  const snapshot = input.state.getSnapshot();
35261
35261
  if (input.insertReference === void 0 || snapshot.draftRev === void 0) return false;
35262
- if (snapshot.phase === "adjudicating" || snapshot.phase === "submitting") return false;
35262
+ if (snapshot.phase === "adjudicating" || snapshot.phase === "submitting" || snapshot.phase === "sending") return false;
35263
35263
  before = snapshot.draftRev;
35264
35264
  const at = appendOffset(snapshot);
35265
35265
  if (at === void 0) return false;
@@ -35314,6 +35314,7 @@ window.__ModuleLoader__.load({
35314
35314
  const { actx, input } = resolved;
35315
35315
  const snapshot = input.state.getSnapshot();
35316
35316
  const draft = snapshot.draft;
35317
+ if (snapshot.phase === "adjudicating" || snapshot.phase === "submitting" || snapshot.phase === "sending") return false;
35317
35318
  if ((snapshot.occurrences ?? []).length > 0 || snapshot.phase === "claimed") {
35318
35319
  const at = appendOffset(snapshot);
35319
35320
  if (snapshot.draftRev === void 0 || at === void 0) {
@@ -38129,7 +38130,7 @@ Integer Float Double Long Int32 Int64 Uint8 uint32_t int32_t uint64_t int64_t si
38129
38130
  const primary = host.querySelectorAll("[class*=\"rvs-ck-\"]").length;
38130
38131
  const fallback = host.querySelectorAll("span[class^=\"rvs-t-\"]").length;
38131
38132
  const count = primary > 0 ? primary : fallback;
38132
- console.info(`[dsh-remote-vs] build 1028-1237 · ${source} · colored tokens: ${count} (${propsRef.current.path})`);
38133
+ console.info(`[dsh-remote-vs] build 1028-1532 · ${source} · colored tokens: ${count} (${propsRef.current.path})`);
38133
38134
  propsRef.current.onTokens?.(count);
38134
38135
  };
38135
38136
  const armFallback = () => {
@@ -41695,7 +41696,7 @@ Integer Float Double Long Int32 Int64 Uint8 uint32_t int32_t uint64_t int64_t si
41695
41696
  hosts: [],
41696
41697
  workspaces: [],
41697
41698
  routing: {
41698
- mode: "off",
41699
+ mode: "enforce",
41699
41700
  aliasPaths: {},
41700
41701
  tombstones: []
41701
41702
  },
@@ -44414,8 +44415,8 @@ Integer Float Double Long Int32 Int64 Uint8 uint32_t int32_t uint64_t int64_t si
44414
44415
  }),
44415
44416
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
44416
44417
  className: "rvs-build",
44417
- title: `dsh-remote-vs build 1028-1237`,
44418
- children: "1028-1237"
44418
+ title: `dsh-remote-vs build 1028-1532`,
44419
+ children: "1028-1532"
44419
44420
  }),
44420
44421
  tokenCount !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
44421
44422
  className: "rvs-build",
@@ -64559,7 +64560,8 @@ Integer Float Double Long Int32 Int64 Uint8 uint32_t int32_t uint64_t int64_t si
64559
64560
  function SettingsHostsSection(_props) {
64560
64561
  const [t] = (0, react.useState)(() => makeT(detectLang()));
64561
64562
  const [hosts, setHosts] = (0, react.useState)([]);
64562
- const [mode, setMode] = (0, react.useState)("off");
64563
+ /** Pre-load placeholder; matches the plugin's default (nforce) until the host answers. */
64564
+ const [mode, setMode] = (0, react.useState)("enforce");
64563
64565
  const [version, setVersion] = (0, react.useState)("");
64564
64566
  const [saving, setSaving] = (0, react.useState)(false);
64565
64567
  const [error, setError] = (0, react.useState)(void 0);
package/lib/index.js CHANGED
@@ -4006,7 +4006,7 @@ const Config = z.object({
4006
4006
  "off",
4007
4007
  "shadow",
4008
4008
  "enforce"
4009
- ]).default("off").volatile(),
4009
+ ]).default("enforce").volatile(),
4010
4010
  gitTimeoutMs: z.number().step(1).min(MIN_TIMEOUT_MS).max(MAX_TIMEOUT_MS).default(DEFAULT_GIT_TIMEOUT_MS).volatile(),
4011
4011
  readTimeoutMs: z.number().step(1).min(MIN_TIMEOUT_MS).max(MAX_TIMEOUT_MS).default(DEFAULT_READ_TIMEOUT_MS).volatile()
4012
4012
  });
@@ -4063,7 +4063,7 @@ function apply(ctx, config) {
4063
4063
  const worlds = new RemoteWorlds(ctx, {
4064
4064
  store,
4065
4065
  pool,
4066
- mode: () => readField(config?.routingMode, "off")
4066
+ mode: () => readField(config?.routingMode, "enforce")
4067
4067
  });
4068
4068
  ctx.provide("remoteWorlds", worlds);
4069
4069
  ctx.effect(() => () => {
@@ -95,7 +95,11 @@ interface InputSnapshot {
95
95
  draft: string;
96
96
  draftRev?: number;
97
97
  occurrences?: readonly OccurrenceLike[];
98
- phase?: 'plain' | 'adjudicating' | 'claimed' | 'submitting';
98
+ /**
99
+ * Host submission plane. `sending` exists since DSH 0.2.2-alpha.2: an ordinary
100
+ * message the host has not accepted yet — the editor is read-only there too.
101
+ */
102
+ phase?: 'plain' | 'adjudicating' | 'claimed' | 'submitting' | 'sending';
99
103
  claim?: {
100
104
  readonly token: string;
101
105
  };
@@ -39,6 +39,11 @@ export interface Config {
39
39
  /**
40
40
  * P2 transparent-routing kill-switch: off = pure local passthrough;
41
41
  * shadow = log remote decisions but execute locally; enforce = route.
42
+ *
43
+ * The DEFAULT is `enforce`: the plugin exists to execute remote workspaces
44
+ * remotely, and a fresh profile that silently kept executing locally was the
45
+ * most common "why is nothing remote" report. `off`/`shadow` stay available as
46
+ * explicit opt-outs for debugging and for hosts that must stay local.
42
47
  */
43
48
  routingMode?: RoutingMode;
44
49
  /** Per-command timeout for git work on a remote host (ms). */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-remote-vs",
3
3
  "description": "VSCode-style remote development for DSH: SSH host manager (key/password), helper deploy pipeline with version check, remote file explorer + editor + terminal in dsh-better-sidebar.",
4
- "version": "0.9.3",
4
+ "version": "0.10.0",
5
5
  "icon": "icon.svg",
6
6
  "keywords": [
7
7
  "dsh",
@@ -59,7 +59,7 @@
59
59
  "./locale/*.json": "./locale/*.json"
60
60
  },
61
61
  "engines": {
62
- "dsh": ">=0.2.1-alpha.3 <0.3.0"
62
+ "dsh": ">=0.2.2-alpha.2 <0.3.0"
63
63
  },
64
64
  "dsh": {
65
65
  "bundle": {
@@ -122,7 +122,7 @@
122
122
  "@codemirror/view": "^6.43.14",
123
123
  "@deepseek-ai/dsh-fs-ssh": "0.2.1-rc.2",
124
124
  "@deepseek-ai/dsh-ssh": "0.2.1-rc.2",
125
- "@deepseek-ai/schemastery": "3.18.5",
125
+ "@deepseek-ai/schemastery": "3.18.6-alpha.1",
126
126
  "@lezer/highlight": "1.2.3",
127
127
  "@xterm/addon-fit": "^0.11.0",
128
128
  "@xterm/xterm": "^6.0.0",
@@ -130,44 +130,44 @@
130
130
  "zod": "4.4.3"
131
131
  },
132
132
  "peerDependencies": {
133
- "@deepseek-ai/cordis": "^4.0.5-alpha.2",
134
- "@deepseek-ai/dsh-agent-preset": "^0.2.1-rc.2",
135
- "@deepseek-ai/dsh-bash-local": "^0.2.1-rc.2",
136
- "@deepseek-ai/dsh-file-reference": "^0.2.1-rc.2",
137
- "@deepseek-ai/dsh-fs": "^0.2.1-rc.2",
138
- "@deepseek-ai/dsh-shell": "^0.2.1-rc.2",
139
- "@deepseek-ai/dsh-spill": "^0.2.1-rc.2",
140
- "@deepseek-ai/dsh-subprocess": "^0.2.1-rc.2",
141
- "@deepseek-ai/dsh-tools": "^0.2.1-rc.2",
133
+ "@deepseek-ai/cordis": "^4.0.6-alpha.1",
134
+ "@deepseek-ai/dsh-agent-preset": "^0.2.2-alpha.2",
135
+ "@deepseek-ai/dsh-bash-local": "^0.2.2-alpha.2",
136
+ "@deepseek-ai/dsh-file-reference": "^0.2.2-alpha.2",
137
+ "@deepseek-ai/dsh-fs": "^0.2.2-alpha.2",
138
+ "@deepseek-ai/dsh-shell": "^0.2.2-alpha.2",
139
+ "@deepseek-ai/dsh-spill": "^0.2.2-alpha.2",
140
+ "@deepseek-ai/dsh-subprocess": "^0.2.2-alpha.2",
141
+ "@deepseek-ai/dsh-tools": "^0.2.2-alpha.2",
142
142
  "react": "^18.2.0",
143
143
  "react-dom": "^18.2.0"
144
144
  },
145
145
  "devDependencies": {
146
- "@deepseek-ai/cordis": "^4.0.4",
147
- "@deepseek-ai/dsh-agent": "0.2.1-rc.2",
148
- "@deepseek-ai/dsh-agent-preset-registry": "0.2.1-rc.2",
149
- "@deepseek-ai/dsh-api-remotes": "0.2.1-rc.2",
150
- "@deepseek-ai/dsh-bash-local": "0.2.1-rc.2",
151
- "@deepseek-ai/dsh-client-locale": "0.2.1-rc.2",
152
- "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-rc.2",
153
- "@deepseek-ai/dsh-client-ui-settings": "0.2.1-rc.2",
154
- "@deepseek-ai/dsh-client-ui-sidebar-right": "0.2.1-rc.2",
155
- "@deepseek-ai/dsh-client-ui-slots": "0.2.1-rc.2",
156
- "@deepseek-ai/dsh-client-ui-workspace": "0.2.1-rc.2",
157
- "@deepseek-ai/dsh-file-reference": "0.2.1-rc.2",
158
- "@deepseek-ai/dsh-fs": "0.2.1-rc.2",
159
- "@deepseek-ai/dsh-host-directory-picker": "0.2.1-rc.2",
160
- "@deepseek-ai/dsh-host-webserver": "0.2.1-rc.2",
161
- "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
162
- "@deepseek-ai/dsh-sandbox": "0.2.1-rc.2",
163
- "@deepseek-ai/dsh-session": "0.2.1-rc.2",
164
- "@deepseek-ai/dsh-shell": "0.2.1-rc.2",
165
- "@deepseek-ai/dsh-spill": "0.2.1-rc.2",
166
- "@deepseek-ai/dsh-subprocess": "0.2.1-rc.2",
167
- "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
168
- "@deepseek-ai/dsh-tools": "0.2.1-rc.2",
169
- "@deepseek-ai/dsh-working-directory": "0.2.1-rc.2",
170
- "@deepseek-ai/dsh-workspace": "0.2.1-rc.2",
146
+ "@deepseek-ai/cordis": "^4.0.6-alpha.1",
147
+ "@deepseek-ai/dsh-agent": "0.2.2-alpha.2",
148
+ "@deepseek-ai/dsh-agent-preset-registry": "0.2.2-alpha.2",
149
+ "@deepseek-ai/dsh-api-remotes": "0.2.2-alpha.2",
150
+ "@deepseek-ai/dsh-bash-local": "0.2.2-alpha.2",
151
+ "@deepseek-ai/dsh-client-locale": "0.2.2-alpha.2",
152
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.2-alpha.2",
153
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.2-alpha.2",
154
+ "@deepseek-ai/dsh-client-ui-sidebar-right": "0.2.2-alpha.2",
155
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.2-alpha.2",
156
+ "@deepseek-ai/dsh-client-ui-workspace": "0.2.2-alpha.2",
157
+ "@deepseek-ai/dsh-file-reference": "0.2.2-alpha.2",
158
+ "@deepseek-ai/dsh-fs": "0.2.2-alpha.2",
159
+ "@deepseek-ai/dsh-host-directory-picker": "0.2.2-alpha.2",
160
+ "@deepseek-ai/dsh-host-webserver": "0.2.2-alpha.2",
161
+ "@deepseek-ai/dsh-llm": "0.2.2-alpha.2",
162
+ "@deepseek-ai/dsh-sandbox": "0.2.2-alpha.2",
163
+ "@deepseek-ai/dsh-session": "0.2.2-alpha.2",
164
+ "@deepseek-ai/dsh-shell": "0.2.2-alpha.2",
165
+ "@deepseek-ai/dsh-spill": "0.2.2-alpha.2",
166
+ "@deepseek-ai/dsh-subprocess": "0.2.2-alpha.2",
167
+ "@deepseek-ai/dsh-system-prompt": "0.2.2-alpha.2",
168
+ "@deepseek-ai/dsh-tools": "0.2.2-alpha.2",
169
+ "@deepseek-ai/dsh-working-directory": "0.2.2-alpha.2",
170
+ "@deepseek-ai/dsh-workspace": "0.2.2-alpha.2",
171
171
  "@types/node": "^22.20.0",
172
172
  "@types/react": "~18.3.1",
173
173
  "@types/react-dom": "^18.3.5",
@@ -22,7 +22,8 @@ import { Notice, Row, hairline, optionStyle, selectStyle } from './ui.tsx'
22
22
  export function SettingsHostsSection(_props: SettingsSectionOwnerProps): ReactNode {
23
23
  const [t] = useState(() => makeT(detectLang()))
24
24
  const [hosts, setHosts] = useState<RemoteHostSummary[]>([])
25
- const [mode, setMode] = useState<'off' | 'shadow' | 'enforce'>('off')
25
+ /** Pre-load placeholder; matches the plugin's default (nforce) until the host answers. */
26
+ const [mode, setMode] = useState<'off' | 'shadow' | 'enforce'>('enforce')
26
27
  const [version, setVersion] = useState('')
27
28
  const [saving, setSaving] = useState(false)
28
29
  const [error, setError] = useState<string | undefined>(undefined)
@@ -100,7 +100,11 @@ interface InputSnapshot {
100
100
  draft: string
101
101
  draftRev?: number
102
102
  occurrences?: readonly OccurrenceLike[]
103
- phase?: 'plain' | 'adjudicating' | 'claimed' | 'submitting'
103
+ /**
104
+ * Host submission plane. `sending` exists since DSH 0.2.2-alpha.2: an ordinary
105
+ * message the host has not accepted yet — the editor is read-only there too.
106
+ */
107
+ phase?: 'plain' | 'adjudicating' | 'claimed' | 'submitting' | 'sending'
104
108
  claim?: { readonly token: string }
105
109
  }
106
110
 
@@ -295,7 +299,7 @@ export function insertDraftReference(
295
299
  const { actx, input } = resolved
296
300
  const snapshot = input.state.getSnapshot()
297
301
  if (input.insertReference === undefined || snapshot.draftRev === undefined) return false
298
- if (snapshot.phase === 'adjudicating' || snapshot.phase === 'submitting') return false
302
+ if (snapshot.phase === 'adjudicating' || snapshot.phase === 'submitting' || snapshot.phase === 'sending') return false
299
303
  before = snapshot.draftRev
300
304
  const at = appendOffset(snapshot)
301
305
  if (at === undefined) return false
@@ -346,6 +350,11 @@ export function appendToDraft(ctx: Context, sessionId: string, text: string): bo
346
350
  const { actx, input } = resolved
347
351
  const snapshot = input.state.getSnapshot()
348
352
  const draft = snapshot.draft
353
+ // The host makes the editor read-only on every submission plane: a slash
354
+ // command being checked, a claimed command running, and (since DSH
355
+ // 0.2.2-alpha.2) an ordinary message waiting for the host to accept it.
356
+ // Writing there would either be refused or land after the send snapshot.
357
+ if (snapshot.phase === 'adjudicating' || snapshot.phase === 'submitting' || snapshot.phase === 'sending') return false
349
358
  if ((snapshot.occurrences ?? []).length > 0 || snapshot.phase === 'claimed') {
350
359
  const at = appendOffset(snapshot)
351
360
  if (snapshot.draftRev === undefined || at === undefined) {
@@ -138,7 +138,7 @@ const zh: Record<string, string> = {
138
138
  'route.off': '关闭(直通本地)',
139
139
  'route.shadow': '影子(记日志仍走本地)',
140
140
  'route.enforce': '强制(真路由)',
141
- 'route.hint': '切换即时生效并写入 profile,无需重启',
141
+ 'route.hint': '切换即时生效并写入 profile,无需重启;默认「强制(真路由)」。',
142
142
  'route.saving': '保存中…',
143
143
  'route.tombstones': '墓碑',
144
144
  'route.aliasPath': '本地别名目录(原生工作区入口)',
@@ -494,7 +494,7 @@ const en: Record<string, string> = {
494
494
  'route.off': 'Off (local passthrough)',
495
495
  'route.shadow': 'Shadow (log only, still local)',
496
496
  'route.enforce': 'Enforce (real routing)',
497
- 'route.hint': 'Applies immediately and persists to the profile; no restart needed.',
497
+ 'route.hint': 'Applies immediately and persists to the profile; no restart needed. Default: enforce (real routing).',
498
498
  'route.saving': 'Saving…',
499
499
  'route.tombstones': 'tombstones',
500
500
  'route.aliasPath': 'Local alias dir (native workspace entry)',
@@ -350,7 +350,8 @@ const state: State = {
350
350
  error: undefined,
351
351
  hosts: [],
352
352
  workspaces: [],
353
- routing: { mode: 'off', aliasPaths: {}, tombstones: [] },
353
+ // Pre-bootstrap placeholder: same default as the plugin config (nforce).
354
+ routing: { mode: 'enforce', aliasPaths: {}, tombstones: [] },
354
355
  workspaceId: readWorkspaceId(),
355
356
  status: undefined,
356
357
  head: undefined,
package/src/index.ts CHANGED
@@ -69,6 +69,11 @@ export interface Config {
69
69
  /**
70
70
  * P2 transparent-routing kill-switch: off = pure local passthrough;
71
71
  * shadow = log remote decisions but execute locally; enforce = route.
72
+ *
73
+ * The DEFAULT is `enforce`: the plugin exists to execute remote workspaces
74
+ * remotely, and a fresh profile that silently kept executing locally was the
75
+ * most common "why is nothing remote" report. `off`/`shadow` stay available as
76
+ * explicit opt-outs for debugging and for hosts that must stay local.
72
77
  */
73
78
  routingMode?: RoutingMode
74
79
  /** Per-command timeout for git work on a remote host (ms). */
@@ -107,7 +112,7 @@ export interface ResolvedConfig {
107
112
  export const Config = z.object({
108
113
  announceToAgent: z.boolean().default(false).volatile(),
109
114
  enabled: z.boolean().default(true).volatile(),
110
- routingMode: z.union(['off', 'shadow', 'enforce'] as const).default('off').volatile(),
115
+ routingMode: z.union(['off', 'shadow', 'enforce'] as const).default('enforce').volatile(),
111
116
  gitTimeoutMs: z.number().step(1).min(MIN_TIMEOUT_MS).max(MAX_TIMEOUT_MS)
112
117
  .default(DEFAULT_GIT_TIMEOUT_MS).volatile(),
113
118
  readTimeoutMs: z.number().step(1).min(MIN_TIMEOUT_MS).max(MAX_TIMEOUT_MS)
@@ -171,7 +176,10 @@ export function apply(ctx: Context, config?: ResolvedConfig): void {
171
176
  const worlds = new RemoteWorlds(ctx, {
172
177
  store,
173
178
  pool,
174
- mode: () => readField(config?.routingMode, 'off' as RoutingMode),
179
+ // Keep the runtime fallback equal to the schema default: a Host that hands
180
+ // the plugin a config object without this field must behave like a fresh
181
+ // profile, not like the old opt-in (`off`) behaviour.
182
+ mode: () => readField(config?.routingMode, 'enforce' as RoutingMode),
175
183
  })
176
184
  ctx.provide('remoteWorlds', worlds)
177
185
  ctx.effect(() => () => { pool.dispose() }, 'dsh-remote-vs: pool')