@sema-agent/client-core 0.110.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 (176) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/README.md +50 -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/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/displayUntrusted.d.ts +1 -0
  26. package/dist/displayUntrusted.js +6 -0
  27. package/dist/engineHealthFacts.d.ts +11 -0
  28. package/dist/engineHealthFacts.js +21 -0
  29. package/dist/engineHealthProbeVerdict.d.ts +23 -0
  30. package/dist/engineHealthProbeVerdict.js +115 -0
  31. package/dist/engineNoticeCodes.js +5 -7
  32. package/dist/engineRepoint.d.ts +12 -5
  33. package/dist/engineRepoint.js +44 -19
  34. package/dist/engineRowRef.d.ts +3 -0
  35. package/dist/engineRowRef.js +11 -1
  36. package/dist/engineSwapCrossing.d.ts +57 -0
  37. package/dist/engineSwapCrossing.js +297 -0
  38. package/dist/engineUnavailable.d.ts +44 -0
  39. package/dist/engineUnavailable.js +492 -0
  40. package/dist/eventLeg.js +3 -3
  41. package/dist/generated/sdkFactTables.d.ts +1 -1
  42. package/dist/generated/sdkFactTables.js +1 -0
  43. package/dist/generated/settingsSchemaTables.d.ts +4 -0
  44. package/dist/generated/settingsSchemaTables.js +1 -0
  45. package/dist/hitl/approvalDelivery.d.ts +1 -0
  46. package/dist/hitl/approvalDelivery.js +1 -1
  47. package/dist/hitl/approvalResolution.d.ts +1 -1
  48. package/dist/hitl/approvalResolution.js +2 -1
  49. package/dist/hitl/askGateWire.js +5 -6
  50. package/dist/hitl/decisionRefusal.d.ts +2 -0
  51. package/dist/hitl/decisionRefusal.js +7 -8
  52. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  53. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  54. package/dist/hitl/decisionSend.d.ts +7 -0
  55. package/dist/hitl/decisionSend.js +34 -12
  56. package/dist/hitl/editedApprovalAcceptance.js +1 -1
  57. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  58. package/dist/hitl/elicitationRefusal.js +23 -0
  59. package/dist/hitl/frameRouter.d.ts +2 -2
  60. package/dist/hitl/frameRouter.js +8 -15
  61. package/dist/hitl/gateIdentity.d.ts +5 -0
  62. package/dist/hitl/gateIdentity.js +23 -0
  63. package/dist/hitl/hitlBridge.d.ts +6 -0
  64. package/dist/hitl/hitlBridge.js +10 -4
  65. package/dist/hitl/hitlHostSurface.d.ts +2 -1
  66. package/dist/hitl/hitlHostSurface.js +7 -2
  67. package/dist/hitl/localAllowRedemption.d.ts +67 -0
  68. package/dist/hitl/localAllowRedemption.js +256 -0
  69. package/dist/hitl/parkResolver.js +7 -4
  70. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  71. package/dist/hitl/persistedRulesWire.js +70 -0
  72. package/dist/hitl/planReviewWire.d.ts +3 -0
  73. package/dist/hitl/planReviewWire.js +23 -6
  74. package/dist/hitl/policyDenyCause.d.ts +1 -1
  75. package/dist/hitl/policyDenyCause.js +1 -1
  76. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  77. package/dist/hitl/resumeRunningCard.js +1 -0
  78. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  79. package/dist/hitl/screenCardCarriers.js +41 -0
  80. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  81. package/dist/hitl/screenDecisionCards.js +362 -0
  82. package/dist/hitl/standingRows.d.ts +3 -1
  83. package/dist/hitl/standingRows.js +18 -0
  84. package/dist/hitl/toolApprovalWire.d.ts +30 -8
  85. package/dist/hitl/toolApprovalWire.js +264 -121
  86. package/dist/hooksWireCaps.js +3 -3
  87. package/dist/hostSettingNoticeArm.d.ts +3 -0
  88. package/dist/hostSettingNoticeArm.js +12 -0
  89. package/dist/hostTurnSettings.d.ts +3 -0
  90. package/dist/hostTurnSettings.js +38 -5
  91. package/dist/humanTurnRead.js +11 -1
  92. package/dist/i18n/messages.en.d.ts +129 -0
  93. package/dist/i18n/messages.en.js +135 -7
  94. package/dist/i18n/messages.zh.js +142 -6
  95. package/dist/i18n/translate.d.ts +3 -0
  96. package/dist/i18n/translate.js +78 -10
  97. package/dist/i18n/zh.d.ts +1 -0
  98. package/dist/i18n/zh.js +1 -0
  99. package/dist/index.d.ts +43 -17
  100. package/dist/index.js +30 -12
  101. package/dist/interactiveHalt.js +2 -1
  102. package/dist/interactiveInterrupt.js +20 -2
  103. package/dist/liveModelCatalog.js +2 -2
  104. package/dist/localEngineSeatReading.d.ts +2 -2
  105. package/dist/localEngineSeatReading.js +5 -3
  106. package/dist/localEngineSpawn.d.ts +0 -7
  107. package/dist/localEngineSpawn.js +0 -92
  108. package/dist/localEngineStep.d.ts +2 -2
  109. package/dist/localEngineStep.js +2 -0
  110. package/dist/mcpReconnect.js +3 -3
  111. package/dist/mcpWireCaps.js +1 -1
  112. package/dist/model/engineModelWords.d.ts +3 -0
  113. package/dist/model/engineModelWords.js +13 -0
  114. package/dist/model/subagentModelWord.d.ts +21 -2
  115. package/dist/model/subagentModelWord.js +100 -29
  116. package/dist/modelCapabilityProbe.js +3 -40
  117. package/dist/notifications.d.ts +19 -0
  118. package/dist/notifications.js +114 -12
  119. package/dist/ownKey.d.ts +6 -0
  120. package/dist/ownKey.js +31 -0
  121. package/dist/pendingUserRow.d.ts +4 -0
  122. package/dist/pendingUserRow.js +16 -0
  123. package/dist/permissionsDeclaration.d.ts +1 -0
  124. package/dist/permissionsDeclaration.js +11 -1
  125. package/dist/planReviewCardFacts.d.ts +3 -0
  126. package/dist/planReviewCardFacts.js +52 -2
  127. package/dist/pluginHooksWire.d.ts +9 -1
  128. package/dist/pluginHooksWire.js +40 -6
  129. package/dist/printToolResultFrame.js +4 -2
  130. package/dist/promptBlockedCopy.js +11 -6
  131. package/dist/promptSuggestionBatch.d.ts +7 -0
  132. package/dist/promptSuggestionBatch.js +9 -0
  133. package/dist/promptSuggestionsTail.d.ts +39 -0
  134. package/dist/promptSuggestionsTail.js +116 -0
  135. package/dist/request/taskRequest.js +10 -6
  136. package/dist/requestCoordinate.d.ts +1 -0
  137. package/dist/requestCoordinate.js +13 -0
  138. package/dist/rewindArchiveReading.js +4 -4
  139. package/dist/runCancelContext.d.ts +2 -1
  140. package/dist/runCancelContext.js +1 -0
  141. package/dist/runPermissionModeSwitch.js +2 -2
  142. package/dist/scenarioRefusal.js +11 -13
  143. package/dist/scenarioSkills.js +11 -6
  144. package/dist/seam.d.ts +16 -2
  145. package/dist/seam.js +10 -0
  146. package/dist/seatContract.d.ts +109 -9
  147. package/dist/seatContract.js +218 -15
  148. package/dist/sessionCostReading.d.ts +70 -0
  149. package/dist/sessionCostReading.js +150 -0
  150. package/dist/sessionHooksRoster.d.ts +4 -0
  151. package/dist/sessionHooksRoster.js +158 -0
  152. package/dist/sessionMemoryStatus.js +2 -2
  153. package/dist/sessionReadRedaction.d.ts +7 -0
  154. package/dist/sessionReadRedaction.js +26 -0
  155. package/dist/sessionSyncFileHistory.js +8 -9
  156. package/dist/settingsHooksSalvage.js +2 -1
  157. package/dist/sseIdleTriage.js +2 -1
  158. package/dist/storeDurabilityWord.d.ts +15 -0
  159. package/dist/storeDurabilityWord.js +18 -0
  160. package/dist/storePostureCapability.d.ts +7 -3
  161. package/dist/storePostureCapability.js +69 -39
  162. package/dist/subagent/engineSubagentResume.js +2 -2
  163. package/dist/subagent/engineSubagentTail.js +2 -1
  164. package/dist/subagentContentStore.d.ts +11 -0
  165. package/dist/subagentContentStore.js +28 -2
  166. package/dist/toolResult.d.ts +1 -0
  167. package/dist/toolResult.js +2 -0
  168. package/dist/userAttachmentEnvelope.d.ts +2 -1
  169. package/dist/userAttachmentEnvelope.js +10 -0
  170. package/dist/wireErrorTriage.js +2 -1
  171. package/dist/wireFailureShape.d.ts +4 -0
  172. package/dist/wireFailureShape.js +141 -19
  173. package/dist/wireRefusalCopy.d.ts +6 -5
  174. package/dist/wireRefusalCopy.js +49 -92
  175. package/docs/INTEGRATION-CLIENTS.md +2118 -17
  176. package/package.json +6 -2
@@ -0,0 +1,362 @@
1
+ import { SCREEN_CARD_ORIGINS } from './gateIdentity.js';
2
+ import { convergeEndedEngineGeneration, } from '../engineGenerationConvergence.js';
3
+ import { checkpointRowVerdictOf } from '../checkpointRowVerdict.js';
4
+ import { planReviewCardFactsFrom } from '../planReviewCardFacts.js';
5
+ import { slotReadOf } from '../wireFailureShape.js';
6
+ import { PARK_GATE_KEY } from './screenCardCarriers.js';
7
+ import { pendingRowAnswersCall } from './hitlBridge.js';
8
+ import { cardIsOnParkRow } from './toolApprovalWire.js';
9
+ export const SCREEN_CARD_KEEP_REASONS = Object.freeze(['not-ready', 'generation-moved', 'not-withdrawable', 'no-gate', 'not-early', 'no-row', 'not-owned', 'verdict-ok', 'verdict-absent']);
10
+ const nonEmpty = (v) => typeof v === 'string' && v.length > 0;
11
+ const finite = (v) => typeof v === 'number' && Number.isFinite(v);
12
+ const ORIGIN_SET = new Set(SCREEN_CARD_ORIGINS);
13
+ const PACKAGE_WITHDRAWABLE = new Set(['park-approval', 'park-question', 'plan-review', 'host-reopen-question']);
14
+ const NOT_ENGINE_DECISION = new Set(['host-choice', 'local-permission']);
15
+ function own(o, key) {
16
+ const r = slotReadOf(o, key);
17
+ return r.presence === 'present' ? r.value : undefined;
18
+ }
19
+ function originOf(v) {
20
+ return typeof v === 'string' && ORIGIN_SET.has(v) ? v : undefined;
21
+ }
22
+ function toolGateOf(v) {
23
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
24
+ return undefined;
25
+ const taskId = own(v, 'taskId');
26
+ const gatedCallId = own(v, 'gatedCallId');
27
+ return nonEmpty(taskId) && nonEmpty(gatedCallId) ? Object.freeze({ kind: 'tool', taskId, gatedCallId }) : undefined;
28
+ }
29
+ export function questionFrameGateOf(frame) {
30
+ try {
31
+ if (typeof frame !== 'object' || frame === null)
32
+ return undefined;
33
+ const park = slotReadOf(frame, PARK_GATE_KEY);
34
+ const plan = slotReadOf(frame, '_sema_planReview');
35
+ if (park.presence === 'unreadable' || plan.presence === 'unreadable')
36
+ return undefined;
37
+ const hasPark = park.presence === 'present' && park.value !== undefined;
38
+ const hasPlan = plan.presence === 'present' && plan.value !== undefined;
39
+ if (hasPark && hasPlan)
40
+ return undefined;
41
+ if (hasPark)
42
+ return toolGateOf(park.value);
43
+ if (!hasPlan)
44
+ return undefined;
45
+ const facts = planReviewCardFactsFrom(plan.value);
46
+ if (facts === undefined)
47
+ return undefined;
48
+ return Object.freeze({ kind: 'plan', taskId: facts.taskId, ...(facts.checkpointId !== undefined ? { checkpointId: facts.checkpointId } : {}) });
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ }
54
+ export function approvalCardGateOf(card) {
55
+ try {
56
+ if (typeof card !== 'object' || card === null || !cardIsOnParkRow(card))
57
+ return undefined;
58
+ const taskId = own(card, 'taskId');
59
+ const park = own(card, 'parkRow');
60
+ if (!nonEmpty(taskId) || typeof park !== 'object' || park === null)
61
+ return undefined;
62
+ const gatedCallId = own(park, 'gatedCallId');
63
+ return nonEmpty(gatedCallId) ? Object.freeze({ kind: 'tool', taskId, gatedCallId }) : undefined;
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ }
69
+ export function approvalCardOriginOf(card) {
70
+ try {
71
+ return cardIsOnParkRow(card) ? 'park-approval' : 'live-approval';
72
+ }
73
+ catch {
74
+ return 'live-approval';
75
+ }
76
+ }
77
+ export function screenCardsHoldEngineDecision(cards) {
78
+ return engineDecisionReadingOf(cards) !== 'none';
79
+ }
80
+ function engineDecisionReadingOf(cards) {
81
+ try {
82
+ if (!Array.isArray(cards))
83
+ return 'none';
84
+ const len = own(cards, 'length');
85
+ if (typeof len !== 'number' || !Number.isSafeInteger(len) || len < 0)
86
+ return 'unreadable';
87
+ for (let i = 0; i < len; i++) {
88
+ const card = own(cards, String(i));
89
+ const origin = typeof card === 'object' && card !== null ? originOf(own(card, 'origin')) : undefined;
90
+ if (origin === undefined || !NOT_ENGINE_DECISION.has(origin))
91
+ return 'holds';
92
+ }
93
+ return 'none';
94
+ }
95
+ catch {
96
+ return 'unreadable';
97
+ }
98
+ }
99
+ function cardReadingOf(card) {
100
+ try {
101
+ if (typeof card !== 'object' || card === null)
102
+ return undefined;
103
+ const instance = own(card, 'instance');
104
+ if (!nonEmpty(instance))
105
+ return undefined;
106
+ return {
107
+ instance,
108
+ origin: originOf(own(card, 'origin')),
109
+ gate: own(card, 'gate'),
110
+ sourceGeneration: own(card, 'sourceGeneration'),
111
+ toolCallId: own(card, 'toolCallId'),
112
+ window: own(card, 'window'),
113
+ };
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ }
119
+ function distinctCards(cards) {
120
+ if (!Array.isArray(cards))
121
+ return [];
122
+ const out = [];
123
+ const seen = new Set();
124
+ for (const card of cards) {
125
+ const r = cardReadingOf(card);
126
+ if (r === undefined || seen.has(r.instance))
127
+ continue;
128
+ seen.add(r.instance);
129
+ out.push(r);
130
+ }
131
+ return out;
132
+ }
133
+ function usableGateOf(v) {
134
+ if (typeof v !== 'object' || v === null)
135
+ return undefined;
136
+ const kind = own(v, 'kind');
137
+ const taskId = own(v, 'taskId');
138
+ if (!nonEmpty(taskId))
139
+ return undefined;
140
+ if (kind === 'tool') {
141
+ const gatedCallId = own(v, 'gatedCallId');
142
+ return nonEmpty(gatedCallId) ? { kind: 'tool', taskId, gatedCallId } : undefined;
143
+ }
144
+ if (kind === 'plan') {
145
+ const checkpointId = own(v, 'checkpointId');
146
+ return nonEmpty(checkpointId) ? { kind: 'plan', taskId, checkpointId } : undefined;
147
+ }
148
+ return undefined;
149
+ }
150
+ function withdrawableSetOf(v) {
151
+ const out = new Set();
152
+ if (!Array.isArray(v))
153
+ return out;
154
+ for (const o of v) {
155
+ const origin = originOf(o);
156
+ if (origin !== undefined && PACKAGE_WITHDRAWABLE.has(origin))
157
+ out.add(origin);
158
+ }
159
+ return out;
160
+ }
161
+ function candidacyOf(card, generation, cap) {
162
+ if (card.origin === undefined || !cap.has(card.origin))
163
+ return { ok: false, why: 'not-withdrawable' };
164
+ const gate = usableGateOf(card.gate);
165
+ if (gate === undefined)
166
+ return { ok: false, why: 'no-gate' };
167
+ if (finite(card.sourceGeneration) && card.sourceGeneration >= generation)
168
+ return { ok: false, why: 'not-early' };
169
+ return { ok: true, origin: card.origin, gate };
170
+ }
171
+ function rowAnswersGate(row, gate) {
172
+ const r = row;
173
+ if (gate.kind === 'tool')
174
+ return pendingRowAnswersCall(r, gate.taskId, gate.gatedCallId);
175
+ return r.taskId === gate.taskId && r.checkpointId === gate.checkpointId;
176
+ }
177
+ function ownedSafely(isOwned, row) {
178
+ try {
179
+ return isOwned(row) === true;
180
+ }
181
+ catch {
182
+ return false;
183
+ }
184
+ }
185
+ const EMPTY_ENDED = Object.freeze({ phase: 'ended', retract: Object.freeze([]), phases: Object.freeze([]) });
186
+ const EMPTY_READY = Object.freeze({ phase: 'ready', withdraw: Object.freeze([]), keep: Object.freeze([]) });
187
+ function endedPlanOf(facts) {
188
+ const cards = distinctCards(own(facts, 'cards')).map((c) => {
189
+ const w = c.window;
190
+ const leftMs = typeof w === 'object' && w !== null ? own(w, 'leftMs') : undefined;
191
+ const closedByEngine = typeof w === 'object' && w !== null ? own(w, 'closedByEngine') : undefined;
192
+ return {
193
+ callKey: c.instance,
194
+ parkRow: c.origin !== 'live-approval',
195
+ ...(nonEmpty(c.toolCallId) ? { toolCallId: c.toolCallId } : {}),
196
+ ...(finite(leftMs) ? { windowLeftMs: leftMs } : {}),
197
+ ...(closedByEngine === true ? { closedByEngine: true } : {}),
198
+ };
199
+ });
200
+ const opened = own(facts, 'opened');
201
+ const phases = own(facts, 'phases');
202
+ const endedGeneration = own(facts, 'endedGeneration');
203
+ const plan = convergeEndedEngineGeneration({
204
+ cards,
205
+ opened: Array.isArray(opened) ? opened : [],
206
+ ...(Array.isArray(phases) ? { phases: phases } : {}),
207
+ endedGeneration: finite(endedGeneration) ? endedGeneration : Number.NaN,
208
+ });
209
+ return Object.freeze({ phase: 'ended', retract: plan.retract, phases: plan.phases });
210
+ }
211
+ function readyPlanOf(facts) {
212
+ const cards = distinctCards(own(facts, 'cards'));
213
+ const generation = own(facts, 'generation');
214
+ const keep = [];
215
+ const withdraw = [];
216
+ const keepAll = (why) => Object.freeze({ phase: 'ready', withdraw: Object.freeze([]), keep: Object.freeze(cards.map((c) => Object.freeze({ instance: c.instance, why }))) });
217
+ if (own(facts, 'ready') !== true || !finite(generation))
218
+ return keepAll('not-ready');
219
+ if (own(facts, 'readGeneration') !== generation)
220
+ return keepAll('generation-moved');
221
+ const cap = withdrawableSetOf(own(facts, 'withdrawable'));
222
+ const rowsRaw = own(facts, 'rows');
223
+ const rows = Array.isArray(rowsRaw) ? rowsRaw.filter((r) => typeof r === 'object' && r !== null) : [];
224
+ const isOwnedRaw = own(facts, 'isOwned');
225
+ const isOwned = typeof isOwnedRaw === 'function' ? isOwnedRaw : () => false;
226
+ for (const card of cards) {
227
+ const c = candidacyOf(card, generation, cap);
228
+ if (!c.ok) {
229
+ keep.push(Object.freeze({ instance: card.instance, why: c.why }));
230
+ continue;
231
+ }
232
+ const matched = rows.filter((r) => rowAnswersGate(r, c.gate));
233
+ if (matched.length === 0) {
234
+ keep.push(Object.freeze({ instance: card.instance, why: 'no-row' }));
235
+ continue;
236
+ }
237
+ const row = matched.find((r) => ownedSafely(isOwned, r));
238
+ if (row === undefined) {
239
+ keep.push(Object.freeze({ instance: card.instance, why: 'not-owned' }));
240
+ continue;
241
+ }
242
+ const verdict = checkpointRowVerdictOf(row);
243
+ if (verdict === undefined) {
244
+ keep.push(Object.freeze({ instance: card.instance, why: 'verdict-absent' }));
245
+ continue;
246
+ }
247
+ if (verdict.kind === 'ok') {
248
+ keep.push(Object.freeze({ instance: card.instance, why: 'verdict-ok' }));
249
+ continue;
250
+ }
251
+ const rowVerdict = verdict;
252
+ withdraw.push(Object.freeze({ instance: card.instance, origin: c.origin, row, withdrawal: Object.freeze({ toldBy: 'host', rowVerdict }) }));
253
+ }
254
+ return Object.freeze({ phase: 'ready', withdraw: Object.freeze(withdraw), keep: Object.freeze(keep) });
255
+ }
256
+ export function engineGenerationCardPlan(facts) {
257
+ let phase;
258
+ try {
259
+ if (typeof facts !== 'object' || facts === null)
260
+ return EMPTY_READY;
261
+ phase = own(facts, 'phase');
262
+ if (phase === 'ended')
263
+ return endedPlanOf(facts);
264
+ if (phase === 'ready')
265
+ return readyPlanOf(facts);
266
+ return EMPTY_READY;
267
+ }
268
+ catch {
269
+ return (phase === 'ended' ? EMPTY_ENDED : EMPTY_READY);
270
+ }
271
+ }
272
+ export function engineGenerationCardRecheckDue(facts) {
273
+ try {
274
+ if (typeof facts !== 'object' || facts === null)
275
+ return false;
276
+ const generation = own(facts, 'generation');
277
+ if (own(facts, 'ready') !== true || !finite(generation))
278
+ return false;
279
+ const cap = withdrawableSetOf(own(facts, 'withdrawable'));
280
+ return distinctCards(own(facts, 'cards')).some((c) => candidacyOf(c, generation, cap).ok);
281
+ }
282
+ catch {
283
+ return false;
284
+ }
285
+ }
286
+ function deepFreeze(v) {
287
+ if (typeof v === 'object' && v !== null && !Object.isFrozen(v)) {
288
+ Object.freeze(v);
289
+ for (const k of Object.keys(v))
290
+ deepFreeze(v[k]);
291
+ }
292
+ return v;
293
+ }
294
+ const GOLD_LACKING = { kind: 'lacking', code: 'checkpoint.unsupported_version', reason: 'version_newer', sentence: 'Newer engine.' };
295
+ const GOLD_DAMAGED = { kind: 'damaged', code: 'checkpoint.invalid_outcome', reason: 'requirements_damaged', sentence: 'Row damaged.' };
296
+ function goldenCases() {
297
+ const ALL = ['park-approval', 'park-question', 'plan-review', 'host-reopen-question'];
298
+ const ready = (name, cards, rows, pick, over = {}) => {
299
+ const facts = { phase: 'ready', generation: 2, ready: true, readGeneration: 2, cards, rows, withdrawable: ALL, ...over };
300
+ return { name, facts, plan: { phase: 'ready', ...pick(facts.rows) } };
301
+ };
302
+ const tool = (taskId, gatedCallId) => ({ kind: 'tool', taskId, gatedCallId });
303
+ const W = (instance, origin, row, rowVerdict) => ({ instance, origin, row, withdrawal: { toldBy: 'host', rowVerdict } });
304
+ const K = (instance, why) => ({ instance, why });
305
+ const lacking = GOLD_LACKING;
306
+ const damaged = GOLD_DAMAGED;
307
+ const cases = [
308
+ {
309
+ name: 'ended-live-window-open-only',
310
+ facts: {
311
+ phase: 'ended',
312
+ endedGeneration: 1,
313
+ cards: [
314
+ { instance: 'live-open', origin: 'live-approval', toolCallId: 'call-1', window: { leftMs: 60_000 } },
315
+ { instance: 'live-margin', origin: 'live-approval', toolCallId: 'call-2', window: { leftMs: 5_000 } },
316
+ { instance: 'live-closed', origin: 'live-approval', toolCallId: 'call-3', window: { leftMs: 60_000, closedByEngine: true } },
317
+ { instance: 'live-no-window', origin: 'live-approval', toolCallId: 'call-4' },
318
+ { instance: 'parked', origin: 'park-approval', toolCallId: 'call-5', window: { leftMs: 60_000 } },
319
+ { instance: 'no-origin', toolCallId: 'call-6', window: { leftMs: 60_000 } },
320
+ ],
321
+ opened: [{ toolCallId: 'call-7', generation: 1 }, { toolCallId: 'call-8', generation: 0 }],
322
+ },
323
+ plan: { phase: 'ended', retract: ['live-open'], phases: [{ toolCallId: 'call-1', phase: 'engine-stopped-awaiting-approval', viaCallKey: 'live-open' }, { toolCallId: 'call-7', phase: 'engine-stopped-before-result' }] },
324
+ },
325
+ {
326
+ name: 'ended-live-without-call-id',
327
+ facts: { phase: 'ended', endedGeneration: 3, cards: [{ instance: 'live', origin: 'live-approval', window: { leftMs: 9_000 } }], opened: [] },
328
+ plan: { phase: 'ended', retract: ['live'], phases: [] },
329
+ },
330
+ ready('ready-park-question-withdrawn', [{ instance: 'q', origin: 'park-question', gate: tool('run-1', 'call-1'), sourceGeneration: 1 }], [{ taskId: 'run-1', toolCallId: 'call-1', rowVerdict: GOLD_LACKING, owned: true }], (rows) => ({ withdraw: [W('q', 'park-question', rows[0], lacking)], keep: [] })),
331
+ ready('ready-bound-call-id-matches', [{ instance: 'a', origin: 'park-approval', gate: tool('run-2', 'call-2'), sourceGeneration: 1 }], [{ taskId: 'run-2', toolCallId: 'call-x', boundCallId: 'call-2', rowVerdict: GOLD_DAMAGED, owned: true }], (rows) => ({ withdraw: [W('a', 'park-approval', rows[0], damaged)], keep: [] })),
332
+ ready('ready-plan-by-checkpoint', [
333
+ { instance: 'plan-same', origin: 'plan-review', gate: { kind: 'plan', taskId: 'run-3', checkpointId: 'cp-1' } },
334
+ { instance: 'plan-moved', origin: 'plan-review', gate: { kind: 'plan', taskId: 'run-4', checkpointId: 'cp-old' }, sourceGeneration: 1 },
335
+ { instance: 'plan-no-checkpoint', origin: 'plan-review', gate: { kind: 'plan', taskId: 'run-5' }, sourceGeneration: 1 },
336
+ ], [{ taskId: 'run-3', checkpointId: 'cp-1', rowVerdict: GOLD_LACKING, owned: true }, { taskId: 'run-4', checkpointId: 'cp-new', rowVerdict: GOLD_LACKING, owned: true }, { taskId: 'run-5', checkpointId: 'cp-5', rowVerdict: GOLD_LACKING, owned: true }], (rows) => ({ withdraw: [W('plan-same', 'plan-review', rows[0], lacking)], keep: [K('plan-moved', 'no-row'), K('plan-no-checkpoint', 'no-gate')] })),
337
+ ready('ready-kinds-never-withdrawn', [
338
+ { instance: 'live', origin: 'live-approval', gate: tool('run-1', 'call-1'), sourceGeneration: 1 },
339
+ { instance: 'choice', origin: 'host-choice', gate: tool('run-1', 'call-1'), sourceGeneration: 1 },
340
+ { instance: 'unnamed', gate: tool('run-1', 'call-1'), sourceGeneration: 1 },
341
+ { instance: 'reopened', origin: 'host-reopen-question', gate: tool('run-1', 'call-1'), sourceGeneration: 1 },
342
+ ], [{ taskId: 'run-1', toolCallId: 'call-1', rowVerdict: GOLD_LACKING, owned: true }], (rows) => ({ withdraw: [W('reopened', 'host-reopen-question', rows[0], lacking)], keep: [K('live', 'not-withdrawable'), K('choice', 'not-withdrawable'), K('unnamed', 'not-withdrawable')] })),
343
+ ready('ready-host-withdrawable-only', [{ instance: 'q', origin: 'park-question', gate: tool('run-1', 'call-1'), sourceGeneration: 1 }, { instance: 'r', origin: 'host-reopen-question', gate: tool('run-1', 'call-1'), sourceGeneration: 1 }], [{ taskId: 'run-1', toolCallId: 'call-1', rowVerdict: GOLD_LACKING, owned: true }], (rows) => ({ withdraw: [W('r', 'host-reopen-question', rows[0], lacking)], keep: [K('q', 'not-withdrawable')] }), { withdrawable: ['host-reopen-question'] }),
344
+ ready('ready-current-generation-kept', [{ instance: 'q', origin: 'park-question', gate: tool('run-1', 'call-1'), sourceGeneration: 2 }], [{ taskId: 'run-1', toolCallId: 'call-1', rowVerdict: GOLD_LACKING, owned: true }], () => ({ withdraw: [], keep: [K('q', 'not-early')] })),
345
+ ready('ready-keep-reasons', [
346
+ { instance: 'no-gate', origin: 'park-question', sourceGeneration: 1 },
347
+ { instance: 'no-row', origin: 'park-question', gate: tool('run-9', 'call-9'), sourceGeneration: 1 },
348
+ { instance: 'foreign', origin: 'park-question', gate: tool('run-7', 'call-7'), sourceGeneration: 1 },
349
+ { instance: 'fine', origin: 'park-question', gate: tool('run-8', 'call-8'), sourceGeneration: 1 },
350
+ { instance: 'no-verdict', origin: 'park-question', gate: tool('run-6', 'call-6'), sourceGeneration: 1 },
351
+ ], [
352
+ { taskId: 'run-7', toolCallId: 'call-7', rowVerdict: GOLD_LACKING, owned: false },
353
+ { taskId: 'run-8', toolCallId: 'call-8', rowVerdict: { kind: 'ok' }, owned: true },
354
+ { taskId: 'run-6', toolCallId: 'call-6', owned: true },
355
+ ], () => ({ withdraw: [], keep: [K('no-gate', 'no-gate'), K('no-row', 'no-row'), K('foreign', 'not-owned'), K('fine', 'verdict-ok'), K('no-verdict', 'verdict-absent')] })),
356
+ ready('ready-not-ready', [{ instance: 'q', origin: 'park-question', gate: tool('run-1', 'call-1'), sourceGeneration: 1 }], [{ taskId: 'run-1', toolCallId: 'call-1', rowVerdict: GOLD_LACKING, owned: true }], () => ({ withdraw: [], keep: [K('q', 'not-ready')] }), { ready: false }),
357
+ ready('ready-generation-moved', [{ instance: 'q', origin: 'park-question', gate: tool('run-1', 'call-1'), sourceGeneration: 1 }], [{ taskId: 'run-1', toolCallId: 'call-1', rowVerdict: GOLD_LACKING, owned: true }], () => ({ withdraw: [], keep: [K('q', 'generation-moved')] }), { readGeneration: 1 }),
358
+ ready('ready-instance-judged-once', [{ instance: 'q', origin: 'park-question', gate: tool('run-8', 'call-8'), sourceGeneration: 1 }, { instance: 'q', origin: 'park-question', gate: tool('run-1', 'call-1'), sourceGeneration: 1 }], [{ taskId: 'run-1', toolCallId: 'call-1', rowVerdict: GOLD_LACKING, owned: true }, { taskId: 'run-8', toolCallId: 'call-8', rowVerdict: { kind: 'ok' }, owned: true }], () => ({ withdraw: [], keep: [K('q', 'verdict-ok')] })),
359
+ ];
360
+ return deepFreeze(cases);
361
+ }
362
+ export const ENGINE_GENERATION_CARD_PLAN_GOLDEN = goldenCases();
@@ -1,6 +1,7 @@
1
1
  import { type PermissionsKeptReason } from '../permissionsDeclaration.js';
2
2
  import type { PermissionsAllowGrantReading } from '../permissionsAllowGrantCapability.js';
3
- export type StandingRowWhy = 'given' | 'card_unreadable' | 'remember_refused' | 'remember_unreadable' | 'route_cannot_carry' | 'classes_unreadable' | 'not_deployment_list_question' | 'evidence_unreadable' | 'offers_unreadable' | 'engine_lane_closed' | 'args_unavailable' | 'grant_binding' | 'tool_name_unreadable' | 'rule_cannot_clear' | 'host_fact_missing' | 'card_face_absent' | 'face_not_generic' | 'rule_not_sent';
3
+ import { type LocalAllowOfferedFacts } from './localAllowRedemption.js';
4
+ export type StandingRowWhy = 'given' | 'card_unreadable' | 'remember_refused' | 'remember_unreadable' | 'route_cannot_carry' | 'classes_unreadable' | 'not_deployment_list_question' | 'evidence_unreadable' | 'offers_unreadable' | 'engine_lane_closed' | 'args_unavailable' | 'grant_binding' | 'tool_name_unreadable' | 'rule_cannot_clear' | 'host_fact_missing' | 'card_face_absent' | 'face_not_generic' | 'rule_not_sent' | 'redemption_closed' | 'redemption_unknown';
4
5
  export type StandingPersistentRow = 'engine_offer' | 'local_whole_tool' | 'none';
5
6
  export interface StandingRowsWhy {
6
7
  readonly session: StandingRowWhy;
@@ -15,6 +16,7 @@ export interface StandingRows {
15
16
  }
16
17
  export interface StandingRowsHostFacts {
17
18
  readonly wholeToolRuleSent?: boolean;
19
+ readonly readOnlyRedemption?: LocalAllowOfferedFacts;
18
20
  }
19
21
  export declare function standingRowsOf(card: object | null | undefined, host?: StandingRowsHostFacts | null): StandingRows;
20
22
  export declare function seatSessionRowOf(card: object | null | undefined, requestId: string | undefined): boolean;
@@ -6,6 +6,7 @@ import { inSlotReadOf, inValueOf } from '../wireFailureShape.js';
6
6
  import { toolPermissionRequestIdDomain } from '../seatContract.js';
7
7
  import { permissionsDeclarationReport } from '../permissionsDeclaration.js';
8
8
  import { readCardUnreadableEvidence, sessionAllowCardReadable, sessionAllowEligible } from './sessionAllowRoute.js';
9
+ import { localAllowRedeemability } from './localAllowRedemption.js';
9
10
  const UNREADABLE = Symbol('unreadable');
10
11
  function ownData(v, key) {
11
12
  try {
@@ -54,6 +55,21 @@ function persistentRowOf(card, host, evidenceUnreadable) {
54
55
  const offers = readRuleOffers(p.state === 'present' ? p.value : undefined);
55
56
  if (offers === undefined || offers.length === 0)
56
57
  return { row: 'none', why: 'offers_unreadable' };
58
+ if (key === 'ruleOffersReadOnly') {
59
+ const facts = typeof host === 'object' && host !== null ? ownData(host, 'readOnlyRedemption') : undefined;
60
+ if (typeof facts !== 'object' || facts === null)
61
+ return { row: 'none', why: 'redemption_unknown' };
62
+ const judged = localAllowRedeemability({
63
+ subject: 'offered',
64
+ managedRulesOnly: ownData(facts, 'managedRulesOnly'),
65
+ scopeRoot: ownData(facts, 'scopeRoot'),
66
+ road: ownData(facts, 'road'),
67
+ });
68
+ if (judged.kind === 'not_redeemable')
69
+ return { row: 'none', why: 'redemption_closed' };
70
+ if (judged.kind === 'unknown')
71
+ return { row: 'none', why: 'redemption_unknown' };
72
+ }
57
73
  return { row: 'engine_offer', why: 'given' };
58
74
  }
59
75
  const absence = present(card, 'ruleOffersAbsence');
@@ -186,6 +202,8 @@ const SESSION_OFFER = Object.freeze({
186
202
  card_face_absent: 'unknown',
187
203
  face_not_generic: 'refused',
188
204
  rule_not_sent: 'refused',
205
+ redemption_closed: 'refused',
206
+ redemption_unknown: 'unknown',
189
207
  });
190
208
  const PERSISTENT_OFFER = Object.freeze({ ...SESSION_OFFER, remember_refused: 'unknown' });
191
209
  export function rememberOfferable(card, host) {
@@ -29,6 +29,7 @@ export interface FsApprovalWireDeps {
29
29
  onPresented?: () => void;
30
30
  onDecideOperation?: DecideOperationObserver;
31
31
  approvalDecidedBy?: ApprovalDecidedByReading | undefined;
32
+ engineGeneration?: () => number;
32
33
  }
33
34
  export declare function structurallyEqual(a: unknown, b: unknown): boolean;
34
35
  export type { RuleOfferMatch };
@@ -101,6 +102,7 @@ export interface ApprovalCardRequest {
101
102
  callKey: string;
102
103
  toolCallId?: string;
103
104
  taskId?: string;
105
+ sourceGeneration?: number;
104
106
  signal?: AbortSignal;
105
107
  workerBadge?: {
106
108
  name: string;
@@ -158,7 +160,6 @@ export interface ApprovalCardSupersedes {
158
160
  readonly priorAnswerOutcome?: 'not_sent' | 'not_accepted' | 'unconfirmed';
159
161
  }
160
162
  export interface ApprovalCardParkRow {
161
- readonly taskId: string;
162
163
  readonly gatedCallId?: string;
163
164
  readonly deadlineMs?: number;
164
165
  }
@@ -238,7 +239,8 @@ export declare function answerPayloadFor(answer: ApprovalCardDecision, lane: 'de
238
239
  export interface AnswerCarriage {
239
240
  readonly allowNote: 'model' | 'none';
240
241
  }
241
- export declare function answerCarriage(card: Pick<ApprovalCardRequest, 'callKey' | 'parkRow'> | null | undefined): AnswerCarriage;
242
+ export declare function answerCarriage(card: Pick<ApprovalCardRequest, 'callKey' | 'parkRow' | 'taskId'> | null | undefined): AnswerCarriage;
243
+ export declare function cardIsOnParkRow(card: object | null | undefined): boolean;
242
244
  export declare function allowNoteNotCarriedSentence(lang?: UiLanguage): string;
243
245
  export interface ApprovalAnswerCommitted {
244
246
  readonly callKey: string;
@@ -286,6 +288,25 @@ export type ToolApprovalParkingBodyReading = {
286
288
  readonly kind: 'claimed_unreadable';
287
289
  };
288
290
  export declare function readToolApprovalRespondParking(v: unknown): ToolApprovalParkingBodyReading | undefined;
291
+ export type ReplayVerdict = {
292
+ readonly kind: 'none';
293
+ } | {
294
+ readonly kind: 'replayed';
295
+ readonly firstDecision: 'allow' | 'deny';
296
+ readonly via: 'arm' | 'claim';
297
+ } | {
298
+ readonly kind: 'unreadable';
299
+ };
300
+ export declare function replayVerdictFrom(raw: {} | null | undefined, parsed: ToolApprovalRespondAckView | undefined, approvalId: string): ReplayVerdict;
301
+ export type ToolApprovalReplayVerdict = {
302
+ readonly kind: 'not_replay';
303
+ } | {
304
+ readonly kind: 'replayed';
305
+ readonly firstDecision: 'allow' | 'deny';
306
+ } | {
307
+ readonly kind: 'unreadable';
308
+ };
309
+ export declare function toolApprovalReplayVerdictOf(raw: {} | null | undefined, approvalId: string): ToolApprovalReplayVerdict;
289
310
  export declare function decisionAttributionKeyOf(reading: ApprovalDecidedByReading | undefined, denySettledBy: ApprovalDenySettledBy | undefined): {
290
311
  decidedBy: 'policy';
291
312
  } | {
@@ -317,6 +338,8 @@ export interface ApprovalCardFrameFactsOptions {
317
338
  readonly streamArgs?: object;
318
339
  readonly argsUnavailable?: true;
319
340
  }
341
+ export type ApprovalCardHostBits = Readonly<Pick<ApprovalCardRequest, 'callKey' | 'taskId' | 'signal' | 'expiresInMs' | 'expiresAtMs' | 'serverNowMs' | 'receivedAtMs' | 'sourceGeneration'>>;
342
+ export declare function approvalCardRequestOf(facts: Readonly<ApprovalCardFacts>, host: ApprovalCardHostBits): ApprovalCardRequest | undefined;
320
343
  export declare function approvalCardFactsOfV1(card: object | null | undefined): Readonly<ApprovalCardV1Facts> | undefined;
321
344
  export declare function approvalCardFactsOfV1Frame(frame: object | null | undefined): Readonly<ApprovalCardFacts> | undefined;
322
345
  export declare function approvalCardFactsOfFrame(frame: object | null | undefined, opts?: ApprovalCardFrameFactsOptions | null): Readonly<ApprovalCardFacts> | undefined;
@@ -329,14 +352,13 @@ export interface ToolApprovalFrameLaneOpts {
329
352
  windowIsCurrent?: boolean;
330
353
  argsUnavailable?: true;
331
354
  }
332
- export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFrame | ApprovalRequestFrame, respond: RespondToolApprovalFn, streamArgs: unknown | undefined, signal?: AbortSignal, lane?: ToolApprovalFrameLaneOpts, run?: {
333
- readonly taskId?: string;
334
- readonly sessionId?: string;
335
- }): Promise<ApprovalResolution>;
336
- export declare function surfaceApprovalFrameWithId(frame: ToolApprovalFrame | ApprovalRequestFrame, approvalId: string, respond: RespondToolApprovalFn, streamArgs: Parameters<typeof surfaceToolApprovalFrameAndRespond>[2], signal: AbortSignal | undefined, lane: ToolApprovalFrameLaneOpts | undefined, run: {
355
+ export interface ToolApprovalFrameRunBits {
337
356
  readonly taskId?: string;
338
357
  readonly sessionId?: string;
339
- } | undefined): Promise<ApprovalResolution>;
358
+ readonly sourceGeneration?: number;
359
+ }
360
+ export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFrame | ApprovalRequestFrame, respond: RespondToolApprovalFn, streamArgs: unknown | undefined, signal?: AbortSignal, lane?: ToolApprovalFrameLaneOpts, run?: ToolApprovalFrameRunBits): Promise<ApprovalResolution>;
361
+ export declare function surfaceApprovalFrameWithId(frame: ToolApprovalFrame | ApprovalRequestFrame, approvalId: string, respond: RespondToolApprovalFn, streamArgs: Parameters<typeof surfaceToolApprovalFrameAndRespond>[2], signal: AbortSignal | undefined, lane: ToolApprovalFrameLaneOpts | undefined, run: ToolApprovalFrameRunBits | undefined): Promise<ApprovalResolution>;
340
362
  export interface ApprovalHolderIdentity {
341
363
  readonly approvalId?: string;
342
364
  readonly taskId?: string;