@dsh-plus/subagent-model 0.1.4 → 0.1.6

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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import z from "@deepseek-ai/schemastery";
2
1
  import "@deepseek-ai/dsh-settings";
2
+ import z from "@deepseek-ai/schemastery";
3
3
  import { Context, Service } from "@deepseek-ai/cordis";
4
4
 
5
5
  //#region src/config.d.ts
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import z from "@deepseek-ai/schemastery";
2
1
  import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
2
+ import z from "@deepseek-ai/schemastery";
3
3
  import { Context, Service } from "@deepseek-ai/cordis";
4
4
 
5
5
  //#region src/ns.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/subagent-model",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "dsh-plus service+ui plugin: 为 subagent/subagent_fork 等子代理按提供商配置模型与思考程度(或继承主代理)",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -31,10 +31,11 @@
31
31
  "platform": "web"
32
32
  }
33
33
  },
34
- "dependencies": {
35
- "@deepseek-ai/cordis": "4.0.1",
36
- "@deepseek-ai/dsh-settings": "0.1.0-rc.8",
37
- "@deepseek-ai/schemastery": "3.18.1"
34
+ "dependencies": {},
35
+ "peerDependencies": {
36
+ "@deepseek-ai/cordis": "^4.0.1",
37
+ "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
38
+ "@deepseek-ai/schemastery": "^3.18.1"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@deepseek-ai/cordis": "4.0.1",
@@ -43,7 +44,9 @@
43
44
  "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.8",
44
45
  "@deepseek-ai/dsh-llm": "0.1.0-rc.8",
45
46
  "@deepseek-ai/dsh-session": "0.1.0-rc.8",
47
+ "@deepseek-ai/dsh-settings": "0.1.0-rc.8",
46
48
  "@deepseek-ai/dsh-subagent": "0.1.0-rc.8",
49
+ "@deepseek-ai/schemastery": "3.18.1",
47
50
  "@types/react": "~18.3.1",
48
51
  "react": "^18.2.0"
49
52
  },
package/src/catalog.ts CHANGED
@@ -48,7 +48,7 @@ function toEffort(effort: LlmReasoningEffortInfo): CatalogEffort {
48
48
  return {
49
49
  id: String(effort.id),
50
50
  name: effort.name,
51
- ...effort.description === undefined ? {} : { description: effort.description },
51
+ ...(effort.description === undefined ? {} : { description: effort.description }),
52
52
  }
53
53
  }
54
54
 
@@ -59,49 +59,62 @@ function toEffort(effort: LlmReasoningEffortInfo): CatalogEffort {
59
59
  export async function buildModelCatalog(ctx: Context): Promise<ModelCatalog> {
60
60
  const llm = ctx.get('llm')
61
61
  if (llm === undefined) {
62
- return { providers: [], failures: [], subagentProviders: subagentProvidersOf(ctx) }
62
+ return {
63
+ providers: [],
64
+ failures: [],
65
+ subagentProviders: subagentProvidersOf(ctx),
66
+ }
63
67
  }
64
- const catalog = await Promise.all(llm.listProviders().map(async (provider) => {
65
- try {
66
- const models = await llm.listModels(provider.id)
67
- const entries = await Promise.all(models.map(async (model) => {
68
- const resolved = await llm.resolveModelInfo(provider.id, model.id)
69
- const reasoning = resolved.reasoning === undefined ? undefined : {
70
- efforts: resolved.reasoning.efforts.map(toEffort),
71
- ...resolved.reasoning.defaultEffort === undefined
72
- ? {}
73
- : { defaultEffort: String(resolved.reasoning.defaultEffort) },
68
+ const catalog = await Promise.all(
69
+ llm.listProviders().map(async (provider) => {
70
+ try {
71
+ const models = await llm.listModels(provider.id)
72
+ const entries = await Promise.all(
73
+ models.map(async (model) => {
74
+ const resolved = await llm.resolveModelInfo(provider.id, model.id)
75
+ const reasoning =
76
+ resolved.reasoning === undefined
77
+ ? undefined
78
+ : {
79
+ efforts: resolved.reasoning.efforts.map(toEffort),
80
+ ...(resolved.reasoning.defaultEffort === undefined
81
+ ? {}
82
+ : {
83
+ defaultEffort: String(resolved.reasoning.defaultEffort),
84
+ }),
85
+ }
86
+ return {
87
+ id: model.id,
88
+ name: model.name,
89
+ ...(model.description === undefined ? {} : { description: model.description }),
90
+ ...(reasoning === undefined ? {} : { reasoning }),
91
+ }
92
+ }),
93
+ )
94
+ return {
95
+ kind: 'group' as const,
96
+ group: { id: provider.id, name: provider.name, models: entries },
74
97
  }
98
+ } catch (error) {
75
99
  return {
76
- id: model.id,
77
- name: model.name,
78
- ...model.description === undefined ? {} : { description: model.description },
79
- ...reasoning === undefined ? {} : { reasoning },
100
+ kind: 'failure' as const,
101
+ failure: {
102
+ id: provider.id,
103
+ name: provider.name,
104
+ message: error instanceof Error ? error.message : String(error),
105
+ },
80
106
  }
81
- }))
82
- return {
83
- kind: 'group' as const,
84
- group: { id: provider.id, name: provider.name, models: entries },
85
107
  }
86
- } catch (error) {
87
- return {
88
- kind: 'failure' as const,
89
- failure: {
90
- id: provider.id,
91
- name: provider.name,
92
- message: error instanceof Error ? error.message : String(error),
93
- },
94
- }
95
- }
96
- }))
108
+ }),
109
+ )
97
110
  return {
98
111
  providers: catalog
99
112
  .filter((item) => item.kind === 'group' && item.group.models.length > 0)
100
- .map((item) => item.kind === 'group' ? item.group : undefined)
113
+ .map((item) => (item.kind === 'group' ? item.group : undefined))
101
114
  .filter((group): group is CatalogProvider => group !== undefined),
102
115
  failures: catalog
103
116
  .filter((item) => item.kind === 'failure')
104
- .map((item) => item.kind === 'failure' ? item.failure : undefined)
117
+ .map((item) => (item.kind === 'failure' ? item.failure : undefined))
105
118
  .filter((failure): failure is CatalogFailure => failure !== undefined),
106
119
  subagentProviders: subagentProvidersOf(ctx),
107
120
  }
@@ -7,12 +7,10 @@
7
7
  * 落盘,自文档化);「模型目录」为唯一保留的自定义端点。
8
8
  * @module subagent-model/client/card
9
9
  */
10
- import { useEffect, useMemo, useSyncExternalStore, useState, type ReactElement } from 'react'
10
+ import { type ReactElement, useEffect, useMemo, useState, useSyncExternalStore } from 'react'
11
11
 
12
12
  import { SETTINGS_NS } from '../ns.ts'
13
- import {
14
- fetchCatalog, type CatalogProvider, type ModelCatalog,
15
- } from './api.ts'
13
+ import { type CatalogProvider, fetchCatalog, type ModelCatalog } from './api.ts'
16
14
  import { CheckRow, SelectField, type SelectOption } from './fields.tsx'
17
15
  import type { Scope, SettingsApi } from './scope.ts'
18
16
 
@@ -53,7 +51,12 @@ interface Status {
53
51
  }
54
52
 
55
53
  const IDLE_STATUS: Status = { kind: 'idle', text: '' }
56
- const EMPTY_ROW: DraftRow = { enabled: false, provider: '', model: '', reasoningEffort: 'inherit' }
54
+ const EMPTY_ROW: DraftRow = {
55
+ enabled: false,
56
+ provider: '',
57
+ model: '',
58
+ reasoningEffort: 'inherit',
59
+ }
57
60
  const EFFORT_INHERIT = 'inherit'
58
61
  const EFFORT_DEFAULT = 'default'
59
62
 
@@ -64,14 +67,15 @@ function draftFrom(value: ConfigValue, catalog: ModelCatalog | null): Draft {
64
67
  for (const name of Object.keys(value.entries).sort()) names.add(name)
65
68
  for (const name of names) {
66
69
  const entry = value.entries[name]
67
- rows[name] = entry === undefined
68
- ? { ...EMPTY_ROW }
69
- : {
70
- enabled: entry.enabled,
71
- provider: entry.provider,
72
- model: entry.model,
73
- reasoningEffort: entry.reasoningEffort,
74
- }
70
+ rows[name] =
71
+ entry === undefined
72
+ ? { ...EMPTY_ROW }
73
+ : {
74
+ enabled: entry.enabled,
75
+ provider: entry.provider,
76
+ model: entry.model,
77
+ reasoningEffort: entry.reasoningEffort,
78
+ }
75
79
  }
76
80
  return { enabled: value.enabled, rows }
77
81
  }
@@ -84,16 +88,25 @@ function unknownOption(value: string, label: string): SelectOption {
84
88
  return { value, label: `${value}(${label})` }
85
89
  }
86
90
 
87
- function providerOptions(catalog: ModelCatalog | null, row: DraftRow, t: (k: string) => string): SelectOption[] {
91
+ function providerOptions(
92
+ catalog: ModelCatalog | null,
93
+ row: DraftRow,
94
+ t: (k: string) => string,
95
+ ): SelectOption[] {
88
96
  const options: SelectOption[] = [{ value: '', label: t('inheritProvider') }]
89
- for (const provider of catalog?.providers ?? []) options.push({ value: provider.id, label: provider.name })
97
+ for (const provider of catalog?.providers ?? [])
98
+ options.push({ value: provider.id, label: provider.name })
90
99
  if (row.provider.length > 0 && !options.some((o) => o.value === row.provider)) {
91
100
  options.push(unknownOption(row.provider, t('unknownValue')))
92
101
  }
93
102
  return options
94
103
  }
95
104
 
96
- function modelOptions(catalog: ModelCatalog | null, row: DraftRow, t: (k: string) => string): SelectOption[] {
105
+ function modelOptions(
106
+ catalog: ModelCatalog | null,
107
+ row: DraftRow,
108
+ t: (k: string) => string,
109
+ ): SelectOption[] {
97
110
  const options: SelectOption[] = [{ value: '', label: t('inheritModel') }]
98
111
  const provider = catalog?.providers.find((p) => p.id === row.provider)
99
112
  for (const model of provider?.models ?? []) options.push({ value: model.id, label: model.name })
@@ -103,14 +116,19 @@ function modelOptions(catalog: ModelCatalog | null, row: DraftRow, t: (k: string
103
116
  return options
104
117
  }
105
118
 
106
- function effortOptions(catalog: ModelCatalog | null, row: DraftRow, t: (k: string) => string): SelectOption[] {
119
+ function effortOptions(
120
+ catalog: ModelCatalog | null,
121
+ row: DraftRow,
122
+ t: (k: string) => string,
123
+ ): SelectOption[] {
107
124
  const options: SelectOption[] = [
108
125
  { value: EFFORT_INHERIT, label: t('effortInherit') },
109
126
  { value: EFFORT_DEFAULT, label: t('effortDefault') },
110
127
  ]
111
128
  const provider = catalog?.providers.find((p) => p.id === row.provider)
112
129
  const model = provider?.models.find((m) => m.id === row.model)
113
- for (const effort of model?.reasoning?.efforts ?? []) options.push({ value: effort.id, label: effort.name })
130
+ for (const effort of model?.reasoning?.efforts ?? [])
131
+ options.push({ value: effort.id, label: effort.name })
114
132
  if (row.reasoningEffort.length > 0 && !options.some((o) => o.value === row.reasoningEffort)) {
115
133
  options.push(unknownOption(row.reasoningEffort, t('unknownValue')))
116
134
  }
@@ -133,20 +151,44 @@ function RowBlock(props: RowBlockProps): ReactElement {
133
151
  <div className="dsm-row">
134
152
  <div className="dsm-rowHead">
135
153
  <span className="dsm-rowName">{name}</span>
136
- <CheckRow id={`dsm-row-${name}`} label={t('rowEnabled')} checked={row.enabled}
137
- disabled={disabled} onEdit={(v) => onEdit({ ...row, enabled: v })} />
154
+ <CheckRow
155
+ id={`dsm-row-${name}`}
156
+ label={t('rowEnabled')}
157
+ checked={row.enabled}
158
+ disabled={disabled}
159
+ onEdit={(v) => onEdit({ ...row, enabled: v })}
160
+ />
138
161
  </div>
139
162
  <p className="dsm-rowHint">{t('rowHint')}</p>
140
- <SelectField id={`dsm-provider-${name}`} label={t('provider')} hint={t('providerHint')}
141
- value={row.provider} options={providerOptions(catalog, row, t)} disabled={disabled}
142
- onEdit={(v) => onEdit({ ...row, provider: v })} />
143
- <SelectField id={`dsm-model-${name}`} label={t('model')} hint={t('modelHint')}
144
- value={row.model} options={modelOptions(catalog, row, t)} disabled={disabled || row.provider.length === 0}
145
- invalid={invalidModel} invalidLabel={t('invalidModel')}
146
- onEdit={(v) => onEdit({ ...row, model: v })} />
147
- <SelectField id={`dsm-effort-${name}`} label={t('effort')} hint={t('effortHint')}
148
- value={row.reasoningEffort} options={effortOptions(catalog, row, t)} disabled={disabled}
149
- onEdit={(v) => onEdit({ ...row, reasoningEffort: v })} />
163
+ <SelectField
164
+ id={`dsm-provider-${name}`}
165
+ label={t('provider')}
166
+ hint={t('providerHint')}
167
+ value={row.provider}
168
+ options={providerOptions(catalog, row, t)}
169
+ disabled={disabled}
170
+ onEdit={(v) => onEdit({ ...row, provider: v })}
171
+ />
172
+ <SelectField
173
+ id={`dsm-model-${name}`}
174
+ label={t('model')}
175
+ hint={t('modelHint')}
176
+ value={row.model}
177
+ options={modelOptions(catalog, row, t)}
178
+ disabled={disabled || row.provider.length === 0}
179
+ invalid={invalidModel}
180
+ invalidLabel={t('invalidModel')}
181
+ onEdit={(v) => onEdit({ ...row, model: v })}
182
+ />
183
+ <SelectField
184
+ id={`dsm-effort-${name}`}
185
+ label={t('effort')}
186
+ hint={t('effortHint')}
187
+ value={row.reasoningEffort}
188
+ options={effortOptions(catalog, row, t)}
189
+ disabled={disabled}
190
+ onEdit={(v) => onEdit({ ...row, reasoningEffort: v })}
191
+ />
150
192
  </div>
151
193
  )
152
194
  }
@@ -202,18 +244,24 @@ export function SubagentModelCard(props: CardProps): ReactElement | null {
202
244
 
203
245
  const dirty = useMemo(
204
246
  () =>
205
- value !== undefined && draft !== null &&
247
+ value !== undefined &&
248
+ draft !== null &&
206
249
  JSON.stringify(toPatch(draft)) !== JSON.stringify(toPatch(draftFrom(value, catalog))),
207
250
  [value, draft, catalog],
208
251
  )
209
252
  const invalid = useMemo(
210
- () => draft !== null && Object.values(draft.rows).some((row) =>
211
- row.model.length > 0 && row.provider.length === 0),
253
+ () =>
254
+ draft !== null &&
255
+ Object.values(draft.rows).some((row) => row.model.length > 0 && row.provider.length === 0),
212
256
  [draft],
213
257
  )
214
258
 
215
259
  if (value === undefined || draft === null) {
216
- return <li className="dsm-card"><p className="dsm-empty">{t('loading')}</p></li>
260
+ return (
261
+ <li className="dsm-card">
262
+ <p className="dsm-empty">{t('loading')}</p>
263
+ </li>
264
+ )
217
265
  }
218
266
  const edit = <K extends keyof Draft>(key: K, editValue: Draft[K]): void => {
219
267
  setDraft({ ...draft, [key]: editValue })
@@ -226,11 +274,12 @@ export function SubagentModelCard(props: CardProps): ReactElement | null {
226
274
  const onSave = (): void => {
227
275
  setSaving(true)
228
276
  const revision = scope.getSnapshot().revision
229
- api.update({
230
- ns: SETTINGS_NS,
231
- patch: toPatch(draft),
232
- ...(revision !== undefined ? { expectedRevision: revision } : {}),
233
- })
277
+ api
278
+ .update({
279
+ ns: SETTINGS_NS,
280
+ patch: toPatch(draft),
281
+ ...(revision !== undefined ? { expectedRevision: revision } : {}),
282
+ })
234
283
  .then(async (response) => {
235
284
  if (!response.result.ok) {
236
285
  throw new Error(response.result.error?.message ?? t('saveFailed'))
@@ -241,7 +290,10 @@ export function SubagentModelCard(props: CardProps): ReactElement | null {
241
290
  setStatus(IDLE_STATUS)
242
291
  })
243
292
  .catch((error: unknown) => {
244
- setStatus({ kind: 'error', text: `${t('saveFailed')}${error instanceof Error ? error.message : ''}` })
293
+ setStatus({
294
+ kind: 'error',
295
+ text: `${t('saveFailed')}${error instanceof Error ? error.message : ''}`,
296
+ })
245
297
  })
246
298
  .finally(() => setSaving(false))
247
299
  }
@@ -271,9 +323,18 @@ export function SubagentModelCard(props: CardProps): ReactElement | null {
271
323
  </button>
272
324
  {open ? (
273
325
  <div className="dsm-body">
274
- {disabled ? <p className="dsm-empty" role="status">{t('readOnly')}</p> : null}
275
- <CheckRow id="dsm-enabled" label={t('enabled')} checked={draft.enabled} disabled={disabled}
276
- onEdit={(v) => edit('enabled', v)} />
326
+ {disabled ? (
327
+ <p className="dsm-empty" role="status">
328
+ {t('readOnly')}
329
+ </p>
330
+ ) : null}
331
+ <CheckRow
332
+ id="dsm-enabled"
333
+ label={t('enabled')}
334
+ checked={draft.enabled}
335
+ disabled={disabled}
336
+ onEdit={(v) => edit('enabled', v)}
337
+ />
277
338
  {catalogFailed ? (
278
339
  <div className="dsm-banner" role="status">
279
340
  <span>{t('catalogError')}</span>
@@ -283,31 +344,53 @@ export function SubagentModelCard(props: CardProps): ReactElement | null {
283
344
  </div>
284
345
  ) : null}
285
346
  {catalog === null && !catalogFailed ? (
286
- <p className="dsm-empty" role="status">{t('catalogLoading')}</p>
347
+ <p className="dsm-empty" role="status">
348
+ {t('catalogLoading')}
349
+ </p>
287
350
  ) : null}
288
351
  {rowNames.length === 0 ? (
289
- <p className="dsm-empty" role="status">{t('noRows')}</p>
352
+ <p className="dsm-empty" role="status">
353
+ {t('noRows')}
354
+ </p>
290
355
  ) : null}
291
356
  {rowNames.map((name) => (
292
- <RowBlock key={name} name={name} row={draft.rows[name] ?? { ...EMPTY_ROW }}
293
- catalog={catalog} disabled={disabled} t={t}
294
- onEdit={(row) => editRow(name, row)} />
357
+ <RowBlock
358
+ key={name}
359
+ name={name}
360
+ row={draft.rows[name] ?? { ...EMPTY_ROW }}
361
+ catalog={catalog}
362
+ disabled={disabled}
363
+ t={t}
364
+ onEdit={(row) => editRow(name, row)}
365
+ />
295
366
  ))}
296
- <p className="dsm-hint dsm-rowHint">
297
- {providerCount > 0 ? t('rowDesc') : ''}
298
- </p>
367
+ <p className="dsm-hint dsm-rowHint">{providerCount > 0 ? t('rowDesc') : ''}</p>
299
368
  <div className="dsm-footer">
300
369
  {status.kind !== 'idle' ? (
301
- <p className={`dsm-status${status.kind === 'error' ? ' dsm-statusError' : ''}`} role="status">
370
+ <p
371
+ className={`dsm-status${status.kind === 'error' ? ' dsm-statusError' : ''}`}
372
+ role="status"
373
+ >
302
374
  {status.text}
303
375
  </p>
304
376
  ) : null}
305
- <button type="button" className="dsm-btn dsm-btnGhost" disabled={!dirty || saving}
306
- onClick={() => { setDraft(draftFrom(value, catalog)); setStatus(IDLE_STATUS) }}>
377
+ <button
378
+ type="button"
379
+ className="dsm-btn dsm-btnGhost"
380
+ disabled={!dirty || saving}
381
+ onClick={() => {
382
+ setDraft(draftFrom(value, catalog))
383
+ setStatus(IDLE_STATUS)
384
+ }}
385
+ >
307
386
  {t('discard')}
308
387
  </button>
309
- <button type="button" className="dsm-btn dsm-btnPrimary" disabled={!dirty || invalid || saving || disabled}
310
- onClick={onSave}>
388
+ <button
389
+ type="button"
390
+ className="dsm-btn dsm-btnPrimary"
391
+ disabled={!dirty || invalid || saving || disabled}
392
+ onClick={onSave}
393
+ >
311
394
  {t(saving ? 'saving' : 'save')}
312
395
  </button>
313
396
  </div>
@@ -63,7 +63,12 @@ interface ClientContext {
63
63
  * (loopback 或 tailnet 信任域名)下行为一致。
64
64
  */
65
65
  function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
66
- let state: ScopeSnapshot = { status: 'loading', value: undefined, revision: undefined, writable: false }
66
+ let state: ScopeSnapshot = {
67
+ status: 'loading',
68
+ value: undefined,
69
+ revision: undefined,
70
+ writable: false,
71
+ }
67
72
  const listeners = new Set<() => void>()
68
73
  let generation = 0
69
74
  const publish = (next: ScopeSnapshot): void => {
@@ -72,7 +77,7 @@ function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
72
77
  }
73
78
  const load = async (): Promise<void> => {
74
79
  const gen = ++generation
75
- let response
80
+ let response: Awaited<ReturnType<SettingsApi['describe']>>
76
81
  try {
77
82
  response = await api.describe({})
78
83
  } catch {
@@ -82,10 +87,20 @@ function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
82
87
  const writable = response.result.value?.writable ?? false
83
88
  const view = response.result.value?.namespaces.find((candidate) => candidate.ns === ns)
84
89
  if (view === undefined) {
85
- publish({ status: 'unavailable', value: undefined, revision: undefined, writable })
90
+ publish({
91
+ status: 'unavailable',
92
+ value: undefined,
93
+ revision: undefined,
94
+ writable,
95
+ })
86
96
  return
87
97
  }
88
- publish({ status: 'ready', value: view.value, revision: view.revision, writable })
98
+ publish({
99
+ status: 'ready',
100
+ value: view.value,
101
+ revision: view.revision,
102
+ writable,
103
+ })
89
104
  }
90
105
  const refresh = (namespace?: unknown): void => {
91
106
  if (namespace !== undefined && namespace !== ns) return
@@ -117,7 +132,6 @@ function createApiScope(api: SettingsApi, ns: string, c: ClientContext): Scope {
117
132
  }
118
133
 
119
134
  export function apply(ctx: Context): void {
120
-
121
135
  const c = ctx as unknown as ClientContext
122
136
  const tag = injectStyle()
123
137
  c.effect(
@@ -126,10 +140,7 @@ export function apply(ctx: Context): void {
126
140
  },
127
141
  'subagent-model: style',
128
142
  )
129
- c.effect(
130
- () => c.locale.register(NS, { zh, en }),
131
- 'subagent-model: locale',
132
- )
143
+ c.effect(() => c.locale.register(NS, { zh, en }), 'subagent-model: locale')
133
144
  const api = c.get('connection').api.settings
134
145
  const scope = createApiScope(api, SETTINGS_NS, c)
135
146
  c.slots.inject('settings.plugin.item', () =>
@@ -20,7 +20,8 @@ export const zh: Record<string, string> = {
20
20
  providerHint: '留空 = 子代理沿用主代理的提供商与模型。',
21
21
  modelHint: '留空 = 沿用主代理的模型(需与所选提供商匹配)。',
22
22
  effortHint: 'inherit = 不干预(fork 会随会话继承);default = 显式使用模型默认档位。',
23
- rowHint: '按子代理 provider 名匹配;standard preset 中 spawn ↔ subagent 工具、fork ↔ subagent_fork 工具。',
23
+ rowHint:
24
+ '按子代理 provider 名匹配;standard preset 中 spawn ↔ subagent 工具、fork ↔ subagent_fork 工具。',
24
25
  rowDesc: '未注册的 provider 行不生效,可保留待用。',
25
26
  noRows: '当前未发现子代理 provider;配置将在 provider 注册后生效。',
26
27
  catalogError: '提供商目录加载失败:',
@@ -42,7 +43,8 @@ export const zh: Record<string, string> = {
42
43
 
43
44
  export const en: Record<string, string> = {
44
45
  title: 'Subagent model config',
45
- description: 'Pick a provider, model, and thinking effort for subagent / subagent_fork children, or inherit from the main agent.',
46
+ description:
47
+ 'Pick a provider, model, and thinking effort for subagent / subagent_fork children, or inherit from the main agent.',
46
48
  enabled: 'Enable plugin',
47
49
  rowEnabled: 'Enable row',
48
50
  provider: 'Provider',
@@ -54,8 +56,10 @@ export const en: Record<string, string> = {
54
56
  effortDefault: 'Follow model default',
55
57
  providerHint: 'Empty = the child follows the main agent\u2019s provider and model.',
56
58
  modelHint: 'Empty = follow the main agent\u2019s model (must match the chosen provider).',
57
- effortHint: 'inherit = do not touch (forks inherit via session seed); default = explicitly use the model\u2019s default level.',
58
- rowHint: 'Matched by subagent provider name; in the standard preset spawn \u2194 subagent tool, fork \u2194 subagent_fork tool.',
59
+ effortHint:
60
+ 'inherit = do not touch (forks inherit via session seed); default = explicitly use the model\u2019s default level.',
61
+ rowHint:
62
+ 'Matched by subagent provider name; in the standard preset spawn \u2194 subagent tool, fork \u2194 subagent_fork tool.',
59
63
  rowDesc: 'Rows for unregistered providers are inert; keep them for later.',
60
64
  noRows: 'No subagent provider registered yet; config takes effect once one appears.',
61
65
  catalogError: 'Failed to load provider catalog: ',
@@ -31,7 +31,26 @@ export interface Scope {
31
31
 
32
32
  /** connection api.settings 的 RPC 面(本插件用到的三个方法)。 */
33
33
  export interface SettingsApi {
34
- describe(payload?: Record<string, never>): Promise<{ result: { ok: boolean; value?: { namespaces: Array<{ ns: string; secrets: Array<{ path: string[]; set: boolean }> }> }; error?: { message?: string } } }>
35
- update(request: { ns: string; patch: Record<string, unknown>; expectedRevision?: number }): Promise<{ result: { ok: boolean; error?: { message?: string } } }>
36
- replace(request: { ns: string; section: Record<string, unknown>; expectedRevision?: number }): Promise<{ result: { ok: boolean; error?: { message?: string } } }>
34
+ describe(payload?: Record<string, never>): Promise<{
35
+ result: {
36
+ ok: boolean
37
+ value?: {
38
+ namespaces: Array<{
39
+ ns: string
40
+ secrets: Array<{ path: string[]; set: boolean }>
41
+ }>
42
+ }
43
+ error?: { message?: string }
44
+ }
45
+ }>
46
+ update(request: {
47
+ ns: string
48
+ patch: Record<string, unknown>
49
+ expectedRevision?: number
50
+ }): Promise<{ result: { ok: boolean; error?: { message?: string } } }>
51
+ replace(request: {
52
+ ns: string
53
+ section: Record<string, unknown>
54
+ expectedRevision?: number
55
+ }): Promise<{ result: { ok: boolean; error?: { message?: string } } }>
37
56
  }
package/src/config.ts CHANGED
@@ -14,8 +14,9 @@
14
14
  * 显式声明输入/输出接口并注解 schema(输出为规范型,输入为可空宽松型)。
15
15
  * @module subagent-model/config
16
16
  */
17
- import z from '@deepseek-ai/schemastery'
17
+
18
18
  import { settingsNamespace } from '@deepseek-ai/dsh-settings'
19
+ import z from '@deepseek-ai/schemastery'
19
20
 
20
21
  import { SETTINGS_NS as NS_LITERAL } from './ns.ts'
21
22
 
@@ -44,20 +45,26 @@ export interface SubagentModelConfig {
44
45
  /** 配置的宽松输入形态(schema 校验前的用户数据)。 */
45
46
  export interface SubagentModelConfigInput {
46
47
  enabled?: boolean | null
47
- entries?: Record<string, {
48
- enabled?: boolean | null
49
- provider?: string | null
50
- model?: string | null
51
- reasoningEffort?: string | null
52
- }> | null
48
+ entries?: Record<
49
+ string,
50
+ {
51
+ enabled?: boolean | null
52
+ provider?: string | null
53
+ model?: string | null
54
+ reasoningEffort?: string | null
55
+ }
56
+ > | null
53
57
  }
54
58
 
55
59
  const EntrySchema = z.object({
56
60
  enabled: z.boolean().description('该子代理 provider 行的开关').default(false),
57
61
  provider: z.string().description('LLM 提供商 id;留空 = 继承主代理').default(''),
58
62
  model: z.string().description('模型 id;留空 = 继承主代理').default(''),
59
- reasoningEffort: z.string()
60
- .description(`思考程度:${EFFORT_INHERIT}(继承主代理)/ ${EFFORT_DEFAULT}(跟随模型默认)/ 提供商目录档位 id`)
63
+ reasoningEffort: z
64
+ .string()
65
+ .description(
66
+ `思考程度:${EFFORT_INHERIT}(继承主代理)/ ${EFFORT_DEFAULT}(跟随模型默认)/ 提供商目录档位 id`,
67
+ )
61
68
  .default(EFFORT_INHERIT),
62
69
  })
63
70
 
package/src/delegation.ts CHANGED
@@ -19,11 +19,17 @@
19
19
  * @module subagent-model/delegation
20
20
  */
21
21
  import type { Context } from '@deepseek-ai/cordis'
22
- import type { Agent, AgentOptions } from '@deepseek-ai/dsh-agent'
22
+ import type { AgentOptions } from '@deepseek-ai/dsh-agent'
23
23
  import type { LlmCallConfig } from '@deepseek-ai/dsh-llm'
24
24
  import type { ContinuableStartSpec, SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
25
25
 
26
- import { applyEffort, mergeAgentOptions, resolveEntry, type RequestConfig, type SubagentModelConfig } from './config.ts'
26
+ import {
27
+ applyEffort,
28
+ mergeAgentOptions,
29
+ type RequestConfig,
30
+ resolveEntry,
31
+ type SubagentModelConfig,
32
+ } from './config.ts'
27
33
 
28
34
  const WRAPPED = Symbol('dsh-plus-subagent-model.wrapped')
29
35
 
package/src/index.ts CHANGED
@@ -15,10 +15,13 @@ export const name = 'dsh-plus-subagent-model'
15
15
 
16
16
  export const inject = ['subagents', 'llm'] as const
17
17
 
18
- export { Config }
19
-
20
- export type { EntryConfig, InjectedOptions, SubagentModelConfig } from './config.ts'
18
+ export type {
19
+ EntryConfig,
20
+ InjectedOptions,
21
+ SubagentModelConfig,
22
+ } from './config.ts'
21
23
  export { SubagentModelService } from './service.ts'
24
+ export { Config }
22
25
 
23
26
  export function apply(ctx: Context, config: SubagentModelConfig): void {
24
27
  ctx.plugin(SubagentModelService, config)
package/src/service.ts CHANGED
@@ -11,9 +11,8 @@
11
11
  */
12
12
  import { Context, Service } from '@deepseek-ai/cordis'
13
13
  import { installSettingsSection } from '@deepseek-ai/dsh-settings'
14
-
14
+ import { Config, SETTINGS_NS, type SubagentModelConfig, validateEntries } from './config.ts'
15
15
  import { registerCatalogApi } from './config-api.ts'
16
- import { Config, SETTINGS_NS, validateEntries, type SubagentModelConfig } from './config.ts'
17
16
  import { installChildRoute, installDelegationHook } from './delegation.ts'
18
17
 
19
18
  declare module '@deepseek-ai/cordis' {