@sema-agent/client-core 0.77.1 → 0.78.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.
Files changed (382) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/README.md +6 -5
  3. package/dist/abortableSleep.d.ts +0 -28
  4. package/dist/abortableSleep.js +0 -28
  5. package/dist/adapt/arms.d.ts +0 -46
  6. package/dist/adapt/arms.js +9 -544
  7. package/dist/adapt/ids.d.ts +0 -58
  8. package/dist/adapt/ids.js +0 -63
  9. package/dist/adapt/instanceLedger.d.ts +0 -25
  10. package/dist/adapt/instanceLedger.js +0 -20
  11. package/dist/adapt/panelTasks.d.ts +0 -69
  12. package/dist/adapt/panelTasks.js +0 -95
  13. package/dist/adapt/textSegmentAuthority.d.ts +1 -137
  14. package/dist/adapt/textSegmentAuthority.js +0 -54
  15. package/dist/adapt/textStream.d.ts +0 -252
  16. package/dist/adapt/textStream.js +2 -281
  17. package/dist/adapt/toolCards.d.ts +0 -46
  18. package/dist/adapt/toolCards.js +0 -23
  19. package/dist/adapt/turnFlags.d.ts +0 -54
  20. package/dist/adapt/turnFlags.js +1 -17
  21. package/dist/adapt/wireShapes.d.ts +0 -92
  22. package/dist/adapt/wireShapes.js +0 -83
  23. package/dist/adapt.d.ts +0 -55
  24. package/dist/adapt.js +1 -120
  25. package/dist/adapter/activeRunSelfHeal.d.ts +22 -515
  26. package/dist/adapter/activeRunSelfHeal.js +10 -625
  27. package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -305
  28. package/dist/adapter/downstream/eventToSdkMessage.js +2 -867
  29. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -314
  30. package/dist/adapter/downstream/terminalToSdkResult.js +13 -560
  31. package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +0 -95
  32. package/dist/adapter/downstream/turnUsageToModelUsage.js +2 -31
  33. package/dist/adapter/runStream.d.ts +0 -206
  34. package/dist/adapter/runStream.js +6 -596
  35. package/dist/adapter/types.d.ts +0 -111
  36. package/dist/adapter/types.js +0 -29
  37. package/dist/agentSession/backgroundView.d.ts +0 -106
  38. package/dist/agentSession/backgroundView.js +3 -49
  39. package/dist/agentSession/contract.d.ts +0 -98
  40. package/dist/agentSession/contract.js +0 -12
  41. package/dist/agentsWireCaps.d.ts +0 -72
  42. package/dist/agentsWireCaps.js +4 -72
  43. package/dist/approvalsStreamLiveCapability.d.ts +0 -25
  44. package/dist/approvalsStreamLiveCapability.js +0 -31
  45. package/dist/argvFlagValue.d.ts +0 -33
  46. package/dist/argvFlagValue.js +3 -35
  47. package/dist/attachmentsWireCaps.d.ts +0 -127
  48. package/dist/attachmentsWireCaps.js +0 -144
  49. package/dist/autoModeUnavailable.d.ts +0 -120
  50. package/dist/autoModeUnavailable.js +0 -144
  51. package/dist/classifierStatus.d.ts +0 -72
  52. package/dist/classifierStatus.js +0 -167
  53. package/dist/classifierVerdictWire.d.ts +0 -54
  54. package/dist/classifierVerdictWire.js +0 -133
  55. package/dist/clientContextWireCaps.d.ts +0 -37
  56. package/dist/clientContextWireCaps.js +0 -36
  57. package/dist/clientSlice.d.ts +0 -63
  58. package/dist/clientSlice.js +0 -45
  59. package/dist/cloudConfigWireCaps.d.ts +0 -86
  60. package/dist/cloudConfigWireCaps.js +2 -65
  61. package/dist/compensations.d.ts +0 -52
  62. package/dist/compensations.js +2 -63
  63. package/dist/controlRouter.d.ts +2 -208
  64. package/dist/controlRouter.js +1 -129
  65. package/dist/coreValuePorts.d.ts +0 -84
  66. package/dist/coreValuePorts.js +0 -29
  67. package/dist/decideReceipt.d.ts +0 -110
  68. package/dist/decideReceipt.js +0 -84
  69. package/dist/detachWire.d.ts +0 -130
  70. package/dist/detachWire.js +1 -130
  71. package/dist/deviceExecutorManagementCapability.d.ts +2 -35
  72. package/dist/deviceExecutorManagementCapability.js +1 -37
  73. package/dist/diagnostics.d.ts +0 -8
  74. package/dist/diagnostics.js +0 -8
  75. package/dist/diff/patch.d.ts +0 -17
  76. package/dist/diff/patch.js +0 -19
  77. package/dist/effectiveFacts.d.ts +0 -40
  78. package/dist/effectiveFacts.js +0 -22
  79. package/dist/effortWire.d.ts +0 -11
  80. package/dist/effortWire.js +0 -12
  81. package/dist/engineAgentPanelStore.d.ts +0 -169
  82. package/dist/engineAgentPanelStore.js +13 -293
  83. package/dist/engineCapReader.d.ts +0 -62
  84. package/dist/engineCapReader.js +1 -43
  85. package/dist/engineCapsCache.d.ts +0 -177
  86. package/dist/engineCapsCache.js +0 -199
  87. package/dist/engineCapsGenerationGuard.d.ts +0 -2
  88. package/dist/engineCapsGenerationGuard.js +0 -11
  89. package/dist/engineErrorCodes.d.ts +1 -316
  90. package/dist/engineErrorCodes.js +0 -427
  91. package/dist/engineHttpTools.d.ts +0 -29
  92. package/dist/engineHttpTools.js +0 -29
  93. package/dist/engineIdentity.d.ts +0 -83
  94. package/dist/engineIdentity.js +0 -88
  95. package/dist/engineInlineTaskStats.d.ts +0 -57
  96. package/dist/engineInlineTaskStats.js +1 -42
  97. package/dist/engineNoticeCodes.d.ts +0 -190
  98. package/dist/engineNoticeCodes.js +0 -182
  99. package/dist/engineSessionParam.d.ts +0 -30
  100. package/dist/engineSessionParam.js +0 -52
  101. package/dist/engineToolLabelStore.d.ts +0 -29
  102. package/dist/engineToolLabelStore.js +0 -31
  103. package/dist/engineWireSdk.d.ts +0 -90
  104. package/dist/engineWireSdk.js +0 -74
  105. package/dist/engineWireTarget.d.ts +0 -14
  106. package/dist/engineWireTarget.js +0 -39
  107. package/dist/env/localeGeo.d.ts +0 -12
  108. package/dist/env/localeGeo.js +2 -77
  109. package/dist/env/localeTag.d.ts +0 -34
  110. package/dist/env/localeTag.js +0 -32
  111. package/dist/env/uiLanguage.d.ts +0 -13
  112. package/dist/env/uiLanguage.js +0 -25
  113. package/dist/envFlag.d.ts +0 -37
  114. package/dist/envFlag.js +0 -40
  115. package/dist/executionLaneCapability.d.ts +0 -48
  116. package/dist/executionLaneCapability.js +0 -54
  117. package/dist/finalVerifyWire.d.ts +0 -67
  118. package/dist/finalVerifyWire.js +3 -43
  119. package/dist/fleet/fleetLedger.d.ts +0 -308
  120. package/dist/fleet/fleetLedger.js +10 -429
  121. package/dist/fleet/fleetProjection.d.ts +0 -240
  122. package/dist/fleet/fleetProjection.js +0 -189
  123. package/dist/fleet/fleetRowAgentType.d.ts +0 -6
  124. package/dist/fleet/fleetRowAgentType.js +1 -32
  125. package/dist/fleet/workflowSizeWarning.d.ts +0 -47
  126. package/dist/fleet/workflowSizeWarning.js +1 -46
  127. package/dist/fleetAgentPanelProjection.d.ts +0 -48
  128. package/dist/fleetAgentPanelProjection.js +8 -173
  129. package/dist/fleetTaskDesc.d.ts +0 -39
  130. package/dist/fleetTaskDesc.js +0 -69
  131. package/dist/forkWireCaps.d.ts +0 -23
  132. package/dist/forkWireCaps.js +1 -24
  133. package/dist/gateOutcome.d.ts +0 -140
  134. package/dist/gateOutcome.js +0 -85
  135. package/dist/gateVocabulary.d.ts +0 -114
  136. package/dist/gateVocabulary.js +3 -161
  137. package/dist/goalStopHook.d.ts +0 -127
  138. package/dist/goalStopHook.js +0 -178
  139. package/dist/headlessPermissionModeWire.d.ts +0 -76
  140. package/dist/headlessPermissionModeWire.js +1 -155
  141. package/dist/headlessReconnectWire.d.ts +0 -84
  142. package/dist/headlessReconnectWire.js +13 -67
  143. package/dist/hitl/approvalDecisionNoteAudit.d.ts +0 -49
  144. package/dist/hitl/approvalDecisionNoteAudit.js +0 -56
  145. package/dist/hitl/approvalOutcomeNote.d.ts +0 -2
  146. package/dist/hitl/approvalOutcomeNote.js +0 -20
  147. package/dist/hitl/approvalResolution.d.ts +0 -113
  148. package/dist/hitl/approvalResolution.js +0 -66
  149. package/dist/hitl/approvalsFeed.d.ts +0 -183
  150. package/dist/hitl/approvalsFeed.js +12 -243
  151. package/dist/hitl/armedGateRegistry.d.ts +0 -55
  152. package/dist/hitl/armedGateRegistry.js +0 -146
  153. package/dist/hitl/askGateWire.d.ts +0 -86
  154. package/dist/hitl/askGateWire.js +1 -95
  155. package/dist/hitl/askParkRowRouting.d.ts +0 -123
  156. package/dist/hitl/askParkRowRouting.js +1 -89
  157. package/dist/hitl/crashConverged.d.ts +0 -148
  158. package/dist/hitl/crashConverged.js +0 -214
  159. package/dist/hitl/editedRuleTextPrecheck.d.ts +0 -109
  160. package/dist/hitl/editedRuleTextPrecheck.js +0 -71
  161. package/dist/hitl/frameRouter.d.ts +0 -134
  162. package/dist/hitl/frameRouter.js +6 -378
  163. package/dist/hitl/gateIdentity.d.ts +0 -39
  164. package/dist/hitl/gateIdentity.js +0 -41
  165. package/dist/hitl/gateLedger.d.ts +0 -267
  166. package/dist/hitl/gateLedger.js +0 -121
  167. package/dist/hitl/hitlBridge.d.ts +7 -431
  168. package/dist/hitl/hitlBridge.js +7 -476
  169. package/dist/hitl/hitlHostSurface.d.ts +0 -150
  170. package/dist/hitl/hitlHostSurface.js +0 -169
  171. package/dist/hitl/livePendingAsk.d.ts +0 -91
  172. package/dist/hitl/livePendingAsk.js +0 -74
  173. package/dist/hitl/localAllowRule.d.ts +0 -62
  174. package/dist/hitl/localAllowRule.js +1 -33
  175. package/dist/hitl/parkOwnership.d.ts +0 -56
  176. package/dist/hitl/parkOwnership.js +0 -22
  177. package/dist/hitl/parkResolver.d.ts +3 -88
  178. package/dist/hitl/parkResolver.js +8 -349
  179. package/dist/hitl/parkRowBirthWait.d.ts +2 -26
  180. package/dist/hitl/parkRowBirthWait.js +3 -93
  181. package/dist/hitl/persistedRulesWire.d.ts +23 -371
  182. package/dist/hitl/persistedRulesWire.js +62 -329
  183. package/dist/hitl/planReviewWire.d.ts +4 -176
  184. package/dist/hitl/planReviewWire.js +9 -312
  185. package/dist/hitl/resumeRunningCard.d.ts +0 -105
  186. package/dist/hitl/resumeRunningCard.js +0 -105
  187. package/dist/hitl/sessionPolicyWire.d.ts +11 -191
  188. package/dist/hitl/sessionPolicyWire.js +0 -149
  189. package/dist/hitl/suspendedReopen.d.ts +3 -24
  190. package/dist/hitl/suspendedReopen.js +0 -14
  191. package/dist/hitl/toolApprovalWire.d.ts +5 -1308
  192. package/dist/hitl/toolApprovalWire.js +5 -945
  193. package/dist/hooksWireCaps.d.ts +0 -38
  194. package/dist/hooksWireCaps.js +0 -190
  195. package/dist/host.d.ts +0 -105
  196. package/dist/host.js +0 -33
  197. package/dist/hostEnv.d.ts +0 -14
  198. package/dist/hostEnv.js +0 -13
  199. package/dist/imagesWireCaps.d.ts +0 -21
  200. package/dist/imagesWireCaps.js +0 -22
  201. package/dist/index.d.ts +0 -134
  202. package/dist/index.js +0 -476
  203. package/dist/interactiveHalt.d.ts +5 -153
  204. package/dist/interactiveHalt.js +0 -111
  205. package/dist/interactiveToolsWire.d.ts +0 -62
  206. package/dist/interactiveToolsWire.js +1 -67
  207. package/dist/leaderConflict.d.ts +0 -59
  208. package/dist/leaderConflict.js +0 -48
  209. package/dist/limitsWire.d.ts +0 -125
  210. package/dist/limitsWire.js +2 -120
  211. package/dist/liveInitToolFace.d.ts +0 -77
  212. package/dist/liveInitToolFace.js +1 -109
  213. package/dist/liveModelCatalog.d.ts +0 -52
  214. package/dist/liveModelCatalog.js +0 -48
  215. package/dist/liveQuestionStore.d.ts +0 -87
  216. package/dist/liveQuestionStore.js +1 -56
  217. package/dist/mcpLiveness.d.ts +0 -151
  218. package/dist/mcpLiveness.js +0 -122
  219. package/dist/mcpPanel.d.ts +0 -104
  220. package/dist/mcpPanel.js +0 -49
  221. package/dist/mcpReconnect.d.ts +0 -58
  222. package/dist/mcpReconnect.js +0 -69
  223. package/dist/mcpWireCaps.d.ts +0 -55
  224. package/dist/mcpWireCaps.js +1 -13
  225. package/dist/memoryComplianceCapability.d.ts +3 -57
  226. package/dist/memoryComplianceCapability.js +0 -57
  227. package/dist/memoryEntriesWire.d.ts +6 -198
  228. package/dist/memoryEntriesWire.js +0 -144
  229. package/dist/memoryOriginCapability.d.ts +3 -53
  230. package/dist/memoryOriginCapability.js +0 -50
  231. package/dist/memorySpecWire.d.ts +0 -116
  232. package/dist/memorySpecWire.js +0 -150
  233. package/dist/model/catalog.d.ts +0 -111
  234. package/dist/model/catalog.js +0 -87
  235. package/dist/model/catalogLoader.d.ts +0 -114
  236. package/dist/model/catalogLoader.js +2 -139
  237. package/dist/model/modelSupplyRules.d.ts +1 -55
  238. package/dist/model/modelSupplyRules.js +0 -62
  239. package/dist/model/providerAuth.d.ts +0 -103
  240. package/dist/model/providerAuth.js +2 -38
  241. package/dist/model/providerCatalog.d.ts +0 -45
  242. package/dist/model/providerCatalog.js +0 -37
  243. package/dist/model/providerPresets.d.ts +0 -33
  244. package/dist/model/providerPresets.js +2 -91
  245. package/dist/model/tierVocabulary.d.ts +0 -31
  246. package/dist/model/tierVocabulary.js +0 -27
  247. package/dist/modelBudgetRule.d.ts +0 -39
  248. package/dist/modelBudgetRule.js +0 -39
  249. package/dist/modelCapabilityProbe.d.ts +0 -175
  250. package/dist/modelCapabilityProbe.js +0 -147
  251. package/dist/modelWireCaps.d.ts +0 -13
  252. package/dist/modelWireCaps.js +0 -13
  253. package/dist/notifications.d.ts +0 -233
  254. package/dist/notifications.js +14 -492
  255. package/dist/oneShotWireCaps.d.ts +0 -34
  256. package/dist/oneShotWireCaps.js +0 -35
  257. package/dist/ownKey.d.ts +0 -33
  258. package/dist/ownKey.js +0 -33
  259. package/dist/panelRunningHistory.d.ts +0 -28
  260. package/dist/panelRunningHistory.js +0 -43
  261. package/dist/peerFrames.d.ts +0 -71
  262. package/dist/peerFrames.js +0 -168
  263. package/dist/peerLaneCapability.d.ts +0 -44
  264. package/dist/peerLaneCapability.js +0 -52
  265. package/dist/permissionRuleIssue.d.ts +0 -30
  266. package/dist/permissionRuleIssue.js +0 -78
  267. package/dist/permissionRulesWriteCapability.d.ts +0 -46
  268. package/dist/permissionRulesWriteCapability.js +0 -54
  269. package/dist/permissionWireCaps.d.ts +0 -37
  270. package/dist/permissionWireCaps.js +0 -37
  271. package/dist/postureKnob.d.ts +0 -71
  272. package/dist/postureKnob.js +0 -86
  273. package/dist/principalWire.d.ts +0 -17
  274. package/dist/principalWire.js +0 -17
  275. package/dist/printToolResultFrame.d.ts +0 -100
  276. package/dist/printToolResultFrame.js +0 -33
  277. package/dist/promptProfileWireCaps.d.ts +0 -13
  278. package/dist/promptProfileWireCaps.js +0 -13
  279. package/dist/readFacePosture.d.ts +0 -43
  280. package/dist/readFacePosture.js +0 -46
  281. package/dist/request/printNotification.d.ts +0 -20
  282. package/dist/request/printNotification.js +0 -55
  283. package/dist/request/taskRequest.d.ts +1 -249
  284. package/dist/request/taskRequest.js +20 -498
  285. package/dist/resumeRefusalCopy.d.ts +0 -136
  286. package/dist/resumeRefusalCopy.js +1 -117
  287. package/dist/retainBackgroundWireCaps.d.ts +0 -48
  288. package/dist/retainBackgroundWireCaps.js +0 -48
  289. package/dist/retryStatus.d.ts +2 -221
  290. package/dist/retryStatus.js +0 -107
  291. package/dist/rewindWireCaps.d.ts +0 -27
  292. package/dist/rewindWireCaps.js +0 -24
  293. package/dist/runCancelContext.d.ts +1 -20
  294. package/dist/runCancelContext.js +0 -34
  295. package/dist/runTerminal.d.ts +0 -273
  296. package/dist/runTerminal.js +0 -177
  297. package/dist/sandboxWire.d.ts +0 -38
  298. package/dist/sandboxWire.js +0 -82
  299. package/dist/scenarioWire.d.ts +0 -60
  300. package/dist/scenarioWire.js +0 -68
  301. package/dist/scratchpadWireCaps.d.ts +0 -11
  302. package/dist/scratchpadWireCaps.js +0 -32
  303. package/dist/sdkWireTransit.d.ts +0 -38
  304. package/dist/sdkWireTransit.js +0 -30
  305. package/dist/seam.d.ts +18 -853
  306. package/dist/seam.js +0 -52
  307. package/dist/seatContract.d.ts +0 -555
  308. package/dist/seatContract.js +5 -304
  309. package/dist/selfOrchestrationDenial.d.ts +0 -189
  310. package/dist/selfOrchestrationDenial.js +0 -162
  311. package/dist/selfOrchestrationWireCaps.d.ts +0 -38
  312. package/dist/selfOrchestrationWireCaps.js +0 -38
  313. package/dist/sessionMap.d.ts +0 -60
  314. package/dist/sessionMap.js +0 -37
  315. package/dist/sessionMemoryStatus.d.ts +11 -114
  316. package/dist/sessionMemoryStatus.js +0 -112
  317. package/dist/sessionModelLatch.d.ts +0 -29
  318. package/dist/sessionModelLatch.js +0 -40
  319. package/dist/sessionPolicyCapability.d.ts +3 -53
  320. package/dist/sessionPolicyCapability.js +0 -52
  321. package/dist/sessionSlot.d.ts +0 -26
  322. package/dist/sessionSlot.js +0 -17
  323. package/dist/skillsWireCaps.d.ts +0 -87
  324. package/dist/skillsWireCaps.js +0 -38
  325. package/dist/sqlEngineCapability.d.ts +0 -118
  326. package/dist/sqlEngineCapability.js +0 -134
  327. package/dist/sseIdleTriage.d.ts +0 -79
  328. package/dist/sseIdleTriage.js +1 -80
  329. package/dist/steering.d.ts +0 -54
  330. package/dist/steering.js +0 -63
  331. package/dist/subagent/engineCompactWire.d.ts +0 -34
  332. package/dist/subagent/engineCompactWire.js +4 -134
  333. package/dist/subagent/engineDelegatedPrompt.d.ts +0 -31
  334. package/dist/subagent/engineDelegatedPrompt.js +1 -103
  335. package/dist/subagent/engineRowStopGate.d.ts +0 -20
  336. package/dist/subagent/engineRowStopGate.js +0 -49
  337. package/dist/subagent/engineSubagentOutput.d.ts +0 -7
  338. package/dist/subagent/engineSubagentOutput.js +1 -51
  339. package/dist/subagent/engineSubagentResume.d.ts +2 -279
  340. package/dist/subagent/engineSubagentResume.js +2 -129
  341. package/dist/subagent/engineSubagentSteer.d.ts +0 -26
  342. package/dist/subagent/engineSubagentSteer.js +0 -63
  343. package/dist/subagent/engineSubagentTail.d.ts +0 -38
  344. package/dist/subagent/engineSubagentTail.js +2 -110
  345. package/dist/subagent/engineTaskHandleWire.d.ts +6 -67
  346. package/dist/subagent/engineTaskHandleWire.js +0 -126
  347. package/dist/subagent/subagentOwnerAbsence.d.ts +0 -2
  348. package/dist/subagent/subagentOwnerAbsence.js +0 -25
  349. package/dist/subagentContentStore.d.ts +3 -260
  350. package/dist/subagentContentStore.js +7 -540
  351. package/dist/systemReminderTag.d.ts +0 -49
  352. package/dist/systemReminderTag.js +0 -61
  353. package/dist/toolResult.d.ts +1 -211
  354. package/dist/toolResult.js +1 -358
  355. package/dist/toolRoster.d.ts +0 -140
  356. package/dist/toolRoster.js +0 -66
  357. package/dist/typePins.d.ts +0 -46
  358. package/dist/types/engineState.d.ts +0 -71
  359. package/dist/types/engineState.js +0 -16
  360. package/dist/ultracodeWireCaps.d.ts +0 -75
  361. package/dist/ultracodeWireCaps.js +0 -92
  362. package/dist/unrefTimer.d.ts +0 -27
  363. package/dist/webSearchBackendCapability.d.ts +0 -72
  364. package/dist/webSearchBackendCapability.js +0 -79
  365. package/dist/webSearchWireCaps.d.ts +0 -37
  366. package/dist/webSearchWireCaps.js +0 -34
  367. package/dist/websearch/searchProviderPresets.d.ts +0 -81
  368. package/dist/websearch/searchProviderPresets.js +0 -27
  369. package/dist/wireErrorTriage.d.ts +4 -160
  370. package/dist/wireErrorTriage.js +0 -171
  371. package/dist/wireRefusalCopy.d.ts +0 -28
  372. package/dist/wireRefusalCopy.js +0 -28
  373. package/dist/workflow.d.ts +0 -40
  374. package/dist/workflow.js +0 -52
  375. package/dist/workflowClient.d.ts +0 -122
  376. package/dist/workflowClient.js +8 -360
  377. package/dist/workflowMonitor.d.ts +2 -72
  378. package/dist/workflowMonitor.js +0 -26
  379. package/dist/writeProtectionCapability.d.ts +0 -112
  380. package/dist/writeProtectionCapability.js +0 -109
  381. package/docs/INTEGRATION-CLIENTS.md +78 -5
  382. package/package.json +3 -3
@@ -1,76 +1,18 @@
1
- /**
2
- * catalogLoader.ts — **目录候选链的宿主 loader**(design/166 §1-§3,2026-08-04)。
3
- *
4
- * ## 它与 `catalog.ts` 的分工(🔴 委托,不平行重做)
5
- *
6
- * `catalog.ts` 已经是三层源解析器:`resolveModelCatalog`(线上→包内→用户覆盖三层合并)+
7
- * `validateOnlineCatalog`(schemaVersion 区间 / 逐行 `isProviderRow` / **绝不半解析** / 不回显载荷)
8
- * + `CatalogFetchJson` 注入端口 + `CatalogSource`·`CatalogRejectReason` 两张词表。
9
- * 本文件**只补 catalog.ts 声明不管的宿主面**,四件事:
10
- *
11
- * ① **候选链遍历**(`DEFAULT_CATALOG_SOURCES` = raw.githubusercontent → jsDelivr,可配);
12
- * ② **传输安全硬门**:https + 域白名单(拨号**之前**判,域外源连请求都不发)——
13
- * 而且**重定向目标过同一道门**(fetch 默认跟随重定向,不设门 = 白名单可被 302 绕过);
14
- * ③ **`catalog.sha256` 旁签**(同源同路径,SHA-256 hex 比对);
15
- * ④ **缓存**(`configHome/cache/model-catalog.json`)的信封与读回语义。
16
- *
17
- * 载荷面的判决(schemaVersion / 逐行形状 / 整份弃 / 不回显)**一行都不在这里重写** ——
18
- * 本文件拿到字节流之后做的唯一一件事是 `JSON.parse`,然后把结果交给 `resolveModelCatalog`,
19
- * 由它去 `validateOnlineCatalog`。UI 渲染也只认 `ModelCatalogResult.source` + `online.reason`
20
- * (+ 本文件加的 `cacheHit`),**不发明第二套 origin 词表**。
21
- *
22
- * ## 🔴 为什么缓存的**落盘动作**在宿主而不在这里(与设计档 §3 的偏差,记在这)
23
- *
24
- * 设计档 §3 写的是「loader 原子写(tmp+rename)」。但本包有一条机械门守着的硬法:
25
- * `src/**` 的 index 值级闭包**零 Node 内建**(`scripts/run-client-core-portability-test.mjs`
26
- * ①c 段),因为同一份代码要在浏览器/桌面渲染进程里跑。`node:fs` 一进来,那道门当场红。
27
- * ⇒ 落盘走**注入口** `CatalogCachePort`(与 `host.ts` 的 `FsPort`、`catalog.ts` 的
28
- * `CatalogFetchJson` 同一条纪律:能力由宿主注入,库不自己 `require('fs')`)。
29
- * **tmp+rename 的原子性是 `writeAtomic` 实现方的契约**,本文件在类型注释里把它写成要求;
30
- * Node 宿主的十行参考实现见 `CatalogCachePort.writeAtomic` 的注释。
31
- * 缺席该口 ⇒ 缓存腿整条不启用(`cacheHit` 键**缺席**,不是 `false` —— 「没查过」和「查过没用上」
32
- * 是两件事,[honest-absence-not-fabricated-zero])。
33
- *
34
- * ## 🔴 `forceRefresh` 为什么没有(与设计档 §1 签名的偏差)
35
- *
36
- * 本 loader **恒先走网络**,缓存只在候选链**全败**时顶上(设计档 §3 自己就是这么定的读时机)。
37
- * 也就是说没有任何一条「优先吃缓存」的路径可供 `forceRefresh` 去绕过 —— 收下这个参数只会
38
- * 得到一个恒为空操作的旋钮,而空操作的旋钮是**假 affordance**(调用方以为自己强制刷新了)。
39
- * 真要「手动刷新」,直接再调一次本函数就是最新语义。
40
- *
41
- * ## 时钟
42
- *
43
- * 与 `catalog.ts` 同一口径:**绝不偷读时钟**。`nowMs` 缺席 ⇒ 缓存信封不写 `fetchedAt`、
44
- * 陈旧判定不做(`cacheStale` 键缺席),而不是 `Date.now()` 兜底。
45
- */
46
1
  import { hostEnv } from '../hostEnv.js';
47
2
  import { resolveModelCatalog, } from './catalog.js';
48
- /**
49
- * 默认候选链(design/166 §1;clay 裁「可配置,默认 github」)。
50
- * 第二跳 jsDelivr 对 gh 仓是被动 CDN、零接入成本 —— 这就是大陆可达性的零维护替身。
51
- * 🔴 顺序即优先级:逐源尝试,前一跳成了就不拨下一跳。
52
- */
53
3
  export const DEFAULT_CATALOG_SOURCES = [
54
4
  'https://raw.githubusercontent.com/sema-agent/sema-model-catalog/main/dist/catalog.json',
55
5
  'https://cdn.jsdelivr.net/gh/sema-agent/sema-model-catalog@main/dist/catalog.json',
56
6
  ];
57
- /** 默认域白名单 = 默认链两跳的 host。用户显式配置的源 host 在运行期并入(用户自担)。 */
58
7
  export const CATALOG_DEFAULT_HOSTS = ['raw.githubusercontent.com', 'cdn.jsdelivr.net'];
59
- /** 候选链的 env 键(一键安装脚本改这里;settings.json `env` 块同名同义,见 design/166 §2)。 */
60
8
  export const CATALOG_SOURCES_ENV = 'SEMA_CATALOG_SOURCES';
61
- /** 每源传输预算(design/166 §1:onboard 不能被网络拖住)。 */
62
9
  const DEFAULT_CATALOG_TIMEOUT_MS = 3500;
63
- /** 缓存相对 configHome 的落点(design/166 §3)。 */
64
10
  const CATALOG_CACHE_RELATIVE_PATH = 'cache/model-catalog.json';
65
- /** 缓存陈旧阈值:30 天(照用,但如实标 stale)。 */
66
11
  const CATALOG_CACHE_STALE_MS = 30 * 24 * 60 * 60 * 1000;
67
- /** 单源允许的重定向跳数上限(有界:302 环不许把 onboard 转死)。 */
68
12
  const MAX_REDIRECTS = 5;
69
- /** 异常 → 短 detail(只取 message 前段,绝不带栈、绝不带载荷;与 catalog.ts 同一口径)。 */
70
13
  function shortError(e) {
71
14
  return (e instanceof Error ? e.message : String(e)).slice(0, 160);
72
15
  }
73
- /** `SEMA_CATALOG_SOURCES` 的逗号列表解析(去空白、丢空项;缺席 ⇒ 空列表)。 */
74
16
  export function parseCatalogSources(raw) {
75
17
  if (typeof raw !== 'string')
76
18
  return [];
@@ -79,10 +21,6 @@ export function parseCatalogSources(raw) {
79
21
  .map((s) => s.trim())
80
22
  .filter((s) => s.length > 0);
81
23
  }
82
- /**
83
- * 候选链优先级(design/166 §2,高→低):env `SEMA_CATALOG_SOURCES` > 调用方(settings)> 默认链。
84
- * 🔴 「配了但解析出空列表」按**没配**处理:空链会把目录腿整条静默关掉,而调用方以为自己配了。
85
- */
86
24
  export function resolveCatalogSources(opts) {
87
25
  const env = opts?.env ?? hostEnv();
88
26
  const fromEnv = parseCatalogSources(env[CATALOG_SOURCES_ENV]);
@@ -93,29 +31,23 @@ export function resolveCatalogSources(opts) {
93
31
  return given;
94
32
  return [...DEFAULT_CATALOG_SOURCES];
95
33
  }
96
- /** 取 URL 的 host(含 port);解析不动 ⇒ undefined。 */
97
34
  function hostOf(url) {
98
35
  let parsed;
99
36
  try {
100
37
  parsed = new URL(url);
101
38
  }
102
39
  catch {
103
- parsed = undefined; // 解析不动 = 不是合法 URL,调用方按「拒」处理(判决在 isAllowedCatalogUrl)
40
+ parsed = undefined;
104
41
  }
105
42
  return parsed?.host;
106
43
  }
107
- /**
108
- * 传输面硬门:**https + host 在白名单内**。拨号之前判 —— 不合格的地址连请求都不发。
109
- * 🔴 白名单是**域**的门、https 是**协议**的门,两道都要过:白名单里的 host 走 http 照样拒
110
- * (明文信道下目录可被改写成钓鱼网关,而目录决定的正是出站地址)。
111
- */
112
44
  export function isAllowedCatalogUrl(url, allowedHosts) {
113
45
  let parsed;
114
46
  try {
115
47
  parsed = new URL(url.trim());
116
48
  }
117
49
  catch {
118
- parsed = undefined; // 非法 URL ⇒ 拒(下面那一行判它)
50
+ parsed = undefined;
119
51
  }
120
52
  if (parsed === undefined)
121
53
  return false;
@@ -123,15 +55,12 @@ export function isAllowedCatalogUrl(url, allowedHosts) {
123
55
  return false;
124
56
  return allowedHosts.has(parsed.host);
125
57
  }
126
- /** 旁签地址 = 同源同路径换后缀(design/165 §4:`dist/catalog.sha256`)。 */
127
58
  export function catalogShaUrlFor(catalogUrl) {
128
59
  return catalogUrl.endsWith('.json') ? `${catalogUrl.slice(0, -'.json'.length)}.sha256` : `${catalogUrl}.sha256`;
129
60
  }
130
- /** 缓存文件绝对路径(design/166 §3)。POSIX 分隔符 —— Node 侧 `path.join` 对它是幂等的。 */
131
61
  export function catalogCachePath(configHome) {
132
62
  return `${configHome.replace(/\/+$/, '')}/${CATALOG_CACHE_RELATIVE_PATH}`;
133
63
  }
134
- /** SHA-256 hex(WebCrypto;Node 18+ 与浏览器都有。拿不到实现 ⇒ null = 本次不比对)。 */
135
64
  async function sha256Hex(text) {
136
65
  const g = globalThis;
137
66
  const subtle = g.crypto?.subtle;
@@ -144,7 +73,6 @@ async function sha256Hex(text) {
144
73
  out += b.toString(16).padStart(2, '0');
145
74
  return out;
146
75
  }
147
- /** sha 文件内容 → hex(容忍 `<hex> <filename>` 的 shasum 形与大小写)。认不出 ⇒ null。 */
148
76
  function parseShaFile(text) {
149
77
  const m = /\b([0-9a-fA-F]{64})\b/.exec(text);
150
78
  return m?.[1] === undefined ? null : m[1].toLowerCase();
@@ -166,8 +94,6 @@ async function guardedGet(url, allowedHosts, fetchImpl, timeoutMs) {
166
94
  let res;
167
95
  let dialError;
168
96
  try {
169
- // 🔴 `redirect:'manual'` 是这道门的**执行体**:默认的 'follow' 会让 undici/浏览器替我们
170
- // 跟到域外去(白名单被 302 绕过);手动跟 = 每一跳都回到上面那道 isAllowedCatalogUrl。
171
97
  res = await fetchImpl(current, { method: 'GET', redirect: 'manual', signal: controller.signal });
172
98
  }
173
99
  catch (e) {
@@ -178,8 +104,6 @@ async function guardedGet(url, allowedHosts, fetchImpl, timeoutMs) {
178
104
  }
179
105
  if (res === undefined)
180
106
  return { outcome: 'network-error', detail: dialError ?? 'dial failed' };
181
- // 浏览器的 `redirect:'manual'` 给的是 opaqueredirect(status 0、无头)——看不见目标就
182
- // **拒**(fail-closed):看不见的重定向恰恰是这道门要防的那种。
183
107
  if (res.status === 0)
184
108
  return { outcome: 'redirect-opaque', status: 0, detail: 'opaque redirect: target not inspectable' };
185
109
  if (res.status >= 300 && res.status < 400) {
@@ -214,16 +138,11 @@ async function guardedGet(url, allowedHosts, fetchImpl, timeoutMs) {
214
138
  }
215
139
  return { outcome: 'redirect-loop', detail: `more than ${MAX_REDIRECTS} redirects` };
216
140
  }
217
- /**
218
- * 解析出一份可用的 provider 目录 —— **候选链 + 传输硬门 + 旁签 + 缓存**,载荷判决全委托
219
- * `resolveModelCatalog`。🔴 **永不 reject**:onboard 不能因为网络死。
220
- */
221
141
  export async function loadCatalogWithSources(opts) {
222
142
  const sources = resolveCatalogSources({
223
143
  ...(opts?.env !== undefined ? { env: opts.env } : {}),
224
144
  ...(opts?.sources !== undefined ? { sources: opts.sources } : {}),
225
145
  });
226
- // 白名单 = 默认两域 + **用户显式配置的源 host**(显式意愿=自担;doctor 如实标 user-configured)。
227
146
  const allowedHosts = new Set(CATALOG_DEFAULT_HOSTS);
228
147
  for (const s of sources) {
229
148
  const h = hostOf(s);
@@ -235,9 +154,6 @@ export async function loadCatalogWithSources(opts) {
235
154
  const attempts = [];
236
155
  const warnings = [];
237
156
  let lastDialed;
238
- // ── 委托:载荷判决 / 三层合并 / 来源标注全在 resolveModelCatalog ──────────────────────────
239
- // 传给它的 `fetchJson` 只是「把已经拿到的这一份交出去」的闭包 —— 候选链与传输门是本文件的活,
240
- // 校验与合并是它的活,两边不重叠也不互相重写。
241
157
  const resolveWith = async (p) => {
242
158
  const onlineUrl = p?.url ?? lastDialed ?? sources[0];
243
159
  const resolveOpts = {
@@ -252,19 +168,6 @@ export async function loadCatalogWithSources(opts) {
252
168
  };
253
169
  return resolveModelCatalog(resolveOpts);
254
170
  };
255
- // ══ 🔴 [4982] 候选② 的**同形存量**(0.42.0 异源复审 [high] 采纳)══════════════════════════
256
- //
257
- // 病根与「缓存腿从没被问过」是**同一条**:候选链的停止判据锚在**传输层**(`hit !== null`)而不是
258
- // **内容判决**。后果比缓存那一格更重 —— 首源发得下来但内容不合格时,`break` 让**后续健康源
259
- // 永远不会被请求**:一次坏发布、或一个 CDN 的半更新窗,就能把整条后备链压掉。
260
- // ⇒ 停止判据换成 `accepted !== null`(内容判决),逐源「传输 → 旁签 → **内容校验**」,
261
- // 只有真被接受的那一份才停链。
262
- //
263
- // 🔴 `rejected` 是**诚实兜底**不是备选源:全链都被拒时,拿**第一份被拒的**去出结果,
264
- // 这样 `online.reason` 说的是真话(`schema-version-unsupported`),而不是退成
265
- // 「一个源都没连上」(`fetch-failed`)—— 那会把「内容坏了」谎报成「网络坏了」。
266
- // 它**绝不**参与「用哪份目录」的竞争:被拒就是被拒,`online.ok === false` 时合并结果本就
267
- // 回落内置表。
268
171
  let accepted = null;
269
172
  let rejected = null;
270
173
  for (const src of sources) {
@@ -295,8 +198,6 @@ export async function loadCatalogWithSources(opts) {
295
198
  attempts.push({ url: src, outcome: 'invalid-json', ...(got.status !== undefined ? { status: got.status } : {}), detail: parseError });
296
199
  continue;
297
200
  }
298
- // 旁签:同源同路径。拿不到 ⇒ 记 warn 放行(design/165 §4:它是防传输损坏的纵深,
299
- // 不是防主动篡改 —— 把「拿不到」当「篡改」会让 CDN 的半更新窗把整条链打死)。
300
201
  const shaGot = await guardedGet(catalogShaUrlFor(finalUrl), allowedHosts, fetchImpl, timeoutMs);
301
202
  let shaChecked = false;
302
203
  if (shaGot.outcome === 'ok' && shaGot.text !== undefined) {
@@ -321,7 +222,6 @@ export async function loadCatalogWithSources(opts) {
321
222
  }
322
223
  attempts.push({ url: src, outcome: 'ok', ...(got.status !== undefined ? { status: got.status } : {}), shaChecked });
323
224
  const candidate = { url: finalUrl, doc, raw: got.text, shaChecked };
324
- // 内容判决就在这里做 —— 传输成功**不等于**这份目录能用。
325
225
  const verdict = await resolveWith(candidate);
326
226
  if (verdict.online.ok) {
327
227
  accepted = { hit: candidate, base: verdict };
@@ -332,18 +232,11 @@ export async function loadCatalogWithSources(opts) {
332
232
  warnings.push(`catalog source ${finalUrl} was fetched but REJECTED by content validation ` +
333
233
  `(${verdict.online.reason ?? 'unknown'}) — trying the next source`);
334
234
  }
335
- // ── 缓存腿(口缺席 ⇒ 整条不启用,cacheHit 键缺席)────────────────────────────────────────
336
235
  const cache = opts?.cache;
337
236
  const cachePath = opts?.configHome !== undefined ? catalogCachePath(opts.configHome) : undefined;
338
237
  let cacheHit = cache !== undefined ? false : undefined;
339
238
  let cacheStale;
340
239
  let cachedSourceUrl;
341
- /**
342
- * 读一次缓存信封 → `SourceHit`(+ 陈旧判)。**永不抛**;坏形/缺件记 warn 返 null。
343
- * 🔴 [4982] 候选②(0.42.0)提出来的:此前这段是内联的,只有「传输全败」那一条路走得到它。
344
- * 现在有**两个**调用点(传输全败 / 传输 ok 但内容被拒),提成一处才不会有两份读法。
345
- * `alreadyWarned` = 第二次调用时不重复堆同一批 warn(同一份坏缓存说两遍是噪音)。
346
- */
347
240
  const readCachedHit = async (alreadyWarned) => {
348
241
  if (cache === undefined || cachePath === undefined)
349
242
  return null;
@@ -379,8 +272,6 @@ export async function loadCatalogWithSources(opts) {
379
272
  warnings.push('catalog cache envelope missing sourceUrl/catalog (ignored)');
380
273
  return null;
381
274
  }
382
- // 缓存里那份**就是**当初的线上载荷 —— 照样过 validateOnlineCatalog(下面同一条路),
383
- // 不给它开后门:一份当年合法、今天已超区间的文档必须照样被拒。
384
275
  const stale = opts?.nowMs !== undefined && typeof env?.fetchedAt === 'number'
385
276
  ? opts.nowMs - env.fetchedAt > CATALOG_CACHE_STALE_MS
386
277
  : undefined;
@@ -389,30 +280,6 @@ export async function loadCatalogWithSources(opts) {
389
280
  ...(stale !== undefined ? { stale } : {}),
390
281
  };
391
282
  };
392
- // ══ 缓存腿:**内容判决**缺席时才问它(传输全败 与 内容被拒 两条路共用这一格)═══════════════
393
- //
394
- // 🔴 [4982] 候选②(P0-KPI,0.42.0):此前这条腿的门槛是 `hit === null`,而 `hit` 只记录**传输层**
395
- // 结果(200 + 旁签过就算 hit)。于是「线上目录发得下来、但内容被 validateOnlineCatalog 拒」
396
- // 这条缝里,loader 认为「有 hit」⇒ **永不查缓存**(即便缓存里躺着一份上次真正被接受过的
397
- // 合法目录),用户只拿到内置精简表;而 `cacheHit:false` 这个**诚实位反过来说谎** —— 它读起来
398
- // 是「没有缓存」,真相是「有缓存,但从头到尾没人问过它」。
399
- // 判据锚在**真正决定结果的量**上:决定「用不用得上目录」的是**内容判决**(`accepted`),
400
- // 不是传输判决(`hit !== null`)。异源复审 [high] 之后,候选链的停止判据也换成了同一个量
401
- // (见上方 `accepted` 头注)—— 两处同根同治,不留同形存量。
402
- // 🔴 缓存那份**不开后门**:它照样过同一个 `resolveModelCatalog`。也被拒时**不拿它顶替**
403
- // (一份同样不合格的文档顶替不了什么),`cacheHit` 诚实留在 `false` —— 本位的语义是
404
- // 「这次的目录**是从缓存来的**」,不是「问过缓存」;抬成 true 会让「缓存救场了」与
405
- // 「缓存也坏了」在读数上不可分。「问过但没用上」那件事由 warning 说。
406
- /**
407
- * 线上腿的**归因汇总**(异源复审 [medium] 采纳,0.42.0)。
408
- *
409
- * 🔴 修的是一句会指错方向的话:此前只要**任一**源产生 `rejected`,回落 warning 就写
410
- * 「every online catalog source was fetched but REJECTED」。而其余源完全可能是网络失败、
411
- * 畸形 JSON、sha 不匹配、或被域白名单挡下 —— 把一次 CDN/网络事故说成「目录发布损坏」,
412
- * 运维会照着错误方向查,恢复时间平白变长。
413
- * ⇒ 按 `attempts` 的**真实计数**分两类措辞:全部进过内容校验且全被拒 ⇒ 说 "every … REJECTED";
414
- * 混合故障 ⇒ 逐类报数,并保留内容拒绝的 reason。
415
- */
416
283
  const onlineLegSummary = () => {
417
284
  const contentRejected = attempts.filter((a) => a.outcome === 'ok').length;
418
285
  const otherFailures = attempts.length - contentRejected;
@@ -446,8 +313,6 @@ export async function loadCatalogWithSources(opts) {
446
313
  warnings.push(`${onlineLegSummary()} — fell back to the cached catalog (${cached.hit.url})`);
447
314
  }
448
315
  else {
449
- // 缓存那份也不合格 ⇒ 诚实回落:`online` 报的仍是**线上腿自己**的判词(有被拒的那一份就用
450
- // 它的,一个都没连上就是 fetch-failed),绝不拿缓存的判词冒充线上的。
451
316
  payload = rejected?.hit ?? null;
452
317
  base = rejected?.base ?? (await resolveWith(null));
453
318
  warnings.push(`${onlineLegSummary()}, and the cached catalog was rejected by content validation too — ` +
@@ -458,8 +323,6 @@ export async function loadCatalogWithSources(opts) {
458
323
  payload = rejected?.hit ?? null;
459
324
  base = rejected?.base ?? (await resolveWith(null));
460
325
  }
461
- // 线上腿真的被接受了才写缓存(被 validateOnlineCatalog 拒掉的载荷绝不进缓存 ——
462
- // 否则下一次断网时我们会把一份已知不合格的文档当兜底)。
463
326
  if (base.online.ok && payload !== null && cacheHit !== true && cache !== undefined && cachePath !== undefined) {
464
327
  const envelope = {
465
328
  ...(opts?.nowMs !== undefined ? { fetchedAt: opts.nowMs } : {}),
@@ -1,81 +1,42 @@
1
- /** vision 生效值的**出处**(UI 那一行「当前生效值与来源」的唯一数据源)。 */
2
- export type VisionSource =
3
- /** 这条 entry 自己显式表态。 */
4
- 'entry'
5
- /** entry 不表态,取 `inferFamily` 的家族缺省。 */
6
- | 'family'
7
- /** 两边都不表态 —— 生效值**未知**,绝不折算成 false。 */
8
- | 'unknown';
9
- /**
10
- * {@link resolveEntryVision} 的结果(具名:本仓 typeshape 门对导出面内联匿名形有棘轮)。
11
- *
12
- * 🔴 `value: undefined` 与 `value: false` 是**两件事**:前者「没人说过这个模型能不能看图」,
13
- * 后者「确认它不能看图」。把前者渲成后者会永久封死一个真能力。
14
- */
1
+ export type VisionSource = 'entry' | 'family' | 'unknown';
15
2
  export interface EntryVisionResolution {
16
3
  value: boolean | undefined;
17
4
  source: VisionSource;
18
5
  }
19
- /**
20
- * 解析一条 entry 的 vision **生效值 + 来源**。纯函数(只吃 modelId/vision 两位,零 IO)。
21
- *
22
- * 🔴 [honest-absence]:家族与 entry 都不表态时给 `undefined`(未知),**不**给 `false`。
23
- * ⚠️ 「未知」只是**客户端这一层**诚实:引擎侧 registry-core `ModelEntry.vision` 是
24
- * `z.boolean().default(false)`,键缺席在那边就等于 false。**呈现层必须把这个后果一起说出来**,
25
- * 别让用户以为「未知」= 引擎会自己去问供应商。
26
- */
27
6
  export declare function resolveEntryVision(entry: {
28
7
  modelId: string;
29
8
  vision?: boolean;
30
9
  }): EntryVisionResolution;
31
- /** 一处挡住删除的引用。`where` 是坐标(用户照着去改),`fix` 是这一处该怎么解开。 */
32
10
  export type PoolDeleteBlocker = {
33
11
  kind: 'default' | 'roles' | 'roster' | 'unreadable';
34
12
  where: string;
35
13
  fix: string;
36
14
  };
37
- /** 一条「照删,但你该知道」的后果。删除**不**被阻断,确认屏必须把它渲出来。 */
38
15
  export type PoolDeleteWarning = {
39
16
  kind: 'allowlist-empties' | 'tier-binding';
40
- /** 人话:删了会发生什么(不是坐标,是后果)。 */
41
17
  consequence: string;
42
18
  };
43
- /**
44
- * 档位组的**归一形**(settings `semaTierGroups`)。两处删除判定共用,单独具名以免两边各写一份内联形。
45
- */
46
19
  export interface TierGroupsView {
47
20
  active: string;
48
21
  groups: Record<string, {
49
22
  tiers: Record<string, string | undefined>;
50
23
  }>;
51
24
  }
52
- /** {@link computeDeleteWarnings} 的入参(具名:导出面禁内联匿名形,B4 棘轮)。 */
53
25
  export interface DeleteWarningsInput {
54
26
  id: string;
55
- /**
56
- * 已读到的 models.json 文档切片。🔴 两位是 `unknown` 而不是收窄形 —— 它们是**盘上 JSON 的原样**,
57
- * 属主是引擎的 models 域 schema(开集、可演进)。在这里手抄一份结构就是手抄一份会漂的上游形;
58
- * 本函数对它们的处理本来就是「只判是不是数组/对象,然后逐项过滤」,窄读没有消费者。
59
- * `null` = 调用方没读到文档(两义分流在调用方,见 {@link computeDeleteBlockers} 头注)。
60
- */
61
27
  doc: {
62
28
  atModelAllowlist?: unknown;
63
29
  tierGroups?: unknown;
64
30
  } | null;
65
- /** settings `semaTierGroups` 归一形(缺席 ⇒ 回落 `doc.tierGroups`,与写路径的取值序同源)。 */
66
31
  tierGroups: TierGroupsView | null;
67
- /** 删除**之后**仍在池里的 catalog name 全集(判「有效名单会不会变空」要的就是它)。 */
68
32
  survivingIds: readonly string[];
69
33
  }
70
- /** {@link computeDeleteBlockers} 的入参(具名:同上)。 */
71
34
  export interface DeleteBlockersInput {
72
35
  id: string;
73
- /** 已读到的 models.json 文档切片;两位 `unknown` 的理由同 {@link DeleteWarningsInput.doc}。 */
74
36
  doc: {
75
37
  default?: unknown;
76
38
  roles?: unknown;
77
39
  } | null;
78
- /** roster 归一形(激活组名 + 全部组的 slot 表;**分阶段组也在里面**,它们是真引用)。 */
79
40
  rosters: {
80
41
  active: string;
81
42
  rosters: Record<string, {
@@ -83,20 +44,5 @@ export interface DeleteBlockersInput {
83
44
  }>;
84
45
  } | null;
85
46
  }
86
- /**
87
- * 纯判定:删这条 entry 会带来哪些**语义变化**(不阻断)。
88
- *
89
- * 两格,都是「链不断但意思变了」:
90
- * ① `atModelAllowlist` 的**有效名单**会被删空 ⇒ 按 registry-core 语义**全部启用模型都可 @**
91
- * —— 一次静默的权限放宽,必须说;名单里还有别人 ⇒ 只是缩小名单,不用说。
92
- * ② `semaTierGroups` 里有绑定指着它 ⇒ 删完那条绑定悬空 ⇒ 下次按档位解析会落到**另一个模型**上
93
- * (而不是报错),用户会以为「我明明配了 pro 档」。
94
- */
95
47
  export declare function computeDeleteWarnings(input: DeleteWarningsInput): PoolDeleteWarning[];
96
- /**
97
- * 纯判定:这条 entry 现在被哪些**断链类**引用挡着。空数组 = 可以删。
98
- *
99
- * 🔴 `doc === null` 的两义**在调用方分流**:这里只认「已读到的文档」;读不出来时调用方自己产
100
- * `unreadable` 阻断 —— 那一刻我们并不知道谁指着它,fail-closed 拒删比「猜没人指着」安全。
101
- */
102
48
  export declare function computeDeleteBlockers(input: DeleteBlockersInput): PoolDeleteBlocker[];
@@ -1,44 +1,7 @@
1
- /**
2
- * modelSupplyRules.ts — **Model Hub 供给面的三端公共判定**(#318 件③ 上收,cli [4752] 预告的
3
- * 「resolveEntryVision / computeDeleteBlockers / computeDeleteWarnings 三纯函数」)。
4
- *
5
- * ── 为什么在这里 ────────────────────────────────────────────────────────────────────────────
6
- * 三端(TUI / web / desktop)的 Model Hub 要回答同样的三个问题:
7
- * ① 这条模型档的 `vision` **生效值**是什么、**出处**是哪(自己表态 / 家族缺省 / 没人说过);
8
- * ② 删这条档会不会**断链**(该拒删,并指路);
9
- * ③ 删这条档会不会**悄悄改语义**(该照删,但确认屏必须逐条说出后果)。
10
- * 判定放在库里、装配与呈现留端 —— 三端各写一遍的话,三个 Hub 会对「同一条档能不能删」给出三个
11
- * 答案,而其中两个是在用户按下 y 之后才被发现的。
12
- *
13
- * ── 🔴 零 IO(本模块的立身之本)──────────────────────────────────────────────────────────────
14
- * 三个函数**只吃已读好的数据**,不碰文件系统、不认识 `models.json` 在哪、不持锁。读盘那半场留在
15
- * 各端(壳 = `modelChannels.poolDeleteBlockers` / `poolDeleteWarnings`,它们读完再调这里)。
16
- * 分层的判据在 cli 侧成文:`doc === null` 的两义**在调用方分流** —— 本模块只认「已读到的文档」,
17
- * 读不出来时由调用方自己产 `unreadable` 阻断(那一刻并不知道谁指着它,fail-closed 拒删比
18
- * 「猜没人指着」安全)。
19
- *
20
- * ── ⚠️ 上收差分(逐条,行为零改动)────────────────────────────────────────────────────────
21
- * · `resolveEntryVision` 的返回型从**内联匿名对象**改为具名 {@link EntryVisionResolution}
22
- * —— 本仓 typeshape 门对导出面的内联匿名形有棘轮(B4)。**结构逐字相同**,壳侧剪切时零适配。
23
- * · `inferFamily` 的来源从壳内 re-export 改为同包 `./providerPresets.js` 直取(壳那份本来就是
24
- * 本包的 re-export)。⚠️ 与 #318 件③ 的**分层遮蔽修同批**:修前 `inferFamily(deepseek-*)?.vision`
25
- * 结构性恒 `undefined`,所以 `source:'family'` 这一档对 deepseek 族**从来没走到过**;修后才真的
26
- * 走得到。上收与修同批落地是刻意的 —— 只上收不修,等于把一条死分支原样搬进三端。
27
- * · `isPlainObject` 在壳里是文件级私有,这里同形自持(不新增公面导出)。
28
- */
29
1
  import { inferFamily } from './providerPresets.js';
30
- /** 局部判据:普通对象(非 null、非数组)。与壳侧 `modelChannels.isPlainObject` 同形。 */
31
2
  function isPlainObject(v) {
32
3
  return typeof v === 'object' && v !== null && !Array.isArray(v);
33
4
  }
34
- /**
35
- * 解析一条 entry 的 vision **生效值 + 来源**。纯函数(只吃 modelId/vision 两位,零 IO)。
36
- *
37
- * 🔴 [honest-absence]:家族与 entry 都不表态时给 `undefined`(未知),**不**给 `false`。
38
- * ⚠️ 「未知」只是**客户端这一层**诚实:引擎侧 registry-core `ModelEntry.vision` 是
39
- * `z.boolean().default(false)`,键缺席在那边就等于 false。**呈现层必须把这个后果一起说出来**,
40
- * 别让用户以为「未知」= 引擎会自己去问供应商。
41
- */
42
5
  export function resolveEntryVision(entry) {
43
6
  if (typeof entry.vision === 'boolean')
44
7
  return { value: entry.vision, source: 'entry' };
@@ -47,24 +10,11 @@ export function resolveEntryVision(entry) {
47
10
  return { value: fam, source: 'family' };
48
11
  return { value: undefined, source: 'unknown' };
49
12
  }
50
- /**
51
- * 纯判定:删这条 entry 会带来哪些**语义变化**(不阻断)。
52
- *
53
- * 两格,都是「链不断但意思变了」:
54
- * ① `atModelAllowlist` 的**有效名单**会被删空 ⇒ 按 registry-core 语义**全部启用模型都可 @**
55
- * —— 一次静默的权限放宽,必须说;名单里还有别人 ⇒ 只是缩小名单,不用说。
56
- * ② `semaTierGroups` 里有绑定指着它 ⇒ 删完那条绑定悬空 ⇒ 下次按档位解析会落到**另一个模型**上
57
- * (而不是报错),用户会以为「我明明配了 pro 档」。
58
- */
59
13
  export function computeDeleteWarnings(input) {
60
14
  const { id, doc, tierGroups, survivingIds } = input;
61
15
  const out = [];
62
16
  const allow = doc?.atModelAllowlist;
63
17
  if (Array.isArray(allow)) {
64
- // 🔴 判据锚在**有效名单**上,不锚「字面上是不是只有它一个」:落盘闸口会把**所有悬空名**一起
65
- // 滤掉,所以 `['victim','already-dangling']` 这种名单删掉 victim 之后有效名单同样变空 = 同样
66
- // 放宽到「全部启用模型都可 @」,而 `every(n => n === id)` 那条旧判据对它一声不吭 = 承诺给用户
67
- // 的权限提示上的**假阴**。
68
18
  const declared = allow.filter((x) => typeof x === 'string');
69
19
  const effectiveBefore = declared.filter(n => n === id || survivingIds.includes(n));
70
20
  const effectiveAfter = declared.filter(n => n !== id && survivingIds.includes(n));
@@ -75,9 +25,6 @@ export function computeDeleteWarnings(input) {
75
25
  });
76
26
  }
77
27
  }
78
- // 档位绑定的取值序与写路径同源:settings 现值优先,settings 没有档位组时回落 models.json 文件里
79
- // 那份投影(只扫 settings 会漏掉「settings 无档位组、models.json 自带 tierGroups」那一格 ——
80
- // 那份绑定同样会在写路径被 byId 静默滤掉)。
81
28
  const groups = tierGroups
82
29
  ? Object.entries(tierGroups.groups).map(([g, def]) => [g, def?.tiers ?? {}])
83
30
  : Array.isArray(doc?.tierGroups)
@@ -97,12 +44,6 @@ export function computeDeleteWarnings(input) {
97
44
  }
98
45
  return out;
99
46
  }
100
- /**
101
- * 纯判定:这条 entry 现在被哪些**断链类**引用挡着。空数组 = 可以删。
102
- *
103
- * 🔴 `doc === null` 的两义**在调用方分流**:这里只认「已读到的文档」;读不出来时调用方自己产
104
- * `unreadable` 阻断 —— 那一刻我们并不知道谁指着它,fail-closed 拒删比「猜没人指着」安全。
105
- */
106
47
  export function computeDeleteBlockers(input) {
107
48
  const { id, doc, rosters } = input;
108
49
  const out = [];
@@ -115,7 +56,6 @@ export function computeDeleteBlockers(input) {
115
56
  }
116
57
  if (doc && isPlainObject(doc.roles)) {
117
58
  for (const [role, target] of Object.entries(doc.roles)) {
118
- // `{ select }` 形是**按 tier/needs 现选**,不是 catalog 引用 —— 删模型影响不到它,不拦。
119
59
  if (!isPlainObject(target) || typeof target.model !== 'string')
120
60
  continue;
121
61
  if (target.model !== id)
@@ -134,8 +74,6 @@ export function computeDeleteBlockers(input) {
134
74
  for (const [slot, ref] of Object.entries(def.slots)) {
135
75
  if (ref !== id)
136
76
  continue;
137
- // 分阶段(未激活)组同样是真引用:它一被 switchRoster 激活就要按 slot 解析池,指着已删的
138
- // entry = 激活当场断链。只看 active.slots 的实现会把分阶段组整个漏掉。
139
77
  const isActive = name === rosters.active;
140
78
  out.push({
141
79
  kind: 'roster',
@@ -1,47 +1,4 @@
1
- /**
2
- * providerAuth.ts — **模型供应商凭证轨**的双轨 seam(design/166 §5「B 档:设备码流」,2026-08-04)。
3
- *
4
- * ## 边界(先说清楚它**不是**什么)
5
- *
6
- * 🔴 [reuse-single-sso-no-parallel-auth]:registry SSO(cloudAuth,「登录 sema」那条)**绝不复用、
7
- * 绝不被改造**。本文件是**模型供应商**的凭证轨(拿一把 API key 去调某家模型),与 registry 登录轨
8
- * 物理分文件、零共享代码。两者唯一的相似点是「都会弹一个浏览器」,那不构成复用理由。
9
- *
10
- * 🔴 design/166 §6 已定谳:**授权码 + PKCE(C 档)不做、不留 seam、不留枚举位** —— Anthropic 侧
11
- * 条款明文禁止第三方代理其订阅凭证,而公开实现全都依赖冒用官方 client_id + 伪装 UA。本文件里
12
- * 因此**只有** api_key 与 device_code 两轨,没有第三个 kind 位。
13
- *
14
- * ## 能力真值 = 目录键 ∧ 包内实现(两者与)
15
- *
16
- * 目录(design/165 §2)里的 `deviceAuth` 键**只是能力开关**,client_id 与端点**绝不目录化下发**
17
- * (远端可改鉴权端点 = 钓鱼面)。所以 UI 是否显示「Sign in with device code」的判据是
18
- * `supportsDeviceCodeAuth(id, preset.deviceAuth)`:
19
- * · 目录说有、包里没有 ⇒ **不显示**(防目录先行造出一个点了没反应的假 affordance);
20
- * · 包里有、目录没说 ⇒ **不显示**(那家还没被目录确认支持)。
21
- *
22
- * ## 🔴 `DEVICE_AUTH_PROVIDERS` 今天是**空表**(宁空勿假 —— 这是有意的,不是没写完)
23
- *
24
- * 设计档写「首发 1 家:GitHub Copilot 形」。落码时的事实是:device flow 需要一个 **client_id**,
25
- * 而公开可查到的 Copilot client_id 全部是**别的编辑器插件自己的** app id(copilot.vim / VS Code
26
- * 扩展)。把它们编进我们的包 = 以别人的 app 身份向 GitHub 发起授权,与 §6 判 C 档出局时用的
27
- * **同一条判据**(「冒用官方 client_id」是主动欺骗,不是灰区)。我们自己名下的 OAuth app 尚未
28
- * 申请 ⇒ 这一格的诚实值是**空**,而不是先填一个能跑的别人的号。
29
- *
30
- * 空表不是把这条腿废了:轮询器、退避、上限、终态词表全部在位并被门覆盖(门用一份合成 descriptor
31
- * 跑真 HTTP)。**注册一家 = 往这张表加一行**,零代码改动。在那之前 `supportsDeviceCodeAuth`
32
- * 对每一家如实返回 false,UI 不显示该选项 —— 这正是「能力真值 = 两者与」自带的正确降级。
33
- *
34
- * ## key 到手之后
35
- *
36
- * 🔴 **落盘不在包内做**。`poll()` 把 key 原样返给宿主,宿主走**与手填完全相同的那条路径**
37
- * (壳 = `applyOnboard.ts` 写 settings `env.MODEL_API_KEY`)。两树都没有 auth.json 实体,
38
- * 本文件也不新建任何持久面(design/166 §5 复核 M4:零新持久面,禁造)。
39
- */
40
1
  import type { ProviderPreset, ProviderDeviceAuthHint } from './providerPresets.js';
41
- /**
42
- * 一个 provider 可用的凭证轨。
43
- * `api_key` 是现状唯一轨(手填,env 名来自 preset.authEnv);`device_code` 是 B 档新轨。
44
- */
45
2
  export type ProviderAuthMethod = {
46
3
  kind: 'api_key';
47
4
  env: string;
@@ -49,99 +6,39 @@ export type ProviderAuthMethod = {
49
6
  kind: 'device_code';
50
7
  providerId: string;
51
8
  };
52
- /**
53
- * 设备码端点三件套 —— **编译进包**(design/165 §2 红线:client_id / token 端点 / authorize 端点
54
- * 一律不进目录)。
55
- */
56
9
  export interface DeviceCodeEndpoints {
57
10
  clientId: string;
58
- /** RFC 8628 device authorization endpoint。 */
59
11
  deviceCodeUrl: string;
60
- /** token endpoint(轮询这一个)。 */
61
12
  tokenUrl: string;
62
- /** 申请的 scope(留空 = 不发这个参数)。 */
63
13
  scope?: string;
64
14
  }
65
- /** 包内登记的一家设备码 provider。 */
66
15
  export interface DeviceAuthProvider {
67
- /** 与目录/preset 的 `id` 同一个命名空间(用户 settings 里的持久引用)。 */
68
16
  providerId: string;
69
17
  label: string;
70
18
  endpoints: DeviceCodeEndpoints;
71
19
  }
72
- /**
73
- * 包内实现的设备码 provider 表。
74
- * 🔴 **今天是空的,理由见文件头**(宁空勿假:没有我们自己名下的 client_id 之前,填一行 = 冒用)。
75
- * 加一家 = 加一行;端点必须 https(门 ④a 逐行校)。
76
- */
77
20
  export declare const DEVICE_AUTH_PROVIDERS: readonly DeviceAuthProvider[];
78
- /** 包内是否实现了这一家(能力真值的一半)。 */
79
21
  export declare function deviceAuthProviderFor(providerId: string): DeviceAuthProvider | undefined;
80
- /**
81
- * UI 是否显示「设备码登录」= **目录说有** ∧ **包里有实现**。
82
- * 任一缺席 ⇒ false(见文件头「能力真值」段)。
83
- */
84
22
  export declare function supportsDeviceCodeAuth(providerId: string, catalogHint: ProviderDeviceAuthHint | undefined): boolean;
85
- /**
86
- * 一家 provider 摆给用户的轨(顺序 = UI 呈现序)。`api_key` 恒在;`device_code` 只在能力真值成立时出。
87
- */
88
23
  export declare function providerAuthMethods(preset: ProviderPreset): ProviderAuthMethod[];
89
- /**
90
- * 一次 `poll()` 的结局。
91
- *
92
- * 🔴 比设计档草案多两个终态,理由是诚实:草案只有 `pending|ok|expired`,于是
93
- * 「用户点了拒绝」与「会话被本地取消」都只能冒充 `expired`(= 骗用户「过期了,再来一次」,
94
- * 而事实是再来一次还会被拒 / 是他自己取消的)。两者各占一位:
95
- * · `denied` —— 授权服务器说 access_denied;
96
- * · `cancelled` —— 本地调过 `cancel()`。
97
- */
98
24
  export type DeviceCodeStatus = 'pending' | 'ok' | 'expired' | 'denied' | 'cancelled';
99
25
  export interface DeviceCodePollResult {
100
26
  status: DeviceCodeStatus;
101
- /** 只在 `ok` 时在场。🔴 宿主拿去走与手填同一条落盘路径;包内零持久面。 */
102
27
  key?: string;
103
- /** 人话细节(分诊用;绝不带 key、绝不带完整响应体)。 */
104
28
  detail?: string;
105
29
  }
106
- /**
107
- * 一次设备码会话。**宿主驱动**:UI 渲大字码 + URL,自己按 `intervalMs` 起节拍调 `poll()`
108
- * (等待型判据,不固定拍)。包内不起定时器 —— 定时器是宿主资产(与 `TimersPort` 同一条纪律)。
109
- */
110
30
  export interface DeviceCodeSession {
111
- /** 用户要在浏览器里输入的大字码。 */
112
31
  userCode: string;
113
- /** 用户要打开的验证页。 */
114
32
  verificationUrl: string;
115
- /** 建议轮询间隔(毫秒)。429 / slow_down 之后会**变大**,宿主每拍都该重读这个值。 */
116
33
  intervalMs: number;
117
- /** 本会话的墙钟终点(min(服务端 expires_in, 15min 上限))。 */
118
34
  expiresAtMs: number;
119
35
  poll(): Promise<DeviceCodePollResult>;
120
36
  cancel(): void;
121
37
  }
122
- /**
123
- * 设备码流的宿主注入面。
124
- * 🔴 `nowMs` **必填**:5s 间隔与 15min 上限都是墙钟判据,而本包绝不偷读时钟
125
- * (与 `catalog.ts` / `AdapterContext.now` 同一口径)。宿主传 `() => Date.now()`。
126
- */
127
38
  export interface DeviceCodeAuthOptions {
128
39
  nowMs: () => number;
129
- /** 传输注入口(缺省全局 `fetch`)。 */
130
40
  fetchImpl?: typeof fetch;
131
- /** 单次 HTTP 预算(缺省 `DEVICE_CODE_HTTP_TIMEOUT_MS`)。 */
132
41
  timeoutMs?: number;
133
42
  }
134
- /**
135
- * 开一次设备码会话 —— **端点由调用方给**(包内表里那一行,或门的合成 descriptor)。
136
- * 🔴 端点是**编译进包的常量**,不是远端数据,所以本函数不对它再做白名单;真正的门是
137
- * 「client_id/端点绝不目录化下发」(design/165 §2 红线)+ 表内每行必须 https(门 ④a)。
138
- *
139
- * 失败(网络 / 非 2xx / 缺 user_code)⇒ **throw**(§C1 三选一里的 throw):开不出会话就是开不出,
140
- * 绝不返回一个永远 pending 的假会话。
141
- */
142
43
  export declare function openDeviceCodeSession(endpoints: DeviceCodeEndpoints, opts: DeviceCodeAuthOptions): Promise<DeviceCodeSession>;
143
- /**
144
- * 按 providerId 开设备码会话(UI 入口)。
145
- * 包内没有这一家 ⇒ **throw**(UI 本就不该显示这个选项;走到这里说明能力判据被绕过了)。
146
- */
147
44
  export declare function beginDeviceCodeAuth(providerId: string, opts: DeviceCodeAuthOptions): Promise<DeviceCodeSession>;