@opengeni/sdk 0.46.0 → 0.52.1
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.
- package/README.md +94 -2
- package/dist/artifact-client.d.ts +20 -2
- package/dist/artifacts.d.ts +4 -0
- package/dist/artifacts.js +11 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/chunk-FHI4DFIG.js +128 -0
- package/dist/chunk-FHI4DFIG.js.map +1 -0
- package/dist/chunk-FRJFNDIR.js +218 -0
- package/dist/chunk-FRJFNDIR.js.map +1 -0
- package/dist/{chunk-QKV5OCLJ.js → chunk-ILQZR5N6.js} +1380 -595
- package/dist/chunk-ILQZR5N6.js.map +1 -0
- package/dist/chunk-MZWTWOX6.js +661 -0
- package/dist/chunk-MZWTWOX6.js.map +1 -0
- package/dist/chunk-VWE2QIVO.js +1162 -0
- package/dist/chunk-VWE2QIVO.js.map +1 -0
- package/dist/chunk-VYCTL62C.js +230 -0
- package/dist/chunk-VYCTL62C.js.map +1 -0
- package/dist/client.d.ts +176 -20
- package/dist/codex-realtime-controller.d.ts +6 -6
- package/dist/codex-realtime-lifecycle.d.ts +1 -1
- package/dist/codex-realtime-v3.d.ts +3 -3
- package/dist/codex-realtime.d.ts +1 -1
- package/dist/company-profile.d.ts +100 -0
- package/dist/core.d.ts +5 -5
- package/dist/core.js +9 -4
- package/dist/desktop.d.ts +1 -1
- package/dist/editable-artifact-resources.d.ts +96 -0
- package/dist/editable-artifacts/browser-session.d.ts +35 -0
- package/dist/editable-artifacts/controller.d.ts +60 -0
- package/dist/editable-artifacts/errors.d.ts +23 -0
- package/dist/editable-artifacts/http-live-transport.d.ts +46 -0
- package/dist/editable-artifacts/index.d.ts +19 -0
- package/dist/editable-artifacts/pool.d.ts +15 -0
- package/dist/editable-artifacts/session.d.ts +96 -0
- package/dist/editable-artifacts/storage.d.ts +72 -0
- package/dist/editable-artifacts/types.d.ts +416 -0
- package/dist/editable-artifacts/worker/browser-client.d.ts +80 -0
- package/dist/editable-artifacts/worker/browser-entry.d.ts +6 -0
- package/dist/editable-artifacts/worker/kernel-adapter.d.ts +45 -0
- package/dist/editable-artifacts/worker/rpc-protocol.d.ts +92 -0
- package/dist/editable-artifacts/worker/runtime.d.ts +66 -0
- package/dist/editable-artifacts/worker/wire-codec.d.ts +52 -0
- package/dist/editable-artifacts-worker.d.ts +8 -0
- package/dist/editable-artifacts-worker.js +1708 -0
- package/dist/editable-artifacts-worker.js.map +1 -0
- package/dist/editable-artifacts.d.ts +6 -0
- package/dist/editable-artifacts.js +5954 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/gateway-realtime-transport.d.ts +1 -1
- package/dist/index.d.ts +41 -35
- package/dist/index.js +104 -68
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +885 -0
- package/dist/interaction.js +55 -0
- package/dist/interaction.js.map +1 -0
- package/dist/memory-slack-client.d.ts +13 -0
- package/dist/memory-slack-delivery.d.ts +99 -0
- package/dist/memory-slack.d.ts +4 -0
- package/dist/memory-slack.js +44 -0
- package/dist/memory-slack.js.map +1 -0
- package/dist/proxy.d.ts +3 -3
- package/dist/realtime.d.ts +12 -12
- package/dist/retained-artifacts.d.ts +12 -0
- package/dist/stream.d.ts +16 -1
- package/dist/terminal.d.ts +1 -1
- package/dist/transcription.d.ts +2 -2
- package/dist/types.d.ts +1203 -23
- package/dist/workspace-control-stream.d.ts +2 -2
- package/dist/workspace-state.d.ts +1 -1
- package/package.json +29 -3
- package/src/artifact-client.ts +129 -0
- package/src/artifacts.ts +17 -0
- package/src/client.ts +1801 -206
- package/src/company-profile.ts +92 -0
- package/src/editable-artifact-resources.ts +119 -0
- package/src/editable-artifacts/browser-session.ts +176 -0
- package/src/editable-artifacts/controller.ts +2632 -0
- package/src/editable-artifacts/errors.ts +52 -0
- package/src/editable-artifacts/http-live-transport.ts +1449 -0
- package/src/editable-artifacts/index.ts +175 -0
- package/src/editable-artifacts/pool.ts +81 -0
- package/src/editable-artifacts/session.ts +778 -0
- package/src/editable-artifacts/storage.ts +1718 -0
- package/src/editable-artifacts/types.ts +499 -0
- package/src/editable-artifacts/worker/browser-client.ts +1167 -0
- package/src/editable-artifacts/worker/browser-entry.ts +57 -0
- package/src/editable-artifacts/worker/kernel-adapter.ts +1039 -0
- package/src/editable-artifacts/worker/rpc-protocol.ts +622 -0
- package/src/editable-artifacts/worker/runtime.ts +1204 -0
- package/src/editable-artifacts/worker/wire-codec.ts +852 -0
- package/src/editable-artifacts-worker.ts +8 -0
- package/src/editable-artifacts.ts +6 -0
- package/src/index.ts +194 -0
- package/src/interaction.ts +1830 -0
- package/src/memory-slack-client.ts +71 -0
- package/src/memory-slack-delivery.ts +128 -0
- package/src/memory-slack.ts +19 -0
- package/src/retained-artifacts.ts +326 -0
- package/src/stream.ts +102 -4
- package/src/transcription.ts +2 -2
- package/src/types.ts +1430 -33
- package/src/workspace-control-stream.ts +15 -5
- package/dist/chunk-QKV5OCLJ.js.map +0 -1
|
@@ -0,0 +1,1708 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArtifactWorkerProtocolError,
|
|
3
|
+
decodeArtifactWorkerRpcMessage,
|
|
4
|
+
decodeAuthorPendingMetadata,
|
|
5
|
+
decodeCommittedMetadata,
|
|
6
|
+
decodeInitialize,
|
|
7
|
+
decodePendingListMetadata,
|
|
8
|
+
decodeReconcileMetadata,
|
|
9
|
+
decodeSnapshotMetadata,
|
|
10
|
+
encodeArtifactWorkerRpcMessage,
|
|
11
|
+
encodeErrorPayload,
|
|
12
|
+
encodePendingListMetadata,
|
|
13
|
+
encodeProjectionResponse,
|
|
14
|
+
encodeStateResponse,
|
|
15
|
+
requireMatchingHash,
|
|
16
|
+
sha256Hex,
|
|
17
|
+
transferListForArtifactWorkerRpcMessage
|
|
18
|
+
} from "./chunk-VWE2QIVO.js";
|
|
19
|
+
|
|
20
|
+
// src/editable-artifacts/worker/kernel-adapter.ts
|
|
21
|
+
import {
|
|
22
|
+
EDITABLE_ARTIFACT_COMMAND_MAX_BYTES,
|
|
23
|
+
EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
|
|
24
|
+
EDITABLE_ARTIFACT_INTENT_VERSION,
|
|
25
|
+
EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
|
|
26
|
+
SPREADSHEET_ARTIFACT_COMMAND_VERSION
|
|
27
|
+
} from "@opengeni/contracts/editable-artifacts";
|
|
28
|
+
import { MAX_COMMITTED_TRANSACTION_BYTES } from "@opengeni/contracts/editable-artifact-committed-transaction";
|
|
29
|
+
var ArtifactWorkerPendingProjectionError = class extends Error {
|
|
30
|
+
code;
|
|
31
|
+
constructor(code, message = "pending transaction cannot project over confirmed state") {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "ArtifactWorkerPendingProjectionError";
|
|
34
|
+
this.code = requireErrorCode(code, "pending projection code");
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var MAX_BUILD_IDENTITY_BYTES = 512;
|
|
38
|
+
var MAX_CAPABILITIES_BYTES = 8 * 1024;
|
|
39
|
+
var MAX_FRONTIER_BYTES = 8 + 2 + 2 + 4 + 1024 * 16 + 8;
|
|
40
|
+
var WASM_MAX_CELLS_PER_BATCH = 5e5;
|
|
41
|
+
var WASM_MAX_COMMAND_BYTES = 8 * 1024 * 1024;
|
|
42
|
+
var WASM_MAX_SNAPSHOT_BYTES = EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES;
|
|
43
|
+
var SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/u;
|
|
44
|
+
var loadBrowserWasmKernelAdapter = async (input) => {
|
|
45
|
+
const rawWasm = await import(
|
|
46
|
+
/* @vite-ignore */
|
|
47
|
+
input.wasmGlueUrl
|
|
48
|
+
);
|
|
49
|
+
const wasm = validateWasmModule(rawWasm, input.modality);
|
|
50
|
+
if (wasm.default) await wasm.default({ module_or_path: input.wasmBinaryUrl });
|
|
51
|
+
const buildIdentity = decodeUtf8(
|
|
52
|
+
requireBoundedBytes(wasm.buildIdentity(), "WASM build identity", MAX_BUILD_IDENTITY_BYTES),
|
|
53
|
+
"WASM build identity"
|
|
54
|
+
);
|
|
55
|
+
if (buildIdentity.length === 0) {
|
|
56
|
+
throw adapterError("incompatible_wasm_build", "WASM build identity is empty");
|
|
57
|
+
}
|
|
58
|
+
const capabilities = decodeCapabilities(
|
|
59
|
+
requireBoundedBytes(wasm.capabilities(), "WASM capabilities", MAX_CAPABILITIES_BYTES)
|
|
60
|
+
);
|
|
61
|
+
assertRequestedLimits(input, capabilities);
|
|
62
|
+
const profile = capabilityProfile(input.modality, capabilities);
|
|
63
|
+
const adapter = {
|
|
64
|
+
modality: input.modality,
|
|
65
|
+
protocolVersion: capabilities.editableArtifactIntentVersion,
|
|
66
|
+
kernelVersion: buildIdentity,
|
|
67
|
+
modelSchemaVersion: profile.modelSchemaVersion,
|
|
68
|
+
commandVersion: profile.commandVersion,
|
|
69
|
+
maximumSnapshotBytes: profile.maximumSnapshotBytes,
|
|
70
|
+
maximumCommandBytes: profile.maximumCommandBytes,
|
|
71
|
+
maximumIntentBytes: capabilities.maxIntentBytes,
|
|
72
|
+
maximumCommittedTransactionBytes: capabilities.maxCommittedTransactionBytes,
|
|
73
|
+
maximumQueryBytes: profile.maximumQueryBytes,
|
|
74
|
+
maximumQueryResponseBytes: profile.maximumQueryResponseBytes,
|
|
75
|
+
canonicalizeSnapshot(snapshotBytes) {
|
|
76
|
+
return requireOwnedOutput(
|
|
77
|
+
wasm.canonicalizeSnapshot(snapshotBytes),
|
|
78
|
+
`canonical ${input.modality} snapshot`,
|
|
79
|
+
input.maximumSnapshotBytes
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
open(snapshotBytes) {
|
|
83
|
+
if (wasm.modality === "spreadsheet") {
|
|
84
|
+
return wrapWasmCollaborationSession(wasm.open(snapshotBytes), input);
|
|
85
|
+
}
|
|
86
|
+
return wrapWasmModalitySession(wasm.open(snapshotBytes), input);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
return Object.freeze(adapter);
|
|
90
|
+
};
|
|
91
|
+
function wrapWasmCollaborationSession(session, limits) {
|
|
92
|
+
requireSessionAbi(session);
|
|
93
|
+
let disposed = false;
|
|
94
|
+
const requireOpen = () => {
|
|
95
|
+
if (disposed || session.isClosed?.() === true) {
|
|
96
|
+
throw adapterError("session_closed", "artifact collaboration session is closed");
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
modality: "spreadsheet",
|
|
101
|
+
applyCommitted(committedTransactionBytes) {
|
|
102
|
+
requireOpen();
|
|
103
|
+
requireInputBytes(
|
|
104
|
+
committedTransactionBytes,
|
|
105
|
+
"committed transaction",
|
|
106
|
+
limits.maximumCommittedTransactionBytes
|
|
107
|
+
);
|
|
108
|
+
session.applyCommitted(committedTransactionBytes);
|
|
109
|
+
},
|
|
110
|
+
applyPending(intentBytes) {
|
|
111
|
+
requireOpen();
|
|
112
|
+
requireInputBytes(intentBytes, "pending intent", limits.maximumIntentBytes);
|
|
113
|
+
const frontier = requireOwnedOutput(
|
|
114
|
+
session.frontier(),
|
|
115
|
+
"causal frontier",
|
|
116
|
+
MAX_FRONTIER_BYTES
|
|
117
|
+
);
|
|
118
|
+
try {
|
|
119
|
+
requireOwnedOutput(
|
|
120
|
+
session.authorTransaction(intentBytes, frontier),
|
|
121
|
+
"speculative committed transaction",
|
|
122
|
+
limits.maximumCommittedTransactionBytes
|
|
123
|
+
);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
const projectionCode = classifyProjectionFailure(error);
|
|
126
|
+
if (projectionCode !== null) {
|
|
127
|
+
throw new ArtifactWorkerPendingProjectionError(projectionCode);
|
|
128
|
+
}
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
applyCommands() {
|
|
133
|
+
throw adapterError(
|
|
134
|
+
"unsupported_modality",
|
|
135
|
+
"spreadsheet session does not apply serialized commands"
|
|
136
|
+
);
|
|
137
|
+
},
|
|
138
|
+
nativeRevision() {
|
|
139
|
+
throw adapterError("unsupported_modality", "spreadsheet session has no native revision");
|
|
140
|
+
},
|
|
141
|
+
fork() {
|
|
142
|
+
requireOpen();
|
|
143
|
+
return wrapWasmCollaborationSession(session.fork(), limits);
|
|
144
|
+
},
|
|
145
|
+
async stateHash() {
|
|
146
|
+
requireOpen();
|
|
147
|
+
const result = session.stateHash();
|
|
148
|
+
if (!SHA256_PATTERN.test(result)) {
|
|
149
|
+
throw adapterError("invalid_state_hash", "WASM kernel returned an invalid state hash");
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
},
|
|
153
|
+
snapshot() {
|
|
154
|
+
requireOpen();
|
|
155
|
+
return requireOwnedOutput(
|
|
156
|
+
session.snapshot(),
|
|
157
|
+
"collaboration snapshot",
|
|
158
|
+
limits.maximumSnapshotBytes
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
query(queryBytes) {
|
|
162
|
+
requireOpen();
|
|
163
|
+
requireInputBytes(queryBytes, "kernel query", limits.maximumQueryBytes);
|
|
164
|
+
return requireOwnedOutput(
|
|
165
|
+
session.query(queryBytes),
|
|
166
|
+
"kernel projection",
|
|
167
|
+
limits.maximumQueryResponseBytes
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
dispose() {
|
|
171
|
+
if (disposed) return;
|
|
172
|
+
disposed = true;
|
|
173
|
+
try {
|
|
174
|
+
session.dispose?.();
|
|
175
|
+
} finally {
|
|
176
|
+
try {
|
|
177
|
+
session.close?.();
|
|
178
|
+
} finally {
|
|
179
|
+
session.free?.();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function wrapWasmModalitySession(session, limits) {
|
|
186
|
+
requireModalitySessionAbi(session);
|
|
187
|
+
let disposed = false;
|
|
188
|
+
const requireOpen = () => {
|
|
189
|
+
if (disposed || session.isClosed?.() === true) {
|
|
190
|
+
throw adapterError("session_closed", `artifact ${limits.modality} session is closed`);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
return {
|
|
194
|
+
modality: limits.modality,
|
|
195
|
+
applyCommitted() {
|
|
196
|
+
throw adapterError(
|
|
197
|
+
"unsupported_modality",
|
|
198
|
+
"serialized session requires decoded OGAST replay"
|
|
199
|
+
);
|
|
200
|
+
},
|
|
201
|
+
applyPending() {
|
|
202
|
+
throw adapterError("unsupported_modality", "serialized session requires exact command bytes");
|
|
203
|
+
},
|
|
204
|
+
applyCommands(commandBytes) {
|
|
205
|
+
requireOpen();
|
|
206
|
+
requireInputBytes(commandBytes, `${limits.modality} command`, limits.maximumCommandBytes);
|
|
207
|
+
return requireOwnedOutput(
|
|
208
|
+
session.applyCommands(commandBytes),
|
|
209
|
+
`${limits.modality} native receipt`,
|
|
210
|
+
limits.maximumCommittedTransactionBytes
|
|
211
|
+
);
|
|
212
|
+
},
|
|
213
|
+
nativeRevision() {
|
|
214
|
+
requireOpen();
|
|
215
|
+
const revision = session.revision();
|
|
216
|
+
if (revision < 0n || revision > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
217
|
+
throw adapterError("invalid_revision", "WASM kernel returned an unsafe native revision");
|
|
218
|
+
}
|
|
219
|
+
return Number(revision);
|
|
220
|
+
},
|
|
221
|
+
fork() {
|
|
222
|
+
requireOpen();
|
|
223
|
+
return wrapWasmModalitySession(session.fork(), limits);
|
|
224
|
+
},
|
|
225
|
+
async stateHash() {
|
|
226
|
+
requireOpen();
|
|
227
|
+
const result = session.stateHash();
|
|
228
|
+
if (!SHA256_PATTERN.test(result)) {
|
|
229
|
+
throw adapterError("invalid_state_hash", "WASM kernel returned an invalid state hash");
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
},
|
|
233
|
+
snapshot() {
|
|
234
|
+
requireOpen();
|
|
235
|
+
return requireOwnedOutput(
|
|
236
|
+
session.snapshot(),
|
|
237
|
+
`${limits.modality} snapshot`,
|
|
238
|
+
limits.maximumSnapshotBytes
|
|
239
|
+
);
|
|
240
|
+
},
|
|
241
|
+
query(queryBytes) {
|
|
242
|
+
requireOpen();
|
|
243
|
+
requireInputBytes(queryBytes, `${limits.modality} query`, limits.maximumQueryBytes);
|
|
244
|
+
return requireOwnedOutput(
|
|
245
|
+
session.query(queryBytes),
|
|
246
|
+
`${limits.modality} projection`,
|
|
247
|
+
limits.maximumQueryResponseBytes
|
|
248
|
+
);
|
|
249
|
+
},
|
|
250
|
+
dispose() {
|
|
251
|
+
if (disposed) return;
|
|
252
|
+
disposed = true;
|
|
253
|
+
try {
|
|
254
|
+
session.dispose?.();
|
|
255
|
+
} finally {
|
|
256
|
+
try {
|
|
257
|
+
session.close?.();
|
|
258
|
+
} finally {
|
|
259
|
+
session.free?.();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function requireModalitySessionAbi(session) {
|
|
266
|
+
if (!isObject(session) || typeof session.applyCommands !== "function" || typeof session.snapshot !== "function" || typeof session.revision !== "function" || typeof session.fork !== "function" || typeof session.stateHash !== "function" || typeof session.query !== "function") {
|
|
267
|
+
session?.dispose?.();
|
|
268
|
+
session?.close?.();
|
|
269
|
+
session?.free?.();
|
|
270
|
+
throw adapterError(
|
|
271
|
+
"incompatible_wasm_build",
|
|
272
|
+
"WASM modality session lacks the required stateful ABI"
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function requireSessionAbi(session) {
|
|
277
|
+
if (!isObject(session) || typeof session.authorTransaction !== "function" || typeof session.applyCommitted !== "function" || typeof session.snapshot !== "function" || typeof session.frontier !== "function" || typeof session.fork !== "function" || typeof session.stateHash !== "function" || typeof session.revision !== "function" || typeof session.query !== "function") {
|
|
278
|
+
session?.dispose?.();
|
|
279
|
+
session?.close?.();
|
|
280
|
+
session?.free?.();
|
|
281
|
+
throw adapterError(
|
|
282
|
+
"incompatible_wasm_build",
|
|
283
|
+
"WASM collaboration session lacks the required stateful ABI"
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function validateWasmModule(input, modality) {
|
|
288
|
+
if (!isObject(input)) throw adapterError("invalid_wasm_module", "WASM glue module is invalid");
|
|
289
|
+
const capabilities = callable(input, "capabilities");
|
|
290
|
+
const buildIdentity = callable(input, "buildIdentity");
|
|
291
|
+
const initializer = optionalCallable(input, "default");
|
|
292
|
+
const base = {
|
|
293
|
+
capabilities,
|
|
294
|
+
buildIdentity,
|
|
295
|
+
...initializer ? { default: initializer } : {}
|
|
296
|
+
};
|
|
297
|
+
const canonicalizeName = modality === "spreadsheet" ? "canonicalizeCollaborationSnapshot" : modality === "document" ? "canonicalizeDocumentSnapshot" : "canonicalizePresentationSnapshot";
|
|
298
|
+
const sessionName = modality === "spreadsheet" ? "ArtifactCollaborationSession" : modality === "document" ? "ArtifactDocumentSession" : "ArtifactPresentationSession";
|
|
299
|
+
const canonicalizeSnapshot = callable(input, canonicalizeName);
|
|
300
|
+
const sessionClass = data(input, sessionName);
|
|
301
|
+
if (!isObject(sessionClass)) {
|
|
302
|
+
throw adapterError("invalid_wasm_module", `WASM glue has no ${sessionName} export`);
|
|
303
|
+
}
|
|
304
|
+
const open = callable(sessionClass, "open");
|
|
305
|
+
if (modality === "spreadsheet") {
|
|
306
|
+
return {
|
|
307
|
+
...base,
|
|
308
|
+
modality,
|
|
309
|
+
canonicalizeSnapshot,
|
|
310
|
+
open
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
...base,
|
|
315
|
+
modality,
|
|
316
|
+
canonicalizeSnapshot,
|
|
317
|
+
open
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function decodeCapabilities(bytes) {
|
|
321
|
+
let value;
|
|
322
|
+
try {
|
|
323
|
+
const text = decodeUtf8(bytes, "WASM capabilities");
|
|
324
|
+
value = JSON.parse(text);
|
|
325
|
+
if (JSON.stringify(value) !== text) {
|
|
326
|
+
throw new TypeError("capabilities JSON is not canonical");
|
|
327
|
+
}
|
|
328
|
+
} catch (error) {
|
|
329
|
+
throw new ArtifactWorkerProtocolError(
|
|
330
|
+
"incompatible_wasm_build",
|
|
331
|
+
"WASM capabilities are not canonical UTF-8 JSON",
|
|
332
|
+
{ cause: error }
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
if (!isPlainRecord(value)) {
|
|
336
|
+
throw adapterError("incompatible_wasm_build", "WASM capabilities must be a plain record");
|
|
337
|
+
}
|
|
338
|
+
const expectedKeys = [
|
|
339
|
+
"abiVersion",
|
|
340
|
+
"buildIdentityFormat",
|
|
341
|
+
"canonicalStateHash",
|
|
342
|
+
"collaboration",
|
|
343
|
+
"collaborationSnapshotVersion",
|
|
344
|
+
"commandSchemaVersion",
|
|
345
|
+
"committedTransactionVersion",
|
|
346
|
+
"document",
|
|
347
|
+
"documentCommandVersion",
|
|
348
|
+
"documentQueryResponseVersion",
|
|
349
|
+
"documentQueryVersion",
|
|
350
|
+
"documentReceiptVersion",
|
|
351
|
+
"documentSnapshotVersion",
|
|
352
|
+
"documentStatefulSessions",
|
|
353
|
+
"editableArtifactIntentVersion",
|
|
354
|
+
"kernelSnapshotVersion",
|
|
355
|
+
"maxCellsPerBatch",
|
|
356
|
+
"maxCommandBytes",
|
|
357
|
+
"maxCommands",
|
|
358
|
+
"maxCommittedTransactionBytes",
|
|
359
|
+
"maxDocumentCommandBytes",
|
|
360
|
+
"maxDocumentCommands",
|
|
361
|
+
"maxDocumentQueryBytes",
|
|
362
|
+
"maxDocumentQueryResponseBytes",
|
|
363
|
+
"maxDocumentSnapshotBytes",
|
|
364
|
+
"maxIntentBytes",
|
|
365
|
+
"maxMetadataScannedCells",
|
|
366
|
+
"maxMetadataSheets",
|
|
367
|
+
"maxPresentationCommandBytes",
|
|
368
|
+
"maxPresentationQueryBytes",
|
|
369
|
+
"maxPresentationResponseBytes",
|
|
370
|
+
"maxPresentationSnapshotBytes",
|
|
371
|
+
"maxQueryBytes",
|
|
372
|
+
"maxQueryResponseBytes",
|
|
373
|
+
"maxSnapshotBytes",
|
|
374
|
+
"maxSpreadsheetCommandBytes",
|
|
375
|
+
"maxTextLayoutFontBundleBytes",
|
|
376
|
+
"maxTextLayoutRequestBytes",
|
|
377
|
+
"maxTextLayoutResponseBytes",
|
|
378
|
+
"maxViewportArea",
|
|
379
|
+
"maxViewportCells",
|
|
380
|
+
"presentation",
|
|
381
|
+
"presentationCommandVersion",
|
|
382
|
+
"presentationQueryResponseVersion",
|
|
383
|
+
"presentationQueryVersion",
|
|
384
|
+
"presentationSnapshotVersion",
|
|
385
|
+
"presentationStatefulSessions",
|
|
386
|
+
"queryResponseVersion",
|
|
387
|
+
"queryVersion",
|
|
388
|
+
"receiptSchemaVersion",
|
|
389
|
+
"retainedRenderPatchVersion",
|
|
390
|
+
"retainedRenderTileVersion",
|
|
391
|
+
"safeRust",
|
|
392
|
+
"sessionForks",
|
|
393
|
+
"spreadsheetCommandVersion",
|
|
394
|
+
"statefulSessions",
|
|
395
|
+
"textLayout",
|
|
396
|
+
"textLayoutFontBundleVersion",
|
|
397
|
+
"textLayoutRequestVersion",
|
|
398
|
+
"textLayoutResponseVersion",
|
|
399
|
+
"textLayoutStatefulSessions",
|
|
400
|
+
"transport",
|
|
401
|
+
"workbookMetadataQueries"
|
|
402
|
+
];
|
|
403
|
+
const keys = Object.keys(value).sort();
|
|
404
|
+
if (keys.length !== expectedKeys.length || keys.some((key, index) => key !== expectedKeys[index])) {
|
|
405
|
+
throw adapterError(
|
|
406
|
+
"incompatible_wasm_build",
|
|
407
|
+
"WASM collaboration capabilities have unknown or missing fields"
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
const normalized = {
|
|
411
|
+
abiVersion: positiveInteger(value.abiVersion, "abiVersion"),
|
|
412
|
+
buildIdentityFormat: value.buildIdentityFormat,
|
|
413
|
+
canonicalStateHash: value.canonicalStateHash,
|
|
414
|
+
collaboration: strictBoolean(value.collaboration, "collaboration"),
|
|
415
|
+
collaborationSnapshotVersion: positiveInteger(
|
|
416
|
+
value.collaborationSnapshotVersion,
|
|
417
|
+
"collaborationSnapshotVersion"
|
|
418
|
+
),
|
|
419
|
+
commandSchemaVersion: positiveInteger(value.commandSchemaVersion, "commandSchemaVersion"),
|
|
420
|
+
committedTransactionVersion: positiveInteger(
|
|
421
|
+
value.committedTransactionVersion,
|
|
422
|
+
"committedTransactionVersion"
|
|
423
|
+
),
|
|
424
|
+
document: strictBoolean(value.document, "document"),
|
|
425
|
+
documentCommandVersion: positiveInteger(value.documentCommandVersion, "documentCommandVersion"),
|
|
426
|
+
documentQueryResponseVersion: positiveInteger(
|
|
427
|
+
value.documentQueryResponseVersion,
|
|
428
|
+
"documentQueryResponseVersion"
|
|
429
|
+
),
|
|
430
|
+
documentQueryVersion: positiveInteger(value.documentQueryVersion, "documentQueryVersion"),
|
|
431
|
+
documentReceiptVersion: positiveInteger(value.documentReceiptVersion, "documentReceiptVersion"),
|
|
432
|
+
documentSnapshotVersion: positiveInteger(
|
|
433
|
+
value.documentSnapshotVersion,
|
|
434
|
+
"documentSnapshotVersion"
|
|
435
|
+
),
|
|
436
|
+
documentStatefulSessions: strictBoolean(
|
|
437
|
+
value.documentStatefulSessions,
|
|
438
|
+
"documentStatefulSessions"
|
|
439
|
+
),
|
|
440
|
+
editableArtifactIntentVersion: positiveInteger(
|
|
441
|
+
value.editableArtifactIntentVersion,
|
|
442
|
+
"editableArtifactIntentVersion"
|
|
443
|
+
),
|
|
444
|
+
kernelSnapshotVersion: positiveInteger(value.kernelSnapshotVersion, "kernelSnapshotVersion"),
|
|
445
|
+
spreadsheetCommandVersion: positiveInteger(
|
|
446
|
+
value.spreadsheetCommandVersion,
|
|
447
|
+
"spreadsheetCommandVersion"
|
|
448
|
+
),
|
|
449
|
+
maxCellsPerBatch: positiveInteger(value.maxCellsPerBatch, "maxCellsPerBatch"),
|
|
450
|
+
maxCommands: positiveInteger(value.maxCommands, "maxCommands"),
|
|
451
|
+
maxSnapshotBytes: positiveInteger(value.maxSnapshotBytes, "maxSnapshotBytes"),
|
|
452
|
+
maxCommandBytes: positiveInteger(value.maxCommandBytes, "maxCommandBytes"),
|
|
453
|
+
maxSpreadsheetCommandBytes: positiveInteger(
|
|
454
|
+
value.maxSpreadsheetCommandBytes,
|
|
455
|
+
"maxSpreadsheetCommandBytes"
|
|
456
|
+
),
|
|
457
|
+
maxIntentBytes: positiveInteger(value.maxIntentBytes, "maxIntentBytes"),
|
|
458
|
+
maxCommittedTransactionBytes: positiveInteger(
|
|
459
|
+
value.maxCommittedTransactionBytes,
|
|
460
|
+
"maxCommittedTransactionBytes"
|
|
461
|
+
),
|
|
462
|
+
maxDocumentCommandBytes: positiveInteger(
|
|
463
|
+
value.maxDocumentCommandBytes,
|
|
464
|
+
"maxDocumentCommandBytes"
|
|
465
|
+
),
|
|
466
|
+
maxDocumentCommands: positiveInteger(value.maxDocumentCommands, "maxDocumentCommands"),
|
|
467
|
+
maxDocumentQueryBytes: positiveInteger(value.maxDocumentQueryBytes, "maxDocumentQueryBytes"),
|
|
468
|
+
maxDocumentQueryResponseBytes: positiveInteger(
|
|
469
|
+
value.maxDocumentQueryResponseBytes,
|
|
470
|
+
"maxDocumentQueryResponseBytes"
|
|
471
|
+
),
|
|
472
|
+
maxDocumentSnapshotBytes: positiveInteger(
|
|
473
|
+
value.maxDocumentSnapshotBytes,
|
|
474
|
+
"maxDocumentSnapshotBytes"
|
|
475
|
+
),
|
|
476
|
+
maxMetadataScannedCells: positiveInteger(
|
|
477
|
+
value.maxMetadataScannedCells,
|
|
478
|
+
"maxMetadataScannedCells"
|
|
479
|
+
),
|
|
480
|
+
maxQueryBytes: positiveInteger(value.maxQueryBytes, "maxQueryBytes"),
|
|
481
|
+
maxQueryResponseBytes: positiveInteger(value.maxQueryResponseBytes, "maxQueryResponseBytes"),
|
|
482
|
+
maxViewportArea: positiveInteger(value.maxViewportArea, "maxViewportArea"),
|
|
483
|
+
maxViewportCells: positiveInteger(value.maxViewportCells, "maxViewportCells"),
|
|
484
|
+
maxMetadataSheets: positiveInteger(value.maxMetadataSheets, "maxMetadataSheets"),
|
|
485
|
+
maxPresentationCommandBytes: positiveInteger(
|
|
486
|
+
value.maxPresentationCommandBytes,
|
|
487
|
+
"maxPresentationCommandBytes"
|
|
488
|
+
),
|
|
489
|
+
maxPresentationQueryBytes: positiveInteger(
|
|
490
|
+
value.maxPresentationQueryBytes,
|
|
491
|
+
"maxPresentationQueryBytes"
|
|
492
|
+
),
|
|
493
|
+
maxPresentationResponseBytes: positiveInteger(
|
|
494
|
+
value.maxPresentationResponseBytes,
|
|
495
|
+
"maxPresentationResponseBytes"
|
|
496
|
+
),
|
|
497
|
+
maxPresentationSnapshotBytes: positiveInteger(
|
|
498
|
+
value.maxPresentationSnapshotBytes,
|
|
499
|
+
"maxPresentationSnapshotBytes"
|
|
500
|
+
),
|
|
501
|
+
maxTextLayoutFontBundleBytes: positiveInteger(
|
|
502
|
+
value.maxTextLayoutFontBundleBytes,
|
|
503
|
+
"maxTextLayoutFontBundleBytes"
|
|
504
|
+
),
|
|
505
|
+
maxTextLayoutRequestBytes: positiveInteger(
|
|
506
|
+
value.maxTextLayoutRequestBytes,
|
|
507
|
+
"maxTextLayoutRequestBytes"
|
|
508
|
+
),
|
|
509
|
+
maxTextLayoutResponseBytes: positiveInteger(
|
|
510
|
+
value.maxTextLayoutResponseBytes,
|
|
511
|
+
"maxTextLayoutResponseBytes"
|
|
512
|
+
),
|
|
513
|
+
queryResponseVersion: positiveInteger(value.queryResponseVersion, "queryResponseVersion"),
|
|
514
|
+
queryVersion: positiveInteger(value.queryVersion, "queryVersion"),
|
|
515
|
+
receiptSchemaVersion: positiveInteger(value.receiptSchemaVersion, "receiptSchemaVersion"),
|
|
516
|
+
retainedRenderPatchVersion: positiveInteger(
|
|
517
|
+
value.retainedRenderPatchVersion,
|
|
518
|
+
"retainedRenderPatchVersion"
|
|
519
|
+
),
|
|
520
|
+
retainedRenderTileVersion: positiveInteger(
|
|
521
|
+
value.retainedRenderTileVersion,
|
|
522
|
+
"retainedRenderTileVersion"
|
|
523
|
+
),
|
|
524
|
+
presentation: strictBoolean(value.presentation, "presentation"),
|
|
525
|
+
presentationCommandVersion: positiveInteger(
|
|
526
|
+
value.presentationCommandVersion,
|
|
527
|
+
"presentationCommandVersion"
|
|
528
|
+
),
|
|
529
|
+
presentationQueryResponseVersion: positiveInteger(
|
|
530
|
+
value.presentationQueryResponseVersion,
|
|
531
|
+
"presentationQueryResponseVersion"
|
|
532
|
+
),
|
|
533
|
+
presentationQueryVersion: positiveInteger(
|
|
534
|
+
value.presentationQueryVersion,
|
|
535
|
+
"presentationQueryVersion"
|
|
536
|
+
),
|
|
537
|
+
presentationSnapshotVersion: positiveInteger(
|
|
538
|
+
value.presentationSnapshotVersion,
|
|
539
|
+
"presentationSnapshotVersion"
|
|
540
|
+
),
|
|
541
|
+
presentationStatefulSessions: strictBoolean(
|
|
542
|
+
value.presentationStatefulSessions,
|
|
543
|
+
"presentationStatefulSessions"
|
|
544
|
+
),
|
|
545
|
+
safeRust: strictBoolean(value.safeRust, "safeRust"),
|
|
546
|
+
statefulSessions: strictBoolean(value.statefulSessions, "statefulSessions"),
|
|
547
|
+
textLayout: strictBoolean(value.textLayout, "textLayout"),
|
|
548
|
+
textLayoutFontBundleVersion: positiveInteger(
|
|
549
|
+
value.textLayoutFontBundleVersion,
|
|
550
|
+
"textLayoutFontBundleVersion"
|
|
551
|
+
),
|
|
552
|
+
textLayoutRequestVersion: positiveInteger(
|
|
553
|
+
value.textLayoutRequestVersion,
|
|
554
|
+
"textLayoutRequestVersion"
|
|
555
|
+
),
|
|
556
|
+
textLayoutResponseVersion: positiveInteger(
|
|
557
|
+
value.textLayoutResponseVersion,
|
|
558
|
+
"textLayoutResponseVersion"
|
|
559
|
+
),
|
|
560
|
+
textLayoutStatefulSessions: strictBoolean(
|
|
561
|
+
value.textLayoutStatefulSessions,
|
|
562
|
+
"textLayoutStatefulSessions"
|
|
563
|
+
),
|
|
564
|
+
sessionForks: strictBoolean(value.sessionForks, "sessionForks"),
|
|
565
|
+
transport: value.transport,
|
|
566
|
+
workbookMetadataQueries: strictBoolean(
|
|
567
|
+
value.workbookMetadataQueries,
|
|
568
|
+
"workbookMetadataQueries"
|
|
569
|
+
)
|
|
570
|
+
};
|
|
571
|
+
if (normalized.abiVersion !== 1 || normalized.buildIdentityFormat !== "utf8" || normalized.canonicalStateHash !== "sha256:canonical-snapshot" || normalized.editableArtifactIntentVersion !== EDITABLE_ARTIFACT_INTENT_VERSION || normalized.spreadsheetCommandVersion !== SPREADSHEET_ARTIFACT_COMMAND_VERSION || normalized.collaborationSnapshotVersion !== 1 || normalized.commandSchemaVersion !== 1 || normalized.committedTransactionVersion !== 1 || normalized.documentCommandVersion !== 1 || normalized.documentQueryResponseVersion !== 1 || normalized.documentQueryVersion !== 1 || normalized.documentReceiptVersion !== 1 || normalized.documentSnapshotVersion !== 1 || normalized.documentStatefulSessions !== normalized.document || normalized.kernelSnapshotVersion !== 1 || normalized.maxCellsPerBatch !== WASM_MAX_CELLS_PER_BATCH || normalized.maxCommandBytes !== WASM_MAX_COMMAND_BYTES || normalized.maxSpreadsheetCommandBytes !== EDITABLE_ARTIFACT_COMMAND_MAX_BYTES || normalized.maxCommands !== 1e4 || normalized.maxIntentBytes !== EDITABLE_ARTIFACT_INTENT_MAX_BYTES || normalized.maxCommittedTransactionBytes !== MAX_COMMITTED_TRANSACTION_BYTES || normalized.maxDocumentCommandBytes !== WASM_MAX_COMMAND_BYTES || normalized.maxDocumentCommands !== 4096 || normalized.maxDocumentQueryBytes !== 256 || normalized.maxDocumentQueryResponseBytes !== 8 * 1024 * 1024 || normalized.maxDocumentSnapshotBytes !== WASM_MAX_SNAPSHOT_BYTES || normalized.maxMetadataScannedCells !== 4e6 || normalized.maxQueryBytes !== 68 || normalized.maxQueryResponseBytes !== 8 * 1024 * 1024 || normalized.maxViewportArea !== 1048576 || normalized.maxViewportCells !== 262144 || normalized.maxMetadataSheets !== 1e4 || normalized.maxPresentationCommandBytes !== 4 * 1024 * 1024 || normalized.maxPresentationQueryBytes !== 96 || normalized.maxPresentationResponseBytes !== 8 * 1024 * 1024 || normalized.maxPresentationSnapshotBytes !== WASM_MAX_SNAPSHOT_BYTES || normalized.maxTextLayoutFontBundleBytes !== 48 * 1024 * 1024 || normalized.maxTextLayoutRequestBytes !== 4 * 1024 * 1024 || normalized.maxTextLayoutResponseBytes !== 32 * 1024 * 1024 || normalized.maxSnapshotBytes !== WASM_MAX_SNAPSHOT_BYTES || normalized.queryResponseVersion !== 1 || normalized.queryVersion !== 1 || normalized.receiptSchemaVersion !== 1 || normalized.retainedRenderPatchVersion !== 1 || normalized.retainedRenderTileVersion !== 1 || normalized.presentationCommandVersion !== 1 || normalized.presentationQueryResponseVersion !== 1 || normalized.presentationQueryVersion !== 1 || normalized.presentationSnapshotVersion !== 1 || normalized.presentationStatefulSessions !== normalized.presentation || normalized.safeRust !== true || normalized.statefulSessions !== true || normalized.textLayoutFontBundleVersion !== 1 || normalized.textLayoutRequestVersion !== 1 || normalized.textLayoutResponseVersion !== 1 || normalized.textLayoutStatefulSessions !== normalized.textLayout || normalized.sessionForks !== true || normalized.transport !== "bounded-uint8array" || normalized.workbookMetadataQueries !== normalized.collaboration) {
|
|
572
|
+
throw adapterError(
|
|
573
|
+
"incompatible_wasm_build",
|
|
574
|
+
"WASM capabilities do not match the SDK protocol"
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return Object.freeze(normalized);
|
|
578
|
+
}
|
|
579
|
+
function assertRequestedLimits(input, capabilities) {
|
|
580
|
+
const profile = capabilityProfile(input.modality, capabilities);
|
|
581
|
+
const pairs = [
|
|
582
|
+
["maximumSnapshotBytes", input.maximumSnapshotBytes, profile.maximumSnapshotBytes],
|
|
583
|
+
["maximumCommandBytes", input.maximumCommandBytes, profile.maximumCommandBytes],
|
|
584
|
+
["maximumIntentBytes", input.maximumIntentBytes, capabilities.maxIntentBytes],
|
|
585
|
+
[
|
|
586
|
+
"maximumCommittedTransactionBytes",
|
|
587
|
+
input.maximumCommittedTransactionBytes,
|
|
588
|
+
capabilities.maxCommittedTransactionBytes
|
|
589
|
+
],
|
|
590
|
+
["maximumQueryBytes", input.maximumQueryBytes, profile.maximumQueryBytes],
|
|
591
|
+
[
|
|
592
|
+
"maximumQueryResponseBytes",
|
|
593
|
+
input.maximumQueryResponseBytes,
|
|
594
|
+
profile.maximumQueryResponseBytes
|
|
595
|
+
]
|
|
596
|
+
];
|
|
597
|
+
for (const [label, requested, advertised] of pairs) {
|
|
598
|
+
if (requested > advertised) {
|
|
599
|
+
throw adapterError(
|
|
600
|
+
"incompatible_wasm_build",
|
|
601
|
+
`${label} exceeds the loaded WASM build capability`
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
function capabilityProfile(modality, capabilities) {
|
|
607
|
+
if (modality === "spreadsheet") {
|
|
608
|
+
if (!capabilities.collaboration || !capabilities.workbookMetadataQueries) {
|
|
609
|
+
throw adapterError(
|
|
610
|
+
"incompatible_wasm_build",
|
|
611
|
+
"WASM module does not provide the spreadsheet collaboration ABI"
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
modelSchemaVersion: capabilities.collaborationSnapshotVersion,
|
|
616
|
+
commandVersion: capabilities.spreadsheetCommandVersion,
|
|
617
|
+
maximumSnapshotBytes: capabilities.maxSnapshotBytes,
|
|
618
|
+
maximumCommandBytes: capabilities.maxSpreadsheetCommandBytes,
|
|
619
|
+
maximumQueryBytes: capabilities.maxQueryBytes,
|
|
620
|
+
maximumQueryResponseBytes: capabilities.maxQueryResponseBytes
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
if (modality === "document") {
|
|
624
|
+
if (!capabilities.document || !capabilities.documentStatefulSessions) {
|
|
625
|
+
throw adapterError(
|
|
626
|
+
"incompatible_wasm_build",
|
|
627
|
+
"WASM module does not provide the document ABI"
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
return {
|
|
631
|
+
modelSchemaVersion: capabilities.documentSnapshotVersion,
|
|
632
|
+
commandVersion: capabilities.documentCommandVersion,
|
|
633
|
+
maximumSnapshotBytes: capabilities.maxDocumentSnapshotBytes,
|
|
634
|
+
maximumCommandBytes: capabilities.maxDocumentCommandBytes,
|
|
635
|
+
maximumQueryBytes: capabilities.maxDocumentQueryBytes,
|
|
636
|
+
maximumQueryResponseBytes: capabilities.maxDocumentQueryResponseBytes
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
if (!capabilities.presentation || !capabilities.presentationStatefulSessions) {
|
|
640
|
+
throw adapterError(
|
|
641
|
+
"incompatible_wasm_build",
|
|
642
|
+
"WASM module does not provide the presentation ABI"
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
modelSchemaVersion: capabilities.presentationSnapshotVersion,
|
|
647
|
+
commandVersion: capabilities.presentationCommandVersion,
|
|
648
|
+
maximumSnapshotBytes: capabilities.maxPresentationSnapshotBytes,
|
|
649
|
+
maximumCommandBytes: capabilities.maxPresentationCommandBytes,
|
|
650
|
+
maximumQueryBytes: capabilities.maxPresentationQueryBytes,
|
|
651
|
+
maximumQueryResponseBytes: capabilities.maxPresentationResponseBytes
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
function classifyProjectionFailure(error) {
|
|
655
|
+
const message = error instanceof Error ? error.message : "";
|
|
656
|
+
const match = /^\[([A-Z][A-Z0-9_]{0,127})\]/u.exec(message);
|
|
657
|
+
if (!match) return null;
|
|
658
|
+
switch (match[1]) {
|
|
659
|
+
case "ARTIFACT_COLLABORATION_REJECTED":
|
|
660
|
+
return "structural_conflict";
|
|
661
|
+
case "ARTIFACT_PROJECTION_REJECTED":
|
|
662
|
+
return "projection_conflict";
|
|
663
|
+
default:
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
function requireInputBytes(value, label, maximumBytes) {
|
|
668
|
+
if (!(value instanceof Uint8Array) || value.byteLength === 0) {
|
|
669
|
+
throw adapterError("invalid_kernel_input", `${label} must be non-empty bytes`);
|
|
670
|
+
}
|
|
671
|
+
if (value.byteLength > maximumBytes) {
|
|
672
|
+
throw adapterError("limit_exceeded", `${label} exceeds its byte limit`);
|
|
673
|
+
}
|
|
674
|
+
return value;
|
|
675
|
+
}
|
|
676
|
+
function requireBoundedBytes(value, label, maximumBytes) {
|
|
677
|
+
if (!(value instanceof Uint8Array) || value.byteLength === 0) {
|
|
678
|
+
throw adapterError("invalid_kernel_output", `${label} must be non-empty bytes`);
|
|
679
|
+
}
|
|
680
|
+
if (value.byteLength > maximumBytes) {
|
|
681
|
+
throw adapterError("limit_exceeded", `${label} exceeds its byte limit`);
|
|
682
|
+
}
|
|
683
|
+
return value;
|
|
684
|
+
}
|
|
685
|
+
function requireOwnedOutput(value, label, maximumBytes) {
|
|
686
|
+
const bytes = requireBoundedBytes(value, label, maximumBytes);
|
|
687
|
+
if (bytes.buffer instanceof ArrayBuffer && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) {
|
|
688
|
+
return bytes;
|
|
689
|
+
}
|
|
690
|
+
return bytes.slice();
|
|
691
|
+
}
|
|
692
|
+
function decodeUtf8(bytes, label) {
|
|
693
|
+
try {
|
|
694
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
throw new ArtifactWorkerProtocolError("incompatible_wasm_build", `${label} is not UTF-8`, {
|
|
697
|
+
cause: error
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
function callable(record, key) {
|
|
702
|
+
const value = data(record, key);
|
|
703
|
+
if (typeof value !== "function") {
|
|
704
|
+
throw adapterError("invalid_wasm_module", `${key} must be a function`);
|
|
705
|
+
}
|
|
706
|
+
return value;
|
|
707
|
+
}
|
|
708
|
+
function optionalCallable(record, key) {
|
|
709
|
+
const value = data(record, key, false);
|
|
710
|
+
if (value === void 0) return void 0;
|
|
711
|
+
if (typeof value !== "function") {
|
|
712
|
+
throw adapterError("invalid_wasm_module", `${key} must be a function`);
|
|
713
|
+
}
|
|
714
|
+
return value;
|
|
715
|
+
}
|
|
716
|
+
function data(record, key, required = true) {
|
|
717
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
718
|
+
if (descriptor) return "value" in descriptor ? descriptor.value : Reflect.get(record, key);
|
|
719
|
+
if (required) throw adapterError("invalid_wasm_module", `module is missing ${key}`);
|
|
720
|
+
return void 0;
|
|
721
|
+
}
|
|
722
|
+
function isObject(value) {
|
|
723
|
+
return value !== null && (typeof value === "object" || typeof value === "function");
|
|
724
|
+
}
|
|
725
|
+
function isPlainRecord(value) {
|
|
726
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
727
|
+
}
|
|
728
|
+
function adapterError(code, message) {
|
|
729
|
+
return new ArtifactWorkerProtocolError(code, message);
|
|
730
|
+
}
|
|
731
|
+
function requireErrorCode(value, label) {
|
|
732
|
+
if (typeof value !== "string" || !/^[a-z][a-z0-9_]{0,127}$/u.test(value)) {
|
|
733
|
+
throw adapterError("invalid_wasm_module", `${label} is invalid`);
|
|
734
|
+
}
|
|
735
|
+
return value;
|
|
736
|
+
}
|
|
737
|
+
function positiveInteger(value, label) {
|
|
738
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
739
|
+
throw adapterError("incompatible_wasm_build", `${label} must be a positive safe integer`);
|
|
740
|
+
}
|
|
741
|
+
return value;
|
|
742
|
+
}
|
|
743
|
+
function strictBoolean(value, label) {
|
|
744
|
+
if (typeof value !== "boolean") {
|
|
745
|
+
throw adapterError("incompatible_wasm_build", `${label} must be a boolean`);
|
|
746
|
+
}
|
|
747
|
+
return value;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// src/editable-artifacts/worker/runtime.ts
|
|
751
|
+
import {
|
|
752
|
+
EDITABLE_ARTIFACT_INTENT_MAX_BYTES as EDITABLE_ARTIFACT_INTENT_MAX_BYTES2,
|
|
753
|
+
EDITABLE_ARTIFACT_INTENT_VERSION as EDITABLE_ARTIFACT_INTENT_VERSION2,
|
|
754
|
+
assertCanonicalSpreadsheetArtifactCommandBytes,
|
|
755
|
+
decodeEditableArtifactMutationIntent,
|
|
756
|
+
hashEditableArtifactMutationIntent
|
|
757
|
+
} from "@opengeni/contracts/editable-artifacts";
|
|
758
|
+
import { decodeCommittedTransactionSummary } from "@opengeni/contracts/editable-artifact-committed-transaction";
|
|
759
|
+
import { editableArtifactCodecFor } from "@opengeni/contracts/editable-artifact-codec-registry";
|
|
760
|
+
import { decodeEditableArtifactSerializedCommit } from "@opengeni/contracts/editable-artifact-serialized-commit";
|
|
761
|
+
var MAX_KNOWN_REQUESTS = 64;
|
|
762
|
+
var MAX_QUEUED_REQUEST_BYTES = 192 * 1024 * 1024;
|
|
763
|
+
var MAX_PENDING_AGGREGATE_BYTES = 64 * 1024 * 1024;
|
|
764
|
+
var ArtifactWorkerRuntime = class {
|
|
765
|
+
endpoint;
|
|
766
|
+
loadAdapter;
|
|
767
|
+
rpcLimits;
|
|
768
|
+
onProtocolError;
|
|
769
|
+
cancelled = /* @__PURE__ */ new Set();
|
|
770
|
+
knownRequests = /* @__PURE__ */ new Map();
|
|
771
|
+
queuedRequestBytes = 0;
|
|
772
|
+
queue = Promise.resolve();
|
|
773
|
+
generation = null;
|
|
774
|
+
initialization = null;
|
|
775
|
+
adapter = null;
|
|
776
|
+
confirmed = null;
|
|
777
|
+
speculative = null;
|
|
778
|
+
installed = null;
|
|
779
|
+
pendingKeys = Object.freeze([]);
|
|
780
|
+
blockedPending = Object.freeze([]);
|
|
781
|
+
disposed = false;
|
|
782
|
+
onMessage = (event) => {
|
|
783
|
+
if (this.disposed) return;
|
|
784
|
+
let frame;
|
|
785
|
+
try {
|
|
786
|
+
frame = decodeArtifactWorkerRpcMessage(event.data, this.rpcLimits);
|
|
787
|
+
} catch (error) {
|
|
788
|
+
const failure = asProtocolError(error);
|
|
789
|
+
this.reportProtocolError(failure);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (frame.kind === 8 /* Cancel */) {
|
|
793
|
+
if (frame.flags === 0 && frame.metadata.byteLength === 0 && frame.segments.length === 0 && this.knownRequests.get(frame.requestId)?.generation === frame.generation) {
|
|
794
|
+
this.cancelled.add(frame.requestId);
|
|
795
|
+
}
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
if (frame.flags !== 0) {
|
|
799
|
+
this.reportProtocolError(runtimeError("noncanonical", "unknown RPC flags are set"));
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (this.knownRequests.has(frame.requestId)) {
|
|
803
|
+
this.reportProtocolError(runtimeError("duplicate_request", "RPC request id is duplicated"));
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
const requestBytes = retainedFrameBytes(frame);
|
|
807
|
+
if (this.knownRequests.size >= MAX_KNOWN_REQUESTS || this.queuedRequestBytes + requestBytes > MAX_QUEUED_REQUEST_BYTES) {
|
|
808
|
+
this.respondError(
|
|
809
|
+
frame.requestId,
|
|
810
|
+
runtimeError("request_limit", "artifact Worker request queue is full"),
|
|
811
|
+
frame.generation
|
|
812
|
+
);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
this.knownRequests.set(frame.requestId, { bytes: requestBytes, generation: frame.generation });
|
|
816
|
+
this.queuedRequestBytes += requestBytes;
|
|
817
|
+
this.queue = this.queue.then(() => this.dispatch(frame)).catch((error) => this.reportProtocolError(asProtocolError(error)));
|
|
818
|
+
};
|
|
819
|
+
constructor(options) {
|
|
820
|
+
this.endpoint = options.endpoint;
|
|
821
|
+
this.loadAdapter = options.loadAdapter;
|
|
822
|
+
this.rpcLimits = options.rpcLimits;
|
|
823
|
+
this.onProtocolError = options.onProtocolError;
|
|
824
|
+
this.endpoint.addEventListener("message", this.onMessage);
|
|
825
|
+
}
|
|
826
|
+
async dispose() {
|
|
827
|
+
if (this.disposed) return;
|
|
828
|
+
this.disposed = true;
|
|
829
|
+
this.endpoint.removeEventListener("message", this.onMessage);
|
|
830
|
+
await this.queue.catch(() => {
|
|
831
|
+
});
|
|
832
|
+
this.releaseState();
|
|
833
|
+
this.disposeAdapter(this.adapter);
|
|
834
|
+
this.adapter = null;
|
|
835
|
+
this.cancelled.clear();
|
|
836
|
+
this.knownRequests.clear();
|
|
837
|
+
this.queuedRequestBytes = 0;
|
|
838
|
+
}
|
|
839
|
+
async dispatch(frame) {
|
|
840
|
+
try {
|
|
841
|
+
if (frame.kind === 1 /* Initialize */) {
|
|
842
|
+
await this.initialize(frame);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
this.requireInitialized(frame.generation);
|
|
846
|
+
this.throwIfCancelled(frame.requestId);
|
|
847
|
+
switch (frame.kind) {
|
|
848
|
+
case 2 /* Reset */:
|
|
849
|
+
this.requireNoPayload(frame);
|
|
850
|
+
this.releaseState();
|
|
851
|
+
this.respond(frame.requestId);
|
|
852
|
+
return;
|
|
853
|
+
case 3 /* LoadSnapshot */:
|
|
854
|
+
await this.loadSnapshot(frame);
|
|
855
|
+
return;
|
|
856
|
+
case 4 /* ApplyRecovered */:
|
|
857
|
+
await this.applyRecovered(frame);
|
|
858
|
+
return;
|
|
859
|
+
case 5 /* ReconcileCommitted */:
|
|
860
|
+
await this.reconcileCommitted(frame);
|
|
861
|
+
return;
|
|
862
|
+
case 6 /* ReplacePending */:
|
|
863
|
+
await this.replacePending(frame);
|
|
864
|
+
return;
|
|
865
|
+
case 7 /* AuthorPending */:
|
|
866
|
+
await this.authorPending(frame);
|
|
867
|
+
return;
|
|
868
|
+
case 10 /* QuerySpreadsheet */:
|
|
869
|
+
this.queryArtifact(frame);
|
|
870
|
+
return;
|
|
871
|
+
case 9 /* Dispose */:
|
|
872
|
+
this.requireNoPayload(frame);
|
|
873
|
+
this.releaseState();
|
|
874
|
+
this.disposeAdapter(this.adapter);
|
|
875
|
+
this.adapter = null;
|
|
876
|
+
this.respond(frame.requestId);
|
|
877
|
+
return;
|
|
878
|
+
default:
|
|
879
|
+
throw runtimeError("unknown_request", `worker cannot execute RPC kind ${frame.kind}`);
|
|
880
|
+
}
|
|
881
|
+
} catch (error) {
|
|
882
|
+
this.respondError(frame.requestId, error, frame.generation);
|
|
883
|
+
} finally {
|
|
884
|
+
this.cancelled.delete(frame.requestId);
|
|
885
|
+
const retainedBytes = this.knownRequests.get(frame.requestId)?.bytes ?? 0;
|
|
886
|
+
this.knownRequests.delete(frame.requestId);
|
|
887
|
+
this.queuedRequestBytes -= retainedBytes;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
async initialize(frame) {
|
|
891
|
+
if (this.generation !== null || this.adapter !== null) {
|
|
892
|
+
throw runtimeError("already_initialized", "artifact Worker is already initialized");
|
|
893
|
+
}
|
|
894
|
+
if (frame.segments.length !== 0)
|
|
895
|
+
throw runtimeError("invalid_segment", "initialize has no segments");
|
|
896
|
+
const initialization = decodeInitialize(frame.metadata);
|
|
897
|
+
this.throwIfCancelled(frame.requestId);
|
|
898
|
+
let adapter = null;
|
|
899
|
+
try {
|
|
900
|
+
adapter = await this.loadAdapter(initialization);
|
|
901
|
+
this.throwIfCancelled(frame.requestId);
|
|
902
|
+
} catch (error) {
|
|
903
|
+
this.disposeAdapter(adapter);
|
|
904
|
+
throw error;
|
|
905
|
+
}
|
|
906
|
+
assertAdapterCompatibility(adapter, initialization);
|
|
907
|
+
this.generation = frame.generation;
|
|
908
|
+
this.initialization = initialization;
|
|
909
|
+
this.adapter = adapter;
|
|
910
|
+
this.respond(frame.requestId);
|
|
911
|
+
}
|
|
912
|
+
async loadSnapshot(frame) {
|
|
913
|
+
const initialization = this.requireInitialization();
|
|
914
|
+
const adapter = this.requireAdapter();
|
|
915
|
+
if (frame.segments.length !== 1)
|
|
916
|
+
throw runtimeError("invalid_segment", "snapshot requires one segment");
|
|
917
|
+
const ownedBytes = requireBytesWithin(
|
|
918
|
+
frame.segments[0],
|
|
919
|
+
initialization.maximumSnapshotBytes,
|
|
920
|
+
"snapshot"
|
|
921
|
+
);
|
|
922
|
+
const snapshot = decodeSnapshotMetadata(frame.metadata, ownedBytes);
|
|
923
|
+
if (snapshot.modality !== initialization.modality || snapshot.modality !== adapter.modality || snapshot.modality === "spreadsheet" && snapshot.protocolVersion !== adapter.protocolVersion || snapshot.kernelVersion !== adapter.kernelVersion || snapshot.modelSchemaVersion !== adapter.modelSchemaVersion) {
|
|
924
|
+
throw runtimeError(
|
|
925
|
+
"unsupported_protocol",
|
|
926
|
+
"snapshot protocol/kernel/schema is incompatible with the loaded collaboration kernel"
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
const digest = await sha256Hex(ownedBytes);
|
|
930
|
+
this.throwIfCancelled(frame.requestId);
|
|
931
|
+
requireMatchingHash(digest, snapshot.digest, "snapshot digest");
|
|
932
|
+
const canonical = requireBytesWithin(
|
|
933
|
+
adapter.canonicalizeSnapshot(ownedBytes),
|
|
934
|
+
initialization.maximumSnapshotBytes,
|
|
935
|
+
"canonical snapshot"
|
|
936
|
+
);
|
|
937
|
+
if (!bytesEqual(canonical, ownedBytes)) {
|
|
938
|
+
throw runtimeError(
|
|
939
|
+
"noncanonical_snapshot",
|
|
940
|
+
"server snapshot bytes are not canonical; refusing to persist a different model"
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
const nextConfirmed = adapter.open(canonical);
|
|
944
|
+
let nextSpeculative = null;
|
|
945
|
+
let stateHash;
|
|
946
|
+
try {
|
|
947
|
+
stateHash = await nextConfirmed.stateHash();
|
|
948
|
+
this.throwIfCancelled(frame.requestId);
|
|
949
|
+
requireMatchingHash(stateHash, snapshot.stateHash, "snapshot state");
|
|
950
|
+
if (snapshot.modality !== "spreadsheet" && nextConfirmed.nativeRevision() !== snapshot.nativeRevision) {
|
|
951
|
+
throw runtimeError(
|
|
952
|
+
"kernel_diverged",
|
|
953
|
+
"serialized snapshot native revision does not match the WASM session"
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
nextSpeculative = nextConfirmed.fork();
|
|
957
|
+
this.throwIfCancelled(frame.requestId);
|
|
958
|
+
} catch (error) {
|
|
959
|
+
this.disposeSession(nextConfirmed);
|
|
960
|
+
this.disposeSession(nextSpeculative);
|
|
961
|
+
throw error;
|
|
962
|
+
}
|
|
963
|
+
if (!nextSpeculative) throw runtimeError("kernel_failed", "kernel fork was not created");
|
|
964
|
+
this.installSessions(
|
|
965
|
+
nextConfirmed,
|
|
966
|
+
nextSpeculative,
|
|
967
|
+
snapshot.modality === "spreadsheet" ? {
|
|
968
|
+
artifactId: snapshot.artifactId,
|
|
969
|
+
modality: "spreadsheet",
|
|
970
|
+
sequence: snapshot.sequence,
|
|
971
|
+
protocolVersion: snapshot.protocolVersion,
|
|
972
|
+
kernelVersion: snapshot.kernelVersion,
|
|
973
|
+
modelSchemaVersion: snapshot.modelSchemaVersion,
|
|
974
|
+
stateHash,
|
|
975
|
+
causalFrontier: snapshot.causalFrontier
|
|
976
|
+
} : {
|
|
977
|
+
artifactId: snapshot.artifactId,
|
|
978
|
+
modality: snapshot.modality,
|
|
979
|
+
sequence: snapshot.sequence,
|
|
980
|
+
protocolVersion: adapter.protocolVersion,
|
|
981
|
+
kernelVersion: snapshot.kernelVersion,
|
|
982
|
+
modelSchemaVersion: snapshot.modelSchemaVersion,
|
|
983
|
+
stateHash,
|
|
984
|
+
nativeRevision: snapshot.nativeRevision
|
|
985
|
+
},
|
|
986
|
+
Object.freeze([]),
|
|
987
|
+
Object.freeze([])
|
|
988
|
+
);
|
|
989
|
+
this.respond(frame.requestId, encodeStateResponse(stateHash, digest));
|
|
990
|
+
}
|
|
991
|
+
async applyRecovered(frame) {
|
|
992
|
+
const initialization = this.requireInitialization();
|
|
993
|
+
if (frame.segments.length !== 1) {
|
|
994
|
+
throw runtimeError("invalid_segment", "recovered transaction requires one operation segment");
|
|
995
|
+
}
|
|
996
|
+
const committedTransactionBytes = requireBytesWithin(
|
|
997
|
+
frame.segments[0],
|
|
998
|
+
initialization.maximumCommittedTransactionBytes,
|
|
999
|
+
"committed transaction"
|
|
1000
|
+
);
|
|
1001
|
+
const transaction = decodeCommittedMetadata(frame.metadata, committedTransactionBytes);
|
|
1002
|
+
const result = await this.buildCommittedCandidate(transaction, [], frame.requestId);
|
|
1003
|
+
this.installCommittedCandidate(result, transaction, []);
|
|
1004
|
+
this.respond(frame.requestId, encodeStateResponse(result.stateHash));
|
|
1005
|
+
}
|
|
1006
|
+
async reconcileCommitted(frame) {
|
|
1007
|
+
const initialization = this.requireInitialization();
|
|
1008
|
+
const decoded = decodeReconcileMetadata(
|
|
1009
|
+
frame.metadata,
|
|
1010
|
+
frame.segments,
|
|
1011
|
+
initialization.maximumPendingTransactions
|
|
1012
|
+
);
|
|
1013
|
+
requireBytesWithin(
|
|
1014
|
+
decoded.committed.committedTransactionBytes,
|
|
1015
|
+
initialization.maximumCommittedTransactionBytes,
|
|
1016
|
+
"committed transaction"
|
|
1017
|
+
);
|
|
1018
|
+
await this.validatePending(decoded.pending);
|
|
1019
|
+
const result = await this.buildCommittedCandidate(
|
|
1020
|
+
decoded.committed,
|
|
1021
|
+
decoded.pending,
|
|
1022
|
+
frame.requestId
|
|
1023
|
+
);
|
|
1024
|
+
this.installCommittedCandidate(result, decoded.committed, decoded.pending);
|
|
1025
|
+
this.respond(
|
|
1026
|
+
frame.requestId,
|
|
1027
|
+
encodeProjectionResponse(result.blockedPending, result.stateHash)
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
async replacePending(frame) {
|
|
1031
|
+
const initialization = this.requireInitialization();
|
|
1032
|
+
const pending = decodePendingListMetadata(
|
|
1033
|
+
frame.metadata,
|
|
1034
|
+
frame.segments,
|
|
1035
|
+
initialization.maximumPendingTransactions
|
|
1036
|
+
);
|
|
1037
|
+
await this.validatePending(pending);
|
|
1038
|
+
const keys = pending.map(pendingKey);
|
|
1039
|
+
if (arraysEqual(keys, this.pendingKeys)) {
|
|
1040
|
+
this.respond(frame.requestId, encodeProjectionResponse(this.blockedPending));
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
const candidate2 = this.requireConfirmed().fork();
|
|
1044
|
+
let blockedPending;
|
|
1045
|
+
try {
|
|
1046
|
+
blockedPending = this.applyPending(candidate2, pending, frame.requestId);
|
|
1047
|
+
this.throwIfCancelled(frame.requestId);
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
this.disposeSession(candidate2);
|
|
1050
|
+
throw error;
|
|
1051
|
+
}
|
|
1052
|
+
const previous = this.speculative;
|
|
1053
|
+
this.speculative = candidate2;
|
|
1054
|
+
this.pendingKeys = Object.freeze(keys);
|
|
1055
|
+
this.blockedPending = blockedPending;
|
|
1056
|
+
this.disposeSession(previous);
|
|
1057
|
+
this.respond(frame.requestId, encodeProjectionResponse(blockedPending));
|
|
1058
|
+
}
|
|
1059
|
+
async authorPending(frame) {
|
|
1060
|
+
const initialization = this.requireInitialization();
|
|
1061
|
+
const adapter = this.requireAdapter();
|
|
1062
|
+
if (frame.segments.length !== 1)
|
|
1063
|
+
throw runtimeError("invalid_segment", "author requires one command segment");
|
|
1064
|
+
const commandBytes = requireBytesWithin(
|
|
1065
|
+
frame.segments[0],
|
|
1066
|
+
initialization.maximumCommandBytes,
|
|
1067
|
+
"pending command"
|
|
1068
|
+
);
|
|
1069
|
+
const input = decodeAuthorPendingMetadata(frame.metadata, commandBytes);
|
|
1070
|
+
if (input.modality !== initialization.modality || input.modality !== adapter.modality || input.protocolVersion !== adapter.protocolVersion || input.kernelVersion !== adapter.kernelVersion || input.modelSchemaVersion !== adapter.modelSchemaVersion || input.commandVersion !== adapter.commandVersion) {
|
|
1071
|
+
throw runtimeError(
|
|
1072
|
+
"unsupported_protocol",
|
|
1073
|
+
"pending authoring versions are incompatible with the loaded kernel"
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
if (this.installed && input.artifactId !== this.installed.artifactId) {
|
|
1077
|
+
throw runtimeError("artifact_mismatch", "pending transaction belongs to another artifact");
|
|
1078
|
+
}
|
|
1079
|
+
try {
|
|
1080
|
+
if (input.modality === "spreadsheet") {
|
|
1081
|
+
assertCanonicalSpreadsheetArtifactCommandBytes(commandBytes);
|
|
1082
|
+
} else {
|
|
1083
|
+
editableArtifactCodecFor({
|
|
1084
|
+
durableModality: input.modality,
|
|
1085
|
+
modelSchemaVersion: input.modelSchemaVersion,
|
|
1086
|
+
commandProtocolVersion: input.commandVersion
|
|
1087
|
+
}).command.assertCanonical(commandBytes);
|
|
1088
|
+
}
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
throw runtimeError(
|
|
1091
|
+
"noncanonical_command",
|
|
1092
|
+
`pending ${input.modality} command must be canonical before it receives an identity: ${error instanceof Error ? error.message : "invalid command"}`
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
const canonicalCommand = commandBytes;
|
|
1096
|
+
const authored = authorCanonicalPendingIntent(input, canonicalCommand);
|
|
1097
|
+
const intentBytes = requireBytesWithin(
|
|
1098
|
+
authored.bytes,
|
|
1099
|
+
EDITABLE_ARTIFACT_INTENT_MAX_BYTES2,
|
|
1100
|
+
"OGATX001 intent"
|
|
1101
|
+
);
|
|
1102
|
+
this.throwIfCancelled(frame.requestId);
|
|
1103
|
+
const commonPending = {
|
|
1104
|
+
artifactId: input.artifactId,
|
|
1105
|
+
clientTransactionId: input.clientTransactionId,
|
|
1106
|
+
requestHash: authored.requestHash,
|
|
1107
|
+
protocolVersion: input.protocolVersion,
|
|
1108
|
+
modelSchemaVersion: input.modelSchemaVersion,
|
|
1109
|
+
commandVersion: input.commandVersion,
|
|
1110
|
+
replicaId: input.replicaId,
|
|
1111
|
+
replicaCounter: input.replicaCounter,
|
|
1112
|
+
previousLocalTransactionId: input.previousLocalTransactionId,
|
|
1113
|
+
observedHeadSequence: input.observedHeadSequence,
|
|
1114
|
+
commandBytes: canonicalCommand,
|
|
1115
|
+
intentBytes,
|
|
1116
|
+
createdAt: input.createdAt
|
|
1117
|
+
};
|
|
1118
|
+
let pending;
|
|
1119
|
+
if (input.modality === "spreadsheet") {
|
|
1120
|
+
pending = Object.freeze({
|
|
1121
|
+
...commonPending,
|
|
1122
|
+
modality: "spreadsheet",
|
|
1123
|
+
causalBase: input.causalBase ?? [],
|
|
1124
|
+
selectiveUndoTargets: input.selectiveUndoTargets ?? []
|
|
1125
|
+
});
|
|
1126
|
+
} else {
|
|
1127
|
+
const installed = this.requireInstalled();
|
|
1128
|
+
if (installed.modality !== input.modality) {
|
|
1129
|
+
throw runtimeError("modality_mismatch", "pending modality does not match installed state");
|
|
1130
|
+
}
|
|
1131
|
+
const expectedHeadSequence = installed.sequence + this.pendingKeys.length;
|
|
1132
|
+
if (input.observedHeadSequence !== expectedHeadSequence) {
|
|
1133
|
+
throw runtimeError(
|
|
1134
|
+
"serialized_head_conflict",
|
|
1135
|
+
"serialized pending command was not authored over the speculative head"
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
pending = Object.freeze({
|
|
1139
|
+
...commonPending,
|
|
1140
|
+
modality: input.modality,
|
|
1141
|
+
observedNativeRevision: this.requireSpeculative().nativeRevision()
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
const metadata = encodePendingListMetadata([pending]);
|
|
1145
|
+
const command = transferableOwnedBuffer(canonicalCommand);
|
|
1146
|
+
const intent = transferableOwnedBuffer(intentBytes);
|
|
1147
|
+
this.respond(frame.requestId, metadata, [command, intent]);
|
|
1148
|
+
}
|
|
1149
|
+
queryArtifact(frame) {
|
|
1150
|
+
if (frame.metadata.byteLength !== 0 || frame.segments.length !== 1) {
|
|
1151
|
+
throw runtimeError("invalid_segment", "artifact query requires one byte segment");
|
|
1152
|
+
}
|
|
1153
|
+
const initialization = this.requireInitialization();
|
|
1154
|
+
const queryBytes = requireBytesWithin(
|
|
1155
|
+
frame.segments[0],
|
|
1156
|
+
initialization.maximumQueryBytes,
|
|
1157
|
+
`${initialization.modality} query`
|
|
1158
|
+
);
|
|
1159
|
+
const result = requireBytesWithin(
|
|
1160
|
+
this.requireSpeculative().query(queryBytes),
|
|
1161
|
+
initialization.maximumQueryResponseBytes,
|
|
1162
|
+
`${initialization.modality} projection`
|
|
1163
|
+
);
|
|
1164
|
+
this.throwIfCancelled(frame.requestId);
|
|
1165
|
+
this.respond(frame.requestId, void 0, [transferableOwnedBuffer(result)]);
|
|
1166
|
+
}
|
|
1167
|
+
async buildCommittedCandidate(transaction, remainingPending, requestId) {
|
|
1168
|
+
const installed = this.requireInstalled();
|
|
1169
|
+
if (transaction.artifactId !== installed.artifactId) {
|
|
1170
|
+
throw runtimeError("artifact_mismatch", "committed transaction belongs to another artifact");
|
|
1171
|
+
}
|
|
1172
|
+
if (transaction.modality !== installed.modality) {
|
|
1173
|
+
throw runtimeError("modality_mismatch", "committed transaction modality is incompatible");
|
|
1174
|
+
}
|
|
1175
|
+
if (transaction.modality === "spreadsheet" && transaction.protocolVersion !== installed.protocolVersion)
|
|
1176
|
+
throw runtimeError("unsupported_protocol", "committed transaction protocol is incompatible");
|
|
1177
|
+
if (transaction.startSequence !== installed.sequence + 1) {
|
|
1178
|
+
throw runtimeError("invalid_sequence", "committed transaction is not contiguous");
|
|
1179
|
+
}
|
|
1180
|
+
assertCommittedEnvelopeMatchesOuter(transaction);
|
|
1181
|
+
if (transaction.modality === "spreadsheet" && installed.modality === "spreadsheet") {
|
|
1182
|
+
requireFrontierAdvance(installed.causalFrontier, transaction.causalFrontier);
|
|
1183
|
+
} else if (transaction.modality !== "spreadsheet" && installed.modality !== "spreadsheet") {
|
|
1184
|
+
if (transaction.priorNativeRevision !== installed.nativeRevision) {
|
|
1185
|
+
throw runtimeError("invalid_revision", "serialized commit prior revision is not current");
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
requireMatchingHash(installed.stateHash, transaction.priorStateHash, "transaction prior state");
|
|
1189
|
+
const nextConfirmed = this.requireConfirmed().fork();
|
|
1190
|
+
let nextSpeculative = null;
|
|
1191
|
+
try {
|
|
1192
|
+
if (transaction.modality === "spreadsheet") {
|
|
1193
|
+
nextConfirmed.applyCommitted(transaction.committedTransactionBytes);
|
|
1194
|
+
} else {
|
|
1195
|
+
const summary = decodeEditableArtifactSerializedCommit(
|
|
1196
|
+
transaction.committedTransactionBytes,
|
|
1197
|
+
transaction.modality
|
|
1198
|
+
);
|
|
1199
|
+
const intent = decodeEditableArtifactMutationIntent(summary.intentBytes);
|
|
1200
|
+
const nativeReceipt = nextConfirmed.applyCommands(intent.commandBytes);
|
|
1201
|
+
if (!bytesEqual(nativeReceipt, summary.nativeReceiptBytes)) {
|
|
1202
|
+
throw runtimeError(
|
|
1203
|
+
"kernel_diverged",
|
|
1204
|
+
"WASM native receipt differs from the exact durable OGAST receipt"
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
if (nextConfirmed.nativeRevision() !== transaction.nativeRevision) {
|
|
1208
|
+
throw runtimeError(
|
|
1209
|
+
"kernel_diverged",
|
|
1210
|
+
"WASM native revision differs from the exact durable OGAST revision"
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
const stateHash = await nextConfirmed.stateHash();
|
|
1215
|
+
this.throwIfCancelled(requestId);
|
|
1216
|
+
requireMatchingHash(stateHash, transaction.stateHash, "committed state");
|
|
1217
|
+
nextSpeculative = nextConfirmed.fork();
|
|
1218
|
+
const blockedPending = this.applyPending(
|
|
1219
|
+
nextSpeculative,
|
|
1220
|
+
remainingPending,
|
|
1221
|
+
requestId,
|
|
1222
|
+
transaction.endSequence
|
|
1223
|
+
);
|
|
1224
|
+
this.throwIfCancelled(requestId);
|
|
1225
|
+
return {
|
|
1226
|
+
confirmed: nextConfirmed,
|
|
1227
|
+
speculative: nextSpeculative,
|
|
1228
|
+
stateHash,
|
|
1229
|
+
blockedPending
|
|
1230
|
+
};
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
this.disposeSession(nextConfirmed);
|
|
1233
|
+
this.disposeSession(nextSpeculative);
|
|
1234
|
+
throw error;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
installCommittedCandidate(candidate2, transaction, pending) {
|
|
1238
|
+
const installed = this.requireInstalled();
|
|
1239
|
+
this.installSessions(
|
|
1240
|
+
candidate2.confirmed,
|
|
1241
|
+
candidate2.speculative,
|
|
1242
|
+
transaction.modality === "spreadsheet" ? {
|
|
1243
|
+
...installed,
|
|
1244
|
+
modality: "spreadsheet",
|
|
1245
|
+
sequence: transaction.endSequence,
|
|
1246
|
+
stateHash: candidate2.stateHash,
|
|
1247
|
+
causalFrontier: transaction.causalFrontier
|
|
1248
|
+
} : {
|
|
1249
|
+
...installed,
|
|
1250
|
+
modality: transaction.modality,
|
|
1251
|
+
sequence: transaction.endSequence,
|
|
1252
|
+
stateHash: candidate2.stateHash,
|
|
1253
|
+
nativeRevision: transaction.nativeRevision
|
|
1254
|
+
},
|
|
1255
|
+
Object.freeze(pending.map(pendingKey)),
|
|
1256
|
+
candidate2.blockedPending
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
applyPending(session, pending, requestId, baseSequence = this.requireInstalled().sequence) {
|
|
1260
|
+
const blocked = [];
|
|
1261
|
+
const blockedIds = /* @__PURE__ */ new Set();
|
|
1262
|
+
for (const transaction of pending) {
|
|
1263
|
+
this.throwIfCancelled(requestId);
|
|
1264
|
+
if (transaction.previousLocalTransactionId !== null && blockedIds.has(transaction.previousLocalTransactionId)) {
|
|
1265
|
+
blockedIds.add(transaction.clientTransactionId);
|
|
1266
|
+
blocked.push(
|
|
1267
|
+
Object.freeze({
|
|
1268
|
+
clientTransactionId: transaction.clientTransactionId,
|
|
1269
|
+
code: "blocked_predecessor"
|
|
1270
|
+
})
|
|
1271
|
+
);
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
try {
|
|
1275
|
+
if (transaction.modality === "spreadsheet") {
|
|
1276
|
+
session.applyPending(transaction.intentBytes);
|
|
1277
|
+
} else {
|
|
1278
|
+
if (transaction.observedHeadSequence !== baseSequence || transaction.observedNativeRevision !== session.nativeRevision()) {
|
|
1279
|
+
throw new ArtifactWorkerPendingProjectionError(
|
|
1280
|
+
"serialized_head_conflict",
|
|
1281
|
+
"serialized pending transaction is stale"
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
session.applyCommands(transaction.commandBytes);
|
|
1285
|
+
baseSequence += 1;
|
|
1286
|
+
}
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
if (!(error instanceof ArtifactWorkerPendingProjectionError)) throw error;
|
|
1289
|
+
blockedIds.add(transaction.clientTransactionId);
|
|
1290
|
+
blocked.push(
|
|
1291
|
+
Object.freeze({ clientTransactionId: transaction.clientTransactionId, code: error.code })
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return Object.freeze(blocked);
|
|
1296
|
+
}
|
|
1297
|
+
async validatePending(pending) {
|
|
1298
|
+
const initialization = this.requireInitialization();
|
|
1299
|
+
const installed = this.requireInstalled();
|
|
1300
|
+
const adapter = this.requireAdapter();
|
|
1301
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1302
|
+
let aggregateBytes = 0;
|
|
1303
|
+
for (const transaction of pending) {
|
|
1304
|
+
if (transaction.artifactId !== installed.artifactId) {
|
|
1305
|
+
throw runtimeError("artifact_mismatch", "pending transaction belongs to another artifact");
|
|
1306
|
+
}
|
|
1307
|
+
if (transaction.modality !== installed.modality) {
|
|
1308
|
+
throw runtimeError("modality_mismatch", "pending transaction modality is incompatible");
|
|
1309
|
+
}
|
|
1310
|
+
if (transaction.protocolVersion !== installed.protocolVersion || transaction.modelSchemaVersion !== installed.modelSchemaVersion || transaction.commandVersion !== adapter.commandVersion) {
|
|
1311
|
+
throw runtimeError(
|
|
1312
|
+
"unsupported_protocol",
|
|
1313
|
+
"pending transaction versions are incompatible with confirmed state"
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
if (ids.has(transaction.clientTransactionId)) {
|
|
1317
|
+
throw runtimeError("duplicate_transaction", "pending transaction id is duplicated");
|
|
1318
|
+
}
|
|
1319
|
+
ids.add(transaction.clientTransactionId);
|
|
1320
|
+
requireBytesWithin(
|
|
1321
|
+
transaction.commandBytes,
|
|
1322
|
+
initialization.maximumCommandBytes,
|
|
1323
|
+
"pending command"
|
|
1324
|
+
);
|
|
1325
|
+
requireBytesWithin(
|
|
1326
|
+
transaction.intentBytes,
|
|
1327
|
+
initialization.maximumIntentBytes,
|
|
1328
|
+
"pending intent"
|
|
1329
|
+
);
|
|
1330
|
+
try {
|
|
1331
|
+
if (transaction.modality === "spreadsheet") {
|
|
1332
|
+
assertCanonicalSpreadsheetArtifactCommandBytes(transaction.commandBytes);
|
|
1333
|
+
} else {
|
|
1334
|
+
editableArtifactCodecFor({
|
|
1335
|
+
durableModality: transaction.modality,
|
|
1336
|
+
modelSchemaVersion: transaction.modelSchemaVersion,
|
|
1337
|
+
commandProtocolVersion: transaction.commandVersion
|
|
1338
|
+
}).command.assertCanonical(transaction.commandBytes);
|
|
1339
|
+
}
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
throw new ArtifactWorkerProtocolError(
|
|
1342
|
+
"noncanonical_command",
|
|
1343
|
+
`pending transaction contains noncanonical ${transaction.modality} command bytes`,
|
|
1344
|
+
{ cause: error }
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
verifyCanonicalPendingIntent(transaction);
|
|
1348
|
+
aggregateBytes += transaction.commandBytes.byteLength + transaction.intentBytes.byteLength;
|
|
1349
|
+
if (!Number.isSafeInteger(aggregateBytes) || aggregateBytes > MAX_PENDING_AGGREGATE_BYTES) {
|
|
1350
|
+
throw runtimeError("limit_exceeded", "pending commands exceed their aggregate byte limit");
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
installSessions(confirmed, speculative, installed, pendingKeys, blockedPending) {
|
|
1355
|
+
const previousConfirmed = this.confirmed;
|
|
1356
|
+
const previousSpeculative = this.speculative;
|
|
1357
|
+
this.confirmed = confirmed;
|
|
1358
|
+
this.speculative = speculative;
|
|
1359
|
+
this.installed = Object.freeze(installed);
|
|
1360
|
+
this.pendingKeys = pendingKeys;
|
|
1361
|
+
this.blockedPending = blockedPending;
|
|
1362
|
+
this.disposeSession(previousConfirmed);
|
|
1363
|
+
this.disposeSession(previousSpeculative);
|
|
1364
|
+
}
|
|
1365
|
+
releaseState() {
|
|
1366
|
+
const confirmed = this.confirmed;
|
|
1367
|
+
const speculative = this.speculative;
|
|
1368
|
+
this.confirmed = null;
|
|
1369
|
+
this.speculative = null;
|
|
1370
|
+
this.installed = null;
|
|
1371
|
+
this.pendingKeys = Object.freeze([]);
|
|
1372
|
+
this.blockedPending = Object.freeze([]);
|
|
1373
|
+
this.disposeSession(confirmed);
|
|
1374
|
+
this.disposeSession(speculative);
|
|
1375
|
+
}
|
|
1376
|
+
disposeSession(session) {
|
|
1377
|
+
if (!session) return;
|
|
1378
|
+
try {
|
|
1379
|
+
session.dispose();
|
|
1380
|
+
} catch (error) {
|
|
1381
|
+
this.reportProtocolError(asProtocolError(error));
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
disposeAdapter(adapter) {
|
|
1385
|
+
if (!adapter?.dispose) return;
|
|
1386
|
+
try {
|
|
1387
|
+
adapter.dispose();
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
this.reportProtocolError(asProtocolError(error));
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
reportProtocolError(error) {
|
|
1393
|
+
try {
|
|
1394
|
+
this.onProtocolError?.(error);
|
|
1395
|
+
} catch {
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
requireNoPayload(frame) {
|
|
1399
|
+
if (frame.metadata.byteLength !== 0 || frame.segments.length !== 0) {
|
|
1400
|
+
throw runtimeError("unexpected_payload", "RPC operation must not contain a payload");
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
requireInitialized(generation) {
|
|
1404
|
+
if (this.generation === null || this.adapter === null || this.initialization === null) {
|
|
1405
|
+
throw runtimeError("not_initialized", "artifact Worker is not initialized");
|
|
1406
|
+
}
|
|
1407
|
+
if (generation !== this.generation) {
|
|
1408
|
+
throw runtimeError("stale_generation", "RPC belongs to a retired Worker generation");
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
requireInitialization() {
|
|
1412
|
+
if (!this.initialization)
|
|
1413
|
+
throw runtimeError("not_initialized", "artifact Worker is not initialized");
|
|
1414
|
+
return this.initialization;
|
|
1415
|
+
}
|
|
1416
|
+
requireAdapter() {
|
|
1417
|
+
if (!this.adapter) throw runtimeError("not_initialized", "artifact Worker is not initialized");
|
|
1418
|
+
return this.adapter;
|
|
1419
|
+
}
|
|
1420
|
+
requireConfirmed() {
|
|
1421
|
+
if (!this.confirmed)
|
|
1422
|
+
throw runtimeError("snapshot_required", "artifact Worker has no confirmed state");
|
|
1423
|
+
return this.confirmed;
|
|
1424
|
+
}
|
|
1425
|
+
requireSpeculative() {
|
|
1426
|
+
if (!this.speculative) {
|
|
1427
|
+
throw runtimeError("snapshot_required", "artifact Worker has no speculative state");
|
|
1428
|
+
}
|
|
1429
|
+
return this.speculative;
|
|
1430
|
+
}
|
|
1431
|
+
requireInstalled() {
|
|
1432
|
+
if (!this.installed)
|
|
1433
|
+
throw runtimeError("snapshot_required", "artifact Worker has no installed snapshot");
|
|
1434
|
+
return this.installed;
|
|
1435
|
+
}
|
|
1436
|
+
throwIfCancelled(requestId) {
|
|
1437
|
+
if (this.cancelled.has(requestId))
|
|
1438
|
+
throw runtimeError("cancelled", "artifact Worker request was cancelled");
|
|
1439
|
+
}
|
|
1440
|
+
respond(requestId, metadata, segments) {
|
|
1441
|
+
if (this.generation === null || this.cancelled.has(requestId)) return;
|
|
1442
|
+
const response = encodeArtifactWorkerRpcMessage({
|
|
1443
|
+
kind: 128 /* Response */,
|
|
1444
|
+
generation: this.generation,
|
|
1445
|
+
requestId,
|
|
1446
|
+
...metadata ? { metadata } : {},
|
|
1447
|
+
...segments ? { segments } : {},
|
|
1448
|
+
...this.rpcLimits ? { limits: this.rpcLimits } : {}
|
|
1449
|
+
});
|
|
1450
|
+
this.endpoint.postMessage(response, transferListForArtifactWorkerRpcMessage(response));
|
|
1451
|
+
}
|
|
1452
|
+
respondError(requestId, error, generation = this.generation) {
|
|
1453
|
+
if (generation === null || this.cancelled.has(requestId)) return;
|
|
1454
|
+
const failure = asWorkerFailure(error);
|
|
1455
|
+
const response = encodeArtifactWorkerRpcMessage({
|
|
1456
|
+
kind: 129 /* Error */,
|
|
1457
|
+
generation,
|
|
1458
|
+
requestId,
|
|
1459
|
+
metadata: encodeErrorPayload(failure),
|
|
1460
|
+
...this.rpcLimits ? { limits: this.rpcLimits } : {}
|
|
1461
|
+
});
|
|
1462
|
+
this.endpoint.postMessage(response, transferListForArtifactWorkerRpcMessage(response));
|
|
1463
|
+
}
|
|
1464
|
+
};
|
|
1465
|
+
function authorCanonicalPendingIntent(input, canonicalCommand) {
|
|
1466
|
+
try {
|
|
1467
|
+
return hashEditableArtifactMutationIntent(canonicalMutationIntent(input, canonicalCommand));
|
|
1468
|
+
} catch (error) {
|
|
1469
|
+
throw new ArtifactWorkerProtocolError(
|
|
1470
|
+
"invalid_intent",
|
|
1471
|
+
"pending transaction cannot be represented by canonical OGATX001",
|
|
1472
|
+
{ cause: error }
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
function transferableOwnedBuffer(bytes) {
|
|
1477
|
+
if (bytes.buffer instanceof ArrayBuffer && bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength) {
|
|
1478
|
+
return bytes.buffer;
|
|
1479
|
+
}
|
|
1480
|
+
return bytes.slice().buffer;
|
|
1481
|
+
}
|
|
1482
|
+
function verifyCanonicalPendingIntent(transaction) {
|
|
1483
|
+
let expected;
|
|
1484
|
+
try {
|
|
1485
|
+
expected = hashEditableArtifactMutationIntent(
|
|
1486
|
+
canonicalMutationIntent(transaction, transaction.commandBytes)
|
|
1487
|
+
);
|
|
1488
|
+
} catch (error) {
|
|
1489
|
+
throw new ArtifactWorkerProtocolError(
|
|
1490
|
+
"intent_mismatch",
|
|
1491
|
+
"pending transaction metadata is not valid canonical OGATX001 input",
|
|
1492
|
+
{ cause: error }
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
if (expected.requestHash !== transaction.requestHash || !bytesEqual(expected.bytes, transaction.intentBytes)) {
|
|
1496
|
+
throw runtimeError(
|
|
1497
|
+
"intent_mismatch",
|
|
1498
|
+
"pending transaction bytes do not bind its exact metadata and command"
|
|
1499
|
+
);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
function assertAdapterCompatibility(adapter, initialization) {
|
|
1503
|
+
if (adapter.modality !== initialization.modality) {
|
|
1504
|
+
throw runtimeError("modality_mismatch", "loaded kernel modality does not match initialization");
|
|
1505
|
+
}
|
|
1506
|
+
if (adapter.kernelVersion !== initialization.kernelVersion || adapter.protocolVersion !== initialization.protocolVersion || adapter.modelSchemaVersion !== initialization.modelSchemaVersion || adapter.commandVersion !== initialization.commandVersion) {
|
|
1507
|
+
throw runtimeError(
|
|
1508
|
+
"runtime_identity_mismatch",
|
|
1509
|
+
"loaded kernel build/protocol/model identity does not match its package"
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
const limits = [
|
|
1513
|
+
[adapter.maximumSnapshotBytes, initialization.maximumSnapshotBytes, "snapshot"],
|
|
1514
|
+
[adapter.maximumCommandBytes, initialization.maximumCommandBytes, "command"],
|
|
1515
|
+
[adapter.maximumIntentBytes, initialization.maximumIntentBytes, "intent"],
|
|
1516
|
+
[
|
|
1517
|
+
adapter.maximumCommittedTransactionBytes,
|
|
1518
|
+
initialization.maximumCommittedTransactionBytes,
|
|
1519
|
+
"committed transaction"
|
|
1520
|
+
],
|
|
1521
|
+
[adapter.maximumQueryBytes, initialization.maximumQueryBytes, "query"],
|
|
1522
|
+
[adapter.maximumQueryResponseBytes, initialization.maximumQueryResponseBytes, "query response"]
|
|
1523
|
+
];
|
|
1524
|
+
for (const [advertised, requested, label] of limits) {
|
|
1525
|
+
if (advertised < requested) {
|
|
1526
|
+
throw runtimeError(
|
|
1527
|
+
"unsupported_protocol",
|
|
1528
|
+
`loaded collaboration kernel ${label} limit is below the requested Worker limit`
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
function assertCommittedEnvelopeMatchesOuter(transaction) {
|
|
1534
|
+
if (transaction.modality !== "spreadsheet") {
|
|
1535
|
+
let summary2;
|
|
1536
|
+
try {
|
|
1537
|
+
summary2 = decodeEditableArtifactSerializedCommit(
|
|
1538
|
+
transaction.committedTransactionBytes,
|
|
1539
|
+
transaction.modality
|
|
1540
|
+
);
|
|
1541
|
+
} catch (error) {
|
|
1542
|
+
throw new ArtifactWorkerProtocolError(
|
|
1543
|
+
"invalid_committed_transaction",
|
|
1544
|
+
"committed transaction is not canonical OGAST001",
|
|
1545
|
+
{ cause: error }
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
if (summary2.commitProtocolVersion !== transaction.commitProtocolVersion || summary2.transactionId !== transaction.transactionId || summary2.requestHash !== transaction.requestHash || summary2.parentHeadSequence + 1 !== transaction.startSequence || summary2.resultHeadSequence !== transaction.endSequence || summary2.priorNativeRevision !== transaction.priorNativeRevision || summary2.nativeReceipt.revision !== transaction.nativeRevision || summary2.priorStateHash !== transaction.priorStateHash || summary2.stateHash !== transaction.stateHash) {
|
|
1549
|
+
throw runtimeError(
|
|
1550
|
+
"committed_metadata_mismatch",
|
|
1551
|
+
"outer committed metadata disagrees with its exact OGAST001 envelope"
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
let summary;
|
|
1557
|
+
try {
|
|
1558
|
+
summary = decodeCommittedTransactionSummary(transaction.committedTransactionBytes);
|
|
1559
|
+
} catch (error) {
|
|
1560
|
+
throw new ArtifactWorkerProtocolError(
|
|
1561
|
+
"invalid_committed_transaction",
|
|
1562
|
+
"committed transaction is not canonical OGACO001",
|
|
1563
|
+
{ cause: error }
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
if (summary.operationProtocolVersion !== transaction.protocolVersion || summary.transactionId !== transaction.transactionId || summary.priorStateHash !== transaction.priorStateHash || summary.stateHash !== transaction.stateHash || !frontiersEqual(summary.resultingCausalFrontier, transaction.causalFrontier)) {
|
|
1567
|
+
throw runtimeError(
|
|
1568
|
+
"committed_metadata_mismatch",
|
|
1569
|
+
"outer committed metadata disagrees with its exact OGACO001 envelope"
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
function frontiersEqual(left, right) {
|
|
1574
|
+
return left.length === right.length && left.every(
|
|
1575
|
+
(entry, index) => entry.replicaId === right[index]?.replicaId && entry.counter === right[index]?.counter
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
function canonicalMutationIntent(input, commandBytes) {
|
|
1579
|
+
return {
|
|
1580
|
+
envelopeVersion: EDITABLE_ARTIFACT_INTENT_VERSION2,
|
|
1581
|
+
protocolVersion: input.protocolVersion,
|
|
1582
|
+
modelSchemaVersion: input.modelSchemaVersion,
|
|
1583
|
+
commandProtocolVersion: input.commandVersion,
|
|
1584
|
+
artifactId: input.artifactId,
|
|
1585
|
+
clientTransactionId: input.clientTransactionId,
|
|
1586
|
+
replicaId: input.replicaId,
|
|
1587
|
+
replicaCounter: input.replicaCounter,
|
|
1588
|
+
previousLocalTransactionId: input.previousLocalTransactionId,
|
|
1589
|
+
observedHeadSequence: input.observedHeadSequence,
|
|
1590
|
+
causalBase: input.modality === "spreadsheet" ? input.causalBase ?? [] : [],
|
|
1591
|
+
selectiveUndoOperationIds: input.modality === "spreadsheet" ? input.selectiveUndoTargets ?? [] : [],
|
|
1592
|
+
commandBytes
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
function retainedFrameBytes(frame) {
|
|
1596
|
+
let bytes = frame.metadata.byteLength;
|
|
1597
|
+
for (const segment of frame.segments) bytes += segment.byteLength;
|
|
1598
|
+
if (!Number.isSafeInteger(bytes)) {
|
|
1599
|
+
throw runtimeError("size_overflow", "artifact Worker request byte count overflowed");
|
|
1600
|
+
}
|
|
1601
|
+
return bytes;
|
|
1602
|
+
}
|
|
1603
|
+
function requireBytesWithin(bytes, maximum, label) {
|
|
1604
|
+
if (!(bytes instanceof Uint8Array) || bytes.byteLength === 0) {
|
|
1605
|
+
throw runtimeError("invalid_bytes", `${label} must be non-empty bytes`);
|
|
1606
|
+
}
|
|
1607
|
+
if (bytes.byteLength > maximum)
|
|
1608
|
+
throw runtimeError("limit_exceeded", `${label} exceeds its byte limit`);
|
|
1609
|
+
return bytes;
|
|
1610
|
+
}
|
|
1611
|
+
function pendingKey(transaction) {
|
|
1612
|
+
return `${transaction.clientTransactionId}\0${transaction.requestHash}`;
|
|
1613
|
+
}
|
|
1614
|
+
function arraysEqual(left, right) {
|
|
1615
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
1616
|
+
}
|
|
1617
|
+
function bytesEqual(left, right) {
|
|
1618
|
+
if (left.byteLength !== right.byteLength) return false;
|
|
1619
|
+
for (let index = 0; index < left.byteLength; index += 1) {
|
|
1620
|
+
if (left[index] !== right[index]) return false;
|
|
1621
|
+
}
|
|
1622
|
+
return true;
|
|
1623
|
+
}
|
|
1624
|
+
function requireFrontierAdvance(previous, next) {
|
|
1625
|
+
const nextByReplica = new Map(next.map((entry) => [entry.replicaId, entry.counter]));
|
|
1626
|
+
for (const entry of previous) {
|
|
1627
|
+
if ((nextByReplica.get(entry.replicaId) ?? -1) < entry.counter) {
|
|
1628
|
+
throw runtimeError("invalid_frontier", "committed causal frontier regresses");
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
function asWorkerFailure(error) {
|
|
1633
|
+
if (error instanceof ArtifactWorkerProtocolError) {
|
|
1634
|
+
return {
|
|
1635
|
+
code: normalizeErrorCode(error.code),
|
|
1636
|
+
message: sanitizeErrorMessage(error.message),
|
|
1637
|
+
retryable: false
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
if (error instanceof Error) {
|
|
1641
|
+
return {
|
|
1642
|
+
code: "kernel_failed",
|
|
1643
|
+
message: sanitizeErrorMessage(error.message || "artifact kernel failed"),
|
|
1644
|
+
retryable: false
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
return { code: "kernel_failed", message: "artifact kernel failed", retryable: false };
|
|
1648
|
+
}
|
|
1649
|
+
function asProtocolError(error) {
|
|
1650
|
+
if (error instanceof ArtifactWorkerProtocolError) return error;
|
|
1651
|
+
return new ArtifactWorkerProtocolError(
|
|
1652
|
+
"runtime_failed",
|
|
1653
|
+
error instanceof Error ? error.message : "artifact Worker runtime failed",
|
|
1654
|
+
{ cause: error }
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
function normalizeErrorCode(value) {
|
|
1658
|
+
return /^[a-z][a-z0-9_]{0,127}$/.test(value) ? value : "kernel_failed";
|
|
1659
|
+
}
|
|
1660
|
+
function sanitizeErrorMessage(value) {
|
|
1661
|
+
let output = "";
|
|
1662
|
+
for (let index = 0; index < value.length && output.length < 2e3; index += 1) {
|
|
1663
|
+
const code = value.charCodeAt(index);
|
|
1664
|
+
if (code >= 55296 && code <= 56319) {
|
|
1665
|
+
const following = value.charCodeAt(index + 1);
|
|
1666
|
+
if (following >= 56320 && following <= 57343) {
|
|
1667
|
+
output += value[index] + value[index + 1];
|
|
1668
|
+
index += 1;
|
|
1669
|
+
} else {
|
|
1670
|
+
output += "\uFFFD";
|
|
1671
|
+
}
|
|
1672
|
+
} else if (code >= 56320 && code <= 57343) {
|
|
1673
|
+
output += "\uFFFD";
|
|
1674
|
+
} else {
|
|
1675
|
+
output += value[index];
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
return output || "artifact kernel failed";
|
|
1679
|
+
}
|
|
1680
|
+
function runtimeError(code, message) {
|
|
1681
|
+
return new ArtifactWorkerProtocolError(code, message);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// src/editable-artifacts/worker/browser-entry.ts
|
|
1685
|
+
function installBrowserArtifactWorkerEntry(scope) {
|
|
1686
|
+
return new ArtifactWorkerRuntime({ endpoint: scope, loadAdapter: loadBrowserWasmKernelAdapter });
|
|
1687
|
+
}
|
|
1688
|
+
var candidate = globalThis;
|
|
1689
|
+
if (candidate.document === void 0 && typeof candidate.addEventListener === "function" && typeof candidate.removeEventListener === "function" && typeof candidate.postMessage === "function") {
|
|
1690
|
+
installBrowserArtifactWorkerEntry({
|
|
1691
|
+
addEventListener(type, listener) {
|
|
1692
|
+
candidate.addEventListener(type, listener);
|
|
1693
|
+
},
|
|
1694
|
+
removeEventListener(type, listener) {
|
|
1695
|
+
candidate.removeEventListener(type, listener);
|
|
1696
|
+
},
|
|
1697
|
+
postMessage(message, transfer) {
|
|
1698
|
+
candidate.postMessage(
|
|
1699
|
+
message,
|
|
1700
|
+
transfer
|
|
1701
|
+
);
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
export {
|
|
1706
|
+
installBrowserArtifactWorkerEntry
|
|
1707
|
+
};
|
|
1708
|
+
//# sourceMappingURL=editable-artifacts-worker.js.map
|