dsh-my-go 0.1.13 → 0.1.15

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.
@@ -199,12 +199,11 @@ function apply(ctx) {
199
199
  const [available, setAvailable] = React.useState({ providers: [], models: {} });
200
200
  React.useEffect(() => {
201
201
  if (!sp) return;
202
- try {
203
- setDraft(sp.read());
204
- } catch {
205
- setDraft({});
206
- }
207
202
  if (connection && connection.rpc && typeof connection.rpc.call === "function") {
203
+ connection.rpc.call("/dsh-my-go", "loadSettings", {}).then((res) => {
204
+ if (res && res.ok && res.value) setDraft(res.value);
205
+ else setDraft({});
206
+ }).catch(() => setDraft({}));
208
207
  connection.rpc.call("/dsh-my-go", "listModels", {}).then((res) => {
209
208
  if (res && res.ok && res.value && Array.isArray(res.value.providers)) setAvailable(res.value);
210
209
  }).catch(() => {
@@ -246,17 +245,6 @@ function apply(ctx) {
246
245
  setSaving(false);
247
246
  }
248
247
  };
249
- const autoSaveTimer = React.useRef(null);
250
- React.useEffect(() => {
251
- if (!draft || !sp) return;
252
- if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current);
253
- autoSaveTimer.current = setTimeout(() => {
254
- void save();
255
- }, 500);
256
- return () => {
257
- if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current);
258
- };
259
- }, [draft, sp]);
260
248
  const selectStyle = { background: "var(--surface, #1e1e1e)", color: "var(--text, #e0e0e0)", border: "1px solid var(--separator, #333)", borderRadius: 4, padding: "4px 8px", fontSize: 13, width: "100%", boxSizing: "border-box" };
261
249
  const labelStyle = { fontSize: 12, color: "var(--text-secondary, #888)", marginBottom: 2 };
262
250
  const cardStyle = { border: "1px solid var(--separator, #333)", borderRadius: 8, padding: 12, marginBottom: 12 };
@@ -352,7 +340,10 @@ function apply(ctx) {
352
340
  const current = snapshot.current;
353
341
  if (current && current.childId && current.status === "running" && lastJumpedTo !== current.childId && sessions) {
354
342
  lastJumpedTo = current.childId;
355
- sessions.openSubagent({ parentSessionId: "", childSessionId: current.childId, mode: "continuable" });
343
+ try {
344
+ sessions.open(current.childId);
345
+ } catch {
346
+ }
356
347
  } else if (!current && lastJumpedTo && sessions) {
357
348
  lastJumpedTo = null;
358
349
  }
package/dist/client.js CHANGED
@@ -204,12 +204,11 @@ function apply(ctx) {
204
204
  const [available, setAvailable] = React.useState({ providers: [], models: {} });
205
205
  React.useEffect(() => {
206
206
  if (!sp) return;
207
- try {
208
- setDraft(sp.read());
209
- } catch {
210
- setDraft({});
211
- }
212
207
  if (connection && connection.rpc && typeof connection.rpc.call === "function") {
208
+ connection.rpc.call("/dsh-my-go", "loadSettings", {}).then((res) => {
209
+ if (res && res.ok && res.value) setDraft(res.value);
210
+ else setDraft({});
211
+ }).catch(() => setDraft({}));
213
212
  connection.rpc.call("/dsh-my-go", "listModels", {}).then((res) => {
214
213
  if (res && res.ok && res.value && Array.isArray(res.value.providers)) setAvailable(res.value);
215
214
  }).catch(() => {
@@ -251,17 +250,6 @@ function apply(ctx) {
251
250
  setSaving(false);
252
251
  }
253
252
  };
254
- const autoSaveTimer = React.useRef(null);
255
- React.useEffect(() => {
256
- if (!draft || !sp) return;
257
- if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current);
258
- autoSaveTimer.current = setTimeout(() => {
259
- void save();
260
- }, 500);
261
- return () => {
262
- if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current);
263
- };
264
- }, [draft, sp]);
265
253
  const selectStyle = { background: "var(--surface, #1e1e1e)", color: "var(--text, #e0e0e0)", border: "1px solid var(--separator, #333)", borderRadius: 4, padding: "4px 8px", fontSize: 13, width: "100%", boxSizing: "border-box" };
266
254
  const labelStyle = { fontSize: 12, color: "var(--text-secondary, #888)", marginBottom: 2 };
267
255
  const cardStyle = { border: "1px solid var(--separator, #333)", borderRadius: 8, padding: 12, marginBottom: 12 };
@@ -357,7 +345,10 @@ function apply(ctx) {
357
345
  const current = snapshot.current;
358
346
  if (current && current.childId && current.status === "running" && lastJumpedTo !== current.childId && sessions) {
359
347
  lastJumpedTo = current.childId;
360
- sessions.openSubagent({ parentSessionId: "", childSessionId: current.childId, mode: "continuable" });
348
+ try {
349
+ sessions.open(current.childId);
350
+ } catch {
351
+ }
361
352
  } else if (!current && lastJumpedTo && sessions) {
362
353
  lastJumpedTo = null;
363
354
  }
package/lib/index.js CHANGED
@@ -359,14 +359,11 @@ export async function apply(ctx, config = {}) {
359
359
  if (!llm) return { ok: true, value: { providers: [], models: {} } }
360
360
  let providers = []
361
361
  try {
362
- const all = llm.listConfigurableProviders()
363
- providers = all.map((p) => p.provider)
364
- } catch {
365
- try {
366
- const active = await llm.listProviders()
367
- providers = active.map((p) => p.id)
368
- } catch { /* llm not available */ }
369
- }
362
+ // Use listProviders() — returns only ACTIVE/configured providers,
363
+ // NOT listConfigurableProviders() which includes unconfigured ones
364
+ const active = await llm.listProviders()
365
+ providers = active.map((p) => p.id)
366
+ } catch { /* llm not available */ }
370
367
  const models = {}
371
368
  for (const pid of providers) {
372
369
  try {
@@ -376,6 +373,16 @@ export async function apply(ctx, config = {}) {
376
373
  }
377
374
  return { ok: true, value: { providers, models } }
378
375
  }
376
+ if (endpoint === 'loadSettings') {
377
+ const settingsService = ctx.get('settings')
378
+ if (!settingsService) return { ok: true, value: {} }
379
+ try {
380
+ const stored = settingsService.get('dsh-my-go')
381
+ return { ok: true, value: stored && typeof stored === 'object' ? stored : {} }
382
+ } catch (e) {
383
+ return { ok: true, value: {} }
384
+ }
385
+ }
379
386
  if (endpoint === 'saveSettings') {
380
387
  const draft = payload
381
388
  if (!draft || typeof draft !== 'object') return { ok: false, error: { code: 'bad-request', message: 'payload must be an object' } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-my-go",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "DSH plugin: Sisyphus agent orchestration — star topology, single-line blocking, 7 specialist sub-agents (Hermes/Explore/Librarian/Looker/Hephaestus/Prometheus/Oracle) with per-type model binding, go_work/continue/need_help/forward communication tools, Web UI tree panel and settings.",
5
5
  "author": "daizihan233",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
 
17
17
  export const name = 'dsh-my-go-broker'
18
18
 
19
- export const inject = ['tools', 'subagents', 'systemPrompt']
19
+ export const inject = ['tools', 'subagents', 'systemPrompt', 'llm']
20
20
 
21
21
  import { access, cp, mkdir } from 'node:fs/promises'
22
22
  import { dirname, join } from 'node:path'
@@ -367,13 +367,20 @@ export async function apply(ctx, config = {}) {
367
367
  }
368
368
  const placeholder = orchestration.beginSpawning(agentType, prompt)
369
369
  try {
370
+ // Build agentOptions with model validation
371
+ const agentOpts = {}
372
+ if (binding.provider !== undefined) agentOpts.provider = binding.provider
373
+ if (binding.model !== undefined) {
374
+ const resolvedProvider = String(binding.provider ?? '')
375
+ if (!resolvedProvider || await modelExists(resolvedProvider, binding.model)) {
376
+ agentOpts.model = binding.model
377
+ }
378
+ }
370
379
  const request = {
371
380
  label: agentLabel(agentType, prompt.slice(0, 60)),
372
381
  prompt: [{ type: 'text', text: prompt }],
373
382
  parent,
374
- ...(binding.provider !== undefined || binding.model !== undefined
375
- ? { agentOptions: { ...(binding.provider !== undefined ? { provider: binding.provider } : {}), ...(binding.model !== undefined ? { model: binding.model } : {}) } }
376
- : {}),
383
+ ...(Object.keys(agentOpts).length > 0 ? { agentOptions: agentOpts } : {}),
377
384
  signal,
378
385
  }
379
386
  const { childId } = await ctx.subagents.startContinuable({
@@ -689,6 +696,25 @@ export async function apply(ctx, config = {}) {
689
696
  return result
690
697
  }
691
698
 
699
+ // ── model validation ─────────────────────────────────────────────────
700
+ const modelCache = new Map()
701
+ async function modelExists(provider, model) {
702
+ const key = String(provider)
703
+ let set = modelCache.get(key)
704
+ if (set === undefined) {
705
+ set = new Set()
706
+ try {
707
+ const llm = ctx.get('llm')
708
+ if (llm) {
709
+ const list = await llm.listModels(key)
710
+ for (const m of list) set.add(m.id)
711
+ }
712
+ } catch { /* provider may not support listing */ }
713
+ modelCache.set(key, set)
714
+ }
715
+ return set.has(String(model))
716
+ }
717
+
692
718
  ctx.on('agent/request', async (payload, next) => {
693
719
  const seed = await next()
694
720
  const agent = payload?.agent
@@ -698,7 +724,13 @@ export async function apply(ctx, config = {}) {
698
724
  const binding = bindings[type ?? 'sisyphus'] ?? {}
699
725
  const nextConfig = { ...seed }
700
726
  if (binding.provider !== undefined) nextConfig.provider = binding.provider
701
- if (binding.model !== undefined) nextConfig.model = binding.model
727
+ if (binding.model !== undefined) {
728
+ // Validate model exists on the resolved provider before applying
729
+ const resolvedProvider = String(nextConfig.provider ?? seed.provider ?? '')
730
+ if (!resolvedProvider || await modelExists(resolvedProvider, binding.model)) {
731
+ nextConfig.model = binding.model
732
+ }
733
+ }
702
734
  const desiredEffort = binding.reasoningEffort
703
735
  if (desiredEffort !== undefined && desiredEffort !== null) {
704
736
  const provider = String(nextConfig.provider ?? binding.provider ?? '')
package/src/client.js CHANGED
@@ -187,8 +187,12 @@ export function apply(ctx) {
187
187
 
188
188
  React.useEffect(() => {
189
189
  if (!sp) return
190
- try { setDraft(sp.read()) } catch { setDraft({}) }
190
+ // Load settings via host RPC (DSH SettingsScope doesn't support nested reads)
191
191
  if (connection && connection.rpc && typeof connection.rpc.call === 'function') {
192
+ connection.rpc.call('/dsh-my-go', 'loadSettings', {}).then((res) => {
193
+ if (res && res.ok && res.value) setDraft(res.value)
194
+ else setDraft({})
195
+ }).catch(() => setDraft({}))
192
196
  connection.rpc.call('/dsh-my-go', 'listModels', {}).then((res) => {
193
197
  if (res && res.ok && res.value && Array.isArray(res.value.providers)) setAvailable(res.value)
194
198
  }).catch(() => {})
@@ -212,6 +216,7 @@ export function apply(ctx) {
212
216
  })
213
217
  }
214
218
 
219
+ // Manual save only — auto-save risks infinite loops with settings/updated events
215
220
  const save = async () => {
216
221
  setSaving(true); setMsg(null)
217
222
  try {
@@ -229,15 +234,6 @@ export function apply(ctx) {
229
234
  } finally { setSaving(false) }
230
235
  }
231
236
 
232
- // Auto-save on draft change with debounce
233
- const autoSaveTimer = React.useRef(null)
234
- React.useEffect(() => {
235
- if (!draft || !sp) return
236
- if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current)
237
- autoSaveTimer.current = setTimeout(() => { void save() }, 500)
238
- return () => { if (autoSaveTimer.current) clearTimeout(autoSaveTimer.current) }
239
- }, [draft, sp])
240
-
241
237
  const selectStyle = { background: 'var(--surface, #1e1e1e)', color: 'var(--text, #e0e0e0)', border: '1px solid var(--separator, #333)', borderRadius: 4, padding: '4px 8px', fontSize: 13, width: '100%', boxSizing: 'border-box' }
242
238
  const labelStyle = { fontSize: 12, color: 'var(--text-secondary, #888)', marginBottom: 2 }
243
239
  const cardStyle = { border: '1px solid var(--separator, #333)', borderRadius: 8, padding: 12, marginBottom: 12 }
@@ -320,9 +316,8 @@ export function apply(ctx) {
320
316
  const current = snapshot.current
321
317
  if (current && current.childId && current.status === 'running' && lastJumpedTo !== current.childId && sessions) {
322
318
  lastJumpedTo = current.childId
323
- sessions.openSubagent({ parentSessionId: '', childSessionId: current.childId, mode: 'continuable' })
319
+ try { sessions.open(current.childId) } catch { /* ignore */ }
324
320
  } else if (!current && lastJumpedTo && sessions) {
325
- // Child settled; jump back to the parent.
326
321
  lastJumpedTo = null
327
322
  }
328
323
  }, 800)