dsh-web-search-enhanced 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ row `web-search-enhanced` and selects its provider in the shared `web` row.
9
9
  | Row id | `web-search-enhanced` |
10
10
  | Provider id in `ctx.web` | `search-enhanced` |
11
11
  | Model surface | DSH's own `web_search` (tool-web), unchanged; with the row's Enhanced tools switch on, also `web_search_enhanced` and `list_web_search_providers` |
12
- | Per conversation | one connection or Off, from the composer menu or `/setup-search` |
12
+ | Per conversation | one connection or Off, from the composer menu or the Host command `/setup-search` (Web GUI and dsh-TUI) |
13
13
  | Settings | row settings page on the Plugins page (`plugins.row.config`) |
14
14
 
15
15
  ## What it does
@@ -19,17 +19,31 @@ Choose a search connection per conversation from the composer menu or
19
19
  the next model request step. Operators decide which connections exist: DSH's
20
20
  own DeepSeek search (the default) and DeepSeek Search API, the conversation's
21
21
  own model (`builtin:session-model`), Exa, Firecrawl, Tavily, Tinyfish,
22
- OpenAlex and Semantic Scholar, plus custom adapter connections and fixed-model
22
+ AnySearch, OpenAlex and Semantic Scholar, plus custom adapter connections and fixed-model
23
23
  connections over Anthropic Messages, OpenAI Responses or OpenAI Chat
24
24
  Completions. Native connections keep the behaviour of DSH's own providers;
25
25
  only their error messages are restated.
26
26
 
27
27
  - **Remote services start off.** DeepSeek Search API, Exa, Firecrawl, Tavily,
28
- Tinyfish, OpenAlex and Semantic Scholar must be turned on in the row
28
+ Tinyfish, AnySearch, OpenAlex and Semantic Scholar must be turned on in the row
29
29
  settings before a conversation can choose them; turning one on agrees that
30
30
  queries go to it. DeepSeek Messages (the default) and the conversation model
31
31
  stay on; custom connections are on when created. When every remote service
32
32
  is off, the composer menu says more services can be turned on in settings.
33
+ - **AnySearch takes a personal API key only** (`ANYSEARCH_API_KEY`, the same
34
+ credential name as AnySearch's own plugin): the service has an anonymous
35
+ tier, but this plugin does not use it. Its connection options are a region
36
+ (`zone`: `cn` or `intl`) and a preferred language (`language`, such as
37
+ `zh-CN` or `en`). Do not install it together with `@anysearch/anysearch-dsh`:
38
+ that bundle's profile layer writes both fields of the `web` row as
39
+ `anysearch`, so routing leaves this plugin.
40
+ - **`/setup-search`.** A Host command, so it works in the Web GUI and in
41
+ dsh-TUI. `off`, a connection id or `freshness auto|fresh|realtime` change
42
+ the choice from the next model request; dsh-TUI completes these arguments.
43
+ Without arguments the Web GUI opens the chooser, and dsh-TUI prints the
44
+ current choice, routing and the enabled connections (0.16.1 shows only the
45
+ start of that, as a one-line notice). Its description and output are
46
+ English.
33
47
  - **Enhanced tools (optional).** With the row's Enhanced tools switch on (off
34
48
  by default; turning it on agrees that the query goes to the service the
35
49
  Agent picks), the Agent also gets `web_search_enhanced` (`queries` plus a
package/lib/client.cjs CHANGED
@@ -37,7 +37,8 @@ window.__ModuleLoader__.load({
37
37
  tools: {
38
38
  action: "web_search_enhanced",
39
39
  list: "list_web_search_providers"
40
- }
40
+ },
41
+ command: "setup-search"
41
42
  };
42
43
  /**
43
44
  * Key of a row's configuration page on the Plugins page (`plugins.row.config`).
@@ -1050,6 +1051,30 @@ window.__ModuleLoader__.load({
1050
1051
  }
1051
1052
  }
1052
1053
  },
1054
+ anysearch: {
1055
+ label: "AnySearch",
1056
+ endpoint: "https://api.anysearch.com/v1/search",
1057
+ access: {
1058
+ keyless: false,
1059
+ apiKey: { credentialRef: "ANYSEARCH_API_KEY" },
1060
+ account: false
1061
+ },
1062
+ kind: "adapter",
1063
+ remote: true,
1064
+ optIn: true,
1065
+ freshness: [],
1066
+ homepage: "https://www.anysearch.com",
1067
+ options: {
1068
+ zone: {
1069
+ kind: "enum",
1070
+ values: ["cn", "intl"]
1071
+ },
1072
+ language: {
1073
+ kind: "text",
1074
+ pattern: /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,2}$/u
1075
+ }
1076
+ }
1077
+ },
1053
1078
  openalex: {
1054
1079
  label: "OpenAlex",
1055
1080
  endpoint: "https://api.openalex.org/works",
@@ -1520,7 +1545,11 @@ window.__ModuleLoader__.load({
1520
1545
  if (id.startsWith("connection:") && id.length > 11) return { connectionId: id.slice(11) };
1521
1546
  return parseOptions?.(id);
1522
1547
  }
1523
- /** A keyboard entry to the same per-session store the composer menu uses. */
1548
+ /**
1549
+ * The browser's bare invocation of the capability's Host command: a popup over
1550
+ * the same per-session store the composer menu uses. The Host command keeps its
1551
+ * menu row and runs every line with arguments.
1552
+ */
1524
1553
  function selectionCommand(selections, spec) {
1525
1554
  const t = (key) => spec.t(spec.keys[key]);
1526
1555
  const read = async (sessionId) => {
@@ -1530,9 +1559,6 @@ window.__ModuleLoader__.load({
1530
1559
  };
1531
1560
  return {
1532
1561
  name: spec.name,
1533
- label: () => t("label"),
1534
- description: () => t("description"),
1535
- icon: spec.icon,
1536
1562
  available: () => true,
1537
1563
  ui: {
1538
1564
  kind: "popupSelect",
@@ -1580,6 +1606,19 @@ window.__ModuleLoader__.load({
1580
1606
  }
1581
1607
  };
1582
1608
  }
1609
+ /**
1610
+ * Call `ran` after this browser ran the Host command `name` in a Session. A line
1611
+ * with arguments (`/setup-search off`) writes on the Host without the selection
1612
+ * store, so that Session's views must read again. DSH reports each Host command
1613
+ * this browser executed with `command/executed`, without its arguments, so every
1614
+ * run counts; runs from another browser or the TUI are not reported here.
1615
+ * @returns the listener's disposer; it also ends with `ctx`'s fiber.
1616
+ */
1617
+ function followHostCommand(ctx, name, ran) {
1618
+ return ctx.on("command/executed", (sessionId, executed) => {
1619
+ if (executed === name) ran(sessionId);
1620
+ });
1621
+ }
1583
1622
  //#endregion
1584
1623
  //#region ../core/src/client/selection.ts
1585
1624
  var ConnectionSelectionStore = class {
@@ -1660,10 +1699,16 @@ window.__ModuleLoader__.load({
1660
1699
  * read again, the others read on their next load.
1661
1700
  */
1662
1701
  invalidate() {
1663
- for (const entry of this.entries.values()) {
1664
- this.outdate(entry);
1665
- if (entry.observers > 0) this.read(entry);
1666
- }
1702
+ for (const entry of this.entries.values()) this.reread(entry);
1703
+ }
1704
+ /**
1705
+ * The Session's selection changed without this store (its Host command ran):
1706
+ * its views read again, at once when observed, otherwise on their next load.
1707
+ * A read already in flight reads once more.
1708
+ */
1709
+ invalidateSession(sessionId) {
1710
+ const entry = this.entries.get(sessionId);
1711
+ if (entry !== void 0) this.reread(entry);
1667
1712
  }
1668
1713
  entry(sessionId) {
1669
1714
  const existing = this.entries.get(sessionId);
@@ -1696,6 +1741,10 @@ window.__ModuleLoader__.load({
1696
1741
  this.entries.set(sessionId, entry);
1697
1742
  return entry;
1698
1743
  }
1744
+ reread(entry) {
1745
+ this.outdate(entry);
1746
+ if (entry.observers > 0) this.read(entry);
1747
+ }
1699
1748
  outdate(entry) {
1700
1749
  entry.stale = true;
1701
1750
  entry.epoch += 1;
@@ -4277,7 +4326,7 @@ window.__ModuleLoader__.load({
4277
4326
  }
4278
4327
  //#endregion
4279
4328
  //#region src/client/command.ts
4280
- const SEARCH_COMMAND = "setup-search";
4329
+ const SEARCH_COMMAND = SEARCH.command;
4281
4330
  const FRESHNESS_PREFIX = "freshness:";
4282
4331
  const parseFreshness = (id) => {
4283
4332
  const freshness = FRESHNESS.find((mode) => id === FRESHNESS_PREFIX + mode);
@@ -4286,11 +4335,8 @@ window.__ModuleLoader__.load({
4286
4335
  function searchCommand(selections, t) {
4287
4336
  return selectionCommand(selections, {
4288
4337
  name: SEARCH_COMMAND,
4289
- icon: _deepseek_ai_dsh_client_ui_primitives.IconGlobeOutlineRegular,
4290
4338
  t,
4291
4339
  keys: {
4292
- label: "command.label",
4293
- description: "command.description",
4294
4340
  placeholder: "command.placeholder",
4295
4341
  empty: "command.empty",
4296
4342
  noResults: "command.noResults",
@@ -4520,6 +4566,9 @@ window.__ModuleLoader__.load({
4520
4566
  "option.tinyfish.purpose": "Purpose",
4521
4567
  "option.tinyfish.purpose.placeholder": "What the searches are for",
4522
4568
  "option.tinyfish.domain_type": "Content type",
4569
+ "option.anysearch.zone": "Region",
4570
+ "option.anysearch.language": "Preferred language",
4571
+ "option.anysearch.language.placeholder": "e.g. zh-CN or en",
4523
4572
  "option.openalex.sort": "Sort",
4524
4573
  "option.openalex.sort.placeholder": "e.g. cited_by_count:desc",
4525
4574
  "option.openalex.filter": "Filter",
@@ -4558,7 +4607,7 @@ window.__ModuleLoader__.load({
4558
4607
  "status.modelReady": "{access} · model search · sent to {name}",
4559
4608
  "status.sessionModelNeedsKey": "The conversation model's API key isn't set",
4560
4609
  "status.sessionModelNeedsSignIn": "Sign in to the DeepSeek account again",
4561
- "status.reason.no-model": "No model chosen for this conversation yet",
4610
+ "status.reason.no-model": "The model for this conversation's next request isn't known yet",
4562
4611
  "status.reason.unknown-route": "Can't read the settings of this model's provider",
4563
4612
  "status.reason.protocol": "Its route declares no supported api",
4564
4613
  "status.reason.endpoint": "Its route has no fixed endpoint",
@@ -4781,6 +4830,9 @@ window.__ModuleLoader__.load({
4781
4830
  "option.tinyfish.purpose": "用途说明",
4782
4831
  "option.tinyfish.purpose.placeholder": "简述搜索目的",
4783
4832
  "option.tinyfish.domain_type": "内容类型",
4833
+ "option.anysearch.zone": "地区",
4834
+ "option.anysearch.language": "偏好语言",
4835
+ "option.anysearch.language.placeholder": "如 zh-CN 或 en",
4784
4836
  "option.openalex.sort": "排序",
4785
4837
  "option.openalex.sort.placeholder": "如 cited_by_count:desc",
4786
4838
  "option.openalex.filter": "过滤条件",
@@ -4819,7 +4871,7 @@ window.__ModuleLoader__.load({
4819
4871
  "status.modelReady": "{access} · 模型搜索 · 查询发送给 {name}",
4820
4872
  "status.sessionModelNeedsKey": "会话模型的 API Key 未设置",
4821
4873
  "status.sessionModelNeedsSignIn": "请重新登录 DeepSeek 账号",
4822
- "status.reason.no-model": "本会话还没有选择模型",
4874
+ "status.reason.no-model": "还不知道本会话下一次请求用哪个模型",
4823
4875
  "status.reason.unknown-route": "无法读取会话模型服务商的设置",
4824
4876
  "status.reason.protocol": "该路由在模型设置里没有声明支持的 api",
4825
4877
  "status.reason.endpoint": "该路由在模型设置里没有固定的接口地址",
@@ -4903,11 +4955,11 @@ window.__ModuleLoader__.load({
4903
4955
  document.head.appendChild(style);
4904
4956
  }
4905
4957
  var Section_module_css_default = {
4906
- "caption": "La6KHG_caption",
4907
- "container": "La6KHG_container",
4908
4958
  "segmented": "La6KHG_segmented",
4959
+ "label": "La6KHG_label",
4960
+ "container": "La6KHG_container",
4909
4961
  "error": "La6KHG_error",
4910
- "label": "La6KHG_label"
4962
+ "caption": "La6KHG_caption"
4911
4963
  };
4912
4964
  //#endregion
4913
4965
  //#region src/client/section.ts
@@ -4974,73 +5026,73 @@ window.__ModuleLoader__.load({
4974
5026
  document.head.appendChild(style);
4975
5027
  }
4976
5028
  var Settings_module_css_default = {
4977
- "optionGroup": "o9fCSq_optionGroup",
4978
- "selectCompact": "o9fCSq_selectCompact",
4979
- "hint": "o9fCSq_hint",
4980
- "actions": "o9fCSq_actions",
4981
- "editorTitle": "o9fCSq_editorTitle",
4982
- "section": "o9fCSq_section",
4983
- "prefix": "o9fCSq_prefix",
4984
- "error": "o9fCSq_error",
4985
- "disclosure": "o9fCSq_disclosure",
4986
- "fieldHead": "o9fCSq_fieldHead",
5029
+ "formField": "o9fCSq_formField",
5030
+ "status": "o9fCSq_status",
4987
5031
  "page": "o9fCSq_page",
4988
- "rowActions": "o9fCSq_rowActions",
4989
- "segmented": "o9fCSq_segmented",
5032
+ "textarea": "o9fCSq_textarea",
4990
5033
  "settingLead": "o9fCSq_settingLead",
4991
- "optionGroupTitle": "o9fCSq_optionGroupTitle",
4992
- "rowTitle": "o9fCSq_rowTitle",
4993
- "formField": "o9fCSq_formField",
4994
- "intro": "o9fCSq_intro",
4995
- "settingText": "o9fCSq_settingText",
4996
- "prefixedDisabled": "o9fCSq_prefixedDisabled",
4997
- "settingControl": "o9fCSq_settingControl",
4998
- "trustCheck": "o9fCSq_trustCheck",
4999
- "prefixed": "o9fCSq_prefixed",
5034
+ "selectAnchor": "o9fCSq_selectAnchor",
5035
+ "success": "o9fCSq_success",
5000
5036
  "sectionHead": "o9fCSq_sectionHead",
5001
- "select": "o9fCSq_select",
5002
- "fieldError": "o9fCSq_fieldError",
5003
- "inline": "o9fCSq_inline",
5004
- "link": "o9fCSq_link",
5005
- "field": "o9fCSq_field",
5006
- "option": "o9fCSq_option",
5007
- "textarea": "o9fCSq_textarea",
5008
- "sectionActions": "o9fCSq_sectionActions",
5009
- "sectionTitle": "o9fCSq_sectionTitle",
5010
5037
  "rowMeta": "o9fCSq_rowMeta",
5011
- "empty": "o9fCSq_empty",
5012
- "fieldState": "o9fCSq_fieldState",
5013
- "row": "o9fCSq_row",
5014
5038
  "note": "o9fCSq_note",
5039
+ "selectValue": "o9fCSq_selectValue",
5040
+ "formGrid": "o9fCSq_formGrid",
5041
+ "prefixedDisabled": "o9fCSq_prefixedDisabled",
5042
+ "sectionActions": "o9fCSq_sectionActions",
5043
+ "panel": "o9fCSq_panel",
5044
+ "prefixed": "o9fCSq_prefixed",
5045
+ "rowLine": "o9fCSq_rowLine",
5046
+ "disclosureIcon": "o9fCSq_disclosureIcon",
5047
+ "rowActions": "o9fCSq_rowActions",
5048
+ "rowMain": "o9fCSq_rowMain",
5049
+ "option": "o9fCSq_option",
5050
+ "intro": "o9fCSq_intro",
5051
+ "optionName": "o9fCSq_optionName",
5015
5052
  "settingRow": "o9fCSq_settingRow",
5016
- "selectChevron": "o9fCSq_selectChevron",
5053
+ "settingText": "o9fCSq_settingText",
5054
+ "optionKey": "o9fCSq_optionKey",
5055
+ "link": "o9fCSq_link",
5017
5056
  "list": "o9fCSq_list",
5018
- "trust": "o9fCSq_trust",
5019
- "status": "o9fCSq_status",
5020
- "input": "o9fCSq_input",
5021
- "confirmRow": "o9fCSq_confirmRow",
5022
- "fieldLabel": "o9fCSq_fieldLabel",
5057
+ "field": "o9fCSq_field",
5058
+ "selectCompact": "o9fCSq_selectCompact",
5023
5059
  "danger": "o9fCSq_danger",
5024
- "formGrid": "o9fCSq_formGrid",
5025
- "selectAnchor": "o9fCSq_selectAnchor",
5060
+ "fieldLabel": "o9fCSq_fieldLabel",
5061
+ "empty": "o9fCSq_empty",
5062
+ "prefix": "o9fCSq_prefix",
5063
+ "editorTitle": "o9fCSq_editorTitle",
5064
+ "disclosureOpen": "o9fCSq_disclosureOpen",
5026
5065
  "formWide": "o9fCSq_formWide",
5027
- "success": "o9fCSq_success",
5066
+ "confirmText": "o9fCSq_confirmText",
5067
+ "section": "o9fCSq_section",
5068
+ "fieldHead": "o9fCSq_fieldHead",
5069
+ "trustCheck": "o9fCSq_trustCheck",
5070
+ "hint": "o9fCSq_hint",
5071
+ "disclosure": "o9fCSq_disclosure",
5072
+ "optionGroupTitle": "o9fCSq_optionGroupTitle",
5073
+ "fieldError": "o9fCSq_fieldError",
5074
+ "sectionTitle": "o9fCSq_sectionTitle",
5075
+ "row": "o9fCSq_row",
5028
5076
  "editor": "o9fCSq_editor",
5029
- "panel": "o9fCSq_panel",
5030
- "disclosureOpen": "o9fCSq_disclosureOpen",
5077
+ "rowTitle": "o9fCSq_rowTitle",
5078
+ "actions": "o9fCSq_actions",
5079
+ "warning": "o9fCSq_warning",
5080
+ "error": "o9fCSq_error",
5031
5081
  "reset": "o9fCSq_reset",
5032
- "prefixedInput": "o9fCSq_prefixedInput",
5033
- "optionKey": "o9fCSq_optionKey",
5034
- "optionName": "o9fCSq_optionName",
5035
- "selectValue": "o9fCSq_selectValue",
5036
- "rowMain": "o9fCSq_rowMain",
5037
- "confirmText": "o9fCSq_confirmText",
5082
+ "trust": "o9fCSq_trust",
5038
5083
  "optionWide": "o9fCSq_optionWide",
5039
- "warning": "o9fCSq_warning",
5040
- "settingTitle": "o9fCSq_settingTitle",
5041
- "rowLine": "o9fCSq_rowLine",
5042
5084
  "optionGrid": "o9fCSq_optionGrid",
5043
- "disclosureIcon": "o9fCSq_disclosureIcon"
5085
+ "select": "o9fCSq_select",
5086
+ "inline": "o9fCSq_inline",
5087
+ "segmented": "o9fCSq_segmented",
5088
+ "confirmRow": "o9fCSq_confirmRow",
5089
+ "settingTitle": "o9fCSq_settingTitle",
5090
+ "fieldState": "o9fCSq_fieldState",
5091
+ "input": "o9fCSq_input",
5092
+ "selectChevron": "o9fCSq_selectChevron",
5093
+ "prefixedInput": "o9fCSq_prefixedInput",
5094
+ "optionGroup": "o9fCSq_optionGroup",
5095
+ "settingControl": "o9fCSq_settingControl"
5044
5096
  };
5045
5097
  //#endregion
5046
5098
  //#region src/client/settings/CredentialEditor.tsx
@@ -5222,6 +5274,13 @@ window.__ModuleLoader__.load({
5222
5274
  },
5223
5275
  domain_type: { label: "option.tinyfish.domain_type" }
5224
5276
  },
5277
+ anysearch: {
5278
+ zone: { label: "option.anysearch.zone" },
5279
+ language: {
5280
+ label: "option.anysearch.language",
5281
+ placeholder: "option.anysearch.language.placeholder"
5282
+ }
5283
+ },
5225
5284
  openalex: {
5226
5285
  sort: {
5227
5286
  label: "option.openalex.sort",
@@ -5777,7 +5836,11 @@ window.__ModuleLoader__.load({
5777
5836
  }, "search: composer section");
5778
5837
  });
5779
5838
  scope.inject(["commandUi"], (command) => {
5780
- command.effect(() => command.commandUi.register(searchCommand(selection, t)), "search: /setup-search");
5839
+ command.effect(() => command.commandUi.decorate(searchCommand(selection, t)), "search: /setup-search");
5840
+ followHostCommand(command, SEARCH.command, (sessionId) => {
5841
+ selection.invalidateSession(sessionId);
5842
+ pairings.invalidate();
5843
+ });
5781
5844
  });
5782
5845
  const invalidate = () => {
5783
5846
  selection.invalidate();