@sema-agent/client-core 0.93.1 → 0.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/CHANGELOG.md +554 -0
  2. package/README.md +89 -55
  3. package/dist/adapt/arms.js +13 -11
  4. package/dist/adapt/panelTasks.js +4 -10
  5. package/dist/adapt/toolCards.js +2 -2
  6. package/dist/adapt/wireShapes.js +3 -8
  7. package/dist/adapter/activeRunSelfHeal.d.ts +8 -3
  8. package/dist/adapter/activeRunSelfHeal.js +20 -12
  9. package/dist/adapter/downstream/eventToSdkMessage.js +24 -17
  10. package/dist/adapter/downstream/terminalToSdkResult.js +13 -6
  11. package/dist/adapter/runStream.js +2 -1
  12. package/dist/adapter/types.d.ts +7 -0
  13. package/dist/adapter/types.js +15 -0
  14. package/dist/agentSession/backgroundView.js +5 -1
  15. package/dist/agentSession/engineAgentRegistryRead.js +2 -1
  16. package/dist/agentSession/engineSessionBackgroundStop.js +5 -3
  17. package/dist/agentsWireCaps.d.ts +17 -0
  18. package/dist/agentsWireCaps.js +111 -1
  19. package/dist/approvalDecidedByCapability.d.ts +0 -1
  20. package/dist/approvalDecidedByCapability.js +2 -7
  21. package/dist/approvalReachWire.d.ts +18 -0
  22. package/dist/approvalReachWire.js +58 -0
  23. package/dist/approvalsStreamLiveCapability.d.ts +0 -1
  24. package/dist/approvalsStreamLiveCapability.js +15 -14
  25. package/dist/atMostOnce.d.ts +8 -0
  26. package/dist/atMostOnce.js +36 -0
  27. package/dist/attachmentSeatCapability.d.ts +0 -1
  28. package/dist/attachmentSeatCapability.js +0 -3
  29. package/dist/attachmentsWireCaps.js +11 -6
  30. package/dist/bashExitReading.d.ts +10 -0
  31. package/dist/bashExitReading.js +12 -0
  32. package/dist/callerCwdCapability.d.ts +15 -0
  33. package/dist/callerCwdCapability.js +42 -0
  34. package/dist/centerWiringCapability.d.ts +1 -3
  35. package/dist/centerWiringCapability.js +18 -21
  36. package/dist/classifierStatus.js +4 -4
  37. package/dist/cloudConfigWireCaps.js +43 -21
  38. package/dist/compensations.js +1 -1
  39. package/dist/controlRouter.js +3 -10
  40. package/dist/coreValuePorts.d.ts +64 -1
  41. package/dist/coreValuePorts.js +30 -0
  42. package/dist/cycleOrder.d.ts +15 -0
  43. package/dist/cycleOrder.js +45 -0
  44. package/dist/decideFailureNote.js +4 -1
  45. package/dist/decideReceipt.d.ts +2 -1
  46. package/dist/decideReceipt.js +41 -13
  47. package/dist/declared/supportFloorDeclared.d.ts +1 -0
  48. package/dist/declared/supportFloorDeclared.js +5 -0
  49. package/dist/detachWire.d.ts +1 -4
  50. package/dist/detachWire.js +1 -1
  51. package/dist/deviceExecutorManagementCapability.d.ts +0 -1
  52. package/dist/deviceExecutorManagementCapability.js +36 -40
  53. package/dist/deviceFlowPolling.d.ts +5 -0
  54. package/dist/deviceFlowPolling.js +28 -0
  55. package/dist/deviceRebind.d.ts +88 -0
  56. package/dist/deviceRebind.js +242 -0
  57. package/dist/displayUntrusted.d.ts +1 -0
  58. package/dist/displayUntrusted.js +963 -30
  59. package/dist/engineAgentAbsence.d.ts +2 -2
  60. package/dist/engineAgentPanelStore.d.ts +8 -18
  61. package/dist/engineAgentPanelStore.js +19 -537
  62. package/dist/engineCapReader.d.ts +6 -6
  63. package/dist/engineCapReader.js +30 -43
  64. package/dist/engineCapsCache.d.ts +7 -2
  65. package/dist/engineCapsCache.js +64 -26
  66. package/dist/engineCredentialRequirement.d.ts +9 -0
  67. package/dist/engineCredentialRequirement.js +23 -0
  68. package/dist/engineEnvSourcing.d.ts +53 -0
  69. package/dist/engineEnvSourcing.js +81 -0
  70. package/dist/engineErrorCodes.d.ts +19 -4
  71. package/dist/engineErrorCodes.js +39 -21
  72. package/dist/engineIdentity.js +2 -0
  73. package/dist/engineNoticeCodes.d.ts +22 -2
  74. package/dist/engineNoticeCodes.js +73 -6
  75. package/dist/engineRefusedToolName.d.ts +1 -0
  76. package/dist/engineRefusedToolName.js +8 -0
  77. package/dist/engineWireSdk.js +2 -2
  78. package/dist/executionLaneCapability.d.ts +2 -3
  79. package/dist/executionLaneCapability.js +28 -28
  80. package/dist/fileHistoryCaptureCapability.d.ts +0 -1
  81. package/dist/fileHistoryCaptureCapability.js +13 -12
  82. package/dist/fileHistoryLegNotice.d.ts +14 -2
  83. package/dist/fileHistoryLegNotice.js +101 -15
  84. package/dist/fleet/fleetLedger.d.ts +2 -0
  85. package/dist/fleet/fleetLedger.js +74 -52
  86. package/dist/fleet/fleetProjection.d.ts +0 -3
  87. package/dist/fleet/fleetProjection.js +9 -16
  88. package/dist/fleetAgentPanelProjection.d.ts +4 -17
  89. package/dist/fleetAgentPanelProjection.js +7 -197
  90. package/dist/fleetAgentRowLike.d.ts +16 -0
  91. package/dist/fleetAgentRowLike.js +1 -0
  92. package/dist/gateOutcome.d.ts +2 -1
  93. package/dist/gateOutcome.js +33 -18
  94. package/dist/gateVocabulary.js +1 -1
  95. package/dist/generated/engineAskFaceTables.d.ts +3 -0
  96. package/dist/generated/engineAskFaceTables.js +12 -0
  97. package/dist/generated/engineFactTables.d.ts +4 -2
  98. package/dist/generated/engineFactTables.js +13 -0
  99. package/dist/generated/engineNoticeTables.d.ts +5 -0
  100. package/dist/generated/engineNoticeTables.js +7 -0
  101. package/dist/generated/sdkFactTables.d.ts +5 -0
  102. package/dist/generated/sdkFactTables.js +34 -0
  103. package/dist/generated/settingsSchemaFns.d.ts +10 -0
  104. package/dist/generated/settingsSchemaFns.js +22 -0
  105. package/dist/generated/settingsSchemaTables.d.ts +3 -0
  106. package/dist/generated/settingsSchemaTables.js +3 -0
  107. package/dist/headlessPermissionModeWire.d.ts +2 -2
  108. package/dist/headlessPermissionModeWire.js +11 -21
  109. package/dist/headlessReconnectWire.js +49 -20
  110. package/dist/hitl/allowRuleShadow.d.ts +1 -1
  111. package/dist/hitl/allowRuleShadow.js +49 -8
  112. package/dist/hitl/approvalCompleteParked.d.ts +1 -0
  113. package/dist/hitl/approvalCompleteParked.js +14 -0
  114. package/dist/hitl/approvalEndingCause.d.ts +5 -0
  115. package/dist/hitl/approvalEndingCause.js +61 -0
  116. package/dist/hitl/approvalHolds.d.ts +46 -0
  117. package/dist/hitl/approvalHolds.js +140 -0
  118. package/dist/hitl/approvalOutcomeNote.js +2 -1
  119. package/dist/hitl/approvalResolution.js +9 -1
  120. package/dist/hitl/approvalsFeed.js +23 -6
  121. package/dist/hitl/askGateWire.d.ts +2 -1
  122. package/dist/hitl/askGateWire.js +29 -1
  123. package/dist/hitl/automaticAnswerVerdict.d.ts +8 -2
  124. package/dist/hitl/automaticAnswerVerdict.js +71 -23
  125. package/dist/hitl/crashConverged.js +2 -0
  126. package/dist/hitl/decisionConflict.d.ts +3 -0
  127. package/dist/hitl/decisionConflict.js +43 -0
  128. package/dist/hitl/decisionRefusal.d.ts +23 -0
  129. package/dist/hitl/decisionRefusal.js +88 -0
  130. package/dist/hitl/frameRouter.d.ts +10 -0
  131. package/dist/hitl/frameRouter.js +71 -32
  132. package/dist/hitl/gateLedger.d.ts +8 -0
  133. package/dist/hitl/gateLedger.js +38 -0
  134. package/dist/hitl/hitlBridge.d.ts +14 -3
  135. package/dist/hitl/hitlBridge.js +75 -62
  136. package/dist/hitl/hitlHostSurface.d.ts +3 -0
  137. package/dist/hitl/hitlHostSurface.js +17 -15
  138. package/dist/hitl/livePendingAsk.d.ts +16 -6
  139. package/dist/hitl/livePendingAsk.js +36 -4
  140. package/dist/hitl/localAllowRule.js +2 -1
  141. package/dist/hitl/noAllowCause.d.ts +9 -0
  142. package/dist/hitl/noAllowCause.js +26 -0
  143. package/dist/hitl/parkExpiryJudgements.d.ts +19 -0
  144. package/dist/hitl/parkExpiryJudgements.js +46 -0
  145. package/dist/hitl/parkResolver.d.ts +3 -7
  146. package/dist/hitl/parkResolver.js +55 -28
  147. package/dist/hitl/parkRowBirthWait.js +2 -1
  148. package/dist/hitl/persistedRulesWire.d.ts +6 -1
  149. package/dist/hitl/persistedRulesWire.js +16 -4
  150. package/dist/hitl/planReviewWire.d.ts +2 -3
  151. package/dist/hitl/planReviewWire.js +86 -39
  152. package/dist/hitl/replayedApproval.d.ts +8 -0
  153. package/dist/hitl/replayedApproval.js +125 -10
  154. package/dist/hitl/resumeRunningCard.d.ts +2 -0
  155. package/dist/hitl/resumeRunningCard.js +15 -4
  156. package/dist/hitl/sessionPolicyDeliverable.d.ts +1 -1
  157. package/dist/hitl/sessionPolicyDeliverable.js +3 -8
  158. package/dist/hitl/sessionPolicyWire.js +1 -1
  159. package/dist/hitl/toolApprovalWire.d.ts +50 -5
  160. package/dist/hitl/toolApprovalWire.js +368 -56
  161. package/dist/hooksWireCaps.js +38 -6
  162. package/dist/host.d.ts +5 -0
  163. package/dist/host.js +3 -1
  164. package/dist/hostCallbackGuard.d.ts +1 -0
  165. package/dist/hostCallbackGuard.js +25 -0
  166. package/dist/humanTurnRead.d.ts +15 -1
  167. package/dist/humanTurnRead.js +28 -2
  168. package/dist/index.d.ts +45 -14
  169. package/dist/index.js +33 -12
  170. package/dist/interactiveInterrupt.d.ts +8 -0
  171. package/dist/interactiveInterrupt.js +54 -0
  172. package/dist/limitsWire.d.ts +5 -1
  173. package/dist/limitsWire.js +51 -13
  174. package/dist/liveModelCatalog.d.ts +109 -0
  175. package/dist/liveModelCatalog.js +344 -18
  176. package/dist/mcpEngineLeg.d.ts +25 -0
  177. package/dist/mcpEngineLeg.js +71 -15
  178. package/dist/mcpPanel.js +2 -1
  179. package/dist/mcpProbeCapability.d.ts +0 -1
  180. package/dist/mcpProbeCapability.js +13 -12
  181. package/dist/mcpReconnect.d.ts +1 -3
  182. package/dist/mcpReconnect.js +20 -10
  183. package/dist/memoryComplianceCapability.d.ts +0 -1
  184. package/dist/memoryComplianceCapability.js +14 -13
  185. package/dist/memoryOriginCapability.d.ts +0 -1
  186. package/dist/memoryOriginCapability.js +14 -13
  187. package/dist/memoryVerbsWire.d.ts +1 -1
  188. package/dist/memoryVerbsWire.js +4 -0
  189. package/dist/model/liveCatalogShape.d.ts +14 -0
  190. package/dist/model/liveCatalogShape.js +71 -0
  191. package/dist/model/modelSupplyRules.d.ts +1 -0
  192. package/dist/model/modelSupplyRules.js +30 -9
  193. package/dist/model/modelsPolicy.d.ts +21 -0
  194. package/dist/model/modelsPolicy.js +90 -0
  195. package/dist/modelIdentityResolvability.d.ts +4 -2
  196. package/dist/modelIdentityResolvability.js +25 -5
  197. package/dist/modelSwitchFallback.d.ts +69 -0
  198. package/dist/modelSwitchFallback.js +206 -0
  199. package/dist/notifications.js +12 -11
  200. package/dist/outputStyleSeatCapability.d.ts +15 -0
  201. package/dist/outputStyleSeatCapability.js +42 -0
  202. package/dist/ownKey.d.ts +7 -0
  203. package/dist/ownKey.js +58 -0
  204. package/dist/panelReduce.d.ts +114 -0
  205. package/dist/panelReduce.js +1135 -0
  206. package/dist/panelStateHolder.d.ts +17 -0
  207. package/dist/panelStateHolder.js +201 -0
  208. package/dist/peerLaneCapability.d.ts +0 -1
  209. package/dist/peerLaneCapability.js +15 -14
  210. package/dist/permissionModesCapability.d.ts +0 -1
  211. package/dist/permissionModesCapability.js +5 -10
  212. package/dist/permissionRuleIssue.js +1 -1
  213. package/dist/permissionRulesWriteCapability.d.ts +0 -1
  214. package/dist/permissionRulesWriteCapability.js +15 -14
  215. package/dist/permissionsAllowGrantCapability.d.ts +2 -1
  216. package/dist/permissionsAllowGrantCapability.js +35 -19
  217. package/dist/permissionsDeclaration.d.ts +16 -1
  218. package/dist/permissionsDeclaration.js +142 -15
  219. package/dist/printToolResultFrame.js +2 -10
  220. package/dist/promptDelivery.js +0 -8
  221. package/dist/protocolSegment.d.ts +1 -0
  222. package/dist/protocolSegment.js +13 -1
  223. package/dist/request/taskRequest.d.ts +11 -3
  224. package/dist/request/taskRequest.js +13 -1
  225. package/dist/resultFrameKeys.d.ts +4 -0
  226. package/dist/resultFrameKeys.js +13 -0
  227. package/dist/resumeRefusalCopy.d.ts +18 -1
  228. package/dist/resumeRefusalCopy.js +132 -31
  229. package/dist/retryStatus.js +6 -4
  230. package/dist/rewindArchiveCapability.d.ts +2 -1
  231. package/dist/rewindArchiveCapability.js +28 -15
  232. package/dist/rewindEngineAction.d.ts +21 -0
  233. package/dist/rewindEngineAction.js +36 -0
  234. package/dist/rewindWireCaps.d.ts +2 -1
  235. package/dist/rewindWireCaps.js +1 -0
  236. package/dist/{hitl/rosterMountedNames.d.ts → rosterMountedNames.d.ts} +1 -1
  237. package/dist/runCancelContext.js +2 -1
  238. package/dist/runTerminal.d.ts +0 -11
  239. package/dist/runTerminal.js +4 -27
  240. package/dist/sdkRegistryTransit.d.ts +2 -1
  241. package/dist/sdkRegistryTransit.js +1 -0
  242. package/dist/sealedKeyCapability.d.ts +0 -1
  243. package/dist/sealedKeyCapability.js +19 -20
  244. package/dist/seatContract.d.ts +85 -28
  245. package/dist/seatContract.js +512 -148
  246. package/dist/sessionIdSegment.d.ts +1 -0
  247. package/dist/sessionIdSegment.js +11 -0
  248. package/dist/sessionMemoryErase.d.ts +1 -1
  249. package/dist/sessionMemoryErase.js +6 -1
  250. package/dist/sessionMemoryStatus.js +3 -2
  251. package/dist/sessionPolicyCapability.d.ts +0 -1
  252. package/dist/sessionPolicyCapability.js +14 -13
  253. package/dist/sessionReadRedactionCapability.d.ts +15 -0
  254. package/dist/sessionReadRedactionCapability.js +42 -0
  255. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
  256. package/dist/sessionSyncFileHistoryCapability.js +13 -12
  257. package/dist/skillToolListsCapability.d.ts +0 -1
  258. package/dist/skillToolListsCapability.js +13 -12
  259. package/dist/skillsWireCaps.d.ts +4 -0
  260. package/dist/skillsWireCaps.js +66 -1
  261. package/dist/sqlEngineCapability.d.ts +0 -1
  262. package/dist/sqlEngineCapability.js +21 -21
  263. package/dist/steerPriorityCapability.d.ts +0 -1
  264. package/dist/steerPriorityCapability.js +18 -19
  265. package/dist/storePostureCapability.d.ts +1 -1
  266. package/dist/storePostureCapability.js +16 -16
  267. package/dist/streamDrainingLegs.d.ts +6 -0
  268. package/dist/streamDrainingLegs.js +23 -0
  269. package/dist/subagent/agentStateDetail.d.ts +1 -0
  270. package/dist/subagent/agentStateDetail.js +41 -0
  271. package/dist/subagent/engineCompactWire.d.ts +15 -0
  272. package/dist/subagent/engineCompactWire.js +82 -45
  273. package/dist/subagent/engineDelegatedPrompt.js +10 -6
  274. package/dist/subagent/engineSubagentOutput.d.ts +0 -1
  275. package/dist/subagent/engineSubagentOutput.js +5 -20
  276. package/dist/subagent/engineSubagentTail.d.ts +0 -1
  277. package/dist/subagent/engineSubagentTail.js +7 -32
  278. package/dist/subagentContentStore.js +3 -3
  279. package/dist/supportFloor.js +2 -5
  280. package/dist/taskRequestWords.d.ts +2 -4
  281. package/dist/taskRequestWords.js +3 -2
  282. package/dist/terminalStatuses.d.ts +30 -0
  283. package/dist/terminalStatuses.js +67 -0
  284. package/dist/toolResult.d.ts +5 -0
  285. package/dist/toolResult.js +88 -36
  286. package/dist/types/engineState.d.ts +2 -1
  287. package/dist/usageAnalytics.d.ts +146 -0
  288. package/dist/usageAnalytics.js +311 -0
  289. package/dist/userAttachmentEnvelope.d.ts +22 -1
  290. package/dist/userAttachmentEnvelope.js +90 -2
  291. package/dist/userInvokedSkillsCapability.d.ts +0 -1
  292. package/dist/userInvokedSkillsCapability.js +13 -12
  293. package/dist/webSearchBackendCapability.d.ts +0 -1
  294. package/dist/webSearchBackendCapability.js +20 -19
  295. package/dist/webSearchWireCaps.js +3 -11
  296. package/dist/wireErrorTriage.d.ts +11 -0
  297. package/dist/wireErrorTriage.js +35 -0
  298. package/dist/wireFailureShape.d.ts +10 -0
  299. package/dist/wireFailureShape.js +72 -5
  300. package/dist/wireRefusalCopy.d.ts +6 -1
  301. package/dist/wireRefusalCopy.js +23 -25
  302. package/dist/wiringInstructionsAttribution.d.ts +10 -0
  303. package/dist/wiringInstructionsAttribution.js +67 -0
  304. package/dist/workflowClient.js +19 -2
  305. package/dist/workflowsJournalCapability.d.ts +0 -1
  306. package/dist/workflowsJournalCapability.js +13 -12
  307. package/dist/writeProtectionCapability.d.ts +0 -1
  308. package/dist/writeProtectionCapability.js +26 -26
  309. package/docs/INTEGRATION-CLIENTS.md +1733 -52
  310. package/package.json +5 -5
  311. package/dist/askSurvivesStandingAnswer.d.ts +0 -13
  312. package/dist/askSurvivesStandingAnswer.js +0 -53
  313. package/dist/engineCapsGenerationGuard.d.ts +0 -4
  314. package/dist/engineCapsGenerationGuard.js +0 -19
  315. package/dist/hitl/askSurvivesPosture.d.ts +0 -20
  316. package/dist/hitl/askSurvivesPosture.js +0 -48
  317. package/dist/panelRunningHistory.d.ts +0 -22
  318. package/dist/panelRunningHistory.js +0 -82
  319. package/dist/registryConflict.d.ts +0 -3
  320. package/dist/registryConflict.js +0 -44
  321. /package/dist/{hitl/rosterMountedNames.js → rosterMountedNames.js} +0 -0
@@ -1,18 +1,22 @@
1
- import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, DecideTransportRetryExhaustedError, HitlBridge, NO_APPROVAL_CARD_PORT_REASON, HitlSafetyError, denyReasonForWire, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
1
+ import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, DecideTransportRetryExhaustedError, HitlBridge, NO_APPROVAL_CARD_PORT_REASON, denyReasonForWire, findPendingForTask, hitlSafetyCodeOf, readDecideCurrentPending, readWireErrorCode, readWireErrorCodeReading, } from './hitlBridge.js';
2
2
  import { askParkRowArm } from './askParkRowRouting.js';
3
3
  import { hostLog } from '../host.js';
4
- import { readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
4
+ import { readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
5
5
  import { readAskClassPair } from './askClassPair.js';
6
- import { legacyShadowedRuleTextOf, readAllowRuleShadowFromWire } from './allowRuleShadow.js';
6
+ import { readAllowRuleShadowFromWire } from './allowRuleShadow.js';
7
7
  import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
8
8
  import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
9
- import { observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
9
+ import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
10
10
  import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
11
+ import { displayUntrusted } from '../displayUntrusted.js';
12
+ import { approvalDecisionHolds, awaitLiveCardHolder, holdLiveCard, joinLiveCard, liveCardHoldFor, liveCardHoldOf } from './approvalHolds.js';
13
+ import { approvalResolutionOf } from './approvalResolution.js';
11
14
  import { isApprovalMandateWord } from '../gateVocabulary.js';
12
15
  import { denyAttributionRefusalFromError } from '../wireRefusalCopy.js';
13
16
  import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
14
17
  import { decideFailureNote } from '../decideFailureNote.js';
15
- import { refusalSaysStillPending } from '../engineErrorCodes.js';
18
+ import { thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
19
+ import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED, refusalSaysStillPending } from '../engineErrorCodes.js';
16
20
  import { readDecideReceipt } from '../decideReceipt.js';
17
21
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, PERSISTED_RULE_MANDATES, } from '@sema-agent/sdk';
18
22
  export function isFsApprovalGate(gate) {
@@ -110,6 +114,7 @@ export function approvalCardPortFor(sessionKey) {
110
114
  export function _resetApprovalCardPortForTest() {
111
115
  cardPortByKey.clear();
112
116
  cardPortMissesByKey.clear();
117
+ callCardLedger.clear();
113
118
  }
114
119
  export function surfaceApprovalCard(req) {
115
120
  const cardPort = approvalCardPortFor(DEFAULT_SESSION_KEY);
@@ -127,17 +132,26 @@ function cardPortMissReason() {
127
132
  return APPROVAL_CARD_PORT_KEY_MISMATCH_REASON;
128
133
  return NO_APPROVAL_CARD_PORT_REASON;
129
134
  }
130
- function rememberRefusedByServer(e) {
131
- switch (readWireErrorCode(e)) {
132
- case 'remember_not_in_proof':
133
- case 'remember_requires_binding':
134
- case 'capability.store_required':
135
- case 'decide.workflow_remember_unsupported':
136
- case 'decide.parked_remember_unsupported':
137
- return true;
138
- default:
139
- return false;
140
- }
135
+ const REMEMBER_REFUSAL_CODES = Object.freeze([
136
+ 'remember_not_in_proof',
137
+ 'remember_requires_binding',
138
+ 'capability.store_required',
139
+ DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED,
140
+ 'decide.parked_remember_unsupported',
141
+ ]);
142
+ export function rememberRefusalOf(error) {
143
+ return rememberRefusalIn(readWireErrorCodeReading(error));
144
+ }
145
+ function rememberRefusalIn(reading) {
146
+ return reading.state === 'code' ? REMEMBER_REFUSAL_CODES.find((c) => c === reading.code) : undefined;
147
+ }
148
+ function rememberFallbackKind(e) {
149
+ const reading = readWireErrorCodeReading(e);
150
+ if (rememberRefusalIn(reading) !== undefined)
151
+ return 'refused';
152
+ if (reading.state !== 'absent')
153
+ return undefined;
154
+ return readToolApprovalRespondRefusal(e)?.status === 400 ? 'legacy' : undefined;
141
155
  }
142
156
  function logAfterDecided(msg) {
143
157
  try {
@@ -170,17 +184,17 @@ function isParkRowInputSizeMarker(v) {
170
184
  const o = v;
171
185
  return o.truncated === true && typeof o.bytes === 'number' && Number.isFinite(o.bytes) && o.bytes >= 0;
172
186
  }
173
- const durableDecisionHolds = { inFlight: new Map(), settled: new Map(), openReads: new Set(), clock: 0 };
174
187
  function durableHoldKey(row, callKey) {
175
- return JSON.stringify([typeof row.sessionId === 'string' ? row.sessionId : null, typeof row.taskId === 'string' ? row.taskId : null, callKey]);
188
+ const createdAt = typeof row.createdAt === 'number' && Number.isFinite(row.createdAt) ? row.createdAt : null;
189
+ return JSON.stringify([typeof row.sessionId === 'string' ? row.sessionId : null, typeof row.taskId === 'string' ? row.taskId : null, callKey, createdAt]);
176
190
  }
177
191
  function openDurableRead() {
178
- const mark = ++durableDecisionHolds.clock;
179
- durableDecisionHolds.openReads.add(mark);
192
+ const mark = ++approvalDecisionHolds.clock;
193
+ approvalDecisionHolds.openReads.add(mark);
180
194
  return mark;
181
195
  }
182
196
  function closeDurableRead(mark) {
183
- const h = durableDecisionHolds;
197
+ const h = approvalDecisionHolds;
184
198
  h.openReads.delete(mark);
185
199
  let oldest = Infinity;
186
200
  for (const m of h.openReads)
@@ -191,14 +205,14 @@ function closeDurableRead(mark) {
191
205
  h.settled.delete(key);
192
206
  }
193
207
  function heldDurableDecision(holdKey, readMark) {
194
- const live = durableDecisionHolds.inFlight.get(holdKey);
208
+ const live = approvalDecisionHolds.inFlight.get(holdKey);
195
209
  if (live !== undefined)
196
- return live;
197
- const recent = durableDecisionHolds.settled.get(holdKey);
210
+ return live.lane === 'durable' ? live : undefined;
211
+ const recent = approvalDecisionHolds.settled.get(holdKey);
198
212
  return recent !== undefined && recent.at > readMark ? recent.hold : undefined;
199
213
  }
200
214
  function holdDurableDecision(holdKey, callerSignal, decide) {
201
- const h = durableDecisionHolds;
215
+ const h = approvalDecisionHolds;
202
216
  const interruption = new AbortController();
203
217
  let settled = false;
204
218
  const interrupt = () => {
@@ -215,7 +229,7 @@ function holdDurableDecision(holdKey, callerSignal, decide) {
215
229
  resolveOutcome = resolve;
216
230
  rejectOutcome = reject;
217
231
  });
218
- const hold = { outcome, presented: false, interrupt, cardSignal: interruption.signal };
232
+ const hold = { lane: 'durable', outcome, presented: false, interrupt, cardSignal: interruption.signal };
219
233
  h.inFlight.set(holdKey, hold);
220
234
  const release = () => {
221
235
  if (h.inFlight.get(holdKey) === hold)
@@ -276,11 +290,50 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
276
290
  return { kind: 'failed', stage: 'fetch', reason: found.reason, ...(found.code !== undefined ? { code: found.code } : {}) };
277
291
  if (held !== undefined)
278
292
  return joinDurableDecision(held, deps, signal);
293
+ const liveHolder = liveCardHoldFor(parkRowHolderIdentity(found.pending, found.gatedCallId));
294
+ if (liveHolder !== undefined) {
295
+ const joined = await awaitLiveCardHolder(liveHolder, signal);
296
+ if (joined !== undefined)
297
+ return fsOutcomeOfJoinedLiveCard(joined, found.gatedCallId);
298
+ return surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signal, parkGatedCallId);
299
+ }
279
300
  const callKey = approvalCallKey(found.gatedCallId, taskId);
280
301
  const fetched = found;
281
- return holdDurableDecision(durableHoldKey(fetched.pending, callKey), signal, (hold) => presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, fetched, decidedByReading, callKey, hold));
302
+ return holdDurableDecision(durableHoldKey(fetched.pending, callKey), signal, async (hold) => {
303
+ const slot = {};
304
+ const out = await presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, fetched, decidedByReading, callKey, hold, slot);
305
+ noteCallOutcome(slot, out);
306
+ return out;
307
+ });
282
308
  }
283
- async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, found, decidedByReading, callKey, hold) {
309
+ function parkRowHolderIdentity(row, gatedCallId) {
310
+ return {
311
+ ...(typeof row.taskId === 'string' && row.taskId !== '' ? { taskId: row.taskId } : {}),
312
+ ...(gatedCallId !== undefined && gatedCallId !== '' ? { toolCallId: gatedCallId } : {}),
313
+ ...(typeof row.sessionId === 'string' && row.sessionId !== '' ? { sessionId: row.sessionId } : {}),
314
+ };
315
+ }
316
+ function fsOutcomeOfJoinedLiveCard(o, gatedCallId) {
317
+ if (o.aborted === true)
318
+ return { kind: 'aborted', gatedCallId, joinedInFlight: true };
319
+ if (o.decision === 'unresolved')
320
+ return { kind: 'retracted', gatedCallId, joinedInFlight: true };
321
+ return {
322
+ kind: 'decided',
323
+ gatedCallId,
324
+ ...(o.decision === 'deny'
325
+ ? {
326
+ denied: true,
327
+ ...(o.denySettledBy !== undefined ? { denySettledBy: o.denySettledBy } : {}),
328
+ ...(o.denyReason !== undefined ? { denyReason: o.denyReason } : {}),
329
+ }
330
+ : {}),
331
+ ...(o.denyAttributionDropped === true ? { denyAttributionDropped: true } : {}),
332
+ ...(o.denyAttributionRenamed === true ? { denyAttributionRenamed: true } : {}),
333
+ joinedInFlight: true,
334
+ };
335
+ }
336
+ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, found, decidedByReading, callKey, hold, ledgerSlot) {
284
337
  const { pending, gatedCallId } = found;
285
338
  const toolName = typeof pending.toolName === 'string' ? pending.toolName : '';
286
339
  const streamArgs = gatedCallId !== undefined ? argsByCall.get(gatedCallId) : undefined;
@@ -304,7 +357,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
304
357
  const durableRuleOffersAbsence = pending.ruleOffersAbsence;
305
358
  hold.presented = true;
306
359
  deps.onPresented?.();
307
- const card = await surfaceApprovalCard({
360
+ const card = await surfaceCallTrackedCard(gatedCallId, undefined, ledgerSlot, {
308
361
  toolName,
309
362
  args,
310
363
  callKey,
@@ -319,16 +372,15 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
319
372
  ...(pending.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
320
373
  ...(hasOwnStrictTrue(pending, 'mandated') ? { mandated: true } : {}),
321
374
  ...((w) => (w !== undefined ? { mandate: w } : {}))(readApprovalMandate(pending)),
322
- ...((o) => (o !== undefined ? { origin: o } : {}))(readOwnNonEmptyString(pending, 'origin')),
375
+ ...((o) => (o !== undefined ? { origin: o } : {}))(readAskOrigin(pending)),
323
376
  ...(readAskClassPair(pending) ?? {}),
324
377
  ...(pending.gateKind === 'irreversible_ask' ? { irreversibleParkGate: true } : {}),
325
378
  ...(pending.hasBidiControls === true ? { hasBidiControls: true } : {}),
326
379
  ...(ruleOffersReadOnly !== undefined ? { ruleOffersReadOnly } : {}),
327
380
  ...(isWireRecordCarrier(durableProbeCause) ? { probeCause: durableProbeCause } : {}),
328
381
  ...(durableProbeReason !== undefined ? { probeReason: durableProbeReason } : {}),
329
- ...(readRememberRefused(pending) ? { rememberRefused: true } : {}),
382
+ ...(readApprovalRememberRefused(pending) ? { rememberRefused: true } : {}),
330
383
  ...(durableShadow !== undefined ? { allowRuleShadow: durableShadow } : {}),
331
- ...((t) => (t !== undefined ? { persistedRuleShadowed: t } : {}))(legacyShadowedRuleTextOf(durableShadow)),
332
384
  ...(typeof durableRuleOffersAbsence === 'string' && durableRuleOffersAbsence !== ''
333
385
  ? { ruleOffersAbsence: durableRuleOffersAbsence }
334
386
  : {}),
@@ -352,7 +404,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
352
404
  return { kind: 'retracted', gatedCallId };
353
405
  case 'aborted':
354
406
  {
355
- const cancelDeny = bridge.decideTool({ decision: 'deny', reason: 'Interrupted by user' }, gatedCallId, undefined, pending);
407
+ const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending);
356
408
  hold.trailing = cancelDeny;
357
409
  observeCancelByDeny(cancelDeny, taskId);
358
410
  }
@@ -369,12 +421,11 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
369
421
  sessionDecided = true;
370
422
  }
371
423
  catch (e) {
372
- if (e instanceof HitlSafetyError || e instanceof DecideTransportRetryExhaustedError)
424
+ const fallbackKind = rememberFallbackKind(e);
425
+ if (fallbackKind === undefined)
373
426
  throw e;
374
- if (readDecideCurrentPending(e) !== undefined)
375
- throw e;
376
- rememberRefused = rememberRefusedByServer(e);
377
- logAfterDecided(`liveToolApprovalWire: decide(approve+remember) failed (${String(e)}) — falling back to plain approve`);
427
+ rememberRefused = fallbackKind === 'refused';
428
+ logAfterDecided(`liveToolApprovalWire: decide(approve+remember) refused the remember part (${String(e)}) — falling back to plain approve`);
378
429
  }
379
430
  if (sessionDecided) {
380
431
  if (sessionReceipt?.rememberApplied === false)
@@ -392,13 +443,15 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
392
443
  const currentPending = readDecideCurrentPending(e);
393
444
  const wireCode = readWireErrorCode(e);
394
445
  const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
395
- const safetyCode = e instanceof HitlSafetyError ? e.code : undefined;
446
+ const safetyCode = hitlSafetyCodeOf(e);
447
+ const failureTexts = wireFailureTextsOf(e);
448
+ hostLog('debug', `liveToolApprovalWire: decide(approve) failed: ${failureTexts.log}`);
396
449
  return {
397
450
  kind: 'failed',
398
451
  stage: 'decide',
399
452
  gatedCallId,
400
- reason: `decide(approve) failed: ${String(e)}${decideFailureNote(e, 'approve')}`,
401
- ...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
453
+ reason: `decide(approve) failed: ${failureTexts.reason}${decideFailureNote(e, 'approve')}`,
454
+ ...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
402
455
  ...(currentPending !== undefined ? { currentPending } : {}),
403
456
  ...(wireCode !== undefined ? { errorCode: wireCode } : {}),
404
457
  ...(wireRetriable ? { retriable: true } : {}),
@@ -476,14 +529,16 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
476
529
  const currentPending = readDecideCurrentPending(e);
477
530
  const wireCode = readWireErrorCode(e);
478
531
  const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
479
- const safetyCode = e instanceof HitlSafetyError ? e.code : undefined;
532
+ const safetyCode = hitlSafetyCodeOf(e);
480
533
  const attributionRefusal = denyAttributionRefusalFromError(e, lastSendCarriedAttribution);
534
+ const failureTexts = wireFailureTextsOf(e);
535
+ hostLog('debug', `liveToolApprovalWire: decide(deny) failed: ${failureTexts.log}`);
481
536
  return {
482
537
  kind: 'failed',
483
538
  stage: 'decide',
484
539
  gatedCallId,
485
- reason: `decide(deny) failed: ${String(e)}${decideFailureNote(e, 'deny')}`,
486
- ...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
540
+ reason: `decide(deny) failed: ${failureTexts.reason}${decideFailureNote(e, 'deny')}`,
541
+ ...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
487
542
  ...(currentPending !== undefined ? { currentPending } : {}),
488
543
  ...(wireCode !== undefined ? { errorCode: wireCode } : {}),
489
544
  ...(wireRetriable ? { retriable: true } : {}),
@@ -548,6 +603,7 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
548
603
  'serverNowMs',
549
604
  'delegation',
550
605
  'outcome',
606
+ 'cause',
551
607
  ];
552
608
  const _mirrorPin = [true, true];
553
609
  void _mirrorPin;
@@ -574,7 +630,7 @@ export function toolPermissionRequestAskBits(card) {
574
630
  return {};
575
631
  const mandated = hasOwnStrictTrue(card, 'mandated');
576
632
  const mandate = readApprovalMandate(card);
577
- const origin = readOwnNonEmptyString(card, 'origin');
633
+ const origin = readAskOrigin(card);
578
634
  const ruleOffersAbsence = readOwnNonEmptyString(card, 'ruleOffersAbsence');
579
635
  const askClassPair = readAskClassPair(card);
580
636
  return {
@@ -656,7 +712,7 @@ export function readToolApprovalRespondAck(v) {
656
712
  }
657
713
  return out;
658
714
  }
659
- function readToolApprovalRespondParking(v) {
715
+ export function readToolApprovalRespondParking(v) {
660
716
  if (typeof v !== 'object' || v === null)
661
717
  return undefined;
662
718
  const read = (fn) => {
@@ -675,7 +731,16 @@ function readToolApprovalRespondParking(v) {
675
731
  const hint = read(() => readOwnDataValue(v, 'retryAfterMs'));
676
732
  const retryAfterMs = typeof hint === 'number' && Number.isFinite(hint) && hint >= 0 ? hint : undefined;
677
733
  const decidePath = read(() => readOwnDataNonEmptyString(v, 'decidePath'));
678
- return { kind: 'parking', view: { approvalId, ...(retryAfterMs !== undefined ? { retryAfterMs } : {}), ...(decidePath !== undefined ? { decidePath } : {}) } };
734
+ const message = read(() => readOwnDataNonEmptyString(v, 'message'));
735
+ return {
736
+ kind: 'parking',
737
+ view: {
738
+ approvalId,
739
+ ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
740
+ ...(decidePath !== undefined ? { decidePath } : {}),
741
+ ...(message !== undefined ? { message } : {}),
742
+ },
743
+ };
679
744
  }
680
745
  function isNonEmptyStringArray(v) {
681
746
  return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x !== '');
@@ -830,14 +895,28 @@ function readProbeReason(v) {
830
895
  try {
831
896
  if (typeof v !== 'object' || v === null || !Object.hasOwn(v, 'probeReason'))
832
897
  return undefined;
833
- const raw = v.probeReason;
834
- return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
898
+ return usableProbeReason(v.probeReason);
835
899
  }
836
900
  catch {
837
901
  return undefined;
838
902
  }
839
903
  }
840
- function readRememberRefused(v) {
904
+ function usableProbeReason(raw) {
905
+ return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
906
+ }
907
+ const PROBE_CAUSE_CODE_WORD = /^[a-z][a-z0-9_]{0,31}(?:\.[a-z][a-z0-9_]{0,31}){1,3}$/;
908
+ export function askProbeReasonDetail(card) {
909
+ const reason = usableProbeReason(ownDataValue(card, 'probeReason'));
910
+ if (reason !== undefined)
911
+ return displayUntrusted(reason);
912
+ const cause = ownDataValue(card, 'probeCause');
913
+ if (!isWireRecordCarrier(cause))
914
+ return undefined;
915
+ const code = ownDataValue(cause, 'code');
916
+ const head = 'An automatic check could not clear this call, so it is asking you; the engine did not say the specific reason';
917
+ return typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code) ? `${head} (check code: ${code}).` : `${head}.`;
918
+ }
919
+ export function readApprovalRememberRefused(v) {
841
920
  try {
842
921
  return typeof v === 'object' && v !== null && Object.hasOwn(v, 'rememberRefused') && v.rememberRefused === true;
843
922
  }
@@ -845,6 +924,167 @@ function readRememberRefused(v) {
845
924
  return false;
846
925
  }
847
926
  }
927
+ function ownDataValue(v, key) {
928
+ if (typeof v !== 'object' || v === null)
929
+ return undefined;
930
+ try {
931
+ const d = Object.getOwnPropertyDescriptor(v, key);
932
+ return d !== undefined && 'value' in d ? d.value : undefined;
933
+ }
934
+ catch {
935
+ return undefined;
936
+ }
937
+ }
938
+ function ownStringList(v) {
939
+ if (!Array.isArray(v))
940
+ return undefined;
941
+ try {
942
+ const length = v.length;
943
+ if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
944
+ return undefined;
945
+ const out = [];
946
+ for (let i = 0; i < length; i++) {
947
+ const d = Object.getOwnPropertyDescriptor(v, i);
948
+ if (d === undefined || !('value' in d) || typeof d.value !== 'string' || d.value === '')
949
+ return undefined;
950
+ out.push(d.value);
951
+ }
952
+ return v.length === length ? Object.freeze(out) : undefined;
953
+ }
954
+ catch {
955
+ return undefined;
956
+ }
957
+ }
958
+ export function readApprovalBoundGrants(v) {
959
+ const g = ownDataValue(v, 'boundGrants');
960
+ if (typeof g !== 'object' || g === null || Array.isArray(g))
961
+ return undefined;
962
+ const skill = ownDataValue(g, 'skill');
963
+ if (!(skill === null || (typeof skill === 'string' && skill !== '')))
964
+ return undefined;
965
+ const allowedTools = ownStringList(ownDataValue(g, 'allowedTools'));
966
+ const disallowedTools = ownStringList(ownDataValue(g, 'disallowedTools'));
967
+ if (allowedTools === undefined || disallowedTools === undefined)
968
+ return undefined;
969
+ return Object.freeze({ skill, allowedTools, disallowedTools });
970
+ }
971
+ const CAUSE_WORD = /^[a-z][a-z0-9_]{0,63}$/;
972
+ export function readApprovalClassifierNoRuling(v) {
973
+ const w = ownDataValue(v, 'classifierNoRuling');
974
+ return typeof w === 'string' && CAUSE_WORD.test(w) ? w : undefined;
975
+ }
976
+ const CLASSIFIER_NO_RULING_REASONS = Object.freeze({
977
+ error: 'could not run for this call (it failed)',
978
+ timeout: 'could not run for this call (it timed out)',
979
+ parse_error: 'answered for this call but gave no verdict it could use',
980
+ });
981
+ const _noRulingPin = [true, true];
982
+ void _noRulingPin;
983
+ export function classifierNoRulingDetail(word) {
984
+ if (typeof word === 'string' && Object.hasOwn(CLASSIFIER_NO_RULING_REASONS, word)) {
985
+ return `The auto-mode classifier ${CLASSIFIER_NO_RULING_REASONS[word]}, so it is asking you instead.`;
986
+ }
987
+ if (typeof word === 'string' && CAUSE_WORD.test(word))
988
+ return `The auto-mode classifier did not rule on this call (reason: ${word}), so it is asking you instead.`;
989
+ return 'The auto-mode classifier did not rule on this call, so it is asking you instead.';
990
+ }
991
+ const entries = (n) => (n === 1 ? '1 tool entry' : `${String(n)} tool entries`);
992
+ export function approvalBoundGrantsDetail(grants) {
993
+ const g = readApprovalBoundGrants({ boundGrants: grants });
994
+ if (g === undefined)
995
+ return undefined;
996
+ if (g.skill === null)
997
+ return 'This call names no loaded skill, so approving it grants nothing beyond this call.';
998
+ const a = g.allowedTools.length;
999
+ const d = g.disallowedTools.length;
1000
+ if (a === 0 && d === 0)
1001
+ return 'Approving this activates the skill shown on the card; it lists no tools to run without asking and none to refuse.';
1002
+ const allow = `${entries(a)} it lists ${a === 1 ? 'runs' : 'run'} without asking once it is active`;
1003
+ const deny = `${entries(d)} ${d === 1 ? 'is' : 'are'} refused for the rest of this run`;
1004
+ return `Approving this activates the skill shown on the card: ${allow}, and ${deny}.`;
1005
+ }
1006
+ const CALL_LEDGER_MAX_CALLS = 256;
1007
+ const CALL_LEDGER_MAX_ASKS_PER_CALL = 8;
1008
+ const callCardLedger = new Map();
1009
+ function askSignatureOf(req) {
1010
+ return JSON.stringify([req.classes !== undefined ? [...req.classes] : null, req.origin ?? null, req.mandate ?? null]);
1011
+ }
1012
+ function priorCardView(a) {
1013
+ return Object.freeze({
1014
+ ...(a.approvalId !== undefined ? { approvalId: a.approvalId } : {}),
1015
+ ...(a.origin !== undefined ? { origin: a.origin } : {}),
1016
+ ...(a.mandate !== undefined ? { mandate: a.mandate } : {}),
1017
+ ...(a.outcome !== undefined ? { outcome: a.outcome } : {}),
1018
+ });
1019
+ }
1020
+ function noteCardOnCall(callId, approvalId, req) {
1021
+ const sig = askSignatureOf(req);
1022
+ let asks = callCardLedger.get(callId);
1023
+ if (asks === undefined) {
1024
+ asks = [];
1025
+ callCardLedger.set(callId, asks);
1026
+ while (callCardLedger.size > CALL_LEDGER_MAX_CALLS) {
1027
+ const oldest = callCardLedger.keys().next().value;
1028
+ if (oldest === undefined)
1029
+ break;
1030
+ callCardLedger.delete(oldest);
1031
+ }
1032
+ }
1033
+ let at = asks.findIndex((a) => a.sig === sig);
1034
+ if (at < 0) {
1035
+ asks.push({ sig, ...(approvalId !== undefined ? { approvalId } : {}), ...(req.origin !== undefined ? { origin: req.origin } : {}), ...(req.mandate !== undefined ? { mandate: req.mandate } : {}) });
1036
+ while (asks.length > CALL_LEDGER_MAX_ASKS_PER_CALL)
1037
+ asks.shift();
1038
+ at = asks.length - 1;
1039
+ }
1040
+ else if (asks[at].approvalId === undefined && approvalId !== undefined) {
1041
+ asks[at].approvalId = approvalId;
1042
+ }
1043
+ const entry = asks[at];
1044
+ const prior = asks.slice(0, at);
1045
+ return { entry, ...(prior.length > 0 ? { prior: Object.freeze(prior.map(priorCardView)) } : {}) };
1046
+ }
1047
+ async function surfaceCallTrackedCard(callId, approvalId, slot, req) {
1048
+ if (callId === undefined || callId === '')
1049
+ return surfaceApprovalCard(req);
1050
+ const { entry, prior } = noteCardOnCall(callId, approvalId, req);
1051
+ slot.entry = entry;
1052
+ return surfaceApprovalCard(prior !== undefined ? { ...req, priorCardsForCall: prior } : req);
1053
+ }
1054
+ function noteCallOutcome(slot, outcome) {
1055
+ const entry = slot.entry;
1056
+ if (entry === undefined)
1057
+ return;
1058
+ const r = approvalResolutionOf(outcome);
1059
+ if (r.kind === 'decided')
1060
+ entry.outcome = r.decision === 'deny' ? 'denied' : 'allowed';
1061
+ }
1062
+ export function approvalPriorCardsDetail(card) {
1063
+ const prior = ownDataValue(card, 'priorCardsForCall');
1064
+ let isList = false;
1065
+ try {
1066
+ isList = Array.isArray(prior);
1067
+ }
1068
+ catch {
1069
+ return undefined;
1070
+ }
1071
+ if (!isList)
1072
+ return undefined;
1073
+ const length = ownDataValue(prior, 'length');
1074
+ if (typeof length !== 'number' || !Number.isSafeInteger(length) || length <= 0 || length > CALL_LEDGER_MAX_ASKS_PER_CALL)
1075
+ return undefined;
1076
+ const items = [];
1077
+ for (let i = 0; i < length; i++)
1078
+ items.push(ownDataValue(prior, String(i)));
1079
+ const outcome = ownDataValue(items[length - 1], 'outcome');
1080
+ const earlier = outcome === 'allowed' ? 'you already approved an earlier one' : outcome === 'denied' ? 'an earlier one was denied' : 'an earlier one was shown first';
1081
+ const fromRule = items.some((p) => ownDataValue(p, 'origin') === 'ask_rule');
1082
+ const thisFromRule = ownDataValue(card, 'origin') === 'ask_rule';
1083
+ if (fromRule && !thisFromRule) {
1084
+ 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.`;
1085
+ }
1086
+ return `This is a second approval for the same call: ${earlier}. This one is a separate check and needs its own answer.`;
1087
+ }
848
1088
  function isNonNegativeFinite(v) {
849
1089
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
850
1090
  }
@@ -1010,8 +1250,56 @@ function subagentBadgeFor(frame) {
1010
1250
  return { name, color: 'cyan' };
1011
1251
  }
1012
1252
  const MAX_RESPOND_NOTE_CHARS = 2048;
1013
- export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane) {
1253
+ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane, run) {
1014
1254
  const approvalId = frame.approvalId;
1255
+ const held = liveCardHoldOf(liveFrameCallKey(approvalId));
1256
+ if (held !== undefined)
1257
+ return joinLiveCard(held, signal);
1258
+ return holdLiveCard(liveCardIdentityOf(approvalId, frame, run), signal, async (hold) => {
1259
+ const slot = {};
1260
+ const out = await presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, slot);
1261
+ noteCallOutcome(slot, out);
1262
+ return out;
1263
+ });
1264
+ }
1265
+ function liveCardIdentityOf(approvalId, frame, run) {
1266
+ const toolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1267
+ const fromSubagent = isFromSubagent(frame);
1268
+ const ownRun = fromSubagent ? frame.sourceTaskId : run?.taskId;
1269
+ const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
1270
+ const sessionId = !fromSubagent && typeof run?.sessionId === 'string' && run.sessionId !== '' ? run.sessionId : undefined;
1271
+ return {
1272
+ approvalId,
1273
+ ...(toolCallId !== undefined ? { toolCallId } : {}),
1274
+ ...(taskId !== undefined ? { taskId } : {}),
1275
+ ...(sessionId !== undefined ? { sessionId } : {}),
1276
+ };
1277
+ }
1278
+ function readApprovalHolderIdentity(v) {
1279
+ if (typeof v !== 'object' || v === null)
1280
+ return {};
1281
+ const out = {};
1282
+ try {
1283
+ for (const k of ['approvalId', 'taskId', 'toolCallId', 'sessionId']) {
1284
+ const d = Object.getOwnPropertyDescriptor(v, k);
1285
+ const x = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
1286
+ if (typeof x === 'string' && x !== '')
1287
+ out[k] = x;
1288
+ }
1289
+ }
1290
+ catch {
1291
+ return {};
1292
+ }
1293
+ return out;
1294
+ }
1295
+ export async function joinApprovalHolder(identity, signal) {
1296
+ const hold = liveCardHoldFor(readApprovalHolderIdentity(identity));
1297
+ if (hold === undefined)
1298
+ return { kind: 'free' };
1299
+ const joined = await awaitLiveCardHolder(hold, signal);
1300
+ return joined !== undefined ? { kind: 'joined', outcome: joined } : { kind: 'free' };
1301
+ }
1302
+ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot) {
1015
1303
  const frameToolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1016
1304
  const decidedByReading = decidedByReadingSnapshotOf(lane);
1017
1305
  const toolName = typeof frame.toolName === 'string' ? frame.toolName : 'Write';
@@ -1041,14 +1329,14 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
1041
1329
  const ruleOffers = readRuleOffers(frame.ruleOffers);
1042
1330
  const denialLimitFallback = readDenialLimitFallback(frame.denialLimitFallback);
1043
1331
  const frameShadow = readAllowRuleShadowFromWire(frame, 'frame');
1044
- const card = await surfaceApprovalCard({
1332
+ const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, {
1045
1333
  toolName,
1046
1334
  args: args,
1047
1335
  callKey: liveFrameCallKey(approvalId),
1048
1336
  ...(frameToolCallId !== undefined
1049
1337
  ? { toolCallId: frameToolCallId }
1050
1338
  : {}),
1051
- ...(signal ? { signal } : {}),
1339
+ signal: hold.cardSignal,
1052
1340
  ...(isFromSubagent(frame) ? { workerBadge: subagentBadgeFor(frame) } : {}),
1053
1341
  ...(wireNote !== undefined ? { wireNote } : {}),
1054
1342
  ...(argsNotRealInput ? { argsUnavailable: true } : {}),
@@ -1068,10 +1356,9 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
1068
1356
  : {}),
1069
1357
  ...(ruleOffers !== undefined ? { ruleOffers } : {}),
1070
1358
  ...((sh) => (sh !== undefined ? { allowRuleShadow: sh } : {}))(frameShadow),
1071
- ...((t) => (t !== undefined ? { persistedRuleShadowed: t } : {}))(legacyShadowedRuleTextOf(frameShadow)),
1072
1359
  ...(isWireRecordCarrier(frame.probeCause) ? { probeCause: frame.probeCause } : {}),
1073
1360
  ...((r) => (r !== undefined ? { probeReason: r } : {}))(readProbeReason(frame)),
1074
- ...(readRememberRefused(frame) ? { rememberRefused: true } : {}),
1361
+ ...(readApprovalRememberRefused(frame) ? { rememberRefused: true } : {}),
1075
1362
  ...(isWireRecordCarrier(frame.ruleEvidence) ? { ruleEvidence: frame.ruleEvidence } : {}),
1076
1363
  ...(frame.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
1077
1364
  ...(hasOwnStrictTrue(frame, 'mandated') ? { mandated: true } : {}),
@@ -1082,9 +1369,18 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
1082
1369
  ? { ruleOffersAbsence: frame.ruleOffersAbsence }
1083
1370
  : {}),
1084
1371
  ...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
1085
- ...((o) => (o !== undefined ? { origin: o } : {}))(readOwnNonEmptyString(frame, 'origin')),
1372
+ ...((o) => (o !== undefined ? { origin: o } : {}))(readAskOrigin(frame)),
1086
1373
  ...(readAskClassPair(frame) ?? {}),
1374
+ ...((g) => (g !== undefined ? { boundGrants: g } : {}))(readApprovalBoundGrants(frame)),
1375
+ ...((w) => (w !== undefined ? { classifierNoRuling: w } : {}))(readApprovalClassifierNoRuling(frame)),
1087
1376
  });
1377
+ if (hold.superseded !== undefined) {
1378
+ hostLog('debug', `liveToolApprovalWire: ${approvalId} was settled by the engine while its card was up${hold.superseded.parked ? ' (parked)' : ''} — card withdrawn; ` +
1379
+ `the card came back ${card.kind}; nothing sent`);
1380
+ if (hold.superseded.parked && (card.kind === 'allow' || card.kind === 'deny'))
1381
+ surfaceApprovalParking();
1382
+ return { decision: 'unresolved', retracted: true };
1383
+ }
1088
1384
  if (argsNotRealInput && card.kind === 'allow' && card.updatedInput !== undefined) {
1089
1385
  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)`);
1090
1386
  surfaceEditRefusedOnBlindAsk();
@@ -1094,6 +1390,22 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
1094
1390
  hostLog('debug', `liveToolApprovalWire: ${approvalId} card retracted without a decision${card.reason ? ` (${card.reason})` : ''} — nothing sent`);
1095
1391
  return { decision: 'unresolved', retracted: true };
1096
1392
  }
1393
+ if (card.kind === 'aborted') {
1394
+ hold.phase = 'respond';
1395
+ const interruptOpts = lane?.approvalDecisionNoteCapable === true ? { note: INTERRUPT_DENY_REASON } : {};
1396
+ let interruptDeny;
1397
+ try {
1398
+ interruptDeny = Promise.resolve(respond(approvalId, 'deny', interruptOpts));
1399
+ }
1400
+ catch (e) {
1401
+ interruptDeny = Promise.reject(e);
1402
+ }
1403
+ hold.trailing = interruptDeny.then(() => undefined, () => undefined);
1404
+ observeCancelByDeny(interruptDeny, approvalId);
1405
+ hostLog('debug', `liveToolApprovalWire: ${approvalId} card aborted — one tool-level deny issued to settle the pending ask`);
1406
+ return { decision: 'unresolved', aborted: true };
1407
+ }
1408
+ hold.phase = 'respond';
1097
1409
  const decision = card.kind === 'allow' ? (card.allowSession ? 'allow_session' : 'allow') : 'deny';
1098
1410
  const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
1099
1411
  const attributionKeys = decision === 'deny' ? decisionAttributionKeyOf(decidedByReading, denySettledByForWire) : {};