dsh-ssh-tui 0.6.2 → 0.6.4

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.
Files changed (44) hide show
  1. package/README.en.md +67 -2
  2. package/README.md +56 -3
  3. package/lib/context-window.js +139 -0
  4. package/lib/context-window.js.map +1 -0
  5. package/lib/dialogs.js +13 -7
  6. package/lib/dialogs.js.map +1 -1
  7. package/lib/i18n/en.js +69 -3
  8. package/lib/i18n/en.js.map +1 -1
  9. package/lib/i18n/zh.js +69 -3
  10. package/lib/i18n/zh.js.map +1 -1
  11. package/lib/index.js +10 -4
  12. package/lib/index.js.map +1 -1
  13. package/lib/job-label.js +45 -0
  14. package/lib/job-label.js.map +1 -0
  15. package/lib/paint.js +3 -3
  16. package/lib/paint.js.map +1 -1
  17. package/lib/preset-label.js +73 -0
  18. package/lib/preset-label.js.map +1 -0
  19. package/lib/preset-rows.js +96 -0
  20. package/lib/preset-rows.js.map +1 -0
  21. package/lib/provider-catalog.js +12 -2
  22. package/lib/provider-catalog.js.map +1 -1
  23. package/lib/quota.js +149 -1
  24. package/lib/quota.js.map +1 -1
  25. package/lib/reasoning.js +9 -4
  26. package/lib/reasoning.js.map +1 -1
  27. package/lib/term-text.js +82 -11
  28. package/lib/term-text.js.map +1 -1
  29. package/lib/tool-present.js +14 -1
  30. package/lib/tool-present.js.map +1 -1
  31. package/lib/tui.js +363 -25
  32. package/lib/tui.js.map +1 -1
  33. package/lib/types/context-window.d.ts +46 -0
  34. package/lib/types/dialogs.d.ts +5 -4
  35. package/lib/types/job-label.d.ts +20 -0
  36. package/lib/types/preset-label.d.ts +38 -0
  37. package/lib/types/preset-rows.d.ts +47 -0
  38. package/lib/types/provider-catalog.d.ts +2 -0
  39. package/lib/types/quota.d.ts +49 -0
  40. package/lib/types/reasoning.d.ts +9 -4
  41. package/lib/types/term-text.d.ts +33 -4
  42. package/lib/types/tool-present.d.ts +1 -0
  43. package/lib/types/tui.d.ts +76 -1
  44. package/package.json +1 -1
package/README.en.md CHANGED
@@ -161,6 +161,49 @@ npm run build
161
161
  dsh plugin --profile tui add "link:$(pwd)"
162
162
  ```
163
163
 
164
+ ### The preset roster `/mode` needs
165
+
166
+ A terminal profile built on `dsh-base` composes no preset roster (only the Web
167
+ bundle, `@deepseek-ai/dsh-web-app`, does), and DSH STORE accepts additive bundle
168
+ patches with plugin-owned ids and no `@deepseek-ai/*` module names — so this
169
+ plugin's own patch cannot mount the roster. The row belongs to the profile's
170
+ user layer, and it is not just the `/mode` menu: the preset owns
171
+ `ask_user_question`, `present`, PTC's presentation layer, and the subagent
172
+ model-selection rows, so without it those tools are absent from the agent's
173
+ catalog.
174
+
175
+ Three ways to repair it (idempotent, pick one):
176
+
177
+ 1. **In-app**: `/mode fix` writes the block below and tells you to restart.
178
+ This is the generic path — an npm install and the in-app "Update now" both go
179
+ through `dsh plugin add` and never run the repository scripts.
180
+ 2. From a checkout: `bash scripts/ensure-profile-rows.sh [profile]`
181
+ (default `tui`).
182
+ 3. By hand, in `$DSH_HOME/profiles/<profile>/cordis.patch.yml`:
183
+
184
+ ```yaml
185
+ - insert:
186
+ - id: agent-presets
187
+ name: '@deepseek-ai/dsh-agent-presets'
188
+ config:
189
+ default: standard
190
+ ```
191
+
192
+ The script also mounts `code-runtime` (the TypeScript runtime the `ptc` preset
193
+ needs) and `subagent-model-selection-settings` (the host-owned delegation
194
+ setting), and skips a profile that already composes the roster (one bundling
195
+ `@deepseek-ai/dsh-web-app`, for example). Restart the TUI to pick it up.
196
+ The reverse order bites: adding `dsh-web-app` to a profile that already carries
197
+ this block lists the roster row twice, and the second mount fails with
198
+ `service "agentPresets" has been registered` — delete the block first.
199
+
200
+ Without the row, the TUI prints a boot line ("No agent-preset roster is
201
+ composed…"), `/mode` reports the patch path plus the `/mode fix` entry point,
202
+ and `scripts/verify.sh` says the same. Preset identity is per module instance,
203
+ so a dsh install tree carrying two copies of `@deepseek-ai/dsh-scope` (an
204
+ npm-nested checkout can) fails the mount with `refusing to compose an unscoped
205
+ context`; a global `npm i -g` install is not affected.
206
+
164
207
  For the optional **智能路由模式 (routing-suite)** mode, also run:
165
208
 
166
209
  ```sh
@@ -192,6 +235,7 @@ On first launch (when no API key is configured) the TUI opens a setup wizard:
192
235
  1. choose a provider template, matching the official Models page:
193
236
  - DeepSeek official;
194
237
  - OpenCode Go (`opencode.ai/zen/go/v1`, Responses protocol);
238
+ - Command Code (`api.commandcode.ai`, Completions protocol, with its own quota);
195
239
  - custom OpenAI-compatible gateway (Completions);
196
240
  - custom OpenAI Responses gateway;
197
241
  - Anthropic Messages-compatible gateway;
@@ -201,6 +245,17 @@ On first launch (when no API key is configured) the TUI opens a setup wizard:
201
245
  current model list straight from the provider endpoint;
202
246
  3. confirm and save.
203
247
 
248
+ The wizard sizes each model's context window automatically: the endpoint
249
+ `/models` capacities first, then the installed pi-ai catalog by model name
250
+ (stripping the thinking-level suffixes providers bake into an id — `-high`,
251
+ `-low`, `-thinking` — plus `vendor/` prefixes, `:free` tags, and date stamps).
252
+ Only when some pick still has no capacity does a route-default step appear,
253
+ pre-filled with the smallest window the route proved, so accepting it needs no
254
+ typing; when every pick matched, that step is skipped entirely. What is saved
255
+ is the route-level `defaultContextWindow`, which models added later through
256
+ `/model` inherit too — and `/model` itself looks the model up in the catalog
257
+ first, writing that model's `contextWindow` directly when it matches.
258
+
204
259
  The wizard writes the key to `~/.dsh/.credentials.yaml` when no environment
205
260
  variable shadows it; if the machine injects `DEEPSEEK_API_KEY` from
206
261
  `/etc/profile.d` or similar, it writes `~/.dsh/env.sh` (sourced by
@@ -256,6 +311,7 @@ The plugin runs on Linux, macOS, and Windows (Node ≥ 22.19):
256
311
  when a session is replayed on `--resume` — from the durable `assistant/chunk`
257
312
  events on 0.1.2 hosts, or from the packed stream inside `assistant/message`
258
313
  on 0.1.5. Steps with no usable timing fall back to `首字 1.2s`.
314
+ - Emoji width: BMP symbols carrying the Unicode `Emoji` property are budgeted two cells. When the monospace font lacks the glyph, the terminal falls back to a colour emoji about 1.6 cells wide that still advances one cell — it covers its neighbours, and the second budgeted cell goes unspent so the row comes up short. `pinEmojiCells()` does both halves at paint time: VS15 asks for the narrow text form, and a reserving space spends the second cell (emitted unconditionally, so "two cells plus every space in the text" equals "one cell of glyph advance, one reserving space, and those same spaces" for every shape). Symbols that already default to emoji presentation (`✅` `⚡` …) and astral emoji (`😀` …) advance two cells and are left alone.
259
315
  - Reconnect: channel readiness is a real connect, so a `.sock` file left behind by a killed Host is no longer listed as attachable (it used to fail the first attach with `write EPIPE`); a Host still cancelling/flushing keeps itself alive when a relay HELLOs mid-hangup; a launcher that hits a vanished peer retries once automatically — silently, with the TTY kept in raw mode and queued bytes dropped so a stale cursor reply is never echoed as `^[[17;1R`; and a probe that misses its window is retried while the Host keeps the last measurement, so the footer chip does not fall back to four hollow circles.
260
316
  - Reporting a problem: `/diag` prints a local, read-only snapshot — plugin/dsh/node
261
317
  versions, platform, session id, whether this process is the launcher or the detached
@@ -297,7 +353,7 @@ The plugin runs on Linux, macOS, and Windows (Node ≥ 22.19):
297
353
  | `Ctrl+D` | exit |
298
354
  | `Ctrl+L` | redraw |
299
355
  | `y` / `n` / `Esc` | answer an approval prompt |
300
- | `1..9` + `Enter` | answer an `ask_user_question` dialog |
356
+ | `1..9` + `Enter` | answer an `ask_user_question` dialog: `1..9` picks directly, `Enter` takes the highlighted option (the first by default), `Esc` cancels |
301
357
 
302
358
  Type `/` to see slash-command suggestions — the panel merges the TUI's own
303
359
  commands (`/find`, `/copy`, `/model`, `/effort`, `/provider`, `/language`, `/view`, `/disconnect`, `/approval`, `/help`, ...) with every command the harness
@@ -383,9 +439,10 @@ labelled `(images ok)` in the command list and completion hints.
383
439
  - **OpenAI Completions gateways** probe `/user/balance` and `credit_grants`;
384
440
  - **SuperGrok** reads `GET cli-chat-proxy.grok.com/v1/billing` (weekly remaining %);
385
441
  - **OpenCode Go** reads the official `/v1/usage` windows (5-hour / week / month);
442
+ - **Command Code** reads `/alpha/billing/credits` (5-hour / week windows plus the USD credit pool);
386
443
  - **OpenCode Zen** is metered — the TUI points at `https://opencode.ai/zen`.
387
444
 
388
- OpenCode Go / SuperGrok quota is fetched silently at start and every 10
445
+ OpenCode Go / Command Code / SuperGrok quota is fetched silently at start and every 10
389
446
  model steps (every 4 when an hourly window is near a threshold). The footer
390
447
  shows plan name + remaining bar + percent; on a narrow row the plan name
391
448
  drops first. DeepSeek official and queryable OpenAI-compatible gateways put
@@ -479,6 +536,14 @@ has not produced work the switch applies immediately; otherwise it is remembered
479
536
  as the default for the next launch. The active mode is shown in the
480
537
  header/status line.
481
538
 
539
+ Shipped presets are labelled in the active `/language` (Standard / Minimal /
540
+ PTC / Cordis in English, 标准模式 / 极简模式 / PTC 模式 / 创造模式 in Chinese); a
541
+ preset you authored keeps the name in its own `preset.yml`. `/mode <id|label>`
542
+ switches directly, e.g. `/mode minimal` or `/mode 极简模式`. When the roster row
543
+ is missing, the TUI says so at boot and `/mode` reports the profile patch path
544
+ plus the `/mode fix` repair — see
545
+ [The preset roster `/mode` needs](#the-preset-roster-mode-needs).
546
+
482
547
  ```sh
483
548
  dsh --profile tui --model deepseek-v4-flash
484
549
  dsh --profile tui --no-color
package/README.md CHANGED
@@ -182,7 +182,43 @@ bash scripts/install.sh work
182
182
  ```
183
183
 
184
184
  脚本会依次:安装依赖 → 构建 `lib/` → 通过 `dsh plugin --profile <name> add link:<repo>`
185
- 把插件链接进 profile,并自动把 `dsh-ssh-tui` 加入该 profile 的 `dsh.profile.bundles`。
185
+ 把插件链接进 profile,自动把 `dsh-ssh-tui` 加入该 profile 的 `dsh.profile.bundles`,
186
+ 最后把 `/mode` 需要的 preset 名单行写进该 profile 的 `cordis.patch.yml`(见下节)。
187
+
188
+ ### `/mode` 的 preset 名单(agent-presets 行)
189
+
190
+ `dsh-base` 的终端 profile 不组合 preset 名单(只有 Web 端的 `dsh-web-app` 组合包会),
191
+ 而 DSH STORE 只接受「附加式、插件自有 id、不出现 `@deepseek-ai/*` 名字」的 Bundle Patch,
192
+ 所以插件自己的 patch 不能挂载官方行 —— 名单行归 profile 的用户层。名单不只是
193
+ `/mode` 的菜单:`ask_user_question`、`present`、PTC 的呈现层、`subagent` 的模型选择
194
+ 这些工具行都由 preset 提供,缺席时它们都不在 Agent 的工具目录里。
195
+
196
+ 三种修复方式(幂等,选一即可):
197
+
198
+ 1. **运行中的应用内修复**:`/mode fix` 写入下面这段并提示重启。npm 安装和
199
+ 「现在更新」走的是 `dsh plugin add`,不会执行仓库脚本,所以这是最通用的一条。
200
+ 2. 仓库安装:`bash scripts/ensure-profile-rows.sh [profile]`(默认 `tui`)。
201
+ 3. 手动在该 profile 的 `$DSH_HOME/profiles/<profile>/cordis.patch.yml` 里加入:
202
+
203
+ ```yaml
204
+ - insert:
205
+ - id: agent-presets
206
+ name: '@deepseek-ai/dsh-agent-presets'
207
+ config:
208
+ default: standard
209
+ ```
210
+
211
+ 脚本会一并挂载 `code-runtime`(PTC 模式需要的 TypeScript 运行时)和
212
+ `subagent-model-selection-settings`(宿主侧子代理委派设置),并跳过已经组合了名单的
213
+ profile(例如同时装了 `@deepseek-ai/dsh-web-app` 的 profile)。改完重启 TUI 生效。
214
+ 反过来,已经写了这段的 profile 之后再装 `dsh-web-app` 会让名单行出现两次(第二次挂载
215
+ 会以 `service "agentPresets" has been registered` 失败),先把 profile 补丁里的这段删掉。
216
+
217
+ 名单缺席时,TUI 启动会打一行提示(中文界面:「未挂载 agent-presets 名单…」),
218
+ `/mode` 会打印补丁路径和 `/mode fix` 修复入口,`scripts/verify.sh` 也会给出提示。
219
+ 另外,preset 的 scope 身份按模块实例判定:一个 dsh 安装树里若存在两份
220
+ `@deepseek-ai/dsh-scope`(npm 嵌套安装的 checkout 可能如此),名单挂载会以
221
+ `refusing to compose an unscoped context` 失败;全局安装(`npm i -g`)不受影响。
186
222
 
187
223
  ### 方式二:手动安装
188
224
 
@@ -246,6 +282,7 @@ dsh --profile tui --no-color
246
282
  | 键 | 作用 |
247
283
  | --- | --- |
248
284
  | `Enter` | 发送;运行中则插入指示;空输入且已选卡片时展开/收起。工具正文超出窗口则单独全览,`Esc` 返回 |
285
+ | `1..9` / `Enter` | 回答 `ask_user_question` 提问:`1..9` 直接选,`Enter` 取当前高亮项(默认第一项),`Esc` 才取消 |
249
286
  | `↑` / `↓` | 空输入:在卡片间移动;有输入:历史(↓ 越过最新一条会回到当前草稿)。与 `Ctrl+N` / `Ctrl+P` 相同 |
250
287
  | `Ctrl+R` | 展开最新一条卡片;已用 ↑/↓ 选中时全部展开或全部收起 |
251
288
  | `Ctrl+T` | 折叠输入框(只影响显示) |
@@ -260,7 +297,12 @@ dsh --profile tui --no-color
260
297
  | `Ctrl+D` | 退出 |
261
298
  | `Ctrl+L` | 重绘整个画面 |
262
299
 
263
- `/mode` 切换官方 preset:标准 (`standard`)、PTC (`ptc`;dsh 0.1.1 上仍是 `code`)、极简 (`minimal`)、创造 (`cordis`),以及本地安装的其它模式。
300
+ `/mode` 切换官方 preset:标准 (`standard`)、PTC (`ptc`;dsh 0.1.1 上仍是 `code`)、极简 (`minimal`)、创造 (`cordis`),以及本地安装的其它模式(如 `routing-suite`)。
301
+ 官方 preset 的名字跟 `/language` 走(中文下显示“标准模式 / 极简模式 / PTC 模式 / 创造模式”,
302
+ 英文下显示 Standard / Minimal / PTC / Cordis);`$DSH_HOME/.agent-presets` 里自己写的
303
+ preset 保留它 `preset.yml` 里的名字不翻译。`/mode <id|名字>` 直接切,比如
304
+ `/mode minimal`、`/mode 极简模式`;当前会话已经跑过一轮时只记住选择,下次启动生效。
305
+ 名单没挂载时启动会打一行提示,`/mode` 会打印 profile 补丁路径和 `/mode fix` 修复入口(见「`/mode` 的 preset 名单」)。
264
306
 
265
307
  斜杠命令:`/help`、`/find`、`/copy`、`/model`、`/effort`、`/provider`、`/language`(`/lang`)、`/view`、`/disconnect`、`/approval`(`auto` / `off` / `status`)、`/submodel`、`/subeffort`、`/mode`、
266
308
  `/status`、`/diag`、`/subagents`、`/usage`(`/balance`、`/quota` 同义)、`/setup`、`/clear`,
@@ -317,9 +359,10 @@ API Key 提供商,不会冲掉其它路由。SuperGrok / X Premium 走本机 O
317
359
  - **OpenAI Completions 兼容网关**:按配置的 base URL 探测 `/user/balance`、`/dashboard/billing/credit_grants` 等;
318
360
  - **SuperGrok**:`GET cli-chat-proxy.grok.com/v1/billing`,显示本周剩余%;
319
361
  - **OpenCode Go**:官方 `/v1/usage`,滚动 5 小时 / 本周 / 本月剩余%;
362
+ - **Command Code**:官方 `/alpha/billing/credits`,滚动 5 小时 / 本周剩余% + 月度额度余额(USD);
320
363
  - **OpenCode Zen**:按量计费、没有固定额度,提示到 `https://opencode.ai/zen`。
321
364
 
322
- OpenCode Go / SuperGrok 启动和运行中都静默查询,底栏显示套餐名 + 剩余条 + 百分比;窄屏先丢掉套餐名,只留条和百分比。DeepSeek 官方和可查询的 OpenAI 兼容网关把剩余余额画进底栏(`余额 86.42 CNY`)。跨过 50% / 25% / 10% / 5% 才往工作区打 ⚠。`/usage` 或 `/balance` 仍打印完整结果。查询默认每 **10 步**一次;小时窗口接近阈值时改 4 步。
365
+ OpenCode Go / Command Code / SuperGrok 启动和运行中都静默查询,底栏显示套餐名 + 剩余条 + 百分比;窄屏先丢掉套餐名,只留条和百分比。DeepSeek 官方和可查询的 OpenAI 兼容网关把剩余余额画进底栏(`余额 86.42 CNY`)。跨过 50% / 25% / 10% / 5% 才往工作区打 ⚠。`/usage` 或 `/balance` 仍打印完整结果。查询默认每 **10 步**一次;小时窗口接近阈值时改 4 步。
323
366
 
324
367
  ## 配置
325
368
 
@@ -354,6 +397,15 @@ web 端与 TUI 共用同一份设置。`/model` 换提供商后**下一步请求
354
397
  首次配置向导的自定义/OpenCode 提供商步骤中,输入模型 ID 前可按
355
398
  `Ctrl+F` 直接从端点拉取模型列表,免去手动输入。
356
399
 
400
+ 向导会尽量自动填好上下文窗口:先读端点 `/models` 的容量字段,查不到再按模型名
401
+ 去内置 pi-ai 目录匹配(会剥离提供商写进模型名的思考档位后缀 `-high` / `-low` /
402
+ `-thinking`,以及 `vendor/` 前缀、`:free` 标签、日期戳),命中后逐模型写入
403
+ `contextWindow`。只有仍有模型查不到容量时,才多出一个「路由默认上下文窗口」步骤,
404
+ 并把推导值预填好(取本路由已探明窗口的最小值,避免超声明)——直接回车采用,也可
405
+ 输入其它数字;全部命中时该步骤自动跳过,不需要任何输入。保存的是路由级
406
+ `defaultContextWindow`,之后用 `/model` 新加的模型会继承它;而且 `/model` 追加
407
+ 模型时本身也会先查一遍目录,命中就直接写入该模型的 `contextWindow`。
408
+
357
409
  ### profile 用户层
358
410
 
359
411
  每个 profile 的 `cordis.patch.yml` 是用户覆盖层,可覆盖插件 patch 的任何行;
@@ -462,6 +514,7 @@ npm run build
462
514
  - **标题栏或铃声不生效**:确认终端支持 OSC 0 与 BEL;铃声可用
463
515
  `DSH_TUI_NO_BELL=1` 关闭。
464
516
  - **滚轮误触取消**:已加入转义序列缓冲,网络拆包也不会把 `ESC` 当取消。
517
+ - **emoji 宽度**:带 Unicode `Emoji` 属性的 BMP 符号按 **2 格**预算。等宽字体缺字形时终端会回退到彩色 emoji 字形——它比所在单元格宽(实测约 1.6 格)却只前进 1 格,于是压住相邻字符;而预算的第二格没人用,整行又比终端宽度少算格子。绘制时 `pinEmojiCells()` 两件事一起做:补 **VS15** 请终端改用窄的文本字形;再补一个**占位空格**把第二格真正用掉(**无条件补**,即使后面本来就有空格——这样"预算 = 符号 2 格 + 文本里每个空格"与"终端实际 = 字形 1 格 + 占位 1 格 + 每个空格"在每种情况下都相等)。字形 1 格 + 占位 1 格正好等于预算的 2 格,所以既不压字也不会短算;已带 VS15/VS16 的会归一成一个 VS15,本就默认 emoji 呈现的符号(`✅` `⚡` 等)和星号区 emoji(`😀` 等)本来就走 2 格,不动。
465
518
  - **跳板机 / 多层代理 SSH 发画**:每一帧只发脏行,并且拼成一次 `stdout.write`。本机 80 ms;SSH 启动时用 CSI 6n 测往返,按 RTT 选 80/160/250/400 ms。`DSH_TUI_PAINT_MS` 始终优先(40–1000)。统计行最左是 `SSH ●●●○ 90ms`(一格红、两格黄、三格及以上绿)。探测不写进转录。
466
519
  - **SSH 断了**:空闲则 flush 后退出(不保活)。忙碌(思考/回复/工具/子代理)则默认取消当前轮次、flush 日志,Host 留下。`/disconnect continue` 则不取消,后台跑完。回来 `--resume` 会接入那个进程(见上文)。不要再开第二份 Host。
467
520
  - **提示会话已在 pid 运行 / 可接入**:那份 Host 还活着。用 `--resume` 接入;只有 pid 已死、显示通道也连不上时才删 `$DSH_HOME/tui-locks/` 再从日志恢复。
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Route context-window sizing for hand-declared gateways.
3
+ *
4
+ * A gateway whose `GET /models` discloses no capacity leaves every model on the
5
+ * route at the harness default (262,144), so a long session compacts far
6
+ * earlier than the endpoint requires. `/setup` closes that gap from two
7
+ * sources, in order: the listing's own `context_length`, then the installed
8
+ * pi-ai catalog, which sizes 1,200-odd models under its own provider ids.
9
+ *
10
+ * A gateway id is rarely a catalog id verbatim — `gemini-claude-sonnet-4-6`
11
+ * proxies `claude-sonnet-4-6`, `deepseek/deepseek-v4.1-flash` is namespaced,
12
+ * `gemini-3.6-flash-high` names its thinking level — so the lookup normalizes
13
+ * before it matches, and reports nothing rather than guessing a window it
14
+ * never found.
15
+ *
16
+ * @module dsh-ssh-tui/context-window
17
+ */
18
+ /** Trailing words a gateway uses to name a thinking level rather than a model. */
19
+ const EFFORT_SUFFIXES = [
20
+ '-nothinking', '-no-thinking', '-thinking', '-reasoning', '-reasoner',
21
+ '-xhigh', '-minimal', '-medium', '-high', '-low', '-max', '-fast',
22
+ ];
23
+ /** Trailing words a gateway uses to name a tier, not a model. */
24
+ const TIER_SUFFIXES = ['-contributor', '-free', '-paid'];
25
+ /** Words a gateway may put in front of the model family it proxies. */
26
+ const NAMESPACE_HEADS = new Set([
27
+ 'gemini', 'vertex', 'google', 'azure', 'bedrock', 'aws', 'openai', 'anthropic', 'antigravity',
28
+ ]);
29
+ /** Family words that make the remainder after a namespace a model id. */
30
+ const FAMILY_HEADS = [
31
+ 'claude', 'gpt', 'gemini', 'deepseek', 'grok', 'kimi', 'glm', 'qwen', 'minimax', 'llama', 'mistral', 'command',
32
+ ];
33
+ /** Drop one gateway namespace word when what follows is a recognizable family. */
34
+ function stripNamespace(id) {
35
+ const cut = id.indexOf('-');
36
+ if (cut <= 0)
37
+ return id;
38
+ const head = id.slice(0, cut).toLowerCase();
39
+ const rest = id.slice(cut + 1);
40
+ if (!NAMESPACE_HEADS.has(head))
41
+ return id;
42
+ const lower = rest.toLowerCase();
43
+ return FAMILY_HEADS.some(family => lower.startsWith(family)) ? rest : id;
44
+ }
45
+ /**
46
+ * Catalog ids to try for one gateway model id, most specific first.
47
+ * @param raw - the id the endpoint advertises.
48
+ * @returns lowercased candidates, deduplicated and order-preserving.
49
+ */
50
+ export function catalogIdCandidates(raw) {
51
+ const out = [];
52
+ const push = (value) => {
53
+ const id = value.trim().toLowerCase();
54
+ if (id !== '' && !out.includes(id))
55
+ out.push(id);
56
+ };
57
+ const expanded = new Set();
58
+ const expand = (seed) => {
59
+ if (seed === '' || expanded.has(seed))
60
+ return;
61
+ expanded.add(seed);
62
+ push(seed);
63
+ // `:free` / `:batch` are listing tags, not part of the model id.
64
+ const untagged = seed.replace(/:[a-z0-9.-]+$/u, '');
65
+ push(untagged);
66
+ push(stripNamespace(untagged));
67
+ for (const suffix of [...EFFORT_SUFFIXES, ...TIER_SUFFIXES]) {
68
+ if (!untagged.endsWith(suffix))
69
+ continue;
70
+ const trimmed = untagged.slice(0, -suffix.length);
71
+ expand(trimmed);
72
+ expand(stripNamespace(trimmed));
73
+ }
74
+ // `-20250929` release stamps.
75
+ const undated = untagged.replace(/-\d{8}$/u, '');
76
+ if (undated !== untagged)
77
+ expand(undated);
78
+ // The catalog carries both spellings of the major/minor separator.
79
+ const dotted = untagged.replace(/(\d)-(\d)(?=$|-)/u, '$1.$2');
80
+ const dashed = untagged.replace(/(\d)\.(\d)/gu, '$1-$2');
81
+ if (dotted !== untagged)
82
+ push(dotted);
83
+ if (dashed !== untagged)
84
+ push(dashed);
85
+ };
86
+ const input = raw.trim();
87
+ expand(input);
88
+ if (input.includes('/'))
89
+ expand(input.slice(input.lastIndexOf('/') + 1));
90
+ return out;
91
+ }
92
+ /**
93
+ * Every catalog window, keyed by lowercased model id. The first provider to
94
+ * size an id wins, so a shared id keeps one stable answer.
95
+ * @param presets - the read catalog presets.
96
+ * @returns the lookup index.
97
+ */
98
+ export function catalogWindowIndex(presets) {
99
+ const index = new Map();
100
+ for (const preset of presets) {
101
+ for (const [id, window] of Object.entries(preset.capacities ?? {})) {
102
+ if (Number.isFinite(window) && window > 0 && !index.has(id))
103
+ index.set(id, window);
104
+ }
105
+ }
106
+ return index;
107
+ }
108
+ /**
109
+ * The catalog window for one gateway model id.
110
+ * @param id - the id the endpoint advertises.
111
+ * @param index - {@link catalogWindowIndex} for the loaded catalog.
112
+ * @returns the window, or `undefined` when no candidate names a sized model.
113
+ */
114
+ export function catalogContextWindow(id, index) {
115
+ for (const candidate of catalogIdCandidates(id)) {
116
+ const window = index.get(candidate);
117
+ if (window !== undefined)
118
+ return window;
119
+ }
120
+ return undefined;
121
+ }
122
+ /**
123
+ * The route-level default to persist: the smallest window discovered for the
124
+ * route's selected models. A model neither the listing nor the catalog sizes
125
+ * then inherits a value the route already proved it serves, never more.
126
+ * @param windows - the per-model windows discovered so far.
127
+ * @returns the window, or `undefined` when nothing was discovered.
128
+ */
129
+ export function suggestedRouteContextWindow(windows) {
130
+ let smallest;
131
+ for (const window of windows) {
132
+ if (!Number.isFinite(window) || window <= 0)
133
+ continue;
134
+ if (smallest === undefined || window < smallest)
135
+ smallest = window;
136
+ }
137
+ return smallest;
138
+ }
139
+ //# sourceMappingURL=context-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-window.js","sourceRoot":"","sources":["../src/context-window.ts"],"names":[],"mappings":"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"}
package/lib/dialogs.js CHANGED
@@ -48,21 +48,27 @@ export function selectQuestionOptionByKey(dialog, key) {
48
48
  return true;
49
49
  }
50
50
  /**
51
- * What Enter means for a question dialog. A single-select list with nothing
52
- * highlighted cancels (the user must not accidentally answer with the first
53
- * option); a multi-select list may answer with an empty selection. With no
54
- * options at all, the typed text is the answer.
51
+ * What Enter means for a question dialog. A single-select list answers with the
52
+ * option its highlight sits on: the list opens on the first option, so Enter
53
+ * without arrowing answers that default instead of cancelling, and Esc stays the
54
+ * explicit cancel. A multi-select list may answer with an empty selection. With
55
+ * no options at all, the typed text is the answer.
55
56
  */
56
57
  export function questionSubmit(dialog, input) {
57
58
  const options = dialog.question.options ?? [];
58
59
  const selected = [...dialog.selected]
59
60
  .map(index => options[index]?.label)
60
61
  .filter((label) => label !== undefined);
61
- if (selected.length === 0 && options.length > 0 && dialog.question.multiSelect !== true) {
62
- return { kind: 'reject' };
63
- }
64
62
  if (options.length === 0)
65
63
  return { kind: 'resolve', selected: [], custom: input };
64
+ if (selected.length === 0 && dialog.question.multiSelect !== true) {
65
+ const highlighted = options[dialog.cursor]?.label;
66
+ // The cursor is clamped to the option list, so this guards only a dialog
67
+ // built without one; Enter must never silently cancel.
68
+ if (highlighted === undefined)
69
+ return { kind: 'reject' };
70
+ return { kind: 'resolve', selected: [highlighted] };
71
+ }
66
72
  return { kind: 'resolve', selected };
67
73
  }
68
74
  /** y/n/ctrl-c/esc answers for a confirm prompt. */
@@ -1 +1 @@
1
- {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"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"}
package/lib/i18n/en.js CHANGED
@@ -76,6 +76,7 @@ export const en = {
76
76
  'quota.windowHourly': 'rolling 5h',
77
77
  'quota.windowWeekly': 'this week',
78
78
  'quota.windowMonthly': 'this month',
79
+ 'quota.windowCredits': 'credits',
79
80
  'quota.snapshotHeader': '{plan} quota ({provider})',
80
81
  'quota.snapshotWindow': ' {label} · {bar} remaining {percent}%{reset}',
81
82
  'quota.unrecognized': 'quota payload was not recognized',
@@ -128,6 +129,52 @@ export const en = {
128
129
  'toolTitle.ask_user_question': 'ask user',
129
130
  'toolTitle.exit_plan_mode': 'submit plan',
130
131
  'toolTitle.get_goal': 'view goal',
132
+ 'toolTitle.bash': 'terminal',
133
+ 'toolTitle.pwsh': 'terminal',
134
+ 'toolTitle.present': 'deliver files',
135
+ 'toolTitle.read_image': 'read image',
136
+ 'toolTitle.run_code': 'run code',
137
+ 'toolTitle.list_subagent_models': 'subagent models',
138
+ 'toolTitle.interrupt_agent': 'interrupt agent',
139
+ 'toolTitle.list_agents': 'list agents',
140
+ 'toolTitle.send_message': 'send message',
141
+ 'toolTitle.job_list': 'background jobs',
142
+ 'toolTitle.job_output': 'job output',
143
+ 'toolTitle.job_kill': 'stop job',
144
+ 'toolTitle.workflow': 'workflow',
145
+ 'toolTitle.ralph': 'Ralph loop',
146
+ 'toolTitle.lsp': 'language server',
147
+ 'toolTitle.terminal_open': 'open terminal',
148
+ 'toolTitle.terminal_send': 'send to terminal',
149
+ 'toolTitle.terminal_read': 'read terminal',
150
+ 'toolTitle.terminal_signal': 'signal terminal',
151
+ 'toolTitle.terminal_close': 'close terminal',
152
+ 'toolTitle.terminal_list': 'list terminals',
153
+ 'toolTitle.schedule_create': 'schedule task',
154
+ 'toolTitle.schedule_delete': 'delete schedule',
155
+ 'toolTitle.schedule_list': 'list schedules',
156
+ 'toolTitle.session_search': 'search sessions',
157
+ 'toolTitle.session_trace': 'trace session',
158
+ 'toolTitle.session_event_read': 'read session event',
159
+ 'toolTitle.session_event_search': 'search session events',
160
+ 'toolTitle.session_event_trace': 'trace session event',
161
+ 'toolTitle.cordis_define': 'define Cordis package',
162
+ 'toolTitle.cordis_run': 'run Cordis package',
163
+ 'toolTitle.cordis_stop': 'stop Cordis package',
164
+ 'toolTitle.cordis_undefine': 'undefine Cordis package',
165
+ 'toolTitle.cordis_inspect_list': 'list Cordis packages',
166
+ 'toolTitle.cordis_inspect_query': 'query Cordis package',
167
+ 'toolTitle.cordis_inspect_self': 'inspect own Cordis',
168
+ 'toolTitle.team_task_create': 'create team task',
169
+ 'toolTitle.team_task_get': 'get team task',
170
+ 'toolTitle.team_task_list': 'list team tasks',
171
+ 'toolTitle.team_task_update': 'update team task',
172
+ 'toolTitle.spawn_teammate': 'spawn teammate',
173
+ 'toolTitle.wait_agent': 'wait for agent',
174
+ 'toolTitle.followup_task': 'follow-up task',
175
+ 'jobAlias.adjectives': 'azure,amber,verdant,crimson,silver,inky,ember,indigo,mossy,violet,gilded,frost',
176
+ 'jobAlias.animals': 'otter,whale,leopard,fox,raven,lark,elk,dolphin,lynx,penguin,antelope,koi',
177
+ 'jobAlias.pattern': '{adjective} {animal}',
131
178
  'card.thinking': 'thinking',
132
179
  'card.plan': 'plan',
133
180
  'card.subagent': 'subagent',
@@ -264,7 +311,12 @@ export const en = {
264
311
  'tool.inspectTitle': 'full tool · {title}',
265
312
  'tool.inspectHint': 'PgUp/PgDn/wheel to scroll · Esc back to session',
266
313
  'tool.inspectFooter': 'full {pos} · Esc to return',
267
- 'mode.standard': 'standard',
314
+ // Shipped preset names, keyed by preset id (the same rule
315
+ // `@deepseek-ai/dsh-agent-presets/display` applies on the browser surface).
316
+ 'mode.preset.standard': 'Standard',
317
+ 'mode.preset.minimal': 'Minimal',
318
+ 'mode.preset.ptc': 'PTC',
319
+ 'mode.preset.cordis': 'Cordis',
268
320
  'boot.help': 'Type /help for keys · /find searches thinking/plan/subagent/reply · empty ↑/↓ selects cards',
269
321
  'cwd.entered': 'Entered session working directory {cwd}',
270
322
  'cwd.notAbsolute': 'Session working directory is not absolute: {path}',
@@ -329,6 +381,11 @@ export const en = {
329
381
  'onboard.confirmBase': ' Base URL: {url}',
330
382
  'onboard.confirmApi': ' API: {api}',
331
383
  'onboard.confirmModels': ' models: {list}',
384
+ 'onboard.confirmContext': ' route context window: {value} tokens',
385
+ 'onboard.contextPrompt': 'Route default context window (inherited by models no capacity sizes)',
386
+ 'onboard.contextValue': 'Current value: {value} tokens — press Enter to accept, or type another number',
387
+ 'onboard.contextHint': 'Inferred automatically: the endpoint /models capacities first, then the installed pi-ai catalog by model name (with -high / -low / -thinking level suffixes stripped); this fallback applies only when neither found one.',
388
+ 'onboard.contextInvalid': 'The context window must be a positive integer, got {value}',
332
389
  'onboard.confirmKey': ' API Key: {head}…{tail} (length {length})',
333
390
  'onboard.confirmHint': ' y = save, n = redo, Esc = cancel',
334
391
  'onboard.defaultParen': '(default)',
@@ -336,6 +393,7 @@ export const en = {
336
393
  'onboard.providerResponses': 'Custom OpenAI Responses gateway',
337
394
  'onboard.providerAnthropic': 'Anthropic Messages-compatible gateway',
338
395
  'onboard.providerGo': 'OpenCode Go (opencode.ai/zen/go)',
396
+ 'onboard.providerCommandCode': 'Command Code (api.commandcode.ai)',
339
397
  'onboard.idInvalid': 'Provider ID may only contain lowercase letters, digits, and hyphens, and must not start with a hyphen.',
340
398
  'onboard.keyEmpty': 'API Key cannot be empty.',
341
399
  'onboard.needModel': 'At least one model ID is required.',
@@ -493,9 +551,16 @@ export const en = {
493
551
  'mode.currentPrefix': 'current · ',
494
552
  'mode.switched': 'Mode: {name} (this session).',
495
553
  'mode.remembered': 'This session already has work; cannot switch mid-session. Remembered {name} for next launch.',
496
- 'mode.missingService': 'agentPresets service is unavailable.',
554
+ 'mode.missingService': 'agentPresets service is unavailable: this profile composes no preset roster, so /mode cannot switch modes.',
555
+ 'mode.missingServiceHint': 'Fix: run /mode fix to add the row to {patch}, then restart the TUI; a checkout can also run bash scripts/ensure-profile-rows.sh {profile}.',
556
+ 'mode.bootMissing': 'No agent-preset roster is composed: /mode cannot switch, and preset-owned tools (ask_user_question, present) are missing. Run /mode for the fix (/mode fix writes it for you).',
557
+ 'mode.fixWritten': 'Wrote {patch}; restart the TUI (/exit, then start it again) to switch modes with /mode.',
558
+ 'mode.fixPresent': '{patch} already carries the agent-presets row; restart the TUI if /mode still reports it missing.',
559
+ 'mode.fixFailed': 'Could not write {patch}: {error}',
497
560
  'mode.none': 'No modes (presets) available.',
498
561
  'mode.unknown': 'Unknown mode "{id}". Available: {available}',
562
+ 'mode.broken': 'Mode {name} cannot start right now: {reason}',
563
+ 'mode.brokenSuffix': 'unavailable · {reason}',
499
564
  'resume.unreadable': '⚠ unreadable · ',
500
565
  'picker.title': 'DeepSeek Harness — pick a session to resume',
501
566
  'picker.new': 'new session',
@@ -526,7 +591,7 @@ export const en = {
526
591
  'cmd.provider': 'Switch LLM provider and model (runs immediately, opens picker)',
527
592
  'cmd.submodel': 'Subagent model: run directly to pick, or /submodel <name> (reset to follow parent)',
528
593
  'cmd.subeffort': 'Subagent reasoning effort: run to pick, or /subeffort <default|off|low|medium|high|max|xhigh>',
529
- 'cmd.mode': 'Switch agent preset: run to pick, or /mode <standard|minimal|ptc|cordis>',
594
+ 'cmd.mode': 'Switch agent preset: run to pick, or /mode <standard|minimal|ptc|cordis>; /mode fix repairs a missing roster',
530
595
  'cmd.quit': 'Exit the TUI and stop the Host (runs immediately; resume from the session log)',
531
596
  'cmd.clear': 'Clear visible transcript and search results (runs immediately, keeps session history)',
532
597
  'cmd.diag': 'Local diagnostics: versions, channel, locks, Host identity, link, log',
@@ -621,6 +686,7 @@ export const en = {
621
686
  'effort.defaultShort': 'default',
622
687
  'effort.defaultExplicit': 'default (omit the field)',
623
688
  'effort.manualNote': ' (undeclared by the model; set manually)',
689
+ 'effort.declareFailed': 'Could not declare reasoningEffort for {provider}/{model}: {error}',
624
690
  'effort.switchedModel': 'Switched to {kind}: {provider}/{model} (effort {effort}{note}); takes effect on the next request.',
625
691
  'effort.updated': 'Updated reasoning effort: {provider}/{model} → {effort}{note}; takes effect on the next request.',
626
692
  'effort.subDefault': 'Subagent effort restored to the provider default (omit the field).',