@moguiyu/dsh-tavily 0.2.6 → 0.2.8

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.js CHANGED
@@ -78,7 +78,7 @@ window.__ModuleLoader__.load({
78
78
  )
79
79
  }
80
80
 
81
- function TavilyCard() {
81
+ function TavilySettingsCard() {
82
82
  const [enabled, setEnabled] = react.useState(null)
83
83
  const [expanded, setExpanded] = react.useState(false)
84
84
  const [server, setServer] = react.useState(null)
@@ -438,13 +438,45 @@ window.__ModuleLoader__.load({
438
438
  )
439
439
  }
440
440
 
441
+ // The card the host renders. 0.1.6-alpha.2 asks a bundle-config entry for
442
+ // `view: 'page'` only; the legacy keyed settings slot passes no view at all.
443
+ function TavilyCard(props) {
444
+ if (props && props.view === 'summary') return react.createElement(TavilySummary, null)
445
+ return react.createElement(TavilySettingsCard, null)
446
+ }
447
+
448
+ // One-liner for the summary view. Deliberately static and cheap: the page
449
+ // draws the title, the icon, and the crumb itself, and never asks this entry
450
+ // for anything but `page`.
451
+ function TavilySummary() {
452
+ return react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
453
+ 'Rotating Tavily API keys for the advanced search tools. The built-in web_search keeps its native provider and is never replaced.'
454
+ )
455
+ }
456
+
441
457
  function apply(ctx) {
458
+ // 0.1.6-alpha.2 moved plugin configuration off the Settings page and
459
+ // replaced the keyed `settings.plugin.item` slot with the Plugins page's
460
+ // `plugins.*` family (`dsh-client-ui-plugin-manager` declares it).
461
+ // `slots.inject` fires only for a slot the host actually declares, so
462
+ // injecting both IS the feature detection: exactly one mounts per harness
463
+ // line, and neither throws on the other. Keep both -- dropping the retired
464
+ // name silently removes the card from rc.7 through 0.1.6-alpha.1, and
465
+ // dropping the new one silently removes it from 0.1.6-alpha.2 onward.
466
+ ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({
467
+ name: 'plugins.bundle.config',
468
+ // Keyed by the bundle's package name. The Plugins page renders this on
469
+ // the @moguiyu/dsh-tavily bundle page, between its description and rows.
470
+ key: '@moguiyu/dsh-tavily',
471
+ id: '@moguiyu/dsh-tavily'
472
+ }, TavilyCard))
473
+
442
474
  ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
443
475
  name: 'settings.plugin.item',
444
- // rc.7 plugin management: this card is keyed by the settings namespace
445
- // the Host serves (`tavily-search`); the Plugins configuration tab
446
- // dispatches it when the namespace is present. The id/order/label props
447
- // keep rc.6 list-slot deployments rendering the card as well.
476
+ // rc.7 through 0.1.6-alpha.1: keyed by the settings namespace the Host
477
+ // serves (`tavily-search`); the Plugins configuration tab dispatches it
478
+ // when the namespace is present. The id/order/label props keep rc.6
479
+ // list-slot deployments rendering the card as well.
448
480
  key: 'tavily-search',
449
481
  id: 'tavily-search',
450
482
  order: 30,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moguiyu/dsh-tavily",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Opt-in advanced Tavily model tools for DeepSeek Harness with the Tavily Search settings card (rc.7/rc.8 and the 0.1.2-0.1.6 plugin management lines). The built-in web_search is never replaced.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -36,14 +36,16 @@
36
36
  "0.1.5-alpha.2": "compatible",
37
37
  "0.1.5-rc.1": "compatible",
38
38
  "0.1.5-rc.2": "compatible",
39
- "0.1.6-alpha.1": "compatible"
39
+ "0.1.6-alpha.1": "compatible",
40
+ "0.1.6-alpha.2": "compatible"
40
41
  }
41
42
  },
42
43
  "client": {
43
44
  "platform": "web",
44
45
  "inject": [
45
46
  "@deepseek-ai/dsh-client-ui-settings",
46
- "@deepseek-ai/dsh-client-ui-settings-plugins"
47
+ "@deepseek-ai/dsh-client-ui-settings-plugins",
48
+ "@deepseek-ai/dsh-client-ui-plugin-manager"
47
49
  ]
48
50
  }
49
51
  },
package/src/client.js CHANGED
@@ -83,7 +83,7 @@ export function factory(require) {
83
83
  )
84
84
  }
85
85
 
86
- function TavilyCard() {
86
+ function TavilySettingsCard() {
87
87
  const [enabled, setEnabled] = react.useState(null)
88
88
  const [expanded, setExpanded] = react.useState(false)
89
89
  const [server, setServer] = react.useState(null)
@@ -443,13 +443,45 @@ export function factory(require) {
443
443
  )
444
444
  }
445
445
 
446
+ // The card the host renders. 0.1.6-alpha.2 asks a bundle-config entry for
447
+ // `view: 'page'` only; the legacy keyed settings slot passes no view at all.
448
+ function TavilyCard(props) {
449
+ if (props && props.view === 'summary') return react.createElement(TavilySummary, null)
450
+ return react.createElement(TavilySettingsCard, null)
451
+ }
452
+
453
+ // One-liner for the summary view. Deliberately static and cheap: the page
454
+ // draws the title, the icon, and the crumb itself, and never asks this entry
455
+ // for anything but `page`.
456
+ function TavilySummary() {
457
+ return react.createElement('p', { style: { margin: 0, fontSize: 12, color: 'var(--dsw-alias-label-tertiary)' } },
458
+ 'Rotating Tavily API keys for the advanced search tools. The built-in web_search keeps its native provider and is never replaced.'
459
+ )
460
+ }
461
+
446
462
  function apply(ctx) {
463
+ // 0.1.6-alpha.2 moved plugin configuration off the Settings page and
464
+ // replaced the keyed `settings.plugin.item` slot with the Plugins page's
465
+ // `plugins.*` family (`dsh-client-ui-plugin-manager` declares it).
466
+ // `slots.inject` fires only for a slot the host actually declares, so
467
+ // injecting both IS the feature detection: exactly one mounts per harness
468
+ // line, and neither throws on the other. Keep both -- dropping the retired
469
+ // name silently removes the card from rc.7 through 0.1.6-alpha.1, and
470
+ // dropping the new one silently removes it from 0.1.6-alpha.2 onward.
471
+ ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({
472
+ name: 'plugins.bundle.config',
473
+ // Keyed by the bundle's package name. The Plugins page renders this on
474
+ // the @moguiyu/dsh-tavily bundle page, between its description and rows.
475
+ key: '@moguiyu/dsh-tavily',
476
+ id: '@moguiyu/dsh-tavily'
477
+ }, TavilyCard))
478
+
447
479
  ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
448
480
  name: 'settings.plugin.item',
449
- // rc.7 plugin management: this card is keyed by the settings namespace
450
- // the Host serves (`tavily-search`); the Plugins configuration tab
451
- // dispatches it when the namespace is present. The id/order/label props
452
- // keep rc.6 list-slot deployments rendering the card as well.
481
+ // rc.7 through 0.1.6-alpha.1: keyed by the settings namespace the Host
482
+ // serves (`tavily-search`); the Plugins configuration tab dispatches it
483
+ // when the namespace is present. The id/order/label props keep rc.6
484
+ // list-slot deployments rendering the card as well.
453
485
  key: 'tavily-search',
454
486
  id: 'tavily-search',
455
487
  order: 30,
package/src/index.js CHANGED
@@ -11,9 +11,15 @@
11
11
  * `tavily-search` settings section (the key the Plugins configuration tab
12
12
  * pairs the card against). The switch value lives both in that namespace and
13
13
  * in `~/.dsh/tavily-tool.json`; every write path converges on
14
- * `settings.update` (or the state file when no settings service exists),
15
- * persisted first, then the row restarts itself so the tool registers or
16
- * unregisters cleanly.
14
+ * `settings.update` (or the state file when no settings service exists).
15
+ *
16
+ * The switch owns the tool half's lifecycle DIRECTLY: it holds the disposer
17
+ * returned by {@link installTavilyTool} and disposes / re-installs it in place.
18
+ * Earlier versions instead restarted the composing row and let `apply()` re-run;
19
+ * that is gone on purpose. A row restart on 0.1.6 re-mounts the fiber into a
20
+ * scope the agent does not see, so the tools ended up registered nowhere and
21
+ * stayed missing for every session until DSH restarted (AGENTS.md §9 gate 5).
22
+ * Do not reintroduce the restart.
17
23
  *
18
24
  * The settings-card switch controls ONLY the opt-in `tavily_search` tool. The
19
25
  * built-in `web_search` tool is never replaced: this package registers no web
@@ -26,7 +32,7 @@ import { installBackend, readToolState, restoreToolState, writeToolState } from
26
32
 
27
33
  export const name = 'dsh-tavily'
28
34
 
29
- export const inject = ['tools', 'webServer', 'credentials', 'systemPrompt', 'loader']
35
+ export const inject = ['tools', 'webServer', 'credentials', 'systemPrompt']
30
36
 
31
37
  export const Config = z.object({
32
38
  enabled: z.boolean().default(false),
@@ -44,31 +50,52 @@ export const Config = z.object({
44
50
  export const TAVILY_NS = 'tavily-search'
45
51
 
46
52
  export function apply(ctx, config) {
47
- // The switch value the current fiber is acting on. Every write path updates
48
- // this before the row restart so a watcher re-fired after a restart no-ops.
53
+ // The switch value this fiber is acting on. Every write path updates it
54
+ // before (un)registering, so a watcher re-fired afterwards no-ops.
49
55
  let lastApplied = isToolEnabled(config)
50
56
  // Settings seam state: the attached provider (null on hosts without one)
51
57
  // and the authoritative switch source — the resolved section while the
52
58
  // provider is attached, the composition entry otherwise.
53
59
  let settingsProvider = null
54
60
  let settingsSource = () => config
61
+ // The live registration of the tool half, or null while it is off.
62
+ let disposeTools = null
63
+
64
+ /**
65
+ * Add or remove the tool half to match `enabled`. This is the whole switch:
66
+ * `installTavilyTool` hands back a disposer covering its tools and prompt
67
+ * sections, so turning the capability off is a disposal rather than a row
68
+ * restart. See the module header for why the restart is gone.
69
+ */
70
+ function setTools(enabled) {
71
+ if (enabled) {
72
+ if (disposeTools === null) disposeTools = installTavilyTool(ctx)
73
+ return
74
+ }
75
+ if (disposeTools !== null) {
76
+ const dispose = disposeTools
77
+ disposeTools = null
78
+ dispose()
79
+ }
80
+ }
55
81
 
56
- /** Persist the switch first, then restart this row so the tool (un)registers. */
82
+ // Seed from the composition entry / persisted switch, and hand the fiber the
83
+ // teardown so unloading the row leaves nothing registered.
84
+ setTools(lastApplied)
85
+ ctx.effect(() => () => { setTools(false) })
86
+
87
+ /** Persist the switch, then (un)register the tool half in place. */
57
88
  async function applySwitchLocal(enabled) {
58
89
  if (enabled === lastApplied) return
59
- lastApplied = enabled
60
90
  const previous = readToolState()
91
+ lastApplied = enabled
61
92
  writeToolState(enabled)
62
93
  try {
63
- const loader = ctx.get('loader')
64
- const self = loader !== undefined && loader !== null ? loader.resolve('include:dsh-tavily') : undefined
65
- if (self === undefined || self === null || self.fiber === undefined) {
66
- throw new Error('dsh-tavily: row include:dsh-tavily is not loaded')
67
- }
68
- await self.fiber.update({ enabled }, true)
94
+ setTools(enabled)
69
95
  } catch (error) {
70
96
  lastApplied = previous !== null ? previous.enabled : (config.enabled !== false)
71
97
  restoreToolState(previous)
98
+ setTools(lastApplied)
72
99
  throw error
73
100
  }
74
101
  }
@@ -131,8 +158,8 @@ export function apply(ctx, config) {
131
158
  enabled: () => isToolEnabled(config),
132
159
  async applySwitch(enabled) {
133
160
  // Every write converges on one pipeline: the settings namespace when a
134
- // settings service is attached (its watcher persists and restarts this
135
- // row), or the plain state-file path otherwise.
161
+ // settings service is attached (its watcher persists and re-applies the
162
+ // switch in place), or the plain state-file path otherwise.
136
163
  if (settingsProvider !== null) {
137
164
  await settingsProvider.update(TAVILY_NS, { enabled })
138
165
  } else {
@@ -140,7 +167,4 @@ export function apply(ctx, config) {
140
167
  }
141
168
  },
142
169
  })
143
-
144
- if (!isToolEnabled(config)) return
145
- installTavilyTool(ctx)
146
170
  }