@sema-agent/client-core 0.97.1 → 0.99.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 (353) hide show
  1. package/CHANGELOG.md +607 -0
  2. package/README.md +96 -46
  3. package/dist/adapt/arms.d.ts +1 -0
  4. package/dist/adapt/arms.js +188 -53
  5. package/dist/adapt/ids.d.ts +12 -1
  6. package/dist/adapt/ids.js +59 -8
  7. package/dist/adapt/instanceLedger.d.ts +50 -0
  8. package/dist/adapt/instanceLedger.js +147 -0
  9. package/dist/adapt/panelTasks.d.ts +1 -1
  10. package/dist/adapt/panelTasks.js +2 -2
  11. package/dist/adapt/textStream.d.ts +1 -0
  12. package/dist/adapt/textStream.js +24 -7
  13. package/dist/adapt/toolCards.d.ts +27 -5
  14. package/dist/adapt/toolCards.js +39 -6
  15. package/dist/adapt/wireShapes.js +6 -2
  16. package/dist/adapt.d.ts +2 -0
  17. package/dist/adapt.js +8 -3
  18. package/dist/adapter/activeRunSelfHeal.d.ts +9 -3
  19. package/dist/adapter/activeRunSelfHeal.js +205 -313
  20. package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -5
  21. package/dist/adapter/downstream/eventToSdkMessage.js +77 -36
  22. package/dist/adapter/downstream/terminalToSdkResult.js +11 -4
  23. package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +5 -2
  24. package/dist/adapter/downstream/turnUsageToModelUsage.js +2 -0
  25. package/dist/adapter/runStream.js +79 -48
  26. package/dist/adapter/types.d.ts +4 -4
  27. package/dist/adapter/types.js +2 -1
  28. package/dist/agentSession/backgroundView.js +2 -1
  29. package/dist/agentSession/engineAgentRegistryRead.d.ts +2 -2
  30. package/dist/agentSession/engineAgentRegistryRead.js +12 -13
  31. package/dist/agentSession/engineSessionBackgroundStop.js +16 -8
  32. package/dist/agentsWireCaps.d.ts +8 -11
  33. package/dist/agentsWireCaps.js +196 -194
  34. package/dist/approvalDecidedByCapability.d.ts +0 -4
  35. package/dist/approvalDecidedByCapability.js +0 -6
  36. package/dist/approvalsStreamLiveCapability.d.ts +0 -4
  37. package/dist/approvalsStreamLiveCapability.js +0 -6
  38. package/dist/assistantInbox.d.ts +34 -0
  39. package/dist/assistantInbox.js +118 -0
  40. package/dist/centerWiringCapability.js +9 -1
  41. package/dist/clientSlice.d.ts +0 -9
  42. package/dist/clientSlice.js +0 -3
  43. package/dist/cloudConfigWireCaps.d.ts +5 -3
  44. package/dist/cloudConfigWireCaps.js +106 -64
  45. package/dist/compensations.js +1 -1
  46. package/dist/connectorStatus.d.ts +21 -0
  47. package/dist/connectorStatus.js +112 -0
  48. package/dist/controlRouter.d.ts +18 -3
  49. package/dist/controlRouter.js +106 -67
  50. package/dist/coreValuePorts.d.ts +29 -4
  51. package/dist/coreValuePorts.js +10 -0
  52. package/dist/corruptSessionCopy.d.ts +2 -1
  53. package/dist/corruptSessionCopy.js +3 -3
  54. package/dist/decideReceipt.d.ts +2 -2
  55. package/dist/decideReceipt.js +5 -2
  56. package/dist/declared/supportFloorDeclared.js +3 -3
  57. package/dist/detachWire.js +3 -2
  58. package/dist/deviceExecutorManagementCapability.d.ts +0 -4
  59. package/dist/deviceExecutorManagementCapability.js +0 -6
  60. package/dist/deviceRebind.d.ts +49 -1
  61. package/dist/deviceRebind.js +221 -3
  62. package/dist/devicesFace.d.ts +34 -0
  63. package/dist/devicesFace.js +90 -0
  64. package/dist/displayUntrusted.d.ts +2 -0
  65. package/dist/displayUntrusted.js +253 -76
  66. package/dist/engineAgentAbsence.d.ts +1 -1
  67. package/dist/engineAgentPanelStore.d.ts +12 -4
  68. package/dist/engineAgentPanelStore.js +16 -11
  69. package/dist/engineCapReader.d.ts +2 -5
  70. package/dist/engineCapReader.js +8 -10
  71. package/dist/engineCapsCache.d.ts +12 -0
  72. package/dist/engineCapsCache.js +76 -18
  73. package/dist/engineCapsChange.d.ts +2 -0
  74. package/dist/engineCapsChange.js +9 -0
  75. package/dist/engineClosedSets.d.ts +1 -0
  76. package/dist/engineClosedSets.js +4 -1
  77. package/dist/engineErrorCodes.d.ts +11 -3
  78. package/dist/engineErrorCodes.js +11 -8
  79. package/dist/engineIdentity.d.ts +1 -0
  80. package/dist/engineIdentity.js +8 -0
  81. package/dist/engineRowRef.d.ts +28 -0
  82. package/dist/engineRowRef.js +114 -0
  83. package/dist/engineSessionMessages.d.ts +68 -0
  84. package/dist/engineSessionMessages.js +370 -0
  85. package/dist/engineTargetSeat.d.ts +27 -0
  86. package/dist/engineTargetSeat.js +199 -0
  87. package/dist/engineToolDirectory.d.ts +0 -9
  88. package/dist/engineToolDirectory.js +1 -94
  89. package/dist/engineUrl.d.ts +18 -0
  90. package/dist/engineUrl.js +52 -0
  91. package/dist/engineWireFor.js +3 -8
  92. package/dist/env/uiLanguage.d.ts +11 -1
  93. package/dist/env/uiLanguage.js +38 -2
  94. package/dist/eventLeg.d.ts +100 -0
  95. package/dist/eventLeg.js +305 -0
  96. package/dist/executionLaneCapability.d.ts +0 -4
  97. package/dist/executionLaneCapability.js +3 -16
  98. package/dist/fileHistoryCaptureCapability.d.ts +2 -5
  99. package/dist/fileHistoryCaptureCapability.js +13 -21
  100. package/dist/fileHistoryLegNotice.d.ts +2 -1
  101. package/dist/fileHistoryLegNotice.js +3 -3
  102. package/dist/fleet/fleetLedger.d.ts +0 -3
  103. package/dist/fleet/fleetLedger.js +21 -36
  104. package/dist/fleet/fleetProjection.d.ts +2 -2
  105. package/dist/fleet/fleetProjection.js +10 -8
  106. package/dist/frameBinding.d.ts +8 -0
  107. package/dist/frameBinding.js +35 -0
  108. package/dist/gateFailureCodes.d.ts +2 -0
  109. package/dist/gateFailureCodes.js +15 -0
  110. package/dist/gateOutcome.d.ts +3 -1
  111. package/dist/gateOutcome.js +41 -8
  112. package/dist/gateVocabulary.d.ts +10 -4
  113. package/dist/gateVocabulary.js +107 -47
  114. package/dist/generated/engineAskFaceTables.js +2 -2
  115. package/dist/generated/engineFactTables.d.ts +22 -3
  116. package/dist/generated/engineFactTables.js +51 -4
  117. package/dist/generated/engineNoticeTables.js +18 -3
  118. package/dist/generated/settingsSchemaFns.d.ts +6 -0
  119. package/dist/generated/settingsSchemaFns.js +57 -0
  120. package/dist/generated/settingsSchemaHooksClassifier.d.ts +2 -0
  121. package/dist/generated/settingsSchemaHooksClassifier.js +254 -0
  122. package/dist/generated/settingsSchemaTables.d.ts +1 -0
  123. package/dist/generated/settingsSchemaTables.js +4 -0
  124. package/dist/generated/settingsSchemaTypes.d.ts +105 -0
  125. package/dist/generated/settingsSchemaTypes.js +1 -0
  126. package/dist/generated/toolNameTables.d.ts +1 -0
  127. package/dist/generated/toolNameTables.js +1 -0
  128. package/dist/hitl/allowRuleShadow.d.ts +8 -2
  129. package/dist/hitl/allowRuleShadow.js +36 -29
  130. package/dist/hitl/approvalCardReading.d.ts +15 -0
  131. package/dist/hitl/approvalCardReading.js +154 -0
  132. package/dist/hitl/approvalCardReasons.d.ts +14 -0
  133. package/dist/hitl/approvalCardReasons.js +119 -0
  134. package/dist/hitl/approvalHolds.d.ts +50 -15
  135. package/dist/hitl/approvalHolds.js +177 -27
  136. package/dist/hitl/approvalResolution.d.ts +51 -26
  137. package/dist/hitl/approvalResolution.js +86 -122
  138. package/dist/hitl/approvalsFeed.js +6 -18
  139. package/dist/hitl/askClassPair.d.ts +2 -0
  140. package/dist/hitl/askClassPair.js +17 -0
  141. package/dist/hitl/askGateWire.d.ts +20 -2
  142. package/dist/hitl/askGateWire.js +50 -1
  143. package/dist/hitl/askParkRowRouting.d.ts +5 -0
  144. package/dist/hitl/askParkRowRouting.js +32 -1
  145. package/dist/hitl/askSettlementLine.d.ts +28 -0
  146. package/dist/hitl/askSettlementLine.js +99 -0
  147. package/dist/hitl/automaticAnswerVerdict.d.ts +3 -2
  148. package/dist/hitl/automaticAnswerVerdict.js +29 -13
  149. package/dist/hitl/crashConverged.d.ts +11 -20
  150. package/dist/hitl/crashConverged.js +56 -0
  151. package/dist/hitl/crashConvergedNotice.d.ts +6 -3
  152. package/dist/hitl/crashConvergedNotice.js +48 -24
  153. package/dist/hitl/decisionConflict.js +3 -10
  154. package/dist/hitl/decisionRefusal.d.ts +40 -1
  155. package/dist/hitl/decisionRefusal.js +199 -42
  156. package/dist/hitl/frameRouter.d.ts +5 -2
  157. package/dist/hitl/frameRouter.js +27 -24
  158. package/dist/hitl/gateLedger.d.ts +2 -0
  159. package/dist/hitl/gateLedger.js +50 -13
  160. package/dist/hitl/hitlBridge.d.ts +2 -2
  161. package/dist/hitl/hitlBridge.js +13 -10
  162. package/dist/hitl/livePendingAsk.d.ts +3 -3
  163. package/dist/hitl/livePendingAsk.js +7 -18
  164. package/dist/hitl/parkResolver.d.ts +2 -24
  165. package/dist/hitl/parkResolver.js +61 -97
  166. package/dist/hitl/persistedRulesWire.d.ts +25 -0
  167. package/dist/hitl/persistedRulesWire.js +104 -2
  168. package/dist/hitl/planReviewWire.d.ts +31 -13
  169. package/dist/hitl/planReviewWire.js +120 -78
  170. package/dist/hitl/refusedToolNameProse.d.ts +0 -1
  171. package/dist/hitl/refusedToolNameProse.js +0 -3
  172. package/dist/hitl/replayedApproval.d.ts +4 -3
  173. package/dist/hitl/replayedApproval.js +7 -4
  174. package/dist/hitl/sessionPolicyDeliverable.d.ts +2 -0
  175. package/dist/hitl/sessionPolicyDeliverable.js +48 -7
  176. package/dist/hitl/sessionPolicyWire.d.ts +34 -21
  177. package/dist/hitl/sessionPolicyWire.js +179 -70
  178. package/dist/hitl/standingRows.d.ts +20 -2
  179. package/dist/hitl/standingRows.js +69 -8
  180. package/dist/hitl/suspendedReopen.d.ts +2 -1
  181. package/dist/hitl/suspendedReopen.js +7 -10
  182. package/dist/hitl/toolApprovalWire.d.ts +103 -156
  183. package/dist/hitl/toolApprovalWire.js +661 -411
  184. package/dist/hookMatcherVerdict.d.ts +6 -1
  185. package/dist/hookMatcherVerdict.js +14 -7
  186. package/dist/hooksWireCaps.d.ts +2 -2
  187. package/dist/hooksWireCaps.js +174 -81
  188. package/dist/host.d.ts +21 -0
  189. package/dist/hostTurnSettings.d.ts +71 -0
  190. package/dist/hostTurnSettings.js +291 -0
  191. package/dist/humanTurn.d.ts +14 -1
  192. package/dist/humanTurn.js +100 -12
  193. package/dist/humanTurnRead.d.ts +2 -0
  194. package/dist/humanTurnRead.js +22 -1
  195. package/dist/i18n/messages.en.d.ts +703 -0
  196. package/dist/i18n/messages.en.js +701 -0
  197. package/dist/i18n/messages.zh.d.ts +3 -0
  198. package/dist/i18n/messages.zh.js +837 -0
  199. package/dist/i18n/translate.d.ts +25 -0
  200. package/dist/i18n/translate.js +127 -0
  201. package/dist/index.d.ts +78 -29
  202. package/dist/index.js +54 -18
  203. package/dist/inheritEnvCapability.d.ts +32 -0
  204. package/dist/inheritEnvCapability.js +104 -0
  205. package/dist/inheritEnvWire.d.ts +3 -2
  206. package/dist/inheritEnvWire.js +12 -14
  207. package/dist/interactiveHalt.js +17 -6
  208. package/dist/liveModelCatalog.d.ts +3 -3
  209. package/dist/liveModelCatalog.js +15 -13
  210. package/dist/liveQuestionStore.d.ts +2 -21
  211. package/dist/mcpLiveness.d.ts +2 -1
  212. package/dist/mcpPanel.d.ts +10 -1
  213. package/dist/mcpPanel.js +40 -13
  214. package/dist/mcpProbeCapability.d.ts +2 -1
  215. package/dist/mcpProbeCapability.js +7 -11
  216. package/dist/mcpProbeWire.d.ts +9 -2
  217. package/dist/mcpProbeWire.js +6 -1
  218. package/dist/mcpWireCaps.d.ts +47 -2
  219. package/dist/mcpWireCaps.js +164 -16
  220. package/dist/memoryEntriesWire.d.ts +4 -9
  221. package/dist/memoryEntriesWire.js +5 -10
  222. package/dist/model/modelSupplyRules.d.ts +0 -2
  223. package/dist/model/modelSupplyRules.js +3 -12
  224. package/dist/model/modelsPolicy.d.ts +0 -5
  225. package/dist/model/modelsPolicy.js +6 -36
  226. package/dist/model/onboardJudgement.d.ts +93 -0
  227. package/dist/model/onboardJudgement.js +415 -0
  228. package/dist/model/subagentModelWord.d.ts +31 -0
  229. package/dist/model/subagentModelWord.js +261 -0
  230. package/dist/model/tierVocabulary.d.ts +2 -0
  231. package/dist/model/tierVocabulary.js +17 -5
  232. package/dist/modelCapabilityProbe.d.ts +64 -0
  233. package/dist/modelCapabilityProbe.js +600 -0
  234. package/dist/modelIdentityResolvability.d.ts +3 -0
  235. package/dist/modelIdentityResolvability.js +26 -17
  236. package/dist/notifications.d.ts +6 -0
  237. package/dist/notifications.js +71 -23
  238. package/dist/outputStyleSeatCapability.d.ts +0 -3
  239. package/dist/outputStyleSeatCapability.js +0 -3
  240. package/dist/ownKey.d.ts +22 -0
  241. package/dist/ownKey.js +94 -3
  242. package/dist/panelReduce.d.ts +21 -10
  243. package/dist/panelReduce.js +728 -550
  244. package/dist/panelStateHolder.d.ts +3 -7
  245. package/dist/panelStateHolder.js +14 -130
  246. package/dist/peerLaneCapability.d.ts +0 -4
  247. package/dist/peerLaneCapability.js +0 -6
  248. package/dist/permissionRuleIssue.js +2 -0
  249. package/dist/permissionRulesWriteCapability.d.ts +2 -5
  250. package/dist/permissionRulesWriteCapability.js +8 -12
  251. package/dist/permissionWireCaps.d.ts +2 -2
  252. package/dist/permissionWireCaps.js +2 -0
  253. package/dist/permissionsAllowGrantCapability.d.ts +0 -4
  254. package/dist/permissionsAllowGrantCapability.js +0 -6
  255. package/dist/permissionsDeclaration.d.ts +0 -11
  256. package/dist/permissionsDeclaration.js +60 -53
  257. package/dist/pluginHooksWire.d.ts +46 -3
  258. package/dist/pluginHooksWire.js +219 -16
  259. package/dist/preemptWire.d.ts +44 -0
  260. package/dist/preemptWire.js +83 -0
  261. package/dist/printModelStatus.d.ts +63 -0
  262. package/dist/printModelStatus.js +155 -0
  263. package/dist/printToolResultFrame.js +15 -4
  264. package/dist/replayBody.d.ts +3 -0
  265. package/dist/replayBody.js +14 -0
  266. package/dist/request/taskRequest.d.ts +11 -6
  267. package/dist/request/taskRequest.js +112 -19
  268. package/dist/resumeRefusalCopy.d.ts +13 -9
  269. package/dist/resumeRefusalCopy.js +81 -118
  270. package/dist/retryStatus.d.ts +45 -0
  271. package/dist/retryStatus.js +185 -1
  272. package/dist/rewindArchiveCapability.d.ts +0 -4
  273. package/dist/rewindArchiveCapability.js +2 -10
  274. package/dist/rewindArchiveReading.d.ts +0 -1
  275. package/dist/rewindArchiveReading.js +0 -3
  276. package/dist/runCancelContext.d.ts +2 -1
  277. package/dist/runCancelContext.js +4 -2
  278. package/dist/runReattachPolicy.d.ts +31 -0
  279. package/dist/runReattachPolicy.js +46 -0
  280. package/dist/runTerminal.d.ts +3 -6
  281. package/dist/runTerminal.js +8 -9
  282. package/dist/sandboxWire.d.ts +3 -1
  283. package/dist/sandboxWire.js +76 -27
  284. package/dist/scenarioRefusal.d.ts +9 -0
  285. package/dist/scenarioRefusal.js +111 -0
  286. package/dist/scenarioWire.js +1 -1
  287. package/dist/seam.d.ts +48 -2
  288. package/dist/seam.js +25 -4
  289. package/dist/seatContract.d.ts +311 -18
  290. package/dist/seatContract.js +824 -100
  291. package/dist/selfOrchestrationDenial.js +7 -19
  292. package/dist/sessionIdSegment.d.ts +6 -0
  293. package/dist/sessionIdSegment.js +24 -0
  294. package/dist/sessionMemoryStatus.js +6 -11
  295. package/dist/sessionPolicyCapability.d.ts +2 -5
  296. package/dist/sessionPolicyCapability.js +8 -12
  297. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -3
  298. package/dist/sessionSyncFileHistoryCapability.js +0 -3
  299. package/dist/settingsHooksSalvage.d.ts +44 -0
  300. package/dist/settingsHooksSalvage.js +211 -0
  301. package/dist/skillToolListsCapability.d.ts +2 -4
  302. package/dist/skillToolListsCapability.js +8 -9
  303. package/dist/sqlEngineCapability.d.ts +0 -4
  304. package/dist/sqlEngineCapability.js +0 -6
  305. package/dist/sseIdleTriage.js +3 -2
  306. package/dist/steerPriorityCapability.d.ts +0 -3
  307. package/dist/steerPriorityCapability.js +0 -3
  308. package/dist/streamDrainingLegs.d.ts +4 -0
  309. package/dist/streamDrainingLegs.js +6 -1
  310. package/dist/subagent/engineCompactWire.d.ts +0 -1
  311. package/dist/subagent/engineCompactWire.js +7 -5
  312. package/dist/subagent/engineSubagentResume.js +15 -9
  313. package/dist/subagent/engineTaskHandleWire.js +4 -7
  314. package/dist/subagentContentStore.d.ts +3 -0
  315. package/dist/subagentContentStore.js +22 -10
  316. package/dist/submitReopen.d.ts +24 -0
  317. package/dist/submitReopen.js +75 -0
  318. package/dist/terminalStatuses.d.ts +3 -0
  319. package/dist/terminalStatuses.js +10 -2
  320. package/dist/toolHistoryMismatch.js +3 -2
  321. package/dist/toolResult.d.ts +10 -1
  322. package/dist/toolResult.js +34 -6
  323. package/dist/toolRoster.d.ts +3 -4
  324. package/dist/toolRoster.js +21 -22
  325. package/dist/turnFailurePresentation.d.ts +39 -0
  326. package/dist/turnFailurePresentation.js +402 -0
  327. package/dist/usageAnalytics.js +6 -11
  328. package/dist/userAttachmentEnvelope.d.ts +1 -0
  329. package/dist/userAttachmentEnvelope.js +37 -7
  330. package/dist/userAttachmentSeat.d.ts +21 -0
  331. package/dist/userAttachmentSeat.js +75 -0
  332. package/dist/userInvokedSkillsCapability.d.ts +11 -4
  333. package/dist/userInvokedSkillsCapability.js +31 -10
  334. package/dist/webSearchBackendCapability.d.ts +0 -4
  335. package/dist/webSearchBackendCapability.js +0 -6
  336. package/dist/wireErrorTriage.d.ts +7 -0
  337. package/dist/wireErrorTriage.js +26 -23
  338. package/dist/wireFailureShape.d.ts +62 -5
  339. package/dist/wireFailureShape.js +106 -5
  340. package/dist/wireRefusalCopy.d.ts +25 -3
  341. package/dist/wireRefusalCopy.js +74 -80
  342. package/dist/workflowsJournalCapability.d.ts +2 -5
  343. package/dist/workflowsJournalCapability.js +8 -12
  344. package/dist/writeProtectionCapability.d.ts +0 -4
  345. package/dist/writeProtectionCapability.js +0 -6
  346. package/docs/INTEGRATION-CLIENTS.md +1775 -56
  347. package/package.json +6 -5
  348. package/dist/attachmentSeatCapability.d.ts +0 -12
  349. package/dist/attachmentSeatCapability.js +0 -23
  350. package/dist/hitl/approvalOutcomeNote.d.ts +0 -10
  351. package/dist/hitl/approvalOutcomeNote.js +0 -29
  352. package/dist/sessionReadRedactionCapability.d.ts +0 -13
  353. package/dist/sessionReadRedactionCapability.js +0 -35
@@ -1,27 +1,31 @@
1
- import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, DecideTransportRetryExhaustedError, HitlBridge, NO_APPROVAL_CARD_PORT_REASON, denyReasonForWire, findPendingForTask, hitlSafetyCodeOf, readDecideCurrentPending, readWireCodes, readWireErrorCodeReading, } from './hitlBridge.js';
1
+ import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, HitlBridge, NO_APPROVAL_CARD_PORT_REASON, denyReasonForWire, findPendingForTask, readWireErrorCodeReading, } from './hitlBridge.js';
2
2
  import { askParkRowArm } from './askParkRowRouting.js';
3
3
  import { parkGateArmOf } from './frameRouter.js';
4
4
  import { seatSessionRowOf } from './standingRows.js';
5
5
  import { hostLog } from '../host.js';
6
6
  import { guardedRead, readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
7
- import { readAskClassPair } from './askClassPair.js';
8
- import { readAllowRuleShadowFromWire } from './allowRuleShadow.js';
7
+ import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPair } from './askClassPair.js';
8
+ import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
9
+ import { approvalWindowKeysOf, readRequiresRealApproval } from './approvalCardReading.js';
10
+ import { readAllowRuleShadowFromWire, retiredAllowRuleShadowKeyPresent, RETIRED_SHADOW_EVIDENCE_LABEL } from './allowRuleShadow.js';
9
11
  import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
10
12
  import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
11
13
  import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
14
+ import { CORE_ALLOW_ENTRY_INERT_REASONS } from '../generated/engineFactTables.js';
15
+ import { msg, renderMessage } from '../i18n/translate.js';
12
16
  import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
13
17
  import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
14
18
  import { displayUntrusted } from '../displayUntrusted.js';
15
- import { approvalDecisionHolds, awaitLiveCardHolder, holdLiveCard, joinLiveCard, liveCardHoldFor, liveCardHoldOf, clearSettledApprovals, settledApprovalOf, settledForDurableLane, settledLiveCardOutcome } from './approvalHolds.js';
16
- import { approvalResolutionOf } from './approvalResolution.js';
19
+ import { frameBindingOf } from '../frameBinding.js';
20
+ import { approvalDecisionHolds, approvalHoldFor, awaitApprovalHolder, claimApprovalCardFor, holdLiveCard, joinLiveCard, liveCardHoldOf, clearSettledApprovals, noteHolderSettledApproval, settledApprovalOf, settledForDurableLane, settledLiveCardOutcome } from './approvalHolds.js';
21
+ import { joinedResolution, resolutionOfDecisionFailure } from './approvalResolution.js';
22
+ import { parkRowDeadlineState } from './parkExpiryJudgements.js';
17
23
  import { isApprovalMandateWord } from '../gateVocabulary.js';
18
24
  import { denyAttributionRefusalFromError } from '../wireRefusalCopy.js';
19
25
  import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
20
26
  import { decideFailureNote } from '../decideFailureNote.js';
21
- import { readRefusalField, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
22
- import { refusalCauseWordOf } from './approvalEndingCause.js';
23
- import { readDecisionRefusalAsk } from './decisionRefusal.js';
24
- import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED, refusalSaysStillPending } from '../engineErrorCodes.js';
27
+ import { wireFailureTextsOf } from '../wireFailureShape.js';
28
+ import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
25
29
  import { readDecideReceipt } from '../decideReceipt.js';
26
30
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, TOOL_APPROVAL_CARD_WORDS, } from '@sema-agent/sdk';
27
31
  export function toolNameIsFsWrite(name) {
@@ -82,6 +86,14 @@ export function readApprovalDenySettledBy(decision) {
82
86
  : undefined;
83
87
  }
84
88
  export const RETRACTED_CARD_DECISION_KIND = 'retracted';
89
+ function parkRowOf(taskId, row, gatedCallId) {
90
+ const d = parkRowDeadlineState(row, Date.now());
91
+ return Object.freeze({
92
+ taskId,
93
+ ...(gatedCallId !== undefined && gatedCallId !== '' ? { gatedCallId } : {}),
94
+ ...(d.kind !== 'none' ? { deadlineMs: d.deadlineMs } : {}),
95
+ });
96
+ }
85
97
  const cardPortByKey = createSessionSlot();
86
98
  const cardPortMissesByKey = new Map();
87
99
  function bumpCardPortMiss(sessionKey) {
@@ -149,14 +161,6 @@ export function rememberRefusalOf(error) {
149
161
  function rememberRefusalIn(reading) {
150
162
  return reading.state === 'code' ? REMEMBER_REFUSAL_CODES.find((c) => c === reading.code) : undefined;
151
163
  }
152
- function rememberFallbackKind(e) {
153
- const reading = readWireErrorCodeReading(e);
154
- if (rememberRefusalIn(reading) !== undefined)
155
- return 'refused';
156
- if (reading.state !== 'absent')
157
- return undefined;
158
- return readToolApprovalRespondRefusal(e)?.status === 400 ? 'legacy' : undefined;
159
- }
160
164
  function logAfterDecided(msg) {
161
165
  try {
162
166
  hostLog('debug', msg);
@@ -223,7 +227,7 @@ function heldDurableDecision(holdKey, readMark) {
223
227
  const recent = approvalDecisionHolds.settled.get(holdKey);
224
228
  return recent !== undefined && recent.at > readMark ? recent.hold : undefined;
225
229
  }
226
- function holdDurableDecision(holdKey, callerSignal, decide) {
230
+ function holdDurableDecision(holdKey, identity, callerSignal, decide) {
227
231
  const h = approvalDecisionHolds;
228
232
  const interruption = new AbortController();
229
233
  let settled = false;
@@ -241,7 +245,7 @@ function holdDurableDecision(holdKey, callerSignal, decide) {
241
245
  resolveOutcome = resolve;
242
246
  rejectOutcome = reject;
243
247
  });
244
- const hold = { lane: 'durable', outcome, presented: false, interrupt, cardSignal: interruption.signal };
248
+ const hold = { lane: 'durable', identity: Object.freeze({ ...identity }), phase: 'card', outcome, presented: false, interrupt, cardSignal: interruption.signal };
245
249
  h.inFlight.set(holdKey, hold);
246
250
  const release = () => {
247
251
  if (h.inFlight.get(holdKey) === hold)
@@ -283,7 +287,7 @@ async function joinDurableDecision(hold, deps, signal) {
283
287
  catch {
284
288
  }
285
289
  }
286
- return { ...o, joinedInFlight: true };
290
+ return joinedResolution(o);
287
291
  }
288
292
  export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signal, parkGatedCallId, parkToolName) {
289
293
  const decidedByReading = decidedByReadingSnapshotOf(deps);
@@ -299,19 +303,22 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
299
303
  closeDurableRead(readMark);
300
304
  }
301
305
  if (!found.ok)
302
- return { kind: 'failed', stage: 'fetch', reason: found.reason, ...(found.code !== undefined ? { code: found.code } : {}) };
306
+ return { kind: 'not_sent', lane: 'decide', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}) };
303
307
  if (held !== undefined)
304
308
  return joinDurableDecision(held, deps, signal);
305
- const liveHolder = liveCardHoldFor(parkRowHolderIdentity(found.pending, found.gatedCallId));
306
- if (liveHolder !== undefined) {
307
- const joined = await awaitLiveCardHolder(liveHolder, signal);
308
- if (joined !== undefined)
309
- return fsOutcomeOfJoinedLiveCard(joined, found.gatedCallId);
309
+ const rowIdentity = parkRowHolderIdentity(found.pending, found.gatedCallId);
310
+ const otherHolder = approvalHoldFor(rowIdentity, (h) => h.lane === 'durable');
311
+ if (otherHolder !== undefined) {
312
+ const wait = await awaitApprovalHolder(otherHolder, signal, 'decide');
313
+ if (wait.kind === 'settled')
314
+ return joinedResolution(wait.outcome, found.gatedCallId);
315
+ if (wait.kind === 'left')
316
+ return { kind: 'not_sent', lane: 'decide', cause: 'retracted', ...(found.gatedCallId !== undefined ? { gatedCallId: found.gatedCallId } : {}), joinedInFlight: true };
310
317
  return surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signal, parkGatedCallId, parkToolName);
311
318
  }
312
319
  const callKey = approvalCallKey(found.gatedCallId, taskId);
313
320
  const fetched = found;
314
- return holdDurableDecision(durableHoldKey(fetched.pending, callKey), signal, async (hold) => {
321
+ return holdDurableDecision(durableHoldKey(fetched.pending, callKey), rowIdentity, signal, async (hold) => {
315
322
  const slot = {};
316
323
  const out = await presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, fetched, decidedByReading, callKey, hold, slot);
317
324
  noteCallOutcome(slot, out);
@@ -319,34 +326,17 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
319
326
  });
320
327
  }
321
328
  function parkRowHolderIdentity(row, gatedCallId) {
329
+ const askSig = carrierAskSig(row);
322
330
  return {
323
331
  ...(typeof row.taskId === 'string' && row.taskId !== '' ? { taskId: row.taskId } : {}),
324
332
  ...(gatedCallId !== undefined && gatedCallId !== '' ? { toolCallId: gatedCallId } : {}),
325
333
  ...(typeof row.sessionId === 'string' && row.sessionId !== '' ? { sessionId: row.sessionId } : {}),
326
- };
327
- }
328
- function fsOutcomeOfJoinedLiveCard(o, gatedCallId) {
329
- if (o.aborted === true)
330
- return { kind: 'aborted', gatedCallId, joinedInFlight: true };
331
- if (o.decision === 'unresolved')
332
- return { kind: 'retracted', gatedCallId, joinedInFlight: true };
333
- return {
334
- kind: 'decided',
335
- gatedCallId,
336
- ...(o.decision === 'deny'
337
- ? {
338
- denied: true,
339
- ...(o.denySettledBy !== undefined ? { denySettledBy: o.denySettledBy } : {}),
340
- ...(o.denyReason !== undefined ? { denyReason: o.denyReason } : {}),
341
- }
342
- : {}),
343
- ...(o.denyAttributionDropped === true ? { denyAttributionDropped: true } : {}),
344
- ...(o.denyAttributionRenamed === true ? { denyAttributionRenamed: true } : {}),
345
- joinedInFlight: true,
334
+ ...(askSig !== undefined ? { askSig } : {}),
346
335
  };
347
336
  }
348
337
  async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, found, decidedByReading, callKey, hold, ledgerSlot) {
349
338
  const { pending, gatedCallId } = found;
339
+ const at = { lane: 'decide', ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
350
340
  const toolName = typeof pending.toolName === 'string' ? pending.toolName : '';
351
341
  const streamArgs = gatedCallId !== undefined ? argsByCall.get(gatedCallId) : undefined;
352
342
  const rowInput = pending.input;
@@ -359,7 +349,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
359
349
  : undefined;
360
350
  const args = streamArgs !== undefined && streamArgs !== null ? streamArgs : rowArgsGap !== undefined ? {} : rowInput;
361
351
  if (typeof args !== 'object' || args === null) {
362
- return { kind: 'failed', stage: 'input', gatedCallId, reason: 'gate has no tool input payload' };
352
+ return { kind: 'not_sent', ...at, cause: 'no_input', detail: 'gate has no tool input payload' };
363
353
  }
364
354
  const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
365
355
  const ruleOffersReadOnly = readRuleOffers(pending.ruleOffers);
@@ -381,7 +371,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
381
371
  }
382
372
  : {}),
383
373
  ...(pending.governanceForced === true ? { governanceForced: true } : {}),
384
- ...(pending.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
374
+ ...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
385
375
  ...(hasOwnStrictTrue(pending, 'mandated') ? { mandated: true } : {}),
386
376
  ...((w) => (w !== undefined ? { mandate: w } : {}))(readApprovalMandate(pending)),
387
377
  ...((o) => (o !== undefined ? { origin: o } : {}))(readAskOrigin(pending)),
@@ -402,92 +392,81 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
402
392
  : {}),
403
393
  allowSessionCarried: true,
404
394
  grantBinding: readApprovalGrantBinding(pending),
395
+ parkRow: parkRowOf(taskId, pending, gatedCallId),
405
396
  }));
397
+ hold.phase = 'respond';
406
398
  if (rowArgsGap !== undefined && card.kind === 'allow' && card.updatedInput !== undefined) {
407
399
  hostLog('error', `liveToolApprovalWire: approval card for ${gatedCallId ?? `task ${taskId}`} returned an edited approval, but the tool input is ${rowArgsGap === 'truncated' ? 'a size marker on the pending row' : 'missing from both the stream and the pending row'} — nothing sent (the park stays pending)`);
408
400
  surfaceEditRefusedOnBlindAsk();
409
- return { kind: 'failed', stage: 'card', gatedCallId, reason: PARK_EDIT_REFUSED_REASON, editRefused: true };
401
+ return { kind: 'not_sent', ...at, cause: 'edit_refused', detail: PARK_EDIT_REFUSED_REASON };
410
402
  }
411
403
  const decideSpan = bridge.openDecideOperation();
412
404
  const decideOpts = { ...(signal ? { signal } : {}), operation: decideSpan };
413
405
  try {
414
406
  switch (card.kind) {
415
407
  case 'failed':
416
- return { kind: 'failed', stage: 'card', gatedCallId, reason: card.reason };
408
+ return { kind: 'not_sent', ...at, cause: 'card_unavailable', ...(typeof card.reason === 'string' && card.reason !== '' ? { detail: card.reason } : {}) };
417
409
  case 'retracted':
418
410
  hostLog('debug', `liveHitlAskWire: approval card retracted without a decision for ${gatedCallId ?? '(no call)'} — nothing sent`);
419
- return { kind: 'retracted', gatedCallId };
411
+ return { kind: 'not_sent', ...at, cause: 'retracted' };
420
412
  case 'aborted':
421
413
  {
422
414
  const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending);
423
415
  hold.trailing = cancelDeny;
424
416
  observeCancelByDeny(cancelDeny, taskId);
425
417
  }
426
- return { kind: 'aborted', gatedCallId };
418
+ return { kind: 'unsettled', ...at, cause: 'interrupted' };
427
419
  case 'allow':
428
420
  try {
429
421
  let rememberRefused = false;
430
- if (card.allowSession) {
422
+ const approveBody = answerPayloadFor(card, 'decide');
423
+ const { remember: _remember, ...plainApproveBody } = approveBody;
424
+ void _remember;
425
+ emitAnswerCommitted(callKey, 'decide', card, approveBody);
426
+ if (approveBody.remember === 'session') {
431
427
  let sessionReceipt;
432
428
  let sessionDecided = false;
433
429
  try {
434
- const raw = await bridge.decideTool({ decision: 'approve', remember: 'session', ...(card.updatedInput !== undefined ? { updatedInput: card.updatedInput } : {}) }, gatedCallId, decideOpts, pending);
430
+ const raw = await bridge.decideTool(approveBody, gatedCallId, decideOpts, pending);
435
431
  sessionReceipt = readDecideReceipt(raw);
436
432
  sessionDecided = true;
437
433
  }
438
434
  catch (e) {
439
- const fallbackKind = rememberFallbackKind(e);
440
- if (fallbackKind === undefined)
435
+ if (rememberRefusalOf(e) === undefined)
441
436
  throw e;
442
- rememberRefused = fallbackKind === 'refused';
437
+ rememberRefused = true;
443
438
  logAfterDecided(`liveToolApprovalWire: decide(approve+remember) refused the remember part (${String(e)}) — falling back to plain approve`);
444
439
  }
445
440
  if (sessionDecided) {
446
441
  if (sessionReceipt?.rememberApplied === false)
447
442
  surfaceRememberNotApplied();
448
- return { kind: 'decided', gatedCallId, ...(sessionReceipt !== undefined ? { receipt: sessionReceipt } : {}) };
443
+ return { kind: 'decided', ...at, decision: 'allow', ...(sessionReceipt !== undefined ? { receipt: sessionReceipt } : {}) };
449
444
  }
450
445
  }
451
- const raw = await bridge.decideTool({ decision: 'approve', ...(card.updatedInput !== undefined ? { updatedInput: card.updatedInput } : {}) }, gatedCallId, decideOpts, pending);
446
+ const raw = await bridge.decideTool(plainApproveBody, gatedCallId, decideOpts, pending);
452
447
  const receipt = readDecideReceipt(raw);
453
448
  if (rememberRefused)
454
449
  surfaceRememberNotApplied();
455
- return { kind: 'decided', gatedCallId, ...(receipt !== undefined ? { receipt } : {}) };
450
+ return { kind: 'decided', ...at, decision: 'allow', ...(receipt !== undefined ? { receipt } : {}) };
456
451
  }
457
452
  catch (e) {
458
- const currentPending = readDecideCurrentPending(e);
459
- const wireCodes = readWireCodes(e);
460
- const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
461
- const safetyCode = hitlSafetyCodeOf(e);
462
453
  const failureTexts = wireFailureTextsOf(e);
463
454
  hostLog('debug', `liveToolApprovalWire: decide(approve) failed: ${failureTexts.log}`);
464
- return {
465
- kind: 'failed',
466
- stage: 'decide',
467
- gatedCallId,
468
- reason: `decide(approve) failed: ${failureTexts.reason}${decideFailureNote(e, 'approve')}`,
469
- ...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
470
- ...(currentPending !== undefined ? { currentPending } : {}),
471
- ...wireCodes,
472
- ...(wireRetriable ? { retriable: true } : {}),
473
- ...(safetyCode !== undefined ? { safetyCode } : {}),
474
- };
455
+ return resolutionOfDecisionFailure(e, { lane: 'decide', route: 'approval_decide', gatedCallId, detail: `decide(approve) failed: ${failureTexts.reason}${decideFailureNote(e, 'approve')}` });
475
456
  }
476
457
  case 'deny': {
477
458
  const denySettledByForWire = readApprovalDenySettledBy(card);
478
459
  const attributionKeys = decisionAttributionKeyOf(decidedByReading, denySettledByForWire);
479
- const denyReasonSnapshot = typeof card.reason === 'string' ? card.reason : undefined;
460
+ const denyReasonSnapshot = ((r) => (typeof r === 'string' ? r : undefined))(card.reason);
480
461
  let lastSendCarriedAttribution = false;
481
462
  try {
482
- const denyBody = {
483
- decision: 'deny',
484
- reason: denyReasonForWire(denyReasonSnapshot, `task ${taskId}`) ?? DEFAULT_DENY_REASON,
485
- ...attributionKeys,
486
- };
463
+ const denyPayload = { decision: 'deny', reason: answerPayloadFor({ kind: 'deny', ...(denyReasonSnapshot !== undefined ? { reason: denyReasonSnapshot } : {}) }, 'decide').reason };
464
+ const denyBody = { ...denyPayload, ...attributionKeys };
487
465
  let attributionDropped = false;
488
466
  let attributionRenamed = false;
489
467
  let raw;
490
468
  lastSendCarriedAttribution = carriesAttributionKey(attributionKeys);
469
+ emitAnswerCommitted(callKey, 'decide', card, denyPayload);
491
470
  try {
492
471
  raw = await bridge.decideTool(denyBody, gatedCallId, decideOpts, pending);
493
472
  }
@@ -527,39 +506,25 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
527
506
  }
528
507
  }
529
508
  const receipt = readDecideReceipt(raw);
530
- const denySettledBy = denySettledByForWire;
531
- const denyReason = denyReasonSnapshot !== undefined && denyReasonSnapshot.trim() !== '' ? denyReasonSnapshot : undefined;
509
+ const deny = denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed);
532
510
  return {
533
511
  kind: 'decided',
534
- gatedCallId,
535
- denied: true,
536
- ...(denySettledBy !== undefined ? { denySettledBy } : {}),
537
- ...(denyReason !== undefined ? { denyReason } : {}),
538
- ...(attributionDropped ? { denyAttributionDropped: true } : {}),
539
- ...(attributionRenamed ? { denyAttributionRenamed: true } : {}),
512
+ ...at,
513
+ decision: 'deny',
514
+ ...(deny !== undefined ? { deny } : {}),
540
515
  ...(receipt !== undefined ? { receipt } : {}),
541
516
  };
542
517
  }
543
518
  catch (e) {
544
- const currentPending = readDecideCurrentPending(e);
545
- const wireCodes = readWireCodes(e);
546
- const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
547
- const safetyCode = hitlSafetyCodeOf(e);
548
- const attributionRefusal = denyAttributionRefusalFromError(e, lastSendCarriedAttribution);
549
519
  const failureTexts = wireFailureTextsOf(e);
550
520
  hostLog('debug', `liveToolApprovalWire: decide(deny) failed: ${failureTexts.log}`);
551
- return {
552
- kind: 'failed',
553
- stage: 'decide',
521
+ return resolutionOfDecisionFailure(e, {
522
+ lane: 'decide',
523
+ route: 'approval_decide',
554
524
  gatedCallId,
555
- reason: `decide(deny) failed: ${failureTexts.reason}${decideFailureNote(e, 'deny')}`,
556
- ...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
557
- ...(currentPending !== undefined ? { currentPending } : {}),
558
- ...wireCodes,
559
- ...(wireRetriable ? { retriable: true } : {}),
560
- ...(safetyCode !== undefined ? { safetyCode } : {}),
561
- ...(attributionRefusal !== null ? { denyAttributionRefusal: attributionRefusal } : {}),
562
- };
525
+ detail: `decide(deny) failed: ${failureTexts.reason}${decideFailureNote(e, 'deny')}`,
526
+ attributionCarried: lastSendCarriedAttribution,
527
+ });
563
528
  }
564
529
  }
565
530
  }
@@ -610,6 +575,7 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
610
575
  'probeReason',
611
576
  'rememberRefused',
612
577
  'boundGrantDigest',
578
+ 'boundGrantAbsence',
613
579
  'boundGrants',
614
580
  'classifierNoRuling',
615
581
  'ruleEvidence',
@@ -621,7 +587,7 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
621
587
  'outcome',
622
588
  'cause',
623
589
  ];
624
- const _mirrorPin = [true, true];
590
+ const _mirrorPin = [true];
625
591
  void _mirrorPin;
626
592
  const MANDATED_ABSENCE_WORD = 'mandated';
627
593
  export function approvalIsMandated(v) {
@@ -686,20 +652,76 @@ function isToolApprovalDelegation(v) {
686
652
  return d.agentName === undefined || typeof d.agentName === 'string';
687
653
  }
688
654
  export function isFromSubagent(frame) {
689
- if (frame.fromSubagent === true)
690
- return true;
691
- return typeof frame.sourceTaskId === 'string' && frame.sourceTaskId.length > 0;
655
+ return frame.fromSubagent === true;
692
656
  }
693
- const RESPOND_DECISIONS = ['allow', 'allow_session', 'deny'];
657
+ export const RESPOND_DECISIONS = Object.freeze(['allow', 'allow_session', 'deny']);
694
658
  const _respondDecisionPins = [true, true];
695
659
  void _respondDecisionPins;
696
660
  function isRespondDecision(v) {
697
661
  return typeof v === 'string' && RESPOND_DECISIONS.includes(v);
698
662
  }
663
+ function respondDecisionOfAnswer(answer) {
664
+ if (answer.kind === 'allow')
665
+ return answer.allowSession ? 'allow_session' : 'allow';
666
+ return answer.kind === 'deny' ? 'deny' : undefined;
667
+ }
668
+ function respondNoteGate(reason, capable) {
669
+ if (reason === undefined || reason.trim() === '')
670
+ return { dropped: 'blank' };
671
+ if (!capable)
672
+ return { dropped: 'capability' };
673
+ if (reason.length > MAX_RESPOND_NOTE_CHARS)
674
+ return { dropped: 'oversize' };
675
+ return { note: reason };
676
+ }
677
+ export function answerPayloadFor(answer, lane, caps) {
678
+ const word = respondDecisionOfAnswer(answer);
679
+ if (word === undefined)
680
+ return undefined;
681
+ if (lane === 'decide') {
682
+ if (answer.kind !== 'allow') {
683
+ const reason = answer.kind === 'deny' && typeof answer.reason === 'string' ? answer.reason : undefined;
684
+ return Object.freeze({ decision: 'deny', reason: denyReasonForWire(reason, 'answer') ?? DEFAULT_DENY_REASON });
685
+ }
686
+ return Object.freeze({
687
+ decision: 'approve',
688
+ ...(word === 'allow_session' ? { remember: 'session' } : {}),
689
+ ...(answer.updatedInput !== undefined ? { updatedInput: answer.updatedInput } : {}),
690
+ });
691
+ }
692
+ if (answer.kind === 'allow')
693
+ return Object.freeze({ decision: word, ...(answer.updatedInput !== undefined ? { updatedInput: answer.updatedInput } : {}) });
694
+ const gate = respondNoteGate(answer.kind === 'deny' && typeof answer.reason === 'string' ? answer.reason : undefined, caps?.approvalDecisionNoteCapable === true);
695
+ return Object.freeze({ decision: word, ...('note' in gate ? { note: gate.note } : {}) });
696
+ }
697
+ const ANSWER_CARRIAGE_NONE = Object.freeze({ allowNote: 'none' });
698
+ export function answerCarriage(card) {
699
+ void card;
700
+ return ANSWER_CARRIAGE_NONE;
701
+ }
702
+ export function observeApprovalAnswerCommitted(listener) {
703
+ answerCommittedListeners.add(listener);
704
+ return () => {
705
+ answerCommittedListeners.delete(listener);
706
+ };
707
+ }
708
+ const answerCommittedListeners = new Set();
709
+ function emitAnswerCommitted(callKey, lane, answer, payload) {
710
+ if (answerCommittedListeners.size === 0)
711
+ return;
712
+ const event = Object.freeze({ callKey, lane, answer: Object.freeze({ ...answer }), payload });
713
+ for (const l of [...answerCommittedListeners])
714
+ invokeHostCallbackIsolated('liveToolApprovalWire: answer-committed listener', () => l(event));
715
+ }
716
+ export function isToolApprovalRespondReplayed(ack) {
717
+ return typeof ack === 'object' && ack !== null && ack.idempotent === true;
718
+ }
699
719
  export function readToolApprovalRespondAck(v) {
700
720
  if (v === null || typeof v !== 'object')
701
721
  return undefined;
702
722
  const o = v;
723
+ if (o.idempotent === true)
724
+ return o.delivery === undefined ? readReplayedAck(o) : undefined;
703
725
  if (o.delivery !== 'applied')
704
726
  return undefined;
705
727
  if (typeof o.approvalId !== 'string' || o.approvalId.length === 0)
@@ -773,6 +795,48 @@ export function readToolApprovalRespondParking(v) {
773
795
  },
774
796
  };
775
797
  }
798
+ function replayClaimOf(raw) {
799
+ const approvalId = guardedRead(() => readOwnDataNonEmptyString(raw, 'approvalId'));
800
+ const d = guardedRead(() => readOwnDataValue(raw, 'decision'));
801
+ let carriesDelivery = true;
802
+ try {
803
+ carriesDelivery = typeof raw === 'object' && raw !== null && Object.hasOwn(raw, 'delivery');
804
+ }
805
+ catch {
806
+ carriesDelivery = true;
807
+ }
808
+ return { ...(approvalId !== undefined ? { approvalId } : {}), ...(d === 'allow' || d === 'deny' ? { decision: d } : {}), carriesDelivery };
809
+ }
810
+ function ownIdempotentTrue(raw) {
811
+ if (typeof raw !== 'object' || raw === null)
812
+ return false;
813
+ try {
814
+ const d = Object.getOwnPropertyDescriptor(raw, 'idempotent');
815
+ return d !== undefined && Object.hasOwn(d, 'value') && d.value === true;
816
+ }
817
+ catch {
818
+ return false;
819
+ }
820
+ }
821
+ function readReplayedAck(o) {
822
+ if (typeof o.approvalId !== 'string' || o.approvalId.length === 0)
823
+ return undefined;
824
+ if (typeof o.askId !== 'string' || o.askId.length === 0)
825
+ return undefined;
826
+ if (o.decision !== 'allow' && o.decision !== 'deny')
827
+ return undefined;
828
+ if (typeof o.decidedAtMs !== 'number' || !Number.isFinite(o.decidedAtMs) || o.decidedAtMs < 0)
829
+ return undefined;
830
+ return {
831
+ approvalId: o.approvalId,
832
+ askId: o.askId,
833
+ idempotent: true,
834
+ decision: o.decision,
835
+ decidedAtMs: o.decidedAtMs,
836
+ ...(typeof o.decisionNote === 'string' && o.decisionNote !== '' ? { decisionNote: o.decisionNote } : {}),
837
+ ...(o.rememberApplied === false ? { rememberApplied: false } : {}),
838
+ };
839
+ }
776
840
  function isNonEmptyStringArray(v) {
777
841
  return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x !== '');
778
842
  }
@@ -852,58 +916,16 @@ function readDecidedByReadingOf(holder) {
852
916
  function decidedByReadingSnapshotOf(holder) {
853
917
  return approvalDecidedByAdmitted(readDecidedByReadingOf(holder)) ? Object.freeze({ kind: 'present', value: true }) : undefined;
854
918
  }
855
- export function readToolApprovalRespondRefusal(err) {
856
- const pick = (key) => {
857
- try {
858
- return err?.[key];
859
- }
860
- catch {
861
- return undefined;
862
- }
863
- };
864
- const pickMachine = (key) => guardedRead(() => (typeof err === 'object' && err !== null ? readOwnDataValue(err, key) : undefined));
865
- const rawStatus = pickMachine('status');
866
- const rawCode = pickMachine('errorCode');
867
- const status = typeof rawStatus === 'number' && Number.isFinite(rawStatus) ? rawStatus : undefined;
868
- const errorCode = typeof rawCode === 'string' && rawCode.length > 0 ? rawCode : undefined;
869
- const message = refusalTextOf(err, pick('message'));
870
- if (status === undefined && errorCode === undefined && message === undefined)
919
+ function denyFactsOf(settledBy, reasonSnapshot, dropped, renamed) {
920
+ const reason = reasonSnapshot !== undefined && reasonSnapshot.trim() !== '' ? reasonSnapshot : undefined;
921
+ const attributionKey = dropped ? 'dropped' : renamed ? 'renamed' : undefined;
922
+ if (settledBy === undefined && reason === undefined && attributionKey === undefined)
871
923
  return undefined;
872
- const machine = typeof err === 'object' && err !== null ? err : undefined;
873
- const retriable = machine !== undefined && refusalSaysStillPending(machine);
874
- const cause = machine !== undefined ? refusalCauseWordOf(machine) : undefined;
875
- const field = machine !== undefined ? readRefusalField(machine) : undefined;
876
- const unreadable = machine !== undefined && readDecisionRefusalAsk(machine).reading === 'unreadable';
877
- const rest = {
878
- ...(status !== undefined ? { status } : {}),
879
- ...(errorCode !== undefined ? { errorCode } : {}),
880
- ...(message !== undefined ? { message } : {}),
881
- ...(retriable ? { retriable: true } : {}),
882
- ...(cause !== undefined || field !== undefined
883
- ? { extra: { ...(cause !== undefined ? { cause } : {}), ...(field !== undefined ? { field } : {}) } }
884
- : {}),
885
- ...(unreadable ? { unreadable: true } : {}),
924
+ return {
925
+ ...(settledBy !== undefined ? { settledBy } : {}),
926
+ ...(reason !== undefined ? { reason } : {}),
927
+ ...(attributionKey !== undefined ? { attributionKey } : {}),
886
928
  };
887
- if (status !== undefined)
888
- return { ...rest, status };
889
- if (errorCode !== undefined)
890
- return { ...rest, errorCode };
891
- return { ...rest, message: message };
892
- }
893
- const USELESS_REFUSAL_TEXTS = new Set(['[object Object]', 'null', 'undefined', '']);
894
- function refusalTextOf(err, message) {
895
- if (typeof message === 'string' && message.length > 0)
896
- return message;
897
- if (typeof err === 'string' && err.length > 0)
898
- return err;
899
- let coerced;
900
- try {
901
- coerced = String(err);
902
- }
903
- catch {
904
- return undefined;
905
- }
906
- return USELESS_REFUSAL_TEXTS.has(coerced) ? undefined : coerced;
907
929
  }
908
930
  const MACHINE_CODE_SHAPE = /^[A-Za-z0-9._:-]{1,64}$/;
909
931
  function logSafeErrorCode(code) {
@@ -947,23 +969,24 @@ function usableProbeReason(raw) {
947
969
  return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
948
970
  }
949
971
  const PROBE_CAUSE_CODE_WORD = /^[a-z][a-z0-9_]{0,31}(?:\.[a-z][a-z0-9_]{0,31}){1,3}$/;
950
- export function askProbeReasonDetail(card) {
972
+ export function askProbeReasonDetail(card, lang) {
951
973
  const prose = usableProbeReason(ownDataValue(card, 'probeReason'));
952
974
  const shown = prose === undefined ? '' : displayUntrusted(prose);
953
- const sentence = probeCauseSentence(ownDataValue(card, 'probeCause'), shown !== '');
975
+ const sentence = probeCauseSentence(ownDataValue(card, 'probeCause'), shown !== '', lang);
954
976
  if (sentence === undefined)
955
977
  return shown === '' ? undefined : shown;
956
- return shown === '' ? sentence : `${sentence} ${shown}`;
978
+ return shown === '' ? sentence : renderMessage(msg('approval.probe.proseJoin', { sentence, prose: shown }), lang);
957
979
  }
958
- function probeCauseSentence(cause, withProse) {
980
+ function probeCauseSentence(cause, withProse, lang) {
959
981
  try {
960
982
  if (!isWireRecordCarrier(cause))
961
983
  return undefined;
962
984
  const code = ownDataValue(cause, 'code');
963
- const head = withProse
964
- ? 'An automatic check could not clear this call, so it is asking you'
965
- : 'An automatic check could not clear this call, so it is asking you; the engine did not say the specific reason';
966
- return typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code) ? `${head} (check code: ${code}).` : `${head}.`;
985
+ const withCode = typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code);
986
+ const key = withProse
987
+ ? withCode ? 'approval.probe.withProseCode' : 'approval.probe.withProse'
988
+ : withCode ? 'approval.probe.noProseCode' : 'approval.probe.noProse';
989
+ return renderMessage(withCode ? msg(key, { code }) : msg(key), lang);
967
990
  }
968
991
  catch {
969
992
  return undefined;
@@ -1021,28 +1044,82 @@ export function readApprovalBoundGrants(v) {
1021
1044
  return undefined;
1022
1045
  return Object.freeze({ skill, allowedTools, disallowedTools });
1023
1046
  }
1047
+ const READS_AS_NOTHING_HEAD = '<reads as nothing (';
1048
+ export function readBoundGrantEntry(entry) {
1049
+ if (typeof entry !== 'string' || entry === '')
1050
+ return undefined;
1051
+ if (!entry.startsWith('<'))
1052
+ return Object.freeze({ kind: 'rule' });
1053
+ if (entry.startsWith(READS_AS_NOTHING_HEAD)) {
1054
+ const close = entry.indexOf('): ', READS_AS_NOTHING_HEAD.length);
1055
+ const word = close > 0 ? entry.slice(READS_AS_NOTHING_HEAD.length, close) : undefined;
1056
+ if (word !== undefined && CORE_ALLOW_ENTRY_INERT_REASONS.includes(word)) {
1057
+ return Object.freeze({ kind: 'reads_as_nothing', reason: word });
1058
+ }
1059
+ }
1060
+ return Object.freeze({ kind: 'reads_as_nothing' });
1061
+ }
1062
+ const BOUND_GRANT_NOT_BINDABLE = 'not_bindable';
1063
+ const SERVER_CLASS_READ_CAP = 64;
1024
1064
  function readApprovalGrantBinding(carrier) {
1025
1065
  if (typeof carrier !== 'object' || carrier === null)
1026
1066
  return 'unreadable';
1027
1067
  try {
1028
- if (Object.hasOwn(carrier, 'boundGrantDigest')) {
1029
- const digest = ownDataValue(carrier, 'boundGrantDigest');
1030
- return typeof digest === 'string' && digest.length > 0 ? 'bound' : 'unreadable';
1031
- }
1032
- const tool = ownDataValue(carrier, 'toolName');
1033
- if (typeof tool !== 'string' || tool.length === 0)
1068
+ const card = cardGrantBindingOf(carrier);
1069
+ const mayCall = serverMayReadCallLaw(carrier);
1070
+ if (cardGrantBindingOf(carrier) !== card)
1071
+ return 'unreadable';
1072
+ if (!mayCall)
1073
+ return card;
1074
+ const call = callGrantBindingOf(carrier);
1075
+ if (callGrantBindingOf(carrier) !== call || cardGrantBindingOf(carrier) !== card)
1034
1076
  return 'unreadable';
1035
- return tool === CORE_SKILL_TOOL_NAME ? 'unreadable' : 'none';
1077
+ return card === 'none' && call === 'none' ? 'none' : 'unreadable';
1036
1078
  }
1037
1079
  catch {
1038
1080
  return 'unreadable';
1039
1081
  }
1040
1082
  }
1083
+ function cardGrantBindingOf(carrier) {
1084
+ if (Object.hasOwn(carrier, 'boundGrantDigest') || 'boundGrantDigest' in carrier) {
1085
+ const digest = ownDataValue(carrier, 'boundGrantDigest');
1086
+ return typeof digest === 'string' && digest.length > 0 ? 'bound' : 'unreadable';
1087
+ }
1088
+ const seen = carrier;
1089
+ return ownDataValue(carrier, 'boundGrantAbsence') === BOUND_GRANT_NOT_BINDABLE && seen.boundGrantAbsence === BOUND_GRANT_NOT_BINDABLE && seen.boundGrantDigest === undefined
1090
+ ? 'none'
1091
+ : 'unreadable';
1092
+ }
1093
+ function callGrantBindingOf(carrier) {
1094
+ const tool = ownDataValue(carrier, 'toolName');
1095
+ return typeof tool === 'string' && tool.length > 0 && tool !== CORE_SKILL_TOOL_NAME && carrier.toolName === tool ? 'none' : 'unreadable';
1096
+ }
1097
+ function serverMayReadCallLaw(carrier) {
1098
+ const seen = carrier;
1099
+ const classes = seen.classes;
1100
+ if (!Array.isArray(classes))
1101
+ return false;
1102
+ const listClass = (c) => DEPLOYMENT_LIST_ASK_CLASSES.includes(c);
1103
+ if (listClass(seen.askClass))
1104
+ return true;
1105
+ const length = classes.length;
1106
+ if (typeof length !== 'number' || !Number.isSafeInteger(length) || length > SERVER_CLASS_READ_CAP)
1107
+ return true;
1108
+ for (let i = 0; i < length; i++)
1109
+ if (listClass(classes[i]))
1110
+ return true;
1111
+ let n = 0;
1112
+ for (const c of classes) {
1113
+ if (listClass(c) || ++n > SERVER_CLASS_READ_CAP)
1114
+ return true;
1115
+ }
1116
+ return false;
1117
+ }
1041
1118
  const UNREADABLE_EVIDENCE_KEYS = Object.freeze({
1042
1119
  frame: Object.freeze([
1043
1120
  ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
1044
1121
  ['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffers'],
1045
- ['allowRuleShadowed', 'allowRuleShadow'], ['persistedRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1122
+ ['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1046
1123
  ]),
1047
1124
  row: Object.freeze([
1048
1125
  ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
@@ -1051,8 +1128,9 @@ const UNREADABLE_EVIDENCE_KEYS = Object.freeze({
1051
1128
  ]),
1052
1129
  v1: Object.freeze([
1053
1130
  ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
1054
- ['allowRuleShadowed', 'allowRuleShadow'], ['persistedRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1131
+ ['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1055
1132
  ]),
1133
+ generic: Object.freeze([['ruleOffersAbsence', 'ruleOffersAbsence'], ['askClass', 'askClass'], ['classes', 'classes']]),
1056
1134
  });
1057
1135
  function carrierHas(carrier, key) {
1058
1136
  if (typeof carrier !== 'object' || carrier === null)
@@ -1075,17 +1153,21 @@ function v1RiskUnreadable(card) {
1075
1153
  return true;
1076
1154
  }
1077
1155
  }
1078
- function withUnreadableEvidence(carrier, leg, req) {
1079
- const keys = new Set();
1156
+ function withUnreadableEvidence(carrier, leg, req, reported = []) {
1157
+ const keys = new Set(reported);
1080
1158
  for (const [from, to] of UNREADABLE_EVIDENCE_KEYS[leg]) {
1081
1159
  if (carrierHas(carrier, from) && !Object.hasOwn(req, to))
1082
1160
  keys.add(from);
1083
1161
  }
1084
1162
  if (leg === 'row') {
1085
1163
  const rd = ownDataValue(carrier, 'riskDescriptor');
1086
- for (const k of ['allowRuleShadowed', 'shadowedRule'])
1087
- if (carrierHas(rd, k) && !Object.hasOwn(req, 'allowRuleShadow'))
1088
- keys.add(`riskDescriptor.${k}`);
1164
+ if (carrierHas(rd, 'allowRuleShadowed') && !Object.hasOwn(req, 'allowRuleShadow'))
1165
+ keys.add('riskDescriptor.allowRuleShadowed');
1166
+ }
1167
+ if (leg !== 'generic' && !Object.hasOwn(req, 'allowRuleShadow')) {
1168
+ const shadowLeg = leg === 'row' ? 'row' : 'frame';
1169
+ if (retiredAllowRuleShadowKeyPresent(carrier, shadowLeg))
1170
+ keys.add(RETIRED_SHADOW_EVIDENCE_LABEL[shadowLeg]);
1089
1171
  }
1090
1172
  if (leg === 'v1' && v1RiskUnreadable(carrier))
1091
1173
  keys.add('risk');
@@ -1097,40 +1179,62 @@ export function readApprovalClassifierNoRuling(v) {
1097
1179
  return typeof w === 'string' && CAUSE_WORD.test(w) ? w : undefined;
1098
1180
  }
1099
1181
  const CLASSIFIER_NO_RULING_REASONS = Object.freeze({
1100
- error: 'could not run for this call (it failed)',
1101
- timeout: 'could not run for this call (it timed out)',
1102
- parse_error: 'answered for this call but gave no verdict it could use',
1182
+ error: 'approval.classifier.noRulingError',
1183
+ timeout: 'approval.classifier.noRulingTimeout',
1184
+ parse_error: 'approval.classifier.noRulingParseError',
1103
1185
  });
1104
1186
  const _noRulingPin = [true, true];
1105
1187
  void _noRulingPin;
1106
- export function classifierNoRulingDetail(word) {
1188
+ export function classifierNoRulingDetail(word, lang) {
1107
1189
  if (typeof word === 'string' && Object.hasOwn(CLASSIFIER_NO_RULING_REASONS, word)) {
1108
- return `The auto-mode classifier ${CLASSIFIER_NO_RULING_REASONS[word]}, so it is asking you instead.`;
1190
+ return renderMessage(msg(CLASSIFIER_NO_RULING_REASONS[word]), lang);
1109
1191
  }
1110
1192
  if (typeof word === 'string' && CAUSE_WORD.test(word))
1111
- return `The auto-mode classifier did not rule on this call (reason: ${word}), so it is asking you instead.`;
1112
- return 'The auto-mode classifier did not rule on this call, so it is asking you instead.';
1193
+ return renderMessage(msg('approval.classifier.noRulingOther', { reason: word }), lang);
1194
+ return renderMessage(msg('approval.classifier.noRulingBare'), lang);
1113
1195
  }
1114
- const entries = (n) => (n === 1 ? '1 tool entry' : `${String(n)} tool entries`);
1115
- export function approvalBoundGrantsDetail(grants) {
1196
+ export function approvalBoundGrantsDetail(grants, lang) {
1116
1197
  const g = readApprovalBoundGrants({ boundGrants: grants });
1117
1198
  if (g === undefined)
1118
1199
  return undefined;
1119
1200
  if (g.skill === null)
1120
- return 'This call names no loaded skill, so approving it grants nothing beyond this call.';
1121
- const a = g.allowedTools.length;
1122
- const d = g.disallowedTools.length;
1123
- if (a === 0 && d === 0)
1124
- return 'Approving this activates the skill shown on the card; it lists no tools to run without asking and none to refuse.';
1125
- const allow = `${entries(a)} it lists ${a === 1 ? 'runs' : 'run'} without asking once it is active`;
1126
- const deny = `${entries(d)} ${d === 1 ? 'is' : 'are'} refused for the rest of this run`;
1127
- return `Approving this activates the skill shown on the card: ${allow}, and ${deny}.`;
1201
+ return renderMessage(msg('approval.boundGrants.noSkill'), lang);
1202
+ const isRule = (e) => readBoundGrantEntry(e)?.kind === 'rule';
1203
+ const a = g.allowedTools.filter(isRule).length;
1204
+ const d = g.disallowedTools.filter(isRule).length;
1205
+ const allowNothing = g.allowedTools.length - a;
1206
+ const denyNothing = g.disallowedTools.length - d;
1207
+ if (denyNothing > 0) {
1208
+ return renderMessage(denyNothing === 1 ? msg('approval.boundGrants.notActivated.one') : msg('approval.boundGrants.notActivated.other', { count: String(denyNothing) }), lang);
1209
+ }
1210
+ if (a === 0 && d === 0) {
1211
+ if (allowNothing > 0)
1212
+ return renderMessage(allowNothing === 1 ? msg('approval.boundGrants.listsNoToolsWithNothing.one') : msg('approval.boundGrants.listsNoToolsWithNothing.other', { count: String(allowNothing) }), lang);
1213
+ return renderMessage(msg('approval.boundGrants.listsNoTools'), lang);
1214
+ }
1215
+ const allow = a === 1 ? msg('approval.boundGrants.allowPart.one') : msg('approval.boundGrants.allowPart.other', { count: a });
1216
+ const deny = d === 1 ? msg('approval.boundGrants.denyPart.one') : msg('approval.boundGrants.denyPart.other', { count: d });
1217
+ if (allowNothing > 0) {
1218
+ return renderMessage(allowNothing === 1 ? msg('approval.boundGrants.activatesWithNothing.one', { allow, deny }) : msg('approval.boundGrants.activatesWithNothing.other', { allow, deny, count: String(allowNothing) }), lang);
1219
+ }
1220
+ return renderMessage(msg('approval.boundGrants.activates', { allow, deny }), lang);
1128
1221
  }
1129
1222
  const CALL_LEDGER_MAX_CALLS = 256;
1130
1223
  const CALL_LEDGER_MAX_ASKS_PER_CALL = 8;
1131
1224
  const callCardLedger = new Map();
1132
1225
  function askSignatureOf(req) {
1133
- return JSON.stringify([req.classes !== undefined ? [...req.classes] : null, req.origin ?? null, req.mandate ?? null]);
1226
+ return askSigOf(req.classes, req.origin, req.mandate);
1227
+ }
1228
+ function askSigOf(classes, origin, mandate) {
1229
+ return JSON.stringify([classes !== undefined ? [...classes] : null, origin ?? null, mandate ?? null]);
1230
+ }
1231
+ function carrierAskSig(v) {
1232
+ try {
1233
+ return askSigOf(readAskClassPair(v)?.classes, readAskOrigin(v), readApprovalMandate(v));
1234
+ }
1235
+ catch {
1236
+ return undefined;
1237
+ }
1134
1238
  }
1135
1239
  function priorCardView(a) {
1136
1240
  return Object.freeze({
@@ -1154,35 +1258,67 @@ function noteCardOnCall(callId, approvalId, req) {
1154
1258
  }
1155
1259
  }
1156
1260
  let at = asks.findIndex((a) => a.sig === sig);
1261
+ let supersedes;
1157
1262
  if (at < 0) {
1158
1263
  asks.push({ sig, ...(approvalId !== undefined ? { approvalId } : {}), ...(req.origin !== undefined ? { origin: req.origin } : {}), ...(req.mandate !== undefined ? { mandate: req.mandate } : {}) });
1159
1264
  while (asks.length > CALL_LEDGER_MAX_ASKS_PER_CALL)
1160
1265
  asks.shift();
1161
1266
  at = asks.length - 1;
1162
1267
  }
1163
- else if (asks[at].approvalId === undefined && approvalId !== undefined) {
1164
- asks[at].approvalId = approvalId;
1268
+ else {
1269
+ supersedes = supersedesOf(asks[at]);
1270
+ delete asks[at].answered;
1271
+ delete asks[at].answerFate;
1272
+ if (asks[at].approvalId === undefined && approvalId !== undefined)
1273
+ asks[at].approvalId = approvalId;
1165
1274
  }
1166
1275
  const entry = asks[at];
1167
1276
  const prior = asks.slice(0, at);
1168
- return { entry, ...(prior.length > 0 ? { prior: Object.freeze(prior.map(priorCardView)) } : {}) };
1277
+ return { entry, ...(prior.length > 0 ? { prior: Object.freeze(prior.map(priorCardView)) } : {}), ...(supersedes !== undefined ? { supersedes } : {}) };
1278
+ }
1279
+ function supersedesOf(a) {
1280
+ const settled = settledApprovalOf(a.approvalId);
1281
+ return Object.freeze({
1282
+ ...(a.approvalId !== undefined ? { approvalId: a.approvalId } : {}),
1283
+ ...(settled?.parked === true ? { parked: true } : {}),
1284
+ ...(a.answered !== undefined && a.outcome === undefined ? { priorAnswer: a.answered, priorAnswerOutcome: a.answerFate ?? 'unconfirmed' } : {}),
1285
+ });
1169
1286
  }
1170
1287
  async function surfaceCallTrackedCard(callId, approvalId, slot, req) {
1171
1288
  if (callId === undefined || callId === '')
1172
1289
  return surfaceApprovalCard(req);
1173
- const { entry, prior } = noteCardOnCall(callId, approvalId, req);
1290
+ const { entry, prior, supersedes } = noteCardOnCall(callId, approvalId, req);
1174
1291
  slot.entry = entry;
1175
- return surfaceApprovalCard(prior !== undefined ? { ...req, priorCardsForCall: prior } : req);
1292
+ const decision = await surfaceApprovalCard(prior === undefined && supersedes === undefined ? req : { ...req, ...(prior !== undefined ? { priorCardsForCall: prior } : {}), ...(supersedes !== undefined ? { supersedes } : {}) });
1293
+ const answered = respondDecisionOfAnswer(decision);
1294
+ if (answered !== undefined)
1295
+ entry.answered = answered;
1296
+ return decision;
1176
1297
  }
1177
- function noteCallOutcome(slot, outcome) {
1298
+ function noteCallOutcome(slot, r) {
1178
1299
  const entry = slot.entry;
1179
1300
  if (entry === undefined)
1180
1301
  return;
1181
- const r = approvalResolutionOf(outcome);
1182
- if (r.kind === 'decided')
1302
+ if (r.kind === 'decided') {
1183
1303
  entry.outcome = r.decision === 'deny' ? 'denied' : 'allowed';
1304
+ delete entry.answerFate;
1305
+ return;
1306
+ }
1307
+ const unsettledFate = {
1308
+ engine_refused: 'not_accepted',
1309
+ parking_in_progress: 'not_accepted',
1310
+ engine_error: 'unconfirmed',
1311
+ no_answer: 'unconfirmed',
1312
+ transport_exhausted: 'unconfirmed',
1313
+ interrupted: 'unconfirmed',
1314
+ orchestration: 'unconfirmed',
1315
+ unreadable: 'unconfirmed',
1316
+ };
1317
+ const next = r.kind === 'not_sent' ? 'not_sent' : Object.hasOwn(unsettledFate, r.cause) ? unsettledFate[r.cause] : 'unconfirmed';
1318
+ const fateRank = { not_sent: 0, not_accepted: 1, unconfirmed: 2 };
1319
+ entry.answerFate = entry.answerFate !== undefined && fateRank[entry.answerFate] > fateRank[next] ? entry.answerFate : next;
1184
1320
  }
1185
- export function approvalPriorCardsDetail(card) {
1321
+ export function approvalPriorCardsDetail(card, lang) {
1186
1322
  const prior = ownDataValue(card, 'priorCardsForCall');
1187
1323
  let isList = false;
1188
1324
  try {
@@ -1200,13 +1336,36 @@ export function approvalPriorCardsDetail(card) {
1200
1336
  for (let i = 0; i < length; i++)
1201
1337
  items.push(ownDataValue(prior, String(i)));
1202
1338
  const outcome = ownDataValue(items[length - 1], 'outcome');
1203
- const earlier = outcome === 'allowed' ? 'you already approved an earlier one' : outcome === 'denied' ? 'an earlier one was denied' : 'an earlier one was shown first';
1339
+ const earlier = msg(outcome === 'allowed' ? 'approval.priorCard.earlier.allowed' : outcome === 'denied' ? 'approval.priorCard.earlier.denied' : 'approval.priorCard.earlier.shown');
1204
1340
  const fromRule = items.some((p) => ownDataValue(p, 'origin') === 'ask_rule');
1205
1341
  const thisFromRule = ownDataValue(card, 'origin') === 'ask_rule';
1206
- if (fromRule && !thisFromRule) {
1207
- return `This is a second approval for the same call: ${earlier}, which your ask rule asked for. This one is a separate check, not from your ask rule, and needs its own answer.`;
1208
- }
1209
- return `This is a second approval for the same call: ${earlier}. This one is a separate check and needs its own answer.`;
1342
+ if (fromRule && !thisFromRule)
1343
+ return renderMessage(msg('approval.priorCard.fromRule', { earlier }), lang);
1344
+ return renderMessage(msg('approval.priorCard.plain', { earlier }), lang);
1345
+ }
1346
+ const PRIOR_ANSWER_PHRASE = Object.freeze({
1347
+ allow: 'Yes',
1348
+ allow_session: 'Yes for this session',
1349
+ deny: 'No',
1350
+ });
1351
+ export function approvalSupersedesDetail(card) {
1352
+ const sup = ownDataValue(card, 'supersedes');
1353
+ if (typeof sup !== 'object' || sup === null)
1354
+ return undefined;
1355
+ const parked = ownDataValue(sup, 'parked') === true;
1356
+ const prior = ownDataValue(sup, 'priorAnswer');
1357
+ const head = parked
1358
+ ? 'This card replaces an earlier one for the same request, which moved to the engine\'s waiting queue.'
1359
+ : 'This card replaces an earlier one for the same request.';
1360
+ if (typeof prior !== 'string' || !Object.hasOwn(PRIOR_ANSWER_PHRASE, prior))
1361
+ return head;
1362
+ const said = PRIOR_ANSWER_PHRASE[prior];
1363
+ const fate = ownDataValue(sup, 'priorAnswerOutcome');
1364
+ if (fate === 'not_sent')
1365
+ return `${head} Your earlier answer (${said}) did not reach the engine, so answer again here.`;
1366
+ if (fate === 'not_accepted')
1367
+ return `${head} Your earlier answer (${said}) was not accepted by the engine, so answer again here.`;
1368
+ return `${head} Your earlier answer (${said}) was not confirmed by the engine — it may already have taken effect, so check the request before answering again here.`;
1210
1369
  }
1211
1370
  function isNonNegativeFinite(v) {
1212
1371
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
@@ -1379,32 +1538,44 @@ function v1Bit(stamp) {
1379
1538
  catch {
1380
1539
  }
1381
1540
  }
1382
- export function approvalCardFactsOfV1(card) {
1383
- if (typeof card !== 'object' || card === null)
1384
- return undefined;
1385
- const c = card;
1386
- const asFrame = card;
1387
- const fromSubagent = (() => {
1388
- try {
1389
- return isFromSubagent(asFrame);
1390
- }
1391
- catch {
1392
- return false;
1393
- }
1394
- })();
1541
+ const SUSPENDED_ASK_ARGS_NOTE = "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
1542
+ 'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
1543
+ 'Edits are not accepted on this card: there is no original input to edit';
1544
+ function cardFactsOf(carrier, leg, opts) {
1545
+ const c = carrier;
1546
+ const full = leg !== 'generic';
1547
+ const asFrame = carrier;
1548
+ const fromSubagent = full
1549
+ ? (() => {
1550
+ try {
1551
+ return isFromSubagent(asFrame);
1552
+ }
1553
+ catch {
1554
+ return false;
1555
+ }
1556
+ })()
1557
+ : false;
1395
1558
  const argsPart = (() => {
1396
1559
  try {
1397
- const raw = c.args !== undefined && c.args !== null ? c.args : undefined;
1560
+ let stream;
1561
+ let blind = false;
1562
+ if (leg === 'frame' && typeof opts === 'object' && opts !== null) {
1563
+ stream = opts.streamArgs;
1564
+ blind = opts.argsUnavailable === true;
1565
+ }
1566
+ const raw = stream !== undefined && stream !== null ? stream : c.args !== undefined && c.args !== null ? c.args : undefined;
1398
1567
  if (typeof raw === 'object' && raw !== null)
1399
- return { args: raw };
1568
+ return blind ? { args: raw, argsUnavailable: true } : { args: raw };
1400
1569
  const fallback = argsFallbackOf(c.message, c.argsOmitted);
1401
- return { args: fallback.args, argsUnavailable: true, ...(fallback.omittedNote !== undefined ? { wireNote: fallback.omittedNote } : {}) };
1570
+ const note = blind ? SUSPENDED_ASK_ARGS_NOTE : fallback.omittedNote;
1571
+ return { args: fallback.args, argsUnavailable: true, ...(note !== undefined ? { wireNote: note } : {}) };
1402
1572
  }
1403
1573
  catch {
1404
1574
  return { args: {}, argsUnavailable: true };
1405
1575
  }
1406
1576
  })();
1407
- const out = { toolName: '', ...argsPart };
1577
+ const out = { toolName: '', args: argsPart.args };
1578
+ let realApprovalUnreadable = false;
1408
1579
  v1Bit(() => {
1409
1580
  if (typeof c.toolName === 'string')
1410
1581
  out.toolName = c.toolName;
@@ -1417,44 +1588,79 @@ export function approvalCardFactsOfV1(card) {
1417
1588
  if (fromSubagent)
1418
1589
  out.workerBadge = subagentBadgeFor(asFrame);
1419
1590
  });
1420
- v1Bit(() => {
1421
- if (c.governanceForced === true)
1422
- out.governanceForced = true;
1423
- });
1424
- v1Bit(() => {
1425
- const d = c.delegation;
1426
- if (fromSubagent && isToolApprovalDelegation(d))
1427
- out.delegation = d;
1428
- });
1429
- v1Bit(() => {
1430
- const offers = readRuleOffers(c.ruleOffers);
1431
- if (offers !== undefined)
1432
- out.ruleOffersReadOnly = offers;
1433
- });
1434
- v1Bit(() => {
1435
- const shadow = readAllowRuleShadowFromWire(card, 'frame');
1436
- if (shadow !== undefined)
1437
- out.allowRuleShadow = shadow;
1438
- });
1439
- v1Bit(() => {
1440
- const pc = c.probeCause;
1441
- if (isWireRecordCarrier(pc))
1442
- out.probeCause = pc;
1443
- });
1444
- v1Bit(() => {
1445
- if (readApprovalRememberRefused(card))
1446
- out.rememberRefused = true;
1447
- });
1448
- v1Bit(() => {
1449
- const re = c.ruleEvidence;
1450
- if (isWireRecordCarrier(re))
1451
- out.ruleEvidence = re;
1452
- });
1453
- v1Bit(() => {
1454
- const risk = c.risk;
1455
- if (typeof risk === 'object' && risk !== null && risk.requiresRealApproval === true)
1456
- out.requiresRealApproval = true;
1457
- });
1591
+ if (argsPart.wireNote !== undefined)
1592
+ out.wireNote = argsPart.wireNote;
1593
+ if (argsPart.argsUnavailable === true)
1594
+ out.argsUnavailable = true;
1595
+ if (full) {
1596
+ v1Bit(() => {
1597
+ if (c.governanceForced === true)
1598
+ out.governanceForced = true;
1599
+ });
1600
+ v1Bit(() => {
1601
+ const d = c.delegation;
1602
+ if (fromSubagent && isToolApprovalDelegation(d))
1603
+ out.delegation = d;
1604
+ });
1605
+ v1Bit(() => {
1606
+ const offers = readRuleOffers(c.ruleOffers);
1607
+ if (offers === undefined)
1608
+ return;
1609
+ if (leg === 'frame')
1610
+ out.ruleOffers = offers;
1611
+ else
1612
+ out.ruleOffersReadOnly = offers;
1613
+ });
1614
+ v1Bit(() => {
1615
+ const shadow = readAllowRuleShadowFromWire(carrier, 'frame');
1616
+ if (shadow !== undefined)
1617
+ out.allowRuleShadow = shadow;
1618
+ });
1619
+ v1Bit(() => {
1620
+ const pc = c.probeCause;
1621
+ if (isWireRecordCarrier(pc))
1622
+ out.probeCause = pc;
1623
+ });
1624
+ if (leg === 'frame') {
1625
+ v1Bit(() => {
1626
+ const r = readProbeReason(carrier);
1627
+ if (r !== undefined)
1628
+ out.probeReason = r;
1629
+ });
1630
+ }
1631
+ v1Bit(() => {
1632
+ if (readApprovalRememberRefused(carrier))
1633
+ out.rememberRefused = true;
1634
+ });
1635
+ v1Bit(() => {
1636
+ const re = c.ruleEvidence;
1637
+ if (isWireRecordCarrier(re))
1638
+ out.ruleEvidence = re;
1639
+ });
1640
+ try {
1641
+ if (readRequiresRealApproval(carrier))
1642
+ out.requiresRealApproval = true;
1643
+ }
1644
+ catch {
1645
+ realApprovalUnreadable = true;
1646
+ }
1647
+ if (leg === 'frame') {
1648
+ v1Bit(() => {
1649
+ if (hasOwnStrictTrue(carrier, 'mandated'))
1650
+ out.mandated = true;
1651
+ });
1652
+ v1Bit(() => {
1653
+ const w = readApprovalMandate(carrier);
1654
+ if (w !== undefined)
1655
+ out.mandate = w;
1656
+ });
1657
+ v1Bit(() => {
1658
+ const rc = readReadRootCandidate(c.readRootCandidate);
1659
+ if (rc !== undefined)
1660
+ out.readRootCandidate = rc;
1661
+ });
1662
+ }
1663
+ }
1458
1664
  v1Bit(() => {
1459
1665
  if (c.inputHasBidi === true)
1460
1666
  out.inputHasBidi = true;
@@ -1469,43 +1675,114 @@ export function approvalCardFactsOfV1(card) {
1469
1675
  if (dlf !== undefined)
1470
1676
  out.denialLimitFallback = dlf;
1471
1677
  });
1678
+ if (leg === 'frame') {
1679
+ v1Bit(() => {
1680
+ const o = readAskOrigin(carrier);
1681
+ if (o !== undefined)
1682
+ out.origin = o;
1683
+ });
1684
+ v1Bit(() => {
1685
+ const w = readApprovalCardWord(carrier);
1686
+ if (w !== undefined)
1687
+ out.approvalCard = w;
1688
+ });
1689
+ }
1472
1690
  v1Bit(() => {
1473
- const pair = readAskClassPair(card);
1691
+ const pair = readAskClassPair(carrier);
1474
1692
  if (pair !== undefined) {
1475
1693
  out.askClass = pair.askClass;
1476
1694
  out.classes = pair.classes;
1477
1695
  }
1478
1696
  });
1479
- out.grantBinding = readApprovalGrantBinding(card);
1480
- return Object.freeze(withUnreadableEvidence(card, 'v1', out));
1697
+ if (leg === 'frame') {
1698
+ v1Bit(() => {
1699
+ const g = readApprovalBoundGrants(carrier);
1700
+ if (g !== undefined)
1701
+ out.boundGrants = g;
1702
+ });
1703
+ v1Bit(() => {
1704
+ const w = readApprovalClassifierNoRuling(carrier);
1705
+ if (w !== undefined)
1706
+ out.classifierNoRuling = w;
1707
+ });
1708
+ }
1709
+ v1Bit(() => {
1710
+ const msg = ownDataValue(carrier, 'message');
1711
+ if (typeof msg === 'string' && msg.trim() !== '')
1712
+ out.message = msg;
1713
+ });
1714
+ if (leg === 'generic')
1715
+ out.cardPayloadUnreadable = true;
1716
+ if (leg === 'frame')
1717
+ out.allowSessionCarried = true;
1718
+ out.grantBinding = readApprovalGrantBinding(carrier);
1719
+ return withUnreadableEvidence(carrier, leg, out, realApprovalUnreadable ? ['requiresRealApproval'] : []);
1720
+ }
1721
+ export function approvalCardFactsOfV1(card) {
1722
+ if (typeof card !== 'object' || card === null)
1723
+ return undefined;
1724
+ return Object.freeze(cardFactsOf(card, 'v1'));
1725
+ }
1726
+ export function approvalCardFactsOfFrame(frame, opts) {
1727
+ if (typeof frame !== 'object' || frame === null)
1728
+ return undefined;
1729
+ return Object.freeze(cardFactsOf(frame, 'frame', opts));
1730
+ }
1731
+ export function approvalCardFactsOfGeneric(card) {
1732
+ if (typeof card !== 'object' || card === null)
1733
+ return undefined;
1734
+ return Object.freeze(cardFactsOf(card, 'generic'));
1481
1735
  }
1482
1736
  const MAX_RESPOND_NOTE_CHARS = 2048;
1483
1737
  export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane, run) {
1738
+ const receivedAtMs = Date.now();
1484
1739
  const approvalId = frame.approvalId;
1740
+ const entry = { frame, approvalId, identity: liveCardIdentityOf(approvalId, frame, run), respond, streamArgs, signal, lane, receivedAtMs };
1741
+ return enterLiveCard(entry);
1742
+ }
1743
+ function enterLiveCard(e) {
1744
+ const { approvalId, signal } = e;
1485
1745
  const held = liveCardHoldOf(liveFrameCallKey(approvalId));
1486
1746
  if (held !== undefined)
1487
1747
  return joinLiveCard(held, signal);
1488
1748
  const settled = settledLiveCardOutcome(approvalId);
1489
1749
  if (settled !== undefined)
1490
1750
  return Promise.resolve(settled);
1491
- return holdLiveCard(liveCardIdentityOf(approvalId, frame, run), signal, async (hold) => {
1751
+ const other = approvalHoldFor(e.identity, (h) => h.lane === 'live');
1752
+ if (other !== undefined)
1753
+ return joinOtherLaneHolder(e, other);
1754
+ return holdLiveCard(e.identity, signal, async (hold) => {
1492
1755
  const slot = {};
1493
- const out = await presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, slot);
1756
+ const out = await presentToolApprovalFrameAndRespond(e.frame, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
1494
1757
  noteCallOutcome(slot, out);
1495
1758
  return out;
1496
1759
  });
1497
1760
  }
1761
+ async function joinOtherLaneHolder(e, other) {
1762
+ const wait = await awaitApprovalHolder(other, e.signal, 'respond');
1763
+ if (wait.kind === 'settled') {
1764
+ noteHolderSettledApproval(e.approvalId, wait.how, wait.outcome);
1765
+ return joinedResolution(wait.outcome);
1766
+ }
1767
+ if (wait.kind === 'left')
1768
+ return { kind: 'not_sent', lane: 'respond', cause: 'retracted', joinedInFlight: true };
1769
+ return enterLiveCard(e);
1770
+ }
1498
1771
  function liveCardIdentityOf(approvalId, frame, run) {
1499
1772
  const toolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1500
1773
  const fromSubagent = isFromSubagent(frame);
1501
- const ownRun = fromSubagent ? frame.sourceTaskId : run?.taskId;
1774
+ const bound = fromSubagent ? undefined : frameBindingOf(frame);
1775
+ const source = bound ?? run;
1776
+ const ownRun = fromSubagent ? frame.sourceTaskId : source?.taskId;
1502
1777
  const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
1503
- const sessionId = !fromSubagent && typeof run?.sessionId === 'string' && run.sessionId !== '' ? run.sessionId : undefined;
1778
+ const sessionId = !fromSubagent && typeof source?.sessionId === 'string' && source.sessionId !== '' ? source.sessionId : undefined;
1779
+ const askSig = carrierAskSig(frame);
1504
1780
  return {
1505
1781
  approvalId,
1506
1782
  ...(toolCallId !== undefined ? { toolCallId } : {}),
1507
1783
  ...(taskId !== undefined ? { taskId } : {}),
1508
1784
  ...(sessionId !== undefined ? { sessionId } : {}),
1785
+ ...(askSig !== undefined ? { askSig } : {}),
1509
1786
  };
1510
1787
  }
1511
1788
  function readApprovalHolderIdentity(v) {
@@ -1527,107 +1804,63 @@ function readApprovalHolderIdentity(v) {
1527
1804
  }
1528
1805
  export async function joinApprovalHolder(identity, signal) {
1529
1806
  const who = readApprovalHolderIdentity(identity);
1530
- const hold = liveCardHoldFor(who);
1807
+ const hold = approvalHoldFor(who);
1531
1808
  if (hold === undefined) {
1532
1809
  const entry = settledApprovalOf(who.approvalId);
1533
- return settledForDurableLane(entry) && entry !== undefined ? { kind: 'joined', outcome: { ...entry.outcome, joinedInFlight: true } } : { kind: 'free' };
1810
+ return settledForDurableLane(entry) && entry !== undefined ? { kind: 'joined', outcome: joinedResolution(entry.outcome) } : { kind: 'free' };
1811
+ }
1812
+ const wait = await awaitApprovalHolder(hold, signal, 'decide');
1813
+ if (wait.kind === 'settled')
1814
+ return { kind: 'joined', outcome: joinedResolution(wait.outcome) };
1815
+ if (wait.kind === 'left')
1816
+ return { kind: 'joined', outcome: { kind: 'not_sent', lane: 'decide', cause: 'retracted', joinedInFlight: true } };
1817
+ return { kind: 'free' };
1818
+ }
1819
+ function readApprovalCardClaimSignal(v) {
1820
+ if (typeof v !== 'object' || v === null)
1821
+ return undefined;
1822
+ try {
1823
+ const d = Object.getOwnPropertyDescriptor(v, 'signal');
1824
+ const x = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
1825
+ return typeof x === 'object' && x !== null ? x : undefined;
1826
+ }
1827
+ catch {
1828
+ return undefined;
1534
1829
  }
1535
- const joined = await awaitLiveCardHolder(hold, signal);
1536
- return joined !== undefined ? { kind: 'joined', outcome: joined } : { kind: 'free' };
1537
1830
  }
1538
- async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot) {
1831
+ export function claimApprovalCard(identity, options) {
1832
+ return claimApprovalCardFor(readApprovalHolderIdentity(identity), readApprovalCardClaimSignal(options));
1833
+ }
1834
+ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
1539
1835
  const frameToolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1540
1836
  const decidedByReading = decidedByReadingSnapshotOf(lane);
1541
- const toolName = typeof frame.toolName === 'string' ? frame.toolName : 'Write';
1542
- let args = streamArgs !== undefined && streamArgs !== null
1543
- ? streamArgs
1544
- : frame.args !== undefined && frame.args !== null
1545
- ? frame.args
1546
- : undefined;
1547
- let wireNote;
1548
- let argsFallback = false;
1549
- if (typeof args !== 'object' || args === null) {
1550
- argsFallback = true;
1551
- const fallback = argsFallbackOf(frame.message, frame.argsOmitted);
1552
- args = fallback.args;
1553
- if (fallback.omittedNote !== undefined) {
1554
- wireNote = fallback.omittedNote;
1555
- hostLog('debug', `liveToolApprovalWire: frame ${approvalId} args omitted (>16KiB wire cap) — card falls back to ${fallback.pathRecovered ? 'the message-derived path' : 'an empty input (no path could be derived from the message)'}`);
1556
- }
1557
- if (lane?.argsUnavailable === true) {
1558
- wireNote =
1559
- "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
1560
- 'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
1561
- 'Edits are not accepted on this card: there is no original input to edit';
1562
- }
1563
- }
1564
- const argsNotRealInput = argsFallback || lane?.argsUnavailable === true;
1565
- const ruleOffers = readRuleOffers(frame.ruleOffers);
1566
- const denialLimitFallback = readDenialLimitFallback(frame.denialLimitFallback);
1567
- const frameShadow = readAllowRuleShadowFromWire(frame, 'frame');
1568
- const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, withUnreadableEvidence(frame, 'frame', {
1569
- toolName,
1570
- args: args,
1837
+ const facts = cardFactsOf(frame, 'frame', { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) });
1838
+ const argsNotRealInput = facts.argsUnavailable === true;
1839
+ const ruleOffers = facts.ruleOffers;
1840
+ if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
1841
+ hostLog('debug', `liveToolApprovalWire: frame ${approvalId} args omitted (>16KiB wire cap) — card falls back to ${Object.hasOwn(facts.args, 'file_path') ? 'the message-derived path' : 'an empty input (no path could be derived from the message)'}`);
1842
+ }
1843
+ const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, {
1844
+ ...facts,
1571
1845
  callKey: liveFrameCallKey(approvalId),
1572
- ...(frameToolCallId !== undefined
1573
- ? { toolCallId: frameToolCallId }
1574
- : {}),
1575
1846
  signal: hold.cardSignal,
1576
- ...(isFromSubagent(frame) ? { workerBadge: subagentBadgeFor(frame) } : {}),
1577
- ...(wireNote !== undefined ? { wireNote } : {}),
1578
- ...(argsNotRealInput ? { argsUnavailable: true } : {}),
1579
- ...(frame.governanceForced === true ? { governanceForced: true } : {}),
1580
- ...(lane?.windowIsCurrent === true
1581
- ? {
1582
- ...(isNonNegativeFinite(frame.expiresInMs) ? { expiresInMs: frame.expiresInMs } : {}),
1583
- ...(isNonNegativeFinite(frame.expiresAtMs) ? { expiresAtMs: frame.expiresAtMs } : {}),
1584
- ...(isNonNegativeFinite(frame.serverNowMs) ? { serverNowMs: frame.serverNowMs } : {}),
1585
- ...(isNonNegativeFinite(frame.expiresInMs) || isNonNegativeFinite(frame.expiresAtMs)
1586
- ? { receivedAtMs: Date.now() }
1587
- : {}),
1588
- }
1589
- : {}),
1590
- ...(isFromSubagent(frame) && isToolApprovalDelegation(frame.delegation)
1591
- ? { delegation: frame.delegation }
1592
- : {}),
1593
- ...(ruleOffers !== undefined ? { ruleOffers } : {}),
1594
- ...((sh) => (sh !== undefined ? { allowRuleShadow: sh } : {}))(frameShadow),
1595
- ...(isWireRecordCarrier(frame.probeCause) ? { probeCause: frame.probeCause } : {}),
1596
- ...((r) => (r !== undefined ? { probeReason: r } : {}))(readProbeReason(frame)),
1597
- ...(readApprovalRememberRefused(frame) ? { rememberRefused: true } : {}),
1598
- ...(isWireRecordCarrier(frame.ruleEvidence) ? { ruleEvidence: frame.ruleEvidence } : {}),
1599
- ...(frame.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
1600
- ...(hasOwnStrictTrue(frame, 'mandated') ? { mandated: true } : {}),
1601
- ...((w) => (w !== undefined ? { mandate: w } : {}))(readApprovalMandate(frame)),
1602
- ...(() => { const rc = readReadRootCandidate(frame.readRootCandidate); return rc !== undefined ? { readRootCandidate: rc } : {}; })(),
1603
- ...(frame.inputHasBidi === true ? { inputHasBidi: true } : {}),
1604
- ...(typeof frame.ruleOffersAbsence === 'string' && frame.ruleOffersAbsence !== ''
1605
- ? { ruleOffersAbsence: frame.ruleOffersAbsence }
1606
- : {}),
1607
- ...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
1608
- ...((o) => (o !== undefined ? { origin: o } : {}))(readAskOrigin(frame)),
1609
- ...((w) => (w !== undefined ? { approvalCard: w } : {}))(readApprovalCardWord(frame)),
1610
- ...(readAskClassPair(frame) ?? {}),
1611
- ...((g) => (g !== undefined ? { boundGrants: g } : {}))(readApprovalBoundGrants(frame)),
1612
- ...((w) => (w !== undefined ? { classifierNoRuling: w } : {}))(readApprovalClassifierNoRuling(frame)),
1613
- allowSessionCarried: true,
1614
- grantBinding: readApprovalGrantBinding(frame),
1615
- }));
1847
+ ...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs }),
1848
+ });
1616
1849
  if (hold.superseded !== undefined) {
1617
1850
  hostLog('debug', `liveToolApprovalWire: ${approvalId} was settled by the engine while its card was up${hold.superseded.parked ? ' (parked)' : ''} — card withdrawn; ` +
1618
1851
  `the card came back ${card.kind}; nothing sent`);
1619
1852
  if (hold.superseded.parked && (card.kind === 'allow' || card.kind === 'deny'))
1620
1853
  surfaceApprovalParking();
1621
- return { decision: 'unresolved', retracted: true };
1854
+ return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
1622
1855
  }
1623
1856
  if (argsNotRealInput && card.kind === 'allow' && card.updatedInput !== undefined) {
1624
1857
  hostLog('error', `liveToolApprovalWire: ${approvalId} card returned an edited approval on a card whose arguments are not the tool's real input (${lane?.argsUnavailable === true ? 'args-unavailable ask' : 'arguments omitted or absent on the frame'}) — nothing sent (the ask stays pending)`);
1625
1858
  surfaceEditRefusedOnBlindAsk();
1626
- return { decision: 'unresolved', editRefused: true };
1859
+ return { kind: 'not_sent', lane: 'respond', cause: 'edit_refused' };
1627
1860
  }
1628
1861
  if (card.kind === RETRACTED_CARD_DECISION_KIND) {
1629
1862
  hostLog('debug', `liveToolApprovalWire: ${approvalId} card retracted without a decision${card.reason ? ` (${card.reason})` : ''} — nothing sent`);
1630
- return { decision: 'unresolved', retracted: true };
1863
+ return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
1631
1864
  }
1632
1865
  if (card.kind === 'aborted') {
1633
1866
  hold.phase = 'respond';
@@ -1642,33 +1875,40 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
1642
1875
  hold.trailing = interruptDeny.then((body) => readToolApprovalRespondParking(body) === undefined, () => false);
1643
1876
  observeCancelByDeny(interruptDeny, approvalId);
1644
1877
  hostLog('debug', `liveToolApprovalWire: ${approvalId} card aborted — one tool-level deny issued to settle the pending ask`);
1645
- return { decision: 'unresolved', aborted: true };
1878
+ return { kind: 'unsettled', lane: 'respond', cause: 'interrupted' };
1646
1879
  }
1647
1880
  hold.phase = 'respond';
1648
- const decision = card.kind === 'allow' ? (card.allowSession ? 'allow_session' : 'allow') : 'deny';
1881
+ const noteCapable = lane?.approvalDecisionNoteCapable === true;
1882
+ const denyReasonRead = card.kind === 'deny' ? ((r) => (typeof r === 'string' ? r : undefined))(card.reason) : undefined;
1883
+ const answerSnapshot = card.kind === 'deny'
1884
+ ? ((by) => ({ kind: 'deny', ...(denyReasonRead !== undefined ? { reason: denyReasonRead } : {}), ...(by !== undefined ? { settledBy: by } : {}) }))(readApprovalDenySettledBy(card))
1885
+ : card;
1886
+ const basePayload = answerPayloadFor(answerSnapshot, 'respond', { approvalDecisionNoteCapable: noteCapable });
1887
+ const decision = basePayload?.decision ?? 'deny';
1888
+ if (card.kind === 'allow' && typeof card.note === 'string' && card.note.trim() !== '' && answerCarriage({ callKey: liveFrameCallKey(approvalId) }).allowNote === 'none') {
1889
+ hostLog('debug', `liveToolApprovalWire: ${approvalId} card carried a note on Yes (len=${card.note.length}) — this engine has no field that brings it to the model, so it was not sent`);
1890
+ }
1649
1891
  const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
1650
1892
  const attributionKeys = decision === 'deny' ? decisionAttributionKeyOf(decidedByReading, denySettledByForWire) : {};
1651
1893
  const sentAttributionKey = carriesAttributionKey(attributionKeys);
1652
1894
  let lastSendCarriedAttribution = sentAttributionKey;
1653
- const denyReasonSnapshot = card.kind === 'deny' && typeof card.reason === 'string' ? card.reason : undefined;
1895
+ const denyReasonSnapshot = denyReasonRead;
1654
1896
  if (card.kind === 'failed') {
1655
1897
  hostLog('debug', `liveToolApprovalWire: approval card unavailable (${card.reason}) — fail-closed deny for ${approvalId}`);
1656
1898
  }
1657
- let note;
1658
- if (denyReasonSnapshot !== undefined && denyReasonSnapshot.trim() !== '') {
1659
- if (lane?.approvalDecisionNoteCapable !== true) {
1899
+ const note = basePayload?.note;
1900
+ const noteGate = respondNoteGate(denyReasonSnapshot, noteCapable);
1901
+ if ('dropped' in noteGate && noteGate.dropped !== 'blank' && denyReasonSnapshot !== undefined) {
1902
+ if (noteGate.dropped === 'capability') {
1660
1903
  hostLog('debug', `liveToolApprovalWire: ${approvalId} card supplied a deny reason (len=${denyReasonSnapshot.length}) but the ` +
1661
1904
  'approvalDecisionNote capability is not confirmed for this engine — note not sent (an unknown key would be ' +
1662
1905
  'silently swallowed by an older server while still acking 200)');
1663
1906
  }
1664
- else if (denyReasonSnapshot.length > MAX_RESPOND_NOTE_CHARS) {
1907
+ else {
1665
1908
  hostLog('debug', `liveToolApprovalWire: ${approvalId} deny reason exceeds the decision_note cap ` +
1666
1909
  `(len=${denyReasonSnapshot.length} > ${MAX_RESPOND_NOTE_CHARS}) — note not sent at all (the server rejects the WHOLE ` +
1667
1910
  'respond with 400 over an oversize note, and a silently halved audit reason is worse than none)');
1668
1911
  }
1669
- else {
1670
- note = denyReasonSnapshot;
1671
- }
1672
1912
  }
1673
1913
  try {
1674
1914
  let persistRule;
@@ -1735,7 +1975,7 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
1735
1975
  }
1736
1976
  const respondOpts = {
1737
1977
  ...(signal && !signal.aborted ? { signal } : {}),
1738
- ...(card.kind === 'allow' && card.updatedInput !== undefined ? { updatedInput: card.updatedInput } : {}),
1978
+ ...(basePayload?.updatedInput !== undefined ? { updatedInput: basePayload.updatedInput } : {}),
1739
1979
  ...(persistRule !== undefined ? { persistRule } : {}),
1740
1980
  ...(persistRuleEdited !== undefined ? { persistRuleEdited } : {}),
1741
1981
  ...(persistRuleBatchOfferIndex !== undefined ? { persistRuleBatchOfferIndex } : {}),
@@ -1745,6 +1985,8 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
1745
1985
  let attributionDropped = false;
1746
1986
  let attributionRenamed = false;
1747
1987
  let raw;
1988
+ if (basePayload !== undefined)
1989
+ emitAnswerCommitted(liveFrameCallKey(approvalId), 'respond', answerSnapshot, basePayload);
1748
1990
  try {
1749
1991
  raw = await respond(approvalId, decision, respondOpts);
1750
1992
  }
@@ -1790,41 +2032,56 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
1790
2032
  hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered 202 parking_in_progress${parking.retryAfterMs !== undefined ? ` (retryAfterMs=${parking.retryAfterMs})` : ''} — ` +
1791
2033
  `the answer did NOT take effect${parking.decidePath !== undefined ? ' (the engine pointed at a decide path for the parked card)' : ''}; not re-sent, not reported as decided; the user is told to answer again on the parked card`);
1792
2034
  surfaceApprovalParking(parking.retryAfterMs);
1793
- return { decision: 'unresolved', parking };
2035
+ return { kind: 'unsettled', lane: 'respond', cause: 'parking_in_progress', parking };
1794
2036
  }
1795
2037
  hostLog('error', `liveToolApprovalWire: 202 parking body for ${approvalId} is ${parkingBody.kind === 'parking' ? `for another approval (${parkingBody.view.approvalId})` : 'unreadable (approvalId is not usable)'} — ` +
1796
2038
  'reporting the answer as NOT settled (never as decided, never as parked); not re-sent');
1797
- return { decision: 'unresolved' };
2039
+ return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
1798
2040
  }
1799
2041
  const parsed = readToolApprovalRespondAck(raw);
2042
+ const replayedForThisCard = parsed !== undefined && isToolApprovalRespondReplayed(parsed) && parsed.approvalId === approvalId;
2043
+ const claimsReplay = ownIdempotentTrue(raw) || (parsed !== undefined && isToolApprovalRespondReplayed(parsed));
2044
+ if (claimsReplay && !replayedForThisCard) {
2045
+ const claimed = replayClaimOf(raw);
2046
+ if (claimed.approvalId === approvalId && claimed.decision !== undefined && !claimed.carriesDelivery) {
2047
+ hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay (body not in the full replay shape) — the approval was already decided ` +
2048
+ `(${claimed.decision}); this answer was not delivered, reporting the earlier decision`);
2049
+ return { kind: 'decided', lane: 'respond', decision: claimed.decision };
2050
+ }
2051
+ hostLog('error', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay of an earlier decision that cannot be read or names another approval — ` +
2052
+ 'reporting the answer as NOT settled (never as the decision this client sent); not re-sent');
2053
+ return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
2054
+ }
1800
2055
  let ack = parsed;
1801
- if (parsed !== undefined && (parsed.approvalId !== approvalId || parsed.decision !== decision)) {
2056
+ if (parsed !== undefined && (parsed.approvalId !== approvalId || (!isToolApprovalRespondReplayed(parsed) && parsed.decision !== decision))) {
1802
2057
  hostLog('error', `liveToolApprovalWire: DISCARDING respond ack for ${approvalId} — it does not correlate ` +
1803
2058
  `(ack.approvalId=${parsed.approvalId} ack.decision=${parsed.decision}, sent decision=${decision}); ` +
1804
2059
  'treating as "no ack" (unknown) — never surfacing a safety notice off an unrelated receipt');
1805
2060
  ack = undefined;
1806
2061
  }
1807
- if (ack !== undefined &&
1808
- (ack.persistedRule !== undefined || ack.persistedRules !== undefined || ack.persistedRuleAnchors !== undefined)) {
2062
+ const replayed = ack !== undefined && isToolApprovalRespondReplayed(ack) ? ack : undefined;
2063
+ let applied = ack !== undefined && replayed === undefined ? ack : undefined;
2064
+ if (applied !== undefined &&
2065
+ (applied.persistedRule !== undefined || applied.persistedRules !== undefined || applied.persistedRuleAnchors !== undefined)) {
1809
2066
  const sentEditedArm = persistRule !== undefined && persistRuleEdited === true;
1810
2067
  const expectedBatchCount = batchArmTarget?.rules.length;
1811
2068
  const anchorKeyOnWire = typeof raw === 'object' &&
1812
2069
  raw !== null &&
1813
2070
  Object.prototype.hasOwnProperty.call(raw, 'persistedRuleAnchors');
1814
- const anchors = ack.persistedRuleAnchors;
2071
+ const anchors = applied.persistedRuleAnchors;
1815
2072
  const anchorsOk = anchors !== undefined
1816
2073
  ? persistRuleBatchOfferIndex !== undefined &&
1817
2074
  expectedBatchCount !== undefined &&
1818
2075
  anchors.every(a => a.offerIndex === persistRuleBatchOfferIndex && a.memberIndex < expectedBatchCount)
1819
2076
  : !anchorKeyOnWire;
1820
- const batchEchoOk = ack.persistedRules !== undefined &&
2077
+ const batchEchoOk = applied.persistedRules !== undefined &&
1821
2078
  expectedBatchCount !== undefined &&
1822
- ack.persistedRules.length === expectedBatchCount &&
2079
+ applied.persistedRules.length === expectedBatchCount &&
1823
2080
  anchorsOk;
1824
2081
  const strayEchoes = [];
1825
- if (ack.persistedRule !== undefined && !sentEditedArm)
2082
+ if (applied.persistedRule !== undefined && !sentEditedArm)
1826
2083
  strayEchoes.push('persistedRule');
1827
- if (ack.persistedRules !== undefined && !batchEchoOk)
2084
+ if (applied.persistedRules !== undefined && !batchEchoOk)
1828
2085
  strayEchoes.push('persistedRules');
1829
2086
  if (anchors !== undefined && !(anchorsOk && batchEchoOk))
1830
2087
  strayEchoes.push('persistedRuleAnchors');
@@ -1833,60 +2090,53 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
1833
2090
  `${approvalId} — they do not correlate with the persistence arm actually sent ` +
1834
2091
  `(editedArm=${String(sentEditedArm)} batchArmMembers=${String(expectedBatchCount ?? 'none')} ` +
1835
2092
  `sentOfferIndex=${String(persistRuleBatchOfferIndex ?? 'none')} ` +
1836
- `echoedRules=${String(ack.persistedRules?.length ?? 'none')} ` +
2093
+ `echoedRules=${String(applied.persistedRules?.length ?? 'none')} ` +
1837
2094
  `echoedAnchorOffer=${String(anchors?.[0]?.offerIndex ?? 'none')}); never telling the user a rule ` +
1838
2095
  'was stored off a receipt that does not line up with what this client actually sent');
1839
- const { persistedRule: _pr, persistedRules: _prs, persistedRuleAnchors: _pra, ...rest } = ack;
2096
+ const { persistedRule: _pr, persistedRules: _prs, persistedRuleAnchors: _pra, ...rest } = applied;
1840
2097
  void _pr;
1841
2098
  void _prs;
1842
2099
  void _pra;
1843
- ack = {
2100
+ applied = {
1844
2101
  ...rest,
1845
- ...(ack.persistedRule !== undefined && sentEditedArm ? { persistedRule: ack.persistedRule } : {}),
1846
- ...(ack.persistedRules !== undefined && batchEchoOk ? { persistedRules: ack.persistedRules } : {}),
2102
+ ...(applied.persistedRule !== undefined && sentEditedArm ? { persistedRule: applied.persistedRule } : {}),
2103
+ ...(applied.persistedRules !== undefined && batchEchoOk ? { persistedRules: applied.persistedRules } : {}),
1847
2104
  ...(anchors !== undefined && anchorsOk && batchEchoOk ? { persistedRuleAnchors: anchors } : {}),
1848
2105
  };
2106
+ ack = applied;
1849
2107
  }
1850
2108
  }
1851
- if (ruleArmDroppedForCaps)
2109
+ if (replayed === undefined && ruleArmDroppedForCaps)
1852
2110
  surfaceRuleArmNotSent();
1853
- if (ruleArmDroppedForCheck !== undefined)
2111
+ if (replayed === undefined && ruleArmDroppedForCheck !== undefined)
1854
2112
  surfaceRuleArmRejected(ruleArmDroppedForCheck);
1855
- if (decision === 'allow_session' && ack?.rememberApplied === false) {
2113
+ if (decision === 'allow_session' && applied?.rememberApplied === false) {
1856
2114
  logAfterDecided(`liveToolApprovalWire: ${approvalId} allow_session ack rememberApplied=false — grant not stored, surfacing honest notice`);
1857
2115
  surfaceRememberNotApplied();
1858
2116
  }
1859
- if (respondOpts.updatedInput !== undefined && ack?.updatedInputForwarded === false) {
2117
+ if (respondOpts.updatedInput !== undefined && applied?.updatedInputForwarded === false) {
1860
2118
  logAfterDecided(`liveToolApprovalWire: ${approvalId} edited args were NOT forwarded (ack.updatedInputForwarded=false) — the tool runs on the ORIGINAL input`);
1861
2119
  surfaceEditNotForwarded();
1862
2120
  }
1863
- if (note !== undefined && ack !== undefined && ack.noteRecorded !== true) {
2121
+ if (note !== undefined && applied !== undefined && applied.noteRecorded !== true) {
1864
2122
  hostLog('debug', `liveToolApprovalWire: ${approvalId} decision note was sent but not persisted ` +
1865
- `(ack.noteRecorded=${String(ack.noteRecorded)}) — decision stood; the audit note did not land on the ask row`);
2123
+ `(ack.noteRecorded=${String(applied.noteRecorded)}) — decision stood; the audit note did not land on the ask row`);
1866
2124
  }
1867
- const denyAttribution = decision === 'deny'
1868
- ? {
1869
- ...(attributionDropped ? { denyAttributionDropped: true } : {}),
1870
- ...(attributionRenamed ? { denyAttributionRenamed: true } : {}),
1871
- ...(denySettledByForWire !== undefined ? { denySettledBy: denySettledByForWire } : {}),
1872
- ...(denyReasonSnapshot !== undefined && denyReasonSnapshot.trim() !== ''
1873
- ? { denyReason: denyReasonSnapshot }
1874
- : {}),
1875
- }
1876
- : {};
1877
- return ack !== undefined ? { decision, ack, ...denyAttribution } : { decision, ...denyAttribution };
2125
+ if (replayed !== undefined) {
2126
+ hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay — the approval was already decided (${replayed.decision}); ` +
2127
+ 'this answer was not delivered, reporting the earlier decision');
2128
+ return { kind: 'decided', lane: 'respond', decision: replayed.decision, ack: replayed };
2129
+ }
2130
+ const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed) : undefined;
2131
+ return { kind: 'decided', lane: 'respond', decision, ...(ack !== undefined ? { ack } : {}), ...(deny !== undefined ? { deny } : {}) };
1878
2132
  }
1879
2133
  catch (e) {
1880
- const respondRefusal = readToolApprovalRespondRefusal(e);
1881
- const attributionRefusal = denyAttributionRefusalFromError(e, lastSendCarriedAttribution);
2134
+ const r = resolutionOfDecisionFailure(e, { lane: 'respond', route: 'live_respond', attributionCarried: lastSendCarriedAttribution });
2135
+ const refusal = r.kind === 'unsettled' ? r.refusal : undefined;
1882
2136
  hostLog('debug', `liveToolApprovalWire: respond(${decision}) failed for ${approvalId} ` +
1883
- `(status=${respondRefusal?.status ?? 'none'} errorCode=${logSafeErrorCode(respondRefusal?.errorCode)} ` +
1884
- `messageLen=${respondRefusal?.message?.length ?? 0}) — engine self-settles (TTL/abort); ` +
1885
- 'the refusal text is handed back on outcome.respondRefusal for the host to surface');
1886
- return {
1887
- decision: 'unresolved',
1888
- ...(respondRefusal !== undefined ? { respondRefusal } : {}),
1889
- ...(attributionRefusal !== null ? { denyAttributionRefusal: attributionRefusal } : {}),
1890
- };
2137
+ `(status=${refusal?.status ?? 'none'} errorCode=${logSafeErrorCode(refusal?.errorCode)} ` +
2138
+ `messageLen=${refusal?.message?.length ?? 0}) — engine self-settles (TTL/abort); ` +
2139
+ 'the refusal reading is handed back on resolution.refusal for the host to surface');
2140
+ return r;
1891
2141
  }
1892
2142
  }