@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,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();
@@ -701,10 +701,12 @@ export function sessionPolicyRemovalNotice(outcome, deployment) {
701
701
  return 'This session’s rule record holds no entry naming a tool the engine refuses that the supplied roster does not mount, so nothing was changed. If runs still fail at startup over a refused tool name, that entry is set somewhere this record does not cover — the settings sent with the run, a parent session’s rule record, or the deployment’s own tool rules — or it is a name the engine refuses that this version does not yet recognise, or the failing run does not mount a tool the supplied roster has under that name.';
702
702
  case 'judge_failed':
703
703
  return 'Nothing was changed: this client could not check which entries in this session’s rule record name a tool the engine refuses (its copy of the engine’s tool-name check is unavailable or gave no clear answer), so it removed none rather than guess. Removing them needs that check available in this client.';
704
- case 'loosen_forbidden':
705
- if (deploymentHasNoOperator(deployment))
706
- return renderMessage(msg('rules.sessionPolicy.deployment.noOperator.removeLoosenForbidden'));
707
- return 'The engine refused to take these entries out of this session’s rule record: on this engine only an operator can remove entries from it, so the record is unchanged. Ask an operator to remove them, or continue in a new session.';
704
+ case 'loosen_forbidden': {
705
+ const stem = msg('rules.sessionPolicy.remove.loosenForbiddenStem');
706
+ return deploymentHasNoOperator(deployment)
707
+ ? renderMessage(msg('rules.sessionPolicy.deployment.noOperator.removeLoosenForbidden', { stem }))
708
+ : renderMessage(msg('rules.sessionPolicy.remove.loosenForbidden', { stem }));
709
+ }
708
710
  case 'conflict':
709
711
  return 'This session’s rule record was changed by someone else while the refused entries were being removed, twice in a row — nothing was changed. Try again once the other change has finished.';
710
712
  case 'read_failed':
@@ -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,9 +16,11 @@ 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;
23
+ export declare function seatAllowNoteCarriedOf(card: object | null | undefined, requestId: string | undefined): boolean;
21
24
  export type RememberOffer = 'offerable' | 'refused' | 'unknown';
22
25
  export interface RememberOfferable {
23
26
  readonly session: RememberOffer;
@@ -1,11 +1,12 @@
1
1
  import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPairState } from './askClassPair.js';
2
2
  import { askLevelVerdict } from './automaticAnswerVerdict.js';
3
- import { readApprovalCardWord, readRuleOffers } from './toolApprovalWire.js';
3
+ import { answerCarriage, readApprovalCardWord, readRuleOffers } from './toolApprovalWire.js';
4
4
  import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
5
5
  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');
@@ -153,6 +169,20 @@ export function seatSessionRowOf(card, requestId) {
153
169
  return false;
154
170
  }
155
171
  }
172
+ export function seatAllowNoteCarriedOf(card, requestId) {
173
+ if (typeof card !== 'object' || card === null)
174
+ return false;
175
+ try {
176
+ if (Object.hasOwn(card, 'requestId'))
177
+ return false;
178
+ if (toolPermissionRequestIdDomain(requestId) !== 'durable:')
179
+ return false;
180
+ return answerCarriage(card).allowNote === 'model';
181
+ }
182
+ catch {
183
+ return false;
184
+ }
185
+ }
156
186
  const SESSION_OFFER = Object.freeze({
157
187
  given: 'offerable',
158
188
  card_unreadable: 'unknown',
@@ -172,6 +202,8 @@ const SESSION_OFFER = Object.freeze({
172
202
  card_face_absent: 'unknown',
173
203
  face_not_generic: 'refused',
174
204
  rule_not_sent: 'refused',
205
+ redemption_closed: 'refused',
206
+ redemption_unknown: 'unknown',
175
207
  });
176
208
  const PERSISTENT_OFFER = Object.freeze({ ...SESSION_OFFER, remember_refused: 'unknown' });
177
209
  export function rememberOfferable(card, host) {