@klarkxy/dsh-model-hub 0.1.7 → 0.2.0

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/diagnostics.ts","../src/probe.ts","../src/owners.ts","../src/changes.ts","../src/cast.ts","../src/running.ts","../src/index.ts"],"sourcesContent":["/** Invalidation is constant-time. Only startup and explicit reads request a\n * diagnostic; a burst shares one deferred run. Work never reschedules itself. */\nexport function createProbeDiagnostics(run: () => Promise<void>, onError: (error: unknown) => void) {\n let dirty = true\n let stopped = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let running: Promise<void> | undefined\n return {\n invalidate() { if (!stopped) dirty = true },\n request() {\n if (stopped || !dirty || timer !== undefined || running) return\n timer = setTimeout(() => {\n timer = undefined\n if (stopped || !dirty) return\n dirty = false\n running = Promise.resolve().then(run).catch(onError).finally(() => { running = undefined })\n }, 0)\n },\n async dispose() {\n stopped = true\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n await running\n },\n }\n}\n","/**\n * Pure probe-report builder for the model hub. Takes the host's\n * `settings.describe()` output and reports every detected model-route field,\n * plus whether this plugin's own self-test markers survived projection.\n */\nimport { detectModelFields, modelRouteOverride, normalizeModelRoute, type ModelRoute } from '@klarkxy/dsh-model-route'\nimport type { ModelSettingsText } from '@klarkxy/dsh-plugin-kit/model-settings'\n\n/** Structural subset of the host SettingsDescriptor the probe needs. */\nexport interface ProbeDescriptorLike {\n ns: string\n schema: unknown\n value: unknown\n revision?: number\n}\n\nexport interface ProbeField {\n path: string[]\n via: 'marker' | 'shape' | 'contribution'\n label?: ModelSettingsText\n description?: ModelSettingsText\n supportsReasoning?: false\n rootKeys?: string[]\n writeKeys?: string[]\n preserveSiblings?: true\n allowFollow?: boolean\n followLabel?: string | { zh: string; en: string }\n marker?: Record<string, unknown>\n current?: ModelRoute\n editable?: false\n arrayItem?: true\n}\n\nexport interface ProbeEntry {\n ns: string\n fields: ProbeField[]\n}\n\nexport type SelfTestChannel = 'marker' | 'shape' | 'absent' | 'contribution'\n\nexport interface ProbeReport {\n generatedAt: string\n totalNamespaces: number\n namespacesWithModelFields: number\n entries: ProbeEntry[]\n selfTest: {\n /** How the custom-key marked field was detected (or not). */\n extraKey: SelfTestChannel\n /** How the renderer-role marked field was detected (or not). */\n role: SelfTestChannel\n /** Raw projection of the hub's own namespace, for key inspection. */\n ownSchema: unknown\n }\n}\n\nexport const SELF_TEST_EXTRA_KEY_PATH = 'selfTestMeta'\nexport const SELF_TEST_ROLE_PATH = 'selfTestRole'\n\nfunction channelOf(fields: ProbeField[], firstSegment: string): SelfTestChannel {\n const hit = fields.find(field => field.path[0] === firstSegment)\n return hit ? hit.via : 'absent'\n}\n\nexport function buildProbeReport(descriptors: readonly ProbeDescriptorLike[], now = new Date()): ProbeReport {\n const entries: ProbeEntry[] = listModelFields(descriptors).map(({ ns, fields }) => ({ ns, fields }))\n let ownSchema: unknown\n for (const descriptor of descriptors) {\n if (descriptor.ns.includes('model-hub')) ownSchema = descriptor.schema\n }\n const own = entries.find(entry => entry.ns.includes('model-hub'))\n return {\n generatedAt: now.toISOString(),\n totalNamespaces: descriptors.length,\n namespacesWithModelFields: entries.length,\n entries,\n selfTest: {\n extraKey: own ? channelOf(own.fields, SELF_TEST_EXTRA_KEY_PATH) : 'absent',\n role: own ? channelOf(own.fields, SELF_TEST_ROLE_PATH) : 'absent',\n ownSchema,\n },\n }\n}\n\n/** One namespace's detected fields plus the CAS revision a write must carry. */\nexport interface HubEntry {\n ns: string\n title?: ModelSettingsText\n source?: 'owner'\n ownerId?: string\n catalog?: unknown\n catalogError?: ModelSettingsText\n error?: ModelSettingsText\n revision: number\n fields: ProbeField[]\n}\n\n/** The field matrix the hub page edits: every detected model-route field. */\nexport function listModelFields(descriptors: readonly ProbeDescriptorLike[]): HubEntry[] {\n const entries: HubEntry[] = []\n for (const descriptor of descriptors) {\n const fields = detectModelFields(descriptor.schema, { value: descriptor.value })\n if (!fields.length) continue\n entries.push({\n ns: descriptor.ns,\n revision: typeof descriptor.revision === 'number' ? descriptor.revision : 0,\n fields: fields.map(field => ({\n path: [...field.path],\n via: field.via,\n ...(field.label ? { label: field.label } : {}),\n ...(field.description ? { description: field.description } : {}),\n ...(field.supportsReasoning === false ? { supportsReasoning: false as const } : {}),\n ...(field.rootKeys ? { rootKeys: [...field.rootKeys] } : {}),\n ...(field.writeKeys ? { writeKeys: [...field.writeKeys] } : {}),\n ...(field.preserveSiblings ? { preserveSiblings: true as const } : {}),\n ...(field.marker ? { marker: field.marker as Record<string, unknown> } : {}),\n ...(field.current ? { current: field.current } : {}),\n ...(field.editable === false ? { editable: false as const } : {}),\n ...(field.arrayItem ? { arrayItem: true as const } : {}),\n })),\n })\n }\n return entries\n}\n\n/** One validated write against a detected field: path ops plus the CAS revision. */\nexport interface FieldWritePlan {\n ns: string\n ops: ReadonlyArray<{ op: 'set'; path: readonly string[]; value: unknown } | { op: 'unset'; path: readonly string[] }>\n expectedRevision: number\n}\n\n/**\n * Validate a hub-page write against a fresh detection pass: the target must be\n * a currently detected model-route field, so the page can never write a\n * non-model key. An empty route unsets the field back to its schema default\n * (\"follow the host default\"); a concrete route is set normalized.\n */\nexport function planFieldWrite(entries: readonly HubEntry[], payload: unknown): FieldWritePlan {\n const row = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : undefined\n const ns = typeof row?.ns === 'string' ? row.ns : ''\n const path = Array.isArray(row?.path) && row.path.every(s => typeof s === 'string' && s) ? row.path as string[] : undefined\n if (!ns || !path) throw new Error('Invalid field reference')\n const entry = entries.find(item => item.ns === ns)\n const field = entry?.fields.find(item => item.path.length === path.length && item.path.every((segment, index) => segment === path[index]))\n if (!entry || !field) throw new Error('Not a detected model field')\n if (field.editable === false) throw new Error('No collection item to edit')\n const override = modelRouteOverride(normalizeModelRoute(row?.route))\n if (row?.expectedRevision !== undefined && (!Number.isSafeInteger(row.expectedRevision) || (row.expectedRevision as number) < 0)) throw new Error('Invalid revision')\n if (typeof row?.expectedRevision !== 'number') throw new Error('A valid expected revision is required; refresh and retry')\n const expectedRevision = row.expectedRevision\n if (!path.length || field.preserveSiblings) return { ns, expectedRevision, ops: (field.rootKeys ?? field.writeKeys ?? ['provider', 'model']).map(key => {\n const route = override ?? (field.arrayItem ? { provider: '', model: '' } : undefined)\n const value = route?.[key as keyof ModelRoute]\n return value === undefined ? { op: 'unset' as const, path: [...path, key] } : { op: 'set' as const, path: [...path, key], value }\n }) }\n // Native unset on an array index splices the element. Follow mode preserves it.\n return { ns, expectedRevision, ops: override || field.arrayItem\n ? [{ op: 'set', path, value: override ?? { provider: '', model: '' } }]\n : [{ op: 'unset', path }] }\n}\n","import { collectModelSettings, type ModelSettingsDiscovery, type ModelSettingsProvider } from '@klarkxy/dsh-plugin-kit/model-settings'\nimport { normalizeModelRoute } from '@klarkxy/dsh-model-route'\nimport { listModelFields, planFieldWrite, type HubEntry, type ProbeDescriptorLike } from './probe.ts'\n\ninterface NativeSettings {\n describe(options: { redactSecrets: true }): ProbeDescriptorLike[]\n mutate(ns: string, ops: ReturnType<typeof planFieldWrite>['ops'], revision: number): Promise<void>\n}\n/** A commit acknowledgement contains no fallible post-save aggregate read. */\nexport interface FieldCommit { committed: true; ns: string }\n\n/** Owner contributions supersede the same native namespace, never its storage. */\nexport function createHubSettings(events: ModelSettingsDiscovery, settings: NativeSettings, ownNamespace = '@klarkxy/dsh-model-hub') {\n const readOwner = async (owner: ModelSettingsProvider, signal: AbortSignal): Promise<HubEntry> => {\n try {\n const snapshot = await owner.read(signal)\n if (!Number.isSafeInteger(snapshot.revision) || snapshot.revision < 0) throw new Error('Invalid owner revision')\n return {\n ns: owner.ns, title: owner.title, source: 'owner', ownerId: owner.instanceId, revision: snapshot.revision,\n fields: snapshot.fields.map(field => ({ ...field, path: [...field.path], via: 'contribution' as const })),\n ...(snapshot.catalog !== undefined ? { catalog: snapshot.catalog } : {}),\n ...(snapshot.catalogError ? { catalogError: snapshot.catalogError } : {}),\n }\n } catch (error) {\n signal.throwIfAborted()\n return { ns: owner.ns, title: owner.title, source: 'owner', ownerId: owner.instanceId, revision: 0, fields: [],\n error: error instanceof Error ? error.message : String(error) }\n }\n }\n const list = async (signal: AbortSignal): Promise<HubEntry[]> => {\n signal.throwIfAborted()\n const owners = await collectModelSettings(events)\n const namespaces = new Set(owners.map(owner => owner.ns))\n const native = listModelFields(settings.describe({ redactSecrets: true })).filter(entry => !namespaces.has(entry.ns))\n .map(entry => ({ ...entry, fields: entry.fields.filter(field => !(\n entry.ns === ownNamespace && ['selfTestMeta', 'selfTestRole'].includes(field.path[0] ?? '')\n )) })).filter(entry => entry.fields.length > 0)\n const entries = await Promise.all(owners.map(owner => readOwner(owner, signal)))\n signal.throwIfAborted()\n return [...native, ...entries].sort((a, b) => a.ns.localeCompare(b.ns))\n }\n const set = async (payload: unknown, signal: AbortSignal): Promise<FieldCommit> => {\n signal.throwIfAborted()\n const row = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : undefined\n if (!row || typeof row.ns !== 'string' || !Array.isArray(row.path) || !row.path.every(segment => typeof segment === 'string' && segment)) throw new Error('Invalid field reference')\n if (!Number.isSafeInteger(row.expectedRevision) || (row.expectedRevision as number) < 0) throw new Error('A valid expected revision is required; refresh and retry')\n const input = row.route && typeof row.route === 'object' && !Array.isArray(row.route) ? row.route as Record<string, unknown> : undefined\n if (!input || ['provider', 'model', 'reasoningEffort'].some(key => input[key] !== undefined && typeof input[key] !== 'string')) throw new Error('Invalid model route')\n const normalized = normalizeModelRoute(input)\n if ((Boolean(String(input.provider ?? '').trim()) || Boolean(String(input.model ?? '').trim())) && (!normalized.provider || !normalized.model)) throw new Error('Invalid model route')\n // Fresh collection rejects unloaded owners; callback guards reject references\n // that unload after collection. Never fall back from an owner to native writes.\n const owners = await collectModelSettings(events)\n const owner = owners.find(item => item.ns === row.ns)\n if (owner) {\n if (row.ownerId !== owner.instanceId) throw new Error('Model settings owner changed; refresh and retry')\n const entry = await readOwner(owner, signal)\n if (entry.error) throw new Error(typeof entry.error === 'string' ? entry.error : entry.error.en)\n if (entry.revision !== row.expectedRevision) throw new Error('Settings changed; refresh and retry')\n const field = entry.fields.find(field => JSON.stringify(field.path) === JSON.stringify(row.path))\n if (!field || field.editable === false) throw new Error('Not an editable model field')\n const route = normalized\n if (field.allowFollow === false && (!route.provider || !route.model)) throw new Error('This model setting requires a concrete model')\n await owner.write(field.path, route, row.expectedRevision as number, signal)\n } else {\n if (row.ownerId !== undefined) throw new Error('Model settings owner unloaded; refresh and retry')\n if (row.ns === ownNamespace) throw new Error('Self-test fields are diagnostic only')\n const plan = planFieldWrite(listModelFields(settings.describe({ redactSecrets: true })), payload)\n await settings.mutate(plan.ns, plan.ops, plan.expectedRevision)\n }\n return { committed: true, ns: row.ns }\n }\n return { list, set }\n}\n","import type { Context } from '@deepseek-ai/cordis'\nimport { modelSettingsChangesIdentity } from '@klarkxy/dsh-plugin-kit/contracts'\nimport { createModelSettingsChanges } from '@klarkxy/dsh-plugin-kit/model-settings-changes'\n\n/** Model Hub forwards every settings namespace; feature plugins filter their own. */\nexport function registerHubModelSettingsChanges(ctx: Context): void {\n createModelSettingsChanges(ctx, modelSettingsChangesIdentity('@klarkxy/dsh-model-hub'))\n}\n","/**\n * dsh-model-hub — subagent cast (host half).\n *\n * Edits the host-native `subagent-model-selection-settings` namespace\n * (`enabled` + `allowedModels` routes) through the official settings CAS\n * path, and keeps one user-annotated \"task tendency\" per route in\n * `<profile>/data/model-hub/cast.json`. Tendencies are advisory hints for\n * the system-prompt injection in index.ts; the main agent still decides\n * which model a delegated subagent receives.\n */\nimport { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport type { ProbeDescriptorLike } from './probe.ts'\n\n/** Native namespace owning the subagent model-selection preference. */\nexport const CAST_NAMESPACE = 'subagent-model-selection-settings'\n/** Separate revision domain; native settings revisions do not cover this file. */\nexport const CAST_TENDENCIES_NAMESPACE = '@klarkxy/dsh-model-hub/cast-tendencies'\n\n/** Tendency ids are limited like host route ids; notes stay short prompt lines. */\nexport const CAST_LIMITS = { id: 250, tendency: 500 } as const\n\n/** One valid route of the cast. `index` is the write handle (see createCast). */\nexport interface CastRoute {\n /** Position inside the raw native allowedModels array. Malformed entries are\n * skipped when reading, so a route's position in `CastState.routes` can\n * diverge from its raw index; every write addresses routes by this index. */\n index: number\n provider: string\n model: string\n reasoningEffort?: string\n tendency?: string\n}\n\n/** Cast snapshot served to the browser half. */\nexport interface CastState {\n enabled: boolean\n revision: number\n tendencyRevision?: number\n tendencyError?: string\n routes: CastRoute[]\n}\n/** Native saves acknowledge only the commit; getCast owns later reads. */\nexport interface CastCommit { committed: true }\n\ntype SettingsOp = { op: 'set'; path: string[]; value: unknown } | { op: 'unset'; path: string[] }\n\nexport interface CastDeps {\n describe(): readonly ProbeDescriptorLike[]\n mutate(ns: string, ops: readonly SettingsOp[], expectedRevision: number): Promise<void>\n profileDir?: string\n logger?: { warn(message: string, ...args: unknown[]): void }\n notifyTendenciesChanged?(revision: number): void\n}\n\n/** One valid allowedModels entry plus its raw array index. */\ntype IndexedRoute = Omit<CastRoute, 'tendency'>\n\ninterface RawCast {\n enabled: boolean\n revision: number\n routes: IndexedRoute[]\n /** Raw allowedModels length, malformed entries included. */\n length: number\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Tendency store key for one provider/model pair. */\nexport function tendencyKey(provider: string, model: string): string {\n return JSON.stringify([provider, model])\n}\n\n/** Keep well-formed entries with their raw indexes; skip malformed ones. */\nfunction parseAllowedModels(value: unknown): { routes: IndexedRoute[]; length: number } {\n const list = Array.isArray(value) ? value : []\n const routes: IndexedRoute[] = []\n list.forEach((entry, index) => {\n if (!isRecord(entry)) return\n const { provider, model, reasoningEffort } = entry\n if (typeof provider !== 'string' || !provider || typeof model !== 'string' || !model) return\n routes.push({\n index, provider, model,\n ...(typeof reasoningEffort === 'string' && reasoningEffort ? { reasoningEffort } : {}),\n })\n })\n return { routes, length: list.length }\n}\n\nfunction normalizeRouteInput(input: unknown): { provider: string; model: string; reasoningEffort?: string } {\n const row = isRecord(input) ? input : {}\n const provider = typeof row.provider === 'string' ? row.provider.trim() : ''\n const model = typeof row.model === 'string' ? row.model.trim() : ''\n if (!provider || !model) throw new Error('A route requires non-empty provider and model ids')\n if (provider.length > CAST_LIMITS.id || model.length > CAST_LIMITS.id) {\n throw new Error(`Route provider and model ids are limited to ${CAST_LIMITS.id} characters`)\n }\n if (row.reasoningEffort !== undefined) {\n const effort = typeof row.reasoningEffort === 'string' ? row.reasoningEffort.trim() : ''\n if (!effort) throw new Error('A route reasoning effort must be a non-empty string')\n if (effort.length > CAST_LIMITS.id) throw new Error(`Route reasoning effort is limited to ${CAST_LIMITS.id} characters`)\n return { provider, model, reasoningEffort: effort }\n }\n return { provider, model }\n}\n\nfunction validateRevision(value: unknown): number {\n if (!Number.isSafeInteger(value) || (value as number) < 0) {\n throw new Error('A valid expected revision is required; refresh and retry')\n }\n return value as number\n}\n\nfunction validateIndex(value: unknown): number {\n if (!Number.isSafeInteger(value) || (value as number) < 0) throw new Error('Invalid route index')\n return value as number\n}\n\n/**\n * Render the injected guidance. Pure: callers resolve enabled state and the\n * tendency-merged routes. Returns '' when the injection should not appear —\n * the host prompt renderer drops empty sections.\n */\nexport function buildCastPrompt(enabled: boolean, routes: readonly CastRoute[]): string {\n if (!enabled || !routes.length) return ''\n const annotated = routes.filter(route => typeof route.tendency === 'string' && route.tendency.trim())\n // Global settings can differ from a Session's saved route policy. Discovery\n // and the actual tool schema, not this section, determine what it can select.\n const guidance = [\n 'Subagent model selection:',\n 'When delegating an independent task, actively choose a suitable model instead of silently inheriting your own. If list_subagent_models and a delegation tool with provider/model fields are available, inspect the available routes before the first model-selectable delegation in this task, reuse those results for later delegations, and supply provider and model together explicitly.',\n 'Keep provider and model as separate arguments. A discovery label is displayed as provider/model; do not copy the whole label into model or prepend provider to the model id. Example only: if provider is \"example-provider\" and its list shows \"example-provider/team/model-a\", pass {\"provider\":\"example-provider\",\"model\":\"team/model-a\"}. Remove only the known display prefix \"example-provider/\" from that combined label; preserve all slashes and namespaces inside the model id. If discovery already gives separate fields, copy both exactly without stripping or adding any prefix. The example is not an available route.',\n 'For independent reviews, alternative approaches, or disputed conclusions, prefer a model different from your own when a suitable candidate is available. Choose for task fit and the user\\'s constraints; using the same model is appropriate when it fits better or no suitable alternative is available. Do not create extra subtasks solely for model diversity.',\n 'The current Session\\'s discovery results and tool schema are authoritative. Its allowed routes are saved when the Session is created and inherited by its subagents; later list edits apply to new top-level Sessions. This limits explicit selection, not pure route inheritance. Configured model notes may refer to routes unavailable in this Session. If discovery or model-selection fields are unavailable, use the supported delegation behavior; do not invent routes or pass unsupported fields to tools such as subagent_fork. When changing models, omit reasoning_effort unless you have verified a supported value.',\n ]\n if (annotated.length) guidance.push(\n 'User-provided task tendencies (advisory): prefer a matching tendency among available models. Models without a tendency remain general-purpose candidates; no model-specific strengths are assumed.',\n ...annotated.map(route =>\n `- ${JSON.stringify({ provider: route.provider, model: route.model })}${route.reasoningEffort ? ` (reasoning effort: ${route.reasoningEffort})` : ''}: ${route.tendency}`),\n )\n return guidance.join('\\n')\n}\n\nexport function createCast(deps: CastDeps) {\n const castFile = (): string => {\n if (!deps.profileDir) throw new Error('No profile directory; cast tendencies cannot be stored')\n return join(deps.profileDir, 'data', 'model-hub', 'cast.json')\n }\n\n // Always read the durable revision: another tab or plugin incarnation may\n // have committed since the last snapshot. Failed reads never become a cache.\n const readTendencies = (): { revision: number; tendencies: Record<string, string> } => {\n if (!deps.profileDir) return { revision: 0, tendencies: {} }\n let raw: string\n try {\n raw = readFileSync(castFile(), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { revision: 0, tendencies: {} }\n throw new Error('Cast tendency store cannot be read; restore access to cast.json and refresh', { cause: error })\n }\n let parsed: unknown\n try { parsed = JSON.parse(raw) } catch (error) {\n throw new Error('Cast tendency store is damaged; repair cast.json and refresh', { cause: error })\n }\n if (isRecord(parsed) && parsed.schemaVersion !== 1 && parsed.schemaVersion !== 2) {\n throw new Error('Cast tendency store version is unsupported; use a compatible plugin before saving')\n }\n try {\n if (!isRecord(parsed) || !isRecord(parsed.tendencies)) throw new Error('Invalid tendency store schema')\n if (parsed.schemaVersion === 2 && (!Number.isSafeInteger(parsed.revision) || (parsed.revision as number) < 0)) throw new Error('Invalid tendency store revision')\n for (const [key, value] of Object.entries(parsed.tendencies)) {\n const ids: unknown = JSON.parse(key)\n if (!Array.isArray(ids) || ids.length !== 2 || !ids.every(id => typeof id === 'string' && id.trim()) || typeof value !== 'string') {\n throw new Error('Invalid tendency store entry')\n }\n }\n return { revision: parsed.schemaVersion === 1 ? 0 : parsed.revision as number, tendencies: parsed.tendencies as Record<string, string> }\n } catch (error) {\n throw new Error('Cast tendency store schema is invalid; repair cast.json and refresh', { cause: error })\n }\n }\n\n /** Atomic store write: temp file plus rename, so a crash never halves JSON. */\n const writeTendencies = (next: Record<string, string>, revision: number): void => {\n const file = castFile()\n mkdirSync(dirname(file), { recursive: true })\n const temp = `${file}.${process.pid}.tmp`\n try {\n writeFileSync(temp, JSON.stringify({ schemaVersion: 2, revision, tendencies: next }, null, 2))\n renameSync(temp, file)\n } catch (error) {\n try {\n rmSync(temp, { force: true })\n } catch {\n // The temp file may never have been created.\n }\n throw error\n }\n }\n\n const readRaw = (): RawCast => {\n const descriptor = deps.describe().find(entry => entry.ns === CAST_NAMESPACE)\n if (!descriptor) throw new Error('native subagent-model-selection-settings not found')\n const value = isRecord(descriptor.value) ? descriptor.value : {}\n const parsed = parseAllowedModels(value.allowedModels)\n return {\n enabled: value.enabled === true,\n revision: typeof descriptor.revision === 'number' ? descriptor.revision : 0,\n routes: parsed.routes,\n length: parsed.length,\n }\n }\n\n const mergeTendencies = (routes: readonly IndexedRoute[], stored: Record<string, string>): CastRoute[] => {\n return routes.map(route => {\n const tendency = stored[tendencyKey(route.provider, route.model)]\n return tendency ? { ...route, tendency } : { ...route }\n })\n }\n\n const getCast = (): CastState => {\n const raw = readRaw()\n try {\n const stored = readTendencies()\n return { enabled: raw.enabled, revision: raw.revision, tendencyRevision: stored.revision, routes: mergeTendencies(raw.routes, stored.tendencies) }\n } catch (error) {\n // Native controls remain available, including repair of enabled + [].\n return { enabled: raw.enabled, revision: raw.revision, routes: raw.routes, tendencyError: (error as Error).message }\n }\n }\n\n /** CAS write: re-describe, pin the revision, then acknowledge the commit.\n * The host owns the next revision; do not invent it or read after saving. */\n const write = async (expectedRevision: unknown, plan: (raw: RawCast) => SettingsOp[]): Promise<CastCommit> => {\n const revision = validateRevision(expectedRevision)\n const raw = readRaw()\n if (raw.revision !== revision) throw new Error('Settings changed; refresh and retry')\n await deps.mutate(CAST_NAMESPACE, plan(raw), revision)\n return { committed: true }\n }\n\n const assertUnique = (route: { provider: string; model: string }, raw: RawCast, except?: number): void => {\n if (raw.routes.some(entry => entry.index !== except && entry.provider === route.provider && entry.model === route.model)) {\n throw new Error(`Route \"${route.provider}/${route.model}\" already exists`)\n }\n }\n\n /* Async all the way down: argument validation must reject, never throw\n * synchronously, so RPC callers and specs see one failure channel. */\n const setEnabled = async (enabled: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n if (typeof enabled !== 'boolean') throw new Error('Invalid enabled state')\n return write(expectedRevision, raw => {\n if (enabled && raw.routes.length === 0) throw new Error('Add a model before enabling subagent cast')\n return [{ op: 'set', path: ['enabled'], value: enabled }]\n })\n }\n\n const addRoute = async (input: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n const route = normalizeRouteInput(input)\n return write(expectedRevision, raw => {\n assertUnique(route, raw)\n return [{ op: 'set', path: ['allowedModels', String(raw.length)], value: route }]\n })\n }\n\n const setRoute = async (index: unknown, input: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n const route = normalizeRouteInput(input)\n const at = validateIndex(index)\n return write(expectedRevision, raw => {\n if (at >= raw.length) throw new Error('Invalid route index')\n assertUnique(route, raw, at)\n return [{ op: 'set', path: ['allowedModels', String(at)], value: route }]\n })\n }\n\n const removeRoute = async (index: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n const at = validateIndex(index)\n return write(expectedRevision, raw => {\n if (at >= raw.length) throw new Error('Invalid route index')\n const ops: SettingsOp[] = [{ op: 'unset', path: ['allowedModels', String(at)] }]\n if (raw.enabled && !raw.routes.some(route => route.index !== at)) ops.push({ op: 'set', path: ['enabled'], value: false })\n return ops\n })\n }\n\n /** Synchronous read/CAS/rename in one host turn; no native settings write. */\n const setTendency = (provider: unknown, model: unknown, tendency: unknown, expectedRevision: unknown, signal?: AbortSignal): CastState => {\n if (typeof provider !== 'string' || !provider.trim() || typeof model !== 'string' || !model.trim()) {\n throw new Error('A tendency requires non-empty provider and model ids')\n }\n if (typeof tendency !== 'string') throw new Error('A tendency must be a string')\n const text = tendency.trim()\n if (text.length > CAST_LIMITS.tendency) throw new Error(`A tendency is limited to ${CAST_LIMITS.tendency} characters`)\n if (!deps.profileDir) throw new Error('No profile directory; cast tendencies cannot be stored')\n const revision = validateRevision(expectedRevision)\n const stored = readTendencies()\n if (stored.revision !== revision) throw new Error('Cast tendencies changed; refresh and retry')\n if (revision === Number.MAX_SAFE_INTEGER) throw new Error('Cast tendency revision limit reached')\n // Resolve native state before committing so a later aggregate read cannot\n // turn a completed tendency save into a rejected RPC.\n const raw = readRaw()\n const next = { ...stored.tendencies }\n const key = tendencyKey(provider.trim(), model.trim())\n if (text) next[key] = text\n else Reflect.deleteProperty(next, key)\n signal?.throwIfAborted()\n writeTendencies(next, revision + 1)\n try { deps.notifyTendenciesChanged?.(revision + 1) } catch { /* Already committed. */ }\n return { enabled: raw.enabled, revision: raw.revision, tendencyRevision: revision + 1, routes: mergeTendencies(raw.routes, next) }\n }\n\n /**\n * Section text for the system-prompt injection. The native\n * `subagentModelSelection` service is authoritative when present; a missing\n * or failing service falls back to the settings descriptor, and a failing\n * descriptor hides the section entirely.\n */\n const promptText = (readCurrent?: () => unknown): string => {\n let snapshot: { enabled: boolean; routes: IndexedRoute[] } | undefined\n try {\n const current = readCurrent?.()\n if (isRecord(current)) snapshot = { enabled: current.enabled === true, routes: parseAllowedModels(current.allowedModels).routes }\n } catch {\n snapshot = undefined\n }\n if (!snapshot) {\n try {\n const raw = readRaw()\n snapshot = { enabled: raw.enabled, routes: raw.routes }\n } catch {\n return ''\n }\n }\n try {\n return buildCastPrompt(snapshot.enabled, mergeTendencies(snapshot.routes, readTendencies().tendencies))\n } catch (error) {\n deps.logger?.warn('model-hub: cast tendencies unavailable: %s', (error as Error).message)\n // Optional user notes must not suppress the generic selection guidance.\n return buildCastPrompt(snapshot.enabled, snapshot.routes)\n }\n }\n\n return { getCast, setEnabled, addRoute, setRoute, removeRoute, setTendency, promptText }\n}\n\nexport type Cast = ReturnType<typeof createCast>\n","/**\n * dsh-model-hub — running subagent routes (host half).\n *\n * Read-only live view answering \"which model and reasoning effort is each\n * running subagent actually using\". `subagent/start` and `subagent/end`\n * maintain the live set; each child's effective route is read from its\n * session epoch header (`session.requestHeader()`) and kept current through\n * the session's own `request/header` events (reason `change` covers mid-run\n * model switches). Children already running when this plugin (re)loads are\n * recovered from the live agent registry via `isSubagentSession`.\n *\n * The effective route is session data recorded by the host; this tracker\n * never writes to sessions or settings. Display-only invalidations reuse the\n * model-settings change channel with RUNNING_NAMESPACE so open pages refetch.\n */\nimport { isSubagentSession } from '@klarkxy/dsh-plugin-kit/contracts'\n\n/** Invalidation namespace for the running-subagent view; carries no settings. */\nexport const RUNNING_NAMESPACE = '@klarkxy/dsh-model-hub/running-subagents'\n\n/** Effective child route recorded by the host epoch header. */\nexport interface RunningRoute {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\n/** One live subagent run as served to the browser half. */\nexport interface RunningSubagentEntry {\n /** Child session id. */\n id: string\n /** Delegation label from the subagent catalog, when resolvable. */\n label?: string\n parentSessionId?: string\n /** Subagent provider owning the run; absent for registry-recovered entries. */\n providerName?: string\n /** False for non-local providers: no local agent exists to read a route from. */\n local: boolean\n startedAt?: number\n /** Effective route of the latest epoch; absent before the first model request. */\n route?: RunningRoute\n /** True when the displayed effort is the adapter default, not an explicit pick. */\n effortIsAdapterDefault?: boolean\n}\n\n/** Snapshot served by the `cast.running` endpoint. */\nexport interface RunningSubagentsState {\n revision: number\n entries: RunningSubagentEntry[]\n}\n\ninterface EpochHeaderLike {\n config?: { provider?: unknown; model?: unknown; reasoningEffort?: unknown }\n adapterDefaults?: { reasoningEffort?: unknown }\n}\n\ninterface SessionLike {\n id?: unknown\n header?: { parentSession?: unknown; createdAt?: unknown }\n requestHeader?: () => EpochHeaderLike | undefined\n}\n\ninterface AgentLike {\n id: string\n session?: SessionLike\n}\n\nexport interface RunningAgentsLike {\n get(id: string): AgentLike | undefined\n list(): AgentLike[]\n}\n\nexport interface RunningSubagentsServiceLike {\n listChildren(parentSessionId: string, signal?: AbortSignal): Promise<readonly { id: string; label?: string }[]>\n}\n\ninterface SubagentRunInfoLike {\n id: string\n provider?: string\n local?: boolean\n}\n\ninterface SessionEventLike {\n type?: unknown\n data?: { header?: EpochHeaderLike }\n}\n\n/** Structural event face; the host context satisfies it directly. */\nexport interface RunningEvents {\n on(name: 'subagent/start', callback: (info: SubagentRunInfoLike) => void): unknown\n on(name: 'subagent/end', callback: (info: SubagentRunInfoLike) => void): unknown\n on(name: 'agent/disposed', callback: (payload: { agent: { id: string } }) => void): unknown\n on(name: 'session/event', callback: (session: SessionLike, event: SessionEventLike) => void): unknown\n}\n\nexport interface RunningDeps {\n events: RunningEvents\n /** Live agent registry; optional so the view degrades to an empty list. */\n agents?: RunningAgentsLike\n /** Subagent catalog used only to resolve delegation labels. */\n subagents?: RunningSubagentsServiceLike\n /** Called with the new revision whenever the served snapshot changed. */\n notify(revision: number): void\n}\n\nfunction text(value: unknown): string {\n return typeof value === 'string' ? value.trim() : ''\n}\n\n/** Normalize one epoch header into the display route plus the adapter-default flag. */\nexport function routeFromHeader(header: EpochHeaderLike | undefined): { route?: RunningRoute; effortIsAdapterDefault?: boolean } {\n const config = header?.config\n const provider = text(config?.provider)\n const model = text(config?.model)\n if (!provider || !model) return {}\n const effort = text(config?.reasoningEffort)\n return {\n route: effort ? { provider, model, reasoningEffort: effort } : { provider, model },\n /* adapterDefaults.reasoningEffort === true marks the recorded effort as\n * adapter-owned rather than explicitly selected (same rule llm-call.ts\n * applies before inheriting a session route). */\n ...(effort && header?.adapterDefaults?.reasoningEffort === true ? { effortIsAdapterDefault: true } : {}),\n }\n}\n\nfunction sameRoute(left: Pick<RunningSubagentEntry, 'route' | 'effortIsAdapterDefault'>, right: Pick<RunningSubagentEntry, 'route' | 'effortIsAdapterDefault'>): boolean {\n return left.route?.provider === right.route?.provider\n && left.route?.model === right.route?.model\n && left.route?.reasoningEffort === right.route?.reasoningEffort\n && left.effortIsAdapterDefault === right.effortIsAdapterDefault\n}\n\nexport function createRunningTracker(deps: RunningDeps): { list(signal?: AbortSignal): Promise<RunningSubagentsState> } {\n const entries = new Map<string, RunningSubagentEntry>()\n let revision = 0\n const bump = () => deps.notify(++revision)\n\n const add = (id: string, info?: SubagentRunInfoLike): void => {\n const agent = deps.agents?.get(id)\n const session = agent?.session\n const next = routeFromHeader(session?.requestHeader?.())\n const existing = entries.get(id)\n if (existing) {\n const providerName = info?.provider ?? existing.providerName\n const local = info?.local ?? existing.local\n const changed = existing.providerName !== providerName || existing.local !== local || !sameRoute(existing, next)\n existing.providerName = providerName\n existing.local = local\n existing.route = next.route\n existing.effortIsAdapterDefault = next.effortIsAdapterDefault\n if (changed) bump()\n return\n }\n entries.set(id, {\n id,\n local: info?.local !== false,\n ...(info?.provider ? { providerName: info.provider } : {}),\n ...(typeof session?.header?.parentSession === 'string' && session.header.parentSession\n ? { parentSessionId: session.header.parentSession } : {}),\n ...(typeof session?.header?.createdAt === 'number' ? { startedAt: session.header.createdAt } : {}),\n route: next.route,\n effortIsAdapterDefault: next.effortIsAdapterDefault,\n })\n bump()\n }\n\n const remove = (id: string): void => {\n if (entries.delete(id)) bump()\n }\n\n /* Children already running when this plugin (re)loads never emit another\n * subagent/start; recover them from the live registry. The origin/depth\n * guard keeps top-level conversations and native human forks out. */\n try {\n for (const agent of deps.agents?.list() ?? []) {\n if (agent?.id && isSubagentSession(agent.session)) add(agent.id)\n }\n } catch { /* A registry failure must not break the plugin load. */ }\n\n deps.events.on('subagent/start', info => { if (typeof info?.id === 'string' && info.id) add(info.id, info) })\n deps.events.on('subagent/end', info => { if (typeof info?.id === 'string') remove(info.id) })\n /* Belt and braces: a child whose run end notification was missed still\n * leaves the list when its agent is disposed. */\n deps.events.on('agent/disposed', payload => { if (typeof payload?.agent?.id === 'string') remove(payload.agent.id) })\n deps.events.on('session/event', (session, event) => {\n if (event?.type !== 'request/header') return\n const id = typeof session?.id === 'string' ? session.id : undefined\n const entry = id ? entries.get(id) : undefined\n if (!entry) return\n const next = routeFromHeader(event.data?.header)\n if (sameRoute(entry, next)) return\n entry.route = next.route\n entry.effortIsAdapterDefault = next.effortIsAdapterDefault\n bump()\n })\n\n return {\n /* Labels live in the subagent catalog, not the session header; resolve\n * them best-effort per parent and cache them on the entry. */\n async list(signal) {\n const pendingParents = new Set<string>()\n for (const entry of entries.values()) {\n if (entry.label === undefined && entry.parentSessionId) pendingParents.add(entry.parentSessionId)\n }\n if (deps.subagents && pendingParents.size) {\n await Promise.allSettled([...pendingParents].map(async parent => {\n const children = await deps.subagents!.listChildren(parent, signal)\n const labels = new Map(children.map(child => [child.id, child.label]))\n for (const entry of entries.values()) {\n if (entry.parentSessionId !== parent || entry.label !== undefined) continue\n const label = labels.get(entry.id)\n if (typeof label === 'string' && label.trim()) entry.label = label.trim()\n }\n }))\n }\n const snapshot = [...entries.values()]\n .map(entry => ({ ...entry, route: entry.route ? { ...entry.route } : undefined }))\n .sort((a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0) || a.id.localeCompare(b.id))\n return { revision, entries: snapshot }\n },\n }\n}\n","/**\n * dsh-model-hub — cross-plugin model settings hub.\n *\n * Host half: reads every plugin's settings descriptor through the host\n * `settings` service, runs model-route field detection, and serves the field\n * matrix to the browser half over an authenticated RPC channel. Writes go\n * through `settings.mutate` — the official settings page's own path — with\n * the descriptor revision as CAS. The boot probe report is kept: it is logged\n * and written to `<profile>/data/model-hub/probe-report.json`. The subagent\n * cast (cast.ts) edits the native subagent model-selection settings the same\n * way and injects per-route task tendencies into the system prompt.\n */\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { createProbeDiagnostics } from './diagnostics.ts'\nimport { join } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\nimport Schema from '@deepseek-ai/schemastery'\nimport type {} from '@deepseek-ai/dsh-settings'\nimport type {} from '@deepseek-ai/dsh-system-prompt'\nimport { registerHostRpc, type HostRpcContext } from '@klarkxy/dsh-plugin-kit/host-rpc'\nimport { modelRouteRoleSchemastery, modelRouteSchemastery } from '@klarkxy/dsh-model-route/schemastery'\nimport { buildProbeReport, planFieldWrite, type ProbeDescriptorLike } from './probe.ts'\nimport { createHubSettings } from './owners.ts'\nimport { notifyModelSettingsChanged, type ModelSettingsDiscovery } from '@klarkxy/dsh-plugin-kit/model-settings'\nimport { registerHubModelSettingsChanges } from './changes.ts'\nimport { CAST_TENDENCIES_NAMESPACE, createCast } from './cast.ts'\nimport { createRunningTracker, RUNNING_NAMESPACE, type RunningAgentsLike, type RunningSubagentsServiceLike } from './running.ts'\n\nexport const name = '@klarkxy/dsh-model-hub'\nexport const inject = ['settings', 'webServer', 'connection', 'systemPrompt'] as const\nexport const CHANNEL = '/dsh-model-hub'\n\nexport const Config = Schema.object({\n selfTestMeta: modelRouteSchemastery({ purpose: 'self-test-meta', label: { zh: '自检字段(自定义键通道)', en: 'Self-test field (custom key)' } }).volatile(),\n selfTestRole: modelRouteRoleSchemastery({ purpose: 'self-test-role', label: { zh: '自检字段(role 通道)', en: 'Self-test field (role)' } }).volatile(),\n})\n\ninterface ProfileContextLike {\n dir: string\n}\n\n/** Structural view of the host settings service, version-checked at runtime. */\ninterface SettingsLike {\n describe(options?: { redactSecrets?: boolean }): unknown[]\n mutate(ns: string, ops: ReturnType<typeof planFieldWrite>['ops'], expectedRevision?: number): Promise<void>\n}\n\nexport function apply(ctx: Context): void {\n registerHubModelSettingsChanges(ctx)\n const profile = ctx.get('profileContext') as ProfileContextLike | undefined\n const settings = ctx.settings as unknown as SettingsLike\n const describe = () => settings.describe({ redactSecrets: true }) as unknown as ProbeDescriptorLike[]\n // Loader adds entry to the runtime fiber; the settings-only type dependency\n // does not import that optional augmentation.\n const ownNamespace = (ctx.fiber as unknown as { entry?: { options?: { id?: string } } }).entry?.options?.id ?? name\n const hub = createHubSettings(ctx as unknown as ModelSettingsDiscovery, {\n describe, mutate: (ns, ops, revision) => settings.mutate(ns, ops, revision),\n }, ownNamespace)\n const cast = createCast({\n describe, mutate: (ns, ops, revision) => settings.mutate(ns, ops, revision),\n profileDir: profile?.dir, logger: ctx.logger,\n notifyTendenciesChanged: revision => notifyModelSettingsChanged(ctx, CAST_TENDENCIES_NAMESPACE, revision),\n })\n\n /* Live subagent routes: read-only session data. The registry and catalog\n * are optional services; without them the view degrades to an empty list. */\n const running = createRunningTracker({\n events: ctx,\n agents: ctx.get('agents') as RunningAgentsLike | undefined,\n subagents: ctx.get('subagents') as RunningSubagentsServiceLike | undefined,\n notify: revision => notifyModelSettingsChanged(ctx, RUNNING_NAMESPACE, revision),\n })\n\n /* Subagent cast guidance: read-only advisory text evaluated at each prompt\n * assembly — it never writes to disk or calls a model. It intentionally\n * applies to every session including subagent threads: the native selection\n * policy is inherited by child sessions, so the same tendencies stay valid\n * there. User-provided tendency text is injected verbatim\n * (interpolate: false). */\n ctx.systemPrompt.section({\n name: 'model-hub:subagent-cast',\n order: ctx.systemPrompt.getSectionOrder('TOOL_SUBAGENT') + 1,\n interpolate: false,\n text: () => cast.promptText(() => (ctx.get('subagentModelSelection') as { current(): unknown } | undefined)?.current()),\n })\n\n const run = async () => {\n const reason = 'requested'\n let report\n try {\n report = buildProbeReport(describe())\n } catch (error) {\n ctx.logger.warn('model-hub: probe failed (%s): %s', reason, error instanceof Error ? error.message : String(error))\n return\n }\n ctx.logger.info(\n 'model-hub: probe (%s): %d namespaces, %d with model fields; self-test extra-key=%s role=%s',\n reason, report.totalNamespaces, report.namespacesWithModelFields, report.selfTest.extraKey, report.selfTest.role,\n )\n if (profile?.dir) {\n try {\n const dir = join(profile.dir, 'data', 'model-hub')\n await mkdir(dir, { recursive: true })\n await writeFile(join(dir, 'probe-report.json'), JSON.stringify(report, null, 2))\n } catch (error) {\n ctx.logger.warn('model-hub: could not write probe report: %s', error instanceof Error ? error.message : String(error))\n }\n }\n }\n\n const diagnostics = createProbeDiagnostics(run, error => {\n ctx.logger.warn('model-hub: diagnostics failed: %s', String(error))\n })\n ctx.effect(() => {\n const off = ctx.on('settings/document-updated', () => diagnostics.invalidate())\n diagnostics.request()\n // Stop notifications before draining the in-flight asynchronous write.\n return async () => { off(); await diagnostics.dispose() }\n })\n\n /* Browser-facing field matrix: list detected fields, write one field back.\n * registerHostRpc keeps the host's origin/auth policy authoritative and\n * fails closed when it is absent. */\n ctx.effect(() => registerHostRpc(ctx as unknown as HostRpcContext, CHANNEL, async (endpoint, payload, signal) => {\n try {\n if (endpoint === 'fields.list') {\n const value = await hub.list(signal)\n diagnostics.request()\n return { ok: true, value }\n }\n if (endpoint === 'fields.set') {\n return { ok: true, value: await hub.set(payload, signal) }\n }\n if (endpoint.startsWith('cast.')) {\n const row = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : {}\n if (endpoint === 'cast.get') return { ok: true, value: cast.getCast() }\n if (endpoint === 'cast.setEnabled') return { ok: true, value: await cast.setEnabled(row.enabled, row.expectedRevision) }\n if (endpoint === 'cast.addRoute') return { ok: true, value: await cast.addRoute(row.route, row.expectedRevision) }\n if (endpoint === 'cast.setRoute') return { ok: true, value: await cast.setRoute(row.index, row.route, row.expectedRevision) }\n if (endpoint === 'cast.removeRoute') return { ok: true, value: await cast.removeRoute(row.index, row.expectedRevision) }\n if (endpoint === 'cast.setTendency') return { ok: true, value: cast.setTendency(row.provider, row.model, row.tendency, row.expectedRevision, signal) }\n if (endpoint === 'cast.running') return { ok: true, value: await running.list(signal) }\n }\n throw new Error(`Unknown endpoint: ${endpoint}`)\n } catch (error) {\n return { ok: false, error: { code: 'model-hub/rejected', message: error instanceof Error ? error.message : String(error) } }\n }\n // Camel-case cast endpoints widen the default lowercase-only endpoint gate.\n }, { isEndpoint: endpoint => /^[a-z][a-zA-Z.]*$/.test(endpoint) }))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/cast.ts","../src/changes.ts","../src/index.ts"],"sourcesContent":["/**\n * dsh-model-hub — subagent cast (host half).\n *\n * Edits the host-native `subagent-model-selection-settings` namespace\n * (`enabled` + `allowedModels` routes) through the official settings CAS\n * path, and keeps the per-route user preferences in\n * `<profile>/data/model-hub/cast.json`: the free-text \"task tendency\" note\n * and the reasoning effort.\n *\n * The native AllowedModelRoute schema carries provider and model only — the\n * host strips a reasoning effort from the settings value — so the effort is a\n * plugin-side preference keyed by provider/model and saved with the file's own\n * CAS. Tendencies and efforts are advisory hints for the system-prompt\n * injection in index.ts; the main agent still decides which model a delegated\n * subagent receives.\n */\nimport { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport type { ProbeDescriptorLike } from './probe.ts'\n\n/** Native namespace owning the subagent model-selection preference. */\nexport const CAST_NAMESPACE = 'subagent-model-selection-settings'\n/** Separate revision domain; native settings revisions do not cover this file. */\nexport const CAST_TENDENCIES_NAMESPACE = '@klarkxy/dsh-model-hub/cast-tendencies'\n\n/** Tendency ids are limited like host route ids; notes stay short prompt lines. */\nexport const CAST_LIMITS = { id: 250, tendency: 500, effort: 80 } as const\n\n/** One valid route of the cast. `index` is the write handle (see createCast). */\nexport interface CastRoute {\n /** Position inside the raw native allowedModels array. Malformed entries are\n * skipped when reading, so a route's position in `CastState.routes` can\n * diverge from its raw index; every write addresses routes by this index. */\n index: number\n provider: string\n model: string\n /** Plugin-side preference; the native schema never carries it. */\n reasoningEffort?: string\n tendency?: string\n}\n\n/** Cast snapshot served to the browser half. */\nexport interface CastState {\n enabled: boolean\n revision: number\n /** Revision of the plugin-side preference store (tendencies and efforts). */\n tendencyRevision?: number\n tendencyError?: string\n routes: CastRoute[]\n}\n/** Native saves acknowledge only the commit; getCast owns later reads. */\nexport interface CastCommit { committed: true }\n\n/**\n * One route's plugin-side preferences. An absent field keeps its stored value;\n * an empty string clears it.\n */\nexport interface RoutePreferences {\n tendency?: string\n reasoningEffort?: string\n}\n\ntype SettingsOp = { op: 'set'; path: string[]; value: unknown } | { op: 'unset'; path: string[] }\n\nexport interface CastDeps {\n describe(): readonly ProbeDescriptorLike[]\n mutate(ns: string, ops: readonly SettingsOp[], expectedRevision: number): Promise<void>\n profileDir?: string\n logger?: { warn(message: string, ...args: unknown[]): void }\n notifyTendenciesChanged?(revision: number): void\n}\n\n/** One valid allowedModels entry plus its raw array index. */\ntype IndexedRoute = Omit<CastRoute, 'tendency' | 'reasoningEffort'>\n\ninterface RawCast {\n enabled: boolean\n revision: number\n routes: IndexedRoute[]\n /** Raw allowedModels length, malformed entries included. */\n length: number\n}\n\n/** The plugin-side preference store: one revision over both maps. */\ninterface PreferenceStore {\n revision: number\n tendencies: Record<string, string>\n efforts: Record<string, string>\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Store key for one provider/model pair; it keys tendencies and efforts. */\nexport function tendencyKey(provider: string, model: string): string {\n return JSON.stringify([provider, model])\n}\n\n/** Keep well-formed entries with their raw indexes; skip malformed ones. */\nfunction parseAllowedModels(value: unknown): { routes: IndexedRoute[]; length: number } {\n const list = Array.isArray(value) ? value : []\n const routes: IndexedRoute[] = []\n list.forEach((entry, index) => {\n if (!isRecord(entry)) return\n const { provider, model } = entry\n if (typeof provider !== 'string' || !provider || typeof model !== 'string' || !model) return\n routes.push({ index, provider, model })\n })\n return { routes, length: list.length }\n}\n\n/**\n * Normalize a native route write. The native schema owns provider and model\n * only: a reasoning effort is a plugin-side preference and never travels\n * through the settings CAS.\n */\nfunction normalizeRouteInput(input: unknown): { provider: string; model: string } {\n const row = isRecord(input) ? input : {}\n const provider = typeof row.provider === 'string' ? row.provider.trim() : ''\n const model = typeof row.model === 'string' ? row.model.trim() : ''\n if (!provider || !model) throw new Error('A route requires non-empty provider and model ids')\n if (provider.length > CAST_LIMITS.id || model.length > CAST_LIMITS.id) {\n throw new Error(`Route provider and model ids are limited to ${CAST_LIMITS.id} characters`)\n }\n return { provider, model }\n}\n\nfunction validateRevision(value: unknown): number {\n if (!Number.isSafeInteger(value) || (value as number) < 0) {\n throw new Error('A valid expected revision is required; refresh and retry')\n }\n return value as number\n}\n\nfunction validateIndex(value: unknown): number {\n if (!Number.isSafeInteger(value) || (value as number) < 0) throw new Error('Invalid route index')\n return value as number\n}\n\n/**\n * Render the injected guidance. Pure: callers resolve enabled state and the\n * preference-merged routes. Returns '' when the injection should not appear —\n * the host prompt renderer drops empty sections.\n */\nexport function buildCastPrompt(enabled: boolean, routes: readonly CastRoute[]): string {\n if (!enabled || !routes.length) return ''\n const annotated = routes.filter(route => typeof route.tendency === 'string' && route.tendency.trim())\n const withEffort = routes.filter(route => typeof route.reasoningEffort === 'string' && route.reasoningEffort.trim())\n // Global settings can differ from a Session's saved route policy. Discovery\n // and the actual tool schema, not this section, determine what it can select.\n const guidance = [\n 'Subagent model selection:',\n 'When delegating an independent task, actively choose a suitable model instead of silently inheriting your own. If list_subagent_models and a delegation tool with provider/model fields are available, inspect the available routes before the first model-selectable delegation in this task, reuse those results for later delegations, and supply provider and model together explicitly.',\n 'Keep provider and model as separate arguments. A discovery label is displayed as provider/model; do not copy the whole label into model or prepend provider to the model id. Example only: if provider is \"example-provider\" and its list shows \"example-provider/team/model-a\", pass {\"provider\":\"example-provider\",\"model\":\"team/model-a\"}. Remove only the known display prefix \"example-provider/\" from that combined label; preserve all slashes and namespaces inside the model id. If discovery already gives separate fields, copy both exactly without stripping or adding any prefix. The example is not an available route.',\n 'For independent reviews, alternative approaches, or disputed conclusions, prefer a model different from your own when a suitable candidate is available. Choose for task fit and the user\\'s constraints; using the same model is appropriate when it fits better or no suitable alternative is available. Do not create extra subtasks solely for model diversity.',\n 'The current Session\\'s discovery results and tool schema are authoritative. Its allowed routes are saved when the Session is created and inherited by its subagents; later list edits apply to new top-level Sessions. This limits explicit selection, not pure route inheritance. Configured model notes may refer to routes unavailable in this Session. If discovery or model-selection fields are unavailable, use the supported delegation behavior; do not invent routes or pass unsupported fields to tools such as subagent_fork. When changing models, omit reasoning_effort unless you have verified a supported value.',\n ]\n if (annotated.length) guidance.push(\n 'User-provided task tendencies (advisory): prefer a matching tendency among available models. Models without a tendency remain general-purpose candidates; no model-specific strengths are assumed.',\n ...annotated.map(route =>\n `- ${JSON.stringify({ provider: route.provider, model: route.model })}${route.reasoningEffort ? ` (reasoning effort: ${route.reasoningEffort})` : ''}: ${route.tendency}`),\n )\n if (withEffort.length) guidance.push(\n 'Configured reasoning effort (advisory reference configuration, applied when a subagent starts; it does not change subagents that are already running): pass reasoning_effort only when the selected route supports that value and omit it otherwise. When a route\\'s provider/model changes, an effort the new route does not support is dropped.',\n ...withEffort.map(route =>\n `- ${JSON.stringify({ provider: route.provider, model: route.model })}: reasoning effort \"${route.reasoningEffort}\"`),\n )\n return guidance.join('\\n')\n}\n\nexport function createCast(deps: CastDeps) {\n const castFile = (): string => {\n if (!deps.profileDir) throw new Error('No profile directory; cast preferences cannot be stored')\n return join(deps.profileDir, 'data', 'model-hub', 'cast.json')\n }\n\n /**\n * Always read the durable revision: another tab or plugin incarnation may\n * have committed since the last snapshot. Failed reads never become a cache.\n * v1 and v2 stores hold tendencies only; v3 adds the effort map.\n */\n const readPreferences = (): PreferenceStore => {\n if (!deps.profileDir) return { revision: 0, tendencies: {}, efforts: {} }\n let raw: string\n try {\n raw = readFileSync(castFile(), 'utf8')\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { revision: 0, tendencies: {}, efforts: {} }\n throw new Error('Cast preference store cannot be read; restore access to cast.json and refresh', { cause: error })\n }\n let parsed: unknown\n try { parsed = JSON.parse(raw) } catch (error) {\n throw new Error('Cast preference store is damaged; repair cast.json and refresh', { cause: error })\n }\n const version = isRecord(parsed) ? parsed.schemaVersion : undefined\n if (version !== 1 && version !== 2 && version !== 3) {\n throw new Error('Cast preference store version is unsupported; use a compatible plugin before saving')\n }\n try {\n if (!isRecord(parsed) || !isRecord(parsed.tendencies)) throw new Error('Invalid preference store schema')\n if (version !== 1 && (!Number.isSafeInteger(parsed.revision) || (parsed.revision as number) < 0)) throw new Error('Invalid preference store revision')\n const efforts = version === 3 ? parsed.efforts : {}\n if (!isRecord(efforts)) throw new Error('Invalid preference store schema')\n for (const map of [parsed.tendencies, efforts]) {\n for (const [key, value] of Object.entries(map)) {\n const ids: unknown = JSON.parse(key)\n if (!Array.isArray(ids) || ids.length !== 2 || !ids.every(id => typeof id === 'string' && id.trim()) || typeof value !== 'string') {\n throw new Error('Invalid preference store entry')\n }\n }\n }\n return {\n revision: version === 1 ? 0 : parsed.revision as number,\n tendencies: parsed.tendencies as Record<string, string>,\n efforts: efforts as Record<string, string>,\n }\n } catch (error) {\n throw new Error('Cast preference store schema is invalid; repair cast.json and refresh', { cause: error })\n }\n }\n\n /** Atomic store write: temp file plus rename, so a crash never halves JSON. */\n const writePreferences = (next: { tendencies: Record<string, string>; efforts: Record<string, string> }, revision: number): void => {\n const file = castFile()\n mkdirSync(dirname(file), { recursive: true })\n const temp = `${file}.${process.pid}.tmp`\n try {\n writeFileSync(temp, JSON.stringify({ schemaVersion: 3, revision, tendencies: next.tendencies, efforts: next.efforts }, null, 2))\n renameSync(temp, file)\n } catch (error) {\n try {\n rmSync(temp, { force: true })\n } catch {\n // The temp file may never have been created.\n }\n throw error\n }\n }\n\n const readRaw = (): RawCast => {\n const descriptor = deps.describe().find(entry => entry.ns === CAST_NAMESPACE)\n if (!descriptor) throw new Error('native subagent-model-selection-settings not found')\n const value = isRecord(descriptor.value) ? descriptor.value : {}\n const parsed = parseAllowedModels(value.allowedModels)\n return {\n enabled: value.enabled === true,\n revision: typeof descriptor.revision === 'number' ? descriptor.revision : 0,\n routes: parsed.routes,\n length: parsed.length,\n }\n }\n\n /** Merge the stored preferences into the native routes. */\n const mergePreferences = (routes: readonly IndexedRoute[], stored: { tendencies: Record<string, string>; efforts: Record<string, string> }): CastRoute[] => {\n return routes.map(route => {\n const key = tendencyKey(route.provider, route.model)\n const tendency = stored.tendencies[key]\n const effort = stored.efforts[key]\n return {\n ...route,\n ...(tendency ? { tendency } : {}),\n ...(effort ? { reasoningEffort: effort } : {}),\n }\n })\n }\n\n const getCast = (): CastState => {\n const raw = readRaw()\n try {\n const stored = readPreferences()\n return { enabled: raw.enabled, revision: raw.revision, tendencyRevision: stored.revision, routes: mergePreferences(raw.routes, stored) }\n } catch (error) {\n // Native controls remain available, including repair of enabled + [].\n return { enabled: raw.enabled, revision: raw.revision, routes: raw.routes, tendencyError: (error as Error).message }\n }\n }\n\n /** CAS write: re-describe, pin the revision, then acknowledge the commit.\n * The host owns the next revision; do not invent it or read after saving. */\n const write = async (expectedRevision: unknown, plan: (raw: RawCast) => SettingsOp[]): Promise<CastCommit> => {\n const revision = validateRevision(expectedRevision)\n const raw = readRaw()\n if (raw.revision !== revision) throw new Error('Settings changed; refresh and retry')\n await deps.mutate(CAST_NAMESPACE, plan(raw), revision)\n return { committed: true }\n }\n\n const assertUnique = (route: { provider: string; model: string }, raw: RawCast, except?: number): void => {\n if (raw.routes.some(entry => entry.index !== except && entry.provider === route.provider && entry.model === route.model)) {\n throw new Error(`Route \"${route.provider}/${route.model}\" already exists`)\n }\n }\n\n /* Async all the way down: argument validation must reject, never throw\n * synchronously, so RPC callers and specs see one failure channel. */\n const setEnabled = async (enabled: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n if (typeof enabled !== 'boolean') throw new Error('Invalid enabled state')\n return write(expectedRevision, raw => {\n if (enabled && raw.routes.length === 0) throw new Error('Add a model before enabling subagent cast')\n return [{ op: 'set', path: ['enabled'], value: enabled }]\n })\n }\n\n const addRoute = async (input: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n const route = normalizeRouteInput(input)\n return write(expectedRevision, raw => {\n assertUnique(route, raw)\n return [{ op: 'set', path: ['allowedModels', String(raw.length)], value: route }]\n })\n }\n\n const setRoute = async (index: unknown, input: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n const route = normalizeRouteInput(input)\n const at = validateIndex(index)\n return write(expectedRevision, raw => {\n if (at >= raw.length) throw new Error('Invalid route index')\n assertUnique(route, raw, at)\n return [{ op: 'set', path: ['allowedModels', String(at)], value: route }]\n })\n }\n\n const removeRoute = async (index: unknown, expectedRevision: unknown): Promise<CastCommit> => {\n const at = validateIndex(index)\n return write(expectedRevision, raw => {\n if (at >= raw.length) throw new Error('Invalid route index')\n const ops: SettingsOp[] = [{ op: 'unset', path: ['allowedModels', String(at)] }]\n if (raw.enabled && !raw.routes.some(route => route.index !== at)) ops.push({ op: 'set', path: ['enabled'], value: false })\n return ops\n })\n }\n\n /**\n * Save one route's plugin-side preferences with the file's own CAS. The\n * returned state is the committed one, so the page never waits for a later\n * refresh that could still carry the previous value.\n */\n const setRoutePreferences = (provider: unknown, model: unknown, preferences: unknown, expectedRevision: unknown, signal?: AbortSignal): CastState => {\n if (typeof provider !== 'string' || !provider.trim() || typeof model !== 'string' || !model.trim()) {\n throw new Error('A route preference requires non-empty provider and model ids')\n }\n if (!deps.profileDir) throw new Error('No profile directory; cast preferences cannot be stored')\n const revision = validateRevision(expectedRevision)\n const stored = readPreferences()\n if (stored.revision !== revision) throw new Error('Cast preferences changed; refresh and retry')\n if (revision === Number.MAX_SAFE_INTEGER) throw new Error('Cast preference revision limit reached')\n const patch = isRecord(preferences) ? preferences : {}\n const next = { tendencies: { ...stored.tendencies }, efforts: { ...stored.efforts } }\n const key = tendencyKey(provider.trim(), model.trim())\n if ('tendency' in patch) {\n if (typeof patch.tendency !== 'string') throw new Error('A tendency must be a string')\n const text = patch.tendency.trim()\n if (text.length > CAST_LIMITS.tendency) throw new Error(`A tendency is limited to ${CAST_LIMITS.tendency} characters`)\n if (text) next.tendencies[key] = text\n else Reflect.deleteProperty(next.tendencies, key)\n }\n if ('reasoningEffort' in patch) {\n if (typeof patch.reasoningEffort !== 'string') throw new Error('A reasoning effort must be a string')\n const effort = patch.reasoningEffort.trim()\n if (effort.length > CAST_LIMITS.effort) throw new Error(`A reasoning effort is limited to ${CAST_LIMITS.effort} characters`)\n if (effort) next.efforts[key] = effort\n else Reflect.deleteProperty(next.efforts, key)\n }\n // Resolve native state before committing so a later aggregate read cannot\n // turn a completed preference save into a rejected RPC.\n const raw = readRaw()\n signal?.throwIfAborted()\n writePreferences(next, revision + 1)\n try { deps.notifyTendenciesChanged?.(revision + 1) } catch { /* Already committed. */ }\n return { enabled: raw.enabled, revision: raw.revision, tendencyRevision: revision + 1, routes: mergePreferences(raw.routes, next) }\n }\n\n /** Synchronous read/CAS/rename in one host turn; no native settings write. */\n const setTendency = (provider: unknown, model: unknown, tendency: unknown, expectedRevision: unknown, signal?: AbortSignal): CastState => {\n if (typeof tendency !== 'string') throw new Error('A tendency must be a string')\n return setRoutePreferences(provider, model, { tendency }, expectedRevision, signal)\n }\n\n /**\n * Section text for the system-prompt injection. The native\n * `subagentModelSelection` service is authoritative when present; a missing\n * or failing service falls back to the settings descriptor, and a failing\n * descriptor hides the section entirely.\n */\n const promptText = (readCurrent?: () => unknown): string => {\n let snapshot: { enabled: boolean; routes: IndexedRoute[] } | undefined\n try {\n const current = readCurrent?.()\n if (isRecord(current)) snapshot = { enabled: current.enabled === true, routes: parseAllowedModels(current.allowedModels).routes }\n } catch {\n snapshot = undefined\n }\n if (!snapshot) {\n try {\n const raw = readRaw()\n snapshot = { enabled: raw.enabled, routes: raw.routes }\n } catch {\n return ''\n }\n }\n try {\n return buildCastPrompt(snapshot.enabled, mergePreferences(snapshot.routes, readPreferences()))\n } catch (error) {\n deps.logger?.warn('model-hub: cast preferences unavailable: %s', (error as Error).message)\n // Optional user notes must not suppress the generic selection guidance.\n return buildCastPrompt(snapshot.enabled, snapshot.routes)\n }\n }\n\n return { getCast, setEnabled, addRoute, setRoute, removeRoute, setTendency, setRoutePreferences, promptText }\n}\n\nexport type Cast = ReturnType<typeof createCast>\n","import type { Context } from '@deepseek-ai/cordis'\nimport { modelSettingsChangesIdentity } from '@klarkxy/dsh-plugin-kit/contracts'\nimport { createModelSettingsChanges } from '@klarkxy/dsh-plugin-kit/model-settings-changes'\n\n/** Model Hub forwards every settings namespace; feature plugins filter their own. */\nexport function registerHubModelSettingsChanges(ctx: Context): void {\n createModelSettingsChanges(ctx, modelSettingsChangesIdentity('@klarkxy/dsh-model-hub'))\n}\n","/** Subagent model allowlist and delegation preferences. */\nimport type { Context } from '@deepseek-ai/cordis'\nimport Schema from '@deepseek-ai/schemastery'\nimport type {} from '@deepseek-ai/dsh-settings'\nimport type {} from '@deepseek-ai/dsh-system-prompt'\nimport { registerHostRpc, type HostRpcContext } from '@klarkxy/dsh-plugin-kit/host-rpc'\nimport { notifyModelSettingsChanged } from '@klarkxy/dsh-plugin-kit/model-settings'\nimport { CAST_TENDENCIES_NAMESPACE, createCast } from './cast.ts'\nimport { registerHubModelSettingsChanges } from './changes.ts'\n\nexport const name = '@klarkxy/dsh-model-hub'\nexport const inject = ['settings', 'webServer', 'connection', 'systemPrompt'] as const\nexport const CHANNEL = '/dsh-model-hub'\nexport const Config = Schema.object({})\n\nexport function apply(ctx: Context): void {\n registerHubModelSettingsChanges(ctx)\n const profile = ctx.get('profileContext') as { dir: string } | undefined\n const settings = ctx.settings as { describe(options?: { redactSecrets?: boolean }): unknown[]; mutate(ns: string, ops: Array<{ op: 'set'; path: string[]; value: unknown } | { op: 'unset'; path: string[] }>, expectedRevision?: number): Promise<void> }\n const cast = createCast({\n describe: () => settings.describe({ redactSecrets: true }) as never,\n mutate: (ns, ops, revision) => settings.mutate(ns, [...ops], revision),\n profileDir: profile?.dir, logger: ctx.logger,\n notifyTendenciesChanged: revision => notifyModelSettingsChanged(ctx, CAST_TENDENCIES_NAMESPACE, revision),\n })\n ctx.systemPrompt.section({\n name: 'model-hub:subagent-cast',\n order: ctx.systemPrompt.getSectionOrder('TOOL_SUBAGENT') + 1,\n interpolate: false,\n text: () => cast.promptText(() => (ctx.get('subagentModelSelection') as { current(): unknown } | undefined)?.current()),\n })\n ctx.effect(() => registerHostRpc(ctx as unknown as HostRpcContext, CHANNEL, async (endpoint, payload, signal) => {\n try {\n if (endpoint.startsWith('cast.')) {\n const row = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : {}\n if (endpoint === 'cast.get') return { ok: true, value: cast.getCast() }\n if (endpoint === 'cast.setEnabled') return { ok: true, value: await cast.setEnabled(row.enabled, row.expectedRevision) }\n if (endpoint === 'cast.addRoute') return { ok: true, value: await cast.addRoute(row.route, row.expectedRevision) }\n if (endpoint === 'cast.setRoute') return { ok: true, value: await cast.setRoute(row.index, row.route, row.expectedRevision) }\n if (endpoint === 'cast.removeRoute') return { ok: true, value: await cast.removeRoute(row.index, row.expectedRevision) }\n if (endpoint === 'cast.setTendency') return { ok: true, value: cast.setTendency(row.provider, row.model, row.tendency, row.expectedRevision, signal) }\n if (endpoint === 'cast.setPreferences') return { ok: true, value: cast.setRoutePreferences(row.provider, row.model, row.preferences, row.expectedRevision, signal) }\n }\n throw new Error(`Unknown endpoint: ${endpoint}`)\n } catch (error) {\n return { ok: false, error: { code: 'model-hub/rejected', message: error instanceof Error ? error.message : String(error) } }\n }\n }, { isEndpoint: endpoint => /^[a-z][a-zA-Z.]*$/.test(endpoint) }))\n}\n"],"mappings":"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"}
package/locale/en.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
- "title": "Model Settings",
4
- "description": "Change the models used by plugins and subagents from one page, and see each running subagent's model and reasoning effort."
3
+ "title": "Subagents",
4
+ "description": "Configure subagent models and reasoning effort, and see each one's last-used model in the conversation popup."
5
5
  }
6
6
  }
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
- "title": "模型设置",
4
- "description": "在一个页面修改各插件和子代理使用的模型,并查看运行中子代理的模型与思考强度。"
3
+ "title": "子代理",
4
+ "description": "配置子代理的模型与思考强度,并在会话弹层查看最后使用的模型。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@klarkxy/dsh-model-hub",
3
- "version": "0.1.7",
4
- "description": "Change the models used by DSH plugins and subagents from one page, and see each running subagent's model and reasoning effort.",
3
+ "version": "0.2.0",
4
+ "description": "Configure models and reasoning effort for new DSH subagents, and see each subagent's last-used model in the conversation popup.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
7
7
  "engines": {
@@ -44,13 +44,13 @@
44
44
  }
45
45
  },
46
46
  "scripts": {
47
- "build": "tsdown && node --check lib/index.js && node ../../scripts/editor-plugins/wrap-client.mjs @klarkxy/dsh-model-hub",
47
+ "build": "tsdown && node ../../scripts/editor-plugins/wrap-client.mjs @klarkxy/dsh-model-hub",
48
48
  "typecheck": "tsc --noEmit -p tsconfig.json",
49
49
  "test": "vitest run --root ../.. --config vitest.editor-plugins.config.ts --maxWorkers=2 plugins/dsh-model-hub/src"
50
50
  },
51
51
  "dependencies": {
52
52
  "@klarkxy/dsh-model-route": "0.2.6",
53
- "@klarkxy/dsh-plugin-kit": "0.5.0"
53
+ "@klarkxy/dsh-plugin-kit": "0.6.0"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@deepseek-ai/cordis": ">=4.0.4",
@@ -106,6 +106,6 @@
106
106
  },
107
107
  "homepage": "https://klarkxy.github.io/dsh-plugins/plugins/model-hub/",
108
108
  "dshRelease": {
109
- "contentHash": "7c37fd51e932a6d8cbe1fc5ff2878676fdbd6c6cda130349e71676892f157eff"
109
+ "contentHash": "693784fbdce97cd974bcd7144e31b1b92253d1e0bd5416c22ec65762ec39e332"
110
110
  }
111
111
  }