@sema-agent/client-core 0.77.0 → 0.77.2

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 +23 -1
  2. package/README.md +5 -4
  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 +1 -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 +13 -358
  182. package/dist/hitl/persistedRulesWire.js +0 -310
  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 +1 -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 +0 -249
  284. package/dist/request/taskRequest.js +10 -496
  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 +11 -65
  346. package/dist/subagent/engineTaskHandleWire.js +20 -131
  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 +62 -1
  382. package/package.json +1 -1
@@ -1,87 +1,40 @@
1
1
  import { stamp } from '../types.js';
2
2
  import { putOwnKey } from '../../ownKey.js';
3
3
  import { readEffectiveReasoning, readEffectiveMemoryScopes } from '../../effectiveFacts.js';
4
- // 0.60.0(engine ≥7.64.0 / sdk 8.4.0):终局读数的**单一读器**(两代字节 → 一个带标因由)。
5
4
  import { isReviewPark, readRunTerminal, runTerminalCode, runTerminalGateToolName, } from '../../runTerminal.js';
6
5
  import { toCcModelUsage } from './turnUsageToModelUsage.js';
7
- // G1 去字面化(2026-08-04):到限/结构化输出/rewind 三族的码字面收编进单一真源,本文件只 import。
8
- // 开集纪律不变——下面三个集合仍是**识别表**,`subtypeForErrorCode` 的 default 臂才是开集的兑现处。
9
6
  import { LIMITS_MAX_COST_EXCEEDED, LIMITS_MAX_TOKENS_EXCEEDED, LIMITS_MAX_TURNS_EXCEEDED, LIMITS_MAX_WALLTIME_EXCEEDED, OUTPUT_INVALID, isRewindFamilyCode, } from '../../engineErrorCodes.js';
10
- /**
11
- * wire 数值 → CC 扁平 usage 里的数值:非数 / 非有限一律 0。
12
- * 与 `turnUsageToModelUsage.finiteOrZero` 同一条规约(那边是 `ModelUsage` 八键的 mint 点,
13
- * 这边是 `NonNullableUsage` 五键的);两处**刻意不共用一个导出**,因为它们分属两个可移植闭包
14
- * 的叶,而这一行判据本身没有会漂的内容(有会漂的内容时才值得上收)。
15
- */
16
7
  function finiteOr0(v) {
17
8
  return typeof v === 'number' && Number.isFinite(v) ? v : 0;
18
9
  }
19
- /**
20
- * Flatten TaskStats → the CC NonNullableUsage placeholder.
21
- * 🔴 REF-CC-055(xlate-01):`failedToSdkResult` 此前手抄了一份「五键全零」的字面量,那不是
22
- * 另一种形状,就是本函数 `stats === undefined` 的退化输出 —— 现在它也走这里(单一构造点)。
23
- */
24
10
  function flattenUsage(stats) {
25
- // D-2 / L-192②:`webSearchRequests` 修前是**字面量 0**。`TaskStats` 是开集索引 ⇒ 按 CC 的名字
26
- // 宽读一次:读得出就是真账(显式 0 = 做了零次,一句正面事实),读不出才是「没有这本账」。
27
11
  const webSearch = stats?.webSearchRequests;
28
12
  const webSearchKnown = typeof webSearch === 'number' && Number.isFinite(webSearch);
29
13
  const totalInput = stats?.totalInputTokens;
30
14
  const totalInputKnown = typeof totalInput === 'number' && Number.isFinite(totalInput);
31
- // D-2 族扫(异源对抗复审 [medium]):1 小时 TTL 的缓存写入(core `cacheWriteTokensLong`)此前
32
- // 在终局的**两处**投影上都读不到 —— 见 {@link SemaFlatUsage._sema_cache_write_tokens_long}。
33
15
  const longWrite = stats?.cacheWriteTokensLong;
34
16
  const longWriteKnown = typeof longWrite === 'number' && Number.isFinite(longWrite);
35
17
  return {
36
- // 🔴 B-073 ③ 族扫(0.65.0):四格一律走 {@link finiteOr0},不再用 `?? 0`。
37
- // `??` 只挡 `null`/`undefined` —— 而 `TaskStats` 带 `[key: string]: unknown` 开集索引,
38
- // wire 是 JSON,一个 `"12"` / `NaN` / `Infinity` 会**原样落进** CC 侧型面写着 `number`
39
- // 的槽位(编译期全绿,消费端在求和时才炸或静默出错)。`Infinity` 尤其糟:它会被当成真
40
- // 数字摊进总计(与本文件 `microUsdToUsd` 头注同一条规约)。
41
18
  inputTokens: finiteOr0(stats?.promptTokens),
42
19
  outputTokens: finiteOr0(stats?.outputTokens),
43
20
  cacheReadInputTokens: finiteOr0(stats?.cachedTokens),
44
- // B-073 ③(L-192② 姊妹):此前是**字面量 0**,而 wire 上一直有这一格
45
- // (`TaskStats.cacheWriteTokens`,sdk `types.d.ts` 真字节)—— 硬编 0 把一笔真实发生的缓存
46
- // 写入抹成「没发生」,而 CC 的 footer/账单面正是拿这一格算缓存成本的。
47
- // 缺席仍折 0:CC `NonNullableUsage` 的这一格型面上是必填 number,而它与**成本**那一格不同
48
- // (token 计数没报就是没写过),缺席与真 0 在 wire 上本来就同义 —— 成本那一格的缺席是 core
49
- // 刻意造出来的第三档,故单有判别位(见 `toCcModelUsage`)。
50
21
  cacheCreationInputTokens: finiteOr0(stats?.cacheWriteTokens),
51
- // D-2:值仍是 number(CC 形不破、只读这一格的旧消费者行为逐字不变);「wire 上没有这本账」
52
- // 这句话交给同行的判别位,见 {@link SemaFlatUsage._sema_web_search_requests_absent}。
53
22
  webSearchRequests: webSearchKnown ? webSearch : 0,
54
23
  ...(webSearchKnown ? {} : { _sema_web_search_requests_absent: true }),
55
- // D-2:cache-INCLUSIVE 总量的超集座位(缺席 ⇒ 键不铸,绝不拿 MISS 分量冒充总量)。
56
24
  ...(totalInputKnown ? { _sema_total_input_tokens: totalInput } : {}),
57
- // D-2 族扫:长 TTL 缓存写入分量原样过境(**不求和**,理由见该位注释)。
58
25
  ...(longWriteKnown ? { _sema_cache_write_tokens_long: longWrite } : {}),
59
26
  };
60
27
  }
61
- /** 非空串窄化(空串按缺席归一 —— 空串骑上一个键就是「这一格有值」的冒充)。 */
62
28
  function nonEmptyStr(v) {
63
29
  return typeof v === 'string' && v.length > 0 ? v : undefined;
64
30
  }
65
- /** 本流 `toolCallId → args` 快照的条数上限;溢出的调用不记 ⇒ 那一行 join 不上,留在 sema 载体(诚实缺席)。 */
66
31
  export const TOOL_INPUT_JOIN_MAX_CALLS = 4096;
67
- /** 传输层的替换记号前缀(闭形 `«redacted»` / `«redacted:<词>»` / `«redacted:<词>:partial»`;按前缀认 = 多认不少认)。 */
68
32
  const REDACTION_TOKEN_PREFIX = '\u00abredacted';
69
- /** 传输层把环 / 超深子树换成的两个占位串(只认「叶值恰等于」)。 */
70
33
  function isTransportPlaceholderLeaf(v) {
71
34
  return v === '[circular]' || v === '[depth-limit]';
72
35
  }
73
- /** 扫描预算:节点数与深度任一超限 ⇒ 按「判不出干净」处置(不铸)。 */
74
36
  const TOOL_INPUT_SCAN_MAX_NODES = 20000;
75
37
  const TOOL_INPUT_SCAN_MAX_DEPTH = 80;
76
- /**
77
- * 流内 `tool_start.args` 能不能当 CC `tool_input` 用。三道闸,任一不过 ⇒ `undefined`(该行留在 sema 载体):
78
- * ① 是普通对象(CC 形是 `Record<string, unknown>`;字符串 / 数组 / null 都不是);
79
- * ② **没被传输层动过**:帧上的入参在到达客户端之前过一道保形脱敏(结构与键名不动,字符串叶里的凭据形
80
- * 子串换成记号;环与超深子树换成占位串)。被换过的对象**不是**那次调用的入参 —— 铸进 CC 键等于
81
- * 声称「模型就是拿这只对象调的工具」。方向是少铸不错铸:入参里本来就含这些字面的极端情形会被
82
- * 一并挡掉,代价是那一行留在 sema 载体;
83
- * ③ 扫描在预算内走完(超大 / 超深 / 自环的对象判不出干净 ⇒ 不铸)。
84
- */
85
38
  function joinableToolInput(args) {
86
39
  if (args === null || typeof args !== 'object' || Array.isArray(args))
87
40
  return undefined;
@@ -104,60 +57,14 @@ function joinableToolInput(args) {
104
57
  }
105
58
  return args;
106
59
  }
107
- /**
108
- * 「这条 gate 是**被拒**的」判词表。
109
- * 🔴 **识别表,不是合法值表**(同 `engineErrorCodes` 的开集纪律):core 今天在两处铸这一格 ——
110
- * 同步 ask 腿写 `resolved.action === 'allow' ? 'allow' : 'deny'`,耐久 resume 腿原样透传
111
- * `ResumeOutcome.decision`。后者的 `plan_review` 臂词表是 `approve` / `edit` / `reject` ——
112
- * 「人否了一份**计划**」不是「一次**工具调用**被拒」,CC 的 `permission_denials` 逐字是
113
- * "One auto-denied **tool call**"。⇒ 认不出的判词一律**不铸** denial:少报一条(消费方还能从
114
- * `humanReview` 看见有人工门)远好过把一次计划否决冒充成一次工具拒绝。
115
- */
116
60
  const GATE_DENIED_DECISIONS = new Set(['deny']);
117
- /**
118
- * **认得出、但不是工具拒绝**的判词。与上面那张表合起来才是「这一行我判得出」的全集 ——
119
- * 落在两张表**之外**的词(上游新词 / 大小写变体 / 空串 / 整个缺席)一律**不猜**:既不铸 denial,
120
- * 也**不许**让这份清单继续声称完整(异源对抗复审 [medium]:修前这些行被静默跳过,`[]` 于是被
121
- * 当成「零拒绝」这句正面事实)。今天的成员:同步 ask 腿的 `allow`,计划复核腿的
122
- * `approve`/`edit`/`reject`(那是「人否了一份**计划**」,不是 CC 说的 one auto-denied tool call)。
123
- */
124
61
  const GATE_NON_DENIAL_DECISIONS = new Set(['allow', 'approve', 'edit', 'reject']);
125
- /**
126
- * D-1 / L-192① —— 终帧拒绝清单的**唯一 mint 点**(成功臂与错误信封共用)。
127
- *
128
- * 修前两处各写一个字面量 `[]`,于是**两句话被折成一句**:「这条 run 一次都没被拒」与「这条帧
129
- * 根本没有这本账」(老引擎 / 409 拒绝信封 / `failed` 事件帧都不带 stats)在 CC 形上都是 `[]`。
130
- *
131
- * 🔴 **为什么是两条清单而不是把 wire 的记录塞进 CC 数组**(异源复审 [medium] 逼出的口径订正):
132
- * CC 的 `SDKPermissionDenial` 三键 `tool_name` / `tool_use_id` / `tool_input` **全是必填**
133
- * (agent-types `permissions.d.ts` 与壳侧 zod `SDKPermissionDenialSchema` 皆然),而 wire 的 gate
134
- * 账本(7.23.0 起六格)给不出 `tool_input`。两条路都不能走:**补零补空 = 编造**;**塞半条记录 = 破坏元素契约**
135
- * ——严格消费方 `safeParse` 会把**整条 result 帧**判非法(丢一格 vs 丢整帧,后者严重得多)。
136
- * ⇒ CC 数组只收**三键齐全**的记录,wire 上真有的每一条走 sema 载体 `_sema_permission_denials`,
137
- * 键按能兑现的铸。`tool_use_id` ⇐ 账本行的 `toolCallId`(引擎 ≥7.23.0);`tool_input` ⇐ 账本行的
138
- * `toolInput`(今天没有)**或**同一条流 `tool_start` 帧上的入参(0.73.4;调用方把本流快照作为按次入参
139
- * 交进来,没交 = 没有流内面 = 与 0.73.3 之前逐字同)。🔴 0.73.2 的发布说明曾把「`tool_use_id` 到货」写成
140
- * 「CC 数组自动填满」—— 那是错的:缺第三键的行进不了 CC 数组,直到本版的流内 join 才真有三键齐的行。
141
- *
142
- * 🔴 判别位 `_sema_permission_denials_absent` = **「CC 那条清单不可声称完整」**,四条路径:
143
- * ① 账本整个读不出(`humanReview` 缺席 / 坏形 / `gates` 不是数组 / 无 stats);
144
- * ② 有读不出的行(非对象);
145
- * ③ 有**认不出的判词**(两张判词表之外的词 / 判词缺席)—— 不猜它是不是拒绝;
146
- * ④ 有被拒记录**没能铸成 CC 形**(行无 `toolCallId` / 本流没见过那只 `tool_start`(重连后的流、
147
- * 宿主自建管线没交快照)/ 入参不是普通对象 / 入参被传输层动过)。
148
- * 读法因此是两键合读:`[]` + 判别位缺席 = **零拒绝**(正面事实);`[]` + 判别位 `true` = 别渲肯定句。
149
- * 判别位**只在该说话时铸,绝不铸 `false`**(本包 additive 一贯纪律)。
150
- *
151
- * 逐行独立:坏行只丢自己,绝不丢整本账(但会让清单失去「完整」资格,见 ②③)。
152
- */
153
62
  function permissionDenialParts(stats, toolInputByCallId) {
154
63
  const hr = stats?.humanReview;
155
64
  const gates = hr !== null && typeof hr === 'object' ? hr.gates : undefined;
156
- // 账本整个读不出(缺席 / 坏形 / 无 stats)—— 诚实缺席,不猜。
157
65
  if (!Array.isArray(gates))
158
66
  return { permission_denials: [], _sema_permission_denials_absent: true };
159
67
  const denials = [];
160
- /** 这份清单还能不能声称「完整」(见头注 ②③④)。 */
161
68
  let complete = true;
162
69
  for (const g of gates) {
163
70
  if (g === null || typeof g !== 'object') {
@@ -167,34 +74,25 @@ function permissionDenialParts(stats, toolInputByCallId) {
167
74
  const row = g;
168
75
  const decision = row.decision;
169
76
  if (typeof decision !== 'string' || decision.length === 0) {
170
- // 判词缺席(耐久 wake 腿真会这样)⇒ 这一行是不是拒绝**判不出**,不猜、也不再声称完整。
171
77
  complete = false;
172
78
  continue;
173
79
  }
174
80
  if (!GATE_DENIED_DECISIONS.has(decision)) {
175
- // 认得出的非拒绝判词照旧跳过(不影响完整性);认不出的词一律降级为「清单不完整」。
176
81
  if (!GATE_NON_DENIAL_DECISIONS.has(decision))
177
82
  complete = false;
178
83
  continue;
179
84
  }
180
85
  const toolName = nonEmptyStr(row.toolName);
181
86
  const toolArg = nonEmptyStr(row.toolArg);
182
- // 🔴 开集宽读两键:`toolCallId` 自引擎 7.23.0 起真在账本上(门里锚着真字节;老引擎 / 不可 join 的行
183
- // 缺席);`toolInput` 今天仍没有,按 core 自己的命名习惯(`toolName` / `toolArg` 同族)预读,
184
- // 上游若用别的名字补上,门里的缺席证据当天红 ⇒ 那一批改读真名。绝不自铸值。
185
87
  const toolUseId = nonEmptyStr(row.toolCallId);
186
88
  const rawInput = row.toolInput;
187
89
  const ledgerInput = rawInput !== null && typeof rawInput === 'object' && !Array.isArray(rawInput)
188
90
  ? rawInput
189
91
  : undefined;
190
- // 0.73.4:账本没带 ⇒ 按行身份去本流快照里 join(账本那份在场时恒优先;`has` 先判,绝不把
191
- // 「没见过这只 id」与「见过、入参是 undefined」混成一格)。
192
92
  const joinedInput = ledgerInput === undefined && toolUseId !== undefined && toolInputByCallId?.has(toolUseId) === true
193
93
  ? joinableToolInput(toolInputByCallId.get(toolUseId))
194
94
  : undefined;
195
95
  const toolInput = ledgerInput ?? joinedInput;
196
- // 键都读不出时仍然 push 一条空记录:**条数**(「这条 run 被拒了几次」)是这本账最要紧的
197
- // 事实,丢掉它等于把一次真实发生的拒绝抹成没发生;键按能兑现的铸,不编。
198
96
  denials.push({
199
97
  ...(toolName !== undefined ? { tool_name: toolName } : {}),
200
98
  ...(toolUseId !== undefined ? { tool_use_id: toolUseId } : {}),
@@ -203,7 +101,6 @@ function permissionDenialParts(stats, toolInputByCallId) {
203
101
  ...(toolArg !== undefined ? { _sema_tool_arg: toolArg } : {}),
204
102
  });
205
103
  }
206
- /** CC 元素契约:三键齐全才进 CC 数组(类型窄化由这只谓词负责)。 */
207
104
  const ccList = denials.filter((d) => d.tool_name !== undefined && d.tool_use_id !== undefined && d.tool_input !== undefined);
208
105
  const claimable = complete && ccList.length === denials.length;
209
106
  return {
@@ -212,67 +109,23 @@ function permissionDenialParts(stats, toolInputByCallId) {
212
109
  ...(claimable ? {} : { _sema_permission_denials_absent: true }),
213
110
  };
214
111
  }
215
- /**
216
- * 0.67.1 —— **以 wire 给的 id / 键名当对象键**时的唯一落键姿势(`__proto__` 陷阱)。
217
- * 🔴 0.68.0 / L-246 B2:实现**下沉到 `src/ownKey.ts`**(单源)—— 修前本文件与
218
- * `hitl/parkResolver.ts` 各持一份同形实现,理由、陷阱与「不改成 null 原型交付」的取舍都在
219
- * 那个模块的头注里。本别名保留是为了本文件三十余处调用点零改动。
220
- */
221
112
  const putOwn = putOwnKey;
222
- /** 有限数窄化(非数 / 非有限 ⇒ 缺席;`0` 是事实不是缺席)。 */
223
113
  function finiteOrAbsent(v) {
224
114
  return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
225
115
  }
226
- /**
227
- * 0.67.1 / B-091 —— usage **下界位**的**唯一判据**(纯函数,两面读的是**同一次计算**)。
228
- *
229
- * 🔴 它为什么必须是一只函数、而不是两处各写一遍的表达式:下界位有**两个来源**——
230
- * · `stats.usageMissing`:只在带得出 `TaskResult` 的终帧上有;
231
- * · **本条流**的流内观测(`failed` 事件帧 / 409 拒绝信封 / park 体**没有 stats**,
232
- * 只读 stats 的话一条已经观测到缺口的 run 会在终帧上被读成「每一轮都报了 usage」)。
233
- * 修前取并只包在**终帧**那一面({@link costFactParts}),而 chrome 对账臂的铸点直接展开
234
- * `readRunCostFacts(stats).reconcile` ⇒ 「流内观测到缺口、终局 stats 对此缄默」那一形上两面各说
235
- * 各的(终帧铸了 `_sema_usage_lower_bound`、同一拍的臂上没有 `usageLowerBound`)——
236
- * [paired-mechanisms-must-share-premise] 的教科书形。⇒ 取并**下沉到这里**,两面共用。
237
- *
238
- * 🔴 **第二参是「这一条流」的观测快照,不是一个跨流留存的状态**(0.67.2 / 车 I 件 I-1):它此前住在
239
- * `EmitContext` 上、只写 `true` 永不清,而 ctx 是调用方的对象、可以复用给多条流 ⇒ 上一条流的缺口
240
- * 会把下一条账数得全的流标成下界(顺序复用),或两条并发流互相串。现在由 `runStream` 按流持有、
241
- * 终局**按值**交给两个投影口;端自建管线同样是**按次调用**传入。
242
- *
243
- * 🔴 **严格 true 才认**(core 契约:`true` 或缺席,恒不写 `false`/`null`);`stats` 不是可读对象时
244
- * 它那一半读作「没说」,而流内观测那一半**照旧成立**。
245
- */
246
116
  function usageLowerBoundOf(stats, observed) {
247
117
  const statsSaid = stats !== null && typeof stats === 'object' && !Array.isArray(stats)
248
118
  ? stats.usageMissing === true
249
119
  : false;
250
120
  return statsSaid || observed?.usageMissingObserved === true;
251
121
  }
252
- /**
253
- * D-3 / B-068 · L-198 —— 终局成本事实的**唯一读器**(终帧超集键与 chrome 对账臂共用)。
254
- *
255
- * 🔴 `stats` 不是可读对象(409 拒绝信封 / park 体 / `failed` 事件帧)⇒ 返 `undefined` =
256
- * **这条帧没有账**,调用方据此「不说话」(不发臂、不铸键),而不是发一条全缺席的空账。
257
- *
258
- * 🔴 **第二参 `observed`(0.67.1 / B-091,additive)**:**这一条流**的流内观测快照。给了它,对账段上的
259
- * {@link RunCostReconcile.usageLowerBound} 就是**取并后**的读数(见 {@link usageLowerBoundOf});
260
- * 不给(旧签名)⇒ 只读 `stats` 那一半,既有端逐位不变。
261
- * ⚠️ 包内的两个调用点(`costFactParts` 与 `runStream` 的 `run_cost_reconciled` 铸点)**都必须**
262
- * 传它 —— 少传一处就是把本件修的那条不对称原样种回去。
263
- * 🔴 0.67.2 / 车 I 件 I-1:它是**按次调用的入参**,不再是 `EmitContext` 上的一格 —— 共享 ctx 被复用
264
- * 给多条流时,那一格会把别的流的缺口串进这一条(见 {@link usageLowerBoundOf} 的第三段 🔴)。
265
- */
266
122
  export function readRunCostFacts(stats, observed) {
267
- // 数组也不是「一份账」:`typeof [] === 'object'` 会把一条畸形载体放进来,然后它的每一格都读不出
268
- // ⇒ 发出一条「own 没定价」的臂,而真相是**这条帧根本没有账**(两句话又折成一句)。
269
123
  if (stats === null || typeof stats !== 'object' || Array.isArray(stats))
270
124
  return undefined;
271
125
  const rawBreakdown = stats.costBreakdown;
272
126
  let breakdown;
273
127
  if (rawBreakdown !== null && typeof rawBreakdown === 'object' && !Array.isArray(rawBreakdown)) {
274
128
  const out = {};
275
- // 逐键独立:坏值只丢自己(整段不连坐)。开集 —— 键名不套闭表,词表属主是 core。
276
129
  for (const [k, v] of Object.entries(rawBreakdown)) {
277
130
  const n = finiteOrAbsent(v);
278
131
  if (n !== undefined)
@@ -282,10 +135,6 @@ export function readRunCostFacts(stats, observed) {
282
135
  breakdown = out;
283
136
  }
284
137
  const rawNested = stats.nested;
285
- // 🔴 「**委派过**」这件事本身是 load-bearing 的(它决定总额算不算得出来),所以它的判据是
286
- // **原始载体**在不在,而**不是**过滤产物空不空(异源对抗复审 [medium] 的实抓:
287
- // `{costMicroUsd:100, nested:{}}` / `nested:{tokens:'bad'}` 修前被当成「没委派」⇒ 铸出一个
288
- // 确定的总额 100,而真相是「委派过、那本账读不出来」)。
289
138
  const nestedRan = rawNested !== null && typeof rawNested === 'object' && !Array.isArray(rawNested);
290
139
  let nested;
291
140
  if (nestedRan) {
@@ -296,20 +145,12 @@ export function readRunCostFacts(stats, observed) {
296
145
  if (v !== undefined)
297
146
  picked[k] = v;
298
147
  }
299
- // 一个数都读不出时仍然铸**空对象**:键在场 = 委派过(与 `costMicroUsd` 缺席 = 那本账不知道
300
- // 是两键合读的两半);整键不铸会把「委派过」这句事实一起抹掉。
301
148
  nested = picked;
302
149
  }
303
150
  const ownMicroUsd = finiteOrAbsent(stats.costMicroUsd);
304
151
  const nestedMicroUsd = nested?.costMicroUsd;
305
- // 「委派过但那本账没定价/读不出」与「压根没委派」是两句话:前者判别位立起来,后者什么都不说。
306
152
  const nestedUnknown = nestedRan && nestedMicroUsd === undefined;
307
- // 求和自身也必须是有限数:两个合法但极端的值相加可以溢出成 Infinity,而 Infinity 会被下游
308
- // 当成真数字摊进总计(与 `microUsdToUsd` 同一条规约)⇒ 宁可不铸。
309
153
  const sum = ownMicroUsd !== undefined && !nestedUnknown ? ownMicroUsd + (nestedMicroUsd ?? 0) : undefined;
310
- // 0.67.0(core 7.14.0):usage 下界位。**严格 true 才铸**(core 契约:`true` 或缺席,恒不写
311
- // `false`/`null`;认宽了就会把一个 falsy 值渲成「数得不全」)。它与成本三段正交,所以读在这里、
312
- // 与三段同一只读器出 —— 两面(终帧超集键 / chrome 对账臂)因此永远不会各算各的。
313
154
  const usageLowerBound = usageLowerBoundOf(stats, observed);
314
155
  const reconcile = {
315
156
  ...(ownMicroUsd !== undefined ? { ownMicroUsd } : { costAbsent: true }),
@@ -318,7 +159,6 @@ export function readRunCostFacts(stats, observed) {
318
159
  ...(nestedUnknown ? { nestedCostAbsent: true } : {}),
319
160
  ...(breakdown?.compactionMicroUsd !== undefined ? { compactionMicroUsd: breakdown.compactionMicroUsd } : {}),
320
161
  ...(breakdown?.llmRootMicroUsd !== undefined ? { llmRootMicroUsd: breakdown.llmRootMicroUsd } : {}),
321
- // 对账值:own 读得出、子代那一边不是「跑了却不知道多少钱」、且和本身有限时才算得出来。
322
162
  ...(sum !== undefined && Number.isFinite(sum) ? { reconciledMicroUsd: sum } : {}),
323
163
  };
324
164
  return {
@@ -327,11 +167,6 @@ export function readRunCostFacts(stats, observed) {
327
167
  reconcile,
328
168
  };
329
169
  }
330
- /**
331
- * CC-06 / B-081:`TaskResult.effectiveReasoning` / `effectiveMemoryScopes` 由 server 终帧键集 verbatim 转发,
332
- * sdk d.ts 未建模 ⇒ 从裸终帧记录窄读后按 `_sema_` 超集键铸到 CC 形 `result` 上;坏形 / 缺席 ⇒ 键不铸。
333
- * 🔴 CC 同名键零改:这两位是 additive,`result` 上既有的任何键字节不动。
334
- */
335
170
  function effectiveFactParts(rec) {
336
171
  const reasoning = readEffectiveReasoning(rec.effectiveReasoning);
337
172
  const scopes = readEffectiveMemoryScopes(rec.effectiveMemoryScopes);
@@ -340,22 +175,8 @@ function effectiveFactParts(rec) {
340
175
  ...(scopes !== undefined ? { _sema_effective_memory_scopes: scopes } : {}),
341
176
  };
342
177
  }
343
- /**
344
- * D-3 —— 终帧上的两个成本超集键(成功臂与错误信封共用的**唯一** mint 点)。
345
- * 🔴 CC 同名键 `total_cost_usd` **语义零改**:它仍然只是 own 花费,`nested` **不折进去**
346
- * (超集键纪律:CC 形上已有的位不许塞我们自己的含义)。「fully-reconciled spend」由消费方按
347
- * 这两个超集键自己加 —— 包给的是**可对账的事实**,不是一个改了口径的数。
348
- */
349
178
  function costFactParts(stats, observed) {
350
179
  const facts = readRunCostFacts(stats, observed);
351
- // 🔴 **下界位与成本三段分开算**(异源对抗复审 [medium] 实抓):`stats` 读不出(`failed` 事件帧 /
352
- // 409 拒绝信封 / park 体)时**成本**那三段确实没有账、一条都不该说;但「这条流观测到过一轮
353
- // 没有 usage」这件事**照旧成立**,而那种终帧的 `usage` 恰恰是 `flattenUsage(undefined)` 的
354
- // 全零 —— 早退回空对象等于把「不知道」渲成精确零(正是本位要修的病的另一条路径)。
355
- // ⇒ 两者**取并**:stats 说了算一半,流内观测算另一半。
356
- // 🔴 0.67.1 / B-091:取并本身已经**下沉**到 {@link usageLowerBoundOf} —— 这里与读器内部、与
357
- // chrome 对账臂读的是**同一只函数**,三面不会各算各的(`facts === undefined` 时读器整只不
358
- // 返回,所以这一行必须自己再调一次那只判据,而不是回头读 `facts`)。
359
180
  const lowerBound = usageLowerBoundOf(stats, observed);
360
181
  const lowerBoundPart = lowerBound ? { _sema_usage_lower_bound: true } : {};
361
182
  if (facts === undefined)
@@ -364,51 +185,9 @@ function costFactParts(stats, observed) {
364
185
  ...lowerBoundPart,
365
186
  ...(facts.breakdown !== undefined ? { _sema_cost_breakdown: facts.breakdown } : {}),
366
187
  ...(facts.nested !== undefined ? { _sema_nested_usage: facts.nested } : {}),
367
- // ── 0.67.0(core 7.14.0 `TaskResult.stats.usageMissing`)────────────────────────────────
368
- // 🔴 CC 同名键**语义零改**:`usage` / `modelUsage` / `total_cost_usd` 的字节一个不动,本位是
369
- // 一个 `_sema_` 超集**判别位**,说的是「上面那些数字是**下界**」。把它折进数字里(比如
370
- // 把 tokens 抹成 null)会让每一个既有消费者当场坏掉,而它们今天读到的是一笔**看起来
371
- // 已知**的账 —— 那正是本位要修的病。
372
- // 🔴 **缺席不铸**(never false):缺席 ⇔ 每一轮都报了 usage。
373
- // 🔴 与 §31d 的 `usage._sema_total_input_tokens` **同帧不同问**:那一位答「这一行报了多少总
374
- // 输入」,本位答「这一整条 run 的账数全了没有」。
375
- // (本位在上面与流内观测取并后已铸;这里不再重复。)
376
188
  };
377
189
  }
378
- // ══ 0.68.0(L-244 包侧半场)—— 子代用量「这笔账是下界」的**自有位**,与终帧那一位**分名** ══
379
- //
380
- // ── 病形(三重复审 A4/B10 在壳上实抓)──────────────────────────────────────────────────────
381
- // 壳的子代用量 store 把「这一行还没收口(`partial`)」∪「这一轮引擎没报账(`usageMissing`)」两件事
382
- // 铸成了一个**与终帧同名**的键 `_sema_usage_lower_bound`。两者是**同名异义**:
383
- // · 终帧那一位答的是「**这条 run 的合计**是下界」(来源 = `stats.usageMissing` ∪ 流内观测);
384
- // · 子代面那一位答的是「**这一只子代的这一行**现在还不是最终数」(来源 = 行还没收口 / 那一轮没报账)。
385
- // 同名的代价是消费面分不出自己读到的是哪一个:一个按键名做聚合的面(把所有 `_sema_usage_lower_bound`
386
- // 收起来渲一句「本次会话的账是下界」)会把一条**只是还没收口的子代行**算成整条 run 的账不可信。
387
- //
388
- // ⇒ 包侧给出**自有名**与**唯一判据**,壳/web/desktop 三端照它渲,谁都不再自己拼一个键名:
389
- /**
390
- * 子代用量面的**下界判别位键名**(`_sema_subagent_usage_partial`)。
391
- *
392
- * 🔴 它与终帧的 `_sema_usage_lower_bound` **刻意不同名**(见上段):两者答的是两个问题,
393
- * 一个消费面同时拿到两者时必须分得出来。
394
- * 🔴 本包**不在任何 wire 帧上铸它** —— 它是**渲染面**的位(端把一行子代用量交给自己的视图时用)。
395
- * 包给名与判据,是为了三端零自拼(同 `gateIdentity` 的三条身份键字面同一条纪律)。
396
- */
397
190
  export const SEMA_SUBAGENT_USAGE_PARTIAL_KEY = '_sema_subagent_usage_partial';
398
- /**
399
- * 一行子代用量的数字**是不是下界**({@link SEMA_SUBAGENT_USAGE_PARTIAL_KEY} 该不该立)。
400
- *
401
- * 三个来源**取并**,每一条都能独立让这一行的数字不是最终数:
402
- * · `row.usageMissing` —— 这只子代**至少有一轮**引擎没报账(数字照累加,但它是下界);
403
- * · `row.keyCollision` —— 两个命名空间的 id 撞了字面,这一行是**几只子代的账并起来的**;
404
- * · `opts.tablePartial` —— 整张表对不上引擎的权威合计(`_sema_nested_usage_by_task_partial`)⇒
405
- * 表里**每一行**都不可证完整。
406
- *
407
- * 🔴 **`false` 不是「这一行是最终数」的证据**:它只说「本端没有任何一条理由认为它是下界」。
408
- * 行本身还没收口(端自己的 store 知道,包不知道)时,端要自己把那一条并进来 —— 所以这只谓词
409
- * 收一个 `opts`,而不是假装它掌握全部真相。
410
- * 🔴 非对象 / 缺席入参 ⇒ `false`(答不出,不是断言);坏形位(非 `true` 的值)不当真。
411
- */
412
191
  export function subagentUsageIsPartial(row, opts) {
413
192
  if (opts?.tablePartial === true)
414
193
  return true;
@@ -416,55 +195,12 @@ export function subagentUsageIsPartial(row, opts) {
416
195
  return false;
417
196
  return row.usageMissing === true || row.keyCollision === true;
418
197
  }
419
- /**
420
- * L-228 —— 流内子代分表 → 终帧两个超集键的**唯一 mint 点**(成功臂与错误信封共用)。
421
- *
422
- * **不导出**:它的入参是一个只有 `runStream` 攒得出来的累加表,推上公面等于邀请宿主自己攒一份
423
- * 「看见了多少」的账(而那份账的 `partial` 判据在宿主手上不成立)。包侧门走**端到端**素材:
424
- * 喂子流 `turn_end` + `done` 给 `runStream`,断言终帧上那两个键 —— 那也是三端真正拿到它的路。
425
- *
426
- * ── 🔴 `partial` 怎么判(设计定谳,与派车单的「恒铸于重连车道」不同,理由写在这里)────────────
427
- * 派车单给的形是「本条流不是从 run 起点观测 ⇒ 恒铸 partial」。而本层**没有**「从不从起点观测」
428
- * 这个读数:`runStream` 手上只有**同一条流内**的事件序号去重表(`seen`),它答的是「这一帧重放过
429
- * 没有」,答不了「这条流之前还有没有别的帧」;`--resume` / 重连开出来的流与首开的流在本层
430
- * **逐位不可分**。按一个读不出来的量铸判别位 = 编一件事实。
431
- * ⇒ 判据改锚在**真正决定结果的量**上([anchor-on-the-deciding-quantity]):拿引擎自己的**权威
432
- * 合计** `stats.nested` 对账 —— 分表的 `turns` 之和等于 `nested.turns` **且**行数等于 `nested.tasks`
433
- * 时,这张表**可证**覆盖了整条 run ⇒ 不铸 partial;任何一边对不上、或 `stats.nested` 根本读不出来
434
- * ⇒ **铸 partial**。
435
- * 🔴 失效方向是安全的那一侧:上游哪天让某类嵌套轮不上子流 `turn_end`(比如更深一层的编排),
436
- * 本判据只会**多**铸 partial(把一张其实完整的表说成不完整),**永远不会**把一张残表说成完整。
437
- * 🔴 **两个键不互证、也不相加**:`_sema_nested_usage`(合计,引擎报的)与本表(流内看见的)是
438
- * 两份独立的账;`partial` 在场时两者**本来就该不等**,消费方不许拿其中一份去「修正」另一份。
439
- * 🔴 **0.67.2(车 I 件 I-2)身份键碰撞**:累加表的键按出身隔离(`s:` / `p:`),交付面按裸 id 归并 ——
440
- * 跨命名空间的同字面合并成一行、立 {@link SemaSubagentUsageRow.keyCollision},并让 `partial` 恒立。
441
- * 🔴 **0.67.2(车 I 件 I-2b)这张表是「本流快照」,不再从 `EmitContext` 上读**(与件 I-1 逐字同形的
442
- * 同形存量,轮二异源复审实抓):流结束后它此前**留在调用方对象上**,而本文件这三只终帧投影器是
443
- * **公面导出** —— 端「A 走 runStream、B 直调终帧投影」共用一个 ctx 时,B 的终帧会带出 **A 的**分表;
444
- * B 的 `nested` 计数若恰好与那张表对得上(`tasks`/`turns` 相等),`partial` 还不铸 = 一张属于别人的
445
- * 表被标成「可证完整」。⇒ 表随终帧那一拍**按值**传进来;没传 ⇒ 两个键都不铸(诚实缺席:这次投影
446
- * 一条子流都没看见),**绝不**回头读残留。
447
- */
448
198
  function nestedUsageByTaskParts(stats, rollup) {
449
- // 🔴 一行都没有 ⇒ **什么都不说**:空表会被读成「这条 run 一个子代都没委派」,而真相可能是
450
- // 「委派了,但这条流没看见任何一轮」(重连车道)。两句话不许折成一句。
451
199
  if (rollup === undefined || rollup.size === 0)
452
200
  return {};
453
201
  let turnsSeen = 0;
454
- // 🔴 0.67.1:行键的**出身**统计(见 {@link MutableSubagentUsageRow.keyFromParentFallback})。
455
- // 出身按行是均匀的 —— 0.67.2 起这件事由**键空间**保证:累加表的键带 `s:` / `p:` 前缀,所以
456
- // 一行的每一轮必然来自同一个命名空间(修前靠的是「`taskId = sourceTaskId ?? parent`」那条推理,
457
- // 而那条推理在两个空间撞字面时不成立)。
458
202
  let fallbackKeyedRows = 0;
459
203
  let sourceKeyedRows = 0;
460
- // ── 🔴 0.67.2(车 I 件 I-2)—— 交付面按**裸 id** 归并,跨命名空间的同字面 = **碰撞** ──────────
461
- // 交付面的键必须是裸 id(端零改),而累加表的键带出身前缀 ⇒ 两个命名空间里字面相同的两行会在这里
462
- // 落到同一个交付键上。本层**没有任何读数**能把它们拆回去(core 的合同不保证两个命名空间互斥),
463
- // 于是:合并数字(不偷偷丢一边)+ 立 `keyCollision`(如实说这一行是混的)+ `partial` 恒立
464
- // (证不出逐任务归属就不许说完整)。
465
- // 🔴 为什么不「两行分列」:交付形是 `Record<taskId, row>`,同一个裸 id 不可能占两格 —— 要分列就得
466
- // 改交付键的形(把出身前缀推到 wire 上),那是三端 BREAKING,而且把本层的内部编码变成端要解的
467
- // 身份语义。合并 + 显式标记是**同一批事实**下唯一不撒谎的交付形。
468
204
  const merged = new Map();
469
205
  for (const r of rollup.values()) {
470
206
  turnsSeen += r.turns;
@@ -486,7 +222,6 @@ function nestedUsageByTaskParts(stats, rollup) {
486
222
  prev.turns += r.turns;
487
223
  prev.inputTokens += r.inputTokens;
488
224
  prev.outputTokens += r.outputTokens;
489
- // `cacheReadTokens` 的缺席语义在合并处也守住:两边都没报过 ⇒ 键仍不铸(0 会被读成「零命中」)。
490
225
  if (r.cacheReadTokens !== undefined)
491
226
  prev.cacheReadTokens = (prev.cacheReadTokens ?? 0) + r.cacheReadTokens;
492
227
  if (r.usageMissing === true)
@@ -496,7 +231,6 @@ function nestedUsageByTaskParts(stats, rollup) {
496
231
  const keyCollisions = [...merged.values()].filter((v) => v.keyCollision === true).length;
497
232
  const rows = {};
498
233
  for (const [taskId, v] of merged) {
499
- // 🔴 落键仍走 `putOwn`(`__proto__` 陷阱):合并臂不是绕过那条纪律的第二条路(见 `putOwn` 头注)。
500
234
  putOwn(rows, taskId, {
501
235
  turns: v.turns,
502
236
  inputTokens: v.inputTokens,
@@ -511,15 +245,7 @@ function nestedUsageByTaskParts(stats, rollup) {
511
245
  : undefined;
512
246
  const authTurns = finiteOrAbsent(nested?.turns);
513
247
  const authTasks = finiteOrAbsent(nested?.tasks);
514
- // 🔴 0.67.1:**出身混合 ⇒ 证不出完整**。计数相等只说明「数目对得上」,说不了「逐任务归属对得上」
515
- // —— 拆一行与并一行的误差方向相反、可以恰好抵消(见 `keyFromParentFallback` 头注的反例)。
516
- // 出身一致时既有两条对账才是充分的:全真身份 ⇒ 一行一任务;全回落 ⇒ 多任务共父会让行数 <
517
- // `nested.tasks`,那一格自己会翻。失效方向仍是安全的那一侧(只会**多**铸 partial)。
518
248
  const mixedKeyOrigin = fallbackKeyedRows > 0 && sourceKeyedRows > 0;
519
- // 🔴 0.67.2:碰撞也单独挡一格。按构造 `keyCollisions > 0 ⇒ mixedKeyOrigin`(同一个命名空间里的键
520
- // 本来就唯一,撞字面只能跨空间发生),所以这一格今天是**冗余的第二道**;留着是因为两条判据问的
521
- // 不是同一件事(「这条流上有两种出身」vs「这一行里混了两种出身」),而上游哪天再加一个身份来源
522
- // 时,前者的判法要改、后者不用。
523
249
  const complete = !mixedKeyOrigin && keyCollisions === 0 && authTurns !== undefined && authTasks !== undefined &&
524
250
  authTurns === turnsSeen && authTasks === Object.keys(rows).length;
525
251
  return {
@@ -527,125 +253,53 @@ function nestedUsageByTaskParts(stats, rollup) {
527
253
  ...(complete ? {} : { _sema_nested_usage_by_task_partial: true }),
528
254
  };
529
255
  }
530
- /** P1-5 — real elapsed ms since the stream opened (runStream stamps startedAtMs); 0 only pre-stamp. */
531
256
  function elapsedMs(ctx) {
532
257
  return typeof ctx.startedAtMs === 'number' ? Math.max(0, Date.now() - ctx.startedAtMs) : 0;
533
258
  }
534
- /**
535
- * micro-USD → USD。引擎账面的成本单位是 **micro-USD**(百万分之一美元,整数轨:1e6 micro = 1 USD;
536
- * 整数是为了不吃 FP 漂移)。CC 的输出面字段 `total_cost_usd` / `costUSD` 单位是 **USD**(字段名即
537
- * 单位),所以出口一律 `/1e6`。非有限值(NaN/Infinity)不上账 —— Infinity 比 undefined 更糟,它会被
538
- * 当成真数字参与求和,把一条离谱账悄悄摊进总计(与 SDK `taskCostMicroUsd()` 的规约逐条同义;
539
- * `mapModelUsage()` 里的 `costMicroUsd / 1e6` 是同一换算的既有用法,两处此后共用本函数)。
540
- *
541
- * 为什么不直接调 SDK 的 `taskCostMicroUsd()`:本文件在 `runStream` **可移植闭包**内,portability 门
542
- * 对该闭包断言「值级外部包 = 空集」(EXPECTED_PACKAGES_KERNEL),值级 import SDK 会当场红。故就地
543
- * 复刻规约,单一真源以 SDK 那份注释为准。
544
- */
545
259
  function microUsdToUsd(micro) {
546
260
  return typeof micro === 'number' && Number.isFinite(micro) ? micro / 1e6 : null;
547
261
  }
548
- /**
549
- * P1-5 — `total_cost_usd` truthfulness: 报**引擎真的说出来的那个数**;引擎**没说**(键缺席 /
550
- * 非有限)才报 `null`(JSON 消费者看到 "unknown",绝不是一个编出来的零)。
551
- * 🔴 **0.65.0(B-073 ①)口径订正**:P1-5 当初写的是「只有真正的正成本才报,其余一律 null」,
552
- * 前提是那时 `0` 分不清「免费」与「没配 MODEL_COST_*」。core 之后把这两件事在 wire 上**分开**
553
- * (缺席 = 没定价;显式 0 = 声明免费),旧口径于是开始把一条真免费的 run 渲成「不知道」——
554
- * 那是同一条病的反向。现在的口径见 {@link costOrNull}。CC 的 schema 字段是 numeric ——
555
- * number-guarded 的读者(replEntry cost fold)本来就把非 number 当缺席。
556
- *
557
- * 🔴 键迁移([2006] cli 自查最重命中,2026-07-29):取数源从 legacy float `TaskStats.costUsd` 换成
558
- * 整数 `TaskStats.costMicroUsd`。`costUsd` 已被 **core 2.0.0 / server 1.319.0 / SDK 1.0.0 同批删除**
559
- * (发端、适配、类型三层),对 core 2.0.0+ 引擎该键恒 undefined ⇒ 本函数恒 null ⇒ `sema -p
560
- * --output-format json` 的 `total_cost_usd` **恒 null**,而账其实一直在 `costMicroUsd` 上
561
- * (本文件 `mapModelUsage()` 早就在读它 = 两代键并存的实锤)。devDep 此前钉 SDK `^0.1.7`(0.x 的 caret 是精确锁)
562
- * ⇒ 类型层看到的还是「两轨都在」的旧声明,对 1.0.0 才报 TS2345 —— 校旧物的门抓不住这类断链,故
563
- * 另有 `run-sdk-floor-test.mjs` 断言实装 SDK 地板 ≥ 1.1。
564
- *
565
- * 老引擎只发 float `costUsd` 时**不做兼容换算**:单轨之后读到 undefined(当场可见「不知道」)好过
566
- * 把一笔来源可疑的账静默吸收成看起来正常的数字(SDK normalize.ts 的 BREAKING 留档同款裁定;
567
- * 本仓 [clean-cut-no-legacy-compat])。
568
- */
569
262
  function costOrNull(stats) {
570
- // 🔴 B-073 ①(0.65.0):修前是 `usd !== null && usd > 0 ? usd : null` —— 一条**数值真值判定
571
- // 当存在性判定**(§B10 病族)的教科书例。core 早已把两件事在 wire 上分开:
572
- // · `costMicroUsd` **ABSENT** = 有一笔花销没定价 ⇒ 报 `null`(不知道);
573
- // · `costMicroUsd === 0` = 这台模型**显式声明免费**(core 逐字
574
- // `an explicit all-zero Model.cost still reports 0`)⇒ 就该报 `0`。
575
- // 旧写法把「声明免费」也渲成 `null`,于是一条真免费的 run 在 `-p --output-format json` 上
576
- // 说不出「免费」;而它当初写成 `> 0` 的理由(「0 可能是没配 MODEL_COST_*」)在 core 把缺席
577
- // 做成可表达的那一刻就失效了 —— 那一档现在由**缺席**自己承载,不必再借 0 当哨兵。
578
- // 负数照报:退款/修正在引擎账面上是合法值,包不当第二个会计(非有限值仍由 microUsdToUsd 判掉)。
579
263
  return microUsdToUsd(stats?.costMicroUsd);
580
264
  }
581
- // 件2a(中断事故修复批 G,2026-07-15,症状2 第一环):参数放宽 TaskStats | undefined + 全链守卫。
582
- // 409 active-run 拒绝时引擎的 done 帧本体是 {status:'failed', errorMessage, activeTaskId} —— NO stats
583
- // (ai-agent-service server.ts:1526 同型),此前 `mapModelUsage` 裸读 `(stats).modelUsage` 直接
584
- // TypeError,炸穿 runStream → seamQuery catch → 误诊 "engine is unreachable"(件2c)。缺 stats 时
585
- // 产出合法空 usage 形状(flattenUsage 本就守卫;这里返回 {},`modelUsageFor` 再按 r.model 合成当前行)。
586
- //
587
- // MF-25 — map the wire's per-model usage split (`stats.modelUsage`, an open `{ <modelId>: usage }` map;
588
- // e.g. `{ "deepseek-v4-pro": {...} }`) into the CC `SDKResult.modelUsage` record (model-id → CC ModelUsage).
589
- // 🔴 REF-CC-062(xlate-08):八键形状与那套 load-bearing 改名**不在本文件构造** —— 单源 mint 点是
590
- // `turnUsageToModelUsage.ts` 的 `toCcModelUsage()`(core 3.0.0 的 inputTokens 语义翻转注也钉在那里)。
591
- // 本函数只负责 wire 边界的窄化:开集 map 的每个值都是 `unknown`,先取出数值位再交给 mint 点。
592
265
  function mapModelUsage(stats) {
593
266
  const raw = stats?.modelUsage;
594
267
  if (raw === undefined || raw === null || typeof raw !== 'object')
595
268
  return {};
596
269
  const out = {};
597
- /** 边界窄化:非数值一律当缺席交给 mint 点(它把缺席与非有限值一并折 0)。 */
598
270
  const numOrAbsent = (v) => (typeof v === 'number' ? v : undefined);
599
271
  for (const [modelId, u] of Object.entries(raw)) {
600
272
  if (u === undefined || u === null || typeof u !== 'object')
601
273
  continue;
602
274
  const m = u;
603
- // 0.67.1 同形族:`modelId` 也是 wire 给的键 ⇒ 落键走 {@link putOwn}。
604
275
  putOwn(out, modelId, withTerminalUsageFacts(toCcModelUsage({
605
276
  inputTokens: numOrAbsent(m.inputTokens),
606
277
  outputTokens: numOrAbsent(m.outputTokens),
607
278
  cacheReadTokens: numOrAbsent(m.cacheReadTokens),
608
279
  cacheWriteTokens: numOrAbsent(m.cacheWriteTokens),
609
280
  costMicroUsd: numOrAbsent(m.costMicroUsd),
610
- // D-2(0.66.0):per-model 行今天也不带这一格 ⇒ 宽读一次,读不出由 mint 点立判别位。
611
281
  webSearchRequests: numOrAbsent(m.webSearchRequests),
612
282
  }), m));
613
283
  }
614
284
  return out;
615
285
  }
616
- /**
617
- * P1-5 — 终帧的 `modelUsage`:优先 wire 的 per-model split;缺席(老引擎 stats 无 MF-25 分表,或
618
- * split-less done)时按**当前模型**从扁平 `TaskStats` 合成一行,JSON 消费者才知道这笔 usage 是谁产的。
619
- * 🔴 REF-CC-055/062:success 臂与五个 error 臂此前各自持有这段逻辑的结果(合成行只在 success 路径
620
- * 算一次、error 臂靠共享局部变量捎带),现在两条路都调本函数 —— 同一输入同一输出,单一构造点。
621
- */
622
286
  function modelUsageFor(stats, model) {
623
287
  const split = mapModelUsage(stats);
624
288
  if (Object.keys(split).length > 0)
625
289
  return split;
626
290
  if (typeof model !== 'string' || model.length === 0)
627
291
  return split;
628
- // 键迁移(见 costOrNull 头注):legacy float `costUsd` → 整数 `costMicroUsd`(/1e6 → USD)。
629
- // ⚠️ 这一处**类型门抓不到**:TaskStats 带 `[key: string]: unknown` 开集索引,`typeof
630
- // stats?.costUsd === 'number'` 的旧写法在 SDK 1.x 下照样编译得过,只是恒假 ⇒ 合成行的
631
- // costUSD 静默恒 0(比报错更隐蔽)。tsc 只红了 6 处直传,这处靠人核 + pure 门的行为钉。
632
292
  return {
633
293
  [model]: withTerminalUsageFacts(toCcModelUsage({
634
294
  inputTokens: stats?.promptTokens,
635
295
  outputTokens: stats?.outputTokens,
636
296
  cacheReadTokens: stats?.cachedTokens,
637
- // B-073 ③ 族扫:合成行是 `flattenUsage` 的**同形第二处**,同批一并改读真值。
638
297
  cacheWriteTokens: stats?.cacheWriteTokens,
639
298
  costMicroUsd: stats?.costMicroUsd,
640
- // D-2(0.66.0):与扁平 usage 同一格同一读法(开集宽读,读不出由 mint 点立判别位)。
641
299
  webSearchRequests: stats?.webSearchRequests,
642
300
  }), stats),
643
301
  };
644
302
  }
645
- /**
646
- * 给一条已铸好的 CC `ModelUsage` 行挂上终局那三个座位(读不出的一律不铸)。
647
- * `raw` = 这一行的 wire 原形(分表的那一行,或扁平 `TaskStats` —— 两处键名相同)。
648
- */
649
303
  function withTerminalUsageFacts(row, raw) {
650
304
  const total = finiteOr0Absent(raw?.totalInputTokens);
651
305
  const long = finiteOr0Absent(raw?.cacheWriteTokensLong);
@@ -657,51 +311,19 @@ function withTerminalUsageFacts(row, raw) {
657
311
  ...(long !== undefined ? { _sema_cache_write_tokens_long: long } : {}),
658
312
  };
659
313
  }
660
- /** 有限数窄化(非数 / 非有限 ⇒ 缺席;`0` 是读数不是缺席)。 */
661
314
  function finiteOr0Absent(v) {
662
315
  return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
663
316
  }
664
- /**
665
- * TaskResult 五键最小消费批([1543] SDK-M,2026-07-23)之一 —— `degraded` = 模型降级链实录
666
- * (from/to/reason/atTurn;多模型网关部署的关键可观测面)。
667
- *
668
- * 🔴 REF-CC-056(xlate-02):文件头共享注一直写着「success/timeout/blocked/unknown 各终态都可能带,
669
- * **恒 additive 附上**」,而实际只有 timeout 与 success 两臂 spread 了它,blocked/park/未知/failed
670
- * 四臂全漏 —— 一条经历过降级并最终失败的 run,降级链在终帧上无痕消失,而注释向下一棒承诺它在。
671
- * 修法不是再补四处 spread(那是第五、六个构造点),而是把它放进 {@link errorResult} 信封本体:
672
- * 「恒 additive 附上」这句话从此是结构,不是散文。
673
- *
674
- * 边界守门(M1):wire 上的值仍按对象守卫 —— 非对象/缺席一律不 stamp 这个键(诚实缺席)。
675
- */
676
317
  function degradedOf(r) {
677
318
  const d = r.degraded;
678
319
  return d !== undefined && d !== null && typeof d === 'object' ? d : undefined;
679
320
  }
680
- // ── 到限码词表(core 5.8.0 [2489] 新码单代)──────────────────────────────────────────────────
681
- // 退役批(server 6.0.0 捆 core 5.8.0 发车,2026-08-04):三个集合里的 5.7 旧码成员
682
- // (`budget.exceeded`/`budget.precall`/`limit.max_turns`)已删,只留新码。放成集合而不是就地 `||`
683
- // 链的原因不变:词表变动集中在一处可数,不必满文件找 `===` 比较。
684
- // G1 去字面化(2026-08-04):**码的字面**已收编进 `engineErrorCodes.ts`(单一真源),这里留的是
685
- // 「哪些码映射到哪个 CC subtype」的**映射事实** —— 那是本文件的职责,不是词表的。
686
- /** 成本到限 → CC `error_max_budget_usd`(唯一一个 CC 有专词的预算轴)。 */
687
321
  const COST_EXCEEDED_CODES = new Set([LIMITS_MAX_COST_EXCEEDED]);
688
- /** 轮数到限 → CC `error_max_turns`。 */
689
322
  const TURNS_EXCEEDED_CODES = new Set([LIMITS_MAX_TURNS_EXCEEDED]);
690
- /**
691
- * 「截断」族到限码:CC 4 词表里**没有**对应词(token 账不是美元账、墙钟停不是轮数耗尽),
692
- * 所以 subtype 落诚实兜底词,真实语义靠 errorCode 透传;且这两种停因都可能已产出正文
693
- * ([2489] 对 max_tokens 明记 result 带正文;墙钟停是既有的写出窗 salvage 语义)⇒ 共用 salvage 腿。
694
- */
695
323
  const CUTOFF_EXCEEDED_CODES = new Set([
696
324
  LIMITS_MAX_TOKENS_EXCEEDED,
697
325
  LIMITS_MAX_WALLTIME_EXCEEDED,
698
326
  ]);
699
- /**
700
- * 🔴 **errorCode → CC subtype 的唯一映射点**。收编前这段 `||`/三元链在 done{failed} 与
701
- * `failedToSdkResult` 两处各写一份,而且两份**并不同义**(事件臂只认 budget.*,连 limit.max_turns
702
- * 都不认 ⇒ 同一个「轮数耗尽」经 done 帧来是 error_max_turns、经 failed 事件来是
703
- * error_during_execution)。5.8.0 的词表迁移要在两处各改一遍正是这类漏配的温床,故先收成单点。
704
- */
705
327
  function subtypeForErrorCode(code) {
706
328
  if (code === undefined || code.length === 0)
707
329
  return 'error_during_execution';
@@ -709,19 +331,10 @@ function subtypeForErrorCode(code) {
709
331
  return 'error_max_budget_usd';
710
332
  if (TURNS_EXCEEDED_CODES.has(code))
711
333
  return 'error_max_turns';
712
- // assemble-result.js:58 该 code 的语义字面就是 CC 这词:「did not produce a valid structured
713
- // output within the retry limit」。
714
334
  if (code === OUTPUT_INVALID)
715
335
  return 'error_max_structured_output_retries';
716
- // 🔴 开集兑现处:未知/未来码(core 5.10.0 的 `config.*` 拒绝族、`usage.window_exhausted`、
717
- // `env.lifetime_expired` 都落这里)一律诚实兜底词 —— CC 四词表里没有它们的对位词,真实语义
718
- // 靠上面 `errorCode` 原样透传辨认。绝不为了「有个更像的词」把它们塞进任何一个已知 subtype。
719
336
  return 'error_during_execution';
720
337
  }
721
- /**
722
- * 写出窗抢救文本:引擎已兑现的最终正文(`result` 优先,回落 `salvagedOutput`)。空白串当缺席
723
- * —— 空串骑上 `result` 键就是「冒充有产出」。
724
- */
725
338
  function salvagedTextOf(r) {
726
339
  const salvagedOutput = r.salvagedOutput;
727
340
  return typeof r.result === 'string' && r.result.trim().length > 0
@@ -730,137 +343,54 @@ function salvagedTextOf(r) {
730
343
  ? salvagedOutput
731
344
  : undefined;
732
345
  }
733
- /**
734
- * 截断臂(墙钟/ token 到限)的公共两位:errors[0] + 可选 salvage。
735
- *
736
- * [2084]②b:引擎的 cutoff 句承诺「cut off to preserve a write-out window」,但 pre-call throw 路径上
737
- * 写出窗可能零兑现(core dist/brain/stream-engine 五处 throw,载体天生空)。抢救为空时补诚实尾注 ——
738
- * 每一层都复述没兑现的承诺,消费者就无从分辨救没救回来。
739
- */
740
346
  function cutoffParts(r, message, fallbackMessage, code) {
741
347
  const salvaged = salvagedTextOf(r);
742
348
  return {
743
349
  errors: [
744
350
  `${message ?? fallbackMessage} (${code})${salvaged === undefined ? ' — no output survived the cutoff' : ''}`,
745
351
  ],
746
- // 写出窗 salvage(additive seam 字段):非空时随帧带出 —— CC error 帧无 result 键,但静默丢弃
747
- // 已救回的答案对 -p 集成面是净损失(TB qe** 实证)。
748
352
  ...(salvaged !== undefined ? { salvagedResult: salvaged } : {}),
749
353
  };
750
354
  }
751
- /**
752
- * 🔴 **CC `SDKResultError` 信封的唯一 mint 点**(REF-CC-055 / xlate-01,E1 单源构造)。
753
- *
754
- * 收编前这 13 个位在**六处**逐字重复:done 的 failed/timeout/blocked/park/未知终态五臂,加上
755
- * `failedToSdkResult` —— 后者看着像「另一种信封」,其实是同一信封 `stats === undefined` 的退化
756
- * 实例被手抄了一遍(`num_turns: 0` ≡ `stats?.turns ?? 0`、`total_cost_usd: null` ≡ `costOrNull(undefined)`、
757
- * 手写五键零 usage ≡ `flattenUsage(undefined)`、`modelUsage: {}` ≡ `mapModelUsage(undefined)`)。
758
- * 六份手抄的代价不是行数,是 REF-CC-056 那种**漏配**:给信封加一个位要改六处,漏掉的那几处没有
759
- * 任何门会说话。
760
- */
761
355
  function errorResult(ctx, parts) {
762
356
  return stamp(ctx, {
763
- // Explicit (always-undefined) uuid/session_id: stamp() always overwrites session_id from
764
- // ctx and falls back to a fresh uuid when body.uuid is nullish, so this is a pure type-level
765
- // nudge (zero runtime effect) — without at least one property name in common with stamp's
766
- // all-optional constraint, TS's "weak type" detection rejects the call (TS2559/TS2345/TS2353).
767
357
  uuid: undefined,
768
358
  session_id: undefined,
769
359
  type: 'result',
770
360
  subtype: parts.subtype,
771
- duration_ms: parts.durationMs, // P1-5 real elapsed (stream-open → terminal)
772
- duration_api_ms: parts.durationMs, // engine run == the API call from the shell's view
361
+ duration_ms: parts.durationMs,
362
+ duration_api_ms: parts.durationMs,
773
363
  is_error: true,
774
- num_turns: parts.stats?.turns ?? 0, // 件2a 同款守卫 —— 缺 stats 不炸
775
- stop_reason: null, // dropped (contract 02 §2.10)
776
- total_cost_usd: costOrNull(parts.stats), // P1-5: null when unknown, never a fake 0
364
+ num_turns: parts.stats?.turns ?? 0,
365
+ stop_reason: null,
366
+ total_cost_usd: costOrNull(parts.stats),
777
367
  usage: flattenUsage(parts.stats),
778
368
  modelUsage: modelUsageFor(parts.stats, parts.model),
779
- // D-1 / L-192①:两句话不再折成一句 —— 清单 + 「有没有这本账」的判别位,见 permissionDenialParts。
780
369
  ...permissionDenialParts(parts.stats, parts.observed?.toolInputByCallId),
781
- // D-3 / B-068:失败/到限/park 的 run 一样花过钱,账不因结局不好就不报。
782
370
  ...costFactParts(parts.stats, parts.observed),
783
- // CC-06:引擎生效面两键(失败臂同带)。
784
371
  ...(parts.facts ?? {}),
785
- // L-228(0.67.0):流内 per-subagent 分表的收口快照(判据本体在 `nestedUsageByTaskParts`)。
786
- // 🔴 与成本三段同理 —— 失败的 run 一样委派过,账不因结局不好就不报。
787
372
  ...nestedUsageByTaskParts(parts.stats, parts.observed?.nestedUsageByTask),
788
373
  errors: [...parts.errors],
789
374
  ...(parts.errorCode !== undefined && parts.errorCode.length > 0 ? { errorCode: parts.errorCode } : {}),
790
375
  ...(parts.degraded !== undefined ? { degraded: parts.degraded } : {}),
791
- // ADAPTER-F4:具名 additive 位(见 `ErrorResultParts.salvagedResult`)。展开的是**一个已知键**,
792
- // 覆写不到上面任何一个不变量;要加第二个位必须动这里,而动这里在 diff 里显形。
793
376
  ...(parts.salvagedResult !== undefined ? { result: parts.salvagedResult } : {}),
794
- // CC-50:结局不知道的稀疏标记(见 `ErrorResultParts.outcomeUnknown`);真终局帧永不带。
795
377
  ...(parts.outcomeUnknown === true ? { _sema_outcome: 'unknown' } : {}),
796
378
  });
797
379
  }
798
- /** `done` → SDKResultSuccess (contract 02 §2.10 / 08 CS-10). */
799
- export function doneToSdkResult(ev, ctx,
800
- /**
801
- * 0.67.2:**这一条流**的收口快照 —— usage 缺口观测(见 {@link usageLowerBoundOf})与 per-subagent
802
- * 累加表(见 {@link nestedUsageByTaskParts})。两格都是 per-stream 的事实,缺席 ⇒ 这次投影没有流内面
803
- * (只读 `stats` 那一半、两个分表键都不铸),**绝不**回头去读调用方对象上可能残留的上一条流。
804
- */
805
- observed) {
806
- // sdk 4.1.0([2395]E 调和):`done.result` 是判别联合 `TaskResult | ActiveRunConflictDoneResult`。
807
- // 🔄 全窗复审 ADAPTER-1/-2 收口(2026-08-03):首版接线用 `'stats' in r` 铸 tr 并让 result/model/
808
- // degraded 全走 tr?.——把「stats 在不在」错当成了这些键的门控(anchor-on-the-deciding-quantity
809
- // 反例):R1b 抢救腿从 run row 合成的 done 帧 stats 可缺席但带答案正文,被投影成空成功;且
810
- // `in` 对非对象 result 抛 TypeError(旧码宽读不抛,件2c 事故形)。正解=各键**独立宽读**
811
- // (键在就读、缺席 undefined,与拆分前逐键同语义),判别联合只影响类型声明不影响读法。
380
+ export function doneToSdkResult(ev, ctx, observed) {
812
381
  const rRaw = ev.result;
813
382
  const rec = typeof rRaw === 'object' && rRaw !== null ? rRaw : {};
814
383
  const r = rec;
815
384
  const stats = r.stats;
816
- // P1-5 — the terminal frame's session_id must be the ENGINE-AUTHORITATIVE id (done.result.sessionId):
817
- // on the headless `-p` path ctx was seeded from the REQUEST (historically the mock constant
818
- // "sema-mock-session-0001" leaked to stdout, so `sema -r <session_id>` on the JSON output could never
819
- // resume). The engine echo is the id its session store actually keyed — adopt it for the result stamp.
820
385
  const echoedSid = r.sessionId;
821
386
  if (typeof echoedSid === 'string' && echoedSid.length > 0)
822
387
  ctx.sessionId = echoedSid;
823
- // P1-5 — modelUsage/usage 的构造已收敛到 `flattenUsage` / `modelUsageFor`(REF-CC-055/062),
824
- // 四个 error 臂经 `errorResult` 信封拿到同一份,success 臂在下方直接调 —— 同一输入同一输出。
825
- // clay P0 壳半场([450],2026-07-05):上游 4xx(如无 vision 模型收到 image)时
826
- // 引擎的 done 帧本体带 result.status='failed' + errorMessage —— 传播链没吞,断点在这:
827
- // 此前无条件投影 subtype:'success'/is_error:false,把失败洗成"成功空回复"= 用户看到的
828
- // 静默卡死。status failed → 走 SDKResultError 形态(errors[] 渲染线现成)。
829
- // TS2367 note: `TaskResult & { status?: string }` does NOT actually widen `status` — TS
830
- // intersects the pre-existing required `RunStatus` with the added optional `string`, and
831
- // `RunStatus & string` stays `RunStatus` (the narrower member wins). That's exactly why the
832
- // status branches below (the SDK's `RunStatus` is a CLOSED union, while core's TaskStatus vocab
833
- // moves on its own release train — see file header) used to type-error as "no overlap": the cast
834
- // looked like a widening but wasn't one. `Omit` the narrow key first so the added wider
835
- // `status?: string` actually applies — that is also what keeps the 未知终态 defensive arm below
836
- // expressible at all.
837
- // 🔴 0.60.0(engine ≥7.64.0 / sdk 8.4.0):终局分流的判据从**八个平面键**换成**一条带标因由**
838
- // (`result.terminal`)。读法单源在 `runTerminal.readRunTerminal` —— 本文件一个 `status` 字面
839
- // 都不再比较(平面形的两个来源、以及「不认识的终态词绝不落成功」那条不变量,全在读器里)。
840
- // `null` = 这份载体上连终态词都没有(mock 车道 / 不带 status 的旧合成帧)⇒ 0.72.12 起 fail-closed +
841
- // `_sema_outcome:'unknown'`(此前维持成功臂;取舍见 `ErrorResultParts.outcomeUnknown`)。
842
388
  const terminal = readRunTerminal(rRaw);
843
389
  const durationMs = elapsedMs(ctx);
844
- // [909]B1 — errorCode 透传(additive seam 字段;CC schema 无此键,与 success 臂 `model` 同款超集
845
- // 姿势):引擎值原样,绝不铸造。所有 error 臂共用(`errorResult` 信封负责 stamp)。
846
- // 🔴 因由形下「码属于失败」是**形状**:paused / blocked 上引擎写不出码 ⇒ 这两臂的 `errorCode`
847
- // 从 0.60.0 起恒缺席(此前 `review.pending` 骑在 park 帧的平面 errorCode 上)。扁平形回放的
848
- // 历史行仍带得动它(读器在那三臂上保留 `code`),所以盘上真有的事实不丢。
849
390
  const errorCode = runTerminalCode(terminal);
850
- // TaskResult 五键最小消费批([1543] SDK-M,2026-07-23):additive seam 字段——
851
- // · degraded = 模型降级链实录,**各终态恒 additive 附上**(见 `degradedOf` 头注 / REF-CC-056);
852
- // · structuredOutput = outputSchema 任务的结构化产出(success 臂);
853
- // · salvagedOutput = degenerate/到限截断抢救出的最后模型文本(`cutoffParts` 的 salvage 回落源:
854
- // failed + limits.max_{tokens,walltime}_exceeded 两族共用)。
855
391
  const degraded = degradedOf(r);
856
- /** 四个 error 臂共享的固定位(信封其余 13 位见 `errorResult`)。 */
857
392
  const errorBase = { durationMs, stats, model: r.model, errorCode, degraded, observed, facts: effectiveFactParts(rec) };
858
393
  if (terminal?.kind === 'failed') {
859
- // [909]B1 — failed 臂 subtype 语义化(见文件头对表);core 5.8.0([2489])起到限码全部改名,
860
- // 映射本身已收进单点 `subtypeForErrorCode`(退役批后只认新码)。
861
- // 5.8.0 的「截断」族到限(token/墙钟):subtype 只能落诚实兜底词,但这两种停因**可能已产出正文**
862
- // —— [2489] 对 limits.max_tokens_exceeded 明记「result 带已产出正文」,墙钟停是既有写出窗语义,
863
- // 两族共用 `cutoffParts`(salvage + 零抢救诚实尾注)。
864
394
  const failedCode = terminal.code;
865
395
  const failedMessage = terminal.message;
866
396
  const failedSubtype = subtypeForErrorCode(failedCode);
@@ -876,9 +406,6 @@ observed) {
876
406
  return errorResult(ctx, {
877
407
  ...errorBase,
878
408
  subtype: failedSubtype,
879
- // rewind 族错误码透传(server 1.192 [838]③ 实测:stream 腿把 resume_at.before_* /
880
- // rewind_snapshot.unresolvable 骑在失败因由的码上)—— 与 failedToSdkResult 的同款 append
881
- // 对齐,message 后附 code 便于对账。
882
409
  errors: [
883
410
  isRewindFamilyCode(failedCode) && failedMessage
884
411
  ? `${failedMessage} (${failedCode})`
@@ -886,15 +413,6 @@ observed) {
886
413
  ],
887
414
  });
888
415
  }
889
- // 留档(退役批,2026-08-04):此处曾有一条 `rr.status === 'timeout'` 的窗口期臂 —— 5.7 的墙钟
890
- // 停是 `status:'timeout'` + `errorCode:'limit.timeout'`([909]B1;此前落 success 臂 = TB 的
891
- // 「4 个引擎已记 timeout 的 trial 全 subtype:success」)。core 5.8.0 把这个终态词整体退役,墙钟停
892
- // 改走上面的 failed + `limits.max_walltime_exceeded`(同处置,共用 `cutoffParts`);server 6.0.0
893
- // 捆 core 5.8.0 发车后旧形不再上 wire,本臂随之删除。未知终态(含万一漏网的 'timeout')不会掉回
894
- // 空成功 —— 下方兜底臂把任何非 completed 终态都投影成 SDKResultError。
895
- // [909]B1 — blocked 终态:agent 自报无法推进(assemble-result.js:99,只带 blockedReason 无
896
- // errorCode)。此前落 success 臂(TB db-wal-recovery__uuH5cAg 实证:引擎终态 blocked、帧却
897
- // subtype:success)。≠空成功 → SDKResultError,blockedReason 如实进 errors[]。
898
416
  if (terminal?.kind === 'blocked') {
899
417
  const blockedReason = terminal.reason;
900
418
  return errorResult(ctx, {
@@ -907,25 +425,9 @@ observed) {
907
425
  ],
908
426
  });
909
427
  }
910
- // [884]A1(TB 公平 P0,2026-07-16):suspended/needs_review park 终帧不许洗成空成功。
911
- // `-p` 恒 DURABLE_APPROVAL=true 起引擎;模型调 AskUserQuestion(或 plan-mode ExitPlanMode)时
912
- // 引擎 park 吐 done{status:'suspended'|'needs_review'}(+ taskId/checkpointGate)。交互 REPL 有
913
- // liveHitlAskWire/planReviewWire 把 park 接成对话框;print/非 REPL 无 overlay → 桥 fail-soft
914
- // 原样吐终帧落到这里,此前落 success 臂 = subtype:'success'/is_error:false/result:'' →
915
- // **exit 0 空成功**(评测 harness 判 0 分且零错误信号,比死等更隐蔽)。分流 SDKResultError:
916
- // 三 output 形态经既有消费面各自正确(json 单文档 is_error、stream-json result 帧 is_error、
917
- // text errors[0]→stderr formatTextModeExecutionError),rc≠0 由 print.ts
918
- // gracefulShutdownSync(is_error ? 1 : 0) 顺出。文案带 run/session id 指路(交互 REPL resume 或
919
- // approvals decide),与批 G 件2 的 409 active-run 文案同源指引——那是 409 拒的
920
- // done{status:'failed', activeTaskId} 帧(上方 failed 臂),这是 park 终帧,两帧互不冲突。
921
- // CC 对照:CC 2.1.207 AskUserQuestion.isEnabled 在非交互且无 SDK permissionPromptTool 时
922
- // return false = 模型源头看不到该工具;core [884] 自领 1.296 加同款非交互闸(roster 源头不进)。
923
- // 本分流是闸到货后的防御纵深:即便引擎吐了 suspended,壳也不许洗成空成功。
924
428
  if (terminal?.kind === 'paused') {
925
429
  const parked = r;
926
430
  const runId = typeof parked.taskId === 'string' && parked.taskId.length > 0 ? parked.taskId : undefined;
927
- // 🔴 0.60.0:两句措辞的判据从退役的 `status` 五词换成**门种**(因由形下 park 只带 gate)——
928
- // 单源 `runTerminal.isReviewPark`(词表 ⊇ 重开臂路由表,包含关系由常驻门钉)。
929
431
  const gateTool = runTerminalGateToolName(terminal);
930
432
  const review = isReviewPark(terminal);
931
433
  const sid = typeof ctx.sessionId === 'string' && ctx.sessionId.length > 0 ? ctx.sessionId : undefined;
@@ -933,8 +435,6 @@ observed) {
933
435
  const wayOut = review
934
436
  ? `Review the plan in the interactive REPL${sid ? ` (sema --resume ${sid})` : ''}.`
935
437
  : `Answer it in the interactive REPL${sid ? ` (sema --resume ${sid})` : ''}, or decide the pending approval via the engine approvals API${sid ? ` (POST /v1/approvals/${sid}/decide)` : ''}.`;
936
- // [909]B1 — 平面形回放的 needs_review 行仍带 review.pending;因由形下 park **写不出**码
937
- // (见上方 errorCode 头注),两代都由信封原样透传。
938
438
  return errorResult(ctx, {
939
439
  ...errorBase,
940
440
  subtype: 'error_during_execution',
@@ -943,14 +443,6 @@ observed) {
943
443
  ],
944
444
  });
945
445
  }
946
- // [909]B1 — 防御臂:四臂之外的未知终态词(core 未来加因由臂 / 未知 wire 变形)绝不洗成空成功
947
- // —— error_during_execution + errors[] 如实报出那个词的字面。读器把两代字节的这一格都归到
948
- // `unknown` 臂(因由形取 `terminal.kind`,扁平形取 `status`),所以本臂对两代同样有效。
949
- // 🔴 **终态词整个缺席**(读器返 `null`):0.72.12 CC-50 起**不再**落 success 臂 —— 此前这里维持成功
950
- // (mock 车道 / 不带终态词的旧合成帧「零行为变化」),等于把「这份载体上连终态词都没有」编成一份成功
951
- // 结果。改为 fail-closed + `_sema_outcome:'unknown'`(取舍与理由见 `ErrorResultParts.outcomeUnknown`);
952
- // 旧合成帧那一路从 success 翻成 error 是**真行为变化**,随 0.72.12 发车帖「取舍」段披露,mock 车道
953
- // 要成功请在帧上带真终态词(`status:'completed'` / `terminal:{kind:'completed'}`)。
954
446
  if (terminal === null || terminal === undefined) {
955
447
  return errorResult(ctx, {
956
448
  ...errorBase,
@@ -970,8 +462,7 @@ observed) {
970
462
  terminal.message ??
971
463
  (terminal.word !== undefined
972
464
  ? `run ended with terminal cause "${terminal.word}" (not a success; this CLI predates that terminal word)`
973
- : // 🔴 因由座在场却整个读不出(坏形 / 混合载体)—— 说的是「读不出」,不是「没有终局」,
974
- // 更不是成功。诚实缺席优先于编一个词([honest-absence-not-fabricated-zero])。
465
+ :
975
466
  'run ended with a terminal cause this client could not read (not a success; the engine sent a malformed terminal record)'),
976
467
  ],
977
468
  });
@@ -981,65 +472,34 @@ observed) {
981
472
  session_id: undefined,
982
473
  type: 'result',
983
474
  subtype: 'success',
984
- duration_ms: durationMs, // P1-5 real elapsed (stream-open → terminal); the wire carries none
985
- duration_api_ms: durationMs, // engine run == the API call from the shell's view
475
+ duration_ms: durationMs,
476
+ duration_api_ms: durationMs,
986
477
  is_error: false,
987
- num_turns: stats?.turns ?? 0, // 件2a:同 costOrNull 那一路 —— 缺 stats 不炸
478
+ num_turns: stats?.turns ?? 0,
988
479
  result: r.result ?? '',
989
- // TaskResult 五键(additive):outputSchema 任务的结构化产出 + 降级实录(见 `degradedOf`)。
990
480
  ...(() => {
991
481
  const so = r.structuredOutput;
992
482
  return so !== undefined ? { structuredOutput: so } : {};
993
483
  })(),
994
484
  ...(degraded !== undefined ? { degraded } : {}),
995
- stop_reason: null, // dropped (contract 02 §2.10)
996
- total_cost_usd: costOrNull(stats), // P1-5: 零成本 = "unconfigured pricing" → null, never a fake 0
485
+ stop_reason: null,
486
+ total_cost_usd: costOrNull(stats),
997
487
  usage: flattenUsage(stats),
998
- // MF-25/P1-5 — the per-model usage split (wire `stats.modelUsage`, or the synthesized current-model row).
999
488
  modelUsage: modelUsageFor(stats, r.model),
1000
- // D-1 / L-192①:同形第二处 —— 与错误信封共用**同一个** mint 点(修前两处各一个字面量 [])。
1001
489
  ...permissionDenialParts(stats, observed?.toolInputByCallId),
1002
- // D-3 / B-068:成本明细与子代那本账(micro-USD 原值);`total_cost_usd` 语义一字不动。
1003
490
  ...costFactParts(stats, observed),
1004
- // CC-06(0.71.3):`_sema_effective_reasoning` / `_sema_effective_memory_scopes`。
1005
491
  ...effectiveFactParts(rec),
1006
- // L-228(0.67.0):流内 per-subagent 分表的收口快照;与 `_sema_nested_usage`(引擎报的合计)
1007
- // 是**两份独立的账**,不相加、不互证(见 `nestedUsageByTaskParts` 顶注)。
1008
492
  ...nestedUsageByTaskParts(stats, observed?.nestedUsageByTask),
1009
- // MF-25 — the effective served model id (`done.result.model`, e.g. "deepseek-v4-pro"). The CC
1010
- // SDKResultSuccess schema has no `model` field, so this rides as an additive seam field a cost/overview
1011
- // consumer reads (it is ALSO surfaced as the `modelUsage` key). Omitted when the wire didn't carry it.
1012
493
  ...(typeof r.model === 'string' ? { model: r.model } : {}),
1013
494
  });
1014
495
  }
1015
- /** `failed` → SDKResultError (contract 02 §2.11 / 08 CS-11). */
1016
- export function failedToSdkResult(ev, ctx,
1017
- /** 0.67.2:同 {@link doneToSdkResult} 的第三参。 */
1018
- observed) {
1019
- // Flatten the 4 CC error subtypes onto the single neutral errorCode.
1020
- // error_max_budget_usd is a non-error "budget exceeded" notice, not a crash
1021
- // (contract 02 §2.11) — the renderer branches on subtype.
1022
- //
1023
- // 🔴 单点(core 5.8.0 [2489]):此臂此前手写「只认 budget.*」的两分支,与 done{failed} 臂
1024
- // 的四分支**并不同义**(同一个轮数耗尽经 done 帧来是 error_max_turns、经 failed 事件来是
1025
- // error_during_execution)。现在两条路共用 `subtypeForErrorCode` —— 到限码与 CC 词表的对应关系
1026
- // 只有一处定义,5.8.0 的改名、以及本批的旧码退役,都只改了一处。
496
+ export function failedToSdkResult(ev, ctx, observed) {
1027
497
  const subtype = subtypeForErrorCode(ev.errorCode);
1028
- // 🔴 REF-CC-055:此臂**不是另一种信封**,是 `errorResult` 在 `stats === undefined` 时的退化实例
1029
- // (`num_turns: 0` ≡ `stats?.turns ?? 0`、`total_cost_usd: null` ≡ `costOrNull(undefined)`、
1030
- // 五键零 usage ≡ `flattenUsage(undefined)`、`modelUsage: {}` ≡ `modelUsageFor(undefined, undefined)`)。
1031
- // `failed` 事件帧本体不带 stats/model/degraded,所以这三个位如实缺席 —— 不是「这里少算了」。
1032
498
  return errorResult(ctx, {
1033
499
  subtype,
1034
500
  observed,
1035
501
  durationMs: elapsedMs(ctx),
1036
- // [909]B1 — errorCode 透传(additive seam 字段;done 臂同款):`failed` 事件的 cancelled/
1037
- // limits.* 等引擎值原样给集成面。
1038
502
  errorCode: ev.errorCode,
1039
- // errorCode + errorMessage flattened into the single CC errors[] string.
1040
- // Rewind-family codes(core 1.292 [833]:resume_at.before_target_not_user /
1041
- // resume_at.before_root_unsupported / rewind_snapshot.unresolvable)透传给用户 —— 这些是
1042
- // 用户可自解的操作性错误(选错目标/回退过根/快照缺失),code 附在 message 后便于对账。
1043
503
  errors: [
1044
504
  isRewindFamilyCode(ev.errorCode) && ev.errorMessage
1045
505
  ? `${ev.errorMessage} (${ev.errorCode})`
@@ -1047,13 +507,6 @@ observed) {
1047
507
  ],
1048
508
  });
1049
509
  }
1050
- /**
1051
- * Dispatch a terminal AgentEvent to its SDKResult arm.
1052
- *
1053
- * 🔴 第三参(0.67.2 / 车 I 件 I-1):**这一条流**的 usage 观测快照。`runStream` 在终帧那一拍**按值**
1054
- * 交给它与 `run_cost_reconciled` 铸臂 —— 两个投影口读的是**同一份本流快照**,而不是一个可能被别的流
1055
- * 写过的共享位。缺席(端直调)⇒ 与 0.67.1 的旧签名逐位同行为。
1056
- */
1057
510
  export function terminalToSdkResult(ev, ctx, observed) {
1058
511
  return ev.type === 'done' ? doneToSdkResult(ev, ctx, observed) : failedToSdkResult(ev, ctx, observed);
1059
512
  }