@sema-agent/client-core 0.109.0 → 0.111.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 (236) hide show
  1. package/CHANGELOG.md +342 -0
  2. package/README.md +88 -32
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +45 -11
  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 +20 -15
  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/attachmentRoute.d.ts +20 -0
  16. package/dist/attachmentRoute.js +39 -0
  17. package/dist/attachmentsWireCaps.js +1 -1
  18. package/dist/builtinOutputStyles.d.ts +12 -0
  19. package/dist/builtinOutputStyles.js +147 -0
  20. package/dist/compactionStatus.d.ts +9 -0
  21. package/dist/compactionStatus.js +29 -5
  22. package/dist/controlRouter.js +2 -1
  23. package/dist/coreValuePorts.d.ts +39 -1
  24. package/dist/coreValuePorts.js +10 -0
  25. package/dist/declared/supportFloorDeclared.js +1 -1
  26. package/dist/displayUntrusted.d.ts +1 -0
  27. package/dist/displayUntrusted.js +138 -14
  28. package/dist/engineErrorCodes.d.ts +3 -0
  29. package/dist/engineErrorCodes.js +3 -0
  30. package/dist/engineHealthFacts.d.ts +20 -0
  31. package/dist/engineHealthFacts.js +42 -1
  32. package/dist/engineHealthProbeVerdict.d.ts +23 -0
  33. package/dist/engineHealthProbeVerdict.js +115 -0
  34. package/dist/engineInlineTaskStats.d.ts +4 -3
  35. package/dist/engineInlineTaskStats.js +18 -2
  36. package/dist/engineNoticeCodes.d.ts +10 -0
  37. package/dist/engineNoticeCodes.js +36 -6
  38. package/dist/engineRepoint.d.ts +147 -0
  39. package/dist/engineRepoint.js +310 -0
  40. package/dist/engineRowRef.d.ts +3 -0
  41. package/dist/engineRowRef.js +13 -1
  42. package/dist/engineSessionMessages.d.ts +2 -1
  43. package/dist/engineSessionMessages.js +5 -0
  44. package/dist/engineSwapCrossing.d.ts +57 -0
  45. package/dist/engineSwapCrossing.js +297 -0
  46. package/dist/engineUnavailable.d.ts +44 -0
  47. package/dist/engineUnavailable.js +492 -0
  48. package/dist/errorSupplementRoles.js +2 -1
  49. package/dist/eventLeg.js +3 -3
  50. package/dist/fleet/fleetProjection.js +3 -2
  51. package/dist/generated/engineFactTables.d.ts +2 -0
  52. package/dist/generated/engineFactTables.js +12 -0
  53. package/dist/generated/sdkFactTables.d.ts +2 -1
  54. package/dist/generated/sdkFactTables.js +5 -0
  55. package/dist/generated/settingsSchemaTables.d.ts +4 -0
  56. package/dist/generated/settingsSchemaTables.js +1 -0
  57. package/dist/hitl/approvalAskers.d.ts +6 -0
  58. package/dist/hitl/approvalAskers.js +30 -0
  59. package/dist/hitl/approvalCardReasons.js +6 -3
  60. package/dist/hitl/approvalDelivery.d.ts +1 -0
  61. package/dist/hitl/approvalDelivery.js +1 -1
  62. package/dist/hitl/approvalRequestFrameReading.d.ts +7 -0
  63. package/dist/hitl/approvalRequestFrameReading.js +11 -0
  64. package/dist/hitl/approvalResolution.d.ts +9 -2
  65. package/dist/hitl/approvalResolution.js +6 -1
  66. package/dist/hitl/askGateWire.js +5 -8
  67. package/dist/hitl/automaticAnswerVerdict.js +2 -2
  68. package/dist/hitl/decisionConflict.js +6 -3
  69. package/dist/hitl/decisionRefusal.d.ts +2 -0
  70. package/dist/hitl/decisionRefusal.js +8 -8
  71. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  72. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  73. package/dist/hitl/decisionSend.d.ts +10 -0
  74. package/dist/hitl/decisionSend.js +57 -17
  75. package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
  76. package/dist/hitl/editedApprovalAcceptance.js +26 -0
  77. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  78. package/dist/hitl/elicitationRefusal.js +23 -0
  79. package/dist/hitl/frameRouter.d.ts +2 -2
  80. package/dist/hitl/frameRouter.js +12 -33
  81. package/dist/hitl/gateIdentity.d.ts +5 -0
  82. package/dist/hitl/gateIdentity.js +23 -0
  83. package/dist/hitl/gateLedger.d.ts +0 -4
  84. package/dist/hitl/gateLedger.js +0 -19
  85. package/dist/hitl/hitlBridge.d.ts +7 -0
  86. package/dist/hitl/hitlBridge.js +14 -8
  87. package/dist/hitl/hitlHostSurface.d.ts +3 -1
  88. package/dist/hitl/hitlHostSurface.js +14 -4
  89. package/dist/hitl/hostWithdrawal.d.ts +15 -0
  90. package/dist/hitl/hostWithdrawal.js +38 -0
  91. package/dist/hitl/localAllowRedemption.d.ts +67 -0
  92. package/dist/hitl/localAllowRedemption.js +256 -0
  93. package/dist/hitl/parkResolver.js +32 -13
  94. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  95. package/dist/hitl/persistedRulesWire.js +72 -2
  96. package/dist/hitl/planReviewWire.d.ts +3 -0
  97. package/dist/hitl/planReviewWire.js +37 -10
  98. package/dist/hitl/policyDenyCause.d.ts +1 -1
  99. package/dist/hitl/policyDenyCause.js +1 -1
  100. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  101. package/dist/hitl/resumeRunningCard.js +1 -0
  102. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  103. package/dist/hitl/screenCardCarriers.js +41 -0
  104. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  105. package/dist/hitl/screenDecisionCards.js +362 -0
  106. package/dist/hitl/sessionPolicyWire.js +6 -4
  107. package/dist/hitl/standingRows.d.ts +4 -1
  108. package/dist/hitl/standingRows.js +33 -1
  109. package/dist/hitl/toolApprovalWire.d.ts +64 -15
  110. package/dist/hitl/toolApprovalWire.js +470 -125
  111. package/dist/hooksWireCaps.js +31 -1
  112. package/dist/hostSettingNoticeArm.d.ts +3 -0
  113. package/dist/hostSettingNoticeArm.js +12 -0
  114. package/dist/hostTurnSettings.d.ts +13 -4
  115. package/dist/hostTurnSettings.js +150 -10
  116. package/dist/humanTurnRead.js +11 -1
  117. package/dist/i18n/messages.en.d.ts +212 -0
  118. package/dist/i18n/messages.en.js +217 -5
  119. package/dist/i18n/messages.zh.js +235 -5
  120. package/dist/i18n/translate.d.ts +3 -0
  121. package/dist/i18n/translate.js +78 -10
  122. package/dist/i18n/zh.d.ts +1 -0
  123. package/dist/i18n/zh.js +1 -0
  124. package/dist/index.d.ts +56 -16
  125. package/dist/index.js +40 -12
  126. package/dist/inheritEnvCapability.js +2 -0
  127. package/dist/inputAuthor.d.ts +16 -0
  128. package/dist/inputAuthor.js +44 -0
  129. package/dist/interactiveHalt.js +2 -1
  130. package/dist/interactiveInterrupt.js +20 -2
  131. package/dist/interruptRefusalNote.d.ts +2 -0
  132. package/dist/interruptRefusalNote.js +17 -0
  133. package/dist/liveModelCatalog.d.ts +2 -0
  134. package/dist/liveModelCatalog.js +23 -9
  135. package/dist/liveQuestionStore.d.ts +4 -1
  136. package/dist/liveQuestionStore.js +12 -3
  137. package/dist/localEngineSeatReading.d.ts +2 -2
  138. package/dist/localEngineSeatReading.js +5 -3
  139. package/dist/localEngineSpawn.d.ts +0 -7
  140. package/dist/localEngineSpawn.js +0 -92
  141. package/dist/localEngineStep.d.ts +2 -2
  142. package/dist/localEngineStep.js +2 -0
  143. package/dist/mcpEngineLeg.d.ts +3 -0
  144. package/dist/mcpEngineLeg.js +38 -1
  145. package/dist/mcpPanel.js +2 -0
  146. package/dist/mcpReconnect.d.ts +9 -1
  147. package/dist/mcpReconnect.js +124 -19
  148. package/dist/mcpWireCaps.js +1 -1
  149. package/dist/model/engineModelWords.d.ts +3 -0
  150. package/dist/model/engineModelWords.js +13 -0
  151. package/dist/model/subagentModelWord.d.ts +21 -2
  152. package/dist/model/subagentModelWord.js +100 -29
  153. package/dist/modelCapabilityProbe.js +7 -43
  154. package/dist/modsFace.d.ts +8 -1
  155. package/dist/modsFace.js +27 -0
  156. package/dist/notifications.d.ts +36 -6
  157. package/dist/notifications.js +170 -20
  158. package/dist/ownKey.d.ts +6 -5
  159. package/dist/ownKey.js +31 -7
  160. package/dist/peerFrames.d.ts +40 -2
  161. package/dist/peerFrames.js +158 -3
  162. package/dist/pendingUserRow.d.ts +4 -0
  163. package/dist/pendingUserRow.js +16 -0
  164. package/dist/permissionsDeclaration.d.ts +1 -0
  165. package/dist/permissionsDeclaration.js +11 -1
  166. package/dist/planReviewCardFacts.d.ts +3 -0
  167. package/dist/planReviewCardFacts.js +52 -2
  168. package/dist/pluginHooksWire.d.ts +10 -1
  169. package/dist/pluginHooksWire.js +96 -7
  170. package/dist/printToolResultFrame.js +4 -2
  171. package/dist/promptBlockedCopy.js +11 -6
  172. package/dist/promptSuggestionBatch.d.ts +7 -0
  173. package/dist/promptSuggestionBatch.js +9 -0
  174. package/dist/promptSuggestionsTail.d.ts +39 -0
  175. package/dist/promptSuggestionsTail.js +116 -0
  176. package/dist/replayBody.js +10 -2
  177. package/dist/request/taskRequest.d.ts +5 -0
  178. package/dist/request/taskRequest.js +142 -21
  179. package/dist/requestCoordinate.d.ts +1 -0
  180. package/dist/requestCoordinate.js +13 -0
  181. package/dist/resumeRefusalCopy.d.ts +1 -3
  182. package/dist/resumeRefusalCopy.js +2 -1
  183. package/dist/retryStatus.d.ts +2 -2
  184. package/dist/retryStatus.js +2 -0
  185. package/dist/rewindArchiveReading.js +4 -4
  186. package/dist/runCancelContext.d.ts +2 -1
  187. package/dist/runCancelContext.js +1 -0
  188. package/dist/runPermissionModeSwitch.js +2 -2
  189. package/dist/scenarioRefusal.js +11 -13
  190. package/dist/scenarioSkills.js +11 -6
  191. package/dist/seam.d.ts +27 -2
  192. package/dist/seam.js +17 -1
  193. package/dist/seatContract.d.ts +189 -12
  194. package/dist/seatContract.js +401 -9
  195. package/dist/sessionBackground.js +2 -1
  196. package/dist/sessionBackgroundHandoff.js +2 -1
  197. package/dist/sessionCostReading.d.ts +70 -0
  198. package/dist/sessionCostReading.js +150 -0
  199. package/dist/sessionEngine.d.ts +2 -0
  200. package/dist/sessionEngine.js +111 -0
  201. package/dist/sessionHooksRoster.d.ts +4 -0
  202. package/dist/sessionHooksRoster.js +158 -0
  203. package/dist/sessionMemoryStatus.d.ts +2 -1
  204. package/dist/sessionMemoryStatus.js +2 -2
  205. package/dist/sessionReadRedaction.d.ts +7 -0
  206. package/dist/sessionReadRedaction.js +26 -0
  207. package/dist/sessionSyncFileHistory.d.ts +4 -0
  208. package/dist/sessionSyncFileHistory.js +27 -0
  209. package/dist/settingsHooksSalvage.js +2 -1
  210. package/dist/sseIdleTriage.d.ts +13 -1
  211. package/dist/sseIdleTriage.js +25 -6
  212. package/dist/storeDurabilityWord.d.ts +15 -0
  213. package/dist/storeDurabilityWord.js +18 -0
  214. package/dist/storePostureCapability.d.ts +7 -3
  215. package/dist/storePostureCapability.js +69 -39
  216. package/dist/subagent/engineSubagentResume.js +2 -2
  217. package/dist/subagent/engineSubagentTail.js +8 -1
  218. package/dist/subagentContentStore.d.ts +11 -0
  219. package/dist/subagentContentStore.js +28 -2
  220. package/dist/terminalStatuses.d.ts +1 -0
  221. package/dist/terminalStatuses.js +4 -1
  222. package/dist/toolResult.d.ts +1 -0
  223. package/dist/toolResult.js +2 -0
  224. package/dist/turnFailurePresentation.js +9 -2
  225. package/dist/typePins.d.ts +11 -0
  226. package/dist/usageAnalytics.d.ts +17 -0
  227. package/dist/usageAnalytics.js +44 -2
  228. package/dist/userAttachmentEnvelope.d.ts +2 -1
  229. package/dist/userAttachmentEnvelope.js +10 -0
  230. package/dist/wireErrorTriage.js +2 -1
  231. package/dist/wireFailureShape.d.ts +6 -5
  232. package/dist/wireFailureShape.js +150 -38
  233. package/dist/wireRefusalCopy.d.ts +16 -5
  234. package/dist/wireRefusalCopy.js +119 -86
  235. package/docs/INTEGRATION-CLIENTS.md +4228 -21
  236. package/package.json +7 -3
@@ -0,0 +1,256 @@
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 const LOCAL_ALLOW_REDEMPTION_REFUSALS = Object.freeze([
103
+ 'managed_only',
104
+ 'no_scope_root',
105
+ 'rule_shape',
106
+ 'request_refused',
107
+ 'preview_mismatch',
108
+ 'engine_refused',
109
+ 'ticket_dead',
110
+ ]);
111
+ export const LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES = Object.freeze(['road_closed', 'road_unknown', 'policy_unreadable', 'input_unreadable', 'prepare_failed']);
112
+ export const LOCAL_ALLOW_REDEMPTION_UNKNOWN_CAUSES = Object.freeze(['redeem_unsettled', 'receipt_unreadable']);
113
+ function singleRuleLayerContent(canonical) {
114
+ return JSON.stringify({ permissions: { allow: [canonical] } });
115
+ }
116
+ function receipt(r) {
117
+ return Object.freeze({ ...r });
118
+ }
119
+ function detailOf(text) {
120
+ if (typeof text !== 'string')
121
+ return {};
122
+ const shown = displayUntrusted(text, { max: DETAIL_SHOWN_MAX });
123
+ return shown.trim() !== '' ? { detail: shown } : {};
124
+ }
125
+ const JUDGED_RECEIPT = {
126
+ managed_only: { kind: 'refused', reason: 'managed_only' },
127
+ no_scope_root: { kind: 'refused', reason: 'no_scope_root' },
128
+ road_closed: { kind: 'unavailable', cause: 'road_closed' },
129
+ road_unknown: { kind: 'unavailable', cause: 'road_unknown' },
130
+ policy_unreadable: { kind: 'unavailable', cause: 'policy_unreadable' },
131
+ input_unreadable: { kind: 'unavailable', cause: 'input_unreadable' },
132
+ tighten_only: { kind: 'unavailable', cause: 'input_unreadable' },
133
+ no_consent: { kind: 'unavailable', cause: 'input_unreadable' },
134
+ consent_unreadable: { kind: 'unavailable', cause: 'input_unreadable' },
135
+ };
136
+ function previewSingle(prep, scopeRoot) {
137
+ const preview = typeof prep === 'object' && prep !== null ? readOwnDataValue(prep, 'preview') : undefined;
138
+ if (typeof preview !== 'object' || preview === null)
139
+ return { mismatch: true };
140
+ const skipped = readOwnDataValue(preview, 'skipped');
141
+ if (Array.isArray(skipped) && skipped.length > 0) {
142
+ const first = readOwnDataValue(skipped, '0');
143
+ const reason = typeof first === 'object' && first !== null ? readOwnDataValue(first, 'reason') : undefined;
144
+ return { mismatch: true, skipped: typeof reason === 'string' ? classifySkippedReason(reason).text : '' };
145
+ }
146
+ if (!Array.isArray(skipped))
147
+ return { mismatch: true };
148
+ const candidates = readOwnDataValue(preview, 'candidates');
149
+ if (!Array.isArray(candidates) || candidates.length !== 1)
150
+ return { mismatch: true };
151
+ const c = readOwnDataValue(candidates, '0');
152
+ if (typeof c !== 'object' || c === null)
153
+ return { mismatch: true };
154
+ const behavior = readOwnDataValue(c, 'behavior');
155
+ const rule = readOwnDataValue(c, 'rule');
156
+ const scope = readOwnDataValue(c, 'scope');
157
+ const kind = typeof scope === 'object' && scope !== null ? readOwnDataValue(scope, 'kind') : undefined;
158
+ const root = typeof scope === 'object' && scope !== null ? readOwnDataValue(scope, 'root') : undefined;
159
+ if (behavior !== ALLOW_BEHAVIOR || typeof rule !== 'string' || kind !== 'project' || root !== scopeRoot)
160
+ return { mismatch: true };
161
+ return { rule };
162
+ }
163
+ export async function redeemReadOnlyRuleOffer(facade, input, opts) {
164
+ let canonical;
165
+ let offered;
166
+ let scopeRoot;
167
+ let settingsPath;
168
+ try {
169
+ if (typeof input !== 'object' || input === null)
170
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
171
+ const judged = localAllowRedeemability({
172
+ subject: 'offered',
173
+ managedRulesOnly: readOwnDataValue(input, 'managedRulesOnly'),
174
+ scopeRoot: readOwnDataValue(input, 'scopeRoot'),
175
+ road: readOwnDataValue(input, 'road'),
176
+ });
177
+ if (judged.kind !== 'redeemable')
178
+ return receipt(JUDGED_RECEIPT[judged.kind === 'not_redeemable' ? judged.reason : judged.cause]);
179
+ scopeRoot = readOwnDataValue(input, 'scopeRoot');
180
+ const path = readOwnDataValue(input, 'settingsPath');
181
+ if (typeof path !== 'string' || path === '')
182
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
183
+ settingsPath = path;
184
+ const rule = readOwnDataValue(input, 'rule');
185
+ const expected = readOwnDataValue(input, 'expectedToolName');
186
+ const deps = readOwnDataValue(input, 'deps');
187
+ if (typeof rule !== 'string' || deps === undefined || (expected !== undefined && typeof expected !== 'string'))
188
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
189
+ const parsed = parseLocalAllowRule(rule, expected, deps);
190
+ if (!parsed.ok)
191
+ return receipt({ kind: 'refused', reason: 'rule_shape', ...detailOf(parsed.error) });
192
+ canonical = parsed.canonical;
193
+ offered = rule;
194
+ }
195
+ catch {
196
+ return receipt({ kind: 'unavailable', cause: 'input_unreadable' });
197
+ }
198
+ const signal = opts?.signal;
199
+ let prep;
200
+ try {
201
+ prep = await facade.ccImportPrepare([{ layer: 'localSettings', path: settingsPath, root: scopeRoot, content: singleRuleLayerContent(canonical) }], signal !== undefined ? { signal } : undefined);
202
+ }
203
+ catch (e) {
204
+ const f = classifyRulesFailure(e);
205
+ if (f.kind === 'lane-unavailable' || f.kind === 'route-missing')
206
+ return receipt({ kind: 'unavailable', cause: 'road_closed' });
207
+ if (f.kind === 'body-shape' || f.kind === 'field-refused' || f.kind === 'too-many-candidates' || f.kind === 'forbidden' || f.kind === 'operators-unconfigured') {
208
+ return receipt({ kind: 'refused', reason: 'request_refused' });
209
+ }
210
+ return receipt({ kind: 'unavailable', cause: 'prepare_failed' });
211
+ }
212
+ let ticket;
213
+ let storedRule;
214
+ try {
215
+ const single = previewSingle(prep, scopeRoot);
216
+ if ('mismatch' in single) {
217
+ return receipt(single.skipped !== undefined ? { kind: 'refused', reason: 'engine_refused', ...detailOf(single.skipped) } : { kind: 'refused', reason: 'preview_mismatch' });
218
+ }
219
+ if (single.rule !== canonical && single.rule !== offered)
220
+ return receipt({ kind: 'refused', reason: 'preview_mismatch' });
221
+ storedRule = single.rule;
222
+ const t = readOwnDataValue(prep, 'ticket');
223
+ if (typeof t !== 'string' || t === '')
224
+ return receipt({ kind: 'refused', reason: 'preview_mismatch' });
225
+ ticket = t;
226
+ }
227
+ catch {
228
+ return receipt({ kind: 'refused', reason: 'preview_mismatch' });
229
+ }
230
+ let body;
231
+ try {
232
+ body = await facade.ccImportRedeem(ticket, signal !== undefined ? { signal } : undefined);
233
+ }
234
+ catch (e) {
235
+ const f = classifyRulesFailure(e);
236
+ if (f.kind === 'ticket-dead')
237
+ return receipt({ kind: 'refused', reason: 'ticket_dead' });
238
+ if (f.kind === 'lane-unavailable')
239
+ return receipt({ kind: 'unavailable', cause: 'road_closed' });
240
+ return receipt({ kind: 'unknown', cause: 'redeem_unsettled' });
241
+ }
242
+ const counts = readCcImportRedeemCounts(body);
243
+ if (counts === null)
244
+ return receipt({ kind: 'unknown', cause: 'receipt_unreadable' });
245
+ const refusedRow = counts.refused?.[0];
246
+ if (refusedRow !== undefined)
247
+ return receipt({ kind: 'refused', reason: 'engine_refused', ...detailOf(refusedRow.detail) });
248
+ if (counts.indeterminate.length > 0)
249
+ return receipt({ kind: 'unknown', cause: 'redeem_unsettled' });
250
+ const scope = PROJECT_SCOPE_PREFIX + scopeRoot;
251
+ if (counts.persisted === 1 && counts.deduped === 0)
252
+ return receipt({ kind: 'applied', outcome: 'persisted', rule: storedRule, scope });
253
+ if (counts.deduped === 1 && counts.persisted === 0)
254
+ return receipt({ kind: 'applied', outcome: 'already_live', rule: storedRule, scope });
255
+ return receipt({ kind: 'unknown', cause: 'receipt_unreadable' });
256
+ }
@@ -1,15 +1,16 @@
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';
@@ -65,6 +66,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
65
66
  if (!hasQuestionOverlay()) {
66
67
  return { kind: 'not_sent', lane: 'ask', cause: 'card_unavailable', detail: NO_QUESTION_OVERLAY_REASON };
67
68
  }
69
+ const sourceGeneration = sourceGenerationNow(deps);
68
70
  const found = await findPendingForTask(deps.client, taskId, isAskTool, signal ? { signal } : undefined, parkGatedCallId, (row) => isAskTool(typeof row.toolName === 'string' ? row.toolName : undefined));
69
71
  if (!found.ok)
70
72
  return { kind: 'not_sent', lane: 'ask', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}), ...(found.rowVerdict !== undefined ? { rowVerdict: found.rowVerdict } : {}) };
@@ -86,11 +88,17 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
86
88
  };
87
89
  }
88
90
  const questionId = askGateQuestionId(approvalCallKey(gatedCallId, taskId));
91
+ let withdrawn;
89
92
  const answer = await new Promise(resolve => {
90
93
  const unregister = registerLocalQuestionResponder(questionId, async (_id, a) => {
91
94
  cleanup();
92
95
  resolve(a);
93
96
  return { ok: true };
97
+ }, (w) => {
98
+ withdrawn = w;
99
+ cleanup();
100
+ publishQuestionFrame({ type: 'question_complete', questionId });
101
+ resolve(null);
94
102
  });
95
103
  const onAbort = () => {
96
104
  cleanup();
@@ -107,11 +115,15 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
107
115
  }
108
116
  signal?.addEventListener('abort', onAbort, { once: true });
109
117
  onPresented?.();
110
- publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}) });
118
+ publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}), ...parkQuestionFrameBits(taskId, gatedCallId, sourceGeneration) });
111
119
  });
120
+ if (withdrawn !== undefined) {
121
+ hostLog('debug', `liveHitlAskWire: question-withdrawn-by-host card=${questionId} verdict=${withdrawn.rowVerdict.kind}`);
122
+ return { kind: 'not_sent', ...at, cause: 'withdrawn', toldBy: withdrawn.toldBy, rowVerdict: withdrawn.rowVerdict };
123
+ }
112
124
  const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
113
125
  if (answer === null) {
114
- observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending), taskId);
126
+ observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, { signal: interruptDenyLifetime() }, pending), taskId);
115
127
  return { kind: 'unsettled', ...at, cause: 'interrupted' };
116
128
  }
117
129
  let declinedReceipt;
@@ -155,7 +167,7 @@ function isFetchStepNoPending(outcome) {
155
167
  return outcome.kind === 'not_sent' && outcome.cause === 'no_pending' && outcome.gatedCallId === undefined;
156
168
  }
157
169
  function isHumanDisposition(r) {
158
- return r.kind === 'decided' || (r.kind === 'unsettled' && r.cause === 'interrupted') || (r.kind === 'not_sent' && r.cause === 'retracted');
170
+ return r.kind === 'decided' || (r.kind === 'unsettled' && r.cause === 'interrupted') || (r.kind === 'not_sent' && (r.cause === 'retracted' || r.cause === 'withdrawn'));
159
171
  }
160
172
  function failureReasonOf(r) {
161
173
  if (r.kind === 'decided')
@@ -184,6 +196,7 @@ async function surfaceParkGate(park, ctx, gatedCallId, onPresented) {
184
196
  ...(onPresented ? { onPresented } : {}),
185
197
  ...(deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : {}),
186
198
  ...(decidedByReading !== undefined ? { approvalDecidedBy: decidedByReading } : {}),
199
+ ...((read) => (typeof read === 'function' ? { engineGeneration: () => read.call(deps) } : {}))(guardedRead(() => deps.engineGeneration)),
187
200
  }, taskId, argsByCallOf(led), ctx.signal, gatedCallId, park.toolName);
188
201
  if (outcome.kind === 'decided' && outcome.decision === 'deny' && outcome.gatedCallId) {
189
202
  led.markDenied(outcome.gatedCallId, denyAttributionOf(outcome.deny));
@@ -282,7 +295,8 @@ export async function resolvePark(park, ctx) {
282
295
  if (outcome.kind !== 'decided') {
283
296
  hostLog('debug', `liveHitlAskWire: gate not decided (${outcome.kind}/${outcome.cause}: ${failureReasonOf(outcome)}) — fail-soft to suspended terminal`);
284
297
  const interrupted = outcome.kind === 'unsettled' && outcome.cause === 'interrupted';
285
- const failedReason = interrupted
298
+ const toldByHost = outcome.kind === 'not_sent' && outcome.cause === 'withdrawn';
299
+ const failedReason = interrupted || toldByHost
286
300
  ? undefined
287
301
  : outcome.kind === 'not_sent' && outcome.cause === 'retracted'
288
302
  ? 'the approval card was retracted by the host without a decision (nothing was sent; the run stays parked and remains decidable elsewhere)'
@@ -312,13 +326,19 @@ export async function resolvePark(park, ctx) {
312
326
  };
313
327
  }
314
328
  function failsoftWayOutOf(outcome) {
329
+ if (outcome === undefined)
330
+ return 'card';
331
+ if (typeof outcome !== 'object' || outcome === null)
332
+ return 'speak';
315
333
  if (outcome.kind === 'not_sent' && (outcome.cause === 'row_unresumable' || (outcome.cause === 'safety_stop' && outcome.safetyCode === 'binding_missing')))
316
334
  return 'reason';
317
- if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && refusedParkFromError(outcome.refusal) !== null)
335
+ if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && (refusedParkFromError(outcome.refusal) !== null || outcome.refusal?.conflict === 'row_integrity'))
318
336
  return 'cancel';
337
+ if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused')
338
+ return 'speak';
319
339
  return 'card';
320
340
  }
321
- function failsoftReasonSentence(park, failedReason, terminal, wayOut = 'card') {
341
+ function failsoftReasonSentence(park, failedReason, terminal, wayOut) {
322
342
  if (terminal !== undefined)
323
343
  return terminal.errorMessage;
324
344
  if (failedReason === undefined)
@@ -328,10 +348,9 @@ function failsoftReasonSentence(park, failedReason, terminal, wayOut = 'card') {
328
348
  return renderMessage(msg('approval.park.stillParked', { gate, reason: failedReason }));
329
349
  if (wayOut === 'cancel')
330
350
  return renderMessage(msg('approval.park.refusedNotResumable', { gate, reason: failedReason }));
331
- return (`${park.gate === 'fs' ? 'Tool approval' : 'AskUserQuestion'} gate could not be answered: ${failedReason}` +
332
- ' — the run is still parked on this approval: decide it on the card when it is shown again, or cancel the run.');
351
+ return renderMessage(msg(wayOut === 'speak' ? 'approval.park.resume.speak' : 'approval.park.resume.card', { gate, reason: failedReason }));
333
352
  }
334
- function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface = false, wayOut = 'card') {
353
+ function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface, wayOut) {
335
354
  const events = [
336
355
  ...flushHeldWithInterruptRewrite(ctx.led, {
337
356
  terminal: park.pendingDone,
@@ -360,12 +379,12 @@ export function stalledTerminal(park, ctx, stalledRounds, lastStallReason, decid
360
379
  `stopped re-attaching. Your last decision was accepted; sema did not observe the run move on after it — check the ` +
361
380
  `transcript, decide the call again if the card comes back, or cancel the run.`;
362
381
  hostLog('debug', `liveHitlAskWire: ${errorMessage}`);
363
- return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, undefined, { errorCode: 'hitl_stalled', errorMessage }) };
382
+ return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, undefined, { errorCode: 'hitl_stalled', errorMessage }, false, failsoftWayOutOf(undefined)) };
364
383
  }
365
384
  const reason = `the run stayed parked across ${stalledRounds} consecutive re-attach rounds without any host progress ` +
366
385
  `(last: ${lastStallReason ?? 'the engine kept parking'})`;
367
386
  hostLog('debug', `liveHitlAskWire: ${reason} — fail-soft after the round committed`);
368
- return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, reason) };
387
+ return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, reason, undefined, false, failsoftWayOutOf(undefined)) };
369
388
  }
370
389
  function notifyParkReattach(ctx, attempt, max, reason) {
371
390
  invokeHostCallbackIsolated('liveHitlAskWire: onParkReattach', () => {
@@ -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
  } | {
@@ -7,7 +7,7 @@ import { hostLog } from '../host.js';
7
7
  import { msg, renderMessage } from '../i18n/translate.js';
8
8
  import { appendOwnElement, guardedRead, readOwnDataValue } from '../ownKey.js';
9
9
  import { observedPermissionRulesWrite, permissionRulesWriteAvailable } from '../permissionRulesWriteCapability.js';
10
- import { isToolApprovalRespondReplayed, readToolApprovalRespondAck } from './toolApprovalWire.js';
10
+ import { isToolApprovalRespondParkedRedeemed, isToolApprovalRespondReplayed, readToolApprovalRespondAck } from './toolApprovalWire.js';
11
11
  export function persistedRulesLaneAvailable(baseUrl) {
12
12
  return engineCapTrue(baseUrl, 'permissionRules');
13
13
  }
@@ -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,9 +664,50 @@ 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
- if (parsed === undefined || isToolApprovalRespondReplayed(parsed))
710
+ if (parsed === undefined || isToolApprovalRespondReplayed(parsed) || isToolApprovalRespondParkedRedeemed(parsed))
641
711
  return { state: 'unknown' };
642
712
  if (parsed.rulePersisted === true)
643
713
  return { state: 'persisted' };
@@ -41,6 +41,8 @@ export interface PlanReviewCardView {
41
41
  readonly note?: string;
42
42
  }
43
43
  export declare function planReviewCardViewOf(frame: object | null | undefined, lang?: UiLanguage): PlanReviewCardView | undefined;
44
+ export type PlanReviewEvidenceMove = 'same' | 'moved' | 'unknown';
45
+ export declare function planReviewEvidenceMovedOf(shown: object | null | undefined, latest: object | null | undefined): PlanReviewEvidenceMove;
44
46
  export declare function notePlanReviewAnsweredIfDecisive(questionId: unknown, answer: QuestionAnswer | null | undefined): void;
45
47
  export { isPlanReviewPark };
46
48
  export declare function _resetArmedPlanReviewsForTest(): void;
@@ -79,6 +81,7 @@ export interface PlanReviewOutcomeMeta {
79
81
  decision: 'approve' | 'reject' | 'ask_again';
80
82
  effect: PlanReviewDecisionEffect;
81
83
  cause?: 'stored_form_unsupported';
84
+ questionId?: string;
82
85
  }
83
86
  export declare function planReviewOutcomeTail(taskId: string, decision: 'approve' | 'reject', effect: PlanReviewDecisionEffect, cause?: 'stored_form_unsupported'): string;
84
87
  export type PlanReviewWireClient = {