@dsh-plus/llm-pi 0.1.26 → 0.1.28

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/src/compat.ts CHANGED
@@ -1,161 +1,74 @@
1
1
  /**
2
- * 逐协议 compat 门控表与校验(0.1.2-alpha.2 复核:官方门控表逐块一致,无需变更)。
2
+ * 逐协议 compat 校验:门控表与取值约束**从官方 dsh-llm-pi-ai 安装副本自动继承**
3
+ * (见 compat-gates.ts),不再手抄镜像。
3
4
  *
4
- * 适配决策:官方 dsh-llm-pi-ai 0.1.2-alpha.2 的 COMPAT_GATES(catalog.ts)
5
- * compat 可配性的唯一事实源——按协议分型(offer/withhold),withhold 字段
6
- * (catalog 已为对应厂商设置,如 openRouterRouting/zaiToolStream/
7
- * sendSessionAffinityHeaders/supportsToolSearch 等)写时拒绝并提示以目录
8
- * provider 名为 route。官方门控表只在 src/catalog.ts 源码子路径内(npm 发布
9
- * 形态不携带 src/、包根也不导出该符号),插件在 dsh 树(npm 布局)与 vendored
10
- * 兜底两条路径都无法静态引用,故本文件按官方 catalog.ts 逐字段镜像门控表
11
- * (字段全集与分型随 pi-ai 升级同步维护,官方以 Record<keyof Compat> 编译期
12
- * 约束漂移,插件以本表 + 注释人工同步)。
5
+ * 语义:官方门控表是 compat 可配性的唯一事实源——按协议分型(offer/withhold),
6
+ * withhold 字段(官方内置目录已为对应厂商设置,如 openRouterRouting/zaiToolStream/
7
+ * sendSessionAffinityHeaders/supportsToolSearch/supportsMidConvoEffort 等)写时
8
+ * 拒绝并提示以目录 provider 名为 route;未知键拒绝;无值键(null/undefined)拒绝
9
+ * (对齐官方 assertOfferedCompatFields:"写了但没生效"的表面状态不允许)。
13
10
  *
14
- * 与旧版字段表的差异(0.1.2-alpha.1 官方门控):
15
- * - completions 新增 offer:supportsFinishReason/chatTemplateArgs/
16
- * supportsThinkingTokenBudget;thinkingFormat 新增 baseten;
17
- * - 原表内 openRouterRouting/vercelGatewayRouting/zaiToolStream/
18
- * sendSessionAffinityHeaders/deferredToolsMode/sessionAffinityFormat/
19
- * supportsOpenAIGrammarTools(completions/responses)与 supportsToolSearch/
20
- * supportsExplicitPromptCacheMode(responses)、supportsToolReferences
21
- * (anthropic)改为 withhold → 写时拒绝;
22
- * - responses 不再 offer sessionAffinityFormat(withhold)。
11
+ * 历史:本表曾是手抄镜像,0.1.5-rc.1 官方扩容 offer 字段而旧表漏收,导致官方可配
12
+ * 字段被本插件**误拒**(静默功能缺失、无任何报错)。现改为现场推导:官方新增字段
13
+ * 即自动可用,`tests/compat-gates.test.ts` 直读官方 bundle 守门推导正确性。
23
14
  *
24
- * pi-ai 侧消费语义:getCompat 逐字段 `??` 覆盖 detectCompat 的
25
- * baseURL/名称猜测;undefined 视为未设置(无法显式清空检测值)。
15
+ * pi-ai 侧消费语义:getCompat 逐字段 `??` 覆盖 detectCompat 的 baseURL/名称猜测;
16
+ * undefined 视为未设置(无法显式清空检测值)。
26
17
  * @module llm-pi/compat
27
18
  */
19
+ import type { CompatDisposition, CompatTable, CompatValue } from './compat-gates.ts'
20
+ import { FALLBACK_TABLE } from './compat-gates.ts'
28
21
  import type { ProtocolId } from './config.ts'
29
22
 
30
- type CompatValue = 'boolean' | 'object' | readonly string[]
31
- type CompatDisposition = 'offer' | 'withhold'
23
+ export type { CompatDisposition, CompatValue } from './compat-gates.ts'
32
24
 
33
25
  /**
34
- * 官方 COMPLETIONS_COMPAT_GATE(llm-pi-ai/src/catalog.ts)逐字段镜像。
35
- * offer 17 字段 / withhold 7 字段。
26
+ * 生效门控表:由 resolve-dsh 的套件加载流程经 {@link installCompatTable} 注入
27
+ * (与 PiAiAdapter 同源——即 dsh 树或 vendored 副本里**正在运行**的那份官方代码)。
28
+ * 未注入时用 FALLBACK_TABLE(纯函数测试路径/极端启动顺序下的保守兜底)。
36
29
  */
37
- const COMPLETIONS_COMPAT_GATE: Readonly<Record<string, CompatDisposition>> = {
38
- supportsStore: 'offer',
39
- supportsDeveloperRole: 'offer',
40
- supportsReasoningEffort: 'offer',
41
- supportsUsageInStreaming: 'offer',
42
- supportsFinishReason: 'offer',
43
- maxTokensField: 'offer',
44
- requiresToolResultName: 'offer',
45
- requiresAssistantAfterToolResult: 'offer',
46
- requiresThinkingAsText: 'offer',
47
- requiresReasoningContentOnAssistantMessages: 'offer',
48
- thinkingFormat: 'offer',
49
- chatTemplateKwargs: 'offer',
50
- chatTemplateArgs: 'offer',
51
- supportsThinkingTokenBudget: 'offer',
52
- supportsStrictMode: 'offer',
53
- cacheControlFormat: 'offer',
54
- supportsLongCacheRetention: 'offer',
55
- openRouterRouting: 'withhold',
56
- vercelGatewayRouting: 'withhold',
57
- zaiToolStream: 'withhold',
58
- supportsOpenAIGrammarTools: 'withhold',
59
- sendSessionAffinityHeaders: 'withhold',
60
- deferredToolsMode: 'withhold',
61
- sessionAffinityFormat: 'withhold',
62
- }
30
+ let active: CompatTable = FALLBACK_TABLE
63
31
 
64
- /**
65
- * 官方 RESPONSES_COMPAT_GATE 逐字段镜像(三协议共享同一 OpenAIResponsesCompat;
66
- * 本插件只服务 openai-responses)。offer 3 字段 / withhold 5 字段。
67
- */
68
- const RESPONSES_COMPAT_GATE: Readonly<Record<string, CompatDisposition>> = {
69
- supportsDeveloperRole: 'offer',
70
- supportsStrictMode: 'offer',
71
- supportsLongCacheRetention: 'offer',
72
- sessionAffinityFormat: 'withhold',
73
- supportsOpenAIGrammarTools: 'withhold',
74
- supportsAdditionalTools: 'withhold',
75
- supportsToolSearch: 'withhold',
76
- supportsExplicitPromptCacheMode: 'withhold',
32
+ /** 注入推导结果(幂等;resolve-dsh 在套件加载后调用一次)。 */
33
+ export function installCompatTable(table: CompatTable): void {
34
+ active = table
77
35
  }
78
36
 
79
- /**
80
- * 官方 ANTHROPIC_COMPAT_GATE 逐字段镜像。offer 7 字段 / withhold 2 字段。
81
- */
82
- const ANTHROPIC_COMPAT_GATE: Readonly<Record<string, CompatDisposition>> = {
83
- supportsEagerToolInputStreaming: 'offer',
84
- supportsLongCacheRetention: 'offer',
85
- supportsCacheControlOnTools: 'offer',
86
- supportsTemperature: 'offer',
87
- forceAdaptiveThinking: 'offer',
88
- allowEmptySignature: 'offer',
89
- supportsStrictTools: 'offer',
90
- sendSessionAffinityHeaders: 'withhold',
91
- supportsToolReferences: 'withhold',
92
- }
93
-
94
- const GATES_BY_PROTOCOL: Record<ProtocolId, Readonly<Record<string, CompatDisposition>>> = {
95
- 'openai-completions': COMPLETIONS_COMPAT_GATE,
96
- 'openai-responses': RESPONSES_COMPAT_GATE,
97
- 'anthropic-messages': ANTHROPIC_COMPAT_GATE,
37
+ /** 当前生效表的来源诊断(状态行/日志)。 */
38
+ export function compatTableInfo(): { source: CompatTable['source']; problem?: string } {
39
+ return active.problem === undefined
40
+ ? { source: active.source }
41
+ : { source: active.source, problem: active.problem }
98
42
  }
99
43
 
100
44
  /** 某协议某字段的可配性('offer'/'withhold';未列出 = 无此字段)。 */
101
- export function compatDispositionOf(api: ProtocolId, field: string): CompatDisposition | undefined {
102
- return GATES_BY_PROTOCOL[api][field]
103
- }
104
-
105
- /**
106
- * 字段取值约束(对齐官方 config.ts compatProfile schema):
107
- * boolean 字段 → 'boolean';maxTokensField/thinkingFormat/cacheControlFormat
108
- * → 枚举;chatTemplateKwargs/chatTemplateArgs → 对象。
109
- */
110
- const VALUE_SPECS: Readonly<Record<string, CompatValue>> = {
111
- supportsStore: 'boolean',
112
- supportsDeveloperRole: 'boolean',
113
- supportsReasoningEffort: 'boolean',
114
- supportsUsageInStreaming: 'boolean',
115
- supportsFinishReason: 'boolean',
116
- maxTokensField: ['max_completion_tokens', 'max_tokens'],
117
- requiresToolResultName: 'boolean',
118
- requiresAssistantAfterToolResult: 'boolean',
119
- requiresThinkingAsText: 'boolean',
120
- requiresReasoningContentOnAssistantMessages: 'boolean',
121
- thinkingFormat: [
122
- // 官方 SUPPORTED_THINKING_FORMATS(含 baseten,随 chatTemplateArgs 使用)
123
- 'openai',
124
- 'deepseek',
125
- 'openrouter',
126
- 'together',
127
- 'baseten',
128
- 'zai',
129
- 'qwen',
130
- 'chat-template',
131
- 'qwen-chat-template',
132
- 'string-thinking',
133
- 'ant-ling',
134
- ],
135
- chatTemplateKwargs: 'object',
136
- chatTemplateArgs: 'object',
137
- supportsThinkingTokenBudget: 'boolean',
138
- supportsStrictMode: 'boolean',
139
- cacheControlFormat: ['anthropic'],
140
- supportsLongCacheRetention: 'boolean',
141
- supportsEagerToolInputStreaming: 'boolean',
142
- supportsCacheControlOnTools: 'boolean',
143
- supportsTemperature: 'boolean',
144
- forceAdaptiveThinking: 'boolean',
145
- allowEmptySignature: 'boolean',
146
- supportsStrictTools: 'boolean',
45
+ export function compatDispositionOf(api: string, field: string): CompatDisposition | undefined {
46
+ return active.gates[api]?.[field]
147
47
  }
148
48
 
149
49
  /** 某协议全部可配置(offer)的 compat 键(UI 渲染字段组与校验共用)。 */
150
- export function compatFieldsOf(api: ProtocolId): readonly string[] {
151
- return Object.entries(GATES_BY_PROTOCOL[api]).flatMap(([field, disposition]) =>
50
+ export function compatFieldsOf(api: ProtocolId | string): readonly string[] {
51
+ const gate = active.gates[api]
52
+ if (gate === undefined) return []
53
+ return Object.entries(gate).flatMap(([field, disposition]) =>
152
54
  disposition === 'offer' ? [field] : [],
153
55
  )
154
56
  }
155
57
 
156
58
  /** 某协议某 offer 字段的取值约束(UI 渲染开关/下拉用)。 */
157
- export function compatFieldSpec(api: ProtocolId, field: string): CompatValue | undefined {
158
- return GATES_BY_PROTOCOL[api][field] === 'offer' ? VALUE_SPECS[field] : undefined
59
+ export function compatFieldSpec(api: string, field: string): CompatValue | undefined {
60
+ return active.gates[api]?.[field] === 'offer' ? active.specs[field] : undefined
61
+ }
62
+
63
+ /** 官方声明的全部可配置字段(未知键报错时列出,对齐官方 allOfferedCompatFields)。 */
64
+ function allOfferedFields(): string[] {
65
+ const out = new Set<string>()
66
+ for (const gate of Object.values(active.gates)) {
67
+ for (const [field, disposition] of Object.entries(gate)) {
68
+ if (disposition === 'offer') out.add(field)
69
+ }
70
+ }
71
+ return [...out]
159
72
  }
160
73
 
161
74
  function checkValue(field: string, spec: CompatValue, value: unknown, where: string): void {
@@ -163,6 +76,19 @@ function checkValue(field: string, spec: CompatValue, value: unknown, where: str
163
76
  if (typeof value !== 'boolean') throw new Error(`${where}: compat.${field} 必须是布尔值`)
164
77
  return
165
78
  }
79
+ if (spec === 'integer') {
80
+ // 官方 z.number().step(1):整数(小数/NaN/Infinity 均不合格)
81
+ if (typeof value !== 'number' || !Number.isInteger(value)) {
82
+ throw new Error(`${where}: compat.${field} 必须是整数`)
83
+ }
84
+ return
85
+ }
86
+ if (spec === 'number') {
87
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
88
+ throw new Error(`${where}: compat.${field} 必须是数字`)
89
+ }
90
+ return
91
+ }
166
92
  if (spec === 'object') {
167
93
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
168
94
  throw new Error(`${where}: compat.${field} 必须是对象`)
@@ -178,10 +104,9 @@ function checkValue(field: string, spec: CompatValue, value: unknown, where: str
178
104
 
179
105
  /**
180
106
  * 校验一份 compat 字典对指定协议合法(对齐官方门控语义 + schema 值约束):
181
- * - 未知键/withhold 字段拒绝(官方 0.1.2-alpha.1 写时拒绝,替代旧版静默丢弃);
107
+ * - 未知键/withhold 字段拒绝(官方写时拒绝,替代旧版静默丢弃);
182
108
  * - 值类型/枚举按官方 schema 校验;
183
- * - 无值键(null/undefined)拒绝(官方 assertOfferedCompatFields 同款:
184
- * "写了但没生效" 的表面状态不允许)。
109
+ * - 无值键(null/undefined)拒绝(官方 assertOfferedCompatFields 同款)。
185
110
  */
186
111
  export function validateCompat(
187
112
  api: string,
@@ -189,13 +114,13 @@ export function validateCompat(
189
114
  where: string,
190
115
  ): void {
191
116
  if (compat === undefined) return
192
- const gate = GATES_BY_PROTOCOL[api as ProtocolId]
117
+ const gate = active.gates[api]
193
118
  if (gate === undefined) {
194
119
  throw new Error(
195
- `${where}: 协议 ${JSON.stringify(api)} 无 compat 字段表(支持:${Object.keys(GATES_BY_PROTOCOL).join(', ')})`,
120
+ `${where}: 协议 ${JSON.stringify(api)} 无 compat 字段表(支持:${Object.keys(active.gates).join(', ')})`,
196
121
  )
197
122
  }
198
- const offered = compatFieldsOf(api as ProtocolId)
123
+ const offered = compatFieldsOf(api)
199
124
  for (const [key, value] of Object.entries(compat)) {
200
125
  const disposition = gate[key]
201
126
  if (disposition !== 'offer') {
@@ -206,13 +131,17 @@ export function validateCompat(
206
131
  )
207
132
  }
208
133
  throw new Error(
209
- `${where}: compat.${key} 不是 ${api} 协议的合法字段(可配置字段:${offered.join(', ')})`,
134
+ `${where}: compat.${key} 不是 ${api} 协议的合法字段(可配置字段:${offered.join(', ')};` +
135
+ `官方全部可配字段:${allOfferedFields().join(', ')})`,
210
136
  )
211
137
  }
212
138
  if (value === undefined || value === null) {
213
139
  throw new Error(`${where}: compat.${key} 未设置值;给出值或移除该键(留空不会生效)`)
214
140
  }
215
- checkValue(key, VALUE_SPECS[key] as CompatValue, value, where)
141
+ // 官方 schema 未给出取值约束的字段(未来新增而推导未覆盖)跳过值校验,
142
+ // 由官方调用链自行裁决——宁可放行也不误拒。
143
+ const spec = active.specs[key]
144
+ if (spec !== undefined) checkValue(key, spec, value, where)
216
145
  }
217
146
  }
218
147
 
package/src/config-api.ts CHANGED
@@ -11,10 +11,36 @@ import type { Context } from '@deepseek-ai/cordis'
11
11
  import type {} from '@deepseek-ai/dsh-host-webserver'
12
12
 
13
13
  import { builtinModelIds } from './catalog/builtin.ts'
14
+ import { compatFieldSpec, compatFieldsOf, compatTableInfo } from './compat.ts'
15
+ import { PROTOCOL_IDS } from './config.ts'
14
16
  import type { LlmPiRuntime } from './service.ts'
15
17
 
16
18
  const ROUTE_CATALOG = '/dsh-plus/llm-pi/catalog'
17
19
 
20
+ /**
21
+ * 服务端推导的 compat 字段表(协议 → 字段 → 取值约束)。浏览器半读不到官方包,
22
+ * 由本端点下发,UI 渲染与服务端校验同源(不再各存一份手抄镜像表)。
23
+ */
24
+ function compatTablePayload(): {
25
+ fields: Record<string, Record<string, unknown>>
26
+ source: string
27
+ problem?: string
28
+ } {
29
+ const fields: Record<string, Record<string, unknown>> = {}
30
+ for (const api of PROTOCOL_IDS) {
31
+ const perField: Record<string, unknown> = {}
32
+ for (const field of compatFieldsOf(api)) {
33
+ const spec = compatFieldSpec(api, field)
34
+ if (spec !== undefined) perField[field] = spec
35
+ }
36
+ fields[api] = perField
37
+ }
38
+ const info = compatTableInfo()
39
+ return info.problem === undefined
40
+ ? { fields, source: info.source }
41
+ : { fields, source: info.source, problem: info.problem }
42
+ }
43
+
18
44
  function sendJson(res: ServerResponse, status: number, body: unknown): void {
19
45
  res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })
20
46
  res.end(JSON.stringify(body))
@@ -32,12 +58,14 @@ function handleCatalog(runtime: LlmPiRuntime, req: IncomingMessage, res: ServerR
32
58
  const url = new URL(req.url ?? '', 'http://localhost')
33
59
  const provider = url.searchParams.get('provider') ?? ''
34
60
  const source = url.searchParams.get('source') ?? 'builtin'
61
+ const compat = compatTablePayload()
35
62
  if (source === 'models-dev') {
36
63
  sendJson(res, 200, {
37
64
  providers: runtime.modelsDev.providerIds(),
38
65
  models: provider.length > 0 ? runtime.modelsDev.modelIds(provider) : [],
39
66
  status: runtime.modelsDev.status(),
40
67
  kitSource,
68
+ compat,
41
69
  })
42
70
  return
43
71
  }
@@ -45,6 +73,7 @@ function handleCatalog(runtime: LlmPiRuntime, req: IncomingMessage, res: ServerR
45
73
  providers: runtime.kit.getBuiltinProviders(),
46
74
  models: provider.length > 0 ? builtinModelIds(runtime.kit, provider) : [],
47
75
  kitSource,
76
+ compat,
48
77
  })
49
78
  }
50
79
 
package/src/config.ts CHANGED
@@ -90,6 +90,13 @@ export interface ProviderProfileConfig {
90
90
  extends?: string
91
91
  displayName?: string
92
92
  api?: ProtocolId
93
+ /**
94
+ * 端点;缺省继承 extends 源 provider 的端点。
95
+ *
96
+ * 不并 `| undefined`:schemastery 的输入契约用 null 表示「显式置空」,省略即未设置;
97
+ * 消费方按 `baseURL ?? 默认值` 处理。并上 undefined 会与 ObjectS 的 `string | null`
98
+ * 冲突(exactOptionalPropertyTypes 下二者不可互赋)。
99
+ */
93
100
  baseURL?: string
94
101
  apiKeyEnv?: string
95
102
  headers?: Record<string, string>
@@ -21,9 +21,9 @@
21
21
  * (auth-inline.ts,recordKeyFor 自包根导出同源注入)——形状自检兜底。
22
22
  * @module llm-pi/resolve-dsh
23
23
  */
24
- import { existsSync, realpathSync } from 'node:fs'
24
+ import { existsSync, readFileSync, realpathSync } from 'node:fs'
25
25
  import { dirname, join } from 'node:path'
26
- import { pathToFileURL } from 'node:url'
26
+ import { fileURLToPath, pathToFileURL } from 'node:url'
27
27
  import type { Context } from '@deepseek-ai/cordis'
28
28
  import type { getOrCreateAnonymousUserId as getAnonIdType } from '@deepseek-ai/dsh-anonymous-user-id'
29
29
  import * as vendoredAnonId from '@deepseek-ai/dsh-anonymous-user-id'
@@ -53,6 +53,8 @@ import {
53
53
  authContextFrom as inlineAuthContextFrom,
54
54
  credentialStoreFrom as inlineCredentialStoreFrom,
55
55
  } from './auth-inline.ts'
56
+ import { installCompatTable } from './compat.ts'
57
+ import { deriveGates, deriveSpecs, FALLBACK_TABLE } from './compat-gates.ts'
56
58
  import type { ProtocolId } from './config.ts'
57
59
  import { type ResolverDeps, resolveProfilesFallback } from './profiles.ts'
58
60
 
@@ -295,6 +297,39 @@ function treeResolverDeps(
295
297
  }
296
298
  }
297
299
 
300
+ /**
301
+ * 从官方安装副本推导 compat 门控表(bundle 文本给分型、Config schema 给取值约束),
302
+ * 并安装为插件生效表。任一环节失败回退 FALLBACK_TABLE 并给诊断——绝不因推导失败
303
+ * 弄挂插件启动(宁可放宽校验,也不误拒官方可配字段)。
304
+ */
305
+ function installOfficialCompatTable(
306
+ bundlePath: string,
307
+ module: Record<string, unknown>,
308
+ diagnostics: string[],
309
+ ): void {
310
+ const label = 'compat 门控表'
311
+ try {
312
+ const bundle = readFileSync(bundlePath, 'utf8')
313
+ const gates = deriveGates(bundle)
314
+ if (gates === undefined) {
315
+ // 官方改打包形态导致 COMPAT_GATES 不可解析:用冻结快照并告警。
316
+ diagnostics.push(
317
+ `${label}未能从官方 bundle 解析 COMPAT_GATES(${bundlePath});` +
318
+ `使用内置快照(官方新增字段可能被误拒,请检查 dsh-llm-pi-ai 打包形态)`,
319
+ )
320
+ installCompatTable(FALLBACK_TABLE)
321
+ return
322
+ }
323
+ // 取值约束:推导值优先,内置快照补缺(官方 schema 未覆盖的字段保持可写)。
324
+ const specs = { ...FALLBACK_TABLE.specs, ...deriveSpecs(module) }
325
+ installCompatTable({ gates, specs, source: 'official' })
326
+ } catch (error) {
327
+ const reason = error instanceof Error ? error.message : String(error)
328
+ diagnostics.push(`${label}推导失败(${reason});使用内置快照`)
329
+ installCompatTable(FALLBACK_TABLE)
330
+ }
331
+ }
332
+
298
333
  function kitFromTree(
299
334
  mods: TreeModules,
300
335
  auth: Record<string, unknown> | undefined,
@@ -350,10 +385,19 @@ function loadVendoredDeepseek(): DeepSeekKit | undefined {
350
385
  return checkDeepseekShape(kit).length === 0 ? kit : undefined
351
386
  }
352
387
 
388
+ /**
389
+ * vendored `dsh-llm-pi-ai` 的 bundle 绝对路径(compat 门控推导用)。
390
+ * 经包名解析拿到入口文件——npm/pnpm 各布局都适用,不硬编码 node_modules 结构。
391
+ */
392
+ function vendoredAdapterBundlePath(): string {
393
+ return fileURLToPath(import.meta.resolve('@deepseek-ai/dsh-llm-pi-ai'))
394
+ }
395
+
353
396
  /**
354
397
  * vendored 兜底副本套件(导出供单测直接使用,免走 dsh 树解析)。
355
398
  * npm 发布形态不携带 src/、lib 仅 index.js/invariant.js——resolveProfiles 与
356
- * auth 助手在此无条件走插件等价实现(内联/门控表对齐,见文件头说明)。
399
+ * auth 助手在此无条件走插件等价实现(内联,见文件头说明);compat 门控表则
400
+ * 仍从 vendored 副本的 bundle + Config schema 现场推导(与 dsh 树同一推导链)。
357
401
  */
358
402
  export function loadVendoredKit(): DshKit {
359
403
  const deepseek = loadVendoredDeepseek()
@@ -362,6 +406,12 @@ export function loadVendoredKit(): DshKit {
362
406
  'openai-responses': vendoredResponses,
363
407
  'anthropic-messages': vendoredAnthropic,
364
408
  }
409
+ // 与 dsh 树路径同源推导:vendored 副本同样是官方发布包(lib/index.js + Config)
410
+ installOfficialCompatTable(
411
+ vendoredAdapterBundlePath(),
412
+ vendoredPiAiAdapter as unknown as Record<string, unknown>,
413
+ [],
414
+ )
365
415
  const kit: DshKit = {
366
416
  source: 'vendored',
367
417
  PiAiAdapter: vendoredPiAiAdapter.PiAiAdapter,
@@ -426,6 +476,7 @@ export async function resolveDshKit(): Promise<{
426
476
  ])
427
477
  const kit = kitFromTree(mods, auth, config)
428
478
  assertKitShape(kit, 'dsh-tree')
479
+ installOfficialCompatTable(join(treePkgDir, 'lib', 'index.js'), mods.piAiAdapter, diagnostics)
429
480
  if (auth === undefined) {
430
481
  diagnostics.push(
431
482
  'dsh 树不含 dsh-llm-pi-ai/src(npm 发布形态);认证助手使用插件内联等价实现',