@deepseek-harness-tui/dsh-tui 0.7.3 → 0.8.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.
Files changed (69) hide show
  1. package/README.md +2 -2
  2. package/bin/dsh-tui.js +19 -3
  3. package/cordis.patch.yml +24 -11
  4. package/cordis.yml +9 -1
  5. package/lib/types/commands.d.ts.map +1 -1
  6. package/lib/types/commands.js +2 -1
  7. package/lib/types/components/MessageList.d.ts +3 -1
  8. package/lib/types/components/MessageList.d.ts.map +1 -1
  9. package/lib/types/components/MessageList.js +4 -4
  10. package/lib/types/components/PluginSceneBoundary.d.ts +31 -0
  11. package/lib/types/components/PluginSceneBoundary.d.ts.map +1 -0
  12. package/lib/types/components/PluginSceneBoundary.js +29 -0
  13. package/lib/types/components/SkillsPicker.d.ts +18 -0
  14. package/lib/types/components/SkillsPicker.d.ts.map +1 -0
  15. package/lib/types/components/SkillsPicker.js +50 -0
  16. package/lib/types/components/SplitDiffView.d.ts +28 -0
  17. package/lib/types/components/SplitDiffView.d.ts.map +1 -0
  18. package/lib/types/components/SplitDiffView.js +417 -0
  19. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +3 -1
  20. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  21. package/lib/types/components/messages/AssistantToolUseMessage.js +17 -7
  22. package/lib/types/dsh-adapter/channel.d.ts +68 -0
  23. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  24. package/lib/types/dsh-adapter/channel.js +120 -5
  25. package/lib/types/dsh-adapter/contract.d.ts +1 -1
  26. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  27. package/lib/types/dsh-adapter/contract.js +1 -0
  28. package/lib/types/dsh-adapter/index.d.ts +4 -0
  29. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  30. package/lib/types/dsh-adapter/index.js +1 -0
  31. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  32. package/lib/types/dsh-adapter/plugin.js +59 -0
  33. package/lib/types/dsh-adapter/promptDebug.d.ts +10 -0
  34. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -0
  35. package/lib/types/dsh-adapter/promptDebug.js +148 -0
  36. package/lib/types/dsh-adapter/scenes.d.ts +75 -0
  37. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -0
  38. package/lib/types/dsh-adapter/scenes.js +78 -0
  39. package/lib/types/dsh-adapter/settings-sections.d.ts +116 -0
  40. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -0
  41. package/lib/types/dsh-adapter/settings-sections.js +63 -0
  42. package/lib/types/dsh-adapter/settingsEditor.d.ts +128 -0
  43. package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -0
  44. package/lib/types/dsh-adapter/settingsEditor.js +244 -0
  45. package/lib/types/i18n.d.ts +137 -10
  46. package/lib/types/i18n.d.ts.map +1 -1
  47. package/lib/types/i18n.js +36 -3
  48. package/lib/types/jsx-runtime.d.ts +2 -0
  49. package/lib/types/jsx-runtime.d.ts.map +1 -0
  50. package/lib/types/jsx-runtime.js +13 -0
  51. package/lib/types/modelRoute.d.ts +10 -2
  52. package/lib/types/modelRoute.d.ts.map +1 -1
  53. package/lib/types/modelRoute.js +33 -4
  54. package/lib/types/scenes.d.ts +3 -0
  55. package/lib/types/scenes.d.ts.map +1 -0
  56. package/lib/types/scenes.js +4 -0
  57. package/lib/types/screens/Chat.d.ts.map +1 -1
  58. package/lib/types/screens/Chat.js +107 -11
  59. package/lib/types/screens/Settings.d.ts +20 -0
  60. package/lib/types/screens/Settings.d.ts.map +1 -0
  61. package/lib/types/screens/Settings.js +333 -0
  62. package/lib/types/screens/StatusLine.js +1 -1
  63. package/lib/types/settings-sections.d.ts +3 -0
  64. package/lib/types/settings-sections.d.ts.map +1 -0
  65. package/lib/types/settings-sections.js +4 -0
  66. package/lib/types/theme.d.ts +12 -0
  67. package/lib/types/theme.d.ts.map +1 -1
  68. package/lib/types/theme.js +36 -0
  69. package/package.json +17 -1
@@ -0,0 +1,244 @@
1
+ /**
2
+ * React-free form model behind the `/settings` screen (issue #165), mirroring
3
+ * the web front door's card-form.ts semantics: a section stages what the user
4
+ * types and writes it only on save — every settings write is a durable,
5
+ * revision-fenced `settings.mutate`, so a control that committed as it
6
+ * settled turned one edit into a write the user never asked for and could
7
+ * not preview; staged text makes what is on screen exactly what a save would
8
+ * store.
9
+ *
10
+ * A field shows its effective value — the user layer over the composition
11
+ * layer over the schema default — and whether the user layer carries it. That
12
+ * presence, not a value comparison, is what marks a field overridden: an
13
+ * override equal to the composition default is still an override.
14
+ *
15
+ * The kernel side (storage, schema validation, layering, revision fencing)
16
+ * stays with the dsh settings / credentials services; this module only
17
+ * translates between drafts and `mutate` path ops.
18
+ */
19
+ /** Read a nested value by path (array indexes as strings). */
20
+ export function getPath(value, path) {
21
+ let current = value;
22
+ for (const key of path) {
23
+ if (Array.isArray(current)) {
24
+ current = current[Number(key)];
25
+ continue;
26
+ }
27
+ if (typeof current !== 'object' || current === null)
28
+ return undefined;
29
+ current = current[key];
30
+ }
31
+ return current;
32
+ }
33
+ /**
34
+ * Whether a value explicitly carries the path — its presence marks a user
35
+ * override, independent of the value stored there.
36
+ */
37
+ export function hasPath(value, path) {
38
+ if (path.length === 0)
39
+ return value !== undefined;
40
+ const parent = getPath(value, path.slice(0, -1));
41
+ const key = path[path.length - 1];
42
+ if (Array.isArray(parent))
43
+ return Number(key) < parent.length;
44
+ if (typeof parent !== 'object' || parent === null)
45
+ return false;
46
+ return key in parent;
47
+ }
48
+ /** Default draft text for a kind; the empty string when the section carries none. */
49
+ function defaultFormat(field, value) {
50
+ if (field.format)
51
+ return field.format(value);
52
+ if (value === undefined || value === null)
53
+ return '';
54
+ switch (field.kind) {
55
+ case 'number':
56
+ return typeof value === 'number' ? String(value) : '';
57
+ case 'boolean':
58
+ return value === true ? 'true' : 'false';
59
+ default:
60
+ return typeof value === 'string' ? value : String(value);
61
+ }
62
+ }
63
+ /** Default write for a kind: an empty text/number draft stages a clear. */
64
+ function defaultParse(field, text) {
65
+ if (field.parse)
66
+ return field.parse(text);
67
+ const trimmed = text.trim();
68
+ switch (field.kind) {
69
+ case 'number': {
70
+ if (trimmed === '')
71
+ return { kind: 'clear' };
72
+ const value = Number(trimmed);
73
+ return Number.isFinite(value) ? { kind: 'set', value } : undefined;
74
+ }
75
+ case 'boolean':
76
+ return { kind: 'set', value: trimmed === 'true' };
77
+ case 'select':
78
+ return field.options?.some(option => option.value === trimmed)
79
+ ? { kind: 'set', value: trimmed }
80
+ : undefined;
81
+ default:
82
+ return trimmed === '' ? { kind: 'clear' } : { kind: 'set', value: text };
83
+ }
84
+ }
85
+ /** Key addressing one field inside a form: its path joined. */
86
+ function fieldKey(field) {
87
+ return field.path.join('.');
88
+ }
89
+ /**
90
+ * The staged form over one settings namespace's declared fields. The screen
91
+ * re-seeds it from a fresh namespace view after every save and whenever the
92
+ * underlying document changes.
93
+ */
94
+ export class SettingsForm {
95
+ host;
96
+ view;
97
+ fields;
98
+ edits = new Map();
99
+ saving = false;
100
+ failed = false;
101
+ constructor(host, view, fields) {
102
+ this.host = host;
103
+ this.view = view;
104
+ this.fields = fields;
105
+ }
106
+ get available() {
107
+ return this.view !== undefined;
108
+ }
109
+ /** The namespace view the form was seeded from (undefined = not served). */
110
+ get namespace() {
111
+ return this.view;
112
+ }
113
+ field(field) {
114
+ const key = fieldKey(field);
115
+ const staged = this.edits.get(key);
116
+ if (field.secret !== undefined) {
117
+ // A credential literal never seeds a draft: blank until typed, and a
118
+ // blank draft writes nothing.
119
+ return { text: staged?.text ?? '', overridden: false, invalid: false };
120
+ }
121
+ if (staged !== undefined) {
122
+ const write = defaultParse(field, staged.text);
123
+ return {
124
+ text: staged.text,
125
+ overridden: write?.kind === 'set',
126
+ invalid: write === undefined,
127
+ };
128
+ }
129
+ return {
130
+ text: defaultFormat(field, this.view ? getPath(this.view.value, field.path) : undefined),
131
+ overridden: this.view !== undefined && hasPath(this.view.user, field.path),
132
+ invalid: false,
133
+ };
134
+ }
135
+ shell() {
136
+ let invalid = false;
137
+ for (const field of this.fields) {
138
+ if (this.field(field).invalid)
139
+ invalid = true;
140
+ }
141
+ return {
142
+ available: this.available,
143
+ dirty: this.edits.size > 0,
144
+ invalid,
145
+ saving: this.saving,
146
+ failed: this.failed,
147
+ };
148
+ }
149
+ /** Whether the field holds a staged (unsaved) edit. */
150
+ isStaged(field) {
151
+ return this.edits.has(fieldKey(field));
152
+ }
153
+ /** Stage draft text for one field. */
154
+ edit(field, text) {
155
+ this.edits.set(fieldKey(field), { text, clear: false });
156
+ this.failed = false;
157
+ }
158
+ /** Stage a clear, so saving lets the field re-inherit the composition layer. */
159
+ resetField(field) {
160
+ this.edits.set(fieldKey(field), { text: '', clear: true });
161
+ this.failed = false;
162
+ }
163
+ /** Drop every staged edit. */
164
+ discard() {
165
+ this.edits.clear();
166
+ this.failed = false;
167
+ }
168
+ /** Whether any staged draft is invalid, which blocks the save. */
169
+ get invalid() {
170
+ return this.shell().invalid;
171
+ }
172
+ /**
173
+ * Write every staged edit. Settings fields land as one revision-fenced
174
+ * `mutate` (one retry on a stale-revision conflict); credential fields
175
+ * write through the credentials seam. Resolves to whether every write
176
+ * landed — the caller re-seeds from a fresh namespace view afterwards.
177
+ *
178
+ * Only the edits THIS save snapshotted are cleared on success, and only
179
+ * when they are still the same edit: a draft typed while the write was in
180
+ * flight is a newer object and survives — it was never written, so it must
181
+ * not be silently dropped.
182
+ */
183
+ async save() {
184
+ if (this.view === undefined || this.invalid || this.saving)
185
+ return false;
186
+ const ns = this.view.ns;
187
+ const snapshot = new Map(this.edits);
188
+ const ops = [];
189
+ const secrets = [];
190
+ for (const field of this.fields) {
191
+ const staged = snapshot.get(fieldKey(field));
192
+ if (staged === undefined)
193
+ continue;
194
+ if (field.secret !== undefined) {
195
+ // Blank drafts write nothing; typed drafts replace the credential.
196
+ if (staged.text !== '')
197
+ secrets.push({ ref: field.secret.ref, text: staged.text });
198
+ continue;
199
+ }
200
+ const write = staged.clear ? { kind: 'clear' } : defaultParse(field, staged.text);
201
+ if (write === undefined)
202
+ return false;
203
+ ops.push(write.kind === 'clear'
204
+ ? { op: 'unset', path: field.path }
205
+ : { op: 'set', path: field.path, value: write.value });
206
+ }
207
+ this.saving = true;
208
+ try {
209
+ if (ops.length > 0) {
210
+ try {
211
+ await this.host.write(ns, ops, this.view.revision);
212
+ }
213
+ catch (error) {
214
+ // One retry on a stale-revision conflict (a concurrent write landed
215
+ // between seed and save); anything else propagates.
216
+ if (error?.code !== 'SETTINGS_CONFLICT')
217
+ throw error;
218
+ const fresh = this.host.listNamespaces().find(entry => entry.ns === ns);
219
+ await this.host.write(ns, ops, fresh?.revision);
220
+ }
221
+ }
222
+ for (const secret of secrets) {
223
+ await this.host.writeCredential(secret.ref, secret.text);
224
+ }
225
+ for (const [key, edit] of snapshot) {
226
+ if (this.edits.get(key) === edit)
227
+ this.edits.delete(key);
228
+ }
229
+ this.failed = false;
230
+ return true;
231
+ }
232
+ catch {
233
+ this.failed = true;
234
+ return false;
235
+ }
236
+ finally {
237
+ this.saving = false;
238
+ }
239
+ }
240
+ /** Whether any layer supplies the credential a secret field addresses. */
241
+ credentialConfigured(field) {
242
+ return field.secret === undefined ? Promise.resolve(false) : this.host.credentialConfigured(field.secret.ref);
243
+ }
244
+ }
@@ -775,14 +775,6 @@ declare const dict: {
775
775
  readonly zh: "需要时可在 cordis.yml 挂载对应 hooks 插件。";
776
776
  readonly en: "Mount the matching hooks plugin in cordis.yml when needed.";
777
777
  };
778
- readonly 'memory-none': {
779
- readonly zh: "DSH 暂无持久记忆服务。";
780
- readonly en: "DSH has no persistent memory service yet.";
781
- };
782
- readonly 'memory-hint': {
783
- readonly zh: "长期约定可写入 AGENTS.md(工作区上下文)或技能(~/.dsh/skills)。";
784
- readonly en: "Long-term conventions can go into AGENTS.md (workspace context) or skills (~/.dsh/skills).";
785
- };
786
778
  readonly 'update-unavailable': {
787
779
  readonly zh: "当前运行方式不支持自动更新(需经 dsh --profile 启动),请在终端执行 dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui";
788
780
  readonly en: "Automatic update is unavailable in this launch mode (needs dsh --profile). Run dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui in a terminal.";
@@ -1227,6 +1219,90 @@ declare const dict: {
1227
1219
  readonly zh: "恢复会话";
1228
1220
  readonly en: "Resume session";
1229
1221
  };
1222
+ readonly 'settings-title': {
1223
+ readonly zh: "插件设置";
1224
+ readonly en: "Plugin settings";
1225
+ };
1226
+ readonly 'settings-unavailable': {
1227
+ readonly zh: "设置服务未挂载——只读";
1228
+ readonly en: "settings service absent — read-only";
1229
+ };
1230
+ readonly 'settings-empty': {
1231
+ readonly zh: "没有可配置的插件设置(尚无插件注册设置区块)";
1232
+ readonly en: "No configurable plugin settings (no plugin has registered a section)";
1233
+ };
1234
+ readonly 'settings-section-unavailable': {
1235
+ readonly zh: "命名空间未注册";
1236
+ readonly en: "namespace not served";
1237
+ };
1238
+ readonly 'settings-readonly-heading': {
1239
+ readonly zh: "其他设置命名空间(只读)";
1240
+ readonly en: "Other settings namespaces (read-only)";
1241
+ };
1242
+ readonly 'settings-readonly-hint': {
1243
+ readonly zh: "以上命名空间尚无 TUI 设置区块,可手工编辑 {{path}}";
1244
+ readonly en: "No TUI section for these namespaces yet — edit {{path}} by hand";
1245
+ };
1246
+ readonly 'settings-badge-override': {
1247
+ readonly zh: "已覆盖";
1248
+ readonly en: "overridden";
1249
+ };
1250
+ readonly 'settings-badge-restart': {
1251
+ readonly zh: "重启生效";
1252
+ readonly en: "applies on restart";
1253
+ };
1254
+ readonly 'settings-badge-dirty': {
1255
+ readonly zh: "未保存";
1256
+ readonly en: "unsaved";
1257
+ };
1258
+ readonly 'settings-badge-saving': {
1259
+ readonly zh: "保存中";
1260
+ readonly en: "saving";
1261
+ };
1262
+ readonly 'settings-badge-failed': {
1263
+ readonly zh: "保存失败";
1264
+ readonly en: "save failed";
1265
+ };
1266
+ readonly 'settings-field-empty': {
1267
+ readonly zh: "(未设置)";
1268
+ readonly en: "(unset)";
1269
+ };
1270
+ readonly 'settings-field-invalid': {
1271
+ readonly zh: "无效输入";
1272
+ readonly en: "invalid";
1273
+ };
1274
+ readonly 'settings-secret-set': {
1275
+ readonly zh: "●●●●●●(已配置)";
1276
+ readonly en: "●●●●●● (configured)";
1277
+ };
1278
+ readonly 'settings-secret-unset': {
1279
+ readonly zh: "(未配置)";
1280
+ readonly en: "(not configured)";
1281
+ };
1282
+ readonly 'settings-secret-staged': {
1283
+ readonly zh: "(待保存)";
1284
+ readonly en: "(pending save)";
1285
+ };
1286
+ readonly 'settings-saved': {
1287
+ readonly zh: "已保存 {{ns}}";
1288
+ readonly en: "Saved {{ns}}";
1289
+ };
1290
+ readonly 'settings-save-failed': {
1291
+ readonly zh: "保存 {{ns}} 失败——请重试";
1292
+ readonly en: "Saving {{ns}} failed — please retry";
1293
+ };
1294
+ readonly 'settings-discarded': {
1295
+ readonly zh: "已放弃所有未保存的修改";
1296
+ readonly en: "Discarded all unsaved edits";
1297
+ };
1298
+ readonly 'settings-hint-list': {
1299
+ readonly zh: "**Enter** 编辑/切换 · s 保存 · d 放弃 · Esc 放弃/退出";
1300
+ readonly en: "**Enter** edit/toggle · s save · d discard · Esc discard/exit";
1301
+ };
1302
+ readonly 'settings-hint-edit': {
1303
+ readonly zh: "**Enter** 确认 · Esc 取消";
1304
+ readonly en: "**Enter** to confirm · Esc to cancel";
1305
+ };
1230
1306
  readonly 'session-loading': {
1231
1307
  readonly zh: "正在读取会话…";
1232
1308
  readonly en: "Reading sessions…";
@@ -1355,6 +1431,10 @@ declare const dict: {
1355
1431
  readonly zh: "**Enter** 选择 · Esc 退出";
1356
1432
  readonly en: "**Enter** to select · Esc to exit";
1357
1433
  };
1434
+ readonly 'hint-fill-exit': {
1435
+ readonly zh: "**Enter** 填入命令 · Esc 退出";
1436
+ readonly en: "**Enter** to insert · Esc to exit";
1437
+ };
1358
1438
  readonly 'hint-rewind-back': {
1359
1439
  readonly zh: "**Enter** 回退 · Esc 返回";
1360
1440
  readonly en: "**Enter** to rewind · Esc to back";
@@ -1375,6 +1455,50 @@ declare const dict: {
1375
1455
  readonly zh: "模型";
1376
1456
  readonly en: "Model";
1377
1457
  };
1458
+ readonly 'picker-title-skills': {
1459
+ readonly zh: "技能";
1460
+ readonly en: "Skills";
1461
+ };
1462
+ readonly 'skills-loading': {
1463
+ readonly zh: "正在加载技能";
1464
+ readonly en: "Loading skills";
1465
+ };
1466
+ readonly 'skills-loading-subtitle': {
1467
+ readonly zh: "正在查询技能注册表…";
1468
+ readonly en: "Querying the skill registry…";
1469
+ };
1470
+ readonly 'skills-empty': {
1471
+ readonly zh: "当前会话没有可用技能";
1472
+ readonly en: "No skills available in this session";
1473
+ };
1474
+ readonly 'skills-load-failed': {
1475
+ readonly zh: "技能列表加载失败";
1476
+ readonly en: "Failed to load the skill list";
1477
+ };
1478
+ readonly 'plugin-scene-crashed': {
1479
+ readonly zh: "插件场景「{{id}}」渲染崩溃:{{err}}(已自动关闭)";
1480
+ readonly en: "Plugin scene \"{{id}}\" crashed while rendering: {{err}} (closed)";
1481
+ };
1482
+ readonly 'skills-source-bundled': {
1483
+ readonly zh: "内置";
1484
+ readonly en: "built-in";
1485
+ };
1486
+ readonly 'skills-source-user': {
1487
+ readonly zh: "用户";
1488
+ readonly en: "user";
1489
+ };
1490
+ readonly 'skills-source-project': {
1491
+ readonly zh: "项目";
1492
+ readonly en: "project";
1493
+ };
1494
+ readonly 'skills-source-runtime': {
1495
+ readonly zh: "运行时";
1496
+ readonly en: "runtime";
1497
+ };
1498
+ readonly 'skills-source-custom': {
1499
+ readonly zh: "自定义";
1500
+ readonly en: "custom";
1501
+ };
1378
1502
  readonly 'picker-title-theme': {
1379
1503
  readonly zh: "颜色主题";
1380
1504
  readonly en: "Color theme";
@@ -1891,6 +2015,9 @@ declare const dict: {
1891
2015
  readonly 'cmd-desc-config': {
1892
2016
  readonly zh: "查看 dsh-tui 配置来源";
1893
2017
  };
2018
+ readonly 'cmd-desc-settings': {
2019
+ readonly zh: "查看和编辑插件设置";
2020
+ };
1894
2021
  readonly 'cmd-desc-doctor': {
1895
2022
  readonly zh: "运行环境检查";
1896
2023
  };
@@ -1942,8 +2069,8 @@ declare const dict: {
1942
2069
  readonly 'cmd-desc-mcp': {
1943
2070
  readonly zh: "查看 MCP 状态";
1944
2071
  };
1945
- readonly 'cmd-desc-memory': {
1946
- readonly zh: "查看记忆状态";
2072
+ readonly 'cmd-desc-skills': {
2073
+ readonly zh: "列出所有可用技能";
1947
2074
  };
1948
2075
  readonly 'cmd-desc-update': {
1949
2076
  readonly zh: "更新 dsh-tui 并重启";
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkqBA,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAUxD,uEAAuE;AACvE,KAAK,QAAQ,GAAG,MAAM,IAAI,CAAA;AAG1B,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,IAAI,CAG5D;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,yDAAyD;AACzD,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAGxC;AAED,iDAAiD;AACjD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAS5D;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAkB,GAAG,IAAI,GAAG,SAAS,CAMtE;AAED,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,GAAE,MAAkB,GAAG,OAAO,CAQ1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAavC;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAIzC"}
package/lib/types/i18n.js CHANGED
@@ -216,8 +216,6 @@ const dict = {
216
216
  'permissions-path-hint': { zh: '模型工具相对路径均解析自该目录;跨目录访问由 fs-policy 拦截。', en: 'Relative paths of model tools resolve from this directory; cross-directory access is blocked by fs-policy.' },
217
217
  'hooks-not-mounted': { zh: 'DSH hooks(dsh-hooks-claude / dsh-hooks-codex)未在本 leaf 挂载。', en: 'DSH hooks (dsh-hooks-claude / dsh-hooks-codex) are not mounted in this leaf.' },
218
218
  'hooks-mount-hint': { zh: '需要时可在 cordis.yml 挂载对应 hooks 插件。', en: 'Mount the matching hooks plugin in cordis.yml when needed.' },
219
- 'memory-none': { zh: 'DSH 暂无持久记忆服务。', en: 'DSH has no persistent memory service yet.' },
220
- 'memory-hint': { zh: '长期约定可写入 AGENTS.md(工作区上下文)或技能(~/.dsh/skills)。', en: 'Long-term conventions can go into AGENTS.md (workspace context) or skills (~/.dsh/skills).' },
221
219
  'update-unavailable': { zh: '当前运行方式不支持自动更新(需经 dsh --profile 启动),请在终端执行 dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui', en: 'Automatic update is unavailable in this launch mode (needs dsh --profile). Run dsh plugin --profile <name> update @deepseek-harness-tui/dsh-tui in a terminal.' },
222
220
  'update-working': { zh: '当前回合仍在运行,请等待完成后再更新 TUI。', en: 'The current turn is still running. Wait for it to finish before updating the TUI.' },
223
221
  'update-starting': { zh: '正在更新 @deepseek-harness-tui/dsh-tui,完成后会自动重启并恢复当前会话……', en: 'Updating @deepseek-harness-tui/dsh-tui. The TUI will restart and resume this session when finished…' },
@@ -343,6 +341,28 @@ const dict = {
343
341
  'resume-hint-delete': { zh: '**Enter** 删除 · Esc 取消', en: '**Enter** to delete · Esc to cancel' },
344
342
  'resume-hint-rename': { zh: '**Enter** 保存 · Esc 取消', en: '**Enter** to save · Esc to cancel' },
345
343
  'resume-title': { zh: '恢复会话', en: 'Resume session' },
344
+ // ── screens/Settings.tsx (/settings, issue #165) ───────────────────
345
+ 'settings-title': { zh: '插件设置', en: 'Plugin settings' },
346
+ 'settings-unavailable': { zh: '设置服务未挂载——只读', en: 'settings service absent — read-only' },
347
+ 'settings-empty': { zh: '没有可配置的插件设置(尚无插件注册设置区块)', en: 'No configurable plugin settings (no plugin has registered a section)' },
348
+ 'settings-section-unavailable': { zh: '命名空间未注册', en: 'namespace not served' },
349
+ 'settings-readonly-heading': { zh: '其他设置命名空间(只读)', en: 'Other settings namespaces (read-only)' },
350
+ 'settings-readonly-hint': { zh: '以上命名空间尚无 TUI 设置区块,可手工编辑 {{path}}', en: 'No TUI section for these namespaces yet — edit {{path}} by hand' },
351
+ 'settings-badge-override': { zh: '已覆盖', en: 'overridden' },
352
+ 'settings-badge-restart': { zh: '重启生效', en: 'applies on restart' },
353
+ 'settings-badge-dirty': { zh: '未保存', en: 'unsaved' },
354
+ 'settings-badge-saving': { zh: '保存中', en: 'saving' },
355
+ 'settings-badge-failed': { zh: '保存失败', en: 'save failed' },
356
+ 'settings-field-empty': { zh: '(未设置)', en: '(unset)' },
357
+ 'settings-field-invalid': { zh: '无效输入', en: 'invalid' },
358
+ 'settings-secret-set': { zh: '●●●●●●(已配置)', en: '●●●●●● (configured)' },
359
+ 'settings-secret-unset': { zh: '(未配置)', en: '(not configured)' },
360
+ 'settings-secret-staged': { zh: '(待保存)', en: '(pending save)' },
361
+ 'settings-saved': { zh: '已保存 {{ns}}', en: 'Saved {{ns}}' },
362
+ 'settings-save-failed': { zh: '保存 {{ns}} 失败——请重试', en: 'Saving {{ns}} failed — please retry' },
363
+ 'settings-discarded': { zh: '已放弃所有未保存的修改', en: 'Discarded all unsaved edits' },
364
+ 'settings-hint-list': { zh: '**Enter** 编辑/切换 · s 保存 · d 放弃 · Esc 放弃/退出', en: '**Enter** edit/toggle · s save · d discard · Esc discard/exit' },
365
+ 'settings-hint-edit': { zh: '**Enter** 确认 · Esc 取消', en: '**Enter** to confirm · Esc to cancel' },
346
366
  // ── 会话浏览器:行、计数、筛选、预览 ───────────────────────────────
347
367
  'session-loading': { zh: '正在读取会话…', en: 'Reading sessions…' },
348
368
  'session-list-failed': { zh: '无法读取会话列表 · {{err}}', en: 'Could not read the session list · {{err}}' },
@@ -380,12 +400,24 @@ const dict = {
380
400
  'hint-confirm-exit': { zh: '**Enter** 确认 · Esc 退出', en: '**Enter** to confirm · Esc to exit' },
381
401
  'hint-confirm-cancel': { zh: '**Enter** 确认 · Esc 取消', en: '**Enter** to confirm · Esc to cancel' },
382
402
  'hint-select-exit': { zh: '**Enter** 选择 · Esc 退出', en: '**Enter** to select · Esc to exit' },
403
+ 'hint-fill-exit': { zh: '**Enter** 填入命令 · Esc 退出', en: '**Enter** to insert · Esc to exit' },
383
404
  'hint-rewind-back': { zh: '**Enter** 回退 · Esc 返回', en: '**Enter** to rewind · Esc to back' },
384
405
  'hint-adjust-done': { zh: '**←/→** 调整 · Enter/Esc 完成', en: '**←/→** to adjust · Enter/Esc to done' },
385
406
  'hint-history-search': { zh: '↑/↓ 选择 · **Enter** 确认 · Esc 取消', en: '↑/↓ to navigate · **Enter** to select · Esc to cancel' },
386
407
  'hint-expand-ctrl-o': { zh: '(ctrl+o 展开)', en: '(ctrl+o to expand)' },
387
408
  // ── components/ModelPicker.tsx / ThemePicker.tsx / ActivityPicker.tsx / EffortSlider.tsx ──
388
409
  'picker-title-model': { zh: '模型', en: 'Model' },
410
+ 'picker-title-skills': { zh: '技能', en: 'Skills' },
411
+ 'skills-loading': { zh: '正在加载技能', en: 'Loading skills' },
412
+ 'skills-loading-subtitle': { zh: '正在查询技能注册表…', en: 'Querying the skill registry…' },
413
+ 'skills-empty': { zh: '当前会话没有可用技能', en: 'No skills available in this session' },
414
+ 'skills-load-failed': { zh: '技能列表加载失败', en: 'Failed to load the skill list' },
415
+ 'plugin-scene-crashed': { zh: '插件场景「{{id}}」渲染崩溃:{{err}}(已自动关闭)', en: 'Plugin scene "{{id}}" crashed while rendering: {{err}} (closed)' },
416
+ 'skills-source-bundled': { zh: '内置', en: 'built-in' },
417
+ 'skills-source-user': { zh: '用户', en: 'user' },
418
+ 'skills-source-project': { zh: '项目', en: 'project' },
419
+ 'skills-source-runtime': { zh: '运行时', en: 'runtime' },
420
+ 'skills-source-custom': { zh: '自定义', en: 'custom' },
389
421
  'picker-title-theme': { zh: '颜色主题', en: 'Color theme' },
390
422
  'picker-title-activity': { zh: '指示器预设', en: 'Indicator preset' },
391
423
  'picker-title-effort': { zh: '推理强度', en: 'Reasoning effort' },
@@ -536,6 +568,7 @@ const dict = {
536
568
  'cmd-desc-status': { zh: '查看会话状态' },
537
569
  'cmd-desc-cost': { zh: '查看会话 token 用量' },
538
570
  'cmd-desc-config': { zh: '查看 dsh-tui 配置来源' },
571
+ 'cmd-desc-settings': { zh: '查看和编辑插件设置' },
539
572
  'cmd-desc-doctor': { zh: '运行环境检查' },
540
573
  'cmd-desc-init': { zh: '在工作目录创建 AGENTS.md' },
541
574
  'cmd-desc-agents': { zh: '查看本会话的子代理' },
@@ -555,7 +588,7 @@ const dict = {
555
588
  'cmd-desc-add-dir': { zh: '查看文件系统策略范围' },
556
589
  'cmd-desc-hooks': { zh: '查看 hooks 状态' },
557
590
  'cmd-desc-mcp': { zh: '查看 MCP 状态' },
558
- 'cmd-desc-memory': { zh: '查看记忆状态' },
591
+ 'cmd-desc-skills': { zh: '列出所有可用技能' },
559
592
  'cmd-desc-update': { zh: '更新 dsh-tui 并重启' },
560
593
  // Built-in skills
561
594
  'cmd-desc-audit': { zh: '对当前项目做全面代码审计' },
@@ -0,0 +1,2 @@
1
+ export * from 'react/jsx-runtime';
2
+ //# sourceMappingURL=jsx-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsx-runtime.d.ts","sourceRoot":"","sources":["../../src/jsx-runtime.ts"],"names":[],"mappings":"AAYA,cAAc,mBAAmB,CAAA"}
@@ -0,0 +1,13 @@
1
+ // Host JSX factory for plugin scenes: re-exports the TUI's OWN
2
+ // react/jsx-runtime, so JSX a plugin compiles against this subpath produces
3
+ // React 19 elements (`Symbol.for('react.transitional.element')`) — the only
4
+ // element flavor this app's reconciler accepts. A plugin bundling its own
5
+ // older React emits `Symbol.for('react.element')` and dies on first render.
6
+ //
7
+ // Plugin tsconfig:
8
+ // "jsx": "react-jsx",
9
+ // "jsxImportSource": "@deepseek-harness-tui/dsh-tui"
10
+ //
11
+ // Hooks still come from the scene props' injected `React` — this module
12
+ // covers element creation only.
13
+ export * from 'react/jsx-runtime';
@@ -61,8 +61,16 @@ export declare function recordedModelRoute(events: readonly {
61
61
  * Best-effort combination check (issue #67): when the llm runtime advertises
62
62
  * a non-empty catalog for the route's provider and the model is not in it,
63
63
  * reject the whole route in favor of `fallback` so a stale persisted choice
64
- * surfaces at startup instead of as a server-side model-name error. Any
65
- * uncertainty — no llm service, a failed or empty catalog — trusts the route.
64
+ * surfaces at startup instead of as a server-side model-name error.
65
+ *
66
+ * Uncertainty trusts the route — no llm service, an empty catalog, a catalog
67
+ * read that failed for transport or adapter-internal reasons. An unregistered
68
+ * PROVIDER is not uncertainty: no adapter owns the name, so the route is
69
+ * proven unusable and falls back exactly like an unknown model does. Trusting
70
+ * it does not keep startup alive either — agent creation dies a few frames
71
+ * later inside the preset setup hook, reported as `refusing to compose an
72
+ * unscoped context`, which names neither the provider nor the stale
73
+ * preference that chose it.
66
74
  * @param llm - The llm runtime seam, when mounted.
67
75
  * @param route - The resolved route to check.
68
76
  * @param fallback - Route to adopt when the check rejects.
@@ -1 +1 @@
1
- {"version":3,"file":"modelRoute.d.ts","sourceRoot":"","sources":["../../src/modelRoute.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,gCAAgC;AAChC,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wHAAwH;AACxH,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAA;AAID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,GAAG,SAAS,CAI5G;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EACjD,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,QAAQ,GAAE,UAAgC,GACzC,UAAU,CAKZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,GAClD,UAAU,GAAG,SAAS,CAWxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE;IAAE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAA;CAAE,GAAG,SAAS,EACrF,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,UAAgC,GACzC,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,CAUvD"}
1
+ {"version":3,"file":"modelRoute.d.ts","sourceRoot":"","sources":["../../src/modelRoute.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEhD,gCAAgC;AAChC,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wHAAwH;AACxH,eAAO,MAAM,mBAAmB,EAAE,UAGjC,CAAA;AAID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,GAAG,SAAS,CAI5G;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EACjD,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,QAAQ,GAAE,UAAgC,GACzC,UAAU,CAKZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EAAE,GAClD,UAAU,GAAG,SAAS,CAWxB;AAyBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE;IAAE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAA;CAAE,GAAG,SAAS,EACrF,KAAK,EAAE,UAAU,EACjB,QAAQ,GAAE,UAAgC,GACzC,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,CAWvD"}
@@ -69,12 +69,39 @@ export function recordedModelRoute(events) {
69
69
  }
70
70
  return undefined;
71
71
  }
72
+ /**
73
+ * `LlmError` code the adapter registry raises for a provider nobody
74
+ * registered. Matched on the code rather than `instanceof LlmError`: a profile
75
+ * that resolves two copies of the llm package would fail the identity check
76
+ * while the code stays stable, and this module deliberately carries no llm
77
+ * import.
78
+ */
79
+ const NO_ADAPTER_CODE = 'NO_ADAPTER';
80
+ /**
81
+ * Whether a catalog read failed because the provider itself is unregistered,
82
+ * as opposed to a transport or adapter-internal failure.
83
+ * @param error - The value `listModels` rejected with.
84
+ * @returns True when no adapter owns the queried provider.
85
+ */
86
+ function isUnregisteredProvider(error) {
87
+ return (typeof error === 'object'
88
+ && error !== null
89
+ && error.code === NO_ADAPTER_CODE);
90
+ }
72
91
  /**
73
92
  * Best-effort combination check (issue #67): when the llm runtime advertises
74
93
  * a non-empty catalog for the route's provider and the model is not in it,
75
94
  * reject the whole route in favor of `fallback` so a stale persisted choice
76
- * surfaces at startup instead of as a server-side model-name error. Any
77
- * uncertainty — no llm service, a failed or empty catalog — trusts the route.
95
+ * surfaces at startup instead of as a server-side model-name error.
96
+ *
97
+ * Uncertainty trusts the route — no llm service, an empty catalog, a catalog
98
+ * read that failed for transport or adapter-internal reasons. An unregistered
99
+ * PROVIDER is not uncertainty: no adapter owns the name, so the route is
100
+ * proven unusable and falls back exactly like an unknown model does. Trusting
101
+ * it does not keep startup alive either — agent creation dies a few frames
102
+ * later inside the preset setup hook, reported as `refusing to compose an
103
+ * unscoped context`, which names neither the provider nor the stale
104
+ * preference that chose it.
78
105
  * @param llm - The llm runtime seam, when mounted.
79
106
  * @param route - The resolved route to check.
80
107
  * @param fallback - Route to adopt when the check rejects.
@@ -87,8 +114,10 @@ export async function validateModelRoute(llm, route, fallback = DEFAULT_MODEL_RO
87
114
  try {
88
115
  models = await llm.listModels(route.provider);
89
116
  }
90
- catch {
91
- return { route };
117
+ catch (error) {
118
+ if (!isUnregisteredProvider(error))
119
+ return { route };
120
+ return { route: fallback, rejected: route };
92
121
  }
93
122
  if (models.length === 0 || models.some(model => model.id === route.model))
94
123
  return { route };
@@ -0,0 +1,3 @@
1
+ export * from './dsh-adapter/scenes.js';
2
+ export { default } from './dsh-adapter/scenes.js';
3
+ //# sourceMappingURL=scenes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenes.d.ts","sourceRoot":"","sources":["../../src/scenes.ts"],"names":[],"mappings":"AAEA,cAAc,yBAAyB,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA"}
@@ -0,0 +1,4 @@
1
+ // Re-export shim: the Cordis-backed implementation lives behind the adapter
2
+ // boundary so UI consumers never import official @deepseek-ai/* packages.
3
+ export * from './dsh-adapter/scenes.js';
4
+ export { default } from './dsh-adapter/scenes.js';
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB,OAAO,EAAqB,KAAK,OAAO,EAAsD,MAAM,2BAA2B,CAAA;AAC/H,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AA0H3D,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,aAAa,EACb,aAAa,EACb,MAAM,EACN,QAAQ,EACR,UAAkB,EAClB,cAAc,EAAE,kBAAkB,GACnC,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,aAAa,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,qBA02DA"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAqB,KAAK,OAAO,EAAsE,MAAM,2BAA2B,CAAA;AAC/I,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AA6H3D,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,aAAa,EACb,aAAa,EACb,MAAM,EACN,QAAQ,EACR,UAAkB,EAClB,cAAc,EAAE,kBAAkB,GACnC,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,aAAa,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,qBAq9DA"}