@sema-agent/client-core 0.93.1 → 0.95.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 (321) hide show
  1. package/CHANGELOG.md +554 -0
  2. package/README.md +89 -55
  3. package/dist/adapt/arms.js +13 -11
  4. package/dist/adapt/panelTasks.js +4 -10
  5. package/dist/adapt/toolCards.js +2 -2
  6. package/dist/adapt/wireShapes.js +3 -8
  7. package/dist/adapter/activeRunSelfHeal.d.ts +8 -3
  8. package/dist/adapter/activeRunSelfHeal.js +20 -12
  9. package/dist/adapter/downstream/eventToSdkMessage.js +24 -17
  10. package/dist/adapter/downstream/terminalToSdkResult.js +13 -6
  11. package/dist/adapter/runStream.js +2 -1
  12. package/dist/adapter/types.d.ts +7 -0
  13. package/dist/adapter/types.js +15 -0
  14. package/dist/agentSession/backgroundView.js +5 -1
  15. package/dist/agentSession/engineAgentRegistryRead.js +2 -1
  16. package/dist/agentSession/engineSessionBackgroundStop.js +5 -3
  17. package/dist/agentsWireCaps.d.ts +17 -0
  18. package/dist/agentsWireCaps.js +111 -1
  19. package/dist/approvalDecidedByCapability.d.ts +0 -1
  20. package/dist/approvalDecidedByCapability.js +2 -7
  21. package/dist/approvalReachWire.d.ts +18 -0
  22. package/dist/approvalReachWire.js +58 -0
  23. package/dist/approvalsStreamLiveCapability.d.ts +0 -1
  24. package/dist/approvalsStreamLiveCapability.js +15 -14
  25. package/dist/atMostOnce.d.ts +8 -0
  26. package/dist/atMostOnce.js +36 -0
  27. package/dist/attachmentSeatCapability.d.ts +0 -1
  28. package/dist/attachmentSeatCapability.js +0 -3
  29. package/dist/attachmentsWireCaps.js +11 -6
  30. package/dist/bashExitReading.d.ts +10 -0
  31. package/dist/bashExitReading.js +12 -0
  32. package/dist/callerCwdCapability.d.ts +15 -0
  33. package/dist/callerCwdCapability.js +42 -0
  34. package/dist/centerWiringCapability.d.ts +1 -3
  35. package/dist/centerWiringCapability.js +18 -21
  36. package/dist/classifierStatus.js +4 -4
  37. package/dist/cloudConfigWireCaps.js +43 -21
  38. package/dist/compensations.js +1 -1
  39. package/dist/controlRouter.js +3 -10
  40. package/dist/coreValuePorts.d.ts +64 -1
  41. package/dist/coreValuePorts.js +30 -0
  42. package/dist/cycleOrder.d.ts +15 -0
  43. package/dist/cycleOrder.js +45 -0
  44. package/dist/decideFailureNote.js +4 -1
  45. package/dist/decideReceipt.d.ts +2 -1
  46. package/dist/decideReceipt.js +41 -13
  47. package/dist/declared/supportFloorDeclared.d.ts +1 -0
  48. package/dist/declared/supportFloorDeclared.js +5 -0
  49. package/dist/detachWire.d.ts +1 -4
  50. package/dist/detachWire.js +1 -1
  51. package/dist/deviceExecutorManagementCapability.d.ts +0 -1
  52. package/dist/deviceExecutorManagementCapability.js +36 -40
  53. package/dist/deviceFlowPolling.d.ts +5 -0
  54. package/dist/deviceFlowPolling.js +28 -0
  55. package/dist/deviceRebind.d.ts +88 -0
  56. package/dist/deviceRebind.js +242 -0
  57. package/dist/displayUntrusted.d.ts +1 -0
  58. package/dist/displayUntrusted.js +963 -30
  59. package/dist/engineAgentAbsence.d.ts +2 -2
  60. package/dist/engineAgentPanelStore.d.ts +8 -18
  61. package/dist/engineAgentPanelStore.js +19 -537
  62. package/dist/engineCapReader.d.ts +6 -6
  63. package/dist/engineCapReader.js +30 -43
  64. package/dist/engineCapsCache.d.ts +7 -2
  65. package/dist/engineCapsCache.js +64 -26
  66. package/dist/engineCredentialRequirement.d.ts +9 -0
  67. package/dist/engineCredentialRequirement.js +23 -0
  68. package/dist/engineEnvSourcing.d.ts +53 -0
  69. package/dist/engineEnvSourcing.js +81 -0
  70. package/dist/engineErrorCodes.d.ts +19 -4
  71. package/dist/engineErrorCodes.js +39 -21
  72. package/dist/engineIdentity.js +2 -0
  73. package/dist/engineNoticeCodes.d.ts +22 -2
  74. package/dist/engineNoticeCodes.js +73 -6
  75. package/dist/engineRefusedToolName.d.ts +1 -0
  76. package/dist/engineRefusedToolName.js +8 -0
  77. package/dist/engineWireSdk.js +2 -2
  78. package/dist/executionLaneCapability.d.ts +2 -3
  79. package/dist/executionLaneCapability.js +28 -28
  80. package/dist/fileHistoryCaptureCapability.d.ts +0 -1
  81. package/dist/fileHistoryCaptureCapability.js +13 -12
  82. package/dist/fileHistoryLegNotice.d.ts +14 -2
  83. package/dist/fileHistoryLegNotice.js +101 -15
  84. package/dist/fleet/fleetLedger.d.ts +2 -0
  85. package/dist/fleet/fleetLedger.js +74 -52
  86. package/dist/fleet/fleetProjection.d.ts +0 -3
  87. package/dist/fleet/fleetProjection.js +9 -16
  88. package/dist/fleetAgentPanelProjection.d.ts +4 -17
  89. package/dist/fleetAgentPanelProjection.js +7 -197
  90. package/dist/fleetAgentRowLike.d.ts +16 -0
  91. package/dist/fleetAgentRowLike.js +1 -0
  92. package/dist/gateOutcome.d.ts +2 -1
  93. package/dist/gateOutcome.js +33 -18
  94. package/dist/gateVocabulary.js +1 -1
  95. package/dist/generated/engineAskFaceTables.d.ts +3 -0
  96. package/dist/generated/engineAskFaceTables.js +12 -0
  97. package/dist/generated/engineFactTables.d.ts +4 -2
  98. package/dist/generated/engineFactTables.js +13 -0
  99. package/dist/generated/engineNoticeTables.d.ts +5 -0
  100. package/dist/generated/engineNoticeTables.js +7 -0
  101. package/dist/generated/sdkFactTables.d.ts +5 -0
  102. package/dist/generated/sdkFactTables.js +34 -0
  103. package/dist/generated/settingsSchemaFns.d.ts +10 -0
  104. package/dist/generated/settingsSchemaFns.js +22 -0
  105. package/dist/generated/settingsSchemaTables.d.ts +3 -0
  106. package/dist/generated/settingsSchemaTables.js +3 -0
  107. package/dist/headlessPermissionModeWire.d.ts +2 -2
  108. package/dist/headlessPermissionModeWire.js +11 -21
  109. package/dist/headlessReconnectWire.js +49 -20
  110. package/dist/hitl/allowRuleShadow.d.ts +1 -1
  111. package/dist/hitl/allowRuleShadow.js +49 -8
  112. package/dist/hitl/approvalCompleteParked.d.ts +1 -0
  113. package/dist/hitl/approvalCompleteParked.js +14 -0
  114. package/dist/hitl/approvalEndingCause.d.ts +5 -0
  115. package/dist/hitl/approvalEndingCause.js +61 -0
  116. package/dist/hitl/approvalHolds.d.ts +46 -0
  117. package/dist/hitl/approvalHolds.js +140 -0
  118. package/dist/hitl/approvalOutcomeNote.js +2 -1
  119. package/dist/hitl/approvalResolution.js +9 -1
  120. package/dist/hitl/approvalsFeed.js +23 -6
  121. package/dist/hitl/askGateWire.d.ts +2 -1
  122. package/dist/hitl/askGateWire.js +29 -1
  123. package/dist/hitl/automaticAnswerVerdict.d.ts +8 -2
  124. package/dist/hitl/automaticAnswerVerdict.js +71 -23
  125. package/dist/hitl/crashConverged.js +2 -0
  126. package/dist/hitl/decisionConflict.d.ts +3 -0
  127. package/dist/hitl/decisionConflict.js +43 -0
  128. package/dist/hitl/decisionRefusal.d.ts +23 -0
  129. package/dist/hitl/decisionRefusal.js +88 -0
  130. package/dist/hitl/frameRouter.d.ts +10 -0
  131. package/dist/hitl/frameRouter.js +71 -32
  132. package/dist/hitl/gateLedger.d.ts +8 -0
  133. package/dist/hitl/gateLedger.js +38 -0
  134. package/dist/hitl/hitlBridge.d.ts +14 -3
  135. package/dist/hitl/hitlBridge.js +75 -62
  136. package/dist/hitl/hitlHostSurface.d.ts +3 -0
  137. package/dist/hitl/hitlHostSurface.js +17 -15
  138. package/dist/hitl/livePendingAsk.d.ts +16 -6
  139. package/dist/hitl/livePendingAsk.js +36 -4
  140. package/dist/hitl/localAllowRule.js +2 -1
  141. package/dist/hitl/noAllowCause.d.ts +9 -0
  142. package/dist/hitl/noAllowCause.js +26 -0
  143. package/dist/hitl/parkExpiryJudgements.d.ts +19 -0
  144. package/dist/hitl/parkExpiryJudgements.js +46 -0
  145. package/dist/hitl/parkResolver.d.ts +3 -7
  146. package/dist/hitl/parkResolver.js +55 -28
  147. package/dist/hitl/parkRowBirthWait.js +2 -1
  148. package/dist/hitl/persistedRulesWire.d.ts +6 -1
  149. package/dist/hitl/persistedRulesWire.js +16 -4
  150. package/dist/hitl/planReviewWire.d.ts +2 -3
  151. package/dist/hitl/planReviewWire.js +86 -39
  152. package/dist/hitl/replayedApproval.d.ts +8 -0
  153. package/dist/hitl/replayedApproval.js +125 -10
  154. package/dist/hitl/resumeRunningCard.d.ts +2 -0
  155. package/dist/hitl/resumeRunningCard.js +15 -4
  156. package/dist/hitl/sessionPolicyDeliverable.d.ts +1 -1
  157. package/dist/hitl/sessionPolicyDeliverable.js +3 -8
  158. package/dist/hitl/sessionPolicyWire.js +1 -1
  159. package/dist/hitl/toolApprovalWire.d.ts +50 -5
  160. package/dist/hitl/toolApprovalWire.js +368 -56
  161. package/dist/hooksWireCaps.js +38 -6
  162. package/dist/host.d.ts +5 -0
  163. package/dist/host.js +3 -1
  164. package/dist/hostCallbackGuard.d.ts +1 -0
  165. package/dist/hostCallbackGuard.js +25 -0
  166. package/dist/humanTurnRead.d.ts +15 -1
  167. package/dist/humanTurnRead.js +28 -2
  168. package/dist/index.d.ts +45 -14
  169. package/dist/index.js +33 -12
  170. package/dist/interactiveInterrupt.d.ts +8 -0
  171. package/dist/interactiveInterrupt.js +54 -0
  172. package/dist/limitsWire.d.ts +5 -1
  173. package/dist/limitsWire.js +51 -13
  174. package/dist/liveModelCatalog.d.ts +109 -0
  175. package/dist/liveModelCatalog.js +344 -18
  176. package/dist/mcpEngineLeg.d.ts +25 -0
  177. package/dist/mcpEngineLeg.js +71 -15
  178. package/dist/mcpPanel.js +2 -1
  179. package/dist/mcpProbeCapability.d.ts +0 -1
  180. package/dist/mcpProbeCapability.js +13 -12
  181. package/dist/mcpReconnect.d.ts +1 -3
  182. package/dist/mcpReconnect.js +20 -10
  183. package/dist/memoryComplianceCapability.d.ts +0 -1
  184. package/dist/memoryComplianceCapability.js +14 -13
  185. package/dist/memoryOriginCapability.d.ts +0 -1
  186. package/dist/memoryOriginCapability.js +14 -13
  187. package/dist/memoryVerbsWire.d.ts +1 -1
  188. package/dist/memoryVerbsWire.js +4 -0
  189. package/dist/model/liveCatalogShape.d.ts +14 -0
  190. package/dist/model/liveCatalogShape.js +71 -0
  191. package/dist/model/modelSupplyRules.d.ts +1 -0
  192. package/dist/model/modelSupplyRules.js +30 -9
  193. package/dist/model/modelsPolicy.d.ts +21 -0
  194. package/dist/model/modelsPolicy.js +90 -0
  195. package/dist/modelIdentityResolvability.d.ts +4 -2
  196. package/dist/modelIdentityResolvability.js +25 -5
  197. package/dist/modelSwitchFallback.d.ts +69 -0
  198. package/dist/modelSwitchFallback.js +206 -0
  199. package/dist/notifications.js +12 -11
  200. package/dist/outputStyleSeatCapability.d.ts +15 -0
  201. package/dist/outputStyleSeatCapability.js +42 -0
  202. package/dist/ownKey.d.ts +7 -0
  203. package/dist/ownKey.js +58 -0
  204. package/dist/panelReduce.d.ts +114 -0
  205. package/dist/panelReduce.js +1135 -0
  206. package/dist/panelStateHolder.d.ts +17 -0
  207. package/dist/panelStateHolder.js +201 -0
  208. package/dist/peerLaneCapability.d.ts +0 -1
  209. package/dist/peerLaneCapability.js +15 -14
  210. package/dist/permissionModesCapability.d.ts +0 -1
  211. package/dist/permissionModesCapability.js +5 -10
  212. package/dist/permissionRuleIssue.js +1 -1
  213. package/dist/permissionRulesWriteCapability.d.ts +0 -1
  214. package/dist/permissionRulesWriteCapability.js +15 -14
  215. package/dist/permissionsAllowGrantCapability.d.ts +2 -1
  216. package/dist/permissionsAllowGrantCapability.js +35 -19
  217. package/dist/permissionsDeclaration.d.ts +16 -1
  218. package/dist/permissionsDeclaration.js +142 -15
  219. package/dist/printToolResultFrame.js +2 -10
  220. package/dist/promptDelivery.js +0 -8
  221. package/dist/protocolSegment.d.ts +1 -0
  222. package/dist/protocolSegment.js +13 -1
  223. package/dist/request/taskRequest.d.ts +11 -3
  224. package/dist/request/taskRequest.js +13 -1
  225. package/dist/resultFrameKeys.d.ts +4 -0
  226. package/dist/resultFrameKeys.js +13 -0
  227. package/dist/resumeRefusalCopy.d.ts +18 -1
  228. package/dist/resumeRefusalCopy.js +132 -31
  229. package/dist/retryStatus.js +6 -4
  230. package/dist/rewindArchiveCapability.d.ts +2 -1
  231. package/dist/rewindArchiveCapability.js +28 -15
  232. package/dist/rewindEngineAction.d.ts +21 -0
  233. package/dist/rewindEngineAction.js +36 -0
  234. package/dist/rewindWireCaps.d.ts +2 -1
  235. package/dist/rewindWireCaps.js +1 -0
  236. package/dist/{hitl/rosterMountedNames.d.ts → rosterMountedNames.d.ts} +1 -1
  237. package/dist/runCancelContext.js +2 -1
  238. package/dist/runTerminal.d.ts +0 -11
  239. package/dist/runTerminal.js +4 -27
  240. package/dist/sdkRegistryTransit.d.ts +2 -1
  241. package/dist/sdkRegistryTransit.js +1 -0
  242. package/dist/sealedKeyCapability.d.ts +0 -1
  243. package/dist/sealedKeyCapability.js +19 -20
  244. package/dist/seatContract.d.ts +85 -28
  245. package/dist/seatContract.js +512 -148
  246. package/dist/sessionIdSegment.d.ts +1 -0
  247. package/dist/sessionIdSegment.js +11 -0
  248. package/dist/sessionMemoryErase.d.ts +1 -1
  249. package/dist/sessionMemoryErase.js +6 -1
  250. package/dist/sessionMemoryStatus.js +3 -2
  251. package/dist/sessionPolicyCapability.d.ts +0 -1
  252. package/dist/sessionPolicyCapability.js +14 -13
  253. package/dist/sessionReadRedactionCapability.d.ts +15 -0
  254. package/dist/sessionReadRedactionCapability.js +42 -0
  255. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
  256. package/dist/sessionSyncFileHistoryCapability.js +13 -12
  257. package/dist/skillToolListsCapability.d.ts +0 -1
  258. package/dist/skillToolListsCapability.js +13 -12
  259. package/dist/skillsWireCaps.d.ts +4 -0
  260. package/dist/skillsWireCaps.js +66 -1
  261. package/dist/sqlEngineCapability.d.ts +0 -1
  262. package/dist/sqlEngineCapability.js +21 -21
  263. package/dist/steerPriorityCapability.d.ts +0 -1
  264. package/dist/steerPriorityCapability.js +18 -19
  265. package/dist/storePostureCapability.d.ts +1 -1
  266. package/dist/storePostureCapability.js +16 -16
  267. package/dist/streamDrainingLegs.d.ts +6 -0
  268. package/dist/streamDrainingLegs.js +23 -0
  269. package/dist/subagent/agentStateDetail.d.ts +1 -0
  270. package/dist/subagent/agentStateDetail.js +41 -0
  271. package/dist/subagent/engineCompactWire.d.ts +15 -0
  272. package/dist/subagent/engineCompactWire.js +82 -45
  273. package/dist/subagent/engineDelegatedPrompt.js +10 -6
  274. package/dist/subagent/engineSubagentOutput.d.ts +0 -1
  275. package/dist/subagent/engineSubagentOutput.js +5 -20
  276. package/dist/subagent/engineSubagentTail.d.ts +0 -1
  277. package/dist/subagent/engineSubagentTail.js +7 -32
  278. package/dist/subagentContentStore.js +3 -3
  279. package/dist/supportFloor.js +2 -5
  280. package/dist/taskRequestWords.d.ts +2 -4
  281. package/dist/taskRequestWords.js +3 -2
  282. package/dist/terminalStatuses.d.ts +30 -0
  283. package/dist/terminalStatuses.js +67 -0
  284. package/dist/toolResult.d.ts +5 -0
  285. package/dist/toolResult.js +88 -36
  286. package/dist/types/engineState.d.ts +2 -1
  287. package/dist/usageAnalytics.d.ts +146 -0
  288. package/dist/usageAnalytics.js +311 -0
  289. package/dist/userAttachmentEnvelope.d.ts +22 -1
  290. package/dist/userAttachmentEnvelope.js +90 -2
  291. package/dist/userInvokedSkillsCapability.d.ts +0 -1
  292. package/dist/userInvokedSkillsCapability.js +13 -12
  293. package/dist/webSearchBackendCapability.d.ts +0 -1
  294. package/dist/webSearchBackendCapability.js +20 -19
  295. package/dist/webSearchWireCaps.js +3 -11
  296. package/dist/wireErrorTriage.d.ts +11 -0
  297. package/dist/wireErrorTriage.js +35 -0
  298. package/dist/wireFailureShape.d.ts +10 -0
  299. package/dist/wireFailureShape.js +72 -5
  300. package/dist/wireRefusalCopy.d.ts +6 -1
  301. package/dist/wireRefusalCopy.js +23 -25
  302. package/dist/wiringInstructionsAttribution.d.ts +10 -0
  303. package/dist/wiringInstructionsAttribution.js +67 -0
  304. package/dist/workflowClient.js +19 -2
  305. package/dist/workflowsJournalCapability.d.ts +0 -1
  306. package/dist/workflowsJournalCapability.js +13 -12
  307. package/dist/writeProtectionCapability.d.ts +0 -1
  308. package/dist/writeProtectionCapability.js +26 -26
  309. package/docs/INTEGRATION-CLIENTS.md +1733 -52
  310. package/package.json +5 -5
  311. package/dist/askSurvivesStandingAnswer.d.ts +0 -13
  312. package/dist/askSurvivesStandingAnswer.js +0 -53
  313. package/dist/engineCapsGenerationGuard.d.ts +0 -4
  314. package/dist/engineCapsGenerationGuard.js +0 -19
  315. package/dist/hitl/askSurvivesPosture.d.ts +0 -20
  316. package/dist/hitl/askSurvivesPosture.js +0 -48
  317. package/dist/panelRunningHistory.d.ts +0 -22
  318. package/dist/panelRunningHistory.js +0 -82
  319. package/dist/registryConflict.d.ts +0 -3
  320. package/dist/registryConflict.js +0 -44
  321. /package/dist/{hitl/rosterMountedNames.js → rosterMountedNames.js} +0 -0
@@ -1,14 +1,17 @@
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 { ApprovalStaleError, NotFoundError, ParkedResumeStartupError } from '@sema-agent/sdk';
3
+ import { DecideTransportRetryExhaustedError, GATE_FAILURE_CODES, HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, hitlSafetyCodeOf, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
4
+ import { ASK_GONE, CHECKPOINT_UNSUPPORTED_VERSION, DECIDE_WORKFLOW_PARK_FORKED, DECIDE_WORKFLOW_PARKS_UNREADABLE, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideAlreadyResolvedCode, isDecideParkMovedCode, refusalSaysStillPending } from '../engineErrorCodes.js';
5
+ import { isHitlSafetyErrorLike } from './parkOwnership.js';
4
6
  import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
5
7
  import { hostLog } from '../host.js';
6
8
  import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
7
- import { observeCancelByDeny } from './hitlHostSurface.js';
9
+ import { INTERRUPT_DENY_REASON, observeCancelByDeny } from './hitlHostSurface.js';
8
10
  import { flushHeldWithInterruptRewrite, isAskTool } from './frameRouter.js';
9
11
  import { approvalCallKey, askGateQuestionId } from './gateIdentity.js';
10
12
  import { readDecideReceipt } from '../decideReceipt.js';
11
13
  import { decideFailureNote } from '../decideFailureNote.js';
14
+ import { thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
12
15
  export const MAX_GATE_HOPS = 2;
13
16
  export function nextHopBudget(prev, round) {
14
17
  const total = (prev?.total ?? 0) + 1;
@@ -17,16 +20,6 @@ export function nextHopBudget(prev, round) {
17
20
  const MAX_TRANSPORT_REATTACHES = 2;
18
21
  export const MAX_TOTAL_PARKS = 64;
19
22
  const MAX_ALREADY_RESOLVED_REATTACHES = 1;
20
- export const GATE_FAILURE_CODES = [
21
- 'no_pending',
22
- 'binding_mismatch',
23
- 'wrong_gate',
24
- 'bad_plan_edit',
25
- 'bad_plan_mode',
26
- 'empty_answer',
27
- ];
28
- const _gateFailureCodePins = [true, true];
29
- void _gateFailureCodePins;
30
23
  function isGateFailureCode(v) {
31
24
  return typeof v === 'string' && GATE_FAILURE_CODES.includes(v);
32
25
  }
@@ -40,27 +33,59 @@ function alreadyResolvedReasonToken(reason) {
40
33
  const r = reason.toLowerCase();
41
34
  return ALREADY_RESOLVED_REASON_TOKENS.find((t) => r.includes(t));
42
35
  }
43
- export function isAlreadyResolvedFailure(outcome) {
36
+ export function isAlreadyResolvedFailure(failure) {
44
37
  try {
45
- return alreadyResolvedFailureVerdict(outcome);
38
+ return alreadyResolvedFailureVerdict(failure);
46
39
  }
47
40
  catch {
48
41
  return false;
49
42
  }
50
43
  }
51
- function alreadyResolvedFailureVerdict(outcome) {
52
- if (typeof outcome !== 'object' || outcome === null)
44
+ const ALREADY_RESOLVED_TYPED_ERROR_NAMES = Object.freeze([ApprovalStaleError.name, NotFoundError.name]);
45
+ const PARKED_RESUME_STARTUP_TYPED_ERROR_NAME = ParkedResumeStartupError.name;
46
+ function decideFailureFactsOf(failure) {
47
+ if (typeof failure !== 'object' || failure === null)
48
+ return undefined;
49
+ const o = failure;
50
+ const ec = o.errorCode;
51
+ const errorCode = typeof ec === 'string' && ec.length > 0 ? ec : undefined;
52
+ const retriable = refusalSaysStillPending(failure);
53
+ if (Object.hasOwn(failure, 'kind') && o.kind === 'failed') {
54
+ const safety = isGateFailureCode(o.safetyCode) ? o.safetyCode : undefined;
55
+ const code = isGateFailureCode(o.code) ? o.code : safety;
56
+ return { code, safetySignal: safety !== undefined, errorCode, retriable, typedErrorName: undefined, reason: o.reason };
57
+ }
58
+ const name = o.name;
59
+ return {
60
+ code: isGateFailureCode(o.code) ? o.code : undefined,
61
+ safetySignal: isHitlSafetyErrorLike(failure),
62
+ errorCode,
63
+ retriable,
64
+ typedErrorName: typeof name === 'string' ? name : undefined,
65
+ reason: String(failure),
66
+ };
67
+ }
68
+ function alreadyResolvedFailureVerdict(failure) {
69
+ const f = decideFailureFactsOf(failure);
70
+ if (f === undefined)
53
71
  return false;
54
- const code = 'code' in outcome && isGateFailureCode(outcome.code) ? outcome.code : undefined;
55
- if (code !== undefined)
56
- return code === 'no_pending';
57
- if ('errorCode' in outcome && isCodeClassifiedGateFailure(outcome.errorCode, Object.hasOwn(outcome, 'retriable') && outcome.retriable === true))
72
+ if (f.code !== undefined)
73
+ return f.code === 'no_pending';
74
+ if (f.safetySignal)
75
+ return false;
76
+ if (isDecideAlreadyResolvedCode(f.errorCode))
77
+ return true;
78
+ if (f.typedErrorName !== undefined && ALREADY_RESOLVED_TYPED_ERROR_NAMES.includes(f.typedErrorName))
79
+ return true;
80
+ if (f.typedErrorName === PARKED_RESUME_STARTUP_TYPED_ERROR_NAME && !isDecideParkMovedCode(f.errorCode))
81
+ return false;
82
+ if (isCodeClassifiedGateFailure(f.errorCode, f.retriable))
58
83
  return false;
59
- if ('errorCode' in outcome && isDecideParkMovedCode(outcome.errorCode))
84
+ if (isDecideParkMovedCode(f.errorCode))
60
85
  return true;
61
- if ('errorCode' in outcome && outcome.errorCode === ASK_GONE)
86
+ if (f.errorCode === ASK_GONE)
62
87
  return true;
63
- return isAlreadyResolvedGateReason(outcome.reason);
88
+ return isAlreadyResolvedGateReason(f.reason);
64
89
  }
65
90
  export function isGateStandingErrorCode(code, retriable) {
66
91
  return (code === RESUME_POLICY_FOLD_CODE ||
@@ -139,7 +164,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
139
164
  });
140
165
  const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
141
166
  if (answer === null) {
142
- observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: 'Interrupted by user' }, gatedCallId, undefined, pending), taskId);
167
+ observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending), taskId);
143
168
  return { kind: 'aborted', gatedCallId };
144
169
  }
145
170
  let declinedReceipt;
@@ -166,17 +191,19 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
166
191
  return { kind: 'decided', gatedCallId, ...(declinedReceipt !== undefined ? { receipt: declinedReceipt } : {}) };
167
192
  }
168
193
  catch (e) {
169
- const code = e instanceof HitlSafetyError && isGateFailureCode(e.code) ? e.code : undefined;
194
+ const code = hitlSafetyCodeOf(e);
170
195
  const currentPending = readDecideCurrentPending(e);
171
196
  const wireCode = readWireErrorCode(e);
172
197
  const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
198
+ const failureTexts = wireFailureTextsOf(e);
199
+ hostLog('debug', `liveHitlAskWire: decide failed: ${failureTexts.log}`);
173
200
  return {
174
201
  kind: 'failed',
175
202
  stage: 'decide',
176
203
  gatedCallId,
177
- reason: `decide failed: ${String(e)}${decideFailureNote(e, attemptedDecision)}`,
204
+ reason: `decide failed: ${failureTexts.reason}${decideFailureNote(e, attemptedDecision)}`,
178
205
  ...(code !== undefined ? { code } : {}),
179
- ...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
206
+ ...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
180
207
  ...(currentPending !== undefined ? { currentPending } : {}),
181
208
  ...(wireCode !== undefined ? { errorCode: wireCode } : {}),
182
209
  ...(wireRetriable ? { retriable: true } : {}),
@@ -1,4 +1,5 @@
1
1
  import { abortableSleep } from '../abortableSleep.js';
2
+ import { wireFailureTextsOf } from '../wireFailureShape.js';
2
3
  function parkRowPollDelay(ms, signal) {
3
4
  return abortableSleep(ms, signal ?? new AbortController().signal);
4
5
  }
@@ -16,7 +17,7 @@ export async function waitForParkRowBirth(deps) {
16
17
  return await deps.probe(attempt, signal);
17
18
  }
18
19
  catch (e) {
19
- return { kind: 'unborn', reason: `probe threw: ${String(e)}` };
20
+ return { kind: 'unborn', reason: `probe threw: ${wireFailureTextsOf(e).reason}` };
20
21
  }
21
22
  };
22
23
  const probeWithinRemaining = async (attempt, remainingMs) => {
@@ -44,6 +44,9 @@ export type RulesFailure = {
44
44
  } | {
45
45
  kind: 'cursor-stale';
46
46
  message: string;
47
+ } | {
48
+ kind: 'query-refused';
49
+ message: string;
47
50
  } | {
48
51
  kind: 'too-many-candidates';
49
52
  message: string;
@@ -78,7 +81,9 @@ export type RulesFailure = {
78
81
  kind: 'error';
79
82
  message: string;
80
83
  };
81
- export declare function classifyRulesFailure(e: unknown): RulesFailure;
84
+ export declare function classifyRulesFailure(e: unknown, context?: {
85
+ readonly cursorSent?: boolean;
86
+ }): RulesFailure;
82
87
  export declare const PERSISTED_RULE_BEHAVIORS: readonly RuleBehavior[];
83
88
  export declare function persistedRuleBehaviorOf(rule: unknown): RuleBehavior | undefined;
84
89
  export declare const PERSISTED_RULE_BEHAVIOR_UNKNOWN = "(unknown)";
@@ -36,7 +36,7 @@ export function readCcImportRedeemCounts(result) {
36
36
  }
37
37
  return null;
38
38
  }
39
- export function classifyRulesFailure(e) {
39
+ export function classifyRulesFailure(e, context) {
40
40
  const { status, errorCode, message, retryAfterSec, committed } = wireFailureShapeOf(e);
41
41
  if (errorCode === 'capability.rule_store_required') {
42
42
  return { kind: 'lane-unavailable', message };
@@ -58,8 +58,20 @@ export function classifyRulesFailure(e) {
58
58
  if (errorCode === 'state.rule_write_failed') {
59
59
  return committed === 'no' ? { kind: 'write-not-committed', message } : { kind: 'write-indeterminate', message };
60
60
  }
61
- if (status === 400 && errorCode === 'request.query_invalid')
62
- return { kind: 'cursor-stale', message };
61
+ if (status === 400 && errorCode === 'request.query_invalid') {
62
+ let cursorSent;
63
+ try {
64
+ cursorSent = context?.cursorSent;
65
+ }
66
+ catch {
67
+ cursorSent = undefined;
68
+ }
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 };
74
+ }
63
75
  if (status === 400 && errorCode === 'request.field_invalid')
64
76
  return { kind: 'field-refused', message };
65
77
  if (status === 400 && errorCode === 'request.body_shape')
@@ -144,7 +156,7 @@ export async function listAllPersistedRules(facade, params = {}, opts) {
144
156
  res = await facade.list({ limit: PAGE_LIMIT, ...cleanParams, ...(cursor !== undefined ? { cursor } : {}) }, opts);
145
157
  }
146
158
  catch (e) {
147
- const failure = classifyRulesFailure(e);
159
+ const failure = classifyRulesFailure(e, { cursorSent: cursor !== undefined });
148
160
  if (failure.kind === 'cursor-stale' && cursor !== undefined && attempt === 0) {
149
161
  hostLog('debug', 'persistedRulesWire: rule list cursor was minted on another rev/principal/scope — dropping it and re-listing from the top (never resuming a stale keyset)');
150
162
  restart = true;
@@ -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<{
@@ -48,7 +49,6 @@ export type PlanReviewWireClient = {
48
49
  };
49
50
  export interface PlanReviewWire {
50
51
  client: PlanReviewWireClient;
51
- capsBaseUrl?: string;
52
52
  }
53
53
  export interface PlanReviewDecisionOutcome extends PlanReviewOutcomeMeta {
54
54
  detail: string;
@@ -77,14 +77,13 @@ export declare function __resetPlanReviewDecisionLatchForTests(): void;
77
77
  export declare function decidePlanReview(taskId: string, decision: 'approve' | 'reject', permissionModeAfter?: string, opts?: DecidePlanReviewOptions): Promise<void>;
78
78
  export interface ReopenPlanReviewOpts {
79
79
  mintFreshQuestionId?: boolean;
80
- deliverDecision?: (taskId: string, decision: 'approve' | 'reject') => void | Promise<void>;
80
+ deliverDecision?: (taskId: string, decision: 'approve' | 'reject', reason?: string) => void | Promise<void>;
81
81
  sessionKey?: string;
82
82
  wire?: PlanReviewWire;
83
83
  onOutcome?: PlanReviewDeliveryOptions['onOutcome'];
84
84
  presentationReceiptMs?: number;
85
85
  trigger?: PlanReviewReopenTrigger;
86
86
  readGateInstanceKeys?: PlanReviewGateInstanceReader;
87
- decisionHandedOver?: (taskId: string) => boolean;
88
87
  pendingReadStartedAt?: number;
89
88
  }
90
89
  export declare function _resetActiveReopenRespondersForTest(): void;
@@ -6,7 +6,8 @@ import { engineWireTarget } from '../engineWireTarget.js';
6
6
  import { enqueuePlanReviewOutcome } from '../notifications.js';
7
7
  import { corruptStoredRowContent, corruptStoredRowWhere } from '../wireErrorTriage.js';
8
8
  import { decideFailureNote } from '../decideFailureNote.js';
9
- import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError } from '../resumeRefusalCopy.js';
9
+ import { wireFailureTextsOf } from '../wireFailureShape.js';
10
+ import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError, resumeContextUnavailableContent, resumeContextUnavailableFromError } from '../resumeRefusalCopy.js';
10
11
  import { REQUEST_FIELD_CONFLICT, isUnavailableFaceCode } from '../engineErrorCodes.js';
11
12
  import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
12
13
  import { planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
@@ -28,31 +29,59 @@ const ALL_KNOWN_LABELS = Object.freeze([
28
29
  PLAN_REVIEW_APPROVE_LABEL,
29
30
  PLAN_REVIEW_REJECT_LABEL,
30
31
  ]);
31
- export function planReviewDecisionFromAnswer(answer, shownLabels) {
32
- const choice = planReviewChoiceFromAnswer(answer, shownLabels);
32
+ export function planReviewDecisionFromAnswer(answer, shownLabels = LEGACY_TWO_CHOICE_LABELS) {
33
+ const { choice } = readPlanReviewAnswer(answer, shownLabels);
33
34
  if (choice === 'dismissed')
34
35
  return 'dismissed';
35
36
  return choice.decision;
36
37
  }
37
- 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) {
38
51
  const answers = answer?.answers;
39
52
  if (!Array.isArray(answers) || answers.length !== 1)
40
- return 'dismissed';
41
- const picked = answers[0]?.selected;
42
- if (!Array.isArray(picked) || picked.length !== 1)
43
- 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();
44
66
  const selected = picked[0];
45
67
  if (typeof selected !== 'string' || !shownLabels.includes(selected))
46
- return 'dismissed';
68
+ return readDismissed();
69
+ const ignored = note !== undefined;
47
70
  if (selected === PLAN_REVIEW_APPROVE_AUTO_LABEL)
48
- return { decision: 'approve', permissionModeAfter: 'acceptEdits' };
71
+ return { choice: { decision: 'approve', permissionModeAfter: 'acceptEdits' }, noteIgnoredOnApprove: ignored };
49
72
  if (selected === PLAN_REVIEW_APPROVE_MANUAL_LABEL)
50
- return { decision: 'approve', permissionModeAfter: 'default' };
73
+ return { choice: { decision: 'approve', permissionModeAfter: 'default' }, noteIgnoredOnApprove: ignored };
51
74
  if (selected === APPROVE_LABEL)
52
- return { decision: 'approve' };
75
+ return { choice: { decision: 'approve' }, noteIgnoredOnApprove: ignored };
53
76
  if (selected === REJECT_LABEL)
54
- return { decision: 'reject' };
55
- 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;
56
85
  }
57
86
  export function planReviewCardOptions(offerModeAfter) {
58
87
  const reject = { label: REJECT_LABEL, description: 'Discard this plan and stay in plan mode to refine it' };
@@ -269,10 +298,12 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
269
298
  const decided = choice.decision;
270
299
  notePlanReviewDecisiveAnswerFor(DEFAULT_SESSION_KEY, taskId);
271
300
  const modeAfter = choice.decision === 'approve' ? choice.permissionModeAfter : undefined;
301
+ const cardReason = choice.decision === 'reject' ? choice.reason : undefined;
302
+ const cardDelivery = cardReason !== undefined ? { ...delivery, reason: cardReason } : delivery;
272
303
  offerModeAfterByTask.delete(taskId);
273
304
  notePlanReviewAnswered(questionId);
274
305
  hostLog('debug', `planReviewWire: arm responder delivering ${decided} for task ${taskId} (path=arm)`);
275
- void decidePlanReviewFor(taskId, decided, modeAfter, delivery, gateBefore);
306
+ void decidePlanReviewFor(taskId, decided, modeAfter, cardDelivery, gateBefore);
276
307
  return { ok: true };
277
308
  });
278
309
  publishPlanReviewCard(taskId);
@@ -309,6 +340,15 @@ export function planReviewOutcomeTail(taskId, decision, effect, cause) {
309
340
  return `Do not state the result as fact: tell the user the ${decision} was submitted but its effect could not be confirmed; TaskOutput("${taskId}") shows where the task stands.`;
310
341
  }
311
342
  }
343
+ function clientOnlyView(wire) {
344
+ if (wire === undefined || wire === null || typeof wire !== 'object')
345
+ return wire;
346
+ return {
347
+ get client() {
348
+ return wire.client;
349
+ },
350
+ };
351
+ }
312
352
  function planReviewDecisionTimedOutOutcome(decision) {
313
353
  return (`The plan_review ${decision} was sent, but no answer came back from the engine within the time limit this host allows for it — ` +
314
354
  `it may have taken effect (${decision === 'approve' ? 'an approval keeps the engine busy until the resumed work finishes' : 'the engine may still be applying it'}).`);
@@ -492,12 +532,12 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
492
532
  let stillParkedProven = false;
493
533
  let statusSeen;
494
534
  const wire = opts?.wire;
495
- const resolved = engineWireFor(wire, DEFAULT_SESSION_KEY, usablePlanReviewWireClient);
496
- const badMode = permissionModeAfter !== undefined && (decision !== 'approve' || !isPlanReviewModeAfter(permissionModeAfter));
535
+ const resolved = engineWireFor(clientOnlyView(wire), DEFAULT_SESSION_KEY, usablePlanReviewWireClient);
536
+ const badMode = permissionModeAfter !== undefined && !isPlanReviewModeAfter(permissionModeAfter);
497
537
  if (badMode) {
498
- hostLog('error', `planReviewWire: ${decision} NOT sent — permissionModeAfter is only valid with approve and must be "default" or "acceptEdits"`);
538
+ hostLog('error', `planReviewWire: ${decision} NOT sent — permissionModeAfter must be "default" or "acceptEdits"`);
499
539
  effect = 'not_sent';
500
- outcome = `The plan_review ${decision} could NOT be sent: the post-approval permission mode given with it is not valid for this decision (it only applies to an approval, and must be "default" or "acceptEdits"). Tell the user plainly that the decision did not go through; the plan is still waiting.`;
540
+ outcome = `The plan_review ${decision} could NOT be sent: the post-approval permission mode given with it is not valid (it must be "default" or "acceptEdits"). Tell the user plainly that the decision did not go through; the plan is still waiting.`;
501
541
  }
502
542
  else if (resolved.kind === 'no-target') {
503
543
  hostLog('error', `planReviewWire: ${decision} NOT sent — engineWireTarget() unavailable`);
@@ -600,12 +640,15 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
600
640
  const refusedCode = e?.errorCode;
601
641
  const noAnswer = typeof status === 'number' || typeof e !== 'object' ? null : noAnswerKindOf(e);
602
642
  if (status === 400 && refusedCode === REQUEST_FIELD_CONFLICT && permissionModeAfter === 'acceptEdits') {
603
- hostLog('debug', 'planReviewWire: approve + acceptEdits refused (request.field_conflict) — NOT re-sent with a different mode');
643
+ hostLog('debug', `planReviewWire: ${decision} + acceptEdits refused (request.field_conflict) — NOT re-sent with a different mode`);
604
644
  effect = 'still_parked';
605
645
  stillParkedProven = true;
606
646
  outcome =
607
- 'The plan approval was NOT applied: the engine refused "auto-accept edits" for this task (it was not started read-only in plan mode, so that option does not apply). ' +
608
- 'Nothing changed engine-side — the plan is still waiting for review. Tell the user plainly, and that approving with "manually approve edits" will go through.';
647
+ decision === 'approve'
648
+ ? 'The plan approval was NOT applied: the engine refused "auto-accept edits" for this task (it was not started read-only in plan mode, so that option does not apply). ' +
649
+ 'Nothing changed engine-side — the plan is still waiting for review. Tell the user plainly, and that approving with "manually approve edits" will go through.'
650
+ : `The plan ${decision} was NOT applied: the engine refused the post-approval permission mode that was sent with it (request.field_conflict). ` +
651
+ 'Nothing changed engine-side — the plan is still waiting for review. Tell the user plainly that the decision did not go through.';
609
652
  }
610
653
  else if (status === 404 && refusedCode === 'not_found.plan_review') {
611
654
  hostLog('debug', `planReviewWire: ${decision} → 404 not_found.plan_review (no pending plan review any more — an earlier decision likely settled it)`);
@@ -622,6 +665,12 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
622
665
  stillParkedCause = 'stored_form_unsupported';
623
666
  outcome = `This ${decision} was not applied. ${checkpointUnsupportedVersionContent(cu, { parkKind: 'plan_review', runId: taskId })}`;
624
667
  }
668
+ else if (resumeContextUnavailableFromError(e) !== null) {
669
+ const ru = resumeContextUnavailableFromError(e);
670
+ hostLog('debug', `planReviewWire: ${decision} → 409 conflict.resume_context_unavailable (the session context needed to continue this run is not available)`);
671
+ effect = 'not_applied';
672
+ outcome = `This ${decision} was not applied. ${resumeContextUnavailableContent(ru)}`;
673
+ }
625
674
  else if (typeof status === 'number' && corruptStoredRowWhere(e) !== undefined) {
626
675
  const where = corruptStoredRowWhere(e);
627
676
  hostLog('debug', `planReviewWire: ${decision} → ${String(status)} corrupt (stored resume context unreadable)`);
@@ -643,7 +692,9 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
643
692
  outcome = planReviewDecisionConnectionLostOutcome(decision);
644
693
  }
645
694
  else {
646
- outcome = `The plan_review decision could not reach the engine: ${String(e)}`;
695
+ const failureTexts = wireFailureTextsOf(e);
696
+ hostLog('debug', `planReviewWire: ${decision} could not reach the engine: ${failureTexts.log}`);
697
+ outcome = `The plan_review decision could not reach the engine: ${failureTexts.reason}`;
647
698
  }
648
699
  }
649
700
  }
@@ -800,9 +851,10 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs) {
800
851
  ? { ...(opts.wire !== undefined ? { wire: opts.wire } : {}), ...(opts.onOutcome !== undefined ? { onOutcome: opts.onOutcome } : {}) }
801
852
  : undefined;
802
853
  const deliver = opts?.deliverDecision ??
803
- ((tid, decision) => {
854
+ ((tid, decision, reason) => {
804
855
  hostLog('debug', `planReviewWire: reopen responder delivering ${decision} for task ${tid} (path=reopen-default)`);
805
- void (delivery === undefined ? decidePlanReview(tid, decision) : decidePlanReview(tid, decision, undefined, delivery));
856
+ const withReason = reason === undefined ? delivery : { ...delivery, reason };
857
+ void (withReason === undefined ? decidePlanReview(tid, decision) : decidePlanReview(tid, decision, undefined, withReason));
806
858
  });
807
859
  const activeKey = `${sessionKey}\u0000${canonicalId}`;
808
860
  const unregister = registerLocalQuestionResponder(questionId, async (_id, answer) => {
@@ -810,15 +862,20 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs) {
810
862
  if (activeReopenResponders.get(activeKey)?.questionId === questionId) {
811
863
  activeReopenResponders.delete(activeKey);
812
864
  }
813
- const decided = planReviewDecisionFromAnswer(answer, LEGACY_TWO_CHOICE_LABELS);
814
- if (decided === 'dismissed') {
865
+ const choice = planReviewChoiceFromAnswer(answer, LEGACY_TWO_CHOICE_LABELS);
866
+ if (choice === 'dismissed') {
815
867
  notePlanReviewUserDismissalFor(sessionKey, taskId, opts?.readGateInstanceKeys, questionId);
816
868
  return { ok: true };
817
869
  }
870
+ const decided = choice.decision;
871
+ const cardReason = choice.decision === 'reject' ? choice.reason : undefined;
818
872
  notePlanReviewDecisiveAnswerFor(sessionKey, taskId);
819
873
  notePlanReviewAnsweredFor(sessionKey, questionId);
820
874
  try {
821
- await deliver(taskId, decided);
875
+ if (cardReason === undefined)
876
+ await deliver(taskId, decided);
877
+ else
878
+ await deliver(taskId, decided, cardReason);
822
879
  }
823
880
  catch (e) {
824
881
  hostLog('error', `planReviewWire: reopen decision delivery threw for task ${taskId}: ${String(e)}`);
@@ -866,18 +923,8 @@ async function reopenAfterDismissalCheck(taskId, opts, sessionKey, receiptMs, ma
866
923
  }
867
924
  }
868
925
  }
869
- let handedOver = false;
870
926
  const ledgerSaysInFlight = planReviewDecisionInFlight(taskId, recheckReadStart);
871
- if (!ledgerSaysInFlight) {
872
- try {
873
- handedOver = opts.decisionHandedOver?.(taskId) === true;
874
- }
875
- catch (err) {
876
- hostLog('debug', `planReviewWire: reopen for task ${taskId} refused — the host's decision hand-over check threw: ${String(err)}`);
877
- return { reopened: false };
878
- }
879
- }
880
- if (ledgerSaysInFlight || handedOver) {
927
+ if (ledgerSaysInFlight) {
881
928
  hostLog('debug', `planReviewWire: reopen for task ${taskId} refused after the dismissal check — a decision for this task went out while the check was reading`);
882
929
  return { reopened: false, _sema_decisionInFlight: true };
883
930
  }
@@ -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,10 @@ 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 replayedApprovalSentence(disposition: ReplayedApprovalDisposition | undefined, opts?: {
28
+ readonly toolName?: string;
29
+ }): string | undefined;