@sema-agent/client-core 0.105.0 → 0.106.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 (132) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/README.md +26 -11
  3. package/dist/adapt/arms.js +10 -1
  4. package/dist/adapter/activeRunSelfHeal.d.ts +13 -2
  5. package/dist/adapter/activeRunSelfHeal.js +80 -7
  6. package/dist/adapter/downstream/eventToSdkMessage.d.ts +0 -5
  7. package/dist/adapter/downstream/eventToSdkMessage.js +20 -19
  8. package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
  9. package/dist/adapter/downstream/wiringManifestView.d.ts +1 -3
  10. package/dist/adapter/downstream/wiringManifestView.js +0 -1
  11. package/dist/adapter/runStream.js +3 -4
  12. package/dist/approvalReachWire.d.ts +1 -0
  13. package/dist/approvalReachWire.js +4 -1
  14. package/dist/approvalSeat.d.ts +21 -0
  15. package/dist/approvalSeat.js +73 -0
  16. package/dist/assistantInbox.d.ts +2 -0
  17. package/dist/assistantInbox.js +3 -0
  18. package/dist/checkpointRowVerdict.d.ts +23 -0
  19. package/dist/checkpointRowVerdict.js +89 -0
  20. package/dist/coreValuePorts.d.ts +12 -1
  21. package/dist/coreValuePorts.js +10 -0
  22. package/dist/decideFailureNote.js +3 -1
  23. package/dist/decideReceipt.d.ts +9 -0
  24. package/dist/decideReceipt.js +29 -1
  25. package/dist/declared/supportFloorDeclared.js +2 -2
  26. package/dist/deviceRebind.d.ts +7 -0
  27. package/dist/deviceRebind.js +39 -5
  28. package/dist/engineAddress.d.ts +3 -0
  29. package/dist/engineAddress.js +25 -0
  30. package/dist/engineDirectory.js +9 -17
  31. package/dist/engineErrorCodes.d.ts +8 -0
  32. package/dist/engineErrorCodes.js +8 -0
  33. package/dist/engineNoticeCodes.d.ts +26 -3
  34. package/dist/engineNoticeCodes.js +52 -7
  35. package/dist/engineRowRef.js +1 -1
  36. package/dist/errorSupplementRoles.js +2 -0
  37. package/dist/eventLeg.d.ts +2 -2
  38. package/dist/eventLeg.js +2 -1
  39. package/dist/fleet/fleetProjection.js +26 -5
  40. package/dist/generated/engineFactTables.d.ts +2 -1
  41. package/dist/generated/engineFactTables.js +3 -1
  42. package/dist/generated/engineNoticeTables.js +19 -4
  43. package/dist/generated/sdkFactTables.d.ts +3 -0
  44. package/dist/generated/sdkFactTables.js +15 -0
  45. package/dist/generated/settingsSchemaFns.d.ts +11 -0
  46. package/dist/generated/settingsSchemaFns.js +2 -1
  47. package/dist/generated/settingsSchemaSafetyMergeSpec.js +1 -1
  48. package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -1
  49. package/dist/generated/settingsSchemaSettingsSources.js +22 -2
  50. package/dist/hitl/approvalLiveFace.d.ts +17 -0
  51. package/dist/hitl/approvalLiveFace.js +35 -0
  52. package/dist/hitl/approvalQueueRow.d.ts +21 -0
  53. package/dist/hitl/approvalQueueRow.js +69 -0
  54. package/dist/hitl/approvalsFeed.d.ts +5 -4
  55. package/dist/hitl/approvalsFeed.js +38 -6
  56. package/dist/hitl/askParkRowRouting.d.ts +4 -3
  57. package/dist/hitl/askParkRowRouting.js +15 -3
  58. package/dist/hitl/automaticAnswerVerdict.js +13 -10
  59. package/dist/hitl/crashConverged.d.ts +5 -1
  60. package/dist/hitl/decisionConflict.js +2 -1
  61. package/dist/hitl/decisionRefusal.js +1 -1
  62. package/dist/hitl/decisionSend.d.ts +1 -0
  63. package/dist/hitl/decisionSend.js +14 -2
  64. package/dist/hitl/frameRouter.js +4 -5
  65. package/dist/hitl/hitlBridge.d.ts +1 -1
  66. package/dist/hitl/hitlBridge.js +26 -5
  67. package/dist/hitl/livePendingAsk.d.ts +5 -0
  68. package/dist/hitl/livePendingAsk.js +6 -1
  69. package/dist/hitl/planReviewCard.d.ts +2 -0
  70. package/dist/hitl/planReviewCard.js +21 -0
  71. package/dist/hitl/planReviewWire.d.ts +2 -2
  72. package/dist/hitl/planReviewWire.js +101 -36
  73. package/dist/hitl/sessionAllowRoute.d.ts +2 -0
  74. package/dist/hitl/sessionAllowRoute.js +7 -0
  75. package/dist/hitl/sessionPolicyWire.d.ts +1 -7
  76. package/dist/hitl/sessionPolicyWire.js +3 -43
  77. package/dist/hitl/subagentSource.d.ts +1 -0
  78. package/dist/hitl/subagentSource.js +13 -0
  79. package/dist/hitl/toolApprovalWire.d.ts +3 -2
  80. package/dist/hitl/toolApprovalWire.js +79 -37
  81. package/dist/hostSettingsSource.d.ts +4 -1
  82. package/dist/hostSettingsSource.js +11 -6
  83. package/dist/hostTurnSettings.d.ts +4 -5
  84. package/dist/hostTurnSettings.js +80 -19
  85. package/dist/i18n/messages.en.d.ts +35 -12
  86. package/dist/i18n/messages.en.js +36 -13
  87. package/dist/i18n/messages.zh.js +38 -13
  88. package/dist/index.d.ts +21 -2
  89. package/dist/index.js +11 -2
  90. package/dist/liveInitToolFace.js +0 -1
  91. package/dist/liveModelCatalog.d.ts +15 -0
  92. package/dist/liveModelCatalog.js +40 -0
  93. package/dist/mcpWireCaps.js +1 -1
  94. package/dist/modelCapabilityProbe.js +66 -33
  95. package/dist/ownKey.d.ts +0 -1
  96. package/dist/ownKey.js +0 -2
  97. package/dist/permissionsDeclaration.d.ts +2 -2
  98. package/dist/permissionsDeclaration.js +12 -14
  99. package/dist/projectSeed.d.ts +39 -0
  100. package/dist/projectSeed.js +80 -0
  101. package/dist/promptDelivery.js +1 -1
  102. package/dist/replayBody.d.ts +7 -6
  103. package/dist/replayBody.js +62 -7
  104. package/dist/request/taskRequest.js +3 -4
  105. package/dist/ruleCompileVerdict.d.ts +0 -2
  106. package/dist/ruleCompileVerdict.js +0 -31
  107. package/dist/ruleRefusalSentence.js +0 -3
  108. package/dist/runReattachPolicy.d.ts +3 -0
  109. package/dist/runReattachPolicy.js +15 -2
  110. package/dist/runTerminal.js +30 -8
  111. package/dist/sandboxWire.d.ts +0 -3
  112. package/dist/sandboxWire.js +0 -9
  113. package/dist/seam.d.ts +14 -7
  114. package/dist/seam.js +12 -0
  115. package/dist/seatContract.d.ts +57 -7
  116. package/dist/seatContract.js +132 -3
  117. package/dist/serverSupportRange.d.ts +18 -0
  118. package/dist/serverSupportRange.js +25 -0
  119. package/dist/sessionEngine.d.ts +156 -0
  120. package/dist/sessionEngine.js +464 -0
  121. package/dist/sessionMap.d.ts +11 -1
  122. package/dist/sessionMap.js +35 -9
  123. package/dist/sessionMemorySync.d.ts +31 -0
  124. package/dist/sessionMemorySync.js +95 -0
  125. package/dist/sessionRowTag.d.ts +4 -0
  126. package/dist/sessionRowTag.js +18 -0
  127. package/dist/settingsLayerAdmission.d.ts +1 -0
  128. package/dist/settingsLayerAdmission.js +4 -1
  129. package/dist/toolResult.js +1 -1
  130. package/dist/wireRefusalCopy.js +2 -6
  131. package/docs/INTEGRATION-CLIENTS.md +1022 -16
  132. package/package.json +5 -5
@@ -0,0 +1,464 @@
1
+ import { engineNamespaceKeyFor } from './sessionMap.js';
2
+ import { carriesNoCredentialSlots, isCenterRelativePath, tupleOriginUrlOf } from './engineAddress.js';
3
+ import { ownSendableField, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
4
+ import { slotReadOf } from './wireFailureShape.js';
5
+ import { msg } from './i18n/translate.js';
6
+ const V1 = 'v1';
7
+ const SELF_KEY = JSON.stringify([V1, 'self']);
8
+ const EMPTY = Object.freeze({});
9
+ function isObj(v) {
10
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
11
+ }
12
+ function isCanonicalAddress(s) {
13
+ return s !== '' && tupleOriginUrlOf(s) !== undefined && engineNamespaceKeyFor(s) === s;
14
+ }
15
+ function decodeKey(key) {
16
+ let parsed;
17
+ try {
18
+ parsed = JSON.parse(key);
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ if (!Array.isArray(parsed) || !parsed.every((x) => typeof x === 'string'))
24
+ return undefined;
25
+ const t = parsed;
26
+ if (JSON.stringify(t) !== key || t[0] !== V1)
27
+ return undefined;
28
+ if (t[1] === 'self' && t.length === 2)
29
+ return { identityKind: 'self' };
30
+ if (t[1] === 'declared' && t.length === 5) {
31
+ const [, , center, scope, name] = t;
32
+ return isCanonicalAddress(center) && scope !== '' && name !== '' ? { identityKind: 'declared', center, scope, name } : undefined;
33
+ }
34
+ if (t[1] === 'address' && t.length === 3) {
35
+ const address = t[2];
36
+ return isCanonicalAddress(address) ? { identityKind: 'address', address } : undefined;
37
+ }
38
+ return undefined;
39
+ }
40
+ const fail = (reason) => Object.freeze({ ok: false, reason });
41
+ function selfBinding() {
42
+ return Object.freeze({ ok: true, identityKind: 'self', key: SELF_KEY, presentation: EMPTY });
43
+ }
44
+ function declaredBinding(center, scope, name, viaCenter) {
45
+ return Object.freeze({
46
+ ok: true,
47
+ identityKind: 'declared',
48
+ key: JSON.stringify([V1, 'declared', center, scope, name]),
49
+ presentation: Object.freeze({ center, scope, name }),
50
+ connect: Object.freeze({ viaCenter }),
51
+ });
52
+ }
53
+ function addressBinding(raw) {
54
+ const u = tupleOriginUrlOf(raw);
55
+ if (u === undefined || !carriesNoCredentialSlots(u))
56
+ return fail('address-invalid');
57
+ const address = engineNamespaceKeyFor(raw);
58
+ return Object.freeze({
59
+ ok: true,
60
+ identityKind: 'address',
61
+ key: JSON.stringify([V1, 'address', address]),
62
+ presentation: Object.freeze({ address }),
63
+ connect: Object.freeze({ url: raw }),
64
+ });
65
+ }
66
+ export function sessionEngineBindingFor(input) {
67
+ try {
68
+ if (!isObj(input))
69
+ return fail('input-invalid');
70
+ const role = readOwnDataValue(input, 'role');
71
+ const row = slotReadOf(input, 'row');
72
+ const address = slotReadOf(input, 'address');
73
+ const center = slotReadOf(input, 'center');
74
+ if (role === 'self') {
75
+ return row.presence === 'absent' && address.presence === 'absent' && center.presence === 'absent' ? selfBinding() : fail('input-invalid');
76
+ }
77
+ if (role !== 'external')
78
+ return fail('input-invalid');
79
+ const rowGiven = row.presence !== 'absent';
80
+ if (rowGiven === (address.presence !== 'absent'))
81
+ return fail('input-invalid');
82
+ if (!rowGiven) {
83
+ if (center.presence !== 'absent')
84
+ return fail('input-invalid');
85
+ return address.presence === 'present' && typeof address.value === 'string' ? addressBinding(address.value) : fail('address-invalid');
86
+ }
87
+ if (row.presence !== 'present' || !isObj(row.value))
88
+ return fail('row-incomplete');
89
+ const r = row.value;
90
+ const name = readOwnDataNonEmptyString(r, 'name');
91
+ const scope = readOwnDataNonEmptyString(r, 'scope');
92
+ const viaCenter = readOwnDataNonEmptyString(r, 'viaCenter');
93
+ if (name === undefined || scope === undefined || viaCenter === undefined || !isCenterRelativePath(viaCenter))
94
+ return fail('row-incomplete');
95
+ if (center.presence !== 'present' || typeof center.value !== 'string' || tupleOriginUrlOf(center.value) === undefined)
96
+ return fail('center-invalid');
97
+ return declaredBinding(engineNamespaceKeyFor(center.value), scope, name, viaCenter);
98
+ }
99
+ catch {
100
+ return fail('input-invalid');
101
+ }
102
+ }
103
+ function assignmentOf(kindRaw, keyRaw, connectSlot) {
104
+ if (kindRaw !== 'local' && kindRaw !== 'cloud')
105
+ return undefined;
106
+ if (typeof keyRaw !== 'string')
107
+ return undefined;
108
+ const decoded = decodeKey(keyRaw);
109
+ if (decoded === undefined)
110
+ return undefined;
111
+ const connectAbsent = connectSlot.presence === 'absent' || (connectSlot.presence === 'present' && connectSlot.value === undefined);
112
+ let identity;
113
+ if (decoded.identityKind === 'self') {
114
+ if (!connectAbsent)
115
+ return undefined;
116
+ identity = selfBinding();
117
+ }
118
+ else {
119
+ if (connectSlot.presence !== 'present' || !isObj(connectSlot.value))
120
+ return undefined;
121
+ const c = connectSlot.value;
122
+ if (decoded.identityKind === 'address') {
123
+ const url = readOwnDataValue(c, 'url');
124
+ if (typeof url !== 'string')
125
+ return undefined;
126
+ const re = addressBinding(url);
127
+ if (!re.ok || re.key !== keyRaw)
128
+ return undefined;
129
+ identity = re;
130
+ }
131
+ else {
132
+ const viaCenter = readOwnDataNonEmptyString(c, 'viaCenter');
133
+ if (viaCenter === undefined || !isCenterRelativePath(viaCenter))
134
+ return undefined;
135
+ identity = declaredBinding(decoded.center, decoded.scope, decoded.name, viaCenter);
136
+ if (identity.key !== keyRaw)
137
+ return undefined;
138
+ }
139
+ }
140
+ return Object.freeze({ kind: kindRaw, identity });
141
+ }
142
+ function sameAssignment(a, b) {
143
+ if (a.kind !== b.kind || a.identity.key !== b.identity.key || a.identity.identityKind !== b.identity.identityKind)
144
+ return false;
145
+ if (a.identity.identityKind === 'address' && b.identity.identityKind === 'address')
146
+ return a.identity.connect.url === b.identity.connect.url;
147
+ if (a.identity.identityKind === 'declared' && b.identity.identityKind === 'declared')
148
+ return a.identity.connect.viaCenter === b.identity.connect.viaCenter;
149
+ return true;
150
+ }
151
+ function viewOf(a) {
152
+ const id = a.identity;
153
+ if (id.identityKind === 'self')
154
+ return Object.freeze({ kind: a.kind, identityKind: 'self', identity: Object.freeze({ key: id.key, presentation: id.presentation }) });
155
+ if (id.identityKind === 'declared')
156
+ return Object.freeze({ kind: a.kind, identityKind: 'declared', identity: Object.freeze({ key: id.key, presentation: id.presentation }) });
157
+ return Object.freeze({ kind: a.kind, identityKind: 'address', identity: Object.freeze({ key: id.key, presentation: id.presentation }) });
158
+ }
159
+ function snapshotOf(a) {
160
+ const id = a.identity;
161
+ if (id.identityKind === 'address')
162
+ return { kind: a.kind, key: id.key, connect: { url: id.connect.url } };
163
+ if (id.identityKind === 'declared')
164
+ return { kind: a.kind, key: id.key, connect: { viaCenter: id.connect.viaCenter } };
165
+ return { kind: a.kind, key: id.key };
166
+ }
167
+ export function isSessionEngineView(v) {
168
+ try {
169
+ if (!isObj(v))
170
+ return false;
171
+ const kind = ownSendableField(v, 'kind');
172
+ if (kind !== 'local' && kind !== 'cloud')
173
+ return false;
174
+ const identityKind = ownSendableField(v, 'identityKind');
175
+ const identity = ownSendableField(v, 'identity');
176
+ if (!isObj(identity))
177
+ return false;
178
+ const key = ownSendableField(identity, 'key');
179
+ const presentation = ownSendableField(identity, 'presentation');
180
+ if (typeof key !== 'string' || !isObj(presentation))
181
+ return false;
182
+ const decoded = decodeKey(key);
183
+ if (decoded === undefined || decoded.identityKind !== identityKind)
184
+ return false;
185
+ const fields = decoded.identityKind === 'self' ? {} : decoded.identityKind === 'declared' ? { center: decoded.center, scope: decoded.scope, name: decoded.name } : { address: decoded.address };
186
+ if (Reflect.ownKeys(presentation).length !== Object.keys(fields).length)
187
+ return false;
188
+ return Object.keys(fields).every((k) => ownSendableField(presentation, k) === fields[k]);
189
+ }
190
+ catch {
191
+ return false;
192
+ }
193
+ }
194
+ function entryIdOf(record, key) {
195
+ const engines = slotReadOf(record, 'engines');
196
+ if (engines.presence !== 'present' || !isObj(engines.value))
197
+ return undefined;
198
+ const entry = slotReadOf(engines.value, key);
199
+ if (entry.presence !== 'present' || !isObj(entry.value))
200
+ return undefined;
201
+ return readOwnDataNonEmptyString(entry.value, 'engineSessionId');
202
+ }
203
+ function hasHistory(record) {
204
+ const engines = slotReadOf(record, 'engines');
205
+ if (engines.presence === 'unreadable')
206
+ return true;
207
+ if (engines.presence === 'present' && engines.value !== undefined) {
208
+ if (!isObj(engines.value))
209
+ return true;
210
+ if (Reflect.ownKeys(engines.value).length > 0)
211
+ return true;
212
+ }
213
+ const top = slotReadOf(record, 'engineSessionId');
214
+ if (top.presence === 'unreadable')
215
+ return true;
216
+ return top.presence === 'present' && top.value !== undefined && top.value !== '';
217
+ }
218
+ export function readSessionEngine(record) {
219
+ try {
220
+ if (record === undefined || record === null)
221
+ return NONE;
222
+ if (!isObj(record))
223
+ return UNRESOLVED;
224
+ const snap = slotReadOf(record, 'boundEngine');
225
+ if (snap.presence === 'unreadable')
226
+ return UNRESOLVED;
227
+ if (snap.presence === 'present' && snap.value !== undefined) {
228
+ if (!isObj(snap.value))
229
+ return UNRESOLVED;
230
+ const s = snap.value;
231
+ const binding = assignmentOf(readOwnDataValue(s, 'kind'), readOwnDataValue(s, 'key'), slotReadOf(s, 'connect'));
232
+ if (binding === undefined)
233
+ return UNRESOLVED;
234
+ const uuid = entryIdOf(record, binding.identity.key);
235
+ if (uuid === undefined)
236
+ return UNRESOLVED;
237
+ return Object.freeze({ kind: 'bound', binding, engine: viewOf(binding), uuid });
238
+ }
239
+ return hasHistory(record) ? LEGACY : NONE;
240
+ }
241
+ catch {
242
+ return UNRESOLVED;
243
+ }
244
+ }
245
+ const NONE = Object.freeze({ kind: 'none' });
246
+ const LEGACY = Object.freeze({ kind: 'legacy' });
247
+ const UNRESOLVED = Object.freeze({ kind: 'unresolved' });
248
+ function judgeNotice(a) {
249
+ return a.identity.identityKind === 'self' && a.kind !== 'local' ? msg('session.engine.selfNotLocal') : undefined;
250
+ }
251
+ function committed(a) {
252
+ const notice = judgeNotice(a);
253
+ const engine = viewOf(a);
254
+ return Object.freeze(notice === undefined ? { kind: 'committed', engine } : { kind: 'committed', engine, notice });
255
+ }
256
+ const unresolvedCommit = (reason) => Object.freeze({ kind: 'unresolved', reason });
257
+ const STORE_FAILED = Object.freeze({ kind: 'store-failed' });
258
+ function requestAssignmentOf(raw) {
259
+ if (!isObj(raw))
260
+ return undefined;
261
+ const identity = readOwnDataValue(raw, 'identity');
262
+ if (!isObj(identity) || readOwnDataValue(identity, 'ok') !== true)
263
+ return undefined;
264
+ const a = assignmentOf(readOwnDataValue(raw, 'kind'), readOwnDataValue(identity, 'key'), slotReadOf(identity, 'connect'));
265
+ if (a === undefined || readOwnDataValue(identity, 'identityKind') !== a.identity.identityKind)
266
+ return undefined;
267
+ return a;
268
+ }
269
+ function legacyKeyOf(a) {
270
+ const id = a.identity;
271
+ if (id.identityKind === 'self')
272
+ return 'local';
273
+ if (id.identityKind === 'address')
274
+ return engineNamespaceKeyFor(id.connect.url);
275
+ const center = tupleOriginUrlOf(id.presentation.center);
276
+ return center === undefined ? undefined : engineNamespaceKeyFor(center.origin + id.connect.viaCenter);
277
+ }
278
+ const nextOf = (step) => (Object.hasOwn(step, 'next') ? step.next : undefined);
279
+ function firstWrite(prior, session, a, now) {
280
+ const engines = { ...(prior?.engines ?? {}) };
281
+ const existing = Object.hasOwn(engines, a.identity.key) ? engines[a.identity.key] : undefined;
282
+ Object.defineProperty(engines, a.identity.key, {
283
+ value: { ...existing, engineSessionId: session.uuid, updatedAt: now },
284
+ writable: true,
285
+ enumerable: true,
286
+ configurable: true,
287
+ });
288
+ return { ...prior, shellSessionId: session.shellSessionId, boundEngine: snapshotOf(a), engines, updatedAt: now };
289
+ }
290
+ function commitStep(prior, session, arm, a, chosenByUser) {
291
+ const reading = readSessionEngine(prior);
292
+ if (prior !== undefined && prior !== null) {
293
+ const handle = isObj(prior) ? readOwnDataValue(prior, 'shellSessionId') : undefined;
294
+ if (handle !== session.shellSessionId) {
295
+ return { result: reading.kind === 'bound' ? Object.freeze({ kind: 'conflict', bound: reading.engine }) : unresolvedCommit('record-mismatch') };
296
+ }
297
+ }
298
+ if (reading.kind === 'unresolved')
299
+ return { result: unresolvedCommit('record-unreadable') };
300
+ if (reading.kind === 'bound') {
301
+ if (sameAssignment(reading.binding, a) && reading.uuid === session.uuid)
302
+ return { result: committed(a) };
303
+ return { result: Object.freeze({ kind: 'conflict', bound: reading.engine }) };
304
+ }
305
+ const now = new Date().toISOString();
306
+ if (reading.kind === 'none') {
307
+ if (arm !== 'create')
308
+ return { result: unresolvedCommit('record-unbound') };
309
+ return { result: committed(a), next: firstWrite(prior, session, a, now) };
310
+ }
311
+ if (arm !== 'recover')
312
+ return { result: unresolvedCommit('record-legacy') };
313
+ const oldKey = legacyKeyOf(a);
314
+ const proven = oldKey !== undefined &&
315
+ isObj(prior) &&
316
+ entryIdOf(prior, oldKey) === session.uuid &&
317
+ (a.identity.identityKind !== 'declared' || chosenByUser) &&
318
+ (entryIdOf(prior, a.identity.key) ?? session.uuid) === session.uuid;
319
+ if (!proven)
320
+ return { result: unresolvedCommit('recover-unproven') };
321
+ return { result: committed(a), next: firstWrite(prior, session, a, now) };
322
+ }
323
+ export function commitSessionEngine(store, session, request) {
324
+ let readMergeWrite;
325
+ let ref;
326
+ let arm;
327
+ let assignment;
328
+ let chosenByUser = false;
329
+ try {
330
+ if (!isObj(store) || !isObj(session) || !isObj(request))
331
+ return unresolvedCommit('request-invalid');
332
+ const rmw = methodOf(store, 'readMergeWrite');
333
+ if (rmw === undefined)
334
+ return unresolvedCommit('request-invalid');
335
+ readMergeWrite = (build) => rmw.call(store, build);
336
+ const shellSessionId = readOwnDataNonEmptyString(session, 'shellSessionId');
337
+ const uuid = readOwnDataNonEmptyString(session, 'uuid');
338
+ if (shellSessionId === undefined || uuid === undefined)
339
+ return unresolvedCommit('request-invalid');
340
+ ref = Object.freeze({ shellSessionId, uuid });
341
+ const armRaw = readOwnDataValue(request, 'arm');
342
+ if (armRaw !== 'create' && armRaw !== 'resume' && armRaw !== 'recover')
343
+ return unresolvedCommit('request-invalid');
344
+ arm = armRaw;
345
+ const a = requestAssignmentOf(readOwnDataValue(request, 'binding'));
346
+ if (a === undefined)
347
+ return unresolvedCommit('request-invalid');
348
+ assignment = a;
349
+ if (arm === 'recover')
350
+ chosenByUser = readOwnDataValue(request, 'chosenByUser') === true;
351
+ }
352
+ catch {
353
+ return unresolvedCommit('request-invalid');
354
+ }
355
+ let step;
356
+ try {
357
+ const returned = readMergeWrite((prior) => {
358
+ step = commitStep(prior, ref, arm, assignment, chosenByUser);
359
+ return nextOf(step);
360
+ });
361
+ if (step === undefined)
362
+ return STORE_FAILED;
363
+ if (nextOf(step) !== undefined && !isObj(returned))
364
+ return STORE_FAILED;
365
+ return step.result;
366
+ }
367
+ catch {
368
+ return STORE_FAILED;
369
+ }
370
+ }
371
+ function methodOf(o, key) {
372
+ try {
373
+ const v = o[key];
374
+ return typeof v === 'function' ? v : undefined;
375
+ }
376
+ catch {
377
+ return undefined;
378
+ }
379
+ }
380
+ const REFUSAL_KEYS = {
381
+ 'not-wired': 'session.engine.dispatch.notWired',
382
+ 'scope-mismatch': 'session.engine.dispatch.scopeMismatch',
383
+ 'scope-unproven': 'session.engine.dispatch.scopeUnproven',
384
+ unresolved: 'session.engine.dispatch.unresolved',
385
+ };
386
+ const refused = (refusal) => Object.freeze({ kind: 'refused', refusal, message: msg(REFUSAL_KEYS[refusal]) });
387
+ function authOf(ctx) {
388
+ const slot = slotReadOf(ctx, 'auth');
389
+ if (slot.presence === 'absent' || (slot.presence === 'present' && slot.value === undefined))
390
+ return { kind: 'none' };
391
+ if (slot.presence !== 'present' || !isObj(slot.value))
392
+ return { kind: 'unknown' };
393
+ const a = slot.value;
394
+ const kind = readOwnDataValue(a, 'kind');
395
+ if (kind === 'token')
396
+ return { kind: 'token' };
397
+ if (kind !== 'registry')
398
+ return { kind: 'unknown' };
399
+ const registryUrl = readOwnDataValue(a, 'registryUrl');
400
+ const u = typeof registryUrl === 'string' ? tupleOriginUrlOf(registryUrl) : undefined;
401
+ if (u === undefined || typeof registryUrl !== 'string')
402
+ return { kind: 'unknown' };
403
+ const scopeSlot = slotReadOf(a, 'scope');
404
+ if (scopeSlot.presence === 'unreadable')
405
+ return { kind: 'unknown' };
406
+ const scope = scopeSlot.presence === 'present' && typeof scopeSlot.value === 'string' && scopeSlot.value !== '' ? scopeSlot.value : undefined;
407
+ return { kind: 'registry', center: engineNamespaceKeyFor(registryUrl), origin: u.origin, scope };
408
+ }
409
+ function addressSetOf(ctx, key) {
410
+ const out = new Set();
411
+ const v = readOwnDataValue(ctx, key);
412
+ if (!Array.isArray(v))
413
+ return out;
414
+ const n = readOwnDataValue(v, 'length');
415
+ if (typeof n !== 'number' || !Number.isSafeInteger(n) || n < 0)
416
+ return out;
417
+ for (let i = 0; i < n; i += 1) {
418
+ const s = readOwnDataValue(v, String(i));
419
+ if (typeof s === 'string' && tupleOriginUrlOf(s) !== undefined)
420
+ out.add(engineNamespaceKeyFor(s));
421
+ }
422
+ return out;
423
+ }
424
+ export function sessionDispatchTarget(reading, ctx) {
425
+ try {
426
+ if (!isObj(reading) || readOwnDataValue(reading, 'kind') !== 'bound')
427
+ return refused('unresolved');
428
+ const a = requestAssignmentOf(readOwnDataValue(reading, 'binding'));
429
+ if (a === undefined)
430
+ return refused('unresolved');
431
+ if (!isObj(ctx))
432
+ return refused('not-wired');
433
+ const id = readOwnDataNonEmptyString(ctx, 'id');
434
+ if (id === undefined)
435
+ return refused('not-wired');
436
+ const identity = a.identity;
437
+ if (identity.identityKind === 'self') {
438
+ return readOwnDataValue(ctx, 'selfSpawned') === true ? Object.freeze({ kind: 'self', wiringSnapshotId: id }) : refused('not-wired');
439
+ }
440
+ const auth = authOf(ctx);
441
+ if (identity.identityKind === 'declared') {
442
+ if (auth.kind !== 'registry' || auth.center !== identity.presentation.center || auth.scope === undefined)
443
+ return refused('scope-unproven');
444
+ if (auth.scope !== identity.presentation.scope)
445
+ return refused('scope-mismatch');
446
+ return Object.freeze({ kind: 'center', url: auth.origin + identity.connect.viaCenter, wiringSnapshotId: id });
447
+ }
448
+ const url = identity.connect.url;
449
+ const u = tupleOriginUrlOf(url);
450
+ if (u === undefined)
451
+ return refused('unresolved');
452
+ const address = engineNamespaceKeyFor(url);
453
+ const viaOtherOrigin = auth.kind === 'registry' && auth.origin !== u.origin;
454
+ const explicitDirect = auth.kind === 'none' && addressSetOf(ctx, 'explicitAddresses').has(address) && u.pathname.replace(/\/+$/, '') === '';
455
+ if (!viaOtherOrigin && !explicitDirect)
456
+ return refused('scope-unproven');
457
+ if (!addressSetOf(ctx, 'wiredAddresses').has(address))
458
+ return refused('not-wired');
459
+ return Object.freeze({ kind: 'address', url, wiringSnapshotId: id });
460
+ }
461
+ catch {
462
+ return refused('unresolved');
463
+ }
464
+ }
@@ -15,15 +15,25 @@ export interface SessionMapRecord {
15
15
  reason: string;
16
16
  at: string;
17
17
  };
18
+ boundEngine?: SessionEngineSnapshot;
18
19
  updatedAt: string;
19
20
  }
21
+ export interface SessionEngineSnapshot {
22
+ kind: 'local' | 'cloud';
23
+ key: string;
24
+ connect?: {
25
+ url: string;
26
+ } | {
27
+ viaCenter: string;
28
+ };
29
+ }
20
30
  export declare function engineNamespaceKeyFor(baseUrl: string | undefined): string;
21
31
  export type SessionMapMergeVerdict = {
22
32
  ok: true;
23
33
  record: SessionMapRecord;
24
34
  } | {
25
35
  ok: false;
26
- reason: 'no-shell-session-id' | 'no-engine-session-id';
36
+ reason: 'no-shell-session-id' | 'no-engine-session-id' | 'bound-entry-conflict';
27
37
  };
28
38
  export declare function mergeSessionMapRecord(prior: SessionMapRecord | undefined, partial: Partial<SessionMapRecord> & {
29
39
  shellSessionId?: string;
@@ -1,22 +1,41 @@
1
+ import { tupleOriginUrlOf } from './engineAddress.js';
2
+ import { readOwnDataValue } from './ownKey.js';
1
3
  export function engineNamespaceKeyFor(baseUrl) {
2
4
  if (!baseUrl)
3
5
  return 'local';
4
- try {
5
- const u = new URL(baseUrl);
6
- if (!/^(?:https?|wss?|ftp):$/.test(u.protocol))
7
- return baseUrl.toLowerCase();
8
- return u.origin.toLowerCase() + u.pathname.replace(/\/+$/, '');
9
- }
10
- catch {
6
+ const u = tupleOriginUrlOf(baseUrl);
7
+ if (u === undefined)
11
8
  return baseUrl.toLowerCase();
12
- }
9
+ return u.origin.toLowerCase() + u.pathname.replace(/\/+$/, '');
10
+ }
11
+ function boundEntryKept(prior, nextEngines) {
12
+ const key = prior?.boundEngine?.key;
13
+ if (typeof key !== 'string')
14
+ return true;
15
+ const before = prior?.engines !== undefined && Object.hasOwn(prior.engines, key) ? prior.engines[key] : undefined;
16
+ const has = nextEngines !== undefined && nextEngines !== null && Object.hasOwn(nextEngines, key);
17
+ const after = has ? readOwnDataValue(nextEngines, key) : undefined;
18
+ if (has && after === undefined)
19
+ return false;
20
+ if (before === undefined || after === undefined)
21
+ return before === after;
22
+ if (after === before)
23
+ return true;
24
+ return typeof after === 'object' && after !== null && readOwnDataValue(after, 'engineSessionId') === before.engineSessionId;
13
25
  }
14
26
  const isoNow = () => new Date().toISOString();
15
27
  export function mergeSessionMapRecord(prior, partial, now = isoNow) {
16
28
  const shellSessionId = partial.shellSessionId ?? prior?.shellSessionId;
17
29
  if (!shellSessionId)
18
30
  return { ok: false, reason: 'no-shell-session-id' };
19
- return { ok: true, record: { ...prior, ...partial, shellSessionId, updatedAt: now() } };
31
+ const record = { ...prior, ...partial, shellSessionId, updatedAt: now() };
32
+ if (prior?.boundEngine !== undefined)
33
+ record.boundEngine = prior.boundEngine;
34
+ else
35
+ delete record.boundEngine;
36
+ if (!boundEntryKept(prior, record.engines))
37
+ return { ok: false, reason: 'bound-entry-conflict' };
38
+ return { ok: true, record };
20
39
  }
21
40
  export function mergeEngineEntry(prior, key, partial, shellSessionId, now = isoNow) {
22
41
  const priorEntry = prior?.engines?.[key];
@@ -26,6 +45,13 @@ export function mergeEngineEntry(prior, key, partial, shellSessionId, now = isoN
26
45
  const sid = shellSessionId ?? prior?.shellSessionId;
27
46
  if (!sid)
28
47
  return { ok: false, reason: 'no-shell-session-id' };
48
+ if (prior?.boundEngine !== undefined && key === prior.boundEngine.key) {
49
+ if (priorEntry === undefined ||
50
+ engineSessionId !== priorEntry.engineSessionId ||
51
+ (Object.hasOwn(partial, 'engineSessionId') && readOwnDataValue(partial, 'engineSessionId') !== priorEntry.engineSessionId)) {
52
+ return { ok: false, reason: 'bound-entry-conflict' };
53
+ }
54
+ }
29
55
  const entry = { ...priorEntry, ...partial, engineSessionId, updatedAt: now() };
30
56
  return {
31
57
  ok: true,
@@ -0,0 +1,31 @@
1
+ import { SDK_SESSION_MEMORY_UNOBSERVED_REASONS } from './generated/sdkFactTables.js';
2
+ import { type UiLanguage } from './env/uiLanguage.js';
3
+ type UnobservedReason = (typeof SDK_SESSION_MEMORY_UNOBSERVED_REASONS)[number];
4
+ export type SessionMemorySyncReportReading = {
5
+ readonly scopes: readonly string[];
6
+ } & ({
7
+ readonly layers: readonly object[];
8
+ readonly refused?: undefined;
9
+ } | {
10
+ readonly refused: object;
11
+ readonly layers?: undefined;
12
+ });
13
+ export type SessionMemorySyncState = {
14
+ readonly kind: 'not_configured';
15
+ readonly report: SessionMemorySyncReportReading;
16
+ } | {
17
+ readonly kind: 'reported';
18
+ readonly report: SessionMemorySyncReportReading;
19
+ } | {
20
+ readonly kind: 'unobtainable';
21
+ readonly reason?: UnobservedReason;
22
+ readonly activeTaskId?: string;
23
+ };
24
+ export type SessionMemorySyncSettled = {
25
+ readonly report: {} | null | undefined;
26
+ } | {
27
+ readonly error: {} | null | undefined;
28
+ };
29
+ export declare function sessionMemorySyncStateOf(settled: SessionMemorySyncSettled | null | undefined): SessionMemorySyncState | null;
30
+ export declare function sessionMemorySyncNotice(state: SessionMemorySyncState | null | undefined, lang?: UiLanguage | (string & {})): string | undefined;
31
+ export {};
@@ -0,0 +1,95 @@
1
+ import { CONFLICT_SESSION_MEMORY_UNOBSERVED } from './engineErrorCodes.js';
2
+ import { SDK_SESSION_MEMORY_UNOBSERVED_REASONS } from './generated/sdkFactTables.js';
3
+ import { isUiLanguage } from './env/uiLanguage.js';
4
+ import { msg, renderMessage } from './i18n/translate.js';
5
+ import { slotReadOf, isSlotCarrier, readErrorString } from './wireFailureShape.js';
6
+ import { effectiveWireErrorCode } from './wireErrorTriage.js';
7
+ const reasonOf = (v) => SDK_SESSION_MEMORY_UNOBSERVED_REASONS.find((w) => w === v);
8
+ function scopesOf(report) {
9
+ const r = slotReadOf(report, 'scopes');
10
+ if (r.presence !== 'present' || !Array.isArray(r.value))
11
+ return undefined;
12
+ const out = [];
13
+ const len = r.value.length;
14
+ if (typeof len !== 'number' || !Number.isSafeInteger(len) || len < 0)
15
+ return undefined;
16
+ for (let i = 0; i < len; i++) {
17
+ const item = r.value[i];
18
+ if (typeof item !== 'string')
19
+ return undefined;
20
+ out.push(item);
21
+ }
22
+ return out;
23
+ }
24
+ export function sessionMemorySyncStateOf(settled) {
25
+ try {
26
+ if (typeof settled !== 'object' || settled === null)
27
+ return null;
28
+ const reportSlot = slotReadOf(settled, 'report');
29
+ const errorSlot = slotReadOf(settled, 'error');
30
+ if (reportSlot.presence === 'present' && errorSlot.presence === 'absent') {
31
+ const report = reportSlot.value;
32
+ if (typeof report !== 'object' || report === null || Array.isArray(report))
33
+ return null;
34
+ const scopes = scopesOf(report);
35
+ if (scopes === undefined)
36
+ return null;
37
+ const refused = slotReadOf(report, 'refused');
38
+ const layers = slotReadOf(report, 'layers');
39
+ if (refused.presence === 'unreadable' || layers.presence === 'unreadable')
40
+ return null;
41
+ const refusedArm = refused.presence === 'present';
42
+ if (refusedArm === (layers.presence === 'present'))
43
+ return null;
44
+ if (refusedArm ? typeof refused.value !== 'object' || refused.value === null || Array.isArray(refused.value) : !Array.isArray(layers.value))
45
+ return null;
46
+ const kind = scopes.length === 0 && !refusedArm ? 'not_configured' : 'reported';
47
+ return Object.freeze({ kind, report: report });
48
+ }
49
+ if (errorSlot.presence === 'present' && reportSlot.presence === 'absent') {
50
+ const err = errorSlot.value;
51
+ if (!isSlotCarrier(err))
52
+ return null;
53
+ if (effectiveWireErrorCode(err) !== CONFLICT_SESSION_MEMORY_UNOBSERVED)
54
+ return null;
55
+ const o = err;
56
+ const reason = reasonOf(readErrorString(o, 'reason'));
57
+ const active = readErrorString(o, 'activeTaskId');
58
+ return Object.freeze({
59
+ kind: 'unobtainable',
60
+ ...(reason !== undefined ? { reason } : {}),
61
+ ...(typeof active === 'string' && active !== '' ? { activeTaskId: active } : {}),
62
+ });
63
+ }
64
+ return null;
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ const UNOBTAINABLE_KEYS = Object.freeze({
71
+ no_settled_run: 'session.memorySyncTrigger.noSettledRun',
72
+ run_result_missing: 'session.memorySyncTrigger.runUnobserved',
73
+ run_result_unobserved: 'session.memorySyncTrigger.runUnobserved',
74
+ memory_not_mounted: 'session.memorySyncTrigger.memoryNotMounted',
75
+ no_leaf: 'session.memorySyncTrigger.noLeaf',
76
+ });
77
+ export function sessionMemorySyncNotice(state, lang) {
78
+ try {
79
+ if (typeof state !== 'object' || state === null)
80
+ return undefined;
81
+ const kind = slotReadOf(state, 'kind');
82
+ if (kind.presence !== 'present')
83
+ return undefined;
84
+ if (kind.value === 'not_configured')
85
+ return renderMessage(msg('session.memorySyncTrigger.notConfigured'), isUiLanguage(lang) ? lang : undefined);
86
+ if (kind.value !== 'unobtainable')
87
+ return undefined;
88
+ const reason = slotReadOf(state, 'reason');
89
+ const word = reason.presence === 'present' && typeof reason.value === 'string' ? reasonOf(reason.value) : undefined;
90
+ return renderMessage(msg(word !== undefined ? UNOBTAINABLE_KEYS[word] : 'session.memorySyncTrigger.unobtainable'), isUiLanguage(lang) ? lang : undefined);
91
+ }
92
+ catch {
93
+ return undefined;
94
+ }
95
+ }