@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
@@ -1,7 +1,7 @@
1
1
  import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
2
2
  import { engineCapTrue } from '../engineCapsCache.js';
3
3
  import { CORE_RULE_BEHAVIORS } from '../generated/engineFactTables.js';
4
- import { slotReadOf, wireFailureShapeOf } from '../wireFailureShape.js';
4
+ import { inValueOf, slotReadOf, wireFailureShapeOf } from '../wireFailureShape.js';
5
5
  import { CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED } from '../engineErrorCodes.js';
6
6
  import { hostLog } from '../host.js';
7
7
  import { msg, renderMessage } from '../i18n/translate.js';
@@ -43,17 +43,21 @@ function ownNonEmpty(o, key) {
43
43
  const r = slotReadOf(o, key);
44
44
  return r.presence === 'present' && typeof r.value === 'string' && r.value !== '' ? r.value : undefined;
45
45
  }
46
- function indeterminateRowOf(row) {
46
+ function ruleKeyOfRow(row) {
47
47
  const k = slotReadOf(row, 'key');
48
48
  const keyObj = k.presence === 'present' && typeof k.value === 'object' && k.value !== null ? k.value : undefined;
49
49
  const behavior = keyObj === undefined ? undefined : ownNonEmpty(keyObj, 'behavior');
50
50
  const rule = keyObj === undefined ? undefined : ownNonEmpty(keyObj, 'rule');
51
51
  const scope = keyObj === undefined ? undefined : ownNonEmpty(keyObj, 'scope');
52
+ return behavior !== undefined && rule !== undefined && scope !== undefined ? Object.freeze({ behavior, rule, scope }) : undefined;
53
+ }
54
+ function unsettledRowOf(row) {
55
+ const key = ruleKeyOfRow(row);
52
56
  const reason = ownNonEmpty(row, 'reason');
53
57
  const d = slotReadOf(row, 'detail');
54
58
  const shown = d.presence === 'present' && typeof d.value === 'string' ? displayUntrusted(d.value, { max: INDETERMINATE_DETAIL_SHOWN_MAX }) : undefined;
55
59
  return Object.freeze({
56
- ...(behavior !== undefined && rule !== undefined && scope !== undefined ? { key: Object.freeze({ behavior, rule, scope }) } : {}),
60
+ ...(key !== undefined ? { key } : {}),
57
61
  ...(reason !== undefined ? { reason } : {}),
58
62
  ...(shown !== undefined && shown.trim() !== '' ? { detail: shown } : {}),
59
63
  });
@@ -65,12 +69,15 @@ const RECEIPT_ROW_ARMS = {
65
69
  'no-op': (acc) => {
66
70
  acc.deduped += 1;
67
71
  },
72
+ refused: (acc, row) => {
73
+ appendOwnElement(acc.refused, unsettledRowOf(row));
74
+ },
68
75
  indeterminate: (acc, row) => {
69
- appendOwnElement(acc.indeterminate, indeterminateRowOf(row));
76
+ appendOwnElement(acc.indeterminate, unsettledRowOf(row));
70
77
  },
71
78
  };
72
79
  function receiptCountsOf(rows) {
73
- const acc = { persisted: 0, deduped: 0, indeterminate: [] };
80
+ const acc = { persisted: 0, deduped: 0, indeterminate: [], refused: [] };
74
81
  for (const row of rows) {
75
82
  if (typeof row !== 'object' || row === null)
76
83
  return null;
@@ -80,7 +87,12 @@ function receiptCountsOf(rows) {
80
87
  return null;
81
88
  RECEIPT_ROW_ARMS[word](acc, row);
82
89
  }
83
- return { persisted: acc.persisted, deduped: acc.deduped, indeterminate: acc.indeterminate.length === 0 ? NO_INDETERMINATE : Object.freeze(acc.indeterminate) };
90
+ return {
91
+ persisted: acc.persisted,
92
+ deduped: acc.deduped,
93
+ indeterminate: acc.indeterminate.length === 0 ? NO_INDETERMINATE : Object.freeze(acc.indeterminate),
94
+ ...(acc.refused.length === 0 ? {} : { refused: Object.freeze(acc.refused) }),
95
+ };
84
96
  }
85
97
  function legacyEnvelopeCountsOf(result) {
86
98
  if (typeof result !== 'object' || result === null)
@@ -317,9 +329,14 @@ export function revokeTargetFromPersistedRule(rule, opts) {
317
329
  };
318
330
  }
319
331
  function readPrincipalOverride(carrier) {
320
- if (carrier === null || typeof carrier !== 'object' || !('principal' in carrier))
332
+ if (carrier === null || typeof carrier !== 'object')
333
+ return { ok: true };
334
+ const slot = inValueOf(carrier, 'principal');
335
+ if (slot.state === 'absent')
321
336
  return { ok: true };
322
- const raw = carrier.principal;
337
+ if (slot.state === 'unproven')
338
+ return { ok: false };
339
+ const raw = slot.value;
323
340
  if (typeof raw !== 'string' || raw.trim().length === 0)
324
341
  return { ok: false };
325
342
  return { ok: true, principal: raw };
@@ -1,5 +1,6 @@
1
1
  import { publishQuestionFrame, publishQuestionFrameFor, registerLocalQuestionResponder, unregisterLocalQuestionResponder, hasLocalQuestionResponder, hasQuestionOverlayFor, } from '../liveQuestionStore.js';
2
2
  import { hostLog } from '../host.js';
3
+ import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
3
4
  import { abortableSleep, settleWithinDeadline } from '../abortableSleep.js';
4
5
  import { clientHasVerbs, engineWireFor, noAnswerKindOf, sendOnce } from '../engineWireFor.js';
5
6
  import { engineWireTarget } from '../engineWireTarget.js';
@@ -1028,22 +1029,17 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
1028
1029
  hostLog('debug', `planReviewWire: outcome enqueue threw: ${String(e)}`);
1029
1030
  }
1030
1031
  if (onOutcome !== undefined) {
1031
- try {
1032
- onOutcome({
1033
- taskId,
1034
- dispatchNo,
1035
- decision: semanticDecision,
1036
- effect,
1037
- ...(effect === 'still_parked' && stillParkedCause !== undefined ? { cause: stillParkedCause } : {}),
1038
- detail: outcome,
1039
- tail,
1040
- prompt,
1041
- ...(statusSeen !== undefined ? { status: statusSeen } : {}),
1042
- });
1043
- }
1044
- catch (e) {
1045
- hostLog('debug', `planReviewWire: onOutcome callback threw for task ${taskId} (ignored): ${String(e)}`);
1046
- }
1032
+ invokeHostCallbackIsolated(`planReviewWire: onOutcome callback (task ${taskId})`, () => onOutcome({
1033
+ taskId,
1034
+ dispatchNo,
1035
+ decision: semanticDecision,
1036
+ effect,
1037
+ ...(effect === 'still_parked' && stillParkedCause !== undefined ? { cause: stillParkedCause } : {}),
1038
+ detail: outcome,
1039
+ tail,
1040
+ prompt,
1041
+ ...(statusSeen !== undefined ? { status: statusSeen } : {}),
1042
+ }));
1047
1043
  }
1048
1044
  }
1049
1045
  function reopenIdSuffix() {
@@ -0,0 +1,3 @@
1
+ export declare const POLICY_DENY_CAUSES: readonly ["no_approval_ui", "dont_ask", "deny_rule", "engine_floor"];
2
+ export type PolicyDenyCause = (typeof POLICY_DENY_CAUSES)[number];
3
+ export declare function readPolicyDenyCause(decision: object | null | undefined): PolicyDenyCause | undefined;
@@ -0,0 +1,16 @@
1
+ export const POLICY_DENY_CAUSES = Object.freeze(['no_approval_ui', 'dont_ask', 'deny_rule', 'engine_floor']);
2
+ export function readPolicyDenyCause(decision) {
3
+ if (decision === null || typeof decision !== 'object')
4
+ return undefined;
5
+ let d;
6
+ try {
7
+ d = Object.getOwnPropertyDescriptor(decision, 'policyCause');
8
+ }
9
+ catch {
10
+ return undefined;
11
+ }
12
+ if (d === undefined || !Object.hasOwn(d, 'value'))
13
+ return undefined;
14
+ const v = d.value;
15
+ return typeof v === 'string' && POLICY_DENY_CAUSES.includes(v) ? v : undefined;
16
+ }
@@ -1,3 +1,4 @@
1
+ import { inSlotReadOf } from '../wireFailureShape.js';
1
2
  export function sessionAllowCarried(leg, approvalId) {
2
3
  if (leg === 'frame')
3
4
  return true;
@@ -7,10 +8,10 @@ export function sessionAllowCarried(leg, approvalId) {
7
8
  }
8
9
  export function readCardUnreadableEvidence(card) {
9
10
  try {
10
- if (!('unreadableEvidence' in card))
11
+ const slot = inSlotReadOf(card, 'unreadableEvidence');
12
+ if (slot.state === 'absent')
11
13
  return 'absent';
12
- const d = Object.getOwnPropertyDescriptor(card, 'unreadableEvidence');
13
- const list = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
14
+ const list = slot.state === 'present' ? slot.value : undefined;
14
15
  return Array.isArray(list) && list.every((k) => typeof k === 'string') ? list : 'unreadable';
15
16
  }
16
17
  catch {
@@ -21,7 +22,7 @@ export function sessionAllowCardReadable(card) {
21
22
  if (typeof card !== 'object' || card === null)
22
23
  return 'card_unreadable';
23
24
  try {
24
- if ('cardPayloadUnreadable' in card)
25
+ if (inSlotReadOf(card, 'cardPayloadUnreadable').state !== 'absent')
25
26
  return 'card_unreadable';
26
27
  }
27
28
  catch {
@@ -12,7 +12,7 @@ const WITHHELD_NOTICE = {
12
12
  qualified_deny: 'Not written to this session’s rule record: that record holds whole tool names and command names only, and this rule has a part in parentheses it cannot carry — recording just the name could block more than this rule does.',
13
13
  peer_wide: 'Not written to this session’s rule record: this rule names a server or agent peer without naming one of its tools, and an engine that matches that record by exact tool name would block none of that peer’s tools.',
14
14
  wildcard: 'Not written to this session’s rule record: this rule uses a wildcard (*), and an engine that matches that record by exact tool name would block none of the tools it covers.',
15
- not_a_tool_name: 'Not written to this session’s rule record: this entry is not exactly one tool name — it is empty, not text, has spaces around the name, is not a single tool name in rule syntax (for example it contains a backslash or a parenthesis, or is longer than a rule can be), or spells its server or agent peer part in a way the engine never uses when naming tools (for example with a dot or a space), so no tool would ever match it.',
15
+ not_a_tool_name: 'Not written to this session’s rule record: this entry is not exactly one tool name — it is empty, not text, has spaces around the name, is not a single tool name in rule syntax (for example it contains a backslash or a parenthesis, or is longer than a rule can be), or spells its server or agent peer part in a form this record does not accept (for example with a dot or a space) · This record does not take entries of that form; record the exact name of a single tool instead.',
16
16
  legacy_tool_name: `Not written to this session’s rule record: unless a tool by exactly this name is available, the engine refuses it — it is a retired tool name, or it contains "__" but does not start with ${PREFIXES_IN_PROSE} — and with it in that record every later run of this session would fail at startup until that entry is removed from the record. ${REFUSED_NAME_FIX}`,
17
17
  not_in_roster: 'Not written to this session’s rule record: the tool roster supplied for this check has no tool this rule would block — no tool by exactly this name, as a name or an alias, with letter case counted, and, where the engine also reads the name as naming every tool of a server (a server whose own name contains "__"), no tool of that server either — it may be misspelled, or name a tool this deployment does not provide — so recorded as it is it would block nothing here.',
18
18
  namespace_covering_unconfirmed: msg('session.policy.withheld.namespaceCoveringUnconfirmed'),
@@ -25,13 +25,9 @@ export interface RememberOfferable {
25
25
  readonly deploymentList: RememberOffer;
26
26
  }
27
27
  export declare function rememberOfferable(card: object | null | undefined, host?: StandingRowsHostFacts | null): RememberOfferable;
28
- export interface LocalAllowRuleHostFacts {
29
- readonly hostLadderEnforces?: boolean;
30
- }
31
28
  export interface LocalAllowRuleEnforcement {
32
29
  readonly enforceable: boolean;
33
- readonly by?: 'host_ladder' | 'engine_grant';
34
30
  readonly engine: 'sent' | 'kept' | 'unreadable';
35
31
  readonly keptReason?: PermissionsKeptReason;
36
32
  }
37
- export declare function localAllowRuleEnforceable(rule: string, caps: PermissionsAllowGrantReading, host?: LocalAllowRuleHostFacts | null): LocalAllowRuleEnforcement;
33
+ export declare function localAllowRuleEnforceable(rule: string, caps: PermissionsAllowGrantReading): LocalAllowRuleEnforcement;
@@ -1,7 +1,8 @@
1
1
  import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPairState } from './askClassPair.js';
2
- import { automaticAnswerVerdict } from './automaticAnswerVerdict.js';
2
+ import { askLevelVerdict } from './automaticAnswerVerdict.js';
3
3
  import { readApprovalCardWord, readRuleOffers } from './toolApprovalWire.js';
4
4
  import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
5
+ import { inSlotReadOf, inValueOf } from '../wireFailureShape.js';
5
6
  import { toolPermissionRequestIdDomain } from '../seatContract.js';
6
7
  import { permissionsDeclarationReport } from '../permissionsDeclaration.js';
7
8
  import { readCardUnreadableEvidence, sessionAllowCardReadable, sessionAllowEligible } from './sessionAllowRoute.js';
@@ -15,9 +16,10 @@ function ownData(v, key) {
15
16
  return undefined;
16
17
  }
17
18
  }
18
- function hasKey(v, key) {
19
+ function candidateSlot(v, key) {
19
20
  try {
20
- return { inCard: key in v };
21
+ const s = inSlotReadOf(v, key);
22
+ return s.state === 'unproven' ? UNREADABLE : s;
21
23
  }
22
24
  catch {
23
25
  return UNREADABLE;
@@ -25,8 +27,10 @@ function hasKey(v, key) {
25
27
  }
26
28
  function present(v, key) {
27
29
  try {
28
- const inCard = key in v;
29
- return { inCard, value: inCard ? v[key] : undefined };
30
+ const s = inValueOf(v, key);
31
+ if (s.state === 'unproven')
32
+ return UNREADABLE;
33
+ return s.state === 'present' ? { inCard: true, value: s.value } : { inCard: false, value: undefined };
30
34
  }
31
35
  catch {
32
36
  return UNREADABLE;
@@ -42,12 +46,12 @@ function persistentRowOf(card, host, evidenceUnreadable) {
42
46
  if (evidenceUnreadable)
43
47
  return { row: 'none', why: 'evidence_unreadable' };
44
48
  for (const key of ['ruleOffers', 'ruleOffersReadOnly']) {
45
- const p = hasKey(card, key);
49
+ const p = candidateSlot(card, key);
46
50
  if (p === UNREADABLE)
47
51
  return UNREADABLE;
48
- if (!p.inCard)
52
+ if (p.state === 'absent')
49
53
  continue;
50
- const offers = readRuleOffers(ownData(card, key));
54
+ const offers = readRuleOffers(p.state === 'present' ? p.value : undefined);
51
55
  if (offers === undefined || offers.length === 0)
52
56
  return { row: 'none', why: 'offers_unreadable' };
53
57
  return { row: 'engine_offer', why: 'given' };
@@ -69,7 +73,7 @@ function persistentRowOf(card, host, evidenceUnreadable) {
69
73
  return { row: 'none', why: 'tool_name_unreadable' };
70
74
  if (tool === CORE_SKILL_TOOL_NAME)
71
75
  return { row: 'none', why: 'grant_binding' };
72
- if (automaticAnswerVerdict(card, 'standing_rule').kind !== 'may_answer')
76
+ if (askLevelVerdict(card, 'standing_rule').kind !== 'may_answer')
73
77
  return { row: 'none', why: 'rule_cannot_clear' };
74
78
  const face = readApprovalCardWord(card);
75
79
  if (face === undefined)
@@ -178,8 +182,7 @@ export function rememberOfferable(card, host) {
178
182
  deploymentList: SESSION_OFFER[rows.why.deploymentList],
179
183
  });
180
184
  }
181
- export function localAllowRuleEnforceable(rule, caps, host) {
182
- const ladder = typeof host === 'object' && host !== null && ownData(host, 'hostLadderEnforces') === true;
185
+ export function localAllowRuleEnforceable(rule, caps) {
183
186
  let engine = 'unreadable';
184
187
  let keptReason;
185
188
  if (typeof rule === 'string' && rule.trim() !== '') {
@@ -199,10 +202,8 @@ export function localAllowRuleEnforceable(rule, caps, host) {
199
202
  engine = 'unreadable';
200
203
  }
201
204
  }
202
- const by = ladder ? 'host_ladder' : engine === 'sent' ? 'engine_grant' : undefined;
203
205
  return Object.freeze({
204
- enforceable: by !== undefined,
205
- ...(by !== undefined ? { by } : {}),
206
+ enforceable: engine === 'sent',
206
207
  engine,
207
208
  ...(keptReason !== undefined ? { keptReason } : {}),
208
209
  });
@@ -1,4 +1,6 @@
1
1
  import { type HitlClientLike, type DecideOperationObserver } from './hitlBridge.js';
2
+ import { type ApprovalAskReason } from './approvalAskers.js';
3
+ export type { ApprovalAskReason, ApprovalAskReasonByHook, ApprovalAskReasonByPolicy } from './approvalAskers.js';
2
4
  import { readApprovalCardWord, type ApprovalCardWord } from './approvalCardReading.js';
3
5
  import { type ApprovalAllowRuleShadow } from './allowRuleShadow.js';
4
6
  import { CORE_PERSISTED_RULE_MATCHES } from '../generated/engineFactTables.js';
@@ -9,6 +11,8 @@ import { type ApprovalResolution } from './approvalResolution.js';
9
11
  import { type ApprovalMandateWord } from '../gateVocabulary.js';
10
12
  import type { ToolPermissionRequest } from '../seatContract.js';
11
13
  import { type ApprovalDecidedByReading } from '../approvalDecidedByCapability.js';
14
+ import { type PolicyDenyCause } from './policyDenyCause.js';
15
+ import { type ApprovalRequestFrameReading, type ApprovalRequestFrameUnreadableWhy } from './approvalRequestFrameReading.js';
12
16
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS } from '@sema-agent/sdk';
13
17
  import type { ApprovalDecidedBy, ApprovalCardDelegation, ToolApprovalDecision as SdkToolApprovalDecision, ToolApprovalRespondAck, ToolApprovalRespondReplayed, PersistedRuleAnchor, RuleOfferMatch, RuleOfferBatchMember, RuleOfferUncoveredDetail, RuleOfferUncoveredReason, AskOrigin, RuleOffersAbsence, DenialLimitKind, DenialLimitFallback, ToolApprovalFrame, ApprovalRequestFrame, RuleOffer as SdkRuleOffer } from '@sema-agent/sdk';
14
18
  export declare function toolNameIsFsWrite(name: string): boolean;
@@ -71,6 +75,7 @@ export interface ApprovalCardDenyDecision {
71
75
  kind: 'deny';
72
76
  reason?: string;
73
77
  settledBy?: ApprovalDenySettledBy;
78
+ policyCause?: PolicyDenyCause;
74
79
  }
75
80
  export declare const APPROVAL_DENY_SETTLED_BY_WORDS: readonly ["human", "policy"];
76
81
  export type ApprovalDenySettledBy = (typeof APPROVAL_DENY_SETTLED_BY_WORDS)[number];
@@ -125,6 +130,8 @@ export interface ApprovalCardRequest {
125
130
  approvalCard?: ApprovalCardWord;
126
131
  askClass?: string;
127
132
  classes?: readonly string[];
133
+ agentId?: string;
134
+ askReasons?: readonly ApprovalAskReason[];
128
135
  boundGrants?: BoundSkillGrantsWire;
129
136
  grantBinding?: ApprovalGrantBinding;
130
137
  unreadableEvidence?: readonly string[];
@@ -132,6 +139,7 @@ export interface ApprovalCardRequest {
132
139
  priorCardsForCall?: readonly ApprovalPriorCard[];
133
140
  message?: string;
134
141
  cardPayloadUnreadable?: true;
142
+ cardPayloadUnreadableWhy?: ApprovalRequestFrameUnreadableWhy;
135
143
  supersedes?: ApprovalCardSupersedes;
136
144
  parkRow?: ApprovalCardParkRow;
137
145
  }
@@ -163,7 +171,7 @@ export declare function approvalCardPortFor(sessionKey: string): ApprovalCardPor
163
171
  export declare function __resetSettledApprovalsForTests(): void;
164
172
  export declare function _resetApprovalCardPortForTest(): void;
165
173
  export declare function surfaceApprovalCard(req: ApprovalCardRequest): Promise<ApprovalCardDecision>;
166
- declare const REMEMBER_REFUSAL_CODES: readonly ["remember_not_in_proof", "capability.store_required", "decide.workflow_remember_unsupported", "decide.parked_remember_unsupported"];
174
+ declare const REMEMBER_REFUSAL_CODES: readonly ["remember_not_in_proof", "capability.store_required", "decide.workflow_remember_unsupported", "decide.parked_remember_unsupported", "remember_requires_binding"];
167
175
  export type RememberRefusalCode = (typeof REMEMBER_REFUSAL_CODES)[number];
168
176
  export declare function rememberRefusalOf(error: unknown): RememberRefusalCode | undefined;
169
177
  export declare function surfaceFsApprovalAndDecide(deps: FsApprovalWireDeps, taskId: string, argsByCall: Map<string, unknown>, signal?: AbortSignal, parkGatedCallId?: string, parkToolName?: string): Promise<ApprovalResolution>;
@@ -276,8 +284,8 @@ export declare function approvalSupersedesDetail(card: Pick<ApprovalCardRequest,
276
284
  export declare function readDenialLimitFallback(v: unknown): DenialLimitFallback | undefined;
277
285
  export declare function readRuleOffers(v: unknown): RuleOfferReading[] | undefined;
278
286
  export declare function readRuleOfferSupply(offers: unknown): RuleOfferReading[] | undefined;
279
- export type ApprovalCardV1Facts = Pick<ApprovalCardRequest, 'toolName' | 'args'> & Pick<ApprovalCardRequest, 'toolCallId' | 'workerBadge' | 'wireNote' | 'argsUnavailable' | 'governanceForced' | 'delegation' | 'ruleOffersReadOnly' | 'inputHasBidi' | 'allowRuleShadow' | 'probeCause' | 'rememberRefused' | 'ruleEvidence' | 'requiresRealApproval' | 'ruleOffersAbsence' | 'denialLimitFallback' | 'askClass' | 'classes' | 'grantBinding' | 'unreadableEvidence' | 'message' | 'approvalCard'>;
280
- export type ApprovalCardFacts = ApprovalCardV1Facts & Pick<ApprovalCardRequest, 'argsRewritten' | 'ruleOffers' | 'probeReason' | 'mandated' | 'mandate' | 'readRootCandidate' | 'origin' | 'boundGrants' | 'classifierNoRuling' | 'allowSessionCarried' | 'cardPayloadUnreadable'>;
287
+ export type ApprovalCardV1Facts = Pick<ApprovalCardRequest, 'toolName' | 'args'> & Pick<ApprovalCardRequest, 'toolCallId' | 'workerBadge' | 'wireNote' | 'argsUnavailable' | 'governanceForced' | 'delegation' | 'ruleOffersReadOnly' | 'inputHasBidi' | 'allowRuleShadow' | 'probeCause' | 'rememberRefused' | 'ruleEvidence' | 'requiresRealApproval' | 'ruleOffersAbsence' | 'denialLimitFallback' | 'askClass' | 'classes' | 'agentId' | 'askReasons' | 'grantBinding' | 'unreadableEvidence' | 'message' | 'approvalCard'>;
288
+ export type ApprovalCardFacts = ApprovalCardV1Facts & Pick<ApprovalCardRequest, 'argsRewritten' | 'ruleOffers' | 'probeReason' | 'mandated' | 'mandate' | 'readRootCandidate' | 'origin' | 'boundGrants' | 'classifierNoRuling' | 'allowSessionCarried' | 'cardPayloadUnreadable' | 'cardPayloadUnreadableWhy'>;
281
289
  export interface ApprovalCardFrameFactsOptions {
282
290
  readonly streamArgs?: object;
283
291
  readonly argsUnavailable?: true;
@@ -285,7 +293,7 @@ export interface ApprovalCardFrameFactsOptions {
285
293
  export declare function approvalCardFactsOfV1(card: object | null | undefined): Readonly<ApprovalCardV1Facts> | undefined;
286
294
  export declare function approvalCardFactsOfV1Frame(frame: object | null | undefined): Readonly<ApprovalCardFacts> | undefined;
287
295
  export declare function approvalCardFactsOfFrame(frame: object | null | undefined, opts?: ApprovalCardFrameFactsOptions | null): Readonly<ApprovalCardFacts> | undefined;
288
- export declare function approvalCardFactsOfGeneric(card: object | null | undefined): Readonly<ApprovalCardFacts> | undefined;
296
+ export declare function approvalCardFactsOfGeneric(card: object | null | undefined, reading?: ApprovalRequestFrameReading | null): Readonly<ApprovalCardFacts> | undefined;
289
297
  export interface ToolApprovalFrameLaneOpts {
290
298
  approvalDecisionNoteCapable?: boolean;
291
299
  respondFreeFormRulesCapable?: boolean;
@@ -298,6 +306,10 @@ export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFr
298
306
  readonly taskId?: string;
299
307
  readonly sessionId?: string;
300
308
  }): Promise<ApprovalResolution>;
309
+ export declare function surfaceApprovalFrameWithId(frame: ToolApprovalFrame | ApprovalRequestFrame, approvalId: string, respond: RespondToolApprovalFn, streamArgs: Parameters<typeof surfaceToolApprovalFrameAndRespond>[2], signal: AbortSignal | undefined, lane: ToolApprovalFrameLaneOpts | undefined, run: {
310
+ readonly taskId?: string;
311
+ readonly sessionId?: string;
312
+ } | undefined): Promise<ApprovalResolution>;
301
313
  export interface ApprovalHolderIdentity {
302
314
  readonly approvalId?: string;
303
315
  readonly taskId?: string;
@@ -7,6 +7,7 @@ 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
11
  import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
11
12
  import { approvalWindowKeysOf, readApprovalCardWord, readRequiresRealApproval } from './approvalCardReading.js';
12
13
  import { readAllowRuleShadowFromWire, retiredAllowRuleShadowKeyPresent, RETIRED_SHADOW_EVIDENCE_LABEL } from './allowRuleShadow.js';
@@ -28,13 +29,15 @@ import { parkRowDeadlineState } from './parkExpiryJudgements.js';
28
29
  import { isApprovalMandateWord } from '../gateVocabulary.js';
29
30
  import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
30
31
  import { decideFailureNote } from '../decideFailureNote.js';
31
- import { slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
32
+ import { readPolicyDenyCause } from './policyDenyCause.js';
33
+ import { inValueOf, slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
32
34
  import { APPROVAL_LIVE_FACE_KEYS, approvalLiveFaceOf, hasOwnStrictTrue } from './approvalLiveFace.js';
35
+ import { approvalFrameUnreadableWhyOf, approvalRequestFrameReadingThrowing, } from './approvalRequestFrameReading.js';
33
36
  import { subagentSourceSessionOf } from './subagentSource.js';
34
37
  import { isPlainRecord } from '../jsonFaithful.js';
35
38
  import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
36
39
  import { readDecideReceipt } from '../decideReceipt.js';
37
- import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, isApprovalRequestFrameV1, } from '@sema-agent/sdk';
40
+ import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, } from '@sema-agent/sdk';
38
41
  export function toolNameIsFsWrite(name) {
39
42
  const n = name.replace(/[\s_-]+/g, '').toLowerCase();
40
43
  return (n === 'write' || n === 'filewrite' || n === 'edit' || n === 'fileedit' || n === 'multiedit' || n === 'notebookedit');
@@ -157,11 +160,13 @@ function cardPortMissReason() {
157
160
  return APPROVAL_CARD_PORT_KEY_MISMATCH_REASON;
158
161
  return NO_APPROVAL_CARD_PORT_REASON;
159
162
  }
163
+ const LEGACY_REMEMBER_REFUSAL_CODE = 'remember_requires_binding';
160
164
  const REMEMBER_REFUSAL_CODES = Object.freeze([
161
165
  'remember_not_in_proof',
162
166
  'capability.store_required',
163
167
  DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED,
164
168
  'decide.parked_remember_unsupported',
169
+ LEGACY_REMEMBER_REFUSAL_CODE,
165
170
  ]);
166
171
  export function rememberRefusalOf(error) {
167
172
  return rememberRefusalIn(readWireErrorCodeReading(error));
@@ -489,6 +494,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
489
494
  const rowArgsGap = reading.from !== 'none' ? undefined : reading.shape === 'absent' ? 'absent' : reading.shape === 'size_marker' ? 'truncated' : undefined;
490
495
  const args = reading.from !== 'none' ? reading.args : rowArgsGap !== undefined ? {} : reading.recorded;
491
496
  const argsRewritten = reading.from === 'recorded' && reading.rewritten;
497
+ const argsFromModelCall = reading.from === 'stream';
492
498
  if (typeof args !== 'object' || args === null) {
493
499
  return { kind: 'not_sent', ...at, cause: 'no_input', detail: 'gate has no tool input payload' };
494
500
  }
@@ -512,11 +518,13 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
512
518
  }
513
519
  : {}),
514
520
  ...(argsRewritten ? { argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : {}),
521
+ ...(argsFromModelCall ? { argsUnavailable: true, wireNote: renderMessage(msg('approval.park.argsFromModelCall')) } : {}),
515
522
  ...(pending.governanceForced === true ? { governanceForced: true } : {}),
516
523
  ...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
517
524
  ...approvalLiveFaceOf(pending).facts,
518
525
  ...((w) => (w !== undefined ? { approvalCard: w } : {}))(readApprovalCardWord(pending)),
519
526
  ...(readAskClassPair(pending) ?? {}),
527
+ ...((r) => (r !== undefined ? { askReasons: r } : {}))(approvalAskReasonsOf(pending)),
520
528
  ...(pending.gateKind === 'irreversible_ask' ? { irreversibleParkGate: true } : {}),
521
529
  ...(pending.hasBidiControls === true ? { hasBidiControls: true } : {}),
522
530
  ...(ruleOffersReadOnly !== undefined ? { ruleOffersReadOnly } : {}),
@@ -536,8 +544,8 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
536
544
  });
537
545
  const card = await surfaceCallTrackedCard(gatedCallId, undefined, ledgerSlot, cardRequest);
538
546
  hold.phase = 'respond';
539
- if (rowArgsGap !== undefined && card.kind === 'allow' && card.updatedInput !== undefined) {
540
- 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' : 'missing from both the stream and the pending row'} — nothing sent (the park stays pending)`);
547
+ if ((rowArgsGap !== undefined || argsFromModelCall) && card.kind === 'allow' && card.updatedInput !== undefined) {
548
+ 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)`);
541
549
  surfaceEditRefusedOnBlindAsk();
542
550
  return { kind: 'not_sent', ...at, cause: 'edit_refused', detail: PARK_EDIT_REFUSED_REASON };
543
551
  }
@@ -609,6 +617,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
609
617
  }
610
618
  case 'deny': {
611
619
  const denySettledByForWire = readApprovalDenySettledBy(card);
620
+ const denyPolicyCauseForWire = readPolicyDenyCause(card);
612
621
  const attributionKeys = decisionAttributionKeyOf(decidedByReading, denySettledByForWire);
613
622
  const denyReasonSnapshot = ((r) => (typeof r === 'string' ? r : undefined))(card.reason);
614
623
  let lastSendCarriedAttribution = false;
@@ -642,7 +651,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
642
651
  const attributionDropped = ladder.dropped;
643
652
  const attributionRenamed = ladder.renamed;
644
653
  const receipt = readDecideReceipt(raw);
645
- const deny = denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed);
654
+ const deny = denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed, denyPolicyCauseForWire);
646
655
  return {
647
656
  kind: 'decided',
648
657
  ...at,
@@ -679,8 +688,11 @@ function readReadRootCandidate(v) {
679
688
  const o = v;
680
689
  if (typeof o.dir !== 'string' || o.dir.length === 0 || o.clearsThisAsk !== true)
681
690
  return undefined;
682
- if ('covers' in o)
683
- return o.covers === 'exact' ? { dir: o.dir, clearsThisAsk: true, covers: 'exact' } : undefined;
691
+ const covers = inValueOf(o, 'covers');
692
+ if (covers.state === 'unproven')
693
+ return undefined;
694
+ if (covers.state === 'present')
695
+ return covers.value === 'exact' ? { dir: o.dir, clearsThisAsk: true, covers: 'exact' } : undefined;
684
696
  return { dir: o.dir, clearsThisAsk: true };
685
697
  }
686
698
  export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
@@ -1014,14 +1026,16 @@ function readDecidedByReadingOf(holder) {
1014
1026
  function decidedByReadingSnapshotOf(holder) {
1015
1027
  return approvalDecidedByAdmitted(readDecidedByReadingOf(holder)) ? Object.freeze({ kind: 'present', value: true }) : undefined;
1016
1028
  }
1017
- function denyFactsOf(settledBy, reasonSnapshot, dropped, renamed) {
1029
+ function denyFactsOf(settledBy, reasonSnapshot, dropped, renamed, policyCauseSnapshot) {
1018
1030
  const reason = reasonSnapshot !== undefined && reasonSnapshot.trim() !== '' ? reasonSnapshot : undefined;
1019
1031
  const attributionKey = dropped ? 'dropped' : renamed ? 'renamed' : undefined;
1032
+ const policyCause = settledBy === 'policy' ? policyCauseSnapshot : undefined;
1020
1033
  if (settledBy === undefined && reason === undefined && attributionKey === undefined)
1021
1034
  return undefined;
1022
1035
  return {
1023
1036
  ...(settledBy !== undefined ? { settledBy } : {}),
1024
1037
  ...(reason !== undefined ? { reason } : {}),
1038
+ ...(policyCause !== undefined ? { policyCause } : {}),
1025
1039
  ...(attributionKey !== undefined ? { attributionKey } : {}),
1026
1040
  };
1027
1041
  }
@@ -1671,7 +1685,7 @@ function v1Bit(stamp) {
1671
1685
  const SUSPENDED_ASK_ARGS_NOTE = "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
1672
1686
  'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
1673
1687
  'Edits are not accepted on this card: there is no original input to edit';
1674
- function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
1688
+ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId, payloadWhy) {
1675
1689
  const c = carrier;
1676
1690
  const full = leg !== 'generic';
1677
1691
  const live = leg === 'frame' ? carrier : leg === 'v1' ? liveFacts : undefined;
@@ -1822,6 +1836,16 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
1822
1836
  if (w !== undefined)
1823
1837
  out.approvalCard = w;
1824
1838
  });
1839
+ v1Bit(() => {
1840
+ const id = approvalAgentIdOf(carrier);
1841
+ if (id !== undefined)
1842
+ out.agentId = id;
1843
+ });
1844
+ v1Bit(() => {
1845
+ const reasons = approvalAskReasonsOf(carrier);
1846
+ if (reasons !== undefined)
1847
+ out.askReasons = reasons;
1848
+ });
1825
1849
  }
1826
1850
  v1Bit(() => {
1827
1851
  const pair = readAskClassPair(carrier);
@@ -1847,8 +1871,11 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
1847
1871
  if (typeof msg === 'string' && msg.trim() !== '')
1848
1872
  out.message = msg;
1849
1873
  });
1850
- if (leg === 'generic')
1874
+ if (leg === 'generic') {
1851
1875
  out.cardPayloadUnreadable = true;
1876
+ if (payloadWhy !== undefined)
1877
+ out.cardPayloadUnreadableWhy = payloadWhy;
1878
+ }
1852
1879
  if (sessionAllowCarried(leg, approvalId))
1853
1880
  out.allowSessionCarried = true;
1854
1881
  out.grantBinding = readApprovalGrantBinding(carrier);
@@ -1880,15 +1907,26 @@ export function approvalCardFactsOfFrame(frame, opts) {
1880
1907
  return undefined;
1881
1908
  return Object.freeze(cardFactsOf(frame, 'frame', opts));
1882
1909
  }
1883
- export function approvalCardFactsOfGeneric(card) {
1910
+ export function approvalCardFactsOfGeneric(card, reading) {
1884
1911
  if (typeof card !== 'object' || card === null)
1885
1912
  return undefined;
1886
- return Object.freeze(cardFactsOf(card, 'generic'));
1913
+ return Object.freeze(cardFactsOf(card, 'generic', null, undefined, undefined, approvalFrameUnreadableWhyOf(reading)));
1887
1914
  }
1888
1915
  const MAX_RESPOND_NOTE_CHARS = 2048;
1889
1916
  export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane, run) {
1917
+ const approvalId = readOwnDataNonEmptyString(frame, 'approvalId');
1918
+ if (approvalId === undefined)
1919
+ return approvalIdUnreadableRefusal();
1920
+ return surfaceApprovalFrameWithId(frame, approvalId, respond, streamArgs, signal, lane, run);
1921
+ }
1922
+ function approvalIdUnreadableRefusal() {
1923
+ hostLog('debug', 'liveToolApprovalWire: refused (approvalId_unreadable)');
1924
+ return { kind: 'not_sent', lane: 'respond', cause: 'card_unavailable' };
1925
+ }
1926
+ export async function surfaceApprovalFrameWithId(frame, approvalId, respond, streamArgs, signal, lane, run) {
1890
1927
  const receivedAtMs = Date.now();
1891
- const approvalId = frame.approvalId;
1928
+ if (typeof approvalId !== 'string' || approvalId === '')
1929
+ return approvalIdUnreadableRefusal();
1892
1930
  const carriers = openingFrameCarriersOf(frame);
1893
1931
  const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, frame, carriers, run), respond, streamArgs, signal, lane, receivedAtMs };
1894
1932
  return enterLiveCard(entry);
@@ -1898,9 +1936,10 @@ function openingFrameCarriersOf(frame) {
1898
1936
  return { body: frame, live: frame, leg: 'frame' };
1899
1937
  const card = frame.card;
1900
1938
  const pinned = Object.create(frame, { card: { value: card, enumerable: true } });
1901
- if (isApprovalRequestFrameV1(pinned))
1939
+ const reading = approvalRequestFrameReadingThrowing(pinned);
1940
+ if (reading.v1)
1902
1941
  return { body: pinned.card, live: frame, leg: 'v1' };
1903
- return { body: typeof card === 'object' && card !== null ? card : {}, live: undefined, leg: 'generic' };
1942
+ return { body: typeof card === 'object' && card !== null ? card : {}, live: undefined, leg: 'generic', why: reading.why };
1904
1943
  }
1905
1944
  function enterLiveCard(e) {
1906
1945
  const { approvalId, signal } = e;
@@ -1998,7 +2037,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
1998
2037
  const bodyToolCallId = carriers.body.toolCallId;
1999
2038
  const frameToolCallId = typeof bodyToolCallId === 'string' && bodyToolCallId !== '' ? bodyToolCallId : undefined;
2000
2039
  const decidedByReading = decidedByReadingSnapshotOf(lane);
2001
- const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live, approvalId);
2040
+ const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live, approvalId, carriers.why);
2002
2041
  const argsNotRealInput = facts.argsUnavailable === true;
2003
2042
  const ruleOffers = facts.ruleOffers;
2004
2043
  if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
@@ -2059,6 +2098,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2059
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`);
2060
2099
  }
2061
2100
  const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
2101
+ const denyPolicyCauseForWire = card.kind === 'deny' ? readPolicyDenyCause(card) : undefined;
2062
2102
  const attributionKeys = decision === 'deny' ? decisionAttributionKeyOf(decidedByReading, denySettledByForWire) : {};
2063
2103
  const sentAttributionKey = carriesAttributionKey(attributionKeys);
2064
2104
  let lastSendCarriedAttribution = sentAttributionKey;
@@ -2300,7 +2340,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
2300
2340
  'this answer was not delivered, reporting the earlier decision');
2301
2341
  return { kind: 'decided', lane: 'respond', decision: replayed.decision, ack: replayed };
2302
2342
  }
2303
- const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed) : undefined;
2343
+ const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed, denyPolicyCauseForWire) : undefined;
2304
2344
  return { kind: 'decided', lane: 'respond', decision, ...(ack !== undefined ? { ack } : {}), ...(deny !== undefined ? { deny } : {}) };
2305
2345
  }
2306
2346
  catch (e) {
@@ -0,0 +1 @@
1
+ export declare function hooksReaderNotReadKeys(serverVersion: string | undefined): readonly string[];
@@ -0,0 +1,12 @@
1
+ import { engineVersionTier } from './supportFloor.js';
2
+ const HOOKS_READER_GENERATIONS = Object.freeze([
3
+ Object.freeze({ from: '7.139.0', below: '7.147.0', notRead: Object.freeze(['onFailure']) }),
4
+ ]);
5
+ const HOOKS_READER_FAIL_CLOSED = Object.freeze([...new Set(HOOKS_READER_GENERATIONS.flatMap((g) => g.notRead))]);
6
+ export function hooksReaderNotReadKeys(serverVersion) {
7
+ for (const g of HOOKS_READER_GENERATIONS) {
8
+ if (engineVersionTier(serverVersion, g.from) === 'engine' && engineVersionTier(serverVersion, g.below) === 'legacy')
9
+ return g.notRead;
10
+ }
11
+ return HOOKS_READER_FAIL_CLOSED;
12
+ }