@sema-agent/client-core 0.108.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 (179) hide show
  1. package/CHANGELOG.md +294 -0
  2. package/README.md +77 -40
  3. package/dist/adapt/arms.js +41 -13
  4. package/dist/adapt/textStream.d.ts +2 -0
  5. package/dist/adapt/textStream.js +37 -16
  6. package/dist/adapter/activeRunSelfHeal.js +1 -1
  7. package/dist/adapter/downstream/eventToSdkMessage.js +11 -5
  8. package/dist/agentSession/engineAgentRegistryRead.js +2 -0
  9. package/dist/bashExitReading.d.ts +12 -0
  10. package/dist/bashExitReading.js +44 -0
  11. package/dist/checkpointRowVerdict.d.ts +1 -1
  12. package/dist/checkpointRowVerdict.js +4 -2
  13. package/dist/compensations.js +1 -1
  14. package/dist/declared/supportFloorDeclared.js +1 -1
  15. package/dist/displayUntrusted.js +132 -14
  16. package/dist/engineAgentAbsence.d.ts +2 -0
  17. package/dist/engineAgentAbsence.js +28 -0
  18. package/dist/engineErrorCodes.d.ts +7 -0
  19. package/dist/engineErrorCodes.js +7 -0
  20. package/dist/engineHealthFacts.d.ts +26 -0
  21. package/dist/engineHealthFacts.js +63 -4
  22. package/dist/engineInlineTaskStats.d.ts +4 -3
  23. package/dist/engineInlineTaskStats.js +18 -2
  24. package/dist/engineNoticeCodes.d.ts +138 -0
  25. package/dist/engineNoticeCodes.js +662 -55
  26. package/dist/engineRepoint.d.ts +140 -0
  27. package/dist/engineRepoint.js +285 -0
  28. package/dist/engineRowRef.js +2 -0
  29. package/dist/engineSessionMessages.d.ts +2 -1
  30. package/dist/engineSessionMessages.js +5 -0
  31. package/dist/errorSupplementRoles.js +5 -1
  32. package/dist/fleet/fleetProjection.js +3 -2
  33. package/dist/fleetTaskDesc.js +3 -3
  34. package/dist/generated/engineFactTables.d.ts +3 -0
  35. package/dist/generated/engineFactTables.js +18 -0
  36. package/dist/generated/engineNoticeTables.js +3 -0
  37. package/dist/generated/sdkFactTables.d.ts +1 -0
  38. package/dist/generated/sdkFactTables.js +4 -0
  39. package/dist/hitl/approvalAskers.d.ts +6 -0
  40. package/dist/hitl/approvalAskers.js +30 -0
  41. package/dist/hitl/approvalCardReasons.js +25 -4
  42. package/dist/hitl/approvalRequestFrameReading.d.ts +21 -0
  43. package/dist/hitl/approvalRequestFrameReading.js +90 -0
  44. package/dist/hitl/approvalResolution.d.ts +8 -1
  45. package/dist/hitl/approvalResolution.js +4 -0
  46. package/dist/hitl/approvalsFeed.js +1 -1
  47. package/dist/hitl/askGateWire.js +0 -2
  48. package/dist/hitl/askParkRowRouting.js +5 -2
  49. package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
  50. package/dist/hitl/automaticAnswerVerdict.js +18 -4
  51. package/dist/hitl/decisionConflict.d.ts +2 -1
  52. package/dist/hitl/decisionConflict.js +10 -4
  53. package/dist/hitl/decisionRefusal.js +1 -0
  54. package/dist/hitl/decisionSend.d.ts +3 -0
  55. package/dist/hitl/decisionSend.js +29 -11
  56. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  57. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  58. package/dist/hitl/frameRouter.js +13 -19
  59. package/dist/hitl/gateLedger.d.ts +0 -4
  60. package/dist/hitl/gateLedger.js +0 -19
  61. package/dist/hitl/hitlBridge.d.ts +5 -0
  62. package/dist/hitl/hitlBridge.js +13 -9
  63. package/dist/hitl/hitlHostSurface.d.ts +1 -0
  64. package/dist/hitl/hitlHostSurface.js +7 -2
  65. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  66. package/dist/hitl/hostWithdrawal.js +38 -0
  67. package/dist/hitl/livePendingAsk.js +17 -16
  68. package/dist/hitl/localAllowRule.js +2 -1
  69. package/dist/hitl/noAllowCause.d.ts +1 -1
  70. package/dist/hitl/noAllowCause.js +4 -2
  71. package/dist/hitl/parkExpiryJudgements.js +26 -1
  72. package/dist/hitl/parkOwnership.d.ts +2 -0
  73. package/dist/hitl/parkOwnership.js +48 -8
  74. package/dist/hitl/parkResolver.js +32 -16
  75. package/dist/hitl/parkedUnresumable.d.ts +13 -0
  76. package/dist/hitl/parkedUnresumable.js +80 -0
  77. package/dist/hitl/permissionRuleScope.d.ts +4 -0
  78. package/dist/hitl/permissionRuleScope.js +31 -0
  79. package/dist/hitl/persistedRulesWire.d.ts +2 -0
  80. package/dist/hitl/persistedRulesWire.js +13 -12
  81. package/dist/hitl/planReviewWire.js +14 -4
  82. package/dist/hitl/sessionAllowRoute.js +5 -4
  83. package/dist/hitl/sessionPolicyWire.js +6 -4
  84. package/dist/hitl/standingRows.d.ts +2 -5
  85. package/dist/hitl/standingRows.js +30 -15
  86. package/dist/hitl/toolApprovalWire.d.ts +42 -9
  87. package/dist/hitl/toolApprovalWire.js +267 -44
  88. package/dist/hooksWireCaps.js +35 -4
  89. package/dist/hostSettingsSource.js +2 -0
  90. package/dist/hostTurnSettings.d.ts +10 -4
  91. package/dist/hostTurnSettings.js +112 -5
  92. package/dist/i18n/messages.en.d.ts +251 -0
  93. package/dist/i18n/messages.en.js +254 -2
  94. package/dist/i18n/messages.zh.js +264 -2
  95. package/dist/index.d.ts +48 -12
  96. package/dist/index.js +29 -7
  97. package/dist/inheritEnvCapability.js +2 -0
  98. package/dist/inputAuthor.d.ts +16 -0
  99. package/dist/inputAuthor.js +44 -0
  100. package/dist/interruptRefusalNote.d.ts +2 -0
  101. package/dist/interruptRefusalNote.js +17 -0
  102. package/dist/liveModelCatalog.d.ts +2 -0
  103. package/dist/liveModelCatalog.js +21 -7
  104. package/dist/liveQuestionStore.d.ts +4 -1
  105. package/dist/liveQuestionStore.js +12 -3
  106. package/dist/localEngineJudge.d.ts +75 -0
  107. package/dist/localEngineJudge.js +242 -0
  108. package/dist/localEngineOccupancy.d.ts +88 -0
  109. package/dist/localEngineOccupancy.js +269 -0
  110. package/dist/localEngineSeatReading.d.ts +29 -0
  111. package/dist/localEngineSeatReading.js +294 -0
  112. package/dist/localEngineSpawn.d.ts +113 -0
  113. package/dist/localEngineSpawn.js +486 -0
  114. package/dist/localEngineStep.d.ts +286 -0
  115. package/dist/localEngineStep.js +1086 -0
  116. package/dist/localEngineStopLadder.d.ts +29 -0
  117. package/dist/localEngineStopLadder.js +139 -0
  118. package/dist/localEngineSwapCard.d.ts +43 -0
  119. package/dist/localEngineSwapCard.js +205 -0
  120. package/dist/localEngineTiming.d.ts +8 -0
  121. package/dist/localEngineTiming.js +8 -0
  122. package/dist/mcpEngineLeg.d.ts +3 -0
  123. package/dist/mcpEngineLeg.js +38 -1
  124. package/dist/mcpPanel.js +2 -0
  125. package/dist/mcpReconnect.d.ts +9 -1
  126. package/dist/mcpReconnect.js +132 -20
  127. package/dist/mcpWireCaps.js +2 -2
  128. package/dist/memoryEntriesWire.d.ts +1 -1
  129. package/dist/memoryEntriesWire.js +5 -0
  130. package/dist/modelCapabilityProbe.js +4 -3
  131. package/dist/modsFace.d.ts +8 -1
  132. package/dist/modsFace.js +27 -0
  133. package/dist/noticePresentation.d.ts +2 -0
  134. package/dist/noticePresentation.js +79 -1
  135. package/dist/notifications.d.ts +17 -6
  136. package/dist/notifications.js +67 -19
  137. package/dist/ownKey.d.ts +5 -0
  138. package/dist/ownKey.js +8 -1
  139. package/dist/peerFrames.d.ts +40 -2
  140. package/dist/peerFrames.js +158 -3
  141. package/dist/pluginHooksWire.d.ts +2 -1
  142. package/dist/pluginHooksWire.js +56 -1
  143. package/dist/replayBody.js +10 -2
  144. package/dist/request/taskRequest.d.ts +5 -0
  145. package/dist/request/taskRequest.js +138 -21
  146. package/dist/resumeRefusalCopy.d.ts +1 -3
  147. package/dist/resumeRefusalCopy.js +2 -1
  148. package/dist/retryStatus.d.ts +2 -2
  149. package/dist/retryStatus.js +2 -0
  150. package/dist/ruleReadings.js +1 -1
  151. package/dist/seam.d.ts +24 -2
  152. package/dist/seam.js +15 -2
  153. package/dist/seatContract.d.ts +125 -10
  154. package/dist/seatContract.js +373 -7
  155. package/dist/sessionBackground.js +4 -1
  156. package/dist/sessionBackgroundHandoff.js +2 -1
  157. package/dist/sessionEngine.d.ts +2 -0
  158. package/dist/sessionEngine.js +111 -0
  159. package/dist/sessionMemoryStatus.d.ts +2 -1
  160. package/dist/sessionSyncFileHistory.d.ts +4 -0
  161. package/dist/sessionSyncFileHistory.js +28 -0
  162. package/dist/sseIdleTriage.d.ts +13 -1
  163. package/dist/sseIdleTriage.js +24 -6
  164. package/dist/subagent/engineSubagentTail.js +6 -0
  165. package/dist/subagentContentStore.d.ts +12 -0
  166. package/dist/subagentContentStore.js +72 -11
  167. package/dist/terminalStatuses.d.ts +1 -0
  168. package/dist/terminalStatuses.js +4 -1
  169. package/dist/toolResult.js +18 -5
  170. package/dist/turnFailurePresentation.js +9 -2
  171. package/dist/typePins.d.ts +11 -0
  172. package/dist/usageAnalytics.d.ts +17 -0
  173. package/dist/usageAnalytics.js +44 -2
  174. package/dist/wireFailureShape.d.ts +22 -5
  175. package/dist/wireFailureShape.js +47 -26
  176. package/dist/wireRefusalCopy.d.ts +31 -1
  177. package/dist/wireRefusalCopy.js +167 -2
  178. package/docs/INTEGRATION-CLIENTS.md +3269 -21
  179. package/package.json +5 -5
@@ -2,7 +2,9 @@ import { displayUntrusted } from '../displayUntrusted.js';
2
2
  import { msg, renderMessage } from '../i18n/translate.js';
3
3
  import { approvalMandateDetail, askOriginNoteOf } from '../gateVocabulary.js';
4
4
  import { readAskOrigin, readOwnDataValue, guardedRead } from '../ownKey.js';
5
+ import { elideMiddleForDisplay } from '../noticePresentation.js';
5
6
  import { allowRuleShadowSentence } from './allowRuleShadow.js';
7
+ import { cardPayloadReadingOf } from './approvalRequestFrameReading.js';
6
8
  import { mandatedApprovalDetailOf } from './automaticAnswerVerdict.js';
7
9
  import { approvalBoundGrantsDetail, approvalIsMandated, approvalPriorCardsDetail, askProbeReasonDetail, classifierNoRulingDetail, readApprovalBoundGrants, readApprovalClassifierNoRuling, readApprovalMandate, readDenialLimitFallback, } from './toolApprovalWire.js';
8
10
  export const APPROVAL_CARD_REASON_SOURCES = Object.freeze([
@@ -21,8 +23,24 @@ export const APPROVAL_CARD_REASON_SOURCES = Object.freeze([
21
23
  ]);
22
24
  export const APPROVAL_CARD_REASONS_DEFAULT_MAX_LINES = 4;
23
25
  const MESSAGE_DISPLAY_MAX = 300;
24
- function payloadUnreadableDetail(lang) {
25
- return renderMessage(msg('approval.card.payloadUnreadable'), lang);
26
+ function engineMessageLine(engineMessage) {
27
+ const shown = displayUntrusted(engineMessage);
28
+ if (shown.length <= MESSAGE_DISPLAY_MAX)
29
+ return shown;
30
+ const settled = displayUntrusted(elideMiddleForDisplay(shown, MESSAGE_DISPLAY_MAX));
31
+ return settled.length <= MESSAGE_DISPLAY_MAX ? settled : displayUntrusted(engineMessage, { max: MESSAGE_DISPLAY_MAX });
32
+ }
33
+ function payloadUnreadableDetail(why, lang) {
34
+ switch (why) {
35
+ case 'version_unknown':
36
+ return renderMessage(msg('approval.card.payloadUnreadable.versionUnknown'), lang);
37
+ case 'kind_unknown':
38
+ return renderMessage(msg('approval.card.payloadUnreadable.partUnknown'), lang);
39
+ case 'unreadable_fields':
40
+ return renderMessage(msg('approval.card.payloadUnreadable.fieldsMalformed'), lang);
41
+ default:
42
+ return renderMessage(msg('approval.card.payloadUnreadable'), lang);
43
+ }
26
44
  }
27
45
  function moreDetail(n, lang) {
28
46
  return renderMessage(n === 1 ? msg('approval.card.more.one') : msg('approval.card.more.other', { count: n }), lang);
@@ -64,7 +82,10 @@ export function approvalCardReasons(card, opts, lang) {
64
82
  catch {
65
83
  denialLimitReadable = false;
66
84
  }
67
- add('payload_unreadable', () => (ownData(card, 'cardPayloadUnreadable') === true ? payloadUnreadableDetail(lang) : undefined));
85
+ add('payload_unreadable', () => {
86
+ const reading = cardPayloadReadingOf(card);
87
+ return reading.payloadUnreadable ? payloadUnreadableDetail(reading.why, lang) : undefined;
88
+ });
68
89
  add('prior_cards', () => approvalPriorCardsDetail(card, lang));
69
90
  add('message', () => {
70
91
  const engineMessage = ownData(card, 'message');
@@ -73,7 +94,7 @@ export function approvalCardReasons(card, opts, lang) {
73
94
  const tool = ownData(card, 'toolName');
74
95
  if (typeof tool === 'string' && engineMessage.trim() === `approval required for "${tool}"`)
75
96
  return undefined;
76
- return displayUntrusted(engineMessage, { max: MESSAGE_DISPLAY_MAX });
97
+ return engineMessageLine(engineMessage);
77
98
  });
78
99
  add('mandated', () => (approvalIsMandated(card) ? mandatedApprovalDetailOf(card, lang) : undefined));
79
100
  add('mandate', () => {
@@ -0,0 +1,21 @@
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 type CardPayloadReading = {
15
+ readonly payloadUnreadable: false;
16
+ } | {
17
+ readonly payloadUnreadable: true;
18
+ readonly why?: ApprovalRequestFrameUnreadableWhy;
19
+ };
20
+ export declare function cardPayloadReadingOf(card: object | null | undefined): CardPayloadReading;
21
+ export declare function approvalFrameUnreadableWhyOf(reading: ApprovalRequestFrameReading | null | undefined): ApprovalRequestFrameUnreadableWhy | undefined;
@@ -0,0 +1,90 @@
1
+ import { isApprovalRequestFrameV1 } from '@sema-agent/sdk';
2
+ import { displayUntrusted } from '../displayUntrusted.js';
3
+ import { guardedRead, readOwnDataValue } from '../ownKey.js';
4
+ export const APPROVAL_REQUEST_FRAME_UNREADABLE_WHYS = Object.freeze(['version_unknown', 'kind_unknown', 'unreadable_fields']);
5
+ const KNOWN_FRAME_TYPE = 'approval_request';
6
+ const KNOWN_SCHEMA_VERSION = 1;
7
+ const KNOWN_KIND = 'permission';
8
+ const FRAME_READING_VALUE_MAX = 24;
9
+ const FRAME_READING_VALUE_PREFIX = 128;
10
+ const V1_READING = Object.freeze({ v1: true });
11
+ const UNREADABLE_READING = Object.freeze({ v1: false, why: 'unreadable_fields' });
12
+ function displayableScalar(v) {
13
+ let raw;
14
+ if (typeof v === 'string')
15
+ raw = v.length > FRAME_READING_VALUE_PREFIX ? v.slice(0, FRAME_READING_VALUE_PREFIX) : v;
16
+ else if (typeof v === 'number' && Number.isFinite(v))
17
+ raw = String(v);
18
+ else
19
+ return undefined;
20
+ const shown = displayUntrusted(raw, { max: FRAME_READING_VALUE_MAX });
21
+ return shown === '' ? undefined : shown;
22
+ }
23
+ export function approvalRequestFrameReadingThrowing(frame) {
24
+ if (typeof frame !== 'object' || frame === null)
25
+ return UNREADABLE_READING;
26
+ const x = frame;
27
+ const type = x.type;
28
+ const schemaVersion = x.schemaVersion;
29
+ const kind = x.kind;
30
+ const pinned = Object.create(frame, {
31
+ type: { value: type, enumerable: true },
32
+ schemaVersion: { value: schemaVersion, enumerable: true },
33
+ kind: { value: kind, enumerable: true },
34
+ });
35
+ if (isApprovalRequestFrameV1(pinned))
36
+ return V1_READING;
37
+ const why = type !== KNOWN_FRAME_TYPE
38
+ ? 'unreadable_fields'
39
+ : schemaVersion !== KNOWN_SCHEMA_VERSION
40
+ ? 'version_unknown'
41
+ : kind !== KNOWN_KIND
42
+ ? 'kind_unknown'
43
+ : 'unreadable_fields';
44
+ const shownVersion = displayableScalar(schemaVersion);
45
+ const shownKind = displayableScalar(kind);
46
+ return Object.freeze({
47
+ v1: false,
48
+ why,
49
+ ...(shownVersion !== undefined ? { schemaVersion: shownVersion } : {}),
50
+ ...(shownKind !== undefined ? { kind: shownKind } : {}),
51
+ });
52
+ }
53
+ export function approvalRequestFrameReadingOf(frame) {
54
+ try {
55
+ return approvalRequestFrameReadingThrowing(frame);
56
+ }
57
+ catch {
58
+ return UNREADABLE_READING;
59
+ }
60
+ }
61
+ export function approvalUnreadableWhyWordOf(v) {
62
+ return APPROVAL_REQUEST_FRAME_UNREADABLE_WHYS.find((w) => w === v);
63
+ }
64
+ export function cardPayloadReadingOf(card) {
65
+ if (typeof card !== 'object' || card === null)
66
+ return Object.freeze({ payloadUnreadable: false });
67
+ const flag = guardedRead(() => readOwnDataValue(card, 'cardPayloadUnreadable'));
68
+ if (flag !== true)
69
+ return Object.freeze({ payloadUnreadable: false });
70
+ const word = guardedRead(() => readOwnDataValue(card, 'cardPayloadUnreadableWhy'));
71
+ const why = typeof word === 'string' ? approvalUnreadableWhyWordOf(word) : undefined;
72
+ return why === undefined ? Object.freeze({ payloadUnreadable: true }) : Object.freeze({ payloadUnreadable: true, why });
73
+ }
74
+ export function approvalFrameUnreadableWhyOf(reading) {
75
+ try {
76
+ if (typeof reading !== 'object' || reading === null)
77
+ return undefined;
78
+ const v1 = Object.getOwnPropertyDescriptor(reading, 'v1');
79
+ const why = Object.getOwnPropertyDescriptor(reading, 'why');
80
+ if (v1 === undefined || !Object.hasOwn(v1, 'value') || v1.value !== false)
81
+ return undefined;
82
+ if (why === undefined || !Object.hasOwn(why, 'value'))
83
+ return undefined;
84
+ const word = why.value;
85
+ return typeof word === 'string' ? approvalUnreadableWhyWordOf(word) : undefined;
86
+ }
87
+ catch {
88
+ return undefined;
89
+ }
90
+ }
@@ -8,7 +8,7 @@ import { type DeliveredReading, type LadderSafetyStop } from './attributionLadde
8
8
  import { type CheckpointRowVerdictReading } from '../checkpointRowVerdict.js';
9
9
  export declare const APPROVAL_RESOLUTION_LANES: readonly ["respond", "decide", "ask"];
10
10
  export type ApprovalResolutionLane = (typeof APPROVAL_RESOLUTION_LANES)[number];
11
- export declare const APPROVAL_NOT_SENT_CAUSES: readonly ["retracted", "edit_refused", "no_pending", "wrong_gate", "fetch_failed", "no_input", "card_unavailable", "safety_stop", "row_unresumable"];
11
+ export declare const APPROVAL_NOT_SENT_CAUSES: readonly ["retracted", "edit_refused", "no_pending", "wrong_gate", "fetch_failed", "no_input", "card_unavailable", "safety_stop", "row_unresumable", "withdrawn"];
12
12
  export type ApprovalNotSentCause = (typeof APPROVAL_NOT_SENT_CAUSES)[number];
13
13
  export declare const APPROVAL_UNSETTLED_CAUSES: readonly ["engine_refused", "engine_error", "no_answer", "transport_exhausted", "parking_in_progress", "interrupted", "orchestration", "unreadable"];
14
14
  export type ApprovalUnsettledCause = (typeof APPROVAL_UNSETTLED_CAUSES)[number];
@@ -18,6 +18,11 @@ export interface ApprovalDenyFacts {
18
18
  readonly policyCause?: PolicyDenyCause;
19
19
  readonly attributionKey?: 'dropped' | 'renamed';
20
20
  }
21
+ export interface ToolPermissionSettledElsewhere {
22
+ readonly firstDecision: 'allow' | 'deny';
23
+ readonly contrary: boolean;
24
+ }
25
+ export declare function toolPermissionSettledElsewhereOf(first: 'allow' | 'deny', sent: ToolApprovalRespondDecision | 'approve'): ToolPermissionSettledElsewhere;
21
26
  interface ApprovalResolutionBase {
22
27
  readonly lane: ApprovalResolutionLane;
23
28
  readonly gatedCallId?: string;
@@ -30,6 +35,7 @@ export interface ApprovalResolutionDecided extends ApprovalResolutionBase {
30
35
  readonly receipt?: DecideReceiptView;
31
36
  readonly ack?: ToolApprovalRespondAckView;
32
37
  readonly answered?: AskAnsweredOutput;
38
+ readonly settledElsewhere?: ToolPermissionSettledElsewhere;
33
39
  }
34
40
  export interface ApprovalResolutionNotSent extends ApprovalResolutionBase {
35
41
  readonly kind: 'not_sent';
@@ -38,6 +44,7 @@ export interface ApprovalResolutionNotSent extends ApprovalResolutionBase {
38
44
  readonly rowVerdict?: Exclude<CheckpointRowVerdictReading, {
39
45
  readonly kind: 'ok';
40
46
  }>;
47
+ readonly toldBy?: 'host';
41
48
  readonly detail?: string;
42
49
  }
43
50
  export interface ApprovalResolutionUnsettled extends ApprovalResolutionBase {
@@ -16,6 +16,7 @@ export const APPROVAL_NOT_SENT_CAUSES = Object.freeze([
16
16
  'card_unavailable',
17
17
  'safety_stop',
18
18
  'row_unresumable',
19
+ 'withdrawn',
19
20
  ]);
20
21
  export const APPROVAL_UNSETTLED_CAUSES = Object.freeze([
21
22
  'engine_refused',
@@ -27,6 +28,9 @@ export const APPROVAL_UNSETTLED_CAUSES = Object.freeze([
27
28
  'orchestration',
28
29
  'unreadable',
29
30
  ]);
31
+ export function toolPermissionSettledElsewhereOf(first, sent) {
32
+ return Object.freeze({ firstDecision: first, contrary: (first === 'deny') !== (sent === 'deny') });
33
+ }
30
34
  export function isApprovalDecided(r) {
31
35
  return typeof r === 'object' && r !== null && r.kind === 'decided';
32
36
  }
@@ -51,7 +51,7 @@ function readDurablePendingRows(raw) {
51
51
  }
52
52
  function durableDigestOf(rows) {
53
53
  return rows
54
- .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)]
55
55
  .join(''))
56
56
  .sort()
57
57
  .join('');
@@ -91,7 +91,6 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
91
91
  await abortableSleep(reopenAfterMs, opts?.signal ?? new AbortController().signal);
92
92
  if (!opts?.signal?.aborted) {
93
93
  const seq = led.lastSeq();
94
- led.beginStreamSegment();
95
94
  stream = deps.runsEvents(taskId.current, {
96
95
  ...(seq !== undefined ? { lastEventId: seq } : {}),
97
96
  ...(opts?.signal ? { signal: opts.signal } : {}),
@@ -170,7 +169,6 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
170
169
  led.forgetRunScopedState();
171
170
  }
172
171
  const seq = handoff !== undefined ? undefined : led.lastSeq();
173
- led.beginStreamSegment();
174
172
  stream = deps.runsEvents(taskId.current, {
175
173
  ...(seq !== undefined ? { lastEventId: seq } : {}),
176
174
  ...(opts?.signal ? { signal: opts.signal } : {}),
@@ -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';
@@ -161,8 +161,11 @@ export function countOwnedPendingRows(listed, deps) {
161
161
  throw new Error('approvals.list answered with a pending row that is not an object — the owned pending count is unknown');
162
162
  }
163
163
  const family = approvalQueueRowFamilyOf(row);
164
- if (family === 'unreadable')
164
+ if (family === 'unreadable') {
165
+ if (pendingRowOwnershipOf(row, deps) === 'foreign')
166
+ continue;
165
167
  throw new Error('approvals.list answered with a pending row whose family (`kind`) cannot be read — the owned pending count is unknown');
168
+ }
166
169
  if (family !== 'checkpoint')
167
170
  continue;
168
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';
@@ -60,13 +61,13 @@ export function foldModeArbitrationRow(classes, askFace) {
60
61
  return ORDINARY_ROW;
61
62
  return Object.freeze({ bypassImmune, classifierEligible, sandboxAdmission, ruleMayClear, hookAskFloor });
62
63
  }
63
- const REAL_APPROVAL_CLASSES = new Set(CORE_REAL_APPROVAL_ASK_CLASSES);
64
+ const HOOK_BLOCKING_CLASSES = new Set([...CORE_REAL_APPROVAL_ASK_CLASSES, ORIGIN_IMPLIES_CLASS.content_question]);
64
65
  function classBlocks(lane, c, face) {
65
66
  if (lane === 'mode_posture')
66
67
  return bypassImmuneOn(c, face);
67
68
  if (lane === 'standing_rule')
68
69
  return !TABLE[c].ruleMayClear;
69
- return REAL_APPROVAL_CLASSES.has(c);
70
+ return HOOK_BLOCKING_CLASSES.has(c);
70
71
  }
71
72
  function mustAsk(road, extra) {
72
73
  return Object.freeze({ kind: 'must_ask', road, ...(extra?.askClass !== undefined ? { askClass: extra.askClass } : {}), ...(extra?.origin !== undefined ? { origin: extra.origin } : {}) });
@@ -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;
@@ -1,4 +1,4 @@
1
- import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_UNSUPPORTED_VERSION, RESUME_OUTCOME_INVALID } from '../engineErrorCodes.js';
1
+ import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_INVALID_OUTCOME, CHECKPOINT_UNSUPPORTED_VERSION, RESUME_OUTCOME_INVALID } from '../engineErrorCodes.js';
2
2
  import { msg, renderMessage } from '../i18n/translate.js';
3
3
  import { ownDataField } from '../ownKey.js';
4
4
  import { SDK_DECIDE_BINDING_KEYS } from '../generated/sdkFactTables.js';
@@ -28,16 +28,19 @@ export function decisionConflictOf(err, captured, protocolNameRead) {
28
28
  return undefined;
29
29
  }
30
30
  }
31
- const protocolName = namedProtocolOf(protocolNameRead ?? protocolErrorNameReading(err, extra));
31
+ const protocolName = namedProtocolOf(protocolNameRead ?? protocolErrorNameReading(err));
32
32
  if (code === CHECKPOINT_UNSUPPORTED_VERSION)
33
33
  return 'format_version';
34
34
  const bindingFamily = code === APPROVAL_BINDING_MISMATCH || (code === undefined && protocolName === PROTOCOL_ERROR_NAME.ApprovalBindingMismatchError);
35
- if (!bindingFamily && code !== RESUME_OUTCOME_INVALID)
35
+ const rowOwnFamily = code === CHECKPOINT_INVALID_OUTCOME || (code === undefined && protocolName === PROTOCOL_ERROR_NAME.CheckpointInvalidOutcomeError);
36
+ if (!bindingFamily && !rowOwnFamily && code !== RESUME_OUTCOME_INVALID)
36
37
  return undefined;
37
38
  const slots = readRefusalSlots(err, REFUSAL_WORD_KEYS, extra.bag);
38
39
  const field = slots.field.word;
39
40
  if (code === RESUME_OUTCOME_INVALID)
40
41
  return field === ANSWER_FIELD ? 'answer_invalid' : field === GRANT_DIGEST_FIELD ? 'grant_changed' : undefined;
42
+ if (rowOwnFamily)
43
+ return field === undefined && !slots.field.present && slots.reason.word !== undefined ? 'row_integrity' : undefined;
41
44
  if (field === undefined) {
42
45
  if (slots.field.present)
43
46
  return undefined;
@@ -45,7 +48,10 @@ export function decisionConflictOf(err, captured, protocolNameRead) {
45
48
  }
46
49
  return BINDING_SEAT_FIELDS.has(field) ? 'binding_mismatch' : undefined;
47
50
  }
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';
51
+ export function decisionAnswerInvalidContent(lang) {
52
+ return renderMessage(msg('approval.decision.answerInvalid'), lang);
53
+ }
54
+ export const DECISION_ANSWER_INVALID_CONTENT = decisionAnswerInvalidContent();
49
55
  export function decisionGrantChangedContent(lang) {
50
56
  return renderMessage(msg('approval.decision.grantChanged'), lang);
51
57
  }
@@ -226,6 +226,7 @@ export function mintDecisionRefusalReading(err, context, attributionCarried, del
226
226
  const acceptableForm = errorCode === RESUME_CONSTRAINT_REJECTED && obj !== undefined ? isolated(() => readErrorPresent(obj, 'acceptableForm')) : undefined;
227
227
  const transient = inRange &&
228
228
  conflict !== 'grant_changed' &&
229
+ conflict !== 'row_integrity' &&
229
230
  !unrecoverable &&
230
231
  (TRANSIENT_STATUSES.has(status) || retriable || (status === 404 && obj !== undefined && isolated(() => notPendingCauseReading(obj)) === 'other_replica'));
231
232
  return Object.freeze({
@@ -1,5 +1,6 @@
1
1
  import type { AgentClient, ElicitResponse } from '@sema-agent/sdk';
2
2
  import type { UiLanguage } from '../env/uiLanguage.js';
3
+ import type { ToolPermissionSettledElsewhere } from './approvalResolution.js';
3
4
  import { type AskGoneDetail } from '../decideReceipt.js';
4
5
  import type { ApprovalDecidedByReading } from '../approvalDecidedByCapability.js';
5
6
  import { type DecisionNoteAudit } from './approvalDecisionNoteAudit.js';
@@ -64,6 +65,7 @@ interface HitlDecisionOutcomeBase {
64
65
  readonly attempts: number;
65
66
  readonly fallback?: 'approval';
66
67
  readonly noteDropped?: true;
68
+ readonly allowNoteDropped?: true;
67
69
  readonly rememberDropped?: true;
68
70
  readonly allowSessionCarried?: true;
69
71
  readonly attributionDropped?: true;
@@ -115,5 +117,6 @@ export interface HitlDecisionSendTiming {
115
117
  export declare function __setHitlDecisionSendTimingForTests(t?: HitlDecisionSendTiming): void;
116
118
  export declare function hitlAskIdempotencyKey(askId: string, verdict: 'approve' | 'deny'): string;
117
119
  export declare function sendHitlDecision(request: HitlDecisionSendRequest, deps: HitlDecisionSendDeps): Promise<HitlDecisionSendOutcome>;
120
+ export declare function toolPermissionSettledLine(fact: ToolPermissionSettledElsewhere | null | undefined, lang?: UiLanguage): string | undefined;
118
121
  export declare function hitlDecisionOutcomeContent(outcome: HitlDecisionSendOutcome, lang?: UiLanguage): string | undefined;
119
122
  export {};
@@ -14,7 +14,7 @@ import { ASK_DECIDED_CODE, classifyDeliveredDecisionRefusal, decisionRefusalClos
14
14
  import { sessionAllowEligible } from './sessionAllowRoute.js';
15
15
  import { addDecisionOpKeys, closeDecisionOp, decisionLedgerKeysOf, decisionOpsWithKey, openDecisionOp } from './decisionLedger.js';
16
16
  import { hostHoldIsActive, takeHostHoldLedgerOp, tryHoldApprovalNow } from './approvalHolds.js';
17
- import { answerPayloadFor, decisionAttributionKeyOf, isToolApprovalRespondReplayed, readApprovalDenySettledBy, readToolApprovalRespondAck, readToolApprovalRespondParking, } from './toolApprovalWire.js';
17
+ import { allowNoteReadingOf, answerPayloadFor, decisionAttributionKeyOf, readApprovalDenySettledBy, readToolApprovalRespondAck, readToolApprovalRespondParking, replayedFirstDecisionOf, settlementAckOf, } from './toolApprovalWire.js';
18
18
  export const HITL_DECISION_FAMILIES = Object.freeze(['approval', 'ask', 'elicitation']);
19
19
  export const HITL_DECISION_NOT_APPLIED_CAUSES = Object.freeze(['gone', 'parked', 'parking', 'lane_disabled', 'refused', 'idempotency_crossed']);
20
20
  export const HITL_DECISION_UNCONFIRMED_CAUSES = Object.freeze(['transport', 'server_error', 'local_error', 'aborted', 'gone_after_retry']);
@@ -163,9 +163,10 @@ async function sendApprovalFamily(respond, approvalId, answer, ctx) {
163
163
  const hint = view !== undefined ? ownReadingSlot(view, 'retryAfterMs') : undefined;
164
164
  return { ...base, kind: 'not_applied', cause: 'parking', ...(view !== undefined ? { parking: view, ...(hint !== undefined ? { retryAfterMs: hint } : {}) } : {}) };
165
165
  }
166
- const parsed = readToolApprovalRespondAck(raw);
167
- if (parsed !== undefined && isToolApprovalRespondReplayed(parsed)) {
168
- const first = parsed.approvalId === approvalId ? verdictOf(parsed.decision) : undefined;
166
+ const parsed = settlementAckOf(readToolApprovalRespondAck(raw));
167
+ const replayWord = replayedFirstDecisionOf(parsed);
168
+ if (parsed !== undefined && replayWord !== undefined) {
169
+ const first = parsed.approvalId === approvalId ? verdictOf(replayWord) : undefined;
169
170
  return {
170
171
  ...base,
171
172
  kind: 'settled_elsewhere',
@@ -183,6 +184,7 @@ function bodyFacts(answer, payload, rememberUnsupported) {
183
184
  return {
184
185
  ...(reason !== undefined && reason.trim() !== '' && payload.note === undefined ? { noteDropped: true } : {}),
185
186
  ...(rememberUnsupported && answer.kind === 'allow' && answer.allowSession === true ? { rememberDropped: true } : {}),
187
+ ...(answer.kind === 'allow' && allowNoteReadingOf(answer).kind === 'text' ? { allowNoteDropped: true } : {}),
186
188
  };
187
189
  }
188
190
  function answerGateCannotTake(out) {
@@ -361,8 +363,10 @@ function snapshotAnswer(raw) {
361
363
  if (kind === 'allow') {
362
364
  const allowSession = guardedRead(() => readOwnDataValue(raw, 'allowSession')) === true;
363
365
  const edited = guardedRead(() => readOwnDataValue(raw, 'updatedInput'));
366
+ const noteRaw = guardedRead(() => readOwnDataValue(raw, 'note'));
367
+ const note = typeof noteRaw === 'string' && noteRaw.trim() !== '' ? noteRaw : undefined;
364
368
  if (edited === undefined)
365
- return Object.freeze({ kind: 'allow', allowSession });
369
+ return Object.freeze({ kind: 'allow', allowSession, ...(note !== undefined ? { note } : {}) });
366
370
  let copy;
367
371
  try {
368
372
  const text = JSON.stringify(edited);
@@ -373,7 +377,7 @@ function snapshotAnswer(raw) {
373
377
  catch {
374
378
  return undefined;
375
379
  }
376
- return Object.freeze({ kind: 'allow', allowSession, updatedInput: copy });
380
+ return Object.freeze({ kind: 'allow', allowSession, ...(note !== undefined ? { note } : {}), updatedInput: copy });
377
381
  }
378
382
  if (kind === 'deny') {
379
383
  const reason = guardedRead(() => readOwnDataValue(raw, 'reason'));
@@ -560,6 +564,24 @@ export async function sendHitlDecision(request, deps) {
560
564
  ownHold.release(out.kind === 'applied' || out.kind === 'settled_elsewhere' ? 'answered' : undefined);
561
565
  return Object.freeze(out);
562
566
  }
567
+ function settledElsewhereContraryContent(first, lang) {
568
+ return first === 'deny'
569
+ ? renderMessage(msg('approval.decision.settledElsewhereContrary.deniedFirst'), lang)
570
+ : renderMessage(msg('approval.decision.settledElsewhereContrary.approvedFirst'), lang);
571
+ }
572
+ export function toolPermissionSettledLine(fact, lang) {
573
+ try {
574
+ if (typeof fact !== 'object' || fact === null)
575
+ return undefined;
576
+ if (readOwnDataValue(fact, 'contrary') !== true)
577
+ return undefined;
578
+ const first = readOwnDataValue(fact, 'firstDecision');
579
+ return first === 'allow' || first === 'deny' ? settledElsewhereContraryContent(first, lang) : undefined;
580
+ }
581
+ catch {
582
+ return undefined;
583
+ }
584
+ }
563
585
  export function hitlDecisionOutcomeContent(outcome, lang) {
564
586
  try {
565
587
  if (typeof outcome !== 'object' || outcome === null)
@@ -569,11 +591,7 @@ export function hitlDecisionOutcomeContent(outcome, lang) {
569
591
  if (readOwnDataValue(outcome, 'contrary') !== true)
570
592
  return undefined;
571
593
  const first = readOwnDataValue(outcome, 'firstDecision');
572
- if (first === 'deny')
573
- return renderMessage(msg('approval.decision.settledElsewhereContrary.deniedFirst'), lang);
574
- if (first === 'approve')
575
- return renderMessage(msg('approval.decision.settledElsewhereContrary.approvedFirst'), lang);
576
- return undefined;
594
+ return first === 'deny' || first === 'approve' ? settledElsewhereContraryContent(first, lang) : undefined;
577
595
  }
578
596
  if (kind === 'unconfirmed' && readOwnDataValue(outcome, 'cause') === 'gone_after_retry')
579
597
  return renderMessage(msg('approval.decision.unconfirmedAfterRetry'), lang);
@@ -0,0 +1,25 @@
1
+ import type { UiLanguage } from '../env/uiLanguage.js';
2
+ export declare const EDITED_APPROVAL_STATES: readonly ["accepts", "refuses", "unknown"];
3
+ export type EditedApprovalState = (typeof EDITED_APPROVAL_STATES)[number];
4
+ export declare const EDITED_APPROVAL_BASES: Readonly<{
5
+ accepts: readonly ["args_available"];
6
+ refuses: readonly ["args_unavailable"];
7
+ unknown: readonly ["card_unreadable", "slot_unreadable", "slot_malformed"];
8
+ }>;
9
+ interface EditedApprovalAccepts {
10
+ readonly state: 'accepts';
11
+ readonly basis: (typeof EDITED_APPROVAL_BASES.accepts)[number];
12
+ }
13
+ interface EditedApprovalRefuses {
14
+ readonly state: 'refuses';
15
+ readonly basis: (typeof EDITED_APPROVAL_BASES.refuses)[number];
16
+ readonly askStaysPending: true;
17
+ }
18
+ interface EditedApprovalUnknown {
19
+ readonly state: 'unknown';
20
+ readonly basis: (typeof EDITED_APPROVAL_BASES.unknown)[number];
21
+ }
22
+ export type EditedApprovalAcceptance = EditedApprovalAccepts | EditedApprovalRefuses | EditedApprovalUnknown;
23
+ export declare function editedApprovalAcceptanceOf(card: {} | null | undefined): EditedApprovalAcceptance;
24
+ export declare function editedApprovalRefusedLine(lang?: UiLanguage): string;
25
+ export {};
@@ -0,0 +1,26 @@
1
+ import { msg, renderMessage } from '../i18n/translate.js';
2
+ import { slotReadOf } from '../wireFailureShape.js';
3
+ export const EDITED_APPROVAL_STATES = Object.freeze(['accepts', 'refuses', 'unknown']);
4
+ export const EDITED_APPROVAL_BASES = Object.freeze({
5
+ accepts: Object.freeze(['args_available']),
6
+ refuses: Object.freeze(['args_unavailable']),
7
+ unknown: Object.freeze(['card_unreadable', 'slot_unreadable', 'slot_malformed']),
8
+ });
9
+ const ACCEPTS = Object.freeze({ state: 'accepts', basis: 'args_available' });
10
+ const REFUSES = Object.freeze({ state: 'refuses', basis: 'args_unavailable', askStaysPending: true });
11
+ const UNKNOWN_CARD = Object.freeze({ state: 'unknown', basis: 'card_unreadable' });
12
+ const UNKNOWN_SLOT = Object.freeze({ state: 'unknown', basis: 'slot_unreadable' });
13
+ const UNKNOWN_MALFORMED = Object.freeze({ state: 'unknown', basis: 'slot_malformed' });
14
+ export function editedApprovalAcceptanceOf(card) {
15
+ if (typeof card !== 'object' || card === null)
16
+ return UNKNOWN_CARD;
17
+ const read = slotReadOf(card, 'argsUnavailable');
18
+ if (read.presence === 'unreadable')
19
+ return UNKNOWN_SLOT;
20
+ if (read.presence === 'absent' || read.value === undefined)
21
+ return ACCEPTS;
22
+ return read.value === true ? REFUSES : UNKNOWN_MALFORMED;
23
+ }
24
+ export function editedApprovalRefusedLine(lang) {
25
+ return renderMessage(msg('approval.editRefusal.line'), lang);
26
+ }