@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
@@ -8,48 +8,27 @@ import { recordEngineToolLabel } from '../engineToolLabelStore.js';
8
8
  import { registerSubagentAlias, registerSubagentContentAlias } from '../subagentContentStore.js';
9
9
  import { readAsyncLaunchedAgentReceipt } from '../toolResult.js';
10
10
  import { isWorkflowAgentTaskId, recordWorkflowAgentTaskId } from '../workflow.js';
11
- // L-215+(0.65.0;core [6908]):终局状态词的单铸谓词(三处同形一次改齐)。
12
11
  import { isTerminalStatus } from '../runTerminal.js';
13
12
  import { resolveEnginePanelTaskId } from '../engineAgentPanelStore.js';
14
- // 0.75.1 CC-95(b):子代类型的诚实来源台账(fleet 行帧折进 name 之前留的那一份;包内叶)。
15
13
  import { fleetRowAgentType } from '../fleet/fleetRowAgentType.js';
16
14
  import { chrome, mainLane, transcript, messageIdentityOf } from './ids.js';
17
15
  import { CANCEL_MESSAGE, decisionOf, flattenWireOutput, REJECT_MESSAGE, sanitizeToolUseBlock, shortTaskLabel, TASK_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './wireShapes.js';
18
- // ══ M2/M1 —— assistant(tool_use 半场 + durable 文本/思考半场)══════════════════════════════════
19
16
  const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
20
17
  const message = m.message;
21
18
  const blocks = Array.isArray(message?.content)
22
19
  ? message.content
23
20
  : [];
24
21
  const toolUseIdx = blocks.findIndex(b => b?.type === 'tool_use');
25
- // 拆分同批消掉一处 `!` 非空断言(规范禁用):`findIndex >= 0` 与「取到的不是 undefined」
26
- // 本来就是同一件事(判别式 `b?.type === 'tool_use'` 对 undefined 恒假),写成显式取值即可。
27
- // 收货裁定(对抗复审 #4):旧版此处直接取值使用,畸形帧(accessor/稀疏形,非 JSON 数据帧)
28
- // 会抛 TypeError;guard 保留=有意的健壮性变化 —— 正常 wire 帧(JSON.parse 产物)下
29
- // `findIndex >= 0 ⇒ 恒非 undefined`,零行为差。
30
- //
31
- // 🔴 ADAPT-F9 收口(2026-08-02 全窗复审):上面那句「零行为差」只覆盖了正常帧。**非正常帧上**
32
- // 旧码抛 TypeError(响亮),而首版 guard 写成 `if (original !== undefined) {…}` ⇒ 取不到块时
33
- // 不 return,而是**继续往下落进 text/thinking 半场** —— 后果不是「静默降级」而是**落进另一条臂**:
34
- // 可能产出一条不该有的 assistant 文本消息 + 置 `markEmittedText()`(压掉 `result` 臂的终答兜底),
35
- // 而卡永远不开 ⇒ 后到的 `tool_end_result` 被当「无对应开卡」直接丢。fail-loud→fail-soft 可以,
36
- // 但绝不许 fail 到隔壁臂上去。判据锚在**决定分支的量**:`toolUseIdx >= 0` 就是「这是一条 tool_use
37
- // 帧」,它一旦成立,text/thinking 半场就与本帧无关 —— 取不到块只能早退。
38
22
  if (toolUseIdx >= 0) {
39
23
  const original = blocks[toolUseIdx];
40
24
  if (original === undefined)
41
- return; // 畸形:声称有 tool_use 块却取不到 ⇒ 本帧到此为止
25
+ return;
42
26
  const decision = decisionOf(original);
43
27
  const san = sanitizeToolUseBlock(original);
44
28
  const renderBlocks = blocks.map((b, i) => (i === toolUseIdx ? san.block : b));
45
29
  const toolUseId = String(san.block.id);
46
30
  const toolName = typeof san.block.name === 'string' ? san.block.name : 'unknown';
47
- // P0-2 侧信道:core 展示名进本包台账(渲染块上已被 sanitize 摘掉)。cli 同位、同顺序
48
- // (在任何 yield 之前),对 reject/cancel/error 三条早退路径同样生效。
49
- // 🔴 时序留钉(矩阵 §2.1①):这必须是臂体的**第一条**副作用语句 —— 挪到下面任何 yield 之后,
50
- // reject/cancel/error 三条早退路径就永远登记不上 label,unknown-tool 卡按 id 查不到展示名。
51
31
  recordEngineToolLabel(toolUseId, san.label);
52
- // 引擎模式下壳工具 call() 从不执行 —— ctrl+t 面板台账只能靠这条侧信道(宿主义务)。
53
32
  if (TASK_TOOL_NAMES.has(toolName)) {
54
33
  yield chrome({
55
34
  kind: 'task_ledger_sync',
@@ -60,21 +39,12 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
60
39
  input: san.block.input,
61
40
  });
62
41
  }
63
- // #27 段序:先把这张卡之前的散文提交,再提交它之前的思考(思考恒在卡上方)。
64
42
  yield* text.takeAnswerSegment();
65
43
  yield* text.takeThinking();
66
- // 0.62.0:卡之前的散文已经落账,卡**之后**的散文属于下一条 assistant 消息 —— 划界。
67
- // (终帧 `result` 在引擎侧只取**最后一条** assistant 消息的文本,补差判据的两边必须是同一种东西。)
68
44
  text.beginAssistantMessage();
69
- // L-215③:身份位取一次(下面只用 `model` —— `id` 是 per-RESPONSE 分组键,见块内注)。
70
45
  const cardIdentity = messageIdentityOf(m, ctx);
71
46
  yield transcript({
72
47
  type: 'assistant',
73
- // S1:per-RESPONSE 铸 id —— CC 的 grouped-agent 渲染按 `${message.id}:${toolName}`
74
- // 分组;id-less 帧会把整个 session 的 Agent 卡塌成一张。tool_use 帧才盖章。
75
- // 🔴 L-215③:这条臂**只接 `model`,不接上游那个 `id`** —— 两个 `id` 是**两种身份**
76
- // (上游铸的是 per-EVENT 引擎身份,这里要的是 per-RESPONSE 分组键);换掉会把一张
77
- // Agent 卡拆成 N 张。`model` 没有这层冲突,照接(不接就在重建时原地丢掉)。
78
48
  message: {
79
49
  id: cards.mintResponseId(m),
80
50
  role: 'assistant',
@@ -86,9 +56,6 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
86
56
  parent_tool_use_id: null,
87
57
  }, ctx.now());
88
58
  if (decision === 'reject' || decision === 'cancel') {
89
- // S1:一个结果关闭该 model response。**yield 之前**复位 —— 矩阵 §2.2 定性为
90
- // 「形不一致的技术债、不是缺陷」(L1 turn 级、下一句就 return、早退时无读者),
91
- // 拆分同批前移成与 tool_end_result / sweep 一致的形,零行为差。
92
59
  cards.resetResponseId();
93
60
  yield transcript({
94
61
  type: 'user',
@@ -109,7 +76,7 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
109
76
  return;
110
77
  }
111
78
  if (decision === 'error') {
112
- cards.resetResponseId(); // S1,同上:yield 之前复位
79
+ cards.resetResponseId();
113
80
  yield transcript({
114
81
  type: 'user',
115
82
  message: {
@@ -128,7 +95,6 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
128
95
  }, ctx.now());
129
96
  return;
130
97
  }
131
- // ⟨帧序耦合 1/7 + 3/7⟩ 开卡:后到的 task_progress 才猜得到父、后到的 tool_end 才关得掉卡。
132
98
  cards.open({
133
99
  id: toolUseId,
134
100
  name: toolName,
@@ -136,7 +102,6 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
136
102
  ...(san.eventId !== undefined ? { eventId: san.eventId } : {}),
137
103
  ...(san.parentToolCallId !== undefined ? { parentToolCallId: san.parentToolCallId } : {}),
138
104
  });
139
- // ⟨帧序耦合 2/7⟩ W1 判据③ 的素材:只有先见过这张 Workflow 卡,后到的 tick 才判得出 workflow lane。
140
105
  if (WORKFLOW_TOOL_NAMES.has(toolName))
141
106
  inst.markWorkflowCard(toolUseId);
142
107
  return;
@@ -144,26 +109,21 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
144
109
  const textBlock = blocks.find(b => b?.type === 'text');
145
110
  if (textBlock && typeof textBlock.text === 'string' && textBlock.text.length > 0) {
146
111
  text.appendAnswer(textBlock.text);
147
- // 0.62.0:durable 的这一帧**本身就是一整条** assistant 消息 —— 先划界,再把它记成这条消息的全部
148
- // (`+=` 会把上一条消息的正文算进来,补差判据当场看到一个从不存在的「更长的消息」)。
149
112
  text.beginAssistantMessage();
150
113
  yield* text.takeThinking();
151
114
  yield transcript({
152
115
  type: 'assistant',
153
- // L-215③ 族扫:durable 内容腿也是**重建**点,身份位在这里接力(见 ids.ts messageIdentityOf)。
154
116
  message: { role: 'assistant', content: [{ type: 'text', text: textBlock.text }], ...messageIdentityOf(m, ctx) },
155
117
  uuid: idOf(m),
156
118
  session_id: ctx.sessionId,
157
119
  parent_tool_use_id: null,
158
120
  }, ctx.now());
159
- // 0.62.0:把**真上屏的这一段**记进已提交账(终帧补差判据读它;不传会让补差少算一截)。
160
121
  text.markEmittedText(textBlock.text);
161
122
  }
162
123
  const thinkingBlock = blocks.find(b => b?.type === 'thinking');
163
124
  if (thinkingBlock && typeof thinkingBlock.thinking === 'string') {
164
125
  yield transcript({
165
126
  type: 'assistant',
166
- // L-215③ 族扫:同上(durable 思考块)。
167
127
  message: { role: 'assistant', content: [thinkingBlock], ...messageIdentityOf(m, ctx) },
168
128
  uuid: idOf(m, 'durable-thinking'),
169
129
  session_id: ctx.sessionId,
@@ -171,8 +131,6 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
171
131
  }, ctx.now());
172
132
  }
173
133
  };
174
- // ══ M5 纯投影臂(零适配器状态,类型即证明)══════════════════════════════════════════════════════
175
- /** DIVERGENCE-1 —— 现役 wire 不产 user 帧;透传是给 desktop 重放存量转录留的门。 */
176
134
  const userArm = function* (m, { ctx, idOf }) {
177
135
  if (m.message === undefined)
178
136
  return;
@@ -180,8 +138,6 @@ const userArm = function* (m, { ctx, idOf }) {
180
138
  };
181
139
  const systemArm = function* (m, { ctx, idOf }) {
182
140
  yield transcript(m, ctx.now());
183
- // core 1.219 attachWorkingFiles:compact_boundary 可携 attachedFiles → CC 的
184
- // "Referenced file {path}" 行(post-compact attachment,零新 UI)。
185
141
  const attachedFiles = m.attachedFiles;
186
142
  if (Array.isArray(attachedFiles)) {
187
143
  let i = 0;
@@ -203,7 +159,7 @@ const systemArm = function* (m, { ctx, idOf }) {
203
159
  };
204
160
  const diagnosticsArm = function* (m, { idOf }) {
205
161
  if (typeof m.parentToolCallId === 'string')
206
- return; // 子流帧不上 leader 面
162
+ return;
207
163
  const att = projectDiagnosticsFrame(m);
208
164
  if (att === null)
209
165
  return;
@@ -239,21 +195,12 @@ const workspaceChangedArm = function* (m, { idOf }) {
239
195
  attachment: { type: 'workspace_changed', cwd },
240
196
  });
241
197
  };
242
- /**
243
- * FIX⑦(2026-08-07,[3017]/[3020])—— `human_input` 生命周期账本帧。
244
- *
245
- * 姿势抄同族的 `steering_injected` / `workspace_changed`(都是「引擎侧发生了一件事,壳记一笔」的
246
- * 非持久帧,归 chrome),但**不走 `attachment` 臂**:那三条 attachment 是要在转录流里渲出一行给
247
- * 用户看的东西,而本帧**不带正文** —— 没有可渲的内容。硬造一行「有人输入了什么」的空 attachment
248
- * 就是在编内容。所以它有自己的 chrome 臂,宿主义务是「可选地记进审计/时间线」。
249
- * 子流帧(带 parentToolCallId)同族让位:不上 leader 面。
250
- */
251
198
  const humanInputArm = function* (m) {
252
199
  if (typeof m.parentToolCallId === 'string')
253
200
  return;
254
201
  const carrier = typeof m.carrier === 'string' ? m.carrier : '';
255
202
  if (carrier.length === 0)
256
- return; // 无载体的账本行渲出去只是噪声(投影层已判 malformed,这里是第二道)
203
+ return;
257
204
  const str = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
258
205
  const rawActor = typeof m.actor === 'object' && m.actor !== null && !Array.isArray(m.actor)
259
206
  ? m.actor
@@ -276,22 +223,9 @@ const humanInputArm = function* (m) {
276
223
  ...(str(m.issuer) !== undefined ? { issuer: str(m.issuer) } : {}),
277
224
  ...(actor !== undefined && Object.keys(actor).length > 0 ? { actor } : {}),
278
225
  ...(str(m.entryId) !== undefined ? { entryId: str(m.entryId) } : {}),
279
- // 宿主幂等落账的键(对抗复审二轮):重放同一条账本帧必须得到同一个 eventId。
280
226
  ...(str(m.eventId) !== undefined ? { eventId: str(m.eventId) } : {}),
281
227
  });
282
228
  };
283
- /**
284
- * B-072 ④(0.65.0)—— `message_committed` **定位账本帧**。
285
- *
286
- * 姿势逐条抄同族的 `human_input`(引擎侧发生了一件事、壳记一笔的非持久帧,归 chrome;**不走
287
- * attachment / 不铸 transcript**:本帧不带正文,铸一行就是替引擎编一句它没说过的话)。
288
- * 它与 `human_input` 的唯一差别是**用途**:那一条是审计,这一条是**定位** —— 端拿它自建
289
- * `entryId → 已渲消息` 的映射表,压缩分割线(`compact_metadata._sema_preserved_segment`)
290
- * 就是靠这张表落到「保留尾段的第一条消息之前」。
291
- *
292
- * 子流帧(带 parentToolCallId)同族让位:不上 leader 面(子代的条目属子代那条流)。
293
- * 两个必填位的**形**已由投影层判过(读不出即 malformed 留痕);这里是第二道,读不出就不发。
294
- */
295
229
  const messageCommittedArm = function* (m) {
296
230
  if (typeof m.parentToolCallId === 'string')
297
231
  return;
@@ -305,25 +239,9 @@ const messageCommittedArm = function* (m) {
305
239
  entryId,
306
240
  role,
307
241
  ...(typeof m.toolCallId === 'string' && m.toolCallId.length > 0 ? { toolCallId: m.toolCallId } : {}),
308
- // 宿主幂等落表的键(同 human_input):durable 续读会重放同一条账本帧。
309
242
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
310
243
  });
311
244
  };
312
- /**
313
- * #310 / #318 件① —— `engine_notice` 引擎结构化通告(server ≥7.36,契约 = ASSISTANT-WIRE-CONTRACT 附录 D)。
314
- *
315
- * 姿势抄同族的 `human_input`(引擎侧发生了一件事、壳记一笔的非持久帧,归 chrome;**不走 attachment**:
316
- * 通告要带结构化事实与幂等键,折成一行文案会逼消费端反解 code)。
317
- *
318
- * 🔴 **开集:一个码都不判**。这里没有识别表、没有 switch、没有「已知才投」—— `code` 认不认得是
319
- * **渲染面**的事,投影面只负责把事实原样送到。`detail` 同理**整对象透传**(禁挑键:逐码不同且开集,
320
- * 挑键 = 新码的事实在本层静默蒸发)。
321
- * 🔴 **不读能力位**:帧到了就投影(A-022「到帧即服务」)。拿探测位 gate 一条已经到手的事实,只会在
322
- * 探测未判/失败时把真事实丢掉。
323
- * 🔴 子流帧(带 `parentToolCallId`)同族让位:不上 leader 面。
324
- * ⚠️ 第二道 `code` 在场判(投影层已判 malformed):同 `human_input` 的 carrier 二道判,防的是**非投影
325
- * 口喂进来的帧**(宿主自建管线 / 重放存量转录),不是重复判据。
326
- */
327
245
  const engineNoticeArm = function* (m) {
328
246
  if (typeof m.parentToolCallId === 'string')
329
247
  return;
@@ -343,34 +261,15 @@ const engineNoticeArm = function* (m) {
343
261
  ...(typeof m.sessionId === 'string' && m.sessionId.length > 0 ? { sessionId: m.sessionId } : {}),
344
262
  ...(typeof m.ts === 'number' && Number.isFinite(m.ts) ? { ts: m.ts } : {}),
345
263
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
346
- // durable 重放的第二层身份(SDK 从 SSE `id:` stamp 的 per-task 序号)—— 两个键两个命名空间,
347
- // 见投影层同位的长注。消费端幂等序:eventId > eventSeq > (都缺才退) code+ts。
348
264
  ...(typeof m.eventSeq === 'string' && m.eventSeq.length > 0 ? { eventSeq: m.eventSeq } : {}),
349
265
  });
350
266
  };
351
- /**
352
- * `wiring_manifest` 内部臂 → chrome `wiring_manifest`(core #524 + core 147③,server ≥7.58.0)。
353
- *
354
- * 契约本体在 `seam.ts` 的 {@link WiringManifestChromeEvent} 头注(它是宿主要读的那一份)。
355
- * 本臂只做两件:
356
- * 🔴 **子流断闸**(与 `engine_notice`/`text_segment_end` 同族):带 `parentToolCallId` 的接线自述
357
- * 属于子代/编排腿,把它的门卸清单渲到 leader 面上,用户会以为**自己这一 turn**少了工具。
358
- * 断闸按「**键在不在**」判不按「是不是串」判(与 `textSegmentEndArm` 同因:本臂决定的是
359
- * 「这份自述算谁的」,方向必须更严;`null` 算在场,不给坏值开口子)。
360
- * 🔴 **第二道成形判**:投影层已把两段各自校验过并保证「至少一段在场」,这里只再确认一次
361
- * ——防的是**非投影口**喂进来的帧(宿主自建管线 / 重放存量转录),不是重复判据。
362
- * 两段都不在场 ⇒ 不产事件(与投影层的「不铸空臂」同一条规矩,两层不许各说各话)。
363
- * 🔴 **一个字都不改**:段内容原样转发(`restore` 逐字、`reason` 六词不映射)——任何加工都必须
364
- * 发生在渲染面,理由见臂注义务②③。
365
- */
366
267
  const wiringManifestArm = function* (m) {
367
- // 三键断闸(修前只判 parentToolCallId:sourceTaskId / bgAgentId 两形的子代快照漏上 leader 面;与 tool_roster_delta 同律)
368
268
  if (isSubFlowSegmentEnd(m))
369
269
  return;
370
270
  const modelGate = m._sema_modelGate;
371
271
  const autoMode = m._sema_autoMode;
372
272
  const mcp = m._sema_mcp;
373
- // CC-08 ①(0.71.0):第四/五/六段
374
273
  const tools = m._sema_tools;
375
274
  const hooks = m._sema_hooks;
376
275
  const lsp = m._sema_lsp;
@@ -385,8 +284,6 @@ const wiringManifestArm = function* (m) {
385
284
  const hasLsp = typeof lsp === 'object' && lsp !== null;
386
285
  const hasGate = typeof modelGate === 'object' && modelGate !== null;
387
286
  const hasAuto = typeof autoMode === 'object' && autoMode !== null;
388
- // S-124(0.59.0):🔴 `Array.isArray` 判在场,**不判 `.length`** —— `mcp: []` 是「这条腿一台都
389
- // 没申报」这句正面事实,按长度判会把它和「老 mint 没报」折成同一件事(core 顶注点名的读法)。
390
287
  const hasMcp = Array.isArray(mcp);
391
288
  if (!hasGate && !hasAuto && !hasMcp && !hasTools && !hasHooks && !hasLsp && !hasWriteProtection && !hasAutoConsolidation && !hasReadDeny)
392
289
  return;
@@ -405,22 +302,6 @@ const wiringManifestArm = function* (m) {
405
302
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
406
303
  });
407
304
  };
408
- /**
409
- * `approval_request` / `approval_revoke` 内部臂 → 同名 chrome 臂(B-078 / L-208,0.65.0;design/172)。
410
- *
411
- * 契约本体在 `seam.ts` 的 {@link import('../seam.js').ApprovalRequestChromeEvent} /
412
- * {@link import('../seam.js').ApprovalRevokeChromeEvent} 头注(那是宿主要读的那一份)。
413
- * 本臂只做两件:
414
- * 🔴 **一个字节都不加工**:`frame` 原样转发(信封纪律 —— 窄化到 v1 是宿主拿 sdk 谓词做的动作,
415
- * 在这里替它窄化会让一条 `schemaVersion: 2` 的合法帧冒充 v1;见投影层头注)。
416
- * 🔴 **第二道信封判**:投影层已经判过 `schemaVersion` 是有限数,这里再确认一次 —— 防的是
417
- * **非投影口**喂进来的帧(宿主自建管线 / 重放存量转录),不是重复判据(与 `wiringManifestArm`
418
- * 的第二道成形判同因)。
419
- * 🔴 **刻意没有子流断闸**(与 `engine_notice` / `text_segment_end` 那三条相反,这是有理由的差别):
420
- * 那几条断闸挡的是「把子代腿的东西渲到 leader 面上」;而审批卡**必须**上到人面前——一张属于
421
- * 子代调用的卡也要人来点,断掉它等于把那条 run 挂死在没人看得见的地方。归属过滤由宿主按
422
- * 帧上的 `taskId` 做(撤卡臂的纪律①),那是**过滤**不是**丢弃**。
423
- */
424
305
  const approvalFrameArm = (kind) => function* (m) {
425
306
  const schemaVersion = m.schemaVersion;
426
307
  if (typeof schemaVersion !== 'number' || !Number.isFinite(schemaVersion))
@@ -430,51 +311,6 @@ const approvalFrameArm = (kind) => function* (m) {
430
311
  return;
431
312
  yield chrome({ kind, laneProof: mainLane(), frame, schemaVersion });
432
313
  };
433
- /**
434
- * `text_end` 内部臂 → **权威段替换** + chrome `text_segment_end`(#323 / core #447;L-310,
435
- * server ≥7.75.3)。
436
- *
437
- * 契约本体在 `seam.ts` 的 {@link TextSegmentEndChromeEvent} 头注(它是宿主要读的那一份)。
438
- * 本臂做四件:
439
- * 🔴 **子流断闸**(与 `engine_notice`/`prompt_suggestions`/thinking 半场同族):带 `parentToolCallId`
440
- * 的段边界属于子代/编排流,上 leader 面就是跨 lane 状态破坏 —— 直接不产事件,
441
- * **也不动 leader 的文本缓冲**(断闸在替换之前,顺序不许倒过来)。
442
- * 🔴 **整段替换文本缓冲**(0.68.1 改口;修前这里写的是「本批不动文本缓冲」)。改口的事实依据:
443
- * server 7.75.3 起 `text_end.content` 与 `result`/账本走**同一只脱敏器**,而 `text_delta` 仍
444
- * 逐字(跨 chunk 的凭据无法就地判)⇒ 两者**可以不相等**,而此前包内 committed 出去的是 delta
445
- * 拼文 = **未脱敏**字节留在本地转录里。契约原文逐字要求消费端「在 `text_end` 到达时以它整段
446
- * 替换已攒的 delta,而不是只当段界信号」。替换语义(四形处置 / 已提交前缀 / 活体尾巴重算)
447
- * 全在 {@link TextStream.replaceAnswerSegment} 的头注。
448
- * ⚠️ **这不等于撤掉 idle-flush 启发式**:那条要按 core 的「诚实缺席」纪律做 **per-stream** 判据
449
- * (整条流一帧都没有才回落启发式),仍未做 —— 替换是**兼容**启发式的(半段已被 flush 那一形
450
- * 由 `committedPrefixLen` / `committedPrefixDiverged` 如实交代),不是它的继任。
451
- * 🔴 **三个 additive 键,全部 never-false**(缺席 = 否定;与包内 `_sema_usage_absent` 同律)。
452
- * 铸一个 `false`/`0` 出来 = 把「这一段没分歧」与「这台引擎不报分歧」折成同一个字节,宿主分不出。
453
- * **为什么恰是三个**(「宿主能不能只凭一帧就知道要不要重渲」的最小解):
454
- * · `diverged` —— **活体面**那份(`stream_delta` 拼出来的)是不是过期的 ⇒ 要不要重渲;
455
- * · `committedPrefixDiverged` —— **转录面**已经 committed 的那一截是不是过期的。它是真正的
456
- * 分水岭:缺席时本臂随后交的 transcript 消息是**尾段**(前缀 + 尾段拼起来 = `content`,
457
- * 宿主什么都不用丢);在场时本包**一个字节都不再交**(撤不回的那截拼不出 `content`,
458
- * 再补一条就是同一段话上屏两遍)⇒ 该段唯一算数的那一份就是帧上的 `content`,
459
- * 宿主用它换掉 `committedPrefixLen` 指的那一截。两个键合成一个位办不到:
460
- * b1/b2 两形的 `diverged` 与 `committedPrefixLen` 完全同形,而宿主动作正相反。
461
- * · `committedPrefixLen` —— **定位量**。单位 = **JS 字符串长度(UTF-16 代码单元)**,
462
- * 🔴 **不是 UTF-8 字节**(轮三 finding① 订正:按字节截会在非 ASCII 正文上截错位置,凭据
463
- * 原样留在屏上)、🔴 **也不是「几条消息」**(轮二 finding② 订正:本包在 idle-flush 那一形下
464
- * 会把「上一段的定稿 + 这一段的半截」合并进**同一条**消息,按消息丢会把已经定稿的上一段
465
- * 一起删掉,而本包不会再补发它)。宿主照抄
466
- * `已committed正文.slice(0, 长度 - committedPrefixLen) + content` 即可。
467
- * 📋 **刻意没有第四个键 `authoritative: true`**(设计取舍,写明免得下一棒再问):它会在**每一帧**
468
- * 上恒为真 ⇒ 零判别力,既分不出新旧引擎(引擎版本不在帧上,本包不猜版本 —— 与 §33b 那条
469
- * 「刻意不留按引擎版本分岔的兼容读」同一条纪律),也不驱动宿主的任何分支。
470
- * · 第二道 `content` 在场判(投影层已判 malformed/empty):同 `engine_notice` 的 carrier 二道判,
471
- * 防的是**非投影口喂进来的帧**(宿主自建管线 / 重放存量转录),不是重复判据。
472
- */
473
- // CC-10 子流身份三键判定 `isSubFlowSegmentEnd`:CC-13(0.69.2)起住在 `adapter/types.ts` 出公面(端改调,不再复刻)。
474
- /**
475
- * CC-08(0.71.0):`tool_disclosure` 内部臂 → chrome `tool_disclosure`(名字占位普查;全量快照整只替换)。
476
- * 🔴 子流断闸与 `text_segment_end` 同族(三键任一在场 = 子代帧,不上 leader 面)。
477
- */
478
314
  const toolDisclosureArm = function* (m) {
479
315
  if (isSubFlowSegmentEnd(m))
480
316
  return;
@@ -482,7 +318,7 @@ const toolDisclosureArm = function* (m) {
482
318
  const deferred = Array.isArray(m.deferred) ? m.deferred.filter((x) => typeof x === 'string') : null;
483
319
  const activated = Array.isArray(m.activated) ? m.activated.filter((x) => typeof x === 'string') : null;
484
320
  if (policy.length === 0 || deferred === null || activated === null)
485
- return; // 二道判:非投影口喂进来的帧
321
+ return;
486
322
  yield chrome({
487
323
  kind: 'tool_disclosure',
488
324
  laneProof: mainLane(),
@@ -493,11 +329,6 @@ const toolDisclosureArm = function* (m) {
493
329
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
494
330
  });
495
331
  };
496
- /**
497
- * CC-08(0.71.0):`tool_progress` 内部臂 → chrome `tool_progress`(C-R50 一帧两拍:Bash 带尾巴三键 / 其他只 elapsed)。
498
- * 🔴 `output` 是 UNTRUSTED RAW(与 `tool_end.output` 同类,server 已脱敏一次),宿主可再封顶展示,**绝不**回喂模型。
499
- * 🔴 子流断闸同族:子代工具的进度属子代那条流,不上 leader 面。
500
- */
501
332
  const toolProgressArm = function* (m) {
502
333
  if (isSubFlowSegmentEnd(m))
503
334
  return;
@@ -518,10 +349,6 @@ const toolProgressArm = function* (m) {
518
349
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
519
350
  });
520
351
  };
521
- /**
522
- * CC-08 ①(0.71.0):`tool_roster_delta` 内部臂 → chrome `tool_roster_delta`(名册增量;LIVE only,重连按 wiring_manifest.tools 快照重同步)。
523
- * 🔴 包不持名册:`delta` 原样(已窄读)交宿主,宿主用 `applyToolRosterDelta` 与手上那份比 digest。三键子流断闸同族。
524
- */
525
352
  const toolRosterDeltaArm = function* (m) {
526
353
  if (isSubFlowSegmentEnd(m))
527
354
  return;
@@ -535,10 +362,6 @@ const toolRosterDeltaArm = function* (m) {
535
362
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
536
363
  });
537
364
  };
538
- /**
539
- * CC-08 ③(0.71.0):`context_usage` 内部臂 → chrome `context_usage`。每个压缩边界都报 ⇒ 一次任务多条,宿主按**最后一条**取当前值;
540
- * 🔴 `sections[].tokens` 与 `usedTokens` 不是同一坐标系,禁相减、禁拿一段比 `compactAtTokens`;`windowTokens` 是 autocompact 窗不是模型上下文大小。
541
- */
542
365
  const contextUsageArm = function* (m) {
543
366
  if (isSubFlowSegmentEnd(m))
544
367
  return;
@@ -559,21 +382,14 @@ const contextUsageArm = function* (m) {
559
382
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
560
383
  });
561
384
  };
562
- /**
563
- * CC-89(0.75.0):`compaction_outcome` 内部臂 → chrome `compaction_outcome`。每帧一条;转录面零行。
564
- * 🔴 lane 如实:`parentToolCallId` 非空串 ⇒ subagent;只有 `sourceTaskId` / `bgAgentId`(或父键空串)的帧没有诚实的 lane 可挂 ⇒ 不发,
565
- * 绝不落到主会话名下。`outcome` 非空串才发(非投影口喂进来的坏帧兜底)。
566
- */
567
385
  const compactionOutcomeArm = function* (m) {
568
386
  const str = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
569
387
  const outcome = str(m.outcome);
570
388
  if (outcome === undefined)
571
389
  return;
572
- // 🔴 身份四键一次性快照(0.76.1;外部验收方对 0.76.0 的 F1):修前先 isSubFlowSegmentEnd(m) 再 str(m.parentToolCallId),
573
- // parentToolCallId 读了两次 ⇒ 值随读变的帧让直喂入口丢事件、经投影却出 subagent chrome —— 两入口分叉。
574
390
  const idSnap = snapshotSegmentIdentity(m);
575
391
  if (idSnap === null)
576
- return; // 读身份就抛 ⇒ 整帧拒(与投影口判 malformed 同一处置,不发 chrome)
392
+ return;
577
393
  let laneProof = mainLane();
578
394
  if (snapshotIsSubFlow(idSnap)) {
579
395
  const parent = str(idSnap.parentToolCallId);
@@ -593,15 +409,6 @@ const compactionOutcomeArm = function* (m) {
593
409
  ...(eventId !== undefined ? { eventId } : {}),
594
410
  });
595
411
  };
596
- /**
597
- * CC-91(0.76.0):逐行**重建**白名单行(不是 `Array.isArray` 就整只透传)——
598
- * 🔴 本臂与投影口是**两个入口**:宿主自建管线 / 重放存量转录可以绕过投影口直接把 wire 帧喂进 `adapt()`,
599
- * 那条路上未校验的行会把本臂对外承诺的四条(零 `tokens`、零 digest 过境、`chars` 有限非负、全坏即缺席)
600
- * 一起破掉,还可能把违反公开型的行(`null` / `chars: -1`)递到消费端手里
601
- * (异源对抗复审 R1 [medium] 实测可复现:`sections:[{id:'x',chars:-1,tokens:123,contentHash:'…'}]` 原样进 chrome)。
602
- * ⇒ 与投影口**同律同形**:好行留、坏行逐条丢、全丢光或非数组 ⇒ 键缺席(不铸 `[]`)。
603
- * 两处实现由门逐字对拍(同一帧走投影口 vs 直喂 adapt,chrome 行必须一模一样),drift 即红。
604
- */
605
412
  const promptManifestRows = (v, pick) => {
606
413
  if (!Array.isArray(v))
607
414
  return undefined;
@@ -615,19 +422,9 @@ const promptManifestRows = (v, pick) => {
615
422
  }
616
423
  return rows.length > 0 ? rows : undefined;
617
424
  };
618
- /**
619
- * CC-91(0.76.0):`prompt_assembled` 内部臂 → chrome `prompt_assembled`。每帧一条;转录面零行。
620
- * 🔴 **包不去重**:一次任务的每一次 prepare(cascade / verify 的每一 rung、resume 腿)各自一条,server 逐条保序落账 ⇒
621
- * 每条都交出去,「只要最后一次」是**宿主**的动作(与 `context_usage` 同律;在包内按最后一条折会把先前已计费 rung 的组成面抹掉)。
622
- * ⚠️ 上游那条流的**重放去重**是另一件事,按 `eventSeq` 在 `adapter/runStream.ts` 做;本臂只说「同一条流上的多条 prepare 都算真事实」。
623
- * 🔴 lane 如实:`parentToolCallId` 非空串 ⇒ subagent;只有 `sourceTaskId` / `bgAgentId`(或父键空串)的帧没有诚实的 lane 可挂 ⇒ 不发,
624
- * 绝不落到主会话名下。一条组成面的键都读不出的帧不发(非投影口喂进来的坏帧兜底)。
625
- * 🔴 **零 tokens 铸点**:本臂只搬 `chars`(上游真字节无 token 位),不在这一层编任何 token 数。
626
- */
627
425
  const promptAssembledArm = function* (m) {
628
426
  const str = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
629
427
  const size = (v) => (typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : undefined);
630
- // ⚠️ 帧上的 `constitution` 刻意不过境(投影口头注写明理由:今天零消费场景,键即契约)。
631
428
  const totalChars = size(m.totalChars);
632
429
  const blocks = promptManifestRows(m.blocks, (o) => {
633
430
  const id = str(o.id);
@@ -639,9 +436,6 @@ const promptAssembledArm = function* (m) {
639
436
  const chars = size(o.chars);
640
437
  if (id === undefined || chars === undefined)
641
438
  return undefined;
642
- // 🔴 语义槽在**两个入口上都只认一个键名** `slot`(上游的名字,本层不改名):投影口产物与 wire 原形同名,
643
- // 所以两条路读的是同一个位。曾经的写法是「`kind` 优先、回退 `slot`」—— 那会让一条**同时**带
644
- // `kind` 与 `slot`(或只带 `kind`)的帧在两条路上读出不同的槽,而这种帧是合法的(上游行是开集)。
645
439
  const slot = str(o.slot);
646
440
  return { id, chars, ...(slot !== undefined ? { slot } : {}) };
647
441
  });
@@ -651,16 +445,9 @@ const promptAssembledArm = function* (m) {
651
445
  });
652
446
  if (totalChars === undefined && blocks === undefined && sections === undefined && tools === undefined)
653
447
  return;
654
- // 🔴 身份四键**一次性快照**,与投影口的 `identitySnapshot` / `identityOrMalformed` 同律(每键只读一次、抛错整帧拒):
655
- // ① 三个子流键**逐个**过形门(在场即必须是串)—— 只守 `parentToolCallId` 的话,一条
656
- // `{parentToolCallId:'tc-9', sourceTaskId: 7}` 会在投影口被整帧判坏、在直喂这条路上照发一条 subagent chrome;
657
- // ② **不能就地多读**:进程内帧可以带 accessor,同一个键读两次拿到两个值时,「形门读一次、子流判定再读一次、
658
- // 取 parent 又读一次」会让同一帧在两条路上归属不同 lane —— 子代的组成信息被洗成主会话的
659
- // (第三轮复审 [medium] 实测可复现)。所以这里**不**用 `isSubFlowSegmentEnd(m)`(它会再读一遍),
660
- // 子流判定直接看快照里「三键有没有在场」,语义与它逐字同(键在场 = 子代帧,空串也算在场)。
661
448
  const idSnap = snapshotSegmentIdentity(m);
662
449
  if (idSnap === null)
663
- return; // 读身份就抛 ⇒ 整帧拒(理由见 snapshotSegmentIdentity 头注与本臂上方的说明)
450
+ return;
664
451
  if (SEGMENT_END_IDENTITY_KEYS.some((k) => idSnap[k] !== undefined && typeof idSnap[k] !== 'string'))
665
452
  return;
666
453
  let laneProof = mainLane();
@@ -682,46 +469,27 @@ const promptAssembledArm = function* (m) {
682
469
  });
683
470
  };
684
471
  const textSegmentEndArm = function* (m, { text }) {
685
- // 🔴 断闸按「**键在不在**」判,不按「是不是串」判(异源对抗复审第三轮 [medium] 采纳)。
686
- // 投影层已对坏 lane 位整帧 fail-closed;这一道是给**非投影口**喂进来的帧(宿主自建管线 /
687
- // 重放存量转录)兜底 —— 那里若沿用同族其它臂的 `typeof === 'string'` 写法,一个坏值就会把
688
- // 子代的段边界**擦成 leader 的**上到宿主面。同族臂的 `typeof` 写法在它们那里只影响一行装饰,
689
- // 在本臂上决定的是「这条边界算谁的」,方向必须更严。
690
- // ⚠️ `null` 也算在场(不给它开口子):wire schema 只允许缺席或 string,`null` 是坏值不是缺席。
691
- // CC-10(0.69.1):三键任一在场即断(只带 `sourceTaskId` 的子代帧是合法形,修前漏断 = 子代覆盖 leader)。
692
472
  if (isSubFlowSegmentEnd(m))
693
473
  return;
694
474
  const content = typeof m.content === 'string' ? m.content : '';
695
475
  if (content.length === 0)
696
476
  return;
697
- // 🔴 替换在**发事件之前**:宿主拿到 `diverged` 那一刻,包内的转录面已经是权威全文了 ——
698
- // 反过来(先发信号再改缓冲)会有一拍窗口,宿主按信号去读转录面读到的还是旧拼文。
699
477
  const r = text.replaceAnswerSegment(content, m);
700
478
  yield chrome({
701
479
  kind: 'text_segment_end',
702
480
  laneProof: mainLane(),
703
481
  content,
704
- // CC-01:帧上带的是**轮换前**的段身份 —— 与本段已过境的 committed 行同值(归属的第二把钥匙)。
705
482
  segmentId: text.segmentId(),
706
483
  ...(r.diverged ? { diverged: true } : {}),
707
484
  ...(r.committedPrefixDiverged ? { committedPrefixDiverged: true } : {}),
708
485
  ...(r.committedPrefixLen > 0 ? { committedPrefixLen: r.committedPrefixLen } : {}),
709
- // CC-16(0.71.0):本窗口已提交行归属交全;`[]` 也透传(在场但空 = 零行),只有形 (f) 才整键缺席。
710
486
  ...(r.committedUuids !== undefined ? { committedUuids: r.committedUuids } : {}),
711
487
  ...(r.committedUuid !== undefined ? { committedUuid: r.committedUuid } : {}),
712
488
  ...(r.committedRows !== undefined ? { committedRows: r.committedRows } : {}),
713
489
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
714
490
  });
715
- // CC-01:段界到此为止 —— 事件已交到宿主手上之后才轮换(宿主按帧上的身份认行时,行上盖的还是同一个)。
716
491
  text.rotateSegmentIdentity(m);
717
492
  };
718
- /**
719
- * `reasoning_end` 内部臂 → 思考块**权威段替换** + chrome `thinking_segment_end`(CC-02,0.68.3;
720
- * server ≥7.77.0 S-310,sdk ≥9.4.0 `SegmentEndFields` 与 `text_end` 单源)。
721
- * 与 {@link textSegmentEndArm} 逐条同律:子流断闸(`parentToolCallId` 键在不在)/ 第二道 `content` 在场判 /
722
- * 替换在**发事件之前**。差别只在两形(见 `TextStream.replaceThinkingSegment` 头注)与多一把 `committedUuid`。
723
- * 🔴 **不轮换段身份**:段身份是文本行的(CC-01),思考块的收口与它无关。
724
- */
725
493
  const thinkingSegmentEndArm = function* (m, { text }) {
726
494
  if (isSubFlowSegmentEnd(m))
727
495
  return;
@@ -737,73 +505,39 @@ const thinkingSegmentEndArm = function* (m, { text }) {
737
505
  ...(r.committedPrefixDiverged ? { committedPrefixDiverged: true } : {}),
738
506
  ...(r.committedPrefixLen > 0 ? { committedPrefixLen: r.committedPrefixLen } : {}),
739
507
  ...(r.committedUuid !== undefined ? { committedUuid: r.committedUuid } : {}),
740
- // CC-15:`[]` 也透传(形 (a) 的正面事实「零已提交行」);只有 `undefined`(形 (f) 零经手)才不铸键。
741
508
  ...(r.committedUuids !== undefined ? { committedUuids: r.committedUuids } : {}),
742
509
  ...(typeof m.eventId === 'string' && m.eventId.length > 0 ? { eventId: m.eventId } : {}),
743
510
  });
744
511
  };
745
512
  const promptSuggestionsArm = function* (m) {
746
- // T57 第五处断闸(#47 矩阵 #5 同族):子流(parentToolCallId 标)的建议绝不骑主 composer。
747
513
  if (typeof m.parentToolCallId === 'string')
748
514
  return;
749
515
  const list = Array.isArray(m.suggestions)
750
516
  ? m.suggestions.filter((s) => typeof s === 'string')
751
517
  : [];
752
- // 空批不发(cli 同门:`if (list.length > 0)`)—— 作废由 turn 开场那一发负责,
753
- // 这里再发一次 null 会把本轮真产出的建议在极端交错下擦掉。
754
518
  if (list.length > 0) {
755
519
  yield chrome({ kind: 'prompt_suggestions', laneProof: mainLane(), suggestions: list });
756
520
  }
757
521
  };
758
- // ══ M2 —— tool_end_result(关卡 + B/D/E 层卡本体 + 两个侧产物)══════════════════════════════════
759
522
  const toolEndResultArm = function* (m, { ctx, idOf, cards, panel }) {
760
- // B4 起本臂做三件:① P0-2 label 补位(只有 tool_end 带 label 的场景;台账自身幂等不覆盖)
761
- // ② MF-10 **关卡 settle**(父卡的 tool_end 是完成信号;⚠️ 0.65.0/B-074 订正:「子代从不发
762
- // 终态 tick」这句旧前提已被 core 7.x 推翻 —— 终态 tick 现在会发,由 taskProgressArm 的
763
- // `TERMINAL_TICK_STATUSES` 分支落行,两条腿共用 `endedPanelTasks` 判重;卡的 output
764
- // 文本 = 子代最终报告,串到行 end 事件上)③ 卡台账出栈 + response-id 复位(S1)。
765
- // 🆕 B5:④ **B/D/E 层** —— 铸 `tool_result` 卡本体 + 三个侧产物
766
- // (Remember→memory_saved / task 族 structured 台账 / #117a+#158 bg Bash+Monitor 桥)。
767
523
  const callId = typeof m.toolCallId === 'string' ? m.toolCallId : undefined;
768
- // 🔴 时序留钉(矩阵 §2.1①):同 assistant 臂 —— label 登记恒在任何 yield 之前。
769
524
  recordEngineToolLabel(callId, m.label);
770
- // ── #158② bg 子代 alias(2026-08-12)────────────────────────────────────────────────────────
771
- // `async_launched` 回执是**唯一**同时握着 {本卡 callId, 引擎 taskId} 这一对的帧,而内容帧
772
- // (EventIdentity)只带 parentToolCallId = 本卡 callId。此前 `registerSubagentAlias` 的唯一喂点
773
- // 是 task_progress tick,而 bg(async)子代**恒零 tick** ⇒ C1 分流进来的内容永远停在
774
- // parentToolCallId 键下,查看态按引擎 taskId 一律取不到(详情页拿不到 per-tool 历史)。
775
- // 🔴 位置两条硬理由:① 恒在任何 yield 之前(同 label 登记,矩阵 §2.1①);② 在下面「无对应
776
- // 开卡即丢」的早退**之前** —— alias 是 store 的事实,不该由「这张卡还在不在台账里」决定
777
- // (durable 重放/乱序时开卡可能已出栈,而内容账本照样需要这条键)。
778
- // 🔴 **只登记内容归账那一半**(`registerSubagentContentAlias`,不是 `registerSubagentAlias`;
779
- // 复审收货定形,2026-08-12):那张表承载两件不同的事,方向与基数都不同(分家说明见 store 侧
780
- // 该函数头注)。回执**足以**宣告「这个 callId 下的内容属于这个 taskId」(多对一,加一条只会
781
- // 更全),但**不足以**改写单值的 `taskToParent` —— 那一位的读口 `parentToolCallIdOf` 是对外的
782
- // C2 steer 投递目标,权威来源是 **wire 的内容帧**;而 SendMessage 复活会为同一 taskId 再发一条
783
- // 回执、骑的是**复活那次调用**的 toolCallId,拿它盖掉真正的委派调用 = 用户插话投到错的目标
784
- // (「复活后该按哪个 target 投」要 server 语义作证,本包无从判定 ⇒ 不猜、不写那一位)。
785
- // 于是本喂点在 steer 面**零影响**:冲突向不存在,也就不需要「先到者持号」那类到达序启发式。
786
- // 幂等:同对重复登记 = 同一个 set 覆盖同值 + 迁移块第二次自然空转。
787
525
  if (callId !== undefined) {
788
526
  const launched = readAsyncLaunchedAgentReceipt(m.structured);
789
527
  if (launched !== null)
790
528
  registerSubagentContentAlias(callId, launched.taskId);
791
529
  }
792
530
  if (callId !== undefined) {
793
- // ⟨帧序耦合 4/7⟩ 关卡 settle(sweep 点 1/4)只结**已发布**的面板行:tick 没到过就没有行可结。
794
531
  yield* panel.settle(callId, m.isError === true, m.output !== undefined ? flattenWireOutput(m.output) : undefined);
795
532
  }
796
533
  const p = callId !== undefined ? cards.peek(callId) : undefined;
797
- // ⟨帧序耦合 3/7⟩ 无对应开卡的 tool_end(乱序 / 重复)直接丢(cli 同)。
798
534
  if (p === undefined || callId === undefined)
799
535
  return;
800
536
  const structured = m.structured;
801
- // 身份键各读一次(0.76.1:此前 typeof 再取值 = 读两次;值随读变的帧会让判形与取值各看一份)。
802
537
  const closeEventIdRaw = m.eventId;
803
538
  const closeParentRaw = m.parentToolCallId;
804
539
  const closeEventId = typeof closeEventIdRaw === 'string' ? closeEventIdRaw : undefined;
805
540
  const closeParent = typeof closeParentRaw === 'string' ? closeParentRaw : undefined;
806
- // 出栈 + S1 复位(**yield 之前**)已收进 close() —— 见 toolCards.ts 的拆分硬约束。
807
541
  yield* cards.close(p, {
808
542
  output: m.output,
809
543
  structured,
@@ -815,26 +549,18 @@ const toolEndResultArm = function* (m, { ctx, idOf, cards, panel }) {
815
549
  const s = structured !== undefined && structured !== null && typeof structured === 'object'
816
550
  ? structured
817
551
  : undefined;
818
- // ── E 层 ①:CC198 gap #2 —— 引擎 Remember 工具 → CC198 automemory「Saved N memories」卡
819
- // (system{subtype:'memory_saved'})。引擎不发写入路径,note 本身就是那一条 writtenPaths。
820
- // 上面的 tool_result 照旧走泛化兜底卡(双覆盖);structuredToToolUseResult 的白名单不动
821
- // (Remember 从不进那个 switch)。core 1.217 形 {type:'memory-saved', ok, note, …}。
822
552
  if (p.name === 'Remember' && m.isError !== true) {
823
553
  if (s && s.ok === true && typeof s.note === 'string' && s.note.length > 0) {
824
554
  yield transcript({
825
555
  type: 'system',
826
556
  subtype: 'memory_saved',
827
557
  writtenPaths: [s.note],
828
- // cli 用 `new Date().toISOString()` + randomUUID();本包一律走 ctx(可重放)。
829
- // 见 DIVERGENCE-7。
830
558
  timestamp: new Date(ctx.now()).toISOString(),
831
559
  uuid: idOf(m, 'memory-saved'),
832
560
  isMeta: false,
833
561
  }, ctx.now());
834
562
  }
835
563
  }
836
- // ── E 层 ②:core 1.217 task 族 structured —— 权威全量态精确同步 ctrl+t 面板(引擎真实 id)。
837
- // 落盘是宿主职责(T44 拆缝:判定+归一进库、落盘留宿主)。
838
564
  if (s && (s.type === 'task' || s.type === 'task-list')) {
839
565
  yield chrome({
840
566
  kind: 'task_ledger_sync',
@@ -845,49 +571,19 @@ const toolEndResultArm = function* (m, { ctx, idOf, cards, panel }) {
845
571
  });
846
572
  }
847
573
  };
848
- /**
849
- * B-074(0.65.0)—— core 明写的**终态那一拍**的两个词。
850
- *
851
- * 🔴 **闭二词,而 `status` 本身按开集读** —— 这两件事不矛盾,分工是:投影层原样透传任何词
852
- * (词表属主在 core),本表只回答「哪些词**是**终态」。一个包不认识的新词(比如上游哪天加
853
- * `cancelled`)走 **running 那条路**,而不是被猜成终态:猜错的代价是把一条还在跑的行提前
854
- * 结掉,而「多渲一拍 running」只是慢一点。core 的臂注也只承诺这两个词
855
- * (`timeout/blocked fold to failed`,即上游自己已经折过了)。
856
- */
857
574
  const TERMINAL_TICK_STATUSES = new Set(['completed', 'failed']);
858
- // ══ M3 —— task_progress(引擎侧子代的累计 usage tick)══════════════════════════════════════════
859
575
  const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
860
- // ── B4 A 层最后一臂(MF-10)—— 引擎侧子代的累计 usage tick。
861
- // 只带 rollup 数字 = CC `AgentProgress` 同形 ⇒ 忠实的 CC-187 面是 footer 面板行
862
- // (CC 自己对进程外 agent 的先例:面板行 + task-notification,从不合成 inline 消息轨)。
863
- // **不产任何 transcript 消息**:全是 chrome。
864
576
  const taskIdRaw = m.taskId;
865
577
  const taskId = typeof taskIdRaw === 'string' ? taskIdRaw : undefined;
866
578
  if (taskId === undefined)
867
579
  return;
868
- const explicitParentRaw = m.parentToolCallId; // 读一次(0.76.1:typeof 再取值 = 读两次)
580
+ const explicitParentRaw = m.parentToolCallId;
869
581
  const explicitParent = typeof explicitParentRaw === 'string' ? explicitParentRaw : undefined;
870
- // ── W1 段2([1617]②,frame-lane-matrix #2/#8)workflow lane 门:workflow 子代的 tick 属
871
- // fleet lane 自描述行(footer 只渲 workflow 聚合行),**绝不**进前台 subagent lane
872
- // (不登 alias、不 bind、不 livePanelTasks、不发 panel tick —— 修前每个 workflow agent
873
- // 都在 footer 铸一条幽灵 local_agent 行,还能骑错 run_workflow 卡的 inline stats)。
874
- // 判据三级:① 帧带 workflowRunId(一手证据)② wa+16hex 形 id(core BCE 合成 id)
875
- // ③ 显式父指向本会话见过的 Workflow 工具卡(实例台账,覆盖 uuid 形子代 id + 跨 turn 迟到 tick)。
876
- // 🔴 复审独立发现的拆分风险:**本门必须先于任何 panel 写**,否则重造 workflow 幽灵前台行 ——
877
- // 这也是它在本臂第一段、`bind()` 之前的原因,别按「先绑定再分类」重排。
878
582
  const workflowRunId = typeof m.workflowRunId === 'string' ? m.workflowRunId : undefined;
879
583
  if (workflowRunId !== undefined ||
880
584
  isWorkflowAgentTaskId(taskId) ||
881
- // ⟨帧序耦合 2/7⟩ 依赖 assistant 臂先登记过那张 Workflow 卡。
882
585
  (explicitParent !== undefined && inst.isWorkflowCard(explicitParent))) {
883
- // 判据④ 回植([1641] workflow 启动闪 subagent 行案):本门是「这条 taskId 属 workflow」的
884
- // **权威判定**。同一 taskId 在 fleet 面还有一条 run 发布器双生行(`${runId} ${taskId}`)
885
- // 且那行不带 workflowRunId —— 登进 belt 台账,fleet 行按尾段查它,双生行就再进不了
886
- // footer task 树。🔴 写者在本包、读者(isKnownWorkflowAgentTaskId)也在本包 = R4 纪律,
887
- // 做成 chrome 臂反而会让「宿主没接线 ⇒ Set 恒空 ⇒ 判据④ 静默失效」复活。
888
586
  recordWorkflowAgentTaskId(taskId);
889
- // CC-37(0.72.2):own 台账同拍登记 —— 只在「帧带 workflowRunId ∧ 父是本会话见过的 Workflow 卡」这一形
890
- // (own-run 正向证明);光有 workflowRunId 不够(老引擎对 fleet 行帧广播,别的会话的 run 也会带它)。
891
587
  if (workflowRunId !== undefined && explicitParent !== undefined && inst.isWorkflowCard(explicitParent))
892
588
  noteOwnWorkflowRun(workflowRunId);
893
589
  ctx.probe?.('tick', JSON.stringify({
@@ -902,12 +598,8 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
902
598
  }));
903
599
  return;
904
600
  }
905
- // C1 alias:content 帧只带 parentToolCallId(EventIdentity 上没有 taskId),这条 tick
906
- // **两者都带** —— 是 store 学到「停放的内容属哪条引擎行」的唯一位置(查看态按引擎 task id 查)。
907
601
  if (explicitParent !== undefined)
908
602
  registerSubagentAlias(explicitParent, taskId);
909
- // 复审修 #1:含糊回落(无显式父 + >1 张未绑候选)⇒ 保持 unbound。
910
- // ⟨帧序耦合 1/7⟩ FIFO 猜父只看**此刻还开着**的卡,所以 assistant 开卡帧必须先到。
911
603
  const cardId = explicitParent === undefined && !panel.isBound(taskId) && panel.unboundCandidates() > 1
912
604
  ? undefined
913
605
  : panel.bind(taskId, explicitParent);
@@ -921,14 +613,8 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
921
613
  currentAction: m.currentAction ?? null,
922
614
  toolUses: usage.toolUses ?? null,
923
615
  }));
924
- // core 1.262.0 [558]B / service 1.154.0 [579] —— 子代的活体"我在干什么"一行
925
- // ("Bash npm test"),service 已 redact。PROGRESS 语义(喂状态/摘要显示槽),不是诊断。
926
- // 老引擎缺席 ⇒ 两个消费面维持今天的显示。
927
616
  const currentAction = typeof m.currentAction === 'string' && m.currentAction.length > 0 ? m.currentAction : undefined;
928
- // 0.75.1 CC-95(a)—— `spawnName`:sdk 9.8.1 的 `task_progress` 臂上还没有这一位(新形才铸),所以按
929
- // **结构读**取(不靠型面),非空串才算数;缺席 = 旧形,不编。
930
617
  const spawnName = m.spawnName;
931
- // inline 群组行孪生:**绑定了**的 tick 同时喂转录里那条行的统计(按委派卡 id 键)。
932
618
  if (cardId !== undefined) {
933
619
  yield chrome({
934
620
  kind: 'inline_task_stats',
@@ -944,8 +630,6 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
944
630
  });
945
631
  }
946
632
  const card = cardId !== undefined ? cards.peek(cardId) : undefined;
947
- // T50 双形读:`rawInput` 是**改名前**捕获的,真 wire 上引擎子代的 input 是
948
- // {agent, task, taskName} —— 两形都读(CC 名优先)。
949
633
  const input = (card?.rawInput ?? {});
950
634
  const description = typeof input.description === 'string'
951
635
  ? input.description
@@ -959,20 +643,8 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
959
643
  : typeof input.task === 'string'
960
644
  ? input.task
961
645
  : undefined;
962
- // #6 常驻台账的两个写口整臂让位给 M3(module store 不许两个模块各写各的)。
963
- // 0.74.3 CC-70:常驻标记在**归一后的键**下(fleet 尾段 = 通知车道清标用的那把);此前 UUID 记、17 位清 ⇒ 永不清。
964
- // 🔴 0.75.1 CC-95(b):归一必须在 `panel.start` **之前**算 —— 子代类型的诚实来源是 fleet 行帧的 `agentType`,
965
- // 而那本账按**行 id 尾段**记,拿 wire 的 UUID 是查不到的。
966
646
  const identity = resolveEnginePanelTaskId(taskId, explicitParent);
967
- // ⟨帧序耦合 5/7⟩ 首 tick 才 fire Start,而通知臂的 if-started 门读的就是它。
968
647
  if (!panel.isLive(taskId)) {
969
- // 这条引擎子代在壳侧**可观测的起点** = 本 turn 的首 tick。
970
- // 🔴 0.75.1 CC-95(b)子代**类型**的诚实来源(此前取的是 `m.name` = 上游由委派卡 description 派生的**展示名**
971
- // ⇒ 宿主的子代启动 hook 收到的是任务描述,一个看起来合理的错答案):
972
- // ① wire `task_progress` 上的结构读类型键 —— 亲核 sdk 9.8.1 `dist/events.d.ts` 的 `task_progress` 臂:
973
- // **没有** `agentType`、也没有 `spawnAgentType`,这条 lane 今天不报类型 ⇒ **有出处才读**,所以不读;
974
- // ② fleet 行帧的 `agentType`(sdk `FleetTaskRow.agentType`,§K-7 短类型 label)—— 按归一后的尾段查;
975
- // ③ 都没有 ⇒ **键缺席**(宿主 hook 侧按 absent 兜底,不编 `subagent`)。
976
648
  yield* panel.start(taskId, fleetRowAgentType(identity.taskId));
977
649
  }
978
650
  panel.noteResidency(taskId, identity.taskId);
@@ -984,65 +656,27 @@ const taskProgressArm = function* (m, { ctx, cards, panel, inst }) {
984
656
  kind: 'tick',
985
657
  taskId,
986
658
  ...(typeof m.name === 'string' && m.name.length > 0 ? { name: m.name } : {}),
987
- // 0.75.1 CC-95(a):派生这只子代时被声明的名字(wire `task_progress.spawnName`)。结构读、非空串才带;
988
- // 缺席 = 旧形的产生者 ⇒ 键不落(不编、不拿展示名 `name` 冒充 —— 两位是两位)。
989
659
  ...(typeof spawnName === 'string' && spawnName.length > 0 ? { spawnName } : {}),
990
660
  ...(description !== undefined ? { description } : {}),
991
661
  ...(prompt !== undefined ? { prompt } : {}),
992
662
  ...(currentAction !== undefined ? { currentAction } : {}),
993
- // 0.74.3 CC-70:身份归一的两把钥匙 —— 退路键 + 发布方提示(绑卡 = 同步委派,漏斗不用等 fleet 行)。
994
663
  ...(explicitParent !== undefined ? { parentToolCallId: explicitParent } : {}),
995
664
  ...(panel.isLiveBound(taskId) ? { cardBound: true } : {}),
996
- // 🔴 这里的 `: 0` 与 `fleetAgentPanelProjection` 那句「绝不在本层 `?? 0`」**不矛盾**,
997
- // 因为不是同一条 lane 的同一种供给形(两层的分层实情写在 `engineAgentPanelStore.ts`
998
- // 的 `PANEL_TOOLUSES_LANE_POLICY` 上,两条 lane 的可选性由编译钉锁住):
999
- // · **tick lane(本处)**:`usage` 是 core `task_progress` 臂的**必填**字段
1000
- // (core 2.12.0 `dist/core/types.d.ts:606-610` 无 `?`),两处发帧点
1001
- // (`runner/runtask.js:1080` 每轮边界 / `:2925` 终态 settle 拍)**无条件**构造
1002
- // `{ totalTokens, toolUses: stats.toolCalls, durationMs }`,server 白名单整键透传
1003
- // (`trace/project.js:118`)⇒ 缺席在这条 lane 上不是「不知道」,是**没有这种帧**。
1004
- // 值本身是**累计**计数(`stats.toolCalls`,从 0 起算),所以真值域含真 0。
1005
- // · **fleet-row lane(那处)**:同名键是 server 1.278.0 才**新加**的可选位,老引擎
1006
- // 真的不发 ⇒ 缺席就是「不知道」,兜 0 会把它冒充成「跑了 0 个工具」,而且会把
1007
- // 台账里已知的累计值**擦回 0**。
1008
- // ⚠️ 别把两层「统一」:把本处改成传播缺席要先把事件形改成可选,而那正是本 lane
1009
- // 刻意不做的事(`engineAgentPanelStore.ts:54-57` 记的就是这条 —— 复用同一个可选形
1010
- // 会让 fleet-row lane 每秒把前台子代由真 tick 写进去的进度数字清一次)。
1011
665
  toolUses: typeof usage.toolUses === 'number' ? usage.toolUses : 0,
1012
666
  totalTokens: typeof usage.totalTokens === 'number' ? usage.totalTokens : 0,
1013
667
  },
1014
668
  });
1015
- // ── B-074(0.65.0)终态那一拍 ────────────────────────────────────────────────────────────
1016
- // 修前本臂的头注写着「**子代从不发终态 tick**,父卡的 tool_end 就是完成信号」—— 那句话在
1017
- // core 7.x 上已经是假的(`task_progress.status` 的臂注逐字:`ONE final tick fires at the
1018
- // sub-run's settle with the terminal face`,理由也写死了:`so a uuid-lane consumer sees the
1019
- // row finish instead of a bare removal after the last running beat`)。
1020
- //
1021
- // 🔴 **为什么落在臂尾、而不是一进来就早退**(异源对抗复审实抓的两条真回归,别按「先判终态再
1022
- // 干活」重排):终态那一拍带的是子代的**最终**累计数字。早退会让
1023
- // ① 面板行与 inline 群组行**冻在倒数第二拍**(实测 3 次工具 /150 tokens 被冻成 2 次 /100);
1024
- // ② 后到的关卡 settle 撞 `endedPanelTasks` 判重而整条让位 ⇒ 子代的**最终报告**(委派卡的
1025
- // output,只有关卡那条腿带得动)从此到不了面板行。
1026
- // 所以这一拍**照常走完上面的 tick 半场**(数字先落地),只在最后多一件事:落终态。
1027
- //
1028
- // 🔴 **只落「关卡腿不会来收」的那些行**(`!panel.isLiveBound`):绑在本 turn 还开着的卡上的行
1029
- // 由 `panel.settle(cardId, isError, report)` 收 —— 那条腿带着最终报告,在这里抢先落行就是
1030
- // 把报告挤掉(上面 ②)。未绑 / inert 绑定(跨 turn bg 子代)= session 常驻行,关卡腿不碰它,
1031
- // 它今天只认通知帧 —— 那正是 core 说的 uuid lane,也正是本件要救的那一条。
1032
- // 🔴 **开集第四词不当终态**:词表属主在 core(`TERMINAL_TICK_STATUSES` 头注)。
1033
669
  if (typeof m.status === 'string' && TERMINAL_TICK_STATUSES.has(m.status) && !panel.isLiveBound(taskId)) {
1034
670
  ctx.probe?.('tick', JSON.stringify({ t: ctx.now(), taskId, lane: 'foreground', settle: m.status }));
1035
671
  yield* panel.settleFromTerminalTick(taskId, m.status === 'failed');
1036
672
  }
1037
673
  };
1038
- // ══ M1 / M6a —— stream_event / turn_usage ══════════════════════════════════════════════════════
1039
674
  const streamEventArm = function* (m, { text }) {
1040
675
  const ev = m.event;
1041
676
  if (ev && ev.type === 'text_delta' && typeof ev.delta === 'string') {
1042
677
  yield* text.feedText(ev.delta, m);
1043
678
  }
1044
679
  else if (ev && ev.type === 'thinking_delta' && typeof ev.delta === 'string') {
1045
- // 子流(parentToolCallId)的推理绝不喂 leader 的计数与预览(#9 的壳侧半场)。
1046
680
  if (ev.parentToolCallId !== undefined)
1047
681
  return;
1048
682
  yield* text.feedThinking(ev.delta, m);
@@ -1050,29 +684,14 @@ const streamEventArm = function* (m, { text }) {
1050
684
  };
1051
685
  const turnUsageArm = function* (m, { text, flags }) {
1052
686
  const ot = typeof m.outputTokens === 'number' ? m.outputTokens : undefined;
1053
- // 🔴 0.68.0(异源对抗复审轮五实抓)——**两件事分家**:
1054
- // · 「这一轮结束了」⇒ 无条件 drain + 压下一 model round 的基线(TTFT 那一拍);
1055
- // · 「这一轮花了多少」⇒ 只有**真有数字**时才发 end 度量。
1056
- // #711 之后,缺账轮这一拍**没有数字可发**(占位零不许当读数)。修前两件事合在
1057
- // `emitTurnUsageEnd` 一个动作里 ⇒ 过滤掉数字就连**模型轮边界**也一起过滤掉了:下一轮不再发
1058
- // `response_metrics{start}`,它的用量按**上一轮**的基线对账(整链实测:首轮 400 字符缺账、
1059
- // 次轮 4 字符 + 50 tokens,计数从 150 掉到 101,下一轮的 TTFT 记录一并丢失)。
1060
687
  yield* text.drainLive();
1061
688
  if (ot !== undefined) {
1062
- // ⟨帧序耦合 7/7⟩ 这一拍置 endEmitted,后到的 result 臂据它决定要不要补发 end 度量。
1063
689
  yield* flags.emitTurnUsageEnd(ot);
1064
690
  }
1065
691
  else {
1066
- // 🔴 不发假零度量,但边界照压(`endEmitted` **不置** —— 终帧若真有一笔账,补发腿仍该补)。
1067
692
  flags.noteModelRoundBoundary();
1068
693
  }
1069
694
  };
1070
- // ══ M0 编排半场 —— result(收口三形之一;与 D7/D8 的次序差异见矩阵 §2.4,不可归一)═══════════════
1071
- /**
1072
- * `whole` 是不是以 `part` 开头 —— 先逐字节比,再容忍**引擎侧的首部修剪**(`result` 出厂 `.trim()` 过,
1073
- * 而屏上那份是原文)。命中时返回**真正对上的那个前缀**(调用方按它的长度切后缀,切点才不会错位)。
1074
- * 两条都不成立 ⇒ `null`(= 不是前缀关系,由调用方落分岔臂)。
1075
- */
1076
695
  function prefixOf(whole, part) {
1077
696
  if (whole.startsWith(part))
1078
697
  return part;
@@ -1083,46 +702,22 @@ function prefixOf(whole, part) {
1083
702
  }
1084
703
  const resultArm = function* (m, { ctx, idOf, text, cards, panel, flags }) {
1085
704
  yield chrome({ kind: 'retry_status', laneProof: mainLane(), status: null });
1086
- // MF-10 防御 sweep(sweep 点 2/4):结掉那些卡从没关过的行(流被截断)。
1087
- // cli 同位:setRetryStatus(null) 之后、开卡兜底关闭之前。
1088
705
  yield* panel.settle(null, false);
1089
- // B5:开卡兜底关闭(cli 同位 —— 在终答文本**之前**,卡才不会挂着空白)。
1090
706
  yield* cards.sweep();
1091
- // 🔴 矩阵 §2.4(b):本臂 takeThinking 在 metrics **之前**、takeAnswerSegment 在 metrics **之后**,
1092
- // 与 T60 尾的连续两拍**不同序**。看着像可以归一,不能。
1093
707
  yield* text.takeThinking();
1094
708
  yield* flags.emitResultEndIfNeeded(() => {
1095
709
  const u = m.usage;
1096
710
  const ot = typeof u?.outputTokens === 'number' && Number.isFinite(u.outputTokens) ? u.outputTokens : undefined;
1097
- // ── 🔴 0.68.0(#711 同形的**第四处**,异源对抗复审轮二实抓)────────────────────────────────
1098
- // 这是终帧的**补发腿**:`turn_usage` 那一拍没发过 end 度量时,由它拿终帧的 usage 补一发。
1099
- // 而 core 7.17.0 之后,一轮没量出账的 run,它的终帧 usage 是**占位全零**(同帧的
1100
- // `_sema_usage_lower_bound` 才是那句「这笔账不知道」)⇒ 修前这里会补发一条**没有任何判别位**
1101
- // 的 `response_metrics{phase:'end', outputTokens: 0}`,而本臂的宿主义务逐字是「驱动
1102
- // responseLength reducer(ttft/对账)」—— 一条精确零会被当成一次真的「这一轮吐了 0 个 token」。
1103
- // 同一条判据在本包一共五处:公面读器 `turnEndUsage`(`eventToSdkMessage.ts`)/
1104
- // `turn_usage.outputTokens` / 子代分表 `cacheReadTokens` / `RunStreamHandle`(三处在
1105
- // `adapter/runStream.ts`)/ **本处**。前四处收口了而这条**消费链的末端**没跟上 = 过滤没贯穿到底。
1106
- // ⇒ 同一条判据:**下界位在场且读数是 `0` ⇒ 那是占位,不补发**(`outputTokens` 读不出时本来
1107
- // 就不发,这一形沿用既有的那条路);**非零照发** —— 占位恒为 0,非零必是真的量到过,
1108
- // 连带丢掉它会让一条有产出的缺账轮在对账面上凭空少一笔。
1109
711
  const lowerBound = m._sema_usage_lower_bound === true;
1110
712
  return lowerBound && ot === 0 ? undefined : ot;
1111
713
  });
1112
714
  yield* text.takeAnswerSegment();
1113
- // 具名成 terminalText:M1 收编后 `text` 是流合并器的名字,原来的同名局部会遮蔽它。
1114
715
  const terminalText = typeof m.result === 'string' ? m.result : '';
1115
- // 🔴 比较对象 = 引擎「最后一条 assistant 消息」在屏上那段(0.63.2):卡后零散文时回退到卡前那条
1116
- // (provider 一条消息 = [text, tool_use];空串是任何终帧的前缀 ⇒ 拿空串比会把整段终帧再渲一遍)。
1117
716
  const committed = text.lastCommittedAnswerText;
1118
- // L-215③:同上,身份位取一次(终帧补差腿**只接 `model`**,理由见块内注)。
1119
717
  const terminalIdentity = messageIdentityOf(m, ctx);
1120
718
  const emitTerminal = function* (body, tag) {
1121
719
  yield transcript({
1122
720
  type: 'assistant',
1123
- // L-215③ 族扫:终帧补差腿。🔴 这条消息的**正文是终帧的 `result`**,不是某条内容帧 ——
1124
- // 所以只接 `model`(那是整条流的事实),**不接 `id`**:把终帧的 eventId 当成一条
1125
- // assistant 消息的身份,会让宿主把「补差出来的那一段」与终帧本身当成同一条东西。
1126
721
  message: {
1127
722
  role: 'assistant',
1128
723
  content: [{ type: 'text', text: body }],
@@ -1134,43 +729,23 @@ const resultArm = function* (m, { ctx, idOf, text, cards, panel, flags }) {
1134
729
  }, ctx.now());
1135
730
  text.markEmittedText(body);
1136
731
  };
1137
- // ── 终答与已上屏正文的五格(0.62.0 补后四格;修前只有第一格)────────────────────────────────
1138
- // 病形:断线前已经流过一部分正文、重连后终帧带的是**完整**那份 ⇒ `emittedAssistantText` 为真,
1139
- // 于是整条终答兜底被跳过,而屏上永远缺断线期间那一截,且没有任何东西说少了。
1140
- //
1141
- // 🔴 **比较的两边必须是同一种东西**(车内异源复审逼出的真病):终帧 `result` 在引擎侧取的是
1142
- // **最后一条** assistant 消息的文本、且 `.trim()` 过,不是整轮正文的拼接。所以左边读的是
1143
- // `committedAnswerText`(**当前这条消息**已上屏的正文,工具边界重置),不是本 turn 全部正文;
1144
- // 并且相等判定先过一次 `trim()`——否则「散文 → 工具 → 散文」这种再正常不过的多步回答,
1145
- // 以及任何只差首尾空白的回答,都会被判成分岔。
1146
732
  if (!text.emittedAssistantText) {
1147
- // ① 一个字都还没上屏 ⇒ 终帧那份就是全部答复,整段渲(修前的唯一行为,逐字保留)。
1148
733
  if (terminalText.length > 0)
1149
734
  yield* emitTerminal(terminalText, 'terminal-text');
1150
735
  }
1151
736
  else if (terminalText.length === 0) {
1152
- // ② 终帧没带正文 ⇒ 无事可做(它不是「答复是空的」这句话)。
1153
737
  }
1154
738
  else if (committed.trim() === terminalText.trim()) {
1155
- // ③ 两份**是同一段话**(引擎那一位出厂就 `.trim()` 过,所以逐字节比会在纯空白差上假红)⇒ 无事可做。
1156
- // 这一格必须排在前缀两格**之前**:否则一个只差首尾空白的正常 turn 会掉进补差或分岔臂。
1157
739
  }
1158
740
  else if (prefixOf(terminalText, committed) !== null) {
1159
- // ④ 已上屏那段是终帧那份的**前缀** ⇒ 只补吐**后缀**那一截。
1160
- // 🔴 只补差不重渲:重渲整段会让屏上出现同一段话两遍(前半还是逐字重复的),那比缺一截更坏。
1161
741
  const matched = prefixOf(terminalText, committed);
1162
742
  const suffix = terminalText.slice(matched.length);
1163
743
  if (suffix.length > 0)
1164
744
  yield* emitTerminal(suffix, 'terminal-text-suffix');
1165
745
  }
1166
746
  else if (committed.startsWith(terminalText)) {
1167
- // ⑤ 终帧那份是已上屏那段的前缀(引擎侧修剪/截断)⇒ **不动**:屏上已经不少任何东西,
1168
- // 补吐没有对象,而拿一份更短的去覆盖更长的会把用户已经读到的内容抹掉。
1169
747
  }
1170
748
  else {
1171
- // ⑤ 两份**互相都不是对方的前缀** ⇒ 从某处起就分了岔。库刻意**什么都不补吐**(谁算数只有引擎
1172
- // 说得清;把终帧那份接在屏上那段后面会拼出一段谁都没说过的话),只给宿主一个事实位让转录行
1173
- // 改口。两个位是**长度**不是内容 —— 不复述任何一份正文。
1174
749
  yield chrome({
1175
750
  kind: 'result_text_diverged',
1176
751
  laneProof: mainLane(),
@@ -1179,21 +754,14 @@ const resultArm = function* (m, { ctx, idOf, text, cards, panel, flags }) {
1179
754
  });
1180
755
  }
1181
756
  };
1182
- // ══ M4 —— 通知族 ═══════════════════════════════════════════════════════════════════════════════
1183
757
  const taskNotificationArm = function* (m, { ctx, idOf, panel, inst }) {
1184
758
  const n = (m.notification ?? {});
1185
759
  const fields = normalizeTaskNotification(n);
1186
760
  if (fields === null)
1187
- return; // 空/缺 task_id:RB-75 同族,丢弃
761
+ return;
1188
762
  const taskId = fields.taskId;
1189
763
  const rawStatus = fields.status;
1190
- // L-215② 族扫(0.65.0):终局判据走单铸谓词 —— 修前手抄三词,core [6908] 的 `blocked`
1191
- // 漏在外面 ⇒ 一条自报走不下去的后台行**永远不 settle**(面板上挂着一条不会再动的「在跑」)。
1192
764
  const terminal = isTerminalStatus(rawStatus);
1193
- // #117a settle 半场 —— **故意在渲染去重之前**:settle 幂等,重放帧仍须 settle 一条
1194
- // 首投之后才注册的行。#47 矩阵 #7:只有 bash/monitor 行(或老引擎无 task_type)能被 settle
1195
- // (#158:monitor 行注册进面板后,它的终局通知也得能关行;事件帧 status="event" 非终态,
1196
- // 由下方 terminal 门挡——事件流绝不错关行)。
1197
765
  const settleAllowed = n.task_type === undefined || n.task_type === 'background_bash' || n.task_type === 'monitor';
1198
766
  if (settleAllowed && terminal) {
1199
767
  yield chrome({
@@ -1212,12 +780,9 @@ const taskNotificationArm = function* (m, { ctx, idOf, panel, inst }) {
1212
780
  ...(typeof n.seq === 'number' || typeof n.seq === 'string' ? { seq: n.seq } : {}),
1213
781
  ...(typeof n.task_type === 'string' ? { taskType: n.task_type } : {}),
1214
782
  });
1215
- // 🔴 时序留钉(矩阵 §2.1④ 的 1377 行判定:**有意序**,别挪):记号在 yield 之前落 ——
1216
- // 代价是早退窗内该通知既不渲也不再重放(丢一条),而反向(add-after-yield)是重放双渲,更坏。
1217
783
  if (inst.hasRenderedNotification(dedupKey))
1218
784
  return;
1219
785
  inst.addRenderedNotification(dedupKey);
1220
- // B1 + #6 —— 必须在 cardEnqueued 早退**之前**:probe 合成先入队的场景恰是双投主形。
1221
786
  if (n.task_type !== 'external' && terminal) {
1222
787
  yield chrome({
1223
788
  kind: 'notification_terminal',
@@ -1226,59 +791,16 @@ const taskNotificationArm = function* (m, { ctx, idOf, panel, inst }) {
1226
791
  status: rawStatus,
1227
792
  ...(typeof n.task_type === 'string' ? { taskType: n.task_type } : {}),
1228
793
  });
1229
- // B1 —— 这帧 = 引擎 server-side steer-inject 的回声,模型已在引擎侧收到一遍;把还躺在
1230
- // 队列里的同 run Path B 条目(workflow_complete/probe/bg feeder)在 turn 收口 drain 前
1231
- // 丢弃,免得 idle auto-submit 再喂模型一遍(跨进程双投的客户端半场)。
1232
- // 🔴 B4 起由**适配器自持**(B2 已把队列端口搬进本包):此前挂在 `notification_terminal`
1233
- // 的宿主义务①,是「说得通但没人做」的典型 —— 宿主没接线就静默双投。
1234
794
  dropQueuedNotificationsForRun(taskId);
1235
- // 🔴 这道门与上面的 `settleAllowed` 配对的是**显式的 bash/monitor 两族**:它们被
1236
- // bgshell_settle 关过一次的行,就不该再被**子代面板**的 settleFromNotification 关第二次。
1237
- // ⚠️ 两半**不是**字面同一个谓词,别顺手写成 `if (!settleAllowed)`:settleAllowed 还放行
1238
- // `task_type === undefined`(老引擎缺类型位的兼容腿),那一支是**有意**的双投兼容 ——
1239
- // 不知道它是哪族,两个面板都关一次比漏关一个面板好。只有显式说了自己是 bash/monitor 的
1240
- // 帧才配对排除。#158 复审车B 修:放行 monitor 那一批漏了这半 ——
1241
- // monitor 终局帧同时发 `bgshell_settle` 与 `panel_task{kind:'end'}` 两条,同一个 taskId
1242
- // 在两个面板语义上各关一次。壳侧只是**碰巧**没出事(bgshell_settle 先 yield 把行翻成终态,
1243
- // 随后 useEngineAgentPanelBridge 的 settle() 撞上 `status !== 'running'` 早退 —— 但那个
1244
- // settle() 根本没有行类型门,靠的是 yield 顺序而不是判据);sema-web 那端是**真错**:
1245
- // turn-mapping.ts panel_task/end 臂固定发 `status: isError ? 'failed' : 'completed'`,
1246
- // 于是 killed 的 monitor 行先被 bgshell_settle 正确记成 killed,再被这条覆盖成 failed。
1247
795
  if (n.task_type !== 'background_bash' && n.task_type !== 'monitor') {
1248
- // ⟨帧序耦合 5/7⟩ #6 通知-settle 边(推送半场)整段让位给 M3 —— 拆分前这里直写
1249
- // endedPanelTasks 又直读实例台账的 firedSubagentStartHookTaskIds,是矩阵点名的两条跨模块面。
1250
796
  yield* panel.settleFromNotification(taskId, rawStatus !== 'completed');
1251
797
  }
1252
798
  }
1253
- // ── design/385:三条引擎注入车道的判定**必须在下面那道完成卡早退之前**做 ────────────────
1254
- // 🔴 异源对抗复审 r2 [high] 实测的吞消息形:`isWorkflowCompletionCardEnqueued(taskId)` 回答的是
1255
- // 「这个 run 的**完成卡**是不是已经入过队」,与「这一帧是不是一条同事消息」毫无关系。而一个
1256
- // 子代**续跑**时复用同一个 taskId(`_sema_provenance.taskId === task_id === senderId`,core
1257
- // 7.2.0 uplink 铸点直证),它的合法 `agentMessage` 帧就会在这道早退上被整条丢掉:零转录输出,
1258
- // 且渲染去重账已经记过 ⇒ 重放也补不回来。复现序:先 `noteWorkflowCompletionCardEnqueued(id)`,
1259
- // 再送同 id 的 uplink 帧 ⇒ 修前只剩 chrome 收尾事件、消息正文完全缺席。
1260
- // ⇒ peer 帧**豁免**这道早退(它不是完成卡,不参与完成卡的双通道去重)。
1261
799
  const peerFrame = classifyPeerNotification(n);
1262
- // 双通道反向去重:probe 合成的完成卡已入队 ⇒ 后到的推送帧只做上面的记账,不渲第二条。
1263
- // 🔴 B5 ①:读的是 **module 台账**(cli `isWorkflowCompletionCardEnqueued` 逐字)——
1264
- // 记账方(两个 enqueue + idle watcher)与清账方(上面的 drop)都在那儿。
1265
800
  if (peerFrame === null && n.task_type !== 'external' && isWorkflowCompletionCardEnqueued(taskId))
1266
801
  return;
1267
- // 跨通道记账(cli markEngineWorkflowNotified 同位、同条件:非 external,不看终态):
1268
- // 这帧到达 = 引擎已 server-side steer-inject 过,补发通道(workflow_complete)不得再注入。
1269
- // external 豁免([769] 对抗复审收紧:external 从不经补发通道,mark 只会缴械 probe 兜底)。
1270
- // 🔴 **peer 帧同样豁免**(异源对抗复审 r3 [high],实测:投一条合法 uplink 之后再给同 taskId 入队
1271
- // `{status:'completed'}`,队列恒 0、`bgCrossChannelDropped` +1)。这本账问的是「这个 run 的
1272
- // **完成**是不是已经被引擎注入过」——而一条同事**消息**不是完成事件(三条 peer 铸点的
1273
- // `status` 都是 `"event"`,core 直证)。子代跑到一半发了句话就把这本账写脏,它自己真正的
1274
- // 空闲期完成通知随后被补发通道整条丢掉,用户只能等下一次开流才可能看见。
1275
802
  if (peerFrame === null && n.task_type !== 'external')
1276
803
  markEngineWorkflowNotified(taskId);
1277
- // 判定单源 = `classifyPeerNotification`(载体在场判别,绝不锚 summary 文本);认不出 ⇒ `null`
1278
- // ⇒ 这一行与本臂此前**逐字节等价**(三条载体一个都不在场的载荷 = 今天的全部载荷,老宿主零差异)。
1279
- // 🔴 除上面那道完成卡早退之外,其余账**全不动**:去重键 / settle / 面板 / 跨通道 mark 判的是
1280
- // 「哪个 task 的哪个状态」,与这条通知在模型面穿哪件外衣无关,跟着换会把几条独立的账搅成一本。
1281
- // (uplink 帧的 `status` 是 `"event"` —— core 7.2.0 铸点直证 ⇒ 终态那几条腿本来就不会对它开火。)
1282
804
  yield transcript({
1283
805
  type: 'user',
1284
806
  message: {
@@ -1294,34 +816,15 @@ const taskNotificationArm = function* (m, { ctx, idOf, panel, inst }) {
1294
816
  session_id: ctx.sessionId,
1295
817
  }, ctx.now());
1296
818
  };
1297
- /**
1298
- * ⚠️ 本臂声明成 `ProjectionArmFn`(碰不到五个适配器状态模块),但它**有副作用** ——
1299
- * 写 notifications.ts 的 module 台账。两级 deps 证明的是「不碰适配器状态」,不是「纯」。
1300
- */
1301
819
  const workflowCompleteArm = function* (m) {
1302
820
  const runId = typeof m.runId === 'string' ? m.runId : undefined;
1303
821
  if (runId === undefined)
1304
822
  return;
1305
- // 跨通道去重(cli enqueueEngineWorkflowNotification 首行):该 run 已由推送帧记账过
1306
- // (引擎 server-side 注入过)⇒ Path B 绝不再喂模型一遍。
1307
- // 🔴 B5 ①:门与记账都在 module 台账 —— cli 里这一整段就是 enqueueEngineWorkflowNotification
1308
- // 的前三行,probe/idle watcher 合成的那两条链写的也是它。
1309
- // 🔴 B6 契约钉死(见 seam.ts 本臂注释「门归包、投递归宿主」):门 + 下面两条记号
1310
- // (markEngineWorkflowNotified / noteWorkflowCompletionCardEnqueued)**都在库内**,
1311
- // 宿主拿到臂只管裸入队。宿主若借自带同一道门的老函数投递 ⇒ 恒早退、一条都进不了队列。
1312
823
  if (isEngineWorkflowNotified(runId))
1313
824
  return;
1314
825
  markEngineWorkflowNotified(runId);
1315
826
  const wfStatus = m.status === 'failed' ? 'failed' : 'completed';
1316
827
  const wfSummary = typeof m.summary === 'string' ? m.summary : '';
1317
- // 🔴 B6 时序修(先红后绿,pure 门「早退 ⇒ cardEnqueued 仍落」):这一行原本在 `yield`
1318
- // **之后** —— 生成器在 yield 处挂起,后面的语句只有消费者**再要下一个值**时才跑;
1319
- // 消费者早退(turn 中止 / `it.return()`)时它**永不执行**。而同臂的
1320
- // `markEngineWorkflowNotified` 在 yield 之前恒执行 ⇒ 两条记号一条恒落一条条件落,
1321
- // 且方向正好是坏的那个:notified 落了 ⇒ idle watcher 不补发;cardEnqueued 没落 ⇒
1322
- // 推送帧到达时判不出卡的所有权 ⇒ **渲第二张完成卡**(B1c 反钉②)。
1323
- // 现存驱动全是走完整流的 `for await`,所以这条路径此前从未被断言过([1521]F1 同形)。
1324
- // 两条记号必须同在 yield 之前落 —— 「臂已发出」这件事在 yield 那一刻就已成立。
1325
828
  noteWorkflowCompletionCardEnqueued(runId);
1326
829
  yield chrome({
1327
830
  kind: 'workflow_notification_enqueue',
@@ -1329,19 +832,12 @@ const workflowCompleteArm = function* (m) {
1329
832
  runId,
1330
833
  status: wfStatus,
1331
834
  summary: wfSummary,
1332
- // 模型面正文照铸(宿主直接入队即可,不必各自拼 XML;与 core 的 tag/段序同形)
1333
835
  message: renderTaskNotificationXml({ taskId: runId, status: wfStatus, summary: wfSummary }),
1334
836
  });
1335
837
  };
1336
- // ══ M6b —— retry_status ════════════════════════════════════════════════════════════════════════
1337
838
  const retryStatusArm = function* (m, { ctx, flags }) {
1338
- // #47 矩阵 #5:子代 lane 帧绝不骑主 spinner(无判别守卫的无条件 store 写=默认可疑)。
1339
839
  if (typeof m.parentToolCallId === 'string')
1340
840
  return;
1341
- // 🔴 十一键全转(0.55.0;首建 #3004 跟修批 2026-08-08):此前这里只挑 phase/detail/retryInSec 三键,attempt /
1342
- // maxRetries / retryInMs 被剥在本层 —— 而 server 两腿的 brainStatusEventData 是全发的,
1343
- // `RetryStatus` 上也早就留好了 attempt/maxRetries 两位。键集真源 =
1344
- // `BRAIN_STATUS_PAYLOAD_KEYS`(engine-vocab G2-c 对实装 core 对账)。
1345
841
  const status = mapBrainStatusToRetry({
1346
842
  phase: typeof m.phase === 'string' ? m.phase : '',
1347
843
  ...(typeof m.detail === 'string' ? { detail: m.detail } : {}),
@@ -1349,46 +845,20 @@ const retryStatusArm = function* (m, { ctx, flags }) {
1349
845
  ...(typeof m.retryInMs === 'number' ? { retryInMs: m.retryInMs } : {}),
1350
846
  ...(typeof m.attempt === 'number' ? { attempt: m.attempt } : {}),
1351
847
  ...(typeof m.maxRetries === 'number' ? { maxRetries: m.maxRetries } : {}),
1352
- // 七键(#307 S44,2026-08-19):core 5.43.0 起 `BrainStatus` 带 `errClass`(等待原因分桶)。
1353
- // 剥掉它 = 用户看得到「在等」看不到「为什么等」,而供给方是发了的。
1354
848
  ...(typeof m.errClass === 'string' ? { errClass: m.errClass } : {}),
1355
- // ── 九键(0.48.0;core 7.0.x #506 ㋑,server ≥7.53 `brainStatusEventData` 两腿全发)────
1356
- // 🔴 与上面那条 errClass 是**同一个病形的第三、第四例**:上游真发、本层这层白名单闭形剥掉。
1357
- // 族扫真源 = 实装 core 的 `BrainStatus` 整个键集(engine-vocab G2-c 逐键等值门),
1358
- // 不是「派工单点名了哪一个」——本批点名的只有 errorStatus,retryAtMs 是族扫捞出的存量漏键。
1359
- // · `retryAtMs` 产生者铸的墙钟截止点(端渲倒计时的权威锚;剥掉它端只能自己 now+剩余量
1360
- // 现算,而那个算法在每个 30s 重播片上重新起跳 —— core 顶注点名的病)。
1361
- // · `errorStatus` 刚失败那次尝试的 HTTP 状态(CC `system/api_retry.error_status` 同一个数;
1362
- // 剥掉它 = 用户看得到「在重试」却看不到「谁失败了/该不该等」)。
1363
849
  ...(typeof m.retryAtMs === 'number' ? { retryAtMs: m.retryAtMs } : {}),
1364
850
  ...(typeof m.errorStatus === 'number' ? { errorStatus: m.errorStatus } : {}),
1365
- // ── 十一键(0.55.0;core 7.3.0 #530,server ≥7.58 `brainStatusEventData` 放行)──────────
1366
- // 🔴 **同一个病形的第五、第六例**,而且这一次剥掉的是**新相的全部内容**:
1367
- // `waiting_first_token` 帧上除了 phase 就只有这两位携带信息(它不带 retryIn*/attempt/
1368
- // errClass/errorStatus —— core 明说本相不宣告等待)。这层白名单不跟车,`mapBrainStatusToRetry`
1369
- // 的新 `waiting` 臂就只能收到一个空壳:kind 对了、秒数永远缺席,屏上那行渲不出「等了多久」。
1370
- // ⇒ 加相**必同批加键**;键集真源仍是实装 core 的 `BrainStatus` 整个键集(G2-c 逐键等值门)。
1371
851
  ...(typeof m.elapsedMs === 'number' ? { elapsedMs: m.elapsedMs } : {}),
1372
852
  ...(typeof m.timeoutMs === 'number' ? { timeoutMs: m.timeoutMs } : {}),
1373
853
  }, ctx.now());
1374
- // `status === null` = 引擎直报 `recovered`(重试成功)。这一拍是**清除腿之一** —— 但间接腿
1375
- // (TurnFlags 的「主 lane 内容帧到达即清」)可能已经清过了:已安装 core 在内容流跑完的 finally
1376
- // 里发 `recovered`,所以 `retrying → 内容 → recovered` 才是常态顺序。故用 `takeRetryOverlay()`
1377
- // 的**原子读落**决定要不要发:旗还在 ⇒ 发这一发并落旗;旗已落 ⇒ 沉默(没有覆盖层可摘)。
1378
- // 首版写成「无条件发 + clearRetryOverlay」,在常态顺序上双清(对抗复审命中)。
1379
854
  if (status === null) {
1380
855
  if (flags.takeRetryOverlay())
1381
856
  yield chrome({ kind: 'retry_status', laneProof: mainLane(), status: null });
1382
857
  return;
1383
858
  }
1384
859
  yield chrome({ kind: 'retry_status', laneProof: mainLane(), status });
1385
- // ⟨帧序耦合 6/7⟩ 挂旗;下一个主 lane 内容帧到达时由 TurnFlags 的帧前置清掉(恢复即清)。
1386
860
  flags.noteRetryOverlay();
1387
861
  };
1388
- /**
1389
- * 帧类型 → 臂。**Map 而不是对象字面量**:`{type:'__proto__'}` 这种 UNTRUSTED 帧命中对象表
1390
- * 就是原型污染(复审结论 3)。表里查不到 = 拆分前 `switch` 的 `default:` 空臂,行为同。
1391
- */
1392
862
  export const ARMS = new Map([
1393
863
  ['assistant', assistantArm],
1394
864
  ['user', userArm],
@@ -1408,18 +878,13 @@ export const ARMS = new Map([
1408
878
  ['engine_notice', engineNoticeArm],
1409
879
  ['text_end', textSegmentEndArm],
1410
880
  ['reasoning_end', thinkingSegmentEndArm],
1411
- // CC-08(0.71.0):两条新帧的 chrome 臂(additive;不接 = 看不见,不属行为丢失)。
1412
881
  ['tool_disclosure', toolDisclosureArm],
1413
882
  ['tool_progress', toolProgressArm],
1414
883
  ['tool_roster_delta', toolRosterDeltaArm],
1415
884
  ['context_usage', contextUsageArm],
1416
- // CC-89(0.75.0):压缩的非 compacted 结局(additive;不接 = 压缩失败对用户不可见)。
1417
885
  ['compaction_outcome', compactionOutcomeArm],
1418
- // CC-91(0.76.0):这一次 prepare 的提示词装配清单(additive;不接 = 端只能按本地镜像自己估)。
1419
886
  ['prompt_assembled', promptAssembledArm],
1420
887
  ['wiring_manifest', wiringManifestArm],
1421
- // B-078 / L-208(0.65.0):design/172 流内审批两帧 —— 修前它们在投影层就 `dropped`,
1422
- // 壳自己另接一份 ⇒ desktop/web 拿不到卡集与撤卡(归层违例)。
1423
888
  ['approval_request', approvalFrameArm('approval_request')],
1424
889
  ['approval_revoke', approvalFrameArm('approval_revoke')],
1425
890
  ['prompt_suggestions', promptSuggestionsArm],