@gehennawu/dsh-service 2.0.7 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +96 -81
- package/README.md +96 -81
- package/client.js +1 -1
- package/index.js +180 -9
- package/model-config-routes.js +641 -0
- package/model-config-schema.js +132 -0
- package/model-smart-fill.js +2206 -0
- package/model-workbench-routes.js +1740 -0
- package/model-workbench-settings.js +389 -0
- package/model-workbench.js +1618 -0
- package/package.json +11 -3
- package/provider-availability.js +84 -0
- package/quota-adapters.js +55 -0
- package/screenshots/README.md +4 -3
- package/screenshots/configuration.png +0 -0
- package/screenshots/configuration_en.png +0 -0
- package/screenshots/health-diagnostics.png +0 -0
- package/screenshots/health-diagnostics_en.png +0 -0
- package/screenshots/model-configuration.png +0 -0
- package/screenshots/model-configuration_en.png +0 -0
- package/screenshots/overview.png +0 -0
- package/screenshots/overview_en.png +0 -0
- package/subagent-routes.js +66 -1
package/index.js
CHANGED
|
@@ -20,6 +20,46 @@ import { createSessionsRoutes } from './session-routes.js'
|
|
|
20
20
|
import { createQuotaRoutes } from './quota-routes.js'
|
|
21
21
|
import { createSubagentRoutes } from './subagent-routes.js'
|
|
22
22
|
import { createBackupRoutes } from './backup-routes.js'
|
|
23
|
+
import { createModelConfigRoutes } from './model-config-routes.js'
|
|
24
|
+
import { createProviderAvailability, installProviderAvailability } from './provider-availability.js'
|
|
25
|
+
import { createWorkbenchStore, loadPiAiCatalog } from './model-workbench.js'
|
|
26
|
+
import {
|
|
27
|
+
WORKBENCH_SIDECAR_VERSION,
|
|
28
|
+
applySuggestions,
|
|
29
|
+
applyUndo,
|
|
30
|
+
arbitrateModelSuggestions,
|
|
31
|
+
buildPiAiIndex,
|
|
32
|
+
abilityPatch,
|
|
33
|
+
consensusFields,
|
|
34
|
+
createFreshnessManager,
|
|
35
|
+
deriveKeyRefFor,
|
|
36
|
+
diffModelLists,
|
|
37
|
+
emptyWorkbenchSidecar,
|
|
38
|
+
fetchModelsProbe,
|
|
39
|
+
hostnameOf,
|
|
40
|
+
indexModelParamsDev,
|
|
41
|
+
isPrivateIpAddress,
|
|
42
|
+
loadModelParamsSnapshot,
|
|
43
|
+
loadWorkbenchSidecar,
|
|
44
|
+
modelParamsCandidates,
|
|
45
|
+
modelParamsDevEfforts,
|
|
46
|
+
modelPatchFromPiAi,
|
|
47
|
+
openRouterModelFacts,
|
|
48
|
+
openRouterVerifySource,
|
|
49
|
+
pickOpenRouterMatch,
|
|
50
|
+
modelsProbeLookup,
|
|
51
|
+
normalizeBaseURLForMatch,
|
|
52
|
+
parseModelListing,
|
|
53
|
+
probeBaseURLCheck,
|
|
54
|
+
profileFingerprint,
|
|
55
|
+
readLlmProfiles,
|
|
56
|
+
saveModelParamsSnapshot,
|
|
57
|
+
saveWorkbenchSidecar,
|
|
58
|
+
selfCheckModelsPatch,
|
|
59
|
+
suggestRouteId,
|
|
60
|
+
suggestionId,
|
|
61
|
+
undoPreviewModels,
|
|
62
|
+
} from './model-workbench.js'
|
|
23
63
|
import { collectPluginHealth, pluginCheckItem, restartPluginEntry } from './plugin-health.js'
|
|
24
64
|
import {
|
|
25
65
|
buildCliproxyAccountPlan as buildCliproxyAccountPlanAdapter,
|
|
@@ -88,6 +128,8 @@ const DEFAULT_FEATURE_SETTINGS = Object.freeze({
|
|
|
88
128
|
// v1.8 新增:composer 模型按钮显示厂家/渠道图标(宽态加在模型名前,
|
|
89
129
|
// 窄态替换官方默认图标;未适配渠道保留官方默认图标)。
|
|
90
130
|
modelProviderIcons: true,
|
|
131
|
+
// v1.10 新增:模型配置工作台(供应商与模型 / 子代理路由),写路径经两段式确认。
|
|
132
|
+
modelCatalog: true,
|
|
91
133
|
})
|
|
92
134
|
/** 标记为「可热更新」的 Config 字段(0.1.7-alpha.1 的表单与原地热更新以此为准)。 */
|
|
93
135
|
const volatileField = (schema) => (typeof schema?.extra === 'function' ? schema.extra('volatile', true) : schema)
|
|
@@ -3993,6 +4035,7 @@ async function listSubagentModels(llm, agentDefaultModel) {
|
|
|
3993
4035
|
|
|
3994
4036
|
/** 子代理路由配置的空档(inherit):与未安装本功能的原生行为完全一致。 */
|
|
3995
4037
|
function createEmptySubagentRoute() {
|
|
4038
|
+
// 不含 preset 键:空预设不落盘,磁盘形状与 v2.0.8 及之前逐字节兼容(注入端按 absent = 跟随父代)。
|
|
3996
4039
|
return { version: SUBAGENT_ROUTE_VERSION, mode: 'inherit' }
|
|
3997
4040
|
}
|
|
3998
4041
|
|
|
@@ -4031,12 +4074,16 @@ function parseSubagentRouteText(text) {
|
|
|
4031
4074
|
if (parsed.mode === 'custom' && (provider === '' || model === '')) return fallback
|
|
4032
4075
|
// reasoningEffort 是可选的 adapter 自有等级 ID:只有字符串、trim 后非空才保留(空串视为默认)。
|
|
4033
4076
|
const reasoningEffort = provider !== '' && model !== '' && typeof parsed.reasoningEffort === 'string' ? parsed.reasoningEffort.trim().slice(0, MAX_SUBAGENT_ROUTE_FIELD) : ''
|
|
4077
|
+
// 独立 Agent 预设(v2.0.9):与模式无关的全局字段;空串 = 跟随父代。存在性校验在保存端做
|
|
4078
|
+
// (磁盘配置容忍失效 id——旧宿主/预设被删时注入端按 provider 能力门跳过,不阻塞派生)。
|
|
4079
|
+
const preset = typeof parsed.preset === 'string' ? parsed.preset.trim().slice(0, MAX_SUBAGENT_ROUTE_FIELD) : ''
|
|
4034
4080
|
return {
|
|
4035
4081
|
version: SUBAGENT_ROUTE_VERSION,
|
|
4036
4082
|
mode: parsed.mode,
|
|
4037
4083
|
...(provider !== '' && model !== '' ? { provider, model } : {}),
|
|
4038
4084
|
...(reasoningEffort !== '' ? { reasoningEffort } : {}),
|
|
4039
4085
|
...(fallbacks.length > 0 ? { fallbacks } : {}),
|
|
4086
|
+
...(preset !== '' ? { preset } : {}),
|
|
4040
4087
|
}
|
|
4041
4088
|
}
|
|
4042
4089
|
|
|
@@ -4236,7 +4283,7 @@ function subagentRouteCandidates(request, config, options = {}) {
|
|
|
4236
4283
|
|
|
4237
4284
|
function resolveSubagentInjection(request, config, options = {}) {
|
|
4238
4285
|
for (const candidate of subagentRouteCandidates(request, config, options)) {
|
|
4239
|
-
if (options.isRoutable !== undefined && !options.isRoutable(candidate.provider)) {
|
|
4286
|
+
if (options.isRoutable !== undefined && !options.isRoutable(candidate.provider, candidate.model)) {
|
|
4240
4287
|
options.note?.(`${candidate.provider}/${candidate.model} skipped (not routable)`)
|
|
4241
4288
|
continue
|
|
4242
4289
|
}
|
|
@@ -4301,6 +4348,8 @@ function buildSubagentDispatchRecord(agent, parent, dispatch, options = {}) {
|
|
|
4301
4348
|
}
|
|
4302
4349
|
if (typeof turn === 'number' && Number.isFinite(turn)) record.turn = turn
|
|
4303
4350
|
if (reasoningEffort !== undefined) record.reasoningEffort = reasoningEffort
|
|
4351
|
+
// 独立 Agent 预设(v2.0.9):仅记录插件注入的值;调用方显式携带时不记(来源不是本插件)。
|
|
4352
|
+
if (typeof dispatch?.preset === 'string' && dispatch.preset !== '') record.preset = dispatch.preset
|
|
4304
4353
|
return record
|
|
4305
4354
|
}
|
|
4306
4355
|
|
|
@@ -5120,7 +5169,9 @@ async function quotaCredentialEndpoint(ctx, refreshQuotaConfig, throttle, payloa
|
|
|
5120
5169
|
try {
|
|
5121
5170
|
await credentials.set(name, value)
|
|
5122
5171
|
} catch (error) {
|
|
5123
|
-
//
|
|
5172
|
+
// 可写性由宿主凭据服务裁决后回传:0.2.1-alpha.3 起托管存储优先、env 层不再只读,
|
|
5173
|
+
// 旧版「进程环境层遮蔽 ⇒ describe().writable=false 且 set 必拒」的成因已不适用;
|
|
5174
|
+
// 这里不预判成因,宿主报什么错就透传什么(seam 契约的显式报错)。
|
|
5124
5175
|
return { ok: false, error: 'credential-write-failed', detail: sanitizeQuotaErrorDetail(error?.message) }
|
|
5125
5176
|
}
|
|
5126
5177
|
// 新凭据落库即清掉该 provider 的退避/冷却闸门:旧失败是旧凭据造成的,
|
|
@@ -5348,6 +5399,18 @@ function apply(ctx, featureConfig) {
|
|
|
5348
5399
|
// 版本一变即自然失效,不必手工清理。
|
|
5349
5400
|
const releaseNotesCache = new Map()
|
|
5350
5401
|
const quotaThrottle = createQuotaThrottle()
|
|
5402
|
+
// 模型配置工作台(v1.10):侧车仓库持有写前快照/绑定/验证摘要/人工恢复态。
|
|
5403
|
+
// 写串行化在仓库内部;目录随 DSH 升级才变,进程内缓存一次。
|
|
5404
|
+
const modelProfileDir = ctx.get('profileContext')?.dir ?? process.env.DSH_PROFILE_DIR
|
|
5405
|
+
const workbenchStore = createWorkbenchStore(typeof modelProfileDir === 'string' && modelProfileDir !== '' ? modelProfileDir : join(dshHome, 'dsh-service-model-config', 'legacy'))
|
|
5406
|
+
const providerAvailability = createProviderAvailability(workbenchStore)
|
|
5407
|
+
const notifyProviderAvailability = () => ctx.emit('llm/adapters-updated')
|
|
5408
|
+
ctx.inject(['llm'], (scope) => {
|
|
5409
|
+
const llm = scope.get('llm') ?? ctx.get('llm')
|
|
5410
|
+
if (!llm) return
|
|
5411
|
+
const dispose = installProviderAvailability(llm, providerAvailability, notifyProviderAvailability)
|
|
5412
|
+
scope.effect(() => dispose)
|
|
5413
|
+
})
|
|
5351
5414
|
// 技能管理(v0.22):侧车索引缓存 + 批量补全状态。批量随 Fiber 销毁中止。
|
|
5352
5415
|
// 可重赋值的 let 一律以 { current } 箱体跨模块传递:端点工厂解构是值快照,
|
|
5353
5416
|
// let 重赋值不会穿过解构边界;箱体靠对象身份保持活绑定。
|
|
@@ -5432,7 +5495,8 @@ function apply(ctx, featureConfig) {
|
|
|
5432
5495
|
const hasActivation = typeof subagents?.startActivation === 'function'
|
|
5433
5496
|
const hasLegacyStarts = typeof subagents?.start === 'function' && typeof subagents?.startContinuable === 'function'
|
|
5434
5497
|
if (!hasActivation && !hasLegacyStarts) return
|
|
5435
|
-
const isRoutable = (provider) => {
|
|
5498
|
+
const isRoutable = (provider, model) => {
|
|
5499
|
+
if (model !== undefined && !providerAvailability.modelEnabled(provider, model)) return false
|
|
5436
5500
|
const llm = ctx.get('llm')
|
|
5437
5501
|
if (llm === undefined || typeof llm.listProviders !== 'function') return false
|
|
5438
5502
|
try {
|
|
@@ -5483,6 +5547,8 @@ function apply(ctx, featureConfig) {
|
|
|
5483
5547
|
return quotaProviderUnusable(view) === false
|
|
5484
5548
|
}
|
|
5485
5549
|
const applyInjection = (request) => {
|
|
5550
|
+
const explicit = request?.agentOptions?.provider
|
|
5551
|
+
if (typeof explicit === 'string' && explicit) providerAvailability.assertEnabled(explicit, request?.agentOptions?.model)
|
|
5486
5552
|
// 功能关闭:零记录(完全静默,与原生行为一致)——记录是 subagentRoute 功能的一部分。
|
|
5487
5553
|
if (!featureEnabled('subagentRoute')) return { request, dispatch: undefined }
|
|
5488
5554
|
const routeOptions = {
|
|
@@ -5493,7 +5559,7 @@ function apply(ctx, featureConfig) {
|
|
|
5493
5559
|
}
|
|
5494
5560
|
const injected = resolveSubagentInjection(request, subagentRouteRef.current, routeOptions)
|
|
5495
5561
|
const routeCandidates = injected === undefined ? [] : subagentRouteCandidates(request, subagentRouteRef.current, routeOptions)
|
|
5496
|
-
.filter((candidate) => isRoutable(candidate.provider) && isQuotaHealthy(candidate.provider) !== false)
|
|
5562
|
+
.filter((candidate) => isRoutable(candidate.provider, candidate.model) && isQuotaHealthy(candidate.provider) !== false)
|
|
5497
5563
|
.filter((candidate, index, candidates) => candidates.findIndex((item) => item.provider === candidate.provider && item.model === candidate.model) === index)
|
|
5498
5564
|
const injectedIndex = routeCandidates.findIndex((candidate) => candidate.provider === injected?.provider && candidate.model === injected?.model)
|
|
5499
5565
|
const eligibleRoutes = injectedIndex < 0 ? [] : routeCandidates.slice(injectedIndex)
|
|
@@ -5517,6 +5583,45 @@ function apply(ctx, featureConfig) {
|
|
|
5517
5583
|
const effort = typeof reasoningEffort === 'string' && reasoningEffort !== '' ? reasoningEffort : undefined
|
|
5518
5584
|
return { request: decorated, dispatch: { parent: request?.parent, provider: injected.provider, model: injected.model, ...(effort !== undefined ? { reasoningEffort: effort } : {}), routeCandidates: eligibleRoutes } }
|
|
5519
5585
|
}
|
|
5586
|
+
// 独立 Agent 预设(v2.0.9):官方 0.2.1-alpha.3 起 startActivation 的请求面新增
|
|
5587
|
+
// request.agentPreset(统一设置子代理角色与工具;省略 = 跟随父代当前预设)。注入门完全
|
|
5588
|
+
// 对齐官方 tool-subagent 的 assertProvider:provider.capabilities.agentPreset === true 且
|
|
5589
|
+
// !provider.inheritsParentContext(fork 类继承父代上下文的 provider 不支持)。调用方显式
|
|
5590
|
+
// 携带的 agentPreset 永远赢。其余一切不明朗情形(服务缺席、清单读不到、预设已删/broken、
|
|
5591
|
+
// 能力不明)一律 fail-closed 不注入——给不支持的 provider 注入该字段会被宿主
|
|
5592
|
+
// assertCapabilities 拒绝、派生直接失败,「不生效」永远好过「失败」。每次派发现查清单
|
|
5593
|
+
// (预设集极小、变更低频),保证预设被删后自动回落跟随父代。仅 startActivation 路径装饰:
|
|
5594
|
+
// 旧版 start/startContinuable 的宿主请求类型没有该字段,其 provider 能力面也不含 agentPreset。
|
|
5595
|
+
const resolvePresetForSpec = async (spec, request) => {
|
|
5596
|
+
const configured = typeof subagentRouteRef.current.preset === 'string' ? subagentRouteRef.current.preset : ''
|
|
5597
|
+
if (configured === '') return undefined
|
|
5598
|
+
if (typeof request?.agentPreset === 'string' && request.agentPreset !== '') return undefined
|
|
5599
|
+
let declared
|
|
5600
|
+
try {
|
|
5601
|
+
const registry = ctx.get('agentPresets')
|
|
5602
|
+
if (registry === undefined || registry === null || typeof registry.list !== 'function') return undefined
|
|
5603
|
+
declared = await registry.list()
|
|
5604
|
+
} catch (_) {
|
|
5605
|
+
return undefined
|
|
5606
|
+
}
|
|
5607
|
+
const hit = (Array.isArray(declared) ? declared : []).find((entry) => entry !== null && typeof entry === 'object' && entry.id === configured)
|
|
5608
|
+
if (hit === undefined || (typeof hit.broken === 'string' && hit.broken !== '')) {
|
|
5609
|
+
ctx.logger?.info?.(`dsh-service: subagent preset "${configured}" skipped: not in the host preset list`)
|
|
5610
|
+
return undefined
|
|
5611
|
+
}
|
|
5612
|
+
let provider
|
|
5613
|
+
try {
|
|
5614
|
+
provider = typeof subagents.getProvider === 'function' ? subagents.getProvider(spec?.provider) : undefined
|
|
5615
|
+
} catch (_) {
|
|
5616
|
+
provider = undefined
|
|
5617
|
+
}
|
|
5618
|
+
if (provider === null || typeof provider !== 'object') return undefined
|
|
5619
|
+
if (provider.capabilities?.agentPreset !== true || provider.inheritsParentContext === true) {
|
|
5620
|
+
ctx.logger?.info?.(`dsh-service: subagent preset "${configured}" skipped: provider "${spec?.provider ?? ''}" does not support independent presets`)
|
|
5621
|
+
return undefined
|
|
5622
|
+
}
|
|
5623
|
+
return configured
|
|
5624
|
+
}
|
|
5520
5625
|
const runWithDispatch = (dispatch, work) => {
|
|
5521
5626
|
if (dispatch === undefined) return work()
|
|
5522
5627
|
return pendingDispatchStorage.run(dispatch, work)
|
|
@@ -5525,17 +5630,23 @@ function apply(ctx, featureConfig) {
|
|
|
5525
5630
|
const originalStart = subagents.start
|
|
5526
5631
|
const originalStartContinuable = subagents.startContinuable
|
|
5527
5632
|
if (hasActivation) {
|
|
5528
|
-
subagents.startActivation = (spec) => {
|
|
5633
|
+
subagents.startActivation = async (spec) => {
|
|
5634
|
+
await providerAvailability.loading
|
|
5529
5635
|
const { request: decorated, dispatch } = applyInjection(spec.request)
|
|
5530
5636
|
if (dispatch === undefined) return originalStartActivation.call(subagents, spec)
|
|
5531
|
-
|
|
5637
|
+
const preset = await resolvePresetForSpec(spec, decorated)
|
|
5638
|
+
const finalRequest = preset !== undefined ? { ...decorated, agentPreset: preset } : decorated
|
|
5639
|
+
if (preset !== undefined) dispatch.preset = preset
|
|
5640
|
+
return runWithDispatch(dispatch, () => originalStartActivation.call(subagents, { ...spec, request: finalRequest }))
|
|
5532
5641
|
}
|
|
5533
5642
|
} else {
|
|
5534
|
-
subagents.start = (name, request) => {
|
|
5643
|
+
subagents.start = async (name, request) => {
|
|
5644
|
+
await providerAvailability.loading
|
|
5535
5645
|
const { request: decorated, dispatch } = applyInjection(request)
|
|
5536
5646
|
return runWithDispatch(dispatch, () => originalStart.call(subagents, name, decorated))
|
|
5537
5647
|
}
|
|
5538
|
-
subagents.startContinuable = (spec) => {
|
|
5648
|
+
subagents.startContinuable = async (spec) => {
|
|
5649
|
+
await providerAvailability.loading
|
|
5539
5650
|
const { request: decorated, dispatch } = applyInjection(spec.request)
|
|
5540
5651
|
return runWithDispatch(dispatch, () => originalStartContinuable.call(subagents, { ...spec, request: decorated }))
|
|
5541
5652
|
}
|
|
@@ -6201,8 +6312,29 @@ function apply(ctx, featureConfig) {
|
|
|
6201
6312
|
createSkillsRoutes({ ctx, describeJobs, dshHome, makeDescribeJobLogger, registerSkillCall, serializeSkillsIndexWrite, skillFileCache, skillsActiveControllers, skillsBatchRef, skillsIndexRef, SKILL_DESCRIPTION_MAX_CHARS, SKILL_USAGE_MAX_CHARS, bodyHashOf, describeSkillDraft, evaluateSkillFile, fixLegacySkillInvocationKeys, listSkillModels, locateSkillFrontmatter, mutateSkillEntryById, name, normalizeSkillDescribeLang, publicSkillEntry, rpcTechnicalFailure, sanitizeSkillDraftText, scanSkillEntries, selectSkillBatchCandidates, setSkillInvocationKey }),
|
|
6202
6313
|
createSessionsRoutes({ ctx, dshHome, sessionBytesCache, sessionDeletePlans, sessionTitleCache, sessionTitlesReady, sessionViewCache, SESSIONS_BYTES_MAX_IDS, SESSIONS_DELETE_PLAN_TTL_MS, SESSIONS_VIEW_PAGE_SIZE, listSessionsForManage, loadDeletedSessions, name, resolveSessionBytesForIds, resolveSessionForDelete, rpcFailure, rpcTechnicalFailure, saveDeletedSessions, searchSessionsContent, sessionExists, sessionIsLive, viewSessionPage }),
|
|
6203
6314
|
createQuotaRoutes({ ctx, kickQuotaRefresh, quotaThrottle, refreshQuotaConfigCache, serializeQuotaConfigWrite, MAX_QUOTA_PROVIDER_NAME, MAX_QUOTA_RESET_CARDS, MAX_QUOTA_RESET_CARDS_PER_PROVIDER, MAX_QUOTA_RESET_CARD_ACCOUNT, canonicalResetCardExpiresAt, QUOTA_ADAPTER_BY_KIND, name, quotaCredentialConfigured, quotaCredentialEndpoint, quotaCredentialHintNames, readQuotaProfiles, resolveQuotaKind, rpcTechnicalFailure }),
|
|
6204
|
-
createSubagentRoutes({ ctx, dispatchRing, serializeSubagentRouteWrite, subagentRouteRef, subagentRouteLoadPromise, subagentSeamRef, MAX_SUBAGENT_ROUTE_FIELD, SUBAGENT_ROUTE_FALLBACK_MAX, SUBAGENT_ROUTE_MODES, listSubagentDispatches, listSubagentModels, rpcTechnicalFailure }),
|
|
6315
|
+
createSubagentRoutes({ ctx, providerAvailability, dispatchRing, serializeSubagentRouteWrite, subagentRouteRef, subagentRouteLoadPromise, subagentSeamRef, MAX_SUBAGENT_ROUTE_FIELD, SUBAGENT_ROUTE_FALLBACK_MAX, SUBAGENT_ROUTE_MODES, listSubagentDispatches, listSubagentModels, rpcTechnicalFailure }),
|
|
6205
6316
|
createBackupRoutes({ ctx, backupIntegrity, backupProgress, clearBackupProgress, downloadTokens, dshHome, dshVersion, setBackupProgress, withBackupLock, backupNameVersionSuffix, createBackup, deleteBackup, exportBackup, formatBackupTimestamp, importBackup, listBackups, listLocalProfiles, name, rpcFailure }),
|
|
6317
|
+
createModelConfigRoutes({
|
|
6318
|
+
ctx,
|
|
6319
|
+
loadPiCatalog: loadPiAiCatalog,
|
|
6320
|
+
workbenchStore,
|
|
6321
|
+
providerAvailability,
|
|
6322
|
+
notifyProviderAvailability,
|
|
6323
|
+
// 移除守卫的只读引用面:子代理路由配置 + 官方默认模型选择(都是宿主侧事实,浏览器不参与)。
|
|
6324
|
+
loadSubagentRoute: async () => {
|
|
6325
|
+
await subagentRouteLoadPromise
|
|
6326
|
+
return subagentRouteRef.current
|
|
6327
|
+
},
|
|
6328
|
+
loadAgentDefaultModelSelection: () => {
|
|
6329
|
+
const service = ctx.get('agentDefaultModel')
|
|
6330
|
+
if (service === undefined || typeof service.currentSelection !== 'function') return undefined
|
|
6331
|
+
return service.currentSelection()
|
|
6332
|
+
},
|
|
6333
|
+
writeSubagentRoute: serializeSubagentRouteWrite,
|
|
6334
|
+
listSubagentModels,
|
|
6335
|
+
dshHome,
|
|
6336
|
+
pluginVersion,
|
|
6337
|
+
}),
|
|
6206
6338
|
])
|
|
6207
6339
|
|
|
6208
6340
|
const dispatchEndpoint = createRpcDispatcher({ endpoints: rpcEndpoints, featureEnabled, logger: ctx.logger })
|
|
@@ -6312,6 +6444,45 @@ export {
|
|
|
6312
6444
|
saveUnifiedConfig,
|
|
6313
6445
|
updateUnifiedConfigSection,
|
|
6314
6446
|
sanitizeSettingsNavConfig,
|
|
6447
|
+
// 模型配置工作台(v1.10):领域纯函数经 index.js 再导出,测试从 '../index.js' 导入的
|
|
6448
|
+
// 既有约定保持不变(旧分支 test/workbench.test.js 即此形状)。
|
|
6449
|
+
WORKBENCH_SIDECAR_VERSION,
|
|
6450
|
+
applySuggestions,
|
|
6451
|
+
applyUndo,
|
|
6452
|
+
arbitrateModelSuggestions,
|
|
6453
|
+
buildPiAiIndex,
|
|
6454
|
+
createWorkbenchStore,
|
|
6455
|
+
deriveKeyRefFor,
|
|
6456
|
+
diffModelLists,
|
|
6457
|
+
emptyWorkbenchSidecar,
|
|
6458
|
+
fetchModelsProbe,
|
|
6459
|
+
hostnameOf,
|
|
6460
|
+
isPrivateIpAddress,
|
|
6461
|
+
loadWorkbenchSidecar,
|
|
6462
|
+
modelPatchFromPiAi,
|
|
6463
|
+
modelsProbeLookup,
|
|
6464
|
+
normalizeBaseURLForMatch,
|
|
6465
|
+
parseModelListing,
|
|
6466
|
+
probeBaseURLCheck,
|
|
6467
|
+
profileFingerprint,
|
|
6468
|
+
readLlmProfiles,
|
|
6469
|
+
saveWorkbenchSidecar,
|
|
6470
|
+
selfCheckModelsPatch,
|
|
6471
|
+
suggestRouteId,
|
|
6472
|
+
suggestionId,
|
|
6473
|
+
undoPreviewModels,
|
|
6474
|
+
// P3.5 补充源(modelparams.dev)与多源共识:同样经 index.js 再导出供测试直达。
|
|
6475
|
+
abilityPatch,
|
|
6476
|
+
consensusFields,
|
|
6477
|
+
createFreshnessManager,
|
|
6478
|
+
indexModelParamsDev,
|
|
6479
|
+
loadModelParamsSnapshot,
|
|
6480
|
+
modelParamsCandidates,
|
|
6481
|
+
modelParamsDevEfforts,
|
|
6482
|
+
openRouterModelFacts,
|
|
6483
|
+
openRouterVerifySource,
|
|
6484
|
+
pickOpenRouterMatch,
|
|
6485
|
+
saveModelParamsSnapshot,
|
|
6315
6486
|
}
|
|
6316
6487
|
// 插件 Config:0.1.7-alpha.1 起宿主配置面由 Profile 的插件 Config 托管;声明它即可让
|
|
6317
6488
|
// 功能开关出现在插件管理页,并让宿主经 `loader/volatile-update` 热更新(无需重启)。
|