@sema-agent/client-core 0.77.1 → 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 +13 -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 +0 -69
  223. package/dist/mcpWireCaps.d.ts +0 -55
  224. package/dist/mcpWireCaps.js +1 -13
  225. package/dist/memoryComplianceCapability.d.ts +3 -57
  226. package/dist/memoryComplianceCapability.js +0 -57
  227. package/dist/memoryEntriesWire.d.ts +6 -198
  228. package/dist/memoryEntriesWire.js +0 -144
  229. package/dist/memoryOriginCapability.d.ts +3 -53
  230. package/dist/memoryOriginCapability.js +0 -50
  231. package/dist/memorySpecWire.d.ts +0 -116
  232. package/dist/memorySpecWire.js +0 -150
  233. package/dist/model/catalog.d.ts +0 -111
  234. package/dist/model/catalog.js +0 -87
  235. package/dist/model/catalogLoader.d.ts +0 -114
  236. package/dist/model/catalogLoader.js +2 -139
  237. package/dist/model/modelSupplyRules.d.ts +1 -55
  238. package/dist/model/modelSupplyRules.js +0 -62
  239. package/dist/model/providerAuth.d.ts +0 -103
  240. package/dist/model/providerAuth.js +2 -38
  241. package/dist/model/providerCatalog.d.ts +0 -45
  242. package/dist/model/providerCatalog.js +0 -37
  243. package/dist/model/providerPresets.d.ts +0 -33
  244. package/dist/model/providerPresets.js +2 -91
  245. package/dist/model/tierVocabulary.d.ts +0 -31
  246. package/dist/model/tierVocabulary.js +0 -27
  247. package/dist/modelBudgetRule.d.ts +0 -39
  248. package/dist/modelBudgetRule.js +0 -39
  249. package/dist/modelCapabilityProbe.d.ts +0 -175
  250. package/dist/modelCapabilityProbe.js +0 -147
  251. package/dist/modelWireCaps.d.ts +0 -13
  252. package/dist/modelWireCaps.js +0 -13
  253. package/dist/notifications.d.ts +0 -233
  254. package/dist/notifications.js +14 -492
  255. package/dist/oneShotWireCaps.d.ts +0 -34
  256. package/dist/oneShotWireCaps.js +0 -35
  257. package/dist/ownKey.d.ts +0 -33
  258. package/dist/ownKey.js +0 -33
  259. package/dist/panelRunningHistory.d.ts +0 -28
  260. package/dist/panelRunningHistory.js +0 -43
  261. package/dist/peerFrames.d.ts +0 -71
  262. package/dist/peerFrames.js +0 -168
  263. package/dist/peerLaneCapability.d.ts +0 -44
  264. package/dist/peerLaneCapability.js +0 -52
  265. package/dist/permissionRuleIssue.d.ts +0 -30
  266. package/dist/permissionRuleIssue.js +0 -78
  267. package/dist/permissionRulesWriteCapability.d.ts +0 -46
  268. package/dist/permissionRulesWriteCapability.js +0 -54
  269. package/dist/permissionWireCaps.d.ts +0 -37
  270. package/dist/permissionWireCaps.js +0 -37
  271. package/dist/postureKnob.d.ts +0 -71
  272. package/dist/postureKnob.js +0 -86
  273. package/dist/principalWire.d.ts +0 -17
  274. package/dist/principalWire.js +0 -17
  275. package/dist/printToolResultFrame.d.ts +0 -100
  276. package/dist/printToolResultFrame.js +0 -33
  277. package/dist/promptProfileWireCaps.d.ts +0 -13
  278. package/dist/promptProfileWireCaps.js +0 -13
  279. package/dist/readFacePosture.d.ts +0 -43
  280. package/dist/readFacePosture.js +0 -46
  281. package/dist/request/printNotification.d.ts +0 -20
  282. package/dist/request/printNotification.js +0 -55
  283. package/dist/request/taskRequest.d.ts +0 -249
  284. package/dist/request/taskRequest.js +6 -498
  285. package/dist/resumeRefusalCopy.d.ts +0 -136
  286. package/dist/resumeRefusalCopy.js +1 -117
  287. package/dist/retainBackgroundWireCaps.d.ts +0 -48
  288. package/dist/retainBackgroundWireCaps.js +0 -48
  289. package/dist/retryStatus.d.ts +2 -221
  290. package/dist/retryStatus.js +0 -107
  291. package/dist/rewindWireCaps.d.ts +0 -27
  292. package/dist/rewindWireCaps.js +0 -24
  293. package/dist/runCancelContext.d.ts +1 -20
  294. package/dist/runCancelContext.js +0 -34
  295. package/dist/runTerminal.d.ts +0 -273
  296. package/dist/runTerminal.js +0 -177
  297. package/dist/sandboxWire.d.ts +0 -38
  298. package/dist/sandboxWire.js +0 -82
  299. package/dist/scenarioWire.d.ts +0 -60
  300. package/dist/scenarioWire.js +0 -68
  301. package/dist/scratchpadWireCaps.d.ts +0 -11
  302. package/dist/scratchpadWireCaps.js +0 -32
  303. package/dist/sdkWireTransit.d.ts +0 -38
  304. package/dist/sdkWireTransit.js +0 -30
  305. package/dist/seam.d.ts +18 -853
  306. package/dist/seam.js +0 -52
  307. package/dist/seatContract.d.ts +0 -555
  308. package/dist/seatContract.js +5 -304
  309. package/dist/selfOrchestrationDenial.d.ts +0 -189
  310. package/dist/selfOrchestrationDenial.js +0 -162
  311. package/dist/selfOrchestrationWireCaps.d.ts +0 -38
  312. package/dist/selfOrchestrationWireCaps.js +0 -38
  313. package/dist/sessionMap.d.ts +0 -60
  314. package/dist/sessionMap.js +0 -37
  315. package/dist/sessionMemoryStatus.d.ts +11 -114
  316. package/dist/sessionMemoryStatus.js +0 -112
  317. package/dist/sessionModelLatch.d.ts +0 -29
  318. package/dist/sessionModelLatch.js +0 -40
  319. package/dist/sessionPolicyCapability.d.ts +3 -53
  320. package/dist/sessionPolicyCapability.js +0 -52
  321. package/dist/sessionSlot.d.ts +0 -26
  322. package/dist/sessionSlot.js +0 -17
  323. package/dist/skillsWireCaps.d.ts +0 -87
  324. package/dist/skillsWireCaps.js +0 -38
  325. package/dist/sqlEngineCapability.d.ts +0 -118
  326. package/dist/sqlEngineCapability.js +0 -134
  327. package/dist/sseIdleTriage.d.ts +0 -79
  328. package/dist/sseIdleTriage.js +1 -80
  329. package/dist/steering.d.ts +0 -54
  330. package/dist/steering.js +0 -63
  331. package/dist/subagent/engineCompactWire.d.ts +0 -34
  332. package/dist/subagent/engineCompactWire.js +4 -134
  333. package/dist/subagent/engineDelegatedPrompt.d.ts +0 -31
  334. package/dist/subagent/engineDelegatedPrompt.js +1 -103
  335. package/dist/subagent/engineRowStopGate.d.ts +0 -20
  336. package/dist/subagent/engineRowStopGate.js +0 -49
  337. package/dist/subagent/engineSubagentOutput.d.ts +0 -7
  338. package/dist/subagent/engineSubagentOutput.js +1 -51
  339. package/dist/subagent/engineSubagentResume.d.ts +2 -279
  340. package/dist/subagent/engineSubagentResume.js +2 -129
  341. package/dist/subagent/engineSubagentSteer.d.ts +0 -26
  342. package/dist/subagent/engineSubagentSteer.js +0 -63
  343. package/dist/subagent/engineSubagentTail.d.ts +0 -38
  344. package/dist/subagent/engineSubagentTail.js +2 -110
  345. package/dist/subagent/engineTaskHandleWire.d.ts +6 -67
  346. package/dist/subagent/engineTaskHandleWire.js +0 -126
  347. package/dist/subagent/subagentOwnerAbsence.d.ts +0 -2
  348. package/dist/subagent/subagentOwnerAbsence.js +0 -25
  349. package/dist/subagentContentStore.d.ts +3 -260
  350. package/dist/subagentContentStore.js +7 -540
  351. package/dist/systemReminderTag.d.ts +0 -49
  352. package/dist/systemReminderTag.js +0 -61
  353. package/dist/toolResult.d.ts +1 -211
  354. package/dist/toolResult.js +1 -358
  355. package/dist/toolRoster.d.ts +0 -140
  356. package/dist/toolRoster.js +0 -66
  357. package/dist/typePins.d.ts +0 -46
  358. package/dist/types/engineState.d.ts +0 -71
  359. package/dist/types/engineState.js +0 -16
  360. package/dist/ultracodeWireCaps.d.ts +0 -75
  361. package/dist/ultracodeWireCaps.js +0 -92
  362. package/dist/unrefTimer.d.ts +0 -27
  363. package/dist/webSearchBackendCapability.d.ts +0 -72
  364. package/dist/webSearchBackendCapability.js +0 -79
  365. package/dist/webSearchWireCaps.d.ts +0 -37
  366. package/dist/webSearchWireCaps.js +0 -34
  367. package/dist/websearch/searchProviderPresets.d.ts +0 -81
  368. package/dist/websearch/searchProviderPresets.js +0 -27
  369. package/dist/wireErrorTriage.d.ts +4 -160
  370. package/dist/wireErrorTriage.js +0 -171
  371. package/dist/wireRefusalCopy.d.ts +0 -28
  372. package/dist/wireRefusalCopy.js +0 -28
  373. package/dist/workflow.d.ts +0 -40
  374. package/dist/workflow.js +0 -52
  375. package/dist/workflowClient.d.ts +0 -122
  376. package/dist/workflowClient.js +8 -360
  377. package/dist/workflowMonitor.d.ts +2 -72
  378. package/dist/workflowMonitor.js +0 -26
  379. package/dist/writeProtectionCapability.d.ts +0 -112
  380. package/dist/writeProtectionCapability.js +0 -109
  381. package/docs/INTEGRATION-CLIENTS.md +30 -1
  382. package/package.json +1 -1
@@ -1,45 +1,10 @@
1
- /**
2
- * ⇄ B1 批搬迁(2026-07-27,多端改造设计稿 §2.4.1-B):cli src/sema/engineAgentPanelStore.ts 逐字搬入(零 import;壳侧改 re-export)。
3
- * 🔴 同 subagentContentStore:buffer/listener/residentTaskIds 是单实例语义,发布侧(bridge/fleet
4
- * 投影)与订阅侧(chrome-agentprogress 的面板 hook)必须同实例,否则事件发进一个副本、面板订另一个。
5
- */
6
- /**
7
- * engineAgentPanelStore — dep-free side-channel store bridging ENGINE-side subagent progress
8
- * (`task_progress` ticks, design/99 MF-10) to CC's CoordinatorTaskPanel (the footer live-agent panel).
9
- *
10
- * WHY A STORE (the retryStatusStore / liveQuestionStore pattern): the upstream bridge is a plain
11
- * generator with no React/AppState access, while the panel reads `AppState.tasks` — so the bridge
12
- * PUBLISHES neutral events here and a sema-owned hook inside chrome-agentprogress.tsx (already mounted
13
- * under the footer) SUBSCRIBES and applies them to AppState via registerTask/updateTaskState. CC files
14
- * stay verbatim; the AppState write side lives in the sema override that owns the panel.
15
- *
16
- * WHY THE PANEL (CC-187 parity): a local sync AgentTool renders an inline progress TRAIL from the
17
- * subagent's real messages. An engine tick carries ONLY rollup numbers ({toolUses, totalTokens}) — the
18
- * exact shape of CC's `AgentProgress` — and CC's own precedent for out-of-process agents (remote agent
19
- * tasks) is the TASK PANEL + task-notification, not a synthetic inline trail. Faithful mapping, no
20
- * fabricated subagent messages.
21
- *
22
- * Buffering: ticks published before the panel mounts are buffered (bounded) and replayed to the first
23
- * subscriber, so early frames in a fast turn aren't lost to mount timing.
24
- */
25
1
  import { retirePanelRowRunning, notePanelRowEnded, panelRowEndedIdentity, peekHeldNewbornTerminalRow, takeHeldNewbornTerminalRow, dropHeldNewbornTerminalRow, heldNewbornTerminalRefs } from './panelRunningHistory.js';
26
2
  const _panelToolUsesLanePins = [true, true];
27
3
  void _panelToolUsesLanePins;
28
- /**
29
- * 两条 lane 对 `toolUses` 的口径(运行期读面,给门用;上面的编译钉保证它没在撒谎)。
30
- * `required-engine-always-emits` = 缺席不可达 ⇒ 兜底值合法;
31
- * `optional-tolerate-absent` = 缺席即「不知道」⇒ 必须传播缺席(键不落)。
32
- */
33
4
  export const PANEL_TOOLUSES_LANE_POLICY = {
34
5
  tick: 'required-engine-always-emits',
35
6
  'fleet-row': 'optional-tolerate-absent',
36
7
  };
37
- // ── #6(frame-lane-matrix)session 常驻行台账 ─────────────────────────────────────────────────
38
- // 行寿命 ≠ turn 寿命:跨 turn 存活的 bg 子代行(tick 绑不到本 turn 开着的卡 = inert/unbound)不该被
39
- // turn 末防御 sweep(bridge settlePanelTasks(null) / seamQuery 'sweep' 事件)结成假 completed——一旦
40
- // 假结,消费端 tick 门(status!=='running' ⇒ return prev)会把后续真 tick 全丢,真终态(failed)也
41
- // 改不动(冻死)。台账 module-level(publisher=bridge 按绑定活性写;两个 sweep 消费点按它跳过;
42
- // 真终态通知(task_notification 帧 / enqueueBgChildNotification 合成)settle 时摘除)。
43
8
  const residentTaskIds = new Set();
44
9
  export function markEnginePanelTaskResident(taskId) {
45
10
  residentTaskIds.add(taskId);
@@ -53,19 +18,11 @@ export function isEnginePanelTaskResident(taskId) {
53
18
  const MAX_BUFFER = 200;
54
19
  let listener = null;
55
20
  const buffer = [];
56
- /** 两条 fleet-row 都带同一种周期身份(launch 锚 / 代际号)且不相等 ⇒ 新周期(任一条没带 = 这一帧没说,不算变化)。 */
57
21
  function launchAnchorChanged(prev, next) {
58
22
  if (prev.kind !== 'fleet-row' || next.kind !== 'fleet-row')
59
23
  return false;
60
24
  return isNewEngineAgentPanelCycle(prev, next);
61
25
  }
62
- /**
63
- * 0.74.5 CC-87 —— 「这一帧是不是**新周期**」的单源判据(`isStaleEngineAgentPanelEnd` 的对偶,壳侧此前各拼一份):
64
- * · 两边都带 `cycleSeq` ⇒ **只**比它:不等 ⇒ 新周期;
65
- * · 否则两边都带 `startedAt` ⇒ 不等 ⇒ 新周期;
66
- * · 任一边判不出 ⇒ **不是**新周期(这一帧没说,不算变化 —— 方向与陈旧判据相同:缺席不制造周期)。
67
- * 与缓冲合并屏障(`launchAnchorChanged`)同一条规则;消费端在 `fleet-row` 到时拿行上记的身份与帧上的身份调它。
68
- */
69
26
  export function isNewEngineAgentPanelCycle(prev, next) {
70
27
  const pc = cycleSeqOf(prev);
71
28
  const nc = cycleSeqOf(next);
@@ -83,15 +40,6 @@ function startedAtOf(v) {
83
40
  const n = v?.startedAt;
84
41
  return typeof n === 'number' && Number.isFinite(n) && n > 0 ? n : undefined;
85
42
  }
86
- /**
87
- * 0.74.0 —— 这条 `end` 是不是**上一周期迟到的**(单源判据,三端共用)。`current` = 消费端记在行上的当前周期
88
- * (取自最近一条 `fleet-row` 的 `cycleSeq` / `startedAt`)。
89
- * · 两边都有 `cycleSeq` ⇒ **只**比它:`end` 更小 ⇒ 陈旧;
90
- * · 否则两边都有 `startedAt` ⇒ `end` 更早 ⇒ 陈旧;
91
- * · 任一边判不出(流内 close 臂的 `end` / 没有代际概念的行 / 老引擎 / 坏形)⇒ **不陈旧** —— 按现行为 settle。
92
- * 方向是宁可照旧也不因为缺席丢掉一条真终态。
93
- * 陈旧的 `end`:忽略(不 settle、不写 report、不摘所有权)。
94
- */
95
43
  export function isStaleEngineAgentPanelEnd(end, current) {
96
44
  const ec = cycleSeqOf(end);
97
45
  const cc = cycleSeqOf(current);
@@ -103,51 +51,16 @@ export function isStaleEngineAgentPanelEnd(end, current) {
103
51
  return es < cs;
104
52
  return false;
105
53
  }
106
- // ── 0.74.3 CC-70:子代身份归一(所有 lane 的唯一漏斗)────────────────────────────────────────
107
- //
108
- // 病(cli L-407 / test 定谳,三端同撞):同一个后台子代在 wire 上有**两把身份** —— fleet 行 id 尾段(17 位,= 注册表 task_id
109
- // = 壳本地 agentId,core #948 按构造)与主流 `task_progress.taskId`(引擎 UUIDv7 = transcriptId = childSessionId)。本包的
110
- // `fleet-row` / 通知 `end` 用前者,tick / 流内 close 臂的 `end` 用后者 ⇒ 壳面板按 `ev.taskId` 落 Map 长成两行;包内常驻标也是
111
- // 「UUID 记、17 位清」永不清。修法 = 在这一只漏斗里把 tick / end 的 `taskId` 归一到 fleet 尾段(消费端零改):
112
- // · 钥匙两把,fleet-row 一到就登记:`transcriptId → 尾段`(主)、`parentToolCallId → 尾段`(退路);
113
- // · 查得到 ⇒ 改写 `taskId`,原词进 `wireTaskId`,`taskIdOrigin:'fleet-row'`;常驻标若记在 UUID 下随之迁移;
114
- // · 查不到:绑卡的 tick(同步委派,`cardBound`)直发;未绑的 tick **缓一拍**等 fleet-row(常态 fleet-row 先到几十秒,但非保证)——
115
- // 触发 = 同 UUID 的 fleet-row 到(归一发)/ 同 UUID 第二条 tick(新的原样发,旧的丢)/ 同 UUID `end`(先 tick 后 end 原样发)/
116
- // 缓冲满(最旧的原样发);原样发的带 `taskIdOrigin:'wire'`。🔴 `sweep` **不放**缓着的 tick:那条 tick 还没有行,sweep 没有东西可结;
117
- // 在「tick 先到、父 turn 先结、行帧后到」的窗里放出去反而把 UUID 行造出来、接着行帧再造第二行 —— 正是要修的病。
118
- // · 两张钥匙表与缓一拍都有界(常量),溢出先进先出 —— 上限是内存约束不是语义,超出只退化成「原样发」;
119
- // · 缓一拍还按**节拍**有界(异源复审 R1 [medium]):别的 fleet-row / end / sweep 每过一条算一拍(别的 tick 不算 —— 它说明不了行帧车道
120
- // 活不活,一阵子代同时起步时也不该互相挤),过 MAX_HELD_WIRE_TICK_BEATS 拍还没等到自己的行帧 ⇒ 原样放(store 无计时器,拍是唯一可数的时间);
121
- // · 钥匙带**周期身份**(复审 R1 [high]):行帧登记时连 cycleSeq / startedAt 一起记,归一后的 end 若自己不带周期就借钥匙上的
122
- // (流内 close 臂的 end 恒不带)—— 复活后旧 transcriptId 的钥匙仍在,迟到的 end{旧 UUID} 归一后带着旧代际,
123
- // isStaleEngineAgentPanelEnd 才判得出它是前代;
124
- // · tick 经退路键(parentToolCallId)归一时,顺手把 UUID → 尾段 记进主表(复审 R1 [high]):随后的 end{UUID} 不带 parentToolCallId,
125
- // 没有这一步它永远归不了一。
126
- // · 🔴 **先发布的键为准**(0.74.4 CC-83;test [7702] N-3 证伪「零改」:tick 归不了一而原样发出之后,行帧再按尾段发 = 消费端仍两行):
127
- // 一条 tick 已按 UUID 原样发出(第二拍 / 拍数耗尽 / 绑卡),这个子代在消费端的键就是 UUID —— 之后到的 fleet-row / fleet 侧 end
128
- // 改写成 UUID(原词进 wireTaskId,taskIdOrigin:'wire'),不再把尾段当第二把键发出去。消费端从此在任何到达序上都只见一把键;
129
- // 常态序(行帧先到)仍归一到尾段。
130
- // · 🔴 前代 UUID 的迟到 tick **丢弃**(0.74.4 CC-84;test [7702] W2 tick 腿):钥匙上的周期比最近行帧旧 ⇒ 这条 progress 属已结束的
131
- // 周期,归一发出去会把前代的累计数写进复活行;end 有陈旧判据(消费端按周期挡),tick 没有 ⇒ 在漏斗里挡。
132
- /** 钥匙表上限(LRU:命中即续命,真在飞的子代不会被一阵新登记挤掉;超出只退化成「原样发」)。 */
133
54
  const MAX_IDENTITY_KEYS = 2048;
134
55
  const MAX_HELD_WIRE_TICKS = 64;
135
- /** 缓一拍的节拍上限:别的 fleet-row / end / sweep 每过一条算一拍(别的 tick 不算)。 */
136
56
  export const MAX_HELD_WIRE_TICK_BEATS = 16;
137
57
  const fleetIdByTranscriptId = new Map();
138
58
  const fleetIdByParentToolCallId = new Map();
139
59
  const heldWireTicks = new Map();
140
- /** 每个 fleet 尾段最近一条行帧带的周期身份(在场才记):清常驻标时用它判「这条终态是不是前代的」(异源复审 R3 [high])。 */
141
60
  const latestCycleByFleetId = new Map();
142
- /**
143
- * 已按 UUID 原样发出过 tick 的子代(先发布的键为准,CC-83):transcriptId(= UUID)与 parentToolCallId 两把钥匙都记。
144
- * 🔴 所有权表**按生命周期退役、不按容量改写**(异源复审 0.74.4 R1 [medium]):一把已发布的键在消费端就是一行,容量到了把它忘掉 =
145
- * 同一子代重新长成两行。所以 `retired`(收到 end)的条目先让位;都没退役时才让最旧的(上限 MAX_OWNERSHIP_KEYS,远大于同时在飞的子代数)。
146
- */
147
61
  const MAX_OWNERSHIP_KEYS = 8192;
148
62
  const publishedWireIds = new Map();
149
63
  const publishedWireByParent = new Map();
150
- /** 尾段 → 已发布的 UUID(CC-83):fleet 侧的 fleet-row / end / 缺席 到时改写成 UUID;带登记时行帧的周期,复活的新周期不继承。 */
151
64
  const wireIdByFleetId = new Map();
152
65
  function bounded(map, key, value) {
153
66
  map.delete(key);
@@ -158,7 +71,6 @@ function bounded(map, key, value) {
158
71
  map.delete(oldest);
159
72
  }
160
73
  }
161
- /** 所有权表的有界:退役的先让位,都没退役才让最旧的。 */
162
74
  function boundedOwnership(map, key, value) {
163
75
  map.delete(key);
164
76
  map.set(key, value);
@@ -174,14 +86,11 @@ function boundedOwnership(map, key, value) {
174
86
  if (oldest !== undefined)
175
87
  map.delete(oldest);
176
88
  }
177
- /** 一条 tick 按 UUID 原样发出去了:从此这个子代的键 = UUID。 */
178
89
  function notePublishedWire(ev) {
179
- // 已退役的键不因一条迟到 tick 复活(异源复审 R3 [high]):退役 = 它的行已结,迟到帧落在它自己的行上,所有权不回来
180
90
  boundedOwnership(publishedWireIds, ev.taskId, { retired: publishedWireIds.get(ev.taskId)?.retired ?? false });
181
91
  if (ev.parentToolCallId !== undefined && ev.parentToolCallId.length > 0)
182
92
  bounded(publishedWireByParent, ev.parentToolCallId, ev.taskId);
183
93
  }
184
- /** 这把 UUID 的子代收到了终态:所有权可退役(容量满时先让位;不删 —— 迟到的同周期行帧 / end 仍要改写到它)。 */
185
94
  function retireWire(wireId) {
186
95
  const w = publishedWireIds.get(wireId);
187
96
  if (w !== undefined)
@@ -190,7 +99,6 @@ function retireWire(wireId) {
190
99
  if (o.wireId === wireId)
191
100
  o.retired = true;
192
101
  }
193
- /** fleet-row 对应的子代是否已经按 UUID 发布过;transcriptId 命中不看退役(同一子代的迟到行帧),parentToolCallId 只认未退役(复活会沿用同一张卡)。 */
194
102
  function publishedWireFor(ev) {
195
103
  if (ev.transcriptId !== undefined && publishedWireIds.has(ev.transcriptId))
196
104
  return ev.transcriptId;
@@ -201,16 +109,14 @@ function publishedWireFor(ev) {
201
109
  }
202
110
  return undefined;
203
111
  }
204
- /** 尾段今天归哪把 UUID(在场 = 这个子代已按 UUID 上屏;复活的新周期不算)。 */
205
112
  function wireOwnerOf(fleetId, current, transcriptId) {
206
113
  const o = wireIdByFleetId.get(fleetId);
207
114
  if (o === undefined)
208
115
  return undefined;
209
- // 0.74.5(test [7718] 契约层反例):行帧的 transcriptId 换了 = 新一代子会话,即使两个周期键全缺 —— 所有权同样到此为止
210
- const newTranscript = transcriptId !== undefined && transcriptId !== (o.transcriptId ?? o.wireId); // 父键登记没记 transcriptId 时,wireId 本身就是那一代的转录身份(复审 R3)
116
+ const newTranscript = transcriptId !== undefined && transcriptId !== (o.transcriptId ?? o.wireId);
211
117
  if (newTranscript || (current !== undefined && isStaleEngineAgentPanelEnd(o, current))) {
212
- wireIdByFleetId.delete(fleetId); // 行帧已是更新的周期:旧 UUID 的所有权到此为止,新周期按常态登记
213
- retireWire(o.wireId); // 旧周期已过:那把 UUID 退役,退路键(同一张卡)不再把新周期的行帧拉回它(异源复审 R2 [high])
118
+ wireIdByFleetId.delete(fleetId);
119
+ retireWire(o.wireId);
214
120
  return undefined;
215
121
  }
216
122
  return o.wireId;
@@ -224,14 +130,10 @@ function remember(map, key, alias) {
224
130
  map.delete(oldest);
225
131
  }
226
132
  }
227
- /**
228
- * 0.74.3 CC-70:把 wire 上的子代 id(task_progress 的 UUID)反查成 fleet 行 id 尾段;`transcriptId` 表优先,退 `parentToolCallId` 表;
229
- * 都查不到 ⇒ 原样(`origin:'wire'`)。fleet 尾段本身传进来也原样(它就是目标键)。
230
- */
231
133
  export function resolveEnginePanelTaskId(wireTaskId, parentToolCallId) {
232
134
  const byTranscript = fleetIdByTranscriptId.get(wireTaskId);
233
135
  if (byTranscript !== undefined) {
234
- remember(fleetIdByTranscriptId, wireTaskId, byTranscript); // LRU 续命(异源复审 R2 [high]:FIFO 会把仍在飞的子代挤掉)
136
+ remember(fleetIdByTranscriptId, wireTaskId, byTranscript);
235
137
  return identityOf(byTranscript);
236
138
  }
237
139
  const byParent = parentToolCallId !== undefined ? fleetIdByParentToolCallId.get(parentToolCallId) : undefined;
@@ -249,7 +151,6 @@ function identityOf(alias) {
249
151
  ...(alias.startedAt !== undefined ? { startedAt: alias.startedAt } : {}),
250
152
  };
251
153
  }
252
- /** 经退路键归一的 tick:把 UUID → 尾段 记进主表,随后不带 parentToolCallId 的 end{UUID} 才归得了一。 */
253
154
  function rememberResolvedWire(wireTaskId, id) {
254
155
  if (fleetIdByTranscriptId.has(wireTaskId))
255
156
  return;
@@ -259,7 +160,6 @@ function rememberResolvedWire(wireTaskId, id) {
259
160
  ...(id.startedAt !== undefined ? { startedAt: id.startedAt } : {}),
260
161
  });
261
162
  }
262
- /** 别的事件过了一拍:缓着的 tick 各记一拍,过上限的原样放出(先进先出)。 */
263
163
  function ageHeld(except) {
264
164
  for (const [wireId, held] of [...heldWireTicks]) {
265
165
  if (wireId === except)
@@ -289,7 +189,6 @@ function asIsTick(ev) {
289
189
  notePublishedWire(ev);
290
190
  return { ...rest, taskIdOrigin: 'wire' };
291
191
  }
292
- /** 钥匙上的周期比最近行帧旧 = 这条 tick 属已结束的周期(CC-84);判不出 ⇒ 不算陈旧。 */
293
192
  function staleWireTick(id) {
294
193
  return isStaleEngineAgentPanelEnd(id, latestCycleByFleetId.get(id.taskId));
295
194
  }
@@ -305,23 +204,16 @@ function flushHeld(wireTaskId) {
305
204
  const id = resolveEnginePanelTaskId(held.ev.taskId, held.ev.parentToolCallId);
306
205
  if (id.origin === 'fleet-row') {
307
206
  if (staleWireTick(id))
308
- return; // 前代周期的 progress:丢
207
+ return;
309
208
  rememberResolvedWire(held.ev.taskId, id);
310
209
  deliverEngineAgentPanelEvent(normalizedTick(held.ev, id.taskId));
311
210
  return;
312
211
  }
313
212
  deliverEngineAgentPanelEvent(asIsTick(held.ev));
314
213
  }
315
- /**
316
- * 0.74.3 CC-70(异源复审 R3 [high]):按 wire 上的子代 id 清常驻标 —— 原键照清;归一到 fleet 尾段的那把键**只在这条终态属当代时**才清:
317
- * 复活后迟到的前代终态(旧 UUID 的流内 close / 终态 tick)归一后带旧代际,清了尾段键 = 撤掉复活行的 sweep 保护 ⇒ 下一次 sweep 假结它。
318
- * 判据与 `end` 的陈旧判据同一只(`isStaleEngineAgentPanelEnd`,拿钥匙上的周期对最近一条行帧的周期);判不出 ⇒ 照清(不因缺席留下永久常驻)。
319
- * `cycle`(0.74.4):调用方(通知车道)带得出这条终态的周期时传进来 —— 尾段归某把 UUID 所有而通知比所有权旧 ⇒ 不清那把 UUID 的常驻标。
320
- */
321
214
  export function clearEnginePanelTaskResidentByWire(wireTaskId, cycle) {
322
215
  residentTaskIds.delete(wireTaskId);
323
- const owner = wireIdByFleetId.get(wireTaskId); // 传进来的是尾段而尾段归某把 UUID 所有(通知车道)⇒ 那把键下的常驻标一起清
324
- // 通知带周期(seq)且比所有权登记时的周期旧 = 前代的终态 ⇒ 不清当代 UUID 的常驻标(异源复审 R3 [high])
216
+ const owner = wireIdByFleetId.get(wireTaskId);
325
217
  if (owner !== undefined && !(cycle !== undefined && isStaleEngineAgentPanelEnd(cycle, owner)))
326
218
  residentTaskIds.delete(owner.wireId);
327
219
  const id = resolveEnginePanelTaskId(wireTaskId);
@@ -331,12 +223,6 @@ export function clearEnginePanelTaskResidentByWire(wireTaskId, cycle) {
331
223
  return;
332
224
  residentTaskIds.delete(id.taskId);
333
225
  }
334
- /**
335
- * 测试钩。逐表清单(每加一张表同批更新这里):① 转录钥匙表(UUID → 行尾段)② 父调用钥匙表(parentToolCallId → 行尾段)
336
- * ③ 缓着的未绑卡 tick ④ 每行最近一次行帧的周期水位 ⑤ 「先发布的键」集合(UUID)⑥ 同上按父调用记的那一份 ⑦ 反向所有权表(行尾段 → UUID)。
337
- * 🔴 不清:订阅者 / 回放缓冲、常驻标、running 历史(那一本由 `__resetPanelRunningHistoryForTests` 清)。
338
- * 🔴 连续场景(同一只子代跨多步的用例)**中途别调**:清掉 ④–⑦ 之后,陈旧判据与「先发布的键为准」都会从零开始,前后两半不再是同一段历史。
339
- */
340
226
  export function __resetEngineAgentPanelIdentityForTests() {
341
227
  fleetIdByTranscriptId.clear();
342
228
  fleetIdByParentToolCallId.clear();
@@ -346,30 +232,12 @@ export function __resetEngineAgentPanelIdentityForTests() {
346
232
  publishedWireByParent.clear();
347
233
  wireIdByFleetId.clear();
348
234
  }
349
- /**
350
- * 0.75.0 W2:一条 end 递达之前,先把投影缓着的「首见即终态」行放出来(它带这一行唯一的一份最终用量),经漏斗原路发布(身份归一照走);
351
- * 递达之后记「本进程结过这一行」—— 投影据此把之后到的终态行当成短命子代的收尾(照发用量)而不是历史行。两步都只对当代 end(陈旧判据与销 running 历史同一只)。
352
- */
353
- /**
354
- * 这一条缓行**放出去会落在哪把消费端键上**(纯读口,不动表;与 fleet-row 臂的改写次序同序:
355
- * 反向所有权 → 已发布的 wire 键 → 自己的行尾段)。第四条腿据它核「行与 end 是否同键」。
356
- * 🔴 保守方向:所有权在表里就按它答(`wireOwnerOf` 遇到新一代会把它删掉再落到下一档 —— 那一档答错只会让本腿
357
- * **少放**一次,不会让一只子代分成两行)。
358
- */
359
235
  function heldRowConsumerKey(tail, held) {
360
236
  const owner = wireIdByFleetId.get(tail);
361
237
  if (owner !== undefined)
362
238
  return owner.wireId;
363
239
  return publishedWireFor(held) ?? tail;
364
240
  }
365
- /**
366
- * 这一条 end 对那一行缓着的终态行的**代际三向**(纯读口,不动表)。先 peek 再决定:先 take 会让前代
367
- * end 把当代唯一的缓行取走丢掉;
368
- * · `'keep'` = end 可证比缓行**旧** ⇒ 缓行留着(等当代的那条 end);
369
- * · `'drop'` = end 可证比缓行**新** ⇒ 缓行作废(前代的用量不许盖当代行);
370
- * · `'release'` = 同代 / 判不出 ⇒ 可以放出;
371
- * · `'none'` = 这一行没有缓着的终态行。
372
- */
373
241
  function heldNewbornTerminalVerdict(taskId, endCycle) {
374
242
  const held = peekHeldNewbornTerminalRow(taskId);
375
243
  if (held === undefined || held.kind !== 'fleet-row')
@@ -381,19 +249,6 @@ function heldNewbornTerminalVerdict(taskId, endCycle) {
381
249
  return 'drop';
382
250
  return 'release';
383
251
  }
384
- /**
385
- * 🔴 放行前的**同键核**:这一条缓行放出去,行帧与随后那条 end 会不会落在**同一把消费端键**上?
386
- *
387
- * 反例(实测):一只子代的委派卡被复用 —— `tick{V, 卡 P}` 已按 V 上过屏;缓着的终态行是
388
- * `{尾段 X, 转录锚 U, 卡 P}`;来的是 `end{U}`。第四条腿按转录锚找到唯一候选 X,可它一放行就被
389
- * 「先发布的键为准」经卡 P 改写到 **V** 上,而这条 `end{U}` 在主路上谁也不命中、原样落在 **U**:
390
- * 用量记到了 V,X 的终态却发给了 U,消费端还可能凭 `end{U}` 另开一行。
391
- *
392
- * 所以只在两种形下放行(其余留着缓行,等真正属于它的那条 end —— 宁可晚交一份用量,不让一只子代分成两行):
393
- * ① 行会被改写到**这条 end 自己那把键**上(随后主路也落在同一把键,`ownedFleetIds` 连带结账);
394
- * ② 行按**自己的尾段**上屏,而这条 end 的键正是它的转录锚且那把键没被发布过 —— 放行登记了转录钥匙,
395
- * 主路随即经钥匙表归一到同一条尾段。
396
- */
397
252
  function heldRowReleasesOntoSameKey(tail, end) {
398
253
  const held = peekHeldNewbornTerminalRow(tail);
399
254
  if (held === undefined || held.kind !== 'fleet-row')
@@ -401,9 +256,6 @@ function heldRowReleasesOntoSameKey(tail, end) {
401
256
  const rowKey = heldRowConsumerKey(tail, held);
402
257
  if (rowKey === end.taskId)
403
258
  return true;
404
- // 末一项 `!publishedWireIds.has(...)` 是**可证冗余**的写方守卫(删掉行为不变:若这把键发布过,
405
- // 上面的 `heldRowConsumerKey` 就会把 rowKey 算成它、第一档已经返回 true,走不到这里)。留着是把
406
- // 「② 只成立于那把键没上过屏」这句话钉在代码里,别当成活判据去删第一档。
407
259
  return rowKey === tail && held.transcriptId === end.taskId && !publishedWireIds.has(end.taskId);
408
260
  }
409
261
  function releaseHeldNewbornTerminal(taskId, endCycle) {
@@ -418,24 +270,18 @@ function releaseHeldNewbornTerminal(taskId, endCycle) {
418
270
  if (held !== undefined)
419
271
  publishEngineAgentPanelEvent(held);
420
272
  }
421
- /** 结算(end 已递达):running 历史销、ended 账单调写(可证比已结周期旧的 end 不倒写)。 */
422
273
  function settleRowHistory(taskId, cycle) {
423
274
  retirePanelRowRunning(taskId);
424
275
  notePanelRowEnded(taskId, cycle, isStaleEngineAgentPanelEnd(cycle, panelRowEndedIdentity(taskId)));
425
276
  }
426
- /** end 的陈旧判据同时参考行帧水位与 ended 账(短命子代常常没有行帧水位 —— 异源复审 R3 [high])。 */
427
277
  function endOlderThanKnown(end, taskId, rowCycle) {
428
278
  return isStaleEngineAgentPanelEnd(end, rowCycle) || isStaleEngineAgentPanelEnd(end, panelRowEndedIdentity(taskId));
429
279
  }
430
- /** 宿主的唯一发布口(三条 lane 都经这里):先归一身份,再进主通道(订阅者 / 缓冲)。 */
431
280
  export function publishEngineAgentPanelEvent(ev) {
432
- // 行帧先登记、先放自己匹配的 tick,再给别的缓着的 tick 记拍(异源复审 R2 [high]:先记拍会让第 16 拍那条正好是自己行帧的 tick 过期);
433
- // end / sweep 没有「自己的」匹配 ⇒ 先记拍。
434
281
  if (ev.kind === 'end' || ev.kind === 'sweep')
435
282
  ageHeld(ev.kind === 'sweep' ? undefined : ev.taskId);
436
283
  switch (ev.kind) {
437
284
  case 'fleet-row': {
438
- // 先发布的键为准(CC-83):这个子代已经按 UUID 上过屏 ⇒ 行帧改写成 UUID 发,并记 尾段 → UUID 供 fleet 侧 end 改写;不登记尾段钥匙。
439
285
  const rowCycle = { ...(ev.cycleSeq !== undefined ? { cycleSeq: ev.cycleSeq } : {}), ...(ev.startedAt !== undefined ? { startedAt: ev.startedAt } : {}) };
440
286
  const published = wireOwnerOf(ev.taskId, rowCycle, ev.transcriptId) ?? publishedWireFor(ev);
441
287
  if (published !== undefined && published !== ev.taskId) {
@@ -463,7 +309,6 @@ export function publishEngineAgentPanelEvent(ev) {
463
309
  remember(fleetIdByTranscriptId, ev.transcriptId, alias);
464
310
  if (ev.parentToolCallId !== undefined && ev.parentToolCallId.length > 0)
465
311
  remember(fleetIdByParentToolCallId, ev.parentToolCallId, alias);
466
- // 先发行帧再放缓着的 tick:tick 一到 fleet lane 就永久让位,行帧说的话(transcriptId / startedAt)得先落在行上。
467
312
  deliverEngineAgentPanelEvent(ev);
468
313
  for (const [wireId, held] of [...heldWireTicks]) {
469
314
  if (wireId === ev.transcriptId || (held.ev.parentToolCallId !== undefined && held.ev.parentToolCallId === ev.parentToolCallId))
@@ -473,8 +318,6 @@ export function publishEngineAgentPanelEvent(ev) {
473
318
  return;
474
319
  }
475
320
  case 'tick': {
476
- // 先发布的键为准也管 tick 自己(异源复审 R2 [high]):这把 UUID 已经是消费端的键,之后的 tick 不再经退路键归一到尾段
477
- //(否则复活后同一张卡的退路键把前代 UUID 的迟到 tick 拉进新周期的行)。
478
321
  if (publishedWireIds.has(ev.taskId)) {
479
322
  heldWireTicks.delete(ev.taskId);
480
323
  deliverEngineAgentPanelEvent(asIsTick(ev));
@@ -484,7 +327,7 @@ export function publishEngineAgentPanelEvent(ev) {
484
327
  if (id.origin === 'fleet-row') {
485
328
  heldWireTicks.delete(ev.taskId);
486
329
  if (staleWireTick(id))
487
- return; // CC-84:前代 UUID 的迟到 tick 不进复活行
330
+ return;
488
331
  rememberResolvedWire(ev.taskId, id);
489
332
  deliverEngineAgentPanelEvent(normalizedTick(ev, id.taskId));
490
333
  return;
@@ -494,7 +337,6 @@ export function publishEngineAgentPanelEvent(ev) {
494
337
  return;
495
338
  }
496
339
  if (heldWireTicks.has(ev.taskId)) {
497
- // 第二拍还没等到 fleet-row ⇒ 不再等:新的原样发,旧的丢(progress 合并语义,旧拍没有信息)。
498
340
  heldWireTicks.delete(ev.taskId);
499
341
  deliverEngineAgentPanelEvent(asIsTick(ev));
500
342
  return;
@@ -508,11 +350,6 @@ export function publishEngineAgentPanelEvent(ev) {
508
350
  return;
509
351
  }
510
352
  case 'end': {
511
- // 0.75.0 W2:缓着的「首见即终态」行**先于一切归一**放出(异源复审 R3 [high]:放出的行帧会登记钥匙 / 所有权,之后的 end 归一必须看到它,
512
- // 否则同一子代长成 UUID 行 + 尾段行两行)。
513
- // 🔴 0.75.1 —— 前三把键的**真实射程**(实测反例):尾段本身 / UUID 经钥匙解析到的尾段 / 这把 UUID 名下的反向所有权尾段,
514
- // 后两把**只在行帧真发布过时才登记**,而缓行的定义就是「还没发布」⇒ `tick{UUID}` 先上屏、终态行 `{尾段, transcriptId: UUID}`
515
- // 被缓住时,这三把全查不到,缓行永远放不出。所以有第四条腿:按缓行**自带的** wire 侧键(transcriptId / 退路键)反查。
516
353
  releaseHeldNewbornTerminal(ev.taskId, ev);
517
354
  {
518
355
  const pre = resolveEnginePanelTaskId(ev.taskId);
@@ -525,13 +362,10 @@ export function publishEngineAgentPanelEvent(ev) {
525
362
  for (const [fleetId, o] of wireIdByFleetId)
526
363
  if (o.wireId === ev.taskId && !o.retired)
527
364
  releaseHeldNewbornTerminal(fleetId, ev);
528
- // 第四条腿:缓着的行自带 `transcriptId`(主键)与委派卡 id(退路键)—— 两把与 `publishedWireFor` 的**同一条律**:
529
- // 转录键命中不看退役(同一子代的迟到终态);退路键只认**未退役**的那把 wire 键(复活沿用同一张卡)。
530
- // 放行仍走 `releaseHeldNewbornTerminal`(代际三向 peek + 经原漏斗归一),所以前代的缓行会被当代终态作废而不是盖上去。
531
365
  const heldMatches = [];
532
366
  for (const ref of heldNewbornTerminalRefs()) {
533
367
  if (ref.taskId === ev.taskId)
534
- continue; // 第一条腿已经试过这把键
368
+ continue;
535
369
  const byTranscript = ref.transcriptId !== undefined && ref.transcriptId === ev.taskId;
536
370
  const byParent = ref.parentToolCallId !== undefined &&
537
371
  publishedWireByParent.get(ref.parentToolCallId) === ev.taskId &&
@@ -539,30 +373,6 @@ export function publishEngineAgentPanelEvent(ev) {
539
373
  if (byTranscript || byParent)
540
374
  heldMatches.push(ref.taskId);
541
375
  }
542
- // 命中之后**先逐条按代际三向判、不动表**,再决定放谁:
543
- // · 可证更旧的缓行(前代那一行)⇒ 当场作废(它的用量不许盖当代);
544
- // · end 可证更旧 ⇒ 那条缓行留着,等属于它自己的那条 end;
545
- // · 剩下的「可放行」候选**恰一条** ⇒ 放行 —— 它一上屏就登记了自己的钥匙 / 所有权,本条 end 随后在主路上
546
- // 归一到同一把键,所以仍是「一行一条 end」。
547
- // · 🔴 候选 **≥2**(两个行尾段自称同一个转录锚 / 同一张委派卡)= **身份两义 ⇒ 一条都不放**:转录钥匙表是
548
- // **单值**的(后写覆盖先写),放两条就会各自上屏成两行而主路只归一到最后那一条 ⇒ 前面那些行永远收不到结算;
549
- // 换成「每行各自补一条 end」则要在这里替主路决定消费端键,会吞掉原 wire 键那一行自己的终态。身份对不上就
550
- // **一帧都不摘**是本包扣留表的既有律(见 `hitl/gateLedger.ts` 的「判不出 ⇒ 一条都不摘」)。代价 = 这种形下
551
- // 那几条行的用量仍缺席(与本件修之前同),换来的是绝不凭空多长一行、绝不替谁编一条终态。
552
- // 🔴 **理由更正(0.76.0;外部验收方 [7764] 逐条回一级源码亲核后证伪了本句旧版)**:旧注释写的是「两个行尾段共享一个
553
- // **转录锚**不成立(转录锚按代际唯一),所以这一支是防御面」—— 那句话**只论证了转录钥匙那条腿**,而 `heldMatches`
554
- // 是**转录锚腿 ∪ 委派卡腿**的并集,两条腿异质:
555
- // · 委派卡那条腿的前提**为假** —— workflow 扇出下一张卡整跑冻结成常量、尾段逐 callKey 不同,**最多 50 个**
556
- // 尾段共享同一张卡且可同时在飞(上游坐标:core `workflow.ts` 的 `bceParentToolCall` 与 `waIdOf(callKey)`;
557
- // 默认 `maxAgents` 50)。这正是本包 lane 判据里 `workflow-card-parent` 那一支的上游本体。
558
- // 🔴 上游当事方 09-20 直讯确认了这一形:**一次扇出的全部子代共享同一张 `parentToolCallId`**(那次 workflow
559
- // 工具调用自己的卡),逐子代能区分的键**只有 `taskId`**(`wa*` 形)—— 也就是说委派卡这条腿在扇出下**天然多对一**。
560
- // · 更要紧:**凑够 ≥2 根本不需要两行共享任何一个键** —— 一行只经转录锚命中、另一行只经委派卡命中即可。
561
- // ⇒ 今天这一支之所以罕见,**靠的不是上面那条律**,而是「workflow 子代的 tick 被 lane 门拦掉 ⇒ 委派卡那条腿的
562
- // `publishedWireByParent` 没有写入」这条**更脆**的保证 —— 它历史上塌过(上游投影白名单曾把 `workflowRunId` 整个剥掉)。
563
- // **失效条件**:workflow 子代的 tick 一旦上屏(白名单变宽 / lane 门放行),这一支立刻变成常态路径。
564
- // 所以本分支**不是**「理论上才会走到的防御面」,它是**有真上游机制**的一支,只是今天被一条脆保证挡着。
565
- // 🔴 无论放没放,**主路照走**:原 wire 键那一行(它可能早就上过屏)自己的 end 绝不能被这条腿吞掉。
566
376
  const releasable = [];
567
377
  for (const tail of heldMatches) {
568
378
  const verdict = heldNewbornTerminalVerdict(tail, ev);
@@ -574,13 +384,11 @@ export function publishEngineAgentPanelEvent(ev) {
574
384
  if (releasable.length === 1)
575
385
  releaseHeldNewbornTerminal(releasable[0], ev);
576
386
  }
577
- // 先发布的键为准(CC-83):尾段已被改写成 UUID 的子代,fleet 侧(投影 / 通知)的 end{尾段} 也改写成 UUID
578
387
  const wireOwner = wireOwnerOf(ev.taskId, ev);
579
388
  if (wireOwner !== undefined) {
580
389
  const ownerRec = wireIdByFleetId.get(ev.taskId);
581
390
  const staleA = endOlderThanKnown(ev, ev.taskId, ownerRec) || isStaleEngineAgentPanelEnd(ev, panelRowEndedIdentity(wireOwner));
582
391
  deliverEngineAgentPanelEvent({ ...ev, taskId: wireOwner, wireTaskId: ev.taskId, taskIdOrigin: 'wire' });
583
- // 前代迟到的 end(比所有权登记时的周期旧)只过境让消费端判陈旧,不退役当代所有权(异源复审 R3)、不销当代 running 历史
584
392
  if (!staleA) {
585
393
  retireWire(wireOwner);
586
394
  settleRowHistory(ev.taskId, ev);
@@ -589,9 +397,7 @@ export function publishEngineAgentPanelEvent(ev) {
589
397
  return;
590
398
  }
591
399
  if (publishedWireIds.has(ev.taskId)) {
592
- // 这把 UUID 是先发布的键 ⇒ 它的 end 落在它自己的行上(不归一;一把已发布的键永远不改写)
593
400
  flushHeld(ev.taskId);
594
- // 这把 UUID 名下的尾段(反向所有权 / 转录别名)在投影里以尾段记账(复审 R3 [medium]),各自带周期守卫:先放缓着的终态行,end 之后再销
595
401
  const ownedFleetIds = [];
596
402
  for (const [fleetId, o] of wireIdByFleetId) {
597
403
  if (o.wireId === ev.taskId && !endOlderThanKnown(ev, fleetId, o))
@@ -602,7 +408,6 @@ export function publishEngineAgentPanelEvent(ev) {
602
408
  ownedFleetIds.push(alias.fleetId);
603
409
  deliverEngineAgentPanelEvent(ev);
604
410
  retireWire(ev.taskId);
605
- // UUID 自己的 ended 账也带守卫(end(U,2) 之后的 end(U,1) 不倒写)
606
411
  if (!isStaleEngineAgentPanelEnd(ev, panelRowEndedIdentity(ev.taskId)))
607
412
  settleRowHistory(ev.taskId, ev);
608
413
  for (const fleetId of ownedFleetIds)
@@ -611,10 +416,8 @@ export function publishEngineAgentPanelEvent(ev) {
611
416
  }
612
417
  const id = resolveEnginePanelTaskId(ev.taskId);
613
418
  if (id.origin === 'fleet-row' && id.taskId !== ev.taskId) {
614
- flushHeld(ev.taskId); // 同 UUID 还缓着一条 tick ⇒ 先归一放出(行先有最后一拍的数字与名字),再结
419
+ flushHeld(ev.taskId);
615
420
  migrateResidency(ev.taskId, id.taskId);
616
- // 任何 lane 的 end 到漏斗 = 这一行结了 ⇒ running 历史销账(只销当代:前代迟到的 end 不擦新一代;异源复审 0.74.5 R2)
617
- // 🔴 用与递达**同一个**有效身份判(事件自带的周期优先、缺席才借钥匙;复审 R3 [high]:按钥匙判会把 end{cycle 1} 当成当代)
618
421
  const effectiveEnd = {
619
422
  ...(ev.cycleSeq !== undefined ? { cycleSeq: ev.cycleSeq } : id.cycleSeq !== undefined ? { cycleSeq: id.cycleSeq } : {}),
620
423
  ...(ev.startedAt !== undefined ? { startedAt: ev.startedAt } : id.startedAt !== undefined ? { startedAt: id.startedAt } : {}),
@@ -625,7 +428,6 @@ export function publishEngineAgentPanelEvent(ev) {
625
428
  taskId: id.taskId,
626
429
  wireTaskId: ev.taskId,
627
430
  taskIdOrigin: 'fleet-row',
628
- // end 自己不带周期身份(流内 close 臂恒不带)⇒ 借钥匙上的:复活后迟到的 end{旧 UUID} 由此带着旧代际,消费端按 isStaleEngineAgentPanelEnd 挡掉
629
431
  ...(ev.cycleSeq === undefined && id.cycleSeq !== undefined ? { cycleSeq: id.cycleSeq } : {}),
630
432
  ...(ev.startedAt === undefined && id.startedAt !== undefined ? { startedAt: id.startedAt } : {}),
631
433
  });
@@ -633,23 +435,19 @@ export function publishEngineAgentPanelEvent(ev) {
633
435
  settleRowHistory(id.taskId, effectiveEnd);
634
436
  return;
635
437
  }
636
- flushHeld(ev.taskId); // 缓着的 tick 先落行,终态才有行可结
637
- // 通知车道 / fleet 投影铸的 end(尾段键):只对当代(前代迟到的 end 带旧 seq / cycleSeq ⇒ 不擦历史、不放缓着的行)
438
+ flushHeld(ev.taskId);
638
439
  const staleD = endOlderThanKnown(ev, ev.taskId, latestCycleByFleetId.get(ev.taskId));
639
440
  deliverEngineAgentPanelEvent(ev);
640
- retireWire(ev.taskId); // 这把 UUID 若是先发布的键,所有权退役(容量满时先让位)
441
+ retireWire(ev.taskId);
641
442
  if (!staleD)
642
443
  settleRowHistory(ev.taskId, ev);
643
444
  return;
644
445
  }
645
446
  default:
646
- // sweep:缓着的 tick 不放(见头注)—— 原样进主通道。
647
447
  deliverEngineAgentPanelEvent(ev);
648
448
  }
649
449
  }
650
450
  function deliverEngineAgentPanelEvent(ev) {
651
- // 同 taskId 的任何主通道事实(tick / fleet-row = 行在场;end = 真终态)都让**缓冲里**那条缺席过期 —— 迟订阅的消费者
652
- // 不许先恢复了行、再被一条旧缺席标成 unknown(已递达订阅者的缺席由订阅者按契约在这些事件上撤标)。
653
451
  if (ev.kind !== 'sweep')
654
452
  absenceBuffer.delete(ev.taskId);
655
453
  if (listener) {
@@ -657,53 +455,15 @@ function deliverEngineAgentPanelEvent(ev) {
657
455
  listener(ev);
658
456
  }
659
457
  catch {
660
- // A consumer render error must never tear down the bridge's stream drain.
661
458
  }
662
459
  return;
663
460
  }
664
- // Buffered (panel not mounted yet / temporarily unmounted).
665
- //
666
- // Two event CLASSES, and every buffering rule below keys off the class (not off one kind):
667
- // · PROGRESS ('tick' / 'fleet-row') — cumulative rollups off the two live lanes. Only the latest
668
- // one per taskId carries information, so they COALESCE per (kind, taskId) and they are the ones
669
- // that yield when the buffer is full.
670
- // · LIFECYCLE ('end' / 'sweep') — each one is a distinct fact that settles a row. They are never
671
- // the ones squeezed out by a long unmount (adversarial review w0zwpa251 #4: dropping a buffered
672
- // 'end' left a row running forever).
673
- //
674
- // 🔴 [F4957-2] (0.41.0) the full-buffer arm used to look ONLY for a 'tick' to evict and then pushed
675
- // UNCONDITIONALLY even when it found none — so a stream carrying no ticks at all (the 'fleet-row'
676
- // lane is exactly that: background subagents never reach the panel through 'task_progress', see the
677
- // fleet-row arm's own header) grew the buffer without bound. MAX_BUFFER is a memory bound on
678
- // UNTRUSTED wire volume; it has to hold for every kind mix, not just the one the first version had
679
- // in mind.
680
- // Adjacent 'sweep' dedup. A sweep settles every row the consumer still has running and its own doc
681
- // calls it idempotent, so a sweep whose immediate predecessor is a sweep is a strict no-op — nothing
682
- // happened in between that it could settle differently. Dropping it is not a micro-optimisation: a
683
- // long unmount publishes one sweep per turn, and 200 of them fill the buffer with pure lifecycle
684
- // events, after which every post-barrier progress event has to yield (that is the other half of the
685
- // bounded policy) — a subagent that starts at that point would replay as "still settled", forever.
686
461
  if (ev.kind === 'sweep' && buffer.length > 0 && buffer[buffer.length - 1]?.kind === 'sweep')
687
462
  return;
688
463
  const progress = ev.kind === 'tick' || ev.kind === 'fleet-row';
689
464
  if (progress) {
690
465
  const kind = ev.kind;
691
466
  const taskId = ev.taskId;
692
- // Search BACKWARDS for a merge target and STOP at the first barrier. The bar for the whole buffer
693
- // is "replaying it leaves the consumer in the same state as if the panel had been mounted all
694
- // along", and each barrier below is a place where the consumer's state machine changes what a
695
- // later event means — so an event on the far side of one cannot be folded into an event on this
696
- // side (0.41.0 adversarial review #5 and #6):
697
- // · 'sweep' (global) and 'end' for this taskId — they SETTLE the row, and the consumer ignores
698
- // progress for a settled row. Live: `tick(v1) → sweep → tick(v2)` builds the row, settles it,
699
- // then drops v2 → the row ends settled. Folded into `sweep → tick(v2)` the replay sweeps an
700
- // empty table and then v2 BUILDS a running row — a ghost that live delivery never produces.
701
- // · the OTHER progress lane for this taskId — the fleet-row arm's own header states that a
702
- // 'tick' makes the fleet lane hand the row over PERMANENTLY, so what the fleet lane said
703
- // BEFORE the handover (transcriptId / startedAt, often carried by exactly one frame) would
704
- // land after it and never be applied.
705
- // Barriers cost buffer slots, and that is the right trade: the bound is still enforced by the
706
- // eviction policy below, while a wrong fold is silently wrong forever.
707
467
  let i = -1;
708
468
  for (let at = buffer.length - 1; at >= 0; at--) {
709
469
  const b = buffer[at];
@@ -717,29 +477,13 @@ function deliverEngineAgentPanelEvent(ev) {
717
477
  continue;
718
478
  }
719
479
  if (b.taskId !== taskId)
720
- continue; // another task's progress — irrelevant to this row
721
- // 0.73.0:launch 锚变了 = 同一个 taskId 的**新周期**,也是一道屏障。逐键合并的「省略的键沿用旧值」只在同一周期内
722
- // 成立;跨周期合并会把上一周期的 totalTokens / toolUses 继承进新周期(投影层刻意省略的键被缓冲层补了回来)。
480
+ continue;
723
481
  if (b.kind === kind && !launchAnchorChanged(b, ev))
724
482
  i = at;
725
- break; // same lane & same cycle ⇒ merge target; other lane / new cycle ⇒ barrier
483
+ break;
726
484
  }
727
485
  if (i >= 0) {
728
- // 🔴 Coalescing must not DROP sticky keys either (0.41.0 adversarial review #4). Both progress
729
- // lanes are published as PARTIAL frames — the projection emits `...(toolUses !== undefined ?
730
- // { toolUses } : {})` and friends, and an absent key means "this frame didn't say, keep what the
731
- // row already has" (the three-state rule on the fleet-row arm above). A mounted panel therefore
732
- // still shows the `toolUses` that arrived on v1 after a v2 that only moved `totalTokens`; a
733
- // replay that kept only v2 would lose it for good. So the surviving copy is a per-key merge
734
- // (later frame wins per key, keys it omits are inherited), not a wholesale replacement.
735
486
  const merged = { ...buffer[i], ...ev };
736
- // 🔴 Coalescing must not REORDER across a lifecycle barrier (0.41.0 adversarial review #2).
737
- // Overwriting in place rewrites `progress(A,v1) → end(A) → progress(A,v2)` into
738
- // `progress(A,v2) → end(A)` — the post-settle rollup is moved BEFORE the settle, so a row that
739
- // was revived (the fleet projection explicitly allows a settled row to run again) gets settled
740
- // by the replay again, with the revived progress applied to the dead cycle. The bar for a
741
- // buffered replay is "same result as if the panel had been mounted the whole time", so the
742
- // surviving copy has to keep the LATEST event's arrival position: drop the old one, append.
743
487
  buffer.splice(i, 1);
744
488
  buffer.push(merged);
745
489
  return;
@@ -749,31 +493,15 @@ function deliverEngineAgentPanelEvent(ev) {
749
493
  buffer.push(ev);
750
494
  return;
751
495
  }
752
- // Full: the oldest PROGRESS event yields (its taskId's next rollup supersedes it anyway).
753
496
  const evictAt = buffer.findIndex(b => b.kind === 'tick' || b.kind === 'fleet-row');
754
497
  if (evictAt >= 0) {
755
498
  buffer.splice(evictAt, 1);
756
499
  buffer.push(ev);
757
500
  return;
758
501
  }
759
- // Nothing but lifecycle events in a full buffer (MAX_BUFFER rows settled while the panel stayed
760
- // unmounted — 200 distinct 'end's is the realistic way there, the adjacent-sweep dedup above having
761
- // removed the cheap one). At a finite bound something must give, and the rule is NEWEST-WINS for
762
- // every kind: the oldest event yields, whatever it is.
763
- //
764
- // 🔴 Why not "progress yields here too" (which is what this arm used to do — adversarial review
765
- // round 7, finding #4): a progress event arriving at this point is the ONLY word the panel will
766
- // ever get about a row that was created or revived after the barrier — `projectFleetAgentRows`
767
- // emits on CHANGE, so a row that then goes quiet never speaks again. Dropping it makes that row
768
- // permanently invisible after a remount, which is a strictly worse loss than dropping the OLDEST
769
- // lifecycle event: that one settles a row that finished ~MAX_BUFFER events ago, and the sweep that
770
- // every turn publishes (newest ⇒ always survives) settles whatever is still running anyway.
771
- // The residual — an old 'end' can be dropped, taking its `report`/`isError` with it — is the honest
772
- // price of a bounded buffer and is registered in CHANGELOG 0.41.0 "已知局限".
773
502
  buffer.shift();
774
503
  buffer.push(ev);
775
504
  }
776
- /** Single consumer (the panel hook). Replays any buffered events, returns an unsubscribe. */
777
505
  export function subscribeEngineAgentPanel(fn) {
778
506
  listener = fn;
779
507
  if (buffer.length > 0) {
@@ -783,7 +511,6 @@ export function subscribeEngineAgentPanel(fn) {
783
511
  fn(ev);
784
512
  }
785
513
  catch {
786
- // ignore — same guard as publish
787
514
  }
788
515
  }
789
516
  }
@@ -794,11 +521,8 @@ export function subscribeEngineAgentPanel(fn) {
794
521
  }
795
522
  const MAX_ABSENCE_BUFFER = 200;
796
523
  let absenceListener = null;
797
- /** 未订阅时的缓冲:按 taskId 留最新一条(同一行只有最新一次缺席有信息),有上界(最旧的先让位)。 */
798
524
  const absenceBuffer = new Map();
799
- /** 发布一条缺席上报(由 `projectFleetAgentRows` 的缺席路径调;消费者抛错不反噬投影)。 */
800
525
  export function publishEngineAgentPanelAbsence(ev) {
801
- // 0.74.4 CC-83(异源复审 R1 [medium]):尾段已归先发布的 UUID 所有 ⇒ 缺席也按那把键发(主通道与侧信道同一把键),原尾段进 wireTaskId
802
526
  const owner = wireOwnerOf(ev.taskId);
803
527
  if (owner !== undefined)
804
528
  ev = { ...ev, taskId: owner, wireTaskId: ev.taskId };
@@ -807,7 +531,6 @@ export function publishEngineAgentPanelAbsence(ev) {
807
531
  absenceListener(ev);
808
532
  }
809
533
  catch {
810
- // A consumer render error must never tear down the projection.
811
534
  }
812
535
  return;
813
536
  }
@@ -819,7 +542,6 @@ export function publishEngineAgentPanelAbsence(ev) {
819
542
  absenceBuffer.delete(oldest);
820
543
  }
821
544
  }
822
- /** 单消费者(面板 hook)。订阅即回放缓冲,返回取消订阅。不订阅 = 行为与 0.72.11 的壳完全相同(行停在最后已知态)。 */
823
545
  export function subscribeEngineAgentPanelAbsence(fn) {
824
546
  absenceListener = fn;
825
547
  if (absenceBuffer.size > 0) {
@@ -830,7 +552,6 @@ export function subscribeEngineAgentPanelAbsence(fn) {
830
552
  fn(ev);
831
553
  }
832
554
  catch {
833
- // ignore — same guard as publish
834
555
  }
835
556
  }
836
557
  }
@@ -839,7 +560,6 @@ export function subscribeEngineAgentPanelAbsence(fn) {
839
560
  absenceListener = null;
840
561
  };
841
562
  }
842
- /** 测试钩:清缺席口的订阅与缓冲。 */
843
563
  export function __resetEngineAgentPanelAbsenceForTests() {
844
564
  absenceListener = null;
845
565
  absenceBuffer.clear();