@sema-agent/client-core 0.97.1 → 0.98.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 (281) hide show
  1. package/CHANGELOG.md +408 -0
  2. package/README.md +74 -37
  3. package/dist/adapt/arms.d.ts +1 -0
  4. package/dist/adapt/arms.js +128 -34
  5. package/dist/adapt/ids.d.ts +12 -1
  6. package/dist/adapt/ids.js +59 -8
  7. package/dist/adapt/instanceLedger.d.ts +50 -0
  8. package/dist/adapt/instanceLedger.js +147 -0
  9. package/dist/adapt/textStream.d.ts +1 -0
  10. package/dist/adapt/textStream.js +24 -7
  11. package/dist/adapt/toolCards.d.ts +27 -5
  12. package/dist/adapt/toolCards.js +39 -6
  13. package/dist/adapt/wireShapes.js +6 -2
  14. package/dist/adapt.d.ts +2 -0
  15. package/dist/adapt.js +8 -3
  16. package/dist/adapter/activeRunSelfHeal.d.ts +5 -0
  17. package/dist/adapter/activeRunSelfHeal.js +51 -28
  18. package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -5
  19. package/dist/adapter/downstream/eventToSdkMessage.js +27 -34
  20. package/dist/adapter/downstream/terminalToSdkResult.js +11 -4
  21. package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +5 -2
  22. package/dist/adapter/downstream/turnUsageToModelUsage.js +2 -0
  23. package/dist/adapter/runStream.js +29 -13
  24. package/dist/adapter/types.d.ts +4 -4
  25. package/dist/agentSession/backgroundView.js +2 -1
  26. package/dist/agentSession/engineAgentRegistryRead.d.ts +2 -2
  27. package/dist/agentSession/engineAgentRegistryRead.js +12 -13
  28. package/dist/agentsWireCaps.d.ts +8 -8
  29. package/dist/agentsWireCaps.js +168 -175
  30. package/dist/approvalDecidedByCapability.d.ts +0 -4
  31. package/dist/approvalDecidedByCapability.js +0 -6
  32. package/dist/approvalsStreamLiveCapability.d.ts +0 -4
  33. package/dist/approvalsStreamLiveCapability.js +0 -6
  34. package/dist/assistantInbox.d.ts +34 -0
  35. package/dist/assistantInbox.js +118 -0
  36. package/dist/centerWiringCapability.js +9 -1
  37. package/dist/clientSlice.d.ts +0 -9
  38. package/dist/clientSlice.js +0 -3
  39. package/dist/cloudConfigWireCaps.d.ts +4 -2
  40. package/dist/cloudConfigWireCaps.js +81 -61
  41. package/dist/compensations.js +1 -1
  42. package/dist/connectorStatus.d.ts +21 -0
  43. package/dist/connectorStatus.js +112 -0
  44. package/dist/controlRouter.d.ts +14 -1
  45. package/dist/controlRouter.js +68 -17
  46. package/dist/coreValuePorts.d.ts +3 -3
  47. package/dist/decideReceipt.d.ts +2 -2
  48. package/dist/decideReceipt.js +5 -2
  49. package/dist/declared/supportFloorDeclared.js +3 -3
  50. package/dist/deviceExecutorManagementCapability.d.ts +0 -4
  51. package/dist/deviceExecutorManagementCapability.js +0 -6
  52. package/dist/deviceRebind.d.ts +49 -1
  53. package/dist/deviceRebind.js +221 -3
  54. package/dist/displayUntrusted.d.ts +1 -0
  55. package/dist/displayUntrusted.js +250 -76
  56. package/dist/engineAgentAbsence.d.ts +1 -1
  57. package/dist/engineAgentPanelStore.d.ts +5 -4
  58. package/dist/engineAgentPanelStore.js +3 -12
  59. package/dist/engineCapReader.d.ts +0 -4
  60. package/dist/engineCapReader.js +1 -5
  61. package/dist/engineCapsCache.d.ts +4 -0
  62. package/dist/engineCapsCache.js +17 -12
  63. package/dist/engineClosedSets.d.ts +1 -0
  64. package/dist/engineClosedSets.js +4 -1
  65. package/dist/engineErrorCodes.d.ts +9 -2
  66. package/dist/engineErrorCodes.js +9 -1
  67. package/dist/engineIdentity.d.ts +1 -0
  68. package/dist/engineIdentity.js +8 -0
  69. package/dist/engineRowRef.d.ts +28 -0
  70. package/dist/engineRowRef.js +111 -0
  71. package/dist/engineSessionMessages.d.ts +67 -0
  72. package/dist/engineSessionMessages.js +364 -0
  73. package/dist/engineToolDirectory.d.ts +0 -9
  74. package/dist/engineToolDirectory.js +1 -94
  75. package/dist/engineUrl.d.ts +18 -0
  76. package/dist/engineUrl.js +52 -0
  77. package/dist/env/uiLanguage.d.ts +9 -0
  78. package/dist/env/uiLanguage.js +35 -0
  79. package/dist/eventLeg.d.ts +92 -0
  80. package/dist/eventLeg.js +285 -0
  81. package/dist/executionLaneCapability.d.ts +0 -4
  82. package/dist/executionLaneCapability.js +3 -16
  83. package/dist/fileHistoryCaptureCapability.d.ts +0 -4
  84. package/dist/fileHistoryCaptureCapability.js +5 -7
  85. package/dist/fleet/fleetLedger.d.ts +0 -3
  86. package/dist/fleet/fleetLedger.js +21 -36
  87. package/dist/fleet/fleetProjection.d.ts +2 -2
  88. package/dist/fleet/fleetProjection.js +10 -8
  89. package/dist/frameBinding.d.ts +5 -0
  90. package/dist/frameBinding.js +23 -0
  91. package/dist/gateFailureCodes.d.ts +2 -0
  92. package/dist/gateFailureCodes.js +15 -0
  93. package/dist/gateOutcome.d.ts +3 -1
  94. package/dist/gateOutcome.js +25 -6
  95. package/dist/gateVocabulary.d.ts +5 -0
  96. package/dist/gateVocabulary.js +64 -2
  97. package/dist/generated/engineAskFaceTables.js +2 -2
  98. package/dist/generated/engineFactTables.d.ts +6 -3
  99. package/dist/generated/engineFactTables.js +22 -4
  100. package/dist/generated/engineNoticeTables.js +9 -0
  101. package/dist/generated/toolNameTables.d.ts +1 -0
  102. package/dist/generated/toolNameTables.js +1 -0
  103. package/dist/hitl/allowRuleShadow.d.ts +6 -1
  104. package/dist/hitl/allowRuleShadow.js +21 -17
  105. package/dist/hitl/approvalCardReading.d.ts +15 -0
  106. package/dist/hitl/approvalCardReading.js +154 -0
  107. package/dist/hitl/approvalCardReasons.d.ts +13 -0
  108. package/dist/hitl/approvalCardReasons.js +120 -0
  109. package/dist/hitl/approvalHolds.d.ts +47 -15
  110. package/dist/hitl/approvalHolds.js +134 -28
  111. package/dist/hitl/approvalResolution.d.ts +51 -26
  112. package/dist/hitl/approvalResolution.js +86 -122
  113. package/dist/hitl/approvalsFeed.js +6 -18
  114. package/dist/hitl/askClassPair.d.ts +2 -0
  115. package/dist/hitl/askClassPair.js +1 -0
  116. package/dist/hitl/askGateWire.d.ts +20 -2
  117. package/dist/hitl/askGateWire.js +49 -1
  118. package/dist/hitl/askParkRowRouting.d.ts +5 -0
  119. package/dist/hitl/askParkRowRouting.js +32 -1
  120. package/dist/hitl/askSettlementLine.d.ts +18 -0
  121. package/dist/hitl/askSettlementLine.js +79 -0
  122. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  123. package/dist/hitl/automaticAnswerVerdict.js +21 -5
  124. package/dist/hitl/crashConverged.d.ts +11 -20
  125. package/dist/hitl/crashConverged.js +56 -0
  126. package/dist/hitl/crashConvergedNotice.d.ts +3 -1
  127. package/dist/hitl/crashConvergedNotice.js +48 -11
  128. package/dist/hitl/decisionConflict.js +3 -10
  129. package/dist/hitl/decisionRefusal.d.ts +40 -1
  130. package/dist/hitl/decisionRefusal.js +199 -42
  131. package/dist/hitl/frameRouter.d.ts +5 -2
  132. package/dist/hitl/frameRouter.js +20 -16
  133. package/dist/hitl/gateLedger.d.ts +2 -0
  134. package/dist/hitl/gateLedger.js +50 -13
  135. package/dist/hitl/hitlBridge.d.ts +1 -2
  136. package/dist/hitl/hitlBridge.js +10 -7
  137. package/dist/hitl/livePendingAsk.d.ts +3 -3
  138. package/dist/hitl/livePendingAsk.js +7 -18
  139. package/dist/hitl/parkResolver.d.ts +2 -24
  140. package/dist/hitl/parkResolver.js +61 -97
  141. package/dist/hitl/persistedRulesWire.d.ts +24 -0
  142. package/dist/hitl/persistedRulesWire.js +122 -2
  143. package/dist/hitl/planReviewWire.d.ts +26 -13
  144. package/dist/hitl/planReviewWire.js +112 -78
  145. package/dist/hitl/refusedToolNameProse.d.ts +0 -1
  146. package/dist/hitl/refusedToolNameProse.js +0 -3
  147. package/dist/hitl/replayedApproval.d.ts +4 -3
  148. package/dist/hitl/replayedApproval.js +7 -4
  149. package/dist/hitl/sessionPolicyDeliverable.js +2 -4
  150. package/dist/hitl/sessionPolicyWire.d.ts +32 -20
  151. package/dist/hitl/sessionPolicyWire.js +162 -52
  152. package/dist/hitl/standingRows.d.ts +20 -2
  153. package/dist/hitl/standingRows.js +69 -8
  154. package/dist/hitl/toolApprovalWire.d.ts +87 -152
  155. package/dist/hitl/toolApprovalWire.js +591 -380
  156. package/dist/hookMatcherVerdict.d.ts +6 -1
  157. package/dist/hookMatcherVerdict.js +14 -7
  158. package/dist/hooksWireCaps.d.ts +2 -2
  159. package/dist/hooksWireCaps.js +59 -55
  160. package/dist/i18n/messages.en.d.ts +121 -0
  161. package/dist/i18n/messages.en.js +119 -0
  162. package/dist/i18n/messages.zh.d.ts +3 -0
  163. package/dist/i18n/messages.zh.js +120 -0
  164. package/dist/i18n/translate.d.ts +25 -0
  165. package/dist/i18n/translate.js +127 -0
  166. package/dist/index.d.ts +53 -20
  167. package/dist/index.js +38 -11
  168. package/dist/inheritEnvCapability.d.ts +31 -0
  169. package/dist/inheritEnvCapability.js +104 -0
  170. package/dist/liveModelCatalog.d.ts +1 -2
  171. package/dist/liveModelCatalog.js +2 -6
  172. package/dist/liveQuestionStore.d.ts +2 -21
  173. package/dist/mcpLiveness.d.ts +2 -1
  174. package/dist/mcpProbeWire.d.ts +6 -1
  175. package/dist/mcpProbeWire.js +1 -1
  176. package/dist/mcpWireCaps.d.ts +13 -2
  177. package/dist/mcpWireCaps.js +48 -0
  178. package/dist/memoryEntriesWire.d.ts +4 -9
  179. package/dist/model/modelSupplyRules.d.ts +0 -2
  180. package/dist/model/modelSupplyRules.js +3 -12
  181. package/dist/model/modelsPolicy.d.ts +0 -5
  182. package/dist/model/modelsPolicy.js +6 -36
  183. package/dist/model/subagentModelWord.d.ts +31 -0
  184. package/dist/model/subagentModelWord.js +261 -0
  185. package/dist/notifications.d.ts +6 -0
  186. package/dist/notifications.js +71 -23
  187. package/dist/outputStyleSeatCapability.d.ts +0 -3
  188. package/dist/outputStyleSeatCapability.js +0 -3
  189. package/dist/ownKey.d.ts +15 -0
  190. package/dist/ownKey.js +60 -3
  191. package/dist/panelReduce.d.ts +15 -9
  192. package/dist/panelReduce.js +680 -549
  193. package/dist/panelStateHolder.d.ts +3 -7
  194. package/dist/panelStateHolder.js +14 -130
  195. package/dist/peerLaneCapability.d.ts +0 -4
  196. package/dist/peerLaneCapability.js +0 -6
  197. package/dist/permissionRulesWriteCapability.d.ts +0 -4
  198. package/dist/permissionRulesWriteCapability.js +0 -6
  199. package/dist/permissionWireCaps.d.ts +2 -2
  200. package/dist/permissionWireCaps.js +2 -0
  201. package/dist/permissionsAllowGrantCapability.d.ts +0 -4
  202. package/dist/permissionsAllowGrantCapability.js +0 -6
  203. package/dist/permissionsDeclaration.d.ts +0 -11
  204. package/dist/permissionsDeclaration.js +60 -53
  205. package/dist/pluginHooksWire.d.ts +1 -1
  206. package/dist/pluginHooksWire.js +3 -6
  207. package/dist/preemptWire.d.ts +44 -0
  208. package/dist/preemptWire.js +83 -0
  209. package/dist/printToolResultFrame.js +15 -4
  210. package/dist/replayBody.d.ts +3 -0
  211. package/dist/replayBody.js +14 -0
  212. package/dist/request/taskRequest.d.ts +0 -1
  213. package/dist/request/taskRequest.js +0 -3
  214. package/dist/resumeRefusalCopy.d.ts +2 -1
  215. package/dist/resumeRefusalCopy.js +38 -2
  216. package/dist/retryStatus.d.ts +8 -0
  217. package/dist/retryStatus.js +106 -1
  218. package/dist/rewindArchiveCapability.d.ts +0 -4
  219. package/dist/rewindArchiveCapability.js +2 -10
  220. package/dist/rewindArchiveReading.d.ts +0 -1
  221. package/dist/rewindArchiveReading.js +0 -3
  222. package/dist/runCancelContext.d.ts +2 -1
  223. package/dist/runCancelContext.js +4 -2
  224. package/dist/runReattachPolicy.d.ts +31 -0
  225. package/dist/runReattachPolicy.js +46 -0
  226. package/dist/runTerminal.d.ts +3 -6
  227. package/dist/runTerminal.js +8 -9
  228. package/dist/sandboxWire.d.ts +3 -1
  229. package/dist/sandboxWire.js +76 -27
  230. package/dist/scenarioWire.js +1 -1
  231. package/dist/seam.d.ts +24 -2
  232. package/dist/seam.js +15 -3
  233. package/dist/seatContract.d.ts +53 -10
  234. package/dist/seatContract.js +132 -81
  235. package/dist/sessionPolicyCapability.d.ts +0 -4
  236. package/dist/sessionPolicyCapability.js +0 -6
  237. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -3
  238. package/dist/sessionSyncFileHistoryCapability.js +0 -3
  239. package/dist/skillToolListsCapability.d.ts +0 -3
  240. package/dist/skillToolListsCapability.js +0 -3
  241. package/dist/sqlEngineCapability.d.ts +0 -4
  242. package/dist/sqlEngineCapability.js +0 -6
  243. package/dist/steerPriorityCapability.d.ts +0 -3
  244. package/dist/steerPriorityCapability.js +0 -3
  245. package/dist/streamDrainingLegs.d.ts +4 -0
  246. package/dist/streamDrainingLegs.js +6 -1
  247. package/dist/subagent/engineCompactWire.d.ts +0 -1
  248. package/dist/subagent/engineCompactWire.js +7 -5
  249. package/dist/subagent/engineTaskHandleWire.js +4 -7
  250. package/dist/subagentContentStore.js +1 -11
  251. package/dist/submitReopen.d.ts +24 -0
  252. package/dist/submitReopen.js +75 -0
  253. package/dist/terminalStatuses.d.ts +3 -0
  254. package/dist/terminalStatuses.js +10 -2
  255. package/dist/toolResult.d.ts +10 -1
  256. package/dist/toolResult.js +34 -6
  257. package/dist/toolRoster.d.ts +0 -2
  258. package/dist/toolRoster.js +0 -2
  259. package/dist/turnFailurePresentation.d.ts +28 -0
  260. package/dist/turnFailurePresentation.js +205 -0
  261. package/dist/userInvokedSkillsCapability.d.ts +0 -3
  262. package/dist/userInvokedSkillsCapability.js +0 -3
  263. package/dist/webSearchBackendCapability.d.ts +0 -4
  264. package/dist/webSearchBackendCapability.js +0 -6
  265. package/dist/wireErrorTriage.js +5 -24
  266. package/dist/wireFailureShape.d.ts +62 -5
  267. package/dist/wireFailureShape.js +105 -5
  268. package/dist/wireRefusalCopy.d.ts +12 -2
  269. package/dist/wireRefusalCopy.js +50 -52
  270. package/dist/workflowsJournalCapability.d.ts +0 -4
  271. package/dist/workflowsJournalCapability.js +0 -6
  272. package/dist/writeProtectionCapability.d.ts +0 -4
  273. package/dist/writeProtectionCapability.js +0 -6
  274. package/docs/INTEGRATION-CLIENTS.md +1253 -46
  275. package/package.json +5 -4
  276. package/dist/attachmentSeatCapability.d.ts +0 -12
  277. package/dist/attachmentSeatCapability.js +0 -23
  278. package/dist/hitl/approvalOutcomeNote.d.ts +0 -10
  279. package/dist/hitl/approvalOutcomeNote.js +0 -29
  280. package/dist/sessionReadRedactionCapability.d.ts +0 -13
  281. package/dist/sessionReadRedactionCapability.js +0 -35
@@ -4,12 +4,19 @@ import { resolvePark, nextHopBudget, stalledTerminal, MAX_GATE_HOPS } from './pa
4
4
  import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
5
5
  import { abortableSleep } from '../abortableSleep.js';
6
6
  import { hostLog } from '../host.js';
7
+ import { decideAcceptedNotResolved, readDecideReceipt } from '../decideReceipt.js';
8
+ import { APPROVAL_EXPIRED } from '../engineErrorCodes.js';
9
+ import { readDecisionRefusalAsk } from './decisionRefusal.js';
10
+ import { resumeReopenFromError } from './suspendedReopen.js';
11
+ import { refusedParkFromError, resumeRefusalFromError } from '../resumeRefusalCopy.js';
12
+ import { guardedRead, readOwnDataValue } from '../ownKey.js';
13
+ import { readToolApprovalRespondParking } from './toolApprovalWire.js';
7
14
  export { HITL_REJECT_MESSAGE, ENGINE_ABORT_TOOL_RESULT } from './frameRouter.js';
8
15
  export { HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE } from './frameRouter.js';
9
16
  export { HITL_POLICY_DENY_MESSAGE } from './frameRouter.js';
10
17
  export { SEMA_COLLATERAL_ABORT_KEY } from './gateLedger.js';
11
18
  export { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
12
- export { toAnsweredOutput, isAlreadyResolvedFailure, isAlreadyResolvedGateReason, isGateStandingErrorCode, isCodeClassifiedGateFailure, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
19
+ export { toAnsweredOutput, isAlreadyResolvedFailure, nextHopBudget, MAX_GATE_HOPS, MAX_TOTAL_PARKS } from './parkResolver.js';
13
20
  export { GATE_FAILURE_CODES } from './hitlBridge.js';
14
21
  export async function* bridgeAskUserQuestionGates(source, deps, opts) {
15
22
  const led = createGateLedger();
@@ -58,6 +65,7 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
58
65
  }
59
66
  park = {
60
67
  gate: action.gate,
68
+ parkFrame: action.parkFrame,
61
69
  ...(action.pendingDone !== undefined ? { pendingDone: action.pendingDone } : {}),
62
70
  ...(action.gatedCallId !== undefined ? { gatedCallId: action.gatedCallId } : {}),
63
71
  ...(action.toolName !== undefined ? { toolName: action.toolName } : {}),
@@ -152,3 +160,43 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
152
160
  });
153
161
  }
154
162
  }
163
+ export const LATE_DECIDE_OUTCOMES = Object.freeze(['accepted-not-resolved', 'resumed', 'expired-reaped', 'settled-elsewhere', 'unknown']);
164
+ const LATE_DECIDE_TASK_ID_MAX = 64;
165
+ export function lateDecideOutcomeOf(reply, opts) {
166
+ try {
167
+ if (reply.ok === true) {
168
+ const body = reply.body;
169
+ if (guardedRead(() => readToolApprovalRespondParking(body)) !== undefined)
170
+ return LATE_UNKNOWN;
171
+ const receipt = guardedRead(() => readDecideReceipt(body));
172
+ const handoff = receipt?.handoffTaskId;
173
+ const rawTask = typeof handoff === 'string' && handoff !== '' ? handoff : guardedRead(() => readOwnDataValue(body, 'taskId'));
174
+ const taskId = typeof rawTask === 'string' && rawTask !== '' ? rawTask.slice(0, LATE_DECIDE_TASK_ID_MAX) : undefined;
175
+ const replayed = guardedRead(() => readOwnDataValue(body, 'idempotent')) === true;
176
+ if (opts?.lane === 'respond' && replayed)
177
+ return Object.freeze({ kind: 'settled-elsewhere' });
178
+ const kind = opts?.lane === 'respond' && !decideAcceptedNotResolved(receipt) ? 'resumed' : 'accepted-not-resolved';
179
+ const disposition = receipt?.executionOutcome?.disposition?.kind;
180
+ const executed = disposition === 'allowed' || disposition === 'denied' ? disposition : undefined;
181
+ return Object.freeze({ kind, ...(taskId !== undefined ? { taskId } : {}), ...(executed !== undefined ? { executed } : {}) });
182
+ }
183
+ const e = reply.error;
184
+ if (typeof e !== 'object' || e === null)
185
+ return LATE_UNKNOWN;
186
+ const code = guardedRead(() => readOwnDataValue(e, 'errorCode'));
187
+ const status = guardedRead(() => readOwnDataValue(e, 'status'));
188
+ if (code === APPROVAL_EXPIRED)
189
+ return Object.freeze({ kind: 'expired-reaped' });
190
+ if (resumeReopenFromError(e) !== null || resumeRefusalFromError(e)?.waitable === true || refusedParkFromError(e)?.stillPending === true)
191
+ return LATE_UNKNOWN;
192
+ const r = readDecisionRefusalAsk(e);
193
+ if (r.reading !== 'settled' && !(r.reading === 'not_settled' && r.gateMoved))
194
+ return LATE_UNKNOWN;
195
+ const probeWorthy = status === 404 || status === 410 || (typeof code === 'string' && code.startsWith('not_found.'));
196
+ return Object.freeze(probeWorthy ? { kind: 'settled-elsewhere', probeWorthy: true } : { kind: 'settled-elsewhere' });
197
+ }
198
+ catch {
199
+ return LATE_UNKNOWN;
200
+ }
201
+ }
202
+ const LATE_UNKNOWN = Object.freeze({ kind: 'unknown' });
@@ -27,3 +27,8 @@ export interface RowArmSingleFlight<T> {
27
27
  clear: () => void;
28
28
  }
29
29
  export declare function createRowArmSingleFlight<T>(): RowArmSingleFlight<T>;
30
+ export declare function pendingRowIsOwnedAndDecidable(row: PendingCheckpoint, deps?: ParkOwnershipDeps): boolean;
31
+ export interface OwnedPendingListView {
32
+ readonly pending?: readonly PendingCheckpoint[] | null;
33
+ }
34
+ export declare function countOwnedPendingRows(listed: OwnedPendingListView | null | undefined, deps?: ParkOwnershipDeps): number;
@@ -1,4 +1,4 @@
1
- import { askParkForeignGateKind } from '../adapter/activeRunSelfHeal.js';
1
+ import { askParkForeignGateKind, durableDecideLegOf } from '../adapter/activeRunSelfHeal.js';
2
2
  import { isAskTool } from './frameRouter.js';
3
3
  import { approvalCallKey } from './gateIdentity.js';
4
4
  import { askQuestionsFromPending, pendingRowIsOwnedByThisSession } from './parkOwnership.js';
@@ -108,3 +108,34 @@ export function createRowArmSingleFlight() {
108
108
  },
109
109
  };
110
110
  }
111
+ export function pendingRowIsOwnedAndDecidable(row, deps) {
112
+ if (typeof row !== 'object' || row === null)
113
+ throw new TypeError('pending row is not an object — ownership cannot be judged');
114
+ if (!pendingRowIsOwnedByThisSession(row, deps))
115
+ return false;
116
+ void row.gateKind;
117
+ const leg = durableDecideLegOf(row);
118
+ if (leg === 'plan_review')
119
+ return true;
120
+ if (leg !== 'approval' && leg !== 'absent')
121
+ return false;
122
+ return askParkRowArm(row) === 'question' ? askQuestionsFromPending(row) !== null : true;
123
+ }
124
+ export function countOwnedPendingRows(listed, deps) {
125
+ if (typeof listed !== 'object' || listed === null || Array.isArray(listed)) {
126
+ throw new Error('approvals.list answered without a readable body — the owned pending count is unknown');
127
+ }
128
+ const pending = listed?.pending;
129
+ if (!Array.isArray(pending)) {
130
+ throw new Error('approvals.list answered without a readable `pending` array — the owned pending count is unknown');
131
+ }
132
+ let n = 0;
133
+ for (const row of pending) {
134
+ if (typeof row !== 'object' || row === null) {
135
+ throw new Error('approvals.list answered with a pending row that is not an object — the owned pending count is unknown');
136
+ }
137
+ if (pendingRowIsOwnedAndDecidable(row, deps))
138
+ n++;
139
+ }
140
+ return n;
141
+ }
@@ -0,0 +1,18 @@
1
+ export declare function askEndingCauseClause(cause: string | undefined): string | undefined;
2
+ export interface AskSettlementInput {
3
+ readonly cause?: string | undefined;
4
+ readonly toolName?: string | undefined;
5
+ readonly parked?: boolean | undefined;
6
+ }
7
+ export declare function askSettlementLine(settled: AskSettlementInput): string | undefined;
8
+ export type AskAcceptedFollow = 'following' | 'already-following' | 'not-following';
9
+ export interface AskAcceptedInput {
10
+ readonly decision?: string | undefined;
11
+ readonly taskId?: string | undefined;
12
+ readonly late?: boolean | undefined;
13
+ readonly follow?: AskAcceptedFollow | undefined;
14
+ readonly executed?: string | undefined;
15
+ }
16
+ export declare function askAcceptedLine(accepted: AskAcceptedInput): string;
17
+ export type AskParkedCardForm = 'approval' | 'question' | 'queue_no_deadline';
18
+ export declare function askParkedCardNote(form?: string | undefined, toolName?: string | undefined): string;
@@ -0,0 +1,79 @@
1
+ import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
2
+ import { guardedRead, readOwnDataValue } from '../ownKey.js';
3
+ const ENDING_CAUSE_CLAUSES = Object.freeze({
4
+ routing_failure_fail_closed: 'the engine could not route it to anywhere that can answer it, so it closed it',
5
+ run_cancelled: 'the run that asked for it was cancelled',
6
+ run_exited: 'the run that asked for it exited before anyone answered',
7
+ batch_aborted: 'the batch of tool calls it belonged to was aborted',
8
+ batch_sibling_withdrawn: 'another approval in the same batch was refused or withdrawn, which withdrew this one too',
9
+ batch_bound_elsewhere: 'the decision for its batch was bound to a different approval',
10
+ adhoc_leg_no_durable_domain: 'this kind of request cannot be kept waiting for an answer on this deployment',
11
+ orphan_ttl_exceeded: "it was left without an owner past the engine's time limit",
12
+ crashed_before_park: 'the engine process stopped before it could keep the request waiting',
13
+ shutdown_before_park: 'the engine shut down before it could keep the request waiting',
14
+ ttl_expired: 'nobody answered within the approval window',
15
+ broadcast_lost: 'it never reached any open connection',
16
+ stream_disconnected: 'the connection carrying this card dropped',
17
+ undeliverable: 'it could not be delivered to any approval surface',
18
+ });
19
+ export function askEndingCauseClause(cause) {
20
+ return typeof cause === 'string' && Object.hasOwn(ENDING_CAUSE_CLAUSES, cause) ? ENDING_CAUSE_CLAUSES[cause] : undefined;
21
+ }
22
+ function ownSlot(o, key) {
23
+ if (typeof o !== 'object' || o === null)
24
+ return undefined;
25
+ return guardedRead(() => readOwnDataValue(o, key));
26
+ }
27
+ function subjectOf(toolName, named, fallback) {
28
+ const quoted = typeof toolName === 'string' ? guardedRead(() => quoteUntrustedForSentence(toolName, 80)) : undefined;
29
+ return quoted === undefined ? fallback : named(quoted);
30
+ }
31
+ const SETTLEMENT_TAIL = ' Nothing was approved from this card; if the step is still needed, ask for it again.';
32
+ export function askSettlementLine(settled) {
33
+ if (ownSlot(settled, 'parked') === true)
34
+ return undefined;
35
+ const cause = ownSlot(settled, 'cause');
36
+ if (typeof cause !== 'string' || cause === '')
37
+ return undefined;
38
+ const subject = subjectOf(ownSlot(settled, 'toolName'), (q) => `the approval request for ${q}`, 'this approval request');
39
+ const clause = askEndingCauseClause(cause);
40
+ const why = clause !== undefined ? `: ${clause}` : " (the engine's reason is one this build does not know yet)";
41
+ return `${subject} ended on the engine without an answer${why}.${SETTLEMENT_TAIL}`;
42
+ }
43
+ const TASK_ID_MAX = 64;
44
+ const EXECUTED_TAILS = Object.freeze({
45
+ allowed: ' The engine also reports that the already-decided step was ALLOWED when it ran.',
46
+ denied: ' The engine also reports that the already-decided step was DENIED at execution time (a deployment review can overturn an approval after the fact).',
47
+ });
48
+ const FOLLOW_CLAUSES = Object.freeze({
49
+ following: 'sema is following that run here',
50
+ 'already-following': 'this screen is already following that run',
51
+ });
52
+ export function askAcceptedLine(accepted) {
53
+ const decision = ownSlot(accepted, 'decision');
54
+ const what = decision === 'approve' ? 'approved' : decision === 'deny' ? 'denied' : 'answered';
55
+ const late = ownSlot(accepted, 'late') === false ? '' : ' late';
56
+ const rawTask = ownSlot(accepted, 'taskId');
57
+ const shownTask = typeof rawTask === 'string' ? guardedRead(() => displayUntrusted(rawTask, { max: TASK_ID_MAX, blanks: true })) : undefined;
58
+ const where = shownTask === undefined || shownTask === '' ? '' : ` (task ${shownTask})`;
59
+ const follow = ownSlot(accepted, 'follow');
60
+ const executed = ownSlot(accepted, 'executed');
61
+ const tail = executed === 'allowed' || executed === 'denied' ? EXECUTED_TAILS[executed] : '';
62
+ if (follow === 'following' || follow === 'already-following') {
63
+ return `${what}${late} — the engine took this decision${where}; ${FOLLOW_CLAUSES[follow]}, so what it does next shows up below.${tail}`;
64
+ }
65
+ return (`${what}${late} — the engine took this decision${where}. Whether the parked call has resumed yet is not something this reply can say; ` +
66
+ `this screen is not following that run, so what it does next will not show up here by itself.${tail}`);
67
+ }
68
+ const PARKED_CARD_NOTES = Object.freeze({
69
+ approval: "This step was already parked in the engine's durable queue and waiting for a decision before this card went up. Yes approves it now and lets the step run late; No abandons the step — the model gets a refusal and can take another route.",
70
+ question: "This step was already parked in the engine's durable queue and waiting for a decision before this card went up. Answering now sends your decision late and lets the parked step continue with it; closing this card without answering abandons the step — the model gets a refusal and can take another route.",
71
+ queue_no_deadline: "{subject} is in the engine's durable queue (no deadline was reported for it). It waits for your decision — nothing has been decided on your behalf.",
72
+ });
73
+ export function askParkedCardNote(form, toolName) {
74
+ const key = form === 'question' || form === 'queue_no_deadline' ? form : 'approval';
75
+ const template = PARKED_CARD_NOTES[key];
76
+ if (key !== 'queue_no_deadline')
77
+ return template;
78
+ return template.split('{subject}').join(subjectOf(toolName, (q) => `the approval for ${q}`, 'this approval'));
79
+ }
@@ -19,7 +19,7 @@ export type AutomaticAnswerVerdict = {
19
19
  readonly kind: 'unknown';
20
20
  readonly why: AutomaticAnswerUnknownWhy;
21
21
  };
22
- export declare const MODE_ARBITRATION: Readonly<Record<"hook" | "write_protection" | "mode" | "rule_unreadable" | "target_unresolved" | "content_question" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "org_rule" | "governance_unavailable" | "ask_rule" | "denial_limit_fallback" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", CoreModeArbitrationRow>>;
22
+ export declare const MODE_ARBITRATION: Readonly<Record<"hook" | "write_protection" | "mode" | "target_unresolved" | "content_question" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "org_rule" | "rule_unreadable" | "ask_rule" | "denial_limit_fallback" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", CoreModeArbitrationRow>>;
23
23
  export declare function foldModeArbitrationRow(classes: Iterable<string>, askFace?: AutomaticAnswerAskFace): CoreModeArbitrationRow;
24
24
  export declare function automaticAnswerVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
25
25
  export declare function mandatedApprovalDetailOf(card: object | null | undefined): string;
@@ -3,18 +3,20 @@ import { CORE_RELEASABLE_ASK_CLASSES_BY_FACE, CORE_SHELL_FACE_TOOL_NAMES } from
3
3
  import { mandatedApprovalDetail } from '../gateVocabulary.js';
4
4
  import { readAskOrigin, readOwnNonEmptyString } from '../ownKey.js';
5
5
  import { readAskClassPairState } from './askClassPair.js';
6
- import { approvalAllowRuleShadowed } from './allowRuleShadow.js';
6
+ import { readRequiresRealApproval } from './approvalCardReading.js';
7
+ import { approvalAllowRuleShadowed, retiredAllowRuleShadowKeyPresent } from './allowRuleShadow.js';
7
8
  import { approvalIsMandated, readApprovalMandate } from './toolApprovalWire.js';
8
9
  export const MODE_ARBITRATION = CORE_MODE_ARBITRATION;
9
10
  const TABLE = CORE_MODE_ARBITRATION;
10
11
  const ORIGIN_IMPLIES_CLASS = Object.freeze({
11
12
  content_question: 'content_question',
12
- org_unavailable: 'governance_unavailable',
13
+ org_unavailable: 'rule_unreadable',
13
14
  org_rule: 'org_rule',
14
15
  hook: 'hook',
15
16
  ask_rule: 'ask_rule',
16
17
  denial_limit_fallback: 'denial_limit_fallback',
17
18
  });
19
+ const RULE_LANE_COULD_CLEAR = 'lane_cannot_speak';
18
20
  function mandateWordClass(raw) {
19
21
  if (raw === 'operator_always')
20
22
  return 'shell_floor';
@@ -102,6 +104,17 @@ export function automaticAnswerVerdict(card, lane, facts) {
102
104
  try {
103
105
  if (typeof card !== 'object' || card === null)
104
106
  return UNKNOWN.card_unreadable;
107
+ if (card.cardPayloadUnreadable === true)
108
+ return UNKNOWN.card_unreadable;
109
+ const unreadable = card.unreadableEvidence;
110
+ if (unreadable !== undefined) {
111
+ if (!Array.isArray(unreadable) || unreadable.some((k) => typeof k !== 'string'))
112
+ return UNKNOWN.card_unreadable;
113
+ if (unreadable.some((k) => k !== 'rememberRefused' && k !== 'ruleOffers'))
114
+ return UNKNOWN.card_unreadable;
115
+ }
116
+ if (!approvalAllowRuleShadowed(card) && retiredAllowRuleShadowKeyPresent(card, 'frame'))
117
+ return UNKNOWN.card_unreadable;
105
118
  const face = lane === 'mode_posture' ? askFaceOfCard(card) : undefined;
106
119
  const origin = readAskOrigin(evidenceView(card, 'origin'));
107
120
  if (origin !== undefined && Object.hasOwn(ORIGIN_IMPLIES_CLASS, origin)) {
@@ -109,7 +122,7 @@ export function automaticAnswerVerdict(card, lane, facts) {
109
122
  if (classBlocks(lane, cls, face))
110
123
  return mustAsk('origin', { origin, askClass: cls });
111
124
  }
112
- if (lane !== 'mode_posture' && evidenceView(card, 'requiresRealApproval').requiresRealApproval === true)
125
+ if (lane !== 'mode_posture' && readRequiresRealApproval(card))
113
126
  return mustAsk('requires_real_approval');
114
127
  if (evidenceView(card, 'governanceForced').governanceForced === true)
115
128
  return mustAsk('governance');
@@ -123,8 +136,11 @@ export function automaticAnswerVerdict(card, lane, facts) {
123
136
  const mandateView = evidenceView(card, 'mandated', 'ruleOffersAbsence', 'mandate');
124
137
  if (approvalIsMandated(mandateView))
125
138
  return mustAsk('mandated');
126
- if (lane === 'standing_rule' && (approvalAllowRuleShadowed(card) || readOwnNonEmptyString(mandateView, 'ruleOffersAbsence') === 'shadowed'))
127
- return mustAsk('allow_rule_shadow');
139
+ if (lane === 'standing_rule') {
140
+ const absence = readOwnNonEmptyString(mandateView, 'ruleOffersAbsence');
141
+ if (approvalAllowRuleShadowed(card) || (absence !== undefined && absence !== RULE_LANE_COULD_CLEAR))
142
+ return mustAsk('allow_rule_shadow');
143
+ }
128
144
  }
129
145
  const pair = readAskClassPairState(card);
130
146
  if (pair.state === 'malformed')
@@ -1,31 +1,18 @@
1
- import type { PendingCheckpoint } from '@sema-agent/sdk';
1
+ import type { CrashConvergedRow, PendingCheckpoint } from '@sema-agent/sdk';
2
2
  export type CrashConvergedOrphanState = 'pending' | 'decided';
3
- export interface CrashConvergedRow {
4
- readonly approvalId: string;
5
- readonly toolName: string;
6
- readonly taskId: string;
7
- readonly sessionId?: string;
8
- readonly ts: number;
9
- readonly expiresAtMs: number;
10
- readonly decision: 'denied';
11
- readonly cause: 'crashed_before_park' | 'shutdown_before_park';
12
- readonly orphanState: CrashConvergedOrphanState;
13
- readonly originalDecision?: 'approve';
14
- readonly decidedAtMs?: number;
15
- readonly convergedAtMs: number;
16
- readonly resumeSafe: boolean;
3
+ export interface CrashConvergedRowReading extends Readonly<CrashConvergedRow> {
17
4
  readonly needsHumanReason?: 'approved_then_interrupted' | 'pending_unsafe' | 'unstable_row' | 'contradictory';
18
5
  }
19
- export declare const isCrashConvergedCause: (v: unknown) => v is CrashConvergedRow["cause"];
6
+ export declare const isCrashConvergedCause: (v: unknown) => v is CrashConvergedRowReading["cause"];
20
7
  export interface ApprovalsListEnvelope {
21
8
  pending: PendingCheckpoint[];
22
9
  livePending?: unknown[];
23
- crashConverged?: CrashConvergedRow[];
10
+ crashConverged?: CrashConvergedRowReading[];
24
11
  }
25
12
  export interface CrashConvergedProjection {
26
13
  readonly total: number;
27
- readonly resumeSafe: readonly CrashConvergedRow[];
28
- readonly needsHuman: readonly CrashConvergedRow[];
14
+ readonly resumeSafe: readonly CrashConvergedRowReading[];
15
+ readonly needsHuman: readonly CrashConvergedRowReading[];
29
16
  readonly dropped: number;
30
17
  }
31
18
  export declare function projectCrashConverged(env: unknown): CrashConvergedProjection | undefined;
@@ -33,6 +20,10 @@ export interface CrashConvergedNoticeFacts {
33
20
  readonly total: number;
34
21
  readonly dropped: number;
35
22
  readonly needsHumanReasons: readonly (string | undefined)[];
36
- readonly causes: readonly CrashConvergedRow['cause'][] | undefined;
23
+ readonly causes: readonly CrashConvergedRowReading['cause'][] | undefined;
37
24
  }
25
+ export declare function crashConvergedRowInProjection(projection: CrashConvergedProjection | undefined, row: Pick<CrashConvergedRowReading, 'approvalId'>): {
26
+ readonly bucket: 'resumeSafe' | 'needsHuman';
27
+ readonly row: CrashConvergedRowReading;
28
+ } | undefined;
38
29
  export declare function readCrashConvergedNoticeFacts(projection: CrashConvergedProjection | undefined): CrashConvergedNoticeFacts | undefined;
@@ -168,6 +168,62 @@ function causeWordsOf(rows) {
168
168
  }
169
169
  return seen;
170
170
  }
171
+ function rowRecordsAgreeUnexecuted(row) {
172
+ try {
173
+ if (typeof row !== 'object' || row === null)
174
+ return false;
175
+ const own = (k) => Object.getOwnPropertyDescriptor(row, k);
176
+ const valueIs = (k, want) => {
177
+ const d = own(k);
178
+ return d !== undefined && Object.hasOwn(d, 'value') && d.value === want;
179
+ };
180
+ const noValue = (k) => {
181
+ const d = own(k);
182
+ return d === undefined || (Object.hasOwn(d, 'value') && d.value === undefined);
183
+ };
184
+ return valueIs('orphanState', 'pending') && valueIs('resumeSafe', true) && noValue('originalDecision') && noValue('decidedAtMs');
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
190
+ export function crashConvergedRowInProjection(projection, row) {
191
+ try {
192
+ if (typeof row !== 'object' || row === null)
193
+ return undefined;
194
+ const id = ownDataValue(row, 'approvalId');
195
+ if (typeof id !== 'string' || id === '')
196
+ return undefined;
197
+ if (typeof projection !== 'object' || projection === null)
198
+ return undefined;
199
+ const total = ownDataValue(projection, 'total');
200
+ if (!isCount(total) || total > MAX_CRASH_CONVERGED_ROWS)
201
+ return undefined;
202
+ const safe = bucketRowsOf(ownDataValue(projection, 'resumeSafe'));
203
+ const human = bucketRowsOf(ownDataValue(projection, 'needsHuman'));
204
+ if (safe === undefined || human === undefined || safe.length + human.length !== total)
205
+ return undefined;
206
+ const find = (rows) => {
207
+ for (const r of rows) {
208
+ if (typeof r === 'object' && r !== null && ownDataValue(r, 'approvalId') === id)
209
+ return r;
210
+ }
211
+ return undefined;
212
+ };
213
+ const inSafe = find(safe);
214
+ const inHuman = find(human);
215
+ if (inSafe !== undefined && inHuman !== undefined)
216
+ return undefined;
217
+ if (inSafe !== undefined)
218
+ return rowRecordsAgreeUnexecuted(inSafe) ? { bucket: 'resumeSafe', row: inSafe } : undefined;
219
+ if (inHuman !== undefined)
220
+ return { bucket: 'needsHuman', row: inHuman };
221
+ return undefined;
222
+ }
223
+ catch {
224
+ return undefined;
225
+ }
226
+ }
171
227
  export function readCrashConvergedNoticeFacts(projection) {
172
228
  try {
173
229
  if (typeof projection !== 'object' || projection === null)
@@ -1,4 +1,6 @@
1
- import type { CrashConvergedProjection } from './crashConverged.js';
1
+ import type { CrashConvergedProjection, CrashConvergedRowReading } from './crashConverged.js';
2
2
  export declare const NEEDS_HUMAN_REASON_ORDER: readonly ["approved_then_interrupted", "pending_unsafe", "contradictory", "unstable_row"];
3
3
  export declare function crashConvergedNeedsHumanSentence(reason: string | undefined, n: number, total: number): string | undefined;
4
4
  export declare function crashConvergedNotice(projection: CrashConvergedProjection | undefined): string | undefined;
5
+ export declare function crashConvergedRowSentence(projection: CrashConvergedProjection | undefined, row: Pick<CrashConvergedRowReading, 'approvalId'>): string | undefined;
6
+ export declare function crashConvergedCauseSentence(cause: string | undefined): string | undefined;
@@ -1,4 +1,5 @@
1
- import { readCrashConvergedNoticeFacts } from './crashConverged.js';
1
+ import { askEndingCauseClause } from './askSettlementLine.js';
2
+ import { crashConvergedRowInProjection, isCrashConvergedCause, readCrashConvergedNoticeFacts } from './crashConverged.js';
2
3
  export const NEEDS_HUMAN_REASON_ORDER = Object.freeze(['approved_then_interrupted', 'pending_unsafe', 'contradictory', 'unstable_row']);
3
4
  const _orderCoversReasons = true;
4
5
  const _orderInsideReasons = true;
@@ -9,14 +10,28 @@ void _orderHasNoDuplicate;
9
10
  const RECORDED = 'based on what was recorded';
10
11
  const NO_AUTO_RERUN = 'sema has not re-run anything';
11
12
  const plural = (n, one, many) => (n === 1 ? one : many);
12
- const SENTENCE = Object.freeze({
13
- approved_then_interrupted: (n, total) => `${n} of ${total} had already been approved and may have been running when the previous engine run ended, so a side effect may have partly landed`,
14
- pending_unsafe: (n, total) => `${n} of ${total} had not started, ${RECORDED}, but the engine did not mark ${plural(n, 'it', 'them')} as safe to re-run`,
15
- contradictory: (n, total) => `${n} of ${total} ${plural(n, 'has a contradictory record', 'have contradictory records')}`,
16
- unstable_row: (n, total) => `${n} of ${total} ${plural(n, 'has a record that read differently each time it was read', 'have records that read differently each time they were read')}`,
13
+ const NEEDS_HUMAN_CLAUSES = Object.freeze({
14
+ approved_then_interrupted: {
15
+ one: 'had already been approved and may have been running when the previous engine run ended, so a side effect may have partly landed',
16
+ many: 'had already been approved and may have been running when the previous engine run ended, so a side effect may have partly landed',
17
+ },
18
+ pending_unsafe: {
19
+ one: `had not started, ${RECORDED}, but the engine did not mark it as safe to re-run`,
20
+ many: `had not started, ${RECORDED}, but the engine did not mark them as safe to re-run`,
21
+ },
22
+ contradictory: { one: 'has a contradictory record', many: 'have contradictory records' },
23
+ unstable_row: {
24
+ one: 'has a record that read differently each time it was read',
25
+ many: 'have records that read differently each time they were read',
26
+ },
17
27
  });
18
- const fallbackSentence = (n, total) => `${n} of ${total} had already been approved, or cannot be accounted for, so a side effect may have partly landed`;
19
- const isNeedsHumanReason = (v) => typeof v === 'string' && Object.hasOwn(SENTENCE, v);
28
+ const FALLBACK_CLAUSE = Object.freeze({
29
+ one: 'had already been approved, or cannot be accounted for, so a side effect may have partly landed',
30
+ many: 'had already been approved, or cannot be accounted for, so a side effect may have partly landed',
31
+ });
32
+ const RESUME_SAFE_CLAUSE = `had not started, ${RECORDED}, and can be re-run`;
33
+ const countedClause = (clause, n, total) => `${n} of ${total} ${n === 1 ? clause.one : clause.many}`;
34
+ const isNeedsHumanReason = (v) => typeof v === 'string' && Object.hasOwn(NEEDS_HUMAN_CLAUSES, v);
20
35
  export function crashConvergedNeedsHumanSentence(reason, n, total) {
21
36
  if (!isNeedsHumanReason(reason))
22
37
  return undefined;
@@ -24,18 +39,18 @@ export function crashConvergedNeedsHumanSentence(reason, n, total) {
24
39
  return undefined;
25
40
  if (typeof total !== 'number' || !Number.isSafeInteger(total) || total < n)
26
41
  return undefined;
27
- return SENTENCE[reason](n, total);
42
+ return countedClause(NEEDS_HUMAN_CLAUSES[reason], n, total);
28
43
  }
29
44
  function needsHumanClauses(reasons, total) {
30
45
  const clauses = [];
31
46
  for (const reason of NEEDS_HUMAN_REASON_ORDER) {
32
47
  const n = reasons.filter((r) => r === reason).length;
33
48
  if (n > 0)
34
- clauses.push(SENTENCE[reason](n, total));
49
+ clauses.push(countedClause(NEEDS_HUMAN_CLAUSES[reason], n, total));
35
50
  }
36
51
  const fallback = reasons.filter((r) => !isNeedsHumanReason(r)).length;
37
52
  if (fallback > 0)
38
- clauses.push(fallbackSentence(fallback, total));
53
+ clauses.push(countedClause(FALLBACK_CLAUSE, fallback, total));
39
54
  return `${clauses.join('; ')} — check ${plural(reasons.length, 'it', 'them')} by hand before re-running`;
40
55
  }
41
56
  export function crashConvergedNotice(projection) {
@@ -55,3 +70,25 @@ export function crashConvergedNotice(projection) {
55
70
  const droppedClause = dropped > 0 ? ` (${dropped} further ${plural(dropped, 'record', 'records')} could not be read.)` : '';
56
71
  return `${head}; ${body}. ${NO_AUTO_RERUN}.${droppedClause}`;
57
72
  }
73
+ export function crashConvergedRowSentence(projection, row) {
74
+ const hit = crashConvergedRowInProjection(projection, row);
75
+ if (hit === undefined)
76
+ return undefined;
77
+ let descriptor;
78
+ try {
79
+ descriptor = Object.getOwnPropertyDescriptor(hit.row, 'needsHumanReason');
80
+ }
81
+ catch {
82
+ return undefined;
83
+ }
84
+ if (hit.bucket === 'resumeSafe')
85
+ return descriptor === undefined ? RESUME_SAFE_CLAUSE : undefined;
86
+ const reason = descriptor !== undefined && Object.hasOwn(descriptor, 'value') ? descriptor.value : undefined;
87
+ return (isNeedsHumanReason(reason) ? NEEDS_HUMAN_CLAUSES[reason] : FALLBACK_CLAUSE).one;
88
+ }
89
+ export function crashConvergedCauseSentence(cause) {
90
+ if (typeof cause !== 'string' || cause === '')
91
+ return undefined;
92
+ const clause = isCrashConvergedCause(cause) ? askEndingCauseClause(cause) : undefined;
93
+ return clause === undefined ? 'the decision converged automatically' : `${clause}, and the decision converged automatically`;
94
+ }
@@ -1,29 +1,22 @@
1
1
  import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_UNSUPPORTED_VERSION, RESUME_OUTCOME_INVALID } from '../engineErrorCodes.js';
2
2
  import { ownDataField } from '../ownKey.js';
3
- import { readRefusalField } from '../wireFailureShape.js';
3
+ import { PROTOCOL_ERROR_NAME, protocolErrorNameOf, readRefusalField } from '../wireFailureShape.js';
4
4
  const BINDING_SEAT_FIELDS = new Set(['boundCallId', 'boundInputHash']);
5
5
  const ANSWER_FIELD = 'answer';
6
- const BINDING_MISMATCH_ERROR_NAME = 'ApprovalBindingMismatchError';
7
6
  export function decisionConflictOf(err) {
8
7
  if (typeof err !== 'object' || err === null)
9
8
  return undefined;
10
9
  let code;
11
- let name;
12
10
  try {
13
11
  code = ownDataField(err, 'errorCode');
14
12
  }
15
13
  catch {
16
14
  return undefined;
17
15
  }
18
- try {
19
- name = ownDataField(err, 'name');
20
- }
21
- catch {
22
- name = undefined;
23
- }
16
+ const protocolName = protocolErrorNameOf(err);
24
17
  if (code === CHECKPOINT_UNSUPPORTED_VERSION)
25
18
  return 'format_version';
26
- const bindingFamily = code === APPROVAL_BINDING_MISMATCH || (code === undefined && name === BINDING_MISMATCH_ERROR_NAME);
19
+ const bindingFamily = code === APPROVAL_BINDING_MISMATCH || (code === undefined && protocolName === PROTOCOL_ERROR_NAME.ApprovalBindingMismatchError);
27
20
  if (!bindingFamily && code !== RESUME_OUTCOME_INVALID)
28
21
  return undefined;
29
22
  let field;
@@ -1,6 +1,23 @@
1
+ import { type GateCurrentPending, type HitlSafetyCode } from './hitlBridge.js';
1
2
  import { type DecisionRefusalConflict } from './decisionConflict.js';
3
+ import { type ProtocolErrorName } from '../wireFailureShape.js';
4
+ import { type DenyAttributionRefusalDetail } from '../wireRefusalCopy.js';
2
5
  export type DecisionRefusalStanding = 'settled-elsewhere' | 'ambiguous' | 'refused-pending';
3
6
  export type { DecisionRefusalConflict } from './decisionConflict.js';
7
+ export interface RefusalCarrier {
8
+ readonly local?: HitlSafetyCode;
9
+ readonly carrier: {} | null | undefined;
10
+ }
11
+ export declare function refusalCarrierOf(err: {} | null | undefined): RefusalCarrier;
12
+ export interface RefusalRouteContext {
13
+ readonly route?: 'approval_decide' | 'live_respond' | 'ask_decide' | 'plan_review' | 'compact';
14
+ }
15
+ export interface RefusalDelivery {
16
+ readonly delivery: 'refused' | 'ambiguous' | 'no_answer';
17
+ readonly status?: number;
18
+ readonly errorCode?: string;
19
+ }
20
+ export declare function refusalDeliveryOf(err: {} | null | undefined, context?: RefusalRouteContext): RefusalDelivery;
4
21
  export declare function decisionRefusalSaysAskGone(err: unknown): boolean;
5
22
  export type DecisionRefusalAskReading = 'settled' | 'not_settled' | 'unreadable';
6
23
  interface DecisionRefusalAskFacts {
@@ -10,6 +27,28 @@ interface DecisionRefusalAskFacts {
10
27
  }
11
28
  export declare function readDecisionRefusalAsk(failure: unknown): DecisionRefusalAskFacts;
12
29
  export declare function decisionRefusalAskReading(failure: unknown): DecisionRefusalAskReading;
30
+ export interface DecisionRefusalReading {
31
+ readonly status?: number;
32
+ readonly errorCode?: string;
33
+ readonly message?: string;
34
+ readonly retriable?: true;
35
+ readonly protocolName?: ProtocolErrorName;
36
+ readonly extra?: {
37
+ readonly cause?: string;
38
+ readonly field?: string;
39
+ };
40
+ readonly blockedBy?: string;
41
+ readonly currentPending?: GateCurrentPending;
42
+ readonly denyAttributionRefusal?: DenyAttributionRefusalDetail;
43
+ readonly ask: DecisionRefusalAskReading;
44
+ readonly gateMoved?: true;
45
+ readonly wordingHint?: true;
46
+ readonly standing?: DecisionRefusalStanding;
47
+ readonly transient?: true;
48
+ readonly conflict?: DecisionRefusalConflict;
49
+ }
50
+ export declare function mintDecisionRefusalReading(err: {} | null | undefined, context?: RefusalRouteContext, attributionCarried?: boolean): DecisionRefusalReading;
51
+ export declare function readDecisionRefusalReading(err: {} | null | undefined, context?: RefusalRouteContext): DecisionRefusalReading;
13
52
  export interface DecisionRefusalClassified {
14
53
  readonly standing: DecisionRefusalStanding;
15
54
  readonly status: number;
@@ -17,7 +56,7 @@ export interface DecisionRefusalClassified {
17
56
  readonly transient?: true;
18
57
  readonly conflict?: DecisionRefusalConflict;
19
58
  }
20
- export declare function classifyDecisionRefusal(err: unknown): DecisionRefusalClassified | undefined;
59
+ export declare function classifyDecisionRefusal(err: unknown, context?: RefusalRouteContext): DecisionRefusalClassified | undefined;
21
60
  export type LiveAskPendingReading = 'pending' | 'not_pending' | 'unknown';
22
61
  export declare function liveAskPendingReading(envelope: unknown, approvalId: string): LiveAskPendingReading;
23
62
  export declare function decisionRefusalClosesOut(refusal: DecisionRefusalClassified | undefined, pending: LiveAskPendingReading): boolean;