@sema-agent/client-core 0.107.0 → 0.109.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 (165) hide show
  1. package/CHANGELOG.md +239 -0
  2. package/README.md +54 -36
  3. package/dist/adapt/arms.js +23 -8
  4. package/dist/adapt/textStream.d.ts +2 -0
  5. package/dist/adapt/textStream.js +37 -16
  6. package/dist/adapter/activeRunSelfHeal.d.ts +2 -0
  7. package/dist/adapter/activeRunSelfHeal.js +21 -4
  8. package/dist/adapter/downstream/eventToSdkMessage.js +2 -1
  9. package/dist/agentSession/engineAgentRegistryRead.js +2 -0
  10. package/dist/bashExitReading.d.ts +12 -0
  11. package/dist/bashExitReading.js +44 -0
  12. package/dist/checkpointRowVerdict.d.ts +6 -1
  13. package/dist/checkpointRowVerdict.js +30 -2
  14. package/dist/cloudConfigWireCaps.d.ts +10 -0
  15. package/dist/cloudConfigWireCaps.js +33 -0
  16. package/dist/compensations.js +1 -1
  17. package/dist/coreValuePorts.d.ts +17 -1
  18. package/dist/coreValuePorts.js +5 -0
  19. package/dist/decideReceipt.js +2 -1
  20. package/dist/engineAgentAbsence.d.ts +2 -0
  21. package/dist/engineAgentAbsence.js +28 -0
  22. package/dist/engineErrorCodes.d.ts +9 -0
  23. package/dist/engineErrorCodes.js +9 -0
  24. package/dist/engineHealthFacts.d.ts +17 -0
  25. package/dist/engineHealthFacts.js +42 -3
  26. package/dist/engineNoticeCodes.d.ts +147 -1
  27. package/dist/engineNoticeCodes.js +694 -57
  28. package/dist/engineTargetSeat.js +1 -0
  29. package/dist/errorSupplementRoles.js +6 -1
  30. package/dist/eventLeg.js +1 -1
  31. package/dist/executionLaneWords.js +2 -1
  32. package/dist/fleetTaskDesc.js +3 -3
  33. package/dist/gateOutcome.d.ts +10 -1
  34. package/dist/gateOutcome.js +48 -1
  35. package/dist/generated/engineFactTables.d.ts +2 -0
  36. package/dist/generated/engineFactTables.js +11 -0
  37. package/dist/generated/engineNoticeTables.js +6 -0
  38. package/dist/generated/settingsSchemaFns.d.ts +4 -2
  39. package/dist/generated/settingsSchemaFns.js +9 -3
  40. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -1
  41. package/dist/generated/settingsSchemaMcpRequestEntry.js +9 -18
  42. package/dist/generated/settingsSchemaReservedMemberName.d.ts +1 -0
  43. package/dist/generated/settingsSchemaReservedMemberName.js +7 -0
  44. package/dist/generated/settingsSchemaTables.d.ts +2 -0
  45. package/dist/generated/settingsSchemaTables.js +10 -0
  46. package/dist/generated/settingsSchemaWireName.d.ts +1 -0
  47. package/dist/generated/settingsSchemaWireName.js +8 -0
  48. package/dist/headlessReconnectWire.js +12 -7
  49. package/dist/hitl/approvalAskers.d.ts +13 -0
  50. package/dist/hitl/approvalAskers.js +79 -0
  51. package/dist/hitl/approvalCardReasons.js +22 -4
  52. package/dist/hitl/approvalRequestFrameReading.d.ts +14 -0
  53. package/dist/hitl/approvalRequestFrameReading.js +79 -0
  54. package/dist/hitl/approvalResolution.d.ts +3 -0
  55. package/dist/hitl/approvalResolution.js +22 -4
  56. package/dist/hitl/approvalsFeed.js +4 -13
  57. package/dist/hitl/askParkRowRouting.js +10 -3
  58. package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
  59. package/dist/hitl/automaticAnswerVerdict.js +16 -2
  60. package/dist/hitl/decisionConflict.d.ts +2 -1
  61. package/dist/hitl/decisionConflict.js +4 -1
  62. package/dist/hitl/decisionRefusal.d.ts +1 -0
  63. package/dist/hitl/decisionRefusal.js +5 -2
  64. package/dist/hitl/decisionSend.js +2 -6
  65. package/dist/hitl/frameRouter.d.ts +3 -2
  66. package/dist/hitl/frameRouter.js +37 -7
  67. package/dist/hitl/gateLedger.d.ts +2 -0
  68. package/dist/hitl/gateLedger.js +3 -1
  69. package/dist/hitl/hitlBridge.d.ts +4 -0
  70. package/dist/hitl/hitlBridge.js +11 -8
  71. package/dist/hitl/livePendingAsk.js +17 -16
  72. package/dist/hitl/localAllowRule.js +2 -1
  73. package/dist/hitl/noAllowCause.d.ts +1 -1
  74. package/dist/hitl/noAllowCause.js +4 -2
  75. package/dist/hitl/parkExpiryJudgements.js +26 -1
  76. package/dist/hitl/parkOwnership.d.ts +2 -0
  77. package/dist/hitl/parkOwnership.js +48 -8
  78. package/dist/hitl/parkResolver.js +39 -30
  79. package/dist/hitl/parkRowBirthWait.d.ts +2 -0
  80. package/dist/hitl/parkRowBirthWait.js +8 -1
  81. package/dist/hitl/parkedUnresumable.d.ts +13 -0
  82. package/dist/hitl/parkedUnresumable.js +80 -0
  83. package/dist/hitl/permissionRuleScope.d.ts +4 -0
  84. package/dist/hitl/permissionRuleScope.js +31 -0
  85. package/dist/hitl/persistedRulesWire.d.ts +7 -1
  86. package/dist/hitl/persistedRulesWire.js +25 -8
  87. package/dist/hitl/planReviewWire.js +12 -16
  88. package/dist/hitl/policyDenyCause.d.ts +3 -0
  89. package/dist/hitl/policyDenyCause.js +16 -0
  90. package/dist/hitl/sessionAllowRoute.js +5 -4
  91. package/dist/hitl/sessionPolicyDeliverable.js +1 -1
  92. package/dist/hitl/standingRows.d.ts +1 -5
  93. package/dist/hitl/standingRows.js +15 -14
  94. package/dist/hitl/toolApprovalWire.d.ts +16 -4
  95. package/dist/hitl/toolApprovalWire.js +57 -17
  96. package/dist/hooksReaderGenerations.d.ts +1 -0
  97. package/dist/hooksReaderGenerations.js +12 -0
  98. package/dist/hooksWireCaps.js +12 -7
  99. package/dist/hostSettingsSource.js +2 -0
  100. package/dist/i18n/messages.en.d.ts +206 -1
  101. package/dist/i18n/messages.en.js +209 -4
  102. package/dist/i18n/messages.zh.js +214 -4
  103. package/dist/index.d.ts +35 -8
  104. package/dist/index.js +20 -5
  105. package/dist/liveModelCatalog.d.ts +4 -2
  106. package/dist/liveModelCatalog.js +164 -26
  107. package/dist/localEngineJudge.d.ts +75 -0
  108. package/dist/localEngineJudge.js +242 -0
  109. package/dist/localEngineOccupancy.d.ts +88 -0
  110. package/dist/localEngineOccupancy.js +269 -0
  111. package/dist/localEngineSeatReading.d.ts +29 -0
  112. package/dist/localEngineSeatReading.js +294 -0
  113. package/dist/localEngineSpawn.d.ts +113 -0
  114. package/dist/localEngineSpawn.js +486 -0
  115. package/dist/localEngineStep.d.ts +286 -0
  116. package/dist/localEngineStep.js +1086 -0
  117. package/dist/localEngineStopLadder.d.ts +29 -0
  118. package/dist/localEngineStopLadder.js +139 -0
  119. package/dist/localEngineSwapCard.d.ts +43 -0
  120. package/dist/localEngineSwapCard.js +205 -0
  121. package/dist/localEngineTiming.d.ts +8 -0
  122. package/dist/localEngineTiming.js +8 -0
  123. package/dist/mcpReconnect.js +12 -5
  124. package/dist/mcpWireCaps.js +2 -2
  125. package/dist/memoryEntriesWire.d.ts +1 -1
  126. package/dist/memoryEntriesWire.js +5 -0
  127. package/dist/model/catalogDefault.d.ts +25 -0
  128. package/dist/model/catalogDefault.js +44 -0
  129. package/dist/noticePresentation.d.ts +2 -0
  130. package/dist/noticePresentation.js +79 -1
  131. package/dist/ownKey.d.ts +10 -0
  132. package/dist/ownKey.js +15 -1
  133. package/dist/permissionWireCaps.d.ts +1 -0
  134. package/dist/permissionWireCaps.js +11 -0
  135. package/dist/pluginHooksWire.d.ts +5 -3
  136. package/dist/pluginHooksWire.js +116 -7
  137. package/dist/queuedDelivery.d.ts +1 -0
  138. package/dist/queuedDelivery.js +12 -0
  139. package/dist/resultFrameKeys.js +4 -2
  140. package/dist/resumeRefusalCopy.js +15 -8
  141. package/dist/ruleReadings.js +1 -1
  142. package/dist/runPermissionModeSwitch.d.ts +2 -1
  143. package/dist/runPermissionModeSwitch.js +16 -6
  144. package/dist/seam.d.ts +13 -2
  145. package/dist/seam.js +8 -1
  146. package/dist/seatContract.d.ts +103 -12
  147. package/dist/seatContract.js +344 -14
  148. package/dist/sessionBackground.js +2 -0
  149. package/dist/sessionEngine.d.ts +2 -1
  150. package/dist/sessionEngine.js +5 -0
  151. package/dist/settingsHooksSalvage.d.ts +5 -0
  152. package/dist/settingsHooksSalvage.js +54 -0
  153. package/dist/subagentContentStore.d.ts +12 -0
  154. package/dist/subagentContentStore.js +73 -12
  155. package/dist/supportFloor.js +2 -0
  156. package/dist/taskNotificationAdmission.d.ts +19 -0
  157. package/dist/taskNotificationAdmission.js +84 -0
  158. package/dist/toolResult.js +20 -6
  159. package/dist/webSearchWireCaps.js +9 -4
  160. package/dist/wireFailureShape.d.ts +20 -0
  161. package/dist/wireFailureShape.js +37 -6
  162. package/dist/wireRefusalCopy.d.ts +23 -2
  163. package/dist/wireRefusalCopy.js +93 -4
  164. package/docs/INTEGRATION-CLIENTS.md +1959 -15
  165. package/package.json +4 -4
@@ -0,0 +1,14 @@
1
+ export declare const APPROVAL_REQUEST_FRAME_UNREADABLE_WHYS: readonly ["version_unknown", "kind_unknown", "unreadable_fields"];
2
+ export type ApprovalRequestFrameUnreadableWhy = (typeof APPROVAL_REQUEST_FRAME_UNREADABLE_WHYS)[number];
3
+ export type ApprovalRequestFrameReading = {
4
+ readonly v1: true;
5
+ } | {
6
+ readonly v1: false;
7
+ readonly why: ApprovalRequestFrameUnreadableWhy;
8
+ readonly schemaVersion?: string;
9
+ readonly kind?: string;
10
+ };
11
+ export declare function approvalRequestFrameReadingThrowing(frame: object | null | undefined): ApprovalRequestFrameReading;
12
+ export declare function approvalRequestFrameReadingOf(frame: object | null | undefined): ApprovalRequestFrameReading;
13
+ export declare function approvalUnreadableWhyWordOf(v: string): ApprovalRequestFrameUnreadableWhy | undefined;
14
+ export declare function approvalFrameUnreadableWhyOf(reading: ApprovalRequestFrameReading | null | undefined): ApprovalRequestFrameUnreadableWhy | undefined;
@@ -0,0 +1,79 @@
1
+ import { isApprovalRequestFrameV1 } from '@sema-agent/sdk';
2
+ import { displayUntrusted } from '../displayUntrusted.js';
3
+ export const APPROVAL_REQUEST_FRAME_UNREADABLE_WHYS = Object.freeze(['version_unknown', 'kind_unknown', 'unreadable_fields']);
4
+ const KNOWN_FRAME_TYPE = 'approval_request';
5
+ const KNOWN_SCHEMA_VERSION = 1;
6
+ const KNOWN_KIND = 'permission';
7
+ const FRAME_READING_VALUE_MAX = 24;
8
+ const FRAME_READING_VALUE_PREFIX = 128;
9
+ const V1_READING = Object.freeze({ v1: true });
10
+ const UNREADABLE_READING = Object.freeze({ v1: false, why: 'unreadable_fields' });
11
+ function displayableScalar(v) {
12
+ let raw;
13
+ if (typeof v === 'string')
14
+ raw = v.length > FRAME_READING_VALUE_PREFIX ? v.slice(0, FRAME_READING_VALUE_PREFIX) : v;
15
+ else if (typeof v === 'number' && Number.isFinite(v))
16
+ raw = String(v);
17
+ else
18
+ return undefined;
19
+ const shown = displayUntrusted(raw, { max: FRAME_READING_VALUE_MAX });
20
+ return shown === '' ? undefined : shown;
21
+ }
22
+ export function approvalRequestFrameReadingThrowing(frame) {
23
+ if (typeof frame !== 'object' || frame === null)
24
+ return UNREADABLE_READING;
25
+ const x = frame;
26
+ const type = x.type;
27
+ const schemaVersion = x.schemaVersion;
28
+ const kind = x.kind;
29
+ const pinned = Object.create(frame, {
30
+ type: { value: type, enumerable: true },
31
+ schemaVersion: { value: schemaVersion, enumerable: true },
32
+ kind: { value: kind, enumerable: true },
33
+ });
34
+ if (isApprovalRequestFrameV1(pinned))
35
+ return V1_READING;
36
+ const why = type !== KNOWN_FRAME_TYPE
37
+ ? 'unreadable_fields'
38
+ : schemaVersion !== KNOWN_SCHEMA_VERSION
39
+ ? 'version_unknown'
40
+ : kind !== KNOWN_KIND
41
+ ? 'kind_unknown'
42
+ : 'unreadable_fields';
43
+ const shownVersion = displayableScalar(schemaVersion);
44
+ const shownKind = displayableScalar(kind);
45
+ return Object.freeze({
46
+ v1: false,
47
+ why,
48
+ ...(shownVersion !== undefined ? { schemaVersion: shownVersion } : {}),
49
+ ...(shownKind !== undefined ? { kind: shownKind } : {}),
50
+ });
51
+ }
52
+ export function approvalRequestFrameReadingOf(frame) {
53
+ try {
54
+ return approvalRequestFrameReadingThrowing(frame);
55
+ }
56
+ catch {
57
+ return UNREADABLE_READING;
58
+ }
59
+ }
60
+ export function approvalUnreadableWhyWordOf(v) {
61
+ return APPROVAL_REQUEST_FRAME_UNREADABLE_WHYS.find((w) => w === v);
62
+ }
63
+ export function approvalFrameUnreadableWhyOf(reading) {
64
+ try {
65
+ if (typeof reading !== 'object' || reading === null)
66
+ return undefined;
67
+ const v1 = Object.getOwnPropertyDescriptor(reading, 'v1');
68
+ const why = Object.getOwnPropertyDescriptor(reading, 'why');
69
+ if (v1 === undefined || !Object.hasOwn(v1, 'value') || v1.value !== false)
70
+ return undefined;
71
+ if (why === undefined || !Object.hasOwn(why, 'value'))
72
+ return undefined;
73
+ const word = why.value;
74
+ return typeof word === 'string' ? approvalUnreadableWhyWordOf(word) : undefined;
75
+ }
76
+ catch {
77
+ return undefined;
78
+ }
79
+ }
@@ -1,6 +1,7 @@
1
1
  import type { DecideReceiptView } from '../decideReceipt.js';
2
2
  import { type HitlSafetyCode } from './hitlBridge.js';
3
3
  import type { AskAnsweredOutput } from './gateLedger.js';
4
+ import type { PolicyDenyCause } from './policyDenyCause.js';
4
5
  import type { ApprovalDenySettledBy, ToolApprovalRespondAckView, ToolApprovalRespondDecision, ToolApprovalRespondParkingView } from './toolApprovalWire.js';
5
6
  import { type DecisionRefusalReading, type RefusalRouteContext } from './decisionRefusal.js';
6
7
  import { type DeliveredReading, type LadderSafetyStop } from './attributionLadder.js';
@@ -14,6 +15,7 @@ export type ApprovalUnsettledCause = (typeof APPROVAL_UNSETTLED_CAUSES)[number];
14
15
  export interface ApprovalDenyFacts {
15
16
  readonly settledBy?: ApprovalDenySettledBy;
16
17
  readonly reason?: string;
18
+ readonly policyCause?: PolicyDenyCause;
17
19
  readonly attributionKey?: 'dropped' | 'renamed';
18
20
  }
19
21
  interface ApprovalResolutionBase {
@@ -53,6 +55,7 @@ export declare function approvalUnsettledCause(r: ApprovalResolution): ApprovalU
53
55
  export declare const APPROVAL_NOTE_RETRACTED_DETAIL = "the card was retracted (this host no longer has a decision port for it)";
54
56
  export declare const APPROVAL_NOTE_EDIT_REFUSED_DETAIL = "edits are not accepted on this card";
55
57
  export declare function approvalResolutionDetail(r: ApprovalResolution): string | undefined;
58
+ export declare function approvalResolutionStatusTag(r: ApprovalResolution): string;
56
59
  export declare function unsettledCauseOfReading(reading: DecisionRefusalReading): 'engine_refused' | 'engine_error' | 'no_answer';
57
60
  export interface DecisionFailureMint {
58
61
  readonly lane: ApprovalResolutionLane;
@@ -1,11 +1,10 @@
1
1
  import { DecideTransportRetryExhaustedError, refusalCarrierOf, safetyStopHeadOf, safetyStopNamed } from './hitlBridge.js';
2
- import { mintDecisionRefusalReading } from './decisionRefusal.js';
2
+ import { decisionStatusTag, mintDecisionRefusalReading } from './decisionRefusal.js';
3
3
  import { isSlotCarrier, thrownIsInstanceOf } from '../wireFailureShape.js';
4
4
  import { carrierReadOf, isLocalSafetyStop, localCodeReadOf, topCauseReadOf } from './attributionLadder.js';
5
5
  import { directFailureReadingOf, transportReadingOf } from './approvalDelivery.js';
6
6
  import { ownReadingSlot } from '../engineWireFor.js';
7
- import { CHECKPOINT_ROW_BAR_SENTENCE } from '../checkpointRowVerdict.js';
8
- import { msg, renderMessage } from '../i18n/translate.js';
7
+ import { CHECKPOINT_ROW_BAR_SENTENCE, unboundWayOutContent } from '../checkpointRowVerdict.js';
9
8
  export const APPROVAL_RESOLUTION_LANES = Object.freeze(['respond', 'decide', 'ask']);
10
9
  export const APPROVAL_NOT_SENT_CAUSES = Object.freeze([
11
10
  'retracted',
@@ -95,7 +94,7 @@ export function approvalResolutionDetail(r) {
95
94
  const code = cause === 'safety_stop' ? ownStr(r, 'safetyCode') : undefined;
96
95
  if (code === undefined)
97
96
  return sentence;
98
- return code === 'binding_missing' ? `${sentence} (${code}) · ${renderMessage(msg('approval.decide.unboundWayOut'))}` : `${sentence} (${code})`;
97
+ return code === 'binding_missing' ? `${sentence} (${code}) · ${unboundWayOutContent('unknown')}` : `${sentence} (${code})`;
99
98
  }
100
99
  if (kind !== 'unsettled')
101
100
  return undefined;
@@ -110,6 +109,25 @@ export function approvalResolutionDetail(r) {
110
109
  const parts = [ownStr(refusal, 'errorCode'), ownStr(r, 'detail') ?? ownStr(refusal, 'message')].filter((x) => x !== undefined);
111
110
  return parts.length > 0 ? parts.join(' · ') : undefined;
112
111
  }
112
+ export function approvalResolutionStatusTag(r) {
113
+ try {
114
+ if (typeof r !== 'object' || r === null)
115
+ return '';
116
+ const kd = Object.getOwnPropertyDescriptor(r, 'kind');
117
+ if (kd === undefined || !Object.hasOwn(kd, 'value') || kd.value !== 'unsettled')
118
+ return '';
119
+ const rd = Object.getOwnPropertyDescriptor(r, 'refusal');
120
+ const refusal = rd !== undefined && Object.hasOwn(rd, 'value') ? rd.value : undefined;
121
+ if (typeof refusal !== 'object' || refusal === null)
122
+ return '';
123
+ const sd = Object.getOwnPropertyDescriptor(refusal, 'status');
124
+ const status = sd !== undefined && Object.hasOwn(sd, 'value') ? sd.value : undefined;
125
+ return typeof status === 'number' && Number.isInteger(status) && status >= 400 && status <= 599 ? decisionStatusTag(status) : '';
126
+ }
127
+ catch {
128
+ return '';
129
+ }
130
+ }
113
131
  export function unsettledCauseOfReading(reading) {
114
132
  const standing = ownReadingSlot(reading, 'standing');
115
133
  if (standing === 'ambiguous')
@@ -1,4 +1,5 @@
1
1
  import { hostLog } from '../host.js';
2
+ import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
2
3
  import { unrefTimer } from '../unrefTimer.js';
3
4
  import { readLivePendingRows } from './livePendingAsk.js';
4
5
  import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
@@ -50,7 +51,7 @@ function readDurablePendingRows(raw) {
50
51
  }
51
52
  function durableDigestOf(rows) {
52
53
  return rows
53
- .map(r => [r.sessionId ?? '', r.taskId ?? '', r.toolCallId ?? '', r.boundCallId ?? '', r.boundInputHash ?? '', r.toolName ?? '', JSON.stringify(checkpointRowVerdictOf(r) ?? null), JSON.stringify(parkRowDelegationLinksOf(r) ?? null)]
54
+ .map(r => [r.sessionId ?? '', r.taskId ?? '', r.toolCallId ?? '', r.boundCallId ?? '', r.boundInputHash ?? '', r.toolName ?? '', r.gateKind ?? '', JSON.stringify(checkpointRowVerdictOf(r) ?? null), JSON.stringify(parkRowDelegationLinksOf(r) ?? null)]
54
55
  .join(''))
55
56
  .sort()
56
57
  .join('');
@@ -176,12 +177,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
176
177
  const at = Date.now();
177
178
  blind = { why, at };
178
179
  stats.unknowns++;
179
- try {
180
- onSnapshot({ kind: 'unknown', why, at, mode: via });
181
- }
182
- catch (e) {
183
- hostLog('debug', `approvalsFeed: subscriber threw on the unknown arm (swallowed): ${String(e)}`);
184
- }
180
+ invokeHostCallbackIsolated('approvalsFeed: subscriber (unknown arm)', () => onSnapshot({ kind: 'unknown', why, at, mode: via }));
185
181
  }
186
182
  async function take(via, leg) {
187
183
  if (stopped)
@@ -293,12 +289,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
293
289
  };
294
290
  last = snap;
295
291
  stats.snapshots++;
296
- try {
297
- onSnapshot(snap);
298
- }
299
- catch (e) {
300
- hostLog('debug', `approvalsFeed: subscriber threw (swallowed): ${String(e)}`);
301
- }
292
+ invokeHostCallbackIsolated('approvalsFeed: subscriber', () => onSnapshot(snap));
302
293
  }
303
294
  function schedulePoll() {
304
295
  if (stopped || ac.signal.aborted)
@@ -1,7 +1,7 @@
1
1
  import { askParkForeignGateKind, durableDecideLegOf } from '../adapter/activeRunSelfHeal.js';
2
2
  import { isAskTool, questionCardVerdict } from './frameRouter.js';
3
3
  import { approvalCallKey } from './gateIdentity.js';
4
- import { askQuestionsFromPending, pendingRowIsOwnedByThisSession } from './parkOwnership.js';
4
+ import { askQuestionsFromPending, pendingRowIsOwnedByThisSession, pendingRowOwnershipOf } from './parkOwnership.js';
5
5
  import { approvalQueueRowFamilyOf, checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
6
6
  import { checkpointRowBarOf, checkpointRowBarSentence } from '../checkpointRowVerdict.js';
7
7
  import { msg, renderMessage } from '../i18n/translate.js';
@@ -37,6 +37,7 @@ export function classifyAskParkRows(queueRows, taskId, deps) {
37
37
  kind: 'settled',
38
38
  reason: renderMessage(msg('approval.rowUnresumable.askNotSurfaced', { taskId: String(pending.taskId), sentence: checkpointRowBarSentence(bar) })),
39
39
  rowVerdict: bar,
40
+ rowTaskId: askParkRowTaskIdOf(taskId, pending),
40
41
  };
41
42
  }
42
43
  if (foreignKind !== null) {
@@ -50,8 +51,11 @@ export function classifyAskParkRows(queueRows, taskId, deps) {
50
51
  function askParkRowGateKind(row) {
51
52
  return typeof row.gateKind === 'string' && row.gateKind.length > 0 ? row.gateKind : null;
52
53
  }
54
+ function askParkRowTaskIdOf(taskId, row) {
55
+ return typeof row.taskId === 'string' && row.taskId.length > 0 ? row.taskId : taskId;
56
+ }
53
57
  export function askParkRowIdentity(taskId, row) {
54
- const rowTaskId = typeof row.taskId === 'string' && row.taskId.length > 0 ? row.taskId : taskId;
58
+ const rowTaskId = askParkRowTaskIdOf(taskId, row);
55
59
  const gatedCallId = row.toolCallId ?? row.boundCallId ?? undefined;
56
60
  return { rowTaskId, gatedCallId, armedKey: approvalCallKey(gatedCallId, rowTaskId) };
57
61
  }
@@ -157,8 +161,11 @@ export function countOwnedPendingRows(listed, deps) {
157
161
  throw new Error('approvals.list answered with a pending row that is not an object — the owned pending count is unknown');
158
162
  }
159
163
  const family = approvalQueueRowFamilyOf(row);
160
- if (family === 'unreadable')
164
+ if (family === 'unreadable') {
165
+ if (pendingRowOwnershipOf(row, deps) === 'foreign')
166
+ continue;
161
167
  throw new Error('approvals.list answered with a pending row whose family (`kind`) cannot be read — the owned pending count is unknown');
168
+ }
162
169
  if (family !== 'checkpoint')
163
170
  continue;
164
171
  if (pendingRowIsOwnedAndDecidable(row, deps))
@@ -1,13 +1,15 @@
1
1
  import { type CoreModeArbitrationRow } from '../generated/engineFactTables.js';
2
2
  import { CORE_ASK_FACES } from '../generated/engineAskFaceTables.js';
3
3
  import type { UiLanguage } from '../env/uiLanguage.js';
4
+ import type { PermissionRuleValue } from '@sema-agent/agent-types';
4
5
  export type AutomaticAnswerLane = 'mode_posture' | 'standing_rule' | 'permission_request_hook';
5
6
  export type AutomaticAnswerAskFace = (typeof CORE_ASK_FACES)[number];
6
7
  export interface AutomaticAnswerFacts {
7
8
  readonly settingsAskRuleMatched?: boolean;
9
+ readonly allowRule?: PermissionRuleValue;
8
10
  }
9
11
  export type AutomaticAnswerRoad = 'origin' | 'requires_real_approval' | 'governance' | 'mandate_word' | 'mandated' | 'allow_rule_shadow' | 'ask_class' | 'settings_ask_rule';
10
- export type AutomaticAnswerUnknownWhy = 'card_unreadable' | 'lane_not_recognised' | 'classes_malformed' | 'classes_absent' | 'class_not_in_table' | 'host_not_reported';
12
+ export type AutomaticAnswerUnknownWhy = 'card_unreadable' | 'lane_not_recognised' | 'classes_malformed' | 'classes_absent' | 'class_not_in_table' | 'host_not_reported' | 'answer_may_read_arguments';
11
13
  export type AutomaticAnswerVerdict = {
12
14
  readonly kind: 'must_ask';
13
15
  readonly road: AutomaticAnswerRoad;
@@ -22,6 +24,7 @@ export type AutomaticAnswerVerdict = {
22
24
  };
23
25
  export declare const MODE_ARBITRATION: Readonly<Record<"hook" | "write_protection" | "mode" | "target_unresolved" | "content_question" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "org_rule" | "rule_unreadable" | "ask_rule" | "denial_limit_fallback" | "classifier_no_ruling" | "requires_user_interaction" | "cross_boundary" | "operator_forced" | "shell_floor" | "probe" | "write_protection_on_disk" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", CoreModeArbitrationRow>>;
24
26
  export declare function foldModeArbitrationRow(classes: Iterable<string>, askFace?: AutomaticAnswerAskFace): CoreModeArbitrationRow;
27
+ export declare function askLevelVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
25
28
  export declare function automaticAnswerVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
26
29
  export declare function mandatedApprovalDetailOf(card: object | null | undefined, lang?: UiLanguage): string;
27
30
  export interface CrossBoundaryTaintReading {
@@ -29,5 +32,5 @@ export interface CrossBoundaryTaintReading {
29
32
  }
30
33
  export declare function crossBoundaryTaintOf(carrier: object | null | undefined): CrossBoundaryTaintReading | undefined;
31
34
  export declare function crossBoundaryTaintWayOutOf(carrier: object | null | undefined, lang?: UiLanguage): string | undefined;
32
- export type PermissionRequestHookScope = 'allow_or_deny' | 'deny_only' | 'skip';
35
+ export type PermissionRequestHookScope = 'deny_only' | 'skip';
33
36
  export declare function permissionRequestHookScope(verdict: AutomaticAnswerVerdict | null | undefined): PermissionRequestHookScope;
@@ -3,6 +3,7 @@ import { CORE_RELEASABLE_ASK_CLASSES_BY_FACE, CORE_SHELL_FACE_TOOL_NAMES } from
3
3
  import { mandatedApprovalDetail } from '../gateVocabulary.js';
4
4
  import { msg, renderMessage } from '../i18n/translate.js';
5
5
  import { guardedRead, readAskOrigin, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
6
+ import { allowRuleScopeOf } from './permissionRuleScope.js';
6
7
  import { readAskClassPairState } from './askClassPair.js';
7
8
  import { readRequiresRealApproval } from './approvalCardReading.js';
8
9
  import { approvalAllowRuleShadowed, retiredAllowRuleShadowKeyPresent } from './allowRuleShadow.js';
@@ -78,6 +79,7 @@ const UNKNOWN = Object.freeze({
78
79
  classes_absent: Object.freeze({ kind: 'unknown', why: 'classes_absent' }),
79
80
  class_not_in_table: Object.freeze({ kind: 'unknown', why: 'class_not_in_table' }),
80
81
  host_not_reported: Object.freeze({ kind: 'unknown', why: 'host_not_reported' }),
82
+ answer_may_read_arguments: Object.freeze({ kind: 'unknown', why: 'answer_may_read_arguments' }),
81
83
  });
82
84
  const MAY_ANSWER = Object.freeze({ kind: 'may_answer' });
83
85
  function evidenceView(card, ...keys) {
@@ -142,7 +144,7 @@ function readHostAskRuleMatched(facts) {
142
144
  const v = guardedRead(() => readOwnDataValue(facts, 'settingsAskRuleMatched'));
143
145
  return typeof v === 'boolean' ? v : undefined;
144
146
  }
145
- export function automaticAnswerVerdict(card, lane, facts) {
147
+ export function askLevelVerdict(card, lane, facts) {
146
148
  if (lane !== 'mode_posture' && lane !== 'standing_rule' && lane !== 'permission_request_hook')
147
149
  return UNKNOWN.lane_not_recognised;
148
150
  try {
@@ -214,6 +216,18 @@ export function automaticAnswerVerdict(card, lane, facts) {
214
216
  return UNKNOWN.card_unreadable;
215
217
  }
216
218
  }
219
+ function reportedWholeToolAllowRule(facts) {
220
+ if (typeof facts !== 'object' || facts === null)
221
+ return false;
222
+ const raw = guardedRead(() => readOwnDataValue(facts, 'allowRule'));
223
+ return allowRuleScopeOf(typeof raw === 'object' ? raw : undefined) === 'whole_tool';
224
+ }
225
+ export function automaticAnswerVerdict(card, lane, facts) {
226
+ const asked = askLevelVerdict(card, lane, facts);
227
+ if (asked.kind !== 'may_answer' || lane === 'mode_posture')
228
+ return asked;
229
+ return lane === 'standing_rule' && reportedWholeToolAllowRule(facts) ? asked : UNKNOWN.answer_may_read_arguments;
230
+ }
217
231
  export function mandatedApprovalDetailOf(card, lang) {
218
232
  try {
219
233
  if (typeof card !== 'object' || card === null)
@@ -276,7 +290,7 @@ export function crossBoundaryTaintWayOutOf(carrier, lang) {
276
290
  export function permissionRequestHookScope(verdict) {
277
291
  try {
278
292
  const kind = typeof verdict === 'object' && verdict !== null ? verdict.kind : undefined;
279
- return kind === 'may_answer' ? 'allow_or_deny' : kind === 'must_ask' ? 'skip' : 'deny_only';
293
+ return kind === 'must_ask' ? 'skip' : 'deny_only';
280
294
  }
281
295
  catch {
282
296
  return 'deny_only';
@@ -3,5 +3,6 @@ import type { CapturedCode } from '../engineWireFor.js';
3
3
  import { type ProtocolErrorNameReading } from '../wireFailureShape.js';
4
4
  export type DecisionRefusalConflict = 'binding_mismatch' | 'row_integrity' | 'format_version' | 'answer_invalid' | 'grant_changed';
5
5
  export declare function decisionConflictOf(err: unknown, captured?: CapturedCode, protocolNameRead?: ProtocolErrorNameReading): DecisionRefusalConflict | undefined;
6
- export declare const DECISION_ANSWER_INVALID_CONTENT = "The engine rejected the answer itself because it does not fit the question that was asked \u00B7 Nothing was decided and the question is still waiting \u00B7 Correct the answer and send it again";
6
+ export declare function decisionAnswerInvalidContent(lang?: UiLanguage): string;
7
+ export declare const DECISION_ANSWER_INVALID_CONTENT: string;
7
8
  export declare function decisionGrantChangedContent(lang?: UiLanguage): string;
@@ -45,7 +45,10 @@ export function decisionConflictOf(err, captured, protocolNameRead) {
45
45
  }
46
46
  return BINDING_SEAT_FIELDS.has(field) ? 'binding_mismatch' : undefined;
47
47
  }
48
- export const DECISION_ANSWER_INVALID_CONTENT = 'The engine rejected the answer itself because it does not fit the question that was asked · Nothing was decided and the question is still waiting · Correct the answer and send it again';
48
+ export function decisionAnswerInvalidContent(lang) {
49
+ return renderMessage(msg('approval.decision.answerInvalid'), lang);
50
+ }
51
+ export const DECISION_ANSWER_INVALID_CONTENT = decisionAnswerInvalidContent();
49
52
  export function decisionGrantChangedContent(lang) {
50
53
  return renderMessage(msg('approval.decision.grantChanged'), lang);
51
54
  }
@@ -78,5 +78,6 @@ export interface UndeliverableDecisionFacts {
78
78
  readonly end: UndeliverableDecisionEnd;
79
79
  readonly runId?: string;
80
80
  }
81
+ export declare function decisionStatusTag(status: number, errorCode?: string): string;
81
82
  export declare function undeliverableDecisionContent(facts: UndeliverableDecisionFacts): string;
82
83
  export declare function decideRowUnrevivableNotice(lang?: UiLanguage | (string & {})): string;
@@ -314,9 +314,12 @@ export function decisionRefusalClosesOut(refusal, pending) {
314
314
  return false;
315
315
  return standing === 'ambiguous' || standing === 'refused-pending';
316
316
  }
317
+ export function decisionStatusTag(status, errorCode) {
318
+ const code = typeof errorCode === 'string' && isDisplayableWireCode(errorCode) ? ` ${errorCode}` : '';
319
+ return `(HTTP ${String(status)}${code})`;
320
+ }
317
321
  export function undeliverableDecisionContent(facts) {
318
- const code = typeof facts.errorCode === 'string' && isDisplayableWireCode(facts.errorCode) ? ` ${facts.errorCode}` : '';
319
- const head = `The engine refused the approval decision this client sent (HTTP ${String(facts.status)}${code}), so the call it asked about could not be answered from here and did not run`;
322
+ const head = `The engine refused the approval decision this client sent ${decisionStatusTag(facts.status, facts.errorCode)}, so the call it asked about could not be answered from here and did not run`;
320
323
  const runId = typeof facts.runId === 'string' && isDisplayableWireCode(facts.runId) ? ` ${facts.runId}` : '';
321
324
  switch (facts.end) {
322
325
  case 'run-stopped':
@@ -1,5 +1,6 @@
1
1
  import { settleWithinDeadline } from '../abortableSleep.js';
2
2
  import { hostLog } from '../host.js';
3
+ import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
3
4
  import { msg, renderMessage } from '../i18n/translate.js';
4
5
  import { askGoneFromError } from '../decideReceipt.js';
5
6
  import { appendOwnElement, guardedRead, readOwnDataNonEmptyString, readOwnDataValue } from '../ownKey.js';
@@ -343,12 +344,7 @@ function holdFits(handle, req) {
343
344
  }
344
345
  }
345
346
  function callHold(call) {
346
- try {
347
- call();
348
- }
349
- catch (e) {
350
- hostLog('debug', `decisionSend.callHold: ${String(e).slice(0, 120)}`);
351
- }
347
+ invokeHostCallbackIsolated('decisionSend: hold verb', call);
352
348
  }
353
349
  function hostField(holder, key) {
354
350
  if (typeof holder !== 'object' || holder === null)
@@ -1,8 +1,8 @@
1
1
  import type { AgentEvent } from '@sema-agent/sdk';
2
2
  import type { DecideOperationObserver, HitlClientLike } from './hitlBridge.js';
3
3
  import { type RespondToolApprovalFn, type ToolApprovalFrame, type ToolApprovalFrameLaneOpts } from './toolApprovalWire.js';
4
- import type { ApprovalResolution } from './approvalResolution.js';
5
- import type { GateLedger } from './gateLedger.js';
4
+ import type { ApprovalDenyFacts, ApprovalResolution } from './approvalResolution.js';
5
+ import type { GateLedger, GateDenyAttribution } from './gateLedger.js';
6
6
  import type { NoAllowCause } from './noAllowCause.js';
7
7
  import { type ReplayedApprovalDisposition } from './replayedApproval.js';
8
8
  import type { ReattachDrainingEvent } from '../runReattachPolicy.js';
@@ -17,6 +17,7 @@ export interface PolicyDenyReasonFacts {
17
17
  readonly budgetSetting?: string;
18
18
  }
19
19
  export declare function policyDenyReason(facts: PolicyDenyReasonFacts): string;
20
+ export declare function denyAttributionOf(deny: ApprovalDenyFacts | undefined): GateDenyAttribution;
20
21
  export declare const HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool use]";
21
22
  export declare const ENGINE_ABORT_TOOL_RESULT = "Operation aborted";
22
23
  export interface ParkReattachNotice {
@@ -13,6 +13,8 @@ import { surfaceForCurrentSession, surfaceReplayedApprovalListUnreadable } from
13
13
  import { liveCardHoldOf, noteEngineClosedApproval, settleEngineClosedApproval } from './approvalHolds.js';
14
14
  import { approvalCompleteParked } from './approvalCompleteParked.js';
15
15
  import { liveFrameCallKey } from './gateIdentity.js';
16
+ import { readPolicyDenyCause } from './policyDenyCause.js';
17
+ import { msg, renderMessage } from '../i18n/translate.js';
16
18
  import { SEMA_DENIAL_KIND_KEY, hasLedgerPositionKey } from './gateLedger.js';
17
19
  import { replayedApprovalClosesCardFrame, replayedApprovalDisposition, settledApprovalDisposition } from './replayedApproval.js';
18
20
  import { settleWithinDeadline } from '../abortableSleep.js';
@@ -30,7 +32,11 @@ export function policyDenyReason(facts) {
30
32
  const one = typeof facts.toolName === 'string' && SENTENCE_TOOL_NAME.test(facts.toolName) ? `this one "${facts.toolName}" call` : 'this one call';
31
33
  const flag = typeof facts.allowRuleFlag === 'string' && SENTENCE_TOOL_NAME.test(facts.allowRuleFlag.replace(/^--?/, '')) ? ` (or ${facts.allowRuleFlag})` : '';
32
34
  const budgetKnob = typeof facts.budgetSetting === 'string' && SENTENCE_TOOL_NAME.test(facts.budgetSetting) ? facts.budgetSetting : undefined;
33
- const cause = facts.cause === 'no_allow_rule' || facts.cause === 'ask_rule' || facts.cause === 'engine_ask_rule' || facts.cause === 'budget' ? facts.cause : 'other';
35
+ const cause = facts.cause === 'no_allow_rule' || facts.cause === 'ask_rule' || facts.cause === 'engine_ask_rule' || facts.cause === 'budget' || facts.cause === 'whole_tool_rule_only'
36
+ ? facts.cause
37
+ : 'other';
38
+ const wholeToolOnly = 'this client does not approve calls from the arguments shown to it, so here only an allow rule for the whole tool can approve calls like this ahead of time — ' +
39
+ 'a rule that names paths, commands or other arguments applies only where the engine checks it against the real arguments';
34
40
  if (facts.lane === 'dont_ask') {
35
41
  const mode = 'denied by the dontAsk permission mode';
36
42
  switch (cause) {
@@ -42,6 +48,8 @@ export function policyDenyReason(facts) {
42
48
  return `${mode}: a rule on the engine side, not one of the user's permission rules, asks about ${one}, and this mode refuses anything that would ask — the user's settings cannot approve it; use another permission mode to answer it`;
43
49
  case 'budget':
44
50
  return `${mode}: ${one} needed approval, and this session's automatic-approval budget is used up, so saved rules no longer approve calls${budgetKnob !== undefined ? ` — raise ${budgetKnob} before launch to change the cap` : ''}`;
51
+ case 'whole_tool_rule_only':
52
+ return `${mode}: ${one} needed approval; ${wholeToolOnly} — and this mode refuses anything that would ask; use another permission mode to answer it`;
45
53
  default:
46
54
  return `${mode}: ${one} needed a person's approval that saved permission rules cannot approve here (the engine or your deployment requires a person for it, or it could not be checked against your rules), and this mode refuses anything that would ask — use another permission mode to answer it`;
47
55
  }
@@ -57,6 +65,8 @@ export function policyDenyReason(facts) {
57
65
  return `${lane}: a rule on the engine side, not one of the user's permission rules, asks about ${one}, so it needed a person's answer, and ${tail}; the user's settings cannot approve it - run the task interactively to answer it`;
58
66
  case 'budget':
59
67
  return `${lane}: ${one} needed a person's approval, and this session's automatic-approval budget is used up, so saved rules no longer approve calls; ${tail}${budgetKnob !== undefined ? `; raise ${budgetKnob} before launch to change the cap` : ''}`;
68
+ case 'whole_tool_rule_only':
69
+ return `${lane}: ${one} needed a person's approval; ${wholeToolOnly}; ${tail}; run the task interactively to answer it`;
60
70
  default:
61
71
  return `${lane}: ${one} needed a person's approval, and saved permission rules cannot approve it here (the engine or your deployment requires a person for it, or it could not be checked against your rules); ${tail}; run the task interactively to answer it`;
62
72
  }
@@ -64,10 +74,33 @@ export function policyDenyReason(facts) {
64
74
  function denyOutputForRender(attribution) {
65
75
  if (attribution?.settledBy !== 'policy')
66
76
  return HITL_REJECT_MESSAGE;
77
+ const head = policyDenyHeadFor(readPolicyDenyCause(attribution));
67
78
  const reason = attribution.reason;
68
79
  return reason !== undefined && reason.trim() !== ''
69
- ? `${HITL_POLICY_DENY_MESSAGE}\n${reason}`
70
- : HITL_POLICY_DENY_MESSAGE;
80
+ ? `${head}\n${reason}`
81
+ : head;
82
+ }
83
+ function policyDenyHeadFor(cause) {
84
+ const tail = msg('approval.policyDeny.tail');
85
+ switch (cause) {
86
+ case 'dont_ask':
87
+ return renderMessage(msg('approval.policyDeny.dontAsk', { tail }));
88
+ case 'deny_rule':
89
+ return renderMessage(msg('approval.policyDeny.denyRule', { tail }));
90
+ case 'engine_floor':
91
+ return renderMessage(msg('approval.policyDeny.engineFloor', { tail }));
92
+ case 'no_approval_ui':
93
+ case undefined:
94
+ return HITL_POLICY_DENY_MESSAGE;
95
+ }
96
+ }
97
+ export function denyAttributionOf(deny) {
98
+ const policyCause = readPolicyDenyCause(deny);
99
+ return {
100
+ ...(deny?.settledBy !== undefined ? { settledBy: deny.settledBy } : {}),
101
+ ...(deny?.reason !== undefined ? { reason: deny.reason } : {}),
102
+ ...(policyCause !== undefined ? { policyCause } : {}),
103
+ };
71
104
  }
72
105
  function denialKindForTranscript(attribution) {
73
106
  return ccToolDenialKindForSettledBy(attribution?.settledBy);
@@ -354,10 +387,7 @@ function settleLiveCardOutcome(ev, outcome, ctx, fromSubagent, gatedCallId) {
354
387
  invokeHostCallbackIsolated('liveHitlAskWire: onToolApprovalOutcome', () => deps.onToolApprovalOutcome?.(ev, outcome));
355
388
  }
356
389
  if (outcome.kind === 'decided' && outcome.decision === 'deny' && !fromSubagent) {
357
- const attribution = {
358
- ...(outcome.deny?.settledBy !== undefined ? { settledBy: outcome.deny.settledBy } : {}),
359
- ...(outcome.deny?.reason !== undefined ? { reason: outcome.deny.reason } : {}),
360
- };
390
+ const attribution = denyAttributionOf(outcome.deny);
361
391
  if (gatedCallId !== undefined)
362
392
  led.markDenied(gatedCallId, attribution);
363
393
  else
@@ -1,9 +1,11 @@
1
1
  import type { AgentEvent } from '@sema-agent/sdk';
2
+ import { type PolicyDenyCause } from './policyDenyCause.js';
2
3
  export declare const SEMA_COLLATERAL_ABORT_KEY = "_sema_collateral_abort";
3
4
  export declare const SEMA_DENIAL_KIND_KEY = "_sema_denial_kind";
4
5
  export interface GateDenyAttribution {
5
6
  settledBy?: 'human' | 'policy';
6
7
  reason?: string;
8
+ policyCause?: PolicyDenyCause;
7
9
  }
8
10
  export interface AskAnsweredOutput {
9
11
  type: 'ask-user-question';
@@ -1,5 +1,6 @@
1
1
  import { parkedToolEndRoleOf } from '../engineRowRef.js';
2
2
  import { inheritFrameBinding } from '../frameBinding.js';
3
+ import { readPolicyDenyCause } from './policyDenyCause.js';
3
4
  export const SEMA_COLLATERAL_ABORT_KEY = '_sema_collateral_abort';
4
5
  export const SEMA_DENIAL_KIND_KEY = '_sema_denial_kind';
5
6
  export function durableSeqOf(ev) {
@@ -27,9 +28,10 @@ function normalizeDenyAttribution(a) {
27
28
  return undefined;
28
29
  const settledBy = a.settledBy === 'human' || a.settledBy === 'policy' ? a.settledBy : undefined;
29
30
  const reason = typeof a.reason === 'string' && a.reason.trim() !== '' ? a.reason : undefined;
31
+ const policyCause = settledBy === 'policy' ? readPolicyDenyCause(a) : undefined;
30
32
  if (settledBy === undefined && reason === undefined)
31
33
  return undefined;
32
- return { ...(settledBy !== undefined ? { settledBy } : {}), ...(reason !== undefined ? { reason } : {}) };
34
+ return { ...(settledBy !== undefined ? { settledBy } : {}), ...(reason !== undefined ? { reason } : {}), ...(policyCause !== undefined ? { policyCause } : {}) };
33
35
  }
34
36
  export function createGateLedger() {
35
37
  const startedCalls = new Set();
@@ -141,6 +141,10 @@ export type FindPendingOutcome = {
141
141
  export declare function findPendingForTask(client: HitlClientLike, taskId: string, matches: (toolName: string | undefined) => boolean, opts?: {
142
142
  signal?: AbortSignal;
143
143
  }, callId?: string, identityAccepts?: (row: PendingCheckpoint) => boolean): Promise<FindPendingOutcome>;
144
+ export declare function checkpointQueueReadingOf(row: object): {
145
+ readonly r: PendingCheckpoint;
146
+ readonly foreign: string | null;
147
+ } | undefined;
144
148
  export declare class HitlBridge {
145
149
  private readonly client;
146
150
  private readonly taskId;
@@ -11,8 +11,7 @@ import { suspendedReopenOf } from './suspendedReopen.js';
11
11
  import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp } from './decisionLedger.js';
12
12
  import { askParkBlockGeneration, noteAskParkDecideSettled } from './askParkAutoReopenBlock.js';
13
13
  import { checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
14
- import { checkpointRowBarOf, checkpointRowBarSentence } from '../checkpointRowVerdict.js';
15
- import { msg, renderMessage } from '../i18n/translate.js';
14
+ import { checkpointDeadlineStateOf, checkpointRowBarOf, checkpointRowBarSentence, unboundWayOutContent } from '../checkpointRowVerdict.js';
16
15
  export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
17
16
  export const MAX_DENY_REASON_CHARS = 4096;
18
17
  export function denyReasonForWire(reason, tag) {
@@ -271,11 +270,8 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
271
270
  return { ok: false, reason: `approvals.list failed: ${t.reason}` };
272
271
  }
273
272
  const readings = rows.flatMap((r) => {
274
- const sort = checkpointQueueRowSortOf(r);
275
- if (sort === 'excluded')
276
- return [];
277
- const cp = r;
278
- return [{ r: cp, foreign: sort === 'unreadable' ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(cp) }];
273
+ const x = checkpointQueueReadingOf(r);
274
+ return x === undefined ? [] : [x];
279
275
  });
280
276
  const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
281
277
  const identityRow = callId !== undefined
@@ -314,6 +310,13 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
314
310
  return { ok: false, reason: checkpointRowBarSentence(bar), code: 'row_unresumable', rowVerdict: bar };
315
311
  return { ok: true, pending, gatedCallId: pending.toolCallId ?? pending.boundCallId ?? undefined };
316
312
  }
313
+ export function checkpointQueueReadingOf(row) {
314
+ const sort = checkpointQueueRowSortOf(row);
315
+ if (sort === 'excluded')
316
+ return undefined;
317
+ const cp = row;
318
+ return { r: cp, foreign: sort === 'unreadable' ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(cp) };
319
+ }
317
320
  export class HitlBridge {
318
321
  client;
319
322
  taskId;
@@ -365,7 +368,7 @@ export class HitlBridge {
365
368
  if (typeof boundCallId !== 'string' || boundCallId.length === 0 || typeof boundInputHash !== 'string' || boundInputHash.length === 0) {
366
369
  throw new HitlSafetyError(`pending checkpoint ${p.id ?? '<no-id>'} is missing D-1 binding fields ` +
367
370
  `(boundCallId=${String(boundCallId)}, boundInputHash=${String(boundInputHash)}) — ` +
368
- `the row itself carries no binding to echo, so ${renderMessage(msg('approval.decide.unboundWayOut'))}`, 'binding_missing');
371
+ `the row itself carries no binding to echo, so ${unboundWayOutContent(checkpointDeadlineStateOf(p))}`, 'binding_missing');
369
372
  }
370
373
  return { boundCallId, boundInputHash };
371
374
  }