@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.
- package/README.md +2 -2
- package/bin/dsh-tui.js +19 -3
- package/cordis.patch.yml +24 -11
- package/cordis.yml +9 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +2 -1
- package/lib/types/components/MessageList.d.ts +3 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +4 -4
- package/lib/types/components/PluginSceneBoundary.d.ts +31 -0
- package/lib/types/components/PluginSceneBoundary.d.ts.map +1 -0
- package/lib/types/components/PluginSceneBoundary.js +29 -0
- package/lib/types/components/SkillsPicker.d.ts +18 -0
- package/lib/types/components/SkillsPicker.d.ts.map +1 -0
- package/lib/types/components/SkillsPicker.js +50 -0
- package/lib/types/components/SplitDiffView.d.ts +28 -0
- package/lib/types/components/SplitDiffView.d.ts.map +1 -0
- package/lib/types/components/SplitDiffView.js +417 -0
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +3 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +17 -7
- package/lib/types/dsh-adapter/channel.d.ts +68 -0
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +120 -5
- package/lib/types/dsh-adapter/contract.d.ts +1 -1
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +1 -0
- package/lib/types/dsh-adapter/index.d.ts +4 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +1 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +59 -0
- package/lib/types/dsh-adapter/promptDebug.d.ts +10 -0
- package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -0
- package/lib/types/dsh-adapter/promptDebug.js +148 -0
- package/lib/types/dsh-adapter/scenes.d.ts +75 -0
- package/lib/types/dsh-adapter/scenes.d.ts.map +1 -0
- package/lib/types/dsh-adapter/scenes.js +78 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts +116 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -0
- package/lib/types/dsh-adapter/settings-sections.js +63 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts +128 -0
- package/lib/types/dsh-adapter/settingsEditor.d.ts.map +1 -0
- package/lib/types/dsh-adapter/settingsEditor.js +244 -0
- package/lib/types/i18n.d.ts +137 -10
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +36 -3
- package/lib/types/jsx-runtime.d.ts +2 -0
- package/lib/types/jsx-runtime.d.ts.map +1 -0
- package/lib/types/jsx-runtime.js +13 -0
- package/lib/types/modelRoute.d.ts +10 -2
- package/lib/types/modelRoute.d.ts.map +1 -1
- package/lib/types/modelRoute.js +33 -4
- package/lib/types/scenes.d.ts +3 -0
- package/lib/types/scenes.d.ts.map +1 -0
- package/lib/types/scenes.js +4 -0
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +107 -11
- package/lib/types/screens/Settings.d.ts +20 -0
- package/lib/types/screens/Settings.d.ts.map +1 -0
- package/lib/types/screens/Settings.js +333 -0
- package/lib/types/screens/StatusLine.js +1 -1
- package/lib/types/settings-sections.d.ts +3 -0
- package/lib/types/settings-sections.d.ts.map +1 -0
- package/lib/types/settings-sections.js +4 -0
- package/lib/types/theme.d.ts +12 -0
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +36 -0
- 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
|
+
}
|
package/lib/types/i18n.d.ts
CHANGED
|
@@ -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-
|
|
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 并重启";
|
package/lib/types/i18n.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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
|
|
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-
|
|
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 @@
|
|
|
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.
|
|
65
|
-
*
|
|
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;
|
|
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"}
|
package/lib/types/modelRoute.js
CHANGED
|
@@ -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.
|
|
77
|
-
*
|
|
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
|
-
|
|
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 @@
|
|
|
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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
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"}
|