@morlay/dsh-session-mode 0.0.14 → 0.0.16
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 +20 -7
- package/dist/client.cjs +4576 -3901
- package/dist/client.d.cts +41 -43
- package/dist/client.d.mts +41 -43
- package/dist/client.mjs +952 -1233
- package/dist/index.cjs +748 -70
- package/dist/index.d.mts +26 -55
- package/dist/index.mjs +742 -65
- package/dist/mode-config-CiQfAy4G.mjs +99 -0
- package/dist/mode-config-DOQ1VjcM.cjs +225 -0
- package/dist/rows-BT6Fpq-E.cjs +99 -0
- package/dist/rows-DnHpp-Ak.mjs +82 -0
- package/dist/rows.cjs +3 -86
- package/dist/rows.d.mts +31 -1
- package/dist/rows.mjs +1 -86
- package/dist/shared-R2j0SuWy.d.mts +118 -0
- package/package.json +70 -56
- package/src/client/SessionModePanel.tsx +803 -0
- package/src/client/api.ts +125 -1
- package/src/client/hints.ts +41 -0
- package/src/client/index.ts +71 -55
- package/src/client/locales.ts +80 -0
- package/src/index.ts +581 -20
- package/src/mode-config.ts +104 -0
- package/src/mode-sources.ts +4 -4
- package/src/mode-store.ts +53 -0
- package/src/modes.ts +23 -28
- package/src/per-agent.ts +53 -0
- package/src/ptc-describe.ts +97 -0
- package/src/ptc-sdk.ts +90 -0
- package/src/rows.ts +4 -8
- package/src/scope.ts +369 -71
- package/src/session-mode-store.ts +20 -0
- package/src/shared.ts +128 -3
- package/src/system-remainder.ts +39 -0
- package/src/tool-groups.ts +19 -0
- package/dist/shared-DO1nZbJ0.cjs +0 -64
- package/dist/shared-vjlwxiMt.mjs +0 -4
- package/src/client/BundleConfigPage.tsx +0 -631
- package/src/client/bundle-config.ts +0 -701
package/dist/client.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import "@morlay/dsh-tool-guidance/groups";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Button, IconAgentPresetOutlineRegular, IconChevronDownOutlineRegular, Input, Menu, Modal, MultilineField, Row, SeatButton, SegmentedControl, Stack, Switch, Text } from "@morlay/dsh-client-ui-primitives";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
// src/catalog-remote.ts
|
|
6
6
|
const CATALOG_NS = "sessionModeCatalog";
|
|
@@ -18,11 +18,20 @@ const SESSION_MODE_CATALOG_REMOTE = {
|
|
|
18
18
|
}]
|
|
19
19
|
};
|
|
20
20
|
// src/shared.ts
|
|
21
|
-
const SESSION_MODE_PATH = "/session-mode";
|
|
22
|
-
const POLICY_NAMES = [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const SESSION_MODE_PATH = "/api/morlay/v1/session-mode";
|
|
22
|
+
const POLICY_NAMES = [
|
|
23
|
+
"fs/write-intent",
|
|
24
|
+
"fs/edit-intent",
|
|
25
|
+
"fs/delete-intent"
|
|
26
|
+
];
|
|
27
|
+
function groupRef(group) {
|
|
28
|
+
return group.startsWith("mcp:") ? group : `group:${group}`;
|
|
29
|
+
}
|
|
30
|
+
// src/client/hints.ts
|
|
31
|
+
const SESSION_MODE_NS = "session-mode";
|
|
32
|
+
function unwrapService(value) {
|
|
33
|
+
if (value === void 0) return void 0;
|
|
34
|
+
return value[Symbol.for("cordis.original")] ?? value;
|
|
26
35
|
}
|
|
27
36
|
function remoteNamespace(scope, namespace) {
|
|
28
37
|
try {
|
|
@@ -79,1231 +88,849 @@ async function loadLlmProviders(scope) {
|
|
|
79
88
|
models: modelsOf(entry, forms)
|
|
80
89
|
}));
|
|
81
90
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
key: "rules",
|
|
111
|
-
labelKey: "group.rules"
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
key: "injections",
|
|
115
|
-
labelKey: "group.injections"
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
key: "model",
|
|
119
|
-
labelKey: "group.model"
|
|
120
|
-
}
|
|
121
|
-
];
|
|
122
|
-
const MODE_FIELDS = [
|
|
123
|
-
{
|
|
124
|
-
path: ["name"],
|
|
125
|
-
control: "text",
|
|
126
|
-
labelKey: "field.name",
|
|
127
|
-
hintKey: "hint.name",
|
|
128
|
-
group: "identity"
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
path: ["description"],
|
|
132
|
-
control: "text",
|
|
133
|
-
labelKey: "field.description",
|
|
134
|
-
hintKey: "hint.description",
|
|
135
|
-
group: "identity"
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
path: ["role"],
|
|
139
|
-
control: "roles",
|
|
140
|
-
labelKey: "field.role",
|
|
141
|
-
hintKey: "hint.role",
|
|
142
|
-
group: "identity"
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
path: ["presetsOnly"],
|
|
146
|
-
control: "tags",
|
|
147
|
-
labelKey: "field.presetsOnly",
|
|
148
|
-
hintKey: "hint.presetsOnly",
|
|
149
|
-
group: "identity"
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
path: ["persona", "prefix"],
|
|
153
|
-
control: "multiline",
|
|
154
|
-
labelKey: "field.prefix",
|
|
155
|
-
hintKey: "hint.prefix",
|
|
156
|
-
group: "persona"
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
path: ["persona", "suffix"],
|
|
160
|
-
control: "multiline",
|
|
161
|
-
labelKey: "field.suffix",
|
|
162
|
-
hintKey: "hint.suffix",
|
|
163
|
-
group: "persona"
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
path: ["allowTools"],
|
|
167
|
-
control: "tags",
|
|
168
|
-
labelKey: "field.allowTools",
|
|
169
|
-
hintKey: "hint.allowTools",
|
|
170
|
-
group: "tools"
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
path: ["denyTools"],
|
|
174
|
-
control: "tags",
|
|
175
|
-
labelKey: "field.denyTools",
|
|
176
|
-
hintKey: "hint.denyTools",
|
|
177
|
-
group: "tools"
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
path: ["allowSkills"],
|
|
181
|
-
control: "tags",
|
|
182
|
-
labelKey: "field.allowSkills",
|
|
183
|
-
hintKey: "hint.allowSkills",
|
|
184
|
-
group: "tools"
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
path: ["denySkills"],
|
|
188
|
-
control: "tags",
|
|
189
|
-
labelKey: "field.denySkills",
|
|
190
|
-
hintKey: "hint.denySkills",
|
|
191
|
-
group: "tools"
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
path: ["allowPolicies"],
|
|
195
|
-
control: "tags",
|
|
196
|
-
labelKey: "field.allowPolicies",
|
|
197
|
-
hintKey: "hint.allowPolicies",
|
|
198
|
-
group: "rules"
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
path: ["denyPolicies"],
|
|
202
|
-
control: "tags",
|
|
203
|
-
labelKey: "field.denyPolicies",
|
|
204
|
-
hintKey: "hint.denyPolicies",
|
|
205
|
-
group: "rules"
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
path: ["instructions"],
|
|
209
|
-
control: "switch",
|
|
210
|
-
labelKey: "field.instructions",
|
|
211
|
-
hintKey: "hint.instructions",
|
|
212
|
-
group: "injections"
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
path: ["skills"],
|
|
216
|
-
control: "tri",
|
|
217
|
-
labelKey: "field.skills",
|
|
218
|
-
hintKey: "hint.skills",
|
|
219
|
-
group: "injections"
|
|
220
|
-
},
|
|
221
|
-
{
|
|
222
|
-
path: ["runtimeContext"],
|
|
223
|
-
control: "switch",
|
|
224
|
-
labelKey: "field.runtimeContext",
|
|
225
|
-
hintKey: "hint.runtimeContext",
|
|
226
|
-
group: "injections"
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
path: ["defaultModel", "reasoningEffort"],
|
|
230
|
-
control: "text",
|
|
231
|
-
labelKey: "field.reasoningEffort",
|
|
232
|
-
hintKey: "hint.reasoningEffort",
|
|
233
|
-
group: "model"
|
|
234
|
-
}
|
|
235
|
-
];
|
|
236
|
-
function unwrapService(value) {
|
|
237
|
-
if (value === void 0) return void 0;
|
|
238
|
-
return value[Symbol.for("cordis.original")] ?? value;
|
|
239
|
-
}
|
|
240
|
-
function fieldKey(path) {
|
|
241
|
-
return JSON.stringify(path);
|
|
242
|
-
}
|
|
243
|
-
function createBundleConfigFace(ctx, t, hintsOf = () => void 0) {
|
|
244
|
-
const forms = ctx.configForms;
|
|
245
|
-
const hints = () => unwrapService(hintsOf());
|
|
246
|
-
const deps = () => ({
|
|
247
|
-
form: forms.get(SESSION_MODE_NS),
|
|
248
|
-
describe: forms.describe(),
|
|
249
|
-
rehydrate: (serialized) => ctx.settingsSchema.rehydrate(serialized),
|
|
250
|
-
validate: (schema, value) => validateSection(schema, value, t),
|
|
251
|
-
t: ctx.locale.bind("settings.schema-form"),
|
|
252
|
-
hints: {
|
|
253
|
-
keysFor: () => [],
|
|
254
|
-
textFor: (path) => hints()?.textFor("session-mode", path) ?? {},
|
|
255
|
-
selectFor: (path) => hints()?.selectFor(SESSION_MODE_NS, path),
|
|
256
|
-
sourceFor: (name) => hints()?.sourceFor(name)
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
const controller = new SchemaFormController(SESSION_MODE_NS, deps());
|
|
260
|
-
const face = controller.face();
|
|
261
|
-
const form = forms.get(SESSION_MODE_NS);
|
|
262
|
-
const statusStore = createSnapshotStore(form.getSnapshot().status);
|
|
263
|
-
const unsubscribeStatus = form.subscribe(() => {
|
|
264
|
-
statusStore.set(form.getSnapshot().status);
|
|
265
|
-
});
|
|
266
|
-
const modelsStore = createSnapshotStore({
|
|
267
|
-
status: "loading",
|
|
268
|
-
routes: []
|
|
269
|
-
});
|
|
270
|
-
const loadModels = async () => {
|
|
271
|
-
try {
|
|
272
|
-
const providers = await loadLlmProviders(ctx);
|
|
273
|
-
modelsStore.set({
|
|
274
|
-
status: "ready",
|
|
275
|
-
routes: routesOf(providers)
|
|
276
|
-
});
|
|
277
|
-
} catch {
|
|
278
|
-
modelsStore.set({
|
|
279
|
-
status: "error",
|
|
280
|
-
routes: []
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
const remote = ctx.get("remote");
|
|
285
|
-
const unsubscribeModels = typeof remote?.$on !== "function" ? [] : [remote.$on("llm/adapters-updated", () => {
|
|
286
|
-
loadModels();
|
|
287
|
-
}), remote.$on("settings/document-updated", () => {
|
|
288
|
-
loadModels();
|
|
289
|
-
})];
|
|
290
|
-
loadModels();
|
|
291
|
-
const diagnose = () => {
|
|
292
|
-
const view = forms.describe().getSnapshot().view;
|
|
293
|
-
const namespaces = view?.namespaces.map((row) => row.ns) ?? [];
|
|
294
|
-
const live = controller.face().hooks.schemaForm.getSnapshot();
|
|
295
|
-
const controllerSummary = `store configured=${String(live.configured)} walked=${String(live.walked.length)}`;
|
|
296
|
-
const own = view?.namespaces.find((row) => row.ns === SESSION_MODE_NS);
|
|
297
|
-
if (own === void 0) return {
|
|
298
|
-
namespaces,
|
|
299
|
-
controller: controllerSummary,
|
|
300
|
-
problem: `describe has no namespace "${SESSION_MODE_NS}"`
|
|
301
|
-
};
|
|
302
|
-
try {
|
|
303
|
-
const schema = ctx.settingsSchema.rehydrate(own.schema);
|
|
304
|
-
const renderable = projectRoot(own.schema, (serialized) => ctx.settingsSchema.rehydrate(serialized)) !== void 0;
|
|
305
|
-
let probe;
|
|
306
|
-
try {
|
|
307
|
-
const rebuilt = new SchemaFormController(SESSION_MODE_NS, deps());
|
|
308
|
-
probe = `probe configured=${String(rebuilt.face().hooks.schemaForm.getSnapshot().configured)}`;
|
|
309
|
-
rebuilt.dispose();
|
|
310
|
-
} catch (error) {
|
|
311
|
-
probe = `probe threw: ${error instanceof Error ? error.message : String(error)}`;
|
|
312
|
-
}
|
|
313
|
-
return {
|
|
314
|
-
namespaces,
|
|
315
|
-
controller: `${controllerSummary}; ${probe}`,
|
|
316
|
-
problem: renderable ? "renderable" : `schema root is "${String(schema.type)}"`
|
|
317
|
-
};
|
|
318
|
-
} catch (error) {
|
|
319
|
-
return {
|
|
320
|
-
namespaces,
|
|
321
|
-
controller: controllerSummary,
|
|
322
|
-
problem: `schema rehydrate failed: ${error instanceof Error ? error.message : String(error)}`
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
const resync = () => {
|
|
327
|
-
statusStore.set(form.getSnapshot().status);
|
|
328
|
-
controller.refresh();
|
|
329
|
-
};
|
|
330
|
-
return {
|
|
331
|
-
face: {
|
|
332
|
-
hooks: {
|
|
333
|
-
bundleConfig: face.hooks.schemaForm,
|
|
334
|
-
bundleStatus: statusStore,
|
|
335
|
-
bundleModels: modelsStore
|
|
336
|
-
},
|
|
337
|
-
diagnose,
|
|
338
|
-
resync,
|
|
339
|
-
set: (path, value) => {
|
|
340
|
-
face.set(path, value);
|
|
341
|
-
},
|
|
342
|
-
editText: (path, text) => {
|
|
343
|
-
if (text === "") face.clear(path);
|
|
344
|
-
else face.setText(path, text, (raw) => ({
|
|
345
|
-
kind: "value",
|
|
346
|
-
value: raw
|
|
347
|
-
}));
|
|
348
|
-
},
|
|
349
|
-
clear: (path) => {
|
|
350
|
-
face.clear(path);
|
|
351
|
-
},
|
|
352
|
-
appendItem: (path) => {
|
|
353
|
-
face.appendItem(path);
|
|
354
|
-
},
|
|
355
|
-
removeItem: (path, index) => {
|
|
356
|
-
face.removeItem(path, index);
|
|
357
|
-
},
|
|
358
|
-
addMode: (id) => {
|
|
359
|
-
const trimmed = id.trim();
|
|
360
|
-
if (trimmed === "") return;
|
|
361
|
-
face.addKey(["modes"], trimmed);
|
|
362
|
-
face.setText([
|
|
363
|
-
"modes",
|
|
364
|
-
trimmed,
|
|
365
|
-
"name"
|
|
366
|
-
], trimmed, (raw) => ({
|
|
367
|
-
kind: "value",
|
|
368
|
-
value: raw
|
|
369
|
-
}));
|
|
370
|
-
},
|
|
371
|
-
removeMode: (id) => {
|
|
372
|
-
if (PROTECTED_MODE_IDS.includes(id)) return;
|
|
373
|
-
face.removeKey(["modes"], id);
|
|
374
|
-
},
|
|
375
|
-
save: () => {
|
|
376
|
-
face.save();
|
|
377
|
-
},
|
|
378
|
-
discard: () => {
|
|
379
|
-
face.discard();
|
|
380
|
-
}
|
|
381
|
-
},
|
|
382
|
-
refresh: resync,
|
|
383
|
-
dispose: () => {
|
|
384
|
-
unsubscribeStatus();
|
|
385
|
-
for (const off of unsubscribeModels) off?.();
|
|
386
|
-
controller.dispose();
|
|
91
|
+
function applyDraft(config, draft, defaults) {
|
|
92
|
+
const view = { ...structuredClone(config) };
|
|
93
|
+
for (const [kind, allowKey, denyKey] of [
|
|
94
|
+
[
|
|
95
|
+
"tools",
|
|
96
|
+
"allowTools",
|
|
97
|
+
"denyTools"
|
|
98
|
+
],
|
|
99
|
+
[
|
|
100
|
+
"skills",
|
|
101
|
+
"allowSkills",
|
|
102
|
+
"denySkills"
|
|
103
|
+
],
|
|
104
|
+
[
|
|
105
|
+
"policies",
|
|
106
|
+
"allowPolicies",
|
|
107
|
+
"denyPolicies"
|
|
108
|
+
]
|
|
109
|
+
]) {
|
|
110
|
+
const allow = new Set(view[allowKey]);
|
|
111
|
+
const deny = new Set(view[denyKey]);
|
|
112
|
+
for (const [id, value] of Object.entries(draft.lists?.[kind] ?? {})) if (value === "deny") deny.add(id);
|
|
113
|
+
else if (value === "allow") {
|
|
114
|
+
deny.delete(id);
|
|
115
|
+
if (allow.size > 0) allow.add(id);
|
|
116
|
+
} else {
|
|
117
|
+
allow.delete(id);
|
|
118
|
+
deny.delete(id);
|
|
387
119
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
function validateSection(schema, value, t) {
|
|
391
|
-
try {
|
|
392
|
-
schema(value);
|
|
393
|
-
} catch (error) {
|
|
394
|
-
return failureOf(error);
|
|
120
|
+
view[allowKey] = [...allow];
|
|
121
|
+
view[denyKey] = [...deny];
|
|
395
122
|
}
|
|
396
|
-
|
|
123
|
+
if (draft.persona !== void 0 && draft.persona !== null) view["persona"] = draft.persona;
|
|
124
|
+
for (const [key, value] of Object.entries(draft.switches ?? {})) if (value !== null) view[key] = value;
|
|
125
|
+
else if (defaults !== void 0) view[key] = defaults[key];
|
|
126
|
+
return view;
|
|
397
127
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
if (ids.length === 0) return {
|
|
407
|
-
message: t("violation.modesEmpty"),
|
|
408
|
-
path: ["modes"]
|
|
409
|
-
};
|
|
410
|
-
const selected = Reflect.get(value, "default");
|
|
411
|
-
if (typeof selected === "string" && selected !== "" && !ids.includes(selected)) return {
|
|
412
|
-
message: t("violation.defaultMissing"),
|
|
413
|
-
path: ["default"]
|
|
414
|
-
};
|
|
415
|
-
for (const id of ids) {
|
|
416
|
-
const mode = Reflect.get(modes, id);
|
|
417
|
-
const model = typeof mode === "object" && mode !== null ? Reflect.get(mode, "defaultModel") : void 0;
|
|
418
|
-
if (typeof model !== "object" || model === null) continue;
|
|
419
|
-
const provider = Reflect.get(model, "provider");
|
|
420
|
-
const name = Reflect.get(model, "model");
|
|
421
|
-
if (provider === void 0 && name === void 0) continue;
|
|
422
|
-
if (typeof provider === "string" && provider !== "" && typeof name === "string" && name !== "") continue;
|
|
423
|
-
return {
|
|
424
|
-
message: t("violation.modelPair"),
|
|
425
|
-
path: [
|
|
426
|
-
"modes",
|
|
427
|
-
id,
|
|
428
|
-
"defaultModel"
|
|
429
|
-
]
|
|
430
|
-
};
|
|
431
|
-
}
|
|
128
|
+
// src/client/api.ts
|
|
129
|
+
async function fetchRoster() {
|
|
130
|
+
const response = await fetch(SESSION_MODE_PATH, {
|
|
131
|
+
method: "GET",
|
|
132
|
+
headers: { accept: "application/json" }
|
|
133
|
+
});
|
|
134
|
+
if (!response.ok) throw await failure(response);
|
|
135
|
+
return await response.json();
|
|
432
136
|
}
|
|
433
|
-
function
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
return {
|
|
440
|
-
available: state.available,
|
|
441
|
-
writable: state.writable,
|
|
442
|
-
dirty: state.dirty,
|
|
443
|
-
invalid: state.invalid,
|
|
444
|
-
saving: state.saving,
|
|
445
|
-
failed: state.failed,
|
|
446
|
-
configured: state.configured,
|
|
447
|
-
readiness: state.configured ? "ready" : status === "loading" ? "loading" : status === "ready" ? "unreadable" : "missing",
|
|
448
|
-
defaultMode: {
|
|
449
|
-
value: selected,
|
|
450
|
-
options: modes.map((id) => ({
|
|
451
|
-
value: id,
|
|
452
|
-
label: labelOf(id, read)
|
|
453
|
-
})),
|
|
454
|
-
invalid: invalidAt(["default"]) ?? (selected === "" || modes.includes(selected) ? void 0 : t("violation.defaultMissing"))
|
|
137
|
+
async function selectMode(sessionId, mode) {
|
|
138
|
+
const response = await fetch(SESSION_MODE_PATH, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: {
|
|
141
|
+
accept: "application/json",
|
|
142
|
+
"content-type": "application/json"
|
|
455
143
|
},
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
model: modelOf(id, read),
|
|
466
|
-
deletable: !PROTECTED_MODE_IDS.includes(id),
|
|
467
|
-
staged: MODE_FIELDS.some((spec) => read([
|
|
468
|
-
"modes",
|
|
469
|
-
id,
|
|
470
|
-
...spec.path
|
|
471
|
-
])?.staged === true),
|
|
472
|
-
groups: GROUPS.map((group) => ({
|
|
473
|
-
key: group.key,
|
|
474
|
-
label: t(group.labelKey),
|
|
475
|
-
fields: MODE_FIELDS.filter((spec) => spec.group === group.key).map((spec) => fieldView(id, spec, read, optionsAt, invalidAt, t))
|
|
476
|
-
}))
|
|
477
|
-
})),
|
|
478
|
-
violation: state.violation?.message
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
function labelOf(id, read) {
|
|
482
|
-
const name = textOf(read([
|
|
483
|
-
"modes",
|
|
484
|
-
id,
|
|
485
|
-
"name"
|
|
486
|
-
]));
|
|
487
|
-
return name === "" ? id : name;
|
|
488
|
-
}
|
|
489
|
-
function collectModeIds(state) {
|
|
490
|
-
const ids = [];
|
|
491
|
-
for (const item of state.walked) {
|
|
492
|
-
if (item.path.length < 2 || item.path[0] !== "modes") continue;
|
|
493
|
-
const id = item.path[1];
|
|
494
|
-
if (id === void 0 || ids.includes(id)) continue;
|
|
495
|
-
ids.push(id);
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
sessionId,
|
|
146
|
+
mode
|
|
147
|
+
})
|
|
148
|
+
});
|
|
149
|
+
const value = await response.json().catch(() => void 0);
|
|
150
|
+
if (!response.ok) {
|
|
151
|
+
const error = value?.["error"];
|
|
152
|
+
throw new Error(typeof error === "string" ? error : `HTTP ${String(response.status)}`);
|
|
496
153
|
}
|
|
497
|
-
return
|
|
154
|
+
return value?.mode ?? mode;
|
|
498
155
|
}
|
|
499
|
-
function
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
id,
|
|
503
|
-
"defaultModel"
|
|
504
|
-
]) === void 0) return void 0;
|
|
505
|
-
return {
|
|
506
|
-
provider: textOf(read([
|
|
507
|
-
"modes",
|
|
508
|
-
id,
|
|
509
|
-
"defaultModel",
|
|
510
|
-
"provider"
|
|
511
|
-
])),
|
|
512
|
-
model: textOf(read([
|
|
513
|
-
"modes",
|
|
514
|
-
id,
|
|
515
|
-
"defaultModel",
|
|
516
|
-
"model"
|
|
517
|
-
]))
|
|
518
|
-
};
|
|
156
|
+
async function failure(response) {
|
|
157
|
+
const value = await response.json().catch(() => void 0);
|
|
158
|
+
return new Error(typeof value?.error === "string" ? value.error : `HTTP ${String(response.status)}`);
|
|
519
159
|
}
|
|
520
|
-
function
|
|
521
|
-
const
|
|
522
|
-
"
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const common = {
|
|
528
|
-
key: fieldKey(path),
|
|
529
|
-
path,
|
|
530
|
-
control: spec.control,
|
|
531
|
-
label: t(spec.labelKey),
|
|
532
|
-
hint: t(spec.hintKey),
|
|
533
|
-
present: field !== void 0,
|
|
534
|
-
overridden: field?.overridden === true,
|
|
535
|
-
options: optionsAt(path),
|
|
536
|
-
invalid: invalidAt(path)
|
|
537
|
-
};
|
|
538
|
-
if (spec.control === "tags" || spec.control === "roles") return {
|
|
539
|
-
...common,
|
|
540
|
-
text: "",
|
|
541
|
-
value: arrayOf(field)
|
|
542
|
-
};
|
|
543
|
-
if (spec.control === "switch") return {
|
|
544
|
-
...common,
|
|
545
|
-
text: "",
|
|
546
|
-
value: field?.value === true
|
|
547
|
-
};
|
|
548
|
-
if (spec.control === "tri") return {
|
|
549
|
-
...common,
|
|
550
|
-
text: "",
|
|
551
|
-
value: triOf(field)
|
|
552
|
-
};
|
|
553
|
-
return {
|
|
554
|
-
...common,
|
|
555
|
-
text: textOf(field),
|
|
556
|
-
value: field?.value
|
|
557
|
-
};
|
|
160
|
+
async function fetchActive(sessionId) {
|
|
161
|
+
const response = await fetch(`${SESSION_MODE_PATH}?sessionId=${encodeURIComponent(sessionId)}`, {
|
|
162
|
+
method: "GET",
|
|
163
|
+
headers: { accept: "application/json" }
|
|
164
|
+
});
|
|
165
|
+
if (!response.ok) throw await failure(response);
|
|
166
|
+
return await response.json();
|
|
558
167
|
}
|
|
559
|
-
function
|
|
560
|
-
|
|
561
|
-
|
|
168
|
+
async function saveAsNewMode(sessionId, code, name) {
|
|
169
|
+
const response = await fetch(SESSION_MODE_PATH, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: {
|
|
172
|
+
accept: "application/json",
|
|
173
|
+
"content-type": "application/json"
|
|
174
|
+
},
|
|
175
|
+
body: JSON.stringify({
|
|
176
|
+
sessionId,
|
|
177
|
+
saveAsNewMode: {
|
|
178
|
+
code,
|
|
179
|
+
name
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
});
|
|
183
|
+
if (!response.ok) throw await failure(response);
|
|
562
184
|
}
|
|
563
|
-
function
|
|
564
|
-
const
|
|
565
|
-
"
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
"defaultModel",
|
|
578
|
-
"provider"
|
|
579
|
-
])?.value;
|
|
580
|
-
const model = read([
|
|
581
|
-
"modes",
|
|
582
|
-
id,
|
|
583
|
-
"defaultModel",
|
|
584
|
-
"model"
|
|
585
|
-
])?.value;
|
|
586
|
-
return `${allowed.length > 0 ? t("summary.tools", { n: String(allowed.length) }) : denied.length > 0 ? t("summary.denyTools", { n: String(denied.length) }) : t("summary.allTools")} · ${typeof model === "string" && model !== "" ? `${typeof provider === "string" && provider !== "" ? `${provider} / ` : ""}${model}` : t("summary.globalModel")}`;
|
|
185
|
+
async function deleteMode(sessionId) {
|
|
186
|
+
const response = await fetch(SESSION_MODE_PATH, {
|
|
187
|
+
method: "POST",
|
|
188
|
+
headers: {
|
|
189
|
+
accept: "application/json",
|
|
190
|
+
"content-type": "application/json"
|
|
191
|
+
},
|
|
192
|
+
body: JSON.stringify({
|
|
193
|
+
sessionId,
|
|
194
|
+
deleteMode: true
|
|
195
|
+
})
|
|
196
|
+
});
|
|
197
|
+
if (!response.ok) throw await failure(response);
|
|
198
|
+
return await response.json();
|
|
587
199
|
}
|
|
588
|
-
function
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
200
|
+
async function applySessionConfig(sessionId, config) {
|
|
201
|
+
const response = await fetch(SESSION_MODE_PATH, {
|
|
202
|
+
method: "POST",
|
|
203
|
+
headers: {
|
|
204
|
+
accept: "application/json",
|
|
205
|
+
"content-type": "application/json"
|
|
206
|
+
},
|
|
207
|
+
body: JSON.stringify({
|
|
208
|
+
sessionId,
|
|
209
|
+
applyConfig: { config }
|
|
210
|
+
})
|
|
211
|
+
});
|
|
212
|
+
if (!response.ok) throw await failure(response);
|
|
213
|
+
return await response.json();
|
|
592
214
|
}
|
|
593
|
-
function
|
|
594
|
-
const
|
|
595
|
-
|
|
215
|
+
async function saveConfigDraft(sessionId, config) {
|
|
216
|
+
const response = await fetch(SESSION_MODE_PATH, {
|
|
217
|
+
method: "POST",
|
|
218
|
+
headers: {
|
|
219
|
+
accept: "application/json",
|
|
220
|
+
"content-type": "application/json"
|
|
221
|
+
},
|
|
222
|
+
body: JSON.stringify({
|
|
223
|
+
sessionId,
|
|
224
|
+
saveConfig: { config }
|
|
225
|
+
})
|
|
226
|
+
});
|
|
227
|
+
if (!response.ok) throw await failure(response);
|
|
228
|
+
return await response.json();
|
|
596
229
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
230
|
+
// src/client/SessionModePanel.tsx
|
|
231
|
+
const PANEL_STYLE = {
|
|
232
|
+
height: "100%",
|
|
233
|
+
overflowY: "auto",
|
|
234
|
+
boxSizing: "border-box",
|
|
235
|
+
padding: "20px 24px 40px"
|
|
236
|
+
};
|
|
237
|
+
function Section({ title, open, onToggle, children }) {
|
|
238
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
239
|
+
"data-role": "section",
|
|
240
|
+
"data-section": title,
|
|
241
|
+
style: { borderTop: "1px solid var(--dsw-alias-border-l1)" },
|
|
242
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
243
|
+
type: "button",
|
|
244
|
+
"data-role": "section-heading",
|
|
245
|
+
onClick: onToggle,
|
|
246
|
+
style: {
|
|
247
|
+
width: "100%",
|
|
248
|
+
display: "flex",
|
|
249
|
+
alignItems: "center",
|
|
250
|
+
justifyContent: "space-between",
|
|
251
|
+
background: "none",
|
|
252
|
+
border: 0,
|
|
253
|
+
paddingBlock: 12,
|
|
254
|
+
paddingInline: 0,
|
|
255
|
+
font: "inherit",
|
|
256
|
+
fontSize: 13,
|
|
257
|
+
fontWeight: 500,
|
|
258
|
+
color: "inherit",
|
|
259
|
+
cursor: "pointer"
|
|
260
|
+
},
|
|
261
|
+
children: [/* @__PURE__ */ jsx("span", { children: title }), /* @__PURE__ */ jsx("span", {
|
|
262
|
+
"data-role": "section-icon",
|
|
263
|
+
style: {
|
|
264
|
+
display: "inline-flex",
|
|
265
|
+
alignItems: "center",
|
|
266
|
+
justifyContent: "center",
|
|
267
|
+
width: 16,
|
|
268
|
+
height: 16,
|
|
269
|
+
fontSize: 14,
|
|
270
|
+
lineHeight: 1,
|
|
271
|
+
color: "var(--dsw-alias-label-tertiary)"
|
|
272
|
+
},
|
|
273
|
+
children: open ? "−" : "+"
|
|
274
|
+
})]
|
|
275
|
+
}), open ? /* @__PURE__ */ jsx("div", {
|
|
276
|
+
style: { padding: "0 0 8px" },
|
|
277
|
+
children: /* @__PURE__ */ jsx(Stack, {
|
|
278
|
+
gap: 6,
|
|
279
|
+
children
|
|
280
|
+
})
|
|
281
|
+
}) : null]
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
function TriStateRow({ id, description, value, indent = 0, t, onChange, onLabelClick }) {
|
|
285
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
286
|
+
"data-role": "tri-state",
|
|
287
|
+
"data-id": id,
|
|
288
|
+
style: {
|
|
289
|
+
display: "flex",
|
|
290
|
+
flexWrap: "wrap",
|
|
291
|
+
alignItems: "center",
|
|
292
|
+
justifyContent: "space-between",
|
|
293
|
+
gap: 8,
|
|
294
|
+
padding: "6px 0",
|
|
295
|
+
paddingLeft: indent
|
|
296
|
+
},
|
|
297
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
298
|
+
style: {
|
|
299
|
+
display: "flex",
|
|
300
|
+
flexDirection: "column",
|
|
301
|
+
gap: 2,
|
|
302
|
+
minWidth: 0,
|
|
303
|
+
flex: "1 1 140px"
|
|
304
|
+
},
|
|
305
|
+
children: [onLabelClick === void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
306
|
+
size: "sm",
|
|
307
|
+
children: id
|
|
308
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
309
|
+
type: "button",
|
|
310
|
+
onClick: onLabelClick,
|
|
311
|
+
style: {
|
|
312
|
+
background: "none",
|
|
313
|
+
border: 0,
|
|
314
|
+
padding: 0,
|
|
315
|
+
font: "inherit",
|
|
316
|
+
fontSize: 13,
|
|
317
|
+
color: "inherit",
|
|
318
|
+
textAlign: "left",
|
|
319
|
+
cursor: "pointer"
|
|
320
|
+
},
|
|
321
|
+
children: id
|
|
322
|
+
}), description === void 0 || description.length === 0 ? null : /* @__PURE__ */ jsx(Text, {
|
|
637
323
|
size: "sm",
|
|
638
324
|
tone: "tertiary",
|
|
639
|
-
|
|
640
|
-
"data-problem": diagnosis.problem,
|
|
641
|
-
"data-controller": diagnosis.controller,
|
|
642
|
-
children: message
|
|
643
|
-
}), state.readiness !== "unreadable" ? null : /* @__PURE__ */ jsx(Text, {
|
|
644
|
-
as: "p",
|
|
645
|
-
size: "xs",
|
|
646
|
-
tone: "tertiary",
|
|
647
|
-
mono: true,
|
|
648
|
-
"data-diagnosis": "true",
|
|
649
|
-
children: `${diagnosis.problem} · ${diagnosis.controller} · namespaces=[${diagnosis.namespaces.join(", ")}]`
|
|
325
|
+
children: description
|
|
650
326
|
})]
|
|
327
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
328
|
+
style: {
|
|
329
|
+
maxWidth: "100%",
|
|
330
|
+
marginLeft: "auto"
|
|
331
|
+
},
|
|
332
|
+
children: /* @__PURE__ */ jsx(SegmentedControl, {
|
|
333
|
+
id: `tri-${id.replace(/[^a-zA-Z0-9_-]+/gu, "-")}`,
|
|
334
|
+
label: id,
|
|
335
|
+
value,
|
|
336
|
+
options: value === "mixed" ? [
|
|
337
|
+
{
|
|
338
|
+
value: "mixed",
|
|
339
|
+
label: t("stateMixed")
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
value: "unset",
|
|
343
|
+
label: t("stateDefault")
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
value: "allow",
|
|
347
|
+
label: t("stateAllow")
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
value: "deny",
|
|
351
|
+
label: t("stateDeny")
|
|
352
|
+
}
|
|
353
|
+
] : [
|
|
354
|
+
{
|
|
355
|
+
value: "unset",
|
|
356
|
+
label: t("stateDefault")
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
value: "allow",
|
|
360
|
+
label: t("stateAllow")
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
value: "deny",
|
|
364
|
+
label: t("stateDeny")
|
|
365
|
+
}
|
|
366
|
+
],
|
|
367
|
+
onChange: (next) => {
|
|
368
|
+
if (next === "mixed") return;
|
|
369
|
+
onChange(next);
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
})]
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
function SessionModePanel({ sessionId, useSessions, t }) {
|
|
376
|
+
const mode = useSessions((state) => {
|
|
377
|
+
const value = sessionId === void 0 ? void 0 : state.byId[sessionId]?.projectionValues?.sessionMode;
|
|
378
|
+
return typeof value === "string" ? value : void 0;
|
|
379
|
+
});
|
|
380
|
+
const preset = useSessions((state) => {
|
|
381
|
+
const value = sessionId === void 0 ? void 0 : state.byId[sessionId]?.projectionValues?.agentPreset;
|
|
382
|
+
return typeof value === "string" ? value : void 0;
|
|
383
|
+
});
|
|
384
|
+
const [load, setLoad] = useState({ kind: "loading" });
|
|
385
|
+
const [notice, setNotice] = useState(null);
|
|
386
|
+
const [draft, setDraft] = useState({
|
|
387
|
+
switches: {},
|
|
388
|
+
items: {
|
|
389
|
+
tools: {},
|
|
390
|
+
skills: {},
|
|
391
|
+
policies: {}
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
const [dialog, setDialog] = useState(null);
|
|
395
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
396
|
+
const [code, setCode] = useState("");
|
|
397
|
+
const [name, setName] = useState("");
|
|
398
|
+
const [openSection, setOpenSection] = useState("general");
|
|
399
|
+
const [openGroups, setOpenGroups] = useState(/* @__PURE__ */ new Set());
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
if (sessionId === void 0) {
|
|
402
|
+
setLoad({
|
|
403
|
+
kind: "error",
|
|
404
|
+
message: t("panelNoSession")
|
|
405
|
+
});
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
let cancelled = false;
|
|
409
|
+
setLoad({ kind: "loading" });
|
|
410
|
+
fetchActive(sessionId).then((value) => {
|
|
411
|
+
if (!cancelled) setLoad({
|
|
412
|
+
kind: "ready",
|
|
413
|
+
value
|
|
414
|
+
});
|
|
415
|
+
}).catch((error) => {
|
|
416
|
+
if (!cancelled) setLoad({
|
|
417
|
+
kind: "error",
|
|
418
|
+
message: error instanceof Error ? error.message : String(error)
|
|
419
|
+
});
|
|
651
420
|
});
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
421
|
+
return () => {
|
|
422
|
+
cancelled = true;
|
|
423
|
+
};
|
|
424
|
+
}, [
|
|
425
|
+
sessionId,
|
|
426
|
+
mode,
|
|
427
|
+
preset
|
|
428
|
+
]);
|
|
429
|
+
if (load.kind === "loading") return /* @__PURE__ */ jsx("div", {
|
|
430
|
+
"data-role": "session-mode-panel",
|
|
431
|
+
style: PANEL_STYLE,
|
|
432
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
433
|
+
size: "sm",
|
|
434
|
+
tone: "tertiary",
|
|
435
|
+
children: t("loading")
|
|
436
|
+
})
|
|
437
|
+
});
|
|
438
|
+
if (load.kind === "error") return /* @__PURE__ */ jsx("div", {
|
|
439
|
+
"data-role": "session-mode-panel",
|
|
440
|
+
style: PANEL_STYLE,
|
|
441
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
442
|
+
as: "p",
|
|
443
|
+
size: "sm",
|
|
444
|
+
tone: "danger",
|
|
445
|
+
children: load.message
|
|
446
|
+
})
|
|
447
|
+
});
|
|
448
|
+
const active = load?.kind === "ready" ? load.value : void 0;
|
|
449
|
+
if (active === void 0 || active === null) return /* @__PURE__ */ jsx("div", {
|
|
450
|
+
"data-role": "session-mode-panel",
|
|
451
|
+
style: PANEL_STYLE,
|
|
452
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
453
|
+
size: "sm",
|
|
454
|
+
tone: "tertiary",
|
|
455
|
+
children: t("loading")
|
|
456
|
+
})
|
|
457
|
+
});
|
|
458
|
+
const effective = active.session ?? active.config;
|
|
459
|
+
const overridden = new Set(active.overriddenFields ?? (active.session === void 0 ? [] : [
|
|
460
|
+
"persona",
|
|
461
|
+
"instructions",
|
|
462
|
+
"skills",
|
|
463
|
+
"runtimeContext"
|
|
464
|
+
]));
|
|
465
|
+
const source = (field) => overridden.has(field) ? t("fromSession") : t("fromMode");
|
|
466
|
+
const configValue = (entry) => {
|
|
467
|
+
if (entry.group !== void 0 && effective.denyTools.includes(groupRef(entry.group))) return "deny";
|
|
468
|
+
return entry.fallback === void 0 || entry.fallback === "default" ? "unset" : entry.fallback;
|
|
659
469
|
};
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
470
|
+
const viewConfig = () => applyDraft(effective, {
|
|
471
|
+
lists: {
|
|
472
|
+
tools: draft.items["tools"] ?? {},
|
|
473
|
+
skills: draft.items["skills"] ?? {},
|
|
474
|
+
policies: draft.items["policies"] ?? {}
|
|
475
|
+
},
|
|
476
|
+
...draft.persona === void 0 ? {} : { persona: draft.persona },
|
|
477
|
+
switches: draft.switches
|
|
478
|
+
}, active.config);
|
|
479
|
+
const commit = (target) => {
|
|
480
|
+
if (sessionId === void 0) return;
|
|
481
|
+
const config = viewConfig();
|
|
482
|
+
(target === "session" ? applySessionConfig(sessionId, config) : saveConfigDraft(sessionId, config)).then((value) => {
|
|
483
|
+
setLoad({
|
|
484
|
+
kind: "ready",
|
|
485
|
+
value
|
|
486
|
+
});
|
|
487
|
+
setDraft({
|
|
488
|
+
switches: {},
|
|
489
|
+
items: {
|
|
490
|
+
tools: {},
|
|
491
|
+
skills: {},
|
|
492
|
+
policies: {}
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
setNotice(t(target === "session" ? "applied" : "saved"));
|
|
496
|
+
}).catch((error) => setNotice(`${t("saveFailed")}: ${String(error)}`));
|
|
497
|
+
};
|
|
498
|
+
const toolGroups = /* @__PURE__ */ new Map();
|
|
499
|
+
for (const entry of active.catalog.tools) {
|
|
500
|
+
const group = entry.group ?? "其它";
|
|
501
|
+
const list = toolGroups.get(group);
|
|
502
|
+
if (list === void 0) toolGroups.set(group, [entry]);
|
|
503
|
+
else list.push(entry);
|
|
504
|
+
}
|
|
505
|
+
const toggleGroup = (group) => {
|
|
506
|
+
setOpenGroups((current) => {
|
|
507
|
+
const next = new Set(current);
|
|
508
|
+
if (next.has(group)) next.delete(group);
|
|
509
|
+
else next.add(group);
|
|
665
510
|
return next;
|
|
666
511
|
});
|
|
667
512
|
};
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
513
|
+
const section = (key, title, children) => /* @__PURE__ */ jsx(Section, {
|
|
514
|
+
title,
|
|
515
|
+
open: openSection === key,
|
|
516
|
+
onToggle: () => {
|
|
517
|
+
setOpenSection((current) => current === key ? null : key);
|
|
518
|
+
},
|
|
519
|
+
children
|
|
520
|
+
});
|
|
521
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
522
|
+
"data-role": "session-mode-panel",
|
|
523
|
+
style: PANEL_STYLE,
|
|
673
524
|
children: [
|
|
674
|
-
/* @__PURE__ */ jsxs(
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
options: state.defaultMode.options.map((option) => ({
|
|
693
|
-
value: String(option.value),
|
|
694
|
-
...option.label === void 0 ? {} : { label: option.label }
|
|
695
|
-
})),
|
|
696
|
-
searchLabel: t("select.search"),
|
|
697
|
-
noMatchLabel: t("select.noMatch"),
|
|
698
|
-
disabled: !state.writable,
|
|
699
|
-
onSelect: (next) => {
|
|
700
|
-
if (next === "") face.clear(["default"]);
|
|
701
|
-
else face.set(["default"], next);
|
|
702
|
-
}
|
|
703
|
-
})
|
|
704
|
-
})
|
|
705
|
-
}), /* @__PURE__ */ jsxs(Stack, {
|
|
706
|
-
as: "section",
|
|
707
|
-
gap: 8,
|
|
708
|
-
"data-section": "modes",
|
|
709
|
-
children: [/* @__PURE__ */ jsxs(Row, {
|
|
710
|
-
gap: 8,
|
|
711
|
-
justify: "between",
|
|
712
|
-
children: [/* @__PURE__ */ jsxs(Stack, {
|
|
713
|
-
gap: 2,
|
|
714
|
-
grow: true,
|
|
715
|
-
children: [/* @__PURE__ */ jsx(Text, {
|
|
716
|
-
as: "h4",
|
|
717
|
-
size: "md",
|
|
718
|
-
tone: "primary",
|
|
719
|
-
weight: "strong",
|
|
720
|
-
children: t("modes.label")
|
|
525
|
+
/* @__PURE__ */ jsxs("div", {
|
|
526
|
+
style: {
|
|
527
|
+
display: "flex",
|
|
528
|
+
flexDirection: "column",
|
|
529
|
+
gap: 48
|
|
530
|
+
},
|
|
531
|
+
children: [
|
|
532
|
+
/* @__PURE__ */ jsx("div", {
|
|
533
|
+
"data-role": "header",
|
|
534
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
535
|
+
gap: 4,
|
|
536
|
+
children: [/* @__PURE__ */ jsx(Row, {
|
|
537
|
+
gap: 6,
|
|
538
|
+
align: "center",
|
|
539
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
540
|
+
size: "lg",
|
|
541
|
+
children: active.mode.name
|
|
542
|
+
})
|
|
721
543
|
}), /* @__PURE__ */ jsx(Text, {
|
|
722
|
-
as: "p",
|
|
723
544
|
size: "sm",
|
|
724
545
|
tone: "tertiary",
|
|
725
|
-
children: t("
|
|
546
|
+
children: active.mode.description.length === 0 ? t("empty") : active.mode.description
|
|
726
547
|
})]
|
|
727
|
-
})
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
548
|
+
})
|
|
549
|
+
}),
|
|
550
|
+
/* @__PURE__ */ jsx("div", { style: { height: 12 } }),
|
|
551
|
+
/* @__PURE__ */ jsxs("div", {
|
|
552
|
+
"data-role": "content",
|
|
553
|
+
children: [
|
|
554
|
+
section("general", t("general"), /* @__PURE__ */ jsx(Fragment, { children: [
|
|
555
|
+
[
|
|
556
|
+
"instructions",
|
|
557
|
+
t("instructions"),
|
|
558
|
+
effective.instructions
|
|
559
|
+
],
|
|
560
|
+
[
|
|
561
|
+
"skills",
|
|
562
|
+
t("skillsCatalog"),
|
|
563
|
+
effective.skills
|
|
564
|
+
],
|
|
565
|
+
[
|
|
566
|
+
"runtimeContext",
|
|
567
|
+
t("runtimeContext"),
|
|
568
|
+
effective.runtimeContext
|
|
569
|
+
]
|
|
570
|
+
].map(([key, label, value]) => /* @__PURE__ */ jsxs("div", {
|
|
571
|
+
style: {
|
|
572
|
+
display: "flex",
|
|
573
|
+
flexWrap: "wrap",
|
|
574
|
+
alignItems: "center",
|
|
575
|
+
justifyContent: "space-between",
|
|
576
|
+
gap: 8,
|
|
577
|
+
padding: "4px 0"
|
|
578
|
+
},
|
|
579
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
580
|
+
style: {
|
|
581
|
+
display: "flex",
|
|
582
|
+
flexDirection: "column",
|
|
583
|
+
gap: 2
|
|
584
|
+
},
|
|
585
|
+
children: [/* @__PURE__ */ jsx(Text, {
|
|
586
|
+
size: "md",
|
|
587
|
+
children: label
|
|
588
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
589
|
+
size: "sm",
|
|
590
|
+
tone: "tertiary",
|
|
591
|
+
children: source(key)
|
|
592
|
+
})]
|
|
593
|
+
}), /* @__PURE__ */ jsxs(Row, {
|
|
594
|
+
gap: 6,
|
|
595
|
+
style: { marginLeft: "auto" },
|
|
596
|
+
children: [overridden.has(key) ? /* @__PURE__ */ jsx(Button, {
|
|
597
|
+
variant: "outline",
|
|
598
|
+
size: "sm",
|
|
599
|
+
onClick: () => {
|
|
600
|
+
setDraft((prev) => ({
|
|
601
|
+
...prev,
|
|
602
|
+
switches: {
|
|
603
|
+
...prev.switches,
|
|
604
|
+
[key]: null
|
|
605
|
+
}
|
|
606
|
+
}));
|
|
607
|
+
},
|
|
608
|
+
children: t("stateDefault")
|
|
609
|
+
}) : null, /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Switch, {
|
|
610
|
+
checked: draft.switches[key] ?? value,
|
|
611
|
+
label,
|
|
612
|
+
onChange: (next) => {
|
|
613
|
+
setDraft((prev) => ({
|
|
614
|
+
...prev,
|
|
615
|
+
switches: {
|
|
616
|
+
...prev.switches,
|
|
617
|
+
[key]: next
|
|
618
|
+
}
|
|
619
|
+
}));
|
|
620
|
+
}
|
|
621
|
+
}) })]
|
|
622
|
+
})]
|
|
623
|
+
}, key)) })),
|
|
624
|
+
section("persona", t("persona"), /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
625
|
+
/* @__PURE__ */ jsx(Text, {
|
|
626
|
+
size: "sm",
|
|
627
|
+
tone: "tertiary",
|
|
628
|
+
children: source("persona")
|
|
629
|
+
}),
|
|
630
|
+
/* @__PURE__ */ jsx(MultilineField, {
|
|
631
|
+
value: draft.persona?.prefix ?? effective.persona.prefix,
|
|
632
|
+
rows: 6,
|
|
633
|
+
"aria-label": t("persona"),
|
|
634
|
+
onChange: (event) => {
|
|
635
|
+
const prefix = event.currentTarget.value;
|
|
636
|
+
setDraft((prev) => ({
|
|
637
|
+
...prev,
|
|
638
|
+
persona: {
|
|
639
|
+
prefix,
|
|
640
|
+
suffix: prev.persona?.suffix ?? effective.persona.suffix
|
|
641
|
+
}
|
|
642
|
+
}));
|
|
643
|
+
}
|
|
644
|
+
}),
|
|
645
|
+
/* @__PURE__ */ jsx(MultilineField, {
|
|
646
|
+
value: draft.persona?.suffix ?? effective.persona.suffix,
|
|
647
|
+
rows: 3,
|
|
648
|
+
"aria-label": t("persona"),
|
|
649
|
+
onChange: (event) => {
|
|
650
|
+
const suffix = event.currentTarget.value;
|
|
651
|
+
setDraft((prev) => ({
|
|
652
|
+
...prev,
|
|
653
|
+
persona: {
|
|
654
|
+
prefix: prev.persona?.prefix ?? effective.persona.prefix,
|
|
655
|
+
suffix
|
|
656
|
+
}
|
|
657
|
+
}));
|
|
658
|
+
}
|
|
659
|
+
})
|
|
660
|
+
] })),
|
|
661
|
+
section("tools", `${t("tools")} (${String(active.catalog.tools.length)})`, /* @__PURE__ */ jsx(Fragment, { children: [...toolGroups].map(([group, entries]) => {
|
|
662
|
+
const groupDraft = draft.items["tools"]?.[groupRef(group)];
|
|
663
|
+
const values = groupDraft !== void 0 ? entries.map(() => groupDraft) : entries.map((entry) => draft.items["tools"]?.[entry.id] ?? entry.override ?? configValue(entry));
|
|
664
|
+
const groupValue = values.every((value) => value === values[0]) ? values[0] ?? "unset" : "mixed";
|
|
665
|
+
return /* @__PURE__ */ jsxs(Stack, {
|
|
666
|
+
gap: 2,
|
|
667
|
+
children: [/* @__PURE__ */ jsx(TriStateRow, {
|
|
668
|
+
id: `${entries[0]?.groupLabel ?? group} (${String(entries.length)})`,
|
|
669
|
+
t,
|
|
670
|
+
value: groupValue,
|
|
671
|
+
onLabelClick: () => {
|
|
672
|
+
toggleGroup(group);
|
|
673
|
+
},
|
|
674
|
+
onChange: (next) => {
|
|
675
|
+
const value = next === "allow" || next === "deny" ? next : "unset";
|
|
676
|
+
setDraft((prev) => ({
|
|
677
|
+
...prev,
|
|
678
|
+
items: {
|
|
679
|
+
...prev.items,
|
|
680
|
+
tools: {
|
|
681
|
+
...prev.items["tools"],
|
|
682
|
+
[groupRef(group)]: value
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}));
|
|
686
|
+
}
|
|
687
|
+
}), openGroups.has(group) ? entries.map((entry) => /* @__PURE__ */ jsx(TriStateRow, {
|
|
688
|
+
id: entry.id,
|
|
689
|
+
description: entry.description,
|
|
690
|
+
indent: 16,
|
|
691
|
+
t,
|
|
692
|
+
value: draft.items["tools"]?.[entry.id] ?? entry.override ?? configValue(entry),
|
|
693
|
+
onChange: (next) => {
|
|
694
|
+
const value = next === "allow" || next === "deny" ? next : "unset";
|
|
695
|
+
setDraft((prev) => ({
|
|
696
|
+
...prev,
|
|
697
|
+
items: {
|
|
698
|
+
...prev.items,
|
|
699
|
+
tools: {
|
|
700
|
+
...prev.items["tools"],
|
|
701
|
+
[entry.id]: value
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}));
|
|
705
|
+
}
|
|
706
|
+
}, entry.id)) : null]
|
|
707
|
+
}, group);
|
|
708
|
+
}) })),
|
|
709
|
+
section("skills", `${t("skills")} (${String(active.catalog.skills.length)})`, /* @__PURE__ */ jsx(Fragment, { children: active.catalog.skills.map((entry) => /* @__PURE__ */ jsx(TriStateRow, {
|
|
710
|
+
id: entry.id,
|
|
711
|
+
description: entry.description,
|
|
712
|
+
t,
|
|
713
|
+
value: draft.items["skills"]?.[entry.id] ?? entry.override ?? configValue(entry),
|
|
714
|
+
onChange: (next) => {
|
|
715
|
+
setDraft((prev) => ({
|
|
716
|
+
...prev,
|
|
717
|
+
items: {
|
|
718
|
+
...prev.items,
|
|
719
|
+
skills: {
|
|
720
|
+
...prev.items.skills,
|
|
721
|
+
[entry.id]: next === "allow" || next === "deny" ? next : "unset"
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}));
|
|
725
|
+
}
|
|
726
|
+
}, entry.id)) })),
|
|
727
|
+
section("policies", `${t("policies")} (${String(active.catalog.policies.length)})`, /* @__PURE__ */ jsx(Fragment, { children: active.catalog.policies.map((entry) => /* @__PURE__ */ jsx(TriStateRow, {
|
|
728
|
+
id: entry.id,
|
|
729
|
+
description: entry.description,
|
|
730
|
+
t,
|
|
731
|
+
value: draft.items["policies"]?.[entry.id] ?? entry.override ?? configValue(entry),
|
|
732
|
+
onChange: (next) => {
|
|
733
|
+
setDraft((prev) => ({
|
|
734
|
+
...prev,
|
|
735
|
+
items: {
|
|
736
|
+
...prev.items,
|
|
737
|
+
policies: {
|
|
738
|
+
...prev.items.policies,
|
|
739
|
+
[entry.id]: next === "allow" || next === "deny" ? next : "unset"
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}));
|
|
743
|
+
}
|
|
744
|
+
}, entry.id)) }))
|
|
745
|
+
]
|
|
746
|
+
}),
|
|
747
|
+
/* @__PURE__ */ jsx("div", {
|
|
748
|
+
"data-role": "actions",
|
|
749
|
+
children: /* @__PURE__ */ jsxs(Row, {
|
|
750
|
+
gap: 8,
|
|
751
|
+
justify: "end",
|
|
752
|
+
align: "center",
|
|
753
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
754
|
+
variant: "outline",
|
|
755
|
+
onClick: () => {
|
|
756
|
+
setDraft({
|
|
757
|
+
switches: {},
|
|
758
|
+
items: {
|
|
759
|
+
tools: {},
|
|
760
|
+
skills: {},
|
|
761
|
+
policies: {}
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
setNotice(null);
|
|
765
|
+
},
|
|
766
|
+
children: t("reset")
|
|
767
|
+
}), /* @__PURE__ */ jsx(Menu, {
|
|
768
|
+
open: menuOpen,
|
|
769
|
+
onClose: () => {
|
|
770
|
+
setMenuOpen(false);
|
|
771
|
+
},
|
|
772
|
+
align: "start",
|
|
773
|
+
portal: true,
|
|
774
|
+
items: [
|
|
775
|
+
{
|
|
776
|
+
id: "saveDefault",
|
|
777
|
+
label: t("saveDefault")
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
id: "saveAs",
|
|
781
|
+
label: t("saveAs")
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
id: "delete",
|
|
785
|
+
label: t("delete"),
|
|
786
|
+
disabled: !active.deletable
|
|
787
|
+
}
|
|
788
|
+
],
|
|
789
|
+
onSelect: (id) => {
|
|
790
|
+
setMenuOpen(false);
|
|
791
|
+
if (id === "saveDefault") {
|
|
792
|
+
commit("config");
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (id === "saveAs") {
|
|
796
|
+
setCode("");
|
|
797
|
+
setName(active.mode.name);
|
|
798
|
+
setDialog("saveAs");
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (active.deletable) setDialog("delete");
|
|
802
|
+
},
|
|
803
|
+
anchor: /* @__PURE__ */ jsxs(Row, {
|
|
804
|
+
gap: 0,
|
|
805
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
806
|
+
variant: "primary",
|
|
807
|
+
style: {
|
|
808
|
+
borderTopRightRadius: 0,
|
|
809
|
+
borderBottomRightRadius: 0,
|
|
810
|
+
borderRightWidth: 0
|
|
811
|
+
},
|
|
812
|
+
onClick: () => {
|
|
813
|
+
commit("session");
|
|
814
|
+
},
|
|
815
|
+
children: t("apply")
|
|
816
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
817
|
+
variant: "primary",
|
|
818
|
+
"aria-haspopup": "menu",
|
|
819
|
+
"aria-expanded": menuOpen,
|
|
820
|
+
style: {
|
|
821
|
+
borderTopLeftRadius: 0,
|
|
822
|
+
borderBottomLeftRadius: 0,
|
|
823
|
+
paddingInline: 8
|
|
824
|
+
},
|
|
825
|
+
onClick: () => {
|
|
826
|
+
setMenuOpen((current) => !current);
|
|
827
|
+
},
|
|
828
|
+
children: "▾"
|
|
829
|
+
})]
|
|
830
|
+
})
|
|
831
|
+
})]
|
|
832
|
+
})
|
|
833
|
+
}),
|
|
834
|
+
notice === null ? null : /* @__PURE__ */ jsx("div", {
|
|
835
|
+
"data-role": "notice",
|
|
836
|
+
style: { paddingTop: 8 },
|
|
837
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
838
|
+
as: "p",
|
|
839
|
+
size: "sm",
|
|
840
|
+
tone: "tertiary",
|
|
841
|
+
children: notice
|
|
842
|
+
})
|
|
843
|
+
})
|
|
844
|
+
]
|
|
752
845
|
}),
|
|
753
846
|
/* @__PURE__ */ jsx(Modal, {
|
|
754
|
-
open:
|
|
847
|
+
open: dialog === "saveAs",
|
|
755
848
|
onClose: () => {
|
|
756
|
-
|
|
849
|
+
setDialog(null);
|
|
757
850
|
},
|
|
758
|
-
title: t("
|
|
759
|
-
closeLabel: t("
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
851
|
+
title: t("saveAs"),
|
|
852
|
+
closeLabel: t("cancel"),
|
|
853
|
+
footer: /* @__PURE__ */ jsxs(Row, {
|
|
854
|
+
gap: 6,
|
|
855
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
856
|
+
variant: "outline",
|
|
857
|
+
size: "sm",
|
|
858
|
+
onClick: () => {
|
|
859
|
+
setDialog(null);
|
|
860
|
+
},
|
|
861
|
+
children: t("cancel")
|
|
862
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
863
|
+
size: "sm",
|
|
864
|
+
disabled: code.trim().length === 0,
|
|
865
|
+
onClick: () => {
|
|
866
|
+
if (sessionId === void 0 || code.trim().length === 0) return;
|
|
867
|
+
saveAsNewMode(sessionId, code, name).then(() => {
|
|
868
|
+
setDialog(null);
|
|
869
|
+
setNotice(t("savedNewMode"));
|
|
870
|
+
}).catch((error) => setNotice(`${t("saveFailed")}: ${String(error)}`));
|
|
871
|
+
},
|
|
872
|
+
children: t("confirm")
|
|
873
|
+
})]
|
|
874
|
+
}),
|
|
875
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
876
|
+
gap: 6,
|
|
877
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
878
|
+
value: code,
|
|
879
|
+
placeholder: t("codePlaceholder"),
|
|
880
|
+
"aria-label": t("codeLabel"),
|
|
881
|
+
onChange: (event) => {
|
|
882
|
+
setCode(event.currentTarget.value);
|
|
883
|
+
}
|
|
884
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
885
|
+
value: name,
|
|
886
|
+
placeholder: t("namePlaceholder"),
|
|
887
|
+
"aria-label": t("nameLabel"),
|
|
888
|
+
onChange: (event) => {
|
|
889
|
+
setName(event.currentTarget.value);
|
|
890
|
+
}
|
|
891
|
+
})]
|
|
789
892
|
})
|
|
790
893
|
}),
|
|
791
894
|
/* @__PURE__ */ jsx(Modal, {
|
|
792
|
-
open:
|
|
895
|
+
open: dialog === "delete",
|
|
793
896
|
onClose: () => {
|
|
794
|
-
|
|
897
|
+
setDialog(null);
|
|
795
898
|
},
|
|
796
|
-
title: t("
|
|
797
|
-
closeLabel: t("
|
|
798
|
-
description: t("
|
|
799
|
-
footer: /* @__PURE__ */ jsxs(
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
"data-deletable": mode.deletable ? "true" : "false",
|
|
826
|
-
children: /* @__PURE__ */ jsx(DisclosureRow, {
|
|
827
|
-
icon: /* @__PURE__ */ jsx(IconAgentPresetOutlineRegular, {}),
|
|
828
|
-
title: mode.title,
|
|
829
|
-
open,
|
|
830
|
-
expandable: true,
|
|
831
|
-
expandOnRowClick: true,
|
|
832
|
-
onToggle,
|
|
833
|
-
keepContentWhenOpen: true,
|
|
834
|
-
collapsedContent: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, {
|
|
835
|
-
as: "code",
|
|
836
|
-
size: "sm",
|
|
837
|
-
tone: "tertiary",
|
|
838
|
-
"data-mode-id": true,
|
|
839
|
-
children: mode.id
|
|
840
|
-
}), /* @__PURE__ */ jsxs(Row, {
|
|
841
|
-
gap: 8,
|
|
842
|
-
push: true,
|
|
843
|
-
children: [/* @__PURE__ */ jsx(Text, {
|
|
844
|
-
as: "span",
|
|
845
|
-
size: "sm",
|
|
846
|
-
tone: "tertiary",
|
|
847
|
-
children: mode.summary
|
|
848
|
-
}), mode.deletable ? /* @__PURE__ */ jsx(IconButton, {
|
|
849
|
-
size: "sm",
|
|
850
|
-
"data-action": "remove-mode",
|
|
851
|
-
disabled: !writable,
|
|
852
|
-
label: t("removeNamed", { name: mode.title }),
|
|
853
|
-
onClick: (event) => {
|
|
854
|
-
event.stopPropagation();
|
|
855
|
-
onRequestRemove();
|
|
856
|
-
},
|
|
857
|
-
children: /* @__PURE__ */ jsx(IconTrashOutlineRegular, {})
|
|
858
|
-
}) : /* @__PURE__ */ jsx(Text, {
|
|
859
|
-
as: "span",
|
|
860
|
-
size: "sm",
|
|
861
|
-
tone: "tertiary",
|
|
862
|
-
"data-protected": "true",
|
|
863
|
-
children: t("protected")
|
|
864
|
-
})]
|
|
865
|
-
})] }),
|
|
866
|
-
children: /* @__PURE__ */ jsx(Stack, {
|
|
867
|
-
gap: 14,
|
|
868
|
-
pad: "body",
|
|
869
|
-
children: mode.groups.map((group) => /* @__PURE__ */ jsxs(Stack, {
|
|
870
|
-
gap: 0,
|
|
871
|
-
"data-group": group.key,
|
|
872
|
-
children: [/* @__PURE__ */ jsx(Row, {
|
|
873
|
-
gap: 8,
|
|
874
|
-
justify: "between",
|
|
875
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
876
|
-
size: "sm",
|
|
877
|
-
tone: "secondary",
|
|
878
|
-
weight: "strong",
|
|
879
|
-
children: group.label
|
|
880
|
-
})
|
|
881
|
-
}), group.key === "model" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
882
|
-
/* @__PURE__ */ jsx(SettingsFieldRow, {
|
|
883
|
-
label: t("field.defaultModel"),
|
|
884
|
-
hint: t("hint.defaultModel"),
|
|
885
|
-
layout: "inline",
|
|
886
|
-
"data-control": "default-model",
|
|
887
|
-
children: /* @__PURE__ */ jsx(Switch, {
|
|
888
|
-
checked: hasModel,
|
|
889
|
-
label: t("field.defaultModel"),
|
|
890
|
-
disabled: !writable,
|
|
891
|
-
onChange: (next) => {
|
|
892
|
-
if (next) face.set([
|
|
893
|
-
"modes",
|
|
894
|
-
mode.id,
|
|
895
|
-
"defaultModel"
|
|
896
|
-
], {
|
|
897
|
-
provider: "",
|
|
898
|
-
model: ""
|
|
899
|
-
});
|
|
900
|
-
else face.clear([
|
|
901
|
-
"modes",
|
|
902
|
-
mode.id,
|
|
903
|
-
"defaultModel"
|
|
904
|
-
]);
|
|
905
|
-
}
|
|
906
|
-
})
|
|
907
|
-
}),
|
|
908
|
-
!hasModel ? null : /* @__PURE__ */ jsx(SettingsFieldRow, {
|
|
909
|
-
label: t("field.modelRoute"),
|
|
910
|
-
hint: t("hint.modelRoute"),
|
|
911
|
-
divider: true,
|
|
912
|
-
"data-control": "model-routes",
|
|
913
|
-
children: /* @__PURE__ */ jsx(ModelRouteList, {
|
|
914
|
-
label: t("field.modelRoute"),
|
|
915
|
-
candidates: models.routes,
|
|
916
|
-
selectedKey: mode.model === void 0 || mode.model.provider === "" ? void 0 : routeKey(mode.model.provider, mode.model.model),
|
|
917
|
-
disabled: !writable,
|
|
918
|
-
status: models.status,
|
|
919
|
-
loadingLabel: t("model.loading"),
|
|
920
|
-
errorLabel: t("model.error"),
|
|
921
|
-
emptyLabel: t("model.empty"),
|
|
922
|
-
onSelect: (candidate) => {
|
|
923
|
-
face.set([
|
|
924
|
-
"modes",
|
|
925
|
-
mode.id,
|
|
926
|
-
"defaultModel"
|
|
927
|
-
], {
|
|
928
|
-
provider: candidate.provider,
|
|
929
|
-
model: candidate.model
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
})
|
|
933
|
-
}),
|
|
934
|
-
group.fields.map((field, index) => /* @__PURE__ */ jsx(FieldRow, {
|
|
935
|
-
field,
|
|
936
|
-
writable,
|
|
937
|
-
t,
|
|
938
|
-
face,
|
|
939
|
-
divider: index > 0 || hasModel
|
|
940
|
-
}, field.key))
|
|
941
|
-
] }) : group.fields.map((field, index) => /* @__PURE__ */ jsx(FieldRow, {
|
|
942
|
-
field,
|
|
943
|
-
writable,
|
|
944
|
-
t,
|
|
945
|
-
face,
|
|
946
|
-
divider: index > 0
|
|
947
|
-
}, field.key))]
|
|
948
|
-
}, group.key))
|
|
949
|
-
})
|
|
950
|
-
})
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
function FieldRow({ field, writable, divider, t, face }) {
|
|
954
|
-
if (field.control === "choice" && !field.present) return null;
|
|
955
|
-
return /* @__PURE__ */ jsx(SettingsFieldRow, {
|
|
956
|
-
label: field.label,
|
|
957
|
-
hint: field.hint,
|
|
958
|
-
divider,
|
|
959
|
-
layout: field.control === "switch" || field.control === "tri" || field.control === "roles" ? "inline" : "stack",
|
|
960
|
-
...field.overridden ? { overriddenLabel: t("overridden") } : {},
|
|
961
|
-
...field.control === "text" ? {
|
|
962
|
-
resetLabel: t("field.reset"),
|
|
963
|
-
onReset: () => {
|
|
964
|
-
face.clear(field.path);
|
|
965
|
-
}
|
|
966
|
-
} : {},
|
|
967
|
-
...field.invalid === void 0 ? {} : { invalid: field.invalid },
|
|
968
|
-
"data-field": field.path.join("."),
|
|
969
|
-
"data-control": field.control,
|
|
970
|
-
children: /* @__PURE__ */ jsx(FieldControl, {
|
|
971
|
-
field,
|
|
972
|
-
writable,
|
|
973
|
-
t,
|
|
974
|
-
face
|
|
975
|
-
})
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
function FieldControl({ field, writable, t, face }) {
|
|
979
|
-
const disabled = !writable;
|
|
980
|
-
const id = useId();
|
|
981
|
-
switch (field.control) {
|
|
982
|
-
case "multiline": return /* @__PURE__ */ jsx(MultilineField, {
|
|
983
|
-
value: field.text,
|
|
984
|
-
rows: 3,
|
|
985
|
-
disabled,
|
|
986
|
-
"aria-label": field.label,
|
|
987
|
-
onChange: (event) => {
|
|
988
|
-
face.editText(field.path, event.currentTarget.value);
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
case "choice": return /* @__PURE__ */ jsx(SearchSelect, {
|
|
992
|
-
label: field.label,
|
|
993
|
-
value: field.text,
|
|
994
|
-
emptyLabel: t("tri.unset"),
|
|
995
|
-
options: field.options.map((option) => ({
|
|
996
|
-
value: String(option.value),
|
|
997
|
-
...option.label === void 0 ? {} : { label: option.label }
|
|
998
|
-
})),
|
|
999
|
-
searchLabel: t("select.search"),
|
|
1000
|
-
noMatchLabel: t("select.noMatch"),
|
|
1001
|
-
disabled,
|
|
1002
|
-
onSelect: (next) => {
|
|
1003
|
-
if (next === "") face.clear(field.path);
|
|
1004
|
-
else face.set(field.path, next);
|
|
1005
|
-
}
|
|
1006
|
-
});
|
|
1007
|
-
case "switch": return /* @__PURE__ */ jsx(Switch, {
|
|
1008
|
-
checked: field.value === true,
|
|
1009
|
-
label: field.label,
|
|
1010
|
-
disabled,
|
|
1011
|
-
onChange: (next) => {
|
|
1012
|
-
face.set(field.path, next);
|
|
1013
|
-
}
|
|
1014
|
-
});
|
|
1015
|
-
case "tri": return /* @__PURE__ */ jsx(SegmentedControl, {
|
|
1016
|
-
id: `${id}-tri`,
|
|
1017
|
-
label: field.label,
|
|
1018
|
-
value: triValue(field),
|
|
1019
|
-
disabled,
|
|
1020
|
-
options: [
|
|
1021
|
-
{
|
|
1022
|
-
value: "unset",
|
|
1023
|
-
label: t("tri.unset")
|
|
1024
|
-
},
|
|
1025
|
-
{
|
|
1026
|
-
value: "on",
|
|
1027
|
-
label: t("tri.on")
|
|
1028
|
-
},
|
|
1029
|
-
{
|
|
1030
|
-
value: "off",
|
|
1031
|
-
label: t("tri.off")
|
|
1032
|
-
}
|
|
1033
|
-
],
|
|
1034
|
-
onChange: (next) => {
|
|
1035
|
-
if (next === "unset") face.clear(field.path);
|
|
1036
|
-
else face.set(field.path, next === "on");
|
|
1037
|
-
}
|
|
1038
|
-
});
|
|
1039
|
-
case "roles": return /* @__PURE__ */ jsx(Row, {
|
|
1040
|
-
gap: 6,
|
|
1041
|
-
wrap: true,
|
|
1042
|
-
"data-control": "roles",
|
|
1043
|
-
children: ROLES.map((role) => {
|
|
1044
|
-
const active = Array.isArray(field.value) && field.value.includes(role);
|
|
1045
|
-
return /* @__PURE__ */ jsx(Button, {
|
|
1046
|
-
variant: active ? "primary" : "outline",
|
|
899
|
+
title: t("delete"),
|
|
900
|
+
closeLabel: t("cancel"),
|
|
901
|
+
description: t("deleteConfirm"),
|
|
902
|
+
footer: /* @__PURE__ */ jsxs(Row, {
|
|
903
|
+
gap: 6,
|
|
904
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
905
|
+
variant: "outline",
|
|
906
|
+
size: "sm",
|
|
907
|
+
onClick: () => {
|
|
908
|
+
setDialog(null);
|
|
909
|
+
},
|
|
910
|
+
children: t("cancel")
|
|
911
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
912
|
+
size: "sm",
|
|
913
|
+
onClick: () => {
|
|
914
|
+
if (sessionId === void 0) return;
|
|
915
|
+
deleteMode(sessionId).then((next) => {
|
|
916
|
+
setDialog(null);
|
|
917
|
+
setLoad({
|
|
918
|
+
kind: "ready",
|
|
919
|
+
value: next
|
|
920
|
+
});
|
|
921
|
+
}).catch((error) => setNotice(`${t("saveFailed")}: ${String(error)}`));
|
|
922
|
+
},
|
|
923
|
+
children: t("confirm")
|
|
924
|
+
})]
|
|
925
|
+
}),
|
|
926
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
927
|
+
as: "p",
|
|
1047
928
|
size: "sm",
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
"aria-pressed": active,
|
|
1051
|
-
onClick: () => {
|
|
1052
|
-
const current = Array.isArray(field.value) ? field.value.map(String) : [];
|
|
1053
|
-
const wanted = active ? current.filter((value) => value !== role) : [...current.filter((value) => value !== role), role];
|
|
1054
|
-
face.set(field.path, wanted);
|
|
1055
|
-
},
|
|
1056
|
-
children: t(`role.${role}`)
|
|
1057
|
-
}, role);
|
|
929
|
+
children: active.mode.name
|
|
930
|
+
})
|
|
1058
931
|
})
|
|
1059
|
-
|
|
1060
|
-
case "tags": return /* @__PURE__ */ jsx(TagInput, {
|
|
1061
|
-
value: Array.isArray(field.value) ? field.value.map(String) : [],
|
|
1062
|
-
options: field.options.map((option) => ({
|
|
1063
|
-
value: String(option.value),
|
|
1064
|
-
...option.label === void 0 ? {} : { label: option.label }
|
|
1065
|
-
})),
|
|
1066
|
-
placeholder: t("tags.placeholder"),
|
|
1067
|
-
label: field.label,
|
|
1068
|
-
noMatchLabel: t("select.noMatch"),
|
|
1069
|
-
removeLabel: (name) => t("tags.remove", { name }),
|
|
1070
|
-
disabled,
|
|
1071
|
-
onChange: (next) => {
|
|
1072
|
-
face.set(field.path, next);
|
|
1073
|
-
}
|
|
1074
|
-
});
|
|
1075
|
-
case "text": return /* @__PURE__ */ jsx(Input, {
|
|
1076
|
-
value: field.text,
|
|
1077
|
-
"aria-label": field.label,
|
|
1078
|
-
disabled,
|
|
1079
|
-
onChange: (event) => {
|
|
1080
|
-
face.editText(field.path, event.currentTarget.value);
|
|
1081
|
-
}
|
|
1082
|
-
});
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
function triValue(field) {
|
|
1086
|
-
return field.value === "unset" || field.value === "on" || field.value === "off" ? field.value : "unset";
|
|
1087
|
-
}
|
|
1088
|
-
// src/client/bundle-locales.ts
|
|
1089
|
-
const BUNDLE_NS = "session-mode-bundle";
|
|
1090
|
-
const bundleZh = {
|
|
1091
|
-
"default.label": "新会话的默认模式",
|
|
1092
|
-
"default.hint": "还没选过模式的会话从这里开始。",
|
|
1093
|
-
"modes.label": "模式",
|
|
1094
|
-
"modes.hint": "展开一个模式,改它的提示词、工具与技能、上游规则和注入内容。",
|
|
1095
|
-
"add.label": "添加模式",
|
|
1096
|
-
"add.placeholder": "模式 id",
|
|
1097
|
-
"add.confirm": "添加",
|
|
1098
|
-
"add.cancel": "取消",
|
|
1099
|
-
"add.close": "关闭",
|
|
1100
|
-
"add.hint": "给新模式一个 id(选择器与列表里也显示它,加好后可以再改名称)。",
|
|
1101
|
-
removeNamed: "删除模式 {name}",
|
|
1102
|
-
protected: "内置模式,不提供删除",
|
|
1103
|
-
untitled: "(未命名)",
|
|
1104
|
-
overridden: "已覆盖",
|
|
1105
|
-
"group.identity": "基本信息",
|
|
1106
|
-
"group.persona": "提示词",
|
|
1107
|
-
"group.tools": "工具与技能",
|
|
1108
|
-
"group.rules": "上游规则",
|
|
1109
|
-
"group.injections": "请求里注入什么",
|
|
1110
|
-
"group.model": "默认模型",
|
|
1111
|
-
"role.main": "会话可选",
|
|
1112
|
-
"role.subagent": "子代理可用",
|
|
1113
|
-
"field.name": "名称",
|
|
1114
|
-
"field.description": "一句话说明",
|
|
1115
|
-
"field.role": "角色",
|
|
1116
|
-
"field.presetsOnly": "允许挂的 preset",
|
|
1117
|
-
"field.prefix": "提示词前缀",
|
|
1118
|
-
"field.suffix": "提示词后缀",
|
|
1119
|
-
"field.allowTools": "允许的工具",
|
|
1120
|
-
"field.denyTools": "禁用的工具",
|
|
1121
|
-
"field.allowSkills": "允许的技能",
|
|
1122
|
-
"field.denySkills": "禁用的技能",
|
|
1123
|
-
"field.allowPolicies": "生效的规则",
|
|
1124
|
-
"field.denyPolicies": "禁用的规则",
|
|
1125
|
-
"field.instructions": "工作区指令",
|
|
1126
|
-
"field.skills": "技能目录",
|
|
1127
|
-
"field.runtimeContext": "动态快照",
|
|
1128
|
-
"field.defaultModel": "设默认模型",
|
|
1129
|
-
"field.modelRoute": "用哪个模型",
|
|
1130
|
-
"field.reasoningEffort": "思考档位",
|
|
1131
|
-
"field.reset": "恢复默认",
|
|
1132
|
-
"hint.name": "选择器和列表里显示的名字。",
|
|
1133
|
-
"hint.description": "一句话说明这个模式做什么。",
|
|
1134
|
-
"hint.role": "「会话可选」= 用户能在会话里选它;「子代理可用」= 可以把子代理跑在这个模式下。",
|
|
1135
|
-
"hint.presetsOnly": "只允许这个模式挂这些 preset(行清单由挂着的那份提供);不填 = 允许全部(哪份 preset 都行,切模式也不动它)。有值时切到这个模式,会话当前那份不在名单里就换成名单第一个。",
|
|
1136
|
-
"hint.prefix": "加在系统提示词开头的一段话。",
|
|
1137
|
-
"hint.suffix": "加在系统提示词结尾的一段话。",
|
|
1138
|
-
"hint.allowTools": "只允许这些工具;不填 = 不限制(用会话当前 preset 的全部工具)。",
|
|
1139
|
-
"hint.denyTools": "从上面那份里去掉这些工具。",
|
|
1140
|
-
"hint.allowSkills": "只允许这些技能;不填 = 不限制。",
|
|
1141
|
-
"hint.denySkills": "这些技能不列进技能目录,`skill` 工具也加载不了。",
|
|
1142
|
-
"hint.allowPolicies": "只让这些上游规则生效;不填 = 全部生效。",
|
|
1143
|
-
"hint.denyPolicies": "禁用这些上游规则,它的判断连拒绝一起丢掉。",
|
|
1144
|
-
"hint.instructions": "每步都把工作区指令(AGENTS.md 之类)发给模型;关掉就不发。",
|
|
1145
|
-
"hint.skills": "每步都把技能目录发给模型;默认按这个模式的工具名单自动判断。",
|
|
1146
|
-
"hint.runtimeContext": "每步都把沙箱与审批策略发给模型。",
|
|
1147
|
-
"hint.defaultModel": "开 = 这个模式自带一个默认模型(会话还没选过模型时用它);关 = 跟随全局模型。",
|
|
1148
|
-
"hint.modelRoute": "从部署里已设置的模型里挑一条:服务商与模型是一对,选它等于两个一起定。",
|
|
1149
|
-
"hint.reasoningEffort": "思考档位;不填用服务商默认。",
|
|
1150
|
-
"model.loading": "正在读部署里已设置的模型…",
|
|
1151
|
-
"model.error": "读不出部署里已设置的模型。",
|
|
1152
|
-
"model.empty": "部署里还没有已设置的模型。",
|
|
1153
|
-
"tags.placeholder": "加一个后回车,或粘贴一串",
|
|
1154
|
-
"tags.add": "添加",
|
|
1155
|
-
"tags.remove": "移除 {name}",
|
|
1156
|
-
"select.search": "搜索候选",
|
|
1157
|
-
"select.noMatch": "没有匹配的候选",
|
|
1158
|
-
"tri.unset": "默认",
|
|
1159
|
-
"tri.on": "开",
|
|
1160
|
-
"tri.off": "关",
|
|
1161
|
-
"remove.title": "删除模式",
|
|
1162
|
-
"remove.description": "删除 {name}?删除只在保存之后生效,保存之前还可以丢弃。",
|
|
1163
|
-
"remove.confirm": "删除",
|
|
1164
|
-
"remove.cancel": "取消",
|
|
1165
|
-
"remove.close": "关闭",
|
|
1166
|
-
"model.set": "设置默认模型",
|
|
1167
|
-
"model.follow": "改为跟随全局",
|
|
1168
|
-
save: "保存",
|
|
1169
|
-
saving: "保存中…",
|
|
1170
|
-
saveFailed: "保存没有生效,请重试。",
|
|
1171
|
-
readOnly: "这份部署的配置是只读的。",
|
|
1172
|
-
unavailable: "这一行当前不可写。",
|
|
1173
|
-
notSaved: "没有保存:{message}",
|
|
1174
|
-
"configured.loading": "正在读取这一行的配置…",
|
|
1175
|
-
"configured.missing": "设置面里没有 `session-mode` 这一行:没装、被禁用,或者还没装配成功(依赖没到齐时会一直 PENDING)。",
|
|
1176
|
-
"configured.unreadable": "`session-mode` 这一行在跑,但这份配置读不出来({problem})。",
|
|
1177
|
-
"violation.modesEmpty": "至少要有一种模式。",
|
|
1178
|
-
"violation.defaultMissing": "默认模式不在模式清单里。",
|
|
1179
|
-
"violation.modelPair": "默认模型要同时给出服务商与模型。",
|
|
1180
|
-
"summary.allTools": "全部工具",
|
|
1181
|
-
"summary.tools": "{n} 件工具",
|
|
1182
|
-
"summary.denyTools": "禁 {n} 件",
|
|
1183
|
-
"summary.globalModel": "跟随全局模型"
|
|
1184
|
-
};
|
|
1185
|
-
const bundleEn = {
|
|
1186
|
-
"default.label": "Mode for new sessions",
|
|
1187
|
-
"default.hint": "Sessions that never picked a mode start here.",
|
|
1188
|
-
"modes.label": "Modes",
|
|
1189
|
-
"modes.hint": "Expand a mode to edit its prompt, tools and skills, upstream rules, and injected content.",
|
|
1190
|
-
"add.label": "Add a mode",
|
|
1191
|
-
"add.placeholder": "mode id",
|
|
1192
|
-
"add.confirm": "Add",
|
|
1193
|
-
"add.cancel": "Cancel",
|
|
1194
|
-
"add.close": "Close",
|
|
1195
|
-
"add.hint": "Give the new mode an id (it also shows in pickers and lists; rename it after adding).",
|
|
1196
|
-
removeNamed: "Delete mode {name}",
|
|
1197
|
-
protected: "Built-in mode: no delete",
|
|
1198
|
-
untitled: "(untitled)",
|
|
1199
|
-
overridden: "Overridden",
|
|
1200
|
-
"group.identity": "Basics",
|
|
1201
|
-
"group.persona": "Prompt",
|
|
1202
|
-
"group.tools": "Tools and skills",
|
|
1203
|
-
"group.rules": "Upstream rules",
|
|
1204
|
-
"group.injections": "Injected into requests",
|
|
1205
|
-
"group.model": "Default model",
|
|
1206
|
-
"role.main": "Selectable in sessions",
|
|
1207
|
-
"role.subagent": "Usable for subagents",
|
|
1208
|
-
"field.name": "Name",
|
|
1209
|
-
"field.description": "One-line description",
|
|
1210
|
-
"field.role": "Roles",
|
|
1211
|
-
"field.presetsOnly": "Presets it may ride on",
|
|
1212
|
-
"field.prefix": "Prompt prefix",
|
|
1213
|
-
"field.suffix": "Prompt suffix",
|
|
1214
|
-
"field.allowTools": "Allowed tools",
|
|
1215
|
-
"field.denyTools": "Denied tools",
|
|
1216
|
-
"field.allowSkills": "Allowed skills",
|
|
1217
|
-
"field.denySkills": "Denied skills",
|
|
1218
|
-
"field.allowPolicies": "Rules in force",
|
|
1219
|
-
"field.denyPolicies": "Rules disabled",
|
|
1220
|
-
"field.instructions": "Workspace instructions",
|
|
1221
|
-
"field.skills": "Skill catalog",
|
|
1222
|
-
"field.runtimeContext": "Runtime snapshot",
|
|
1223
|
-
"field.defaultModel": "Default model",
|
|
1224
|
-
"field.modelRoute": "Which model",
|
|
1225
|
-
"field.reasoningEffort": "Reasoning effort",
|
|
1226
|
-
"field.reset": "Reset",
|
|
1227
|
-
"hint.name": "Shown in the picker and in lists.",
|
|
1228
|
-
"hint.description": "One line saying what this mode is for.",
|
|
1229
|
-
"hint.role": "Selectable in sessions = the user can pick it; usable for subagents = subagents may run in it.",
|
|
1230
|
-
"hint.presetsOnly": "Only these presets may back this mode (the one it rides on supplies the row list); empty = every preset is allowed (and selecting the mode switches nothing). When set, selecting the mode switches to the first entry if the session's current preset is not listed.",
|
|
1231
|
-
"hint.prefix": "Text prepended to the system prompt.",
|
|
1232
|
-
"hint.suffix": "Text appended to the system prompt.",
|
|
1233
|
-
"hint.allowTools": "Only these tools; empty = no restriction (every tool of the session's preset).",
|
|
1234
|
-
"hint.denyTools": "Those tools are removed from the list above.",
|
|
1235
|
-
"hint.allowSkills": "Only these skills; empty = no restriction.",
|
|
1236
|
-
"hint.denySkills": "Those skills leave the catalog, and the `skill` tool cannot load them.",
|
|
1237
|
-
"hint.allowPolicies": "Only these upstream rules stay in force; empty = all of them.",
|
|
1238
|
-
"hint.denyPolicies": "Those upstream rules are disabled, rejection included.",
|
|
1239
|
-
"hint.instructions": "Sends the workspace instructions (AGENTS.md and friends) with every step; off means it is not sent.",
|
|
1240
|
-
"hint.skills": "Sends the skill catalog with every step; default decides from this mode's tool lists.",
|
|
1241
|
-
"hint.runtimeContext": "Sends the sandbox and approval policies with every step.",
|
|
1242
|
-
"hint.defaultModel": "On = this mode carries a default model (used while a session has picked none); off = follow the global model.",
|
|
1243
|
-
"hint.modelRoute": "Pick one model already set up in this deployment: a provider and a model come as one route, so choosing it sets both.",
|
|
1244
|
-
"hint.reasoningEffort": "Reasoning effort; empty uses the provider default.",
|
|
1245
|
-
"model.loading": "Reading the models set up in this deployment…",
|
|
1246
|
-
"model.error": "Could not read the models set up in this deployment.",
|
|
1247
|
-
"model.empty": "No model is set up in this deployment yet.",
|
|
1248
|
-
"tags.placeholder": "Add one and press Enter, or paste a list",
|
|
1249
|
-
"tags.add": "Add",
|
|
1250
|
-
"tags.remove": "Remove {name}",
|
|
1251
|
-
"select.search": "Search candidates",
|
|
1252
|
-
"select.noMatch": "No matching candidate",
|
|
1253
|
-
"tri.unset": "Default",
|
|
1254
|
-
"tri.on": "On",
|
|
1255
|
-
"tri.off": "Off",
|
|
1256
|
-
"remove.title": "Delete mode",
|
|
1257
|
-
"remove.description": "Delete {name}? The deletion applies on save; until then you can discard it.",
|
|
1258
|
-
"remove.confirm": "Delete",
|
|
1259
|
-
"remove.cancel": "Cancel",
|
|
1260
|
-
"remove.close": "Close",
|
|
1261
|
-
"model.set": "Set a default model",
|
|
1262
|
-
"model.follow": "Follow the global model",
|
|
1263
|
-
save: "Save",
|
|
1264
|
-
saving: "Saving…",
|
|
1265
|
-
saveFailed: "The save did not take effect; try again.",
|
|
1266
|
-
readOnly: "This deployment stores settings read-only.",
|
|
1267
|
-
unavailable: "This row is not writable right now.",
|
|
1268
|
-
notSaved: "Not saved: {message}",
|
|
1269
|
-
"configured.loading": "Reading this row's configuration…",
|
|
1270
|
-
"configured.missing": "The settings surface has no `session-mode` row: not installed, disabled, or not composed yet (it stays PENDING while a dependency is missing).",
|
|
1271
|
-
"configured.unreadable": "The `session-mode` row is running, but its configuration cannot be read ({problem}).",
|
|
1272
|
-
"violation.modesEmpty": "At least one mode is required.",
|
|
1273
|
-
"violation.defaultMissing": "The default mode is not in the mode list.",
|
|
1274
|
-
"violation.modelPair": "A default model needs both a provider and a model.",
|
|
1275
|
-
"summary.allTools": "every tool",
|
|
1276
|
-
"summary.tools": "{n} tools",
|
|
1277
|
-
"summary.denyTools": "{n} denied",
|
|
1278
|
-
"summary.globalModel": "global model"
|
|
1279
|
-
};
|
|
1280
|
-
// src/client/api.ts
|
|
1281
|
-
async function fetchRoster() {
|
|
1282
|
-
const response = await fetch(SESSION_MODE_PATH, {
|
|
1283
|
-
method: "GET",
|
|
1284
|
-
headers: { accept: "application/json" }
|
|
1285
|
-
});
|
|
1286
|
-
if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
|
|
1287
|
-
return await response.json();
|
|
1288
|
-
}
|
|
1289
|
-
async function selectMode(sessionId, mode) {
|
|
1290
|
-
const response = await fetch(SESSION_MODE_PATH, {
|
|
1291
|
-
method: "POST",
|
|
1292
|
-
headers: {
|
|
1293
|
-
accept: "application/json",
|
|
1294
|
-
"content-type": "application/json"
|
|
1295
|
-
},
|
|
1296
|
-
body: JSON.stringify({
|
|
1297
|
-
sessionId,
|
|
1298
|
-
mode
|
|
1299
|
-
})
|
|
932
|
+
]
|
|
1300
933
|
});
|
|
1301
|
-
const value = await response.json().catch(() => void 0);
|
|
1302
|
-
if (!response.ok) {
|
|
1303
|
-
const error = value?.["error"];
|
|
1304
|
-
throw new Error(typeof error === "string" ? error : `HTTP ${String(response.status)}`);
|
|
1305
|
-
}
|
|
1306
|
-
return value?.mode ?? mode;
|
|
1307
934
|
}
|
|
1308
935
|
// src/client/use-roster.ts
|
|
1309
936
|
let pending;
|
|
@@ -1403,7 +1030,46 @@ const zh = {
|
|
|
1403
1030
|
model: "模型",
|
|
1404
1031
|
modelHint: "模型 id。",
|
|
1405
1032
|
reasoningEffort: "思考档位",
|
|
1406
|
-
reasoningEffortHint: "默认就跟服务商自己的默认。"
|
|
1033
|
+
reasoningEffortHint: "默认就跟服务商自己的默认。",
|
|
1034
|
+
panel: "会话模式",
|
|
1035
|
+
panelDescription: "当前会话的模式与生效面:工具、技能、政策与注入开关。",
|
|
1036
|
+
panelNoSession: "当前没有会话",
|
|
1037
|
+
persona: "系统提示词",
|
|
1038
|
+
tools: "工具",
|
|
1039
|
+
skills: "技能",
|
|
1040
|
+
policies: "政策规则",
|
|
1041
|
+
switches: "注入面",
|
|
1042
|
+
instructions: "工作区指令",
|
|
1043
|
+
skillsCatalog: "技能目录",
|
|
1044
|
+
runtimeContext: "动态快照",
|
|
1045
|
+
empty: "(无)",
|
|
1046
|
+
loading: "读取中…",
|
|
1047
|
+
saveDefault: "保存为默认",
|
|
1048
|
+
reset: "重置",
|
|
1049
|
+
apply: "应用到会话",
|
|
1050
|
+
applied: "已应用到会话",
|
|
1051
|
+
fromSession: "会话覆盖",
|
|
1052
|
+
fromMode: "模式默认",
|
|
1053
|
+
saveAs: "另存为",
|
|
1054
|
+
delete: "删除",
|
|
1055
|
+
deleteConfirm: "删除这个模式?它会从配置里移除,使用它的会话回到默认模式。",
|
|
1056
|
+
cancel: "取消",
|
|
1057
|
+
confirm: "确定",
|
|
1058
|
+
nameLabel: "显示名(可选)",
|
|
1059
|
+
codeLabel: "模式 code(必填)",
|
|
1060
|
+
codePlaceholder: "例如 my-mode",
|
|
1061
|
+
namePlaceholder: "新模式名",
|
|
1062
|
+
addValue: "添加一项",
|
|
1063
|
+
general: "通用配置",
|
|
1064
|
+
stateDefault: "默认",
|
|
1065
|
+
stateMixed: "混合",
|
|
1066
|
+
stateOn: "开",
|
|
1067
|
+
stateOff: "关",
|
|
1068
|
+
stateAllow: "允许",
|
|
1069
|
+
stateDeny: "禁止",
|
|
1070
|
+
savedNewMode: "已另存为新模式,对新会话生效",
|
|
1071
|
+
saved: "已保存,对新会话生效",
|
|
1072
|
+
saveFailed: "保存失败"
|
|
1407
1073
|
};
|
|
1408
1074
|
const en = {
|
|
1409
1075
|
seatHint: "Pick the mode for this session",
|
|
@@ -1414,10 +1080,50 @@ const en = {
|
|
|
1414
1080
|
model: "Model",
|
|
1415
1081
|
modelHint: "Model id.",
|
|
1416
1082
|
reasoningEffort: "Reasoning effort",
|
|
1417
|
-
reasoningEffortHint: "Default keeps the provider's own default."
|
|
1083
|
+
reasoningEffortHint: "Default keeps the provider's own default.",
|
|
1084
|
+
panel: "Session mode",
|
|
1085
|
+
panelDescription: "The mode this session runs and what it currently exposes: tools, skills, policies and injections.",
|
|
1086
|
+
panelNoSession: "No session",
|
|
1087
|
+
persona: "System prompt",
|
|
1088
|
+
tools: "Tools",
|
|
1089
|
+
skills: "Skills",
|
|
1090
|
+
policies: "Policies",
|
|
1091
|
+
switches: "Injections",
|
|
1092
|
+
instructions: "Workspace instructions",
|
|
1093
|
+
skillsCatalog: "Skill catalog",
|
|
1094
|
+
runtimeContext: "Runtime context",
|
|
1095
|
+
empty: "(none)",
|
|
1096
|
+
loading: "Loading…",
|
|
1097
|
+
saveDefault: "Save as default",
|
|
1098
|
+
reset: "Reset",
|
|
1099
|
+
apply: "Apply to session",
|
|
1100
|
+
applied: "Applied to session",
|
|
1101
|
+
fromSession: "session override",
|
|
1102
|
+
fromMode: "mode default",
|
|
1103
|
+
saveAs: "Save as",
|
|
1104
|
+
delete: "Delete",
|
|
1105
|
+
deleteConfirm: "Delete this mode? It is removed from the configuration and sessions using it fall back to the default.",
|
|
1106
|
+
cancel: "Cancel",
|
|
1107
|
+
confirm: "Confirm",
|
|
1108
|
+
nameLabel: "Display name (optional)",
|
|
1109
|
+
codeLabel: "Mode code (required)",
|
|
1110
|
+
codePlaceholder: "e.g. my-mode",
|
|
1111
|
+
namePlaceholder: "New mode name",
|
|
1112
|
+
addValue: "Add an entry",
|
|
1113
|
+
general: "General",
|
|
1114
|
+
stateDefault: "Default",
|
|
1115
|
+
stateMixed: "Mixed",
|
|
1116
|
+
stateOn: "On",
|
|
1117
|
+
stateOff: "Off",
|
|
1118
|
+
stateAllow: "Allow",
|
|
1119
|
+
stateDeny: "Deny",
|
|
1120
|
+
savedNewMode: "Saved as a new mode; applies to new sessions",
|
|
1121
|
+
saved: "Saved; applies to new sessions",
|
|
1122
|
+
saveFailed: "Save failed"
|
|
1418
1123
|
};
|
|
1419
1124
|
// src/client/index.ts
|
|
1420
1125
|
const NS = "session-mode";
|
|
1126
|
+
const PANEL_ID = "@morlay/dsh-session-mode";
|
|
1421
1127
|
const FIELDS = [
|
|
1422
1128
|
"provider",
|
|
1423
1129
|
"model",
|
|
@@ -1557,45 +1263,58 @@ function apply(ctx) {
|
|
|
1557
1263
|
};
|
|
1558
1264
|
}, "session-mode: composer chip");
|
|
1559
1265
|
});
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
const
|
|
1566
|
-
|
|
1266
|
+
ctx.inject([
|
|
1267
|
+
"sidebarRightTabs",
|
|
1268
|
+
"slots",
|
|
1269
|
+
"locale"
|
|
1270
|
+
], (scope) => scope.effect(() => {
|
|
1271
|
+
const panelT = scope.locale.bind(NS);
|
|
1272
|
+
const offType = scope.sidebarRightTabs.register({
|
|
1273
|
+
id: PANEL_ID,
|
|
1274
|
+
kind: PANEL_ID,
|
|
1275
|
+
priority: "extension",
|
|
1276
|
+
title: () => panelT("panel"),
|
|
1277
|
+
guide: [{
|
|
1278
|
+
id: PANEL_ID,
|
|
1279
|
+
order: 20,
|
|
1280
|
+
title: () => panelT("panel"),
|
|
1281
|
+
description: () => panelT("panelDescription")
|
|
1282
|
+
}]
|
|
1567
1283
|
});
|
|
1284
|
+
const body = scope.slots.inject("sidebar.right.pane.tab", () => scope.slots.register({
|
|
1285
|
+
name: "sidebar.right.pane.tab",
|
|
1286
|
+
key: PANEL_ID,
|
|
1287
|
+
locale: NS
|
|
1288
|
+
}, SessionModePanel));
|
|
1568
1289
|
return () => {
|
|
1569
|
-
|
|
1570
|
-
|
|
1290
|
+
body();
|
|
1291
|
+
offType();
|
|
1571
1292
|
};
|
|
1572
|
-
}, "session-mode:
|
|
1573
|
-
ctx.inject([
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1293
|
+
}, "session-mode: sidebar panel"));
|
|
1294
|
+
ctx.inject(["sidebarRight", "uiSession"], (scope) => scope.effect(() => {
|
|
1295
|
+
const current = scope.uiSession.adapter.current;
|
|
1296
|
+
const mounted = scope.sidebarRight.mounted;
|
|
1297
|
+
let queued = false;
|
|
1298
|
+
const sync = () => {
|
|
1299
|
+
if (queued) return;
|
|
1300
|
+
queued = true;
|
|
1301
|
+
queueMicrotask(() => {
|
|
1302
|
+
queued = false;
|
|
1303
|
+
const key = current.getSnapshot().key;
|
|
1304
|
+
if (typeof key !== "string" || key.length === 0) return;
|
|
1305
|
+
if (mounted.getSnapshot() !== key) return;
|
|
1306
|
+
try {
|
|
1307
|
+
scope.sidebarRight.openTab(PANEL_ID);
|
|
1308
|
+
} catch {}
|
|
1585
1309
|
});
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
refreshPage = void 0;
|
|
1596
|
-
dispose();
|
|
1597
|
-
};
|
|
1598
|
-
}, "session-mode: bundle configuration page");
|
|
1599
|
-
});
|
|
1310
|
+
};
|
|
1311
|
+
const offCurrent = current.subscribe(sync);
|
|
1312
|
+
const offMounted = mounted.subscribe(sync);
|
|
1313
|
+
sync();
|
|
1314
|
+
return () => {
|
|
1315
|
+
offCurrent();
|
|
1316
|
+
offMounted();
|
|
1317
|
+
};
|
|
1318
|
+
}, "session-mode: panel follows the session"));
|
|
1600
1319
|
}
|
|
1601
1320
|
export { SESSION_MODE_NS, apply, inject };
|