@sema-agent/client-core 0.110.0 → 0.112.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 (243) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +59 -18
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +25 -4
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  16. package/dist/attachmentRoute.d.ts +20 -0
  17. package/dist/attachmentRoute.js +39 -0
  18. package/dist/attachmentsWireCaps.js +1 -1
  19. package/dist/builtinOutputStyles.d.ts +12 -0
  20. package/dist/builtinOutputStyles.js +147 -0
  21. package/dist/checkpointRowVerdict.d.ts +3 -0
  22. package/dist/checkpointRowVerdict.js +14 -1
  23. package/dist/compactionStatus.d.ts +9 -0
  24. package/dist/compactionStatus.js +29 -5
  25. package/dist/consoleCaPin.d.ts +136 -0
  26. package/dist/consoleCaPin.js +827 -0
  27. package/dist/controlRouter.js +2 -1
  28. package/dist/coreValuePorts.d.ts +39 -1
  29. package/dist/coreValuePorts.js +10 -0
  30. package/dist/decideReceipt.d.ts +2 -1
  31. package/dist/deviceRebind.js +3 -3
  32. package/dist/displayUntrusted.d.ts +1 -0
  33. package/dist/displayUntrusted.js +6 -0
  34. package/dist/effectiveFacts.d.ts +9 -0
  35. package/dist/effectiveFacts.js +33 -0
  36. package/dist/engineAddress.d.ts +1 -0
  37. package/dist/engineAddress.js +4 -1
  38. package/dist/engineCredentialRelease.d.ts +54 -0
  39. package/dist/engineCredentialRelease.js +146 -0
  40. package/dist/engineErrorCodes.d.ts +1 -0
  41. package/dist/engineErrorCodes.js +1 -0
  42. package/dist/engineHealthProbeVerdict.d.ts +24 -0
  43. package/dist/engineHealthProbeVerdict.js +123 -0
  44. package/dist/engineNoticeCodes.js +5 -7
  45. package/dist/engineRepoint.d.ts +12 -5
  46. package/dist/engineRepoint.js +44 -19
  47. package/dist/engineRowRef.d.ts +3 -0
  48. package/dist/engineRowRef.js +11 -1
  49. package/dist/engineSwapCrossing.d.ts +57 -0
  50. package/dist/engineSwapCrossing.js +297 -0
  51. package/dist/engineUnavailable.d.ts +45 -0
  52. package/dist/engineUnavailable.js +496 -0
  53. package/dist/engineUrl.js +4 -2
  54. package/dist/eventLeg.js +3 -3
  55. package/dist/gateFailureCodes.js +2 -2
  56. package/dist/generated/sdkFactTables.d.ts +2 -1
  57. package/dist/generated/sdkFactTables.js +14 -0
  58. package/dist/generated/settingsSchemaFns.d.ts +1 -0
  59. package/dist/generated/settingsSchemaFns.js +3 -0
  60. package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
  61. package/dist/generated/settingsSchemaTables.d.ts +56 -0
  62. package/dist/generated/settingsSchemaTables.js +19 -0
  63. package/dist/generated/settingsSchemaTypes.d.ts +23 -13
  64. package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
  65. package/dist/generated/settingsSchemaUrlShape.js +17 -0
  66. package/dist/hitl/approvalDelivery.d.ts +1 -0
  67. package/dist/hitl/approvalDelivery.js +1 -1
  68. package/dist/hitl/approvalResolution.d.ts +1 -1
  69. package/dist/hitl/approvalResolution.js +2 -1
  70. package/dist/hitl/askGateWire.js +5 -6
  71. package/dist/hitl/decisionRefusal.d.ts +6 -0
  72. package/dist/hitl/decisionRefusal.js +19 -10
  73. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  74. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  75. package/dist/hitl/decisionSend.d.ts +7 -0
  76. package/dist/hitl/decisionSend.js +34 -12
  77. package/dist/hitl/editedApprovalAcceptance.js +1 -1
  78. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  79. package/dist/hitl/elicitationRefusal.js +23 -0
  80. package/dist/hitl/frameRouter.d.ts +2 -2
  81. package/dist/hitl/frameRouter.js +8 -15
  82. package/dist/hitl/gateIdentity.d.ts +5 -0
  83. package/dist/hitl/gateIdentity.js +23 -0
  84. package/dist/hitl/hitlBridge.d.ts +6 -0
  85. package/dist/hitl/hitlBridge.js +10 -4
  86. package/dist/hitl/hitlHostSurface.d.ts +2 -1
  87. package/dist/hitl/hitlHostSurface.js +7 -2
  88. package/dist/hitl/localAllowRedemption.d.ts +70 -0
  89. package/dist/hitl/localAllowRedemption.js +276 -0
  90. package/dist/hitl/parkOwnership.js +2 -1
  91. package/dist/hitl/parkResolver.js +14 -4
  92. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  93. package/dist/hitl/persistedRulesWire.js +70 -0
  94. package/dist/hitl/planReviewWire.d.ts +3 -0
  95. package/dist/hitl/planReviewWire.js +23 -6
  96. package/dist/hitl/policyDenyCause.d.ts +1 -1
  97. package/dist/hitl/policyDenyCause.js +1 -1
  98. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  99. package/dist/hitl/resumeRunningCard.js +1 -0
  100. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  101. package/dist/hitl/screenCardCarriers.js +41 -0
  102. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  103. package/dist/hitl/screenDecisionCards.js +363 -0
  104. package/dist/hitl/sessionPolicyWire.js +2 -2
  105. package/dist/hitl/standingRows.d.ts +6 -2
  106. package/dist/hitl/standingRows.js +27 -2
  107. package/dist/hitl/toolApprovalWire.d.ts +30 -8
  108. package/dist/hitl/toolApprovalWire.js +264 -121
  109. package/dist/hooksReaderGenerations.js +1 -1
  110. package/dist/hooksWireCaps.js +3 -3
  111. package/dist/hostSettingNoticeArm.d.ts +3 -0
  112. package/dist/hostSettingNoticeArm.js +12 -0
  113. package/dist/hostTurnSettings.d.ts +3 -0
  114. package/dist/hostTurnSettings.js +38 -5
  115. package/dist/humanTurnRead.js +11 -1
  116. package/dist/i18n/messages.en.d.ts +292 -0
  117. package/dist/i18n/messages.en.js +298 -7
  118. package/dist/i18n/messages.zh.js +316 -6
  119. package/dist/i18n/translate.d.ts +3 -0
  120. package/dist/i18n/translate.js +78 -10
  121. package/dist/i18n/zh.d.ts +1 -0
  122. package/dist/i18n/zh.js +1 -0
  123. package/dist/index.d.ts +71 -20
  124. package/dist/index.js +44 -13
  125. package/dist/interactiveHalt.js +2 -1
  126. package/dist/interactiveInterrupt.js +20 -2
  127. package/dist/liveModelCatalog.d.ts +4 -0
  128. package/dist/liveModelCatalog.js +12 -3
  129. package/dist/localConfig/documents.d.ts +61 -0
  130. package/dist/localConfig/documents.js +51 -0
  131. package/dist/localConfig/kernel.d.ts +149 -0
  132. package/dist/localConfig/kernel.js +597 -0
  133. package/dist/localConfig/mcp/edit.d.ts +161 -0
  134. package/dist/localConfig/mcp/edit.js +846 -0
  135. package/dist/localConfig/mcp/servers.d.ts +165 -0
  136. package/dist/localConfig/mcp/servers.js +776 -0
  137. package/dist/localConfig/mcp/views.d.ts +170 -0
  138. package/dist/localConfig/mcp/views.js +429 -0
  139. package/dist/localConfig/modelPool.d.ts +262 -0
  140. package/dist/localConfig/modelPool.js +1489 -0
  141. package/dist/localConfig/modelPoolWords.d.ts +13 -0
  142. package/dist/localConfig/modelPoolWords.js +20 -0
  143. package/dist/localConfig/receiptShape.d.ts +7 -0
  144. package/dist/localConfig/receiptShape.js +135 -0
  145. package/dist/localConfig/settingsWrite.d.ts +127 -0
  146. package/dist/localConfig/settingsWrite.js +237 -0
  147. package/dist/localConfig/skills.d.ts +124 -0
  148. package/dist/localConfig/skills.js +397 -0
  149. package/dist/localConfig/skillsWords.d.ts +10 -0
  150. package/dist/localConfig/skillsWords.js +5 -0
  151. package/dist/localEngineSeatReading.d.ts +2 -2
  152. package/dist/localEngineSeatReading.js +5 -3
  153. package/dist/localEngineSpawn.d.ts +0 -7
  154. package/dist/localEngineSpawn.js +0 -92
  155. package/dist/localEngineStep.d.ts +2 -2
  156. package/dist/localEngineStep.js +2 -0
  157. package/dist/mcpProbeWire.js +2 -1
  158. package/dist/mcpReconnect.js +3 -3
  159. package/dist/mcpWireCaps.js +1 -1
  160. package/dist/memoryVerbsWire.js +3 -2
  161. package/dist/model/engineModelWords.d.ts +3 -0
  162. package/dist/model/engineModelWords.js +13 -0
  163. package/dist/model/onboardJudgement.d.ts +14 -4
  164. package/dist/model/onboardJudgement.js +91 -139
  165. package/dist/model/subagentModelWord.d.ts +21 -2
  166. package/dist/model/subagentModelWord.js +100 -29
  167. package/dist/modelCapabilityProbe.js +3 -40
  168. package/dist/modelConfigGapNotice.d.ts +20 -0
  169. package/dist/modelConfigGapNotice.js +102 -0
  170. package/dist/modsKernelIdentity.js +2 -47
  171. package/dist/notifications.d.ts +19 -0
  172. package/dist/notifications.js +114 -12
  173. package/dist/ownKey.d.ts +7 -0
  174. package/dist/ownKey.js +71 -8
  175. package/dist/pendingUserRow.d.ts +4 -0
  176. package/dist/pendingUserRow.js +16 -0
  177. package/dist/permissionsDeclaration.d.ts +3 -1
  178. package/dist/permissionsDeclaration.js +33 -9
  179. package/dist/planReviewCardFacts.d.ts +3 -0
  180. package/dist/planReviewCardFacts.js +52 -2
  181. package/dist/pluginHooksWire.d.ts +9 -1
  182. package/dist/pluginHooksWire.js +40 -6
  183. package/dist/printToolResultFrame.js +4 -2
  184. package/dist/promptBlockedCopy.js +12 -7
  185. package/dist/promptSuggestionBatch.d.ts +7 -0
  186. package/dist/promptSuggestionBatch.js +9 -0
  187. package/dist/promptSuggestionsTail.d.ts +39 -0
  188. package/dist/promptSuggestionsTail.js +116 -0
  189. package/dist/request/taskRequest.js +10 -6
  190. package/dist/requestCoordinate.d.ts +1 -0
  191. package/dist/requestCoordinate.js +13 -0
  192. package/dist/resumeRefusalCopy.d.ts +3 -1
  193. package/dist/resumeRefusalCopy.js +12 -4
  194. package/dist/rewindArchiveReading.js +4 -4
  195. package/dist/runCancelContext.d.ts +2 -1
  196. package/dist/runCancelContext.js +1 -0
  197. package/dist/runLedgerSnapshot.js +3 -3
  198. package/dist/runPermissionModeSwitch.js +3 -4
  199. package/dist/scenarioRefusal.js +11 -13
  200. package/dist/scenarioSkills.js +11 -6
  201. package/dist/seam.d.ts +16 -2
  202. package/dist/seam.js +10 -0
  203. package/dist/seatContract.d.ts +259 -9
  204. package/dist/seatContract.js +444 -15
  205. package/dist/sessionCostReading.d.ts +70 -0
  206. package/dist/sessionCostReading.js +150 -0
  207. package/dist/sessionHooksRoster.d.ts +4 -0
  208. package/dist/sessionHooksRoster.js +158 -0
  209. package/dist/sessionIdSegment.d.ts +1 -0
  210. package/dist/sessionIdSegment.js +4 -1
  211. package/dist/sessionMemoryErase.js +2 -1
  212. package/dist/sessionMemoryStatus.js +2 -2
  213. package/dist/sessionReadRedaction.d.ts +7 -0
  214. package/dist/sessionReadRedaction.js +26 -0
  215. package/dist/sessionSyncFileHistory.js +8 -9
  216. package/dist/settingsHooksSalvage.js +2 -1
  217. package/dist/sha256Bytes.d.ts +1 -0
  218. package/dist/sha256Bytes.js +46 -0
  219. package/dist/skillOverrideChain.d.ts +83 -0
  220. package/dist/skillOverrideChain.js +378 -0
  221. package/dist/skillsWireCaps.d.ts +3 -0
  222. package/dist/skillsWireCaps.js +16 -1
  223. package/dist/sseIdleTriage.js +2 -1
  224. package/dist/storeDurabilityWord.d.ts +15 -0
  225. package/dist/storeDurabilityWord.js +18 -0
  226. package/dist/storePostureCapability.d.ts +7 -3
  227. package/dist/storePostureCapability.js +69 -39
  228. package/dist/subagent/engineSubagentResume.js +2 -2
  229. package/dist/subagent/engineSubagentTail.js +2 -1
  230. package/dist/subagentContentStore.d.ts +11 -0
  231. package/dist/subagentContentStore.js +28 -2
  232. package/dist/toolResult.d.ts +1 -0
  233. package/dist/toolResult.js +2 -0
  234. package/dist/turnFailurePresentation.js +4 -10
  235. package/dist/userAttachmentEnvelope.d.ts +2 -1
  236. package/dist/userAttachmentEnvelope.js +10 -0
  237. package/dist/wireErrorTriage.js +2 -1
  238. package/dist/wireFailureShape.d.ts +4 -0
  239. package/dist/wireFailureShape.js +141 -19
  240. package/dist/wireRefusalCopy.d.ts +6 -5
  241. package/dist/wireRefusalCopy.js +49 -92
  242. package/docs/INTEGRATION-CLIENTS.md +2718 -17
  243. package/package.json +7 -3
@@ -8,6 +8,7 @@ import { guardedRead, ownDataField, readOwnDataValue } from '../ownKey.js';
8
8
  import { PROTOCOL_ERROR_NAME, isSlotCarrier, protocolErrorNameReading, readBlockedBy, readThrownSlot, thrownIsInstanceOf, wireFailureTextsOf } from '../wireFailureShape.js';
9
9
  import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_INVALID_OUTCOME, RESUME_POLICY_FOLD_CODE } from '../engineErrorCodes.js';
10
10
  import { suspendedReopenOf } from './suspendedReopen.js';
11
+ import { decisionConflictOf } from './decisionConflict.js';
11
12
  import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp } from './decisionLedger.js';
12
13
  import { askParkBlockGeneration, noteAskParkDecideSettled } from './askParkAutoReopenBlock.js';
13
14
  import { checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
@@ -259,6 +260,9 @@ function notifyDecideObserver(observer, ev) {
259
260
  invokeHostCallbackIsolated(`hitlBridge: onDecideOperation(${ev.phase})`, () => observer(ev));
260
261
  }
261
262
  const ANY_TOOL_FAMILY = () => true;
263
+ export function pendingRowAnswersCall(row, taskId, callId) {
264
+ return row.taskId === taskId && (row.boundCallId === callId || row.toolCallId === callId);
265
+ }
262
266
  export async function findPendingForTask(client, taskId, matches, opts, callId, identityAccepts = (r) => matches(typeof r.toolName === 'string' ? r.toolName : undefined)) {
263
267
  let rows;
264
268
  try {
@@ -275,10 +279,10 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
275
279
  });
276
280
  const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
277
281
  const identityRow = callId !== undefined
278
- ? decidable.find((r) => r.taskId === taskId && (r.boundCallId === callId || r.toolCallId === callId))
282
+ ? decidable.find((r) => pendingRowAnswersCall(r, taskId, callId))
279
283
  : undefined;
280
284
  const identityForeign = callId !== undefined && identityRow === undefined
281
- ? readings.find((x) => x.r.taskId === taskId && (x.r.boundCallId === callId || x.r.toolCallId === callId) && x.foreign !== null)
285
+ ? readings.find((x) => pendingRowAnswersCall(x.r, taskId, callId) && x.foreign !== null)
282
286
  : undefined;
283
287
  if (identityForeign !== undefined) {
284
288
  const identityBar = identityForeign.foreign === QUEUE_ROW_FAMILY_UNREADABLE_GATE ? checkpointRowBarOf(identityForeign.r) : undefined;
@@ -602,8 +606,10 @@ function bindingMismatchReading(e) {
602
606
  const codeReclassified = code.state === 'code' && code.code === CHECKPOINT_INVALID_OUTCOME;
603
607
  const family = (code.state === 'code' && code.code === APPROVAL_BINDING_MISMATCH) ||
604
608
  (!codeReclassified && nameReading.state === 'named' && nameReading.name === PROTOCOL_ERROR_NAME.ApprovalBindingMismatchError);
605
- if (family)
606
- return 'mismatch';
609
+ if (family) {
610
+ const captured = code.state === 'code' ? { state: 'read', wire: code.code } : code.state === 'absent' ? { state: 'read' } : { state: 'unreadable' };
611
+ return decisionConflictOf(e, captured, nameReading) === 'row_integrity' ? 'not' : 'mismatch';
612
+ }
607
613
  if (code.state === 'unreadable')
608
614
  return 'unreadable';
609
615
  return 'not';
@@ -22,7 +22,8 @@ export declare function hitlHostSurfaceMissesFor(sessionKey: string): number;
22
22
  export declare function hitlHostSurfaceFor(sessionKey: string): HitlHostSurface | null;
23
23
  export declare function _resetHitlHostSurfaceForTest(): void;
24
24
  export declare function surfaceForCurrentSession(): HitlHostSurface | null;
25
- export declare const CANCEL_DENY_BUDGET_MS = 2000;
25
+ export declare const CANCEL_DENY_BUDGET_MS = 8000;
26
+ export declare function interruptDenyLifetime(): AbortSignal;
26
27
  export declare const INTERRUPT_DENY_REASON = "Interrupted by user";
27
28
  export declare const CANCEL_DENY_WARN_TEXT = "could not cancel the pending question \u2014 the session may stay locked; the run may need engine-side recovery";
28
29
  export declare const REMEMBER_NOT_APPLIED_WARN_TEXT = "this session-wide allow was not remembered by the worker \u2014 you will be asked again for this tool";
@@ -4,6 +4,7 @@ import { unrefTimer } from '../unrefTimer.js';
4
4
  import { decideFailureNote } from '../decideFailureNote.js';
5
5
  import { editedApprovalRefusedLine } from './editedApprovalAcceptance.js';
6
6
  import { decisionRefusalSaysAskGone } from './decisionRefusal.js';
7
+ import { deliveryTiming, RETRY_BUDGET_MS } from './approvalDelivery.js';
7
8
  const hostSurfaceByKey = createSessionSlot();
8
9
  const hostSurfaceMissesByKey = new Map();
9
10
  export function installHitlHostSurface(surface) {
@@ -39,7 +40,10 @@ function surfaceForSession(sessionKey) {
39
40
  }
40
41
  return hostSurface;
41
42
  }
42
- export const CANCEL_DENY_BUDGET_MS = 2000;
43
+ export const CANCEL_DENY_BUDGET_MS = RETRY_BUDGET_MS;
44
+ export function interruptDenyLifetime() {
45
+ return AbortSignal.timeout(Math.max(1, deliveryTiming().budgetMs));
46
+ }
43
47
  export const INTERRUPT_DENY_REASON = 'Interrupted by user';
44
48
  export const CANCEL_DENY_WARN_TEXT = 'could not cancel the pending question — the session may stay locked; the run may need engine-side recovery';
45
49
  const CANCEL_DENY_WARN_KEY = 'hitl-cancel-deny-warn';
@@ -140,8 +144,9 @@ export function surfaceRuleArmRejected(why) {
140
144
  }
141
145
  export function observeCancelByDeny(settle, taskId) {
142
146
  let timer;
147
+ const budgetMs = deliveryTiming().budgetMs;
143
148
  const budget = new Promise((_, reject) => {
144
- timer = setTimeout(() => reject(new Error(`cancel-by-deny did not settle within ${CANCEL_DENY_BUDGET_MS}ms`)), CANCEL_DENY_BUDGET_MS);
149
+ timer = setTimeout(() => reject(new Error(`cancel-by-deny did not settle within ${budgetMs}ms`)), budgetMs);
145
150
  unrefTimer(timer);
146
151
  });
147
152
  void Promise.race([settle, budget])
@@ -0,0 +1,70 @@
1
+ import type { EngineCapState } from '../engineCapsCache.js';
2
+ import type { HostSettingsLayer, WorkspaceTrustState } from '../hostSettingsSource.js';
3
+ import { type LocalAllowRuleDeps } from './localAllowRule.js';
4
+ import { type ListAllPersistedRulesOutcome, type RulesFacade } from './persistedRulesWire.js';
5
+ export declare const LOCAL_ALLOW_NOT_REDEEMABLE_REASONS: readonly ["managed_only", "tighten_only", "no_scope_root", "no_consent", "road_closed"];
6
+ export type LocalAllowNotRedeemableReason = (typeof LOCAL_ALLOW_NOT_REDEEMABLE_REASONS)[number];
7
+ export declare const LOCAL_ALLOW_UNKNOWN_CAUSES: readonly ["input_unreadable", "policy_unreadable", "consent_unreadable", "road_unknown"];
8
+ export type LocalAllowUnknownCause = (typeof LOCAL_ALLOW_UNKNOWN_CAUSES)[number];
9
+ export type LocalAllowRedeemability = {
10
+ readonly kind: 'redeemable';
11
+ readonly via: 'layer' | 'consent';
12
+ } | {
13
+ readonly kind: 'not_redeemable';
14
+ readonly reason: LocalAllowNotRedeemableReason;
15
+ } | {
16
+ readonly kind: 'unknown';
17
+ readonly cause: LocalAllowUnknownCause;
18
+ };
19
+ export interface LocalAllowOfferedFacts {
20
+ readonly managedRulesOnly: boolean | 'unreadable';
21
+ readonly scopeRoot: string | undefined;
22
+ readonly road: EngineCapState;
23
+ }
24
+ export interface LocalAllowDeclaredFacts {
25
+ readonly source: HostSettingsLayer;
26
+ readonly workspace: WorkspaceTrustState | 'unreadable';
27
+ readonly managedRulesOnly: boolean | 'unreadable';
28
+ readonly scopeRoot: string | undefined;
29
+ readonly store: ListAllPersistedRulesOutcome;
30
+ }
31
+ export type LocalAllowRedeemabilityInput = ({
32
+ readonly subject: 'offered';
33
+ } & LocalAllowOfferedFacts) | ({
34
+ readonly subject: 'declared';
35
+ readonly rule: string;
36
+ } & LocalAllowDeclaredFacts);
37
+ export declare function localAllowRedeemability(input: LocalAllowRedeemabilityInput): LocalAllowRedeemability;
38
+ export declare function localAllowLandingRedeemability(rule: {} | null | undefined, landing: {} | null | undefined): LocalAllowRedeemability;
39
+ export declare const LOCAL_ALLOW_REDEMPTION_REFUSALS: readonly ["managed_only", "no_scope_root", "rule_shape", "request_refused", "preview_mismatch", "engine_refused", "ticket_dead"];
40
+ export type LocalAllowRedemptionRefusal = (typeof LOCAL_ALLOW_REDEMPTION_REFUSALS)[number];
41
+ export declare const LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES: readonly ["road_closed", "road_unknown", "policy_unreadable", "input_unreadable", "prepare_failed"];
42
+ export type LocalAllowRedemptionUnavailableCause = (typeof LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES)[number];
43
+ export declare const LOCAL_ALLOW_REDEMPTION_UNKNOWN_CAUSES: readonly ["redeem_unsettled", "receipt_unreadable"];
44
+ export type LocalAllowRedemptionUnknownCause = (typeof LOCAL_ALLOW_REDEMPTION_UNKNOWN_CAUSES)[number];
45
+ export type LocalAllowRedemptionReceipt = {
46
+ readonly kind: 'applied';
47
+ readonly outcome: 'persisted' | 'already_live';
48
+ readonly rule: string;
49
+ readonly scope: string;
50
+ } | {
51
+ readonly kind: 'refused';
52
+ readonly reason: LocalAllowRedemptionRefusal;
53
+ readonly detail?: string;
54
+ } | {
55
+ readonly kind: 'unavailable';
56
+ readonly cause: LocalAllowRedemptionUnavailableCause;
57
+ } | {
58
+ readonly kind: 'unknown';
59
+ readonly cause: LocalAllowRedemptionUnknownCause;
60
+ };
61
+ export type LocalAllowRedemptionFacade = Pick<RulesFacade, 'ccImportPrepare' | 'ccImportRedeem'>;
62
+ export interface ReadOnlyRuleOfferRedemption extends LocalAllowOfferedFacts {
63
+ readonly rule: string;
64
+ readonly expectedToolName?: string;
65
+ readonly settingsPath: string;
66
+ readonly deps: LocalAllowRuleDeps;
67
+ }
68
+ export declare function redeemReadOnlyRuleOffer(facade: LocalAllowRedemptionFacade, input: ReadOnlyRuleOfferRedemption, opts?: {
69
+ readonly signal?: AbortSignal;
70
+ }): Promise<LocalAllowRedemptionReceipt>;
@@ -0,0 +1,276 @@
1
+ import { displayUntrusted } from '../displayUntrusted.js';
2
+ import { readOwnDataValue } from '../ownKey.js';
3
+ import { layerAdmissionWord } from '../settingsLayerAdmission.js';
4
+ import { parseLocalAllowRule } from './localAllowRule.js';
5
+ import { classifyRulesFailure, classifySkippedReason, readCcImportRedeemCounts, } from './persistedRulesWire.js';
6
+ export const LOCAL_ALLOW_NOT_REDEEMABLE_REASONS = Object.freeze(['managed_only', 'tighten_only', 'no_scope_root', 'no_consent', 'road_closed']);
7
+ export const LOCAL_ALLOW_UNKNOWN_CAUSES = Object.freeze(['input_unreadable', 'policy_unreadable', 'consent_unreadable', 'road_unknown']);
8
+ function verdict(v) {
9
+ return Object.freeze({ ...v });
10
+ }
11
+ const notRedeemable = (reason) => verdict({ kind: 'not_redeemable', reason });
12
+ const unknownBecause = (cause) => verdict({ kind: 'unknown', cause });
13
+ const PROJECT_SCOPE_PREFIX = 'project:';
14
+ const ALLOW_BEHAVIOR = 'allow';
15
+ const MANAGED_LAYER = 'policySettings';
16
+ const DETAIL_SHOWN_MAX = 400;
17
+ function policyStep(v) {
18
+ if (v === true)
19
+ return notRedeemable('managed_only');
20
+ if (v === false)
21
+ return undefined;
22
+ return unknownBecause('policy_unreadable');
23
+ }
24
+ function scopeRootOf(v) {
25
+ return typeof v === 'string' && v !== '' ? v : undefined;
26
+ }
27
+ function storeRowConsents(row, rule, scopeRoot) {
28
+ if (typeof row !== 'object' || row === null)
29
+ return false;
30
+ const behavior = readOwnDataValue(row, 'behavior');
31
+ const text = readOwnDataValue(row, 'rule');
32
+ const scope = readOwnDataValue(row, 'scope');
33
+ return behavior === ALLOW_BEHAVIOR && text === rule && (scope === 'global' || scope === PROJECT_SCOPE_PREFIX + scopeRoot);
34
+ }
35
+ function consentRecorded(store, rule, scopeRoot) {
36
+ if (typeof store !== 'object' || store === null)
37
+ return undefined;
38
+ const ok = readOwnDataValue(store, 'ok');
39
+ if (ok !== true)
40
+ return undefined;
41
+ const rows = readOwnDataValue(store, 'rules');
42
+ if (!Array.isArray(rows))
43
+ return undefined;
44
+ for (let i = 0; i < rows.length; i++) {
45
+ if (storeRowConsents(readOwnDataValue(rows, String(i)), rule, scopeRoot))
46
+ return true;
47
+ }
48
+ return false;
49
+ }
50
+ function roadStep(road) {
51
+ if (road === 'true')
52
+ return verdict({ kind: 'redeemable', via: 'consent' });
53
+ if (road === 'false' || road === 'absent')
54
+ return notRedeemable('road_closed');
55
+ return unknownBecause('road_unknown');
56
+ }
57
+ export function localAllowRedeemability(input) {
58
+ try {
59
+ if (typeof input !== 'object' || input === null)
60
+ return unknownBecause('input_unreadable');
61
+ const subject = readOwnDataValue(input, 'subject');
62
+ if (subject !== 'offered' && subject !== 'declared')
63
+ return unknownBecause('input_unreadable');
64
+ const source = readOwnDataValue(input, 'source');
65
+ if (!(subject === 'declared' && source === MANAGED_LAYER)) {
66
+ const policy = policyStep(readOwnDataValue(input, 'managedRulesOnly'));
67
+ if (policy !== undefined)
68
+ return policy;
69
+ }
70
+ const scopeRoot = scopeRootOf(readOwnDataValue(input, 'scopeRoot'));
71
+ if (subject === 'offered') {
72
+ if (scopeRoot === undefined)
73
+ return notRedeemable('no_scope_root');
74
+ return roadStep(readOwnDataValue(input, 'road'));
75
+ }
76
+ const workspace = readOwnDataValue(input, 'workspace');
77
+ let word;
78
+ try {
79
+ word = layerAdmissionWord(source, workspace, 'permissions', ALLOW_BEHAVIOR);
80
+ }
81
+ catch {
82
+ return unknownBecause('input_unreadable');
83
+ }
84
+ if (word === 'admitted')
85
+ return verdict({ kind: 'redeemable', via: 'layer' });
86
+ if (word === 'tighten_only')
87
+ return notRedeemable('tighten_only');
88
+ if (scopeRoot === undefined)
89
+ return notRedeemable('no_scope_root');
90
+ const rule = readOwnDataValue(input, 'rule');
91
+ if (typeof rule !== 'string' || rule === '')
92
+ return unknownBecause('input_unreadable');
93
+ const consent = consentRecorded(readOwnDataValue(input, 'store'), rule, scopeRoot);
94
+ if (consent === undefined)
95
+ return unknownBecause('consent_unreadable');
96
+ return consent ? verdict({ kind: 'redeemable', via: 'consent' }) : notRedeemable('no_consent');
97
+ }
98
+ catch {
99
+ return unknownBecause('input_unreadable');
100
+ }
101
+ }
102
+ export function localAllowLandingRedeemability(rule, landing) {
103
+ try {
104
+ if (typeof rule !== 'string' || rule.trim() === '')
105
+ return unknownBecause('input_unreadable');
106
+ if (typeof landing !== 'object' || landing === null)
107
+ return unknownBecause('input_unreadable');
108
+ return localAllowRedeemability({
109
+ subject: 'declared',
110
+ rule,
111
+ source: readOwnDataValue(landing, 'source'),
112
+ workspace: readOwnDataValue(landing, 'workspace'),
113
+ managedRulesOnly: readOwnDataValue(landing, 'managedRulesOnly'),
114
+ scopeRoot: readOwnDataValue(landing, 'scopeRoot'),
115
+ store: readOwnDataValue(landing, 'store'),
116
+ });
117
+ }
118
+ catch {
119
+ return unknownBecause('input_unreadable');
120
+ }
121
+ }
122
+ export const LOCAL_ALLOW_REDEMPTION_REFUSALS = Object.freeze([
123
+ 'managed_only',
124
+ 'no_scope_root',
125
+ 'rule_shape',
126
+ 'request_refused',
127
+ 'preview_mismatch',
128
+ 'engine_refused',
129
+ 'ticket_dead',
130
+ ]);
131
+ export const LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES = Object.freeze(['road_closed', 'road_unknown', 'policy_unreadable', 'input_unreadable', 'prepare_failed']);
132
+ export const LOCAL_ALLOW_REDEMPTION_UNKNOWN_CAUSES = Object.freeze(['redeem_unsettled', 'receipt_unreadable']);
133
+ function singleRuleLayerContent(canonical) {
134
+ return JSON.stringify({ permissions: { allow: [canonical] } });
135
+ }
136
+ function receipt(r) {
137
+ return Object.freeze({ ...r });
138
+ }
139
+ function detailOf(text) {
140
+ if (typeof text !== 'string')
141
+ return {};
142
+ const shown = displayUntrusted(text, { max: DETAIL_SHOWN_MAX });
143
+ return shown.trim() !== '' ? { detail: shown } : {};
144
+ }
145
+ const JUDGED_RECEIPT = {
146
+ managed_only: { kind: 'refused', reason: 'managed_only' },
147
+ no_scope_root: { kind: 'refused', reason: 'no_scope_root' },
148
+ road_closed: { kind: 'unavailable', cause: 'road_closed' },
149
+ road_unknown: { kind: 'unavailable', cause: 'road_unknown' },
150
+ policy_unreadable: { kind: 'unavailable', cause: 'policy_unreadable' },
151
+ input_unreadable: { kind: 'unavailable', cause: 'input_unreadable' },
152
+ tighten_only: { kind: 'unavailable', cause: 'input_unreadable' },
153
+ no_consent: { kind: 'unavailable', cause: 'input_unreadable' },
154
+ consent_unreadable: { kind: 'unavailable', cause: 'input_unreadable' },
155
+ };
156
+ function previewSingle(prep, scopeRoot) {
157
+ const preview = typeof prep === 'object' && prep !== null ? readOwnDataValue(prep, 'preview') : undefined;
158
+ if (typeof preview !== 'object' || preview === null)
159
+ return { mismatch: true };
160
+ const skipped = readOwnDataValue(preview, 'skipped');
161
+ if (Array.isArray(skipped) && skipped.length > 0) {
162
+ const first = readOwnDataValue(skipped, '0');
163
+ const reason = typeof first === 'object' && first !== null ? readOwnDataValue(first, 'reason') : undefined;
164
+ return { mismatch: true, skipped: typeof reason === 'string' ? classifySkippedReason(reason).text : '' };
165
+ }
166
+ if (!Array.isArray(skipped))
167
+ return { mismatch: true };
168
+ const candidates = readOwnDataValue(preview, 'candidates');
169
+ if (!Array.isArray(candidates) || candidates.length !== 1)
170
+ return { mismatch: true };
171
+ const c = readOwnDataValue(candidates, '0');
172
+ if (typeof c !== 'object' || c === null)
173
+ return { mismatch: true };
174
+ const behavior = readOwnDataValue(c, 'behavior');
175
+ const rule = readOwnDataValue(c, 'rule');
176
+ const scope = readOwnDataValue(c, 'scope');
177
+ const kind = typeof scope === 'object' && scope !== null ? readOwnDataValue(scope, 'kind') : undefined;
178
+ const root = typeof scope === 'object' && scope !== null ? readOwnDataValue(scope, 'root') : undefined;
179
+ if (behavior !== ALLOW_BEHAVIOR || typeof rule !== 'string' || kind !== 'project' || root !== scopeRoot)
180
+ return { mismatch: true };
181
+ return { rule };
182
+ }
183
+ export async function redeemReadOnlyRuleOffer(facade, input, opts) {
184
+ let canonical;
185
+ let offered;
186
+ let scopeRoot;
187
+ let settingsPath;
188
+ try {
189
+ if (typeof input !== 'object' || input === null)
190
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
191
+ const judged = localAllowRedeemability({
192
+ subject: 'offered',
193
+ managedRulesOnly: readOwnDataValue(input, 'managedRulesOnly'),
194
+ scopeRoot: readOwnDataValue(input, 'scopeRoot'),
195
+ road: readOwnDataValue(input, 'road'),
196
+ });
197
+ if (judged.kind !== 'redeemable')
198
+ return receipt(JUDGED_RECEIPT[judged.kind === 'not_redeemable' ? judged.reason : judged.cause]);
199
+ scopeRoot = readOwnDataValue(input, 'scopeRoot');
200
+ const path = readOwnDataValue(input, 'settingsPath');
201
+ if (typeof path !== 'string' || path === '')
202
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
203
+ settingsPath = path;
204
+ const rule = readOwnDataValue(input, 'rule');
205
+ const expected = readOwnDataValue(input, 'expectedToolName');
206
+ const deps = readOwnDataValue(input, 'deps');
207
+ if (typeof rule !== 'string' || deps === undefined || (expected !== undefined && typeof expected !== 'string'))
208
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
209
+ const parsed = parseLocalAllowRule(rule, expected, deps);
210
+ if (!parsed.ok)
211
+ return receipt({ kind: 'refused', reason: 'rule_shape', ...detailOf(parsed.error) });
212
+ canonical = parsed.canonical;
213
+ offered = rule;
214
+ }
215
+ catch {
216
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
217
+ }
218
+ const signal = opts?.signal;
219
+ let prep;
220
+ try {
221
+ prep = await facade.ccImportPrepare([{ layer: 'localSettings', path: settingsPath, root: scopeRoot, content: singleRuleLayerContent(canonical) }], signal !== undefined ? { signal } : undefined);
222
+ }
223
+ catch (e) {
224
+ const f = classifyRulesFailure(e);
225
+ if (f.kind === 'lane-unavailable' || f.kind === 'route-missing')
226
+ return receipt({ kind: 'unavailable', cause: 'road_closed' });
227
+ if (f.kind === 'body-shape' || f.kind === 'field-refused' || f.kind === 'too-many-candidates' || f.kind === 'forbidden' || f.kind === 'operators-unconfigured') {
228
+ return receipt({ kind: 'refused', reason: 'request_refused' });
229
+ }
230
+ return receipt({ kind: 'unavailable', cause: 'prepare_failed' });
231
+ }
232
+ let ticket;
233
+ let storedRule;
234
+ try {
235
+ const single = previewSingle(prep, scopeRoot);
236
+ if ('mismatch' in single) {
237
+ return receipt(single.skipped !== undefined ? { kind: 'refused', reason: 'engine_refused', ...detailOf(single.skipped) } : { kind: 'refused', reason: 'preview_mismatch' });
238
+ }
239
+ if (single.rule !== canonical && single.rule !== offered)
240
+ return receipt({ kind: 'refused', reason: 'preview_mismatch' });
241
+ storedRule = single.rule;
242
+ const t = readOwnDataValue(prep, 'ticket');
243
+ if (typeof t !== 'string' || t === '')
244
+ return receipt({ kind: 'refused', reason: 'preview_mismatch' });
245
+ ticket = t;
246
+ }
247
+ catch {
248
+ return receipt({ kind: 'refused', reason: 'preview_mismatch' });
249
+ }
250
+ let body;
251
+ try {
252
+ body = await facade.ccImportRedeem(ticket, signal !== undefined ? { signal } : undefined);
253
+ }
254
+ catch (e) {
255
+ const f = classifyRulesFailure(e);
256
+ if (f.kind === 'ticket-dead')
257
+ return receipt({ kind: 'refused', reason: 'ticket_dead' });
258
+ if (f.kind === 'lane-unavailable')
259
+ return receipt({ kind: 'unavailable', cause: 'road_closed' });
260
+ return receipt({ kind: 'unknown', cause: 'redeem_unsettled' });
261
+ }
262
+ const counts = readCcImportRedeemCounts(body);
263
+ if (counts === null)
264
+ return receipt({ kind: 'unknown', cause: 'receipt_unreadable' });
265
+ const refusedRow = counts.refused?.[0];
266
+ if (refusedRow !== undefined)
267
+ return receipt({ kind: 'refused', reason: 'engine_refused', ...detailOf(refusedRow.detail) });
268
+ if (counts.indeterminate.length > 0)
269
+ return receipt({ kind: 'unknown', cause: 'redeem_unsettled' });
270
+ const scope = PROJECT_SCOPE_PREFIX + scopeRoot;
271
+ if (counts.persisted === 1 && counts.deduped === 0)
272
+ return receipt({ kind: 'applied', outcome: 'persisted', rule: storedRule, scope });
273
+ if (counts.deduped === 1 && counts.persisted === 0)
274
+ return receipt({ kind: 'applied', outcome: 'already_live', rule: storedRule, scope });
275
+ return receipt({ kind: 'unknown', cause: 'receipt_unreadable' });
276
+ }
@@ -4,6 +4,7 @@ import { hostSessionFor } from '../host.js';
4
4
  import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
5
5
  import { pendingRowIsOfSession } from './parkRowDelegation.js';
6
6
  import { slotReadOf } from '../wireFailureShape.js';
7
+ import { safetyStopNamed } from './hitlBridge.js';
7
8
  export function pendingRowIsOwnedByThisSession(row, deps) {
8
9
  return ownershipVerdictOf(row, deps) === 'owned';
9
10
  }
@@ -70,5 +71,5 @@ export function askQuestionsFromPending(pending) {
70
71
  return usableAskQuestions(input.questions);
71
72
  }
72
73
  export function isHitlSafetyErrorLike(e) {
73
- return typeof e === 'object' && e !== null && e.name === 'HitlSafetyError';
74
+ return typeof e === 'object' && e !== null && safetyStopNamed(e);
74
75
  }
@@ -1,21 +1,23 @@
1
- import { putOwnKey, readOwnDataValue } from '../ownKey.js';
1
+ import { guardedRead, putOwnKey, readOwnDataValue } from '../ownKey.js';
2
2
  import { HitlBridge, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, } from './hitlBridge.js';
3
3
  import { approvalResolutionDetail, approvalResolutionStatusTag, directSafetyStopOf, resolutionOfDecisionFailure } from './approvalResolution.js';
4
4
  import { readDecisionRefusalAsk } from './decisionRefusal.js';
5
5
  import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
6
6
  import { hostLog } from '../host.js';
7
7
  import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
8
- import { INTERRUPT_DENY_REASON, observeCancelByDeny } from './hitlHostSurface.js';
8
+ import { INTERRUPT_DENY_REASON, interruptDenyLifetime, observeCancelByDeny } from './hitlHostSurface.js';
9
9
  import { denyAttributionOf, flushHeldWithInterruptRewrite, isAskTool } from './frameRouter.js';
10
10
  import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
11
11
  import { inheritFrameBinding } from '../frameBinding.js';
12
12
  import { approvalCallKey, askGateQuestionId } from './gateIdentity.js';
13
+ import { parkQuestionFrameBits, sourceGenerationNow } from './screenCardCarriers.js';
13
14
  import { usableAskQuestions } from './parkOwnership.js';
14
15
  import { readDecideReceipt } from '../decideReceipt.js';
15
16
  import { decideFailureNote } from '../decideFailureNote.js';
16
17
  import { wireFailureTextsOf } from '../wireFailureShape.js';
17
18
  import { msg, renderMessage } from '../i18n/translate.js';
18
19
  import { refusedParkFromError } from '../resumeRefusalCopy.js';
20
+ import { decisionRefusalWayOutContent } from '../checkpointRowVerdict.js';
19
21
  export const MAX_GATE_HOPS = 2;
20
22
  export function nextHopBudget(prev, round) {
21
23
  const total = (prev?.total ?? 0) + 1;
@@ -65,6 +67,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
65
67
  if (!hasQuestionOverlay()) {
66
68
  return { kind: 'not_sent', lane: 'ask', cause: 'card_unavailable', detail: NO_QUESTION_OVERLAY_REASON };
67
69
  }
70
+ const sourceGeneration = sourceGenerationNow(deps);
68
71
  const found = await findPendingForTask(deps.client, taskId, isAskTool, signal ? { signal } : undefined, parkGatedCallId, (row) => isAskTool(typeof row.toolName === 'string' ? row.toolName : undefined));
69
72
  if (!found.ok)
70
73
  return { kind: 'not_sent', lane: 'ask', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}), ...(found.rowVerdict !== undefined ? { rowVerdict: found.rowVerdict } : {}) };
@@ -113,7 +116,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
113
116
  }
114
117
  signal?.addEventListener('abort', onAbort, { once: true });
115
118
  onPresented?.();
116
- publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}) });
119
+ publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}), ...parkQuestionFrameBits(taskId, gatedCallId, sourceGeneration) });
117
120
  });
118
121
  if (withdrawn !== undefined) {
119
122
  hostLog('debug', `liveHitlAskWire: question-withdrawn-by-host card=${questionId} verdict=${withdrawn.rowVerdict.kind}`);
@@ -121,7 +124,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
121
124
  }
122
125
  const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
123
126
  if (answer === null) {
124
- observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending), taskId);
127
+ observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, { signal: interruptDenyLifetime() }, pending), taskId);
125
128
  return { kind: 'unsettled', ...at, cause: 'interrupted' };
126
129
  }
127
130
  let declinedReceipt;
@@ -194,6 +197,7 @@ async function surfaceParkGate(park, ctx, gatedCallId, onPresented) {
194
197
  ...(onPresented ? { onPresented } : {}),
195
198
  ...(deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : {}),
196
199
  ...(decidedByReading !== undefined ? { approvalDecidedBy: decidedByReading } : {}),
200
+ ...((read) => (typeof read === 'function' ? { engineGeneration: () => read.call(deps) } : {}))(guardedRead(() => deps.engineGeneration)),
197
201
  }, taskId, argsByCallOf(led), ctx.signal, gatedCallId, park.toolName);
198
202
  if (outcome.kind === 'decided' && outcome.decision === 'deny' && outcome.gatedCallId) {
199
203
  led.markDenied(outcome.gatedCallId, denyAttributionOf(outcome.deny));
@@ -329,6 +333,10 @@ function failsoftWayOutOf(outcome) {
329
333
  return 'speak';
330
334
  if (outcome.kind === 'not_sent' && (outcome.cause === 'row_unresumable' || (outcome.cause === 'safety_stop' && outcome.safetyCode === 'binding_missing')))
331
335
  return 'reason';
336
+ if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && outcome.refusal?.wayOut === 'newer_worker')
337
+ return 'newer_worker';
338
+ if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && outcome.refusal?.wayOut === 'cancel_run')
339
+ return 'cancel';
332
340
  if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && (refusedParkFromError(outcome.refusal) !== null || outcome.refusal?.conflict === 'row_integrity'))
333
341
  return 'cancel';
334
342
  if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused')
@@ -345,6 +353,8 @@ function failsoftReasonSentence(park, failedReason, terminal, wayOut) {
345
353
  return renderMessage(msg('approval.park.stillParked', { gate, reason: failedReason }));
346
354
  if (wayOut === 'cancel')
347
355
  return renderMessage(msg('approval.park.refusedNotResumable', { gate, reason: failedReason }));
356
+ if (wayOut === 'newer_worker')
357
+ return renderMessage(msg('approval.park.newerWorker', { gate, reason: failedReason, wayOut: decisionRefusalWayOutContent('newer_worker') ?? '' }));
348
358
  return renderMessage(msg(wayOut === 'speak' ? 'approval.park.resume.speak' : 'approval.park.resume.card', { gate, reason: failedReason }));
349
359
  }
350
360
  function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface, wayOut) {
@@ -44,6 +44,11 @@ export interface CcImportRedeemCounts {
44
44
  }
45
45
  export type CcImportReceiptOutcome = 'persisted' | 'no-op' | 'refused' | 'indeterminate';
46
46
  export declare function readCcImportRedeemCounts(body: unknown): CcImportRedeemCounts | null;
47
+ export interface CcImportRefusedReading {
48
+ readonly count: number;
49
+ readonly rows: readonly CcImportRedeemRefused[];
50
+ }
51
+ export declare function refusedRowOf(counts: CcImportRedeemCounts | null | undefined): CcImportRefusedReading | null;
47
52
  export type RulesFailure = {
48
53
  kind: 'lane-unavailable';
49
54
  message: string;
@@ -189,6 +194,17 @@ export declare function classifySkippedReason(reason: string): {
189
194
  code?: string;
190
195
  text: string;
191
196
  };
197
+ export interface CcImportSkippedGroup {
198
+ readonly entries: readonly string[];
199
+ readonly reason: string;
200
+ readonly code?: string;
201
+ }
202
+ export interface CcImportSkippedReading {
203
+ readonly shape: 'absent' | 'malformed' | 'read';
204
+ readonly groups: readonly CcImportSkippedGroup[];
205
+ readonly unreadable: number;
206
+ }
207
+ export declare function ccImportSkippedRowsOf(skipped: {} | null | undefined): CcImportSkippedReading;
192
208
  export type RulePersistOutcome = {
193
209
  state: 'unknown';
194
210
  } | {
@@ -133,6 +133,35 @@ export function readCcImportRedeemCounts(body) {
133
133
  return null;
134
134
  }
135
135
  }
136
+ const isCount = (r) => r.presence === 'present' && typeof r.value === 'number' && Number.isSafeInteger(r.value) && r.value >= 0;
137
+ const NO_REFUSED_ROWS = Object.freeze([]);
138
+ const NO_REFUSED = Object.freeze({ count: 0, rows: NO_REFUSED_ROWS });
139
+ export function refusedRowOf(counts) {
140
+ try {
141
+ if (typeof counts !== 'object' || counts === null)
142
+ return null;
143
+ if (!isCount(slotReadOf(counts, 'persisted')) || !isCount(slotReadOf(counts, 'deduped')))
144
+ return null;
145
+ const slot = slotReadOf(counts, 'refused');
146
+ if (slot.presence === 'absent')
147
+ return NO_REFUSED;
148
+ if (slot.presence !== 'present')
149
+ return null;
150
+ const rows = ownArrayItems(slot.value);
151
+ if (rows === undefined)
152
+ return null;
153
+ const kept = [];
154
+ for (const row of rows) {
155
+ if (typeof row !== 'object' || row === null)
156
+ return null;
157
+ kept.push(row);
158
+ }
159
+ return kept.length === 0 ? NO_REFUSED : Object.freeze({ count: kept.length, rows: Object.freeze(kept) });
160
+ }
161
+ catch {
162
+ return null;
163
+ }
164
+ }
136
165
  export function classifyRulesFailure(e, context) {
137
166
  const { status, errorCode, message, retryAfterSec, committed } = wireFailureShapeOf(e);
138
167
  if (errorCode === 'capability.rule_store_required') {
@@ -635,6 +664,47 @@ export function classifySkippedReason(reason) {
635
664
  return { text };
636
665
  return { code: head, text };
637
666
  }
667
+ const CC_IMPORT_SKIPPED_READ_BUDGET = 20_000;
668
+ const CC_IMPORT_SKIPPED_SHOWN_MAX = 2000;
669
+ function ccImportSkippedReading(shape, groups, unreadable) {
670
+ return Object.freeze({ shape, groups: Object.freeze([...groups]), unreadable });
671
+ }
672
+ export function ccImportSkippedRowsOf(skipped) {
673
+ if (skipped === undefined)
674
+ return ccImportSkippedReading('absent', [], 0);
675
+ const grouped = new Map();
676
+ let unreadable = 0;
677
+ try {
678
+ if (!Array.isArray(skipped))
679
+ return ccImportSkippedReading('malformed', [], 0);
680
+ const length = readOwnDataValue(skipped, 'length');
681
+ if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0 || length > CC_IMPORT_SKIPPED_READ_BUDGET)
682
+ return ccImportSkippedReading('malformed', [], 0);
683
+ for (let i = 0; i < length; i++) {
684
+ const row = guardedRead(() => readOwnDataValue(skipped, String(i)));
685
+ const rule = typeof row === 'object' && row !== null ? guardedRead(() => readOwnDataValue(row, 'rule')) : undefined;
686
+ const reason = typeof row === 'object' && row !== null ? guardedRead(() => readOwnDataValue(row, 'reason')) : undefined;
687
+ if (typeof rule !== 'string' || typeof reason !== 'string') {
688
+ unreadable += 1;
689
+ continue;
690
+ }
691
+ const entries = grouped.get(reason);
692
+ if (entries === undefined)
693
+ grouped.set(reason, [displayUntrusted(rule, { max: CC_IMPORT_SKIPPED_SHOWN_MAX })]);
694
+ else
695
+ entries.push(displayUntrusted(rule, { max: CC_IMPORT_SKIPPED_SHOWN_MAX }));
696
+ }
697
+ }
698
+ catch {
699
+ return ccImportSkippedReading('malformed', [], 0);
700
+ }
701
+ const groups = [];
702
+ for (const [raw, entries] of grouped) {
703
+ const code = classifySkippedReason(raw).code;
704
+ groups.push(Object.freeze({ entries: Object.freeze(entries), reason: displayUntrusted(raw, { max: CC_IMPORT_SKIPPED_SHOWN_MAX }), ...(code !== undefined ? { code } : {}) }));
705
+ }
706
+ return ccImportSkippedReading('read', groups, unreadable);
707
+ }
638
708
  export function readRulePersistOutcome(ack) {
639
709
  const parsed = readToolApprovalRespondAck(ack);
640
710
  if (parsed === undefined || isToolApprovalRespondReplayed(parsed) || isToolApprovalRespondParkedRedeemed(parsed))