@sema-agent/client-core 0.109.0 → 0.110.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 (123) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +46 -22
  3. package/dist/adapt/arms.js +20 -7
  4. package/dist/adapter/downstream/eventToSdkMessage.js +11 -6
  5. package/dist/declared/supportFloorDeclared.js +1 -1
  6. package/dist/displayUntrusted.js +132 -14
  7. package/dist/engineErrorCodes.d.ts +3 -0
  8. package/dist/engineErrorCodes.js +3 -0
  9. package/dist/engineHealthFacts.d.ts +9 -0
  10. package/dist/engineHealthFacts.js +21 -1
  11. package/dist/engineInlineTaskStats.d.ts +4 -3
  12. package/dist/engineInlineTaskStats.js +18 -2
  13. package/dist/engineNoticeCodes.d.ts +10 -0
  14. package/dist/engineNoticeCodes.js +33 -1
  15. package/dist/engineRepoint.d.ts +140 -0
  16. package/dist/engineRepoint.js +285 -0
  17. package/dist/engineRowRef.js +2 -0
  18. package/dist/engineSessionMessages.d.ts +2 -1
  19. package/dist/engineSessionMessages.js +5 -0
  20. package/dist/errorSupplementRoles.js +2 -1
  21. package/dist/fleet/fleetProjection.js +3 -2
  22. package/dist/generated/engineFactTables.d.ts +2 -0
  23. package/dist/generated/engineFactTables.js +12 -0
  24. package/dist/generated/sdkFactTables.d.ts +1 -0
  25. package/dist/generated/sdkFactTables.js +4 -0
  26. package/dist/hitl/approvalAskers.d.ts +6 -0
  27. package/dist/hitl/approvalAskers.js +30 -0
  28. package/dist/hitl/approvalCardReasons.js +6 -3
  29. package/dist/hitl/approvalRequestFrameReading.d.ts +7 -0
  30. package/dist/hitl/approvalRequestFrameReading.js +11 -0
  31. package/dist/hitl/approvalResolution.d.ts +8 -1
  32. package/dist/hitl/approvalResolution.js +4 -0
  33. package/dist/hitl/askGateWire.js +0 -2
  34. package/dist/hitl/automaticAnswerVerdict.js +2 -2
  35. package/dist/hitl/decisionConflict.js +6 -3
  36. package/dist/hitl/decisionRefusal.js +1 -0
  37. package/dist/hitl/decisionSend.d.ts +3 -0
  38. package/dist/hitl/decisionSend.js +29 -11
  39. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  40. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  41. package/dist/hitl/frameRouter.js +4 -18
  42. package/dist/hitl/gateLedger.d.ts +0 -4
  43. package/dist/hitl/gateLedger.js +0 -19
  44. package/dist/hitl/hitlBridge.d.ts +1 -0
  45. package/dist/hitl/hitlBridge.js +4 -4
  46. package/dist/hitl/hitlHostSurface.d.ts +1 -0
  47. package/dist/hitl/hitlHostSurface.js +7 -2
  48. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  49. package/dist/hitl/hostWithdrawal.js +38 -0
  50. package/dist/hitl/parkResolver.js +25 -9
  51. package/dist/hitl/persistedRulesWire.js +2 -2
  52. package/dist/hitl/planReviewWire.js +14 -4
  53. package/dist/hitl/sessionPolicyWire.js +6 -4
  54. package/dist/hitl/standingRows.d.ts +1 -0
  55. package/dist/hitl/standingRows.js +15 -1
  56. package/dist/hitl/toolApprovalWire.d.ts +35 -8
  57. package/dist/hitl/toolApprovalWire.js +235 -33
  58. package/dist/hooksWireCaps.js +31 -1
  59. package/dist/hostTurnSettings.d.ts +10 -4
  60. package/dist/hostTurnSettings.js +112 -5
  61. package/dist/i18n/messages.en.d.ts +83 -0
  62. package/dist/i18n/messages.en.js +86 -2
  63. package/dist/i18n/messages.zh.js +96 -2
  64. package/dist/index.d.ts +21 -7
  65. package/dist/index.js +14 -4
  66. package/dist/inheritEnvCapability.js +2 -0
  67. package/dist/inputAuthor.d.ts +16 -0
  68. package/dist/inputAuthor.js +44 -0
  69. package/dist/interruptRefusalNote.d.ts +2 -0
  70. package/dist/interruptRefusalNote.js +17 -0
  71. package/dist/liveModelCatalog.d.ts +2 -0
  72. package/dist/liveModelCatalog.js +21 -7
  73. package/dist/liveQuestionStore.d.ts +4 -1
  74. package/dist/liveQuestionStore.js +12 -3
  75. package/dist/mcpEngineLeg.d.ts +3 -0
  76. package/dist/mcpEngineLeg.js +38 -1
  77. package/dist/mcpPanel.js +2 -0
  78. package/dist/mcpReconnect.d.ts +9 -1
  79. package/dist/mcpReconnect.js +121 -16
  80. package/dist/modelCapabilityProbe.js +4 -3
  81. package/dist/modsFace.d.ts +8 -1
  82. package/dist/modsFace.js +27 -0
  83. package/dist/notifications.d.ts +17 -6
  84. package/dist/notifications.js +67 -19
  85. package/dist/ownKey.d.ts +0 -5
  86. package/dist/ownKey.js +0 -7
  87. package/dist/peerFrames.d.ts +40 -2
  88. package/dist/peerFrames.js +158 -3
  89. package/dist/pluginHooksWire.d.ts +2 -1
  90. package/dist/pluginHooksWire.js +56 -1
  91. package/dist/replayBody.js +10 -2
  92. package/dist/request/taskRequest.d.ts +5 -0
  93. package/dist/request/taskRequest.js +138 -21
  94. package/dist/resumeRefusalCopy.d.ts +1 -3
  95. package/dist/resumeRefusalCopy.js +2 -1
  96. package/dist/retryStatus.d.ts +2 -2
  97. package/dist/retryStatus.js +2 -0
  98. package/dist/seam.d.ts +12 -1
  99. package/dist/seam.js +7 -1
  100. package/dist/seatContract.d.ts +86 -9
  101. package/dist/seatContract.js +196 -7
  102. package/dist/sessionBackground.js +2 -1
  103. package/dist/sessionBackgroundHandoff.js +2 -1
  104. package/dist/sessionEngine.d.ts +2 -0
  105. package/dist/sessionEngine.js +111 -0
  106. package/dist/sessionMemoryStatus.d.ts +2 -1
  107. package/dist/sessionSyncFileHistory.d.ts +4 -0
  108. package/dist/sessionSyncFileHistory.js +28 -0
  109. package/dist/sseIdleTriage.d.ts +13 -1
  110. package/dist/sseIdleTriage.js +24 -6
  111. package/dist/subagent/engineSubagentTail.js +6 -0
  112. package/dist/terminalStatuses.d.ts +1 -0
  113. package/dist/terminalStatuses.js +4 -1
  114. package/dist/turnFailurePresentation.js +9 -2
  115. package/dist/typePins.d.ts +11 -0
  116. package/dist/usageAnalytics.d.ts +17 -0
  117. package/dist/usageAnalytics.js +44 -2
  118. package/dist/wireFailureShape.d.ts +2 -5
  119. package/dist/wireFailureShape.js +10 -20
  120. package/dist/wireRefusalCopy.d.ts +10 -0
  121. package/dist/wireRefusalCopy.js +76 -0
  122. package/docs/INTEGRATION-CLIENTS.md +2126 -20
  123. package/package.json +3 -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
7
  import { appendOwnElement, guardedRead, 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,22 +17,24 @@ 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, 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';
@@ -98,6 +100,16 @@ export function readApprovalDenySettledBy(decision) {
98
100
  : undefined;
99
101
  }
100
102
  export const RETRACTED_CARD_DECISION_KIND = 'retracted';
103
+ export const WITHDRAWN_CARD_DECISION_KIND = 'withdrawn';
104
+ function withdrawnCardResolution(card, at, logTag) {
105
+ const w = hostWithdrawalOf(card);
106
+ if (w === undefined) {
107
+ hostLog('debug', `${logTag}: card-withdrawn-unreadable as=retracted`);
108
+ return { kind: 'not_sent', ...at, cause: 'retracted' };
109
+ }
110
+ hostLog('debug', `${logTag}: card-withdrawn-by-host verdict=${w.rowVerdict.kind}`);
111
+ return { kind: 'not_sent', ...at, cause: 'withdrawn', toldBy: w.toldBy, rowVerdict: w.rowVerdict };
112
+ }
101
113
  function parkRowOf(taskId, row, gatedCallId) {
102
114
  const d = parkRowDeadlineState(row, Date.now());
103
115
  return Object.freeze({
@@ -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);
@@ -249,6 +271,14 @@ function cardInputOf(recorded, stream) {
249
271
  return { from: 'stream', args: stream, shape };
250
272
  return { from: 'none', shape, recorded };
251
273
  }
274
+ function inputSourceBitOf(reading) {
275
+ return reading.from === 'recorded' ? { inputSource: 'record' } : reading.from === 'stream' ? { inputSource: 'stream' } : {};
276
+ }
277
+ function approvalInputSourceOf(card) {
278
+ const raw = ownDataValue(card, 'inputSource');
279
+ const known = { record: true, stream: true };
280
+ return typeof raw === 'string' && Object.hasOwn(known, raw) ? raw : undefined;
281
+ }
252
282
  const CARD_ARGS_COMPARE_MAX_NODES = 20_000;
253
283
  const CARD_ARGS_COMPARE_MAX_DEPTH = 256;
254
284
  const NOT_COMPARABLE = Symbol('not comparable');
@@ -519,6 +549,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
519
549
  : {}),
520
550
  ...(argsRewritten ? { argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : {}),
521
551
  ...(argsFromModelCall ? { argsUnavailable: true, wireNote: renderMessage(msg('approval.park.argsFromModelCall')) } : {}),
552
+ ...inputSourceBitOf(reading),
522
553
  ...(pending.governanceForced === true ? { governanceForced: true } : {}),
523
554
  ...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
524
555
  ...approvalLiveFaceOf(pending).facts,
@@ -541,10 +572,12 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
541
572
  allowSessionCarried: true,
542
573
  grantBinding: readApprovalGrantBinding(pending),
543
574
  parkRow: parkRowOf(taskId, pending, gatedCallId),
575
+ ...(typeof taskId === 'string' && taskId !== '' ? { taskId } : {}),
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
583
  return { kind: 'not_sent', ...at, cause: 'edit_refused', detail: PARK_EDIT_REFUSED_REASON };
@@ -558,6 +591,8 @@ 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
598
  const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending);
@@ -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
+ 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(park, '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,12 +950,30 @@ 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
973
  const o = v;
974
+ const accepted = slotReadOf(o, 'accepted');
975
+ if (accepted.presence === 'present' && accepted.value === 'parked_redeemed')
976
+ return readParkedRedeemedAck(o);
861
977
  if (o.idempotent === true)
862
978
  return o.delivery === undefined ? readReplayedAck(o) : undefined;
863
979
  if (o.delivery !== 'applied')
@@ -975,6 +1091,44 @@ function readReplayedAck(o) {
975
1091
  ...(o.rememberApplied === false ? { rememberApplied: false } : {}),
976
1092
  };
977
1093
  }
1094
+ function readParkedRedeemedAck(o) {
1095
+ if (slotReadOf(o, 'delivery').presence !== 'absent')
1096
+ return undefined;
1097
+ const text = (key) => {
1098
+ const slot = slotReadOf(o, key);
1099
+ return slot.presence === 'present' && typeof slot.value === 'string' && slot.value !== '' ? slot.value : undefined;
1100
+ };
1101
+ const approvalId = text('approvalId');
1102
+ const askId = text('askId');
1103
+ if (approvalId === undefined || askId === undefined)
1104
+ return undefined;
1105
+ const idem = slotReadOf(o, 'idempotent');
1106
+ if (idem.presence === 'unreadable')
1107
+ return undefined;
1108
+ const replay = idem.presence === 'present' && idem.value === true;
1109
+ const dec = slotReadOf(o, 'decision');
1110
+ let decision;
1111
+ if (replay) {
1112
+ if (dec.presence !== 'present' || (dec.value !== 'allow' && dec.value !== 'deny'))
1113
+ return undefined;
1114
+ decision = dec.value === 'allow' ? 'allow' : 'deny';
1115
+ }
1116
+ else {
1117
+ decision = dec.presence === 'present' && typeof dec.value === 'string' && dec.value !== '' ? dec.value : undefined;
1118
+ }
1119
+ const remember = slotReadOf(o, 'rememberApplied');
1120
+ return {
1121
+ approvalId,
1122
+ askId,
1123
+ accepted: 'parked_redeemed',
1124
+ ...(replay ? { idempotent: true } : {}),
1125
+ ...(decision !== undefined ? { decision } : {}),
1126
+ ...(remember.presence === 'present' && typeof remember.value === 'boolean' ? { rememberApplied: remember.value } : {}),
1127
+ };
1128
+ }
1129
+ function replayedDecisionOf(first, sent, ack) {
1130
+ return { kind: 'decided', lane: 'respond', decision: first, ...(ack !== undefined ? { ack } : {}), settledElsewhere: toolPermissionSettledElsewhereOf(first, sent) };
1131
+ }
978
1132
  function isNonEmptyStringArray(v) {
979
1133
  return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x !== '');
980
1134
  }
@@ -1080,19 +1234,44 @@ function readProbeReason(v) {
1080
1234
  function usableProbeReason(raw) {
1081
1235
  return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
1082
1236
  }
1237
+ function probeFactsAt(at) {
1238
+ const prose = usableProbeReason(ownDataValue(at, 'probeReason'));
1239
+ let cause;
1240
+ try {
1241
+ const raw = ownDataValue(at, 'probeCause');
1242
+ if (isWireRecordCarrier(raw))
1243
+ cause = raw;
1244
+ }
1245
+ catch {
1246
+ cause = undefined;
1247
+ }
1248
+ return { prose, cause };
1249
+ }
1250
+ function probeCarrierOf(carrier) {
1251
+ let d;
1252
+ try {
1253
+ d = Object.getOwnPropertyDescriptor(carrier, 'riskDescriptor');
1254
+ }
1255
+ catch {
1256
+ return undefined;
1257
+ }
1258
+ if (d === undefined)
1259
+ return carrier;
1260
+ return Object.hasOwn(d, 'value') ? d.value : undefined;
1261
+ }
1083
1262
  const PROBE_CAUSE_CODE_WORD = /^[a-z][a-z0-9_]{0,31}(?:\.[a-z][a-z0-9_]{0,31}){1,3}$/;
1084
1263
  export function askProbeReasonDetail(card, lang) {
1085
- const prose = usableProbeReason(ownDataValue(card, 'probeReason'));
1264
+ const { prose, cause } = probeFactsAt(card);
1086
1265
  const shown = prose === undefined ? '' : displayUntrusted(prose);
1087
- const sentence = probeCauseSentence(ownDataValue(card, 'probeCause'), shown !== '', lang);
1266
+ const sentence = probeCauseSentence(cause, shown !== '', lang);
1088
1267
  if (sentence === undefined)
1089
1268
  return shown === '' ? undefined : shown;
1090
1269
  return shown === '' ? sentence : renderMessage(msg('approval.probe.proseJoin', { sentence, prose: shown }), lang);
1091
1270
  }
1092
1271
  function probeCauseSentence(cause, withProse, lang) {
1272
+ if (cause === undefined)
1273
+ return undefined;
1093
1274
  try {
1094
- if (!isWireRecordCarrier(cause))
1095
- return undefined;
1096
1275
  const code = ownDataValue(cause, 'code');
1097
1276
  const withCode = typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code);
1098
1277
  const key = withProse
@@ -1713,12 +1892,14 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
1713
1892
  if (reading.from === 'recorded') {
1714
1893
  if (blind)
1715
1894
  return { args: reading.args, argsUnavailable: true };
1716
- return reading.rewritten ? { args: reading.args, argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : { args: reading.args };
1895
+ return reading.rewritten
1896
+ ? { args: reading.args, argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE, ...inputSourceBitOf(reading) }
1897
+ : { args: reading.args, ...inputSourceBitOf(reading) };
1717
1898
  }
1718
1899
  if (reading.from === 'stream' && typeof reading.args === 'object' && reading.args !== null) {
1719
1900
  if (blind)
1720
1901
  return { args: reading.args, argsUnavailable: true };
1721
- return { args: reading.args, wireNote: ARGS_FROM_MODEL_CALL_NOTE };
1902
+ return { args: reading.args, wireNote: ARGS_FROM_MODEL_CALL_NOTE, ...inputSourceBitOf(reading) };
1722
1903
  }
1723
1904
  const omitted = c.argsOmitted === true || reading.shape === 'size_marker';
1724
1905
  const fallback = argsFallbackOf(c.message, omitted);
@@ -1749,6 +1930,8 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
1749
1930
  out.argsUnavailable = true;
1750
1931
  if (argsPart.argsRewritten === true)
1751
1932
  out.argsRewritten = true;
1933
+ if (argsPart.inputSource !== undefined)
1934
+ out.inputSource = argsPart.inputSource;
1752
1935
  if (full) {
1753
1936
  v1Bit(() => {
1754
1937
  if (c.governanceForced === true)
@@ -1784,6 +1967,11 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
1784
1967
  if (r !== undefined)
1785
1968
  out.probeReason = r;
1786
1969
  });
1970
+ v1Bit(() => {
1971
+ const by = approvalRequestedByOf(live);
1972
+ if (by !== undefined)
1973
+ out.requestedBy = by;
1974
+ });
1787
1975
  }
1788
1976
  v1Bit(() => {
1789
1977
  if (readApprovalRememberRefused(carrier))
@@ -1928,7 +2116,8 @@ export async function surfaceApprovalFrameWithId(frame, approvalId, respond, str
1928
2116
  if (typeof approvalId !== 'string' || approvalId === '')
1929
2117
  return approvalIdUnreadableRefusal();
1930
2118
  const carriers = openingFrameCarriersOf(frame);
1931
- const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, frame, carriers, run), respond, streamArgs, signal, lane, receivedAtMs };
2119
+ const liveRun = liveCardRunOf(frame, run);
2120
+ const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, carriers, liveRun), respond, streamArgs, signal, lane, receivedAtMs };
1932
2121
  return enterLiveCard(entry);
1933
2122
  }
1934
2123
  function openingFrameCarriersOf(frame) {
@@ -1954,7 +2143,7 @@ function enterLiveCard(e) {
1954
2143
  return joinOtherLaneHolder(e, other);
1955
2144
  return holdLiveCard(e.identity, signal, async (hold) => {
1956
2145
  const slot = {};
1957
- const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
2146
+ const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs, e.identity.taskId);
1958
2147
  noteCallOutcome(slot, out);
1959
2148
  return out;
1960
2149
  });
@@ -1969,15 +2158,20 @@ async function joinOtherLaneHolder(e, other) {
1969
2158
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted', joinedInFlight: true };
1970
2159
  return enterLiveCard(e);
1971
2160
  }
1972
- function liveCardIdentityOf(approvalId, frame, carriers, run) {
2161
+ function liveCardRunOf(frame, run) {
2162
+ const source = frameBindingOf(frame) ?? run;
2163
+ return {
2164
+ taskId: guardedRead(() => readOwnDataNonEmptyString(source, 'taskId')),
2165
+ sessionId: guardedRead(() => readOwnDataNonEmptyString(source, 'sessionId')),
2166
+ };
2167
+ }
2168
+ function liveCardIdentityOf(approvalId, carriers, liveRun) {
1973
2169
  const body = carriers.body;
1974
2170
  const toolCallId = typeof body.toolCallId === 'string' && body.toolCallId !== '' ? body.toolCallId : undefined;
1975
2171
  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;
2172
+ const ownRun = fromSubagent ? subagentSourceSessionOf(carriers.body) : liveRun.taskId;
1979
2173
  const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
1980
- const sessionId = !fromSubagent && typeof source?.sessionId === 'string' && source.sessionId !== '' ? source.sessionId : undefined;
2174
+ const sessionId = fromSubagent ? undefined : liveRun.sessionId;
1981
2175
  const askSig = carrierAskSig(carriers.body, carriers.live);
1982
2176
  return {
1983
2177
  approvalId,
@@ -2033,12 +2227,13 @@ function readApprovalCardClaimSignal(v) {
2033
2227
  export function claimApprovalCard(identity, options) {
2034
2228
  return claimApprovalCardFor(readApprovalHolderIdentity(identity), readApprovalCardClaimSignal(options));
2035
2229
  }
2036
- async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
2230
+ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs, cardTaskId) {
2037
2231
  const bodyToolCallId = carriers.body.toolCallId;
2038
2232
  const frameToolCallId = typeof bodyToolCallId === 'string' && bodyToolCallId !== '' ? bodyToolCallId : undefined;
2039
2233
  const decidedByReading = decidedByReadingSnapshotOf(lane);
2040
2234
  const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live, approvalId, carriers.why);
2041
2235
  const argsNotRealInput = facts.argsUnavailable === true;
2236
+ const editAcceptance = editedApprovalAcceptanceOf(facts);
2042
2237
  const ruleOffers = facts.ruleOffers;
2043
2238
  if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
2044
2239
  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)'}`);
@@ -2046,6 +2241,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2046
2241
  const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, {
2047
2242
  ...facts,
2048
2243
  callKey: liveFrameCallKey(approvalId),
2244
+ ...(cardTaskId !== undefined ? { taskId: cardTaskId } : {}),
2049
2245
  signal: hold.cardSignal,
2050
2246
  ...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs, ...(carriers.leg === 'generic' ? { unknownShape: true } : {}) }),
2051
2247
  });
@@ -2056,11 +2252,13 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2056
2252
  surfaceApprovalParking();
2057
2253
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
2058
2254
  }
2059
- if (argsNotRealInput && card.kind === 'allow' && card.updatedInput !== undefined) {
2255
+ if (editAcceptance.state !== 'accepts' && card.kind === 'allow' && card.updatedInput !== undefined) {
2060
2256
  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
2257
  surfaceEditRefusedOnBlindAsk();
2062
2258
  return { kind: 'not_sent', lane: 'respond', cause: 'edit_refused' };
2063
2259
  }
2260
+ if (card.kind === WITHDRAWN_CARD_DECISION_KIND)
2261
+ return withdrawnCardResolution(card, { lane: 'respond' }, 'liveToolApprovalWire');
2064
2262
  if (card.kind === RETRACTED_CARD_DECISION_KIND) {
2065
2263
  hostLog('debug', `liveToolApprovalWire: ${approvalId} card retracted without a decision${card.reason ? ` (${card.reason})` : ''} — nothing sent`);
2066
2264
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted' };
@@ -2094,8 +2292,10 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2094
2292
  : card;
2095
2293
  const basePayload = answerPayloadFor(answerSnapshot, 'respond', { approvalDecisionNoteCapable: noteCapable });
2096
2294
  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`);
2295
+ const allowNoteText = decision !== 'deny' ? allowNoteReadingOf(answerSnapshot) : { kind: 'absent' };
2296
+ const allowNoteDropped = allowNoteText.kind === 'text' && answerCarriage({ callKey: liveFrameCallKey(approvalId) }).allowNote === 'none';
2297
+ if (allowNoteDropped && allowNoteText.kind === 'text') {
2298
+ 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
2299
  }
2100
2300
  const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
2101
2301
  const denyPolicyCauseForWire = card.kind === 'deny' ? readPolicyDenyCause(card) : undefined;
@@ -2249,15 +2449,15 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2249
2449
  'reporting the answer as NOT settled (never as decided, never as parked); not re-sent');
2250
2450
  return { kind: 'unsettled', lane: 'respond', cause: 'unreadable' };
2251
2451
  }
2252
- const parsed = readToolApprovalRespondAck(raw);
2452
+ const parsed = settlementAckOf(readToolApprovalRespondAck(raw));
2253
2453
  const replayedForThisCard = parsed !== undefined && isToolApprovalRespondReplayed(parsed) && parsed.approvalId === approvalId;
2254
- const claimsReplay = ownIdempotentTrue(raw) || (parsed !== undefined && isToolApprovalRespondReplayed(parsed));
2454
+ const claimsReplay = ownIdempotentTrue(raw) || replayedFirstDecisionOf(parsed) !== undefined;
2255
2455
  if (claimsReplay && !replayedForThisCard) {
2256
2456
  const claimed = replayClaimOf(raw);
2257
2457
  if (claimed.approvalId === approvalId && claimed.decision !== undefined && !claimed.carriesDelivery) {
2258
2458
  hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay (body not in the full replay shape) — the approval was already decided ` +
2259
2459
  `(${claimed.decision}); this answer was not delivered, reporting the earlier decision`);
2260
- return { kind: 'decided', lane: 'respond', decision: claimed.decision };
2460
+ return replayedDecisionOf(claimed.decision, decision, parsed !== undefined && isToolApprovalRespondParkedRedeemed(parsed) && parsed.approvalId === approvalId ? parsed : undefined);
2261
2461
  }
2262
2462
  hostLog('error', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay of an earlier decision that cannot be read or names another approval — ` +
2263
2463
  'reporting the answer as NOT settled (never as the decision this client sent); not re-sent');
@@ -2327,6 +2527,8 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2327
2527
  }
2328
2528
  if (replayed === undefined && sessionDowngraded)
2329
2529
  surfaceRememberNotApplied();
2530
+ if (replayed === undefined && allowNoteDropped)
2531
+ surfaceAllowNoteNotCarried(allowNoteNotCarriedSentence());
2330
2532
  if (respondOpts.updatedInput !== undefined && applied?.updatedInputForwarded === false) {
2331
2533
  logAfterDecided(`liveToolApprovalWire: ${approvalId} edited args were NOT forwarded (ack.updatedInputForwarded=false) — the tool runs on the ORIGINAL input`);
2332
2534
  surfaceEditNotForwarded();
@@ -2338,7 +2540,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2338
2540
  if (replayed !== undefined) {
2339
2541
  hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered with a replay — the approval was already decided (${replayed.decision}); ` +
2340
2542
  'this answer was not delivered, reporting the earlier decision');
2341
- return { kind: 'decided', lane: 'respond', decision: replayed.decision, ack: replayed };
2543
+ return replayedDecisionOf(replayed.decision, decision, replayed);
2342
2544
  }
2343
2545
  const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed, denyPolicyCauseForWire) : undefined;
2344
2546
  return { kind: 'decided', lane: 'respond', decision, ...(ack !== undefined ? { ack } : {}), ...(deny !== undefined ? { deny } : {}) };
@@ -9,7 +9,7 @@ import { admittedLayers, layerAdmissionWord } from './settingsLayerAdmission.js'
9
9
  import { guardedRead, readOwnDataValue } from './ownKey.js';
10
10
  import { HOOK_SETTINGS_SOURCE_TEXT, hooksSalvageMaskOf, salvageSettingsHooksEntries, settingsHooksSalvageNotice } from './settingsHooksSalvage.js';
11
11
  import { submissionOriginOf } from './request/submissionIdentity.js';
12
- import { createSettingsHookJudge, decidePluginHooks, decideSessionHooks, dedupeSettingsHookGroups, filterSettingsHookGroups, hooksWirePlanShapeOf, occupancyOf, } from './pluginHooksWire.js';
12
+ import { createSettingsHookJudge, decidePluginHooks, decideSessionHooks, dedupeSettingsHookGroups, filterSettingsHookGroups, hooksWirePlanShapeOf, isEngineFiredHookEvent, occupancyOf, } from './pluginHooksWire.js';
13
13
  import { MAX_HOOK_NOTICE_TEXT_CHARS } from './notifications.js';
14
14
  import { applyHttpHookPolicy, httpHookAdmittedSources, readHttpHookPolicy, wireHasHttpEntry } from './httpHookPolicy.js';
15
15
  import { goalStopHookMatcher, __resetGoalStopHookForTests } from './goalStopHook.js';
@@ -300,6 +300,32 @@ function withheldRowsOf(candidates, trust, layers) {
300
300
  }
301
301
  return rows;
302
302
  }
303
+ function closedDropRowsOf(source, doc, reason) {
304
+ const rows = [];
305
+ try {
306
+ const hooks = readOwnDataValue(doc, 'hooks');
307
+ if (typeof hooks !== 'object' || hooks === null || Array.isArray(hooks))
308
+ return rows;
309
+ for (const event of Object.keys(hooks)) {
310
+ const groups = readOwnDataValue(hooks, event);
311
+ if (!Array.isArray(groups))
312
+ continue;
313
+ groups.forEach((group, groupIndex) => {
314
+ if (typeof group !== 'object' || group === null)
315
+ return;
316
+ const entries = readOwnDataValue(group, 'hooks');
317
+ if (!Array.isArray(entries))
318
+ return;
319
+ entries.forEach((_entry, hookIndex) => {
320
+ rows.push({ source, event, groupIndex, hookIndex, reason, notRun: true });
321
+ });
322
+ });
323
+ }
324
+ }
325
+ catch {
326
+ }
327
+ return rows;
328
+ }
303
329
  function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppressedLocally, readerNotRead, compactionOnShell = false) {
304
330
  const settingsDropped = [];
305
331
  const settingsVerdicts = [];
@@ -333,6 +359,8 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
333
359
  });
334
360
  const closedPlan = (reason) => {
335
361
  closedBy = reason;
362
+ for (const source of ALL_HOOK_SOURCES)
363
+ settingsDropped.push(...closedDropRowsOf(source, read.layers[source].doc, reason));
336
364
  sessionReader = read.sessionHooks.state;
337
365
  const pr = read.plugins;
338
366
  if (pr.reading === undefined)
@@ -472,6 +500,8 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
472
500
  settingsDropped.push(...kept.drops);
473
501
  settingsVerdicts.push(...kept.verdicts);
474
502
  settingsExcluded.push(...kept.excluded);
503
+ if (!isEngineFiredHookEvent(event))
504
+ continue;
475
505
  settingsStripped += kept.stripped;
476
506
  if (kept.groups.length === 0)
477
507
  continue;