@morlay/dsh-session-mode 0.0.15 → 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/dist/client.cjs CHANGED
@@ -36,9 +36,9 @@ window.__ModuleLoader__.load({
36
36
  value: mod,
37
37
  enumerable: true
38
38
  }) : target, mod));
39
+ let react = require("react");
39
40
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
40
41
  let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
41
- let react = require("react");
42
42
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
43
43
  let react_jsx_runtime = require("react/jsx-runtime");
44
44
  // src/catalog-remote.ts
@@ -57,8 +57,77 @@ window.__ModuleLoader__.load({
57
57
  }]
58
58
  };
59
59
  // src/shared.ts
60
- const SESSION_MODE_PATH = "/session-mode";
61
- const POLICY_NAMES = ["fs/write-intent", "fs/edit-intent"];
60
+ const SESSION_MODE_PATH = "/api/morlay/v1/session-mode";
61
+ const POLICY_NAMES = [
62
+ "fs/write-intent",
63
+ "fs/edit-intent",
64
+ "fs/delete-intent"
65
+ ];
66
+ function groupRef(group) {
67
+ return group.startsWith("mcp:") ? group : `group:${group}`;
68
+ }
69
+ // src/client/hints.ts
70
+ const SESSION_MODE_NS = "session-mode";
71
+ function unwrapService(value) {
72
+ if (value === void 0) return void 0;
73
+ return value[Symbol.for("cordis.original")] ?? value;
74
+ }
75
+ // src/client/llm-directory.ts
76
+ function remoteNamespace(scope, namespace) {
77
+ try {
78
+ return scope.get(`remote.${namespace}`);
79
+ } catch {
80
+ return;
81
+ }
82
+ }
83
+ function readAt(root, path) {
84
+ return path.reduce((node, segment) => {
85
+ if (Array.isArray(node)) return node[Number(segment)];
86
+ if (typeof node !== "object" || node === null) return void 0;
87
+ return Reflect.get(node, segment);
88
+ }, root);
89
+ }
90
+ function modelsOf(entry, forms) {
91
+ if (entry.settingsNs === "") return [];
92
+ const profile = readAt(forms.get(entry.settingsNs).getSnapshot().value, entry.settingsPath);
93
+ const models = typeof profile === "object" && profile !== null ? Reflect.get(profile, "models") : void 0;
94
+ if (!Array.isArray(models)) return [];
95
+ return models.flatMap((model) => {
96
+ if (typeof model !== "object" || model === null) return [];
97
+ const id = Reflect.get(model, "id");
98
+ if (typeof id !== "string") return [];
99
+ const name = Reflect.get(model, "name");
100
+ return [{
101
+ value: id,
102
+ label: typeof name === "string" && name !== "" ? name : id
103
+ }];
104
+ });
105
+ }
106
+ async function loadLlmProviders(scope) {
107
+ const llm = remoteNamespace(scope, "llm");
108
+ const forms = scope.get("configForms");
109
+ if (llm === void 0 || forms === void 0) return [];
110
+ const [routes, directory] = await Promise.all([llm.listProviders(), llm.listConfigurableProviders()]);
111
+ if (!routes.ok || !directory.ok) return [];
112
+ const merged = /* @__PURE__ */ new Map();
113
+ for (const entry of directory.value) merged.set(entry.provider, {
114
+ value: entry.provider,
115
+ label: entry.displayName,
116
+ settingsNs: entry.settingsNs,
117
+ settingsPath: [...entry.settingsPath]
118
+ });
119
+ for (const route of routes.value) if (!merged.has(route.id)) merged.set(route.id, {
120
+ value: route.id,
121
+ label: route.name,
122
+ settingsNs: "",
123
+ settingsPath: []
124
+ });
125
+ return [...merged.values()].map((entry) => ({
126
+ value: entry.value,
127
+ label: entry.label,
128
+ models: modelsOf(entry, forms)
129
+ }));
130
+ }
62
131
  function emptyMeta() {
63
132
  return {
64
133
  description: void 0,
@@ -846,7 +915,7 @@ window.__ModuleLoader__.load({
846
915
  const name = Reflect.get(extra, "source");
847
916
  return typeof name === "string" && name.length > 0 ? name : void 0;
848
917
  }
849
- function fieldKey$1(path) {
918
+ function fieldKey(path) {
850
919
  return JSON.stringify(path);
851
920
  }
852
921
  function projectRoot(serialized, rehydrate) {
@@ -1010,23 +1079,23 @@ window.__ModuleLoader__.load({
1010
1079
  const root = this.#root;
1011
1080
  const preview = this.#model.preview();
1012
1081
  const walked = root === void 0 ? [] : walkFields(root, preview);
1013
- for (const item of walked) fields.set(fieldKey$1(item.path), this.#model.field(item.path, preview));
1082
+ for (const item of walked) fields.set(fieldKey(item.path), this.#model.field(item.path, preview));
1014
1083
  const secrets = /* @__PURE__ */ new Map();
1015
- for (const secret of this.#view?.secrets ?? []) secrets.set(fieldKey$1(secret.path), secret.set);
1084
+ for (const secret of this.#view?.secrets ?? []) secrets.set(fieldKey(secret.path), secret.set);
1016
1085
  const texts = /* @__PURE__ */ new Map();
1017
1086
  const options = /* @__PURE__ */ new Map();
1018
1087
  const addable = /* @__PURE__ */ new Map();
1019
1088
  for (const item of walked) {
1020
1089
  const text = this.#deps.hints?.textFor(item.path);
1021
- if (text !== void 0) texts.set(fieldKey$1(item.path), text);
1090
+ if (text !== void 0) texts.set(fieldKey(item.path), text);
1022
1091
  const candidates = optionsFor(item.node, item.path, preview, this.#deps.hints);
1023
- if (candidates !== void 0) options.set(fieldKey$1(item.path), candidates);
1092
+ if (candidates !== void 0) options.set(fieldKey(item.path), candidates);
1024
1093
  const addableHere = addableAt(item.node, item.path, readPath(preview, item.path), this.#deps.hints);
1025
- if (addableHere.length > 0) addable.set(fieldKey$1(item.path), addableHere);
1094
+ if (addableHere.length > 0) addable.set(fieldKey(item.path), addableHere);
1026
1095
  }
1027
1096
  const violation = this.#model.violation();
1028
1097
  const invalidAt = /* @__PURE__ */ new Map();
1029
- if (violation !== void 0 && violation.path.length > 0) invalidAt.set(fieldKey$1(violation.path), violation.message);
1098
+ if (violation !== void 0 && violation.path.length > 0) invalidAt.set(fieldKey(violation.path), violation.message);
1030
1099
  return {
1031
1100
  ...this.#model.shell(),
1032
1101
  configured: root !== void 0,
@@ -1256,7 +1325,6 @@ window.__ModuleLoader__.load({
1256
1325
  }
1257
1326
  return plans;
1258
1327
  }
1259
- const FALLBACK_PRIORITY = 100;
1260
1328
  var RowRegistration = class {
1261
1329
  #deps;
1262
1330
  #live = /* @__PURE__ */ new Map();
@@ -1291,18 +1359,6 @@ window.__ModuleLoader__.load({
1291
1359
  this.#live.delete(ns);
1292
1360
  this.#deps.release(ns);
1293
1361
  }
1294
- for (const [ns, plan] of wanted) {
1295
- if (this.#live.has(ns)) continue;
1296
- const entry = this.#deps.entryFor(ns);
1297
- if (entry === void 0) continue;
1298
- const dispose = this.#deps.slots.register({
1299
- name: "plugins.row.config",
1300
- key: plan.key,
1301
- priority: FALLBACK_PRIORITY,
1302
- locale: this.#deps.locale
1303
- }, entry.component);
1304
- this.#live.set(ns, dispose);
1305
- }
1306
1362
  }
1307
1363
  #release() {
1308
1364
  for (const [ns, dispose] of this.#live) {
@@ -1331,17 +1387,17 @@ window.__ModuleLoader__.load({
1331
1387
  }
1332
1388
  }
1333
1389
  function visibleFields(state, fold) {
1334
- const containers = new Set(state.walked.filter((item) => containerShape(item.node, valueAt(state, item.path)) !== void 0).map((item) => fieldKey$1(item.path)));
1390
+ const containers = new Set(state.walked.filter((item) => containerShape(item.node, valueAt(state, item.path)) !== void 0).map((item) => fieldKey(item.path)));
1335
1391
  return state.walked.filter((item) => {
1336
1392
  for (let depth = 1; depth < item.path.length; depth++) {
1337
1393
  const ancestor = item.path.slice(0, depth);
1338
- if (containers.has(fieldKey$1(ancestor)) && fold.collapsed(ancestor)) return false;
1394
+ if (containers.has(fieldKey(ancestor)) && fold.collapsed(ancestor)) return false;
1339
1395
  }
1340
1396
  return true;
1341
1397
  });
1342
1398
  }
1343
1399
  function valueAt(state, path) {
1344
- return state.fields.get(fieldKey$1(path))?.value;
1400
+ return state.fields.get(fieldKey(path))?.value;
1345
1401
  }
1346
1402
  function addOf(node, shape, options) {
1347
1403
  if (shape === "array") return { kind: "item" };
@@ -1398,7 +1454,7 @@ window.__ModuleLoader__.load({
1398
1454
  const shape = containerShape(node, value);
1399
1455
  if (shape !== void 0) {
1400
1456
  const collapsed = fold.collapsed(item.path);
1401
- const add = addOf(node, shape, state.addable.get(fieldKey$1(item.path)) ?? []);
1457
+ const add = addOf(node, shape, state.addable.get(fieldKey(item.path)) ?? []);
1402
1458
  lines.push({
1403
1459
  kind: "open",
1404
1460
  path: item.path,
@@ -1424,7 +1480,7 @@ window.__ModuleLoader__.load({
1424
1480
  path: item.path,
1425
1481
  node,
1426
1482
  depth,
1427
- field: state.fields.get(fieldKey$1(item.path)) ?? {
1483
+ field: state.fields.get(fieldKey(item.path)) ?? {
1428
1484
  value: void 0,
1429
1485
  text: void 0,
1430
1486
  invalid: void 0,
@@ -1466,7 +1522,7 @@ window.__ModuleLoader__.load({
1466
1522
  return lines;
1467
1523
  }
1468
1524
  function commentFor(state, item, resolveText, t) {
1469
- const key = fieldKey$1(item.path);
1525
+ const key = fieldKey(item.path);
1470
1526
  const failure = state.fields.get(key)?.invalid ?? state.invalidAt.get(key);
1471
1527
  if (failure !== void 0) return {
1472
1528
  text: failure,
@@ -1474,7 +1530,7 @@ window.__ModuleLoader__.load({
1474
1530
  };
1475
1531
  const node = unwrapLazy(item.node) ?? item.node;
1476
1532
  const parts = [];
1477
- const text = state.texts.get(fieldKey$1(item.path));
1533
+ const text = state.texts.get(fieldKey(item.path));
1478
1534
  const hint = [text?.label, text?.hint].filter((part) => part !== void 0 && part !== "").join(":") || fieldHint(node, resolveText);
1479
1535
  if (hint !== void 0 && hint !== "") parts.push(hint);
1480
1536
  if (node.meta.disabled) parts.push(t("readOnlyField"));
@@ -1499,25 +1555,25 @@ window.__ModuleLoader__.load({
1499
1555
  document.head.appendChild(tag);
1500
1556
  }
1501
1557
  var schema_form_module_css_default = {
1502
- "lineKey": "oZhWGq_lineKey",
1503
- "lineNumber": "oZhWGq_lineNumber",
1558
+ "lineRow": "oZhWGq_lineRow",
1559
+ "lineFold": "oZhWGq_lineFold",
1560
+ "lineValue": "oZhWGq_lineValue",
1504
1561
  "lineComment": "oZhWGq_lineComment",
1505
- "lineBody": "oZhWGq_lineBody",
1506
- "hint": "oZhWGq_hint",
1507
- "hoverActions": "oZhWGq_hoverActions",
1508
1562
  "lineFoldSpacer": "oZhWGq_lineFoldSpacer",
1563
+ "lineToken": "oZhWGq_lineToken",
1509
1564
  "compactInput": "oZhWGq_compactInput",
1510
- "lineFold": "oZhWGq_lineFold",
1511
1565
  "editorRoot": "oZhWGq_editorRoot",
1512
- "valueTrigger": "oZhWGq_valueTrigger",
1513
- "lineToken": "oZhWGq_lineToken",
1514
1566
  "valueSlot": "oZhWGq_valueSlot",
1567
+ "lineActions": "oZhWGq_lineActions",
1515
1568
  "compactTextField": "oZhWGq_compactTextField",
1516
- "lineRow": "oZhWGq_lineRow",
1517
- "lineValue": "oZhWGq_lineValue",
1569
+ "lineBody": "oZhWGq_lineBody",
1518
1570
  "lineInvalid": "oZhWGq_lineInvalid",
1519
- "lineActions": "oZhWGq_lineActions",
1520
- "addWrap": "oZhWGq_addWrap"
1571
+ "hoverActions": "oZhWGq_hoverActions",
1572
+ "lineNumber": "oZhWGq_lineNumber",
1573
+ "lineKey": "oZhWGq_lineKey",
1574
+ "valueTrigger": "oZhWGq_valueTrigger",
1575
+ "addWrap": "oZhWGq_addWrap",
1576
+ "hint": "oZhWGq_hint"
1521
1577
  };
1522
1578
  // ../../client/dsh-client-ui-primitives/src/client/schema-form/TruncatedTooltip.tsx
1523
1579
  function TruncatedTooltip({ label, children }) {
@@ -1793,35 +1849,35 @@ window.__ModuleLoader__.load({
1793
1849
  });
1794
1850
  },
1795
1851
  onAdded: (path) => {
1796
- setEditing(fieldKey$1(path));
1852
+ setEditing(fieldKey(path));
1797
1853
  },
1798
1854
  dismissed,
1799
1855
  onDismiss: (key) => {
1800
1856
  setDismissed((previous) => new Set(previous).add(key));
1801
1857
  },
1802
1858
  onSelect: (path) => {
1803
- setSelected(fieldKey$1(path));
1859
+ setSelected(fieldKey(path));
1804
1860
  },
1805
1861
  renderField
1806
- }, `${line.kind}:${fieldKey$1(line.path)}:${String(index)}`))
1862
+ }, `${line.kind}:${fieldKey(line.path)}:${String(index)}`))
1807
1863
  });
1808
1864
  }
1809
1865
  function useFold(state, toggled, setToggled) {
1810
1866
  const defaults = (0, react.useMemo)(() => {
1811
1867
  const collapsed = /* @__PURE__ */ new Set();
1812
1868
  for (const item of state.walked) {
1813
- const value = state.fields.get(fieldKey$1(item.path))?.value;
1814
- if (item.node.meta.collapse && containerShape(item.node, value) !== void 0) collapsed.add(fieldKey$1(item.path));
1869
+ const value = state.fields.get(fieldKey(item.path))?.value;
1870
+ if (item.node.meta.collapse && containerShape(item.node, value) !== void 0) collapsed.add(fieldKey(item.path));
1815
1871
  }
1816
1872
  return collapsed;
1817
1873
  }, [state.walked, state.fields]);
1818
1874
  return (0, react.useMemo)(() => ({
1819
1875
  collapsed: (path) => {
1820
- const key = fieldKey$1(path);
1876
+ const key = fieldKey(path);
1821
1877
  return toggled.has(key) !== defaults.has(key);
1822
1878
  },
1823
1879
  toggle: (path) => {
1824
- const key = fieldKey$1(path);
1880
+ const key = fieldKey(path);
1825
1881
  const next = new Set(toggled);
1826
1882
  if (next.has(key)) next.delete(key);
1827
1883
  else next.add(key);
@@ -1834,7 +1890,7 @@ window.__ModuleLoader__.load({
1834
1890
  ]);
1835
1891
  }
1836
1892
  function Row$1({ line, n, ns, state, face, fold, t, resolveText, disabled, editing, selected, dismissed, onEdit, onAdded, onDismiss, onSelect, renderField }) {
1837
- const key = fieldKey$1(line.path);
1893
+ const key = fieldKey(line.path);
1838
1894
  const indent = { paddingLeft: `${String(line.depth * 14)}px` };
1839
1895
  const body = line.kind === "comment" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1840
1896
  className: schema_form_module_css_default.lineFoldSpacer,
@@ -1934,7 +1990,7 @@ window.__ModuleLoader__.load({
1934
1990
  });
1935
1991
  }
1936
1992
  function FieldLine({ line, ns, state, face, t, disabled, editing, dismissed, onEdit, onDismiss, renderField }) {
1937
- const key = fieldKey$1(line.path);
1993
+ const key = fieldKey(line.path);
1938
1994
  const member = line.member;
1939
1995
  const owner = {
1940
1996
  ns,
@@ -2215,7 +2271,7 @@ window.__ModuleLoader__.load({
2215
2271
  face.addKey(line.path, raw);
2216
2272
  onAdded([...line.path, raw]);
2217
2273
  } else if (spec.kind === "item") {
2218
- const current = state.fields.get(fieldKey$1(line.path))?.value;
2274
+ const current = state.fields.get(fieldKey(line.path))?.value;
2219
2275
  const index = Array.isArray(current) ? current.length : 0;
2220
2276
  face.appendItem(line.path);
2221
2277
  onAdded([...line.path, String(index)]);
@@ -2355,7 +2411,7 @@ window.__ModuleLoader__.load({
2355
2411
  containerShape: () => containerShape,
2356
2412
  editorLines: () => editorLines,
2357
2413
  failureOf: () => failureOf,
2358
- fieldKey: () => fieldKey$1,
2414
+ fieldKey: () => fieldKey,
2359
2415
  inject: () => inject$2,
2360
2416
  optionsFor: () => optionsFor,
2361
2417
  parseFor: () => parseFor,
@@ -2501,55 +2557,55 @@ window.__ModuleLoader__.load({
2501
2557
  document.head.appendChild(tag);
2502
2558
  }
2503
2559
  var layout_module_css_default = {
2504
- "gap10": "uxI0LG_gap10",
2505
- "gap4": "uxI0LG_gap4",
2506
- "stack": "uxI0LG_stack",
2560
+ "fixed": "uxI0LG_fixed",
2561
+ "plain": "uxI0LG_plain",
2562
+ "narrow": "uxI0LG_narrow",
2563
+ "gap16": "uxI0LG_gap16",
2564
+ "gap0": "uxI0LG_gap0",
2565
+ "gap6": "uxI0LG_gap6",
2566
+ "alignStart": "uxI0LG_alignStart",
2567
+ "gap12": "uxI0LG_gap12",
2568
+ "gap14": "uxI0LG_gap14",
2569
+ "padBlocking": "uxI0LG_padBlocking",
2570
+ "padBody": "uxI0LG_padBody",
2571
+ "mono": "uxI0LG_mono",
2572
+ "grow": "uxI0LG_grow",
2507
2573
  "toneDanger": "uxI0LG_toneDanger",
2574
+ "gap2": "uxI0LG_gap2",
2575
+ "padPage": "uxI0LG_padPage",
2576
+ "justifyBetween": "uxI0LG_justifyBetween",
2577
+ "row": "uxI0LG_row",
2508
2578
  "text": "uxI0LG_text",
2509
- "wrap": "uxI0LG_wrap",
2510
- "padBlocking": "uxI0LG_padBlocking",
2511
2579
  "sizeSm": "uxI0LG_sizeSm",
2512
- "tabular": "uxI0LG_tabular",
2513
- "truncate": "uxI0LG_truncate",
2514
- "tonePrimary": "uxI0LG_tonePrimary",
2515
- "gap8": "uxI0LG_gap8",
2516
- "fill": "uxI0LG_fill",
2517
- "weightMedium": "uxI0LG_weightMedium",
2518
- "gap2": "uxI0LG_gap2",
2519
- "push": "uxI0LG_push",
2520
- "gap14": "uxI0LG_gap14",
2521
- "alignEnd": "uxI0LG_alignEnd",
2522
- "grow": "uxI0LG_grow",
2580
+ "sizeMd": "uxI0LG_sizeMd",
2581
+ "gap10": "uxI0LG_gap10",
2582
+ "gapWide": "uxI0LG_gapWide",
2523
2583
  "justifyCenter": "uxI0LG_justifyCenter",
2584
+ "alignEnd": "uxI0LG_alignEnd",
2585
+ "gap4": "uxI0LG_gap4",
2586
+ "wrap": "uxI0LG_wrap",
2524
2587
  "padCard": "uxI0LG_padCard",
2525
- "strong": "uxI0LG_strong",
2526
- "boxed": "uxI0LG_boxed",
2527
- "padRow": "uxI0LG_padRow",
2528
- "justifyEnd": "uxI0LG_justifyEnd",
2529
- "gap6": "uxI0LG_gap6",
2530
- "narrow": "uxI0LG_narrow",
2531
- "alignStart": "uxI0LG_alignStart",
2532
- "fixed": "uxI0LG_fixed",
2588
+ "fill": "uxI0LG_fill",
2533
2589
  "sizeXs": "uxI0LG_sizeXs",
2534
- "gapWide": "uxI0LG_gapWide",
2535
- "padBody": "uxI0LG_padBody",
2536
- "gap16": "uxI0LG_gap16",
2537
- "plain": "uxI0LG_plain",
2538
- "toneTertiary": "uxI0LG_toneTertiary",
2539
- "sizeBase": "uxI0LG_sizeBase",
2540
- "toneSecondary": "uxI0LG_toneSecondary",
2541
- "gap12": "uxI0LG_gap12",
2542
- "padNone": "uxI0LG_padNone",
2590
+ "justifyEnd": "uxI0LG_justifyEnd",
2591
+ "tabular": "uxI0LG_tabular",
2543
2592
  "wSearch": "uxI0LG_wSearch",
2544
- "mono": "uxI0LG_mono",
2545
- "row": "uxI0LG_row",
2546
- "sizeMd": "uxI0LG_sizeMd",
2547
- "gap0": "uxI0LG_gap0",
2548
- "justifyBetween": "uxI0LG_justifyBetween",
2549
- "scroll": "uxI0LG_scroll",
2550
2593
  "sizeLg": "uxI0LG_sizeLg",
2551
- "padPage": "uxI0LG_padPage",
2552
- "alignCenter": "uxI0LG_alignCenter"
2594
+ "padRow": "uxI0LG_padRow",
2595
+ "gap8": "uxI0LG_gap8",
2596
+ "tonePrimary": "uxI0LG_tonePrimary",
2597
+ "stack": "uxI0LG_stack",
2598
+ "push": "uxI0LG_push",
2599
+ "toneSecondary": "uxI0LG_toneSecondary",
2600
+ "toneTertiary": "uxI0LG_toneTertiary",
2601
+ "truncate": "uxI0LG_truncate",
2602
+ "boxed": "uxI0LG_boxed",
2603
+ "scroll": "uxI0LG_scroll",
2604
+ "padNone": "uxI0LG_padNone",
2605
+ "sizeBase": "uxI0LG_sizeBase",
2606
+ "alignCenter": "uxI0LG_alignCenter",
2607
+ "weightMedium": "uxI0LG_weightMedium",
2608
+ "strong": "uxI0LG_strong"
2553
2609
  };
2554
2610
  // ../../client/dsh-client-ui-primitives/src/client/layout/scales.ts
2555
2611
  function gapClass(css, gap) {
@@ -2627,15 +2683,15 @@ window.__ModuleLoader__.load({
2627
2683
  document.head.appendChild(tag);
2628
2684
  }
2629
2685
  var SettingsFieldRow_module_css_default = {
2630
- "row": "_0Kpzea_row",
2631
- "label": "_0Kpzea_label",
2632
- "hint": "_0Kpzea_hint",
2633
- "invalid": "_0Kpzea_invalid",
2686
+ "inline": "_0Kpzea_inline",
2634
2687
  "text": "_0Kpzea_text",
2688
+ "reset": "_0Kpzea_reset",
2635
2689
  "badge": "_0Kpzea_badge",
2690
+ "label": "_0Kpzea_label",
2636
2691
  "body": "_0Kpzea_body",
2637
- "inline": "_0Kpzea_inline",
2638
- "reset": "_0Kpzea_reset"
2692
+ "invalid": "_0Kpzea_invalid",
2693
+ "hint": "_0Kpzea_hint",
2694
+ "row": "_0Kpzea_row"
2639
2695
  };
2640
2696
  // ../../client/dsh-client-ui-primitives/src/client/settings-form/SettingsFieldRow.tsx
2641
2697
  function SettingsFieldRow({ label, hint, layout = "stack", divider = false, overriddenLabel, resetLabel, onReset, invalid, children, className, ...rest }) {
@@ -2722,8 +2778,8 @@ window.__ModuleLoader__.load({
2722
2778
  document.head.appendChild(tag);
2723
2779
  }
2724
2780
  var Button_module_css_default = {
2725
- "danger": "SBA8ka_danger",
2726
- "label": "SBA8ka_label"
2781
+ "label": "SBA8ka_label",
2782
+ "danger": "SBA8ka_danger"
2727
2783
  };
2728
2784
  // ../../client/dsh-client-ui-primitives/src/client/controls/Button.tsx
2729
2785
  const Button$1 = (0, react.forwardRef)(function Button({ className, tone, ...rest }, ref) {
@@ -2767,11 +2823,11 @@ window.__ModuleLoader__.load({
2767
2823
  document.head.appendChild(tag);
2768
2824
  }
2769
2825
  var SeatButton_module_css_default = {
2826
+ "expanded": "io5HbW_expanded",
2770
2827
  "chevron": "io5HbW_chevron",
2771
- "seat": "io5HbW_seat",
2772
2828
  "label": "io5HbW_label",
2773
- "icon": "io5HbW_icon",
2774
- "expanded": "io5HbW_expanded"
2829
+ "seat": "io5HbW_seat",
2830
+ "icon": "io5HbW_icon"
2775
2831
  };
2776
2832
  // ../../client/dsh-client-ui-primitives/src/client/controls/SeatButton.tsx
2777
2833
  function SeatButton({ icon, label, expanded = false, chevron, type = "button", className, ...rest }) {
@@ -2866,9 +2922,9 @@ window.__ModuleLoader__.load({
2866
2922
  document.head.appendChild(tag);
2867
2923
  }
2868
2924
  var IconButton_module_css_default = {
2869
- "circle": "G842ma_circle",
2870
2925
  "sm": "G842ma_sm",
2871
- "iconButton": "G842ma_iconButton"
2926
+ "iconButton": "G842ma_iconButton",
2927
+ "circle": "G842ma_circle"
2872
2928
  };
2873
2929
  // ../../client/dsh-client-ui-primitives/src/client/controls/IconButton.tsx
2874
2930
  function IconButton({ label, children, type = "button", shape = "square", size = "md", className, ...rest }) {
@@ -2892,16 +2948,16 @@ window.__ModuleLoader__.load({
2892
2948
  document.head.appendChild(tag);
2893
2949
  }
2894
2950
  var ModelRouteList_module_css_default = {
2895
- "group": "aGmQkW_group",
2951
+ "list": "aGmQkW_list",
2896
2952
  "id": "aGmQkW_id",
2953
+ "provider": "aGmQkW_provider",
2897
2954
  "item": "aGmQkW_item",
2898
- "error": "aGmQkW_error",
2899
- "list": "aGmQkW_list",
2955
+ "group": "aGmQkW_group",
2900
2956
  "name": "aGmQkW_name",
2901
- "text": "aGmQkW_text",
2957
+ "error": "aGmQkW_error",
2958
+ "notice": "aGmQkW_notice",
2902
2959
  "picked": "aGmQkW_picked",
2903
- "provider": "aGmQkW_provider",
2904
- "notice": "aGmQkW_notice"
2960
+ "text": "aGmQkW_text"
2905
2961
  };
2906
2962
  // ../../client/dsh-client-ui-primitives/src/client/controls/ModelRouteList.tsx
2907
2963
  function ModelRouteList({ label, candidates, selectedKey, disabled = false, status, loadingLabel, errorLabel, emptyLabel, onSelect }) {
@@ -3005,9 +3061,9 @@ window.__ModuleLoader__.load({
3005
3061
  }
3006
3062
  var SearchSelect_module_css_default = {
3007
3063
  "trigger": "pNIIcq_trigger",
3064
+ "searchRow": "pNIIcq_searchRow",
3008
3065
  "searchInput": "pNIIcq_searchInput",
3009
- "empty": "pNIIcq_empty",
3010
- "searchRow": "pNIIcq_searchRow"
3066
+ "empty": "pNIIcq_empty"
3011
3067
  };
3012
3068
  // ../../client/dsh-client-ui-primitives/src/client/controls/SearchSelect.tsx
3013
3069
  function SearchSelect({ label, value, emptyLabel, options, disabled = false, searchLabel, noMatchLabel, onSelect }) {
@@ -3117,13 +3173,13 @@ window.__ModuleLoader__.load({
3117
3173
  document.head.appendChild(tag);
3118
3174
  }
3119
3175
  var TagInput_module_css_default = {
3120
- "chip": "_0uSpIq_chip",
3121
- "empty": "_0uSpIq_empty",
3122
3176
  "chipRemove": "_0uSpIq_chipRemove",
3123
- "row": "_0uSpIq_row",
3124
- "anchor": "_0uSpIq_anchor",
3177
+ "empty": "_0uSpIq_empty",
3178
+ "chip": "_0uSpIq_chip",
3179
+ "input": "_0uSpIq_input",
3125
3180
  "box": "_0uSpIq_box",
3126
- "input": "_0uSpIq_input"
3181
+ "anchor": "_0uSpIq_anchor",
3182
+ "row": "_0uSpIq_row"
3127
3183
  };
3128
3184
  // ../../client/dsh-client-ui-primitives/src/client/controls/TagInput.tsx
3129
3185
  function TagInput({ value, onChange, options, placeholder, label, disabled = false, noMatchLabel = "", removeLabel }) {
@@ -18678,7 +18734,7 @@ window.__ModuleLoader__.load({
18678
18734
  const child = children[index];
18679
18735
  if (child === void 0) continue;
18680
18736
  if (child.type === "link") {
18681
- const reference = parseReference(child.url ?? "", labelOf$1(child));
18737
+ const reference = parseReference(child.url ?? "", labelOf(child));
18682
18738
  if (reference !== void 0) {
18683
18739
  children[index] = referenceNodeOf(reference, child, children[index - 1]);
18684
18740
  continue;
@@ -18713,14 +18769,14 @@ window.__ModuleLoader__.load({
18713
18769
  end: position.end
18714
18770
  };
18715
18771
  }
18716
- function labelOf$1(node) {
18717
- const label = textOf$1(node);
18772
+ function labelOf(node) {
18773
+ const label = textOf(node);
18718
18774
  return label === "" ? void 0 : label;
18719
18775
  }
18720
- function textOf$1(node) {
18776
+ function textOf(node) {
18721
18777
  if (node.value !== void 0) return node.value;
18722
18778
  let text = "";
18723
- for (const child of node.children ?? []) text += textOf$1(child);
18779
+ for (const child of node.children ?? []) text += textOf(child);
18724
18780
  return text;
18725
18781
  }
18726
18782
  function parseBareReference(raw) {
@@ -18921,1290 +18977,850 @@ window.__ModuleLoader__.load({
18921
18977
  function apply$1(ctx) {
18922
18978
  apply$2(ctx);
18923
18979
  }
18924
- // src/client/llm-directory.ts
18925
- function routeKey(provider, model) {
18926
- return `${provider}\u0000${model}`;
18980
+ // src/mode-config.ts
18981
+ function applyDraft(config, draft, defaults) {
18982
+ const view = { ...structuredClone(config) };
18983
+ for (const [kind, allowKey, denyKey] of [
18984
+ [
18985
+ "tools",
18986
+ "allowTools",
18987
+ "denyTools"
18988
+ ],
18989
+ [
18990
+ "skills",
18991
+ "allowSkills",
18992
+ "denySkills"
18993
+ ],
18994
+ [
18995
+ "policies",
18996
+ "allowPolicies",
18997
+ "denyPolicies"
18998
+ ]
18999
+ ]) {
19000
+ const allow = new Set(view[allowKey]);
19001
+ const deny = new Set(view[denyKey]);
19002
+ for (const [id, value] of Object.entries(draft.lists?.[kind] ?? {})) if (value === "deny") deny.add(id);
19003
+ else if (value === "allow") {
19004
+ deny.delete(id);
19005
+ if (allow.size > 0) allow.add(id);
19006
+ } else {
19007
+ allow.delete(id);
19008
+ deny.delete(id);
19009
+ }
19010
+ view[allowKey] = [...allow];
19011
+ view[denyKey] = [...deny];
19012
+ }
19013
+ if (draft.persona !== void 0 && draft.persona !== null) view["persona"] = draft.persona;
19014
+ for (const [key, value] of Object.entries(draft.switches ?? {})) if (value !== null) view[key] = value;
19015
+ else if (defaults !== void 0) view[key] = defaults[key];
19016
+ return view;
18927
19017
  }
18928
- function remoteNamespace(scope, namespace) {
18929
- try {
18930
- return scope.get(`remote.${namespace}`);
18931
- } catch {
18932
- return;
19018
+ // src/client/api.ts
19019
+ async function fetchRoster() {
19020
+ const response = await fetch(SESSION_MODE_PATH, {
19021
+ method: "GET",
19022
+ headers: { accept: "application/json" }
19023
+ });
19024
+ if (!response.ok) throw await failure(response);
19025
+ return await response.json();
19026
+ }
19027
+ async function selectMode(sessionId, mode) {
19028
+ const response = await fetch(SESSION_MODE_PATH, {
19029
+ method: "POST",
19030
+ headers: {
19031
+ accept: "application/json",
19032
+ "content-type": "application/json"
19033
+ },
19034
+ body: JSON.stringify({
19035
+ sessionId,
19036
+ mode
19037
+ })
19038
+ });
19039
+ const value = await response.json().catch(() => void 0);
19040
+ if (!response.ok) {
19041
+ const error = value?.["error"];
19042
+ throw new Error(typeof error === "string" ? error : `HTTP ${String(response.status)}`);
18933
19043
  }
19044
+ return value?.mode ?? mode;
18934
19045
  }
18935
- function readAt(root, path) {
18936
- return path.reduce((node, segment) => {
18937
- if (Array.isArray(node)) return node[Number(segment)];
18938
- if (typeof node !== "object" || node === null) return void 0;
18939
- return Reflect.get(node, segment);
18940
- }, root);
19046
+ async function failure(response) {
19047
+ const value = await response.json().catch(() => void 0);
19048
+ return new Error(typeof value?.error === "string" ? value.error : `HTTP ${String(response.status)}`);
18941
19049
  }
18942
- function modelsOf(entry, forms) {
18943
- if (entry.settingsNs === "") return [];
18944
- const profile = readAt(forms.get(entry.settingsNs).getSnapshot().value, entry.settingsPath);
18945
- const models = typeof profile === "object" && profile !== null ? Reflect.get(profile, "models") : void 0;
18946
- if (!Array.isArray(models)) return [];
18947
- return models.flatMap((model) => {
18948
- if (typeof model !== "object" || model === null) return [];
18949
- const id = Reflect.get(model, "id");
18950
- if (typeof id !== "string") return [];
18951
- const name = Reflect.get(model, "name");
18952
- return [{
18953
- value: id,
18954
- label: typeof name === "string" && name !== "" ? name : id
18955
- }];
19050
+ async function fetchActive(sessionId) {
19051
+ const response = await fetch(`${SESSION_MODE_PATH}?sessionId=${encodeURIComponent(sessionId)}`, {
19052
+ method: "GET",
19053
+ headers: { accept: "application/json" }
18956
19054
  });
19055
+ if (!response.ok) throw await failure(response);
19056
+ return await response.json();
18957
19057
  }
18958
- async function loadLlmProviders(scope) {
18959
- const llm = remoteNamespace(scope, "llm");
18960
- const forms = scope.get("configForms");
18961
- if (llm === void 0 || forms === void 0) return [];
18962
- const [routes, directory] = await Promise.all([llm.listProviders(), llm.listConfigurableProviders()]);
18963
- if (!routes.ok || !directory.ok) return [];
18964
- const merged = /* @__PURE__ */ new Map();
18965
- for (const entry of directory.value) merged.set(entry.provider, {
18966
- value: entry.provider,
18967
- label: entry.displayName,
18968
- settingsNs: entry.settingsNs,
18969
- settingsPath: [...entry.settingsPath]
19058
+ async function saveAsNewMode(sessionId, code, name) {
19059
+ const response = await fetch(SESSION_MODE_PATH, {
19060
+ method: "POST",
19061
+ headers: {
19062
+ accept: "application/json",
19063
+ "content-type": "application/json"
19064
+ },
19065
+ body: JSON.stringify({
19066
+ sessionId,
19067
+ saveAsNewMode: {
19068
+ code,
19069
+ name
19070
+ }
19071
+ })
18970
19072
  });
18971
- for (const route of routes.value) if (!merged.has(route.id)) merged.set(route.id, {
18972
- value: route.id,
18973
- label: route.name,
18974
- settingsNs: "",
18975
- settingsPath: []
19073
+ if (!response.ok) throw await failure(response);
19074
+ }
19075
+ async function deleteMode(sessionId) {
19076
+ const response = await fetch(SESSION_MODE_PATH, {
19077
+ method: "POST",
19078
+ headers: {
19079
+ accept: "application/json",
19080
+ "content-type": "application/json"
19081
+ },
19082
+ body: JSON.stringify({
19083
+ sessionId,
19084
+ deleteMode: true
19085
+ })
18976
19086
  });
18977
- return [...merged.values()].map((entry) => ({
18978
- value: entry.value,
18979
- label: entry.label,
18980
- models: modelsOf(entry, forms)
18981
- }));
19087
+ if (!response.ok) throw await failure(response);
19088
+ return await response.json();
18982
19089
  }
18983
- function routesOf(providers) {
18984
- return providers.flatMap((provider) => provider.models.map((model) => ({
18985
- key: routeKey(provider.value, model.value),
18986
- provider: provider.value,
18987
- providerName: provider.label,
18988
- model: model.value,
18989
- modelName: model.label
18990
- })));
19090
+ async function applySessionConfig(sessionId, config) {
19091
+ const response = await fetch(SESSION_MODE_PATH, {
19092
+ method: "POST",
19093
+ headers: {
19094
+ accept: "application/json",
19095
+ "content-type": "application/json"
19096
+ },
19097
+ body: JSON.stringify({
19098
+ sessionId,
19099
+ applyConfig: { config }
19100
+ })
19101
+ });
19102
+ if (!response.ok) throw await failure(response);
19103
+ return await response.json();
18991
19104
  }
18992
- // src/client/bundle-config.ts
18993
- const SESSION_MODE_NS = "session-mode";
18994
- const BUNDLE_CONFIG_KEY = "@morlay/session-mode-profile";
18995
- const PROTECTED_MODE_IDS = ["noop"];
18996
- const ROLE_VALUES = ["main", "subagent"];
18997
- const GROUPS = [
18998
- {
18999
- key: "identity",
19000
- labelKey: "group.identity"
19001
- },
19002
- {
19003
- key: "persona",
19004
- labelKey: "group.persona"
19005
- },
19006
- {
19007
- key: "tools",
19008
- labelKey: "group.tools"
19009
- },
19010
- {
19011
- key: "rules",
19012
- labelKey: "group.rules"
19013
- },
19014
- {
19015
- key: "injections",
19016
- labelKey: "group.injections"
19017
- },
19018
- {
19019
- key: "model",
19020
- labelKey: "group.model"
19021
- }
19022
- ];
19023
- const MODE_FIELDS = [
19024
- {
19025
- path: ["name"],
19026
- control: "text",
19027
- labelKey: "field.name",
19028
- hintKey: "hint.name",
19029
- group: "identity"
19030
- },
19031
- {
19032
- path: ["description"],
19033
- control: "text",
19034
- labelKey: "field.description",
19035
- hintKey: "hint.description",
19036
- group: "identity"
19037
- },
19038
- {
19039
- path: ["role"],
19040
- control: "roles",
19041
- labelKey: "field.role",
19042
- hintKey: "hint.role",
19043
- group: "identity"
19044
- },
19045
- {
19046
- path: ["presetsOnly"],
19047
- control: "tags",
19048
- labelKey: "field.presetsOnly",
19049
- hintKey: "hint.presetsOnly",
19050
- group: "identity"
19051
- },
19052
- {
19053
- path: ["persona", "prefix"],
19054
- control: "multiline",
19055
- labelKey: "field.prefix",
19056
- hintKey: "hint.prefix",
19057
- group: "persona"
19058
- },
19059
- {
19060
- path: ["persona", "suffix"],
19061
- control: "multiline",
19062
- labelKey: "field.suffix",
19063
- hintKey: "hint.suffix",
19064
- group: "persona"
19065
- },
19066
- {
19067
- path: ["allowTools"],
19068
- control: "tags",
19069
- labelKey: "field.allowTools",
19070
- hintKey: "hint.allowTools",
19071
- group: "tools"
19072
- },
19073
- {
19074
- path: ["denyTools"],
19075
- control: "tags",
19076
- labelKey: "field.denyTools",
19077
- hintKey: "hint.denyTools",
19078
- group: "tools"
19079
- },
19080
- {
19081
- path: ["allowSkills"],
19082
- control: "tags",
19083
- labelKey: "field.allowSkills",
19084
- hintKey: "hint.allowSkills",
19085
- group: "tools"
19086
- },
19087
- {
19088
- path: ["denySkills"],
19089
- control: "tags",
19090
- labelKey: "field.denySkills",
19091
- hintKey: "hint.denySkills",
19092
- group: "tools"
19093
- },
19094
- {
19095
- path: ["allowPolicies"],
19096
- control: "tags",
19097
- labelKey: "field.allowPolicies",
19098
- hintKey: "hint.allowPolicies",
19099
- group: "rules"
19100
- },
19101
- {
19102
- path: ["denyPolicies"],
19103
- control: "tags",
19104
- labelKey: "field.denyPolicies",
19105
- hintKey: "hint.denyPolicies",
19106
- group: "rules"
19107
- },
19108
- {
19109
- path: ["instructions"],
19110
- control: "switch",
19111
- labelKey: "field.instructions",
19112
- hintKey: "hint.instructions",
19113
- group: "injections"
19114
- },
19115
- {
19116
- path: ["skills"],
19117
- control: "tri",
19118
- labelKey: "field.skills",
19119
- hintKey: "hint.skills",
19120
- group: "injections"
19121
- },
19122
- {
19123
- path: ["runtimeContext"],
19124
- control: "switch",
19125
- labelKey: "field.runtimeContext",
19126
- hintKey: "hint.runtimeContext",
19127
- group: "injections"
19128
- },
19129
- {
19130
- path: ["defaultModel", "reasoningEffort"],
19131
- control: "text",
19132
- labelKey: "field.reasoningEffort",
19133
- hintKey: "hint.reasoningEffort",
19134
- group: "model"
19135
- }
19136
- ];
19137
- function unwrapService(value) {
19138
- if (value === void 0) return void 0;
19139
- return value[Symbol.for("cordis.original")] ?? value;
19105
+ async function saveConfigDraft(sessionId, config) {
19106
+ const response = await fetch(SESSION_MODE_PATH, {
19107
+ method: "POST",
19108
+ headers: {
19109
+ accept: "application/json",
19110
+ "content-type": "application/json"
19111
+ },
19112
+ body: JSON.stringify({
19113
+ sessionId,
19114
+ saveConfig: { config }
19115
+ })
19116
+ });
19117
+ if (!response.ok) throw await failure(response);
19118
+ return await response.json();
19140
19119
  }
19141
- function fieldKey(path) {
19142
- return JSON.stringify(path);
19120
+ // src/client/SessionModePanel.tsx
19121
+ const PANEL_STYLE = {
19122
+ height: "100%",
19123
+ overflowY: "auto",
19124
+ boxSizing: "border-box",
19125
+ padding: "20px 24px 40px"
19126
+ };
19127
+ function Section({ title, open, onToggle, children }) {
19128
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19129
+ "data-role": "section",
19130
+ "data-section": title,
19131
+ style: { borderTop: "1px solid var(--dsw-alias-border-l1)" },
19132
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
19133
+ type: "button",
19134
+ "data-role": "section-heading",
19135
+ onClick: onToggle,
19136
+ style: {
19137
+ width: "100%",
19138
+ display: "flex",
19139
+ alignItems: "center",
19140
+ justifyContent: "space-between",
19141
+ background: "none",
19142
+ border: 0,
19143
+ paddingBlock: 12,
19144
+ paddingInline: 0,
19145
+ font: "inherit",
19146
+ fontSize: 13,
19147
+ fontWeight: 500,
19148
+ color: "inherit",
19149
+ cursor: "pointer"
19150
+ },
19151
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
19152
+ "data-role": "section-icon",
19153
+ style: {
19154
+ display: "inline-flex",
19155
+ alignItems: "center",
19156
+ justifyContent: "center",
19157
+ width: 16,
19158
+ height: 16,
19159
+ fontSize: 14,
19160
+ lineHeight: 1,
19161
+ color: "var(--dsw-alias-label-tertiary)"
19162
+ },
19163
+ children: open ? "−" : "+"
19164
+ })]
19165
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19166
+ style: { padding: "0 0 8px" },
19167
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stack, {
19168
+ gap: 6,
19169
+ children
19170
+ })
19171
+ }) : null]
19172
+ });
19143
19173
  }
19144
- function createBundleConfigFace(ctx, t, hintsOf = () => void 0) {
19145
- const forms = ctx.configForms;
19146
- const hints = () => unwrapService(hintsOf());
19147
- const deps = () => ({
19148
- form: forms.get(SESSION_MODE_NS),
19149
- describe: forms.describe(),
19150
- rehydrate: (serialized) => ctx.settingsSchema.rehydrate(serialized),
19151
- validate: (schema, value) => validateSection(schema, value, t),
19152
- t: ctx.locale.bind("settings.schema-form"),
19153
- hints: {
19154
- keysFor: () => [],
19155
- textFor: (path) => hints()?.textFor("session-mode", path) ?? {},
19156
- selectFor: (path) => hints()?.selectFor(SESSION_MODE_NS, path),
19157
- sourceFor: (name) => hints()?.sourceFor(name)
19158
- }
19174
+ function TriStateRow({ id, description, value, indent = 0, t, onChange, onLabelClick }) {
19175
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19176
+ "data-role": "tri-state",
19177
+ "data-id": id,
19178
+ style: {
19179
+ display: "flex",
19180
+ flexWrap: "wrap",
19181
+ alignItems: "center",
19182
+ justifyContent: "space-between",
19183
+ gap: 8,
19184
+ padding: "6px 0",
19185
+ paddingLeft: indent
19186
+ },
19187
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
19188
+ style: {
19189
+ display: "flex",
19190
+ flexDirection: "column",
19191
+ gap: 2,
19192
+ minWidth: 0,
19193
+ flex: "1 1 140px"
19194
+ },
19195
+ children: [onLabelClick === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19196
+ size: "sm",
19197
+ children: id
19198
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
19199
+ type: "button",
19200
+ onClick: onLabelClick,
19201
+ style: {
19202
+ background: "none",
19203
+ border: 0,
19204
+ padding: 0,
19205
+ font: "inherit",
19206
+ fontSize: 13,
19207
+ color: "inherit",
19208
+ textAlign: "left",
19209
+ cursor: "pointer"
19210
+ },
19211
+ children: id
19212
+ }), description === void 0 || description.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19213
+ size: "sm",
19214
+ tone: "tertiary",
19215
+ children: description
19216
+ })]
19217
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19218
+ style: {
19219
+ maxWidth: "100%",
19220
+ marginLeft: "auto"
19221
+ },
19222
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.SegmentedControl, {
19223
+ id: `tri-${id.replace(/[^a-zA-Z0-9_-]+/gu, "-")}`,
19224
+ label: id,
19225
+ value,
19226
+ options: value === "mixed" ? [
19227
+ {
19228
+ value: "mixed",
19229
+ label: t("stateMixed")
19230
+ },
19231
+ {
19232
+ value: "unset",
19233
+ label: t("stateDefault")
19234
+ },
19235
+ {
19236
+ value: "allow",
19237
+ label: t("stateAllow")
19238
+ },
19239
+ {
19240
+ value: "deny",
19241
+ label: t("stateDeny")
19242
+ }
19243
+ ] : [
19244
+ {
19245
+ value: "unset",
19246
+ label: t("stateDefault")
19247
+ },
19248
+ {
19249
+ value: "allow",
19250
+ label: t("stateAllow")
19251
+ },
19252
+ {
19253
+ value: "deny",
19254
+ label: t("stateDeny")
19255
+ }
19256
+ ],
19257
+ onChange: (next) => {
19258
+ if (next === "mixed") return;
19259
+ onChange(next);
19260
+ }
19261
+ })
19262
+ })]
19159
19263
  });
19160
- const controller = new SchemaFormController(SESSION_MODE_NS, deps());
19161
- const face = controller.face();
19162
- const form = forms.get(SESSION_MODE_NS);
19163
- const statusStore = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(form.getSnapshot().status);
19164
- const unsubscribeStatus = form.subscribe(() => {
19165
- statusStore.set(form.getSnapshot().status);
19264
+ }
19265
+ function SessionModePanel({ sessionId, useSessions, t }) {
19266
+ const mode = useSessions((state) => {
19267
+ const value = sessionId === void 0 ? void 0 : state.byId[sessionId]?.projectionValues?.sessionMode;
19268
+ return typeof value === "string" ? value : void 0;
19166
19269
  });
19167
- const modelsStore = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
19168
- status: "loading",
19169
- routes: []
19270
+ const preset = useSessions((state) => {
19271
+ const value = sessionId === void 0 ? void 0 : state.byId[sessionId]?.projectionValues?.agentPreset;
19272
+ return typeof value === "string" ? value : void 0;
19170
19273
  });
19171
- const loadModels = async () => {
19172
- try {
19173
- const providers = await loadLlmProviders(ctx);
19174
- modelsStore.set({
19175
- status: "ready",
19176
- routes: routesOf(providers)
19177
- });
19178
- } catch {
19179
- modelsStore.set({
19180
- status: "error",
19181
- routes: []
19182
- });
19274
+ const [load, setLoad] = (0, react.useState)({ kind: "loading" });
19275
+ const [notice, setNotice] = (0, react.useState)(null);
19276
+ const [draft, setDraft] = (0, react.useState)({
19277
+ switches: {},
19278
+ items: {
19279
+ tools: {},
19280
+ skills: {},
19281
+ policies: {}
19183
19282
  }
19184
- };
19185
- const remote = ctx.get("remote");
19186
- const unsubscribeModels = typeof remote?.$on !== "function" ? [] : [remote.$on("llm/adapters-updated", () => {
19187
- loadModels();
19188
- }), remote.$on("settings/document-updated", () => {
19189
- loadModels();
19190
- })];
19191
- loadModels();
19192
- const diagnose = () => {
19193
- const view = forms.describe().getSnapshot().view;
19194
- const namespaces = view?.namespaces.map((row) => row.ns) ?? [];
19195
- const live = controller.face().hooks.schemaForm.getSnapshot();
19196
- const controllerSummary = `store configured=${String(live.configured)} walked=${String(live.walked.length)}`;
19197
- const own = view?.namespaces.find((row) => row.ns === SESSION_MODE_NS);
19198
- if (own === void 0) return {
19199
- namespaces,
19200
- controller: controllerSummary,
19201
- problem: `describe has no namespace "${SESSION_MODE_NS}"`
19202
- };
19203
- try {
19204
- const schema = ctx.settingsSchema.rehydrate(own.schema);
19205
- const renderable = projectRoot(own.schema, (serialized) => ctx.settingsSchema.rehydrate(serialized)) !== void 0;
19206
- let probe;
19207
- try {
19208
- const rebuilt = new SchemaFormController(SESSION_MODE_NS, deps());
19209
- probe = `probe configured=${String(rebuilt.face().hooks.schemaForm.getSnapshot().configured)}`;
19210
- rebuilt.dispose();
19211
- } catch (error) {
19212
- probe = `probe threw: ${error instanceof Error ? error.message : String(error)}`;
19213
- }
19214
- return {
19215
- namespaces,
19216
- controller: `${controllerSummary}; ${probe}`,
19217
- problem: renderable ? "renderable" : `schema root is "${String(schema.type)}"`
19218
- };
19219
- } catch (error) {
19220
- return {
19221
- namespaces,
19222
- controller: controllerSummary,
19223
- problem: `schema rehydrate failed: ${error instanceof Error ? error.message : String(error)}`
19224
- };
19225
- }
19226
- };
19227
- const resync = () => {
19228
- statusStore.set(form.getSnapshot().status);
19229
- controller.refresh();
19230
- };
19231
- return {
19232
- face: {
19233
- hooks: {
19234
- bundleConfig: face.hooks.schemaForm,
19235
- bundleStatus: statusStore,
19236
- bundleModels: modelsStore
19237
- },
19238
- diagnose,
19239
- resync,
19240
- set: (path, value) => {
19241
- face.set(path, value);
19242
- },
19243
- editText: (path, text) => {
19244
- if (text === "") face.clear(path);
19245
- else face.setText(path, text, (raw) => ({
19246
- kind: "value",
19247
- value: raw
19248
- }));
19249
- },
19250
- clear: (path) => {
19251
- face.clear(path);
19252
- },
19253
- appendItem: (path) => {
19254
- face.appendItem(path);
19255
- },
19256
- removeItem: (path, index) => {
19257
- face.removeItem(path, index);
19258
- },
19259
- addMode: (id) => {
19260
- const trimmed = id.trim();
19261
- if (trimmed === "") return;
19262
- face.addKey(["modes"], trimmed);
19263
- face.setText([
19264
- "modes",
19265
- trimmed,
19266
- "name"
19267
- ], trimmed, (raw) => ({
19268
- kind: "value",
19269
- value: raw
19270
- }));
19271
- },
19272
- removeMode: (id) => {
19273
- if (PROTECTED_MODE_IDS.includes(id)) return;
19274
- face.removeKey(["modes"], id);
19275
- },
19276
- save: () => {
19277
- face.save();
19278
- },
19279
- discard: () => {
19280
- face.discard();
19281
- }
19282
- },
19283
- refresh: resync,
19284
- dispose: () => {
19285
- unsubscribeStatus();
19286
- for (const off of unsubscribeModels) off?.();
19287
- controller.dispose();
19283
+ });
19284
+ const [dialog, setDialog] = (0, react.useState)(null);
19285
+ const [menuOpen, setMenuOpen] = (0, react.useState)(false);
19286
+ const [code, setCode] = (0, react.useState)("");
19287
+ const [name, setName] = (0, react.useState)("");
19288
+ const [openSection, setOpenSection] = (0, react.useState)("general");
19289
+ const [openGroups, setOpenGroups] = (0, react.useState)(/* @__PURE__ */ new Set());
19290
+ (0, react.useEffect)(() => {
19291
+ if (sessionId === void 0) {
19292
+ setLoad({
19293
+ kind: "error",
19294
+ message: t("panelNoSession")
19295
+ });
19296
+ return;
19288
19297
  }
19289
- };
19290
- }
19291
- function validateSection(schema, value, t) {
19292
- try {
19293
- schema(value);
19294
- } catch (error) {
19295
- return failureOf(error);
19296
- }
19297
- return rosterProblem(value, t);
19298
- }
19299
- function rosterProblem(value, t) {
19300
- if (typeof value !== "object" || value === null) return void 0;
19301
- const modes = Reflect.get(value, "modes");
19302
- if (typeof modes !== "object" || modes === null || Array.isArray(modes)) return {
19303
- message: t("violation.modesEmpty"),
19304
- path: ["modes"]
19305
- };
19306
- const ids = Object.keys(modes);
19307
- if (ids.length === 0) return {
19308
- message: t("violation.modesEmpty"),
19309
- path: ["modes"]
19310
- };
19311
- const selected = Reflect.get(value, "default");
19312
- if (typeof selected === "string" && selected !== "" && !ids.includes(selected)) return {
19313
- message: t("violation.defaultMissing"),
19314
- path: ["default"]
19315
- };
19316
- for (const id of ids) {
19317
- const mode = Reflect.get(modes, id);
19318
- const model = typeof mode === "object" && mode !== null ? Reflect.get(mode, "defaultModel") : void 0;
19319
- if (typeof model !== "object" || model === null) continue;
19320
- const provider = Reflect.get(model, "provider");
19321
- const name = Reflect.get(model, "model");
19322
- if (provider === void 0 && name === void 0) continue;
19323
- if (typeof provider === "string" && provider !== "" && typeof name === "string" && name !== "") continue;
19324
- return {
19325
- message: t("violation.modelPair"),
19326
- path: [
19327
- "modes",
19328
- id,
19329
- "defaultModel"
19330
- ]
19298
+ let cancelled = false;
19299
+ setLoad({ kind: "loading" });
19300
+ fetchActive(sessionId).then((value) => {
19301
+ if (!cancelled) setLoad({
19302
+ kind: "ready",
19303
+ value
19304
+ });
19305
+ }).catch((error) => {
19306
+ if (!cancelled) setLoad({
19307
+ kind: "error",
19308
+ message: error instanceof Error ? error.message : String(error)
19309
+ });
19310
+ });
19311
+ return () => {
19312
+ cancelled = true;
19331
19313
  };
19332
- }
19333
- }
19334
- function projectBundleConfig(state, t, status = "ready") {
19335
- const read = (path) => state.fields.get(fieldKey(path));
19336
- const optionsAt = (path) => state.options.get(fieldKey(path)) ?? [];
19337
- const invalidAt = (path) => state.invalidAt.get(fieldKey(path)) ?? read(path)?.invalid;
19338
- const modes = collectModeIds(state);
19339
- const selected = textOf(read(["default"]));
19340
- return {
19341
- available: state.available,
19342
- writable: state.writable,
19343
- dirty: state.dirty,
19344
- invalid: state.invalid,
19345
- saving: state.saving,
19346
- failed: state.failed,
19347
- configured: state.configured,
19348
- readiness: state.configured ? "ready" : status === "loading" ? "loading" : status === "ready" ? "unreadable" : "missing",
19349
- defaultMode: {
19350
- value: selected,
19351
- options: modes.map((id) => ({
19352
- value: id,
19353
- label: labelOf(id, read)
19354
- })),
19355
- invalid: invalidAt(["default"]) ?? (selected === "" || modes.includes(selected) ? void 0 : t("violation.defaultMissing"))
19356
- },
19357
- modes: modes.map((id) => ({
19358
- id,
19359
- title: labelOf(id, read),
19360
- role: arrayOf(read([
19361
- "modes",
19362
- id,
19363
- "role"
19364
- ])),
19365
- summary: summaryOf(id, read, t),
19366
- model: modelOf(id, read),
19367
- deletable: !PROTECTED_MODE_IDS.includes(id),
19368
- staged: MODE_FIELDS.some((spec) => read([
19369
- "modes",
19370
- id,
19371
- ...spec.path
19372
- ])?.staged === true),
19373
- groups: GROUPS.map((group) => ({
19374
- key: group.key,
19375
- label: t(group.labelKey),
19376
- fields: MODE_FIELDS.filter((spec) => spec.group === group.key).map((spec) => fieldView(id, spec, read, optionsAt, invalidAt, t))
19377
- }))
19378
- })),
19379
- violation: state.violation?.message
19380
- };
19381
- }
19382
- function labelOf(id, read) {
19383
- const name = textOf(read([
19384
- "modes",
19385
- id,
19386
- "name"
19387
- ]));
19388
- return name === "" ? id : name;
19389
- }
19390
- function collectModeIds(state) {
19391
- const ids = [];
19392
- for (const item of state.walked) {
19393
- if (item.path.length < 2 || item.path[0] !== "modes") continue;
19394
- const id = item.path[1];
19395
- if (id === void 0 || ids.includes(id)) continue;
19396
- ids.push(id);
19397
- }
19398
- return ids;
19399
- }
19400
- function modelOf(id, read) {
19401
- if (read([
19402
- "modes",
19403
- id,
19404
- "defaultModel"
19405
- ]) === void 0) return void 0;
19406
- return {
19407
- provider: textOf(read([
19408
- "modes",
19409
- id,
19410
- "defaultModel",
19411
- "provider"
19412
- ])),
19413
- model: textOf(read([
19414
- "modes",
19415
- id,
19416
- "defaultModel",
19417
- "model"
19418
- ]))
19419
- };
19420
- }
19421
- function fieldView(id, spec, read, optionsAt, invalidAt, t) {
19422
- const path = [
19423
- "modes",
19424
- id,
19425
- ...spec.path
19426
- ];
19427
- const field = read(path);
19428
- const common = {
19429
- key: fieldKey(path),
19430
- path,
19431
- control: spec.control,
19432
- label: t(spec.labelKey),
19433
- hint: t(spec.hintKey),
19434
- present: field !== void 0,
19435
- overridden: field?.overridden === true,
19436
- options: optionsAt(path),
19437
- invalid: invalidAt(path)
19438
- };
19439
- if (spec.control === "tags" || spec.control === "roles") return {
19440
- ...common,
19441
- text: "",
19442
- value: arrayOf(field)
19443
- };
19444
- if (spec.control === "switch") return {
19445
- ...common,
19446
- text: "",
19447
- value: field?.value === true
19448
- };
19449
- if (spec.control === "tri") return {
19450
- ...common,
19451
- text: "",
19452
- value: triOf(field)
19453
- };
19454
- return {
19455
- ...common,
19456
- text: textOf(field),
19457
- value: field?.value
19458
- };
19459
- }
19460
- function triOf(field) {
19461
- if (field === void 0 || field.value === void 0) return "unset";
19462
- return field.value === true ? "on" : "off";
19463
- }
19464
- function summaryOf(id, read, t) {
19465
- const allowed = arrayOf(read([
19466
- "modes",
19467
- id,
19468
- "allowTools"
19469
- ]));
19470
- const denied = arrayOf(read([
19471
- "modes",
19472
- id,
19473
- "denyTools"
19314
+ }, [
19315
+ sessionId,
19316
+ mode,
19317
+ preset
19318
+ ]);
19319
+ if (load.kind === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19320
+ "data-role": "session-mode-panel",
19321
+ style: PANEL_STYLE,
19322
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19323
+ size: "sm",
19324
+ tone: "tertiary",
19325
+ children: t("loading")
19326
+ })
19327
+ });
19328
+ if (load.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19329
+ "data-role": "session-mode-panel",
19330
+ style: PANEL_STYLE,
19331
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19332
+ as: "p",
19333
+ size: "sm",
19334
+ tone: "danger",
19335
+ children: load.message
19336
+ })
19337
+ });
19338
+ const active = load?.kind === "ready" ? load.value : void 0;
19339
+ if (active === void 0 || active === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19340
+ "data-role": "session-mode-panel",
19341
+ style: PANEL_STYLE,
19342
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19343
+ size: "sm",
19344
+ tone: "tertiary",
19345
+ children: t("loading")
19346
+ })
19347
+ });
19348
+ const effective = active.session ?? active.config;
19349
+ const overridden = new Set(active.overriddenFields ?? (active.session === void 0 ? [] : [
19350
+ "persona",
19351
+ "instructions",
19352
+ "skills",
19353
+ "runtimeContext"
19474
19354
  ]));
19475
- const provider = read([
19476
- "modes",
19477
- id,
19478
- "defaultModel",
19479
- "provider"
19480
- ])?.value;
19481
- const model = read([
19482
- "modes",
19483
- id,
19484
- "defaultModel",
19485
- "model"
19486
- ])?.value;
19487
- 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")}`;
19488
- }
19489
- function textOf(field) {
19490
- if (field === void 0) return "";
19491
- if (typeof field.text === "string") return field.text;
19492
- return typeof field.value === "string" ? field.value : "";
19493
- }
19494
- function arrayOf(field) {
19495
- const value = field?.value;
19496
- return Array.isArray(value) ? value.map((item) => String(item)) : [];
19497
- }
19498
- const ROLES = ROLE_VALUES;
19499
- // src/client/BundleConfigPage.tsx
19500
- function BundleConfigPage(props) {
19501
- const { t, view } = props;
19502
- const face = {
19503
- set: props.set,
19504
- editText: props.editText,
19505
- clear: props.clear,
19506
- appendItem: props.appendItem,
19507
- removeItem: props.removeItem,
19508
- addMode: props.addMode,
19509
- removeMode: props.removeMode,
19510
- save: props.save,
19511
- discard: props.discard
19355
+ const source = (field) => overridden.has(field) ? t("fromSession") : t("fromMode");
19356
+ const configValue = (entry) => {
19357
+ if (entry.group !== void 0 && effective.denyTools.includes(groupRef(entry.group))) return "deny";
19358
+ return entry.fallback === void 0 || entry.fallback === "default" ? "unset" : entry.fallback;
19512
19359
  };
19513
- const snapshot = props.useBundleConfig((current) => current);
19514
- const status = props.useBundleStatus((current) => current);
19515
- const models = props.useBundleModels((current) => current);
19516
- const state = (0, react.useMemo)(() => projectBundleConfig(snapshot, t, status), [
19517
- snapshot,
19518
- t,
19519
- status
19520
- ]);
19521
- const [expanded, setExpanded] = (0, react.useState)(() => /* @__PURE__ */ new Set());
19522
- const [addingId, setAddingId] = (0, react.useState)("");
19523
- const [addingOpen, setAddingOpen] = (0, react.useState)(false);
19524
- const [removing, setRemoving] = (0, react.useState)(null);
19525
- const resync = props.resync;
19526
- (0, react.useEffect)(() => {
19527
- resync();
19528
- }, [resync]);
19529
- if (view !== "page") return null;
19530
- if (state.readiness !== "ready") {
19531
- const diagnosis = props.diagnose();
19532
- const message = state.readiness === "loading" ? t("configured.loading") : state.readiness === "missing" ? t("configured.missing") : t("configured.unreadable", { problem: diagnosis.problem });
19533
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19534
- "data-bundle-config": state.readiness,
19535
- "data-namespace": SESSION_MODE_NS,
19536
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19537
- as: "p",
19538
- size: "sm",
19539
- tone: "tertiary",
19540
- "data-namespaces": diagnosis.namespaces.join(","),
19541
- "data-problem": diagnosis.problem,
19542
- "data-controller": diagnosis.controller,
19543
- children: message
19544
- }), state.readiness !== "unreadable" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19545
- as: "p",
19546
- size: "xs",
19547
- tone: "tertiary",
19548
- mono: true,
19549
- "data-diagnosis": "true",
19550
- children: `${diagnosis.problem} · ${diagnosis.controller} · namespaces=[${diagnosis.namespaces.join(", ")}]`
19551
- })]
19552
- });
19553
- }
19554
- const labels = {
19555
- unavailable: t("unavailable"),
19556
- readOnly: t("readOnly"),
19557
- saveFailed: t("saveFailed"),
19558
- save: t("save"),
19559
- saving: t("saving")
19360
+ const viewConfig = () => applyDraft(effective, {
19361
+ lists: {
19362
+ tools: draft.items["tools"] ?? {},
19363
+ skills: draft.items["skills"] ?? {},
19364
+ policies: draft.items["policies"] ?? {}
19365
+ },
19366
+ ...draft.persona === void 0 ? {} : { persona: draft.persona },
19367
+ switches: draft.switches
19368
+ }, active.config);
19369
+ const commit = (target) => {
19370
+ if (sessionId === void 0) return;
19371
+ const config = viewConfig();
19372
+ (target === "session" ? applySessionConfig(sessionId, config) : saveConfigDraft(sessionId, config)).then((value) => {
19373
+ setLoad({
19374
+ kind: "ready",
19375
+ value
19376
+ });
19377
+ setDraft({
19378
+ switches: {},
19379
+ items: {
19380
+ tools: {},
19381
+ skills: {},
19382
+ policies: {}
19383
+ }
19384
+ });
19385
+ setNotice(t(target === "session" ? "applied" : "saved"));
19386
+ }).catch((error) => setNotice(`${t("saveFailed")}: ${String(error)}`));
19560
19387
  };
19561
- const toggle = (id) => {
19562
- setExpanded((previous) => {
19563
- const next = new Set(previous);
19564
- if (next.has(id)) next.delete(id);
19565
- else next.add(id);
19388
+ const toolGroups = /* @__PURE__ */ new Map();
19389
+ for (const entry of active.catalog.tools) {
19390
+ const group = entry.group ?? "其它";
19391
+ const list = toolGroups.get(group);
19392
+ if (list === void 0) toolGroups.set(group, [entry]);
19393
+ else list.push(entry);
19394
+ }
19395
+ const toggleGroup = (group) => {
19396
+ setOpenGroups((current) => {
19397
+ const next = new Set(current);
19398
+ if (next.has(group)) next.delete(group);
19399
+ else next.add(group);
19566
19400
  return next;
19567
19401
  });
19568
19402
  };
19569
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(client_exports.SettingsForm, {
19570
- labels,
19571
- state,
19572
- onSave: face.save,
19573
- onDiscard: face.discard,
19403
+ const section = (key, title, children) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, {
19404
+ title,
19405
+ open: openSection === key,
19406
+ onToggle: () => {
19407
+ setOpenSection((current) => current === key ? null : key);
19408
+ },
19409
+ children
19410
+ });
19411
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19412
+ "data-role": "session-mode-panel",
19413
+ style: PANEL_STYLE,
19574
19414
  children: [
19575
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Stack, {
19576
- gap: 16,
19577
- "data-bundle-config": "page",
19578
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stack, {
19579
- as: "section",
19580
- gap: 8,
19581
- "data-section": "default",
19582
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsFieldRow, {
19583
- label: t("default.label"),
19584
- hint: t("default.hint"),
19585
- layout: "inline",
19586
- ...state.defaultMode.invalid === void 0 ? {} : { invalid: state.defaultMode.invalid },
19587
- "data-field": "default",
19588
- "data-control": "choice",
19589
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchSelect, {
19590
- label: t("default.label"),
19591
- value: state.defaultMode.value,
19592
- emptyLabel: t("tri.unset"),
19593
- options: state.defaultMode.options.map((option) => ({
19594
- value: String(option.value),
19595
- ...option.label === void 0 ? {} : { label: option.label }
19596
- })),
19597
- searchLabel: t("select.search"),
19598
- noMatchLabel: t("select.noMatch"),
19599
- disabled: !state.writable,
19600
- onSelect: (next) => {
19601
- if (next === "") face.clear(["default"]);
19602
- else face.set(["default"], next);
19603
- }
19604
- })
19605
- })
19606
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Stack, {
19607
- as: "section",
19608
- gap: 8,
19609
- "data-section": "modes",
19610
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
19611
- gap: 8,
19612
- justify: "between",
19613
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Stack, {
19614
- gap: 2,
19615
- grow: true,
19616
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19617
- as: "h4",
19618
- size: "md",
19619
- tone: "primary",
19620
- weight: "strong",
19621
- children: t("modes.label")
19415
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19416
+ style: {
19417
+ display: "flex",
19418
+ flexDirection: "column",
19419
+ gap: 48
19420
+ },
19421
+ children: [
19422
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19423
+ "data-role": "header",
19424
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Stack, {
19425
+ gap: 4,
19426
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
19427
+ gap: 6,
19428
+ align: "center",
19429
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19430
+ size: "lg",
19431
+ children: active.mode.name
19432
+ })
19622
19433
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19623
- as: "p",
19624
19434
  size: "sm",
19625
19435
  tone: "tertiary",
19626
- children: t("modes.hint")
19436
+ children: active.mode.description.length === 0 ? t("empty") : active.mode.description
19627
19437
  })]
19628
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
19629
- label: t("add.label"),
19630
- "data-action": "open-add-mode",
19631
- disabled: !state.writable,
19632
- onClick: () => {
19633
- setAddingId("");
19634
- setAddingOpen(true);
19635
- },
19636
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.IconPlusOutlineRegular, {})
19637
- })]
19638
- }), state.modes.map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModeCard, {
19639
- mode,
19640
- models,
19641
- open: expanded.has(mode.id),
19642
- writable: state.writable,
19643
- t,
19644
- face,
19645
- onRequestRemove: () => {
19646
- setRemoving(mode);
19647
- },
19648
- onToggle: () => {
19649
- toggle(mode.id);
19650
- }
19651
- }, mode.id))]
19652
- })]
19438
+ })
19439
+ }),
19440
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { style: { height: 12 } }),
19441
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19442
+ "data-role": "content",
19443
+ children: [
19444
+ section("general", t("general"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: [
19445
+ [
19446
+ "instructions",
19447
+ t("instructions"),
19448
+ effective.instructions
19449
+ ],
19450
+ [
19451
+ "skills",
19452
+ t("skillsCatalog"),
19453
+ effective.skills
19454
+ ],
19455
+ [
19456
+ "runtimeContext",
19457
+ t("runtimeContext"),
19458
+ effective.runtimeContext
19459
+ ]
19460
+ ].map(([key, label, value]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
19461
+ style: {
19462
+ display: "flex",
19463
+ flexWrap: "wrap",
19464
+ alignItems: "center",
19465
+ justifyContent: "space-between",
19466
+ gap: 8,
19467
+ padding: "4px 0"
19468
+ },
19469
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
19470
+ style: {
19471
+ display: "flex",
19472
+ flexDirection: "column",
19473
+ gap: 2
19474
+ },
19475
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19476
+ size: "md",
19477
+ children: label
19478
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19479
+ size: "sm",
19480
+ tone: "tertiary",
19481
+ children: source(key)
19482
+ })]
19483
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
19484
+ gap: 6,
19485
+ style: { marginLeft: "auto" },
19486
+ children: [overridden.has(key) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19487
+ variant: "outline",
19488
+ size: "sm",
19489
+ onClick: () => {
19490
+ setDraft((prev) => ({
19491
+ ...prev,
19492
+ switches: {
19493
+ ...prev.switches,
19494
+ [key]: null
19495
+ }
19496
+ }));
19497
+ },
19498
+ children: t("stateDefault")
19499
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Switch, {
19500
+ checked: draft.switches[key] ?? value,
19501
+ label,
19502
+ onChange: (next) => {
19503
+ setDraft((prev) => ({
19504
+ ...prev,
19505
+ switches: {
19506
+ ...prev.switches,
19507
+ [key]: next
19508
+ }
19509
+ }));
19510
+ }
19511
+ }) })]
19512
+ })]
19513
+ }, key)) })),
19514
+ section("persona", t("persona"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
19515
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19516
+ size: "sm",
19517
+ tone: "tertiary",
19518
+ children: source("persona")
19519
+ }),
19520
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultilineField, {
19521
+ value: draft.persona?.prefix ?? effective.persona.prefix,
19522
+ rows: 6,
19523
+ "aria-label": t("persona"),
19524
+ onChange: (event) => {
19525
+ const prefix = event.currentTarget.value;
19526
+ setDraft((prev) => ({
19527
+ ...prev,
19528
+ persona: {
19529
+ prefix,
19530
+ suffix: prev.persona?.suffix ?? effective.persona.suffix
19531
+ }
19532
+ }));
19533
+ }
19534
+ }),
19535
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultilineField, {
19536
+ value: draft.persona?.suffix ?? effective.persona.suffix,
19537
+ rows: 3,
19538
+ "aria-label": t("persona"),
19539
+ onChange: (event) => {
19540
+ const suffix = event.currentTarget.value;
19541
+ setDraft((prev) => ({
19542
+ ...prev,
19543
+ persona: {
19544
+ prefix: prev.persona?.prefix ?? effective.persona.prefix,
19545
+ suffix
19546
+ }
19547
+ }));
19548
+ }
19549
+ })
19550
+ ] })),
19551
+ section("tools", `${t("tools")} (${String(active.catalog.tools.length)})`, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: [...toolGroups].map(([group, entries]) => {
19552
+ const groupDraft = draft.items["tools"]?.[groupRef(group)];
19553
+ const values = groupDraft !== void 0 ? entries.map(() => groupDraft) : entries.map((entry) => draft.items["tools"]?.[entry.id] ?? entry.override ?? configValue(entry));
19554
+ const groupValue = values.every((value) => value === values[0]) ? values[0] ?? "unset" : "mixed";
19555
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Stack, {
19556
+ gap: 2,
19557
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateRow, {
19558
+ id: `${entries[0]?.groupLabel ?? group} (${String(entries.length)})`,
19559
+ t,
19560
+ value: groupValue,
19561
+ onLabelClick: () => {
19562
+ toggleGroup(group);
19563
+ },
19564
+ onChange: (next) => {
19565
+ const value = next === "allow" || next === "deny" ? next : "unset";
19566
+ setDraft((prev) => ({
19567
+ ...prev,
19568
+ items: {
19569
+ ...prev.items,
19570
+ tools: {
19571
+ ...prev.items["tools"],
19572
+ [groupRef(group)]: value
19573
+ }
19574
+ }
19575
+ }));
19576
+ }
19577
+ }), openGroups.has(group) ? entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateRow, {
19578
+ id: entry.id,
19579
+ description: entry.description,
19580
+ indent: 16,
19581
+ t,
19582
+ value: draft.items["tools"]?.[entry.id] ?? entry.override ?? configValue(entry),
19583
+ onChange: (next) => {
19584
+ const value = next === "allow" || next === "deny" ? next : "unset";
19585
+ setDraft((prev) => ({
19586
+ ...prev,
19587
+ items: {
19588
+ ...prev.items,
19589
+ tools: {
19590
+ ...prev.items["tools"],
19591
+ [entry.id]: value
19592
+ }
19593
+ }
19594
+ }));
19595
+ }
19596
+ }, entry.id)) : null]
19597
+ }, group);
19598
+ }) })),
19599
+ section("skills", `${t("skills")} (${String(active.catalog.skills.length)})`, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: active.catalog.skills.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateRow, {
19600
+ id: entry.id,
19601
+ description: entry.description,
19602
+ t,
19603
+ value: draft.items["skills"]?.[entry.id] ?? entry.override ?? configValue(entry),
19604
+ onChange: (next) => {
19605
+ setDraft((prev) => ({
19606
+ ...prev,
19607
+ items: {
19608
+ ...prev.items,
19609
+ skills: {
19610
+ ...prev.items.skills,
19611
+ [entry.id]: next === "allow" || next === "deny" ? next : "unset"
19612
+ }
19613
+ }
19614
+ }));
19615
+ }
19616
+ }, entry.id)) })),
19617
+ section("policies", `${t("policies")} (${String(active.catalog.policies.length)})`, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: active.catalog.policies.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriStateRow, {
19618
+ id: entry.id,
19619
+ description: entry.description,
19620
+ t,
19621
+ value: draft.items["policies"]?.[entry.id] ?? entry.override ?? configValue(entry),
19622
+ onChange: (next) => {
19623
+ setDraft((prev) => ({
19624
+ ...prev,
19625
+ items: {
19626
+ ...prev.items,
19627
+ policies: {
19628
+ ...prev.items.policies,
19629
+ [entry.id]: next === "allow" || next === "deny" ? next : "unset"
19630
+ }
19631
+ }
19632
+ }));
19633
+ }
19634
+ }, entry.id)) }))
19635
+ ]
19636
+ }),
19637
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19638
+ "data-role": "actions",
19639
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
19640
+ gap: 8,
19641
+ justify: "end",
19642
+ align: "center",
19643
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19644
+ variant: "outline",
19645
+ onClick: () => {
19646
+ setDraft({
19647
+ switches: {},
19648
+ items: {
19649
+ tools: {},
19650
+ skills: {},
19651
+ policies: {}
19652
+ }
19653
+ });
19654
+ setNotice(null);
19655
+ },
19656
+ children: t("reset")
19657
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Menu, {
19658
+ open: menuOpen,
19659
+ onClose: () => {
19660
+ setMenuOpen(false);
19661
+ },
19662
+ align: "start",
19663
+ portal: true,
19664
+ items: [
19665
+ {
19666
+ id: "saveDefault",
19667
+ label: t("saveDefault")
19668
+ },
19669
+ {
19670
+ id: "saveAs",
19671
+ label: t("saveAs")
19672
+ },
19673
+ {
19674
+ id: "delete",
19675
+ label: t("delete"),
19676
+ disabled: !active.deletable
19677
+ }
19678
+ ],
19679
+ onSelect: (id) => {
19680
+ setMenuOpen(false);
19681
+ if (id === "saveDefault") {
19682
+ commit("config");
19683
+ return;
19684
+ }
19685
+ if (id === "saveAs") {
19686
+ setCode("");
19687
+ setName(active.mode.name);
19688
+ setDialog("saveAs");
19689
+ return;
19690
+ }
19691
+ if (active.deletable) setDialog("delete");
19692
+ },
19693
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
19694
+ gap: 0,
19695
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19696
+ variant: "primary",
19697
+ style: {
19698
+ borderTopRightRadius: 0,
19699
+ borderBottomRightRadius: 0,
19700
+ borderRightWidth: 0
19701
+ },
19702
+ onClick: () => {
19703
+ commit("session");
19704
+ },
19705
+ children: t("apply")
19706
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19707
+ variant: "primary",
19708
+ "aria-haspopup": "menu",
19709
+ "aria-expanded": menuOpen,
19710
+ style: {
19711
+ borderTopLeftRadius: 0,
19712
+ borderBottomLeftRadius: 0,
19713
+ paddingInline: 8
19714
+ },
19715
+ onClick: () => {
19716
+ setMenuOpen((current) => !current);
19717
+ },
19718
+ children: "▾"
19719
+ })]
19720
+ })
19721
+ })]
19722
+ })
19723
+ }),
19724
+ notice === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
19725
+ "data-role": "notice",
19726
+ style: { paddingTop: 8 },
19727
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19728
+ as: "p",
19729
+ size: "sm",
19730
+ tone: "tertiary",
19731
+ children: notice
19732
+ })
19733
+ })
19734
+ ]
19653
19735
  }),
19654
19736
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Modal, {
19655
- open: addingOpen,
19737
+ open: dialog === "saveAs",
19656
19738
  onClose: () => {
19657
- setAddingOpen(false);
19739
+ setDialog(null);
19658
19740
  },
19659
- title: t("add.label"),
19660
- closeLabel: t("add.close"),
19661
- description: t("add.hint"),
19662
- footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19663
- variant: "outline",
19664
- onClick: () => {
19665
- setAddingOpen(false);
19666
- },
19667
- children: t("add.cancel")
19668
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19669
- variant: "primary",
19670
- "data-action": "add-mode",
19671
- disabled: addingId.trim() === "",
19672
- onClick: () => {
19673
- const id = addingId.trim();
19674
- face.addMode(id);
19675
- setAddingId("");
19676
- setAddingOpen(false);
19677
- setExpanded((previous) => new Set(previous).add(id));
19678
- },
19679
- children: t("add.confirm")
19680
- })] }),
19681
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Input, {
19682
- "data-field": "new-mode",
19683
- "data-modal-autofocus": true,
19684
- value: addingId,
19685
- placeholder: t("add.placeholder"),
19686
- "aria-label": t("add.label"),
19687
- onChange: (event) => {
19688
- setAddingId(event.currentTarget.value);
19689
- }
19741
+ title: t("saveAs"),
19742
+ closeLabel: t("cancel"),
19743
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
19744
+ gap: 6,
19745
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19746
+ variant: "outline",
19747
+ size: "sm",
19748
+ onClick: () => {
19749
+ setDialog(null);
19750
+ },
19751
+ children: t("cancel")
19752
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19753
+ size: "sm",
19754
+ disabled: code.trim().length === 0,
19755
+ onClick: () => {
19756
+ if (sessionId === void 0 || code.trim().length === 0) return;
19757
+ saveAsNewMode(sessionId, code, name).then(() => {
19758
+ setDialog(null);
19759
+ setNotice(t("savedNewMode"));
19760
+ }).catch((error) => setNotice(`${t("saveFailed")}: ${String(error)}`));
19761
+ },
19762
+ children: t("confirm")
19763
+ })]
19764
+ }),
19765
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Stack, {
19766
+ gap: 6,
19767
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Input, {
19768
+ value: code,
19769
+ placeholder: t("codePlaceholder"),
19770
+ "aria-label": t("codeLabel"),
19771
+ onChange: (event) => {
19772
+ setCode(event.currentTarget.value);
19773
+ }
19774
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Input, {
19775
+ value: name,
19776
+ placeholder: t("namePlaceholder"),
19777
+ "aria-label": t("nameLabel"),
19778
+ onChange: (event) => {
19779
+ setName(event.currentTarget.value);
19780
+ }
19781
+ })]
19690
19782
  })
19691
19783
  }),
19692
19784
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Modal, {
19693
- open: removing !== null,
19785
+ open: dialog === "delete",
19694
19786
  onClose: () => {
19695
- setRemoving(null);
19787
+ setDialog(null);
19696
19788
  },
19697
- title: t("remove.title"),
19698
- closeLabel: t("remove.close"),
19699
- description: t("remove.description", { name: removing?.title ?? "" }),
19700
- footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19701
- variant: "outline",
19702
- onClick: () => {
19703
- setRemoving(null);
19704
- },
19705
- children: t("remove.cancel")
19706
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19707
- variant: "primary",
19708
- tone: "danger",
19709
- "data-action": "confirm-remove",
19710
- onClick: () => {
19711
- if (removing !== null) face.removeMode(removing.id);
19712
- setRemoving(null);
19713
- },
19714
- children: t("remove.confirm")
19715
- })] })
19716
- })
19717
- ]
19718
- });
19719
- }
19720
- function ModeCard({ mode, models, open, writable, t, face, onRequestRemove, onToggle }) {
19721
- const hasModel = mode.model !== void 0;
19722
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stack, {
19723
- boxed: true,
19724
- pad: "card",
19725
- "data-mode": mode.id,
19726
- "data-deletable": mode.deletable ? "true" : "false",
19727
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DisclosureRow, {
19728
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.IconAgentPresetOutlineRegular, {}),
19729
- title: mode.title,
19730
- open,
19731
- expandable: true,
19732
- expandOnRowClick: true,
19733
- onToggle,
19734
- keepContentWhenOpen: true,
19735
- collapsedContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19736
- as: "code",
19737
- size: "sm",
19738
- tone: "tertiary",
19739
- "data-mode-id": true,
19740
- children: mode.id
19741
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
19742
- gap: 8,
19743
- push: true,
19744
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19745
- as: "span",
19746
- size: "sm",
19747
- tone: "tertiary",
19748
- children: mode.summary
19749
- }), mode.deletable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
19750
- size: "sm",
19751
- "data-action": "remove-mode",
19752
- disabled: !writable,
19753
- label: t("removeNamed", { name: mode.title }),
19754
- onClick: (event) => {
19755
- event.stopPropagation();
19756
- onRequestRemove();
19757
- },
19758
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.IconTrashOutlineRegular, {})
19759
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19760
- as: "span",
19761
- size: "sm",
19762
- tone: "tertiary",
19763
- "data-protected": "true",
19764
- children: t("protected")
19765
- })]
19766
- })] }),
19767
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Stack, {
19768
- gap: 14,
19769
- pad: "body",
19770
- children: mode.groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Stack, {
19771
- gap: 0,
19772
- "data-group": group.key,
19773
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
19774
- gap: 8,
19775
- justify: "between",
19776
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19777
- size: "sm",
19778
- tone: "secondary",
19779
- weight: "strong",
19780
- children: group.label
19781
- })
19782
- }), group.key === "model" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
19783
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsFieldRow, {
19784
- label: t("field.defaultModel"),
19785
- hint: t("hint.defaultModel"),
19786
- layout: "inline",
19787
- "data-control": "default-model",
19788
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Switch, {
19789
- checked: hasModel,
19790
- label: t("field.defaultModel"),
19791
- disabled: !writable,
19792
- onChange: (next) => {
19793
- if (next) face.set([
19794
- "modes",
19795
- mode.id,
19796
- "defaultModel"
19797
- ], {
19798
- provider: "",
19799
- model: ""
19800
- });
19801
- else face.clear([
19802
- "modes",
19803
- mode.id,
19804
- "defaultModel"
19805
- ]);
19806
- }
19807
- })
19808
- }),
19809
- !hasModel ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsFieldRow, {
19810
- label: t("field.modelRoute"),
19811
- hint: t("hint.modelRoute"),
19812
- divider: true,
19813
- "data-control": "model-routes",
19814
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelRouteList, {
19815
- label: t("field.modelRoute"),
19816
- candidates: models.routes,
19817
- selectedKey: mode.model === void 0 || mode.model.provider === "" ? void 0 : routeKey(mode.model.provider, mode.model.model),
19818
- disabled: !writable,
19819
- status: models.status,
19820
- loadingLabel: t("model.loading"),
19821
- errorLabel: t("model.error"),
19822
- emptyLabel: t("model.empty"),
19823
- onSelect: (candidate) => {
19824
- face.set([
19825
- "modes",
19826
- mode.id,
19827
- "defaultModel"
19828
- ], {
19829
- provider: candidate.provider,
19830
- model: candidate.model
19831
- });
19832
- }
19833
- })
19834
- }),
19835
- group.fields.map((field, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldRow, {
19836
- field,
19837
- writable,
19838
- t,
19839
- face,
19840
- divider: index > 0 || hasModel
19841
- }, field.key))
19842
- ] }) : group.fields.map((field, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldRow, {
19843
- field,
19844
- writable,
19845
- t,
19846
- face,
19847
- divider: index > 0
19848
- }, field.key))]
19849
- }, group.key))
19850
- })
19851
- })
19852
- });
19853
- }
19854
- function FieldRow({ field, writable, divider, t, face }) {
19855
- if (field.control === "choice" && !field.present) return null;
19856
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsFieldRow, {
19857
- label: field.label,
19858
- hint: field.hint,
19859
- divider,
19860
- layout: field.control === "switch" || field.control === "tri" || field.control === "roles" ? "inline" : "stack",
19861
- ...field.overridden ? { overriddenLabel: t("overridden") } : {},
19862
- ...field.control === "text" ? {
19863
- resetLabel: t("field.reset"),
19864
- onReset: () => {
19865
- face.clear(field.path);
19866
- }
19867
- } : {},
19868
- ...field.invalid === void 0 ? {} : { invalid: field.invalid },
19869
- "data-field": field.path.join("."),
19870
- "data-control": field.control,
19871
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FieldControl, {
19872
- field,
19873
- writable,
19874
- t,
19875
- face
19876
- })
19877
- });
19878
- }
19879
- function FieldControl({ field, writable, t, face }) {
19880
- const disabled = !writable;
19881
- const id = (0, react.useId)();
19882
- switch (field.control) {
19883
- case "multiline": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MultilineField, {
19884
- value: field.text,
19885
- rows: 3,
19886
- disabled,
19887
- "aria-label": field.label,
19888
- onChange: (event) => {
19889
- face.editText(field.path, event.currentTarget.value);
19890
- }
19891
- });
19892
- case "choice": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchSelect, {
19893
- label: field.label,
19894
- value: field.text,
19895
- emptyLabel: t("tri.unset"),
19896
- options: field.options.map((option) => ({
19897
- value: String(option.value),
19898
- ...option.label === void 0 ? {} : { label: option.label }
19899
- })),
19900
- searchLabel: t("select.search"),
19901
- noMatchLabel: t("select.noMatch"),
19902
- disabled,
19903
- onSelect: (next) => {
19904
- if (next === "") face.clear(field.path);
19905
- else face.set(field.path, next);
19906
- }
19907
- });
19908
- case "switch": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Switch, {
19909
- checked: field.value === true,
19910
- label: field.label,
19911
- disabled,
19912
- onChange: (next) => {
19913
- face.set(field.path, next);
19914
- }
19915
- });
19916
- case "tri": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.SegmentedControl, {
19917
- id: `${id}-tri`,
19918
- label: field.label,
19919
- value: triValue(field),
19920
- disabled,
19921
- options: [
19922
- {
19923
- value: "unset",
19924
- label: t("tri.unset")
19925
- },
19926
- {
19927
- value: "on",
19928
- label: t("tri.on")
19929
- },
19930
- {
19931
- value: "off",
19932
- label: t("tri.off")
19933
- }
19934
- ],
19935
- onChange: (next) => {
19936
- if (next === "unset") face.clear(field.path);
19937
- else face.set(field.path, next === "on");
19938
- }
19939
- });
19940
- case "roles": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Row, {
19941
- gap: 6,
19942
- wrap: true,
19943
- "data-control": "roles",
19944
- children: ROLES.map((role) => {
19945
- const active = Array.isArray(field.value) && field.value.includes(role);
19946
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19947
- variant: active ? "primary" : "outline",
19789
+ title: t("delete"),
19790
+ closeLabel: t("cancel"),
19791
+ description: t("deleteConfirm"),
19792
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Row, {
19793
+ gap: 6,
19794
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19795
+ variant: "outline",
19796
+ size: "sm",
19797
+ onClick: () => {
19798
+ setDialog(null);
19799
+ },
19800
+ children: t("cancel")
19801
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button$1, {
19802
+ size: "sm",
19803
+ onClick: () => {
19804
+ if (sessionId === void 0) return;
19805
+ deleteMode(sessionId).then((next) => {
19806
+ setDialog(null);
19807
+ setLoad({
19808
+ kind: "ready",
19809
+ value: next
19810
+ });
19811
+ }).catch((error) => setNotice(`${t("saveFailed")}: ${String(error)}`));
19812
+ },
19813
+ children: t("confirm")
19814
+ })]
19815
+ }),
19816
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Text, {
19817
+ as: "p",
19948
19818
  size: "sm",
19949
- "data-mode-role": role,
19950
- disabled,
19951
- "aria-pressed": active,
19952
- onClick: () => {
19953
- const current = Array.isArray(field.value) ? field.value.map(String) : [];
19954
- const wanted = active ? current.filter((value) => value !== role) : [...current.filter((value) => value !== role), role];
19955
- face.set(field.path, wanted);
19956
- },
19957
- children: t(`role.${role}`)
19958
- }, role);
19819
+ children: active.mode.name
19820
+ })
19959
19821
  })
19960
- });
19961
- case "tags": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TagInput, {
19962
- value: Array.isArray(field.value) ? field.value.map(String) : [],
19963
- options: field.options.map((option) => ({
19964
- value: String(option.value),
19965
- ...option.label === void 0 ? {} : { label: option.label }
19966
- })),
19967
- placeholder: t("tags.placeholder"),
19968
- label: field.label,
19969
- noMatchLabel: t("select.noMatch"),
19970
- removeLabel: (name) => t("tags.remove", { name }),
19971
- disabled,
19972
- onChange: (next) => {
19973
- face.set(field.path, next);
19974
- }
19975
- });
19976
- case "text": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(client_exports.Input, {
19977
- value: field.text,
19978
- "aria-label": field.label,
19979
- disabled,
19980
- onChange: (event) => {
19981
- face.editText(field.path, event.currentTarget.value);
19982
- }
19983
- });
19984
- }
19985
- }
19986
- function triValue(field) {
19987
- return field.value === "unset" || field.value === "on" || field.value === "off" ? field.value : "unset";
19988
- }
19989
- // src/client/bundle-locales.ts
19990
- const BUNDLE_NS = "session-mode-bundle";
19991
- const bundleZh = {
19992
- "default.label": "新会话的默认模式",
19993
- "default.hint": "还没选过模式的会话从这里开始。",
19994
- "modes.label": "模式",
19995
- "modes.hint": "展开一个模式,改它的提示词、工具与技能、上游规则和注入内容。",
19996
- "add.label": "添加模式",
19997
- "add.placeholder": "模式 id",
19998
- "add.confirm": "添加",
19999
- "add.cancel": "取消",
20000
- "add.close": "关闭",
20001
- "add.hint": "给新模式一个 id(选择器与列表里也显示它,加好后可以再改名称)。",
20002
- removeNamed: "删除模式 {name}",
20003
- protected: "内置模式,不提供删除",
20004
- untitled: "(未命名)",
20005
- overridden: "已覆盖",
20006
- "group.identity": "基本信息",
20007
- "group.persona": "提示词",
20008
- "group.tools": "工具与技能",
20009
- "group.rules": "上游规则",
20010
- "group.injections": "请求里注入什么",
20011
- "group.model": "默认模型",
20012
- "role.main": "会话可选",
20013
- "role.subagent": "子代理可用",
20014
- "field.name": "名称",
20015
- "field.description": "一句话说明",
20016
- "field.role": "角色",
20017
- "field.presetsOnly": "允许挂的 preset",
20018
- "field.prefix": "提示词前缀",
20019
- "field.suffix": "提示词后缀",
20020
- "field.allowTools": "允许的工具",
20021
- "field.denyTools": "禁用的工具",
20022
- "field.allowSkills": "允许的技能",
20023
- "field.denySkills": "禁用的技能",
20024
- "field.allowPolicies": "生效的规则",
20025
- "field.denyPolicies": "禁用的规则",
20026
- "field.instructions": "工作区指令",
20027
- "field.skills": "技能目录",
20028
- "field.runtimeContext": "动态快照",
20029
- "field.defaultModel": "设默认模型",
20030
- "field.modelRoute": "用哪个模型",
20031
- "field.reasoningEffort": "思考档位",
20032
- "field.reset": "恢复默认",
20033
- "hint.name": "选择器和列表里显示的名字。",
20034
- "hint.description": "一句话说明这个模式做什么。",
20035
- "hint.role": "「会话可选」= 用户能在会话里选它;「子代理可用」= 可以把子代理跑在这个模式下。",
20036
- "hint.presetsOnly": "只允许这个模式挂这些 preset(行清单由挂着的那份提供);不填 = 允许全部(哪份 preset 都行,切模式也不动它)。有值时切到这个模式,会话当前那份不在名单里就换成名单第一个。",
20037
- "hint.prefix": "加在系统提示词开头的一段话。",
20038
- "hint.suffix": "加在系统提示词结尾的一段话。",
20039
- "hint.allowTools": "只允许这些工具;不填 = 不限制(用会话当前 preset 的全部工具)。",
20040
- "hint.denyTools": "从上面那份里去掉这些工具。",
20041
- "hint.allowSkills": "只允许这些技能;不填 = 不限制。",
20042
- "hint.denySkills": "这些技能不列进技能目录,`skill` 工具也加载不了。",
20043
- "hint.allowPolicies": "只让这些上游规则生效;不填 = 全部生效。",
20044
- "hint.denyPolicies": "禁用这些上游规则,它的判断连拒绝一起丢掉。",
20045
- "hint.instructions": "每步都把工作区指令(AGENTS.md 之类)发给模型;关掉就不发。",
20046
- "hint.skills": "每步都把技能目录发给模型;默认按这个模式的工具名单自动判断。",
20047
- "hint.runtimeContext": "每步都把沙箱与审批策略发给模型。",
20048
- "hint.defaultModel": "开 = 这个模式自带一个默认模型(会话还没选过模型时用它);关 = 跟随全局模型。",
20049
- "hint.modelRoute": "从部署里已设置的模型里挑一条:服务商与模型是一对,选它等于两个一起定。",
20050
- "hint.reasoningEffort": "思考档位;不填用服务商默认。",
20051
- "model.loading": "正在读部署里已设置的模型…",
20052
- "model.error": "读不出部署里已设置的模型。",
20053
- "model.empty": "部署里还没有已设置的模型。",
20054
- "tags.placeholder": "加一个后回车,或粘贴一串",
20055
- "tags.add": "添加",
20056
- "tags.remove": "移除 {name}",
20057
- "select.search": "搜索候选",
20058
- "select.noMatch": "没有匹配的候选",
20059
- "tri.unset": "默认",
20060
- "tri.on": "开",
20061
- "tri.off": "关",
20062
- "remove.title": "删除模式",
20063
- "remove.description": "删除 {name}?删除只在保存之后生效,保存之前还可以丢弃。",
20064
- "remove.confirm": "删除",
20065
- "remove.cancel": "取消",
20066
- "remove.close": "关闭",
20067
- "model.set": "设置默认模型",
20068
- "model.follow": "改为跟随全局",
20069
- save: "保存",
20070
- saving: "保存中…",
20071
- saveFailed: "保存没有生效,请重试。",
20072
- readOnly: "这份部署的配置是只读的。",
20073
- unavailable: "这一行当前不可写。",
20074
- notSaved: "没有保存:{message}",
20075
- "configured.loading": "正在读取这一行的配置…",
20076
- "configured.missing": "设置面里没有 `session-mode` 这一行:没装、被禁用,或者还没装配成功(依赖没到齐时会一直 PENDING)。",
20077
- "configured.unreadable": "`session-mode` 这一行在跑,但这份配置读不出来({problem})。",
20078
- "violation.modesEmpty": "至少要有一种模式。",
20079
- "violation.defaultMissing": "默认模式不在模式清单里。",
20080
- "violation.modelPair": "默认模型要同时给出服务商与模型。",
20081
- "summary.allTools": "全部工具",
20082
- "summary.tools": "{n} 件工具",
20083
- "summary.denyTools": "禁 {n} 件",
20084
- "summary.globalModel": "跟随全局模型"
20085
- };
20086
- const bundleEn = {
20087
- "default.label": "Mode for new sessions",
20088
- "default.hint": "Sessions that never picked a mode start here.",
20089
- "modes.label": "Modes",
20090
- "modes.hint": "Expand a mode to edit its prompt, tools and skills, upstream rules, and injected content.",
20091
- "add.label": "Add a mode",
20092
- "add.placeholder": "mode id",
20093
- "add.confirm": "Add",
20094
- "add.cancel": "Cancel",
20095
- "add.close": "Close",
20096
- "add.hint": "Give the new mode an id (it also shows in pickers and lists; rename it after adding).",
20097
- removeNamed: "Delete mode {name}",
20098
- protected: "Built-in mode: no delete",
20099
- untitled: "(untitled)",
20100
- overridden: "Overridden",
20101
- "group.identity": "Basics",
20102
- "group.persona": "Prompt",
20103
- "group.tools": "Tools and skills",
20104
- "group.rules": "Upstream rules",
20105
- "group.injections": "Injected into requests",
20106
- "group.model": "Default model",
20107
- "role.main": "Selectable in sessions",
20108
- "role.subagent": "Usable for subagents",
20109
- "field.name": "Name",
20110
- "field.description": "One-line description",
20111
- "field.role": "Roles",
20112
- "field.presetsOnly": "Presets it may ride on",
20113
- "field.prefix": "Prompt prefix",
20114
- "field.suffix": "Prompt suffix",
20115
- "field.allowTools": "Allowed tools",
20116
- "field.denyTools": "Denied tools",
20117
- "field.allowSkills": "Allowed skills",
20118
- "field.denySkills": "Denied skills",
20119
- "field.allowPolicies": "Rules in force",
20120
- "field.denyPolicies": "Rules disabled",
20121
- "field.instructions": "Workspace instructions",
20122
- "field.skills": "Skill catalog",
20123
- "field.runtimeContext": "Runtime snapshot",
20124
- "field.defaultModel": "Default model",
20125
- "field.modelRoute": "Which model",
20126
- "field.reasoningEffort": "Reasoning effort",
20127
- "field.reset": "Reset",
20128
- "hint.name": "Shown in the picker and in lists.",
20129
- "hint.description": "One line saying what this mode is for.",
20130
- "hint.role": "Selectable in sessions = the user can pick it; usable for subagents = subagents may run in it.",
20131
- "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.",
20132
- "hint.prefix": "Text prepended to the system prompt.",
20133
- "hint.suffix": "Text appended to the system prompt.",
20134
- "hint.allowTools": "Only these tools; empty = no restriction (every tool of the session's preset).",
20135
- "hint.denyTools": "Those tools are removed from the list above.",
20136
- "hint.allowSkills": "Only these skills; empty = no restriction.",
20137
- "hint.denySkills": "Those skills leave the catalog, and the `skill` tool cannot load them.",
20138
- "hint.allowPolicies": "Only these upstream rules stay in force; empty = all of them.",
20139
- "hint.denyPolicies": "Those upstream rules are disabled, rejection included.",
20140
- "hint.instructions": "Sends the workspace instructions (AGENTS.md and friends) with every step; off means it is not sent.",
20141
- "hint.skills": "Sends the skill catalog with every step; default decides from this mode's tool lists.",
20142
- "hint.runtimeContext": "Sends the sandbox and approval policies with every step.",
20143
- "hint.defaultModel": "On = this mode carries a default model (used while a session has picked none); off = follow the global model.",
20144
- "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.",
20145
- "hint.reasoningEffort": "Reasoning effort; empty uses the provider default.",
20146
- "model.loading": "Reading the models set up in this deployment…",
20147
- "model.error": "Could not read the models set up in this deployment.",
20148
- "model.empty": "No model is set up in this deployment yet.",
20149
- "tags.placeholder": "Add one and press Enter, or paste a list",
20150
- "tags.add": "Add",
20151
- "tags.remove": "Remove {name}",
20152
- "select.search": "Search candidates",
20153
- "select.noMatch": "No matching candidate",
20154
- "tri.unset": "Default",
20155
- "tri.on": "On",
20156
- "tri.off": "Off",
20157
- "remove.title": "Delete mode",
20158
- "remove.description": "Delete {name}? The deletion applies on save; until then you can discard it.",
20159
- "remove.confirm": "Delete",
20160
- "remove.cancel": "Cancel",
20161
- "remove.close": "Close",
20162
- "model.set": "Set a default model",
20163
- "model.follow": "Follow the global model",
20164
- save: "Save",
20165
- saving: "Saving…",
20166
- saveFailed: "The save did not take effect; try again.",
20167
- readOnly: "This deployment stores settings read-only.",
20168
- unavailable: "This row is not writable right now.",
20169
- notSaved: "Not saved: {message}",
20170
- "configured.loading": "Reading this row's configuration…",
20171
- "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).",
20172
- "configured.unreadable": "The `session-mode` row is running, but its configuration cannot be read ({problem}).",
20173
- "violation.modesEmpty": "At least one mode is required.",
20174
- "violation.defaultMissing": "The default mode is not in the mode list.",
20175
- "violation.modelPair": "A default model needs both a provider and a model.",
20176
- "summary.allTools": "every tool",
20177
- "summary.tools": "{n} tools",
20178
- "summary.denyTools": "{n} denied",
20179
- "summary.globalModel": "global model"
20180
- };
20181
- // src/client/api.ts
20182
- async function fetchRoster() {
20183
- const response = await fetch(SESSION_MODE_PATH, {
20184
- method: "GET",
20185
- headers: { accept: "application/json" }
20186
- });
20187
- if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
20188
- return await response.json();
20189
- }
20190
- async function selectMode(sessionId, mode) {
20191
- const response = await fetch(SESSION_MODE_PATH, {
20192
- method: "POST",
20193
- headers: {
20194
- accept: "application/json",
20195
- "content-type": "application/json"
20196
- },
20197
- body: JSON.stringify({
20198
- sessionId,
20199
- mode
20200
- })
19822
+ ]
20201
19823
  });
20202
- const value = await response.json().catch(() => void 0);
20203
- if (!response.ok) {
20204
- const error = value?.["error"];
20205
- throw new Error(typeof error === "string" ? error : `HTTP ${String(response.status)}`);
20206
- }
20207
- return value?.mode ?? mode;
20208
19824
  }
20209
19825
  // src/client/use-roster.ts
20210
19826
  let pending;
@@ -20304,7 +19920,46 @@ window.__ModuleLoader__.load({
20304
19920
  model: "模型",
20305
19921
  modelHint: "模型 id。",
20306
19922
  reasoningEffort: "思考档位",
20307
- reasoningEffortHint: "默认就跟服务商自己的默认。"
19923
+ reasoningEffortHint: "默认就跟服务商自己的默认。",
19924
+ panel: "会话模式",
19925
+ panelDescription: "当前会话的模式与生效面:工具、技能、政策与注入开关。",
19926
+ panelNoSession: "当前没有会话",
19927
+ persona: "系统提示词",
19928
+ tools: "工具",
19929
+ skills: "技能",
19930
+ policies: "政策规则",
19931
+ switches: "注入面",
19932
+ instructions: "工作区指令",
19933
+ skillsCatalog: "技能目录",
19934
+ runtimeContext: "动态快照",
19935
+ empty: "(无)",
19936
+ loading: "读取中…",
19937
+ saveDefault: "保存为默认",
19938
+ reset: "重置",
19939
+ apply: "应用到会话",
19940
+ applied: "已应用到会话",
19941
+ fromSession: "会话覆盖",
19942
+ fromMode: "模式默认",
19943
+ saveAs: "另存为",
19944
+ delete: "删除",
19945
+ deleteConfirm: "删除这个模式?它会从配置里移除,使用它的会话回到默认模式。",
19946
+ cancel: "取消",
19947
+ confirm: "确定",
19948
+ nameLabel: "显示名(可选)",
19949
+ codeLabel: "模式 code(必填)",
19950
+ codePlaceholder: "例如 my-mode",
19951
+ namePlaceholder: "新模式名",
19952
+ addValue: "添加一项",
19953
+ general: "通用配置",
19954
+ stateDefault: "默认",
19955
+ stateMixed: "混合",
19956
+ stateOn: "开",
19957
+ stateOff: "关",
19958
+ stateAllow: "允许",
19959
+ stateDeny: "禁止",
19960
+ savedNewMode: "已另存为新模式,对新会话生效",
19961
+ saved: "已保存,对新会话生效",
19962
+ saveFailed: "保存失败"
20308
19963
  };
20309
19964
  const en = {
20310
19965
  seatHint: "Pick the mode for this session",
@@ -20315,10 +19970,50 @@ window.__ModuleLoader__.load({
20315
19970
  model: "Model",
20316
19971
  modelHint: "Model id.",
20317
19972
  reasoningEffort: "Reasoning effort",
20318
- reasoningEffortHint: "Default keeps the provider's own default."
19973
+ reasoningEffortHint: "Default keeps the provider's own default.",
19974
+ panel: "Session mode",
19975
+ panelDescription: "The mode this session runs and what it currently exposes: tools, skills, policies and injections.",
19976
+ panelNoSession: "No session",
19977
+ persona: "System prompt",
19978
+ tools: "Tools",
19979
+ skills: "Skills",
19980
+ policies: "Policies",
19981
+ switches: "Injections",
19982
+ instructions: "Workspace instructions",
19983
+ skillsCatalog: "Skill catalog",
19984
+ runtimeContext: "Runtime context",
19985
+ empty: "(none)",
19986
+ loading: "Loading…",
19987
+ saveDefault: "Save as default",
19988
+ reset: "Reset",
19989
+ apply: "Apply to session",
19990
+ applied: "Applied to session",
19991
+ fromSession: "session override",
19992
+ fromMode: "mode default",
19993
+ saveAs: "Save as",
19994
+ delete: "Delete",
19995
+ deleteConfirm: "Delete this mode? It is removed from the configuration and sessions using it fall back to the default.",
19996
+ cancel: "Cancel",
19997
+ confirm: "Confirm",
19998
+ nameLabel: "Display name (optional)",
19999
+ codeLabel: "Mode code (required)",
20000
+ codePlaceholder: "e.g. my-mode",
20001
+ namePlaceholder: "New mode name",
20002
+ addValue: "Add an entry",
20003
+ general: "General",
20004
+ stateDefault: "Default",
20005
+ stateMixed: "Mixed",
20006
+ stateOn: "On",
20007
+ stateOff: "Off",
20008
+ stateAllow: "Allow",
20009
+ stateDeny: "Deny",
20010
+ savedNewMode: "Saved as a new mode; applies to new sessions",
20011
+ saved: "Saved; applies to new sessions",
20012
+ saveFailed: "Save failed"
20319
20013
  };
20320
20014
  // src/client/index.ts
20321
20015
  const NS = "session-mode";
20016
+ const PANEL_ID = "@morlay/dsh-session-mode";
20322
20017
  const FIELDS = [
20323
20018
  "provider",
20324
20019
  "model",
@@ -20458,46 +20153,59 @@ window.__ModuleLoader__.load({
20458
20153
  };
20459
20154
  }, "session-mode: composer chip");
20460
20155
  });
20461
- let refreshPage;
20462
- let hints;
20463
- ctx.inject(["schemaFormHints"], (scope) => scope.effect(() => {
20464
- const service = unwrapHintService(scope);
20465
- hints = service;
20466
- const off = service.subscribe(() => {
20467
- refreshPage?.();
20156
+ ctx.inject([
20157
+ "sidebarRightTabs",
20158
+ "slots",
20159
+ "locale"
20160
+ ], (scope) => scope.effect(() => {
20161
+ const panelT = scope.locale.bind(NS);
20162
+ const offType = scope.sidebarRightTabs.register({
20163
+ id: PANEL_ID,
20164
+ kind: PANEL_ID,
20165
+ priority: "extension",
20166
+ title: () => panelT("panel"),
20167
+ guide: [{
20168
+ id: PANEL_ID,
20169
+ order: 20,
20170
+ title: () => panelT("panel"),
20171
+ description: () => panelT("panelDescription")
20172
+ }]
20468
20173
  });
20174
+ const body = scope.slots.inject("sidebar.right.pane.tab", () => scope.slots.register({
20175
+ name: "sidebar.right.pane.tab",
20176
+ key: PANEL_ID,
20177
+ locale: NS
20178
+ }, SessionModePanel));
20469
20179
  return () => {
20470
- off();
20471
- hints = void 0;
20180
+ body();
20181
+ offType();
20472
20182
  };
20473
- }, "session-mode: bundle page candidates"));
20474
- ctx.inject([
20475
- "slots",
20476
- "locale",
20477
- "configForms",
20478
- "settingsSchema"
20479
- ], (scope) => {
20480
- scope.effect(() => {
20481
- const { face, refresh, dispose } = createBundleConfigFace(scope, scope.locale.bind(BUNDLE_NS), () => hints);
20482
- refreshPage = refresh;
20483
- const bundleDictionary = scope.locale.register(BUNDLE_NS, {
20484
- zh: bundleZh,
20485
- en: bundleEn
20183
+ }, "session-mode: sidebar panel"));
20184
+ ctx.inject(["sidebarRight", "uiSession"], (scope) => scope.effect(() => {
20185
+ const current = scope.uiSession.adapter.current;
20186
+ const mounted = scope.sidebarRight.mounted;
20187
+ let queued = false;
20188
+ const sync = () => {
20189
+ if (queued) return;
20190
+ queued = true;
20191
+ queueMicrotask(() => {
20192
+ queued = false;
20193
+ const key = current.getSnapshot().key;
20194
+ if (typeof key !== "string" || key.length === 0) return;
20195
+ if (mounted.getSnapshot() !== key) return;
20196
+ try {
20197
+ scope.sidebarRight.openTab(PANEL_ID);
20198
+ } catch {}
20486
20199
  });
20487
- const off = scope.slots.inject("plugins.bundle.config", () => scope.slots.register({
20488
- name: "plugins.bundle.config",
20489
- key: BUNDLE_CONFIG_KEY,
20490
- locale: BUNDLE_NS,
20491
- inject: () => face
20492
- }, BundleConfigPage));
20493
- return () => {
20494
- off();
20495
- bundleDictionary();
20496
- refreshPage = void 0;
20497
- dispose();
20498
- };
20499
- }, "session-mode: bundle configuration page");
20500
- });
20200
+ };
20201
+ const offCurrent = current.subscribe(sync);
20202
+ const offMounted = mounted.subscribe(sync);
20203
+ sync();
20204
+ return () => {
20205
+ offCurrent();
20206
+ offMounted();
20207
+ };
20208
+ }, "session-mode: panel follows the session"));
20501
20209
  }
20502
20210
  exports.SESSION_MODE_NS = SESSION_MODE_NS;
20503
20211
  exports.apply = apply;