@sema-agent/client-core 0.77.1 → 0.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (382) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/README.md +6 -5
  3. package/dist/abortableSleep.d.ts +0 -28
  4. package/dist/abortableSleep.js +0 -28
  5. package/dist/adapt/arms.d.ts +0 -46
  6. package/dist/adapt/arms.js +9 -544
  7. package/dist/adapt/ids.d.ts +0 -58
  8. package/dist/adapt/ids.js +0 -63
  9. package/dist/adapt/instanceLedger.d.ts +0 -25
  10. package/dist/adapt/instanceLedger.js +0 -20
  11. package/dist/adapt/panelTasks.d.ts +0 -69
  12. package/dist/adapt/panelTasks.js +0 -95
  13. package/dist/adapt/textSegmentAuthority.d.ts +1 -137
  14. package/dist/adapt/textSegmentAuthority.js +0 -54
  15. package/dist/adapt/textStream.d.ts +0 -252
  16. package/dist/adapt/textStream.js +2 -281
  17. package/dist/adapt/toolCards.d.ts +0 -46
  18. package/dist/adapt/toolCards.js +0 -23
  19. package/dist/adapt/turnFlags.d.ts +0 -54
  20. package/dist/adapt/turnFlags.js +1 -17
  21. package/dist/adapt/wireShapes.d.ts +0 -92
  22. package/dist/adapt/wireShapes.js +0 -83
  23. package/dist/adapt.d.ts +0 -55
  24. package/dist/adapt.js +1 -120
  25. package/dist/adapter/activeRunSelfHeal.d.ts +22 -515
  26. package/dist/adapter/activeRunSelfHeal.js +10 -625
  27. package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -305
  28. package/dist/adapter/downstream/eventToSdkMessage.js +2 -867
  29. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -314
  30. package/dist/adapter/downstream/terminalToSdkResult.js +13 -560
  31. package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +0 -95
  32. package/dist/adapter/downstream/turnUsageToModelUsage.js +2 -31
  33. package/dist/adapter/runStream.d.ts +0 -206
  34. package/dist/adapter/runStream.js +6 -596
  35. package/dist/adapter/types.d.ts +0 -111
  36. package/dist/adapter/types.js +0 -29
  37. package/dist/agentSession/backgroundView.d.ts +0 -106
  38. package/dist/agentSession/backgroundView.js +3 -49
  39. package/dist/agentSession/contract.d.ts +0 -98
  40. package/dist/agentSession/contract.js +0 -12
  41. package/dist/agentsWireCaps.d.ts +0 -72
  42. package/dist/agentsWireCaps.js +4 -72
  43. package/dist/approvalsStreamLiveCapability.d.ts +0 -25
  44. package/dist/approvalsStreamLiveCapability.js +0 -31
  45. package/dist/argvFlagValue.d.ts +0 -33
  46. package/dist/argvFlagValue.js +3 -35
  47. package/dist/attachmentsWireCaps.d.ts +0 -127
  48. package/dist/attachmentsWireCaps.js +0 -144
  49. package/dist/autoModeUnavailable.d.ts +0 -120
  50. package/dist/autoModeUnavailable.js +0 -144
  51. package/dist/classifierStatus.d.ts +0 -72
  52. package/dist/classifierStatus.js +0 -167
  53. package/dist/classifierVerdictWire.d.ts +0 -54
  54. package/dist/classifierVerdictWire.js +0 -133
  55. package/dist/clientContextWireCaps.d.ts +0 -37
  56. package/dist/clientContextWireCaps.js +0 -36
  57. package/dist/clientSlice.d.ts +0 -63
  58. package/dist/clientSlice.js +0 -45
  59. package/dist/cloudConfigWireCaps.d.ts +0 -86
  60. package/dist/cloudConfigWireCaps.js +2 -65
  61. package/dist/compensations.d.ts +0 -52
  62. package/dist/compensations.js +2 -63
  63. package/dist/controlRouter.d.ts +2 -208
  64. package/dist/controlRouter.js +1 -129
  65. package/dist/coreValuePorts.d.ts +0 -84
  66. package/dist/coreValuePorts.js +0 -29
  67. package/dist/decideReceipt.d.ts +0 -110
  68. package/dist/decideReceipt.js +0 -84
  69. package/dist/detachWire.d.ts +0 -130
  70. package/dist/detachWire.js +1 -130
  71. package/dist/deviceExecutorManagementCapability.d.ts +2 -35
  72. package/dist/deviceExecutorManagementCapability.js +1 -37
  73. package/dist/diagnostics.d.ts +0 -8
  74. package/dist/diagnostics.js +0 -8
  75. package/dist/diff/patch.d.ts +0 -17
  76. package/dist/diff/patch.js +0 -19
  77. package/dist/effectiveFacts.d.ts +0 -40
  78. package/dist/effectiveFacts.js +0 -22
  79. package/dist/effortWire.d.ts +0 -11
  80. package/dist/effortWire.js +0 -12
  81. package/dist/engineAgentPanelStore.d.ts +0 -169
  82. package/dist/engineAgentPanelStore.js +13 -293
  83. package/dist/engineCapReader.d.ts +0 -62
  84. package/dist/engineCapReader.js +1 -43
  85. package/dist/engineCapsCache.d.ts +0 -177
  86. package/dist/engineCapsCache.js +0 -199
  87. package/dist/engineCapsGenerationGuard.d.ts +0 -2
  88. package/dist/engineCapsGenerationGuard.js +0 -11
  89. package/dist/engineErrorCodes.d.ts +1 -316
  90. package/dist/engineErrorCodes.js +0 -427
  91. package/dist/engineHttpTools.d.ts +0 -29
  92. package/dist/engineHttpTools.js +0 -29
  93. package/dist/engineIdentity.d.ts +0 -83
  94. package/dist/engineIdentity.js +0 -88
  95. package/dist/engineInlineTaskStats.d.ts +0 -57
  96. package/dist/engineInlineTaskStats.js +1 -42
  97. package/dist/engineNoticeCodes.d.ts +0 -190
  98. package/dist/engineNoticeCodes.js +0 -182
  99. package/dist/engineSessionParam.d.ts +0 -30
  100. package/dist/engineSessionParam.js +0 -52
  101. package/dist/engineToolLabelStore.d.ts +0 -29
  102. package/dist/engineToolLabelStore.js +0 -31
  103. package/dist/engineWireSdk.d.ts +0 -90
  104. package/dist/engineWireSdk.js +0 -74
  105. package/dist/engineWireTarget.d.ts +0 -14
  106. package/dist/engineWireTarget.js +0 -39
  107. package/dist/env/localeGeo.d.ts +0 -12
  108. package/dist/env/localeGeo.js +2 -77
  109. package/dist/env/localeTag.d.ts +0 -34
  110. package/dist/env/localeTag.js +0 -32
  111. package/dist/env/uiLanguage.d.ts +0 -13
  112. package/dist/env/uiLanguage.js +0 -25
  113. package/dist/envFlag.d.ts +0 -37
  114. package/dist/envFlag.js +0 -40
  115. package/dist/executionLaneCapability.d.ts +0 -48
  116. package/dist/executionLaneCapability.js +0 -54
  117. package/dist/finalVerifyWire.d.ts +0 -67
  118. package/dist/finalVerifyWire.js +3 -43
  119. package/dist/fleet/fleetLedger.d.ts +0 -308
  120. package/dist/fleet/fleetLedger.js +10 -429
  121. package/dist/fleet/fleetProjection.d.ts +0 -240
  122. package/dist/fleet/fleetProjection.js +0 -189
  123. package/dist/fleet/fleetRowAgentType.d.ts +0 -6
  124. package/dist/fleet/fleetRowAgentType.js +1 -32
  125. package/dist/fleet/workflowSizeWarning.d.ts +0 -47
  126. package/dist/fleet/workflowSizeWarning.js +1 -46
  127. package/dist/fleetAgentPanelProjection.d.ts +0 -48
  128. package/dist/fleetAgentPanelProjection.js +8 -173
  129. package/dist/fleetTaskDesc.d.ts +0 -39
  130. package/dist/fleetTaskDesc.js +0 -69
  131. package/dist/forkWireCaps.d.ts +0 -23
  132. package/dist/forkWireCaps.js +1 -24
  133. package/dist/gateOutcome.d.ts +0 -140
  134. package/dist/gateOutcome.js +0 -85
  135. package/dist/gateVocabulary.d.ts +0 -114
  136. package/dist/gateVocabulary.js +3 -161
  137. package/dist/goalStopHook.d.ts +0 -127
  138. package/dist/goalStopHook.js +0 -178
  139. package/dist/headlessPermissionModeWire.d.ts +0 -76
  140. package/dist/headlessPermissionModeWire.js +1 -155
  141. package/dist/headlessReconnectWire.d.ts +0 -84
  142. package/dist/headlessReconnectWire.js +13 -67
  143. package/dist/hitl/approvalDecisionNoteAudit.d.ts +0 -49
  144. package/dist/hitl/approvalDecisionNoteAudit.js +0 -56
  145. package/dist/hitl/approvalOutcomeNote.d.ts +0 -2
  146. package/dist/hitl/approvalOutcomeNote.js +0 -20
  147. package/dist/hitl/approvalResolution.d.ts +0 -113
  148. package/dist/hitl/approvalResolution.js +0 -66
  149. package/dist/hitl/approvalsFeed.d.ts +0 -183
  150. package/dist/hitl/approvalsFeed.js +12 -243
  151. package/dist/hitl/armedGateRegistry.d.ts +0 -55
  152. package/dist/hitl/armedGateRegistry.js +0 -146
  153. package/dist/hitl/askGateWire.d.ts +0 -86
  154. package/dist/hitl/askGateWire.js +1 -95
  155. package/dist/hitl/askParkRowRouting.d.ts +0 -123
  156. package/dist/hitl/askParkRowRouting.js +1 -89
  157. package/dist/hitl/crashConverged.d.ts +0 -148
  158. package/dist/hitl/crashConverged.js +0 -214
  159. package/dist/hitl/editedRuleTextPrecheck.d.ts +0 -109
  160. package/dist/hitl/editedRuleTextPrecheck.js +0 -71
  161. package/dist/hitl/frameRouter.d.ts +0 -134
  162. package/dist/hitl/frameRouter.js +6 -378
  163. package/dist/hitl/gateIdentity.d.ts +0 -39
  164. package/dist/hitl/gateIdentity.js +0 -41
  165. package/dist/hitl/gateLedger.d.ts +0 -267
  166. package/dist/hitl/gateLedger.js +0 -121
  167. package/dist/hitl/hitlBridge.d.ts +7 -431
  168. package/dist/hitl/hitlBridge.js +7 -476
  169. package/dist/hitl/hitlHostSurface.d.ts +0 -150
  170. package/dist/hitl/hitlHostSurface.js +0 -169
  171. package/dist/hitl/livePendingAsk.d.ts +0 -91
  172. package/dist/hitl/livePendingAsk.js +0 -74
  173. package/dist/hitl/localAllowRule.d.ts +0 -62
  174. package/dist/hitl/localAllowRule.js +1 -33
  175. package/dist/hitl/parkOwnership.d.ts +0 -56
  176. package/dist/hitl/parkOwnership.js +0 -22
  177. package/dist/hitl/parkResolver.d.ts +3 -88
  178. package/dist/hitl/parkResolver.js +8 -349
  179. package/dist/hitl/parkRowBirthWait.d.ts +2 -26
  180. package/dist/hitl/parkRowBirthWait.js +3 -93
  181. package/dist/hitl/persistedRulesWire.d.ts +23 -371
  182. package/dist/hitl/persistedRulesWire.js +62 -329
  183. package/dist/hitl/planReviewWire.d.ts +4 -176
  184. package/dist/hitl/planReviewWire.js +9 -312
  185. package/dist/hitl/resumeRunningCard.d.ts +0 -105
  186. package/dist/hitl/resumeRunningCard.js +0 -105
  187. package/dist/hitl/sessionPolicyWire.d.ts +11 -191
  188. package/dist/hitl/sessionPolicyWire.js +0 -149
  189. package/dist/hitl/suspendedReopen.d.ts +3 -24
  190. package/dist/hitl/suspendedReopen.js +0 -14
  191. package/dist/hitl/toolApprovalWire.d.ts +5 -1308
  192. package/dist/hitl/toolApprovalWire.js +5 -945
  193. package/dist/hooksWireCaps.d.ts +0 -38
  194. package/dist/hooksWireCaps.js +0 -190
  195. package/dist/host.d.ts +0 -105
  196. package/dist/host.js +0 -33
  197. package/dist/hostEnv.d.ts +0 -14
  198. package/dist/hostEnv.js +0 -13
  199. package/dist/imagesWireCaps.d.ts +0 -21
  200. package/dist/imagesWireCaps.js +0 -22
  201. package/dist/index.d.ts +0 -134
  202. package/dist/index.js +0 -476
  203. package/dist/interactiveHalt.d.ts +5 -153
  204. package/dist/interactiveHalt.js +0 -111
  205. package/dist/interactiveToolsWire.d.ts +0 -62
  206. package/dist/interactiveToolsWire.js +1 -67
  207. package/dist/leaderConflict.d.ts +0 -59
  208. package/dist/leaderConflict.js +0 -48
  209. package/dist/limitsWire.d.ts +0 -125
  210. package/dist/limitsWire.js +2 -120
  211. package/dist/liveInitToolFace.d.ts +0 -77
  212. package/dist/liveInitToolFace.js +1 -109
  213. package/dist/liveModelCatalog.d.ts +0 -52
  214. package/dist/liveModelCatalog.js +0 -48
  215. package/dist/liveQuestionStore.d.ts +0 -87
  216. package/dist/liveQuestionStore.js +1 -56
  217. package/dist/mcpLiveness.d.ts +0 -151
  218. package/dist/mcpLiveness.js +0 -122
  219. package/dist/mcpPanel.d.ts +0 -104
  220. package/dist/mcpPanel.js +0 -49
  221. package/dist/mcpReconnect.d.ts +0 -58
  222. package/dist/mcpReconnect.js +0 -69
  223. package/dist/mcpWireCaps.d.ts +0 -55
  224. package/dist/mcpWireCaps.js +1 -13
  225. package/dist/memoryComplianceCapability.d.ts +3 -57
  226. package/dist/memoryComplianceCapability.js +0 -57
  227. package/dist/memoryEntriesWire.d.ts +6 -198
  228. package/dist/memoryEntriesWire.js +0 -144
  229. package/dist/memoryOriginCapability.d.ts +3 -53
  230. package/dist/memoryOriginCapability.js +0 -50
  231. package/dist/memorySpecWire.d.ts +0 -116
  232. package/dist/memorySpecWire.js +0 -150
  233. package/dist/model/catalog.d.ts +0 -111
  234. package/dist/model/catalog.js +0 -87
  235. package/dist/model/catalogLoader.d.ts +0 -114
  236. package/dist/model/catalogLoader.js +2 -139
  237. package/dist/model/modelSupplyRules.d.ts +1 -55
  238. package/dist/model/modelSupplyRules.js +0 -62
  239. package/dist/model/providerAuth.d.ts +0 -103
  240. package/dist/model/providerAuth.js +2 -38
  241. package/dist/model/providerCatalog.d.ts +0 -45
  242. package/dist/model/providerCatalog.js +0 -37
  243. package/dist/model/providerPresets.d.ts +0 -33
  244. package/dist/model/providerPresets.js +2 -91
  245. package/dist/model/tierVocabulary.d.ts +0 -31
  246. package/dist/model/tierVocabulary.js +0 -27
  247. package/dist/modelBudgetRule.d.ts +0 -39
  248. package/dist/modelBudgetRule.js +0 -39
  249. package/dist/modelCapabilityProbe.d.ts +0 -175
  250. package/dist/modelCapabilityProbe.js +0 -147
  251. package/dist/modelWireCaps.d.ts +0 -13
  252. package/dist/modelWireCaps.js +0 -13
  253. package/dist/notifications.d.ts +0 -233
  254. package/dist/notifications.js +14 -492
  255. package/dist/oneShotWireCaps.d.ts +0 -34
  256. package/dist/oneShotWireCaps.js +0 -35
  257. package/dist/ownKey.d.ts +0 -33
  258. package/dist/ownKey.js +0 -33
  259. package/dist/panelRunningHistory.d.ts +0 -28
  260. package/dist/panelRunningHistory.js +0 -43
  261. package/dist/peerFrames.d.ts +0 -71
  262. package/dist/peerFrames.js +0 -168
  263. package/dist/peerLaneCapability.d.ts +0 -44
  264. package/dist/peerLaneCapability.js +0 -52
  265. package/dist/permissionRuleIssue.d.ts +0 -30
  266. package/dist/permissionRuleIssue.js +0 -78
  267. package/dist/permissionRulesWriteCapability.d.ts +0 -46
  268. package/dist/permissionRulesWriteCapability.js +0 -54
  269. package/dist/permissionWireCaps.d.ts +0 -37
  270. package/dist/permissionWireCaps.js +0 -37
  271. package/dist/postureKnob.d.ts +0 -71
  272. package/dist/postureKnob.js +0 -86
  273. package/dist/principalWire.d.ts +0 -17
  274. package/dist/principalWire.js +0 -17
  275. package/dist/printToolResultFrame.d.ts +0 -100
  276. package/dist/printToolResultFrame.js +0 -33
  277. package/dist/promptProfileWireCaps.d.ts +0 -13
  278. package/dist/promptProfileWireCaps.js +0 -13
  279. package/dist/readFacePosture.d.ts +0 -43
  280. package/dist/readFacePosture.js +0 -46
  281. package/dist/request/printNotification.d.ts +0 -20
  282. package/dist/request/printNotification.js +0 -55
  283. package/dist/request/taskRequest.d.ts +1 -249
  284. package/dist/request/taskRequest.js +20 -498
  285. package/dist/resumeRefusalCopy.d.ts +0 -136
  286. package/dist/resumeRefusalCopy.js +1 -117
  287. package/dist/retainBackgroundWireCaps.d.ts +0 -48
  288. package/dist/retainBackgroundWireCaps.js +0 -48
  289. package/dist/retryStatus.d.ts +2 -221
  290. package/dist/retryStatus.js +0 -107
  291. package/dist/rewindWireCaps.d.ts +0 -27
  292. package/dist/rewindWireCaps.js +0 -24
  293. package/dist/runCancelContext.d.ts +1 -20
  294. package/dist/runCancelContext.js +0 -34
  295. package/dist/runTerminal.d.ts +0 -273
  296. package/dist/runTerminal.js +0 -177
  297. package/dist/sandboxWire.d.ts +0 -38
  298. package/dist/sandboxWire.js +0 -82
  299. package/dist/scenarioWire.d.ts +0 -60
  300. package/dist/scenarioWire.js +0 -68
  301. package/dist/scratchpadWireCaps.d.ts +0 -11
  302. package/dist/scratchpadWireCaps.js +0 -32
  303. package/dist/sdkWireTransit.d.ts +0 -38
  304. package/dist/sdkWireTransit.js +0 -30
  305. package/dist/seam.d.ts +18 -853
  306. package/dist/seam.js +0 -52
  307. package/dist/seatContract.d.ts +0 -555
  308. package/dist/seatContract.js +5 -304
  309. package/dist/selfOrchestrationDenial.d.ts +0 -189
  310. package/dist/selfOrchestrationDenial.js +0 -162
  311. package/dist/selfOrchestrationWireCaps.d.ts +0 -38
  312. package/dist/selfOrchestrationWireCaps.js +0 -38
  313. package/dist/sessionMap.d.ts +0 -60
  314. package/dist/sessionMap.js +0 -37
  315. package/dist/sessionMemoryStatus.d.ts +11 -114
  316. package/dist/sessionMemoryStatus.js +0 -112
  317. package/dist/sessionModelLatch.d.ts +0 -29
  318. package/dist/sessionModelLatch.js +0 -40
  319. package/dist/sessionPolicyCapability.d.ts +3 -53
  320. package/dist/sessionPolicyCapability.js +0 -52
  321. package/dist/sessionSlot.d.ts +0 -26
  322. package/dist/sessionSlot.js +0 -17
  323. package/dist/skillsWireCaps.d.ts +0 -87
  324. package/dist/skillsWireCaps.js +0 -38
  325. package/dist/sqlEngineCapability.d.ts +0 -118
  326. package/dist/sqlEngineCapability.js +0 -134
  327. package/dist/sseIdleTriage.d.ts +0 -79
  328. package/dist/sseIdleTriage.js +1 -80
  329. package/dist/steering.d.ts +0 -54
  330. package/dist/steering.js +0 -63
  331. package/dist/subagent/engineCompactWire.d.ts +0 -34
  332. package/dist/subagent/engineCompactWire.js +4 -134
  333. package/dist/subagent/engineDelegatedPrompt.d.ts +0 -31
  334. package/dist/subagent/engineDelegatedPrompt.js +1 -103
  335. package/dist/subagent/engineRowStopGate.d.ts +0 -20
  336. package/dist/subagent/engineRowStopGate.js +0 -49
  337. package/dist/subagent/engineSubagentOutput.d.ts +0 -7
  338. package/dist/subagent/engineSubagentOutput.js +1 -51
  339. package/dist/subagent/engineSubagentResume.d.ts +2 -279
  340. package/dist/subagent/engineSubagentResume.js +2 -129
  341. package/dist/subagent/engineSubagentSteer.d.ts +0 -26
  342. package/dist/subagent/engineSubagentSteer.js +0 -63
  343. package/dist/subagent/engineSubagentTail.d.ts +0 -38
  344. package/dist/subagent/engineSubagentTail.js +2 -110
  345. package/dist/subagent/engineTaskHandleWire.d.ts +6 -67
  346. package/dist/subagent/engineTaskHandleWire.js +0 -126
  347. package/dist/subagent/subagentOwnerAbsence.d.ts +0 -2
  348. package/dist/subagent/subagentOwnerAbsence.js +0 -25
  349. package/dist/subagentContentStore.d.ts +3 -260
  350. package/dist/subagentContentStore.js +7 -540
  351. package/dist/systemReminderTag.d.ts +0 -49
  352. package/dist/systemReminderTag.js +0 -61
  353. package/dist/toolResult.d.ts +1 -211
  354. package/dist/toolResult.js +1 -358
  355. package/dist/toolRoster.d.ts +0 -140
  356. package/dist/toolRoster.js +0 -66
  357. package/dist/typePins.d.ts +0 -46
  358. package/dist/types/engineState.d.ts +0 -71
  359. package/dist/types/engineState.js +0 -16
  360. package/dist/ultracodeWireCaps.d.ts +0 -75
  361. package/dist/ultracodeWireCaps.js +0 -92
  362. package/dist/unrefTimer.d.ts +0 -27
  363. package/dist/webSearchBackendCapability.d.ts +0 -72
  364. package/dist/webSearchBackendCapability.js +0 -79
  365. package/dist/webSearchWireCaps.d.ts +0 -37
  366. package/dist/webSearchWireCaps.js +0 -34
  367. package/dist/websearch/searchProviderPresets.d.ts +0 -81
  368. package/dist/websearch/searchProviderPresets.js +0 -27
  369. package/dist/wireErrorTriage.d.ts +4 -160
  370. package/dist/wireErrorTriage.js +0 -171
  371. package/dist/wireRefusalCopy.d.ts +0 -28
  372. package/dist/wireRefusalCopy.js +0 -28
  373. package/dist/workflow.d.ts +0 -40
  374. package/dist/workflow.js +0 -52
  375. package/dist/workflowClient.d.ts +0 -122
  376. package/dist/workflowClient.js +8 -360
  377. package/dist/workflowMonitor.d.ts +2 -72
  378. package/dist/workflowMonitor.js +0 -26
  379. package/dist/writeProtectionCapability.d.ts +0 -112
  380. package/dist/writeProtectionCapability.js +0 -109
  381. package/docs/INTEGRATION-CLIENTS.md +78 -5
  382. package/package.json +3 -3
@@ -1,27 +1,7 @@
1
1
  import { hostLog } from '../host.js';
2
2
  import { unrefTimer } from '../unrefTimer.js';
3
3
  import { readLivePendingRows } from './livePendingAsk.js';
4
- /** [2393] hitl-F5:poll 回调体**连续**逃逸多少次之后判定「这条腿真挂了」并把 `mode` 打成 idle。
5
- * 取 5 是为了让「偶发一拍畸形 payload」不误判(单次逃逸下一拍就归零),同时不让一条永远抛的腿
6
- * 无限期地对着端的自检假装自己还活着。 */
7
4
  const MAX_CONSECUTIVE_POLL_ESCAPES = 5;
8
- /**
9
- * 🔴 CC-98(异源复审 R2 [medium] 采修)`pending` 段的**结构窄读**。
10
- *
11
- * 修前这一段一个字都没校验:摘要函数对缺键一律 `?? ''`,于是 `{pending:[{}]}` / `{pending:[7]}` /
12
- * `{pending:{map:()=>[]}}` 这类回体会被当成**合形真快照**发出去,端照着它渲一个确定的数字
13
- * (`durable: 1`)—— 与「取不到渲成没有」同一个病形的镜像:**读不懂渲成读懂了**。
14
- *
15
- * 三分界与 `livePending` 段逐字同:
16
- * · 不是数组 ⇒ `malformed`(整次不提交、发「不知道」);
17
- * · **非空数组却一行都读不出** ⇒ `malformed`(不许答「有 0 条」);
18
- * · **部分**行读不出 ⇒ 逐条丢 + `present` + `dropped`(读得出的行是真的;`dropped` 上快照,
19
- * 计数口据此把 durable 格答 `null`);空数组 ⇒ `present`(引擎明说一条都没有)。
20
- *
21
- * 一行的**最低可读位** = 非空串 `sessionId`:它就是决断口(`POST /v1/approvals/:sessionId/decide`)的钥匙,
22
- * 也是按会话过滤计数的键;没有它这一行既决断不了也归不了属。`scope`(sdk 型面上也是必填)**刻意不要求** ——
23
- * 缺它的行仍然决断得了,fail-safe 方向是留行不是丢行(丢一行 = 一条等人的审批在端上消失)。
24
- */
25
5
  function readDurablePendingRows(raw) {
26
6
  if (!Array.isArray(raw))
27
7
  return { kind: 'malformed' };
@@ -43,17 +23,6 @@ function readDurablePendingRows(raw) {
43
23
  return { kind: 'malformed' };
44
24
  return { kind: 'present', rows, dropped };
45
25
  }
46
- /**
47
- * 🔴 CC-98(异源复审 R3 [medium] 采修)**「修前这一形会不会让回调体逃逸」的判据 —— 真去算一遍修前那个摘要**。
48
- *
49
- * 为什么不许猜:逃逸是 [2393] hitl-F5 **熔断**的输入(连续 5 次 ⇒ `mode` 诚实转 idle),本车不许悄悄把它拿掉。
50
- * 上一版在读器里手列了「整段不是数组 / 行里有 null」两形当作逃逸形 —— 一条普通 JSON 就能证伪:
51
- * `{"pending":[{"sessionId":{"toString":null}}]}` 在修前是 `Array.join` 做字符串转换时抛 TypeError(逃逸),
52
- * 而手列的两形认不出它 ⇒ 持续坏回体不再触发 F5。⇒ 判据换成**行为等价**:把原摘要在原始值上真算一遍,
53
- * 抛了就是逃逸形。这样「逐字保留」是被**执行**出来的,不是被声明出来的。
54
- *
55
- * 只在坏形路上算(多算一次字符串拼接,常态零成本);本函数自己**绝不**抛。
56
- */
57
26
  function baselineDurableDigestThrows(raw) {
58
27
  try {
59
28
  durableDigestOf(raw);
@@ -63,11 +32,6 @@ function baselineDurableDigestThrows(raw) {
63
32
  return true;
64
33
  }
65
34
  }
66
- /**
67
- * pending 列表的稳定摘要 —— 只在**内容变了**的时候发快照。
68
- * 键里带 `boundInputHash`:同一条 pending 的绑定被服务端换掉(TOCTOU 场景)也算变化,
69
- * 否则端会拿着一个「id 没变但绑定变了」的行继续渲染。
70
- */
71
35
  function durableDigestOf(rows) {
72
36
  return rows
73
37
  .map(r => [r.sessionId ?? '', r.taskId ?? '', r.toolCallId ?? '', r.boundCallId ?? '', r.boundInputHash ?? '', r.toolName ?? '']
@@ -78,33 +42,15 @@ function durableDigestOf(rows) {
78
42
  const LIVE_FIELD_SEP = String.fromCharCode(1);
79
43
  const LIVE_ROW_SEP = String.fromCharCode(2);
80
44
  const LIVE_SECTION_SEP = String.fromCharCode(3);
81
- /**
82
- * 0.72.14:流内 ask 段进摘要 —— 此前只算 `pending`,悬挂 ask 出现 / 消失都不触发快照。
83
- * 「未报」与「报了且为空」是两个不同的事实(前者读不出计数),摘要上也分开。
84
- */
85
- function digestOf(rows, live,
86
- /** CC-98:读不出的行数也进摘要 —— 读得懂的那几行没变、但「另有几行读不懂」变了,同样是消费端要知道的变化
87
- * (不进摘要 ⇒ 不发快照 ⇒ 端手上那面「不完整」旗永远是上一拍的)。 */
88
- dropped = 0,
89
- /** CC-98:`pending` 段读不出的行数(同理进摘要)。 */
90
- droppedDurable = 0) {
45
+ function digestOf(rows, live, dropped = 0, droppedDurable = 0) {
91
46
  const liveDigest = live === undefined
92
47
  ? 'absent'
93
48
  : live
94
- // 0.73.2:`frame` 在场性进摘要 —— 同一行先盲后全(server 补帧)是内容变了,端要换全卡。
95
49
  .map(r => [r.approvalId, r.toolName, String(r.expiresAtMs), r.sessionId ?? '', r.originTaskId ?? '', r.frame !== undefined ? 'f' : ''].join(LIVE_FIELD_SEP))
96
50
  .sort()
97
51
  .join(LIVE_ROW_SEP);
98
52
  return `${durableDigestOf(rows)}${LIVE_SECTION_SEP}${liveDigest}${LIVE_SECTION_SEP}${dropped}${LIVE_SECTION_SEP}${droppedDurable}`;
99
53
  }
100
- /**
101
- * 起一条 pending-approvals feed。**幂等性归调用方**:一个 client 起一条就够了,起两条 =
102
- * 两倍取件(不是错误,但没意义)。
103
- *
104
- * 🔴 CC-98 订阅口收**两臂**({@link ApprovalsFeedEmission}):真快照(`kind:'snapshot'`)与
105
- * 「这次取不到」({@link ApprovalsFeedUnknownSnapshot},`kind:'unknown'`)。先判 `kind` 再读别的键 ——
106
- * 把 unknown 臂当成一张空列表就是把「不知道」渲成「没有」。三态的拉面读口是 `reading()`。
107
- */
108
54
  export function startApprovalsFeed(client, onSnapshot, opts) {
109
55
  const pollIntervalMs = opts?.pollIntervalMs ?? 5000;
110
56
  const streamRetryMs = opts?.streamRetryMs ?? 60_000;
@@ -119,52 +65,23 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
119
65
  let mode = client.approvals.stream ? 'push' : 'poll';
120
66
  let stopped = false;
121
67
  let revision = 0;
122
- /**
123
- * 🔴 CC-98(异源复审 R1 [medium] 采修):**内容**变化计数 —— 只在摘要真的变了时 +1。
124
- * 对账节拍的退避判据(「取件后快照没变 ⇒ 间隔翻倍」)用本位,**不用** `revision`:后者现在还会为
125
- * 「恢复必发」+1(内容一字未变),拿它判退避就等于每次间歇失败都把节拍无故拉回基础间隔 ——
126
- * 那是把本车不该动的 KL-15 面动了。两个计数分开之后,对账退避逐字与 0.75.0 同。
127
- */
128
68
  let contentRevision = 0;
129
69
  let lastDigest = null;
130
70
  let last = null;
131
- /**
132
- * CC-98:「现在取不到」的粘性态(null = 不盲)。两处承重:
133
- * ① `reading()` 的第三态;
134
- * ② **恢复必发**的判据 —— 只要它非空,下一次提交就算摘要与失败前一字不差也要发一张真快照
135
- * (否则端永远停在「不知道」)。真快照落地即清。
136
- */
137
71
  let blind = null;
138
72
  let pollTimer;
139
73
  let retryTimer;
140
- /** [2393] hitl-F5:连续「回调体抛出」的次数(一次跑完就归零)——见 `schedulePoll` 的 catch 臂。 */
141
74
  let consecutivePollEscapes = 0;
142
- /** [F4957-1] 轮询腿的**代际**:push 腿每重连一次 +1。在途的旧拍完成时凭它认出自己已经过期 ——
143
- * 它的成败属于上一条腿,不该改现在这条腿的连续计数,也不该由它来续排(见 `schedulePoll`)。 */
144
75
  let pollLeg = 0;
145
- /**
146
- * [F4957-1 三审] 取件的**单调发号**与「已观察到的最新一号」。乱序回滚是**取件面的通病**,不是
147
- * 轮询腿的私事:`refresh()`(端上的显式刷新)与 push 腿的重取同样可能后发先至。所以提交面的闸
148
- * 从「代际」升成请求序号 —— 一次响应只有在「没有更晚发起的请求先落过地」时才允许改视图。
149
- */
150
76
  let takeSeq = 0;
151
77
  let lastObservedSeq = 0;
152
78
  const stats = {
153
79
  pushEvents: 0, heartbeats: 0, polls: 0, listErrors: 0, streamFailures: 0, snapshots: 0, reconciles: 0, pushTakeRetries: 0,
154
80
  unknowns: 0,
155
81
  };
156
- /** 域词表-14 收编:「怎么调 unref」的单一实现见 unrefTimer.ts,本函数只是就地起个短名。 */
157
82
  function arm(t) {
158
83
  return unrefTimer(t);
159
84
  }
160
- /**
161
- * [C77]② 这一拍要不要真去取件。缺省(宿主没装旋钮)恒 true —— 缺省路径上连一个函数调用都不多。
162
- *
163
- * 两个不确定方向都倒向**「查」**(fail-safe 方向定在这里,理由是代价不对称):多查一次 =
164
- * 一个 `list()` 的流量;少查一次 = 一条待审批在端上迟迟不出现,而这条腿服务的正是 HITL 面。
165
- * · 谓词抛错 ⇒ 查(并记一行:宿主的谓词坏了它自己得看得见,不是静默吞);
166
- * · 谓词返回非布尔(宿主 bug / 忘了 return)⇒ 查,同样记一行。
167
- */
168
85
  function pollGateOpen() {
169
86
  const gate = opts?.shouldPoll;
170
87
  if (!gate)
@@ -183,19 +100,12 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
183
100
  }
184
101
  return verdict;
185
102
  }
186
- // ── 0.73.4 欠一次看(见 `pushTakeRetryMs` 头注)──
187
103
  const pushTakeRetryBaseMs = opts?.pushTakeRetryMs ?? 1000;
188
104
  const pushTakeRetryMaxMs = Math.max(pushTakeRetryBaseMs, 30_000);
189
105
  let pushTakeRetryTimer;
190
106
  let pushTakeRetryDelayMs = pushTakeRetryBaseMs;
191
- /** 欠账锚在**发起序号**上:最近一次没看成的 push 侧取件是第几号(0 = 不欠)。 */
192
107
  let owedSinceSeq = 0;
193
- /** 已经看成的最晚发起序号(与欠账独立记):比它更早发起的失败是迟到的旧账,不记(对偶的乱序形)。 */
194
108
  let lastLookedSeq = 0;
195
- /**
196
- * 销账只认**比欠账更晚发起**的取件:更早发起的那一次看的是失败之前的世界,它迟到的成功说明不了
197
- * 「引擎说变了的那一下」已经有人看过(乱序是真的会发生的:慢 list / `refresh()` 与 push 重取撞车)。
198
- */
199
109
  function noteTakeLooked(seq) {
200
110
  if (seq > lastLookedSeq)
201
111
  lastLookedSeq = seq;
@@ -209,22 +119,16 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
209
119
  pushTakeRetryDelayMs = pushTakeRetryBaseMs;
210
120
  }
211
121
  function noteTakeMissed(via, seq) {
212
- // 🔴 CC-98(异源复审 R2 [high] 采修)原条件是「只在 push 腿记账 —— 轮询模式下轮询腿自己会再来」。
213
- // 那条**前提**在当前模式已经是 push 时不成立:重连那一拍把 `pollTimer` 清了、也不再续排,
214
- // 于是一发**轮询腿的**失败(典型:重连那一刻还在途的那一拍)之后再没有任何人来取件 ——
215
- // 「不知道」没人清([paired-mechanisms-must-share-premise]:配对机制必须共享前提)。
216
- // ⇒ 条件放宽成「这一发是 push 腿 **或** 现在是 push 模式」。纯轮询模式(两者都是 poll)一字不变:
217
- // 轮询腿的下一拍照旧自己会来,不额外记账、不额外流量。
218
122
  if ((via !== 'push' && mode !== 'push') || pushTakeRetryBaseMs <= 0)
219
123
  return;
220
124
  if (seq < lastLookedSeq)
221
- return; // 更晚发起的一眼已经看成 ⇒ 这次迟到的失败不欠
125
+ return;
222
126
  if (seq > owedSinceSeq)
223
127
  owedSinceSeq = seq;
224
128
  if (stopped || ac.signal.aborted)
225
129
  return;
226
130
  if (pushTakeRetryTimer !== undefined)
227
- return; // 幂等:已经有一只在排
131
+ return;
228
132
  const delay = pushTakeRetryDelayMs;
229
133
  pushTakeRetryDelayMs = Math.min(pushTakeRetryDelayMs * 2, pushTakeRetryMaxMs);
230
134
  pushTakeRetryTimer = arm(setTimeout(() => {
@@ -232,30 +136,14 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
232
136
  if (stopped || ac.signal.aborted)
233
137
  return;
234
138
  stats.pushTakeRetries++;
235
- // 欠账不随模式走:期间若降级到轮询,这一次照取(轮询腿可能被 shouldPoll 关着)。
236
139
  void take('push').catch(e => {
237
- // 重试自己逃逸(回体坏形):欠账已在 take() 内续记并再排,这里只接住 rejection。
238
140
  hostLog('debug', `approvalsFeed: push-take retry escaped (unreadable body?) — still owed: ${String(e)}`);
239
141
  });
240
142
  }, delay));
241
143
  }
242
- /**
243
- * CC-98:发一张「不知道」。三条纪律:
244
- * ① **乱序闸与真快照共用一条**:比「已观察到的最新一号」更早发起的失败,其结论已被后发者取代 ⇒
245
- * 一个字节都不发(一发迟到的 404 不许把一发更晚发起的成功盖成「不知道」);发得出去就同样推进那一号。
246
- * ② **一段只发一张**(与「内容没变 ⇒ 不打扰订阅者」同一条纪律):同一成因连着失败不重复发,
247
- * `stats.listErrors` 照旧逐次记;成因变了(抛 ⇄ 读不懂)是两句话 ⇒ 再发一张。
248
- * ③ 与**重试 / 退避 / 熔断解耦**:发过「不知道」之后欠账重试照旧排、轮询腿照旧续排、
249
- * F5 的连续逃逸照旧计数 —— 本臂只说「现在不知道」,不说「不再试了」。
250
- */
251
144
  function publishUnknown(why, via, seq) {
252
145
  if (stopped || ac.signal.aborted)
253
146
  return;
254
- // 🔴 CC-98 **失败路只过发起序号闸,不过腿代际闸**(异源复审 R2 [high] 纠 R1 的修法 —— 上一版在这里加了
255
- // 一道代际闸,换来的是另一半谎:退役腿的失败被整份吞掉,而没有任何**更新的**结论取代它 ⇒ 端继续
256
- // 读到 `present` 和一个确定的数字,而最近一次取件其实失败了)。
257
- // 判据只能是「有没有更晚发起的结论已经落地」= 序号;**腿换代不是那件事的证据**(重连只说明推面活了,
258
- // 不说明有人重新取过件)。「那条退役的腿不会再来一拍」这件事由 `noteTakeMissed` 负责(见其头注)。
259
147
  if (seq < lastObservedSeq) {
260
148
  hostLog('debug', `approvalsFeed: dropping out-of-order failure #${seq} (newest observed #${lastObservedSeq}) — view untouched`);
261
149
  return;
@@ -270,26 +158,17 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
270
158
  onSnapshot({ kind: 'unknown', why, at, mode: via });
271
159
  }
272
160
  catch (e) {
273
- // 订阅者抛错绝不能把 feed 打死(与真快照臂同款纪律)。
274
161
  hostLog('debug', `approvalsFeed: subscriber threw on the unknown arm (swallowed): ${String(e)}`);
275
162
  }
276
163
  }
277
- async function take(via,
278
- /** [F4957-1 二审] 发起这次取件时的轮询代际(只有轮询腿传;push/refresh 不受代际管)。 */
279
- leg) {
164
+ async function take(via, leg) {
280
165
  if (stopped)
281
166
  return;
282
167
  const seq = ++takeSeq;
283
168
  let rows;
284
169
  let live;
285
- /** CC-98:`livePending` 段里读不出来的行数(0 = 这一段完整)。见 `ApprovalsFeedSnapshot.livePendingDropped`。 */
286
170
  let dropped = 0;
287
- /** CC-98:`pending` 段里读不出来的行数(0 = 这一段完整)。见 `ApprovalsFeedSnapshot.pendingDropped`。 */
288
171
  let droppedDurable = 0;
289
- // 🔴 CC-98 两段 try 是**刻意**分开的,不是洁癖:「没拿到」(取件自己抛 / reject)与「拿到了但不敢信」
290
- // (回体不是对象 / 键坏形)是两句话,`why` 要分得清 —— 合成一段的话一只 `null` 回体会被报成
291
- // 「取件失败」,宿主的提示文案与自检口径都会指错方向。两段的处置完全一致(记 listErrors、记欠账、
292
- // 发一张「不知道」、整次不提交),只有成因词不同。
293
172
  let body;
294
173
  try {
295
174
  body = await client.approvals.list({ signal: ac.signal });
@@ -298,21 +177,17 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
298
177
  stats.listErrors++;
299
178
  hostLog('debug', `approvalsFeed: list() failed (${via} leg): ${String(e)}`);
300
179
  noteTakeMissed(via, seq);
301
- publishUnknown('list_threw', via, seq); // 🔴 CC-98:失败也要有出口,不说 = 端拿上一张当现在
180
+ publishUnknown('list_threw', via, seq);
302
181
  return;
303
182
  }
304
- /** CC-98:`pending` 段的原始值。**在 try 里取**(回体是 `null` / 取键 getter 抛 ⇒ 与修前同档:不逃逸)。 */
305
183
  let rawPending;
306
184
  try {
307
185
  rawPending = body.pending;
308
- // 0.72.14:`livePending` 段按结构窄读。键在场却读不懂 ⇒ **整次取件不提交**(保留上一张快照):
309
- // 把「读不懂」提交成「未报 / 为空」会让端撤掉一张还在等人的卡。
310
186
  const reading = readLivePendingRows(body);
311
187
  if (reading.kind === 'malformed') {
312
188
  stats.listErrors++;
313
189
  hostLog('debug', `approvalsFeed: list() body has an unreadable livePending section (${via} leg) — snapshot not committed`);
314
190
  noteTakeMissed(via, seq);
315
- // 🔴 CC-98:视图不提交,但**必须说一声** —— 不说 = 端继续拿上一张当现在(把「读不懂」渲成「没有」)。
316
191
  publishUnknown('unreadable_payload', via, seq);
317
192
  return;
318
193
  }
@@ -324,35 +199,24 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
324
199
  }
325
200
  }
326
201
  catch (e) {
327
- // 回体本身读不出(`null` / 非对象 / `pending` 位是个会抛的 getter)—— 与上一段同档处置,成因不同。
328
202
  stats.listErrors++;
329
203
  hostLog('debug', `approvalsFeed: list() body unreadable (${via} leg): ${String(e)}`);
330
204
  noteTakeMissed(via, seq);
331
205
  publishUnknown('unreadable_payload', via, seq);
332
206
  return;
333
207
  }
334
- // ── 🔴 CC-98 第三段:`pending` 段按结构窄读(见 readDurablePendingRows 头注:修前一个字都没校验)。
335
- // 刻意排在上面那个 try **之外** —— 「逃逸形照旧逃出 take()」是 F5 熔断的输入,包在 try 里就等于
336
- // 悄悄把熔断拿掉了(实测:第一版包在里面,F5 门当场从「连续逃逸转 idle」变成永远 poll)。
337
208
  {
338
209
  let durableRead;
339
210
  try {
340
211
  durableRead = readDurablePendingRows(rawPending);
341
212
  }
342
213
  catch {
343
- // 载体上的 getter / 撤销过的 Proxy 抛 ⇒ 判 malformed;**是不是逃逸形**由下面那条行为等价判据答
344
- // (同一份原始值上原摘要同样会抛 ⇒ 照旧逃),不在这里猜。
345
214
  durableRead = { kind: 'malformed' };
346
215
  }
347
- // 🔴 F5 输入逐字保留的判据(见 baselineDurableDigestThrows 头注):**只在坏形路上**问一次
348
- // 「修前那个摘要会不会在这份原始值上抛」。抛 ⇒ 这一形修前是逃逸的,照旧逃(整次不交,连半程都不交);
349
- // 不抛 ⇒ 修前是**静默发布**成确定数字的那几形,现在改判「不知道」并 return。
350
216
  const badDurable = durableRead.kind === 'malformed' || durableRead.dropped > 0;
351
217
  const escapes = badDurable && baselineDurableDigestThrows(rawPending);
352
218
  if (durableRead.kind === 'malformed' || escapes) {
353
219
  hostLog('debug', `approvalsFeed: list() body has an unreadable pending section (${via} leg) — snapshot not committed`);
354
- // 修前静默发布的那几形现在与 `livePending` 段同档:记一次取件失败。修前**逃逸**的那几形不记
355
- // —— 它们走下面那条 throw(修前也不记,`listErrors` 的账逐字同)。
356
220
  if (!escapes)
357
221
  stats.listErrors++;
358
222
  noteTakeMissed(via, seq);
@@ -368,59 +232,32 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
368
232
  }
369
233
  if (stopped)
370
234
  return;
371
- // 🔴 摘要排在销账**之前**:回体坏形(例:`pending` 不是数组)会让它抛出、整次取件逃逸(各调用点自己接盘,
372
- // 轮询腿的连续逃逸计数靠的就是这条路)。先销账再抛 = 一份读不懂的回体把欠账销了,之后再没人来看。
373
- // 逃逸本身也是「没看成」:在**这里**统一记欠账再原样抛出,四个调用点(起手 / 事件 / refresh / 重试)不必各记各的。
374
235
  let d;
375
236
  try {
376
237
  d = digestOf(rows, live, dropped, droppedDurable);
377
238
  }
378
239
  catch (e) {
379
240
  noteTakeMissed(via, seq);
380
- // 🔴 CC-98:逃逸(`pending` 不是数组 / 行畸形)同样是「这次取不到」—— 先发臂再原样抛出,
381
- // 免得四个调用点(起手 / 事件 / refresh / 重试)各自去猜该不该通知端。
382
241
  publishUnknown('unreadable_payload', via, seq);
383
242
  throw e;
384
243
  }
385
- // 🔴 CC-98(异源复审 R3 [high] 采修)**代际闸排在销账之前**。原注说「销账与提交闸无关」——
386
- // 那句话只对**序号闸**成立(被序号闸丢掉 = 有更晚的结论已经落地 ⇒ 视图是新的 ⇒ 确实不欠了);
387
- // 对**代际闸**不成立:那份结论被整份丢掉、没有任何东西取代它,端手上什么都没更新 ——
388
- // 拿它去销账 = 让「谁来补这一眼」这件事凭空消失(实复现:较晚的成功先被丢掉、较早的失败随后发布
389
- // 「不知道」,而欠账因 `seq < lastLookedSeq` 不记 ⇒ 没人再取件 ⇒ 永久粘住)。
390
- // 两句话合成一条纪律:**只有真的改变了端手上那份视图、或被更新的结论取代的取件,才算「有人看过」**。
391
244
  if (leg !== undefined && leg !== pollLeg && blind === null) {
392
245
  hostLog('debug', `approvalsFeed: dropping stale poll snapshot from leg ${leg} (current ${pollLeg}) — debt untouched`);
393
246
  return;
394
247
  }
395
- // 看成了(回体读得懂)⇒ 销掉**更早发起**的欠账(判据在发起序号上,见 noteTakeLooked;与**序号**闸无关:
396
- // 销账问的是「失败之后有没有人再看过」,序号闸问的是「这份结论还新不新」)。
397
248
  noteTakeLooked(seq);
398
- // 🔴 [F4957-1 二审 finding②] 代际闸必须排在**提交之前**:下面四行(lastDigest / revision /
399
- // last / onSnapshot)一落,视图就已经被改了 —— 守卫排在 take() 之外只挡得住计数与续排,挡不住
400
- // 回滚。乱序是真的会发生的:旧 poll 在途 → push 重连并发布新快照 → 旧 poll 才带着**更老**的
401
- // 结果返回 ⇒ 端拿到一张 revision 更高、内容更旧的 `mode:'poll'` 快照(刚出现的待审批被抹掉,
402
- // 或刚解决的又冒回来)。过期的结论一个字节都不提交。
403
- // (代际闸已上移到销账之前 —— 见上面那段 R3 [high] 采修的头注。**盲着时让位**的理由照旧:
404
- // `blind !== null` 时让位给序号闸,否则旧腿那份读得懂的结论被丢掉、blind 没人清 ⇒ 永久粘住;
405
- // 让位是安全的,乱序本来就由序号闸守。)
406
- // 🔴 [F4957-1 三审 finding②] 乱序闸(三条取件路径共用):比「已观察到的最新一号」更早发起的
407
- // 响应,其结论已经被后发者的结论取代 —— 一个字节都不提交。`refresh()` 不带代际,单靠上面那道
408
- // 闸盖不住它(端上「用户点了刷新」正是最容易与 push 重取撞车的那一发)。
409
249
  if (seq < lastObservedSeq) {
410
250
  hostLog('debug', `approvalsFeed: dropping out-of-order take #${seq} (newest observed #${lastObservedSeq})`);
411
251
  return;
412
252
  }
413
253
  lastObservedSeq = seq;
414
- // 🔴 CC-98 **恢复路**:上一次发布是「不知道」⇒ 即使摘要与失败前一字不差也必须发一张真快照。
415
- // 少了这一条(修前的形):失败期间内容没变 ⇒ 摘要相等 ⇒ 这里 return ⇒ 「又能取到了、而且还是那 N 条」
416
- // 这件事没有任何出口,端永远停在「不知道」。
417
254
  const recovering = blind !== null;
418
255
  const contentChanged = d !== lastDigest;
419
256
  if (!contentChanged && !recovering)
420
- return; // 内容没变 ⇒ 不打扰订阅者
257
+ return;
421
258
  blind = null;
422
259
  if (contentChanged)
423
- contentRevision++; // 对账退避只认**内容**变化(见 contentRevision 头注)
260
+ contentRevision++;
424
261
  lastDigest = d;
425
262
  revision++;
426
263
  const snap = {
@@ -438,18 +275,9 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
438
275
  onSnapshot(snap);
439
276
  }
440
277
  catch (e) {
441
- // 订阅者抛错绝不能把 feed 打死(与 liveQuestionStore.publishQuestionFrame 同款纪律)。
442
278
  hostLog('debug', `approvalsFeed: subscriber threw (swallowed): ${String(e)}`);
443
279
  }
444
280
  }
445
- /**
446
- * 起轮询腿。**幂等**(`pollTimer` 在场就不再排一个)——
447
- * 🔴 第一版把「排定时器」挂在 `mode !== 'poll'` 这个**状态迁移**上,于是「一开始就没有
448
- * `approvals.stream`」的宿主(老 SDK / mock)因为初始 mode 本来就是 'poll',永远排不上
449
- * 定时器 = 只取了起手那一次就再也不动了。测试当场逮住(先红后绿)。
450
- * 教训与 [paired-mechanisms-must-share-premise] 同族:别把「该做的动作」挂在「状态变了」上,
451
- * 挂在「现在该处于什么状态」上。
452
- */
453
281
  function schedulePoll() {
454
282
  if (stopped || ac.signal.aborted)
455
283
  return;
@@ -457,14 +285,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
457
285
  return;
458
286
  pollTimer = arm(setTimeout(() => {
459
287
  pollTimer = undefined;
460
- // 🔴 [F4957-1 二审] 这一拍属于**哪一代**轮询腿。`take('poll')` 是可以挂很久的(慢 list /
461
- // 挂死读面),期间 push 腿完全可能重连成功并把连续计数清零 —— 那一拍的结论此时已经是
462
- // **上一条腿**的历史,回写到新计数里就等于把「被健康 push 窗隔开的失败」又算回连续
463
- // (回落之后只需 4 拍就转 idle),而这正是本批要修掉的那条语义。代际在重连处 +1。
464
288
  const leg = pollLeg;
465
- // [C77]② 跳拍:消费方说这一拍不用查 ⇒ 不取件、不计 `stats.polls`(没取件却计数 = 谎报流量)、
466
- // **不动** `consecutivePollEscapes`(跳拍不是失败,连着跳一万拍也不该被 F5 判成腿挂了),
467
- // 但下一拍照排 —— 这是「腿活着但闲着」,不是停摆。
468
289
  if (!pollGateOpen()) {
469
290
  if (mode === 'poll')
470
291
  schedulePoll();
@@ -472,32 +293,14 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
472
293
  }
473
294
  void (async () => {
474
295
  stats.polls++;
475
- await take('poll', leg); // 代际随取件一路带进去(提交面的闸在 take 内,见其头注)
476
- // 代际已翻篇 ⇒ 这一拍的成败都不属于现在这条腿:既不清零(会掩盖新腿真正的连续失败),
477
- // 也不续排(当代那条链由 degradeToPoll 自己排,这里再排一次纯属两条腿并行的种子)。
296
+ await take('poll', leg);
478
297
  if (leg !== pollLeg)
479
298
  return;
480
- consecutivePollEscapes = 0; // 跑完一整拍 ⇒ 之前的逃逸不再连续
299
+ consecutivePollEscapes = 0;
481
300
  if (mode === 'poll')
482
301
  schedulePoll();
483
302
  })().catch(e => {
484
- // REF-CC-024 ①:take() 理论上自己兜底(见 take() 内的 try/catch),但这是它逃逸时的最后一道
485
- // 接盘 —— 缺了它,轮询定时器的回调体抛出会变成 unhandled rejection(Node 默认致命)。
486
- //
487
- // 🔴 [2393] hitl-F5(2026-08-02):光接盘不够,**接盘之后还要接着排下一拍**。旧码在这里只记
488
- // 一行 debug 就结束:那一拍的 `schedulePoll()` 在 `await take()` 之后、永远轮不到执行 ⇒
489
- // 轮询腿当场死透。而 `mode()` 照旧报 'poll'、`stats()` 照旧有数 —— 文件头 38-42 卖的正是
490
- // 「跑了 N 秒之后 mode 不是 idle 且 polls+pushEvents > 0」这条端自检,于是自检看着一个
491
- // 已经死掉的 feed 说它活着(而「一条 pending 都没有」与「腿挂了」本来就长得一样,这恰恰是
492
- // 那段诚实面存在的理由)。逃逸不是理论形:`take()` 的 try/catch 只护 `list()` 本身,其后的
493
- // `digestOf(rows)` 在 wire 出畸形 payload(`pending` 不是数组)时就会从这里出去。
494
- //
495
- // 两条腿(不是二选一):① **续排** —— 一次抛出不该判 feed 死刑,下一拍照跑(下一拍在
496
- // `pollIntervalMs` 之后,天然不忙转;不新开定时器,单例卫兵仍是 `pollTimer`);
497
- // ② **连续失败上限转 idle** —— 一直抛就是真的挂了,那时 `mode` 必须说真话,让端的自检
498
- // 看得见([honest-absence-not-fabricated-zero]:诚实缺席优先于一个还在报活的假象)。
499
303
  hostLog('debug', `approvalsFeed: poll leg threw unexpectedly (take() should have self-caught): ${String(e)}`);
500
- // 🔴 [F4957-1 二审] 同上:上一代的逃逸不喂现在这条腿的连续计数,也不由它来续排。
501
304
  if (leg !== pollLeg) {
502
305
  hostLog('debug', `approvalsFeed: stale poll completion from leg ${leg} (current ${pollLeg}) — not counted as a consecutive escape`);
503
306
  return;
@@ -514,7 +317,6 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
514
317
  });
515
318
  }, pollIntervalMs));
516
319
  }
517
- /** 0.72.14 对账节拍(见 {@link ApprovalsFeedReconcileOptions})。独立于两条腿:不读 `mode`、不动 F5 计数、不计 `polls`。 */
518
320
  const reconcileBaseMs = Math.max(1, opts?.reconcile?.intervalMs ?? 5000);
519
321
  const reconcileMaxMs = Math.max(reconcileBaseMs, opts?.reconcile?.maxIntervalMs ?? 30_000);
520
322
  let reconcileTimer;
@@ -546,12 +348,12 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
546
348
  if (stopped || ac.signal.aborted)
547
349
  return;
548
350
  if (!reconcileGateOpen()) {
549
- reconcileDelayMs = reconcileBaseMs; // 闸关 ⇒ 下次转开时从基础间隔起
351
+ reconcileDelayMs = reconcileBaseMs;
550
352
  scheduleReconcile();
551
353
  return;
552
354
  }
553
355
  void (async () => {
554
- const before = contentRevision; // 🔴 CC-98:退避判据是**内容**变化,不是发布次数(恢复必发也发布)
356
+ const before = contentRevision;
555
357
  stats.reconciles++;
556
358
  await take('poll');
557
359
  reconcileDelayMs = contentRevision !== before ? reconcileBaseMs : Math.min(reconcileDelayMs * 2, reconcileMaxMs);
@@ -562,11 +364,6 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
562
364
  .then(() => scheduleReconcile());
563
365
  }, reconcileDelayMs));
564
366
  }
565
- /**
566
- * 与 schedulePoll 对称(域词表-18):**幂等卫兵**(retryTimer 已在飞则不再排一个)+ **回调首行
567
- * 自清**(否则 retryTimer 触发后仍持有已死句柄,degradeToPoll 若被再次调用会直接覆盖它而不清掉
568
- * 旧的——旧的照样触发一次 `void runStream()`,两条 push 腿并行 = 双倍取件)。
569
- */
570
367
  function scheduleStreamRetry() {
571
368
  if (stopped || ac.signal.aborted || streamRetryMs <= 0)
572
369
  return;
@@ -582,12 +379,6 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
582
379
  void runStream();
583
380
  }, streamRetryMs));
584
381
  }
585
- /**
586
- * REF-CC-024 ③:`real`(缺省 true)只计**真正的** stream 故障(抛出 / 空转结束)进
587
- * `stats.streamFailures`。「SDK 根本没有 `approvals.stream` 这个动词」不是失败,是能力缺席
588
- * (老 SDK / mock)——此前无条件 `++` 会让这类宿主在 t=0 就报 streamFailures=1,而文件头
589
- * 38-42 正是拿这个数卖「区分『连着但没事发生』与『断了』」,能力缺席被计成故障就说反了。
590
- */
591
382
  function degradeToPoll(why, real = true) {
592
383
  if (stopped)
593
384
  return;
@@ -597,7 +388,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
597
388
  hostLog('debug', `approvalsFeed: push leg unavailable (${why}) — falling back to polling every ${pollIntervalMs}ms`);
598
389
  }
599
390
  mode = 'poll';
600
- schedulePoll(); // 幂等 —— 见 schedulePoll 头注(挂状态不挂迁移)
391
+ schedulePoll();
601
392
  scheduleStreamRetry();
602
393
  }
603
394
  async function runStream() {
@@ -611,22 +402,12 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
611
402
  if (stopped || ac.signal.aborted)
612
403
  return;
613
404
  if (mode !== 'push') {
614
- // 重试成功:停掉轮询腿(两条腿同时跑 = 双倍取件 + 快照抖动)。
615
405
  mode = 'push';
616
406
  if (pollTimer !== undefined) {
617
407
  clearTimeout(pollTimer);
618
408
  pollTimer = undefined;
619
409
  }
620
- // 🔴 [F4957-1](0.41.0):**同批清零 F5 的连续逃逸计数**。这一拍与 `schedulePoll` 里
621
- // 「跑完一整拍 ⇒ 之前的逃逸不再连续」是同等强度的健康证据 —— 端真的从服务端收到了一个
622
- // 事件。不清零的后果不是多算几次:被一整段健康 push 窗**隔开**的两批逃逸会被当成连续
623
- // 累加(3 次 → 重连 → 再 2 次 = 5),于是刚从 push 回落的轮询腿才逃逸两拍就撞上限,
624
- // `mode()` 谎报 idle —— 而文件头 38-42 卖的正是「mode 不是 idle」这条端自检,
625
- // 它会指着一条刚活过来的腿说它停摆了([honest-absence-not-fabricated-zero] 的反面:
626
- // 把「活着」渲成「死了」同样是撒谎)。计数的语义是**连续**,那就只能被真正连续的失败喂大。
627
410
  consecutivePollEscapes = 0;
628
- // 与清零**同一拍**翻代际:清零只管已经落地的计数,代际管的是那些还挂在 `take()` 里、
629
- // 稍后才回写的在途拍(二审 finding③ —— 少了这一半,清零会被一个旧拍当场推回 1)。
630
411
  pollLeg++;
631
412
  hostLog('debug', 'approvalsFeed: push leg re-established — polling leg stopped');
632
413
  }
@@ -635,8 +416,6 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
635
416
  continue;
636
417
  }
637
418
  stats.pushEvents++;
638
- // 🔴 绝不解析 delta 载荷(SDK 自述 FORWARD-DRAFT)—— 任何非 heartbeat 事件都只当
639
- // 「变了」的信号,权威值一律重取 list()。
640
419
  await take('push');
641
420
  }
642
421
  if (!stopped && !ac.signal.aborted)
@@ -648,18 +427,8 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
648
427
  degradeToPoll(String(e));
649
428
  }
650
429
  }
651
- // 首帧之前也要有内容:立刻取一次权威快照(否则端在第一个事件到达前是空白的,
652
- // 而「空白」与「没有 pending」看起来一样)。
653
- //
654
- // 🔴 [2393] hitl-F5(同一条缺陷的**第二个**站点,2026-08-02):catch 必须排在 then **之前**。
655
- // 旧写法是 `take().then(起两条腿).catch(记一行)` —— 首帧 take 逃逸时 `.then` 整个被跳过,
656
- // 于是 `runStream()` / `degradeToPoll()` 一条都没起:feed 在 t=0 就是死的,而 `mode()` 还报着
657
- // 构造期算出来的 'push'/'poll'。把接盘挪到前面,「起腿」这件事就从「首帧成功」这个前提上解耦了
658
- // ——它本来也不该依赖首帧成不成功([paired-mechanisms-must-share-premise])。
659
430
  void take(mode === 'push' ? 'push' : 'poll')
660
431
  .catch(e => {
661
- // REF-CC-024 ①:同款接盘(见 schedulePoll 里那份同款注释)——首帧取件是唯一没被 try/catch
662
- // 包住的调用点之一,这里补上不让它成为 unhandled rejection。
663
432
  hostLog('debug', `approvalsFeed: initial take() threw unexpectedly (take() should have self-caught): ${String(e)}`);
664
433
  })
665
434
  .then(() => {
@@ -1,71 +1,16 @@
1
- /** 登记一个已呈现的决断卡身份键(空/非法输入静默忽略 —— 登记面绝不炸渲染链)。
2
- * 每次调用都发事件(复见重开的第二次呈现也要有回执;Set 成员级当不了事件源)。 */
3
1
  export declare function registerArmedGate(key: string | null | undefined): void;
4
- /** W1 带 key 变体(多会话宿主每会话一键,互不串账)。 */
5
2
  export declare function registerArmedGateFor(sessionKey: string, key: string | null | undefined): void;
6
- /** 该身份键的卡在本进程本会话呈现过吗?(false = 首见;文案分形的唯一判据) */
7
3
  export declare function wasGateArmed(key: string | null | undefined): boolean;
8
- /** W1 带 key 变体。 */
9
4
  export declare function wasGateArmedFor(sessionKey: string, key: string | null | undefined): boolean;
10
- /** 消费一个身份键(plan 族的决断消费请优先走 {@link notePlanReviewAnswered} 族 —— 它同时推代;
11
- * 本口保留给「只清账不推代」的宿主场景与 0.30.0 存量消费方,语义不变)。 */
12
5
  export declare function clearArmedGate(key: string | null | undefined): void;
13
- /**
14
- * 订阅 arm 事件(#269 上收):返回退订钩,调用方**必须**在自己的生命周期末调它(监听器挂在
15
- * 模块级长存表上,漏退 = 闭包泄漏)。
16
- *
17
- * 🔴 为什么不复用 {@link waitForGateArmed}:那是**带窗**的一次性回执(超时即 resolve false),
18
- * 它答的是「看门狗窗内呈现了没有」;#269 的 tool 门臂另外要问一个**无窗**的事实 ——「这条链的
19
- * 整个寿命里,这张卡到底有没有呈现过」。用带窗的那只当事实源,会在「迟到卡」(窗后才入队)上
20
- * 给出 false ⇒ 把一次真呈现误判成「行从未出生」⇒ 回环再铸一张卡,同一个待决项两张可按的卡。
21
- */
22
6
  export declare function onGateArmed(listener: (key: string) => void): () => void;
23
- /** 测试用:缩短回执看门狗(null 复位)。 */
24
7
  export declare function _setGateArmedWaitMsForTest(ms: number | null): void;
25
- /** 回执看门狗现值(重开臂的缺省等待窗)。 */
26
8
  export declare function gateArmedWaitMs(): number;
27
- /**
28
- * 等「这几个键里任意一个被 arm」的**事件**(#250 件1 呈现回执)。resolve true = 回执到手;
29
- * false = 超时(呈现链没走到真入队 —— hook 自动应答/去重丢帧/enqueue 失败/处理器僵死)。
30
- *
31
- * 🔴 timer **不 unref**(包 `abortableSleep` 同判据):调用方(重开臂)正 await 本 promise 收
32
- * verdict —— 回执窗里这只 timer 可能是事件循环里唯一的活,unref 会让 `-p` 车道进程在 await
33
- * 中途直接退出,诚实的 reopen-failed 行一并蒸发。代价上限 = 一次 timeoutMs 的进程存活延长,
34
- * 换判决必达。(浏览器宿主无 unref 概念,裸 setTimeout 两端行为一致 —— portability 零分支。)
35
- */
36
9
  export declare function waitForGateArmed(keys: readonly string[], timeoutMs: number): Promise<boolean>;
37
- /** W1 带 key 变体。 */
38
10
  export declare function waitForGateArmedFor(sessionKey: string, keys: readonly string[], timeoutMs: number): Promise<boolean>;
39
- /** 宿主呈现面登记口:收到可渲染 question 帧即记(键归一见 gateIdentity;坏形静默忽略 ——
40
- * id 是 wire/合成位,入参按边界收 unknown,本函数就是窄化动作本身)。
41
- *
42
- * 尝试级回执(#250 复审 轮二 [medium]):原始帧 id 自带 `#reopen-*` 尝试序号,是现成的
43
- * attempt token —— 归一键之外**事件级**再发一枪原始 id(不入 Set,台账词汇保持归一键)。
44
- * 重开臂锚它,同 gate 两次在飞重开时一次真入队只唤对应那次尝试,不再同键互唤。 */
45
11
  export declare function registerArmedGateFromQuestionId(questionId: unknown): void;
46
- /**
47
- * plan_review 的台账键(当代)。0 代 = `planReviewQuestionId(taskId)`(canonical 原形 ——
48
- * overlay 对**原卡**(done 帧首扎)的登记落进同一键);≥1 代 = 同形 + `#g<N>` 尾。
49
- */
50
12
  export declare function planReviewArmedKey(taskId: string): string;
51
- /** W1 带 key 变体。 */
52
13
  export declare function planReviewArmedKeyFor(sessionKey: string, taskId: string): string;
53
- /**
54
- * 记「一张 plan 问答卡被答掉了」⇒ 当代门已被消费,同 run 的**下一只** plan 门是新实例,落新键。
55
- *
56
- * ── 为什么锚在「答卡」而不是「决断送达」(局限成文,A-024.4)────────────────────────────────
57
- * wire 上没有 gate 实例位,宿主能看见的最晚可靠时刻是作答(送达成败在 responder 返回之后才
58
- * 揭晓)。所以按「答卡」推代。方向代价:决断若终败(重试后仍没送达),门 1 其实还 pending,
59
- * 下一次重开会判成首见 ——「呈上了一张卡」零历史断言,诚实方向安全;反向(不推代)才会编造
60
- * 「被关过」。当代键同时从台账清掉(`clearArmedGate` 的 0.30.0 语义在 0 代逐字保持:决断递交后
61
- * `wasGateArmed(canonical)` 读回 false)。
62
- *
63
- * 🔴 决断性门控请走 {@link notePlanReviewAnsweredIfDecisive}(`planReviewWire`):Esc/空答是
64
- * dismissal 不是决断 —— 那时推代会把「被关过」翻成「首见」,恰是 A-024.4 在最常见路径(Esc
65
- * 关卡)上要保住的区分。本口只做记账,不做分类。
66
- */
67
14
  export declare function notePlanReviewAnswered(questionId: unknown): void;
68
- /** W1 带 key 变体。 */
69
15
  export declare function notePlanReviewAnsweredFor(sessionKey: string, questionId: unknown): void;
70
- /** 测试用:清全部会话的台账(含监听器/代数/答卡记账 —— 半清会让跨用例判决互相污染)。 */
71
16
  export declare function _resetArmedGateRegistryForTest(): void;