@sema-agent/client-core 0.95.0 → 0.97.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 (233) hide show
  1. package/CHANGELOG.md +269 -0
  2. package/README.md +61 -31
  3. package/dist/adapt/arms.js +7 -1
  4. package/dist/adapt/toolCards.d.ts +1 -0
  5. package/dist/adapt/toolCards.js +16 -3
  6. package/dist/adapt/wireShapes.js +2 -16
  7. package/dist/adapter/activeRunSelfHeal.d.ts +4 -2
  8. package/dist/adapter/activeRunSelfHeal.js +32 -6
  9. package/dist/adapter/downstream/eventToSdkMessage.js +11 -3
  10. package/dist/adapter/downstream/terminalToSdkResult.js +14 -11
  11. package/dist/adapter/runStream.js +30 -47
  12. package/dist/agentSession/backgroundView.js +2 -15
  13. package/dist/agentSession/engineAgentRegistryRead.js +5 -4
  14. package/dist/agentSession/engineSessionBackgroundStop.js +22 -15
  15. package/dist/agentsWireCaps.d.ts +1 -1
  16. package/dist/agentsWireCaps.js +29 -12
  17. package/dist/approvalDecidedByCapability.d.ts +2 -0
  18. package/dist/approvalDecidedByCapability.js +0 -1
  19. package/dist/approvalsStreamLiveCapability.d.ts +2 -0
  20. package/dist/approvalsStreamLiveCapability.js +3 -2
  21. package/dist/attachmentSeatCapability.d.ts +2 -0
  22. package/dist/attachmentSeatCapability.js +0 -1
  23. package/dist/callerCwdCapability.d.ts +2 -4
  24. package/dist/callerCwdCapability.js +0 -7
  25. package/dist/centerWiringCapability.d.ts +2 -4
  26. package/dist/centerWiringCapability.js +4 -9
  27. package/dist/classifierVerdictWire.js +4 -16
  28. package/dist/clientSlice.js +1 -1
  29. package/dist/cloudConfigWireCaps.d.ts +4 -4
  30. package/dist/cloudConfigWireCaps.js +241 -85
  31. package/dist/coreValuePorts.d.ts +11 -7
  32. package/dist/coreValuePorts.js +5 -8
  33. package/dist/credentialProvenance.d.ts +28 -0
  34. package/dist/credentialProvenance.js +76 -0
  35. package/dist/decideFailureNote.js +3 -1
  36. package/dist/decideReceipt.js +7 -10
  37. package/dist/declared/supportFloorDeclared.js +3 -3
  38. package/dist/deviceExecutorManagementCapability.d.ts +2 -0
  39. package/dist/deviceExecutorManagementCapability.js +3 -2
  40. package/dist/displayUntrusted.d.ts +10 -3
  41. package/dist/displayUntrusted.js +371 -60
  42. package/dist/engineCapReader.d.ts +8 -2
  43. package/dist/engineCapReader.js +15 -7
  44. package/dist/engineCapsCache.d.ts +17 -3
  45. package/dist/engineCapsCache.js +268 -43
  46. package/dist/engineDirectory.d.ts +79 -0
  47. package/dist/engineDirectory.js +186 -0
  48. package/dist/engineErrorCodes.d.ts +17 -1
  49. package/dist/engineErrorCodes.js +46 -16
  50. package/dist/engineNoticeCodes.d.ts +17 -0
  51. package/dist/engineNoticeCodes.js +54 -1
  52. package/dist/executionLaneCapability.d.ts +2 -0
  53. package/dist/executionLaneCapability.js +3 -2
  54. package/dist/fileHistoryCaptureCapability.d.ts +16 -0
  55. package/dist/fileHistoryCaptureCapability.js +98 -2
  56. package/dist/fileHistoryLegNotice.js +4 -0
  57. package/dist/fleet/bgSubagentRow.d.ts +15 -0
  58. package/dist/fleet/bgSubagentRow.js +67 -0
  59. package/dist/fleet/fleetLedger.d.ts +1 -0
  60. package/dist/fleet/fleetLedger.js +164 -126
  61. package/dist/fleet/fleetProjection.d.ts +1 -0
  62. package/dist/fleet/fleetProjection.js +10 -3
  63. package/dist/fleetTaskDesc.js +4 -17
  64. package/dist/gateOutcome.d.ts +12 -1
  65. package/dist/gateOutcome.js +56 -1
  66. package/dist/gateVocabulary.d.ts +2 -2
  67. package/dist/gateVocabulary.js +4 -3
  68. package/dist/generated/engineAskFaceTables.js +1 -1
  69. package/dist/generated/engineFactTables.d.ts +5 -3
  70. package/dist/generated/engineFactTables.js +17 -4
  71. package/dist/generated/engineNoticeTables.js +12 -3
  72. package/dist/generated/sdkFactTables.d.ts +2 -0
  73. package/dist/generated/sdkFactTables.js +7 -0
  74. package/dist/generated/settingsSchemaFns.d.ts +3 -0
  75. package/dist/generated/settingsSchemaFns.js +53 -0
  76. package/dist/generated/toolNameTables.d.ts +1 -1
  77. package/dist/generated/toolNameTables.js +1 -42
  78. package/dist/hitl/approvalDecisionNoteAudit.js +2 -2
  79. package/dist/hitl/approvalEndingCause.d.ts +2 -0
  80. package/dist/hitl/approvalEndingCause.js +26 -21
  81. package/dist/hitl/approvalHolds.d.ts +16 -1
  82. package/dist/hitl/approvalHolds.js +52 -12
  83. package/dist/hitl/askGateWire.js +1 -0
  84. package/dist/hitl/askParkRowRouting.js +1 -1
  85. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  86. package/dist/hitl/automaticAnswerVerdict.js +13 -10
  87. package/dist/hitl/decisionConflict.js +4 -5
  88. package/dist/hitl/decisionRefusal.d.ts +8 -0
  89. package/dist/hitl/decisionRefusal.js +55 -14
  90. package/dist/hitl/frameRouter.d.ts +3 -1
  91. package/dist/hitl/frameRouter.js +58 -39
  92. package/dist/hitl/gateLedger.d.ts +0 -5
  93. package/dist/hitl/gateLedger.js +0 -21
  94. package/dist/hitl/hitlBridge.d.ts +6 -2
  95. package/dist/hitl/hitlBridge.js +46 -11
  96. package/dist/hitl/localAllowRule.d.ts +1 -0
  97. package/dist/hitl/localAllowRule.js +2 -2
  98. package/dist/hitl/noAllowCause.d.ts +2 -1
  99. package/dist/hitl/noAllowCause.js +3 -2
  100. package/dist/hitl/parkResolver.d.ts +1 -0
  101. package/dist/hitl/parkResolver.js +14 -56
  102. package/dist/hitl/parkRowBirthWait.d.ts +1 -0
  103. package/dist/hitl/parkRowBirthWait.js +2 -2
  104. package/dist/hitl/planReviewWire.d.ts +1 -0
  105. package/dist/hitl/planReviewWire.js +98 -20
  106. package/dist/hitl/replayedApproval.d.ts +3 -3
  107. package/dist/hitl/replayedApproval.js +21 -16
  108. package/dist/hitl/sessionPolicyDeliverable.d.ts +0 -1
  109. package/dist/hitl/sessionPolicyDeliverable.js +17 -17
  110. package/dist/hitl/sessionPolicyWire.d.ts +2 -0
  111. package/dist/hitl/sessionPolicyWire.js +15 -3
  112. package/dist/hitl/standingRows.d.ts +19 -0
  113. package/dist/hitl/standingRows.js +148 -0
  114. package/dist/hitl/suspendedReopen.js +2 -1
  115. package/dist/hitl/toolApprovalWire.d.ts +22 -10
  116. package/dist/hitl/toolApprovalWire.js +286 -47
  117. package/dist/hookMatcherVerdict.d.ts +1 -9
  118. package/dist/hookMatcherVerdict.js +21 -61
  119. package/dist/hooksWireCaps.js +2 -2
  120. package/dist/hostCallbackGuard.d.ts +1 -0
  121. package/dist/hostCallbackGuard.js +6 -0
  122. package/dist/index.d.ts +26 -12
  123. package/dist/index.js +20 -10
  124. package/dist/liveModelCatalog.d.ts +13 -0
  125. package/dist/liveModelCatalog.js +56 -0
  126. package/dist/mcpProbeCapability.d.ts +2 -4
  127. package/dist/mcpProbeCapability.js +3 -8
  128. package/dist/mcpWireCaps.d.ts +1 -0
  129. package/dist/mcpWireCaps.js +4 -1
  130. package/dist/memoryComplianceCapability.d.ts +2 -4
  131. package/dist/memoryComplianceCapability.js +3 -8
  132. package/dist/memoryOriginCapability.d.ts +2 -4
  133. package/dist/memoryOriginCapability.js +3 -8
  134. package/dist/memorySpecWire.js +2 -2
  135. package/dist/modelIdentityResolvability.d.ts +2 -2
  136. package/dist/modelIdentityResolvability.js +14 -5
  137. package/dist/noticePresentation.d.ts +68 -0
  138. package/dist/noticePresentation.js +247 -0
  139. package/dist/outputStyleSeatCapability.d.ts +2 -1
  140. package/dist/outputStyleSeatCapability.js +0 -4
  141. package/dist/ownKey.d.ts +2 -0
  142. package/dist/ownKey.js +18 -0
  143. package/dist/peerFrames.js +13 -9
  144. package/dist/peerLaneCapability.d.ts +2 -0
  145. package/dist/peerLaneCapability.js +3 -2
  146. package/dist/permissionModesCapability.d.ts +2 -4
  147. package/dist/permissionModesCapability.js +0 -7
  148. package/dist/permissionRuleIssue.js +0 -3
  149. package/dist/permissionRulesWriteCapability.d.ts +2 -0
  150. package/dist/permissionRulesWriteCapability.js +3 -2
  151. package/dist/permissionsAllowGrantCapability.d.ts +2 -0
  152. package/dist/permissionsAllowGrantCapability.js +4 -2
  153. package/dist/permissionsDeclaration.d.ts +2 -0
  154. package/dist/permissionsDeclaration.js +72 -59
  155. package/dist/printPermissionOutcome.d.ts +12 -0
  156. package/dist/printPermissionOutcome.js +50 -0
  157. package/dist/printToolResultFrame.d.ts +1 -0
  158. package/dist/printToolResultFrame.js +5 -9
  159. package/dist/promptBlockedCopy.d.ts +5 -0
  160. package/dist/promptBlockedCopy.js +35 -0
  161. package/dist/protocolSegment.d.ts +9 -6
  162. package/dist/protocolSegment.js +31 -21
  163. package/dist/providerRejection.d.ts +4 -0
  164. package/dist/providerRejection.js +75 -14
  165. package/dist/replayBody.d.ts +12 -0
  166. package/dist/replayBody.js +25 -0
  167. package/dist/request/taskRequest.d.ts +1 -4
  168. package/dist/request/taskRequest.js +74 -19
  169. package/dist/resumeRefusalCopy.js +9 -25
  170. package/dist/retryStatus.d.ts +9 -1
  171. package/dist/retryStatus.js +9 -2
  172. package/dist/rewindArchiveCapability.d.ts +3 -13
  173. package/dist/rewindArchiveCapability.js +12 -58
  174. package/dist/rewindArchiveReading.d.ts +17 -0
  175. package/dist/rewindArchiveReading.js +55 -0
  176. package/dist/ruleCompileVerdict.d.ts +9 -1
  177. package/dist/ruleCompileVerdict.js +29 -4
  178. package/dist/runTerminal.d.ts +48 -5
  179. package/dist/runTerminal.js +215 -12
  180. package/dist/sandboxWire.js +13 -0
  181. package/dist/sdkWireTransit.d.ts +2 -1
  182. package/dist/sdkWireTransit.js +1 -0
  183. package/dist/sealedKeyCapability.d.ts +2 -4
  184. package/dist/sealedKeyCapability.js +0 -7
  185. package/dist/seam.js +4 -2
  186. package/dist/seatContract.d.ts +76 -11
  187. package/dist/seatContract.js +222 -27
  188. package/dist/sessionPolicyCapability.d.ts +2 -0
  189. package/dist/sessionPolicyCapability.js +3 -2
  190. package/dist/sessionReadRedactionCapability.d.ts +2 -4
  191. package/dist/sessionReadRedactionCapability.js +0 -7
  192. package/dist/sessionSyncFileHistory.d.ts +4 -0
  193. package/dist/sessionSyncFileHistory.js +66 -0
  194. package/dist/sessionSyncFileHistoryCapability.d.ts +2 -1
  195. package/dist/sessionSyncFileHistoryCapability.js +3 -5
  196. package/dist/skillToolListsCapability.d.ts +2 -1
  197. package/dist/skillToolListsCapability.js +3 -5
  198. package/dist/sqlEngineCapability.d.ts +2 -0
  199. package/dist/sqlEngineCapability.js +4 -3
  200. package/dist/steerPriorityCapability.d.ts +2 -1
  201. package/dist/steerPriorityCapability.js +3 -5
  202. package/dist/storePostureCapability.d.ts +2 -4
  203. package/dist/storePostureCapability.js +4 -9
  204. package/dist/subagent/engineSubagentSteer.js +7 -5
  205. package/dist/subagentContentStore.js +4 -15
  206. package/dist/supportFloor.d.ts +1 -0
  207. package/dist/supportFloor.js +13 -9
  208. package/dist/systemReminderTag.d.ts +1 -0
  209. package/dist/systemReminderTag.js +8 -0
  210. package/dist/toolHistoryBadTurn.js +3 -2
  211. package/dist/toolHistoryMismatch.d.ts +0 -3
  212. package/dist/toolHistoryMismatch.js +3 -7
  213. package/dist/toolResult.d.ts +12 -1
  214. package/dist/toolResult.js +55 -1
  215. package/dist/toolResultContent.d.ts +2 -0
  216. package/dist/toolResultContent.js +63 -0
  217. package/dist/usageAnalytics.js +40 -16
  218. package/dist/userInvokedSkillsCapability.d.ts +2 -1
  219. package/dist/userInvokedSkillsCapability.js +3 -5
  220. package/dist/webSearchBackendCapability.d.ts +2 -0
  221. package/dist/webSearchBackendCapability.js +3 -2
  222. package/dist/wireErrorTriage.d.ts +2 -0
  223. package/dist/wireErrorTriage.js +91 -38
  224. package/dist/wireFailureShape.d.ts +20 -3
  225. package/dist/wireFailureShape.js +128 -72
  226. package/dist/workflowsJournalCapability.d.ts +2 -0
  227. package/dist/workflowsJournalCapability.js +3 -2
  228. package/dist/writeProtectionCapability.d.ts +2 -0
  229. package/dist/writeProtectionCapability.js +3 -2
  230. package/docs/INTEGRATION-CLIENTS.md +1784 -20
  231. package/package.json +5 -5
  232. package/dist/engineRefusedToolName.d.ts +0 -1
  233. package/dist/engineRefusedToolName.js +0 -8
@@ -46,7 +46,7 @@ export interface PlanReviewOutcome {
46
46
  export declare const PLAN_REVIEW_MODE_AFTER_WORDS: readonly ["default", "acceptEdits"];
47
47
  export type PlanReviewModeAfter = (typeof PLAN_REVIEW_MODE_AFTER_WORDS)[number];
48
48
  export declare function isPlanReviewModeAfter(v: string | undefined): v is PlanReviewModeAfter;
49
- export declare const GATE_FAILURE_CODES: readonly ["no_pending", "binding_mismatch", "wrong_gate", "bad_plan_edit", "bad_plan_mode", "empty_answer"];
49
+ export declare const GATE_FAILURE_CODES: readonly ["no_pending", "binding_mismatch", "wrong_gate", "bad_plan_edit", "bad_plan_mode", "empty_answer", "binding_missing"];
50
50
  export type HitlSafetyCode = (typeof GATE_FAILURE_CODES)[number];
51
51
  export type HitlFailureStage = 'fetch' | 'input' | 'card' | 'decide' | 'orchestration';
52
52
  export declare const NO_APPROVAL_CARD_PORT_REASON = "no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)";
@@ -76,6 +76,10 @@ export type WireErrorCodeReading = {
76
76
  readonly state: 'unreadable';
77
77
  };
78
78
  export declare function readWireErrorCodeReading(e: unknown): WireErrorCodeReading;
79
+ export declare function readWireCodes(e: unknown): {
80
+ readonly errorCode?: string;
81
+ readonly blockedBy?: string;
82
+ };
79
83
  export declare function __setDecideTimeoutRetryBudgetForTests(ms?: number): void;
80
84
  export declare class DecideTransportRetryExhaustedError extends Error {
81
85
  readonly attempts: number;
@@ -116,7 +120,7 @@ export type FindPendingOutcome = {
116
120
  } | {
117
121
  ok: false;
118
122
  reason: string;
119
- code?: 'no_pending';
123
+ code?: 'no_pending' | 'wrong_gate';
120
124
  };
121
125
  export declare function findPendingForTask(client: HitlClientLike, taskId: string, matches: (toolName: string | undefined) => boolean, opts?: {
122
126
  signal?: AbortSignal;
@@ -3,9 +3,9 @@ import { eventSeq } from '../adapter/types.js';
3
3
  import { hostLog } from '../host.js';
4
4
  import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
5
5
  import { abortableSleep } from '../abortableSleep.js';
6
- import { readOwnDataValue } from '../ownKey.js';
7
- import { THROWN_SLOT_UNREADABLE, readThrownSlot, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
8
- import { decisionConflictOf } from './decisionConflict.js';
6
+ import { ownDataField, readOwnDataValue } from '../ownKey.js';
7
+ import { THROWN_SLOT_UNREADABLE, readBlockedBy, readThrownSlot, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
8
+ import { RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
9
9
  import { suspendedReopenOf } from './suspendedReopen.js';
10
10
  export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
11
11
  const MAX_DENY_REASON_CHARS = 4096;
@@ -29,14 +29,15 @@ export const PLAN_REVIEW_MODE_AFTER_WORDS = Object.freeze(['default', 'acceptEdi
29
29
  export function isPlanReviewModeAfter(v) {
30
30
  return v !== undefined && PLAN_REVIEW_MODE_AFTER_WORDS.includes(v);
31
31
  }
32
- export const GATE_FAILURE_CODES = [
32
+ export const GATE_FAILURE_CODES = Object.freeze([
33
33
  'no_pending',
34
34
  'binding_mismatch',
35
35
  'wrong_gate',
36
36
  'bad_plan_edit',
37
37
  'bad_plan_mode',
38
38
  'empty_answer',
39
- ];
39
+ 'binding_missing',
40
+ ]);
40
41
  export const NO_APPROVAL_CARD_PORT_REASON = 'no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)';
41
42
  export const APPROVAL_CARD_PORT_KEY_MISMATCH_REASON = 'no approval card port installed for the default session key, but a non-default sessionKey has one — likely a keyed-install/default-read mismatch (installApprovalCardPortFor vs the zero-arg default-key read), not a missing install';
42
43
  export const NO_QUESTION_OVERLAY_REASON = 'no question overlay mounted (print/non-REPL mode)';
@@ -54,7 +55,13 @@ export class HitlSafetyError extends Error {
54
55
  export function hitlSafetyCodeOf(e) {
55
56
  if (!thrownIsInstanceOf(e, HitlSafetyError))
56
57
  return undefined;
57
- const raw = readThrownSlot(e, 'code');
58
+ let raw;
59
+ try {
60
+ raw = ownDataField(e, 'code');
61
+ }
62
+ catch {
63
+ return undefined;
64
+ }
58
65
  return typeof raw === 'string' && GATE_FAILURE_CODES.includes(raw) ? raw : undefined;
59
66
  }
60
67
  const _gateCurrentPendingReadonlyPin = true;
@@ -87,11 +94,28 @@ export function readWireErrorCode(e) {
87
94
  export function readWireErrorCodeReading(e) {
88
95
  if (e === null || e === undefined)
89
96
  return { state: 'absent' };
90
- const raw = readThrownSlot(e, 'errorCode');
91
- if (raw === THROWN_SLOT_UNREADABLE)
97
+ let raw;
98
+ try {
99
+ if (typeof e === 'object' || typeof e === 'function') {
100
+ raw = ownDataField(e, 'errorCode');
101
+ if (raw === undefined && !Object.hasOwn(e, 'errorCode'))
102
+ void e.errorCode;
103
+ }
104
+ }
105
+ catch {
92
106
  return { state: 'unreadable' };
107
+ }
93
108
  return typeof raw === 'string' && raw.length > 0 ? { state: 'code', code: raw } : { state: 'absent' };
94
109
  }
110
+ export function readWireCodes(e) {
111
+ const reading = readWireErrorCodeReading(e);
112
+ if (reading.state !== 'code')
113
+ return {};
114
+ if (reading.code !== RESUME_POLICY_FOLD_CODE)
115
+ return { errorCode: reading.code };
116
+ const folded = readBlockedBy(e);
117
+ return folded.kind === 'named' ? { errorCode: reading.code, blockedBy: folded.code } : { errorCode: reading.code };
118
+ }
95
119
  const DECIDE_TRANSPORT_RETRY_BACKOFF_MS = 750;
96
120
  const DECIDE_RETRY_BACKOFF_MAX_MS = 5_000;
97
121
  const DECIDE_ATTEMPT_TIMEOUT_CAP_MS = 60_000;
@@ -224,10 +248,21 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
224
248
  hostLog('debug', `hitlBridge: approvals.list failed: ${t.log}`);
225
249
  return { ok: false, reason: `approvals.list failed: ${t.reason}` };
226
250
  }
227
- const decidable = rows.filter((r) => askParkForeignGateKind(r) === null);
251
+ const readings = rows.map((r) => ({ r, foreign: askParkForeignGateKind(r) }));
252
+ const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
228
253
  const identityRow = callId !== undefined
229
254
  ? decidable.find((r) => r.taskId === taskId && (r.boundCallId === callId || r.toolCallId === callId))
230
255
  : undefined;
256
+ const identityForeign = callId !== undefined && identityRow === undefined
257
+ ? readings.find((x) => x.r.taskId === taskId && (x.r.boundCallId === callId || x.r.toolCallId === callId) && x.foreign !== null)
258
+ : undefined;
259
+ if (identityForeign !== undefined) {
260
+ return {
261
+ ok: false,
262
+ reason: `the pending row this park names (call ${callId}) is parked on a '${String(identityForeign.foreign)}' gate — not an approval this leg decides; refusing to decide a different row`,
263
+ code: 'wrong_gate',
264
+ };
265
+ }
231
266
  const identityConflicts = identityRow !== undefined && !identityAccepts(identityRow);
232
267
  const pool = identityConflicts ? decidable.filter((r) => identityAccepts(r)) : decidable;
233
268
  const pending = (identityConflicts ? undefined : identityRow) ??
@@ -286,7 +321,7 @@ export class HitlBridge {
286
321
  if (p.boundCallId === undefined || p.boundInputHash === undefined) {
287
322
  throw new HitlSafetyError(`pending checkpoint ${p.id ?? '<no-id>'} is missing D-1 binding fields ` +
288
323
  `(boundCallId=${String(p.boundCallId)}, boundInputHash=${String(p.boundInputHash)}) — ` +
289
- 'pre-D-1 workers are no longer supported (server 5.x requires the verbatim two-tuple echo)', 'binding_mismatch');
324
+ 'pre-D-1 workers are no longer supported (server 5.x requires the verbatim two-tuple echo)', 'binding_missing');
290
325
  }
291
326
  return { boundCallId: p.boundCallId, boundInputHash: p.boundInputHash };
292
327
  }
@@ -500,7 +535,7 @@ function bindingMismatchReading(e) {
500
535
  const name = readThrownSlot(e, 'name');
501
536
  const family = (code.state === 'code' && code.code === 'approval_binding_mismatch') || name === 'ApprovalBindingMismatchError';
502
537
  if (family)
503
- return decisionConflictOf(e) !== 'answer_invalid' ? 'mismatch' : 'not';
538
+ return 'mismatch';
504
539
  if (code.state === 'unreadable')
505
540
  return 'unreadable';
506
541
  return 'not';
@@ -6,6 +6,7 @@ export interface LocalAllowRuleDeps {
6
6
  bashRuleContentHasDangerousBarePrefix(ruleContent: string): boolean;
7
7
  isDangerousBashPermission(toolName: string, ruleContent: string): boolean;
8
8
  isDangerousPowerShellPermission(toolName: string, ruleContent: string): boolean;
9
+ onFailure?(log: string): void;
9
10
  }
10
11
  export type LocalAllowRuleParse = {
11
12
  ok: true;
@@ -1,4 +1,4 @@
1
- import { wireFailureTextsOf } from '../wireFailureShape.js';
1
+ import { reasonOfThrownWithSink } from '../hostCallbackGuard.js';
2
2
  const BASH_TOOL_NAME_FOR_RULES = 'Bash';
3
3
  export function parseLocalAllowRule(rule, expectedToolName, deps) {
4
4
  if (typeof rule !== 'string' || rule === '') {
@@ -9,7 +9,7 @@ export function parseLocalAllowRule(rule, expectedToolName, deps) {
9
9
  value = deps.parseRule(rule);
10
10
  }
11
11
  catch (e) {
12
- return { ok: false, error: `rule text did not parse (${wireFailureTextsOf(e).reason})` };
12
+ return { ok: false, error: `rule text did not parse (${reasonOfThrownWithSink(e, (log) => deps.onFailure?.(log))})` };
13
13
  }
14
14
  if (typeof value.toolName !== 'string' || value.toolName === '') {
15
15
  return { ok: false, error: 'rule text carries no tool name' };
@@ -1,9 +1,10 @@
1
- export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'budget' | 'other';
1
+ export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'engine_ask_rule' | 'budget' | 'other';
2
2
  export interface RuleLadderNoAllowFacts {
3
3
  readonly allowed?: boolean;
4
4
  readonly denied?: boolean;
5
5
  readonly askRuleMatched?: boolean;
6
6
  readonly budgetExhausted?: boolean;
7
7
  readonly noRuleMatched?: boolean;
8
+ readonly noAskRuleMatched?: boolean;
8
9
  }
9
10
  export declare function noAllowCauseOf(card: object | null | undefined, ladder: RuleLadderNoAllowFacts | null | undefined): NoAllowCause | undefined;
@@ -18,8 +18,9 @@ export function noAllowCauseOf(card, ladder) {
18
18
  if (ladderSays(ladder, 'budgetExhausted'))
19
19
  return 'budget';
20
20
  const standing = automaticAnswerVerdict(card, 'standing_rule');
21
- if (standing.kind === 'must_ask' && (standing.askClass === 'ask_rule' || standing.origin === 'ask_rule'))
22
- return 'ask_rule';
21
+ if (standing.kind === 'must_ask' && (standing.askClass === 'ask_rule' || standing.origin === 'ask_rule')) {
22
+ return ladderSays(ladder, 'noAskRuleMatched') || ladderSays(ladder, 'noRuleMatched') ? 'engine_ask_rule' : 'ask_rule';
23
+ }
23
24
  if (ladderSays(ladder, 'noRuleMatched') && standing.kind === 'may_answer')
24
25
  return 'no_allow_rule';
25
26
  return 'other';
@@ -28,6 +28,7 @@ export type GateOutcome = {
28
28
  reason: string;
29
29
  code?: GateFailureCode | undefined;
30
30
  errorCode?: string | undefined;
31
+ blockedBy?: string | undefined;
31
32
  retriable?: true;
32
33
  retryExhausted?: true;
33
34
  currentPending?: GateCurrentPending | undefined;
@@ -1,8 +1,8 @@
1
1
  import { putOwnKey, readOwnDataValue } from '../ownKey.js';
2
- import { ApprovalStaleError, NotFoundError, ParkedResumeStartupError } from '@sema-agent/sdk';
3
- import { DecideTransportRetryExhaustedError, GATE_FAILURE_CODES, HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, hitlSafetyCodeOf, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
4
- import { ASK_GONE, CHECKPOINT_UNSUPPORTED_VERSION, DECIDE_WORKFLOW_PARK_FORKED, DECIDE_WORKFLOW_PARKS_UNREADABLE, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideAlreadyResolvedCode, isDecideParkMovedCode, refusalSaysStillPending } from '../engineErrorCodes.js';
5
- import { isHitlSafetyErrorLike } from './parkOwnership.js';
2
+ import { DecideTransportRetryExhaustedError, GATE_FAILURE_CODES, HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, hitlSafetyCodeOf, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireCodes, } from './hitlBridge.js';
3
+ import { DECIDE_WORKFLOW_PARK_FORKED, DECIDE_WORKFLOW_PARKS_UNREADABLE, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, RESUME_REOPEN_CODES, RESUME_RETRY_LATER_CODES, isRefusedParkCode, refusalSaysStillPending } from '../engineErrorCodes.js';
4
+ import { RESUME_REFUSAL_CODES } from '../resumeRefusalCopy.js';
5
+ import { readDecisionRefusalAsk } from './decisionRefusal.js';
6
6
  import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
7
7
  import { hostLog } from '../host.js';
8
8
  import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
@@ -35,67 +35,25 @@ function alreadyResolvedReasonToken(reason) {
35
35
  }
36
36
  export function isAlreadyResolvedFailure(failure) {
37
37
  try {
38
- return alreadyResolvedFailureVerdict(failure);
38
+ const r = readDecisionRefusalAsk(failure);
39
+ return r.reading === 'settled' || (r.reading === 'not_settled' && (r.gateMoved || r.wordingHint));
39
40
  }
40
41
  catch {
41
42
  return false;
42
43
  }
43
44
  }
44
- const ALREADY_RESOLVED_TYPED_ERROR_NAMES = Object.freeze([ApprovalStaleError.name, NotFoundError.name]);
45
- const PARKED_RESUME_STARTUP_TYPED_ERROR_NAME = ParkedResumeStartupError.name;
46
- function decideFailureFactsOf(failure) {
47
- if (typeof failure !== 'object' || failure === null)
48
- return undefined;
49
- const o = failure;
50
- const ec = o.errorCode;
51
- const errorCode = typeof ec === 'string' && ec.length > 0 ? ec : undefined;
52
- const retriable = refusalSaysStillPending(failure);
53
- if (Object.hasOwn(failure, 'kind') && o.kind === 'failed') {
54
- const safety = isGateFailureCode(o.safetyCode) ? o.safetyCode : undefined;
55
- const code = isGateFailureCode(o.code) ? o.code : safety;
56
- return { code, safetySignal: safety !== undefined, errorCode, retriable, typedErrorName: undefined, reason: o.reason };
57
- }
58
- const name = o.name;
59
- return {
60
- code: isGateFailureCode(o.code) ? o.code : undefined,
61
- safetySignal: isHitlSafetyErrorLike(failure),
62
- errorCode,
63
- retriable,
64
- typedErrorName: typeof name === 'string' ? name : undefined,
65
- reason: String(failure),
66
- };
67
- }
68
- function alreadyResolvedFailureVerdict(failure) {
69
- const f = decideFailureFactsOf(failure);
70
- if (f === undefined)
71
- return false;
72
- if (f.code !== undefined)
73
- return f.code === 'no_pending';
74
- if (f.safetySignal)
75
- return false;
76
- if (isDecideAlreadyResolvedCode(f.errorCode))
77
- return true;
78
- if (f.typedErrorName !== undefined && ALREADY_RESOLVED_TYPED_ERROR_NAMES.includes(f.typedErrorName))
79
- return true;
80
- if (f.typedErrorName === PARKED_RESUME_STARTUP_TYPED_ERROR_NAME && !isDecideParkMovedCode(f.errorCode))
81
- return false;
82
- if (isCodeClassifiedGateFailure(f.errorCode, f.retriable))
83
- return false;
84
- if (isDecideParkMovedCode(f.errorCode))
85
- return true;
86
- if (f.errorCode === ASK_GONE)
87
- return true;
88
- return isAlreadyResolvedGateReason(f.reason);
89
- }
90
45
  export function isGateStandingErrorCode(code, retriable) {
91
46
  return (code === RESUME_POLICY_FOLD_CODE ||
92
47
  code === 'corrupt' ||
93
- (code === CHECKPOINT_UNSUPPORTED_VERSION && retriable === true) ||
48
+ (isRefusedParkCode(code) && retriable === true) ||
49
+ (typeof code === 'string' && RESUME_REOPEN_CODES.includes(code) && retriable === true) ||
94
50
  code === DECIDE_WORKFLOW_PARKS_UNREADABLE ||
95
51
  code === DECIDE_WORKFLOW_PARK_FORKED);
96
52
  }
97
53
  export function isCodeClassifiedGateFailure(code, retriable) {
98
- return isGateStandingErrorCode(code, retriable) || code === PARKED_RESUME_STARTUP_FAILED;
54
+ return (isGateStandingErrorCode(code, retriable) ||
55
+ code === PARKED_RESUME_STARTUP_FAILED ||
56
+ (typeof code === 'string' && (RESUME_RETRY_LATER_CODES.includes(code) || RESUME_REFUSAL_CODES.includes(code))));
99
57
  }
100
58
  export function toAnsweredOutput(questions, answer) {
101
59
  const answers = {};
@@ -193,7 +151,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
193
151
  catch (e) {
194
152
  const code = hitlSafetyCodeOf(e);
195
153
  const currentPending = readDecideCurrentPending(e);
196
- const wireCode = readWireErrorCode(e);
154
+ const wireCodes = readWireCodes(e);
197
155
  const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
198
156
  const failureTexts = wireFailureTextsOf(e);
199
157
  hostLog('debug', `liveHitlAskWire: decide failed: ${failureTexts.log}`);
@@ -205,7 +163,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
205
163
  ...(code !== undefined ? { code } : {}),
206
164
  ...(thrownIsInstanceOf(e, DecideTransportRetryExhaustedError) ? { retryExhausted: true } : {}),
207
165
  ...(currentPending !== undefined ? { currentPending } : {}),
208
- ...(wireCode !== undefined ? { errorCode: wireCode } : {}),
166
+ ...wireCodes,
209
167
  ...(wireRetriable ? { retriable: true } : {}),
210
168
  };
211
169
  }
@@ -235,7 +193,7 @@ async function surfaceParkGate(park, ctx, gatedCallId, onPresented) {
235
193
  ...(onPresented ? { onPresented } : {}),
236
194
  ...(deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : {}),
237
195
  ...(decidedByReading !== undefined ? { approvalDecidedBy: decidedByReading } : {}),
238
- }, taskId, argsByCallOf(led), ctx.signal, gatedCallId);
196
+ }, taskId, argsByCallOf(led), ctx.signal, gatedCallId, park.toolName);
239
197
  const outcomeDenied = 'denied' in outcome ? outcome.denied : undefined;
240
198
  if (outcome.kind === 'decided' && outcomeDenied === true && outcome.gatedCallId) {
241
199
  const denySettledBy = 'denySettledBy' in outcome ? outcome.denySettledBy : undefined;
@@ -35,5 +35,6 @@ export interface ParkRowBirthWaitDeps<Row> {
35
35
  now?: () => number;
36
36
  sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
37
37
  signal?: AbortSignal;
38
+ onFailure?: (log: string) => void;
38
39
  }
39
40
  export declare function waitForParkRowBirth<Row>(deps: ParkRowBirthWaitDeps<Row>): Promise<ParkRowWaitVerdict<Row>>;
@@ -1,5 +1,5 @@
1
1
  import { abortableSleep } from '../abortableSleep.js';
2
- import { wireFailureTextsOf } from '../wireFailureShape.js';
2
+ import { reasonOfThrownWithSink } from '../hostCallbackGuard.js';
3
3
  function parkRowPollDelay(ms, signal) {
4
4
  return abortableSleep(ms, signal ?? new AbortController().signal);
5
5
  }
@@ -17,7 +17,7 @@ export async function waitForParkRowBirth(deps) {
17
17
  return await deps.probe(attempt, signal);
18
18
  }
19
19
  catch (e) {
20
- return { kind: 'unborn', reason: `probe threw: ${wireFailureTextsOf(e).reason}` };
20
+ return { kind: 'unborn', reason: `probe threw: ${reasonOfThrownWithSink(e, (log) => deps.onFailure?.(log))}` };
21
21
  }
22
22
  };
23
23
  const probeWithinRemaining = async (attempt, remainingMs) => {
@@ -62,6 +62,7 @@ export interface PlanReviewDeliveryOptions {
62
62
  }
63
63
  export interface DecidePlanReviewOptions extends PlanReviewDeliveryOptions {
64
64
  reason?: string;
65
+ parkedResult?: unknown;
65
66
  }
66
67
  export interface PlanReviewDecisionHandle {
67
68
  settle(): void;
@@ -13,7 +13,7 @@ import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
13
13
  import { planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
14
14
  import { notePlanReviewAnswered, notePlanReviewAnsweredFor, planReviewArmedKeyFor, registerArmedGateFor, waitForGateArmedFor, wasGateArmedFor, } from './armedGateRegistry.js';
15
15
  import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
16
- import { PLAN_REVIEW_GATE_KIND } from '../adapter/activeRunSelfHeal.js';
16
+ import { PLAN_REVIEW_GATE_KIND, durableDecideLegOf } from '../adapter/activeRunSelfHeal.js';
17
17
  import { readRunTerminal, runTerminalGateKind } from '../runTerminal.js';
18
18
  const PLAN_REVIEW_RESUME_TIMEOUT_MS = 6 * 60 * 60_000;
19
19
  export const PLAN_REVIEW_APPROVE_LABEL = 'Yes, approve and run the plan';
@@ -130,6 +130,18 @@ function pausedCheckpointIdOf(carrier) {
130
130
  return undefined;
131
131
  }
132
132
  }
133
+ function pausedGateKindOf(carrier) {
134
+ try {
135
+ const read = readRunTerminal(carrier);
136
+ if (read === null || read.kind !== 'paused')
137
+ return undefined;
138
+ const kind = runTerminalGateKind(read);
139
+ return typeof kind === 'string' && kind.length > 0 ? kind : undefined;
140
+ }
141
+ catch {
142
+ return undefined;
143
+ }
144
+ }
133
145
  const armedPlanReviewTaskIds = new Set();
134
146
  const offerModeAfterByTask = new Map();
135
147
  export function _resetArmedPlanReviewsForTest() {
@@ -470,7 +482,53 @@ export function __resetPlanReviewDecisionLatchForTests() {
470
482
  planReviewDecisionOps.length = 0;
471
483
  }
472
484
  export async function decidePlanReview(taskId, decision, permissionModeAfter, opts) {
473
- return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, undefined);
485
+ return decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, parkedGateIdentityOf(opts));
486
+ }
487
+ function parkedGateIdentityOf(opts) {
488
+ try {
489
+ return opts !== null && typeof opts === 'object' ? pausedCheckpointIdOf(opts.parkedResult) : undefined;
490
+ }
491
+ catch {
492
+ return undefined;
493
+ }
494
+ }
495
+ function planReviewSettledVerdict(r) {
496
+ const status = r.postStatus ?? r.bodyStatus;
497
+ const gateMoved = r.gateBefore !== undefined && r.gateAfter !== undefined && r.gateAfter !== r.gateBefore;
498
+ if (r.reparked)
499
+ return status !== 'completed' && !gateMoved ? { effect: 'still_parked', proven: true } : { effect: 'unconfirmed', proven: false };
500
+ if (status === 'completed')
501
+ return { effect: 'took_effect', proven: false };
502
+ const newGateHasCard = () => {
503
+ const leg = durableDecideLegOf({ gateKind: r.gateKindAfter ?? null });
504
+ return leg === 'approval' || leg === 'plan_review';
505
+ };
506
+ if (status === 'suspended' && r.postStatus === 'suspended' && newGateHasCard())
507
+ return { effect: 'advanced', proven: false };
508
+ if (status === 'needs_review' && r.postStatus === 'needs_review' && r.gateBefore !== undefined && r.gateAfter !== undefined) {
509
+ if (r.gateAfter === r.gateBefore)
510
+ return { effect: 'still_parked', proven: true };
511
+ if (newGateHasCard())
512
+ return { effect: 'advanced', proven: false };
513
+ }
514
+ return { effect: 'unconfirmed', proven: false };
515
+ }
516
+ function bodyWordOf(body, key) {
517
+ try {
518
+ const v = body !== null && typeof body === 'object' ? body[key] : undefined;
519
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
520
+ }
521
+ catch {
522
+ return undefined;
523
+ }
524
+ }
525
+ function bodySaysReparked(body) {
526
+ try {
527
+ return body !== null && typeof body === 'object' && body.retriable === true;
528
+ }
529
+ catch {
530
+ return false;
531
+ }
474
532
  }
475
533
  async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts, gateBefore) {
476
534
  if (typeof taskId === 'string' && planReviewDecisionOps.some((op) => op.taskId === taskId && op.phase === 'posting')) {
@@ -586,6 +644,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
586
644
  hostLog('debug', `planReviewWire: ${decision} → ok (dispatch #${dispatchNo}) ${JSON.stringify(body).slice(0, 200)}`);
587
645
  let postStatus;
588
646
  let gateAfter;
647
+ let gateKindAfter;
589
648
  try {
590
649
  const probe = clients.repull();
591
650
  if (probe) {
@@ -601,35 +660,54 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
601
660
  else {
602
661
  post = (await probe.runs.get(taskId));
603
662
  }
604
- postStatus = typeof post?.status === 'string' ? post.status : undefined;
663
+ postStatus = bodyWordOf(post, 'status');
605
664
  gateAfter = pausedCheckpointIdOf(post?.result);
665
+ gateKindAfter = pausedGateKindOf(post?.result);
606
666
  }
607
667
  }
608
668
  catch (e) {
609
669
  hostLog('debug', `planReviewWire: post-decide status re-pull failed (fail-soft): ${String(e)}`);
610
670
  }
611
- const effective = postStatus ?? (typeof body?.status === 'string' ? body.status : undefined);
671
+ const bodyStatus = bodyWordOf(body, 'status');
672
+ const effective = postStatus ?? bodyStatus;
612
673
  statusSeen = effective;
613
- const gateMoved = postStatus === 'needs_review' && gateBefore !== undefined && gateAfter !== undefined && gateAfter !== gateBefore;
614
- if (effective === 'needs_review' && gateMoved) {
615
- effect = 'advanced';
616
- outcome = planReviewAdvancedOutcome(decision, 'needs_review');
617
- }
618
- else if (effective === 'needs_review') {
619
- effect = 'still_parked';
620
- stillParkedProven = postStatus === 'needs_review' && gateBefore !== undefined && gateAfter !== undefined && gateAfter === gateBefore;
674
+ const verdict = planReviewSettledVerdict({ postStatus, bodyStatus, reparked: bodySaysReparked(body), gateBefore, gateAfter, gateKindAfter });
675
+ effect = verdict.effect;
676
+ stillParkedProven = verdict.proven;
677
+ const code = bodyWordOf(body, 'errorCode');
678
+ if (effect === 'still_parked' && postStatus === 'needs_review' && !bodySaysReparked(body)) {
621
679
  outcome =
622
680
  `The plan_review ${decision} returned HTTP 200 but the session is STILL locked on the same review gate ` +
623
681
  `(post-decide status: needs_review). The decision did NOT take effect (known engine issue, RB-471 family). ` +
624
682
  `Tell the user plainly that the ${decision} did not go through; the reliable exits today are approving the plan or cancelling the task.`;
625
683
  }
626
- else if (effective === 'suspended') {
627
- effect = 'advanced';
628
- outcome = planReviewAdvancedOutcome(decision, 'suspended');
684
+ else if (effect === 'still_parked') {
685
+ outcome =
686
+ `The plan_review ${decision} was NOT applied: the engine could not continue the task and put the plan back to wait for a decision` +
687
+ `${code !== undefined ? ` (engine code: ${code})` : ''}. Tell the user plainly that the ${decision} did not go through.`;
688
+ }
689
+ else if (effect === 'advanced') {
690
+ outcome = planReviewAdvancedOutcome(decision, effective === 'needs_review' ? 'needs_review' : 'suspended');
691
+ }
692
+ else if (effect === 'took_effect') {
693
+ outcome =
694
+ `The plan was ${decision === 'approve' ? 'approved and the parked task resumed to completion' : 'rejected (plan discarded)'} — final status: ${effective} ` +
695
+ `(${postStatus !== undefined ? 'post-decide re-checked: task left the review gate' : "from the decision's own reply; the post-decide re-check was unavailable"}).`;
696
+ }
697
+ else if (effective === 'needs_review' && (gateBefore === undefined || gateAfter === undefined || postStatus !== 'needs_review')) {
698
+ outcome =
699
+ `The plan_review ${decision} returned HTTP 200 and the task is waiting at a plan review gate again (post-decide status: needs_review), ` +
700
+ `but it could not be told whether this is the same review (the ${decision} did not take effect) or a new plan written after it (it did).`;
701
+ }
702
+ else if (effective === 'suspended' || effective === 'needs_review') {
703
+ outcome =
704
+ `The plan_review ${decision} was accepted by the engine (HTTP 200) and the task is paused again (post-decide status: ${effective}), ` +
705
+ `${gateKindAfter !== undefined ? `at a '${gateKindAfter}' pause that is not an approval card` : 'but the pause it stopped at was not reported'} — what the ${decision} led to could not be confirmed.`;
629
706
  }
630
707
  else if (effective !== undefined) {
631
- effect = 'took_effect';
632
- outcome = `The plan was ${decision === 'approve' ? 'approved and the parked task resumed to completion' : 'rejected (plan discarded)'} — final status: ${effective} (post-decide re-checked: task left the review gate).`;
708
+ outcome =
709
+ `The plan_review ${decision} was accepted by the engine (HTTP 200), but the task then reported status ${effective}` +
710
+ `${code !== undefined ? ` (engine code: ${code})` : ''} — not a completion; what the ${decision} led to could not be confirmed.`;
633
711
  }
634
712
  else {
635
713
  outcome = `The plan_review ${decision} was accepted by the engine (HTTP 200) but its effect is unconfirmed — final status: unknown (post-decide verification unavailable). Do not treat this as ${decision === 'approve' ? 'an approval that resumed the task' : 'a discarded plan'}.`;
@@ -678,10 +756,10 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
678
756
  outcome = `This ${decision} was not applied. ${corruptStoredRowContent(where)}`;
679
757
  }
680
758
  else if (typeof status === 'number') {
681
- const msg = e instanceof Error ? e.message : String(e);
682
- hostLog('debug', `planReviewWire: ${decision} → ${status} ${msg.slice(0, 200)}`);
759
+ const failureTexts = wireFailureTextsOf(e);
760
+ hostLog('debug', `planReviewWire: ${decision} → ${status} ${failureTexts.log.slice(0, 200)}`);
683
761
  effect = (status >= 400 && status < 500) || (typeof refusedCode === 'string' && isUnavailableFaceCode(refusedCode)) ? 'not_applied' : 'unconfirmed';
684
- outcome = `${`The plan_review decision failed: HTTP ${status} ${msg}`.trim()}${decideFailureNote(e)}`;
762
+ outcome = `${`The plan_review decision failed: HTTP ${status} ${failureTexts.reason}`.trim()}${decideFailureNote(e)}`;
685
763
  }
686
764
  else if (noAnswer === 'time-limit' || noAnswer === 'deadline') {
687
765
  hostLog('debug', `planReviewWire: ${decision} sent but no answer within the request time limit (task ${taskId})`);
@@ -1,4 +1,4 @@
1
- import { type ApprovalSettledHow } from './gateLedger.js';
1
+ import { type ApprovalSettledHow } from './approvalHolds.js';
2
2
  import { type ApprovalsListEnvelope, type CrashConvergedRow } from './crashConverged.js';
3
3
  export type ReplayedApprovalDisposition = {
4
4
  readonly kind: 'live';
@@ -20,10 +20,10 @@ export interface ReplayPendingBasis {
20
20
  } | {
21
21
  readonly read: 'failed';
22
22
  };
23
- readonly settled?: ReadonlyMap<string, ApprovalSettledHow>;
24
23
  }
25
- export declare function settledApprovalDisposition(frame: object, settled: ReplayPendingBasis['settled']): ReplayedApprovalDisposition | undefined;
24
+ export declare function settledApprovalDisposition(frame: object): ReplayedApprovalDisposition | undefined;
26
25
  export declare function replayedApprovalDisposition(frame: object, basis: ReplayPendingBasis): ReplayedApprovalDisposition;
26
+ export declare function replayedApprovalClosesCardFrame(disposition: ReplayedApprovalDisposition | null | undefined): boolean;
27
27
  export declare function replayedApprovalSentence(disposition: ReplayedApprovalDisposition | undefined, opts?: {
28
28
  readonly toolName?: string;
29
29
  }): string | undefined;
@@ -1,7 +1,9 @@
1
- import { APPROVAL_SETTLED_HOWS, hasLedgerPositionKey } from './gateLedger.js';
1
+ import { hasLedgerPositionKey } from './gateLedger.js';
2
+ import { anyApprovalSettled, settledApprovalOf } from './approvalHolds.js';
2
3
  import { readLivePendingRows } from './livePendingAsk.js';
3
4
  import { projectCrashConverged } from './crashConverged.js';
4
5
  import { quoteUntrustedForSentence } from '../displayUntrusted.js';
6
+ import { readOwnDataValue } from '../ownKey.js';
5
7
  const live = (basis) => Object.freeze({ kind: 'live', basis });
6
8
  const unknown = (why) => Object.freeze({ kind: 'unknown', why });
7
9
  const decided = (row) => Object.freeze(row === undefined
@@ -22,15 +24,12 @@ function crashConvergedFactsOf(body, approvalId) {
22
24
  }
23
25
  return undefined;
24
26
  }
25
- export function settledApprovalDisposition(frame, settled) {
27
+ export function settledApprovalDisposition(frame) {
26
28
  try {
27
- if (typeof settled !== 'object' || settled === null || typeof settled.get !== 'function')
29
+ if (!anyApprovalSettled())
28
30
  return undefined;
29
- const id = approvalIdOf(frame);
30
- if (id === undefined)
31
- return undefined;
32
- const how = settled.get(id);
33
- return typeof how === 'string' && APPROVAL_SETTLED_HOWS.includes(how) ? decidedSettled(how) : undefined;
31
+ const entry = settledApprovalOf(approvalIdOf(frame));
32
+ return entry === undefined ? undefined : decidedSettled(entry.how);
34
33
  }
35
34
  catch {
36
35
  return undefined;
@@ -47,14 +46,7 @@ export function replayedApprovalDisposition(frame, basis) {
47
46
  catch {
48
47
  return unknown('unreadable_frame');
49
48
  }
50
- let settledBasis;
51
- try {
52
- settledBasis = typeof basis === 'object' && basis !== null ? basis.settled : undefined;
53
- }
54
- catch {
55
- settledBasis = undefined;
56
- }
57
- const early = settledApprovalDisposition(frame, settledBasis);
49
+ const early = settledApprovalDisposition(frame);
58
50
  if (early !== undefined)
59
51
  return early;
60
52
  try {
@@ -162,6 +154,19 @@ const SETTLED_PHRASE = Object.freeze({
162
154
  });
163
155
  const settledRepeat = (what, how) => `${sentenceInitial(what)} was ${SETTLED_PHRASE[how]} earlier, so this repeated copy of it was not raised as a new question and no decision was sent for it.`;
164
156
  const decidedPlain = (what) => `${sentenceInitial(what)} is no longer listed by the engine as waiting for a decision, so no decision was sent for it.`;
157
+ export function replayedApprovalClosesCardFrame(disposition) {
158
+ try {
159
+ if (typeof disposition !== 'object' || disposition === null)
160
+ return false;
161
+ if (readOwnDataValue(disposition, 'kind') !== 'decided')
162
+ return false;
163
+ const cause = readOwnDataValue(disposition, 'cause');
164
+ return typeof cause === 'string' && Object.hasOwn(STOPPED_BY_CAUSE, cause);
165
+ }
166
+ catch {
167
+ return false;
168
+ }
169
+ }
165
170
  const unconfirmed = (what) => `Whether ${what} is still waiting for a decision could not be confirmed, so no decision was sent for it.`;
166
171
  function guardedGet(o, key) {
167
172
  try {
@@ -15,7 +15,6 @@ export interface SessionPolicyDeliverability {
15
15
  export interface SessionPolicyDeliverableOptions {
16
16
  readonly roster?: ToolRosterView;
17
17
  }
18
- export { isEngineRefusedToolName } from '../engineRefusedToolName.js';
19
18
  export declare function sessionPolicyDeliverable(behavior: 'deny' | 'ask', rules: readonly string[], opts?: SessionPolicyDeliverableOptions): SessionPolicyDeliverability;
20
19
  export declare function sessionPolicyWithheldNotice(why: SessionPolicyWithheldWhy): string;
21
20
  export declare function legacyToolNameFailureNoteOf(code: string | undefined): string | undefined;