@lqc123qwe/car-runtime 1.13.0 → 1.14.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/README.md +3 -2
- package/dist/SHA256SUMS +1 -1
- package/dist/car-runtime-1.14.0.tgz +0 -0
- package/dist/car-runtime-1.14.0.tgz.sig.bundle +1 -0
- package/dist/cli.js +48 -16
- package/dist/load/config.d.ts +45 -0
- package/dist/load/config.js +84 -1
- package/dist/load/pool.d.ts +97 -0
- package/dist/load/pool.js +261 -0
- package/dist/loop/stop.js +9 -1
- package/dist/release-audit.jsonl +4 -4
- package/dist/runtime-core/chatStep.js +2 -0
- package/dist/runtime-core/credentials.d.ts +2 -0
- package/dist/runtime-core/credentials.js +2 -1
- package/dist/runtime-core/llm.d.ts +6 -0
- package/dist/runtime-core/llm.js +54 -3
- package/dist/runtime-core/types.d.ts +15 -0
- package/dist/sbom.json +22 -12
- package/dist/seam.d.ts +7 -0
- package/dist/seam.js +8 -0
- package/dist/session/compact.d.ts +57 -0
- package/dist/session/compact.js +128 -0
- package/dist/session/log.d.ts +5 -1
- package/dist/session/log.js +22 -2
- package/package.json +1 -1
- package/src/cli.ts +48 -17
- package/src/load/config.ts +135 -1
- package/src/load/pool.ts +339 -0
- package/src/loop/stop.ts +8 -2
- package/src/runtime-core/chatStep.ts +2 -0
- package/src/runtime-core/credentials.ts +4 -1
- package/src/runtime-core/llm.ts +59 -3
- package/src/runtime-core/types.ts +11 -0
- package/src/seam.ts +8 -0
- package/src/session/compact.ts +168 -0
- package/src/session/log.ts +19 -3
- package/dist/car-runtime-1.13.0.tgz +0 -0
- package/dist/car-runtime-1.13.0.tgz.sig.bundle +0 -1
package/README.md
CHANGED
|
@@ -278,7 +278,7 @@ node --experimental-transform-types --test test/*.spec.ts
|
|
|
278
278
|
|
|
279
279
|
## 1.12 状态(seam MCP/PTC 真桥导出面 + jiti epoch 面确认)
|
|
280
280
|
|
|
281
|
-
| `src/seam.ts` | 1.12-S1(D-20/D-35/D-36):**MCP/PTC 真桥出面**——运行时符号 21→27(+McpGateway
|
|
281
|
+
| `src/seam.ts` | 1.12-S1(D-20/D-35/D-36):**MCP/PTC 真桥出面**——运行时符号 21→27(+McpGateway、createStdioClientTransport、createHttpMcpTransport、resolveMcpHeaders、makePtcToolDefinition、renderSdkFromRegistry)+ 8 类型(ClientTransport、McpServerConfig、McpToolDefinition、StdioServerSpec、HttpMcpServerSpec、McpClientSpanHandle、ToolBridge、PtcRelayHandle);**负面清单保持内部**——runCode/worker-entry/budget/erasable/JSON-RPC 编码层 = PTC 红线单点(erasable-only/预算/授权门仅经 makePtcToolDefinition 行为面可达);D-20 预留的最后一个 1.x 加法前置出清,1.x 主线转入候选池维持项 + 平台 P1 需求拉动 | 平台 MCP/PTC 真桥(C402001 守卫翻转前置,CLI D-18/D-21/D-29 蓝本)+ 平台 D-23 reload(jiti epoch 面自 1.10 可达,D-37 零加法确认) |
|
|
282
282
|
| `test/s47`(8) | 蓝本回归**全经 seam 公共 API 驱动 + 禁深路径 import**(窄面完备性反证):MCP 注册/T-22 缺省 write/traceparent 三态(无 tp 字节面不变、有 tp 双载体同值、BD-02 崩溃隔离)/跨源同名冲突显式;凭据红线 env+headers 双入口 + `${ENV}` fail-visible(明文检测 = 值前缀形态);PTC worker 真执行 + ptcRelay 桥 + 审计结构化;传输工厂构造面(stdio 即刻 spawn/http TLS 门含 loopback 豁免);reload epoch 击穿确认 | 1.12-GO 第 1/5 项承载;平台接线契约对拍基准 |
|
|
283
283
|
|
|
284
284
|
- **1.12 工程收口(2026-10-07,`4fee6da`)**:全量 **363(360 PASS + 3 能力门控 skip / 0 fail)** = 基线 355 + s47 8;tsc strict 0 + secrets-scan 0(commit 后复跑)+ contract-check 4/4(golden 不动);存量 spec 零改动通过 = 兼容验收线。规划:`car-docs/10-内核v1/1.12-迭代规划.md`(D-35~D-38;类型计数勘误 9→8 随文档登记)。
|
|
@@ -286,7 +286,8 @@ node --experimental-transform-types --test test/*.spec.ts
|
|
|
286
286
|
|
|
287
287
|
## 状态
|
|
288
288
|
|
|
289
|
-
- **版本主线**:S1-S4 48/48 | M2 89/89(v0.2.0)| M3 127/127(v0.3.0)| M4 139/139(**1.0.0-rc.1 已发布**)| M5 156/156(DEC-4 预埋项转正)| M6 179/179(2026-09-15 收口)| M7 全量 195(192 PASS + 3 能力门控 skip,2026-09-19 收口)| **M5-S28 装载签名门 全量 204(201 PASS + 3 skip,2026-09-26)** | **M8 运行时底座 全量 231(228 PASS + 3 skip,2026-09-26 收口,§3.2 八模块全部收口)** | **1.1 签名收尾最小集 全量 249(246 PASS + 3 skip,2026-09-27 收口,1.1.0 已发布,enforce 翻转准备就绪)** | **1.2 会话/遥测小项 全量 263(260 PASS + 3 skip,2026-09-27 收口,1.2.0 已发布)** | **1.3 发布资产自动化 全量 270(267 PASS + 3 skip,2026-09-27 收口,1.3.0 已发布,全链零人工拖拽兑现)** | **1.4 真实模型接入 全量 282(279 PASS + 3 skip,2026-09-27 收口,1.4.0 已发布)** | **1.5 provider 扩展+远程 MCP+配置完整形态 全量 300(297 PASS + 3 skip,2026-09-28 收口,1.5.0 已发布)** | **1.6 宿主工具面+投影收敛+jiti 载体 全量 318(315 PASS + 3 skip,2026-09-28 收口,1.6.0 已发布)** | **1.7 OTLP span 语义收口+metrics 时序修正+mcp-serve span 面 全量 328(325 PASS + 3 skip,2026-09-28 收口,1.7.0 已发布)** | **1.8 W3C traceparent 跨进程传播+LLM 出站通道修复 全量 335(332 PASS + 3 skip,2026-09-29 收口,1.8.0 已发布)** | **1.9 远程 MCP traceparent 传播(HTTP 头通道) 全量 339(336 PASS + 3 skip,2026-09-29 收口,1.9.0 已发布)** | **1.10 KernelSeam 公开导出面+库构建产物 全量 341(338 PASS + 3 skip,2026-09-29 收口,1.10.0 已发布)** | **1.11 seam authz 导出+usage 事件计量采集 全量 355(352 PASS + 3 skip,2026-10-07 收口,1.11.0 已发布)** | **1.12 seam MCP/PTC 真桥导出面+jiti epoch 面确认 全量 363(360 PASS + 3 skip,2026-10-07 收口,1.12.0 已发布)** | **1.13 桌面 S3 治理面(K-FS-1/2/3+K-SC-1 authorizeAll/decideByRule/fs-read·fs-delete 能力 + seam 出面 redact/loadSessionLog/exportForensicsBundle;恢复面 K-S3-2 零登记确认——fromEvents 自 M5 在产) 全量 374(371 PASS + 3 skip,2026-10-11 收口,1.13.0
|
|
289
|
+
- **版本主线**:S1-S4 48/48 | M2 89/89(v0.2.0)| M3 127/127(v0.3.0)| M4 139/139(**1.0.0-rc.1 已发布**)| M5 156/156(DEC-4 预埋项转正)| M6 179/179(2026-09-15 收口)| M7 全量 195(192 PASS + 3 能力门控 skip,2026-09-19 收口)| **M5-S28 装载签名门 全量 204(201 PASS + 3 skip,2026-09-26)** | **M8 运行时底座 全量 231(228 PASS + 3 skip,2026-09-26 收口,§3.2 八模块全部收口)** | **1.1 签名收尾最小集 全量 249(246 PASS + 3 skip,2026-09-27 收口,1.1.0 已发布,enforce 翻转准备就绪)** | **1.2 会话/遥测小项 全量 263(260 PASS + 3 skip,2026-09-27 收口,1.2.0 已发布)** | **1.3 发布资产自动化 全量 270(267 PASS + 3 skip,2026-09-27 收口,1.3.0 已发布,全链零人工拖拽兑现)** | **1.4 真实模型接入 全量 282(279 PASS + 3 skip,2026-09-27 收口,1.4.0 已发布)** | **1.5 provider 扩展+远程 MCP+配置完整形态 全量 300(297 PASS + 3 skip,2026-09-28 收口,1.5.0 已发布)** | **1.6 宿主工具面+投影收敛+jiti 载体 全量 318(315 PASS + 3 skip,2026-09-28 收口,1.6.0 已发布)** | **1.7 OTLP span 语义收口+metrics 时序修正+mcp-serve span 面 全量 328(325 PASS + 3 skip,2026-09-28 收口,1.7.0 已发布)** | **1.8 W3C traceparent 跨进程传播+LLM 出站通道修复 全量 335(332 PASS + 3 skip,2026-09-29 收口,1.8.0 已发布)** | **1.9 远程 MCP traceparent 传播(HTTP 头通道) 全量 339(336 PASS + 3 skip,2026-09-29 收口,1.9.0 已发布)** | **1.10 KernelSeam 公开导出面+库构建产物 全量 341(338 PASS + 3 skip,2026-09-29 收口,1.10.0 已发布)** | **1.11 seam authz 导出+usage 事件计量采集 全量 355(352 PASS + 3 skip,2026-10-07 收口,1.11.0 已发布)** | **1.12 seam MCP/PTC 真桥导出面+jiti epoch 面确认 全量 363(360 PASS + 3 skip,2026-10-07 收口,1.12.0 已发布)** | **1.13 桌面 S3 治理面(K-FS-1/2/3+K-SC-1 authorizeAll/decideByRule/fs-read·fs-delete 能力 + seam 出面 redact/loadSessionLog/exportForensicsBundle;恢复面 K-S3-2 零登记确认——fromEvents 自 M5 在产) 全量 374(371 PASS + 3 skip,2026-10-11 收口,1.13.0 已发布)**
|
|
290
|
+
- **1.13.0 正式发布(2026-10-11,桌面 S3-S1)**:npm `@lqc123qwe/car-runtime@1.13.0` 在架——**dist-tags latest → 1.13.0 + provenance 在案**。Release `1.13.0`(id 409512865,target_commitish=完整 SHA;main 推送经 Git Data API 逐字节复刻——blob 9/9 + tree `eea7249` + commit `ab59468` 全 SHA 对拍零分叉;**发布工程事件两项**:①GitHub commit API 保留 author/committer 时区偏移(+08:00 原样入对象)但**消息不自动补尾 `\n`**——payload 须带原文尾换行,否则 SHA 分叉(39477b4 教训,本地 `$()` 吞尾换行加剧排查成本);②registry 元数据 CDN 滞后使本地 `npm install` notarget——cache-buster 复查口径 + `--prefer-online` 安装)→ **run 38122530955 全步 success** → registry tarball dist/seam.js 32 符号文本核验 ✓ + **桌面侧消费验证:car-desktop 装 1.13.0 后 29/29 测试全绿 + SMOKE PASS(seamVersion 1.13.0 · symbolCount 32)**。
|
|
290
291
|
- **1.0.0 正式发布(2026-09-26,S29/S30)**:npm `@lqc123qwe/car-runtime@1.0.0` 在架——**dist-tag latest → 1.0.0,rc 保留指向 1.0.0-rc.1**(§1.1 口径);SLSA provenance(Actions OIDC,透明日志在案);GitHub Release v1.0.0 六资产(tgz/bundle/SHA256SUMS/SBOM/审计/公钥);**post-publish 全链 13 PASS / 0 DRY-RUN / 0 FAIL** + 外部验证者等价检查(Release 实下载件 SHA256 MATCH + cosign Verified OK)。**宽限期时钟:2026-09-26 起算,2026-12-25 到期 verifier 默认值 warn→enforce 翻转(计划内变更)**。终验报告:`car-docs/10-内核v1/M5-S30-1.0收口终验报告.md`。发布工程事件:j16 首跑 E403(npm 2FA 策略 vs granular token 无 bypass——清单预设回退路径兑现)→ bypass token re-run 绿;G-09 期望 tag 动态化(`4c84b17`)在真实发布兑现价值。
|
|
291
292
|
- **1.0.0-rc.1 已发布(2026-09-09)**:npm `@lqc123qwe/car-runtime@1.0.0-rc.1`(rc + latest 双 tag,SLSA provenance 在案)+ GitHub Release 6 制品;发布预演 **13 PASS / 0 DRY-RUN / 0 FAIL**(c762b72)。S22 GO 清单已全绿收口。
|
|
292
293
|
- **轨道 A 三项全部完成**:E-1 WSL2 逃逸矩阵 **20/20 PASS 零逃逸**(2026-09-07,G-07 转 PASS)| E-3 远端发布通道 **13/13 全门禁 PASS**(2026-09-09,G-08/09/10 转 PASS)| E-2 Windows koffi FFI **四判据双环境全 PASS**(S24/S25/S26,2026-09-09~10;本机非提权 + windows-latest 提权 runner 双绿,CI 门禁已接入)。
|
package/dist/SHA256SUMS
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
cb43ec0b9b1f7ad4365dc1a30dbdc6b41ec65a5b23326c97f42b2d486b20167d car-runtime-1.14.0.tgz
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"base64Signature":"MEQCIG6L65Cw/EJuDjO6HFYCTaYCu8OOGPkVkVDoPVft3cWUAiAXMhPi5i0bbyhPkqysB2s1G4D1ElUz1xWDe4doxmWOkg==","cert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUc2RENDQm02Z0F3SUJBZ0lVSGEya1N2UWhCTHFtcGNrcWg3K0lSYUhnRHJBd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpZeE1ERTRNRGd4T1RReFdoY05Nall4TURFNE1EZ3lPVFF4V2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVsa29IQkJmWGsyWXFOdEwzQkN4ZlZRaVkweFcxMWh6aGlwTFAKaXY3UWliKzFpTjBnWkR3amJVZFVBNC90Tm1jSXpya1JIQmRiaE9DL2VFR0pJMXlCcWFPQ0JZMHdnZ1dKTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWZCZ05WSFNNRUdEQVdnQlRmCjArblBWaVFSbHZtbzJPa29WYUxHTGhoa1B6QmlCZ05WSFJFQkFmOEVXREJXaGxSb2RIUndjem92TDJkcGRHaDEKWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHpMM0psYkdWaApjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakUwTGpBd09RWUtLd1lCQkFHRHZ6QUJBUVFyYUhSMGNITTZMeTkwCmIydGxiaTVoWTNScGIyNXpMbWRwZEdoMVluVnpaWEpqYjI1MFpXNTBMbU52YlRBVkJnb3JCZ0VFQVlPL01BRUMKQkFkeVpXeGxZWE5sTURZR0Npc0dBUVFCZzc4d0FRTUVLRGt5WVRNMU9UQTBZelV4TnpsaE9UbGpaR0kyT1dRNApNalUwTWprMU0yWTRNbVppT1RJd1ltRXdGUVlLS3dZQkJBR0R2ekFCQkFRSGNtVnNaV0Z6WlRBZ0Jnb3JCZ0VFCkFZTy9NQUVGQkJKc2NXTTJNalF2WTJGeUxYSjFiblJwYldVd0hnWUtLd1lCQkFHRHZ6QUJCZ1FRY21WbWN5OTAKWVdkekx6RXVNVFF1TURBN0Jnb3JCZ0VFQVlPL01BRUlCQzBNSzJoMGRIQnpPaTh2ZEc5clpXNHVZV04wYVc5dQpjeTVuYVhSb2RXSjFjMlZ5WTI5dWRHVnVkQzVqYjIwd1pBWUtLd1lCQkFHRHZ6QUJDUVJXREZSb2RIUndjem92CkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMEwyTmhjaTF5ZFc1MGFXMWxMeTVuYVhSb2RXSXZkMjl5YTJac2IzZHoKTDNKbGJHVmhjMlV1ZVcxc1FISmxabk12ZEdGbmN5OHhMakUwTGpBd09BWUtLd1lCQkFHRHZ6QUJDZ1FxRENnNQpNbUV6TlRrd05HTTFNVGM1WVRrNVkyUmlOamxrT0RJMU5ESTVOVE5tT0RKbVlqa3lNR0poTUIwR0Npc0dBUVFCCmc3OHdBUXNFRHd3TloybDBhSFZpTFdodmMzUmxaREExQmdvckJnRUVBWU8vTUFFTUJDY01KV2gwZEhCek9pOHYKWjJsMGFIVmlMbU52YlM5c2NXTTJNalF2WTJGeUxYSjFiblJwYldVd09BWUtLd1lCQkFHRHZ6QUJEUVFxRENnNQpNbUV6TlRrd05HTTFNVGM1WVRrNVkyUmlOamxrT0RJMU5ESTVOVE5tT0RKbVlqa3lNR0poTUNBR0Npc0dBUVFCCmc3OHdBUTRFRWd3UWNtVm1jeTkwWVdkekx6RXVNVFF1TURBYUJnb3JCZ0VFQVlPL01BRVBCQXdNQ2pFek5qRXgKT1RJM01UUXdLUVlLS3dZQkJBR0R2ekFCRUFRYkRCbG9kSFJ3Y3pvdkwyZHBkR2gxWWk1amIyMHZiSEZqTmpJMApNQmdHQ2lzR0FRUUJnNzh3QVJFRUNnd0lOamt5TXpRMU5qUXdaQVlLS3dZQkJBR0R2ekFCRWdSV0RGUm9kSFJ3CmN6b3ZMMmRwZEdoMVlpNWpiMjB2YkhGak5qSTBMMk5oY2kxeWRXNTBhVzFsTHk1bmFYUm9kV0l2ZDI5eWEyWnMKYjNkekwzSmxiR1ZoYzJVdWVXMXNRSEpsWm5NdmRHRm5jeTh4TGpFMExqQXdPQVlLS3dZQkJBR0R2ekFCRXdRcQpEQ2c1TW1Fek5Ua3dOR00xTVRjNVlUazVZMlJpTmpsa09ESTFOREk1TlRObU9ESm1Zamt5TUdKaE1CY0dDaXNHCkFRUUJnNzh3QVJRRUNRd0hjbVZzWldGelpUQlpCZ29yQmdFRUFZTy9NQUVWQkVzTVNXaDBkSEJ6T2k4dloybDAKYUhWaUxtTnZiUzlzY1dNMk1qUXZZMkZ5TFhKMWJuUnBiV1V2WVdOMGFXOXVjeTl5ZFc1ekx6TTVNVEV4TURBeApOemN6TDJGMGRHVnRjSFJ6THpFd0ZnWUtLd1lCQkFHRHZ6QUJGZ1FJREFad2RXSnNhV013VUFZS0t3WUJCQUdECnZ6QUJHQVJDREVCeVpYQnZPbXh4WXpZeU5FQTJPVEl6TkRVMk5DOWpZWEl0Y25WdWRHbHRaVUF4TXpZeE1Ua3kKTnpFME9uSmxaanB5WldaekwzUmhaM012TVM0eE5DNHdNSUdMQmdvckJnRUVBZFo1QWdRQ0JIMEVld0I1QUhjQQozVDB3YXNiSEVUSmpHUjRjbVdjM0FxSktYcmplUEszL2g0cHlnQzhwN280QUFBR2hUaGhaYmdBQUJBTUFTREJHCkFpRUFxZkN4Nnh6R3V0T1NveVRLSDdsVFN4dFRMMHBmNGtQR1REMlliWEtISmxJQ0lRQ01CZ1ZaR0dZR3pPa0IKSk1VL2JENWxMMXA4S3lWYkR0ZlNBRWl0NDN0cFpqQUtCZ2dxaGtqT1BRUURBd05vQURCbEFqQkx5a0dvTHp3dwpVZXF5SmZad1IrOTR6bWsxRzZRR0swVWtpSUtsUEM0eWxtNUlueXhBT1pwaWpIZ25aUHl2VTZNQ01RRHI4Mm10CmpKemEwRXd2NDFubkxpUDBEVy9qYW1MOXF3aC93ak54VmxrclVGakQzcGcyNk5ybThCVVhQZzZqTE1JPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==","rekorBundle":{"SignedEntryTimestamp":"MEUCIG4EQ9bsQTzIrNCFQRZJmPPk2dKTOUsPeM6kNbcaPbYCAiEApBqdeQqsIRei7A1LULq4t5e1lBrMT5oBf+nC0GoPyzM=","Payload":{"body":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJjYjQzZWMwYjliMWY3YWQ0MzY1ZGMxYTMwZGJkYzZiNDFlYzY1YTViMjMzMjZjOTdmNDJiMmQ0ODZiMjAxNjdkIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJRzZMNjVDdy9FSnVEak82SEZZQ1RhWUN1OE9PR1BrVmtWRG9QVmZ0M2NXVUFpQVhNaFBpNWkwYmJ5aFBrcXlzQjJzMUc0RDFFbFV6MXhXRGU0ZG94bVdPa2c9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVYzJSRU5EUW0wMlowRjNTVUpCWjBsVlNHRXlhMU4yVVdoQ1RIRnRjR05yY1dnM0swbFNZVWhuUkhKQmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFplRTFFUlRSTlJHZDRUMVJSZUZkb1kwNU5hbGw0VFVSRk5FMUVaM2xQVkZGNFYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZzYTI5SVFrSm1XR3N5V1hGT2RFd3pRa040WmxaUmFWa3dlRmN4TVdoNmFHbHdURkFLYVhZM1VXbGlLekZwVGpCbldrUjNhbUpWWkZWQk5DOTBUbTFqU1hweWExSklRbVJpYUU5REwyVkZSMHBKTVhsQ2NXRlBRMEpaTUhkbloxZEtUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXWkNaMDVXU0ZOTlJVZEVRVmRuUWxSbUNqQXJibEJXYVZGU2JIWnRiekpQYTI5V1lVeEhUR2hvYTFCNlFtbENaMDVXU0ZKRlFrRm1PRVZYUkVKWGFHeFNiMlJJVW5kamVtOTJUREprY0dSSGFERUtXV2sxYW1JeU1IWmlTRVpxVG1wSk1Fd3lUbWhqYVRGNVpGYzFNR0ZYTVd4TWVUVnVZVmhTYjJSWFNYWmtNamw1WVRKYWMySXpaSHBNTTBwc1lrZFdhQXBqTWxWMVpWY3hjMUZJU214YWJrMTJaRWRHYm1ONU9IaE1ha1V3VEdwQmQwOVJXVXRMZDFsQ1FrRkhSSFo2UVVKQlVWRnlZVWhTTUdOSVRUWk1lVGt3Q21JeWRHeGlhVFZvV1ROU2NHSXlOWHBNYldSd1pFZG9NVmx1Vm5wYVdFcHFZakkxTUZwWE5UQk1iVTUyWWxSQlZrSm5iM0pDWjBWRlFWbFBMMDFCUlVNS1FrRmtlVnBYZUd4WldFNXNUVVJaUjBOcGMwZEJVVkZDWnpjNGQwRlJUVVZMUkd0NVdWUk5NVTlVUVRCWmVsVjRUbnBzYUU5VWJHcGFSMGt5VDFkUk5BcE5hbFV3VFdwck1VMHlXVFJOYlZwcFQxUkpkMWx0UlhkR1VWbExTM2RaUWtKQlIwUjJla0ZDUWtGUlNHTnRWbk5hVjBaNldsUkJaMEpuYjNKQ1owVkZDa0ZaVHk5TlFVVkdRa0pLYzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMGhuV1V0TGQxbENRa0ZIUkhaNlFVSkNaMUZSWTIxV2JXTjVPVEFLV1Zka2VreDZSWFZOVkZGMVRVUkJOMEpuYjNKQ1owVkZRVmxQTDAxQlJVbENRekJOU3pKb01HUklRbnBQYVRoMlpFYzVjbHBYTkhWWlYwNHdZVmM1ZFFwamVUVnVZVmhTYjJSWFNqRmpNbFo1V1RJNWRXUkhWblZrUXpWcVlqSXdkMXBCV1V0TGQxbENRa0ZIUkhaNlFVSkRVVkpYUkVaU2IyUklVbmRqZW05MkNrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUV3eVRtaGphVEY1WkZjMU1HRlhNV3hNZVRWdVlWaFNiMlJYU1haa01qbDVZVEphYzJJelpIb0tURE5LYkdKSFZtaGpNbFYxWlZjeGMxRklTbXhhYmsxMlpFZEdibU41T0hoTWFrVXdUR3BCZDA5QldVdExkMWxDUWtGSFJIWjZRVUpEWjFGeFJFTm5OUXBOYlVWNlRsUnJkMDVIVFRGTlZHTTFXVlJyTlZreVVtbE9hbXhyVDBSSk1VNUVTVFZPVkU1dFQwUktiVmxxYTNsTlIwcG9UVUl3UjBOcGMwZEJVVkZDQ21jM09IZEJVWE5GUkhkM1Rsb3liREJoU0ZacFRGZG9kbU16VW14YVJFRXhRbWR2Y2tKblJVVkJXVTh2VFVGRlRVSkRZMDFLVjJnd1pFaENlazlwT0hZS1dqSnNNR0ZJVm1sTWJVNTJZbE01YzJOWFRUSk5hbEYyV1RKR2VVeFlTakZpYmxKd1lsZFZkMDlCV1V0TGQxbENRa0ZIUkhaNlFVSkVVVkZ4UkVObk5RcE5iVVY2VGxScmQwNUhUVEZOVkdNMVdWUnJOVmt5VW1sT2FteHJUMFJKTVU1RVNUVk9WRTV0VDBSS2JWbHFhM2xOUjBwb1RVTkJSME5wYzBkQlVWRkNDbWMzT0hkQlVUUkZSV2QzVVdOdFZtMWplVGt3V1Zka2VreDZSWFZOVkZGMVRVUkJZVUpuYjNKQ1owVkZRVmxQTDAxQlJWQkNRWGROUTJwRmVrNXFSWGdLVDFSSk0wMVVVWGRMVVZsTFMzZFpRa0pCUjBSMmVrRkNSVUZSWWtSQ2JHOWtTRkozWTNwdmRrd3laSEJrUjJneFdXazFhbUl5TUhaaVNFWnFUbXBKTUFwTlFtZEhRMmx6UjBGUlVVSm5OemgzUVZKRlJVTm5kMGxPYW10NVRYcFJNVTVxVVhkYVFWbExTM2RaUWtKQlIwUjJla0ZDUldkU1YwUkdVbTlrU0ZKM0NtTjZiM1pNTW1Sd1pFZG9NVmxwTldwaU1qQjJZa2hHYWs1cVNUQk1NazVvWTJreGVXUlhOVEJoVnpGc1RIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTUtZak5rZWt3elNteGlSMVpvWXpKVmRXVlhNWE5SU0Vwc1dtNU5kbVJIUm01amVUaDRUR3BGTUV4cVFYZFBRVmxMUzNkWlFrSkJSMFIyZWtGQ1JYZFJjUXBFUTJjMVRXMUZlazVVYTNkT1IwMHhUVlJqTlZsVWF6VlpNbEpwVG1wc2EwOUVTVEZPUkVrMVRsUk9iVTlFU20xWmFtdDVUVWRLYUUxQ1kwZERhWE5IQ2tGUlVVSm5OemgzUVZKUlJVTlJkMGhqYlZaeldsZEdlbHBVUWxwQ1oyOXlRbWRGUlVGWlR5OU5RVVZXUWtWelRWTlhhREJrU0VKNlQyazRkbG95YkRBS1lVaFdhVXh0VG5aaVV6bHpZMWROTWsxcVVYWlpNa1o1VEZoS01XSnVVbkJpVjFWMldWZE9NR0ZYT1hWamVUbDVaRmMxZWt4NlRUVk5WRVY0VFVSQmVBcE9lbU42VERKR01HUkhWblJqU0ZKNlRIcEZkMFpuV1V0TGQxbENRa0ZIUkhaNlFVSkdaMUZKUkVGYWQyUlhTbk5oVjAxM1ZVRlpTMHQzV1VKQ1FVZEVDblo2UVVKSFFWSkRSRVZDZVZwWVFuWlBiWGg0V1hwWmVVNUZRVEpQVkVsNlRrUlZNazVET1dwWldFbDBZMjVXZFdSSGJIUmFWVUY0VFhwWmVFMVVhM2tLVG5wRk1FOXVTbXhhYW5CNVdsZGFla3d6VW1oYU0wMTJUVk0wZUU1RE5IZE5TVWRNUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJTUVWbGQwSTFRVWhqUVFvelZEQjNZWE5pU0VWVVNtcEhValJqYlZkak0wRnhTa3RZY21wbFVFc3pMMmcwY0hsblF6aHdOMjgwUVVGQlIyaFVhR2hhWW1kQlFVSkJUVUZUUkVKSENrRnBSVUZ4WmtONE5uaDZSM1YwVDFOdmVWUkxTRGRzVkZONGRGUk1NSEJtTkd0UVIxUkVNbGxpV0V0SVNteEpRMGxSUTAxQ1oxWmFSMGRaUjNwUGEwSUtTazFWTDJKRU5XeE1NWEE0UzNsV1lrUjBabE5CUldsME5ETjBjRnBxUVV0Q1oyZHhhR3RxVDFCUlVVUkJkMDV2UVVSQ2JFRnFRa3g1YTBkdlRIcDNkd3BWWlhGNVNtWmFkMUlyT1RSNmJXc3hSelpSUjBzd1ZXdHBTVXRzVUVNMGVXeHROVWx1ZVhoQlQxcHdhV3BJWjI1YVVIbDJWVFpOUTAxUlJISTRNbTEwQ21wS2VtRXdSWGQyTkRGdWJreHBVREJFVnk5cVlXMU1PWEYzYUM5M2FrNTRWbXhyY2xWR2FrUXpjR2N5Tms1eWJUaENWVmhRWnpacVRFMUpQUW90TFMwdExVVk9SQ0JEUlZKVVNVWkpRMEZVUlMwdExTMHRDZz09In19fX0=","integratedTime":1792311581,"logIndex":3308757191,"logID":"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d"}}}
|
package/dist/cli.js
CHANGED
|
@@ -25,6 +25,7 @@ import { Context } from './kernel/context.js';
|
|
|
25
25
|
import { mountPlugin } from './load/loader.js';
|
|
26
26
|
import { verifyPluginFile } from './load/sigGate.js';
|
|
27
27
|
import { loadCarConfig, mergeSignatureGate, mergeLlmConfig, warmCarConfig } from './load/config.js';
|
|
28
|
+
import { assembleModelPool } from './load/pool.js';
|
|
28
29
|
import { generateSigningKeypair, signPluginFile, resolveTrustRoot } from './load/sign.js';
|
|
29
30
|
import { doctorCredentials, doctorConnectivity, doctorKeychain, doctorSignature, doctorModelReadiness } from './dx/doctor.js';
|
|
30
31
|
import { RuntimeCore, createOpenAICompatAdapter, createAnthropicAdapter } from './runtime-core/llm.js';
|
|
@@ -62,8 +63,9 @@ const HELP = `用法: car <command> [args]
|
|
|
62
63
|
CAR_UNSIGNED_ALLOW=1 显式声明豁免(warn 缺签路径计数 confirmed=yes,横幅保留)
|
|
63
64
|
配置文件通道(1.5 起双载体):cwd car.config.ts(export default {…};发现序 ts > json)或
|
|
64
65
|
car.config.json 或 --config <path>;键位 sandbox.unsigned.allow / sandbox.sig.enforce /
|
|
65
|
-
sandbox.sig.trustRoot / llm.* /
|
|
66
|
-
|
|
66
|
+
sandbox.sig.trustRoot / llm.* / llm.modelPool.*(多端点 fallback:endpoints id/baseUrl/model/
|
|
67
|
+
adapterId/keyEnv/priority/weight/maxTokens + probe + maxAttempts;路由决策落链)/ mcp.servers.*
|
|
68
|
+
(stdio: command/args/env;远程: url/headers,\${ENV_VAR} 引用注入凭据);优先级 flag > env > 配置 > 缺省;未知键 fail-visible`;
|
|
67
69
|
// 进程内热重载会话(ReloadManager 持有 epoch 与实例注册表;同进程连续 reload 语义完整)
|
|
68
70
|
let reloadMgrPromise;
|
|
69
71
|
function getReloadManager() {
|
|
@@ -204,27 +206,56 @@ async function main() {
|
|
|
204
206
|
...(mtFlag !== undefined ? { maxTokens: Number(mtFlag) } : {}),
|
|
205
207
|
});
|
|
206
208
|
const prompt = flagValue(rest, '--prompt');
|
|
207
|
-
if (
|
|
208
|
-
console.error('CAR-E-LLM-CONFIG: 真实模型路径需要
|
|
209
|
-
console.error(' 配置:car.config.ts(export default)或 car.config.json {"llm":{"baseUrl":"https://…","model":"…"}} / env CAR_LLM_BASE_URL+CAR_LLM_MODEL / flag --base-url --model --prompt');
|
|
210
|
-
console.error(' anthropic:llm.adapterId "anthropic"(或 --adapter-id anthropic)+ baseUrl https://api.anthropic.com;凭据 ANTHROPIC_API_KEY');
|
|
211
|
-
console.error(' 凭据:OPENAI_API_KEY / ANTHROPIC_API_KEY(env fallback 需 CAR_ALLOW_ENV_CREDENTIALS=1 或 llm.allowEnvFallback)或 keychain;诊断:car doctor');
|
|
209
|
+
if (prompt === undefined) {
|
|
210
|
+
console.error('CAR-E-LLM-CONFIG: 真实模型路径需要 --prompt——');
|
|
212
211
|
console.error(' (无模型演示流:--demo)');
|
|
213
212
|
return 2;
|
|
214
213
|
}
|
|
215
|
-
|
|
214
|
+
// 1.14-S4(K-POOL-1 / D-42):modelPool 登记时池装配压过 llm.baseUrl/model/adapterId(每次路由决策落链可解释)
|
|
215
|
+
const poolCfg = cfgLlm.config.modelPool ?? null;
|
|
216
|
+
const cfgBaseUrl = llm.baseUrl;
|
|
217
|
+
const cfgModel = llm.model;
|
|
218
|
+
let modelName = cfgModel ?? '';
|
|
216
219
|
const credService = new CredentialService();
|
|
217
220
|
const core = new RuntimeCore(credService);
|
|
218
221
|
core.bindContext(ctx);
|
|
219
222
|
// 适配器经根作用域 effect 可逆注册(bindContext 必须先于 register——否则走非可逆直注册分支);
|
|
220
|
-
// 1.5-S1(D-20):adapterId='anthropic' → anthropic 适配器(Messages API);缺省 openai-compat
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
// 1.5-S1(D-20):adapterId='anthropic' → anthropic 适配器(Messages API);缺省 openai-compat 不变;
|
|
224
|
+
// 1.14-S4(K-POOL-1 / D-42):modelPool 登记时池装配压过单端点(onDecide → routing/decide 落链)
|
|
225
|
+
if (poolCfg) {
|
|
226
|
+
const assembly = assembleModelPool({
|
|
227
|
+
pool: poolCfg,
|
|
225
228
|
credentials: credService,
|
|
226
|
-
|
|
227
|
-
|
|
229
|
+
onDecide: d => log.append('runtime', 'routing', d.turnId ?? 'T0', {
|
|
230
|
+
type: 'decide',
|
|
231
|
+
selected: d.selected,
|
|
232
|
+
reason: d.reason,
|
|
233
|
+
retries: d.retries,
|
|
234
|
+
attempts: d.attempts,
|
|
235
|
+
}),
|
|
236
|
+
});
|
|
237
|
+
core.registerLlmAdapter(assembly.adapter, { default: true });
|
|
238
|
+
// chatStep model 形参 = 首位候选(池内逐成员改写 req.model——usage 按成员模型落账)
|
|
239
|
+
modelName = assembly.members[0].model;
|
|
240
|
+
console.log(`[pool] ${assembly.members.length} 端点入池(候选序:${assembly.members.map(m => m.id).join(' → ')})`);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
if (!cfgBaseUrl || !cfgModel) {
|
|
244
|
+
console.error('CAR-E-LLM-CONFIG: 真实模型路径需要 baseUrl + model——');
|
|
245
|
+
console.error(' 配置:car.config.ts(export default)或 car.config.json {"llm":{"baseUrl":"https://…","model":"…"}} / env CAR_LLM_BASE_URL+CAR_LLM_MODEL / flag --base-url --model');
|
|
246
|
+
console.error(' anthropic:llm.adapterId "anthropic"(或 --adapter-id anthropic)+ baseUrl https://api.anthropic.com;凭据 ANTHROPIC_API_KEY');
|
|
247
|
+
console.error(' 模型池:llm.modelPool.endpoints(多端点 fallback + health 预检;凭据 keyEnv 引用;路由决策落链)');
|
|
248
|
+
console.error(' 凭据:OPENAI_API_KEY / ANTHROPIC_API_KEY(env fallback 需 CAR_ALLOW_ENV_CREDENTIALS=1 或 llm.allowEnvFallback)或 keychain;诊断:car doctor');
|
|
249
|
+
return 2;
|
|
250
|
+
}
|
|
251
|
+
core.registerLlmAdapter(llm.adapterId === 'anthropic'
|
|
252
|
+
? createAnthropicAdapter({ baseUrl: cfgBaseUrl, credentials: credService, provider: 'anthropic' })
|
|
253
|
+
: createOpenAICompatAdapter({
|
|
254
|
+
baseUrl: cfgBaseUrl,
|
|
255
|
+
credentials: credService,
|
|
256
|
+
provider: 'openai-compat',
|
|
257
|
+
}), { default: true });
|
|
258
|
+
}
|
|
228
259
|
// MCP 工具进 turn(D-18):--mcp <serverId>(可重复;server 启动规格在 car.config.json mcp.servers 登记)
|
|
229
260
|
const mcpIds = [];
|
|
230
261
|
// 1.8-D29:活跃 turn span 句柄(startTurn 后赋值——MCP 工具闭包运行期取 client span 挂链面)
|
|
@@ -326,7 +357,8 @@ async function main() {
|
|
|
326
357
|
model: modelName,
|
|
327
358
|
tools: toToolDefinitions(toolsMap),
|
|
328
359
|
...(llm.maxTokens !== undefined ? { maxTokens: llm.maxTokens } : {}),
|
|
329
|
-
|
|
360
|
+
// 1.14-S4:池装配时不传 adapterId(池适配器 = default;req.model 由池内按成员改写)
|
|
361
|
+
...(poolCfg ? {} : llm.adapterId ? { adapterId: llm.adapterId } : {}),
|
|
330
362
|
signal,
|
|
331
363
|
}).then(res => { step.end(res.stopReason); return res; }, e => { step.recordException(e); step.end('error'); throw e; });
|
|
332
364
|
},
|
package/dist/load/config.d.ts
CHANGED
|
@@ -5,12 +5,55 @@ export interface SandboxSigConfig {
|
|
|
5
5
|
sigEnforce?: boolean;
|
|
6
6
|
sigTrustRoot?: string;
|
|
7
7
|
}
|
|
8
|
+
export interface CompactionConfig {
|
|
9
|
+
/** 缺省 false = 行为零变化(1.13 兼容线) */
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
/** 距上次压缩的新增事件数阈值(事实计数非估算);缺省 200 */
|
|
12
|
+
thresholdEvents?: number;
|
|
13
|
+
/** 保护最近 N 个 turn 不折叠;缺省 4 */
|
|
14
|
+
protectLastTurns?: number;
|
|
15
|
+
}
|
|
8
16
|
export interface LlmConfig {
|
|
9
17
|
baseUrl?: string;
|
|
10
18
|
model?: string;
|
|
11
19
|
adapterId?: string;
|
|
12
20
|
maxTokens?: number;
|
|
13
21
|
allowEnvFallback?: boolean;
|
|
22
|
+
/** 1.14-S2(K-MP-2 / D-40):历史压缩策略(阈值自动触发判定入参;立即压缩 API 独立可用) */
|
|
23
|
+
compaction?: CompactionConfig;
|
|
24
|
+
}
|
|
25
|
+
/** 1.14-S4(K-POOL-1 / D-42):模型池端点登记(多端点 fallback 候选;凭据仅收 keyEnv 引用) */
|
|
26
|
+
export interface ModelPoolEndpointSpec {
|
|
27
|
+
/** 端点标识(routing/decide 落链定位;缺省 ep-1..N 派生;重复显式拒绝) */
|
|
28
|
+
id?: string;
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
model: string;
|
|
31
|
+
/** 适配器家族(config 可装配集 = 内置两族;缺省 openai-compat) */
|
|
32
|
+
adapterId?: 'openai-compat' | 'anthropic';
|
|
33
|
+
/** 凭据 env 变量名引用(明文 key 显式拒绝——CR-05;缺省按 adapterId 缺省表 + CAR_LLM_API_KEY) */
|
|
34
|
+
keyEnv?: string;
|
|
35
|
+
/** 优先级:数值越小越优先(缺省 0) */
|
|
36
|
+
priority?: number;
|
|
37
|
+
/** 同优先级内次序:越大越先(缺省 0——D-42:登记生效为排序 tie-break,不做负载均衡调度) */
|
|
38
|
+
weight?: number;
|
|
39
|
+
/** 成员级 maxTokens(缺省回落 llm.maxTokens) */
|
|
40
|
+
maxTokens?: number;
|
|
41
|
+
}
|
|
42
|
+
/** 1.14-S4(K-POOL-1 / D-42):health 预检策略(doctor 面同款口径——值不打印) */
|
|
43
|
+
export interface ModelPoolProbeConfig {
|
|
44
|
+
/** 缺省开;不可达端点按序跳过(fail-visible 落 routing/decide attempts) */
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
/** 单端点探测超时(缺省 4000ms) */
|
|
47
|
+
timeoutMs?: number;
|
|
48
|
+
/** 探测结果缓存 TTL(缺省 30000ms;0 = 每请求重探) */
|
|
49
|
+
ttlMs?: number;
|
|
50
|
+
}
|
|
51
|
+
/** 1.14-S4(K-POOL-1 / D-42):模型池登记。与 llm.baseUrl/model/adapterId 并存时池装配压过(每次路由决策落链可解释);llm.maxTokens/allowEnvFallback/compaction 仍生效。无 modelPool 配置 = 行为与 1.13 逐字节一致(兼容验收线) */
|
|
52
|
+
export interface ModelPoolConfig {
|
|
53
|
+
endpoints: ModelPoolEndpointSpec[];
|
|
54
|
+
probe?: ModelPoolProbeConfig;
|
|
55
|
+
/** 候选请求尝试上限(含首选;缺省 = 端点数) */
|
|
56
|
+
maxAttempts?: number;
|
|
14
57
|
}
|
|
15
58
|
export interface McpServerSpec {
|
|
16
59
|
/** stdio 通道启动命令(与 url 互斥——1.5-S2 起二选一显式登记) */
|
|
@@ -27,6 +70,8 @@ export interface CarConfig {
|
|
|
27
70
|
sandbox?: SandboxSigConfig;
|
|
28
71
|
/** 1.4-S3(D-16):模型接入配置键位空间;凭据禁入(CR-05)——只放端点/模型名/开关 */
|
|
29
72
|
llm?: LlmConfig;
|
|
73
|
+
/** 1.14-S4(K-POOL-1 / D-42):模型池登记(多端点 fallback;与 llm.* 并存时池压过,路由决策落链) */
|
|
74
|
+
modelPool?: ModelPoolConfig;
|
|
30
75
|
/** 1.4-S7(D-18):MCP server 登记面(--mcp <id> 显式启用) */
|
|
31
76
|
mcp?: {
|
|
32
77
|
servers: Record<string, McpServerSpec>;
|
package/dist/load/config.js
CHANGED
|
@@ -32,6 +32,61 @@ const nonEmptyTrimmed = () => z.string({ invalid_type_error: '必须为非空字
|
|
|
32
32
|
.trim()
|
|
33
33
|
.refine(v => v.length > 0, { message: '必须为非空字符串' });
|
|
34
34
|
const stringMap = () => z.record(z.string(), z.string({ invalid_type_error: '必须为字符串' }), { invalid_type_error: '必须为字符串映射', required_error: '必须为字符串映射' });
|
|
35
|
+
// 1.14-S4(K-POOL-1)辅助:正整数 / 非负整数
|
|
36
|
+
const positiveInt = () => z.number({ invalid_type_error: '必须为正整数', required_error: '必须为正整数' })
|
|
37
|
+
.refine(v => Number.isFinite(v) && Number.isInteger(v) && v > 0, { message: '必须为正整数' });
|
|
38
|
+
const nonNegativeInt = () => z.number({ invalid_type_error: '必须为非负整数', required_error: '必须为非负整数' })
|
|
39
|
+
.refine(v => Number.isFinite(v) && Number.isInteger(v) && v >= 0, { message: '必须为非负整数' });
|
|
40
|
+
// 明文凭据键黑名单(1.14-S4 / CR-05):专用文案先于未知键检查(首个 issue 渲染)
|
|
41
|
+
const PLAINTEXT_CRED_KEYS = ['key', 'apiKey', 'api_key', 'apikey', 'token', 'password'];
|
|
42
|
+
const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
43
|
+
const POOL_ENDPOINT_KEYS = new Set(['id', 'baseUrl', 'model', 'adapterId', 'keyEnv', 'priority', 'weight', 'maxTokens']);
|
|
44
|
+
/**
|
|
45
|
+
* 池端点 schema(1.14-S4):passthrough + superRefine 显式拒绝(等价 strict,但明文凭据键
|
|
46
|
+
* 给专用文案——CR-05 红线「明文 key 显式拒绝」必须可读可引导,不能只报「未知键」)。
|
|
47
|
+
*/
|
|
48
|
+
const poolEndpointSchema = z.object({
|
|
49
|
+
id: nonEmptyTrimmed().optional(),
|
|
50
|
+
baseUrl: nonEmptyTrimmed().refine(v => /^https?:\/\//.test(v), { message: '必须为 http(s) URL' }),
|
|
51
|
+
model: nonEmptyTrimmed(),
|
|
52
|
+
adapterId: z.enum(['openai-compat', 'anthropic'], { errorMap: () => ({ message: '必须为 openai-compat 或 anthropic(config 可装配集 = 内置两族)' }) }).optional(),
|
|
53
|
+
keyEnv: nonEmptyTrimmed().refine(v => ENV_NAME_RE.test(v), { message: '必须为环境变量名(明文凭据禁入——凭据仅收 keyEnv 引用,CR-05)' }).optional(),
|
|
54
|
+
priority: positiveInt().optional(),
|
|
55
|
+
weight: nonNegativeInt().optional(),
|
|
56
|
+
maxTokens: positiveInt().optional(),
|
|
57
|
+
}).passthrough().superRefine((obj, ctx) => {
|
|
58
|
+
for (const k of PLAINTEXT_CRED_KEYS) {
|
|
59
|
+
if (k in obj) {
|
|
60
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: [k], message: `明文凭据禁入——凭据仅收 keyEnv(环境变量名引用),明文键 "${k}" 一律拒绝` });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const k of Object.keys(obj)) {
|
|
65
|
+
if (!POOL_ENDPOINT_KEYS.has(k)) {
|
|
66
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: [k], message: '未登记键(登记键位:id/baseUrl/model/adapterId/keyEnv/priority/weight/maxTokens)' });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
const modelPoolSchema = z.object({
|
|
72
|
+
endpoints: z.array(poolEndpointSchema, { invalid_type_error: '必须为端点数组', required_error: '必须为端点数组' })
|
|
73
|
+
.min(1, { message: '至少登记一个端点(池空 = CAR-E-CONFIG;无池需求请移除 modelPool 键,行为与 1.13 一致)' }),
|
|
74
|
+
probe: z.object({
|
|
75
|
+
enabled: z.boolean({ invalid_type_error: '必须为布尔', required_error: '必须为布尔' }).optional(),
|
|
76
|
+
timeoutMs: positiveInt().optional(),
|
|
77
|
+
ttlMs: nonNegativeInt().optional(),
|
|
78
|
+
}).strict().optional(),
|
|
79
|
+
maxAttempts: positiveInt().optional(),
|
|
80
|
+
}).strict().superRefine((obj, ctx) => {
|
|
81
|
+
// 端点 id 唯一(缺省派生同口径)——routing/decide 落链定位歧义禁入
|
|
82
|
+
const ids = new Set();
|
|
83
|
+
for (const [i, ep] of (obj.endpoints ?? []).entries()) {
|
|
84
|
+
const id = ep.id ?? `ep-${i + 1}`;
|
|
85
|
+
if (ids.has(id))
|
|
86
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['endpoints', i], message: `端点 id "${id}" 重复(落链定位歧义,禁静默)` });
|
|
87
|
+
ids.add(id);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
35
90
|
const mcpServerSchema = z.object({
|
|
36
91
|
command: nonEmptyTrimmed().optional(),
|
|
37
92
|
url: nonEmptyTrimmed().refine(v => /^https?:\/\//.test(v), { message: '必须为 http(s) URL' }).optional(),
|
|
@@ -70,13 +125,23 @@ const rawSchema = z.object({
|
|
|
70
125
|
.refine(v => Number.isFinite(v) && Number.isInteger(v) && v > 0, { message: '必须为正整数' })
|
|
71
126
|
.optional(),
|
|
72
127
|
allowEnvFallback: z.boolean({ invalid_type_error: '必须为布尔', required_error: '必须为布尔' }).optional(),
|
|
128
|
+
// 1.14-S2(K-MP-2 / D-40):历史压缩策略(阈值事实计数,非 token 估算)
|
|
129
|
+
compaction: z.object({
|
|
130
|
+
enabled: z.boolean({ invalid_type_error: '必须为布尔', required_error: '必须为布尔' }).optional(),
|
|
131
|
+
thresholdEvents: z.number({ invalid_type_error: '必须为正整数', required_error: '必须为正整数' })
|
|
132
|
+
.refine(v => Number.isInteger(v) && v > 0, { message: '必须为正整数' }).optional(),
|
|
133
|
+
protectLastTurns: z.number({ invalid_type_error: '必须为正整数', required_error: '必须为正整数' })
|
|
134
|
+
.refine(v => Number.isInteger(v) && v > 0, { message: '必须为正整数' }).optional(),
|
|
135
|
+
}).strict().optional(),
|
|
73
136
|
}).strict().optional(),
|
|
137
|
+
// 1.14-S4(K-POOL-1 / D-42):模型池登记(多端点 fallback + health + routing/decide 落链)
|
|
138
|
+
modelPool: modelPoolSchema.optional(),
|
|
74
139
|
mcp: z.object({
|
|
75
140
|
servers: z.record(z.string(), mcpServerSchema, { invalid_type_error: '必须为对象(serverId → 启动规格)' }),
|
|
76
141
|
}).strict().optional(),
|
|
77
142
|
}, { invalid_type_error: '配置顶层必须为 JSON 对象' }).strict();
|
|
78
143
|
/** 未登记键提示(渲染层拼 path)——与系统设计键位空间同步 */
|
|
79
|
-
const KEY_SPACES = '登记键位:sandbox.* / llm.* / mcp.servers.*';
|
|
144
|
+
const KEY_SPACES = '登记键位:sandbox.* / llm.* / llm.modelPool.* / mcp.servers.*';
|
|
80
145
|
/** zod issue → CAR-E-CONFIG 渲染(首个 issue;unrecognized_keys 按键路径展开) */
|
|
81
146
|
function renderIssue(error) {
|
|
82
147
|
const iss = error.issues[0];
|
|
@@ -119,6 +184,16 @@ function parseCarConfig(raw) {
|
|
|
119
184
|
llm.maxTokens = d.llm.maxTokens;
|
|
120
185
|
if (d.llm.allowEnvFallback !== undefined)
|
|
121
186
|
llm.allowEnvFallback = d.llm.allowEnvFallback;
|
|
187
|
+
if (d.llm.compaction) {
|
|
188
|
+
const cp = {};
|
|
189
|
+
if (d.llm.compaction.enabled !== undefined)
|
|
190
|
+
cp.enabled = d.llm.compaction.enabled;
|
|
191
|
+
if (d.llm.compaction.thresholdEvents !== undefined)
|
|
192
|
+
cp.thresholdEvents = d.llm.compaction.thresholdEvents;
|
|
193
|
+
if (d.llm.compaction.protectLastTurns !== undefined)
|
|
194
|
+
cp.protectLastTurns = d.llm.compaction.protectLastTurns;
|
|
195
|
+
llm.compaction = cp;
|
|
196
|
+
}
|
|
122
197
|
out.llm = llm;
|
|
123
198
|
}
|
|
124
199
|
if (d.mcp) {
|
|
@@ -139,6 +214,14 @@ function parseCarConfig(raw) {
|
|
|
139
214
|
}
|
|
140
215
|
out.mcp = { servers };
|
|
141
216
|
}
|
|
217
|
+
if (d.modelPool) {
|
|
218
|
+
const mp = d.modelPool;
|
|
219
|
+
out.modelPool = {
|
|
220
|
+
endpoints: mp.endpoints.map(ep => ({ ...ep })),
|
|
221
|
+
...(mp.probe ? { probe: { ...mp.probe } } : {}),
|
|
222
|
+
...(mp.maxAttempts !== undefined ? { maxAttempts: mp.maxAttempts } : {}),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
142
225
|
return { config: out };
|
|
143
226
|
}
|
|
144
227
|
// ==================== 载体装载(JSON 同步 / TS 预热缓存) ====================
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { ModelPoolConfig } from './config.ts';
|
|
2
|
+
import { CredentialService } from '../runtime-core/credentials.ts';
|
|
3
|
+
import type { LlmAdapter } from '../runtime-core/types.ts';
|
|
4
|
+
/** 池成员(解析后形态——id 缺省已派生;候选序已定) */
|
|
5
|
+
export interface PoolMember {
|
|
6
|
+
id: string;
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
model: string;
|
|
9
|
+
adapterId: 'openai-compat' | 'anthropic';
|
|
10
|
+
keyEnv?: string;
|
|
11
|
+
maxTokens?: number;
|
|
12
|
+
priority?: number;
|
|
13
|
+
weight?: number;
|
|
14
|
+
}
|
|
15
|
+
/** 候选序:priority 升序(缺省 0)→ weight 降序(缺省 0,D-42 tie-break)→ 登记序(稳定) */
|
|
16
|
+
export declare function orderPoolMembers(members: PoolMember[]): PoolMember[];
|
|
17
|
+
/** config modelPool → 池成员(id 缺省 ep-1..N 派生;重复 id 显式拒绝;候选序就位) */
|
|
18
|
+
export declare function resolveModelPoolMembers(pool: ModelPoolConfig): {
|
|
19
|
+
members: PoolMember[];
|
|
20
|
+
error?: string;
|
|
21
|
+
};
|
|
22
|
+
export interface PoolMemberHealth {
|
|
23
|
+
/** 传输可达(任意 HTTP 状态码)= true;网络层失败/超时 = false */
|
|
24
|
+
ok: boolean;
|
|
25
|
+
status?: number;
|
|
26
|
+
latencyMs: number;
|
|
27
|
+
/** 不健康原因类别(值不打印纪律——只出类别) */
|
|
28
|
+
errorCategory?: string;
|
|
29
|
+
}
|
|
30
|
+
/** 单端点 health 探测(doctor/池预检复用;值不打印——只出状态/延迟/类别) */
|
|
31
|
+
export declare function probePoolMember(member: Pick<PoolMember, 'baseUrl' | 'adapterId'>, opts?: {
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}): Promise<PoolMemberHealth>;
|
|
34
|
+
/** 逐候选结果(attempts 全量在册——健康跳过与请求失败都显性化) */
|
|
35
|
+
export interface RoutingAttempt {
|
|
36
|
+
id: string;
|
|
37
|
+
outcome: 'selected' | 'failed' | 'skipped-unhealthy';
|
|
38
|
+
/** 失败/跳过类别(错误码/名称,值不打印) */
|
|
39
|
+
detail?: string;
|
|
40
|
+
}
|
|
41
|
+
/** 路由决策(routing/decide 事件 payload 源——每次请求一条) */
|
|
42
|
+
export interface RoutingDecision {
|
|
43
|
+
/** 选中端点 id(耗尽 = null) */
|
|
44
|
+
selected: string | null;
|
|
45
|
+
/** 首位候选选中 = primary;降级选中 = fallback;全部不可用 = exhausted */
|
|
46
|
+
reason: 'primary' | 'fallback' | 'exhausted';
|
|
47
|
+
/** 降级重试次数 = 发起请求且失败的候选数(健康跳过不计——attempts 全量在册) */
|
|
48
|
+
retries: number;
|
|
49
|
+
attempts: RoutingAttempt[];
|
|
50
|
+
turnId?: string;
|
|
51
|
+
sessionId?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface PoolAdapterOptions {
|
|
54
|
+
id?: string;
|
|
55
|
+
/** 成员与适配器(装配层产出的候选序) */
|
|
56
|
+
members: Array<{
|
|
57
|
+
member: PoolMember;
|
|
58
|
+
adapter: LlmAdapter;
|
|
59
|
+
}>;
|
|
60
|
+
/** health 预检(enabled 缺省 true;probeImpl 测试注入缝——缺省真实 GET 探测) */
|
|
61
|
+
probe?: {
|
|
62
|
+
enabled?: boolean;
|
|
63
|
+
timeoutMs?: number;
|
|
64
|
+
ttlMs?: number;
|
|
65
|
+
probeImpl?: typeof probePoolMember;
|
|
66
|
+
};
|
|
67
|
+
/** 候选请求尝试上限(缺省 = 成员数) */
|
|
68
|
+
maxAttempts?: number;
|
|
69
|
+
/** routing/decide 审计回调(宿主接线 SessionLog.append——禁静默换模型红线) */
|
|
70
|
+
onDecide?: (d: RoutingDecision) => void;
|
|
71
|
+
}
|
|
72
|
+
export declare function createModelPoolAdapter(opts: PoolAdapterOptions): LlmAdapter & {
|
|
73
|
+
id: string;
|
|
74
|
+
members: PoolMember[];
|
|
75
|
+
};
|
|
76
|
+
export interface ModelPoolAssemblyOptions {
|
|
77
|
+
pool: ModelPoolConfig;
|
|
78
|
+
/** 共享凭据服务(成员级 keyEnv 经 credentialEnvVars 精确候选) */
|
|
79
|
+
credentials?: CredentialService;
|
|
80
|
+
/** 测试注入(成员适配器 fetch/退避——缺省真实通道) */
|
|
81
|
+
fetchImpl?: typeof fetch;
|
|
82
|
+
timeoutMs?: number;
|
|
83
|
+
firstByteMs?: number;
|
|
84
|
+
retries?: number;
|
|
85
|
+
backoffMs?: number[];
|
|
86
|
+
sleep?: (ms: number) => Promise<void>;
|
|
87
|
+
/** routing/decide 审计回调(透传路由引擎) */
|
|
88
|
+
onDecide?: (d: RoutingDecision) => void;
|
|
89
|
+
}
|
|
90
|
+
/** 池装配:config 解析(候选序/重复拒绝)→ 成员适配器(内置两族 + keyEnv)→ 路由适配器 */
|
|
91
|
+
export declare function assembleModelPool(opts: ModelPoolAssemblyOptions): {
|
|
92
|
+
adapter: LlmAdapter & {
|
|
93
|
+
id: string;
|
|
94
|
+
members: PoolMember[];
|
|
95
|
+
};
|
|
96
|
+
members: PoolMember[];
|
|
97
|
+
};
|