@sema-agent/client-core 0.109.0 → 0.111.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 (236) hide show
  1. package/CHANGELOG.md +342 -0
  2. package/README.md +88 -32
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +45 -11
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +20 -15
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/attachmentRoute.d.ts +20 -0
  16. package/dist/attachmentRoute.js +39 -0
  17. package/dist/attachmentsWireCaps.js +1 -1
  18. package/dist/builtinOutputStyles.d.ts +12 -0
  19. package/dist/builtinOutputStyles.js +147 -0
  20. package/dist/compactionStatus.d.ts +9 -0
  21. package/dist/compactionStatus.js +29 -5
  22. package/dist/controlRouter.js +2 -1
  23. package/dist/coreValuePorts.d.ts +39 -1
  24. package/dist/coreValuePorts.js +10 -0
  25. package/dist/declared/supportFloorDeclared.js +1 -1
  26. package/dist/displayUntrusted.d.ts +1 -0
  27. package/dist/displayUntrusted.js +138 -14
  28. package/dist/engineErrorCodes.d.ts +3 -0
  29. package/dist/engineErrorCodes.js +3 -0
  30. package/dist/engineHealthFacts.d.ts +20 -0
  31. package/dist/engineHealthFacts.js +42 -1
  32. package/dist/engineHealthProbeVerdict.d.ts +23 -0
  33. package/dist/engineHealthProbeVerdict.js +115 -0
  34. package/dist/engineInlineTaskStats.d.ts +4 -3
  35. package/dist/engineInlineTaskStats.js +18 -2
  36. package/dist/engineNoticeCodes.d.ts +10 -0
  37. package/dist/engineNoticeCodes.js +36 -6
  38. package/dist/engineRepoint.d.ts +147 -0
  39. package/dist/engineRepoint.js +310 -0
  40. package/dist/engineRowRef.d.ts +3 -0
  41. package/dist/engineRowRef.js +13 -1
  42. package/dist/engineSessionMessages.d.ts +2 -1
  43. package/dist/engineSessionMessages.js +5 -0
  44. package/dist/engineSwapCrossing.d.ts +57 -0
  45. package/dist/engineSwapCrossing.js +297 -0
  46. package/dist/engineUnavailable.d.ts +44 -0
  47. package/dist/engineUnavailable.js +492 -0
  48. package/dist/errorSupplementRoles.js +2 -1
  49. package/dist/eventLeg.js +3 -3
  50. package/dist/fleet/fleetProjection.js +3 -2
  51. package/dist/generated/engineFactTables.d.ts +2 -0
  52. package/dist/generated/engineFactTables.js +12 -0
  53. package/dist/generated/sdkFactTables.d.ts +2 -1
  54. package/dist/generated/sdkFactTables.js +5 -0
  55. package/dist/generated/settingsSchemaTables.d.ts +4 -0
  56. package/dist/generated/settingsSchemaTables.js +1 -0
  57. package/dist/hitl/approvalAskers.d.ts +6 -0
  58. package/dist/hitl/approvalAskers.js +30 -0
  59. package/dist/hitl/approvalCardReasons.js +6 -3
  60. package/dist/hitl/approvalDelivery.d.ts +1 -0
  61. package/dist/hitl/approvalDelivery.js +1 -1
  62. package/dist/hitl/approvalRequestFrameReading.d.ts +7 -0
  63. package/dist/hitl/approvalRequestFrameReading.js +11 -0
  64. package/dist/hitl/approvalResolution.d.ts +9 -2
  65. package/dist/hitl/approvalResolution.js +6 -1
  66. package/dist/hitl/askGateWire.js +5 -8
  67. package/dist/hitl/automaticAnswerVerdict.js +2 -2
  68. package/dist/hitl/decisionConflict.js +6 -3
  69. package/dist/hitl/decisionRefusal.d.ts +2 -0
  70. package/dist/hitl/decisionRefusal.js +8 -8
  71. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  72. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  73. package/dist/hitl/decisionSend.d.ts +10 -0
  74. package/dist/hitl/decisionSend.js +57 -17
  75. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  76. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  77. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  78. package/dist/hitl/elicitationRefusal.js +23 -0
  79. package/dist/hitl/frameRouter.d.ts +2 -2
  80. package/dist/hitl/frameRouter.js +12 -33
  81. package/dist/hitl/gateIdentity.d.ts +5 -0
  82. package/dist/hitl/gateIdentity.js +23 -0
  83. package/dist/hitl/gateLedger.d.ts +0 -4
  84. package/dist/hitl/gateLedger.js +0 -19
  85. package/dist/hitl/hitlBridge.d.ts +7 -0
  86. package/dist/hitl/hitlBridge.js +14 -8
  87. package/dist/hitl/hitlHostSurface.d.ts +3 -1
  88. package/dist/hitl/hitlHostSurface.js +14 -4
  89. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  90. package/dist/hitl/hostWithdrawal.js +38 -0
  91. package/dist/hitl/localAllowRedemption.d.ts +67 -0
  92. package/dist/hitl/localAllowRedemption.js +256 -0
  93. package/dist/hitl/parkResolver.js +32 -13
  94. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  95. package/dist/hitl/persistedRulesWire.js +72 -2
  96. package/dist/hitl/planReviewWire.d.ts +3 -0
  97. package/dist/hitl/planReviewWire.js +37 -10
  98. package/dist/hitl/policyDenyCause.d.ts +1 -1
  99. package/dist/hitl/policyDenyCause.js +1 -1
  100. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  101. package/dist/hitl/resumeRunningCard.js +1 -0
  102. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  103. package/dist/hitl/screenCardCarriers.js +41 -0
  104. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  105. package/dist/hitl/screenDecisionCards.js +362 -0
  106. package/dist/hitl/sessionPolicyWire.js +6 -4
  107. package/dist/hitl/standingRows.d.ts +4 -1
  108. package/dist/hitl/standingRows.js +33 -1
  109. package/dist/hitl/toolApprovalWire.d.ts +64 -15
  110. package/dist/hitl/toolApprovalWire.js +470 -125
  111. package/dist/hooksWireCaps.js +31 -1
  112. package/dist/hostSettingNoticeArm.d.ts +3 -0
  113. package/dist/hostSettingNoticeArm.js +12 -0
  114. package/dist/hostTurnSettings.d.ts +13 -4
  115. package/dist/hostTurnSettings.js +150 -10
  116. package/dist/humanTurnRead.js +11 -1
  117. package/dist/i18n/messages.en.d.ts +212 -0
  118. package/dist/i18n/messages.en.js +217 -5
  119. package/dist/i18n/messages.zh.js +235 -5
  120. package/dist/i18n/translate.d.ts +3 -0
  121. package/dist/i18n/translate.js +78 -10
  122. package/dist/i18n/zh.d.ts +1 -0
  123. package/dist/i18n/zh.js +1 -0
  124. package/dist/index.d.ts +56 -16
  125. package/dist/index.js +40 -12
  126. package/dist/inheritEnvCapability.js +2 -0
  127. package/dist/inputAuthor.d.ts +16 -0
  128. package/dist/inputAuthor.js +44 -0
  129. package/dist/interactiveHalt.js +2 -1
  130. package/dist/interactiveInterrupt.js +20 -2
  131. package/dist/interruptRefusalNote.d.ts +2 -0
  132. package/dist/interruptRefusalNote.js +17 -0
  133. package/dist/liveModelCatalog.d.ts +2 -0
  134. package/dist/liveModelCatalog.js +23 -9
  135. package/dist/liveQuestionStore.d.ts +4 -1
  136. package/dist/liveQuestionStore.js +12 -3
  137. package/dist/localEngineSeatReading.d.ts +2 -2
  138. package/dist/localEngineSeatReading.js +5 -3
  139. package/dist/localEngineSpawn.d.ts +0 -7
  140. package/dist/localEngineSpawn.js +0 -92
  141. package/dist/localEngineStep.d.ts +2 -2
  142. package/dist/localEngineStep.js +2 -0
  143. package/dist/mcpEngineLeg.d.ts +3 -0
  144. package/dist/mcpEngineLeg.js +38 -1
  145. package/dist/mcpPanel.js +2 -0
  146. package/dist/mcpReconnect.d.ts +9 -1
  147. package/dist/mcpReconnect.js +124 -19
  148. package/dist/mcpWireCaps.js +1 -1
  149. package/dist/model/engineModelWords.d.ts +3 -0
  150. package/dist/model/engineModelWords.js +13 -0
  151. package/dist/model/subagentModelWord.d.ts +21 -2
  152. package/dist/model/subagentModelWord.js +100 -29
  153. package/dist/modelCapabilityProbe.js +7 -43
  154. package/dist/modsFace.d.ts +8 -1
  155. package/dist/modsFace.js +27 -0
  156. package/dist/notifications.d.ts +36 -6
  157. package/dist/notifications.js +170 -20
  158. package/dist/ownKey.d.ts +6 -5
  159. package/dist/ownKey.js +31 -7
  160. package/dist/peerFrames.d.ts +40 -2
  161. package/dist/peerFrames.js +158 -3
  162. package/dist/pendingUserRow.d.ts +4 -0
  163. package/dist/pendingUserRow.js +16 -0
  164. package/dist/permissionsDeclaration.d.ts +1 -0
  165. package/dist/permissionsDeclaration.js +11 -1
  166. package/dist/planReviewCardFacts.d.ts +3 -0
  167. package/dist/planReviewCardFacts.js +52 -2
  168. package/dist/pluginHooksWire.d.ts +10 -1
  169. package/dist/pluginHooksWire.js +96 -7
  170. package/dist/printToolResultFrame.js +4 -2
  171. package/dist/promptBlockedCopy.js +11 -6
  172. package/dist/promptSuggestionBatch.d.ts +7 -0
  173. package/dist/promptSuggestionBatch.js +9 -0
  174. package/dist/promptSuggestionsTail.d.ts +39 -0
  175. package/dist/promptSuggestionsTail.js +116 -0
  176. package/dist/replayBody.js +10 -2
  177. package/dist/request/taskRequest.d.ts +5 -0
  178. package/dist/request/taskRequest.js +142 -21
  179. package/dist/requestCoordinate.d.ts +1 -0
  180. package/dist/requestCoordinate.js +13 -0
  181. package/dist/resumeRefusalCopy.d.ts +1 -3
  182. package/dist/resumeRefusalCopy.js +2 -1
  183. package/dist/retryStatus.d.ts +2 -2
  184. package/dist/retryStatus.js +2 -0
  185. package/dist/rewindArchiveReading.js +4 -4
  186. package/dist/runCancelContext.d.ts +2 -1
  187. package/dist/runCancelContext.js +1 -0
  188. package/dist/runPermissionModeSwitch.js +2 -2
  189. package/dist/scenarioRefusal.js +11 -13
  190. package/dist/scenarioSkills.js +11 -6
  191. package/dist/seam.d.ts +27 -2
  192. package/dist/seam.js +17 -1
  193. package/dist/seatContract.d.ts +189 -12
  194. package/dist/seatContract.js +401 -9
  195. package/dist/sessionBackground.js +2 -1
  196. package/dist/sessionBackgroundHandoff.js +2 -1
  197. package/dist/sessionCostReading.d.ts +70 -0
  198. package/dist/sessionCostReading.js +150 -0
  199. package/dist/sessionEngine.d.ts +2 -0
  200. package/dist/sessionEngine.js +111 -0
  201. package/dist/sessionHooksRoster.d.ts +4 -0
  202. package/dist/sessionHooksRoster.js +158 -0
  203. package/dist/sessionMemoryStatus.d.ts +2 -1
  204. package/dist/sessionMemoryStatus.js +2 -2
  205. package/dist/sessionReadRedaction.d.ts +7 -0
  206. package/dist/sessionReadRedaction.js +26 -0
  207. package/dist/sessionSyncFileHistory.d.ts +4 -0
  208. package/dist/sessionSyncFileHistory.js +27 -0
  209. package/dist/settingsHooksSalvage.js +2 -1
  210. package/dist/sseIdleTriage.d.ts +13 -1
  211. package/dist/sseIdleTriage.js +25 -6
  212. package/dist/storeDurabilityWord.d.ts +15 -0
  213. package/dist/storeDurabilityWord.js +18 -0
  214. package/dist/storePostureCapability.d.ts +7 -3
  215. package/dist/storePostureCapability.js +69 -39
  216. package/dist/subagent/engineSubagentResume.js +2 -2
  217. package/dist/subagent/engineSubagentTail.js +8 -1
  218. package/dist/subagentContentStore.d.ts +11 -0
  219. package/dist/subagentContentStore.js +28 -2
  220. package/dist/terminalStatuses.d.ts +1 -0
  221. package/dist/terminalStatuses.js +4 -1
  222. package/dist/toolResult.d.ts +1 -0
  223. package/dist/toolResult.js +2 -0
  224. package/dist/turnFailurePresentation.js +9 -2
  225. package/dist/typePins.d.ts +11 -0
  226. package/dist/usageAnalytics.d.ts +17 -0
  227. package/dist/usageAnalytics.js +44 -2
  228. package/dist/userAttachmentEnvelope.d.ts +2 -1
  229. package/dist/userAttachmentEnvelope.js +10 -0
  230. package/dist/wireErrorTriage.js +2 -1
  231. package/dist/wireFailureShape.d.ts +6 -5
  232. package/dist/wireFailureShape.js +150 -38
  233. package/dist/wireRefusalCopy.d.ts +16 -5
  234. package/dist/wireRefusalCopy.js +119 -86
  235. package/docs/INTEGRATION-CLIENTS.md +4228 -21
  236. package/package.json +7 -3
@@ -1,13 +1,13 @@
1
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
- import { seatSessionRowOf } from './standingRows.js';
4
+ import { seatAllowNoteCarriedOf, seatSessionRowOf } from './standingRows.js';
5
5
  import { hostLog } from '../host.js';
6
6
  import { trimEditedRuleText } from './editedRuleTextPrecheck.js';
7
- import { appendOwnElement, guardedRead, readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
7
+ import { appendOwnElement, guardedRead, ownSendableField, ownSnapshot, readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
8
8
  import { ownReadingSlot } from '../engineWireFor.js';
9
9
  import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPair } from './askClassPair.js';
10
- import { approvalAgentIdOf, approvalAskReasonsOf } from './approvalAskers.js';
10
+ import { approvalAgentIdOf, approvalAskReasonsOf, approvalRequestedByOf } from './approvalAskers.js';
11
11
  import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
12
12
  import { approvalWindowKeysOf, readApprovalCardWord, readRequiresRealApproval } from './approvalCardReading.js';
13
13
  import { readAllowRuleShadowFromWire, retiredAllowRuleShadowKeyPresent, RETIRED_SHADOW_EVIDENCE_LABEL } from './allowRuleShadow.js';
@@ -17,26 +17,29 @@ import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
17
17
  import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_PERSISTED_RULE_MATCHES } from '../generated/engineFactTables.js';
18
18
  import { msg, renderMessage } from '../i18n/translate.js';
19
19
  import { sessionAllowCarried, sessionAllowEligible } from './sessionAllowRoute.js';
20
- import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
20
+ import { INTERRUPT_DENY_REASON, interruptDenyLifetime, observeCancelByDeny, surfaceApprovalParking, surfaceAllowNoteNotCarried, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
21
21
  import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
22
22
  import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
23
23
  import { frameBindingOf } from '../frameBinding.js';
24
24
  import { carriesAttributionKey, deliverWithAttributionLadder } from './attributionLadder.js';
25
25
  import { deliverApprovalDecision, newSendTally } from './approvalDelivery.js';
26
26
  import { approvalDecisionHolds, approvalHoldFor, awaitApprovalHolder, claimApprovalCardFor, enterLiveRespondPhase, holdLiveCard, joinLiveCard, liveCardHoldOf, clearSettledApprovals, noteHolderSettledApproval, settledApprovalOf, settledForDurableLane, settledLiveCardOutcome } from './approvalHolds.js';
27
- import { directFailureVerdictOf, directSafetyStopOf, joinedResolution, resolutionOfDecisionFailure } from './approvalResolution.js';
27
+ import { directFailureVerdictOf, directSafetyStopOf, joinedResolution, resolutionOfDecisionFailure, toolPermissionSettledElsewhereOf } from './approvalResolution.js';
28
+ import { hostWithdrawalOf } from './hostWithdrawal.js';
28
29
  import { parkRowDeadlineState } from './parkExpiryJudgements.js';
29
30
  import { isApprovalMandateWord } from '../gateVocabulary.js';
30
31
  import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
31
32
  import { decideFailureNote } from '../decideFailureNote.js';
33
+ import { editedApprovalAcceptanceOf } from './editedApprovalAcceptance.js';
32
34
  import { readPolicyDenyCause } from './policyDenyCause.js';
33
- import { inValueOf, slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
35
+ import { inValueOf, slotPresenceOf, slotReadOf, wireFailureTextsOf } from '../wireFailureShape.js';
34
36
  import { APPROVAL_LIVE_FACE_KEYS, approvalLiveFaceOf, hasOwnStrictTrue } from './approvalLiveFace.js';
35
- import { approvalFrameUnreadableWhyOf, approvalRequestFrameReadingThrowing, } from './approvalRequestFrameReading.js';
37
+ import { approvalFrameUnreadableWhyOf, approvalRequestFrameReadingThrowing, cardPayloadReadingOf, } from './approvalRequestFrameReading.js';
36
38
  import { subagentSourceSessionOf } from './subagentSource.js';
37
39
  import { isPlainRecord } from '../jsonFaithful.js';
38
40
  import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
39
41
  import { readDecideReceipt } from '../decideReceipt.js';
42
+ import { sourceGenerationBit, sourceGenerationNow } from './screenCardCarriers.js';
40
43
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, } from '@sema-agent/sdk';
41
44
  export function toolNameIsFsWrite(name) {
42
45
  const n = name.replace(/[\s_-]+/g, '').toLowerCase();
@@ -98,10 +101,19 @@ export function readApprovalDenySettledBy(decision) {
98
101
  : undefined;
99
102
  }
100
103
  export const RETRACTED_CARD_DECISION_KIND = 'retracted';
101
- function parkRowOf(taskId, row, gatedCallId) {
104
+ export const WITHDRAWN_CARD_DECISION_KIND = 'withdrawn';
105
+ function withdrawnCardResolution(card, at, logTag) {
106
+ const w = hostWithdrawalOf(card);
107
+ if (w === undefined) {
108
+ hostLog('debug', `${logTag}: card-withdrawn-unreadable as=retracted`);
109
+ return { kind: 'not_sent', ...at, cause: 'retracted' };
110
+ }
111
+ hostLog('debug', `${logTag}: card-withdrawn-by-host verdict=${w.rowVerdict.kind}`);
112
+ return { kind: 'not_sent', ...at, cause: 'withdrawn', toldBy: w.toldBy, rowVerdict: w.rowVerdict };
113
+ }
114
+ function parkRowOf(row, gatedCallId) {
102
115
  const d = parkRowDeadlineState(row, Date.now());
103
116
  return Object.freeze({
104
- taskId,
105
117
  ...(gatedCallId !== undefined && gatedCallId !== '' ? { gatedCallId } : {}),
106
118
  ...(d.kind !== 'none' ? { deadlineMs: d.deadlineMs } : {}),
107
119
  });
@@ -174,6 +186,16 @@ export function rememberRefusalOf(error) {
174
186
  function rememberRefusalIn(reading) {
175
187
  return reading.state === 'code' ? REMEMBER_REFUSAL_CODES.find((c) => c === reading.code) : undefined;
176
188
  }
189
+ const FEEDBACK_REFUSAL_CODES = Object.freeze(['feedback_not_in_proof']);
190
+ function feedbackRefusalOf(error) {
191
+ const reading = readWireErrorCodeReading(error);
192
+ return reading.state === 'code' && FEEDBACK_REFUSAL_CODES.some((c) => c === reading.code);
193
+ }
194
+ function approveBodyWithoutFeedback(body) {
195
+ const { feedback: _feedback, ...rest } = body;
196
+ void _feedback;
197
+ return rest;
198
+ }
177
199
  function logAfterDecided(msg) {
178
200
  try {
179
201
  hostLog('debug', msg);
@@ -203,12 +225,10 @@ const PARK_ROW_ARGS_TRUNCATED_NOTE = "this request's tool arguments were too lar
203
225
  'Edits are not accepted on this card: there is no original input to edit';
204
226
  const ARGS_REWRITTEN_NOTE = "this request's tool arguments were changed after the model made the call (for example by a hook) — " +
205
227
  'the arguments shown here are the changed ones recorded for this approval, and they differ from the call shown in the conversation';
206
- const ARGS_FROM_MODEL_CALL_NOTE = 'this approval request did not carry usable tool arguments (for example because they were too large to include), ' +
207
- "so the arguments shown are the model's original call from the conversation — " +
208
- 'if they were changed after that call (for example by a hook), what runs differs from what is shown';
209
- const PARK_EDIT_REFUSED_REASON = "the card returned an edited approval, but this request's tool arguments are not available on this surface " +
210
- '(missing, or replaced by a size marker on the pending approval record), so the edit cannot be applied — nothing was sent; ' +
211
- 'approve or deny it as it is';
228
+ function argsFromModelCallBitsOf() {
229
+ return { argsUnavailable: true, wireNote: renderMessage(msg('approval.park.argsFromModelCall')) };
230
+ }
231
+ const parkEditRefusedReason = () => renderMessage(msg('approval.editRefusal.parkReason', { stem: msg('approval.editRefusal.stem') }));
212
232
  function isParkRowInputSizeMarker(v) {
213
233
  if (typeof v !== 'object' || v === null || Array.isArray(v))
214
234
  return false;
@@ -249,6 +269,14 @@ function cardInputOf(recorded, stream) {
249
269
  return { from: 'stream', args: stream, shape };
250
270
  return { from: 'none', shape, recorded };
251
271
  }
272
+ function inputSourceBitOf(reading) {
273
+ return reading.from === 'recorded' ? { inputSource: 'record' } : reading.from === 'stream' ? { inputSource: 'stream' } : {};
274
+ }
275
+ function approvalInputSourceOf(card) {
276
+ const raw = ownDataValue(card, 'inputSource');
277
+ const known = { record: true, stream: true };
278
+ return typeof raw === 'string' && Object.hasOwn(known, raw) ? raw : undefined;
279
+ }
252
280
  const CARD_ARGS_COMPARE_MAX_NODES = 20_000;
253
281
  const CARD_ARGS_COMPARE_MAX_DEPTH = 256;
254
282
  const NOT_COMPARABLE = Symbol('not comparable');
@@ -442,6 +470,7 @@ async function joinDurableDecision(hold, deps, signal) {
442
470
  }
443
471
  export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signal, parkGatedCallId, parkToolName) {
444
472
  const decidedByReading = decidedByReadingSnapshotOf(deps);
473
+ const sourceGeneration = sourceGenerationNow(deps);
445
474
  const readMark = openDurableRead();
446
475
  let found;
447
476
  let held;
@@ -471,7 +500,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
471
500
  const fetched = found;
472
501
  return holdDurableDecision(durableHoldKey(fetched.pending, callKey), rowIdentity, signal, async (hold) => {
473
502
  const slot = {};
474
- const out = await presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, fetched, decidedByReading, callKey, hold, slot);
503
+ const out = await presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, fetched, decidedByReading, callKey, hold, slot, sourceGeneration);
475
504
  noteCallOutcome(slot, out);
476
505
  return out;
477
506
  });
@@ -485,7 +514,7 @@ function parkRowHolderIdentity(row, gatedCallId) {
485
514
  ...(askSig !== undefined ? { askSig } : {}),
486
515
  };
487
516
  }
488
- async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, found, decidedByReading, callKey, hold, ledgerSlot) {
517
+ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, found, decidedByReading, callKey, hold, ledgerSlot, sourceGeneration) {
489
518
  const { pending, gatedCallId } = found;
490
519
  const at = { lane: 'decide', ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
491
520
  const toolName = typeof pending.toolName === 'string' ? pending.toolName : '';
@@ -518,7 +547,8 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
518
547
  }
519
548
  : {}),
520
549
  ...(argsRewritten ? { argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : {}),
521
- ...(argsFromModelCall ? { argsUnavailable: true, wireNote: renderMessage(msg('approval.park.argsFromModelCall')) } : {}),
550
+ ...(argsFromModelCall ? argsFromModelCallBitsOf() : {}),
551
+ ...inputSourceBitOf(reading),
522
552
  ...(pending.governanceForced === true ? { governanceForced: true } : {}),
523
553
  ...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
524
554
  ...approvalLiveFaceOf(pending).facts,
@@ -540,14 +570,17 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
540
570
  : {}),
541
571
  allowSessionCarried: true,
542
572
  grantBinding: readApprovalGrantBinding(pending),
543
- parkRow: parkRowOf(taskId, pending, gatedCallId),
573
+ parkRow: parkRowOf(pending, gatedCallId),
574
+ ...(typeof taskId === 'string' && taskId !== '' ? { taskId } : {}),
575
+ ...sourceGenerationBit(sourceGeneration),
544
576
  });
577
+ const editAcceptance = editedApprovalAcceptanceOf(cardRequest);
545
578
  const card = await surfaceCallTrackedCard(gatedCallId, undefined, ledgerSlot, cardRequest);
546
579
  hold.phase = 'respond';
547
- if ((rowArgsGap !== undefined || argsFromModelCall) && card.kind === 'allow' && card.updatedInput !== undefined) {
580
+ if (editAcceptance.state !== 'accepts' && card.kind === 'allow' && card.updatedInput !== undefined) {
548
581
  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' : rowArgsGap === 'absent' ? 'missing from both the stream and the pending row' : 'not recorded on the pending row'} — nothing sent (the park stays pending)`);
549
582
  surfaceEditRefusedOnBlindAsk();
550
- return { kind: 'not_sent', ...at, cause: 'edit_refused', detail: PARK_EDIT_REFUSED_REASON };
583
+ return { kind: 'not_sent', ...at, cause: 'edit_refused', detail: parkEditRefusedReason() };
551
584
  }
552
585
  const decideSpan = bridge.openDecideOperation();
553
586
  const decideOpts = { ...(signal ? { signal } : {}), operation: decideSpan };
@@ -558,9 +591,11 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
558
591
  case 'retracted':
559
592
  hostLog('debug', `liveHitlAskWire: approval card retracted without a decision for ${gatedCallId ?? '(no call)'} — nothing sent`);
560
593
  return { kind: 'not_sent', ...at, cause: 'retracted' };
594
+ case WITHDRAWN_CARD_DECISION_KIND:
595
+ return withdrawnCardResolution(card, at, 'liveHitlAskWire');
561
596
  case 'aborted':
562
597
  {
563
- const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending);
598
+ const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, { signal: interruptDenyLifetime() }, pending);
564
599
  hold.trailing = cancelDeny;
565
600
  observeCancelByDeny(cancelDeny, taskId);
566
601
  }
@@ -578,11 +613,30 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
578
613
  const { remember: _remember, ...plainApproveBody } = approveBody;
579
614
  void _remember;
580
615
  emitAnswerCommitted(callKey, 'decide', answered, approveBody);
616
+ let feedbackDropped = false;
617
+ const sendApprove = async (body) => {
618
+ const wire = feedbackDropped ? approveBodyWithoutFeedback(body) : body;
619
+ try {
620
+ return await bridge.decideTool(wire, gatedCallId, decideOpts, pending);
621
+ }
622
+ catch (e) {
623
+ if (wire.feedback === undefined || !feedbackRefusalOf(e))
624
+ throw e;
625
+ const verdict = directFailureVerdictOf(e, directSafetyStopOf(e));
626
+ if (verdict.yields) {
627
+ rememberStop = ownReadingSlot(verdict, 'directStop');
628
+ throw e;
629
+ }
630
+ feedbackDropped = true;
631
+ logAfterDecided(`liveToolApprovalWire: decide(approve+feedback) refused the feedback part (${String(e)}) — resending without the note`);
632
+ return await bridge.decideTool(approveBodyWithoutFeedback(wire), gatedCallId, decideOpts, pending);
633
+ }
634
+ };
581
635
  if (approveBody.remember === 'session') {
582
636
  let sessionReceipt;
583
637
  let sessionDecided = false;
584
638
  try {
585
- const raw = await bridge.decideTool(approveBody, gatedCallId, decideOpts, pending);
639
+ const raw = await sendApprove(approveBody);
586
640
  sessionReceipt = readDecideReceipt(raw);
587
641
  sessionDecided = true;
588
642
  }
@@ -600,13 +654,17 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
600
654
  if (sessionDecided) {
601
655
  if (sessionReceipt?.rememberApplied === false)
602
656
  surfaceRememberNotApplied();
657
+ if (feedbackDropped)
658
+ surfaceAllowNoteNotCarried(allowNoteNotCarriedSentence());
603
659
  return { kind: 'decided', ...at, decision: 'allow', ...(sessionReceipt !== undefined ? { receipt: sessionReceipt } : {}) };
604
660
  }
605
661
  }
606
- const raw = await bridge.decideTool(plainApproveBody, gatedCallId, decideOpts, pending);
662
+ const raw = await sendApprove(plainApproveBody);
607
663
  const receipt = readDecideReceipt(raw);
608
664
  if (rememberRefused || sessionDowngraded)
609
665
  surfaceRememberNotApplied();
666
+ if (feedbackDropped)
667
+ surfaceAllowNoteNotCarried(allowNoteNotCarriedSentence());
610
668
  return { kind: 'decided', ...at, decision: 'allow', ...(receipt !== undefined ? { receipt } : {}) };
611
669
  }
612
670
  catch (e) {
@@ -770,6 +828,12 @@ export function toolPermissionRequestAskBits(card, requestId) {
770
828
  const origin = readAskOrigin(card);
771
829
  const ruleOffersAbsence = readOwnNonEmptyString(card, 'ruleOffersAbsence');
772
830
  const askClassPair = readAskClassPair(card);
831
+ const probeLayer = probeCarrierOf(card);
832
+ const probe = probeFactsAt(probeLayer);
833
+ const onTop = probeLayer === card;
834
+ const requestedBy = onTop ? approvalRequestedByOf(card) : undefined;
835
+ const payload = onTop ? cardPayloadReadingOf(card) : undefined;
836
+ const inputSource = onTop ? approvalInputSourceOf(card) : undefined;
773
837
  return {
774
838
  ...(mandated ? { mandated: true } : {}),
775
839
  ...(mandate !== undefined ? { mandate } : {}),
@@ -777,6 +841,12 @@ export function toolPermissionRequestAskBits(card, requestId) {
777
841
  ...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
778
842
  ...(askClassPair ?? {}),
779
843
  ...(seatSessionRowOf(card, requestId) ? { sessionRow: true } : {}),
844
+ ...(probe.prose !== undefined ? { probeReason: probe.prose } : {}),
845
+ ...(probe.cause !== undefined ? { probeCause: probe.cause } : {}),
846
+ ...(seatAllowNoteCarriedOf(card, requestId) ? { feedbackCapable: true } : {}),
847
+ ...(requestedBy !== undefined ? { requestedBy } : {}),
848
+ ...(payload !== undefined && payload.payloadUnreadable && payload.why !== undefined ? { frameReading: payload.why } : {}),
849
+ ...(inputSource !== undefined ? { inputSource } : {}),
780
850
  };
781
851
  }
782
852
  function isToolApprovalDelegation(v) {
@@ -803,6 +873,14 @@ function respondDecisionOfAnswer(answer) {
803
873
  return answer.allowSession ? 'allow_session' : 'allow';
804
874
  return answer.kind === 'deny' ? 'deny' : undefined;
805
875
  }
876
+ export function allowNoteReadingOf(answer) {
877
+ const note = readOwnDataValue(answer, 'note');
878
+ if (note === undefined)
879
+ return { kind: 'absent' };
880
+ if (typeof note !== 'string')
881
+ return { kind: 'invalid' };
882
+ return note.trim() === '' ? { kind: 'blank' } : { kind: 'text', text: note };
883
+ }
806
884
  function respondNoteGate(reason, capable) {
807
885
  if (reason === undefined || reason.trim() === '')
808
886
  return { dropped: 'blank' };
@@ -821,10 +899,14 @@ export function answerPayloadFor(answer, lane, caps) {
821
899
  const reason = answer.kind === 'deny' && typeof answer.reason === 'string' ? answer.reason : undefined;
822
900
  return Object.freeze({ decision: 'deny', reason: denyReasonForWire(reason, 'answer') ?? DEFAULT_DENY_REASON });
823
901
  }
902
+ const fb = allowNoteReadingOf(answer);
903
+ if (fb.kind === 'invalid')
904
+ hostLog('error', `answerPayloadFor: the allow note must be a string (got ${typeof readOwnDataValue(answer, 'note')}) — not sent as feedback; the decision itself is sent`);
824
905
  return Object.freeze({
825
906
  decision: 'approve',
826
907
  ...(word === 'allow_session' ? { remember: 'session' } : {}),
827
908
  ...(answer.updatedInput !== undefined ? { updatedInput: answer.updatedInput } : {}),
909
+ ...(fb.kind === 'text' ? { feedback: fb.text } : {}),
828
910
  });
829
911
  }
830
912
  if (answer.kind === 'allow')
@@ -833,9 +915,25 @@ export function answerPayloadFor(answer, lane, caps) {
833
915
  return Object.freeze({ decision: word, ...('note' in gate ? { note: gate.note } : {}) });
834
916
  }
835
917
  const ANSWER_CARRIAGE_NONE = Object.freeze({ allowNote: 'none' });
918
+ const ANSWER_CARRIAGE_MODEL = Object.freeze({ allowNote: 'model' });
836
919
  export function answerCarriage(card) {
837
- void card;
838
- return ANSWER_CARRIAGE_NONE;
920
+ return cardIsOnParkRow(card) ? ANSWER_CARRIAGE_MODEL : ANSWER_CARRIAGE_NONE;
921
+ }
922
+ export function cardIsOnParkRow(card) {
923
+ try {
924
+ if (typeof card !== 'object' || card === null)
925
+ return false;
926
+ const park = readOwnDataValue(card, 'parkRow');
927
+ if (typeof park !== 'object' || park === null || Array.isArray(park))
928
+ return false;
929
+ return readOwnDataNonEmptyString(card, 'taskId') !== undefined;
930
+ }
931
+ catch {
932
+ return false;
933
+ }
934
+ }
935
+ export function allowNoteNotCarriedSentence(lang) {
936
+ return renderMessage(msg('approval.allowNote.notCarried'), lang);
839
937
  }
840
938
  export function observeApprovalAnswerCommitted(listener) {
841
939
  answerCommittedListeners.add(listener);
@@ -852,37 +950,63 @@ function emitAnswerCommitted(callKey, lane, answer, payload) {
852
950
  invokeHostCallbackIsolated('liveToolApprovalWire: answer-committed listener', () => l(event));
853
951
  }
854
952
  export function isToolApprovalRespondReplayed(ack) {
855
- return typeof ack === 'object' && ack !== null && ack.idempotent === true;
953
+ return typeof ack === 'object' && ack !== null && ack.idempotent === true && ack.accepted !== 'parked_redeemed';
954
+ }
955
+ export function isToolApprovalRespondParkedRedeemed(ack) {
956
+ return typeof ack === 'object' && ack !== null && ack.accepted === 'parked_redeemed';
957
+ }
958
+ export function replayedFirstDecisionOf(ack) {
959
+ if (ack === undefined)
960
+ return undefined;
961
+ if (isToolApprovalRespondReplayed(ack))
962
+ return ack.decision;
963
+ if (isToolApprovalRespondParkedRedeemed(ack) && ack.idempotent === true)
964
+ return ack.decision === 'allow' || ack.decision === 'deny' ? ack.decision : undefined;
965
+ return undefined;
966
+ }
967
+ export function settlementAckOf(read) {
968
+ return read !== undefined && isToolApprovalRespondParkedRedeemed(read) && read.idempotent !== true ? undefined : read;
856
969
  }
857
970
  export function readToolApprovalRespondAck(v) {
858
971
  if (v === null || typeof v !== 'object')
859
972
  return undefined;
860
- const o = v;
861
- if (o.idempotent === true)
862
- return o.delivery === undefined ? readReplayedAck(o) : undefined;
863
- if (o.delivery !== 'applied')
864
- return undefined;
865
- if (typeof o.approvalId !== 'string' || o.approvalId.length === 0)
866
- return undefined;
867
- if (!isRespondDecision(o.decision))
868
- return undefined;
973
+ const s = respondAckSlotsOf(v);
974
+ if (s.accepted.presence === 'unreadable' || s.idempotent.presence === 'unreadable' || s.delivery.presence === 'unreadable')
975
+ return undefined;
976
+ if (slotValueOf(s.accepted) === 'parked_redeemed')
977
+ return readParkedRedeemedAck(s);
978
+ if (slotValueOf(s.idempotent) === true)
979
+ return s.delivery.presence === 'absent' ? readReplayedAck(s) : undefined;
980
+ if (slotValueOf(s.delivery) !== 'applied')
981
+ return undefined;
982
+ const approvalId = slotValueOf(s.approvalId);
983
+ const decision = slotValueOf(s.decision);
984
+ if (typeof approvalId !== 'string' || approvalId.length === 0)
985
+ return undefined;
986
+ if (!isRespondDecision(decision))
987
+ return undefined;
988
+ const rememberApplied = slotValueOf(s.rememberApplied);
989
+ const updatedInputForwarded = slotValueOf(s.updatedInputForwarded);
990
+ const rulePersisted = slotValueOf(s.rulePersisted);
991
+ const ruleRefusal = slotValueOf(s.ruleRefusal);
992
+ const noteRecorded = slotValueOf(s.noteRecorded);
993
+ const persistedRule = slotValueOf(s.persistedRule);
994
+ const persistedRules = slotValueOf(s.persistedRules);
995
+ const anchors = readPersistedRuleAnchors(slotValueOf(s.persistedRuleAnchors));
869
996
  const out = {
870
- approvalId: o.approvalId,
997
+ approvalId,
871
998
  delivery: 'applied',
872
- decision: o.decision,
873
- ...(typeof o.rememberApplied === 'boolean' ? { rememberApplied: o.rememberApplied } : {}),
874
- ...(typeof o.updatedInputForwarded === 'boolean' ? { updatedInputForwarded: o.updatedInputForwarded } : {}),
875
- ...(typeof o.rulePersisted === 'boolean' ? { rulePersisted: o.rulePersisted } : {}),
876
- ...(typeof o.ruleRefusal === 'string' && o.ruleRefusal !== ''
877
- ? { ruleRefusal: o.ruleRefusal }
999
+ decision: decision,
1000
+ ...(typeof rememberApplied === 'boolean' ? { rememberApplied } : {}),
1001
+ ...(typeof updatedInputForwarded === 'boolean' ? { updatedInputForwarded } : {}),
1002
+ ...(typeof rulePersisted === 'boolean' ? { rulePersisted } : {}),
1003
+ ...(typeof ruleRefusal === 'string' && ruleRefusal !== ''
1004
+ ? { ruleRefusal: ruleRefusal }
878
1005
  : {}),
879
- ...(typeof o.noteRecorded === 'boolean' ? { noteRecorded: o.noteRecorded } : {}),
880
- ...(typeof o.persistedRule === 'string' && o.persistedRule !== '' ? { persistedRule: o.persistedRule } : {}),
881
- ...(isNonEmptyStringArray(o.persistedRules) ? { persistedRules: o.persistedRules } : {}),
882
- ...(() => {
883
- const anchors = readPersistedRuleAnchors(o.persistedRuleAnchors);
884
- return anchors !== undefined ? { persistedRuleAnchors: anchors } : {};
885
- })(),
1006
+ ...(typeof noteRecorded === 'boolean' ? { noteRecorded } : {}),
1007
+ ...(typeof persistedRule === 'string' && persistedRule !== '' ? { persistedRule } : {}),
1008
+ ...(isNonEmptyStringArray(persistedRules) ? { persistedRules: persistedRules.slice() } : {}),
1009
+ ...(anchors !== undefined ? { persistedRuleAnchors: anchors } : {}),
886
1010
  };
887
1011
  if (out.persistedRule !== undefined && (out.persistedRules !== undefined || out.persistedRuleAnchors !== undefined)) {
888
1012
  delete out.persistedRule;
@@ -903,6 +1027,35 @@ export function readToolApprovalRespondAck(v) {
903
1027
  }
904
1028
  return out;
905
1029
  }
1030
+ const RESPOND_ACK_SLOT_KEYS = [
1031
+ 'accepted',
1032
+ 'idempotent',
1033
+ 'delivery',
1034
+ 'approvalId',
1035
+ 'askId',
1036
+ 'decision',
1037
+ 'decidedAtMs',
1038
+ 'decisionNote',
1039
+ 'rememberApplied',
1040
+ 'updatedInputForwarded',
1041
+ 'rulePersisted',
1042
+ 'ruleRefusal',
1043
+ 'noteRecorded',
1044
+ 'persistedRule',
1045
+ 'persistedRules',
1046
+ 'persistedRuleAnchors',
1047
+ ];
1048
+ function respondAckSlotsOf(o) {
1049
+ const slots = {};
1050
+ for (const key of RESPOND_ACK_SLOT_KEYS) {
1051
+ const slot = slotReadOf(o, key);
1052
+ slots[key] = slot.presence === 'present' ? { presence: 'present', value: ownSnapshot(slot.value) } : slot;
1053
+ }
1054
+ return slots;
1055
+ }
1056
+ function slotValueOf(slot) {
1057
+ return slot.presence === 'present' ? slot.value : undefined;
1058
+ }
906
1059
  export function readToolApprovalRespondParking(v) {
907
1060
  if (typeof v !== 'object' || v === null)
908
1061
  return undefined;
@@ -945,6 +1098,17 @@ function replayClaimOf(raw) {
945
1098
  }
946
1099
  return { ...(approvalId !== undefined ? { approvalId } : {}), ...(d === 'allow' || d === 'deny' ? { decision: d } : {}), carriesDelivery };
947
1100
  }
1101
+ function idempotentSlotUnreadable(raw) {
1102
+ if (typeof raw !== 'object' || raw === null)
1103
+ return false;
1104
+ try {
1105
+ const d = Object.getOwnPropertyDescriptor(raw, 'idempotent');
1106
+ return d !== undefined && !Object.hasOwn(d, 'value');
1107
+ }
1108
+ catch {
1109
+ return true;
1110
+ }
1111
+ }
948
1112
  function ownIdempotentTrue(raw) {
949
1113
  if (typeof raw !== 'object' || raw === null)
950
1114
  return false;
@@ -956,27 +1120,103 @@ function ownIdempotentTrue(raw) {
956
1120
  return false;
957
1121
  }
958
1122
  }
959
- function readReplayedAck(o) {
960
- if (typeof o.approvalId !== 'string' || o.approvalId.length === 0)
1123
+ export function replayVerdictFrom(raw, parsed, approvalId) {
1124
+ if (parsed !== undefined && isToolApprovalRespondReplayed(parsed) && parsed.approvalId === approvalId)
1125
+ return { kind: 'replayed', firstDecision: parsed.decision, via: 'arm' };
1126
+ if (!(ownIdempotentTrue(raw) || replayedFirstDecisionOf(parsed) !== undefined))
1127
+ return idempotentSlotUnreadable(raw) ? { kind: 'unreadable' } : { kind: 'none' };
1128
+ const claimed = replayClaimOf(raw);
1129
+ if (claimed.approvalId === approvalId && claimed.decision !== undefined && !claimed.carriesDelivery)
1130
+ return { kind: 'replayed', firstDecision: claimed.decision, via: 'claim' };
1131
+ return { kind: 'unreadable' };
1132
+ }
1133
+ const REPLAY_VERDICT_NONE = Object.freeze({ kind: 'not_replay' });
1134
+ const REPLAY_VERDICT_ALLOW = Object.freeze({ kind: 'replayed', firstDecision: 'allow' });
1135
+ const REPLAY_VERDICT_DENY = Object.freeze({ kind: 'replayed', firstDecision: 'deny' });
1136
+ const REPLAY_VERDICT_UNREADABLE = Object.freeze({ kind: 'unreadable' });
1137
+ export function toolApprovalReplayVerdictOf(raw, approvalId) {
1138
+ try {
1139
+ if (typeof approvalId !== 'string' || approvalId === '')
1140
+ return REPLAY_VERDICT_UNREADABLE;
1141
+ const v = replayVerdictFrom(raw, settlementAckOf(readToolApprovalRespondAck(raw)), approvalId);
1142
+ if (v.kind === 'none')
1143
+ return REPLAY_VERDICT_NONE;
1144
+ if (v.kind === 'replayed')
1145
+ return v.firstDecision === 'allow' ? REPLAY_VERDICT_ALLOW : REPLAY_VERDICT_DENY;
1146
+ return REPLAY_VERDICT_UNREADABLE;
1147
+ }
1148
+ catch {
1149
+ return REPLAY_VERDICT_UNREADABLE;
1150
+ }
1151
+ }
1152
+ function readReplayedAck(s) {
1153
+ const approvalId = slotValueOf(s.approvalId);
1154
+ const askId = slotValueOf(s.askId);
1155
+ const decision = slotValueOf(s.decision);
1156
+ const decidedAtMs = slotValueOf(s.decidedAtMs);
1157
+ const decisionNote = slotValueOf(s.decisionNote);
1158
+ if (typeof approvalId !== 'string' || approvalId.length === 0)
961
1159
  return undefined;
962
- if (typeof o.askId !== 'string' || o.askId.length === 0)
1160
+ if (typeof askId !== 'string' || askId.length === 0)
963
1161
  return undefined;
964
- if (o.decision !== 'allow' && o.decision !== 'deny')
1162
+ if (decision !== 'allow' && decision !== 'deny')
965
1163
  return undefined;
966
- if (typeof o.decidedAtMs !== 'number' || !Number.isFinite(o.decidedAtMs) || o.decidedAtMs < 0)
1164
+ if (typeof decidedAtMs !== 'number' || !Number.isFinite(decidedAtMs) || decidedAtMs < 0)
967
1165
  return undefined;
968
1166
  return {
969
- approvalId: o.approvalId,
970
- askId: o.askId,
1167
+ approvalId,
1168
+ askId,
971
1169
  idempotent: true,
972
- decision: o.decision,
973
- decidedAtMs: o.decidedAtMs,
974
- ...(typeof o.decisionNote === 'string' && o.decisionNote !== '' ? { decisionNote: o.decisionNote } : {}),
975
- ...(o.rememberApplied === false ? { rememberApplied: false } : {}),
1170
+ decision,
1171
+ decidedAtMs,
1172
+ ...(typeof decisionNote === 'string' && decisionNote !== '' ? { decisionNote } : {}),
1173
+ ...(slotValueOf(s.rememberApplied) === false ? { rememberApplied: false } : {}),
976
1174
  };
977
1175
  }
1176
+ function readParkedRedeemedAck(s) {
1177
+ if (s.delivery.presence !== 'absent')
1178
+ return undefined;
1179
+ const text = (slot) => {
1180
+ const v = slotValueOf(slot);
1181
+ return typeof v === 'string' && v !== '' ? v : undefined;
1182
+ };
1183
+ const approvalId = text(s.approvalId);
1184
+ const askId = text(s.askId);
1185
+ if (approvalId === undefined || askId === undefined)
1186
+ return undefined;
1187
+ const replay = slotValueOf(s.idempotent) === true;
1188
+ const dec = slotValueOf(s.decision);
1189
+ let decision;
1190
+ if (replay) {
1191
+ if (dec !== 'allow' && dec !== 'deny')
1192
+ return undefined;
1193
+ decision = dec;
1194
+ }
1195
+ else {
1196
+ decision = typeof dec === 'string' && dec !== '' ? dec : undefined;
1197
+ }
1198
+ const remember = slotValueOf(s.rememberApplied);
1199
+ return {
1200
+ approvalId,
1201
+ askId,
1202
+ accepted: 'parked_redeemed',
1203
+ ...(replay ? { idempotent: true } : {}),
1204
+ ...(decision !== undefined ? { decision } : {}),
1205
+ ...(typeof remember === 'boolean' ? { rememberApplied: remember } : {}),
1206
+ };
1207
+ }
1208
+ function replayedDecisionOf(first, sent, ack) {
1209
+ return { kind: 'decided', lane: 'respond', decision: first, ...(ack !== undefined ? { ack } : {}), settledElsewhere: toolPermissionSettledElsewhereOf(first, sent) };
1210
+ }
978
1211
  function isNonEmptyStringArray(v) {
979
- return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x !== '');
1212
+ if (!Array.isArray(v) || v.length === 0)
1213
+ return false;
1214
+ for (let i = 0; i < v.length; i++) {
1215
+ const x = v[i];
1216
+ if (typeof x !== 'string' || x === '')
1217
+ return false;
1218
+ }
1219
+ return true;
980
1220
  }
981
1221
  function readPersistedRuleAnchors(v) {
982
1222
  if (!Array.isArray(v) || v.length === 0)
@@ -1080,19 +1320,44 @@ function readProbeReason(v) {
1080
1320
  function usableProbeReason(raw) {
1081
1321
  return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
1082
1322
  }
1323
+ function probeFactsAt(at) {
1324
+ const prose = usableProbeReason(ownDataValue(at, 'probeReason'));
1325
+ let cause;
1326
+ try {
1327
+ const raw = ownDataValue(at, 'probeCause');
1328
+ if (isWireRecordCarrier(raw))
1329
+ cause = raw;
1330
+ }
1331
+ catch {
1332
+ cause = undefined;
1333
+ }
1334
+ return { prose, cause };
1335
+ }
1336
+ function probeCarrierOf(carrier) {
1337
+ let d;
1338
+ try {
1339
+ d = Object.getOwnPropertyDescriptor(carrier, 'riskDescriptor');
1340
+ }
1341
+ catch {
1342
+ return undefined;
1343
+ }
1344
+ if (d === undefined)
1345
+ return carrier;
1346
+ return Object.hasOwn(d, 'value') ? d.value : undefined;
1347
+ }
1083
1348
  const PROBE_CAUSE_CODE_WORD = /^[a-z][a-z0-9_]{0,31}(?:\.[a-z][a-z0-9_]{0,31}){1,3}$/;
1084
1349
  export function askProbeReasonDetail(card, lang) {
1085
- const prose = usableProbeReason(ownDataValue(card, 'probeReason'));
1350
+ const { prose, cause } = probeFactsAt(card);
1086
1351
  const shown = prose === undefined ? '' : displayUntrusted(prose);
1087
- const sentence = probeCauseSentence(ownDataValue(card, 'probeCause'), shown !== '', lang);
1352
+ const sentence = probeCauseSentence(cause, shown !== '', lang);
1088
1353
  if (sentence === undefined)
1089
1354
  return shown === '' ? undefined : shown;
1090
1355
  return shown === '' ? sentence : renderMessage(msg('approval.probe.proseJoin', { sentence, prose: shown }), lang);
1091
1356
  }
1092
1357
  function probeCauseSentence(cause, withProse, lang) {
1358
+ if (cause === undefined)
1359
+ return undefined;
1093
1360
  try {
1094
- if (!isWireRecordCarrier(cause))
1095
- return undefined;
1096
1361
  const code = ownDataValue(cause, 'code');
1097
1362
  const withCode = typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code);
1098
1363
  const key = withProse
@@ -1675,6 +1940,67 @@ function subagentBadgeFor(frame) {
1675
1940
  name = `${name.slice(0, 31)}…`;
1676
1941
  return { name, color: 'cyan' };
1677
1942
  }
1943
+ const APPROVAL_CARD_FACT_KEYS = [
1944
+ 'toolName', 'args', 'toolCallId', 'workerBadge', 'wireNote', 'argsUnavailable', 'inputSource', 'governanceForced', 'delegation', 'ruleOffersReadOnly',
1945
+ 'inputHasBidi', 'allowRuleShadow', 'probeCause', 'rememberRefused', 'ruleEvidence', 'requiresRealApproval', 'ruleOffersAbsence', 'denialLimitFallback',
1946
+ 'askClass', 'classes', 'agentId', 'askReasons', 'grantBinding', 'unreadableEvidence', 'message', 'approvalCard',
1947
+ 'argsRewritten', 'ruleOffers', 'probeReason', 'mandated', 'mandate', 'readRootCandidate', 'origin', 'boundGrants', 'classifierNoRuling',
1948
+ 'allowSessionCarried', 'cardPayloadUnreadable', 'cardPayloadUnreadableWhy', 'requestedBy',
1949
+ ];
1950
+ const APPROVAL_CARD_HOST_KEYS = ['callKey', 'taskId', 'signal', 'expiresInMs', 'expiresAtMs', 'serverNowMs', 'receivedAtMs', 'sourceGeneration'];
1951
+ const _pinCardRequestPartition = [true, true, true, true, true];
1952
+ void _pinCardRequestPartition;
1953
+ const FACT_KEY_SET = new Set(APPROVAL_CARD_FACT_KEYS);
1954
+ const HOST_KEY_SET = new Set(APPROVAL_CARD_HOST_KEYS);
1955
+ function hostBitInDomain(key, v) {
1956
+ switch (key) {
1957
+ case 'callKey':
1958
+ case 'taskId':
1959
+ return typeof v === 'string' && v !== '';
1960
+ case 'signal':
1961
+ return typeof v === 'object' && v !== null;
1962
+ case 'sourceGeneration':
1963
+ return typeof v === 'number' && Number.isFinite(v);
1964
+ default:
1965
+ return typeof v === 'number' && Number.isFinite(v) && v >= 0;
1966
+ }
1967
+ }
1968
+ function closedKeysListing(from) {
1969
+ try {
1970
+ return { kind: 'listed', keys: Reflect.ownKeys(from) };
1971
+ }
1972
+ catch {
1973
+ return { kind: 'unreadable' };
1974
+ }
1975
+ }
1976
+ function copyClosedKeys(into, from, keys, admit) {
1977
+ const listed = closedKeysListing(from);
1978
+ if (listed.kind === 'unreadable')
1979
+ return;
1980
+ for (let i = 0; i < listed.keys.length; i += 1) {
1981
+ const k = listed.keys[i];
1982
+ if (typeof k !== 'string' || !keys.has(k))
1983
+ continue;
1984
+ const v = guardedRead(() => ownSendableField(from, k));
1985
+ if (v === undefined || (admit !== undefined && !admit(k, v)))
1986
+ continue;
1987
+ into[k] = v;
1988
+ }
1989
+ }
1990
+ function cardRequestOf(facts, host) {
1991
+ const out = {};
1992
+ copyClosedKeys(out, facts, FACT_KEY_SET);
1993
+ copyClosedKeys(out, host, HOST_KEY_SET, (k, v) => hostBitInDomain(k, v));
1994
+ return out;
1995
+ }
1996
+ export function approvalCardRequestOf(facts, host) {
1997
+ if (typeof facts !== 'object' || facts === null || typeof host !== 'object' || host === null)
1998
+ return undefined;
1999
+ const out = cardRequestOf(facts, host);
2000
+ if (typeof out.toolName !== 'string' || typeof out.args !== 'object' || out.args === null || typeof out.callKey !== 'string')
2001
+ return undefined;
2002
+ return out;
2003
+ }
1678
2004
  function v1Bit(stamp) {
1679
2005
  try {
1680
2006
  stamp();
@@ -1713,12 +2039,14 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
1713
2039
  if (reading.from === 'recorded') {
1714
2040
  if (blind)
1715
2041
  return { args: reading.args, argsUnavailable: true };
1716
- return reading.rewritten ? { args: reading.args, argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : { args: reading.args };
2042
+ return reading.rewritten
2043
+ ? { args: reading.args, argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE, ...inputSourceBitOf(reading) }
2044
+ : { args: reading.args, ...inputSourceBitOf(reading) };
1717
2045
  }
1718
2046
  if (reading.from === 'stream' && typeof reading.args === 'object' && reading.args !== null) {
1719
2047
  if (blind)
1720
2048
  return { args: reading.args, argsUnavailable: true };
1721
- return { args: reading.args, wireNote: ARGS_FROM_MODEL_CALL_NOTE };
2049
+ return { args: reading.args, ...argsFromModelCallBitsOf(), ...inputSourceBitOf(reading) };
1722
2050
  }
1723
2051
  const omitted = c.argsOmitted === true || reading.shape === 'size_marker';
1724
2052
  const fallback = argsFallbackOf(c.message, omitted);
@@ -1749,6 +2077,8 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
1749
2077
  out.argsUnavailable = true;
1750
2078
  if (argsPart.argsRewritten === true)
1751
2079
  out.argsRewritten = true;
2080
+ if (argsPart.inputSource !== undefined)
2081
+ out.inputSource = argsPart.inputSource;
1752
2082
  if (full) {
1753
2083
  v1Bit(() => {
1754
2084
  if (c.governanceForced === true)
@@ -1784,6 +2114,11 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
1784
2114
  if (r !== undefined)
1785
2115
  out.probeReason = r;
1786
2116
  });
2117
+ v1Bit(() => {
2118
+ const by = approvalRequestedByOf(live);
2119
+ if (by !== undefined)
2120
+ out.requestedBy = by;
2121
+ });
1787
2122
  }
1788
2123
  v1Bit(() => {
1789
2124
  if (readApprovalRememberRefused(carrier))
@@ -1928,7 +2263,10 @@ export async function surfaceApprovalFrameWithId(frame, approvalId, respond, str
1928
2263
  if (typeof approvalId !== 'string' || approvalId === '')
1929
2264
  return approvalIdUnreadableRefusal();
1930
2265
  const carriers = openingFrameCarriersOf(frame);
1931
- const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, frame, carriers, run), respond, streamArgs, signal, lane, receivedAtMs };
2266
+ const liveRun = liveCardRunOf(frame, run);
2267
+ const sourceGenerationRaw = guardedRead(() => readOwnDataValue(run, 'sourceGeneration'));
2268
+ const sourceGeneration = typeof sourceGenerationRaw === 'number' && Number.isFinite(sourceGenerationRaw) ? sourceGenerationRaw : undefined;
2269
+ const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, carriers, liveRun), respond, streamArgs, signal, lane, receivedAtMs, sourceGeneration };
1932
2270
  return enterLiveCard(entry);
1933
2271
  }
1934
2272
  function openingFrameCarriersOf(frame) {
@@ -1954,7 +2292,7 @@ function enterLiveCard(e) {
1954
2292
  return joinOtherLaneHolder(e, other);
1955
2293
  return holdLiveCard(e.identity, signal, async (hold) => {
1956
2294
  const slot = {};
1957
- const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
2295
+ const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs, e.identity.taskId, e.sourceGeneration);
1958
2296
  noteCallOutcome(slot, out);
1959
2297
  return out;
1960
2298
  });
@@ -1969,15 +2307,20 @@ async function joinOtherLaneHolder(e, other) {
1969
2307
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted', joinedInFlight: true };
1970
2308
  return enterLiveCard(e);
1971
2309
  }
1972
- function liveCardIdentityOf(approvalId, frame, carriers, run) {
2310
+ function liveCardRunOf(frame, run) {
2311
+ const source = frameBindingOf(frame) ?? run;
2312
+ return {
2313
+ taskId: guardedRead(() => readOwnDataNonEmptyString(source, 'taskId')),
2314
+ sessionId: guardedRead(() => readOwnDataNonEmptyString(source, 'sessionId')),
2315
+ };
2316
+ }
2317
+ function liveCardIdentityOf(approvalId, carriers, liveRun) {
1973
2318
  const body = carriers.body;
1974
2319
  const toolCallId = typeof body.toolCallId === 'string' && body.toolCallId !== '' ? body.toolCallId : undefined;
1975
2320
  const fromSubagent = isFromSubagent(carriers.body);
1976
- const bound = fromSubagent ? undefined : frameBindingOf(frame);
1977
- const source = bound ?? run;
1978
- const ownRun = fromSubagent ? subagentSourceSessionOf(carriers.body) : source?.taskId;
2321
+ const ownRun = fromSubagent ? subagentSourceSessionOf(carriers.body) : liveRun.taskId;
1979
2322
  const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
1980
- const sessionId = !fromSubagent && typeof source?.sessionId === 'string' && source.sessionId !== '' ? source.sessionId : undefined;
2323
+ const sessionId = fromSubagent ? undefined : liveRun.sessionId;
1981
2324
  const askSig = carrierAskSig(carriers.body, carriers.live);
1982
2325
  return {
1983
2326
  approvalId,
@@ -2033,22 +2376,24 @@ function readApprovalCardClaimSignal(v) {
2033
2376
  export function claimApprovalCard(identity, options) {
2034
2377
  return claimApprovalCardFor(readApprovalHolderIdentity(identity), readApprovalCardClaimSignal(options));
2035
2378
  }
2036
- async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
2379
+ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs, cardTaskId, sourceGeneration) {
2037
2380
  const bodyToolCallId = carriers.body.toolCallId;
2038
2381
  const frameToolCallId = typeof bodyToolCallId === 'string' && bodyToolCallId !== '' ? bodyToolCallId : undefined;
2039
2382
  const decidedByReading = decidedByReadingSnapshotOf(lane);
2040
2383
  const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live, approvalId, carriers.why);
2041
2384
  const argsNotRealInput = facts.argsUnavailable === true;
2385
+ const editAcceptance = editedApprovalAcceptanceOf(facts);
2042
2386
  const ruleOffers = facts.ruleOffers;
2043
- if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
2387
+ if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined && facts.inputSource !== 'stream') {
2044
2388
  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)'}`);
2045
2389
  }
2046
- const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, {
2047
- ...facts,
2390
+ const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, cardRequestOf(facts, {
2048
2391
  callKey: liveFrameCallKey(approvalId),
2392
+ ...(cardTaskId !== undefined ? { taskId: cardTaskId } : {}),
2393
+ ...sourceGenerationBit(sourceGeneration),
2049
2394
  signal: hold.cardSignal,
2050
2395
  ...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs, ...(carriers.leg === 'generic' ? { unknownShape: true } : {}) }),
2051
- });
2396
+ }));
2052
2397
  if (hold.superseded !== undefined) {
2053
2398
  hostLog('debug', `liveToolApprovalWire: ${approvalId} was settled by the engine while its card was up${hold.superseded.parked ? ' (parked)' : ''} — card withdrawn; ` +
2054
2399
  `the card came back ${card.kind}; nothing sent`);
@@ -2056,28 +2401,38 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2056
2401
  surfaceApprovalParking();
2057
2402
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
2058
2403
  }
2059
- if (argsNotRealInput && card.kind === 'allow' && card.updatedInput !== undefined) {
2404
+ if (editAcceptance.state !== 'accepts' && card.kind === 'allow' && card.updatedInput !== undefined) {
2060
2405
  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)`);
2061
2406
  surfaceEditRefusedOnBlindAsk();
2062
2407
  return { kind: 'not_sent', lane: 'respond', cause: 'edit_refused' };
2063
2408
  }
2409
+ if (card.kind === WITHDRAWN_CARD_DECISION_KIND)
2410
+ return withdrawnCardResolution(card, { lane: 'respond' }, 'liveToolApprovalWire');
2064
2411
  if (card.kind === RETRACTED_CARD_DECISION_KIND) {
2065
2412
  hostLog('debug', `liveToolApprovalWire: ${approvalId} card retracted without a decision${card.reason ? ` (${card.reason})` : ''} — nothing sent`);
2066
2413
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
2067
2414
  }
2415
+ const respondParkingOf = (body) => {
2416
+ const p = readToolApprovalRespondParking(body);
2417
+ return p?.kind === 'parking' ? p.view : undefined;
2418
+ };
2419
+ const deliverOnThisCard = (d, body, carrySignal, deliverySignal, ladder, onLadderStep) => deliverApprovalDecision({
2420
+ approvalId,
2421
+ body,
2422
+ send: (o, sig) => respond(approvalId, d, carrySignal && sig !== undefined ? { ...o, signal: sig } : o),
2423
+ signal: deliverySignal,
2424
+ tally: newSendTally(),
2425
+ ladder,
2426
+ ...(onLadderStep !== undefined ? { onLadderStep } : {}),
2427
+ parkingOf: respondParkingOf,
2428
+ });
2068
2429
  if (card.kind === 'aborted') {
2069
2430
  enterLiveRespondPhase(hold);
2070
2431
  const interruptOpts = lane?.approvalDecisionNoteCapable === true ? { note: INTERRUPT_DENY_REASON } : {};
2071
- let interruptDeny;
2072
- try {
2073
- interruptDeny = Promise.resolve(respond(approvalId, 'deny', interruptOpts));
2074
- }
2075
- catch (e) {
2076
- interruptDeny = Promise.reject(e);
2077
- }
2432
+ const interruptDeny = deliverOnThisCard('deny', interruptOpts, true, interruptDenyLifetime(), { lastCarried: false, dropped: false, renamed: false }).then((delivery) => (delivery.kind === 'thrown' ? Promise.reject(delivery.error) : delivery.raw));
2078
2433
  hold.trailing = interruptDeny.then((body) => readToolApprovalRespondParking(body) === undefined, () => false);
2079
2434
  observeCancelByDeny(interruptDeny, approvalId);
2080
- hostLog('debug', `liveToolApprovalWire: ${approvalId} card aborted — one tool-level deny issued to settle the pending ask`);
2435
+ hostLog('debug', `liveToolApprovalWire: ${approvalId} card aborted — a tool-level deny issued to settle the pending ask (bounded retry; at most one is accepted)`);
2081
2436
  return { kind: 'unsettled', lane: 'respond', cause: 'interrupted' };
2082
2437
  }
2083
2438
  enterLiveRespondPhase(hold);
@@ -2094,8 +2449,10 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2094
2449
  : card;
2095
2450
  const basePayload = answerPayloadFor(answerSnapshot, 'respond', { approvalDecisionNoteCapable: noteCapable });
2096
2451
  const decision = basePayload?.decision ?? 'deny';
2097
- if (card.kind === 'allow' && typeof card.note === 'string' && card.note.trim() !== '' && answerCarriage({ callKey: liveFrameCallKey(approvalId) }).allowNote === 'none') {
2098
- 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`);
2452
+ const allowNoteText = decision !== 'deny' ? allowNoteReadingOf(answerSnapshot) : { kind: 'absent' };
2453
+ const allowNoteDropped = allowNoteText.kind === 'text' && answerCarriage({ callKey: liveFrameCallKey(approvalId) }).allowNote === 'none';
2454
+ if (allowNoteDropped && allowNoteText.kind === 'text') {
2455
+ hostLog('debug', `liveToolApprovalWire: ${approvalId} card carried a note on Yes (len=${allowNoteText.text.length}) — this approval door has no field that brings it to the model, so it was not sent`);
2099
2456
  }
2100
2457
  const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
2101
2458
  const denyPolicyCauseForWire = card.kind === 'deny' ? readPolicyDenyCause(card) : undefined;
@@ -2198,30 +2555,18 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2198
2555
  if (basePayload !== undefined)
2199
2556
  emitAnswerCommitted(liveFrameCallKey(approvalId), 'respond', answerSnapshot, basePayload);
2200
2557
  try {
2201
- const delivery = await deliverApprovalDecision({
2202
- approvalId,
2203
- body: respondOpts,
2204
- send: (o, sig) => respond(approvalId, decision, !signalPreAborted && sig !== undefined ? { ...o, signal: sig } : o),
2205
- signal,
2206
- tally: newSendTally(),
2207
- ladder,
2208
- onLadderStep: (step) => {
2209
- hostLog('debug', step === 'renamed'
2210
- ? `liveToolApprovalWire: respond(deny) for ${approvalId} was refused because the settlement note was sent under a name ` +
2211
- 'this engine has retired — re-sending the SAME decision exactly once with that one key renamed to the name the refusal points to ' +
2558
+ const delivery = await deliverOnThisCard(decision, respondOpts, !signalPreAborted, signal, ladder, (step) => {
2559
+ hostLog('debug', step === 'renamed'
2560
+ ? `liveToolApprovalWire: respond(deny) for ${approvalId} was refused because the settlement note was sent under a name ` +
2561
+ 'this engine has retired — re-sending the SAME decision exactly once with that one key renamed to the name the refusal points to ' +
2562
+ '(the refusal happened before the approval was judged, so nothing was consumed and this is a re-delivery, not a second act)'
2563
+ : step === 'dropped'
2564
+ ? `liveToolApprovalWire: respond(deny) for ${approvalId} was refused because the settlement note is not part of ` +
2565
+ 'what this deployment signs — re-sending the SAME decision exactly once with that one key removed ' +
2212
2566
  '(the refusal happened before the approval was judged, so nothing was consumed and this is a re-delivery, not a second act)'
2213
- : step === 'dropped'
2214
- ? `liveToolApprovalWire: respond(deny) for ${approvalId} was refused because the settlement note is not part of ` +
2215
- 'what this deployment signs — re-sending the SAME decision exactly once with that one key removed ' +
2216
- '(the refusal happened before the approval was judged, so nothing was consumed and this is a re-delivery, not a second act)'
2217
- : `liveToolApprovalWire: respond(deny) for ${approvalId} was refused again because the settlement note is not part of ` +
2218
- 'what this deployment signs — re-sending the SAME decision exactly once more with that key removed ' +
2219
- '(still before the approval was judged)');
2220
- },
2221
- parkingOf: (body) => {
2222
- const p = readToolApprovalRespondParking(body);
2223
- return p?.kind === 'parking' ? p.view : undefined;
2224
- },
2567
+ : `liveToolApprovalWire: respond(deny) for ${approvalId} was refused again because the settlement note is not part of ` +
2568
+ 'what this deployment signs — re-sending the SAME decision exactly once more with that key removed ' +
2569
+ '(still before the approval was judged)');
2225
2570
  });
2226
2571
  if (delivery.kind === 'thrown') {
2227
2572
  deliveryFailure = delivery;
@@ -2249,16 +2594,14 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2249
2594
  'reporting the answer as NOT settled (never as decided, never as parked); not re-sent');
2250
2595
  return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
2251
2596
  }
2252
- const parsed = readToolApprovalRespondAck(raw);
2253
- const replayedForThisCard = parsed !== undefined && isToolApprovalRespondReplayed(parsed) && parsed.approvalId === approvalId;
2254
- const claimsReplay = ownIdempotentTrue(raw) || (parsed !== undefined && isToolApprovalRespondReplayed(parsed));
2255
- if (claimsReplay && !replayedForThisCard) {
2256
- const claimed = replayClaimOf(raw);
2257
- if (claimed.approvalId === approvalId && claimed.decision !== undefined && !claimed.carriesDelivery) {
2258
- hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay (body not in the full replay shape) — the approval was already decided ` +
2259
- `(${claimed.decision}); this answer was not delivered, reporting the earlier decision`);
2260
- return { kind: 'decided', lane: 'respond', decision: claimed.decision };
2261
- }
2597
+ const parsed = settlementAckOf(readToolApprovalRespondAck(raw));
2598
+ const replayVerdict = replayVerdictFrom(raw, parsed, approvalId);
2599
+ if (replayVerdict.kind === 'replayed' && replayVerdict.via === 'claim') {
2600
+ hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay (body not in the full replay shape) — the approval was already decided ` +
2601
+ `(${replayVerdict.firstDecision}); this answer was not delivered, reporting the earlier decision`);
2602
+ return replayedDecisionOf(replayVerdict.firstDecision, decision, parsed !== undefined && isToolApprovalRespondParkedRedeemed(parsed) && parsed.approvalId === approvalId ? parsed : undefined);
2603
+ }
2604
+ if (replayVerdict.kind === 'unreadable') {
2262
2605
  hostLog('error', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay of an earlier decision that cannot be read or names another approval — ` +
2263
2606
  'reporting the answer as NOT settled (never as the decision this client sent); not re-sent');
2264
2607
  return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
@@ -2327,6 +2670,8 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2327
2670
  }
2328
2671
  if (replayed === undefined && sessionDowngraded)
2329
2672
  surfaceRememberNotApplied();
2673
+ if (replayed === undefined && allowNoteDropped)
2674
+ surfaceAllowNoteNotCarried(allowNoteNotCarriedSentence());
2330
2675
  if (respondOpts.updatedInput !== undefined && applied?.updatedInputForwarded === false) {
2331
2676
  logAfterDecided(`liveToolApprovalWire: ${approvalId} edited args were NOT forwarded (ack.updatedInputForwarded=false) — the tool runs on the ORIGINAL input`);
2332
2677
  surfaceEditNotForwarded();
@@ -2338,7 +2683,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2338
2683
  if (replayed !== undefined) {
2339
2684
  hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay — the approval was already decided (${replayed.decision}); ` +
2340
2685
  'this answer was not delivered, reporting the earlier decision');
2341
- return { kind: 'decided', lane: 'respond', decision: replayed.decision, ack: replayed };
2686
+ return replayedDecisionOf(replayed.decision, decision, replayed);
2342
2687
  }
2343
2688
  const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed, denyPolicyCauseForWire) : undefined;
2344
2689
  return { kind: 'decided', lane: 'respond', decision, ...(ack !== undefined ? { ack } : {}), ...(deny !== undefined ? { deny } : {}) };