dsh-web-search-enhanced 0.2.0 → 0.2.2

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/lib/client.cjs CHANGED
@@ -33,7 +33,12 @@ window.__ModuleLoader__.load({
33
33
  const SEARCH = {
34
34
  packageName: "dsh-web-search-enhanced",
35
35
  rowId: "web-search-enhanced",
36
- providerId: "search-enhanced"
36
+ providerId: "search-enhanced",
37
+ tools: {
38
+ action: "web_search_enhanced",
39
+ list: "list_web_search_providers"
40
+ },
41
+ command: "setup-search"
37
42
  };
38
43
  /**
39
44
  * Key of a row's configuration page on the Plugins page (`plugins.row.config`).
@@ -151,6 +156,7 @@ window.__ModuleLoader__.load({
151
156
  "version",
152
157
  "defaultConnection",
153
158
  "showSelector",
159
+ "enhancedTools",
154
160
  "defaults",
155
161
  "connections"
156
162
  ];
@@ -193,8 +199,16 @@ window.__ModuleLoader__.load({
193
199
  }
194
200
  return result;
195
201
  }
202
+ /**
203
+ * Operator text shown as one line (a label in menus, error messages and the
204
+ * Agent's provider list): control characters and line or paragraph separators
205
+ * become spaces, runs of whitespace one space, the ends trimmed.
206
+ */
207
+ function singleLine(value) {
208
+ return value.replaceAll(/[\p{Cc}\p{Zl}\p{Zp}]/gu, " ").replaceAll(/\s+/gu, " ").trim();
209
+ }
196
210
  const text = zod.z.string().trim().min(1).max(TEXT_MAX_LENGTH);
197
- const label = zod.z.string().trim().min(1).max(200);
211
+ const label = zod.z.string().transform(singleLine).pipe(zod.z.string().min(1).max(200));
198
212
  const credentialRef = text.regex(CREDENTIAL_REF, "must be a credential reference such as EXA_API_KEY");
199
213
  const endpoint = text.refine(isEndpoint, "must be an absolute http(s) URL without credentials, query or fragment");
200
214
  const options = zod.z.record(zod.z.string(), zod.z.unknown());
@@ -203,6 +217,7 @@ window.__ModuleLoader__.load({
203
217
  version: zod.z.literal(currentVersion).optional(),
204
218
  defaultConnection: zod.z.string().min(1).max(200).optional(),
205
219
  showSelector: zod.z.boolean().optional(),
220
+ enhancedTools: zod.z.boolean().optional(),
206
221
  defaults: defaults.optional(),
207
222
  connections: zod.z.record(zod.z.string().max(200), zod.z.unknown()).refine((value) => Object.keys(value).length <= 200, "must contain at most 200 connections").optional()
208
223
  });
@@ -264,7 +279,7 @@ window.__ModuleLoader__.load({
264
279
  const customSchema = customConnectionSchema(adapterKeys, protocolKeys);
265
280
  const connections = /* @__PURE__ */ new Map();
266
281
  const rejectId = (id, path) => fail(path, "unknown connection id \"" + id + "\"; this release does not read 0.1.x settings; remove the field or use Reset on the settings page");
267
- const resolve = (id, key, override, customEndpoint) => {
282
+ const resolve = (id, key, override, origin) => {
268
283
  const info = catalog[key];
269
284
  const path = "connections." + id;
270
285
  const access = info.kind === "model" ? "api-key" : override.access ?? defaultAccess(info.access);
@@ -276,11 +291,12 @@ window.__ModuleLoader__.load({
276
291
  if (problems.length > 0) fail(path + ".options", problems.join("; ") + "; this release does not read 0.1.x settings; remove the field or use Reset on the settings page");
277
292
  const trustedEndpoint = override.trustedEndpoint ?? false;
278
293
  for (const [name, rule] of Object.entries(info.options)) if (rule.kind === "url" && values[name] !== void 0 && values[name] !== info.endpoint && !trustedEndpoint) fail(path + ".trustedEndpoint", "a non-official " + name + " needs trustedEndpoint: true");
294
+ const disabled = override.disabled ?? (origin.kind === "builtin" ? info.optIn : false);
279
295
  const base = {
280
296
  id,
281
297
  catalogKey: key,
282
298
  label: override.label ?? info.label,
283
- disabled: override.disabled ?? false,
299
+ disabled,
284
300
  remote: info.remote,
285
301
  trustedEndpoint,
286
302
  options: values
@@ -297,7 +313,7 @@ window.__ModuleLoader__.load({
297
313
  };
298
314
  if (info.kind !== "native" && info.kind !== "adapter") return fail(path + ".kind", "unsupported catalog kind");
299
315
  if (!info.access.apiKey || info.endpoint === void 0) return fail(path, "remote catalog entry needs credentials and an endpoint");
300
- const url = customEndpoint ?? info.endpoint;
316
+ const url = origin.kind === "custom" && origin.endpoint !== void 0 ? origin.endpoint : info.endpoint;
301
317
  if (url !== info.endpoint && !trustedEndpoint) fail(path + ".trustedEndpoint", "a custom endpoint needs trustedEndpoint: true");
302
318
  return {
303
319
  ...base,
@@ -318,7 +334,7 @@ window.__ModuleLoader__.load({
318
334
  ]) if (Object.hasOwn(raw, field)) fail("connections." + id + "." + field, "this connection takes its route from the Session model");
319
335
  }
320
336
  const override = raw === void 0 ? {} : parse(builtinOverrideSchema, raw, "connections." + id);
321
- connections.set(id, resolve(id, key, override));
337
+ connections.set(id, resolve(id, key, override, { kind: "builtin" }));
322
338
  }
323
339
  for (const [id, raw] of Object.entries(overrides)) {
324
340
  if (connections.has(id)) continue;
@@ -328,7 +344,10 @@ window.__ModuleLoader__.load({
328
344
  if (custom.kind === "adapter") connections.set(id, resolve(id, custom.adapter, {
329
345
  ...custom,
330
346
  access: "api-key"
331
- }, custom.endpoint));
347
+ }, {
348
+ kind: "custom",
349
+ endpoint: custom.endpoint
350
+ }));
332
351
  else {
333
352
  const info = protocols[custom.protocol];
334
353
  const values = custom.options ?? {};
@@ -359,6 +378,7 @@ window.__ModuleLoader__.load({
359
378
  version: migrations.currentVersion,
360
379
  defaultConnection,
361
380
  showSelector: settings.showSelector ?? true,
381
+ enhancedTools: settings.enhancedTools ?? false,
362
382
  defaults: settings.defaults ?? parse(defaults.schema, {}, "defaults"),
363
383
  connections
364
384
  };
@@ -800,6 +820,7 @@ window.__ModuleLoader__.load({
800
820
  const MODEL_PROTOCOLS = {
801
821
  "anthropic-messages": {
802
822
  label: "Anthropic Messages",
823
+ use: "the model searches and answers",
803
824
  options: {
804
825
  maxTokens,
805
826
  anthropicTool,
@@ -815,6 +836,7 @@ window.__ModuleLoader__.load({
815
836
  },
816
837
  "openai-responses": {
817
838
  label: "OpenAI Responses",
839
+ use: "the model searches and answers",
818
840
  options: {
819
841
  maxTokens,
820
842
  responsesTool,
@@ -828,6 +850,7 @@ window.__ModuleLoader__.load({
828
850
  },
829
851
  "openai-chat-completions": {
830
852
  label: "OpenAI Chat Completions",
853
+ use: "the model searches and answers",
831
854
  options: {
832
855
  maxTokens,
833
856
  searchContextSize,
@@ -847,6 +870,8 @@ window.__ModuleLoader__.load({
847
870
  label: "DeepSeek Messages",
848
871
  kind: "native",
849
872
  remote: true,
873
+ optIn: false,
874
+ use: "answers with a summary and sources",
850
875
  endpoint: "https://api.deepseek.com/anthropic/v1",
851
876
  homepage: "https://platform.deepseek.com",
852
877
  access: nativeAccess,
@@ -877,6 +902,7 @@ window.__ModuleLoader__.load({
877
902
  label: "DeepSeek Search API",
878
903
  kind: "native",
879
904
  remote: true,
905
+ optIn: true,
880
906
  endpoint: "https://api.deepseek.com/v1/tools",
881
907
  homepage: "https://platform.deepseek.com",
882
908
  access: nativeAccess,
@@ -895,6 +921,8 @@ window.__ModuleLoader__.load({
895
921
  label: "Session model",
896
922
  kind: "model",
897
923
  remote: true,
924
+ optIn: false,
925
+ use: "the session model searches and answers",
898
926
  access: {
899
927
  keyless: false,
900
928
  apiKey: false,
@@ -914,6 +942,7 @@ window.__ModuleLoader__.load({
914
942
  },
915
943
  kind: "adapter",
916
944
  remote: true,
945
+ optIn: true,
917
946
  freshness: ["api-key"],
918
947
  homepage: "https://exa.ai",
919
948
  options: { type: {
@@ -936,6 +965,7 @@ window.__ModuleLoader__.load({
936
965
  },
937
966
  kind: "adapter",
938
967
  remote: true,
968
+ optIn: true,
939
969
  freshness: ["keyless", "api-key"],
940
970
  homepage: "https://www.firecrawl.dev",
941
971
  options: {
@@ -961,6 +991,7 @@ window.__ModuleLoader__.load({
961
991
  },
962
992
  kind: "adapter",
963
993
  remote: true,
994
+ optIn: true,
964
995
  freshness: [],
965
996
  homepage: "https://www.tavily.com",
966
997
  options: {
@@ -993,6 +1024,7 @@ window.__ModuleLoader__.load({
993
1024
  },
994
1025
  kind: "adapter",
995
1026
  remote: true,
1027
+ optIn: true,
996
1028
  freshness: [],
997
1029
  homepage: "https://www.tinyfish.ai",
998
1030
  options: {
@@ -1029,8 +1061,10 @@ window.__ModuleLoader__.load({
1029
1061
  },
1030
1062
  kind: "adapter",
1031
1063
  remote: true,
1064
+ optIn: true,
1032
1065
  freshness: [],
1033
1066
  homepage: "https://openalex.org",
1067
+ use: "scholarly works (papers, citations); keyword queries",
1034
1068
  options: {
1035
1069
  sort: {
1036
1070
  kind: "text",
@@ -1059,8 +1093,10 @@ window.__ModuleLoader__.load({
1059
1093
  },
1060
1094
  kind: "adapter",
1061
1095
  remote: true,
1096
+ optIn: true,
1062
1097
  freshness: [],
1063
1098
  homepage: "https://www.semanticscholar.org/product/api",
1099
+ use: "academic papers (citations, TL;DR); keyword queries",
1064
1100
  options: {
1065
1101
  year: {
1066
1102
  kind: "text",
@@ -1106,6 +1142,378 @@ window.__ModuleLoader__.load({
1106
1142
  return (0, react.useSyncExternalStore)(subscribe, getSnapshot);
1107
1143
  }
1108
1144
  //#endregion
1145
+ //#region ../core/src/client/settings/parts.tsx
1146
+ /** Building blocks shared by the settings sections. */
1147
+ function classNames(...args) {
1148
+ return args.flat(Number.POSITIVE_INFINITY).filter(Boolean).map((arg) => {
1149
+ if (typeof arg === "string") return arg;
1150
+ if (typeof arg === "object" && arg !== null) return Object.entries(arg).filter(([, v]) => Boolean(v)).map(([k]) => k).join(" ");
1151
+ return "";
1152
+ }).filter(Boolean).join(" ");
1153
+ }
1154
+ /** A local schema failure whose diagnostic stays out of the translated message. */
1155
+ var SettingsValidationError = class extends Error {};
1156
+ function createSettingsParts({ classes: css, keys }) {
1157
+ /** `titleId` lets a control inside name itself by the section title. */
1158
+ function Section({ title, titleId, actions, children }) {
1159
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1160
+ className: css.section,
1161
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1162
+ className: css.sectionHead,
1163
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1164
+ id: titleId,
1165
+ className: css.sectionTitle,
1166
+ children: title
1167
+ }), actions]
1168
+ }), children]
1169
+ });
1170
+ }
1171
+ /** Title and hint on the left, one control on the right; wraps under the text on narrow pages. */
1172
+ function SettingRow({ title, hint, children }) {
1173
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1174
+ className: css.settingRow,
1175
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1176
+ className: css.settingText,
1177
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1178
+ className: css.settingTitle,
1179
+ children: title
1180
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1181
+ className: css.hint,
1182
+ children: hint
1183
+ })]
1184
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1185
+ className: css.settingControl,
1186
+ children
1187
+ })]
1188
+ });
1189
+ }
1190
+ /** A dropdown in the settings pill style, built on the shared Menu. */
1191
+ function SelectMenu({ label, title, value, options, disabled, onChange, compact = false }) {
1192
+ const [open, setOpen] = (0, react.useState)(false);
1193
+ const current = options.find((option) => option.id === value);
1194
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1195
+ open,
1196
+ onClose: () => {
1197
+ setOpen(false);
1198
+ },
1199
+ items: options.map((option) => ({
1200
+ id: option.id,
1201
+ label: option.label
1202
+ })),
1203
+ selectedId: value,
1204
+ onSelect: (id) => {
1205
+ setOpen(false);
1206
+ if (id !== value) onChange(id);
1207
+ },
1208
+ align: "end",
1209
+ portal: true,
1210
+ anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1211
+ type: "button",
1212
+ className: compact ? css.selectCompact : css.select,
1213
+ "aria-label": label,
1214
+ title,
1215
+ "aria-haspopup": "menu",
1216
+ "aria-expanded": open,
1217
+ disabled,
1218
+ onClick: () => {
1219
+ setOpen(!open);
1220
+ },
1221
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1222
+ className: css.selectValue,
1223
+ children: current?.label ?? value
1224
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: css.selectChevron })]
1225
+ })
1226
+ });
1227
+ }
1228
+ /** Pending state and the last failure of one section's writes. */
1229
+ function useWrite(t) {
1230
+ const [state, setState] = (0, react.useState)({
1231
+ busy: false,
1232
+ error: void 0,
1233
+ errorTitle: void 0
1234
+ });
1235
+ const run = async (work) => {
1236
+ setState({
1237
+ busy: true,
1238
+ error: void 0,
1239
+ errorTitle: void 0
1240
+ });
1241
+ try {
1242
+ const accepted = await work();
1243
+ setState({
1244
+ busy: false,
1245
+ error: accepted ? void 0 : t(keys.refused),
1246
+ errorTitle: void 0
1247
+ });
1248
+ return accepted;
1249
+ } catch (error) {
1250
+ const message = error instanceof Error ? error.message : String(error);
1251
+ const invalid = error instanceof SettingsValidationError;
1252
+ setState({
1253
+ busy: false,
1254
+ error: invalid ? t(keys.invalid) : message || t(keys.failed),
1255
+ errorTitle: invalid ? message : void 0
1256
+ });
1257
+ return false;
1258
+ }
1259
+ };
1260
+ return {
1261
+ ...state,
1262
+ run
1263
+ };
1264
+ }
1265
+ function ErrorText({ children, title }) {
1266
+ return children === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1267
+ className: css.error,
1268
+ role: "alert",
1269
+ title,
1270
+ children
1271
+ });
1272
+ }
1273
+ /** Inline confirmation of a destructive action: the question beside cancel and confirm. */
1274
+ function ConfirmRow({ message, confirmLabel, cancelLabel, disabled, onConfirm, onCancel }) {
1275
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1276
+ className: css.confirmRow,
1277
+ role: "group",
1278
+ "aria-label": message,
1279
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1280
+ className: css.confirmText,
1281
+ children: message
1282
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1283
+ className: css.actions,
1284
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1285
+ variant: "ghost",
1286
+ size: "sm",
1287
+ onClick: onCancel,
1288
+ children: cancelLabel
1289
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1290
+ variant: "outline",
1291
+ size: "sm",
1292
+ className: css.danger,
1293
+ disabled,
1294
+ onClick: onConfirm,
1295
+ children: confirmLabel
1296
+ })]
1297
+ })]
1298
+ });
1299
+ }
1300
+ return {
1301
+ Section,
1302
+ SettingRow,
1303
+ SelectMenu,
1304
+ ErrorText,
1305
+ ConfirmRow,
1306
+ useWrite
1307
+ };
1308
+ }
1309
+ function applyOps(user, ops) {
1310
+ const root = structuredClone(user !== null && typeof user === "object" && !Array.isArray(user) ? user : {});
1311
+ for (const op of ops) {
1312
+ if (op.path.length === 0) continue;
1313
+ if (op.op === "set") {
1314
+ let current = root;
1315
+ for (let i = 0; i < op.path.length - 1; i++) {
1316
+ const seg = op.path[i];
1317
+ if (current[seg] === null || typeof current[seg] !== "object" || Array.isArray(current[seg])) current[seg] = {};
1318
+ current = current[seg];
1319
+ }
1320
+ current[op.path.at(-1)] = structuredClone(op.value);
1321
+ } else if (op.op === "unset") {
1322
+ let current = root;
1323
+ for (let i = 0; i < op.path.length - 1; i++) {
1324
+ const seg = op.path[i];
1325
+ if (current[seg] === null || typeof current[seg] !== "object" || Array.isArray(current[seg])) break;
1326
+ current = current[seg];
1327
+ }
1328
+ delete current[op.path.at(-1)];
1329
+ }
1330
+ }
1331
+ return root;
1332
+ }
1333
+ //#endregion
1334
+ //#region ../core/src/client/settings/document.ts
1335
+ /** ConfigForm.value is schema-projected; the raw layers retain unknown fields for validation. */
1336
+ function effectiveDocument(snapshot) {
1337
+ return merge(snapshot.base, snapshot.user);
1338
+ }
1339
+ /** Validate before exposing normal editors; only successful older migrations need writeback. */
1340
+ function editableSettings(raw, layers, writable, resolve, migrations = SETTINGS_MIGRATIONS) {
1341
+ try {
1342
+ const version = documentVersion$2(raw, migrations.currentVersion);
1343
+ const config = migrateDocument(raw, migrations);
1344
+ resolve(config);
1345
+ return {
1346
+ kind: "ready",
1347
+ config,
1348
+ layers,
1349
+ needsWriteback: writable && version.kind === "older"
1350
+ };
1351
+ } catch (error) {
1352
+ return {
1353
+ kind: "invalid",
1354
+ diagnostic: diagnostic(error),
1355
+ layers,
1356
+ needsWriteback: false
1357
+ };
1358
+ }
1359
+ }
1360
+ /** Check the exact recursively overlaid candidate that the settings store will persist. */
1361
+ function settingsOperations(layers, operations, resolve) {
1362
+ try {
1363
+ const candidate = effectiveDocument({
1364
+ base: layers.base,
1365
+ user: applyOps(layers.user, operations)
1366
+ });
1367
+ const result = [...operations];
1368
+ if (result.some((op) => op.op === "set") && candidate.version === void 0) {
1369
+ result.push({
1370
+ op: "set",
1371
+ path: ["version"],
1372
+ value: 2
1373
+ });
1374
+ candidate.version = 2;
1375
+ }
1376
+ resolve(candidate);
1377
+ return result;
1378
+ } catch (error) {
1379
+ throw new SettingsValidationError(diagnostic(error));
1380
+ }
1381
+ }
1382
+ /**
1383
+ * Share validation, revision fencing and automatic future migrations between both pages.
1384
+ * @param snapshot - the settings snapshot the page renders.
1385
+ * @param read - the settings snapshot now, which a write the form accepted has already updated.
1386
+ */
1387
+ function useSettingsDocument(snapshot, read, resolve, mutate, migrations = SETTINGS_MIGRATIONS) {
1388
+ const view = (0, react.useMemo)(() => {
1389
+ if (snapshot.status === "unavailable" || snapshot.value === void 0 || snapshot.revision === void 0) return void 0;
1390
+ const result = editableSettings(effectiveDocument(snapshot), {
1391
+ base: snapshot.base,
1392
+ user: snapshot.user
1393
+ }, snapshot.writable, resolve, migrations);
1394
+ if (snapshot.diagnostic === void 0) return result;
1395
+ return {
1396
+ kind: "invalid",
1397
+ layers: result.layers,
1398
+ needsWriteback: false,
1399
+ diagnostic: result.kind === "invalid" ? result.diagnostic + "; " + snapshot.diagnostic : snapshot.diagnostic
1400
+ };
1401
+ }, [
1402
+ snapshot,
1403
+ resolve,
1404
+ migrations
1405
+ ]);
1406
+ const attemptedRevision = (0, react.useRef)(void 0);
1407
+ const [failure, setFailure] = (0, react.useState)();
1408
+ const revision = snapshot.revision;
1409
+ (0, react.useEffect)(() => {
1410
+ if (view?.kind !== "ready" || !view.needsWriteback || revision === void 0 || attemptedRevision.current === revision) return;
1411
+ attemptedRevision.current = revision;
1412
+ const writeback = async () => {
1413
+ try {
1414
+ const operations = replaceDocumentOperations(view.layers.user, view.config);
1415
+ await mutate(settingsOperations(view.layers, operations, resolve), revision);
1416
+ } catch (error) {
1417
+ setFailure({
1418
+ revision,
1419
+ diagnostic: diagnostic(error)
1420
+ });
1421
+ }
1422
+ };
1423
+ writeback();
1424
+ }, [
1425
+ view,
1426
+ revision,
1427
+ resolve,
1428
+ mutate
1429
+ ]);
1430
+ const queue = (0, react.useRef)(Promise.resolve());
1431
+ const submit = async (change, displayedRevision) => {
1432
+ if (typeof change !== "function") {
1433
+ if (change.length === 0) return true;
1434
+ if (view === void 0 || !snapshot.writable || displayedRevision === void 0) return false;
1435
+ return mutate(settingsOperations(view.layers, change, resolve), displayedRevision);
1436
+ }
1437
+ const current = read();
1438
+ if (current.status !== "ready" || !current.writable || current.revision === void 0) return false;
1439
+ const layers = {
1440
+ base: current.base,
1441
+ user: current.user
1442
+ };
1443
+ const operations = change(layers);
1444
+ if (operations.length === 0) return true;
1445
+ return mutate(settingsOperations(layers, operations, resolve), current.revision);
1446
+ };
1447
+ const write = (change, expectedRevision = revision) => {
1448
+ const task = queue.current.then(() => submit(change, expectedRevision));
1449
+ queue.current = task.then(() => {}, () => {});
1450
+ return task;
1451
+ };
1452
+ return {
1453
+ view,
1454
+ write,
1455
+ migrationError: failure?.revision === revision ? failure?.diagnostic : void 0
1456
+ };
1457
+ }
1458
+ function replaceDocumentOperations(user, document) {
1459
+ const operations = Object.keys(objectOf$1(user)).filter((key) => !Object.hasOwn(document, key)).map((key) => ({
1460
+ op: "unset",
1461
+ path: [key]
1462
+ }));
1463
+ for (const [key, value] of Object.entries(document)) operations.push({
1464
+ op: "set",
1465
+ path: [key],
1466
+ value
1467
+ });
1468
+ return operations;
1469
+ }
1470
+ function diagnostic(error) {
1471
+ return error instanceof Error ? error.message : String(error);
1472
+ }
1473
+ function isObject$1(value) {
1474
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1475
+ }
1476
+ function objectOf$1(value) {
1477
+ return isObject$1(value) ? value : {};
1478
+ }
1479
+ function merge(base, user) {
1480
+ const result = { ...objectOf$1(base) };
1481
+ for (const [key, value] of Object.entries(objectOf$1(user))) Object.defineProperty(result, key, {
1482
+ value: isObject$1(value) && isObject$1(result[key]) ? merge(result[key], value) : value,
1483
+ enumerable: true,
1484
+ writable: true,
1485
+ configurable: true
1486
+ });
1487
+ return result;
1488
+ }
1489
+ //#endregion
1490
+ //#region ../core/src/client/remote-services.ts
1491
+ /** Whether the catalog has remote services and every one of them is off. */
1492
+ function remoteServicesOff(catalog, resolved) {
1493
+ const remote = Object.keys(catalog).filter((key) => catalog[key].optIn);
1494
+ return remote.length > 0 && remote.every((key) => resolved.connections.get(builtinId(key)).disabled);
1495
+ }
1496
+ /** The fact for the current row settings, recomputed only when the settings snapshot changes. */
1497
+ function remoteServicesOffSource(settings, catalog, resolve) {
1498
+ let cached;
1499
+ const compute = (source) => {
1500
+ if (source.value === void 0) return false;
1501
+ const view = editableSettings(effectiveDocument(source), source, false, resolve);
1502
+ return view.kind === "ready" && remoteServicesOff(catalog, resolve(view.config));
1503
+ };
1504
+ return {
1505
+ getSnapshot() {
1506
+ const source = settings.getSnapshot();
1507
+ if (cached === void 0 || cached.source !== source) cached = {
1508
+ source,
1509
+ off: compute(source)
1510
+ };
1511
+ return cached.off;
1512
+ },
1513
+ subscribe: (listener) => settings.subscribe(listener)
1514
+ };
1515
+ }
1516
+ //#endregion
1109
1517
  //#region ../core/src/client/command.ts
1110
1518
  /** Shared ids cannot be shadowed by capability-specific option groups. */
1111
1519
  function parseSelectionCommandOption(id, parseOptions) {
@@ -1113,7 +1521,11 @@ window.__ModuleLoader__.load({
1113
1521
  if (id.startsWith("connection:") && id.length > 11) return { connectionId: id.slice(11) };
1114
1522
  return parseOptions?.(id);
1115
1523
  }
1116
- /** A keyboard entry to the same per-session store the composer menu uses. */
1524
+ /**
1525
+ * The browser's bare invocation of the capability's Host command: a popup over
1526
+ * the same per-session store the composer menu uses. The Host command keeps its
1527
+ * menu row and runs every line with arguments.
1528
+ */
1117
1529
  function selectionCommand(selections, spec) {
1118
1530
  const t = (key) => spec.t(spec.keys[key]);
1119
1531
  const read = async (sessionId) => {
@@ -1123,9 +1535,6 @@ window.__ModuleLoader__.load({
1123
1535
  };
1124
1536
  return {
1125
1537
  name: spec.name,
1126
- label: () => t("label"),
1127
- description: () => t("description"),
1128
- icon: spec.icon,
1129
1538
  available: () => true,
1130
1539
  ui: {
1131
1540
  kind: "popupSelect",
@@ -1173,6 +1582,19 @@ window.__ModuleLoader__.load({
1173
1582
  }
1174
1583
  };
1175
1584
  }
1585
+ /**
1586
+ * Call `ran` after this browser ran the Host command `name` in a Session. A line
1587
+ * with arguments (`/setup-search off`) writes on the Host without the selection
1588
+ * store, so that Session's views must read again. DSH reports each Host command
1589
+ * this browser executed with `command/executed`, without its arguments, so every
1590
+ * run counts; runs from another browser or the TUI are not reported here.
1591
+ * @returns the listener's disposer; it also ends with `ctx`'s fiber.
1592
+ */
1593
+ function followHostCommand(ctx, name, ran) {
1594
+ return ctx.on("command/executed", (sessionId, executed) => {
1595
+ if (executed === name) ran(sessionId);
1596
+ });
1597
+ }
1176
1598
  //#endregion
1177
1599
  //#region ../core/src/client/selection.ts
1178
1600
  var ConnectionSelectionStore = class {
@@ -1253,10 +1675,16 @@ window.__ModuleLoader__.load({
1253
1675
  * read again, the others read on their next load.
1254
1676
  */
1255
1677
  invalidate() {
1256
- for (const entry of this.entries.values()) {
1257
- this.outdate(entry);
1258
- if (entry.observers > 0) this.read(entry);
1259
- }
1678
+ for (const entry of this.entries.values()) this.reread(entry);
1679
+ }
1680
+ /**
1681
+ * The Session's selection changed without this store (its Host command ran):
1682
+ * its views read again, at once when observed, otherwise on their next load.
1683
+ * A read already in flight reads once more.
1684
+ */
1685
+ invalidateSession(sessionId) {
1686
+ const entry = this.entries.get(sessionId);
1687
+ if (entry !== void 0) this.reread(entry);
1260
1688
  }
1261
1689
  entry(sessionId) {
1262
1690
  const existing = this.entries.get(sessionId);
@@ -1289,6 +1717,10 @@ window.__ModuleLoader__.load({
1289
1717
  this.entries.set(sessionId, entry);
1290
1718
  return entry;
1291
1719
  }
1720
+ reread(entry) {
1721
+ this.outdate(entry);
1722
+ if (entry.observers > 0) this.read(entry);
1723
+ }
1292
1724
  outdate(entry) {
1293
1725
  entry.stale = true;
1294
1726
  entry.epoch += 1;
@@ -1505,26 +1937,26 @@ window.__ModuleLoader__.load({
1505
1937
  };
1506
1938
  //#endregion
1507
1939
  //#region ../core/src/client/settings/sparse.ts
1508
- const objectOf$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1509
- const isObject$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1940
+ const objectOf = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1941
+ const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1510
1942
  function equal(a, b) {
1511
1943
  if (Object.is(a, b)) return true;
1512
1944
  if (Array.isArray(a) || Array.isArray(b)) return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((value, index) => equal(value, b[index]));
1513
- if (!isObject$1(a) || !isObject$1(b)) return false;
1945
+ if (!isObject(a) || !isObject(b)) return false;
1514
1946
  const keys = Object.keys(a);
1515
1947
  return keys.length === Object.keys(b).length && keys.every((key) => Object.hasOwn(b, key) && equal(a[key], b[key]));
1516
1948
  }
1517
1949
  /** Remove what an object value shares with its inherited counterpart, key by key. */
1518
1950
  function delta(value, inherited) {
1519
1951
  if (equal(value, inherited)) return void 0;
1520
- if (!isObject$1(value)) return value;
1521
- const base = objectOf$1(inherited);
1952
+ if (!isObject(value)) return value;
1953
+ const base = objectOf(inherited);
1522
1954
  return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, delta(child, base[key])]).filter(([, child]) => child !== void 0));
1523
1955
  }
1524
1956
  function documentVersion$1(layers) {
1525
1957
  return documentVersion$2({
1526
- ...objectOf$1(layers.base),
1527
- ...objectOf$1(layers.user)
1958
+ ...objectOf(layers.base),
1959
+ ...objectOf(layers.user)
1528
1960
  });
1529
1961
  }
1530
1962
  function assertCurrentVersion(layers) {
@@ -1540,241 +1972,54 @@ window.__ModuleLoader__.load({
1540
1972
  assertCurrentVersion(layers);
1541
1973
  const inherited = valueAt(layers.base, path) ?? fallback;
1542
1974
  const user = valueAt(layers.user, path);
1543
- const ops = [];
1544
- if (target === void 0 || equal(target, inherited)) {
1545
- if (user !== void 0) ops.push({
1546
- op: "unset",
1547
- path: prunedPath(layers.user, path)
1548
- });
1549
- } else {
1550
- const sparse = delta(target, inherited);
1551
- if (!equal(sparse, user)) ops.push({
1552
- op: "set",
1553
- path: [...path],
1554
- value: sparse
1555
- });
1556
- }
1557
- if (ops.some((op) => op.op === "set") && objectOf$1(layers.user).version !== spec.version) ops.push({
1558
- op: "set",
1559
- path: ["version"],
1560
- value: spec.version
1561
- });
1562
- return ops;
1563
- }
1564
- function valueAt(root, path) {
1565
- return path.reduce((value, key) => objectOf$1(value)[key], root);
1566
- }
1567
- /**
1568
- * The path to unset so no empty object stays behind: removing the only key of
1569
- * a user-layer object removes that object instead, up to a top-level field.
1570
- */
1571
- function prunedPath(user, path) {
1572
- let end = path.length;
1573
- while (end > 1 && Object.keys(objectOf$1(valueAt(user, path.slice(0, end - 1)))).length === 1) end -= 1;
1574
- return path.slice(0, end);
1575
- }
1576
- /** Unset every field this plugin stores in the user layer; inherited values and credentials stay. */
1577
- function resetSettingsOperations$1(layers) {
1578
- return Object.keys(objectOf$1(layers.user)).map((key) => ({
1579
- op: "unset",
1580
- path: [key]
1581
- }));
1582
- }
1583
- /** Bind the current document version and fields; no predecessor schema is inferred. */
1584
- function createSparseSettings(spec) {
1585
- return {
1586
- documentVersion: (layers) => documentVersion$1(layers),
1587
- sparseSettingOperations: (layers, path, target, fallback) => sparseSettingOperations$1(spec, layers, path, target, fallback),
1588
- resetSettingsOperations: (layers) => resetSettingsOperations$1(layers)
1589
- };
1590
- }
1591
- //#endregion
1592
- //#region ../core/src/client/settings/parts.tsx
1593
- /** Building blocks shared by the settings sections. */
1594
- function classNames(...args) {
1595
- return args.flat(Number.POSITIVE_INFINITY).filter(Boolean).map((arg) => {
1596
- if (typeof arg === "string") return arg;
1597
- if (typeof arg === "object" && arg !== null) return Object.entries(arg).filter(([, v]) => Boolean(v)).map(([k]) => k).join(" ");
1598
- return "";
1599
- }).filter(Boolean).join(" ");
1600
- }
1601
- /** A local schema failure whose diagnostic stays out of the translated message. */
1602
- var SettingsValidationError = class extends Error {};
1603
- function createSettingsParts({ classes: css, keys }) {
1604
- function Section({ title, actions, children }) {
1605
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1606
- className: css.section,
1607
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1608
- className: css.sectionHead,
1609
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
1610
- className: css.sectionTitle,
1611
- children: title
1612
- }), actions]
1613
- }), children]
1614
- });
1615
- }
1616
- /** Title and hint on the left, one control on the right; wraps under the text on narrow pages. */
1617
- function SettingRow({ title, hint, children }) {
1618
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1619
- className: css.settingRow,
1620
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1621
- className: css.settingText,
1622
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1623
- className: css.settingTitle,
1624
- children: title
1625
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1626
- className: css.hint,
1627
- children: hint
1628
- })]
1629
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1630
- className: css.settingControl,
1631
- children
1632
- })]
1633
- });
1634
- }
1635
- /** A dropdown in the settings pill style, built on the shared Menu. */
1636
- function SelectMenu({ label, title, value, options, disabled, onChange, compact = false }) {
1637
- const [open, setOpen] = (0, react.useState)(false);
1638
- const current = options.find((option) => option.id === value);
1639
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
1640
- open,
1641
- onClose: () => {
1642
- setOpen(false);
1643
- },
1644
- items: options.map((option) => ({
1645
- id: option.id,
1646
- label: option.label
1647
- })),
1648
- selectedId: value,
1649
- onSelect: (id) => {
1650
- setOpen(false);
1651
- if (id !== value) onChange(id);
1652
- },
1653
- align: "end",
1654
- portal: true,
1655
- anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1656
- type: "button",
1657
- className: compact ? css.selectCompact : css.select,
1658
- "aria-label": label,
1659
- title,
1660
- "aria-haspopup": "menu",
1661
- "aria-expanded": open,
1662
- disabled,
1663
- onClick: () => {
1664
- setOpen(!open);
1665
- },
1666
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1667
- className: css.selectValue,
1668
- children: current?.label ?? value
1669
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: css.selectChevron })]
1670
- })
1671
- });
1672
- }
1673
- /** Pending state and the last failure of one section's writes. */
1674
- function useWrite(t) {
1675
- const [state, setState] = (0, react.useState)({
1676
- busy: false,
1677
- error: void 0,
1678
- errorTitle: void 0
1679
- });
1680
- const run = async (work) => {
1681
- setState({
1682
- busy: true,
1683
- error: void 0,
1684
- errorTitle: void 0
1685
- });
1686
- try {
1687
- const accepted = await work();
1688
- setState({
1689
- busy: false,
1690
- error: accepted ? void 0 : t(keys.refused),
1691
- errorTitle: void 0
1692
- });
1693
- return accepted;
1694
- } catch (error) {
1695
- const message = error instanceof Error ? error.message : String(error);
1696
- const invalid = error instanceof SettingsValidationError;
1697
- setState({
1698
- busy: false,
1699
- error: invalid ? t(keys.invalid) : message || t(keys.failed),
1700
- errorTitle: invalid ? message : void 0
1701
- });
1702
- return false;
1703
- }
1704
- };
1705
- return {
1706
- ...state,
1707
- run
1708
- };
1709
- }
1710
- function ErrorText({ children, title }) {
1711
- return children === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1712
- className: css.error,
1713
- role: "alert",
1714
- title,
1715
- children
1975
+ const ops = [];
1976
+ if (target === void 0 || equal(target, inherited)) {
1977
+ if (user !== void 0) ops.push({
1978
+ op: "unset",
1979
+ path: prunedPath(layers.user, path)
1716
1980
  });
1717
- }
1718
- /** Inline confirmation of a destructive action: the question beside cancel and confirm. */
1719
- function ConfirmRow({ message, confirmLabel, cancelLabel, disabled, onConfirm, onCancel }) {
1720
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1721
- className: css.confirmRow,
1722
- role: "group",
1723
- "aria-label": message,
1724
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1725
- className: css.confirmText,
1726
- children: message
1727
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1728
- className: css.actions,
1729
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1730
- variant: "ghost",
1731
- size: "sm",
1732
- onClick: onCancel,
1733
- children: cancelLabel
1734
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1735
- variant: "outline",
1736
- size: "sm",
1737
- className: css.danger,
1738
- disabled,
1739
- onClick: onConfirm,
1740
- children: confirmLabel
1741
- })]
1742
- })]
1981
+ } else {
1982
+ const sparse = delta(target, inherited);
1983
+ if (!equal(sparse, user)) ops.push({
1984
+ op: "set",
1985
+ path: [...path],
1986
+ value: sparse
1743
1987
  });
1744
1988
  }
1989
+ if (ops.some((op) => op.op === "set") && objectOf(layers.user).version !== spec.version) ops.push({
1990
+ op: "set",
1991
+ path: ["version"],
1992
+ value: spec.version
1993
+ });
1994
+ return ops;
1995
+ }
1996
+ function valueAt(root, path) {
1997
+ return path.reduce((value, key) => objectOf(value)[key], root);
1998
+ }
1999
+ /**
2000
+ * The path to unset so no empty object stays behind: removing the only key of
2001
+ * a user-layer object removes that object instead, up to a top-level field.
2002
+ */
2003
+ function prunedPath(user, path) {
2004
+ let end = path.length;
2005
+ while (end > 1 && Object.keys(objectOf(valueAt(user, path.slice(0, end - 1)))).length === 1) end -= 1;
2006
+ return path.slice(0, end);
2007
+ }
2008
+ /** Unset every field this plugin stores in the user layer; inherited values and credentials stay. */
2009
+ function resetSettingsOperations$1(layers) {
2010
+ return Object.keys(objectOf(layers.user)).map((key) => ({
2011
+ op: "unset",
2012
+ path: [key]
2013
+ }));
2014
+ }
2015
+ /** Bind the current document version and fields; no predecessor schema is inferred. */
2016
+ function createSparseSettings(spec) {
1745
2017
  return {
1746
- Section,
1747
- SettingRow,
1748
- SelectMenu,
1749
- ErrorText,
1750
- ConfirmRow,
1751
- useWrite
2018
+ documentVersion: (layers) => documentVersion$1(layers),
2019
+ sparseSettingOperations: (layers, path, target, fallback) => sparseSettingOperations$1(spec, layers, path, target, fallback),
2020
+ resetSettingsOperations: (layers) => resetSettingsOperations$1(layers)
1752
2021
  };
1753
2022
  }
1754
- function applyOps(user, ops) {
1755
- const root = structuredClone(user !== null && typeof user === "object" && !Array.isArray(user) ? user : {});
1756
- for (const op of ops) {
1757
- if (op.path.length === 0) continue;
1758
- if (op.op === "set") {
1759
- let current = root;
1760
- for (let i = 0; i < op.path.length - 1; i++) {
1761
- const seg = op.path[i];
1762
- if (current[seg] === null || typeof current[seg] !== "object" || Array.isArray(current[seg])) current[seg] = {};
1763
- current = current[seg];
1764
- }
1765
- current[op.path.at(-1)] = structuredClone(op.value);
1766
- } else if (op.op === "unset") {
1767
- let current = root;
1768
- for (let i = 0; i < op.path.length - 1; i++) {
1769
- const seg = op.path[i];
1770
- if (current[seg] === null || typeof current[seg] !== "object" || Array.isArray(current[seg])) break;
1771
- current = current[seg];
1772
- }
1773
- delete current[op.path.at(-1)];
1774
- }
1775
- }
1776
- return root;
1777
- }
1778
2023
  //#endregion
1779
2024
  //#region ../core/src/client/settings/CredentialEditor.tsx
1780
2025
  /** Write-only key entry: the value goes to DSH Credentials and never into settings. */
@@ -2217,14 +2462,25 @@ window.__ModuleLoader__.load({
2217
2462
  function adapterKeys(catalog) {
2218
2463
  return Object.entries(catalog).filter(([, info]) => info.kind === "adapter").map(([key]) => key);
2219
2464
  }
2465
+ /** A built-in entry's override as stored, its label on one line as the grammar reads it. */
2220
2466
  function builtinOverride(config, key) {
2221
2467
  const stored = config.connections === void 0 ? void 0 : config.connections[builtinId(key)];
2222
- return stored === void 0 ? {} : stored;
2468
+ if (stored === void 0) return {};
2469
+ return stored.label === void 0 ? stored : {
2470
+ ...stored,
2471
+ label: singleLine(stored.label)
2472
+ };
2223
2473
  }
2224
- /** Custom connections in document order; the document has passed the capability's validation. */
2474
+ /** Custom connections in document order, labels on one line; the document has passed the capability's validation. */
2225
2475
  function customConnections(config) {
2226
2476
  if (config.connections === void 0) return [];
2227
- return Object.entries(config.connections).filter(([id]) => CUSTOM_CONNECTION_ID.test(id)).map(([id, connection]) => [id, connection]);
2477
+ return Object.entries(config.connections).filter(([id]) => CUSTOM_CONNECTION_ID.test(id)).map(([id, stored]) => {
2478
+ const connection = stored;
2479
+ return [id, {
2480
+ ...connection,
2481
+ label: singleLine(connection.label)
2482
+ }];
2483
+ });
2228
2484
  }
2229
2485
  /** Built-in connections default to the anonymous route where one exists. */
2230
2486
  function builtinAccess(info, override) {
@@ -2560,15 +2816,19 @@ window.__ModuleLoader__.load({
2560
2816
  });
2561
2817
  function ServicesSection(props) {
2562
2818
  const catalogKeys = Object.keys(catalog);
2563
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Section, {
2819
+ const optIn = catalogKeys.some((key) => catalog[key].optIn);
2820
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
2564
2821
  title: props.t(keys["services.title"]),
2565
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2822
+ children: [optIn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2823
+ className: css.note,
2824
+ children: props.t(keys["services.optInNotice"])
2825
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
2566
2826
  className: css.list,
2567
2827
  children: catalogKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServiceRow, {
2568
2828
  catalogKey: key,
2569
2829
  ...props
2570
2830
  }, key))
2571
- })
2831
+ })]
2572
2832
  });
2573
2833
  }
2574
2834
  function ServiceRow({ catalogKey, doc, write, credentials, saveCredential, t }) {
@@ -2584,18 +2844,18 @@ window.__ModuleLoader__.load({
2584
2844
  const credential = builtinCredential(info, override);
2585
2845
  const credentialRef = credential === void 0 ? void 0 : credential.ref;
2586
2846
  const credentialStatus = credentialRef === void 0 ? void 0 : credentials[credentialRef];
2587
- const enabled = override.disabled !== true;
2847
+ const enabled = !(override.disabled ?? info.optIn);
2588
2848
  const disabled = !doc.editable || action.busy;
2589
- const operations = (path, target, fallback) => sparse.sparseSettingOperations(doc.layers, [
2849
+ const operations = (layers, path, target, fallback) => sparse.sparseSettingOperations(layers, [
2590
2850
  "connections",
2591
2851
  connectionId,
2592
2852
  ...path
2593
2853
  ], target, fallback);
2594
- const save = (path, target, fallback) => action.run(() => write(operations(path, target, fallback)));
2854
+ const save = (path, target, fallback) => action.run(() => write((layers) => operations(layers, path, target, fallback)));
2595
2855
  const stored = override.options === void 0 ? {} : override.options;
2596
- const saveOptions = (changes) => action.run(() => {
2597
- let layers = doc.layers;
2598
- return write(Object.entries(changes).flatMap(([option, value]) => {
2856
+ const saveOptions = (changes) => action.run(() => write((initial) => {
2857
+ let layers = initial;
2858
+ return Object.entries(changes).flatMap(([option, value]) => {
2599
2859
  const next = sparse.sparseSettingOperations(layers, [
2600
2860
  "connections",
2601
2861
  connectionId,
@@ -2607,15 +2867,22 @@ window.__ModuleLoader__.load({
2607
2867
  user: applyOps(layers.user, next)
2608
2868
  };
2609
2869
  return next;
2610
- }));
2611
- });
2612
- /** The keyless route takes only the options it accepts; the rest go in the same write. */
2870
+ });
2871
+ }));
2872
+ /**
2873
+ * The keyless route takes only the options it accepts; the rest go in the
2874
+ * same write. Which ones those are is read from the layers the write runs
2875
+ * on: options changed while it waited stay as they are now.
2876
+ */
2613
2877
  const switchAccess = (next) => {
2614
2878
  if (next === "api-key") return save(["access"], next, "keyless");
2615
- const rejected = keylessRejected(info.options, stored);
2616
- const kept = Object.fromEntries(Object.entries(stored).filter(([option]) => !rejected.includes(option)));
2617
- const optionOps = rejected.length === 0 ? [] : operations(["options"], Object.keys(kept).length === 0 ? void 0 : kept);
2618
- return action.run(() => write([...operations(["access"], next, "keyless"), ...optionOps]));
2879
+ return action.run(() => write((layers) => {
2880
+ const current = builtinOverride(effectiveDocument(layers), catalogKey).options;
2881
+ const options = current === void 0 ? {} : current;
2882
+ const rejected = keylessRejected(info.options, options);
2883
+ const kept = Object.fromEntries(Object.entries(options).filter(([option]) => !rejected.includes(option)));
2884
+ return [...operations(layers, ["access"], next, "keyless"), ...rejected.length === 0 ? [] : operations(layers, ["options"], Object.keys(kept).length === 0 ? void 0 : kept)];
2885
+ }));
2619
2886
  };
2620
2887
  const note = notes[catalogKey];
2621
2888
  const extra = extras[catalogKey];
@@ -2665,7 +2932,7 @@ window.__ModuleLoader__.load({
2665
2932
  checked: enabled,
2666
2933
  label: t(keys["services.enable"], { name }),
2667
2934
  disabled,
2668
- onChange: (next) => void save(["disabled"], !next, false)
2935
+ onChange: (next) => void save(["disabled"], !next, info.optIn)
2669
2936
  })]
2670
2937
  })
2671
2938
  ]
@@ -2890,7 +3157,7 @@ window.__ModuleLoader__.load({
2890
3157
  const action = useWrite(t);
2891
3158
  const disabled = !doc.editable || action.busy;
2892
3159
  const save = (path, target, fallback) => {
2893
- action.run(() => write(sparse.sparseSettingOperations(doc.layers, path, target, fallback)));
3160
+ action.run(() => write((layers) => sparse.sparseSettingOperations(layers, path, target, fallback)));
2894
3161
  };
2895
3162
  const missing = defaultConnection !== void 0 && !choices.some((choice) => choice.id === defaultConnection);
2896
3163
  const warning = missing ? t(keys.connectionMissing, { id: defaultConnection }) : defaultAvailability && (defaultAvailability.disabled || configurationState(true, defaultAvailability.access, defaultAvailability.credentialStatus) === "warning") ? defaultAvailability.warning : void 0;
@@ -2937,6 +3204,53 @@ window.__ModuleLoader__.load({
2937
3204
  };
2938
3205
  }
2939
3206
  //#endregion
3207
+ //#region ../core/src/client/settings/EnhancedToolsSection.tsx
3208
+ /**
3209
+ * The row's Enhanced tools switch (docs/design.md, "开关与同意"): what turning
3210
+ * it on lets the Agent do, and what the operator agrees to by doing so.
3211
+ */
3212
+ function createEnhancedToolsSection({ classes: css, parts: { Section, ErrorText, useWrite }, sparse, keys }) {
3213
+ return function EnhancedToolsSection({ doc, write, t }) {
3214
+ const id = (0, react.useId)();
3215
+ const action = useWrite(t);
3216
+ const enabled = doc.config.enhancedTools ?? false;
3217
+ const save = (next) => action.run(() => write((layers) => sparse.sparseSettingOperations(layers, ["enhancedTools"], next, false)));
3218
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
3219
+ title: t(keys.title),
3220
+ titleId: id + "-title",
3221
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3222
+ className: css.settingRow,
3223
+ role: "group",
3224
+ "aria-labelledby": id + "-title",
3225
+ "aria-describedby": id + "-description " + id + "-consent",
3226
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3227
+ className: css.settingText,
3228
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3229
+ id: id + "-description",
3230
+ className: css.settingLead,
3231
+ children: t(keys.description)
3232
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3233
+ id: id + "-consent",
3234
+ className: css.hint,
3235
+ children: t(keys.consent)
3236
+ })]
3237
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3238
+ className: css.settingControl,
3239
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
3240
+ checked: enabled,
3241
+ label: t(keys.title),
3242
+ disabled: !doc.editable || action.busy,
3243
+ onChange: (next) => void save(next)
3244
+ })
3245
+ })]
3246
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorText, {
3247
+ title: action.errorTitle,
3248
+ children: action.error
3249
+ })]
3250
+ });
3251
+ };
3252
+ }
3253
+ //#endregion
2940
3254
  //#region ../core/src/client/settings/ResetSection.tsx
2941
3255
  function createResetSection({ classes: css, keys, parts: { ConfirmRow, ErrorText, useWrite }, sparse }) {
2942
3256
  return function ResetSection({ doc, write, t }) {
@@ -2971,174 +3285,39 @@ window.__ModuleLoader__.load({
2971
3285
  onClick: () => {
2972
3286
  setConfirming(true);
2973
3287
  },
2974
- children: t(keys["reset.button"])
2975
- })]
2976
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorText, {
2977
- title: action.errorTitle,
2978
- children: action.error
2979
- })]
2980
- });
2981
- };
2982
- }
2983
- //#endregion
2984
- //#region ../core/src/client/settings/RouteNotice.tsx
2985
- function createRouteNotice({ classes: css, keys, other }) {
2986
- return function RouteNotice({ route, t }) {
2987
- let text;
2988
- switch (route.kind) {
2989
- case "this": return null;
2990
- case "unknown":
2991
- text = t(keys.unknown);
2992
- break;
2993
- case "auto":
2994
- text = t(keys.auto, { id: route.providerId });
2995
- break;
2996
- case "other": text = other?.(route, t) ?? t(keys.other, {
2997
- provider: route.provider,
2998
- id: route.providerId
2999
- });
3000
- }
3001
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3002
- className: css.warning,
3003
- role: "status",
3004
- children: text
3005
- });
3006
- };
3007
- }
3008
- //#endregion
3009
- //#region ../core/src/client/settings/document.ts
3010
- /** ConfigForm.value is schema-projected; the raw layers retain unknown fields for validation. */
3011
- function effectiveDocument(snapshot) {
3012
- return merge(snapshot.base, snapshot.user);
3013
- }
3014
- /** Validate before exposing normal editors; only successful older migrations need writeback. */
3015
- function editableSettings(raw, layers, writable, resolve, migrations = SETTINGS_MIGRATIONS) {
3016
- try {
3017
- const version = documentVersion$2(raw, migrations.currentVersion);
3018
- const config = migrateDocument(raw, migrations);
3019
- resolve(config);
3020
- return {
3021
- kind: "ready",
3022
- config,
3023
- layers,
3024
- needsWriteback: writable && version.kind === "older"
3025
- };
3026
- } catch (error) {
3027
- return {
3028
- kind: "invalid",
3029
- diagnostic: diagnostic(error),
3030
- layers,
3031
- needsWriteback: false
3032
- };
3033
- }
3034
- }
3035
- /** Check the exact recursively overlaid candidate that the settings store will persist. */
3036
- function settingsOperations(layers, operations, resolve) {
3037
- try {
3038
- const candidate = effectiveDocument({
3039
- base: layers.base,
3040
- user: applyOps(layers.user, operations)
3041
- });
3042
- const result = [...operations];
3043
- if (result.some((op) => op.op === "set") && candidate.version === void 0) {
3044
- result.push({
3045
- op: "set",
3046
- path: ["version"],
3047
- value: 2
3048
- });
3049
- candidate.version = 2;
3050
- }
3051
- resolve(candidate);
3052
- return result;
3053
- } catch (error) {
3054
- throw new SettingsValidationError(diagnostic(error));
3055
- }
3056
- }
3057
- /** Share validation, revision fencing and automatic future migrations between both pages. */
3058
- function useSettingsDocument(snapshot, resolve, mutate, migrations = SETTINGS_MIGRATIONS) {
3059
- const view = (0, react.useMemo)(() => {
3060
- if (snapshot.status === "unavailable" || snapshot.value === void 0 || snapshot.revision === void 0) return void 0;
3061
- const result = editableSettings(effectiveDocument(snapshot), {
3062
- base: snapshot.base,
3063
- user: snapshot.user
3064
- }, snapshot.writable, resolve, migrations);
3065
- if (snapshot.diagnostic === void 0) return result;
3066
- return {
3067
- kind: "invalid",
3068
- layers: result.layers,
3069
- needsWriteback: false,
3070
- diagnostic: result.kind === "invalid" ? result.diagnostic + "; " + snapshot.diagnostic : snapshot.diagnostic
3071
- };
3072
- }, [
3073
- snapshot,
3074
- resolve,
3075
- migrations
3076
- ]);
3077
- const attemptedRevision = (0, react.useRef)(void 0);
3078
- const [failure, setFailure] = (0, react.useState)();
3079
- const revision = snapshot.revision;
3080
- (0, react.useEffect)(() => {
3081
- if (view?.kind !== "ready" || !view.needsWriteback || revision === void 0 || attemptedRevision.current === revision) return;
3082
- attemptedRevision.current = revision;
3083
- const writeback = async () => {
3084
- try {
3085
- const operations = replaceDocumentOperations(view.layers.user, view.config);
3086
- await mutate(settingsOperations(view.layers, operations, resolve), revision);
3087
- } catch (error) {
3088
- setFailure({
3089
- revision,
3090
- diagnostic: diagnostic(error)
3091
- });
3092
- }
3093
- };
3094
- writeback();
3095
- }, [
3096
- view,
3097
- revision,
3098
- resolve,
3099
- mutate
3100
- ]);
3101
- const write = async (operations, expectedRevision = revision) => {
3102
- if (operations.length === 0) return true;
3103
- if (view === void 0 || !snapshot.writable || expectedRevision === void 0) return false;
3104
- return mutate(settingsOperations(view.layers, operations, resolve), expectedRevision);
3105
- };
3106
- return {
3107
- view,
3108
- write,
3109
- migrationError: failure?.revision === revision ? failure?.diagnostic : void 0
3288
+ children: t(keys["reset.button"])
3289
+ })]
3290
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorText, {
3291
+ title: action.errorTitle,
3292
+ children: action.error
3293
+ })]
3294
+ });
3110
3295
  };
3111
3296
  }
3112
- function replaceDocumentOperations(user, document) {
3113
- const operations = Object.keys(objectOf(user)).filter((key) => !Object.hasOwn(document, key)).map((key) => ({
3114
- op: "unset",
3115
- path: [key]
3116
- }));
3117
- for (const [key, value] of Object.entries(document)) operations.push({
3118
- op: "set",
3119
- path: [key],
3120
- value
3121
- });
3122
- return operations;
3123
- }
3124
- function diagnostic(error) {
3125
- return error instanceof Error ? error.message : String(error);
3126
- }
3127
- function isObject(value) {
3128
- return value !== null && typeof value === "object" && !Array.isArray(value);
3129
- }
3130
- function objectOf(value) {
3131
- return isObject(value) ? value : {};
3132
- }
3133
- function merge(base, user) {
3134
- const result = { ...objectOf(base) };
3135
- for (const [key, value] of Object.entries(objectOf(user))) Object.defineProperty(result, key, {
3136
- value: isObject(value) && isObject(result[key]) ? merge(result[key], value) : value,
3137
- enumerable: true,
3138
- writable: true,
3139
- configurable: true
3140
- });
3141
- return result;
3297
+ //#endregion
3298
+ //#region ../core/src/client/settings/RouteNotice.tsx
3299
+ function createRouteNotice({ classes: css, keys, other }) {
3300
+ return function RouteNotice({ route, t }) {
3301
+ let text;
3302
+ switch (route.kind) {
3303
+ case "this": return null;
3304
+ case "unknown":
3305
+ text = t(keys.unknown);
3306
+ break;
3307
+ case "auto":
3308
+ text = t(keys.auto, { id: route.providerId });
3309
+ break;
3310
+ case "other": text = other?.(route, t) ?? t(keys.other, {
3311
+ provider: route.provider,
3312
+ id: route.providerId
3313
+ });
3314
+ }
3315
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3316
+ className: css.warning,
3317
+ role: "status",
3318
+ children: text
3319
+ });
3320
+ };
3142
3321
  }
3143
3322
  //#endregion
3144
3323
  //#region ../core/src/client/settings/secret-diagnostics.ts
@@ -3248,7 +3427,7 @@ window.__ModuleLoader__.load({
3248
3427
  const options = connection.options === void 0 ? {} : connection.options;
3249
3428
  const fields = {
3250
3429
  id,
3251
- label: connection.label,
3430
+ label: singleLine(connection.label),
3252
3431
  credentialRef: connection.credentialRef,
3253
3432
  endpoint: connection.endpoint === void 0 ? "" : connection.endpoint,
3254
3433
  options: Object.fromEntries(Object.entries(options).map(([name, value]) => [name, fieldText(value)]))
@@ -3322,7 +3501,7 @@ window.__ModuleLoader__.load({
3322
3501
  const form = {
3323
3502
  ...input,
3324
3503
  id: input.id.trim(),
3325
- label: input.label.trim(),
3504
+ label: singleLine(input.label),
3326
3505
  credentialRef: input.credentialRef.trim(),
3327
3506
  endpoint: input.endpoint.trim()
3328
3507
  };
@@ -3841,7 +4020,7 @@ window.__ModuleLoader__.load({
3841
4020
  const disabled = !doc.editable || action.busy;
3842
4021
  const userConnections = doc.layers.user?.connections;
3843
4022
  const removable = userConnections !== void 0 && Object.hasOwn(userConnections, id);
3844
- const save = (path, target, fallback) => action.run(() => write(sparse.sparseSettingOperations(doc.layers, [
4023
+ const save = (path, target, fallback) => action.run(() => write((layers) => sparse.sparseSettingOperations(layers, [
3845
4024
  "connections",
3846
4025
  id,
3847
4026
  ...path
@@ -3973,6 +4152,49 @@ window.__ModuleLoader__.load({
3973
4152
  "searchContextSize"
3974
4153
  ];
3975
4154
  //#endregion
4155
+ //#region src/settings/resolve.ts
4156
+ const defaultsSchema = zod.z.strictObject({ freshness: zod.z.enum(FRESHNESS).default("auto") });
4157
+ /** Pure sparse resolution shared by Host and browser. */
4158
+ function resolveSettings(document = {}) {
4159
+ const resolved = resolveDocument({
4160
+ catalog: CATALOG,
4161
+ protocols: MODEL_PROTOCOLS,
4162
+ document,
4163
+ retiredFields: RETIRED_FIELDS,
4164
+ defaults: {
4165
+ schema: defaultsSchema,
4166
+ defaultConnection: DEFAULT_CONNECTION
4167
+ },
4168
+ capabilityLabel: "search"
4169
+ });
4170
+ const connections = /* @__PURE__ */ new Map();
4171
+ for (const [id, connection] of resolved.connections) {
4172
+ const { catalogKey, ...base } = connection;
4173
+ switch (base.kind) {
4174
+ case "local": throw new Error("search catalog cannot contain a local connection");
4175
+ case "model":
4176
+ connections.set(id, base);
4177
+ break;
4178
+ case "native":
4179
+ connections.set(id, {
4180
+ ...base,
4181
+ kind: "native",
4182
+ native: catalogKey
4183
+ });
4184
+ break;
4185
+ case "adapter": connections.set(id, {
4186
+ ...base,
4187
+ kind: "adapter",
4188
+ adapter: catalogKey
4189
+ });
4190
+ }
4191
+ }
4192
+ return {
4193
+ ...resolved,
4194
+ connections
4195
+ };
4196
+ }
4197
+ //#endregion
3976
4198
  //#region src/client/connections.ts
3977
4199
  /** The Session-model connection keeps its catalog name only until the operator renames it; that name is localized. */
3978
4200
  function connectionLabel(connection, t) {
@@ -4080,7 +4302,7 @@ window.__ModuleLoader__.load({
4080
4302
  }
4081
4303
  //#endregion
4082
4304
  //#region src/client/command.ts
4083
- const SEARCH_COMMAND = "setup-search";
4305
+ const SEARCH_COMMAND = SEARCH.command;
4084
4306
  const FRESHNESS_PREFIX = "freshness:";
4085
4307
  const parseFreshness = (id) => {
4086
4308
  const freshness = FRESHNESS.find((mode) => id === FRESHNESS_PREFIX + mode);
@@ -4089,11 +4311,8 @@ window.__ModuleLoader__.load({
4089
4311
  function searchCommand(selections, t) {
4090
4312
  return selectionCommand(selections, {
4091
4313
  name: SEARCH_COMMAND,
4092
- icon: _deepseek_ai_dsh_client_ui_primitives.IconGlobeOutlineRegular,
4093
4314
  t,
4094
4315
  keys: {
4095
- label: "command.label",
4096
- description: "command.description",
4097
4316
  placeholder: "command.placeholder",
4098
4317
  empty: "command.empty",
4099
4318
  noResults: "command.noResults",
@@ -4134,49 +4353,6 @@ window.__ModuleLoader__.load({
4134
4353
  });
4135
4354
  }
4136
4355
  //#endregion
4137
- //#region src/settings/resolve.ts
4138
- const defaultsSchema = zod.z.strictObject({ freshness: zod.z.enum(FRESHNESS).default("auto") });
4139
- /** Pure sparse resolution shared by Host and browser. */
4140
- function resolveSettings(document = {}) {
4141
- const resolved = resolveDocument({
4142
- catalog: CATALOG,
4143
- protocols: MODEL_PROTOCOLS,
4144
- document,
4145
- retiredFields: RETIRED_FIELDS,
4146
- defaults: {
4147
- schema: defaultsSchema,
4148
- defaultConnection: DEFAULT_CONNECTION
4149
- },
4150
- capabilityLabel: "search"
4151
- });
4152
- const connections = /* @__PURE__ */ new Map();
4153
- for (const [id, connection] of resolved.connections) {
4154
- const { catalogKey, ...base } = connection;
4155
- switch (base.kind) {
4156
- case "local": throw new Error("search catalog cannot contain a local connection");
4157
- case "model":
4158
- connections.set(id, base);
4159
- break;
4160
- case "native":
4161
- connections.set(id, {
4162
- ...base,
4163
- kind: "native",
4164
- native: catalogKey
4165
- });
4166
- break;
4167
- case "adapter": connections.set(id, {
4168
- ...base,
4169
- kind: "adapter",
4170
- adapter: catalogKey
4171
- });
4172
- }
4173
- }
4174
- return {
4175
- ...resolved,
4176
- connections
4177
- };
4178
- }
4179
- //#endregion
4180
4356
  //#region src/client/credentials.ts
4181
4357
  /** The DSH credential a connection names; the Session-model connection uses the Session route's own. */
4182
4358
  function credentialRefsOf(connection) {
@@ -4275,6 +4451,10 @@ window.__ModuleLoader__.load({
4275
4451
  "defaults.showSelectorHint": "When hidden, switch the current conversation with /setup-search.",
4276
4452
  "defaults.freshnessKeyOnly": "{name} (personal key)",
4277
4453
  "services.title": "Search connections",
4454
+ "services.optInNotice": "Remote services are off by default.",
4455
+ "enhanced.title": "Enhanced tools",
4456
+ "enhanced.description": "Lets the Agent pick which enabled service runs a search with web_search_enhanced, and see the choices with list_web_search_providers. Applies to conversations started afterwards.",
4457
+ "enhanced.consent": "Turning this on means you agree that when the Agent picks a service, the query is sent to that service.",
4278
4458
  "services.enable": "Enable {name}",
4279
4459
  "services.configure": "Configure",
4280
4460
  "services.configureLabel": "Configure {name}",
@@ -4400,7 +4580,7 @@ window.__ModuleLoader__.load({
4400
4580
  "status.modelReady": "{access} · model search · sent to {name}",
4401
4581
  "status.sessionModelNeedsKey": "The conversation model's API key isn't set",
4402
4582
  "status.sessionModelNeedsSignIn": "Sign in to the DeepSeek account again",
4403
- "status.reason.no-model": "No model chosen for this conversation yet",
4583
+ "status.reason.no-model": "The model for this conversation's next request isn't known yet",
4404
4584
  "status.reason.unknown-route": "Can't read the settings of this model's provider",
4405
4585
  "status.reason.protocol": "Its route declares no supported api",
4406
4586
  "status.reason.endpoint": "Its route has no fixed endpoint",
@@ -4488,7 +4668,7 @@ window.__ModuleLoader__.load({
4488
4668
  "freshness.auto": "自动",
4489
4669
  "freshness.fresh": "24 小时内",
4490
4670
  "freshness.realtime": "实时",
4491
- "status.disabled": "{name} 已在设置中停用。",
4671
+ "status.disabled": "{name} 未在设置中启用。",
4492
4672
  "status.needsCredential": "需要 API Key",
4493
4673
  "status.needsAccount": "需要 DeepSeek 账号或 API Key",
4494
4674
  "status.unsupported": "本版本不支持 {name}。",
@@ -4501,7 +4681,7 @@ window.__ModuleLoader__.load({
4501
4681
  "selector.freshnessIgnored": "{name} 不支持此偏好,选择不会生效。",
4502
4682
  "selector.offRow": "关闭",
4503
4683
  "write.conflict": "搜索设置已在别处更改,菜单已显示最新选择。",
4504
- "write.unavailable": "该连接已停用或已移除。",
4684
+ "write.unavailable": "该连接未启用或已移除。",
4505
4685
  "write.failed": "保存失败,请重试。",
4506
4686
  "command.label": "搜索连接",
4507
4687
  "command.description": "切换当前会话的搜索连接与内容实时性",
@@ -4532,10 +4712,14 @@ window.__ModuleLoader__.load({
4532
4712
  "defaults.showSelectorHint": "隐藏后可用 /setup-search 切换当前会话。",
4533
4713
  "defaults.freshnessKeyOnly": "{name}(个人密钥)",
4534
4714
  "services.title": "搜索连接",
4715
+ "services.optInNotice": "远程服务默认关闭。",
4716
+ "enhanced.title": "增强工具",
4717
+ "enhanced.description": "让 Agent 用 web_search_enhanced 在已启用的服务中自行选择本次搜索用哪个,并用 list_web_search_providers 查看可选项。只影响之后新建的会话。",
4718
+ "enhanced.consent": "打开即表示同意:Agent 选用某个服务时,查询会发送给该服务。",
4535
4719
  "services.enable": "启用 {name}",
4536
4720
  "services.configure": "配置",
4537
4721
  "services.configureLabel": "配置 {name}",
4538
- "services.off": "已停用",
4722
+ "services.off": "未启用",
4539
4723
  "services.keyless": "无需密钥",
4540
4724
  "services.personalKey": "个人密钥 {ref}",
4541
4725
  "services.account": "DeepSeek 账号或 {ref}",
@@ -4657,7 +4841,7 @@ window.__ModuleLoader__.load({
4657
4841
  "status.modelReady": "{access} · 模型搜索 · 查询发送给 {name}",
4658
4842
  "status.sessionModelNeedsKey": "会话模型的 API Key 未设置",
4659
4843
  "status.sessionModelNeedsSignIn": "请重新登录 DeepSeek 账号",
4660
- "status.reason.no-model": "本会话还没有选择模型",
4844
+ "status.reason.no-model": "还不知道本会话下一次请求用哪个模型",
4661
4845
  "status.reason.unknown-route": "无法读取会话模型服务商的设置",
4662
4846
  "status.reason.protocol": "该路由在模型设置里没有声明支持的 api",
4663
4847
  "status.reason.endpoint": "该路由在模型设置里没有固定的接口地址",
@@ -4742,14 +4926,14 @@ window.__ModuleLoader__.load({
4742
4926
  }
4743
4927
  var Section_module_css_default = {
4744
4928
  "container": "La6KHG_container",
4929
+ "label": "La6KHG_label",
4745
4930
  "caption": "La6KHG_caption",
4746
- "segmented": "La6KHG_segmented",
4747
4931
  "error": "La6KHG_error",
4748
- "label": "La6KHG_label"
4932
+ "segmented": "La6KHG_segmented"
4749
4933
  };
4750
4934
  //#endregion
4751
4935
  //#region src/client/section.ts
4752
- function searchSection(selection, settings, t) {
4936
+ function searchSection(selection, settings, remoteServicesOff, t) {
4753
4937
  function Options({ sessionId }) {
4754
4938
  const state = useObservable(selection.source(sessionId));
4755
4939
  const [error, setError] = (0, react.useState)("");
@@ -4787,6 +4971,7 @@ window.__ModuleLoader__.load({
4787
4971
  title: () => t("section.title"),
4788
4972
  selection,
4789
4973
  settings,
4974
+ remoteServicesOff,
4790
4975
  Options,
4791
4976
  describe: (connection) => describe(connection, t),
4792
4977
  sourceText: (view) => sourceText(view, t),
@@ -4802,7 +4987,7 @@ window.__ModuleLoader__.load({
4802
4987
  };
4803
4988
  //#endregion
4804
4989
  //#region \0dsh-css-module:/home/runner/work/dsh-tool-web-enhanced/dsh-tool-web-enhanced/packages/search-enhanced/src/client/settings/Settings.module.css.mjs
4805
- const css = ".o9fCSq_page{min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;gap:28px;display:flex;container-type:inline-size}.o9fCSq_intro{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;line-height:20px}.o9fCSq_status{color:var(--dsw-alias-label-tertiary);align-items:center;gap:6px;margin:0;font-size:13px;line-height:20px;display:flex}.o9fCSq_warning{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label);margin:0;padding:8px 12px;font-size:12px;line-height:18px}.o9fCSq_section{flex-direction:column;gap:4px;min-width:0;display:flex}.o9fCSq_sectionHead{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;min-height:28px;display:flex}.o9fCSq_sectionTitle{margin:0;font-size:14px;font-weight:500;line-height:20px}.o9fCSq_sectionActions{flex-wrap:wrap;gap:4px;display:flex}.o9fCSq_hint,.o9fCSq_note{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.o9fCSq_panel .o9fCSq_hint,.o9fCSq_editor .o9fCSq_hint{color:var(--dsw-alias-label-secondary)}.o9fCSq_hint [role=status]{color:var(--dsw-alias-label-secondary);align-items:baseline;gap:6px;margin-top:4px;display:flex}.o9fCSq_note{color:var(--dsw-alias-label-secondary)}.o9fCSq_error,.o9fCSq_success,.o9fCSq_fieldError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.o9fCSq_success{color:var(--dsw-alias-state-success-primary)}.o9fCSq_settingRow{border-bottom:.5px solid var(--dsw-alias-border-l2);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 24px;padding:14px 2px;display:flex}.o9fCSq_settingRow:last-of-type{border-bottom:0}.o9fCSq_settingText{flex-direction:column;flex:200px;gap:2px;min-width:0;display:flex}.o9fCSq_settingTitle{font-size:13.5px;font-weight:500;line-height:20px}.o9fCSq_settingControl{flex:0 auto;justify-content:flex-end;min-width:0;max-width:100%;display:flex}.o9fCSq_selectAnchor{min-width:0;max-width:100%}.o9fCSq_select,.o9fCSq_selectCompact{box-sizing:border-box;border-radius:var(--dsw-radius-md);min-width:0;max-width:100%;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;align-items:center;gap:8px;font-size:13px;line-height:20px;display:inline-flex}.o9fCSq_select{background:var(--dsw-alias-bg-module-platform);height:36px;padding:0 12px 0 14px}.o9fCSq_selectCompact{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);justify-content:space-between;width:100%;height:36px;padding:0 10px 0 12px}.o9fCSq_select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.o9fCSq_select:disabled,.o9fCSq_selectCompact:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.o9fCSq_select:focus-visible,.o9fCSq_selectCompact:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}.o9fCSq_selectValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.o9fCSq_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none}.o9fCSq_list{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.o9fCSq_row{border-bottom:.5px solid var(--dsw-alias-border-l2);padding:12px 2px}.o9fCSq_row:last-child{border-bottom:0}.o9fCSq_rowLine{align-items:center;gap:8px 12px;min-width:0;display:flex}.o9fCSq_rowMain{flex-direction:column;flex:1 1 0;gap:2px;min-width:0;display:flex}.o9fCSq_rowTitle{flex-wrap:wrap;align-items:center;gap:6px;font-size:13.5px;font-weight:500;line-height:20px;display:inline-flex}.o9fCSq_rowMeta{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:12px;line-height:18px}.o9fCSq_rowMeta code,.o9fCSq_optionKey{font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);font-size:11.5px}.o9fCSq_rowActions{box-sizing:border-box;flex-wrap:wrap;flex:none;align-items:center;gap:4px;display:flex}@container (width<=560px){.o9fCSq_rowLine:has(.o9fCSq_rowActions){flex-wrap:wrap}.o9fCSq_rowActions{flex-basis:100%;order:3;padding-left:10px}}.o9fCSq_segmented{align-self:flex-start;max-width:100%}.o9fCSq_disclosure{color:var(--dsw-alias-label-secondary)}.o9fCSq_disclosureIcon{flex:none;width:12px;height:12px;transition:transform .12s}.o9fCSq_disclosureOpen{transform:rotate(180deg)}@media (prefers-reduced-motion:reduce){.o9fCSq_disclosureIcon{transition:none}}.o9fCSq_danger{color:var(--dsw-alias-state-error-primary)}.o9fCSq_panel,.o9fCSq_editor{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:16px;margin:12px 0 2px;padding:16px;display:flex}.o9fCSq_field,.o9fCSq_formField{flex-direction:column;gap:6px;min-width:0;display:flex}.o9fCSq_fieldHead{align-items:center;gap:8px;display:flex}.o9fCSq_fieldLabel{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.o9fCSq_fieldState{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.o9fCSq_inline{flex-wrap:wrap;gap:8px;display:flex}.o9fCSq_inline>.o9fCSq_input{flex:160px}.o9fCSq_input{box-sizing:border-box;min-width:0;height:36px}.o9fCSq_input:has(input[aria-invalid=true]){border-color:var(--dsw-alias-state-error-primary)}.o9fCSq_link{color:var(--dsw-alias-link);align-self:flex-start;align-items:center;gap:2px;font-size:12px;font-weight:500;line-height:18px;text-decoration:none;display:inline-flex}.o9fCSq_link:hover,.o9fCSq_link:focus-visible{text-underline-offset:3px;text-decoration:underline dotted}.o9fCSq_optionGrid{grid-template-columns:repeat(auto-fill,minmax(min(100%,200px),1fr));gap:12px 16px;display:grid}.o9fCSq_option{flex-direction:column;gap:4px;min-width:0;display:flex}.o9fCSq_optionName{min-width:0;color:var(--dsw-alias-label-secondary);align-items:baseline;gap:6px;font-size:12px;line-height:18px;display:flex}.o9fCSq_optionKey{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);overflow:hidden}.o9fCSq_actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}.o9fCSq_empty{color:var(--dsw-alias-label-tertiary);margin:0;padding:8px 2px;font-size:13px;line-height:20px}.o9fCSq_editorTitle{margin:0;font-size:13.5px;font-weight:500;line-height:20px}.o9fCSq_formGrid{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:14px 16px;display:grid}.o9fCSq_formWide{grid-column:1/-1}@container (width<=760px){.o9fCSq_optionGrid{grid-template-columns:repeat(2,minmax(0,1fr))}}@container (width<=480px){.o9fCSq_optionGrid{grid-template-columns:minmax(0,1fr)}.o9fCSq_settingControl{flex-basis:100%;justify-content:flex-start}.o9fCSq_panel{padding:12px}}@container (width<=520px){.o9fCSq_formGrid{grid-template-columns:minmax(0,1fr)}}.o9fCSq_prefixed{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);align-items:center;height:36px;padding:0 8px 0 10px;display:flex}.o9fCSq_prefixed:focus-within{border-color:var(--dsw-alias-state-business-primary)}.o9fCSq_prefixedDisabled{color:var(--dsw-alias-label-tertiary)}.o9fCSq_prefix{font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-tertiary);flex:none;font-size:13px;line-height:22px}.o9fCSq_prefixedInput{min-width:0;font:inherit;color:var(--dsw-alias-label-primary);background:0 0;border:none;outline:none;flex:1;padding:0;font-size:14px;line-height:22px}.o9fCSq_prefixedInput::placeholder{color:var(--dsw-alias-label-dimmed)}.o9fCSq_reset{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:16px;display:flex}.o9fCSq_confirmRow{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 24px;display:flex}.o9fCSq_row .o9fCSq_confirmRow{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-module-platform);margin-top:10px;padding:8px 12px}.o9fCSq_confirmText{min-width:0;color:var(--dsw-alias-label-secondary);flex:240px;font-size:12px;line-height:18px}.o9fCSq_optionWide{grid-column:1/-1}.o9fCSq_textarea{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;min-width:0;min-height:72px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);padding:8px 10px;font-size:12px;line-height:20px}.o9fCSq_textarea:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}.o9fCSq_textarea::placeholder{color:var(--dsw-alias-label-dimmed)}.o9fCSq_textarea[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary)}.o9fCSq_textarea:disabled{cursor:not-allowed;opacity:.5}.o9fCSq_optionGroup{border-top:.5px solid var(--dsw-alias-border-l4);flex-direction:column;gap:8px;padding-top:12px;display:flex}.o9fCSq_optionGroup:first-child,.o9fCSq_fieldHead+.o9fCSq_optionGroup{border-top:0;padding-top:0}.o9fCSq_optionGroupTitle{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;line-height:18px}.o9fCSq_trust{border:.5px solid var(--dsw-alias-state-warn-primary);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);padding:10px 12px}.o9fCSq_trust .o9fCSq_trustCheck{align-items:flex-start}.o9fCSq_trust .o9fCSq_trustCheck input{margin-top:2px}.o9fCSq_actions>.o9fCSq_hint{flex:auto;align-self:center}";
4990
+ const css = ".o9fCSq_page{min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;gap:28px;display:flex;container-type:inline-size}.o9fCSq_intro{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;line-height:20px}.o9fCSq_status{color:var(--dsw-alias-label-tertiary);align-items:center;gap:6px;margin:0;font-size:13px;line-height:20px;display:flex}.o9fCSq_warning{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label);margin:0;padding:8px 12px;font-size:12px;line-height:18px}.o9fCSq_section{flex-direction:column;gap:4px;min-width:0;display:flex}.o9fCSq_sectionHead{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;min-height:28px;display:flex}.o9fCSq_sectionTitle{margin:0;font-size:14px;font-weight:500;line-height:20px}.o9fCSq_sectionActions{flex-wrap:wrap;gap:4px;display:flex}.o9fCSq_hint,.o9fCSq_note{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.o9fCSq_panel .o9fCSq_hint,.o9fCSq_editor .o9fCSq_hint{color:var(--dsw-alias-label-secondary)}.o9fCSq_hint [role=status]{color:var(--dsw-alias-label-secondary);align-items:baseline;gap:6px;margin-top:4px;display:flex}.o9fCSq_note{color:var(--dsw-alias-label-secondary)}.o9fCSq_error,.o9fCSq_success,.o9fCSq_fieldError{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.o9fCSq_success{color:var(--dsw-alias-state-success-primary)}.o9fCSq_settingRow{border-bottom:.5px solid var(--dsw-alias-border-l2);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 24px;padding:14px 2px;display:flex}.o9fCSq_settingRow:last-of-type{border-bottom:0}.o9fCSq_settingText{flex-direction:column;flex:200px;gap:2px;min-width:0;display:flex}.o9fCSq_settingTitle{font-size:13.5px;font-weight:500;line-height:20px}.o9fCSq_settingLead{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;font-weight:400;line-height:20px}.o9fCSq_settingControl{flex:0 auto;justify-content:flex-end;min-width:0;max-width:100%;display:flex}.o9fCSq_selectAnchor{min-width:0;max-width:100%}.o9fCSq_select,.o9fCSq_selectCompact{box-sizing:border-box;border-radius:var(--dsw-radius-md);min-width:0;max-width:100%;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;align-items:center;gap:8px;font-size:13px;line-height:20px;display:inline-flex}.o9fCSq_select{background:var(--dsw-alias-bg-module-platform);height:36px;padding:0 12px 0 14px}.o9fCSq_selectCompact{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-1);justify-content:space-between;width:100%;height:36px;padding:0 10px 0 12px}.o9fCSq_select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.o9fCSq_select:disabled,.o9fCSq_selectCompact:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.o9fCSq_select:focus-visible,.o9fCSq_selectCompact:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:1px}.o9fCSq_selectValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.o9fCSq_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none}.o9fCSq_list{flex-direction:column;margin:0;padding:0;list-style:none;display:flex}.o9fCSq_row{border-bottom:.5px solid var(--dsw-alias-border-l2);padding:12px 2px}.o9fCSq_row:last-child{border-bottom:0}.o9fCSq_rowLine{align-items:center;gap:8px 12px;min-width:0;display:flex}.o9fCSq_rowMain{flex-direction:column;flex:1 1 0;gap:2px;min-width:0;display:flex}.o9fCSq_rowTitle{flex-wrap:wrap;align-items:center;gap:6px;font-size:13.5px;font-weight:500;line-height:20px;display:inline-flex}.o9fCSq_rowMeta{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:12px;line-height:18px}.o9fCSq_rowMeta code,.o9fCSq_optionKey{font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);font-size:11.5px}.o9fCSq_rowActions{box-sizing:border-box;flex-wrap:wrap;flex:none;align-items:center;gap:4px;display:flex}@container (width<=560px){.o9fCSq_rowLine:has(.o9fCSq_rowActions){flex-wrap:wrap}.o9fCSq_rowActions{flex-basis:100%;order:3;padding-left:10px}}.o9fCSq_segmented{align-self:flex-start;max-width:100%}.o9fCSq_disclosure{color:var(--dsw-alias-label-secondary)}.o9fCSq_disclosureIcon{flex:none;width:12px;height:12px;transition:transform .12s}.o9fCSq_disclosureOpen{transform:rotate(180deg)}@media (prefers-reduced-motion:reduce){.o9fCSq_disclosureIcon{transition:none}}.o9fCSq_danger{color:var(--dsw-alias-state-error-primary)}.o9fCSq_panel,.o9fCSq_editor{border-radius:var(--dsw-radius-lg);background:var(--dsw-alias-bg-module-platform);flex-direction:column;gap:16px;margin:12px 0 2px;padding:16px;display:flex}.o9fCSq_field,.o9fCSq_formField{flex-direction:column;gap:6px;min-width:0;display:flex}.o9fCSq_fieldHead{align-items:center;gap:8px;display:flex}.o9fCSq_fieldLabel{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.o9fCSq_fieldState{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.o9fCSq_inline{flex-wrap:wrap;gap:8px;display:flex}.o9fCSq_inline>.o9fCSq_input{flex:160px}.o9fCSq_input{box-sizing:border-box;min-width:0;height:36px}.o9fCSq_input:has(input[aria-invalid=true]){border-color:var(--dsw-alias-state-error-primary)}.o9fCSq_link{color:var(--dsw-alias-link);align-self:flex-start;align-items:center;gap:2px;font-size:12px;font-weight:500;line-height:18px;text-decoration:none;display:inline-flex}.o9fCSq_link:hover,.o9fCSq_link:focus-visible{text-underline-offset:3px;text-decoration:underline dotted}.o9fCSq_optionGrid{grid-template-columns:repeat(auto-fill,minmax(min(100%,200px),1fr));gap:12px 16px;display:grid}.o9fCSq_option{flex-direction:column;gap:4px;min-width:0;display:flex}.o9fCSq_optionName{min-width:0;color:var(--dsw-alias-label-secondary);align-items:baseline;gap:6px;font-size:12px;line-height:18px;display:flex}.o9fCSq_optionKey{text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-tertiary);overflow:hidden}.o9fCSq_actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}.o9fCSq_empty{color:var(--dsw-alias-label-tertiary);margin:0;padding:8px 2px;font-size:13px;line-height:20px}.o9fCSq_editorTitle{margin:0;font-size:13.5px;font-weight:500;line-height:20px}.o9fCSq_formGrid{grid-template-columns:repeat(2,minmax(0,1fr));align-items:start;gap:14px 16px;display:grid}.o9fCSq_formWide{grid-column:1/-1}@container (width<=760px){.o9fCSq_optionGrid{grid-template-columns:repeat(2,minmax(0,1fr))}}@container (width<=480px){.o9fCSq_optionGrid{grid-template-columns:minmax(0,1fr)}.o9fCSq_settingControl{flex-basis:100%;justify-content:flex-start}.o9fCSq_panel{padding:12px}}@container (width<=520px){.o9fCSq_formGrid{grid-template-columns:minmax(0,1fr)}}.o9fCSq_prefixed{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);align-items:center;height:36px;padding:0 8px 0 10px;display:flex}.o9fCSq_prefixed:focus-within{border-color:var(--dsw-alias-state-business-primary)}.o9fCSq_prefixedDisabled{color:var(--dsw-alias-label-tertiary)}.o9fCSq_prefix{font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-tertiary);flex:none;font-size:13px;line-height:22px}.o9fCSq_prefixedInput{min-width:0;font:inherit;color:var(--dsw-alias-label-primary);background:0 0;border:none;outline:none;flex:1;padding:0;font-size:14px;line-height:22px}.o9fCSq_prefixedInput::placeholder{color:var(--dsw-alias-label-dimmed)}.o9fCSq_reset{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:6px;padding-top:16px;display:flex}.o9fCSq_confirmRow{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px 24px;display:flex}.o9fCSq_row .o9fCSq_confirmRow{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-module-platform);margin-top:10px;padding:8px 12px}.o9fCSq_confirmText{min-width:0;color:var(--dsw-alias-label-secondary);flex:240px;font-size:12px;line-height:18px}.o9fCSq_optionWide{grid-column:1/-1}.o9fCSq_textarea{box-sizing:border-box;resize:vertical;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);width:100%;min-width:0;min-height:72px;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-mono,ui-monospace, SFMono-Regular, Menlo, monospace);padding:8px 10px;font-size:12px;line-height:20px}.o9fCSq_textarea:focus{border-color:var(--dsw-alias-state-business-primary);outline:none}.o9fCSq_textarea::placeholder{color:var(--dsw-alias-label-dimmed)}.o9fCSq_textarea[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary)}.o9fCSq_textarea:disabled{cursor:not-allowed;opacity:.5}.o9fCSq_optionGroup{border-top:.5px solid var(--dsw-alias-border-l4);flex-direction:column;gap:8px;padding-top:12px;display:flex}.o9fCSq_optionGroup:first-child,.o9fCSq_fieldHead+.o9fCSq_optionGroup{border-top:0;padding-top:0}.o9fCSq_optionGroupTitle{color:var(--dsw-alias-label-primary);font-size:12px;font-weight:500;line-height:18px}.o9fCSq_trust{border:.5px solid var(--dsw-alias-state-warn-primary);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);padding:10px 12px}.o9fCSq_trust .o9fCSq_trustCheck{align-items:flex-start}.o9fCSq_trust .o9fCSq_trustCheck input{margin-top:2px}.o9fCSq_actions>.o9fCSq_hint{flex:auto;align-self:center}";
4806
4991
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify("dsh-web-search-enhanced/src/client/settings/Settings.module.css") + "]") === null) {
4807
4992
  const style = document.createElement("style");
4808
4993
  style.dataset.plugin = "dsh-web-search-enhanced";
@@ -4811,72 +4996,73 @@ window.__ModuleLoader__.load({
4811
4996
  document.head.appendChild(style);
4812
4997
  }
4813
4998
  var Settings_module_css_default = {
4814
- "panel": "o9fCSq_panel",
4815
- "inline": "o9fCSq_inline",
4816
- "hint": "o9fCSq_hint",
4817
- "rowMain": "o9fCSq_rowMain",
4818
- "optionGroupTitle": "o9fCSq_optionGroupTitle",
4819
- "warning": "o9fCSq_warning",
4820
- "list": "o9fCSq_list",
4821
- "actions": "o9fCSq_actions",
4999
+ "intro": "o9fCSq_intro",
5000
+ "disclosureOpen": "o9fCSq_disclosureOpen",
5001
+ "optionGroup": "o9fCSq_optionGroup",
4822
5002
  "trustCheck": "o9fCSq_trustCheck",
4823
- "fieldState": "o9fCSq_fieldState",
4824
- "row": "o9fCSq_row",
4825
- "selectChevron": "o9fCSq_selectChevron",
4826
- "optionKey": "o9fCSq_optionKey",
4827
- "textarea": "o9fCSq_textarea",
4828
- "formGrid": "o9fCSq_formGrid",
4829
- "status": "o9fCSq_status",
4830
- "option": "o9fCSq_option",
4831
- "selectAnchor": "o9fCSq_selectAnchor",
4832
- "confirmText": "o9fCSq_confirmText",
4833
- "editor": "o9fCSq_editor",
4834
5003
  "trust": "o9fCSq_trust",
4835
- "settingTitle": "o9fCSq_settingTitle",
5004
+ "field": "o9fCSq_field",
5005
+ "optionKey": "o9fCSq_optionKey",
4836
5006
  "success": "o9fCSq_success",
4837
- "fieldError": "o9fCSq_fieldError",
5007
+ "sectionHead": "o9fCSq_sectionHead",
5008
+ "confirmText": "o9fCSq_confirmText",
4838
5009
  "confirmRow": "o9fCSq_confirmRow",
4839
- "formField": "o9fCSq_formField",
4840
- "rowMeta": "o9fCSq_rowMeta",
4841
- "optionGrid": "o9fCSq_optionGrid",
4842
- "sectionTitle": "o9fCSq_sectionTitle",
4843
- "input": "o9fCSq_input",
5010
+ "inline": "o9fCSq_inline",
4844
5011
  "prefixedInput": "o9fCSq_prefixedInput",
4845
- "reset": "o9fCSq_reset",
4846
- "optionWide": "o9fCSq_optionWide",
4847
- "intro": "o9fCSq_intro",
5012
+ "rowTitle": "o9fCSq_rowTitle",
5013
+ "rowMain": "o9fCSq_rowMain",
5014
+ "rowActions": "o9fCSq_rowActions",
4848
5015
  "page": "o9fCSq_page",
4849
- "link": "o9fCSq_link",
4850
- "error": "o9fCSq_error",
4851
- "editorTitle": "o9fCSq_editorTitle",
4852
- "disclosure": "o9fCSq_disclosure",
5016
+ "selectValue": "o9fCSq_selectValue",
5017
+ "prefixedDisabled": "o9fCSq_prefixedDisabled",
5018
+ "rowMeta": "o9fCSq_rowMeta",
5019
+ "reset": "o9fCSq_reset",
5020
+ "actions": "o9fCSq_actions",
5021
+ "settingControl": "o9fCSq_settingControl",
5022
+ "fieldLabel": "o9fCSq_fieldLabel",
5023
+ "section": "o9fCSq_section",
5024
+ "formGrid": "o9fCSq_formGrid",
5025
+ "formWide": "o9fCSq_formWide",
5026
+ "selectCompact": "o9fCSq_selectCompact",
5027
+ "textarea": "o9fCSq_textarea",
5028
+ "prefix": "o9fCSq_prefix",
5029
+ "fieldError": "o9fCSq_fieldError",
5030
+ "sectionActions": "o9fCSq_sectionActions",
4853
5031
  "optionName": "o9fCSq_optionName",
5032
+ "row": "o9fCSq_row",
5033
+ "editorTitle": "o9fCSq_editorTitle",
5034
+ "optionWide": "o9fCSq_optionWide",
5035
+ "sectionTitle": "o9fCSq_sectionTitle",
5036
+ "fieldHead": "o9fCSq_fieldHead",
5037
+ "optionGrid": "o9fCSq_optionGrid",
5038
+ "input": "o9fCSq_input",
5039
+ "list": "o9fCSq_list",
5040
+ "optionGroupTitle": "o9fCSq_optionGroupTitle",
5041
+ "fieldState": "o9fCSq_fieldState",
4854
5042
  "settingText": "o9fCSq_settingText",
4855
- "segmented": "o9fCSq_segmented",
5043
+ "note": "o9fCSq_note",
5044
+ "settingRow": "o9fCSq_settingRow",
5045
+ "error": "o9fCSq_error",
5046
+ "rowLine": "o9fCSq_rowLine",
5047
+ "formField": "o9fCSq_formField",
5048
+ "link": "o9fCSq_link",
4856
5049
  "danger": "o9fCSq_danger",
4857
- "sectionActions": "o9fCSq_sectionActions",
4858
- "settingControl": "o9fCSq_settingControl",
4859
5050
  "select": "o9fCSq_select",
4860
- "selectValue": "o9fCSq_selectValue",
4861
- "rowLine": "o9fCSq_rowLine",
4862
- "prefixed": "o9fCSq_prefixed",
4863
- "disclosureOpen": "o9fCSq_disclosureOpen",
4864
- "prefix": "o9fCSq_prefix",
5051
+ "status": "o9fCSq_status",
5052
+ "editor": "o9fCSq_editor",
5053
+ "panel": "o9fCSq_panel",
5054
+ "selectAnchor": "o9fCSq_selectAnchor",
5055
+ "segmented": "o9fCSq_segmented",
5056
+ "disclosure": "o9fCSq_disclosure",
5057
+ "hint": "o9fCSq_hint",
5058
+ "option": "o9fCSq_option",
5059
+ "selectChevron": "o9fCSq_selectChevron",
5060
+ "settingTitle": "o9fCSq_settingTitle",
4865
5061
  "empty": "o9fCSq_empty",
4866
- "sectionHead": "o9fCSq_sectionHead",
5062
+ "settingLead": "o9fCSq_settingLead",
5063
+ "prefixed": "o9fCSq_prefixed",
4867
5064
  "disclosureIcon": "o9fCSq_disclosureIcon",
4868
- "settingRow": "o9fCSq_settingRow",
4869
- "fieldLabel": "o9fCSq_fieldLabel",
4870
- "field": "o9fCSq_field",
4871
- "section": "o9fCSq_section",
4872
- "fieldHead": "o9fCSq_fieldHead",
4873
- "optionGroup": "o9fCSq_optionGroup",
4874
- "rowActions": "o9fCSq_rowActions",
4875
- "note": "o9fCSq_note",
4876
- "formWide": "o9fCSq_formWide",
4877
- "prefixedDisabled": "o9fCSq_prefixedDisabled",
4878
- "rowTitle": "o9fCSq_rowTitle",
4879
- "selectCompact": "o9fCSq_selectCompact"
5065
+ "warning": "o9fCSq_warning"
4880
5066
  };
4881
5067
  //#endregion
4882
5068
  //#region src/client/settings/CredentialEditor.tsx
@@ -5317,6 +5503,18 @@ window.__ModuleLoader__.load({
5317
5503
  });
5318
5504
  }
5319
5505
  //#endregion
5506
+ //#region src/client/settings/EnhancedToolsSection.tsx
5507
+ const EnhancedToolsSection = createEnhancedToolsSection({
5508
+ classes: Settings_module_css_default,
5509
+ parts: settingsParts,
5510
+ sparse: sparse_exports,
5511
+ keys: {
5512
+ title: "enhanced.title",
5513
+ description: "enhanced.description",
5514
+ consent: "enhanced.consent"
5515
+ }
5516
+ });
5517
+ //#endregion
5320
5518
  //#region src/client/settings/PairingsSection.tsx
5321
5519
  const PairingsSection = createPairingsSection({
5322
5520
  classes: Settings_module_css_default,
@@ -5350,6 +5548,7 @@ window.__ModuleLoader__.load({
5350
5548
  optionGroups: { "session-model": SESSION_MODEL_GROUPS },
5351
5549
  keys: {
5352
5550
  "services.title": "services.title",
5551
+ "services.optInNotice": "services.optInNotice",
5353
5552
  "options.hint": "options.hint",
5354
5553
  "options.keylessMore": "options.keylessMore",
5355
5554
  "options.invalid": "options.invalid",
@@ -5385,12 +5584,12 @@ window.__ModuleLoader__.load({
5385
5584
  * Controls write immediately as sparse operations against the revision the
5386
5585
  * page shows; credentials go to DSH Credentials only.
5387
5586
  */
5388
- function SettingsPage({ useSettings, useCredentials, useRoute, usePairings, mutate, saveCredential, forgetPairing, t }) {
5587
+ function SettingsPage({ useSettings, useCredentials, useRoute, usePairings, readSettings, mutate, saveCredential, forgetPairing, t }) {
5389
5588
  const snapshot = useSettings((value) => value);
5390
5589
  const credentials = useCredentials((value) => value);
5391
5590
  const route = useRoute((value) => value);
5392
5591
  const pairings = usePairings((value) => value);
5393
- const { view, write, migrationError } = useSettingsDocument(snapshot, resolveSettings, mutate);
5592
+ const { view, write, migrationError } = useSettingsDocument(snapshot, readSettings, resolveSettings, mutate);
5394
5593
  if (snapshot.status === "unavailable") return null;
5395
5594
  if (snapshot.value === void 0 || snapshot.revision === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
5396
5595
  className: Settings_module_css_default.status,
@@ -5455,6 +5654,11 @@ window.__ModuleLoader__.load({
5455
5654
  credentials,
5456
5655
  t
5457
5656
  }),
5657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnhancedToolsSection, {
5658
+ doc,
5659
+ write,
5660
+ t
5661
+ }),
5458
5662
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ServicesSection, {
5459
5663
  doc,
5460
5664
  write,
@@ -5554,6 +5758,7 @@ window.__ModuleLoader__.load({
5554
5758
  route: route.source,
5555
5759
  pairings: pairings.source
5556
5760
  },
5761
+ readSettings: () => settings.getSnapshot(),
5557
5762
  async mutate(ops, revision) {
5558
5763
  return settings.mutate(ops, revision);
5559
5764
  },
@@ -5572,7 +5777,7 @@ window.__ModuleLoader__.load({
5572
5777
  locale: NS,
5573
5778
  inject: () => page
5574
5779
  }, SettingsPage))), "search: settings page");
5575
- const section = searchSection(selection, navigation, t);
5780
+ const section = searchSection(selection, navigation, remoteServicesOffSource(settings, CATALOG, resolveSettings), t);
5576
5781
  scope.inject(["webConnections"], (ui) => {
5577
5782
  ui.effect(() => ui.webConnections.registerRoute("search", route), "search: route facts");
5578
5783
  ui.effect(() => {
@@ -5594,7 +5799,11 @@ window.__ModuleLoader__.load({
5594
5799
  }, "search: composer section");
5595
5800
  });
5596
5801
  scope.inject(["commandUi"], (command) => {
5597
- command.effect(() => command.commandUi.register(searchCommand(selection, t)), "search: /setup-search");
5802
+ command.effect(() => command.commandUi.decorate(searchCommand(selection, t)), "search: /setup-search");
5803
+ followHostCommand(command, SEARCH.command, (sessionId) => {
5804
+ selection.invalidateSession(sessionId);
5805
+ pairings.invalidate();
5806
+ });
5598
5807
  });
5599
5808
  const invalidate = () => {
5600
5809
  selection.invalidate();