@sema-agent/client-core 0.94.0 → 0.96.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 (332) hide show
  1. package/CHANGELOG.md +521 -0
  2. package/README.md +104 -57
  3. package/dist/adapt/arms.js +12 -7
  4. package/dist/adapt/panelTasks.js +4 -10
  5. package/dist/adapt/toolCards.d.ts +1 -0
  6. package/dist/adapt/toolCards.js +18 -5
  7. package/dist/adapt/wireShapes.js +5 -24
  8. package/dist/adapter/activeRunSelfHeal.d.ts +12 -3
  9. package/dist/adapter/activeRunSelfHeal.js +50 -16
  10. package/dist/adapter/downstream/eventToSdkMessage.js +33 -18
  11. package/dist/adapter/downstream/terminalToSdkResult.js +19 -12
  12. package/dist/adapter/runStream.js +30 -46
  13. package/dist/adapter/types.d.ts +7 -0
  14. package/dist/adapter/types.js +15 -0
  15. package/dist/agentSession/backgroundView.js +5 -1
  16. package/dist/agentSession/engineAgentRegistryRead.js +7 -5
  17. package/dist/agentSession/engineSessionBackgroundStop.js +25 -17
  18. package/dist/agentsWireCaps.d.ts +2 -0
  19. package/dist/agentsWireCaps.js +13 -2
  20. package/dist/approvalDecidedByCapability.d.ts +0 -1
  21. package/dist/approvalDecidedByCapability.js +2 -7
  22. package/dist/approvalsStreamLiveCapability.d.ts +0 -1
  23. package/dist/approvalsStreamLiveCapability.js +2 -6
  24. package/dist/atMostOnce.d.ts +8 -0
  25. package/dist/atMostOnce.js +36 -0
  26. package/dist/attachmentSeatCapability.d.ts +0 -1
  27. package/dist/attachmentSeatCapability.js +0 -3
  28. package/dist/attachmentsWireCaps.js +11 -6
  29. package/dist/bashExitReading.d.ts +10 -0
  30. package/dist/bashExitReading.js +12 -0
  31. package/dist/callerCwdCapability.d.ts +0 -5
  32. package/dist/callerCwdCapability.js +2 -13
  33. package/dist/centerWiringCapability.d.ts +1 -7
  34. package/dist/centerWiringCapability.js +18 -27
  35. package/dist/classifierStatus.js +4 -4
  36. package/dist/classifierVerdictWire.js +4 -16
  37. package/dist/clientSlice.js +1 -1
  38. package/dist/cloudConfigWireCaps.d.ts +3 -3
  39. package/dist/cloudConfigWireCaps.js +85 -43
  40. package/dist/compensations.js +1 -1
  41. package/dist/coreValuePorts.d.ts +11 -7
  42. package/dist/coreValuePorts.js +5 -8
  43. package/dist/credentialProvenance.d.ts +28 -0
  44. package/dist/credentialProvenance.js +76 -0
  45. package/dist/cycleOrder.d.ts +15 -0
  46. package/dist/cycleOrder.js +45 -0
  47. package/dist/decideFailureNote.js +7 -2
  48. package/dist/decideReceipt.d.ts +2 -1
  49. package/dist/decideReceipt.js +39 -14
  50. package/dist/declared/supportFloorDeclared.d.ts +1 -0
  51. package/dist/declared/supportFloorDeclared.js +5 -0
  52. package/dist/deviceExecutorManagementCapability.d.ts +0 -1
  53. package/dist/deviceExecutorManagementCapability.js +7 -16
  54. package/dist/deviceRebind.d.ts +88 -0
  55. package/dist/deviceRebind.js +242 -0
  56. package/dist/displayUntrusted.d.ts +8 -3
  57. package/dist/displayUntrusted.js +661 -63
  58. package/dist/engineAgentAbsence.d.ts +2 -2
  59. package/dist/engineAgentPanelStore.d.ts +8 -18
  60. package/dist/engineAgentPanelStore.js +19 -537
  61. package/dist/engineCapReader.d.ts +6 -6
  62. package/dist/engineCapReader.js +30 -43
  63. package/dist/engineCapsCache.d.ts +13 -2
  64. package/dist/engineCapsCache.js +276 -44
  65. package/dist/engineCredentialRequirement.d.ts +9 -0
  66. package/dist/engineCredentialRequirement.js +23 -0
  67. package/dist/engineDirectory.d.ts +79 -0
  68. package/dist/engineDirectory.js +186 -0
  69. package/dist/engineEnvSourcing.d.ts +53 -0
  70. package/dist/engineEnvSourcing.js +81 -0
  71. package/dist/engineErrorCodes.d.ts +28 -4
  72. package/dist/engineErrorCodes.js +68 -25
  73. package/dist/engineIdentity.js +2 -0
  74. package/dist/engineNoticeCodes.d.ts +17 -0
  75. package/dist/engineNoticeCodes.js +57 -3
  76. package/dist/engineWireSdk.js +2 -2
  77. package/dist/engineWordRenames.d.ts +8 -0
  78. package/dist/engineWordRenames.js +13 -0
  79. package/dist/executionLaneCapability.d.ts +2 -3
  80. package/dist/executionLaneCapability.js +18 -23
  81. package/dist/fileHistoryCaptureCapability.d.ts +14 -1
  82. package/dist/fileHistoryCaptureCapability.js +72 -6
  83. package/dist/fileHistoryLegNotice.d.ts +3 -2
  84. package/dist/fileHistoryLegNotice.js +51 -2
  85. package/dist/fleet/bgSubagentRow.d.ts +15 -0
  86. package/dist/fleet/bgSubagentRow.js +67 -0
  87. package/dist/fleet/fleetLedger.d.ts +3 -0
  88. package/dist/fleet/fleetLedger.js +153 -122
  89. package/dist/fleet/fleetProjection.d.ts +1 -3
  90. package/dist/fleet/fleetProjection.js +19 -19
  91. package/dist/fleetAgentPanelProjection.d.ts +4 -17
  92. package/dist/fleetAgentPanelProjection.js +7 -197
  93. package/dist/fleetAgentRowLike.d.ts +16 -0
  94. package/dist/fleetAgentRowLike.js +1 -0
  95. package/dist/fleetTaskDesc.js +4 -17
  96. package/dist/gateOutcome.d.ts +12 -1
  97. package/dist/gateOutcome.js +87 -18
  98. package/dist/gateVocabulary.d.ts +7 -2
  99. package/dist/gateVocabulary.js +5 -1
  100. package/dist/generated/engineAskFaceTables.js +1 -1
  101. package/dist/generated/engineFactTables.d.ts +5 -3
  102. package/dist/generated/engineFactTables.js +17 -4
  103. package/dist/generated/engineNoticeTables.js +12 -3
  104. package/dist/generated/sdkFactTables.d.ts +2 -0
  105. package/dist/generated/sdkFactTables.js +17 -0
  106. package/dist/generated/settingsSchemaFns.d.ts +13 -0
  107. package/dist/generated/settingsSchemaFns.js +75 -0
  108. package/dist/generated/toolNameTables.d.ts +1 -0
  109. package/dist/generated/toolNameTables.js +1 -0
  110. package/dist/headlessPermissionModeWire.d.ts +1 -1
  111. package/dist/headlessPermissionModeWire.js +1 -1
  112. package/dist/headlessReconnectWire.js +49 -20
  113. package/dist/hitl/allowRuleShadow.d.ts +1 -1
  114. package/dist/hitl/allowRuleShadow.js +49 -8
  115. package/dist/hitl/approvalCompleteParked.d.ts +1 -0
  116. package/dist/hitl/approvalCompleteParked.js +14 -0
  117. package/dist/hitl/approvalEndingCause.d.ts +7 -0
  118. package/dist/hitl/approvalEndingCause.js +71 -0
  119. package/dist/hitl/approvalHolds.d.ts +12 -2
  120. package/dist/hitl/approvalHolds.js +53 -2
  121. package/dist/hitl/approvalsFeed.js +23 -11
  122. package/dist/hitl/askGateWire.d.ts +2 -1
  123. package/dist/hitl/askGateWire.js +26 -1
  124. package/dist/hitl/askParkRowRouting.js +1 -1
  125. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  126. package/dist/hitl/automaticAnswerVerdict.js +15 -10
  127. package/dist/hitl/crashConverged.js +2 -0
  128. package/dist/hitl/decisionConflict.d.ts +3 -0
  129. package/dist/hitl/decisionConflict.js +41 -0
  130. package/dist/hitl/decisionRefusal.d.ts +12 -0
  131. package/dist/hitl/decisionRefusal.js +63 -4
  132. package/dist/hitl/frameRouter.d.ts +0 -1
  133. package/dist/hitl/frameRouter.js +32 -57
  134. package/dist/hitl/gateLedger.d.ts +5 -0
  135. package/dist/hitl/gateLedger.js +21 -0
  136. package/dist/hitl/hitlBridge.d.ts +17 -2
  137. package/dist/hitl/hitlBridge.js +91 -55
  138. package/dist/hitl/hitlHostSurface.js +3 -11
  139. package/dist/hitl/livePendingAsk.d.ts +16 -6
  140. package/dist/hitl/livePendingAsk.js +36 -4
  141. package/dist/hitl/localAllowRule.d.ts +1 -0
  142. package/dist/hitl/localAllowRule.js +2 -2
  143. package/dist/hitl/noAllowCause.d.ts +2 -1
  144. package/dist/hitl/noAllowCause.js +3 -2
  145. package/dist/hitl/parkExpiryJudgements.d.ts +19 -0
  146. package/dist/hitl/parkExpiryJudgements.js +46 -0
  147. package/dist/hitl/parkResolver.d.ts +4 -7
  148. package/dist/hitl/parkResolver.js +12 -34
  149. package/dist/hitl/parkRowBirthWait.d.ts +1 -0
  150. package/dist/hitl/parkRowBirthWait.js +2 -2
  151. package/dist/hitl/persistedRulesWire.js +5 -1
  152. package/dist/hitl/planReviewWire.d.ts +3 -2
  153. package/dist/hitl/planReviewWire.js +154 -50
  154. package/dist/hitl/replayedApproval.d.ts +9 -0
  155. package/dist/hitl/replayedApproval.js +139 -10
  156. package/dist/hitl/resumeRunningCard.d.ts +2 -0
  157. package/dist/hitl/resumeRunningCard.js +15 -4
  158. package/dist/hitl/sessionPolicyDeliverable.js +8 -6
  159. package/dist/hitl/standingRows.d.ts +18 -0
  160. package/dist/hitl/standingRows.js +127 -0
  161. package/dist/hitl/toolApprovalWire.d.ts +39 -7
  162. package/dist/hitl/toolApprovalWire.js +351 -61
  163. package/dist/hookMatcherVerdict.d.ts +1 -9
  164. package/dist/hookMatcherVerdict.js +21 -61
  165. package/dist/hooksWireCaps.js +40 -8
  166. package/dist/host.d.ts +5 -0
  167. package/dist/host.js +3 -1
  168. package/dist/hostCallbackGuard.d.ts +2 -0
  169. package/dist/hostCallbackGuard.js +31 -0
  170. package/dist/index.d.ts +48 -17
  171. package/dist/index.js +36 -15
  172. package/dist/interactiveInterrupt.d.ts +8 -0
  173. package/dist/interactiveInterrupt.js +54 -0
  174. package/dist/limitsWire.d.ts +5 -0
  175. package/dist/limitsWire.js +49 -3
  176. package/dist/liveModelCatalog.d.ts +95 -0
  177. package/dist/liveModelCatalog.js +297 -32
  178. package/dist/mcpEngineLeg.d.ts +25 -0
  179. package/dist/mcpEngineLeg.js +71 -15
  180. package/dist/mcpPanel.js +2 -1
  181. package/dist/mcpProbeCapability.d.ts +0 -5
  182. package/dist/mcpProbeCapability.js +2 -12
  183. package/dist/mcpReconnect.d.ts +1 -3
  184. package/dist/mcpReconnect.js +10 -5
  185. package/dist/memoryComplianceCapability.d.ts +0 -5
  186. package/dist/memoryComplianceCapability.js +2 -12
  187. package/dist/memoryOriginCapability.d.ts +0 -5
  188. package/dist/memoryOriginCapability.js +2 -12
  189. package/dist/memorySpecWire.js +2 -2
  190. package/dist/memoryVerbsWire.d.ts +1 -1
  191. package/dist/memoryVerbsWire.js +4 -0
  192. package/dist/model/liveCatalogShape.d.ts +14 -0
  193. package/dist/model/liveCatalogShape.js +71 -0
  194. package/dist/model/modelSupplyRules.d.ts +1 -0
  195. package/dist/model/modelSupplyRules.js +30 -9
  196. package/dist/model/modelsPolicy.d.ts +21 -0
  197. package/dist/model/modelsPolicy.js +90 -0
  198. package/dist/modelIdentityResolvability.d.ts +2 -2
  199. package/dist/modelIdentityResolvability.js +14 -12
  200. package/dist/modelSwitchFallback.d.ts +69 -0
  201. package/dist/modelSwitchFallback.js +206 -0
  202. package/dist/noticePresentation.d.ts +68 -0
  203. package/dist/noticePresentation.js +247 -0
  204. package/dist/notifications.js +12 -11
  205. package/dist/outputStyleSeatCapability.d.ts +0 -2
  206. package/dist/outputStyleSeatCapability.js +2 -10
  207. package/dist/ownKey.d.ts +8 -0
  208. package/dist/ownKey.js +66 -0
  209. package/dist/panelReduce.d.ts +114 -0
  210. package/dist/panelReduce.js +1135 -0
  211. package/dist/panelStateHolder.d.ts +17 -0
  212. package/dist/panelStateHolder.js +201 -0
  213. package/dist/peerFrames.js +13 -9
  214. package/dist/peerLaneCapability.d.ts +0 -1
  215. package/dist/peerLaneCapability.js +2 -6
  216. package/dist/permissionModesCapability.d.ts +0 -5
  217. package/dist/permissionModesCapability.js +5 -16
  218. package/dist/permissionRuleIssue.js +0 -3
  219. package/dist/permissionRulesWriteCapability.d.ts +0 -1
  220. package/dist/permissionRulesWriteCapability.js +2 -6
  221. package/dist/permissionsAllowGrantCapability.d.ts +0 -1
  222. package/dist/permissionsAllowGrantCapability.js +3 -9
  223. package/dist/permissionsDeclaration.d.ts +8 -0
  224. package/dist/permissionsDeclaration.js +101 -41
  225. package/dist/printPermissionOutcome.d.ts +12 -0
  226. package/dist/printPermissionOutcome.js +50 -0
  227. package/dist/printToolResultFrame.d.ts +1 -0
  228. package/dist/printToolResultFrame.js +5 -17
  229. package/dist/promptBlockedCopy.d.ts +5 -0
  230. package/dist/promptBlockedCopy.js +35 -0
  231. package/dist/promptDelivery.js +0 -8
  232. package/dist/protocolSegment.d.ts +9 -6
  233. package/dist/protocolSegment.js +31 -21
  234. package/dist/providerRejection.d.ts +4 -0
  235. package/dist/providerRejection.js +75 -14
  236. package/dist/replayBody.d.ts +12 -0
  237. package/dist/replayBody.js +25 -0
  238. package/dist/resumeRefusalCopy.d.ts +18 -1
  239. package/dist/resumeRefusalCopy.js +127 -32
  240. package/dist/retryStatus.d.ts +9 -1
  241. package/dist/retryStatus.js +9 -2
  242. package/dist/rewindArchiveCapability.d.ts +5 -14
  243. package/dist/rewindArchiveCapability.js +18 -61
  244. package/dist/rewindArchiveReading.d.ts +15 -0
  245. package/dist/rewindArchiveReading.js +56 -0
  246. package/dist/rewindEngineAction.d.ts +21 -0
  247. package/dist/rewindEngineAction.js +36 -0
  248. package/dist/rewindWireCaps.d.ts +2 -1
  249. package/dist/rewindWireCaps.js +1 -0
  250. package/dist/ruleCompileVerdict.d.ts +9 -1
  251. package/dist/ruleCompileVerdict.js +29 -4
  252. package/dist/runTerminal.d.ts +15 -12
  253. package/dist/runTerminal.js +60 -35
  254. package/dist/sandboxWire.js +13 -0
  255. package/dist/sdkRegistryTransit.d.ts +2 -1
  256. package/dist/sdkRegistryTransit.js +1 -0
  257. package/dist/sealedKeyCapability.d.ts +0 -5
  258. package/dist/sealedKeyCapability.js +3 -15
  259. package/dist/seam.js +4 -2
  260. package/dist/seatContract.d.ts +125 -34
  261. package/dist/seatContract.js +583 -179
  262. package/dist/sessionIdSegment.d.ts +1 -0
  263. package/dist/sessionIdSegment.js +11 -0
  264. package/dist/sessionMemoryErase.d.ts +1 -1
  265. package/dist/sessionMemoryErase.js +6 -1
  266. package/dist/sessionMemoryStatus.js +3 -2
  267. package/dist/sessionPolicyCapability.d.ts +0 -1
  268. package/dist/sessionPolicyCapability.js +2 -6
  269. package/dist/sessionReadRedactionCapability.d.ts +0 -5
  270. package/dist/sessionReadRedactionCapability.js +2 -13
  271. package/dist/sessionSyncFileHistory.d.ts +4 -0
  272. package/dist/sessionSyncFileHistory.js +66 -0
  273. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -2
  274. package/dist/sessionSyncFileHistoryCapability.js +2 -9
  275. package/dist/skillToolListsCapability.d.ts +0 -2
  276. package/dist/skillToolListsCapability.js +2 -9
  277. package/dist/skillsWireCaps.d.ts +4 -0
  278. package/dist/skillsWireCaps.js +66 -1
  279. package/dist/sqlEngineCapability.d.ts +0 -1
  280. package/dist/sqlEngineCapability.js +9 -14
  281. package/dist/steerPriorityCapability.d.ts +0 -2
  282. package/dist/steerPriorityCapability.js +3 -12
  283. package/dist/storePostureCapability.d.ts +1 -5
  284. package/dist/storePostureCapability.js +16 -22
  285. package/dist/streamDrainingLegs.d.ts +6 -0
  286. package/dist/streamDrainingLegs.js +23 -0
  287. package/dist/subagent/engineCompactWire.js +13 -38
  288. package/dist/subagent/engineDelegatedPrompt.js +10 -6
  289. package/dist/subagent/engineSubagentOutput.d.ts +0 -1
  290. package/dist/subagent/engineSubagentOutput.js +5 -20
  291. package/dist/subagent/engineSubagentSteer.js +7 -5
  292. package/dist/subagent/engineSubagentTail.d.ts +0 -1
  293. package/dist/subagent/engineSubagentTail.js +7 -32
  294. package/dist/subagentContentStore.js +7 -18
  295. package/dist/supportFloor.d.ts +1 -0
  296. package/dist/supportFloor.js +15 -14
  297. package/dist/systemReminderTag.d.ts +1 -0
  298. package/dist/systemReminderTag.js +8 -0
  299. package/dist/terminalStatuses.d.ts +30 -0
  300. package/dist/terminalStatuses.js +67 -0
  301. package/dist/toolHistoryMismatch.d.ts +0 -3
  302. package/dist/toolHistoryMismatch.js +3 -7
  303. package/dist/toolResult.d.ts +6 -1
  304. package/dist/toolResult.js +90 -37
  305. package/dist/toolResultContent.d.ts +2 -0
  306. package/dist/toolResultContent.js +63 -0
  307. package/dist/types/engineState.d.ts +2 -1
  308. package/dist/usageAnalytics.d.ts +146 -0
  309. package/dist/usageAnalytics.js +335 -0
  310. package/dist/userInvokedSkillsCapability.d.ts +0 -2
  311. package/dist/userInvokedSkillsCapability.js +2 -9
  312. package/dist/webSearchBackendCapability.d.ts +0 -1
  313. package/dist/webSearchBackendCapability.js +3 -7
  314. package/dist/webSearchWireCaps.js +3 -11
  315. package/dist/wireErrorTriage.d.ts +2 -0
  316. package/dist/wireErrorTriage.js +34 -5
  317. package/dist/wireFailureShape.d.ts +23 -2
  318. package/dist/wireFailureShape.js +88 -43
  319. package/dist/wireRefusalCopy.js +2 -18
  320. package/dist/workflowClient.js +7 -11
  321. package/dist/workflowsJournalCapability.d.ts +0 -1
  322. package/dist/workflowsJournalCapability.js +2 -6
  323. package/dist/writeProtectionCapability.d.ts +0 -1
  324. package/dist/writeProtectionCapability.js +5 -10
  325. package/docs/INTEGRATION-CLIENTS.md +2279 -27
  326. package/package.json +5 -5
  327. package/dist/engineCapsGenerationGuard.d.ts +0 -4
  328. package/dist/engineCapsGenerationGuard.js +0 -19
  329. package/dist/panelRunningHistory.d.ts +0 -22
  330. package/dist/panelRunningHistory.js +0 -82
  331. package/dist/registryConflict.d.ts +0 -3
  332. package/dist/registryConflict.js +0 -44
@@ -1,6 +1,7 @@
1
1
  import { putOwnKey, readOwnDataValue } from '../ownKey.js';
2
- import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
3
- import { ASK_GONE, CHECKPOINT_UNSUPPORTED_VERSION, DECIDE_WORKFLOW_PARK_FORKED, DECIDE_WORKFLOW_PARKS_UNREADABLE, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideParkMovedCode, refusalSaysStillPending } from '../engineErrorCodes.js';
2
+ import { DecideTransportRetryExhaustedError, GATE_FAILURE_CODES, HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, hitlSafetyCodeOf, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireCodes, } from './hitlBridge.js';
3
+ import { DECIDE_WORKFLOW_PARK_FORKED, DECIDE_WORKFLOW_PARKS_UNREADABLE, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isRefusedParkCode, refusalSaysStillPending } from '../engineErrorCodes.js';
4
+ import { readDecisionRefusalAsk } from './decisionRefusal.js';
4
5
  import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
5
6
  import { hostLog } from '../host.js';
6
7
  import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
@@ -9,7 +10,7 @@ import { flushHeldWithInterruptRewrite, isAskTool } from './frameRouter.js';
9
10
  import { approvalCallKey, askGateQuestionId } from './gateIdentity.js';
10
11
  import { readDecideReceipt } from '../decideReceipt.js';
11
12
  import { decideFailureNote } from '../decideFailureNote.js';
12
- import { wireFailureTextsOf } from '../wireFailureShape.js';
13
+ import { thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
13
14
  export const MAX_GATE_HOPS = 2;
14
15
  export function nextHopBudget(prev, round) {
15
16
  const total = (prev?.total ?? 0) + 1;
@@ -18,16 +19,6 @@ export function nextHopBudget(prev, round) {
18
19
  const MAX_TRANSPORT_REATTACHES = 2;
19
20
  export const MAX_TOTAL_PARKS = 64;
20
21
  const MAX_ALREADY_RESOLVED_REATTACHES = 1;
21
- export const GATE_FAILURE_CODES = [
22
- 'no_pending',
23
- 'binding_mismatch',
24
- 'wrong_gate',
25
- 'bad_plan_edit',
26
- 'bad_plan_mode',
27
- 'empty_answer',
28
- ];
29
- const _gateFailureCodePins = [true, true];
30
- void _gateFailureCodePins;
31
22
  function isGateFailureCode(v) {
32
23
  return typeof v === 'string' && GATE_FAILURE_CODES.includes(v);
33
24
  }
@@ -41,32 +32,19 @@ function alreadyResolvedReasonToken(reason) {
41
32
  const r = reason.toLowerCase();
42
33
  return ALREADY_RESOLVED_REASON_TOKENS.find((t) => r.includes(t));
43
34
  }
44
- export function isAlreadyResolvedFailure(outcome) {
35
+ export function isAlreadyResolvedFailure(failure) {
45
36
  try {
46
- return alreadyResolvedFailureVerdict(outcome);
37
+ const r = readDecisionRefusalAsk(failure);
38
+ return r.reading === 'settled' || (r.reading === 'not_settled' && (r.gateMoved || r.wordingHint));
47
39
  }
48
40
  catch {
49
41
  return false;
50
42
  }
51
43
  }
52
- function alreadyResolvedFailureVerdict(outcome) {
53
- if (typeof outcome !== 'object' || outcome === null)
54
- return false;
55
- const code = 'code' in outcome && isGateFailureCode(outcome.code) ? outcome.code : undefined;
56
- if (code !== undefined)
57
- return code === 'no_pending';
58
- if ('errorCode' in outcome && isCodeClassifiedGateFailure(outcome.errorCode, Object.hasOwn(outcome, 'retriable') && outcome.retriable === true))
59
- return false;
60
- if ('errorCode' in outcome && isDecideParkMovedCode(outcome.errorCode))
61
- return true;
62
- if ('errorCode' in outcome && outcome.errorCode === ASK_GONE)
63
- return true;
64
- return isAlreadyResolvedGateReason(outcome.reason);
65
- }
66
44
  export function isGateStandingErrorCode(code, retriable) {
67
45
  return (code === RESUME_POLICY_FOLD_CODE ||
68
46
  code === 'corrupt' ||
69
- (code === CHECKPOINT_UNSUPPORTED_VERSION && retriable === true) ||
47
+ (isRefusedParkCode(code) && retriable === true) ||
70
48
  code === DECIDE_WORKFLOW_PARKS_UNREADABLE ||
71
49
  code === DECIDE_WORKFLOW_PARK_FORKED);
72
50
  }
@@ -167,9 +145,9 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
167
145
  return { kind: 'decided', gatedCallId, ...(declinedReceipt !== undefined ? { receipt: declinedReceipt } : {}) };
168
146
  }
169
147
  catch (e) {
170
- const code = e instanceof HitlSafetyError && isGateFailureCode(e.code) ? e.code : undefined;
148
+ const code = hitlSafetyCodeOf(e);
171
149
  const currentPending = readDecideCurrentPending(e);
172
- const wireCode = readWireErrorCode(e);
150
+ const wireCodes = readWireCodes(e);
173
151
  const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
174
152
  const failureTexts = wireFailureTextsOf(e);
175
153
  hostLog('debug', `liveHitlAskWire: decide failed: ${failureTexts.log}`);
@@ -179,9 +157,9 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
179
157
  gatedCallId,
180
158
  reason: `decide failed: ${failureTexts.reason}${decideFailureNote(e, attemptedDecision)}`,
181
159
  ...(code !== undefined ? { code } : {}),
182
- ...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
160
+ ...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
183
161
  ...(currentPending !== undefined ? { currentPending } : {}),
184
- ...(wireCode !== undefined ? { errorCode: wireCode } : {}),
162
+ ...wireCodes,
185
163
  ...(wireRetriable ? { retriable: true } : {}),
186
164
  };
187
165
  }
@@ -35,5 +35,6 @@ export interface ParkRowBirthWaitDeps<Row> {
35
35
  now?: () => number;
36
36
  sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
37
37
  signal?: AbortSignal;
38
+ onFailure?: (log: string) => void;
38
39
  }
39
40
  export declare function waitForParkRowBirth<Row>(deps: ParkRowBirthWaitDeps<Row>): Promise<ParkRowWaitVerdict<Row>>;
@@ -1,5 +1,5 @@
1
1
  import { abortableSleep } from '../abortableSleep.js';
2
- import { wireFailureTextsOf } from '../wireFailureShape.js';
2
+ import { reasonOfThrownWithSink } from '../hostCallbackGuard.js';
3
3
  function parkRowPollDelay(ms, signal) {
4
4
  return abortableSleep(ms, signal ?? new AbortController().signal);
5
5
  }
@@ -17,7 +17,7 @@ export async function waitForParkRowBirth(deps) {
17
17
  return await deps.probe(attempt, signal);
18
18
  }
19
19
  catch (e) {
20
- return { kind: 'unborn', reason: `probe threw: ${wireFailureTextsOf(e).reason}` };
20
+ return { kind: 'unborn', reason: `probe threw: ${reasonOfThrownWithSink(e, (log) => deps.onFailure?.(log))}` };
21
21
  }
22
22
  };
23
23
  const probeWithinRemaining = async (attempt, remainingMs) => {
@@ -66,7 +66,11 @@ export function classifyRulesFailure(e, context) {
66
66
  catch {
67
67
  cursorSent = undefined;
68
68
  }
69
- return cursorSent === false ? { kind: 'query-refused', message } : { kind: 'cursor-stale', message };
69
+ if (cursorSent === true)
70
+ return { kind: 'cursor-stale', message };
71
+ if (cursorSent === false)
72
+ return { kind: 'query-refused', message };
73
+ return { kind: 'error', message };
70
74
  }
71
75
  if (status === 400 && errorCode === 'request.field_invalid')
72
76
  return { kind: 'field-refused', message };
@@ -12,6 +12,7 @@ export type PlanReviewChoice = {
12
12
  permissionModeAfter?: PlanReviewModeAfter;
13
13
  } | {
14
14
  decision: 'reject';
15
+ reason?: string;
15
16
  };
16
17
  export declare function planReviewChoiceFromAnswer(answer: QuestionAnswer | null | undefined, shownLabels?: readonly string[]): PlanReviewChoice | 'dismissed';
17
18
  export declare function planReviewCardOptions(offerModeAfter: boolean): Array<{
@@ -61,6 +62,7 @@ export interface PlanReviewDeliveryOptions {
61
62
  }
62
63
  export interface DecidePlanReviewOptions extends PlanReviewDeliveryOptions {
63
64
  reason?: string;
65
+ parkedResult?: unknown;
64
66
  }
65
67
  export interface PlanReviewDecisionHandle {
66
68
  settle(): void;
@@ -76,14 +78,13 @@ export declare function __resetPlanReviewDecisionLatchForTests(): void;
76
78
  export declare function decidePlanReview(taskId: string, decision: 'approve' | 'reject', permissionModeAfter?: string, opts?: DecidePlanReviewOptions): Promise<void>;
77
79
  export interface ReopenPlanReviewOpts {
78
80
  mintFreshQuestionId?: boolean;
79
- deliverDecision?: (taskId: string, decision: 'approve' | 'reject') => void | Promise<void>;
81
+ deliverDecision?: (taskId: string, decision: 'approve' | 'reject', reason?: string) => void | Promise<void>;
80
82
  sessionKey?: string;
81
83
  wire?: PlanReviewWire;
82
84
  onOutcome?: PlanReviewDeliveryOptions['onOutcome'];
83
85
  presentationReceiptMs?: number;
84
86
  trigger?: PlanReviewReopenTrigger;
85
87
  readGateInstanceKeys?: PlanReviewGateInstanceReader;
86
- decisionHandedOver?: (taskId: string) => boolean;
87
88
  pendingReadStartedAt?: number;
88
89
  }
89
90
  export declare function _resetActiveReopenRespondersForTest(): void;
@@ -13,7 +13,7 @@ import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
13
13
  import { planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
14
14
  import { notePlanReviewAnswered, notePlanReviewAnsweredFor, planReviewArmedKeyFor, registerArmedGateFor, waitForGateArmedFor, wasGateArmedFor, } from './armedGateRegistry.js';
15
15
  import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
16
- import { PLAN_REVIEW_GATE_KIND } from '../adapter/activeRunSelfHeal.js';
16
+ import { PLAN_REVIEW_GATE_KIND, durableDecideLegOf } from '../adapter/activeRunSelfHeal.js';
17
17
  import { readRunTerminal, runTerminalGateKind } from '../runTerminal.js';
18
18
  const PLAN_REVIEW_RESUME_TIMEOUT_MS = 6 * 60 * 60_000;
19
19
  export const PLAN_REVIEW_APPROVE_LABEL = 'Yes, approve and run the plan';
@@ -29,31 +29,59 @@ const ALL_KNOWN_LABELS = Object.freeze([
29
29
  PLAN_REVIEW_APPROVE_LABEL,
30
30
  PLAN_REVIEW_REJECT_LABEL,
31
31
  ]);
32
- export function planReviewDecisionFromAnswer(answer, shownLabels) {
33
- const choice = planReviewChoiceFromAnswer(answer, shownLabels);
32
+ export function planReviewDecisionFromAnswer(answer, shownLabels = LEGACY_TWO_CHOICE_LABELS) {
33
+ const { choice } = readPlanReviewAnswer(answer, shownLabels);
34
34
  if (choice === 'dismissed')
35
35
  return 'dismissed';
36
36
  return choice.decision;
37
37
  }
38
- export function planReviewChoiceFromAnswer(answer, shownLabels = LEGACY_TWO_CHOICE_LABELS) {
38
+ function readDismissed() {
39
+ return { choice: 'dismissed', noteIgnoredOnApprove: false };
40
+ }
41
+ function freeTextNoteOf(entry) {
42
+ try {
43
+ const note = entry?.note;
44
+ return typeof note === 'string' && note.trim() !== '' ? note : undefined;
45
+ }
46
+ catch {
47
+ return undefined;
48
+ }
49
+ }
50
+ function readPlanReviewAnswer(answer, shownLabels) {
39
51
  const answers = answer?.answers;
40
52
  if (!Array.isArray(answers) || answers.length !== 1)
41
- return 'dismissed';
42
- const picked = answers[0]?.selected;
43
- if (!Array.isArray(picked) || picked.length !== 1)
44
- return 'dismissed';
53
+ return readDismissed();
54
+ const entry = answers[0];
55
+ const picked = entry?.selected;
56
+ if (!Array.isArray(picked))
57
+ return readDismissed();
58
+ const note = freeTextNoteOf(entry);
59
+ if (picked.length === 0) {
60
+ if (note === undefined || !shownLabels.includes(REJECT_LABEL))
61
+ return readDismissed();
62
+ return { choice: { decision: 'reject', reason: note }, noteIgnoredOnApprove: false };
63
+ }
64
+ if (picked.length !== 1)
65
+ return readDismissed();
45
66
  const selected = picked[0];
46
67
  if (typeof selected !== 'string' || !shownLabels.includes(selected))
47
- return 'dismissed';
68
+ return readDismissed();
69
+ const ignored = note !== undefined;
48
70
  if (selected === PLAN_REVIEW_APPROVE_AUTO_LABEL)
49
- return { decision: 'approve', permissionModeAfter: 'acceptEdits' };
71
+ return { choice: { decision: 'approve', permissionModeAfter: 'acceptEdits' }, noteIgnoredOnApprove: ignored };
50
72
  if (selected === PLAN_REVIEW_APPROVE_MANUAL_LABEL)
51
- return { decision: 'approve', permissionModeAfter: 'default' };
73
+ return { choice: { decision: 'approve', permissionModeAfter: 'default' }, noteIgnoredOnApprove: ignored };
52
74
  if (selected === APPROVE_LABEL)
53
- return { decision: 'approve' };
75
+ return { choice: { decision: 'approve' }, noteIgnoredOnApprove: ignored };
54
76
  if (selected === REJECT_LABEL)
55
- return { decision: 'reject' };
56
- return 'dismissed';
77
+ return { choice: note === undefined ? { decision: 'reject' } : { decision: 'reject', reason: note }, noteIgnoredOnApprove: false };
78
+ return readDismissed();
79
+ }
80
+ export function planReviewChoiceFromAnswer(answer, shownLabels = LEGACY_TWO_CHOICE_LABELS) {
81
+ const { choice, noteIgnoredOnApprove } = readPlanReviewAnswer(answer, shownLabels);
82
+ if (noteIgnoredOnApprove)
83
+ hostLog('debug', 'planReviewWire: note ignored on approve — an approval carries no feedback text; the note was not sent');
84
+ return choice;
57
85
  }
58
86
  export function planReviewCardOptions(offerModeAfter) {
59
87
  const reject = { label: REJECT_LABEL, description: 'Discard this plan and stay in plan mode to refine it' };
@@ -102,6 +130,18 @@ function pausedCheckpointIdOf(carrier) {
102
130
  return undefined;
103
131
  }
104
132
  }
133
+ function pausedGateKindOf(carrier) {
134
+ try {
135
+ const read = readRunTerminal(carrier);
136
+ if (read === null || read.kind !== 'paused')
137
+ return undefined;
138
+ const kind = runTerminalGateKind(read);
139
+ return typeof kind === 'string' && kind.length > 0 ? kind : undefined;
140
+ }
141
+ catch {
142
+ return undefined;
143
+ }
144
+ }
105
145
  const armedPlanReviewTaskIds = new Set();
106
146
  const offerModeAfterByTask = new Map();
107
147
  export function _resetArmedPlanReviewsForTest() {
@@ -270,10 +310,12 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
270
310
  const decided = choice.decision;
271
311
  notePlanReviewDecisiveAnswerFor(DEFAULT_SESSION_KEY, taskId);
272
312
  const modeAfter = choice.decision === 'approve' ? choice.permissionModeAfter : undefined;
313
+ const cardReason = choice.decision === 'reject' ? choice.reason : undefined;
314
+ const cardDelivery = cardReason !== undefined ? { ...delivery, reason: cardReason } : delivery;
273
315
  offerModeAfterByTask.delete(taskId);
274
316
  notePlanReviewAnswered(questionId);
275
317
  hostLog('debug', `planReviewWire: arm responder delivering ${decided} for task ${taskId} (path=arm)`);
276
- void decidePlanReviewFor(taskId, decided, modeAfter, delivery, gateBefore);
318
+ void decidePlanReviewFor(taskId, decided, modeAfter, cardDelivery, gateBefore);
277
319
  return { ok: true };
278
320
  });
279
321
  publishPlanReviewCard(taskId);
@@ -440,7 +482,53 @@ export function __resetPlanReviewDecisionLatchForTests() {
440
482
  planReviewDecisionOps.length = 0;
441
483
  }
442
484
  export async function decidePlanReview(taskId, decision, permissionModeAfter, opts) {
443
- return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, undefined);
485
+ return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, parkedGateIdentityOf(opts));
486
+ }
487
+ function parkedGateIdentityOf(opts) {
488
+ try {
489
+ return opts !== null && typeof opts === 'object' ? pausedCheckpointIdOf(opts.parkedResult) : undefined;
490
+ }
491
+ catch {
492
+ return undefined;
493
+ }
494
+ }
495
+ function planReviewSettledVerdict(r) {
496
+ const status = r.postStatus ?? r.bodyStatus;
497
+ const gateMoved = r.gateBefore !== undefined && r.gateAfter !== undefined && r.gateAfter !== r.gateBefore;
498
+ if (r.reparked)
499
+ return status !== 'completed' && !gateMoved ? { effect: 'still_parked', proven: true } : { effect: 'unconfirmed', proven: false };
500
+ if (status === 'completed')
501
+ return { effect: 'took_effect', proven: false };
502
+ const newGateHasCard = () => {
503
+ const leg = durableDecideLegOf({ gateKind: r.gateKindAfter ?? null });
504
+ return leg === 'approval' || leg === 'plan_review';
505
+ };
506
+ if (status === 'suspended' && r.postStatus === 'suspended' && newGateHasCard())
507
+ return { effect: 'advanced', proven: false };
508
+ if (status === 'needs_review' && r.postStatus === 'needs_review' && r.gateBefore !== undefined && r.gateAfter !== undefined) {
509
+ if (r.gateAfter === r.gateBefore)
510
+ return { effect: 'still_parked', proven: true };
511
+ if (newGateHasCard())
512
+ return { effect: 'advanced', proven: false };
513
+ }
514
+ return { effect: 'unconfirmed', proven: false };
515
+ }
516
+ function bodyWordOf(body, key) {
517
+ try {
518
+ const v = body !== null && typeof body === 'object' ? body[key] : undefined;
519
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
520
+ }
521
+ catch {
522
+ return undefined;
523
+ }
524
+ }
525
+ function bodySaysReparked(body) {
526
+ try {
527
+ return body !== null && typeof body === 'object' && body.retriable === true;
528
+ }
529
+ catch {
530
+ return false;
531
+ }
444
532
  }
445
533
  async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, gateBefore) {
446
534
  if (typeof taskId === 'string' && planReviewDecisionOps.some((op) => op.taskId === taskId && op.phase === 'posting')) {
@@ -556,6 +644,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
556
644
  hostLog('debug', `planReviewWire: ${decision} → ok (dispatch #${dispatchNo}) ${JSON.stringify(body).slice(0, 200)}`);
557
645
  let postStatus;
558
646
  let gateAfter;
647
+ let gateKindAfter;
559
648
  try {
560
649
  const probe = clients.repull();
561
650
  if (probe) {
@@ -571,35 +660,54 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
571
660
  else {
572
661
  post = (await probe.runs.get(taskId));
573
662
  }
574
- postStatus = typeof post?.status === 'string' ? post.status : undefined;
663
+ postStatus = bodyWordOf(post, 'status');
575
664
  gateAfter = pausedCheckpointIdOf(post?.result);
665
+ gateKindAfter = pausedGateKindOf(post?.result);
576
666
  }
577
667
  }
578
668
  catch (e) {
579
669
  hostLog('debug', `planReviewWire: post-decide status re-pull failed (fail-soft): ${String(e)}`);
580
670
  }
581
- const effective = postStatus ?? (typeof body?.status === 'string' ? body.status : undefined);
671
+ const bodyStatus = bodyWordOf(body, 'status');
672
+ const effective = postStatus ?? bodyStatus;
582
673
  statusSeen = effective;
583
- const gateMoved = postStatus === 'needs_review' && gateBefore !== undefined && gateAfter !== undefined && gateAfter !== gateBefore;
584
- if (effective === 'needs_review' && gateMoved) {
585
- effect = 'advanced';
586
- outcome = planReviewAdvancedOutcome(decision, 'needs_review');
587
- }
588
- else if (effective === 'needs_review') {
589
- effect = 'still_parked';
590
- stillParkedProven = postStatus === 'needs_review' && gateBefore !== undefined && gateAfter !== undefined && gateAfter === gateBefore;
674
+ const verdict = planReviewSettledVerdict({ postStatus, bodyStatus, reparked: bodySaysReparked(body), gateBefore, gateAfter, gateKindAfter });
675
+ effect = verdict.effect;
676
+ stillParkedProven = verdict.proven;
677
+ const code = bodyWordOf(body, 'errorCode');
678
+ if (effect === 'still_parked' && postStatus === 'needs_review' && !bodySaysReparked(body)) {
591
679
  outcome =
592
680
  `The plan_review ${decision} returned HTTP 200 but the session is STILL locked on the same review gate ` +
593
681
  `(post-decide status: needs_review). The decision did NOT take effect (known engine issue, RB-471 family). ` +
594
682
  `Tell the user plainly that the ${decision} did not go through; the reliable exits today are approving the plan or cancelling the task.`;
595
683
  }
596
- else if (effective === 'suspended') {
597
- effect = 'advanced';
598
- outcome = planReviewAdvancedOutcome(decision, 'suspended');
684
+ else if (effect === 'still_parked') {
685
+ outcome =
686
+ `The plan_review ${decision} was NOT applied: the engine could not continue the task and put the plan back to wait for a decision` +
687
+ `${code !== undefined ? ` (engine code: ${code})` : ''}. Tell the user plainly that the ${decision} did not go through.`;
688
+ }
689
+ else if (effect === 'advanced') {
690
+ outcome = planReviewAdvancedOutcome(decision, effective === 'needs_review' ? 'needs_review' : 'suspended');
691
+ }
692
+ else if (effect === 'took_effect') {
693
+ outcome =
694
+ `The plan was ${decision === 'approve' ? 'approved and the parked task resumed to completion' : 'rejected (plan discarded)'} — final status: ${effective} ` +
695
+ `(${postStatus !== undefined ? 'post-decide re-checked: task left the review gate' : "from the decision's own reply; the post-decide re-check was unavailable"}).`;
696
+ }
697
+ else if (effective === 'needs_review' && (gateBefore === undefined || gateAfter === undefined || postStatus !== 'needs_review')) {
698
+ outcome =
699
+ `The plan_review ${decision} returned HTTP 200 and the task is waiting at a plan review gate again (post-decide status: needs_review), ` +
700
+ `but it could not be told whether this is the same review (the ${decision} did not take effect) or a new plan written after it (it did).`;
701
+ }
702
+ else if (effective === 'suspended' || effective === 'needs_review') {
703
+ outcome =
704
+ `The plan_review ${decision} was accepted by the engine (HTTP 200) and the task is paused again (post-decide status: ${effective}), ` +
705
+ `${gateKindAfter !== undefined ? `at a '${gateKindAfter}' pause that is not an approval card` : 'but the pause it stopped at was not reported'} — what the ${decision} led to could not be confirmed.`;
599
706
  }
600
707
  else if (effective !== undefined) {
601
- effect = 'took_effect';
602
- outcome = `The plan was ${decision === 'approve' ? 'approved and the parked task resumed to completion' : 'rejected (plan discarded)'} — final status: ${effective} (post-decide re-checked: task left the review gate).`;
708
+ outcome =
709
+ `The plan_review ${decision} was accepted by the engine (HTTP 200), but the task then reported status ${effective}` +
710
+ `${code !== undefined ? ` (engine code: ${code})` : ''} — not a completion; what the ${decision} led to could not be confirmed.`;
603
711
  }
604
712
  else {
605
713
  outcome = `The plan_review ${decision} was accepted by the engine (HTTP 200) but its effect is unconfirmed — final status: unknown (post-decide verification unavailable). Do not treat this as ${decision === 'approve' ? 'an approval that resumed the task' : 'a discarded plan'}.`;
@@ -648,10 +756,10 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
648
756
  outcome = `This ${decision} was not applied. ${corruptStoredRowContent(where)}`;
649
757
  }
650
758
  else if (typeof status === 'number') {
651
- const msg = e instanceof Error ? e.message : String(e);
652
- hostLog('debug', `planReviewWire: ${decision} → ${status} ${msg.slice(0, 200)}`);
759
+ const failureTexts = wireFailureTextsOf(e);
760
+ hostLog('debug', `planReviewWire: ${decision} → ${status} ${failureTexts.log.slice(0, 200)}`);
653
761
  effect = (status >= 400 && status < 500) || (typeof refusedCode === 'string' && isUnavailableFaceCode(refusedCode)) ? 'not_applied' : 'unconfirmed';
654
- outcome = `${`The plan_review decision failed: HTTP ${status} ${msg}`.trim()}${decideFailureNote(e)}`;
762
+ outcome = `${`The plan_review decision failed: HTTP ${status} ${failureTexts.reason}`.trim()}${decideFailureNote(e)}`;
655
763
  }
656
764
  else if (noAnswer === 'time-limit' || noAnswer === 'deadline') {
657
765
  hostLog('debug', `planReviewWire: ${decision} sent but no answer within the request time limit (task ${taskId})`);
@@ -821,9 +929,10 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs) {
821
929
  ? { ...(opts.wire !== undefined ? { wire: opts.wire } : {}), ...(opts.onOutcome !== undefined ? { onOutcome: opts.onOutcome } : {}) }
822
930
  : undefined;
823
931
  const deliver = opts?.deliverDecision ??
824
- ((tid, decision) => {
932
+ ((tid, decision, reason) => {
825
933
  hostLog('debug', `planReviewWire: reopen responder delivering ${decision} for task ${tid} (path=reopen-default)`);
826
- void (delivery === undefined ? decidePlanReview(tid, decision) : decidePlanReview(tid, decision, undefined, delivery));
934
+ const withReason = reason === undefined ? delivery : { ...delivery, reason };
935
+ void (withReason === undefined ? decidePlanReview(tid, decision) : decidePlanReview(tid, decision, undefined, withReason));
827
936
  });
828
937
  const activeKey = `${sessionKey}\u0000${canonicalId}`;
829
938
  const unregister = registerLocalQuestionResponder(questionId, async (_id, answer) => {
@@ -831,15 +940,20 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs) {
831
940
  if (activeReopenResponders.get(activeKey)?.questionId === questionId) {
832
941
  activeReopenResponders.delete(activeKey);
833
942
  }
834
- const decided = planReviewDecisionFromAnswer(answer, LEGACY_TWO_CHOICE_LABELS);
835
- if (decided === 'dismissed') {
943
+ const choice = planReviewChoiceFromAnswer(answer, LEGACY_TWO_CHOICE_LABELS);
944
+ if (choice === 'dismissed') {
836
945
  notePlanReviewUserDismissalFor(sessionKey, taskId, opts?.readGateInstanceKeys, questionId);
837
946
  return { ok: true };
838
947
  }
948
+ const decided = choice.decision;
949
+ const cardReason = choice.decision === 'reject' ? choice.reason : undefined;
839
950
  notePlanReviewDecisiveAnswerFor(sessionKey, taskId);
840
951
  notePlanReviewAnsweredFor(sessionKey, questionId);
841
952
  try {
842
- await deliver(taskId, decided);
953
+ if (cardReason === undefined)
954
+ await deliver(taskId, decided);
955
+ else
956
+ await deliver(taskId, decided, cardReason);
843
957
  }
844
958
  catch (e) {
845
959
  hostLog('error', `planReviewWire: reopen decision delivery threw for task ${taskId}: ${String(e)}`);
@@ -887,18 +1001,8 @@ async function reopenAfterDismissalCheck(taskId, opts, sessionKey, receiptMs, ma
887
1001
  }
888
1002
  }
889
1003
  }
890
- let handedOver = false;
891
1004
  const ledgerSaysInFlight = planReviewDecisionInFlight(taskId, recheckReadStart);
892
- if (!ledgerSaysInFlight) {
893
- try {
894
- handedOver = opts.decisionHandedOver?.(taskId) === true;
895
- }
896
- catch (err) {
897
- hostLog('debug', `planReviewWire: reopen for task ${taskId} refused — the host's decision hand-over check threw: ${String(err)}`);
898
- return { reopened: false };
899
- }
900
- }
901
- if (ledgerSaysInFlight || handedOver) {
1005
+ if (ledgerSaysInFlight) {
902
1006
  hostLog('debug', `planReviewWire: reopen for task ${taskId} refused after the dismissal check — a decision for this task went out while the check was reading`);
903
1007
  return { reopened: false, _sema_decisionInFlight: true };
904
1008
  }
@@ -1,3 +1,4 @@
1
+ import { type ApprovalSettledHow } from './gateLedger.js';
1
2
  import { type ApprovalsListEnvelope, type CrashConvergedRow } from './crashConverged.js';
2
3
  export type ReplayedApprovalDisposition = {
3
4
  readonly kind: 'live';
@@ -5,6 +6,8 @@ export type ReplayedApprovalDisposition = {
5
6
  } | {
6
7
  readonly kind: 'decided';
7
8
  readonly cause?: CrashConvergedRow['cause'];
9
+ readonly needsHumanReason?: NonNullable<CrashConvergedRow['needsHumanReason']>;
10
+ readonly settled?: ApprovalSettledHow;
8
11
  } | {
9
12
  readonly kind: 'unknown';
10
13
  readonly why: 'unreadable_frame' | 'no_approval_id' | 'not_read' | 'list_unreadable' | 'list_not_reported' | 'list_incomplete';
@@ -17,5 +20,11 @@ export interface ReplayPendingBasis {
17
20
  } | {
18
21
  readonly read: 'failed';
19
22
  };
23
+ readonly settled?: ReadonlyMap<string, ApprovalSettledHow>;
20
24
  }
25
+ export declare function settledApprovalDisposition(frame: object, settled: ReplayPendingBasis['settled']): ReplayedApprovalDisposition | undefined;
21
26
  export declare function replayedApprovalDisposition(frame: object, basis: ReplayPendingBasis): ReplayedApprovalDisposition;
27
+ export declare function replayedApprovalClosesCardFrame(disposition: ReplayedApprovalDisposition | null | undefined): boolean;
28
+ export declare function replayedApprovalSentence(disposition: ReplayedApprovalDisposition | undefined, opts?: {
29
+ readonly toolName?: string;
30
+ }): string | undefined;